diff --git a/backend/.gitignore b/backend/.gitignore index 6bb840d..d4bebea 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,5 +1,8 @@ -# Created by https://www.toptal.com/developers/gitignore/api/go,goland,visualstudiocode,vim -# Edit at https://www.toptal.com/developers/gitignore?templates=go,goland,visualstudiocode,vim +# Created by https://www.toptal.com/developers/gitignore/api/vim,dotenv,go +# Edit at https://www.toptal.com/developers/gitignore?templates=vim,dotenv,go + +### dotenv ### +.env ### Go ### # If you prefer the allow list template instead of the deny list, see community template: @@ -24,119 +27,6 @@ # Go workspace file go.work -### GoLand ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# AWS User-specific -.idea/**/aws.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# SonarLint plugin -.idea/sonarlint/ - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -### GoLand Patch ### -# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 - -# *.iml -# modules.xml -# .idea/misc.xml -# *.ipr - -# Sonarlint plugin -# https://plugins.jetbrains.com/plugin/7973-sonarlint -.idea/**/sonarlint/ - -# SonarQube Plugin -# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin -.idea/**/sonarIssues.xml - -# Markdown Navigator plugin -# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced -.idea/**/markdown-navigator.xml -.idea/**/markdown-navigator-enh.xml -.idea/**/markdown-navigator/ - -# Cache file creation bug -# See https://youtrack.jetbrains.com/issue/JBR-2257 -.idea/$CACHE_FILE$ - -# CodeStream plugin -# https://plugins.jetbrains.com/plugin/12206-codestream -.idea/codestream.xml - -# Azure Toolkit for IntelliJ plugin -# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij -.idea/**/azureSettings.xml - ### Vim ### # Swap [._]*.s[a-v][a-z] @@ -158,24 +48,5 @@ tags # Persistent undo [._]*.un~ -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# Local History for Visual Studio Code -.history/ - -# Built Visual Studio Code Extensions -*.vsix - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history -.ionide - -# End of https://www.toptal.com/developers/gitignore/api/go,goland,visualstudiocode,vim +# End of https://www.toptal.com/developers/gitignore/api/vim,dotenv,go diff --git a/backend/cmd/main.go b/backend/cmd/main.go new file mode 100644 index 0000000..bafc00a --- /dev/null +++ b/backend/cmd/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "github.com/gin-gonic/gin" + + "easywish/config" + "easywish/internal/logger" +) + +func main() { + + // Load the configuration + if err := config.LoadConfig(); err != nil { + panic(err) + } + + // Setup logger + logger.InitLogger() + defer logger.GetLogger().Sync() + + // Connect & migrate database + // models.Init() + + // Setup routes + r := gin.Default() + // serviceController.SetupRoutes(r) + // animalController.SetupRoutes(r) + r.Run() +} + diff --git a/backend/config/config.go b/backend/config/config.go new file mode 100644 index 0000000..4ad42a0 --- /dev/null +++ b/backend/config/config.go @@ -0,0 +1,92 @@ +package config + +import ( + "log" + "reflect" + "fmt" + + "github.com/spf13/viper" +) + +type Config struct { + Hostname string `mapstructure:"HOSTNAME"` + Port string `mapstructure:"PORT"` + + DatabaseUrl string `mapstructure:"DATABASE_URL"` + RedisUrl string `mapstructure:"REDIS_URL"` + MinioUrl string `mapstructure:"MINIO_URL"` + + JwtAlgorithm string `mapstructure:"JWT_ALGORITHM"` + JwtSecret string `mapstructure:"JWT_SECRET"` + JwtIssuer string `mapstructure:"JWT_ISSUER"` + JwtAudience string `mapstructure:"JWT_AUDIENCE"` + JwtExpAccess string `mapstructure:"JWT_EXP_ACCESS"` + JwtExpRefresh string `mapstructure:"JWT_EXP_REFRESH"` + + Environment string `mapstructure:"ENVIRONMENT"` +} + +var config *Config + +// TODO: migrate logging to Zap +func LoadConfig() error { + + // Load .env file + if err := viper.ReadInConfig(); err != nil { + log.Printf("Error reading config file, proceeding with environment variables. %s", err) + } + + // Set default parameters + viper.SetDefault("HOSTNAME", "localhost") + viper.SetDefault("PORT", "8080") + viper.SetDefault("DATABASE_URL", "mydb") + viper.SetDefault("REDIS_URL", "myredis") + viper.SetDefault("MINIO_URL", "myminio") + viper.SetDefault("JWT_ALGORITHM", "HS256") + viper.SetDefault("JWT_SECRET", "default_jwt_secret_please_change") // TODO: remove and randomly generate + viper.SetDefault("JWT_EXP_ACCESS", "5m") + viper.SetDefault("JWT_EXP_REFRESH", "1w") + viper.SetDefault("JWT_AUDIENCE", "easywish") + viper.SetDefault("JWT_ISSUER", "easywish") + + viper.SetDefault("ENVIRONMENT", "production") + + // Set the file name and type for Viper + viper.SetConfigName(".env") // name of config file (without extension) + viper.SetConfigType("env") // REQUIRED if the config file does not have the extension + viper.AddConfigPath(".") // optionally look for config in the working directory + + // Unmarshal the configuration into the Config struct + if err := viper.Unmarshal(&config); err != nil { + log.Fatalf("Unable to decode into struct, %v", err) + } + + // Perform validation + if err := validateConfig(); err != nil { + return err + } + + return nil +} + +func validateConfig() error { + v := reflect.ValueOf(*config) + t := v.Type() + + for i := range v.NumField() { + field := v.Field(i) + fieldType := t.Field(i) + + // Check if the field is a string and is empty + if field.Kind() == reflect.String && field.String() == "" { + return fmt.Errorf("Missing required configuration: %s", fieldType.Name) + } + } + + return nil +} + +func GetConfig() *Config { + return config +} + diff --git a/backend/go.mod b/backend/go.mod index cb0e2c6..e704a84 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -29,6 +29,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang-jwt/jwt/v5 v5.2.2 // indirect + github.com/joho/godotenv v1.5.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect diff --git a/backend/go.sum b/backend/go.sum index 79e397e..54604c9 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -55,6 +55,8 @@ github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeD github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= diff --git a/backend/internal/logger/logger.go b/backend/internal/logger/logger.go new file mode 100644 index 0000000..b519398 --- /dev/null +++ b/backend/internal/logger/logger.go @@ -0,0 +1,28 @@ +package logger + +import ( + "go.uber.org/zap" + "easywish/config" +) + +var logger *zap.Logger + +func InitLogger() { + var err error + cfg := config.GetConfig() + + // TODO: make this configurable + if cfg.Environment == "production" { + logger, err = zap.NewProduction() + } else { + logger, err = zap.NewDevelopment() + } + if err != nil { + panic(err) + } +} + +func GetLogger() *zap.Logger { + return logger +} +