DEV: Working on environments and the recovery from nil pointers. #29

Merged
azpect merged 4 commits from dev into master 2025-07-22 22:45:34 -07:00
4 changed files with 110 additions and 49 deletions
Showing only changes of commit e0e1230660 - Show all commits

View File

@ -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()

View File

@ -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))
}

View File

@ -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")
if err != nil {
fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err)
// 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))
}
redirect_domain := os.Getenv("DOMAIN")
func (s *Server) Setup() *Server {
// SETUP THE ENVIRONMENT CONFIGURATION
cfg, err := domain.LoadEnvironment()
if err != nil {
panic(err.Error())
}
if cfg == nil {
panic("Environment configuration is nil, crashing.")
}
// 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

View File

@ -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
}