From 3f8414c70abf0dc282a42b0e4e75fdc97fad7272 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Tue, 6 Apr 2021 21:25:44 +0100 Subject: [PATCH] use unix timestamp for inv created & usedBy usedBy is still stored as a string in invites.json to cope with existing invites with times stored formatted. knz/strtime requires cgo for strptime, so it has been replaced with the native itchyny/timefmt-go. --- api.go | 24 ++++++++++++++++++------ email.go | 6 +++--- go.mod | 2 +- go.sum | 4 ++-- models.go | 34 +++++++++++++++++----------------- storage.go | 29 +++++++++++++++-------------- ts/modules/invites.ts | 30 +++++++++++++++++------------- ts/modules/update.ts | 4 ++-- ts/typings/d.ts | 4 ++-- 9 files changed, 77 insertions(+), 60 deletions(-) diff --git a/api.go b/api.go index c55cd8a..d823c2a 100644 --- a/api.go +++ b/api.go @@ -11,7 +11,7 @@ import ( "github.com/dgrijalva/jwt-go" "github.com/gin-gonic/gin" "github.com/hrfee/mediabrowser" - "github.com/knz/strtime" + "github.com/itchyny/timefmt-go" "github.com/lithammer/shortuuid/v3" "gopkg.in/ini.v1" ) @@ -48,8 +48,8 @@ func (app *appContext) loadStrftime() { } func (app *appContext) prettyTime(dt time.Time) (date, time string) { - date, _ = strtime.Strftime(dt, app.datePattern) - time, _ = strtime.Strftime(dt, app.timePattern) + date = timefmt.Format(dt, app.datePattern) + time = timefmt.Format(dt, app.timePattern) return } @@ -189,7 +189,7 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool // 0 means infinite i guess? newInv.RemainingUses-- } - newInv.UsedBy = append(newInv.UsedBy, []string{username, app.formatDatetime(currentTime)}) + newInv.UsedBy = append(newInv.UsedBy, []string{username, strconv.FormatInt(currentTime.Unix(), 10)}) if !del { app.storage.invites[code] = newInv } @@ -870,13 +870,25 @@ func (app *appContext) GetInvites(gc *gin.Context) { UserDays: inv.UserDays, UserHours: inv.UserHours, UserMinutes: inv.UserMinutes, - Created: app.formatDatetime(inv.Created), + Created: inv.Created.Unix(), Profile: inv.Profile, NoLimit: inv.NoLimit, Label: inv.Label, } if len(inv.UsedBy) != 0 { - invite.UsedBy = inv.UsedBy + invite.UsedBy = map[string]int64{} + for _, pair := range inv.UsedBy { + // These used to be stored formatted instead of as a unix timestamp. + unix, err := strconv.ParseInt(pair[1], 10, 64) + if err != nil { + date, err := timefmt.Parse(pair[1], app.datePattern+" "+app.timePattern) + if err != nil { + app.err.Printf("Failed to parse usedBy time: %v", err) + } + unix = date.Unix() + } + invite.UsedBy[pair[0]] = unix + } } invite.RemainingUses = 1 if inv.RemainingUses != 0 { diff --git a/email.go b/email.go index ed6e089..9dbffba 100644 --- a/email.go +++ b/email.go @@ -19,8 +19,8 @@ import ( "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/html" + "github.com/itchyny/timefmt-go" jEmail "github.com/jordan-wright/email" - "github.com/knz/strtime" "github.com/mailgun/mailgun-go/v4" ) @@ -101,8 +101,8 @@ type Email struct { } func (emailer *Emailer) formatExpiry(expiry time.Time, tzaware bool, datePattern, timePattern string) (d, t, expiresIn string) { - d, _ = strtime.Strftime(expiry, datePattern) - t, _ = strtime.Strftime(expiry, timePattern) + d = timefmt.Format(expiry, datePattern) + t = timefmt.Format(expiry, timePattern) currentTime := time.Now() if tzaware { currentTime = currentTime.UTC() diff --git a/go.mod b/go.mod index 0732d46..2839507 100644 --- a/go.mod +++ b/go.mod @@ -26,8 +26,8 @@ require ( github.com/hrfee/jfa-go/docs v0.0.0-20201112212552-b6f3cd7c1f71 github.com/hrfee/jfa-go/ombi v0.0.0-20201112212552-b6f3cd7c1f71 github.com/hrfee/mediabrowser v0.3.3 + github.com/itchyny/timefmt-go v0.1.2 github.com/jordan-wright/email v4.0.1-0.20200917010138-e1c00e156980+incompatible - github.com/knz/strtime v0.0.0-20200924090105-187c67f2bf5e github.com/lithammer/shortuuid/v3 v3.0.4 github.com/mailgun/mailgun-go/v4 v4.3.0 github.com/mailru/easyjson v0.7.7 // indirect diff --git a/go.sum b/go.sum index b96bed3..9134854 100644 --- a/go.sum +++ b/go.sum @@ -129,6 +129,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/hrfee/mediabrowser v0.3.3 h1:7E05uiol8hh2ytKn3WVLrUIvHAyifYEIy3Y5qtuNh8I= github.com/hrfee/mediabrowser v0.3.3/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= +github.com/itchyny/timefmt-go v0.1.2 h1:q0Xa4P5it6K6D7ISsbLAMwx1PnWlixDcJL6/sFs93Hs= +github.com/itchyny/timefmt-go v0.1.2/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= github.com/jordan-wright/email v4.0.1-0.20200917010138-e1c00e156980+incompatible h1:CL0ooBNfbNyJTJATno+m0h+zM5bW6v7fKlboKUGP/dI= github.com/jordan-wright/email v4.0.1-0.20200917010138-e1c00e156980+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -141,8 +143,6 @@ github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/knz/strtime v0.0.0-20200924090105-187c67f2bf5e h1:ViPE0JEOvtw5I0EGUiFSr2VNKGNU+3oBT+oHbDXHbxk= -github.com/knz/strtime v0.0.0-20200924090105-187c67f2bf5e/go.mod h1:4ZxfWkxwtc7dBeifERVVWRy9F9rTU9p0yCDgeCtlius= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= diff --git a/models.go b/models.go index 48e9a8e..0309d87 100644 --- a/models.go +++ b/models.go @@ -72,23 +72,23 @@ type newProfileDTO struct { } type inviteDTO struct { - Code string `json:"code" example:"sajdlj23423j23"` // Invite code - Days int `json:"days" example:"1"` // Number of days till expiry - Hours int `json:"hours" example:"2"` // Number of hours till expiry - Minutes int `json:"minutes" example:"3"` // Number of minutes till expiry - UserExpiry bool `json:"user-expiry"` // Whether or not user expiry is enabled - UserDays int `json:"user-days,omitempty" example:"1"` // Number of days till user expiry - UserHours int `json:"user-hours,omitempty" example:"2"` // Number of hours till user expiry - UserMinutes int `json:"user-minutes,omitempty" example:"3"` // Number of minutes till user expiry - Created string `json:"created" example:"01/01/20 12:00"` // Date of creation - Profile string `json:"profile" example:"DefaultProfile"` // Profile used on this invite - UsedBy [][]string `json:"used-by,omitempty"` // Users who have used this invite - NoLimit bool `json:"no-limit,omitempty"` // If true, invite can be used any number of times - RemainingUses int `json:"remaining-uses,omitempty"` // Remaining number of uses (if applicable) - Email string `json:"email,omitempty"` // Email the invite was sent to (if applicable) - NotifyExpiry bool `json:"notify-expiry,omitempty"` // Whether to notify the requesting user of expiry or not - NotifyCreation bool `json:"notify-creation,omitempty"` // Whether to notify the requesting user of account creation or not - Label string `json:"label,omitempty" example:"For Friends"` // Optional label for the invite + Code string `json:"code" example:"sajdlj23423j23"` // Invite code + Days int `json:"days" example:"1"` // Number of days till expiry + Hours int `json:"hours" example:"2"` // Number of hours till expiry + Minutes int `json:"minutes" example:"3"` // Number of minutes till expiry + UserExpiry bool `json:"user-expiry"` // Whether or not user expiry is enabled + UserDays int `json:"user-days,omitempty" example:"1"` // Number of days till user expiry + UserHours int `json:"user-hours,omitempty" example:"2"` // Number of hours till user expiry + UserMinutes int `json:"user-minutes,omitempty" example:"3"` // Number of minutes till user expiry + Created int64 `json:"created" example:"1617737207510"` // Date of creation + Profile string `json:"profile" example:"DefaultProfile"` // Profile used on this invite + UsedBy map[string]int64 `json:"used-by,omitempty"` // Users who have used this invite mapped to their creation time in Epoch/Unix time + NoLimit bool `json:"no-limit,omitempty"` // If true, invite can be used any number of times + RemainingUses int `json:"remaining-uses,omitempty"` // Remaining number of uses (if applicable) + Email string `json:"email,omitempty"` // Email the invite was sent to (if applicable) + NotifyExpiry bool `json:"notify-expiry,omitempty"` // Whether to notify the requesting user of expiry or not + NotifyCreation bool `json:"notify-creation,omitempty"` // Whether to notify the requesting user of account creation or not + Label string `json:"label,omitempty" example:"For Friends"` // Optional label for the invite } type getInvitesDTO struct { diff --git a/storage.go b/storage.go index f9f2a56..edc8599 100644 --- a/storage.go +++ b/storage.go @@ -59,20 +59,21 @@ type Profile struct { } type Invite struct { - Created time.Time `json:"created"` - NoLimit bool `json:"no-limit"` - RemainingUses int `json:"remaining-uses"` - ValidTill time.Time `json:"valid_till"` - UserExpiry bool `json:"user-duration"` - UserDays int `json:"user-days,omitempty"` - UserHours int `json:"user-hours,omitempty"` - UserMinutes int `json:"user-minutes,omitempty"` - Email string `json:"email"` - UsedBy [][]string `json:"used-by"` - Notify map[string]map[string]bool `json:"notify"` - Profile string `json:"profile"` - Label string `json:"label,omitempty"` - Keys []string `json:"keys,omitempty"` + Created time.Time `json:"created"` + NoLimit bool `json:"no-limit"` + RemainingUses int `json:"remaining-uses"` + ValidTill time.Time `json:"valid_till"` + UserExpiry bool `json:"user-duration"` + UserDays int `json:"user-days,omitempty"` + UserHours int `json:"user-hours,omitempty"` + UserMinutes int `json:"user-minutes,omitempty"` + Email string `json:"email"` + // Used to be stored as formatted time, now as Unix. + UsedBy [][]string `json:"used-by"` + Notify map[string]map[string]bool `json:"notify"` + Profile string `json:"profile"` + Label string `json:"label,omitempty"` + Keys []string `json:"keys,omitempty"` } type Lang struct { diff --git a/ts/modules/invites.ts b/ts/modules/invites.ts index e125b01..42eff2b 100644 --- a/ts/modules/invites.ts +++ b/ts/modules/invites.ts @@ -1,4 +1,4 @@ -import { _get, _post, _delete, toClipboard, toggleLoader } from "../modules/common.js"; +import { _get, _post, _delete, toClipboard, toggleLoader, toDateString } from "../modules/common.js"; export class DOMInvite implements Invite { updateNotify = (checkbox: HTMLInputElement) => { @@ -116,10 +116,9 @@ export class DOMInvite implements Invite { tooltip.textContent = address; } - private _usedBy: string[][]; - get usedBy(): string[][] { return this._usedBy; } - set usedBy(uB: string[][]) { - // ub[i][0]: username, ub[i][1]: date + private _usedBy: { [name: string]: number }; + get usedBy(): { [name: string]: number } { return this._usedBy; } + set usedBy(uB: { [name: string]: number }) { this._usedBy = uB; if (uB.length == 0) { this._right.classList.add("empty"); @@ -137,11 +136,11 @@ export class DOMInvite implements Invite { `; - for (let user of uB) { + for (let username in uB) { innerHTML += ` - ${user[0]} - ${user[1]} + ${username} + ${toDateString(new Date(uB[username] * 1000))} `; } @@ -152,11 +151,16 @@ export class DOMInvite implements Invite { this._userTable.innerHTML = innerHTML; } - private _created: string; - get created(): string { return this._created; } - set created(created: string) { - this._created = created; - this._middle.querySelector("strong.inv-created").textContent = created; + private _createdUnix: number; + get created(): number { return this._createdUnix; } + set created(unix: number) { + this._createdUnix = unix; + const el = this._middle.querySelector("strong.inv-created"); + if (unix == 0) { + el.textContent = "n/a"; + } else { + el.textContent = toDateString(new Date(unix*1000)); + } } private _notifyExpiry: boolean = false; diff --git a/ts/modules/update.ts b/ts/modules/update.ts index 36d51bc..f3c8b1b 100644 --- a/ts/modules/update.ts +++ b/ts/modules/update.ts @@ -1,4 +1,4 @@ -import { _get, _post, toggleLoader } from "../modules/common.js"; +import { _get, _post, toggleLoader, toDateString } from "../modules/common.js"; import { Marked, Renderer } from "@ts-stack/markdown"; interface updateDTO { @@ -29,7 +29,7 @@ export class Updater implements updater { get date(): number { return Math.floor(this._date.getTime() / 1000); } set date(unix: number) { this._date = new Date(unix * 1000); - document.getElementById("update-date").textContent = this._date.toDateString() + " " + this._date.toLocaleTimeString(); + document.getElementById("update-date").textContent = toDateString(this._date); } get description(): string { return this._update.description; } diff --git a/ts/typings/d.ts b/ts/typings/d.ts index 23de5a1..a6aa1b9 100644 --- a/ts/typings/d.ts +++ b/ts/typings/d.ts @@ -104,8 +104,8 @@ interface Invite { expiresIn?: string; remainingUses?: string; email?: string; - usedBy?: string[][]; - created?: string; + usedBy?: { [name: string]: number }; + created?: number; notifyExpiry?: boolean; notifyCreation?: boolean; profile?: string;