add: Environment Var support

This commit is contained in:
$(pass /github/name)
2024-07-17 16:46:29 +02:00
parent 9eef7f7681
commit f4a9166bee
9 changed files with 86 additions and 68 deletions

View File

@@ -2,11 +2,11 @@ package main
import ( import (
"GoMembership/internal/server" "GoMembership/internal/server"
"log" "GoMembership/pkg/logger"
) )
func main() { func main() {
log.Println("startup...") logger.Info.Println("startup...")
server.Run() server.Run()
} }

7
go.mod
View File

@@ -6,19 +6,19 @@ require (
github.com/gin-gonic/gin v1.10.0 github.com/gin-gonic/gin v1.10.0
github.com/go-playground/validator/v10 v10.22.0 github.com/go-playground/validator/v10 v10.22.0
github.com/jbub/banking v0.8.0 github.com/jbub/banking v0.8.0
github.com/stretchr/testify v1.9.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gorm.io/driver/sqlite v1.5.6 gorm.io/driver/sqlite v1.5.6
gorm.io/gorm v1.25.10 gorm.io/gorm v1.25.10
) )
require github.com/kelseyhightower/envconfig v1.4.0
require ( require (
github.com/bytedance/sonic v1.11.9 // indirect github.com/bytedance/sonic v1.11.9 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect github.com/cloudwego/iasm v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.4 // indirect; in
github.com/gabriel-vasile/mimetype v1.4.4 // in
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
@@ -33,7 +33,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.8.0 // indirect golang.org/x/arch v0.8.0 // indirect

2
go.sum
View File

@@ -36,6 +36,8 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=

View File

@@ -1,36 +1,39 @@
package config package config
import ( import (
"GoMembership/internal/utils"
"GoMembership/pkg/logger"
"encoding/json" "encoding/json"
"os" "os"
"path/filepath" "path/filepath"
"sync"
"github.com/kelseyhightower/envconfig"
"GoMembership/internal/utils"
"GoMembership/pkg/logger"
) )
type DatabaseConfig struct { type DatabaseConfig struct {
Path string `json:"Path"` Path string `json:"Path" envconfig:"DB_PATH"`
} }
type AuthenticationConfig struct { type AuthenticationConfig struct {
JWTSecret string JWTSecret string
CSRFSecret string CSRFSecret string
APIKEY string `json:"APIKey"` APIKEY string `json:"APIKey" envconfig:"API_KEY"`
} }
type SMTPConfig struct { type SMTPConfig struct {
Host string `json:"Host"` Host string `json:"Host" envconfig:"SMTP_HOST"`
User string `json:"User"` User string `json:"User" envconfig:"SMTP_USER"`
Password string `json:"Password"` Password string `json:"Password" envconfig:"SMTP_PASS"`
Mailtype string `json:"Mailtype"` Mailtype string `json:"Mailtype" envconfig:"MAIL_TYPE"`
AdminEmail string `json:"AdminEmail"` AdminEmail string `json:"AdminEmail" envconfig:"ADMIN_MAIL"`
Port int `json:"Port"` Port int `json:"Port" envconfig:"SMTP_PORT"`
} }
type TemplateConfig struct { type TemplateConfig struct {
MailDir string `json:"MailDir"` MailDir string `json:"MailDir" envconfig:"TEMPLATE_DIR"`
} }
type Config struct { type Config struct {
Auth AuthenticationConfig `json:"auth"` Auth AuthenticationConfig `json:"auth"`
DB DatabaseConfig `json:"db"` DB DatabaseConfig `json:"db"`
@@ -39,32 +42,16 @@ type Config struct {
} }
var ( var (
pConfig Config CFG Config
once sync.Once Auth AuthenticationConfig
loaded bool DB DatabaseConfig
Templates TemplateConfig
SMTP SMTPConfig
) )
func LoadConfig() *Config { func LoadConfig() {
path, err := os.Getwd() readFile(&CFG)
if err != nil { readEnv(&CFG)
logger.Error.Fatalf("could not get working directory: %v", err)
}
configFile, err := os.Open(filepath.Join(path, "configs", "config.json"))
if err != nil {
logger.Error.Fatalf("could not open config file: %v", err)
}
defer configFile.Close()
decoder := json.NewDecoder(configFile)
// pConfig = &Config{}
err = decoder.Decode(&pConfig)
if err != nil {
logger.Error.Fatalf("could not decode config file: %v", err)
}
if !loaded {
once.Do(
func() {
csrfSecret, err := utils.GenerateRandomString(32) csrfSecret, err := utils.GenerateRandomString(32)
if err != nil { if err != nil {
logger.Error.Fatalf("could not generate CSRF secret: %v", err) logger.Error.Fatalf("could not generate CSRF secret: %v", err)
@@ -74,11 +61,38 @@ func LoadConfig() *Config {
if err != nil { if err != nil {
logger.Error.Fatalf("could not generate JWT secret: %v", err) logger.Error.Fatalf("could not generate JWT secret: %v", err)
} }
pConfig.Auth.JWTSecret = jwtSecret CFG.Auth.JWTSecret = jwtSecret
pConfig.Auth.CSRFSecret = csrfSecret CFG.Auth.CSRFSecret = csrfSecret
loaded = true
}) Auth = CFG.Auth
DB = CFG.DB
Templates = CFG.Templates
SMTP = CFG.SMTP
}
func readFile(cfg *Config) {
path, err := os.Getwd()
if err != nil {
logger.Error.Fatalf("could not get working directory: %v", err)
} }
return &pConfig configFile, err := os.Open(filepath.Join(path, "configs", "config.json"))
// configFile, err := os.Open("config.json")
if err != nil {
logger.Error.Fatalf("could not open config file: %v", err)
}
defer configFile.Close()
decoder := json.NewDecoder(configFile)
err = decoder.Decode(cfg)
if err != nil {
logger.Error.Fatalf("could not decode config file: %v", err)
}
}
func readEnv(cfg *Config) {
err := envconfig.Process("", cfg)
if err != nil {
logger.Error.Fatalf("could not decode env variables: %#v", err)
}
} }

View File

@@ -28,14 +28,14 @@ func (mc *MembershipController) RegisterSubscription(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode subscription data"}) c.JSON(http.StatusBadRequest, gin.H{"error": "Couldn't decode subscription data"})
} }
logger.Info.Printf("Using API key: %v", config.LoadConfig().Auth.APIKEY) logger.Info.Printf("Using API key: %v", config.Auth.APIKEY)
if regData.APIKey == "" { if regData.APIKey == "" {
logger.Error.Println("API Key is missing") logger.Error.Println("API Key is missing")
c.JSON(http.StatusBadRequest, "API Key is missing") c.JSON(http.StatusBadRequest, "API Key is missing")
return return
} }
if regData.APIKey != config.LoadConfig().Auth.APIKEY { if regData.APIKey != config.Auth.APIKEY {
logger.Error.Printf("API Key not valid: %v", regData.APIKey) logger.Error.Printf("API Key not valid: %v", regData.APIKey)
c.JSON(http.StatusExpectationFailed, "API Key is missing") c.JSON(http.StatusExpectationFailed, "API Key is missing")
return return

View File

@@ -42,8 +42,8 @@ func TestUserController(t *testing.T) {
t.Errorf("Failed to create DB: %#v", err) t.Errorf("Failed to create DB: %#v", err)
} }
cfg := config.LoadConfig() config.LoadConfig()
emailService := services.NewEmailService(cfg.SMTP.Host, cfg.SMTP.Port, cfg.SMTP.User, cfg.SMTP.Password, cfg.SMTP.AdminEmail) emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password, config.SMTP.AdminEmail)
var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{} var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{}
consentService := &services.ConsentService{Repo: consentRepo} consentService := &services.ConsentService{Repo: consentRepo}

View File

@@ -1,14 +1,16 @@
package middlewares package middlewares
import ( import (
"GoMembership/internal/config"
"GoMembership/internal/utils"
"GoMembership/pkg/logger"
"crypto/hmac" "crypto/hmac"
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
"net/http" "net/http"
"strings" "strings"
"GoMembership/internal/config"
"GoMembership/internal/server"
"GoMembership/internal/utils"
"GoMembership/pkg/logger"
) )
// GenerateCSRFToken generates HMAC-signed CSRF token // GenerateCSRFToken generates HMAC-signed CSRF token
@@ -46,7 +48,7 @@ func CSRFMiddleware(next http.Handler) http.Handler {
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
return return
} }
csrfSecret := config.LoadConfig().Auth.CSRFSecret csrfSecret := config.Auth.CSRFSecret
// Retrieve CSRF token from request (e.g., from cookie, header, or form data) // Retrieve CSRF token from request (e.g., from cookie, header, or form data)
csrfToken := r.Header.Get("X-CSRF-Token") csrfToken := r.Header.Get("X-CSRF-Token")
@@ -78,7 +80,7 @@ func GenerateCSRFTokenHandler(w http.ResponseWriter, r *http.Request) {
sessionID := "exampleSessionID123" sessionID := "exampleSessionID123"
// Generate HMAC-signed CSRF token // Generate HMAC-signed CSRF token
csrfToken := GenerateCSRFToken(sessionID, config.LoadConfig().Auth.CSRFSecret) csrfToken := GenerateCSRFToken(sessionID, config.Auth.CSRFSecret)
// Set CSRF token in a cookie (example) // Set CSRF token in a cookie (example)
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{

View File

@@ -17,14 +17,15 @@ import (
) )
func Run() { func Run() {
cfg := config.LoadConfig() config.LoadConfig()
logger.Info.Printf("Config: %+v", cfg) logger.Info.Printf("Config loaded: %#v", config.CFG)
err := database.InitDB(cfg.DB.Path)
err := database.InitDB(config.DB.Path)
if err != nil { if err != nil {
logger.Error.Fatalf("Couldn't init database: %v", err) logger.Error.Fatalf("Couldn't init database: %v", err)
} }
emailService := services.NewEmailService(cfg.SMTP.Host, cfg.SMTP.Port, cfg.SMTP.User, cfg.SMTP.Password, cfg.SMTP.AdminEmail) emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password, config.SMTP.AdminEmail)
var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{} var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{}
consentService := &services.ConsentService{Repo: consentRepo} consentService := &services.ConsentService{Repo: consentRepo}

View File

@@ -37,7 +37,7 @@ func (s *EmailService) SendEmail(to string, subject string, body string) error {
func ParseTemplate(filename string, data interface{}) (string, error) { func ParseTemplate(filename string, data interface{}) (string, error) {
// Read the email template file // Read the email template file
templateDir := config.LoadConfig().Templates.MailDir templateDir := config.Templates.MailDir
tpl, err := template.ParseFiles(templateDir + "/" + filename) tpl, err := template.ParseFiles(templateDir + "/" + filename)
if err != nil { if err != nil {
logger.Error.Printf("Failed to parse email template: %v", err) logger.Error.Printf("Failed to parse email template: %v", err)
@@ -106,20 +106,20 @@ func (s *EmailService) SendWelcomeEmail(user *models.User) error {
func (s *EmailService) NotifyAdminOfNewUser(user *models.User) error { func (s *EmailService) NotifyAdminOfNewUser(user *models.User) error {
// Prepare data to be injected into the template // Prepare data to be injected into the template
data := struct { data := struct {
Company string City string
FirstName string Email string
LastName string LastName string
MembershipModel string MembershipModel string
MembershipID int64
MembershipFee float32
RentalFee float32
Address string Address string
ZipCode string
City string
DateOfBirth string
Email string
Phone string
IBAN string IBAN string
FirstName string
Phone string
DateOfBirth string
Company string
ZipCode string
MembershipID int64
RentalFee float32
MembershipFee float32
}{ }{
Company: *user.Company, Company: *user.Company,
FirstName: user.FirstName, FirstName: user.FirstName,
@@ -143,5 +143,5 @@ func (s *EmailService) NotifyAdminOfNewUser(user *models.User) error {
logger.Error.Print("Couldn't send admin notification mail") logger.Error.Print("Couldn't send admin notification mail")
return err return err
} }
return s.SendEmail(config.LoadConfig().SMTP.AdminEmail, subject, body) return s.SendEmail(config.SMTP.AdminEmail, subject, body)
} }