package domain import ( "fmt" "os" "time" "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 { UserService domainUser.UserService 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 // Google email provided. type JwtClaims struct { UserId int `json:"id"` Email string `json:"email"` jwt.RegisteredClaims } // IsLoggedIn checks the cookies in a request and returns whether the user is logged in. func IsLoggedIn(ctx *gin.Context) bool { _, id := ctx.Get("userId") _, email := ctx.Get("userEmail") return id && email } // LoadEnvironment loads the environment values from either an .env file or docker environment. In // the event that required fields are not provided, an error will return and the caller should handle // the missing value or panic. Toggles between 'dev', 'prod', etc are also handled by this method, // the values can be access assuming they are the proper values based on the provided environment. 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 } // SetCookie sets a cookie value with a duration provided. This function handles setting the security // configuration as well as the domain. These values are based on the EnvironmentConfig, therefore // the value should be set. Nothing is returned by this function, but the cookie will be set. // // This function can also be used to clear cookies, if a blank value ("") and invalid duration (-1) // is provided. // // If 0 is provided as the duration, then a session cookie is created, which will be cleared when // the browser is closed. func SetCookie(ctx *gin.Context, name, value string, duration time.Duration) { deps := ctx.MustGet("deps").(*InjectedDependencies) var ( path string = "/" httpOnly bool = true maxAge int secure bool domain string ) if duration < 0 { // Delete the cookie maxAge = -1 } else if duration == 0 { // Session cookie, clears when browser is closed maxAge = 0 } else { // Normal calculation maxAge = int(time.Now().Add(duration).Sub(time.Now()).Seconds()) } if deps.EnvironmentConfig.Environment == "prod" { secure = true domain = deps.EnvironmentConfig.Domain } else if deps.EnvironmentConfig.Environment == "dev" { secure = false domain = deps.EnvironmentConfig.Domain } else { // Defaults secure = false domain = "" } ctx.SetCookie(name, value, maxAge, path, domain, secure, httpOnly) }