commit c781b7cabf8c742d8dea2c190a8295c9f1a83187 Author: teddy Date: Thu Apr 17 15:32:39 2025 +0700 Initial commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..7ffe267 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,40 @@ +# tahapan +stages: + - cleaning + - build_image + - deploy + +# build image on development server +build_image: + stage: build_image + script: + - docker build -t 36.66.3.44:7007/$PROJECT_NAME:$CI_PIPELINE_ID . + - docker push 36.66.3.44:7007/$PROJECT_NAME:$CI_PIPELINE_ID + environment: + only: + - main + tags: + - runner-internal-his + +# deploy to development server +deploy_development: + stage: deploy + script: + - docker stack deploy --compose-file compose.yml email-notification + environment: + only: + - main + tags: + - runner-internal-his + +# deploy to development server +cleaning_image_and_container: + stage: cleaning + script: + - echo y | docker container prune + - echo y | docker image prune -a + environment: + only: + - main + tags: + - runner-internal-his diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1d2fe04 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM golang:latest +ENV TZ="Asia/Jakarta" + +WORKDIR /app + +COPY . . + +RUN go mod download + +RUN go build -o email-notification + +EXPOSE 3000 + +CMD [ "./email-notification" ] \ No newline at end of file diff --git a/app/app.go b/app/app.go new file mode 100644 index 0000000..4879f7a --- /dev/null +++ b/app/app.go @@ -0,0 +1 @@ +package app diff --git a/app/mail-config.go b/app/mail-config.go new file mode 100644 index 0000000..5f70992 --- /dev/null +++ b/app/mail-config.go @@ -0,0 +1,72 @@ +package app + +import ( + "bytes" + "fmt" + "log" + "net/smtp" + "os" + "text/template" +) + +const CONFIG_SMTP_HOST = "smtp.gmail.com" +const CONFIG_SMTP_PORT = 587 +const CONFIG_SENDER_NAME = "HIS " +const CONFIG_AUTH_EMAIL = "hats.nutech@gmail.com" +const CONFIG_AUTH_PASSWORD = "lbfjfuywqktkexij" + +func Mail() { + // dialer := gomail.NewDialer( + // CONFIG_SMTP_HOST, + // CONFIG_SMTP_PORT, + // CONFIG_AUTH_EMAIL, + // CONFIG_AUTH_PASSWORD, + // ) + + // return dialer +} + +func SendEmail(to []string, cc []string, subject, message string) error { + tmpl, err := template.ParseFiles("template/test.html") + if err != nil { + log.Fatalf("Error parsing template: %v", err) + } + + fmt.Println("qwqwqw", os.Getenv("PORT")) + + type Data struct { + Name string + Company string + } + + data := Data{Name: "Salwa Balqis Sulaeman", Company: "HIS"} + + var buf bytes.Buffer + if err := tmpl.Execute(&buf, data); err != nil { + log.Fatalf("Error executing template: %v", err) + } + + // content, err := os.ReadFile("template/test.html") + // if err != nil { + // fmt.Println("Error reading file:", err) + // return err + // } + + htmlString := buf.String() + + sender := "From: " + CONFIG_SENDER_NAME + "\n" + subjectt := "Subject: 👋 Hello from Go\n" + mime := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n" + body := htmlString + messages := []byte(sender + subjectt + mime + body) + + auth := smtp.PlainAuth("", CONFIG_AUTH_EMAIL, CONFIG_AUTH_PASSWORD, CONFIG_SMTP_HOST) + smtpAddr := fmt.Sprintf("%s:%d", CONFIG_SMTP_HOST, CONFIG_SMTP_PORT) + + err = smtp.SendMail(smtpAddr, auth, CONFIG_AUTH_EMAIL, append(to, cc...), messages) + if err != nil { + return err + } + + return nil +} diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..5ca2480 --- /dev/null +++ b/compose.yml @@ -0,0 +1,16 @@ +version: '3.8' + +services: + app: + image: 36.66.3.44:7007/$PROJECT_NAME:$CI_PIPELINE_ID + deploy: + replicas : 1 + restart_policy : + condition: on-failure + resources: + limits: + cpus: "0.1" + memory: "300m" + restart: always + ports: + - "7010:3000" \ No newline at end of file diff --git a/config/db.go b/config/db.go new file mode 100644 index 0000000..7394199 --- /dev/null +++ b/config/db.go @@ -0,0 +1,51 @@ +package config + +import ( + "fmt" + "os" + "strconv" + "time" + + "gorm.io/driver/postgres" + "gorm.io/gorm" +) + +func NewDatabase() *gorm.DB { + username := os.Getenv("DB_USERNAME") + password := "123$Nt1" + port, err := strconv.Atoi(os.Getenv("DB_PORT")) + if err != nil { + fmt.Println("error ", err.Error()) + } + host := os.Getenv("DB_HOST") + database := os.Getenv("DB_NAME") + idleConnection := 10 + maxConnection := 100 + maxLifeTimeConnection := 300 + + dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Jakarta", host, username, password, database, port) + + db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ + // Logger: logger.New(&logrusWriter{Logger: log}, logger.Config{ + // SlowThreshold: time.Second * 5, + // Colorful: false, + // IgnoreRecordNotFoundError: true, + // ParameterizedQueries: true, + // LogLevel: logger.Info, + // }), + }) + if err != nil { + fmt.Println("failed to connect database: ", err) + } + + connection, err := db.DB() + if err != nil { + fmt.Println("failed to connect database: ", err) + } + + connection.SetMaxIdleConns(idleConnection) + connection.SetMaxOpenConns(maxConnection) + connection.SetConnMaxLifetime(time.Second * time.Duration(maxLifeTimeConnection)) + + return db +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3da1f21 --- /dev/null +++ b/go.mod @@ -0,0 +1,22 @@ +module email-notification + +go 1.21.1 + +require ( + github.com/joho/godotenv v1.5.1 + gorm.io/driver/postgres v1.5.11 + gorm.io/gorm v1.25.12 +) + +require ( + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.5.5 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/stretchr/testify v1.10.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/text v0.21.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ad9624e --- /dev/null +++ b/go.sum @@ -0,0 +1,38 @@ +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/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +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= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= +gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= diff --git a/main.go b/main.go new file mode 100644 index 0000000..fb159f4 --- /dev/null +++ b/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "email-notification/app" + "email-notification/config" + "fmt" + "log" + "net/http" + "os" + + "github.com/joho/godotenv" +) + +func main() { + fmt.Println("Test") + err := godotenv.Load() + if err != nil { + log.Fatal("Error loading .env file") + } + + config.NewDatabase() + + to := []string{"teddy.nutech@gmail.com"} + cc := []string{"teddy.nutech@gmail.com"} + subject := "test" + message := "test" + + // conn, err := amqp091.Dial("amqp://guest:guest@36.66.3.44:7012/") + // if err != nil { + // fmt.Println("Failed to connect to RabbitMQ: ", err) + // } + // defer conn.Close() + + // ch, err := conn.Channel() + // if err != nil { + // fmt.Println("Failed to open a channel: ", err) + // } + // defer ch.Close() + + http.HandleFunc("/notif-email", func(w http.ResponseWriter, r *http.Request) { + + // ch.Publish("notification", "email", false, false, amqp091.Publishing{ + // ContentType: "application/json", + // Body: []byte("test-email"), + // }) + + go func() { + fmt.Println("sending email...") + err := app.SendEmail(to, cc, subject, message) + if err != nil { + fmt.Println("error", err.Error()) + } + fmt.Println("email sent") + }() + + w.Write([]byte{}) + }) + + port := os.Getenv("PORT") + + http.ListenAndServe(":"+port, nil) + + // q, err := ch.QueueDeclare( + // "email", // name + // false, + // false, + // false, + // false, + // nil, + // ) + // if err != nil { + // fmt.Println("Failed to declare a queue:", err) + // } + + // msgs, err := ch.Consume( + // "email", + // "", // consumer + // true, // auto-ack + // false, // exclusive + // false, // no-local + // false, // no-wait + // nil, + // ) + // if err != nil { + // fmt.Println("Failed to register a consumer: ", err) + // } + + // forever := make(chan bool) + + // go func() { + // for d := range msgs { + // log.Printf("Received a message: %s", d.Body) + // } + // }() + + // fmt.Println(" [*] Waiting for messages. To exit press CTRL+C") + // <-forever +} diff --git a/template/test.html b/template/test.html new file mode 100644 index 0000000..77685cd --- /dev/null +++ b/template/test.html @@ -0,0 +1,56 @@ + + + + + Selamat Ulang Tahun! + + + +
+
🎉🎂🥳
+

Selamat Ulang Tahun, {{.Name}}!

+

+ Semoga hari spesialmu dipenuhi dengan kebahagiaan, cinta, dan tawa yang + tak berakhir.
+ Terima kasih telah menjadi bagian dari perjalanan kami. 🎈 +

+ + logo +
+ +