Skip to content

Commit 27f44b3

Browse files
committed
first commit
0 parents  commit 27f44b3

File tree

7 files changed

+394
-0
lines changed

7 files changed

+394
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea
2+
*.db

README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Statping Email Notifier
2+
This repo contains a dedicated service for Statping that allows users to receive notifications to their email address using our SMTP servers.
3+
4+
# Required Environment Variables
5+
- `HOST` - SMTP host
6+
- `USERNAME` - SMTP username
7+
- `PASSWORD` - SMTP password
8+
- `PORT` - SMTP port
9+
10+
> Server runs on port :8080
11+
12+
# API Endpoints
13+
14+
#### POST `/request`
15+
This endpoint will send an email to the user with a confirmation link/key. Email's won't be sent until this confirmation link is clicked.
16+
```json
17+
{
18+
"email": "info@myaddress.com",
19+
"version": "0.90.33"
20+
}
21+
```
22+
23+
#### GET `/confirm/{id}`
24+
Confirm an email address with the key provided in the request email.a
25+
26+
#### GET `/check?email=info@myaddress.com`
27+
Check the status of your email address.
28+
29+
#### GET `/resend?email=info@myaddress.com`
30+
Attempt to resend the confirmation request email.
31+
32+
#### GET `/unsubscribe?email=info@myaddress.com`
33+
Unsubscribe the email address, and remove them from database.
34+
35+
#### POST `/send`
36+
```json
37+
{
38+
"email": "info@myaddress.com",
39+
"key": "<secret_key_here>",
40+
"version": "0.90.33",
41+
"service": {
42+
...service JSON
43+
},
44+
"failure": {
45+
...failure JSON
46+
}
47+
}
48+
```
49+
The main endpoint that will send an email about uptime or downtime.
50+
51+
52+

emailer.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package main
2+
3+
import (
4+
"crypto/tls"
5+
"fmt"
6+
"gopkg.in/gomail.v2"
7+
"os"
8+
"strconv"
9+
)
10+
11+
var (
12+
config *Config
13+
)
14+
15+
type Config struct {
16+
Host string
17+
Username string
18+
Password string
19+
Port int
20+
}
21+
22+
func InitConfig() {
23+
config = &Config{
24+
Host: os.Getenv("HOST"),
25+
Username: os.Getenv("USERNAME"),
26+
Password: os.Getenv("PASSWORD"),
27+
Port: toInt(os.Getenv("PORT")),
28+
}
29+
}
30+
31+
func SendEmail(u *User) error {
32+
m := gomail.NewMessage()
33+
m.SetHeader("From", "info@betatude.com")
34+
m.SetHeader("To", u.Email)
35+
m.SetHeader("Subject", "Hello!")
36+
m.SetBody("text/html", "Hello <b>Bob</b> and <i>Cora</i>!")
37+
38+
d := gomail.NewDialer(config.Host, config.Port, config.Username, config.Password)
39+
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
40+
41+
fmt.Printf("Sending email to %s...\n", u.Email)
42+
43+
if err := d.DialAndSend(m); err != nil {
44+
fmt.Println(err)
45+
return err
46+
}
47+
48+
fmt.Printf("Sent email to %s\n", u.Email)
49+
50+
return nil
51+
}
52+
53+
func toInt(s string) int {
54+
num, _ := strconv.ParseInt(s, 10, 64)
55+
return int(num)
56+
}

