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
}
func (ctx *appContext) checkInvite(code string, used bool, username string) bool {
func (ctx *appContext) checkInvites() {
current_time := time.Now()
ctx.storage.loadInvites()
match := false
changed := false
for invCode, data := range ctx.storage.invites {
for code, data := range ctx.storage.invites {
expiry := data.ValidTill
if current_time.After(expiry) {
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)
for address, settings := range notify {
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)
} else if ctx.email.send(address, ctx) != nil {
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
delete(ctx.storage.invites, invCode)
} 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
}
}
delete(ctx.storage.invites, code)
}
}
if changed {
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!
@ -270,12 +300,8 @@ func (ctx *appContext) GenerateInvite(gc *gin.Context) {
func (ctx *appContext) GetInvites(gc *gin.Context) {
ctx.debug.Println("Invites requested")
current_time := time.Now()
// checking one checks all of them
ctx.storage.loadInvites()
for key := range ctx.storage.invites {
ctx.checkInvite(key, false, "")
break
}
ctx.checkInvites()
var invites []map[string]interface{}
for code, inv := range ctx.storage.invites {
_, _, 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) {
for _, val := range ctx.users {
fmt.Println("userid", val.UserID)
}
header := strings.SplitN(gc.Request.Header.Get("Authorization"), " ", 2)
if header[0] != "Basic" {
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()
tpl, err := template.ParseFiles(fpath)
if err != nil {
fmt.Println("failed email", err)
return err
}
var tplData bytes.Buffer
@ -85,7 +84,6 @@ func (email *Emailer) constructInvite(code string, invite Invite, ctx *appContex
"message": message,
})
if err != nil {
fmt.Println("failed email", err)
return err
}
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()
tpl, err := template.ParseFiles(fpath)
if err != nil {
fmt.Println("failed email", err)
return err
}
var tplData bytes.Buffer
@ -114,7 +111,6 @@ func (email *Emailer) constructExpiry(code string, invite Invite, ctx *appContex
"expiry": expiry,
})
if err != nil {
fmt.Println("failed email", err)
return err
}
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()
tpl, err := template.ParseFiles(fpath)
if err != nil {
fmt.Println("failed email", err)
return err
}
var tplData bytes.Buffer
@ -151,7 +146,6 @@ func (email *Emailer) constructCreated(code, username, address string, invite In
"time": created,
})
if err != nil {
fmt.Println("failed email", err)
return err
}
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 {
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(
fmt.Sprintf("%s <%s>", email.fromName, email.fromAddr),
email.content.subject,
@ -201,9 +168,10 @@ func (email *Emailer) send(address string, ctx *appContext) error {
message.SetHtml(email.content.html)
mgctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()
_, id, err := email.mg.Send(mgctx, message)
fmt.Println("mailgun:", id, err)
_, _, err := email.mg.Send(mgctx, message)
if err != nil {
return err
}
}
return nil
}

View File

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

View File

@ -3,7 +3,6 @@ package main
import (
"encoding/json"
"io/ioutil"
"os"
"time"
)
@ -78,8 +77,6 @@ func loadJSON(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)
if err != nil {
return err