send authentication emailo

This commit is contained in:
tavo-wasd 2024-09-03 12:50:49 -06:00
parent 8433d662ad
commit ecb0e58cae
8 changed files with 201 additions and 35 deletions

View file

@ -4,9 +4,12 @@ SRC = ${SRCDIR}/init.go \
${SRCDIR}/main.go \
${SRCDIR}/paypal.go \
${SRCDIR}/db.go \
${SRCDIR}/auth.go \
GOFILES = ${SRCDIR}/go.sum ${SRCDIR}/go.mod
GOMODS = github.com/joho/godotenv github.com/lib/pq
GOMODS = github.com/joho/godotenv \
github.com/lib/pq \
gopkg.in/gomail.v2 \
all: ${BIN}

View file

@ -22,7 +22,7 @@ CREATE DATABASE iterone OWNER conex;
DROP TABLE IF EXISTS changes;
DROP TABLE IF EXISTS payments;
DROP TABLE IF EXISTS sites;
CREATE TABLE sites (
id SERIAL PRIMARY KEY,
folder VARCHAR(35) UNIQUE NOT NULL,
@ -33,7 +33,9 @@ CREATE TABLE sites (
email VARCHAR(100) NOT NULL,
phone VARCHAR(20),
code VARCHAR(2),
raw JSONB NOT NULL
raw JSONB NOT NULL,
auth INTEGER,
valid TIMESTAMPTZ
);
#+END_SRC
@ -42,8 +44,8 @@ SELECT * FROM sites;
#+END_SRC
#+RESULTS:
| id | folder | status | due | name | sur | email | phone | code | raw |
|----+--------+--------+-----+------+-----+-------+-------+------+-----|
| id | folder | status | due | name | sur | email | phone | code | raw | auth | valid |
|----+--------+--------+-----+------+-----+-------+-------+------+-----+------+-------|
** Payments table
@ -58,7 +60,7 @@ CREATE TABLE payments (
amount DECIMAL(10, 2) NOT NULL,
currency VARCHAR(3) NOT NULL,
status VARCHAR(18) NOT NULL, -- PayPal capture status length -- https://developer.paypal.com/docs/api/orders/v2/#orders_capture
date DATE NOT NULL
date TIMESTAMPTZ NOT NULL
);
#+END_SRC
@ -76,13 +78,12 @@ SELECT * FROM payments;
DROP TABLE IF EXISTS changes;
CREATE TABLE changes (
id INTEGER PRIMARY KEY,
id SERIAL PRIMARY KEY,
by VARCHAR(20) NOT NULL,
site INTEGER REFERENCES sites(id),
payment INTEGER REFERENCES payments(id),
col VARCHAR(6) NOT NULL,
prev VARCHAR(8) NOT NULL,
next VARCHAR(8) NOT NULL,
descrip VARCHAR(120) NOT NULL,
date DATE NOT NULL
);
#+END_SRC
@ -92,7 +93,8 @@ SELECT * FROM changes;
#+END_SRC
#+RESULTS:
|---|
| id | by | site | payment | col | descrip | date |
|----+----+------+---------+-----+---------+------|
** Types of changes
@ -111,6 +113,7 @@ SELECT * FROM changes;
- email: prev/next
- phone: prev/next
- code: prev/next
- raw: prev/next
* Error codes

0
public/client.js Normal file
View file

View file

@ -20,7 +20,7 @@
<!-- Load Editor.js's Core -->
<script src="https://cdn.jsdelivr.net/npm/@editorjs/editorjs@latest"></script>
<!-- EDITORJS -->
<script src="/app.js"></script>
<script src="/paypal.js"></script>
<!-- <script src="/static/js/config.editor.js"></script> -->
<script>
// Initialize Editor.js

39
server/auth.go Normal file
View file

@ -0,0 +1,39 @@
package main
import (
"fmt"
"math/rand"
"os"
"strconv"
"time"
"gopkg.in/gomail.v2"
)
func GenerateCode() string {
rand.Seed(time.Now().UnixNano())
return fmt.Sprintf("%06d", rand.Intn(1000000))
}
func SendAuthEmail(recipient string, code string) error {
smtpHost := os.Getenv("EMAIL_HOST")
smtpPortStr := os.Getenv("EMAIL_PORT")
smtpUser := os.Getenv("EMAIL_USER")
smtpPass := os.Getenv("EMAIL_PASS")
smtpPort, _ := strconv.Atoi(smtpPortStr)
subject := os.Getenv("EMAIL_SUBJECT")
body := os.Getenv("EMAIL_BODY")
m := gomail.NewMessage()
m.SetHeader("From", smtpUser)
m.SetHeader("To", recipient)
m.SetHeader("Subject", subject)
m.SetBody("text/plain", body+code)
d := gomail.NewDialer(smtpHost, smtpPort, smtpUser, smtpPass)
if err := d.DialAndSend(m); err != nil {
return err
}
return nil
}

View file

@ -10,17 +10,20 @@ import (
)
const (
errDBRegisterSite = "Error: db.go (sites): Register site"
errDBUpdateDue = "Error: db.go (sites): Update due date"
errDBRegisterPayment = "Error: db.go (payments): Register payment"
errDBTXBeginUpdateSite = "Error: db.go: Begin transaction"
errDBTXCommitUpdateSite = "Error: db.go: Commit transaction"
errDBGetPrevRaw = "Error: db.go (sites): Query old raw json"
errDBUpdateRaw = "Error: db.go (sites): Update raw json"
errDBChangesRaw = "Error: db.go (changes): Register raw json change"
errDBRegisterSite = "db.go (sites): Register site"
errDBUpdateDue = "db.go (sites): Update due date"
errDBRegisterPayment = "db.go (payments): Register payment"
errDBTXBeginUpdateSite = "db.go: Begin transaction"
errDBTXCommitUpdateSite = "db.go: Commit transaction"
errDBGetPrevRaw = "db.go (sites): Query old raw json"
errDBUpdateRaw = "db.go (sites): Update raw json"
errDBChangesRaw = "db.go (changes): Register raw json change"
errDBUpdateSiteAuth = "db.go (sites): Auth"
)
func RegisterSitePayment(capture Capture, directory string, editorData json.RawMessage) error {
func RegisterSitePayment(
capture Capture, directory string, editorData json.RawMessage,
) error {
var (
// Payment
id string
@ -60,7 +63,8 @@ func RegisterSitePayment(capture Capture, directory string, editorData json.RawM
if newSite == sql.ErrNoRows {
if err := db.QueryRow(`
INSERT INTO sites (folder, status, due, name, sur, email, phone, code, raw)
INSERT INTO sites
(folder, status, due, name, sur, email, phone, code, raw)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id
`, directory, wstatus, due,
@ -79,7 +83,8 @@ func RegisterSitePayment(capture Capture, directory string, editorData json.RawM
}
if _, err := db.Exec(`
INSERT INTO payments (capture, site, amount, currency, date, status)
INSERT INTO payments
(capture, site, amount, currency, date, status)
VALUES ($1, $2, $3, $4, $5, $6)
`, id, pkey, amount, currency, date, pstatus); err != nil {
return fmt.Errorf("%s: %v", errDBRegisterPayment, err)
@ -88,7 +93,7 @@ func RegisterSitePayment(capture Capture, directory string, editorData json.RawM
return nil
}
func UpdateSite(by string, pkey int, editorData json.RawMessage) error {
func UpdateSite(pkey int, editorData json.RawMessage) error {
tx, err := db.Begin()
if err != nil {
return fmt.Errorf("%s: %v", errDBTXBeginUpdateSite, err)
@ -114,9 +119,10 @@ func UpdateSite(by string, pkey int, editorData json.RawMessage) error {
}
if _, err = tx.Exec(`
INSERT INTO changes (by, site, payment, col, prev, next, date)
VALUES ($1, $2, NULL, 'raw', $3, $4, CURRENT_DATE);
`, by, pkey, prev, editorData); err != nil {
INSERT INTO changes
(by, site, payment, col, descrip, date)
VALUES ($1, $2, NULL, 'raw', $3, CURRENT_DATE);
`, "server", pkey, "Cambio automatizado de sitio"); err != nil {
return fmt.Errorf("%s: %v", errDBChangesRaw, err)
}
@ -126,3 +132,55 @@ func UpdateSite(by string, pkey int, editorData json.RawMessage) error {
return nil
}
func UpdateSiteAuth(folder string, code string) (string, error) {
valid := time.Now().Add(5 * time.Minute)
var email string
if err := db.QueryRow(`
UPDATE sites
SET auth = $1, valid = $2
WHERE folder = $3
RETURNING email;
`, code, valid, folder).Scan(&email); err != nil {
return "", fmt.Errorf("%s: %v", errDBUpdateSiteAuth, err)
}
return email, nil
}
func ValidateSiteAuth(folder string, code string) (int, error) {
var dbCode string
var validTime time.Time
err := db.QueryRow(`
SELECT auth, valid FROM sites
WHERE folder = $1;
`, folder).Scan(&dbCode, &validTime)
if err != nil {
if err == sql.ErrNoRows {
return 0, fmt.Errorf("%s: %v", "No such directory", err)
}
return 0, fmt.Errorf("%s: %v", "Failed to query DB", err)
}
if code != dbCode {
return 0, fmt.Errorf("%s", "Incorrect code")
}
if time.Now().After(validTime) {
return 0, fmt.Errorf("%s", "Auth expired")
}
var pkey int
if err := db.QueryRow(`
UPDATE sites
SET valid = $1
WHERE folder = $2
RETURNING id;
`, time.Now(), folder).Scan(&pkey); err != nil {
return 0, fmt.Errorf("%s: %v", "Void used code", err)
}
return pkey, nil
}

View file

@ -21,6 +21,10 @@ const (
errRegisterSite = "Error: main.go: Register site in database"
errEncodeResponse = "Error: main.go: Encode response"
errCreateOrder = "Error: main.go: Obtain orderID"
errAuthGen = "Error: main.go: Gen and register auth"
errAuthEmail = "Error: main.go: Send auth email"
errAuthValidate = "Error: main.go: Validate changes"
errUpdateSite = "Error: main.go: Updating site data"
)
func main() {
@ -28,7 +32,8 @@ func main() {
http.HandleFunc("/api/orders", CreateOrderHandler)
http.HandleFunc("/api/orders/", CaptureOrderHandler)
// http.HandleFunc("/api/updateadfasad/", UpdateSiteHandler)
http.HandleFunc("/api/update", UpdateSiteHandler)
http.HandleFunc("/api/confirm", ConfirmChangesHandler)
http.Handle("/", http.FileServer(http.Dir("./public")))
stop := make(chan os.Signal, 1)
@ -52,9 +57,11 @@ func msg(notice string) {
log.Println(notice)
}
func fail(w http.ResponseWriter, err error, notice string) {
func httpErrorAndLog(w http.ResponseWriter,
err error, notice string, client string,
) {
log.Printf("%s: %v", notice, err)
http.Error(w, notice, http.StatusInternalServerError)
http.Error(w, client, http.StatusInternalServerError)
}
func fatal(err error, notice string) {
@ -65,7 +72,7 @@ func fatal(err error, notice string) {
func CreateOrderHandler(w http.ResponseWriter, r *http.Request) {
orderID, err := CreateOrder()
if err != nil {
fail(w, err, errCreateOrder)
httpErrorAndLog(w, err, errCreateOrder, "Error creating order")
return
}
@ -80,12 +87,14 @@ func CreateOrderHandler(w http.ResponseWriter, r *http.Request) {
}
func CaptureOrderHandler(w http.ResponseWriter, r *http.Request) {
errClientNotice := "Error capturing order"
var cart struct {
Directory string `json:"directory"`
EditorData json.RawMessage `json:"editor_data"`
}
if err := json.NewDecoder(r.Body).Decode(&cart); err != nil {
fail(w, err, errReadBody)
httpErrorAndLog(w, err, errReadBody, errClientNotice)
return
}
@ -93,24 +102,78 @@ func CaptureOrderHandler(w http.ResponseWriter, r *http.Request) {
parts := strings.Split(path, "/")
orderID := parts[0]
if orderID == "" {
fail(w, nil, errGetOrderID)
httpErrorAndLog(w, nil, errGetOrderID, errClientNotice)
return
}
capture, receipt, err := CaptureOrder(orderID)
if err != nil {
fail(w, err, errCaptureOrder)
httpErrorAndLog(w, err, errCaptureOrder, errClientNotice)
return
}
if err := RegisterSitePayment(capture, cart.Directory, cart.EditorData); err != nil {
fail(w, err, errRegisterSite+": "+cart.Directory)
httpErrorAndLog(w, err, errRegisterSite+": "+cart.Directory, errClientNotice)
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(receipt); err != nil {
fail(w, err, errEncodeResponse)
httpErrorAndLog(w, err, errEncodeResponse, errClientNotice)
return
}
return
}
func UpdateSiteHandler(w http.ResponseWriter, r *http.Request) {
errClientNotice := "Error handling update request"
var cart struct {
Directory string `json:"directory"`
}
if err := json.NewDecoder(r.Body).Decode(&cart); err != nil {
httpErrorAndLog(w, err, errReadBody, errClientNotice)
return
}
code := GenerateCode()
email, err := UpdateSiteAuth(cart.Directory, code)
if err != nil {
httpErrorAndLog(w, err, errAuthGen, errClientNotice)
return
}
if err := SendAuthEmail(email, code); err != nil {
httpErrorAndLog(w, err, errAuthEmail, errClientNotice)
return
}
return
}
func ConfirmChangesHandler(w http.ResponseWriter, r *http.Request) {
errClientNotice := "Error handling confirm changes request"
var cart struct {
Directory string `json:"directory"`
Code string `json:"auth_code"`
EditorData json.RawMessage `json:"editor_data"`
}
if err := json.NewDecoder(r.Body).Decode(&cart); err != nil {
httpErrorAndLog(w, err, errReadBody, errClientNotice)
return
}
pkey, err := ValidateSiteAuth(cart.Directory, cart.Code)
if err != nil {
httpErrorAndLog(w, err, errAuthValidate, errClientNotice)
return
}
if err := UpdateSite(pkey, cart.EditorData); err != nil {
httpErrorAndLog(w, err, errUpdateSite, errClientNotice)
return
}