handlers.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"github.com/gorilla/mux"
6+
"net/http"
7+
)
8+
9+
func ConfirmHandler(w http.ResponseWriter, r *http.Request) {
10+
defer r.Body.Close()
11+
vars := mux.Vars(r)
12+
key := vars["id"]
13+
14+
user, err := FindUserKey(key)
15+
if err != nil || user == nil {
16+
sendError(w, notFound)
17+
return
18+
}
19+
if user.Key == key && user.Confirmed {
20+
response := requestResponse{
21+
Email: user.Email,
22+
Message: "email address has already been confirmed",
23+
}
24+
sendJSON(w, response)
25+
return
26+
}
27+
28+
if user.Key == key {
29+
user.Confirmed = true
30+
user.Update()
31+
}
32+
response := requestResponse{
33+
Email: user.Email,
34+
Message: "email address is now confirmed",
35+
}
36+
sendJSON(w, response)
37+
}
38+
39+
func UnsubscribeHandler(w http.ResponseWriter, r *http.Request) {
40+
defer r.Body.Close()
41+
email := r.URL.Query().Get("email")
42+
43+
user, err := FindUser(email)
44+
if err != nil || user == nil {
45+
sendError(w, notFound)
46+
return
47+
}
48+
49+
user.Delete()
50+
51+
status := &requestResponse{
52+
Email: user.Email,
53+
Message: "email has been unsubscribed",
54+
}
55+
56+
sendJSON(w, status)
57+
}
58+
59+
func ResendHandler(w http.ResponseWriter, r *http.Request) {
60+
defer r.Body.Close()
61+
email := r.URL.Query().Get("email")
62+
63+
user, err := FindUser(email)
64+
if err != nil || user == nil {
65+
sendError(w, notFound)
66+
return
67+
}
68+
status := &requestResponse{
69+
Email: user.Email,
70+
}
71+
if user.Confirmed {
72+
status.Message = "email has already been confirmed"
73+
} else {
74+
status.Message = "email has not been confirmed yet"
75+
}
76+
sendJSON(w, status)
77+
}
78+
79+
func CheckHandler(w http.ResponseWriter, r *http.Request) {
80+
defer r.Body.Close()
81+
email := r.URL.Query().Get("email")
82+
83+
user, err := FindUser(email)
84+
if err != nil || user == nil {
85+
sendError(w, notFound)
86+
return
87+
}
88+
status := &requestResponse{
89+
Email: user.Email,
90+
}
91+
if user.Confirmed {
92+
status.Message = "email has been confirmed"
93+
} else {
94+
status.Message = "email has not been confirmed yet " + user.Key
95+
}
96+
sendJSON(w, status)
97+
}
98+
99+
func SendHandler(w http.ResponseWriter, r *http.Request) {
100+
defer r.Body.Close()
101+
102+
user, err := FindUser("info@socialeck.com")
103+
if err != nil || user == nil {
104+
sendError(w, notFound)
105+
return
106+
}
107+
108+
err = SendEmail(user)
109+
if err != nil {
110+
sendError(w, err)
111+
return
112+
}
113+
}
114+
115+
func RequestHandler(w http.ResponseWriter, r *http.Request) {
116+
defer r.Body.Close()
117+
var req requestJSON
118+
decoder := json.NewDecoder(r.Body)
119+
if err := decoder.Decode(&req); err != nil {
120+
sendError(w, err)
121+
return
122+
}
123+
124+
user, err := FindUser(req.Email)
125+
if err != nil {
126+
sendError(w, notFound)
127+
return
128+
}
129+
130+
if user != nil {
131+
response := &requestResponse{
132+
Email: user.Email,
133+
Message: "this email need to be confirmed",
134+
}
135+
sendJSON(w, response)
136+
return
137+
}
138+
139+
user = &User{
140+
Email: req.Email,
141+
Key: RandomString(32),
142+
Sent: 0,
143+
}
144+
145+
user.Create()
146+
147+
response := &requestResponse{
148+
Email: user.Email,
149+
Message: "check email",
150+
}
151+
152+
sendJSON(w, response)
153+
}
154+
155+
func sendJSON(w http.ResponseWriter, obj interface{}) {
156+
w.Header().Set("Content-Type", "application/json")
157+
json.NewEncoder(w).Encode(obj)
158+
}
159+
160+
func sendError(w http.ResponseWriter, err error) {
161+
w.Header().Set("Content-Type", "application/json")
162+
json.NewEncoder(w).Encode(&errorResponse{err.Error()})
163+
}

main.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package main
2+
3+
import (
4+
"github.com/gorilla/mux"
5+
"github.com/jinzhu/gorm"
6+
"math/rand"
7+
"net/http"
8+
"time"
9+
10+
_ "github.com/jinzhu/gorm/dialects/sqlite"
11+
)
12+
13+
var (
14+
db *gorm.DB
15+
)
16+
17+
func init() {
18+
var err error
19+
db, err = gorm.Open("sqlite3", "emailer.db")
20+
if err != nil {
21+
panic(err)
22+
}
23+
db.AutoMigrate(&User{})
24+
25+
InitConfig()
26+
}
27+
28+
func main() {
29+
r := mux.NewRouter()
30+
r.HandleFunc("/request", RequestHandler).Methods("POST")
31+
r.HandleFunc("/confirm/{id}", ConfirmHandler).Methods("GET")
32+
r.HandleFunc("/check", CheckHandler).Methods("GET")
33+
r.HandleFunc("/resend", ResendHandler).Methods("GET")
34+
r.HandleFunc("/unsubscribe", UnsubscribeHandler).Methods("GET")
35+
r.HandleFunc("/send", SendHandler).Methods("POST")
36+
http.ListenAndServe(":8000", r)
37+
}
38+
39+
var characterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
40+
41+
// RandomString generates a random string of n length
42+
func RandomString(n int) string {
43+
b := make([]rune, n)
44+
rand.Seed(time.Now().UnixNano())
45+
for i := range b {
46+
b[i] = characterRunes[rand.Intn(len(characterRunes))]
47+
}
48+
return string(b)
49+
}

methods.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func FindUser(email string) (*User, error) {
6+
var u User
7+
err := db.Model(&User{}).Where("email = ?", email).Find(&u)
8+
if err.Error != nil {
9+
return nil, err.Error
10+
}
11+
return &u, nil
12+
}
13+
14+
func FindUserKey(key string) (*User, error) {
15+
var u User
16+
err := db.Model(&User{}).Where("key = ?", key).Find(&u)
17+
if err.Error != nil {
18+
return nil, err.Error
19+
}
20+
return &u, nil
21+
}
22+
23+
func (u *User) Create() error {
24+
e := db.Model(&User{}).Create(u)
25+
return e.Error
26+
}
27+
28+
func (u *User) Update() error {
29+
e := db.Model(&User{}).Update(u)
30+
return e.Error
31+
}
32+
33+
func (u *User) Delete() error {
34+
e := db.Model(&User{}).Delete(u)
35+
return e.Error
36+
}
37+
38+
func (u *User) ConfirmLink() string {
39+
return fmt.Sprintf("https://emailer.statping.com/confirm/%s", u.Key)
40+
}

0 commit comments

Comments
 (0)