Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
380ad2d
refactor env to seperate env variables based on category
projectmap Nov 8, 2024
9d164ab
rename AWSConfig userPoolID and ClientID field name to include cognit…
projectmap Nov 8, 2024
450979c
rename DatabaseConfig DBUsername to Username and ServerConfig ServerP…
projectmap Nov 8, 2024
3906de3
feat: implement database provider interface with MySQL and PostgreSQL…
Denes-cilwal Oct 28, 2025
4e6c3f1
fix: remove sensitive URL logging in Postgres connection
Denes-cilwal Oct 28, 2025
f4718f4
Update pkg/infrastructure/db.go
Denes-cilwal Oct 30, 2025
c0931e5
Update pkg/infrastructure/db.go
Denes-cilwal Oct 30, 2025
0fa4d0d
Initial plan
Copilot Oct 30, 2025
b22a0cc
Initial plan
Copilot Oct 30, 2025
fd5cea5
Merge pull request #125 from wesionaryTEAM/copilot/sub-pr-122-again
Denes-cilwal Oct 30, 2025
52d7413
Merge pull request #124 from wesionaryTEAM/copilot/sub-pr-122
Denes-cilwal Oct 30, 2025
81d9dc9
Initial plan
Copilot Oct 30, 2025
0dd97d4
Merge pull request #126 from wesionaryTEAM/copilot/sub-pr-122-another…
Denes-cilwal Oct 30, 2025
08bdbf5
Add TranslateError option to gorm.Config in MySQL and PostgreSQL conn…
Denes-cilwal Oct 30, 2025
fe3d2b7
Initial plan
Copilot Oct 30, 2025
06f0724
feat: add database name validation to prevent SQL injection
Copilot Oct 30, 2025
42d3ccb
refactor: improve database name validation with stricter rules
Copilot Oct 30, 2025
c676101
Merge pull request #127 from wesionaryTEAM/copilot/sub-pr-122-another…
Denes-cilwal Oct 30, 2025
78f4bc3
refactor: remove redundant comments and improve database name validation
Denes-cilwal Oct 30, 2025
9aa0ad2
Merge branch 'develop' into env/refactor
Denes-cilwal Oct 30, 2025
eb0e9cd
refactor: restructure environment configuration and update references…
Denes-cilwal Oct 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ DB_USER=root
DB_PASS=secret
DB_FORWARD_PORT =

# cloudsql, mysql
# cloudsql, mysql, postgres
DB_TYPE=mysql

