mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-10-18 17:10:11 +00:00
Compare commits
No commits in common. "3e73d16cce77bc0ae877629c2a3304673d0be7fe" and "8a6cfe0b4d0439ee49b4af9733ac362e11d084ce" have entirely different histories.
3e73d16cce
...
8a6cfe0b4d
34
api.go
34
api.go
@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/hrfee/mediabrowser"
|
"github.com/hrfee/mediabrowser"
|
||||||
"github.com/itchyny/timefmt-go"
|
"github.com/knz/strtime"
|
||||||
"github.com/lithammer/shortuuid/v3"
|
"github.com/lithammer/shortuuid/v3"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
@ -48,8 +48,8 @@ func (app *appContext) loadStrftime() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (app *appContext) prettyTime(dt time.Time) (date, time string) {
|
func (app *appContext) prettyTime(dt time.Time) (date, time string) {
|
||||||
date = timefmt.Format(dt, app.datePattern)
|
date, _ = strtime.Strftime(dt, app.datePattern)
|
||||||
time = timefmt.Format(dt, app.timePattern)
|
time, _ = strtime.Strftime(dt, app.timePattern)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +189,7 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
|
|||||||
// 0 means infinite i guess?
|
// 0 means infinite i guess?
|
||||||
newInv.RemainingUses--
|
newInv.RemainingUses--
|
||||||
}
|
}
|
||||||
newInv.UsedBy = append(newInv.UsedBy, []string{username, strconv.FormatInt(currentTime.Unix(), 10)})
|
newInv.UsedBy = append(newInv.UsedBy, []string{username, app.formatDatetime(currentTime)})
|
||||||
if !del {
|
if !del {
|
||||||
app.storage.invites[code] = newInv
|
app.storage.invites[code] = newInv
|
||||||
}
|
}
|
||||||
@ -870,25 +870,13 @@ func (app *appContext) GetInvites(gc *gin.Context) {
|
|||||||
UserDays: inv.UserDays,
|
UserDays: inv.UserDays,
|
||||||
UserHours: inv.UserHours,
|
UserHours: inv.UserHours,
|
||||||
UserMinutes: inv.UserMinutes,
|
UserMinutes: inv.UserMinutes,
|
||||||
Created: inv.Created.Unix(),
|
Created: app.formatDatetime(inv.Created),
|
||||||
Profile: inv.Profile,
|
Profile: inv.Profile,
|
||||||
NoLimit: inv.NoLimit,
|
NoLimit: inv.NoLimit,
|
||||||
Label: inv.Label,
|
Label: inv.Label,
|
||||||
}
|
}
|
||||||
if len(inv.UsedBy) != 0 {
|
if len(inv.UsedBy) != 0 {
|
||||||
invite.UsedBy = map[string]int64{}
|
invite.UsedBy = inv.UsedBy
|
||||||
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
|
invite.RemainingUses = 1
|
||||||
if inv.RemainingUses != 0 {
|
if inv.RemainingUses != 0 {
|
||||||
@ -1044,8 +1032,6 @@ func (app *appContext) GetUsers(gc *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
i := 0
|
i := 0
|
||||||
app.storage.usersLock.Lock()
|
|
||||||
defer app.storage.usersLock.Unlock()
|
|
||||||
for _, jfUser := range users {
|
for _, jfUser := range users {
|
||||||
user := respUser{
|
user := respUser{
|
||||||
ID: jfUser.ID,
|
ID: jfUser.ID,
|
||||||
@ -1053,15 +1039,15 @@ func (app *appContext) GetUsers(gc *gin.Context) {
|
|||||||
Admin: jfUser.Policy.IsAdministrator,
|
Admin: jfUser.Policy.IsAdministrator,
|
||||||
Disabled: jfUser.Policy.IsDisabled,
|
Disabled: jfUser.Policy.IsDisabled,
|
||||||
}
|
}
|
||||||
|
user.LastActive = "n/a"
|
||||||
if !jfUser.LastActivityDate.IsZero() {
|
if !jfUser.LastActivityDate.IsZero() {
|
||||||
user.LastActive = jfUser.LastActivityDate.Unix()
|
user.LastActive = app.formatDatetime(jfUser.LastActivityDate.Time)
|
||||||
}
|
}
|
||||||
if email, ok := app.storage.emails[jfUser.ID]; ok {
|
if email, ok := app.storage.emails[jfUser.ID]; ok {
|
||||||
user.Email = email.(string)
|
user.Email = email.(string)
|
||||||
}
|
}
|
||||||
expiry, ok := app.storage.users[jfUser.ID]
|
if expiry, ok := app.storage.users[jfUser.ID]; ok {
|
||||||
if ok {
|
user.Expiry = app.formatDatetime(expiry)
|
||||||
user.Expiry = expiry.Unix()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.UserList[i] = user
|
resp.UserList[i] = user
|
||||||
|
6
email.go
6
email.go
@ -19,8 +19,8 @@ import (
|
|||||||
|
|
||||||
"github.com/gomarkdown/markdown"
|
"github.com/gomarkdown/markdown"
|
||||||
"github.com/gomarkdown/markdown/html"
|
"github.com/gomarkdown/markdown/html"
|
||||||
"github.com/itchyny/timefmt-go"
|
|
||||||
jEmail "github.com/jordan-wright/email"
|
jEmail "github.com/jordan-wright/email"
|
||||||
|
"github.com/knz/strtime"
|
||||||
"github.com/mailgun/mailgun-go/v4"
|
"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) {
|
func (emailer *Emailer) formatExpiry(expiry time.Time, tzaware bool, datePattern, timePattern string) (d, t, expiresIn string) {
|
||||||
d = timefmt.Format(expiry, datePattern)
|
d, _ = strtime.Strftime(expiry, datePattern)
|
||||||
t = timefmt.Format(expiry, timePattern)
|
t, _ = strtime.Strftime(expiry, timePattern)
|
||||||
currentTime := time.Now()
|
currentTime := time.Now()
|
||||||
if tzaware {
|
if tzaware {
|
||||||
currentTime = currentTime.UTC()
|
currentTime = currentTime.UTC()
|
||||||
|
2
go.mod
2
go.mod
@ -26,8 +26,8 @@ require (
|
|||||||
github.com/hrfee/jfa-go/docs v0.0.0-20201112212552-b6f3cd7c1f71
|
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/jfa-go/ombi v0.0.0-20201112212552-b6f3cd7c1f71
|
||||||
github.com/hrfee/mediabrowser v0.3.3
|
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/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/lithammer/shortuuid/v3 v3.0.4
|
||||||
github.com/mailgun/mailgun-go/v4 v4.3.0
|
github.com/mailgun/mailgun-go/v4 v4.3.0
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
4
go.sum
4
go.sum
@ -129,8 +129,6 @@ 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/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 h1:7E05uiol8hh2ytKn3WVLrUIvHAyifYEIy3Y5qtuNh8I=
|
||||||
github.com/hrfee/mediabrowser v0.3.3/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U=
|
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 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/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=
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
@ -143,6 +141,8 @@ 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/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 h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
@ -8,10 +8,9 @@
|
|||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"emailAddress": "E-Mail-Adresse",
|
"emailAddress": "E-Mail-Adresse",
|
||||||
"submit": "Absenden",
|
"submit": "Absenden",
|
||||||
"success": "Erfolgreich",
|
"success": "Erfolg",
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"copy": "Kopieren",
|
"copy": "Kopieren",
|
||||||
"theme": "Thema",
|
"theme": "Thema"
|
||||||
"copied": "Kopiert"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
"success": "Success",
|
"success": "Success",
|
||||||
"error": "Fout",
|
"error": "Fout",
|
||||||
"copy": "Kopiëer",
|
"copy": "Kopiëer",
|
||||||
"theme": "Thema",
|
"theme": "Thema"
|
||||||
"copied": "Gekopieerd"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
"success": "Sucesso",
|
"success": "Sucesso",
|
||||||
"error": "Erro",
|
"error": "Erro",
|
||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
"theme": "Tema",
|
"theme": "Tema"
|
||||||
"copied": "Copiado"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,10 @@
|
|||||||
"passwordReset": {
|
"passwordReset": {
|
||||||
"title": "Wachtwoordreset aangevraagd - Jellyfin",
|
"title": "Wachtwoordreset aangevraagd - Jellyfin",
|
||||||
"someoneHasRequestedReset": "Iemand heeft recentelijk een wachtwoordreset aangevraagd in Jellyfin.",
|
"someoneHasRequestedReset": "Iemand heeft recentelijk een wachtwoordreset aangevraagd in Jellyfin.",
|
||||||
"ifItWasYou": "Als jij dit was, voer dan onderstaande PIN in.",
|
"ifItWasYou": "Als jij dit was, voor dan onderstaande PIN in.",
|
||||||
"codeExpiry": "De code verloopt op {date}, op {time} UTC, dat is over {expiresInMinutes}.",
|
"codeExpiry": "De code verloopt op {date}, op {time} UTC, dat is over {expiresInMinutes}.",
|
||||||
"pin": "PIN",
|
"pin": "PIN",
|
||||||
"name": "Wachtwoordreset",
|
"name": "Wachtwoordreset"
|
||||||
"ifItWasYouLink": "Als jij dit was, klik dan op onderstaande link."
|
|
||||||
},
|
},
|
||||||
"userDeleted": {
|
"userDeleted": {
|
||||||
"title": "Je account is verwijderd - Jellyfin",
|
"title": "Je account is verwijderd - Jellyfin",
|
||||||
|
@ -26,8 +26,7 @@
|
|||||||
"ifItWasYou": "Se foi você, insira o PIN abaixo.",
|
"ifItWasYou": "Se foi você, insira o PIN abaixo.",
|
||||||
"codeExpiry": "O código irá expirar em {date}, ás {time}, que está em {expiresInMinutes}.",
|
"codeExpiry": "O código irá expirar em {date}, ás {time}, que está em {expiresInMinutes}.",
|
||||||
"pin": "PIN",
|
"pin": "PIN",
|
||||||
"name": "Redefinir senha",
|
"name": "Redefinir senha"
|
||||||
"ifItWasYouLink": "Se foi você, clique no link abaixo."
|
|
||||||
},
|
},
|
||||||
"userDeleted": {
|
"userDeleted": {
|
||||||
"title": "Sua conta foi excluída - Jellyfin",
|
"title": "Sua conta foi excluída - Jellyfin",
|
||||||
|
@ -13,11 +13,11 @@
|
|||||||
"reEnterPasswordInvalid": "Passwörter stimmen nicht überein.",
|
"reEnterPasswordInvalid": "Passwörter stimmen nicht überein.",
|
||||||
"createAccountButton": "Konto erstellen",
|
"createAccountButton": "Konto erstellen",
|
||||||
"passwordRequirementsHeader": "Passwortanforderungen",
|
"passwordRequirementsHeader": "Passwortanforderungen",
|
||||||
"successHeader": "Erfolgreich!",
|
"successHeader": "Erfolg!",
|
||||||
"successContinueButton": "Weiter",
|
"successContinueButton": "Weiter",
|
||||||
"confirmationRequired": "E-Mail-Bestätigung erforderlich",
|
"confirmationRequired": "E-Mail-Bestätigung erforderlich",
|
||||||
"confirmationRequiredMessage": "Bitte überprüfe dein Posteingang und bestätige deine E-Mail-Adresse.",
|
"confirmationRequiredMessage": "Bitte überprüfe dein Posteingang und bestätige deine E-Mail-Adresse.",
|
||||||
"yourAccountIsValidUntil": "Dein Konto wird bis zum {date} gültig sein."
|
"yourAccountIsValidUntil": "Dein Konto wird bis am {date} gültig sein."
|
||||||
},
|
},
|
||||||
"validationStrings": {
|
"validationStrings": {
|
||||||
"length": {
|
"length": {
|
||||||
|
@ -31,9 +31,9 @@
|
|||||||
"language": {
|
"language": {
|
||||||
"title": "Sprache",
|
"title": "Sprache",
|
||||||
"description": "Gemeinschaftsübersetzungen sind für die meisten Teile von jfa-go verfügbar. Du kannst unten die Standardsprachen auswählen, aber Benutzer können dies immer noch ändern, wenn sie wollen. Wenn du helfen willst zu übersetzen, melde dich bei {n} an, um anzufangen, etwas beizutragen!",
|
"description": "Gemeinschaftsübersetzungen sind für die meisten Teile von jfa-go verfügbar. Du kannst unten die Standardsprachen auswählen, aber Benutzer können dies immer noch ändern, wenn sie wollen. Wenn du helfen willst zu übersetzen, melde dich bei {n} an, um anzufangen, etwas beizutragen!",
|
||||||
"defaultAdminLang": "Admin Standardsprache",
|
"defaultAdminLang": "Standardsprache Admin",
|
||||||
"defaultFormLang": "Kontoerstellung Standardsprache",
|
"defaultFormLang": "Standardsprache Kontoerstellung",
|
||||||
"defaultEmailLang": "E-Mail Standardsprache"
|
"defaultEmailLang": "Standardsprache E-Mail"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"title": "Allgemein",
|
"title": "Allgemein",
|
||||||
|
42
main.go
42
main.go
@ -489,47 +489,31 @@ func start(asDaemon, firstCall bool) {
|
|||||||
}
|
}
|
||||||
if noHyphens == app.jf.Hyphens {
|
if noHyphens == app.jf.Hyphens {
|
||||||
var newEmails map[string]interface{}
|
var newEmails map[string]interface{}
|
||||||
var newUsers map[string]time.Time
|
var status int
|
||||||
var status, status2 int
|
var err error
|
||||||
var err, err2 error
|
|
||||||
if app.jf.Hyphens {
|
if app.jf.Hyphens {
|
||||||
app.info.Println(info("Your build of Jellyfin appears to hypenate user IDs. Your emails.json/users.json file will be modified to match."))
|
app.info.Println(info("Your build of Jellyfin appears to hypenate user IDs. Your emails.json file will be modified to match."))
|
||||||
time.Sleep(time.Second * time.Duration(3))
|
time.Sleep(time.Second * time.Duration(3))
|
||||||
newEmails, status, err = app.hyphenateEmailStorage(app.storage.emails)
|
newEmails, status, err = app.hyphenateEmailStorage(app.storage.emails)
|
||||||
newUsers, status2, err2 = app.hyphenateUserStorage(app.storage.users)
|
|
||||||
} else {
|
} else {
|
||||||
app.info.Println(info("Your emails.json/users.json file uses hyphens, but the Jellyfin server no longer does. It will be modified."))
|
app.info.Println(info("Your emails.json file uses hyphens, but the Jellyfin server no longer does. It will be modified."))
|
||||||
time.Sleep(time.Second * time.Duration(3))
|
time.Sleep(time.Second * time.Duration(3))
|
||||||
newEmails, status, err = app.deHyphenateEmailStorage(app.storage.emails)
|
newEmails, status, err = app.deHyphenateEmailStorage(app.storage.emails)
|
||||||
newUsers, status2, err2 = app.deHyphenateUserStorage(app.storage.users)
|
|
||||||
}
|
}
|
||||||
if status != 200 || err != nil {
|
if status != 200 || err != nil {
|
||||||
app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
|
app.err.Printf("Failed to get users from Jellyfin: Code %d", status)
|
||||||
|
app.debug.Printf("Error: %s", err)
|
||||||
app.err.Fatalf("Couldn't upgrade emails.json")
|
app.err.Fatalf("Couldn't upgrade emails.json")
|
||||||
}
|
}
|
||||||
if status2 != 200 || err2 != nil {
|
bakFile := app.storage.emails_path + ".bak"
|
||||||
app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
|
err = storeJSON(bakFile, app.storage.emails)
|
||||||
app.err.Fatalf("Couldn't upgrade users.json")
|
|
||||||
}
|
|
||||||
emailBakFile := app.storage.emails_path + ".bak"
|
|
||||||
usersBakFile := app.storage.users_path + ".bak"
|
|
||||||
err = storeJSON(emailBakFile, app.storage.emails)
|
|
||||||
err2 = storeJSON(usersBakFile, app.storage.users)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Fatalf("couldn't store emails.json backup: %v", err)
|
app.err.Fatalf("couldn't store emails.json backup: %s", err)
|
||||||
}
|
|
||||||
if err2 != nil {
|
|
||||||
app.err.Fatalf("couldn't store users.json backup: %v", err)
|
|
||||||
}
|
}
|
||||||
app.storage.emails = newEmails
|
app.storage.emails = newEmails
|
||||||
app.storage.users = newUsers
|
|
||||||
err = app.storage.storeEmails()
|
err = app.storage.storeEmails()
|
||||||
err2 = app.storage.storeUsers()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Fatalf("couldn't store emails.json: %v", err)
|
app.err.Fatalf("couldn't store emails.json: %s", err)
|
||||||
}
|
|
||||||
if err2 != nil {
|
|
||||||
app.err.Fatalf("couldn't store users.json: %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -576,13 +560,11 @@ func start(asDaemon, firstCall bool) {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
invDaemon := newInviteDaemon(time.Duration(60*time.Second), app)
|
inviteDaemon := newInviteDaemon(time.Duration(60*time.Second), app)
|
||||||
go invDaemon.run()
|
go inviteDaemon.run()
|
||||||
defer invDaemon.shutdown()
|
|
||||||
|
|
||||||
userDaemon := newUserDaemon(time.Duration(60*time.Second), app)
|
userDaemon := newUserDaemon(time.Duration(60*time.Second), app)
|
||||||
go userDaemon.run()
|
go userDaemon.run()
|
||||||
defer userDaemon.shutdown()
|
|
||||||
|
|
||||||
if app.config.Section("password_resets").Key("enabled").MustBool(false) && serverType == mediabrowser.JellyfinServer {
|
if app.config.Section("password_resets").Key("enabled").MustBool(false) && serverType == mediabrowser.JellyfinServer {
|
||||||
go app.StartPWR()
|
go app.StartPWR()
|
||||||
|
@ -80,9 +80,9 @@ type inviteDTO struct {
|
|||||||
UserDays int `json:"user-days,omitempty" example:"1"` // Number of days till user expiry
|
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
|
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
|
UserMinutes int `json:"user-minutes,omitempty" example:"3"` // Number of minutes till user expiry
|
||||||
Created int64 `json:"created" example:"1617737207510"` // Date of creation
|
Created string `json:"created" example:"01/01/20 12:00"` // Date of creation
|
||||||
Profile string `json:"profile" example:"DefaultProfile"` // Profile used on this invite
|
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
|
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
|
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)
|
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)
|
Email string `json:"email,omitempty"` // Email the invite was sent to (if applicable)
|
||||||
@ -112,9 +112,9 @@ type respUser struct {
|
|||||||
ID string `json:"id" example:"fdgsdfg45534fa"` // userID of user
|
ID string `json:"id" example:"fdgsdfg45534fa"` // userID of user
|
||||||
Name string `json:"name" example:"jeff"` // Username of user
|
Name string `json:"name" example:"jeff"` // Username of user
|
||||||
Email string `json:"email,omitempty" example:"jeff@jellyf.in"` // Email address of user (if available)
|
Email string `json:"email,omitempty" example:"jeff@jellyf.in"` // Email address of user (if available)
|
||||||
LastActive int64 `json:"last_active" example:"1617737207510"` // Time of last activity on Jellyfin
|
LastActive string `json:"last_active"` // Time of last activity on Jellyfin
|
||||||
Admin bool `json:"admin" example:"false"` // Whether or not the user is Administrator
|
Admin bool `json:"admin" example:"false"` // Whether or not the user is Administrator
|
||||||
Expiry int64 `json:"expiry" example:"1617737207510"` // Expiry time of user as Epoch/Unix time.
|
Expiry string `json:"expiry" example:"01/02/21 12:00"` // Expiry time of user, if applicable.
|
||||||
Disabled bool `json:"disabled"` // Whether or not the user is disabled.
|
Disabled bool `json:"disabled"` // Whether or not the user is disabled.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
57
storage.go
57
storage.go
@ -68,7 +68,6 @@ type Invite struct {
|
|||||||
UserHours int `json:"user-hours,omitempty"`
|
UserHours int `json:"user-hours,omitempty"`
|
||||||
UserMinutes int `json:"user-minutes,omitempty"`
|
UserMinutes int `json:"user-minutes,omitempty"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
// Used to be stored as formatted time, now as Unix.
|
|
||||||
UsedBy [][]string `json:"used-by"`
|
UsedBy [][]string `json:"used-by"`
|
||||||
Notify map[string]map[string]bool `json:"notify"`
|
Notify map[string]map[string]bool `json:"notify"`
|
||||||
Profile string `json:"profile"`
|
Profile string `json:"profile"`
|
||||||
@ -481,11 +480,11 @@ func (st *Storage) storeInvites() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (st *Storage) loadUsers() error {
|
func (st *Storage) loadUsers() error {
|
||||||
st.usersLock.Lock()
|
|
||||||
defer st.usersLock.Unlock()
|
|
||||||
if st.users == nil {
|
if st.users == nil {
|
||||||
st.users = map[string]time.Time{}
|
st.users = map[string]time.Time{}
|
||||||
}
|
}
|
||||||
|
st.usersLock.Lock()
|
||||||
|
defer st.usersLock.Unlock()
|
||||||
temp := map[string]time.Time{}
|
temp := map[string]time.Time{}
|
||||||
err := loadJSON(st.users_path, &temp)
|
err := loadJSON(st.users_path, &temp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -639,7 +638,7 @@ func hyphenate(userID string) string {
|
|||||||
return userID[:8] + "-" + userID[8:12] + "-" + userID[12:16] + "-" + userID[16:20] + "-" + userID[20:]
|
return userID[:8] + "-" + userID[8:12] + "-" + userID[12:16] + "-" + userID[16:20] + "-" + userID[20:]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *appContext) deHyphenateStorage(old map[string]interface{}) (map[string]interface{}, int, error) {
|
func (app *appContext) deHyphenateEmailStorage(old map[string]interface{}) (map[string]interface{}, int, error) {
|
||||||
jfUsers, status, err := app.jf.GetUsers(false)
|
jfUsers, status, err := app.jf.GetUsers(false)
|
||||||
if status != 200 || err != nil {
|
if status != 200 || err != nil {
|
||||||
return nil, status, err
|
return nil, status, err
|
||||||
@ -648,15 +647,15 @@ func (app *appContext) deHyphenateStorage(old map[string]interface{}) (map[strin
|
|||||||
for _, user := range jfUsers {
|
for _, user := range jfUsers {
|
||||||
unHyphenated := user.ID
|
unHyphenated := user.ID
|
||||||
hyphenated := hyphenate(unHyphenated)
|
hyphenated := hyphenate(unHyphenated)
|
||||||
val, ok := old[hyphenated]
|
email, ok := old[hyphenated]
|
||||||
if ok {
|
if ok {
|
||||||
newEmails[unHyphenated] = val
|
newEmails[unHyphenated] = email
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newEmails, status, err
|
return newEmails, status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *appContext) hyphenateStorage(old map[string]interface{}) (map[string]interface{}, int, error) {
|
func (app *appContext) hyphenateEmailStorage(old map[string]interface{}) (map[string]interface{}, int, error) {
|
||||||
jfUsers, status, err := app.jf.GetUsers(false)
|
jfUsers, status, err := app.jf.GetUsers(false)
|
||||||
if status != 200 || err != nil {
|
if status != 200 || err != nil {
|
||||||
return nil, status, err
|
return nil, status, err
|
||||||
@ -665,50 +664,10 @@ func (app *appContext) hyphenateStorage(old map[string]interface{}) (map[string]
|
|||||||
for _, user := range jfUsers {
|
for _, user := range jfUsers {
|
||||||
unstripped := user.ID
|
unstripped := user.ID
|
||||||
stripped := strings.ReplaceAll(unstripped, "-", "")
|
stripped := strings.ReplaceAll(unstripped, "-", "")
|
||||||
val, ok := old[stripped]
|
email, ok := old[stripped]
|
||||||
if ok {
|
if ok {
|
||||||
newEmails[unstripped] = val
|
newEmails[unstripped] = email
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newEmails, status, err
|
return newEmails, status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *appContext) hyphenateEmailStorage(old map[string]interface{}) (map[string]interface{}, int, error) {
|
|
||||||
return app.hyphenateStorage(old)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *appContext) deHyphenateEmailStorage(old map[string]interface{}) (map[string]interface{}, int, error) {
|
|
||||||
return app.deHyphenateStorage(old)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *appContext) hyphenateUserStorage(old map[string]time.Time) (map[string]time.Time, int, error) {
|
|
||||||
asInterface := map[string]interface{}{}
|
|
||||||
for k, v := range old {
|
|
||||||
asInterface[k] = v
|
|
||||||
}
|
|
||||||
fixed, status, err := app.hyphenateStorage(asInterface)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status, err
|
|
||||||
}
|
|
||||||
out := map[string]time.Time{}
|
|
||||||
for k, v := range fixed {
|
|
||||||
out[k] = v.(time.Time)
|
|
||||||
}
|
|
||||||
return out, status, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *appContext) deHyphenateUserStorage(old map[string]time.Time) (map[string]time.Time, int, error) {
|
|
||||||
asInterface := map[string]interface{}{}
|
|
||||||
for k, v := range old {
|
|
||||||
asInterface[k] = v
|
|
||||||
}
|
|
||||||
fixed, status, err := app.deHyphenateStorage(asInterface)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status, err
|
|
||||||
}
|
|
||||||
out := map[string]time.Time{}
|
|
||||||
for k, v := range fixed {
|
|
||||||
out[k] = v.(time.Time)
|
|
||||||
}
|
|
||||||
return out, status, err
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { _get, _post, _delete, toggleLoader, toDateString } from "../modules/common.js";
|
import { _get, _post, _delete, toggleLoader } from "../modules/common.js";
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
email: string | undefined;
|
email: string | undefined;
|
||||||
last_active: number;
|
last_active: string;
|
||||||
admin: boolean;
|
admin: boolean;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
expiry: number;
|
expiry: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class user implements User {
|
class user implements User {
|
||||||
@ -20,9 +20,7 @@ class user implements User {
|
|||||||
private _emailAddress: string;
|
private _emailAddress: string;
|
||||||
private _emailEditButton: HTMLElement;
|
private _emailEditButton: HTMLElement;
|
||||||
private _expiry: HTMLTableDataCellElement;
|
private _expiry: HTMLTableDataCellElement;
|
||||||
private _expiryUnix: number;
|
|
||||||
private _lastActive: HTMLTableDataCellElement;
|
private _lastActive: HTMLTableDataCellElement;
|
||||||
private _lastActiveUnix: number;
|
|
||||||
id: string;
|
id: string;
|
||||||
private _selected: boolean;
|
private _selected: boolean;
|
||||||
|
|
||||||
@ -69,25 +67,11 @@ class user implements User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get expiry(): number { return this._expiryUnix; }
|
get expiry(): string { return this._expiry.textContent; }
|
||||||
set expiry(unix: number) {
|
set expiry(value: string) { this._expiry.textContent = value; }
|
||||||
this._expiryUnix = unix;
|
|
||||||
if (unix == 0) {
|
|
||||||
this._expiry.textContent = "";
|
|
||||||
} else {
|
|
||||||
this._expiry.textContent = toDateString(new Date(unix*1000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get last_active(): number { return this._lastActiveUnix; }
|
get last_active(): string { return this._lastActive.textContent; }
|
||||||
set last_active(unix: number) {
|
set last_active(value: string) { this._lastActive.textContent = value; }
|
||||||
this._lastActiveUnix = unix;
|
|
||||||
if (unix == 0) {
|
|
||||||
this._lastActive.textContent == "";
|
|
||||||
} else {
|
|
||||||
this._lastActive.textContent = toDateString(new Date(unix*1000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _checkEvent = new CustomEvent("accountCheckEvent");
|
private _checkEvent = new CustomEvent("accountCheckEvent");
|
||||||
private _uncheckEvent = new CustomEvent("accountUncheckEvent");
|
private _uncheckEvent = new CustomEvent("accountUncheckEvent");
|
||||||
|
@ -6,13 +6,6 @@ export function createEl(html: string): HTMLElement {
|
|||||||
return div.firstElementChild as HTMLElement;
|
return div.firstElementChild as HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toDateString(date: Date): string {
|
|
||||||
return date.toLocaleDateString() + " " + date.toLocaleString([], {
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serializeForm(id: string): Object {
|
export function serializeForm(id: string): Object {
|
||||||
const form = document.getElementById(id) as HTMLFormElement;
|
const form = document.getElementById(id) as HTMLFormElement;
|
||||||
let formData = {};
|
let formData = {};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { _get, _post, _delete, toClipboard, toggleLoader, toDateString } from "../modules/common.js";
|
import { _get, _post, _delete, toClipboard, toggleLoader } from "../modules/common.js";
|
||||||
|
|
||||||
export class DOMInvite implements Invite {
|
export class DOMInvite implements Invite {
|
||||||
updateNotify = (checkbox: HTMLInputElement) => {
|
updateNotify = (checkbox: HTMLInputElement) => {
|
||||||
@ -116,9 +116,10 @@ export class DOMInvite implements Invite {
|
|||||||
tooltip.textContent = address;
|
tooltip.textContent = address;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _usedBy: { [name: string]: number };
|
private _usedBy: string[][];
|
||||||
get usedBy(): { [name: string]: number } { return this._usedBy; }
|
get usedBy(): string[][] { return this._usedBy; }
|
||||||
set usedBy(uB: { [name: string]: number }) {
|
set usedBy(uB: string[][]) {
|
||||||
|
// ub[i][0]: username, ub[i][1]: date
|
||||||
this._usedBy = uB;
|
this._usedBy = uB;
|
||||||
if (uB.length == 0) {
|
if (uB.length == 0) {
|
||||||
this._right.classList.add("empty");
|
this._right.classList.add("empty");
|
||||||
@ -136,11 +137,11 @@ export class DOMInvite implements Invite {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
`;
|
`;
|
||||||
for (let username in uB) {
|
for (let user of uB) {
|
||||||
innerHTML += `
|
innerHTML += `
|
||||||
<tr>
|
<tr>
|
||||||
<td>${username}</td>
|
<td>${user[0]}</td>
|
||||||
<td>${toDateString(new Date(uB[username] * 1000))}</td>
|
<td>${user[1]}</td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -151,16 +152,11 @@ export class DOMInvite implements Invite {
|
|||||||
this._userTable.innerHTML = innerHTML;
|
this._userTable.innerHTML = innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createdUnix: number;
|
private _created: string;
|
||||||
get created(): number { return this._createdUnix; }
|
get created(): string { return this._created; }
|
||||||
set created(unix: number) {
|
set created(created: string) {
|
||||||
this._createdUnix = unix;
|
this._created = created;
|
||||||
const el = this._middle.querySelector("strong.inv-created");
|
this._middle.querySelector("strong.inv-created").textContent = created;
|
||||||
if (unix == 0) {
|
|
||||||
el.textContent = "n/a";
|
|
||||||
} else {
|
|
||||||
el.textContent = toDateString(new Date(unix*1000));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _notifyExpiry: boolean = false;
|
private _notifyExpiry: boolean = false;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { _get, _post, toggleLoader, toDateString } from "../modules/common.js";
|
import { _get, _post, toggleLoader } from "../modules/common.js";
|
||||||
import { Marked, Renderer } from "@ts-stack/markdown";
|
import { Marked, Renderer } from "@ts-stack/markdown";
|
||||||
|
|
||||||
interface updateDTO {
|
interface updateDTO {
|
||||||
@ -29,7 +29,7 @@ export class Updater implements updater {
|
|||||||
get date(): number { return Math.floor(this._date.getTime() / 1000); }
|
get date(): number { return Math.floor(this._date.getTime() / 1000); }
|
||||||
set date(unix: number) {
|
set date(unix: number) {
|
||||||
this._date = new Date(unix * 1000);
|
this._date = new Date(unix * 1000);
|
||||||
document.getElementById("update-date").textContent = toDateString(this._date);
|
document.getElementById("update-date").textContent = this._date.toDateString() + " " + this._date.toLocaleTimeString();
|
||||||
}
|
}
|
||||||
|
|
||||||
get description(): string { return this._update.description; }
|
get description(): string { return this._update.description; }
|
||||||
|
@ -104,8 +104,8 @@ interface Invite {
|
|||||||
expiresIn?: string;
|
expiresIn?: string;
|
||||||
remainingUses?: string;
|
remainingUses?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
usedBy?: { [name: string]: number };
|
usedBy?: string[][];
|
||||||
created?: number;
|
created?: string;
|
||||||
notifyExpiry?: boolean;
|
notifyExpiry?: boolean;
|
||||||
notifyCreation?: boolean;
|
notifyCreation?: boolean;
|
||||||
profile?: string;
|
profile?: string;
|
||||||
|
@ -35,6 +35,7 @@ func (rt *userDaemon) run() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
started := time.Now()
|
started := time.Now()
|
||||||
|
rt.app.storage.loadInvites()
|
||||||
rt.app.checkUsers()
|
rt.app.checkUsers()
|
||||||
finished := time.Now()
|
finished := time.Now()
|
||||||
duration := finished.Sub(started)
|
duration := finished.Sub(started)
|
||||||
@ -42,13 +43,6 @@ func (rt *userDaemon) run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rt *userDaemon) shutdown() {
|
|
||||||
rt.Stopped = true
|
|
||||||
rt.ShutdownChannel <- "Down"
|
|
||||||
<-rt.ShutdownChannel
|
|
||||||
close(rt.ShutdownChannel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *appContext) checkUsers() {
|
func (app *appContext) checkUsers() {
|
||||||
if err := app.storage.loadUsers(); err != nil {
|
if err := app.storage.loadUsers(); err != nil {
|
||||||
app.err.Printf("Failed to load user expiries: %v", err)
|
app.err.Printf("Failed to load user expiries: %v", err)
|
||||||
@ -82,8 +76,12 @@ func (app *appContext) checkUsers() {
|
|||||||
}
|
}
|
||||||
for id, expiry := range app.storage.users {
|
for id, expiry := range app.storage.users {
|
||||||
if _, ok := userExists[id]; !ok {
|
if _, ok := userExists[id]; !ok {
|
||||||
app.info.Printf("Deleting expiry for non-existent user \"%s\"", id)
|
app.debug.Printf("Deleting expiry for non-existent user \"%s\"", id)
|
||||||
delete(app.storage.users, id)
|
delete(app.storage.users, id)
|
||||||
|
err = app.storage.storeUsers()
|
||||||
|
if err != nil {
|
||||||
|
app.err.Printf("Failed to store user duration: %s", err)
|
||||||
|
}
|
||||||
} else if time.Now().After(expiry) {
|
} else if time.Now().After(expiry) {
|
||||||
found := false
|
found := false
|
||||||
var user mediabrowser.User
|
var user mediabrowser.User
|
||||||
@ -113,7 +111,10 @@ func (app *appContext) checkUsers() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
delete(app.storage.users, id)
|
delete(app.storage.users, id)
|
||||||
app.jf.CacheExpiry = time.Now()
|
err = app.storage.storeUsers()
|
||||||
|
if err != nil {
|
||||||
|
app.err.Printf("Failed to store user duration: %s", err)
|
||||||
|
}
|
||||||
if email {
|
if email {
|
||||||
address, ok := app.storage.emails[id]
|
address, ok := app.storage.emails[id]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -130,8 +131,4 @@ func (app *appContext) checkUsers() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = app.storage.storeUsers()
|
|
||||||
if err != nil {
|
|
||||||
app.err.Printf("Failed to store user expiries: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user