Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Grafiters committed Aug 15, 2023
0 parents commit 03143df
Show file tree
Hide file tree
Showing 20 changed files with 549 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
JWT_ALGORITHM=<jwt algorithm encrypted>
JWT_PUBLIC_KEY=<jwt public key not path>

RABBITMQ_URL=<rabbitmq host with format "amqp://{username}:{password}@{host}:{port}/">
RABBITMQ_QUEUE_NAME=<rabbitmq queue name>

SMTP_HOST={smtp host }
SMTP_PORT={ smtp port }
SMTP_USER={ smtp username }
SMTP_PASS={ smtp password }
SMTP_SENDER_EMAIL={ smtp sender email }
SMTP_SENDER_NAME={ smttp sender name }
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
107 changes: 107 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# ======> MAILER SERVICE <========
golang project provided to handle mailer service and has been integrated with `rabbitmq`, on channel `mailer_queue` and templateing with file `.html`, for provider sending email using smtp2go for the testing with template of `.env` is ready on this project,

the project using `enqueue` concept that set in file `rabbitmq/rabbitmq.go` will be automatically send `message` after consumming message from channel set on file `.env`

for example of list template email to send you can see in file `templates/*_template.html` for the lsit of template that will using message with variable `tag` on the message from channel for list of `tag` you can see at the file `handlers/handlers.go` and here this of list the `tag`

1. login
2. send_code
3. reset_password
4. api_key

that must be the same name on file `templates/*_template.html`, and if you want to customize the project you can update the file list

1. handlers -> file to handle tag of message from consuming message from queue
2. interfaces -> file to handling interfaces message like all variable they have
3. mailes -> file to handling send mail with configuration template from message
4. rabbitmq -> file to handling configuration of queue and subcribe to channel has been declared on rabbitmq queue
5. templates -> file to handling templating style to show for user
6. token -> file to handling decrypted message jwt from queue channel

flow of system on this project just like this :

the flow of system is started from your project

```
-> publish message json to rabbitmq channel when has been encrypted with jwt private key (example of json you see after this)
-> mailer service subcribe to channel and consume the message
-> message will decrypted first to check the tag
-> system will generate template of message with using json message after decryption
-> system will send message to target email
```

project will using dual decrypted when `first decrypted` is for to get `tag` group that using `Record` interfaces and then project will decrypt again using file in `interfaces` to get list of json messages `when is suitable on tag and handlers`

```
example of json data
{
record: {
email: string, // email address user
tag: string, // tag is for description to generate file
subject: string // subject of message to subject on email send
},
iss: 'name_project',
iat: <Time on Integer>,
exp: <Time on Integer>,
jti: SecureRandom.hex(10)
}
```

## ======> SETUP <========
Setup for this project is simple, you must prepare the configuration on file `.env`, configuration channel on the `rabbitmq`, the list of configuration you must following the step :

1. RabbitMQ

configuration rabbitmq you can follow this link `https://www.rabbitmq.com/download.html`

or you want to easly install RabbitMQ configuration you can running command on your terminal using

```bash
$ cd config && docker-compose up -Vd
```
after your config for rabbitmq installation don't forget to update sthe file `.env` on variable `RABBITMQ_HOST` with host to your `rabbitmq` has `installed`

2. Channel configuration

Channel configration you can using file in `config/rabbitmq_channel.yaml` on your project because this project just for consuming from config channel when you set in rabbitmq
you can create channel in rabbitmq in `exchange` and `queue` and don't forget to set the `queue_name` when you has been config in file `.env`

3. SMTP

Mailer using `github.com/go-mail/mail` for the package and for the configuration you can set in file `.env` on list variable in below
```
SMTP_HOST={smtp host }
SMTP_PORT={ smtp port }
SMTP_USER={ smtp username }
SMTP_PASS={ smtp password }
SMTP_SENDER_EMAIL={ smtp sender email }
SMTP_SENDER_NAME={ smttp sender name }
```
don't forget to change that `smtp configuration` with your smtp mailer you have, but on `sender email` must using `activated email` like `[email protected]` or something else, if you want to customize that configuration you can change in file `mailers/mailers.go`

4. JWT

configuration jwt is easy, you just need to `generate` the `jwt_public_key` and `jwt_private_key` and set the `algorithm` on file `.env`, you can follow the step :

- Generate Private Key
```bash
$ openssl genpkey -algorithm RSA -out private_key.pem
```

- Generate Public Key
```bash
$ openssl rsa -in private_key.pem -pubout -out public_key.pem
```

but in this project you must to convert that file output to `base64` configuration `private_key` or `public_key`
- Convert private key to base64
```bash
$ openssl base64 -in private_key.pem
```
- Convert Public key to base64
```bash
$ openssl rsa -in private_key.pem -pubout -outform DER | openssl base64 -A
```

after that you can set that `private_key` on your project to protect the message from your project for executing on this `mailer-service`, and for `mailer-service` you can must to using that `public_key` to decrypted the message from your `project` after consuming channel has you declared in file `.env`
16 changes: 16 additions & 0 deletions config/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: '3.6'
rabbitmq:
image: rabbitmq:3.7.6-management
restart: always
ports:
- "5672:5672"
- "15672:15672"
volumes:
- rabbitmq_data:/var/lib/rabbitmq
logging:
driver: "json-file"
options:
max-size: "50m"

