diff --git a/cmd/web/main.go b/cmd/web/main.go index 8f3951d..56c437e 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -5,7 +5,7 @@ import "github.com/haydenhargreaves/Potion/internal/app/server" const PORT = 3000 func main() { - s := server.Init(PORT).ConfigureAuth().ConnectDatabase().Setup() + s := server.Init(PORT).Setup() defer s.DB.Close() s.Start() diff --git a/internal/app/handlers/page_handler.go b/internal/app/handlers/page_handler.go index cf90ba2..61dfe22 100644 --- a/internal/app/handlers/page_handler.go +++ b/internal/app/handlers/page_handler.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "net/http" - "os" "strconv" "github.com/a-h/templ" @@ -189,7 +188,7 @@ func RecipePage(ctx *gin.Context) { } title := "Potion - View Recipe" - page := pages.RecipePage(*recipe, *user, loggedIn, os.Getenv("DOMAIN")) + page := pages.RecipePage(*recipe, *user, loggedIn, deps.EnvironmentConfig.Domain) ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page)) } diff --git a/internal/app/server/server.go b/internal/app/server/server.go index e543222..233c031 100644 --- a/internal/app/server/server.go +++ b/internal/app/server/server.go @@ -4,7 +4,6 @@ import ( "database/sql" "fmt" "net/http" - "os" "strings" "github.com/a-h/templ/examples/integration-gin/gintemplrenderer" @@ -15,7 +14,6 @@ import ( domain "github.com/haydenhargreaves/Potion/internal/domain/server" "github.com/haydenhargreaves/Potion/internal/infrastructure/auth" "github.com/haydenhargreaves/Potion/internal/infrastructure/database/repository" - "github.com/joho/godotenv" _ "github.com/lib/pq" ) @@ -53,18 +51,26 @@ func Init(port int) *Server { return server } -func (s *Server) ConfigureAuth() *Server { - err := godotenv.Load(".env") +// Start starts the server on the port provided when the server was initialized +func (s *Server) Start() { + s.Router.Run(fmt.Sprintf(":%d", s.port)) +} + +func (s *Server) Setup() *Server { + // SETUP THE ENVIRONMENT CONFIGURATION + cfg, err := domain.LoadEnvironment() if err != nil { - fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err) + panic(err.Error()) + } + if cfg == nil { + panic("Environment configuration is nil, crashing.") } - redirect_domain := os.Getenv("DOMAIN") - + // SETUP GOOGLE AUTH var ( - redirectUrl string = fmt.Sprintf("%s%s", redirect_domain, domain.API_AUTH_CALLBACK) - clientId string = os.Getenv("GOOGLE_CLIENT_ID") - clientSecret string = os.Getenv("GOOGLE_CLIENT_SECRET") + redirectUrl string = fmt.Sprintf("%s%s", cfg.Domain, domain.API_AUTH_CALLBACK) + clientId string = cfg.GoogleClientId + clientSecret string = cfg.GoogleClientSecret scope []string = []string{ "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", @@ -74,18 +80,8 @@ func (s *Server) ConfigureAuth() *Server { // Setup Google OAuth auth.NewGoogleConfig(redirectUrl, clientId, clientSecret, scope) - return s -} - -func (s *Server) ConnectDatabase() *Server { - err := godotenv.Load(".env") - if err != nil { - fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err) - } - - var connUrl string = os.Getenv("DATABASE_URL") - - db, err := sql.Open("postgres", connUrl) + // SETUP DATABASE + db, err := sql.Open("postgres", cfg.DatabaseUrl) if err != nil { panic("Could not connect to database: " + err.Error()) } @@ -96,21 +92,8 @@ func (s *Server) ConnectDatabase() *Server { s.DB = db - return s -} - -// Start starts the server on the port provided when the server was initialized -func (s *Server) Start() { - s.Router.Run(fmt.Sprintf(":%d", s.port)) -} - -func (s *Server) Setup() *Server { - err := godotenv.Load(".env") - if err != nil { - fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err) - } - - jwtSecret := []byte(os.Getenv("JWT_SECRET")) + // SETUP JWT + jwtSecret := []byte(cfg.JwtSecret) // Initialize and inject dependencies userRepo := repository.NewUserRepository(s.DB) @@ -126,6 +109,7 @@ func (s *Server) Setup() *Server { AuthService: authService, RecipeService: recipeService, EngagementService: engagementService, + EnvironmentConfig: *cfg, } // Apply middleware @@ -150,15 +134,8 @@ func (s *Server) Setup() *Server { // API router endpoints router_api.GET("/", func(ctx *gin.Context) { ctx.JSON(200, gin.H{"message": "Server is active."}) }) router_api.GET("/tmp", func(ctx *gin.Context) { - if !domain.IsLoggedIn(ctx) { - ctx.JSON(200, gin.H{"error": "User is not logged in. Please login to continue"}) - return - } - - userId := ctx.MustGet("userId").(int) - userEmail := ctx.MustGet("userEmail").(string) - - ctx.JSON(200, gin.H{"id": userId, "email": userEmail}) + deps := ctx.MustGet("deps").(*domain.InjectedDependencies) + ctx.JSON(200, gin.H{"config": deps.EnvironmentConfig}) }) // WEB router endpoints diff --git a/internal/domain/server/server.go b/internal/domain/server/server.go index a8861aa..e2d2712 100644 --- a/internal/domain/server/server.go +++ b/internal/domain/server/server.go @@ -1,14 +1,30 @@ package domain import ( + "fmt" + "os" + "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" domainAuth "github.com/haydenhargreaves/Potion/internal/domain/auth" domainEngagement "github.com/haydenhargreaves/Potion/internal/domain/engagement" domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe" domainUser "github.com/haydenhargreaves/Potion/internal/domain/user" + "github.com/joho/godotenv" ) +// EnvironmentConfig stores the configuration of the environment. Anything loaded from the .env +// or docker environment will be stored here and can be accessed from the InjectedDependencies +// struct, which this is attached to. +type EnvironmentConfig struct { + GoogleClientId string + GoogleClientSecret string + JwtSecret string + DatabaseUrl string + Environment string + Domain string +} + // InjectedDependencies is a collection of dependencies that are injected into the application. They // are stored in the context and can be accessed by handlers via the context. type InjectedDependencies struct { @@ -16,6 +32,7 @@ type InjectedDependencies struct { AuthService domainAuth.AuthService RecipeService domainRecipe.RecipeService EngagementService domainEngagement.EngagementService + EnvironmentConfig EnvironmentConfig } // JwtClaims is the data stored in the JSON web token. All that is needed is the users ID and their @@ -32,3 +49,71 @@ func IsLoggedIn(ctx *gin.Context) bool { _, email := ctx.Get("userEmail") return id && email } + +func LoadEnvironment() (*EnvironmentConfig, error) { + err := godotenv.Load(".env") + if err != nil { + fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err) + } + + env := os.Getenv("ENVIRONMENT") + if env == "" { + return nil, fmt.Errorf("ENVIRONMENT environment variable is required.") + } + + googleClientId := os.Getenv("GOOGLE_CLIENT_ID") + if googleClientId == "" { + return nil, fmt.Errorf("GOOGLE_CLIENT_ID environment variable is required.") + } + + googleClientSecret := os.Getenv("GOOGLE_CLIENT_SECRET") + if googleClientSecret == "" { + return nil, fmt.Errorf("GOOGLE_CLIENT_SECRET environment variable is required.") + } + + jwtSecret := os.Getenv("JWT_SECRET") + if jwtSecret == "" { + return nil, fmt.Errorf("JWT_SECRET environment variable is required.") + } + + var domain string + if env == "dev" { + domain = os.Getenv("DOMAIN_DEV") + if domain == "" { + return nil, fmt.Errorf("DOMAIN_DEV environment variable is required when ENVIRONMENT is 'dev'.") + } + } else if env == "prod" { + domain = os.Getenv("DOMAIN_PROD") + if domain == "" { + return nil, fmt.Errorf("DOMAIN_PROD environment variable is required when ENVIRONMENT is 'prod'.") + } + } else { + return nil, fmt.Errorf("ENVIRONMENT environment variable is required and must be 'dev' or 'prod'.") + } + + var dbUrl string + if env == "dev" { + dbUrl = os.Getenv("DATABASE_URL_DEV") + if dbUrl == "" { + return nil, fmt.Errorf("DATABASE_URL_DEV environment variable is required when ENVIRONMENT is 'dev'.") + } + } else if env == "prod" { + dbUrl = os.Getenv("DATABASE_URL_PROD") + if dbUrl == "" { + return nil, fmt.Errorf("DATABASE_URL_PROD environment variable is required when ENVIRONMENT is 'prod'.") + } + } else { + return nil, fmt.Errorf("ENVIRONMENT environment variable is required and must be 'dev' or 'prod'.") + } + + cfg := &EnvironmentConfig{ + GoogleClientId: googleClientId, + GoogleClientSecret: googleClientSecret, + JwtSecret: jwtSecret, + DatabaseUrl: dbUrl, + Environment: env, + Domain: domain, + } + + return cfg, nil +}