SENTRY_DSN=
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ require (
github.com/getsentry/sentry-go v0.28.0
github.com/gin-contrib/cors v1.7.2
github.com/gin-gonic/gin v1.10.0
github.com/go-sql-driver/mysql v1.8.1
github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1
github.com/lestrrat-go/jwx v1.2.29
Expand All @@ -31,6 +30,7 @@ require (
go.uber.org/zap v1.27.0
golang.org/x/sync v0.7.0
gorm.io/driver/mysql v1.5.6
gorm.io/driver/postgres v1.5.7
gorm.io/gorm v1.25.10
)

Expand Down Expand Up @@ -62,6 +62,7 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.21.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
Expand Down Expand Up @@ -112,7 +113,6 @@ require (
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/postgres v1.5.7 // indirect
gorm.io/driver/sqlite v1.5.5 // indirect
gorm.io/driver/sqlserver v1.5.3 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDm
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4=
github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U=
github.com/aws/aws-sdk-go-v2 v1.27.1 h1:xypCL2owhog46iFxBKKpBcw+bPTX/RJzwNj8uSilENw=
github.com/aws/aws-sdk-go-v2 v1.27.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to=
Expand Down Expand Up @@ -298,6 +300,8 @@ golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuh
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand Down Expand Up @@ -355,6 +359,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
Expand Down
17 changes: 17 additions & 0 deletions pkg/infrastructure/database_factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package infrastructure

import "clean-architecture/pkg/framework"

// NewDBProvider is an Fx constructor that provides the concrete DBProvider.
func NewDBProvider(env *framework.Env) DBProvider {
return providerDBFactory(env)
}

// NewDatabase receives a DBProvider via Fx and returns a connected Database.
func NewDatabase(logger framework.Logger, env *framework.Env, provider DBProvider) Database {
db, err := provider.Connect(logger, env)
if err != nil {
logger.Panic("failed to connect database: ", err)
}
return *db
}
75 changes: 53 additions & 22 deletions pkg/infrastructure/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"time"

"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)

Expand All @@ -14,48 +15,78 @@
*gorm.DB
}

// NewDatabase creates a new database instance
func NewDatabase(logger framework.Logger, env *framework.Env) Database {
// MySQLConnect implements provided MySQL logic.
func MySQLConnect(logger framework.Logger, env *framework.Env) (*gorm.DB, error) {
url := fmt.Sprintf("%s:%s@tcp(%s:%s)/?charset=utf8mb4&parseTime=True&loc=Local", env.DBUsername, env.DBPassword, env.DBHost, env.DBPort)

logger.Info("opening db connection")
logger.Info("opening db connection (mysql)")
db, err := gorm.Open(mysql.Open(url), &gorm.Config{Logger: logger.GetGormLogger()})
if err != nil {
logger.Panic(err)
return nil, err
}

logger.Info("creating database if it doesn't exist")
logger.Info("creating database if it doesn't exist (mysql)")
if err = db.Exec("CREATE DATABASE IF NOT EXISTS " + env.DBName).Error; err != nil {
logger.Info("couldn't create database")
logger.Panic(err)
logger.Info("couldn't create database (mysql)")
return nil, err
}

// close the current connection
sqlDb, err := db.DB()
if err != nil {
logger.Panic(err)
return nil, err
}
if dbErr := sqlDb.Close(); dbErr != nil {
logger.Panic(err)
return nil, dbErr
}

// reopen connection with the given database, after creating or checking if the database exists
logger.Info("using given database")
logger.Info("using given database (mysql)")
urlWithDB := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", env.DBUsername, env.DBPassword, env.DBHost, env.DBPort, env.DBName)
db, err = gorm.Open(mysql.Open(urlWithDB), &gorm.Config{Logger: logger.GetGormLogger()})
if err != nil {
logger.Panic(err)
return nil, err
}

conn, err := db.DB()
if err != nil {
logger.Info("couldn't get db connection")
logger.Panic(err)
logger.Info("couldn't get db connection (mysql)")
return nil, err
}

conn.SetConnMaxLifetime(time.Minute * 5)
conn.SetMaxOpenConns(5)
conn.SetMaxIdleConns(1)
return db, nil
}

return Database{DB: db}
// PostgresConnect implements provided PostgreSQL logic.
func PostgresConnect(logger framework.Logger, env *framework.Env) (*gorm.DB, error) {
// Determine SSL mode based on the environment
sslMode := "require"
if env.Environment == "local" || env.Environment == "workflow" {
sslMode = "disable"
}
url := fmt.Sprintf("host=%s port=%s user=%s password=%s database=postgres sslmode=%s", env.DBHost, env.DBPort, env.DBUsername, env.DBPassword, sslMode)
logger.Info("Connection to database (postgres)")
db, err := gorm.Open(postgres.Open(url), &gorm.Config{Logger: logger.GetGormLogger()})
if err != nil {
logger.Info("Url: ", url)
return nil, err
}
logger.Info("checking if the target database exists (postgres)")
var exists bool
if err = db.Raw("SELECT 1 FROM pg_database WHERE datname = ?", env.DBName).Scan(&exists).Error; err != nil {
return nil, fmt.Errorf("failed to check if the database exists: %w", err)
}
if !exists {
logger.Info("creating target database (postgres)")
if err := db.Exec(fmt.Sprintf("CREATE DATABASE %s", env.DBName)).Error; err != nil {
return nil, fmt.Errorf("couldn't create database: %w", err)
}
}
sqlDB, _ := db.DB()
_ = sqlDB.Close()

// connect to the target database
url = fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", env.DBHost, env.DBPort, env.DBUsername, env.DBPassword, env.DBName, sslMode)
logger.Info("connecting to target database (postgres)")
db, err = gorm.Open(postgres.Open(url), &gorm.Config{Logger: logger.GetGormLogger(), TranslateError: true})
if err != nil {
return nil, fmt.Errorf("failed to connect to target database: %w", err)
}
logger.Info("database connection established (postgres)")
return db, nil
}
28 changes: 28 additions & 0 deletions pkg/infrastructure/db_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package infrastructure

import (
"clean-architecture/pkg/framework"
"strings"
)

// DBProvider interface
type DBProvider interface {
Connect(logger framework.Logger, env *framework.Env) (*Database, error)
Type() string
}

// providerDBFactory chooses correct provider based on env.DBType.
func providerDBFactory(env *framework.Env) DBProvider {
dbType := strings.ToLower(strings.TrimSpace(env.DBType))
if dbType == "" {
return &MySQLProvider{}
}
switch dbType {
case "mysql":
return &MySQLProvider{}
case "postgres", "postgresql":
return &PostgresProvider{}
default:
return &MySQLProvider{} // fallback for unknown types
}
}
1 change: 1 addition & 0 deletions pkg/infrastructure/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "go.uber.org/fx"
var Module = fx.Options(
fx.Provide(
NewRouter,
NewDBProvider,
NewDatabase,
//NewS3Client,
//NewAWSConfig,
Expand Down
16 changes: 16 additions & 0 deletions pkg/infrastructure/mysql_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package infrastructure

import "clean-architecture/pkg/framework"

// MySQLProvider implements DBProvider for MySQL.
type MySQLProvider struct{}

func (p *MySQLProvider) Type() string { return "mysql" }

func (p *MySQLProvider) Connect(logger framework.Logger, env *framework.Env) (*Database, error) {
db, err := MySQLConnect(logger, env)
if err != nil {
return nil, err
}
return &Database{DB: db}, nil
}
16 changes: 16 additions & 0 deletions pkg/infrastructure/postgres_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package infrastructure

import "clean-architecture/pkg/framework"

// PostgresProvider implements DBProvider for PostgreSQL.
type PostgresProvider struct{}

func (p *PostgresProvider) Type() string { return "postgres" }

func (p *PostgresProvider) Connect(logger framework.Logger, env *framework.Env) (*Database, error) {
db, err := PostgresConnect(logger, env)
if err != nil {
return nil, err
}
return &Database{DB: db}, nil
}