1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-12-22 17:10:10 +00:00

make checkInvite check only one invite, invite daemon

checkInvite no longer loops over all invites and checks for expiry, that
functionality has moved to checkInvites. Couple more rogue print
statements removed aswell.
This commit is contained in:
Harvey Tindall 2020-08-01 15:22:30 +01:00
parent dba20bd3ea
commit 4e16f6fd48
6 changed files with 113 additions and 71 deletions

84
api.go
View File

@ -76,12 +76,11 @@ func timeDiff(a, b time.Time) (year, month, day, hour, min, sec int) {
return return
} }
func (ctx *appContext) checkInvite(code string, used bool, username string) bool { func (ctx *appContext) checkInvites() {
current_time := time.Now() current_time := time.Now()
ctx.storage.loadInvites() ctx.storage.loadInvites()
match := false
changed := false changed := false
for invCode, data := range ctx.storage.invites { for code, data := range ctx.storage.invites {
expiry := data.ValidTill expiry := data.ValidTill
if current_time.After(expiry) { if current_time.After(expiry) {
ctx.debug.Printf("Housekeeping: Deleting old invite %s", code) ctx.debug.Printf("Housekeeping: Deleting old invite %s", code)
@ -90,7 +89,7 @@ func (ctx *appContext) checkInvite(code string, used bool, username string) bool
ctx.debug.Printf("%s: Expiry notification", code) ctx.debug.Printf("%s: Expiry notification", code)
for address, settings := range notify { for address, settings := range notify {
if settings["notify-expiry"] { if settings["notify-expiry"] {
if ctx.email.constructExpiry(invCode, data, ctx) != nil { if ctx.email.constructExpiry(code, data, ctx) != nil {
ctx.err.Printf("%s: Failed to construct expiry notification", code) ctx.err.Printf("%s: Failed to construct expiry notification", code)
} else if ctx.email.send(address, ctx) != nil { } else if ctx.email.send(address, ctx) != nil {
ctx.err.Printf("%s: Failed to send expiry notification", code) ctx.err.Printf("%s: Failed to send expiry notification", code)
@ -101,31 +100,62 @@ func (ctx *appContext) checkInvite(code string, used bool, username string) bool
} }
} }
changed = true changed = true
delete(ctx.storage.invites, invCode) delete(ctx.storage.invites, code)
} else if invCode == code {
match = true
if used {
changed = true
del := false
newInv := data
if newInv.RemainingUses == 1 {
del = true
delete(ctx.storage.invites, invCode)
} else if newInv.RemainingUses != 0 {
// 0 means infinite i guess?
newInv.RemainingUses -= 1
}
newInv.UsedBy = append(newInv.UsedBy, []string{username, ctx.formatDatetime(current_time)})
if !del {
ctx.storage.invites[invCode] = newInv
}
}
} }
} }
if changed { if changed {
ctx.storage.storeInvites() ctx.storage.storeInvites()
} }
return match }
func (ctx *appContext) checkInvite(code string, used bool, username string) bool {
current_time := time.Now()
ctx.storage.loadInvites()
changed := false
if inv, match := ctx.storage.invites[code]; match {
expiry := inv.ValidTill
if current_time.After(expiry) {
ctx.debug.Printf("Housekeeping: Deleting old invite %s", code)
notify := inv.Notify
if ctx.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
ctx.debug.Printf("%s: Expiry notification", code)
for address, settings := range notify {
if settings["notify-expiry"] {
if ctx.email.constructExpiry(code, inv, ctx) != nil {
ctx.err.Printf("%s: Failed to construct expiry notification", code)
} else if ctx.email.send(address, ctx) != nil {
ctx.err.Printf("%s: Failed to send expiry notification", code)
} else {
ctx.info.Printf("Sent expiry notification to %s", address)
}
}
}
}
changed = true
match = false
delete(ctx.storage.invites, code)
} else if used {
changed = true
del := false
newInv := inv
if newInv.RemainingUses == 1 {
del = true
delete(ctx.storage.invites, code)
} else if newInv.RemainingUses != 0 {
// 0 means infinite i guess?
newInv.RemainingUses -= 1
}
newInv.UsedBy = append(newInv.UsedBy, []string{username, ctx.formatDatetime(current_time)})
if !del {
ctx.storage.invites[code] = newInv
}
}
if changed {
ctx.storage.storeInvites()
}
return match
}
return false
} }
// Routes from now on! // Routes from now on!
@ -270,12 +300,8 @@ func (ctx *appContext) GenerateInvite(gc *gin.Context) {
func (ctx *appContext) GetInvites(gc *gin.Context) { func (ctx *appContext) GetInvites(gc *gin.Context) {
ctx.debug.Println("Invites requested") ctx.debug.Println("Invites requested")
current_time := time.Now() current_time := time.Now()
// checking one checks all of them
ctx.storage.loadInvites() ctx.storage.loadInvites()
for key := range ctx.storage.invites { ctx.checkInvites()
ctx.checkInvite(key, false, "")
break
}
var invites []map[string]interface{} var invites []map[string]interface{}
for code, inv := range ctx.storage.invites { for code, inv := range ctx.storage.invites {
_, _, days, hours, minutes, _ := timeDiff(inv.ValidTill, current_time) _, _, days, hours, minutes, _ := timeDiff(inv.ValidTill, current_time)

View File

@ -16,9 +16,6 @@ func (ctx *appContext) webAuth() gin.HandlerFunc {
} }
func (ctx *appContext) authenticate(gc *gin.Context) { func (ctx *appContext) authenticate(gc *gin.Context) {
for _, val := range ctx.users {
fmt.Println("userid", val.UserID)
}
header := strings.SplitN(gc.Request.Header.Get("Authorization"), " ", 2) header := strings.SplitN(gc.Request.Header.Get("Authorization"), " ", 2)
if header[0] != "Basic" { if header[0] != "Basic" {
ctx.debug.Println("Invalid authentication header") ctx.debug.Println("Invalid authentication header")

50
daemon.go Normal file
View File

@ -0,0 +1,50 @@
package main
import "time"
// https://bbengfort.github.io/snippets/2016/06/26/background-work-goroutines-timer.html THANKS
type Repeater struct {
Stopped bool
ShutdownChannel chan string
Interval time.Duration
period time.Duration
ctx *appContext
}
func NewRepeater(interval time.Duration, ctx *appContext) *Repeater {
return &Repeater{
Stopped: false,
ShutdownChannel: make(chan string),
Interval: interval,
period: interval,
ctx: ctx,
}
}
func (rt *Repeater) Run() {
rt.ctx.info.Println("Invite daemon started")
for {
select {
case <-rt.ShutdownChannel:
rt.ShutdownChannel <- "Down"
return
case <-time.After(rt.period):
break
}
started := time.Now()
rt.ctx.storage.loadInvites()
rt.ctx.debug.Println("Daemon: Checking invites")
rt.ctx.checkInvites()
finished := time.Now()
duration := finished.Sub(started)
rt.period = rt.Interval - duration
}
}
func (rt *Repeater) Shutdown() {
rt.Stopped = true
rt.ShutdownChannel <- "Down"
<-rt.ShutdownChannel
close(rt.ShutdownChannel)
}

View File

@ -73,7 +73,6 @@ func (email *Emailer) constructInvite(code string, invite Invite, ctx *appContex
fpath := ctx.config.Section("invite_emails").Key("email_" + key).String() fpath := ctx.config.Section("invite_emails").Key("email_" + key).String()
tpl, err := template.ParseFiles(fpath) tpl, err := template.ParseFiles(fpath)
if err != nil { if err != nil {
fmt.Println("failed email", err)
return err return err
} }
var tplData bytes.Buffer var tplData bytes.Buffer
@ -85,7 +84,6 @@ func (email *Emailer) constructInvite(code string, invite Invite, ctx *appContex
"message": message, "message": message,
}) })
if err != nil { if err != nil {
fmt.Println("failed email", err)
return err return err
} }
if key == "html" { if key == "html" {
@ -105,7 +103,6 @@ func (email *Emailer) constructExpiry(code string, invite Invite, ctx *appContex
fpath := ctx.config.Section("notifications").Key("expiry_" + key).String() fpath := ctx.config.Section("notifications").Key("expiry_" + key).String()
tpl, err := template.ParseFiles(fpath) tpl, err := template.ParseFiles(fpath)
if err != nil { if err != nil {
fmt.Println("failed email", err)
return err return err
} }
var tplData bytes.Buffer var tplData bytes.Buffer
@ -114,7 +111,6 @@ func (email *Emailer) constructExpiry(code string, invite Invite, ctx *appContex
"expiry": expiry, "expiry": expiry,
}) })
if err != nil { if err != nil {
fmt.Println("failed email", err)
return err return err
} }
if key == "html" { if key == "html" {
@ -140,7 +136,6 @@ func (email *Emailer) constructCreated(code, username, address string, invite In
fpath := ctx.config.Section("notifications").Key("created_" + key).String() fpath := ctx.config.Section("notifications").Key("created_" + key).String()
tpl, err := template.ParseFiles(fpath) tpl, err := template.ParseFiles(fpath)
if err != nil { if err != nil {
fmt.Println("failed email", err)
return err return err
} }
var tplData bytes.Buffer var tplData bytes.Buffer
@ -151,7 +146,6 @@ func (email *Emailer) constructCreated(code, username, address string, invite In
"time": created, "time": created,
}) })
if err != nil { if err != nil {
fmt.Println("failed email", err)
return err return err
} }
if key == "html" { if key == "html" {
@ -166,33 +160,6 @@ func (email *Emailer) constructCreated(code, username, address string, invite In
func (email *Emailer) send(address string, ctx *appContext) error { func (email *Emailer) send(address string, ctx *appContext) error {
if email.sendMethod == "mailgun" { if email.sendMethod == "mailgun" {
// reqData := map[string]string{
// "to": fmt.Sprintf("%s <%s>", "test", email.to),
// "from": email.fromAddr,
// "subject": email.subject,
// }
// if email.sendType == "invite" {
// reqData["text"] = email.invite.text
// reqData["html"] = email.invite.html
// }
// data := &bytes.Buffer{}
// encoder := json.NewEncoder(data)
// encoder.SetEscapeHTML(false)
// err := encoder.Encode(reqData)
// fmt.Println("marshaled:", data)
// if err != nil {
// fmt.Println("Failed marshal:", err, ">", data)
// return err
// }
// var req *http.Request
// req, err = http.NewRequest("POST", ctx.config.Section("mailgun").Key("api_url").String(), data)
// req.SetBasicAuth("api", ctx.config.Section("mailgun").Key("api_key").String())
// var resp *http.Response
// resp, err = email.httpClient.Do(req)
// if err != nil || !(resp.StatusCode == 200 || resp.StatusCode == 204) {
// fmt.Println("failed send:", err, resp.StatusCode)
// fmt.Println("resp:", resp.Header.Get("Content-Encoding"), resp.Body)
// }
message := email.mg.NewMessage( message := email.mg.NewMessage(
fmt.Sprintf("%s <%s>", email.fromName, email.fromAddr), fmt.Sprintf("%s <%s>", email.fromName, email.fromAddr),
email.content.subject, email.content.subject,
@ -201,9 +168,10 @@ func (email *Emailer) send(address string, ctx *appContext) error {
message.SetHtml(email.content.html) message.SetHtml(email.content.html)
mgctx, cancel := context.WithTimeout(context.Background(), time.Second*30) mgctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel() defer cancel()
_, id, err := email.mg.Send(mgctx, message) _, _, err := email.mg.Send(mgctx, message)
fmt.Println("mailgun:", id, err) if err != nil {
return err
}
} }
return nil return nil
} }

View File

@ -13,6 +13,7 @@ import (
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"time"
) )
// Username is JWT! // Username is JWT!
@ -193,6 +194,9 @@ func main() {
ctx.email.init(ctx) ctx.email.init(ctx)
inviteDaemon := NewRepeater(time.Duration(60*time.Second), ctx)
go inviteDaemon.Run()
ctx.info.Println("Loading routes") ctx.info.Println("Loading routes")
router := gin.New() router := gin.New()

View File

@ -3,7 +3,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"os"
"time" "time"
) )
@ -78,8 +77,6 @@ func loadJSON(path string, obj interface{}) error {
} }
func storeJSON(path string, obj interface{}) error { func storeJSON(path string, obj interface{}) error {
test := json.NewEncoder(os.Stdout)
test.Encode(obj)
data, err := json.Marshal(obj) data, err := json.Marshal(obj)
if err != nil { if err != nil {
return err return err