volumes:
rabbitmq_data:
12 changes: 12 additions & 0 deletions config/rabbitmq_channel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exchanges:
- name: mailer_exchange
type: direct
durable: false
auto_delete: false
queues:
- name: mailer_queue
durable: true
auto_delete: false
bindings:
- exchange: mailer_exchange
routing_key: mailer.send_email
11 changes: 11 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module mailer

go 1.16

require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/go-mail/mail v2.3.1+incompatible // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/streadway/amqp v1.1.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/go-mail/mail v2.3.1+incompatible h1:UzNOn0k5lpfVtO31cK3hn6I4VEVGhe3lX8AJBAxXExM=
github.com/go-mail/mail v2.3.1+incompatible/go.mod h1:VPWjmmNyRsWXQZHVHT3g0YbIINUkSmuKOiLIDkWbL6M=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/streadway/amqp v1.1.0 h1:py12iX8XSyI7aN/3dUT8DFIDJazNJsVJdxNVEpnQTZM=
github.com/streadway/amqp v1.1.0/go.mod h1:WYSrTEYHOXHd0nwFeUXAe2G2hRnQT+deZJJf88uS9Bg=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
54 changes: 54 additions & 0 deletions handlers/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package handlers

import (
"log"

"mailer/token"
"mailer/interfaces"
)

func ClaimAllRecordData(msg string, tag string) (interface{}, error) {
switch tag {
case "login":
var interfaceData interfaces.Login
record := token.DecodeToken(msg, &interfaceData)
if record != nil {
log.Println(record)
return nil, record
}

return interfaceData, nil
case "send_code":
var interfaceData interfaces.VerifToken
record := token.DecodeToken(msg, &interfaceData)
if record != nil {
log.Println(record)
return nil, record
}

return interfaceData, nil
case "reset_password":
var interfaceData interfaces.VerifToken
record := token.DecodeToken(msg, &interfaceData)
if record != nil {
log.Println(record)
return nil, record
}

return interfaceData, nil
case "api_key":
var interfaceData interfaces.APIKey
record := token.DecodeToken(msg, &interfaceData)
if record != nil {
log.Println(record)
return nil, record
}

return interfaceData, nil
default:
log.Println("Tag not recognized")

var interfaceData interfaces.Record
return interfaceData, nil
}
}
7 changes: 7 additions & 0 deletions interfaces/api_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package interfaces

type APIKey struct {
Label string `json:"label"`
ApiKey string `json:"api_key"`
ActivationToken int `json:"activation_token"`
}
7 changes: 7 additions & 0 deletions interfaces/login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package interfaces

type Login struct {
Email string `json:"email"`
Device string `json:"device,omitempty"`
LoginTime string `json:"login_time,omitempty"`
}
7 changes: 7 additions & 0 deletions interfaces/record.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package interfaces

type Record struct {
Subject string `json:"subject"`
Tag string `json:"tag"`
Email string `json:"email"`
}
10 changes: 10 additions & 0 deletions interfaces/verif_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package interfaces

type VerifToken struct {
Username string `json:"username"`
Email string `json:"email"`
GoogleID string `json:"google_id"`
Role string `json:"role"`
ActivationCode int `json:"email_verification_token"`
ResetPasswordToken int `json:"reset_password_token"`
}
68 changes: 68 additions & 0 deletions mailers/mailers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package mailers

import (
"os"
"log"
"bytes"
"strconv"
"text/template"
"mailer/interfaces"

"github.com/go-mail/mail"
"github.com/joho/godotenv"
)

func SendMail(record interfaces.Record, msg interface{}){
err := godotenv.Load()
if err != nil {
log.Println("Error loading .env file:", err)
}

smtpHost := os.Getenv("SMTP_HOST")
smtpPortStr := os.Getenv("SMTP_PORT")
smtpUser := os.Getenv("SMTP_USER")
smtpPass := os.Getenv("SMTP_PASS")
senderEmail := os.Getenv("SMTP_SENDER_EMAIL")
senderName := os.Getenv("SMTP_SENDER_NAME")

templateName := record.Tag+"_template.html"

var tmpl *template.Template

tmpl, err = template.ParseFiles("templates/"+templateName)
if err != nil {
log.Printf("Failed to load %v template: %v\n", record.Tag, err)
return
}

smtpPort, err := strconv.Atoi(smtpPortStr)
if err != nil {
log.Printf("Failed to convert SMTP port to integer: %v\n", err)
return
}

mailer := mail.NewDialer(smtpHost, smtpPort, smtpUser, smtpPass)
m := mail.NewMessage()

if record.Email == "" || record.Subject == ""{
log.Printf("Invalid record: %+v\n", record)
return
}

m.SetHeader("From", senderName+" <"+senderEmail+">")
m.SetHeader("To", record.Email)
m.SetHeader("Subject", record.Subject)

body := &bytes.Buffer{}
if err := tmpl.Execute(body, msg); err != nil {
log.Printf("Failed to execute template: %v\n", err)
return
}

m.SetBody("text/html", body.String())
if err := mailer.DialAndSend(m); err != nil {
log.Printf("Failed to send email: %v\n", err)
}else{
log.Printf("Sent email Successfully")
}
}
Loading

0 comments on commit 03143df

Please sign in to comment.