mirror of
https://github.com/tavo-wasd-gh/conex-builder.git
synced 2025-06-06 11:43:29 -06:00
send authentication emailo
This commit is contained in:
parent
8433d662ad
commit
ecb0e58cae
8 changed files with 201 additions and 35 deletions
5
Makefile
5
Makefile
|
@ -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}
|
||||
|
||||
|
|
21
README.org
21
README.org
|
@ -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
0
public/client.js
Normal 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
39
server/auth.go
Normal 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
|
||||
}
|
88
server/db.go
88
server/db.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue