Initial commit

This commit is contained in:
teddy
2025-04-17 15:32:39 +07:00
commit c781b7cabf
12 changed files with 410 additions and 0 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
.env

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.env

40
.gitlab-ci.yml Normal file
View File

@@ -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

14
Dockerfile Normal file
View File

@@ -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" ]

1
app/app.go Normal file
View File

@@ -0,0 +1 @@
package app

72
app/mail-config.go Normal file
View File

@@ -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 <hats.nutech@gmail.com>"
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
}

16
compose.yml Normal file
View File

@@ -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"

51
config/db.go Normal file
View File

@@ -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
}

22
go.mod Normal file
View File

@@ -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
)

38
go.sum Normal file
View File

@@ -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=

98
main.go Normal file
View File

@@ -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
}

56
template/test.html Normal file
View File

@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Selamat Ulang Tahun!</title>
<style>
.container {
max-width: 600px;
margin: auto;
padding: 30px;
background-color: #fef9f4;
font-family: Arial, sans-serif;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
text-align: center;
}
.title {
font-size: 28px;
color: #d35400;
margin-bottom: 10px;
}
.message {
font-size: 18px;
color: #333;
margin-bottom: 30px;
}
.footer {
font-size: 14px;
/* color: #aaa; */
}
.cake-emoji {
font-size: 50px;
}
</style>
</head>
<body>
<div class="container">
<div class="cake-emoji">🎉🎂🥳</div>
<h1 class="title">Selamat Ulang Tahun, {{.Name}}!</h1>
<p class="message">
Semoga hari spesialmu dipenuhi dengan kebahagiaan, cinta, dan tawa yang
tak berakhir.<br />
Terima kasih telah menjadi bagian dari perjalanan kami. 🎈
</p>
<p class="footer">
Salam hangat dari kami,<br />
<b>Tim {{.Company}}</b>
</p>
<img
src="http://36.66.3.44:9000/create-bucket-test/test-folder/LOGO_HIS_NI_New_Contrasted-removebg-preview.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=BDYv2npRYZw5fcXzd80c%2F20250417%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250417T045323Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=c399ff0031b0d8bc9b7073171bca8ad3a9c2b82a374e25e60dbd35bd5bd5775e"
alt="logo"
width="100px"
/>
</div>
</body>
</html>