(FEAT): Dev/prod environment toggle is complete!
We can now use environment values to dictate which values are used for the DB and the domain. This is a simple solution, but for now, it works! This will merge into master and we can then see it live in action!
This commit is contained in:
parent
94740b4b54
commit
e0e1230660
@ -5,7 +5,7 @@ import "github.com/haydenhargreaves/Potion/internal/app/server"
|
|||||||
const PORT = 3000
|
const PORT = 3000
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s := server.Init(PORT).ConfigureAuth().ConnectDatabase().Setup()
|
s := server.Init(PORT).Setup()
|
||||||
defer s.DB.Close()
|
defer s.DB.Close()
|
||||||
|
|
||||||
s.Start()
|
s.Start()
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/a-h/templ"
|
"github.com/a-h/templ"
|
||||||
@ -189,7 +188,7 @@ func RecipePage(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
title := "Potion - View Recipe"
|
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))
|
ctx.HTML(http.StatusOK, "", layouts.AppLayout(title, page))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/a-h/templ/examples/integration-gin/gintemplrenderer"
|
"github.com/a-h/templ/examples/integration-gin/gintemplrenderer"
|
||||||
@ -15,7 +14,6 @@ import (
|
|||||||
domain "github.com/haydenhargreaves/Potion/internal/domain/server"
|
domain "github.com/haydenhargreaves/Potion/internal/domain/server"
|
||||||
"github.com/haydenhargreaves/Potion/internal/infrastructure/auth"
|
"github.com/haydenhargreaves/Potion/internal/infrastructure/auth"
|
||||||
"github.com/haydenhargreaves/Potion/internal/infrastructure/database/repository"
|
"github.com/haydenhargreaves/Potion/internal/infrastructure/database/repository"
|
||||||
"github.com/joho/godotenv"
|
|
||||||
|
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
@ -53,18 +51,26 @@ func Init(port int) *Server {
|
|||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ConfigureAuth() *Server {
|
// Start starts the server on the port provided when the server was initialized
|
||||||
err := godotenv.Load(".env")
|
func (s *Server) Start() {
|
||||||
if err != nil {
|
s.Router.Run(fmt.Sprintf(":%d", s.port))
|
||||||
fmt.Printf("No .env file found or error loading .env: %v. Relying on system environment variables.", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (
|
var (
|
||||||
redirectUrl string = fmt.Sprintf("%s%s", redirect_domain, domain.API_AUTH_CALLBACK)
|
redirectUrl string = fmt.Sprintf("%s%s", cfg.Domain, domain.API_AUTH_CALLBACK)
|
||||||
clientId string = os.Getenv("GOOGLE_CLIENT_ID")
|
clientId string = cfg.GoogleClientId
|
||||||
clientSecret string = os.Getenv("GOOGLE_CLIENT_SECRET")
|
clientSecret string = cfg.GoogleClientSecret
|
||||||
scope []string = []string{
|
scope []string = []string{
|
||||||
"https://www.googleapis.com/auth/userinfo.email",
|
"https://www.googleapis.com/auth/userinfo.email",
|
||||||
"https://www.googleapis.com/auth/userinfo.profile",
|
"https://www.googleapis.com/auth/userinfo.profile",
|
||||||
@ -74,18 +80,8 @@ func (s *Server) ConfigureAuth() *Server {
|
|||||||
// Setup Google OAuth
|
// Setup Google OAuth
|
||||||
auth.NewGoogleConfig(redirectUrl, clientId, clientSecret, scope)
|
auth.NewGoogleConfig(redirectUrl, clientId, clientSecret, scope)
|
||||||
|
|
||||||
return s
|
// SETUP DATABASE
|
||||||
}
|
db, err := sql.Open("postgres", cfg.DatabaseUrl)
|
||||||
|
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("Could not connect to database: " + err.Error())
|
panic("Could not connect to database: " + err.Error())
|
||||||
}
|
}
|
||||||
@ -96,21 +92,8 @@ func (s *Server) ConnectDatabase() *Server {
|
|||||||
|
|
||||||
s.DB = db
|
s.DB = db
|
||||||
|
|
||||||
return s
|
// SETUP JWT
|
||||||
}
|
jwtSecret := []byte(cfg.JwtSecret)
|
||||||
|
|
||||||
// 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"))
|
|
||||||
|
|
||||||
// Initialize and inject dependencies
|
// Initialize and inject dependencies
|
||||||
userRepo := repository.NewUserRepository(s.DB)
|
userRepo := repository.NewUserRepository(s.DB)
|
||||||
@ -126,6 +109,7 @@ func (s *Server) Setup() *Server {
|
|||||||
AuthService: authService,
|
AuthService: authService,
|
||||||
RecipeService: recipeService,
|
RecipeService: recipeService,
|
||||||
EngagementService: engagementService,
|
EngagementService: engagementService,
|
||||||
|
EnvironmentConfig: *cfg,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply middleware
|
// Apply middleware
|
||||||
@ -150,15 +134,8 @@ func (s *Server) Setup() *Server {
|
|||||||
// API router endpoints
|
// API router endpoints
|
||||||
router_api.GET("/", func(ctx *gin.Context) { ctx.JSON(200, gin.H{"message": "Server is active."}) })
|
router_api.GET("/", func(ctx *gin.Context) { ctx.JSON(200, gin.H{"message": "Server is active."}) })
|
||||||
router_api.GET("/tmp", func(ctx *gin.Context) {
|
router_api.GET("/tmp", func(ctx *gin.Context) {
|
||||||
if !domain.IsLoggedIn(ctx) {
|
deps := ctx.MustGet("deps").(*domain.InjectedDependencies)
|
||||||
ctx.JSON(200, gin.H{"error": "User is not logged in. Please login to continue"})
|
ctx.JSON(200, gin.H{"config": deps.EnvironmentConfig})
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userId := ctx.MustGet("userId").(int)
|
|
||||||
userEmail := ctx.MustGet("userEmail").(string)
|
|
||||||
|
|
||||||
ctx.JSON(200, gin.H{"id": userId, "email": userEmail})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// WEB router endpoints
|
// WEB router endpoints
|
||||||
|
|||||||
@ -1,14 +1,30 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
domainAuth "github.com/haydenhargreaves/Potion/internal/domain/auth"
|
domainAuth "github.com/haydenhargreaves/Potion/internal/domain/auth"
|
||||||
domainEngagement "github.com/haydenhargreaves/Potion/internal/domain/engagement"
|
domainEngagement "github.com/haydenhargreaves/Potion/internal/domain/engagement"
|
||||||
domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
domainRecipe "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
||||||
domainUser "github.com/haydenhargreaves/Potion/internal/domain/user"
|
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
|
// 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.
|
// are stored in the context and can be accessed by handlers via the context.
|
||||||
type InjectedDependencies struct {
|
type InjectedDependencies struct {
|
||||||
@ -16,6 +32,7 @@ type InjectedDependencies struct {
|
|||||||
AuthService domainAuth.AuthService
|
AuthService domainAuth.AuthService
|
||||||
RecipeService domainRecipe.RecipeService
|
RecipeService domainRecipe.RecipeService
|
||||||
EngagementService domainEngagement.EngagementService
|
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
|
// 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")
|
_, email := ctx.Get("userEmail")
|
||||||
return id && email
|
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
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user