diff --git a/.env.sample b/.env.sample index 9e2e974..525e79e 100644 --- a/.env.sample +++ b/.env.sample @@ -12,5 +12,9 @@ SMTP_PORT= SMTP_FROM= SMTP_TO= #set it if you dest is always the same +#DB +DB_URL="./data/mailer.db" + +# CORS CORS_ALLOW_ORIGIN= #to complete if APP_ENV!=dev CORS_METHODS= #to complete if APP_ENV!=dev \ No newline at end of file diff --git a/Makefile b/Makefile index 7209a29..acd7026 100644 --- a/Makefile +++ b/Makefile @@ -11,13 +11,19 @@ build: run: @go run cmd/api/main.go +# reset sqlite db +db-reset: + @docker compose down && rm ./data/mailer.db && docker compose up -d + # Create DB container dc-up: - docker compose up -d + @echo "Starting your application..." + @docker compose up -d # Shutdown DB container dc-down: - docker compose down + @echo "Closing..." + @docker compose down # Test the application test: @@ -27,7 +33,7 @@ test: # Clean the binary clean: @echo "Cleaning..." - @rm -f main + @go mod tidy # Live Reload watch: diff --git a/cmd/api/main.go b/cmd/api/main.go index 58ce208..6b898e3 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -3,6 +3,7 @@ package main import ( "fmt" "log" + "mailer_ms/cmd/database" "mailer_ms/src/router" "os" @@ -17,6 +18,15 @@ func main() { } } + db, err := database.Connect() + migration := database.NewMigration(db) + if err != nil { + log.Println("Failed to connect to db") + } + if err = migration.Migrate(); err != nil { + fmt.Printf("Failed to migrate db: %s", err) + } + r := router.Serve() log.Printf("📡 Server start on port %s \n", os.Getenv("PORT")) if err := r.Run(); err != nil { diff --git a/cmd/database/database.go b/cmd/database/database.go new file mode 100644 index 0000000..73dfaac --- /dev/null +++ b/cmd/database/database.go @@ -0,0 +1,12 @@ +package database + +import ( + "database/sql" + "os" + + _ "github.com/mattn/go-sqlite3" +) + +func Connect() (*sql.DB, error) { + return sql.Open("sqlite3", os.Getenv("DB_URL")) +} diff --git a/cmd/database/migration.go b/cmd/database/migration.go new file mode 100644 index 0000000..acbe939 --- /dev/null +++ b/cmd/database/migration.go @@ -0,0 +1,57 @@ +package database + +import ( + "database/sql" + "io" + "os" + "strings" +) + +type Migration struct { + dbPool *sql.DB +} + +func NewMigration(db *sql.DB) *Migration { + return &Migration{ + db, + } +} + +func (m Migration) Migrate() error { + if err := m.migrateFromFile("mail.sql"); err != nil { + return err + } + return nil +} + +func (m Migration) migrateFromFile(filename string) error { + workingDir, _ := os.Getwd() + fileOpen, err := os.Open(workingDir + "/cmd/database/migrations/" + filename) + if err != nil { + return err + } + defer fileOpen.Close() + + content, err := io.ReadAll(fileOpen) + if err != nil { + return err + } + + queries := string(content) + queriesSplit := strings.Split(queries, "--") + + for _, query := range queriesSplit { + if strings.TrimSpace(query) == "" { + continue + } + + _, err = m.dbPool.Exec(query + ";") + if err != nil { + if err.Error() == "trigger set_viewed_param already exists" { + continue + } + return err + } + } + return nil +} diff --git a/cmd/database/migrations/.gitignore b/cmd/database/migrations/.gitignore new file mode 100644 index 0000000..ff6a511 --- /dev/null +++ b/cmd/database/migrations/.gitignore @@ -0,0 +1,4 @@ +internal.sql +backup.sql +old.sql +test.sql \ No newline at end of file diff --git a/cmd/database/migrations/mail.sql b/cmd/database/migrations/mail.sql new file mode 100644 index 0000000..1f42d40 --- /dev/null +++ b/cmd/database/migrations/mail.sql @@ -0,0 +1,28 @@ +/* Caution : each query must be separated with empty comment */ + +CREATE TABLE IF NOT EXISTS mail +( + _id integer PRIMARY KEY AUTOINCREMENT not null, + date date, + "to" varchar(255) not null, + subject varchar(255) not null, + sent boolean not null, + error text, + viewed boolean +); + +-- + +CREATE TRIGGER set_viewed_param + BEFORE INSERT + ON "mail" +BEGIN + INSERT INTO "mail" ("to", "subject", "sent", "error", "viewed", "date") + VALUES (NEW."to", + NEW."subject", + NEW."sent", + NEW."error", + CASE WHEN NEW."error" IS NULL THEN 1 ELSE 0 END, + DATE('now')); + SELECT RAISE(IGNORE); +END; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index bc42190..ed2e85d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,4 @@ services: ports: - 4545:4545 volumes: - - .:/app - - db:/app/data -volumes: - db: \ No newline at end of file + - .:/app \ No newline at end of file diff --git a/go.mod b/go.mod index 46ce79b..def730e 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,23 @@ module mailer_ms + go 1.20 +require ( + github.com/gin-gonic/gin v1.9.1 + github.com/joho/godotenv v1.5.1 + github.com/mattn/go-sqlite3 v1.14.19 +) + require ( github.com/bytedance/sonic v1.10.0-rc3 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.9.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/validator/v10 v10.14.1 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/joho/godotenv v1.5.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/leodido/go-urn v1.2.4 // indirect diff --git a/go.sum b/go.sum index b5fc9d1..707ff1f 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,7 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpV github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= @@ -16,6 +17,7 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -25,6 +27,7 @@ github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QX github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= @@ -39,6 +42,8 @@ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= +github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -46,6 +51,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -56,6 +62,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= @@ -74,10 +81,12 @@ golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/src/controllers/helloWorld.controller.go b/src/controllers/helloWorld.controller.go index 1bd5410..5e6918b 100644 --- a/src/controllers/helloWorld.controller.go +++ b/src/controllers/helloWorld.controller.go @@ -26,7 +26,7 @@ func (a *ApiController) HelloWorldWithHtml(c *gin.Context) { if err != nil { fmt.Println(err) c.JSON(http.StatusBadRequest, gin.H{ - "message": "failed to parse html template with given variables", + "message": "Error: Failed to parse html template with given variables", }) return } diff --git a/src/mailer/mailer.go b/src/mailer/mailer.go index 41c7c2f..a323237 100644 --- a/src/mailer/mailer.go +++ b/src/mailer/mailer.go @@ -7,14 +7,14 @@ import ( ) type Mailer struct { - From string + from string smtpUrl string plainAuth smtp.Auth } func NewMailer() *Mailer { m := &Mailer{ - From: os.Getenv("SMTP_FROM"), + from: os.Getenv("SMTP_FROM"), } m.smtpAuthentication() @@ -34,7 +34,7 @@ func (m *Mailer) smtpAuthentication() { func (m *Mailer) SendEmail(r *Request) { r.SetHeaders() message := r.Headers + r.Body - err := smtp.SendMail(m.smtpUrl, m.plainAuth, m.From, r.To, []byte(message)) + err := smtp.SendMail(m.smtpUrl, m.plainAuth, m.from, r.To, []byte(message)) if err != nil { log.Fatal(err) }