db: migrate invites, user expiry

some fixes to stuff in there too, probably
db
Harvey Tindall 11 months ago
parent a470d77938
commit 63948a6de0
No known key found for this signature in database
GPG Key ID: BBC65952848FB1A2

@ -15,16 +15,15 @@ import (
func (app *appContext) checkInvites() { func (app *appContext) checkInvites() {
currentTime := time.Now() currentTime := time.Now()
app.storage.loadInvites() app.storage.loadInvites()
changed := false for _, data := range app.storage.GetInvites() {
for code, data := range app.storage.GetInvites() {
expiry := data.ValidTill expiry := data.ValidTill
if !currentTime.After(expiry) { if !currentTime.After(expiry) {
continue continue
} }
app.debug.Printf("Housekeeping: Deleting old invite %s", code) app.debug.Printf("Housekeeping: Deleting old invite %s", data.Code)
notify := data.Notify notify := data.Notify
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 { if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
app.debug.Printf("%s: Expiry notification", code) app.debug.Printf("%s: Expiry notification", data.Code)
var wait sync.WaitGroup var wait sync.WaitGroup
for address, settings := range notify { for address, settings := range notify {
if !settings["notify-expiry"] { if !settings["notify-expiry"] {
@ -33,9 +32,9 @@ func (app *appContext) checkInvites() {
wait.Add(1) wait.Add(1)
go func(addr string) { go func(addr string) {
defer wait.Done() defer wait.Done()
msg, err := app.email.constructExpiry(code, data, app, false) msg, err := app.email.constructExpiry(data.Code, data, app, false)
if err != nil { if err != nil {
app.err.Printf("%s: Failed to construct expiry notification: %v", code, err) app.err.Printf("%s: Failed to construct expiry notification: %v", data.Code, err)
} else { } else {
// Check whether notify "address" is an email address of Jellyfin ID // Check whether notify "address" is an email address of Jellyfin ID
if strings.Contains(addr, "@") { if strings.Contains(addr, "@") {
@ -44,7 +43,7 @@ func (app *appContext) checkInvites() {
err = app.sendByID(msg, addr) err = app.sendByID(msg, addr)
} }
if err != nil { if err != nil {
app.err.Printf("%s: Failed to send expiry notification: %v", code, err) app.err.Printf("%s: Failed to send expiry notification: %v", data.Code, err)
} else { } else {
app.info.Printf("Sent expiry notification to %s", addr) app.info.Printf("Sent expiry notification to %s", addr)
} }
@ -53,18 +52,13 @@ func (app *appContext) checkInvites() {
} }
wait.Wait() wait.Wait()
} }
changed = true app.storage.DeleteInvitesKey(data.Code)
app.storage.DeleteInvitesKey(code)
}
if changed {
app.storage.storeInvites()
} }
} }
func (app *appContext) checkInvite(code string, used bool, username string) bool { func (app *appContext) checkInvite(code string, used bool, username string) bool {
currentTime := time.Now() currentTime := time.Now()
app.storage.loadInvites() app.storage.loadInvites()
changed := false
inv, match := app.storage.GetInvitesKey(code) inv, match := app.storage.GetInvitesKey(code)
if !match { if !match {
return false return false
@ -103,11 +97,9 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
} }
wait.Wait() wait.Wait()
} }
changed = true
match = false match = false
app.storage.DeleteInvitesKey(code) app.storage.DeleteInvitesKey(code)
} else if used { } else if used {
changed = true
del := false del := false
newInv := inv newInv := inv
if newInv.RemainingUses == 1 { if newInv.RemainingUses == 1 {
@ -122,9 +114,6 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
app.storage.SetInvitesKey(code, newInv) app.storage.SetInvitesKey(code, newInv)
} }
} }
if changed {
app.storage.storeInvites()
}
return match return match
} }
@ -220,7 +209,6 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
} }
} }
app.storage.SetInvitesKey(inviteCode, invite) app.storage.SetInvitesKey(inviteCode, invite)
app.storage.storeInvites()
respondBool(200, true, gc) respondBool(200, true, gc)
} }
@ -236,10 +224,10 @@ func (app *appContext) GetInvites(gc *gin.Context) {
app.storage.loadInvites() app.storage.loadInvites()
app.checkInvites() app.checkInvites()
var invites []inviteDTO var invites []inviteDTO
for code, inv := range app.storage.GetInvites() { for _, inv := range app.storage.GetInvites() {
_, months, days, hours, minutes, _ := timeDiff(inv.ValidTill, currentTime) _, months, days, hours, minutes, _ := timeDiff(inv.ValidTill, currentTime)
invite := inviteDTO{ invite := inviteDTO{
Code: code, Code: inv.Code,
Months: months, Months: months,
Days: days, Days: days,
Hours: hours, Hours: hours,
@ -277,21 +265,19 @@ func (app *appContext) GetInvites(gc *gin.Context) {
invite.SendTo = inv.SendTo invite.SendTo = inv.SendTo
} }
if len(inv.Notify) != 0 { if len(inv.Notify) != 0 {
var address string // app.err.Printf("%s has notify section: %+v, you are %s\n", inv.Code, inv.Notify, gc.GetString("jfId"))
var addressOrID string
if app.config.Section("ui").Key("jellyfin_login").MustBool(false) { if app.config.Section("ui").Key("jellyfin_login").MustBool(false) {
app.storage.loadEmails() addressOrID = gc.GetString("jfId")
if addr, ok := app.storage.GetEmailsKey(gc.GetString("jfId")); ok && addr.Addr != "" {
address = addr.Addr
}
} else { } else {
address = app.config.Section("ui").Key("email").String() addressOrID = app.config.Section("ui").Key("email").String()
} }
if _, ok := inv.Notify[address]; ok { if _, ok := inv.Notify[addressOrID]; ok {
if _, ok = inv.Notify[address]["notify-expiry"]; ok { if _, ok = inv.Notify[addressOrID]["notify-expiry"]; ok {
invite.NotifyExpiry = inv.Notify[address]["notify-expiry"] invite.NotifyExpiry = inv.Notify[addressOrID]["notify-expiry"]
} }
if _, ok = inv.Notify[address]["notify-creation"]; ok { if _, ok = inv.Notify[addressOrID]["notify-creation"]; ok {
invite.NotifyCreation = inv.Notify[address]["notify-creation"] invite.NotifyCreation = inv.Notify[addressOrID]["notify-creation"]
} }
} }
} }
@ -338,7 +324,6 @@ func (app *appContext) SetProfile(gc *gin.Context) {
inv, _ := app.storage.GetInvitesKey(req.Invite) inv, _ := app.storage.GetInvitesKey(req.Invite)
inv.Profile = req.Profile inv.Profile = req.Profile
app.storage.SetInvitesKey(req.Invite, inv) app.storage.SetInvitesKey(req.Invite, inv)
app.storage.storeInvites()
respondBool(200, true, gc) respondBool(200, true, gc)
} }
@ -401,9 +386,6 @@ func (app *appContext) SetNotify(gc *gin.Context) {
app.storage.SetInvitesKey(code, invite) app.storage.SetInvitesKey(code, invite)
} }
} }
if changed {
app.storage.storeInvites()
}
} }
// @Summary Delete an invite. // @Summary Delete an invite.
@ -422,7 +404,6 @@ func (app *appContext) DeleteInvite(gc *gin.Context) {
_, ok = app.storage.GetInvitesKey(req.Code) _, ok = app.storage.GetInvitesKey(req.Code)
if ok { if ok {
app.storage.DeleteInvitesKey(req.Code) app.storage.DeleteInvitesKey(req.Code)
app.storage.storeInvites()
app.info.Printf("%s: Invite deleted", req.Code) app.info.Printf("%s: Invite deleted", req.Code)
respondBool(200, true, gc) respondBool(200, true, gc)
return return

@ -387,11 +387,6 @@ func (app *appContext) setContactMethods(req SetContactMethodsDTO, gc *gin.Conte
change := dcUser.Contact != req.Discord change := dcUser.Contact != req.Discord
dcUser.Contact = req.Discord dcUser.Contact = req.Discord
app.storage.SetDiscordKey(req.ID, dcUser) app.storage.SetDiscordKey(req.ID, dcUser)
if err := app.storage.storeDiscordUsers(); err != nil {
respondBool(500, false, gc)
app.err.Printf("Discord: Failed to store users: %v", err)
return
}
if change { if change {
msg := "" msg := ""
if !req.Discord { if !req.Discord {
@ -404,11 +399,6 @@ func (app *appContext) setContactMethods(req SetContactMethodsDTO, gc *gin.Conte
change := mxUser.Contact != req.Matrix change := mxUser.Contact != req.Matrix
mxUser.Contact = req.Matrix mxUser.Contact = req.Matrix
app.storage.SetMatrixKey(req.ID, mxUser) app.storage.SetMatrixKey(req.ID, mxUser)
if err := app.storage.storeMatrixUsers(); err != nil {
respondBool(500, false, gc)
app.err.Printf("Matrix: Failed to store users: %v", err)
return
}
if change { if change {
msg := "" msg := ""
if !req.Matrix { if !req.Matrix {
@ -421,11 +411,6 @@ func (app *appContext) setContactMethods(req SetContactMethodsDTO, gc *gin.Conte
change := email.Contact != req.Email change := email.Contact != req.Email
email.Contact = req.Email email.Contact = req.Email
app.storage.SetEmailsKey(req.ID, email) app.storage.SetEmailsKey(req.ID, email)
if err := app.storage.storeEmails(); err != nil {
respondBool(500, false, gc)
app.err.Printf("Failed to store emails: %v", err)
return
}
if change { if change {
msg := "" msg := ""
if !req.Email { if !req.Email {
@ -646,7 +631,7 @@ func (app *appContext) MatrixConnect(gc *gin.Context) {
var req MatrixConnectUserDTO var req MatrixConnectUserDTO
gc.BindJSON(&req) gc.BindJSON(&req)
if app.storage.GetMatrix() == nil { if app.storage.GetMatrix() == nil {
app.storage.matrix = matrixStore{} app.storage.deprecatedMatrix = matrixStore{}
} }
roomID, encrypted, err := app.matrix.CreateRoom(req.UserID) roomID, encrypted, err := app.matrix.CreateRoom(req.UserID)
if err != nil { if err != nil {
@ -662,11 +647,6 @@ func (app *appContext) MatrixConnect(gc *gin.Context) {
Encrypted: encrypted, Encrypted: encrypted,
}) })
app.matrix.isEncrypted[roomID] = encrypted app.matrix.isEncrypted[roomID] = encrypted
if err := app.storage.storeMatrixUsers(); err != nil {
app.err.Printf("Failed to store Matrix users: %v", err)
respondBool(500, false, gc)
return
}
respondBool(200, true, gc) respondBool(200, true, gc)
} }
@ -717,11 +697,6 @@ func (app *appContext) DiscordConnect(gc *gin.Context) {
return return
} }
app.storage.SetDiscordKey(req.JellyfinID, user) app.storage.SetDiscordKey(req.JellyfinID, user)
if err := app.storage.storeDiscordUsers(); err != nil {
app.err.Printf("Failed to store Discord users: %v", err)
respondBool(500, false, gc)
return
}
linkExistingOmbiDiscordTelegram(app) linkExistingOmbiDiscordTelegram(app)
respondBool(200, true, gc) respondBool(200, true, gc)
} }

@ -38,8 +38,8 @@ func (app *appContext) MyDetails(gc *gin.Context) {
} }
resp.Disabled = user.Policy.IsDisabled resp.Disabled = user.Policy.IsDisabled
if exp, ok := app.storage.users[user.ID]; ok { if exp, ok := app.storage.GetUserExpiryKey(user.ID); ok {
resp.Expiry = exp.Unix() resp.Expiry = exp.Expiry.Unix()
} }
app.storage.loadEmails() app.storage.loadEmails()
@ -199,7 +199,6 @@ func (app *appContext) confirmMyAction(gc *gin.Context, key string) {
} }
} }
app.storage.storeEmails()
app.info.Println("Email list modified") app.info.Println("Email list modified")
gc.Redirect(http.StatusSeeOther, "/my/account") gc.Redirect(http.StatusSeeOther, "/my/account")
return return

@ -62,7 +62,6 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
app.jf.CacheExpiry = time.Now() app.jf.CacheExpiry = time.Now()
if emailEnabled { if emailEnabled {
app.storage.SetEmailsKey(id, EmailAddress{Addr: req.Email, Contact: true}) app.storage.SetEmailsKey(id, EmailAddress{Addr: req.Email, Contact: true})
app.storage.storeEmails()
} }
if app.config.Section("ombi").Key("enabled").MustBool(false) { if app.config.Section("ombi").Key("enabled").MustBool(false) {
app.storage.loadOmbiTemplate() app.storage.loadOmbiTemplate()
@ -327,29 +326,19 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
// if app.config.Section("password_resets").Key("enabled").MustBool(false) { // if app.config.Section("password_resets").Key("enabled").MustBool(false) {
if req.Email != "" { if req.Email != "" {
app.storage.SetEmailsKey(id, EmailAddress{Addr: req.Email, Contact: true}) app.storage.SetEmailsKey(id, EmailAddress{Addr: req.Email, Contact: true})
app.storage.storeEmails()
} }
expiry := time.Time{} expiry := time.Time{}
if invite.UserExpiry { if invite.UserExpiry {
app.storage.usersLock.Lock()
defer app.storage.usersLock.Unlock()
expiry = time.Now().AddDate(0, invite.UserMonths, invite.UserDays).Add(time.Duration((60*invite.UserHours)+invite.UserMinutes) * time.Minute) expiry = time.Now().AddDate(0, invite.UserMonths, invite.UserDays).Add(time.Duration((60*invite.UserHours)+invite.UserMinutes) * time.Minute)
app.storage.users[id] = expiry app.storage.SetUserExpiryKey(id, UserExpiry{Expiry: expiry})
if err := app.storage.storeUsers(); err != nil {
app.err.Printf("Failed to store user duration: %v", err)
}
} }
if discordVerified { if discordVerified {
discordUser.Contact = req.DiscordContact discordUser.Contact = req.DiscordContact
if app.storage.discord == nil { if app.storage.deprecatedDiscord == nil {
app.storage.discord = discordStore{} app.storage.deprecatedDiscord = discordStore{}
} }
app.storage.SetDiscordKey(user.ID, discordUser) app.storage.SetDiscordKey(user.ID, discordUser)
if err := app.storage.storeDiscordUsers(); err != nil { delete(app.discord.verifiedTokens, req.DiscordPIN)
app.err.Printf("Failed to store Discord users: %v", err)
} else {
delete(app.discord.verifiedTokens, req.DiscordPIN)
}
} }
if telegramVerified { if telegramVerified {
tgUser := TelegramUser{ tgUser := TelegramUser{
@ -360,8 +349,8 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
if lang, ok := app.telegram.languages[tgToken.ChatID]; ok { if lang, ok := app.telegram.languages[tgToken.ChatID]; ok {
tgUser.Lang = lang tgUser.Lang = lang
} }
if app.storage.telegram == nil { if app.storage.deprecatedTelegram == nil {
app.storage.telegram = telegramStore{} app.storage.deprecatedTelegram = telegramStore{}
} }
app.telegram.DeleteVerifiedToken(req.TelegramPIN) app.telegram.DeleteVerifiedToken(req.TelegramPIN)
app.storage.SetTelegramKey(user.ID, tgUser) app.storage.SetTelegramKey(user.ID, tgUser)
@ -404,13 +393,10 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
if matrixVerified { if matrixVerified {
matrixUser.Contact = req.MatrixContact matrixUser.Contact = req.MatrixContact
delete(app.matrix.tokens, req.MatrixPIN) delete(app.matrix.tokens, req.MatrixPIN)
if app.storage.matrix == nil { if app.storage.deprecatedMatrix == nil {
app.storage.matrix = matrixStore{} app.storage.deprecatedMatrix = matrixStore{}
} }
app.storage.SetMatrixKey(user.ID, matrixUser) app.storage.SetMatrixKey(user.ID, matrixUser)
if err := app.storage.storeMatrixUsers(); err != nil {
app.err.Printf("Failed to store Matrix users: %v", err)
}
} }
if (emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "") || telegramVerified || discordVerified || matrixVerified { if (emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "") || telegramVerified || discordVerified || matrixVerified {
name := app.getAddressOrName(user.ID) name := app.getAddressOrName(user.ID)
@ -629,21 +615,16 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) {
respondBool(400, false, gc) respondBool(400, false, gc)
return return
} }
app.storage.usersLock.Lock()
defer app.storage.usersLock.Unlock()
for _, id := range req.Users { for _, id := range req.Users {
if expiry, ok := app.storage.users[id]; ok { base := time.Now()
app.storage.users[id] = expiry.AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute) if expiry, ok := app.storage.GetUserExpiryKey(id); ok {
base = expiry.Expiry
app.debug.Printf("Expiry extended for \"%s\"", id) app.debug.Printf("Expiry extended for \"%s\"", id)
} else { } else {
app.storage.users[id] = time.Now().AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute)
app.debug.Printf("Created expiry for \"%s\"", id) app.debug.Printf("Created expiry for \"%s\"", id)
} }
} expiry := UserExpiry{Expiry: base.AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute)}
if err := app.storage.storeUsers(); err != nil { app.storage.SetUserExpiryKey(id, expiry)
app.err.Printf("Failed to store user duration: %v", err)
respondBool(500, false, gc)
return
} }
respondBool(204, true, gc) respondBool(204, true, gc)
} }
@ -853,8 +834,6 @@ func (app *appContext) GetUsers(gc *gin.Context) {
adminOnly := app.config.Section("ui").Key("admin_only").MustBool(true) adminOnly := app.config.Section("ui").Key("admin_only").MustBool(true)
allowAll := app.config.Section("ui").Key("allow_all").MustBool(false) allowAll := app.config.Section("ui").Key("allow_all").MustBool(false)
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,
@ -871,9 +850,9 @@ func (app *appContext) GetUsers(gc *gin.Context) {
user.Label = email.Label user.Label = email.Label
user.AccountsAdmin = (app.jellyfinLogin) && (email.Admin || (adminOnly && jfUser.Policy.IsAdministrator) || allowAll) user.AccountsAdmin = (app.jellyfinLogin) && (email.Admin || (adminOnly && jfUser.Policy.IsAdministrator) || allowAll)
} }
expiry, ok := app.storage.users[jfUser.ID] expiry, ok := app.storage.GetUserExpiryKey(jfUser.ID)
if ok { if ok {
user.Expiry = expiry.Unix() user.Expiry = expiry.Expiry.Unix()
} }
if tgUser, ok := app.storage.GetTelegramKey(jfUser.ID); ok { if tgUser, ok := app.storage.GetTelegramKey(jfUser.ID); ok {
user.Telegram = tgUser.Username user.Telegram = tgUser.Username
@ -924,10 +903,6 @@ func (app *appContext) SetAccountsAdmin(gc *gin.Context) {
app.storage.SetEmailsKey(id, emailStore) app.storage.SetEmailsKey(id, emailStore)
} }
} }
if err := app.storage.storeEmails(); err != nil {
app.err.Printf("Failed to store email list: %v", err)
respondBool(500, false, gc)
}
app.info.Println("Email list modified") app.info.Println("Email list modified")
respondBool(204, true, gc) respondBool(204, true, gc)
} }
@ -961,10 +936,6 @@ func (app *appContext) ModifyLabels(gc *gin.Context) {
app.storage.SetEmailsKey(id, emailStore) app.storage.SetEmailsKey(id, emailStore)
} }
} }
if err := app.storage.storeEmails(); err != nil {
app.err.Printf("Failed to store email list: %v", err)
respondBool(500, false, gc)
}
app.info.Println("Email list modified") app.info.Println("Email list modified")
respondBool(204, true, gc) respondBool(204, true, gc)
} }
@ -999,7 +970,6 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
// Auto enable contact by email for newly added addresses // Auto enable contact by email for newly added addresses
if !ok || oldEmail.Addr == "" { if !ok || oldEmail.Addr == "" {
emailStore.Contact = true emailStore.Contact = true
app.storage.storeEmails()
} }
emailStore.Addr = address emailStore.Addr = address
@ -1016,7 +986,6 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
} }
} }
} }
app.storage.storeEmails()
app.info.Println("Email list modified") app.info.Println("Email list modified")
respondBool(200, true, gc) respondBool(200, true, gc)
} }

@ -362,7 +362,7 @@ func start(asDaemon, firstCall bool) {
app.err.Printf("Failed to load Displayprefs: %v", err) app.err.Printf("Failed to load Displayprefs: %v", err)
} }
app.storage.users_path = app.config.Section("files").Key("users").String() app.storage.users_path = app.config.Section("files").Key("users").String()
if err := app.storage.loadUsers(); err != nil { if err := app.storage.loadUserExpiries(); err != nil {
app.err.Printf("Failed to load Users: %v", err) app.err.Printf("Failed to load Users: %v", err)
} }
app.storage.telegram_path = app.config.Section("files").Key("telegram_users").String() app.storage.telegram_path = app.config.Section("files").Key("telegram_users").String()
@ -400,11 +400,6 @@ func start(asDaemon, firstCall bool) {
app.storage.db_path = filepath.Join(app.dataPath, "db") app.storage.db_path = filepath.Join(app.dataPath, "db")
app.ConnectDB() app.ConnectDB()
defer app.storage.db.Close() defer app.storage.db.Close()
if !app.config.Section("").Key("migrated_to_db").MustBool(false) {
// FIXME: Mark as done at some point
migrateToBadger(app)
}
// Read config-base for settings on web. // Read config-base for settings on web.
app.configBasePath = "config-base.json" app.configBasePath = "config-base.json"
configBase, _ := fs.ReadFile(localFS, app.configBasePath) configBase, _ := fs.ReadFile(localFS, app.configBasePath)

@ -16,26 +16,28 @@ func runMigrations(app *appContext) {
migrateNotificationMethods(app) migrateNotificationMethods(app)
linkExistingOmbiDiscordTelegram(app) linkExistingOmbiDiscordTelegram(app)
// migrateHyphens(app) // migrateHyphens(app)
migrateToBadger(app)
} }
// Migrate pre-0.2.0 user templates to profiles // Migrate pre-0.2.0 user templates to profiles
func migrateProfiles(app *appContext) { func migrateProfiles(app *appContext) {
if !(app.storage.policy.BlockedTags == nil && app.storage.configuration.GroupedFolders == nil && len(app.storage.displayprefs) == 0) { if app.storage.policy.BlockedTags == nil && app.storage.configuration.GroupedFolders == nil && len(app.storage.displayprefs) == 0 {
app.info.Println("Migrating user template files to new profile format") return
app.storage.migrateToProfile() }
for _, path := range [3]string{app.storage.policy_path, app.storage.configuration_path, app.storage.displayprefs_path} { app.info.Println("Migrating user template files to new profile format")
if _, err := os.Stat(path); !os.IsNotExist(err) { app.storage.migrateToProfile()
dir, fname := filepath.Split(path) for _, path := range [3]string{app.storage.policy_path, app.storage.configuration_path, app.storage.displayprefs_path} {
newFname := strings.Replace(fname, ".json", ".old.json", 1) if _, err := os.Stat(path); !os.IsNotExist(err) {
err := os.Rename(path, filepath.Join(dir, newFname)) dir, fname := filepath.Split(path)
if err != nil { newFname := strings.Replace(fname, ".json", ".old.json", 1)
app.err.Fatalf("Failed to rename %s: %s", fname, err) err := os.Rename(path, filepath.Join(dir, newFname))
} if err != nil {
app.err.Fatalf("Failed to rename %s: %s", fname, err)
} }
} }
app.info.Println("In case of a problem, your original files have been renamed to <file>.old.json")
app.storage.storeProfiles()
} }
app.info.Println("In case of a problem, your original files have been renamed to <file>.old.json")
app.storage.storeProfiles()
} }
// Migrate pre-0.2.5 bootstrap theme choice to a17t version. // Migrate pre-0.2.5 bootstrap theme choice to a17t version.
@ -131,7 +133,7 @@ func migrateNotificationMethods(app *appContext) error {
return nil return nil
} }
changes := false changes := false
for code, invite := range app.storage.invites { for code, invite := range app.storage.deprecatedInvites {
if invite.Notify == nil { if invite.Notify == nil {
continue continue
} }
@ -149,7 +151,7 @@ func migrateNotificationMethods(app *appContext) error {
} }
} }
if changes { if changes {
app.storage.invites[code] = invite app.storage.deprecatedInvites[code] = invite
} }
} }
if changes { if changes {
@ -195,31 +197,45 @@ func linkExistingOmbiDiscordTelegram(app *appContext) error {
} }
func migrateToBadger(app *appContext) { func migrateToBadger(app *appContext) {
if app.config.Section("").Key("migrated_to_db").MustBool(false) {
return
// FIXME: Mark as done at some point
}
app.info.Println("Migrating to Badger(hold)") app.info.Println("Migrating to Badger(hold)")
app.storage.loadAnnouncements() app.storage.loadAnnouncements()
for k, v := range app.storage.announcements { for k, v := range app.storage.deprecatedAnnouncements {
app.storage.SetAnnouncementsKey(k, v) app.storage.SetAnnouncementsKey(k, v)
} }
app.storage.loadDiscordUsers() app.storage.loadDiscordUsers()
for jfID, v := range app.storage.discord { for jfID, v := range app.storage.deprecatedDiscord {
app.storage.SetDiscordKey(jfID, v) app.storage.SetDiscordKey(jfID, v)
} }
app.storage.loadTelegramUsers() app.storage.loadTelegramUsers()
for jfID, v := range app.storage.telegram { for jfID, v := range app.storage.deprecatedTelegram {
app.storage.SetTelegramKey(jfID, v) app.storage.SetTelegramKey(jfID, v)
} }
app.storage.loadMatrixUsers() app.storage.loadMatrixUsers()
for jfID, v := range app.storage.matrix { for jfID, v := range app.storage.deprecatedMatrix {
app.storage.SetMatrixKey(jfID, v) app.storage.SetMatrixKey(jfID, v)
} }
app.storage.loadEmails() app.storage.loadEmails()
for jfID, v := range app.storage.emails { for jfID, v := range app.storage.deprecatedEmails {
app.storage.SetEmailsKey(jfID, v) app.storage.SetEmailsKey(jfID, v)
} }
app.storage.loadInvites()
for k, v := range app.storage.deprecatedInvites {
app.storage.SetInvitesKey(k, v)
}
app.storage.loadUserExpiries()
for k, v := range app.storage.deprecatedUserExpiries {
app.storage.SetUserExpiryKey(k, UserExpiry{Expiry: v})
}
} }
// Migrate between hyphenated & non-hyphenated user IDs. Doesn't seem to happen anymore, so disabled. // Migrate between hyphenated & non-hyphenated user IDs. Doesn't seem to happen anymore, so disabled.
@ -283,7 +299,7 @@ func migrateToBadger(app *appContext) {
// app.storage.emails = newEmails // app.storage.emails = newEmails
// app.storage.users = newUsers // app.storage.users = newUsers
// err = app.storage.storeEmails() // err = app.storage.storeEmails()
// err2 = app.storage.storeUsers() // err2 = app.storage.storeUserExpiries()
// if err != nil { // if err != nil {
// app.err.Fatalf("couldn't store emails.json: %v", err) // app.err.Fatalf("couldn't store emails.json: %v", err)
// } // }

@ -8,7 +8,6 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/hrfee/mediabrowser" "github.com/hrfee/mediabrowser"
@ -21,6 +20,11 @@ type telegramStore map[string]TelegramUser
type matrixStore map[string]MatrixUser type matrixStore map[string]MatrixUser
type emailStore map[string]EmailAddress type emailStore map[string]EmailAddress
type UserExpiry struct {
JellyfinID string `badgerhold:"key"`
Expiry time.Time
}
type Storage struct { type Storage struct {
timePattern string timePattern string
@ -28,22 +32,21 @@ type Storage struct {
db *badgerhold.Store db *badgerhold.Store
invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path, profiles_path, customEmails_path, users_path, telegram_path, discord_path, matrix_path, announcements_path, matrix_sql_path, userPage_path string invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path, profiles_path, customEmails_path, users_path, telegram_path, discord_path, matrix_path, announcements_path, matrix_sql_path, userPage_path string
users map[string]time.Time // Map of Jellyfin User IDs to their expiry times. deprecatedUserExpiries map[string]time.Time // Map of Jellyfin User IDs to their expiry times.
invites Invites deprecatedInvites Invites
profiles map[string]Profile profiles map[string]Profile
defaultProfile string defaultProfile string
displayprefs, ombi_template map[string]interface{} displayprefs, ombi_template map[string]interface{}
emails emailStore // Map of Jellyfin User IDs to Email addresses. deprecatedEmails emailStore // Map of Jellyfin User IDs to Email addresses.
telegram telegramStore // Map of Jellyfin User IDs to telegram users. deprecatedTelegram telegramStore // Map of Jellyfin User IDs to telegram users.
discord discordStore // Map of Jellyfin user IDs to discord users. deprecatedDiscord discordStore // Map of Jellyfin user IDs to discord users.
matrix matrixStore // Map of Jellyfin user IDs to Matrix users. deprecatedMatrix matrixStore // Map of Jellyfin user IDs to Matrix users.
customEmails customEmails customEmails customEmails
userPage userPageContent userPage userPageContent
policy mediabrowser.Policy policy mediabrowser.Policy
configuration mediabrowser.Configuration configuration mediabrowser.Configuration
lang Lang lang Lang
announcements map[string]announcementTemplate deprecatedAnnouncements map[string]announcementTemplate
invitesLock, usersLock, discordLock, telegramLock, matrixLock, emailsLock sync.Mutex
} }
func (app *appContext) ConnectDB() { func (app *appContext) ConnectDB() {
@ -203,36 +206,39 @@ func (st *Storage) DeleteMatrixKey(k string) {
} }
// GetInvites returns a copy of the store. // GetInvites returns a copy of the store.
func (st *Storage) GetInvites() Invites { func (st *Storage) GetInvites() []Invite {
if st.invites == nil { result := []Invite{}
st.invites = Invites{} err := st.db.Find(&result, &badgerhold.Query{})
if err != nil {
// fmt.Printf("Failed to find invites: %v\n", err)
} }
return st.invites return result
} }
// GetInvitesKey returns the value stored in the store's key. // GetInvitesKey returns the value stored in the store's key.
func (st *Storage) GetInvitesKey(k string) (Invite, bool) { func (st *Storage) GetInvitesKey(k string) (Invite, bool) {
v, ok := st.invites[k] result := Invite{}
return v, ok err := st.db.Get(k, &result)
ok := true
if err != nil {
// fmt.Printf("Failed to find invite: %v\n", err)
ok = false
}
return result, ok
} }
// SetInvitesKey stores value v in key k. // SetInvitesKey stores value v in key k.
func (st *Storage) SetInvitesKey(k string, v Invite) { func (st *Storage) SetInvitesKey(k string, v Invite) {
st.invitesLock.Lock() v.Code = k
if st.invites == nil { err := st.db.Upsert(k, v)
st.invites = Invites{} if err != nil {
// fmt.Printf("Failed to set invite: %v\n", err)
} }
st.invites[k] = v
st.storeInvites()
st.invitesLock.Unlock()
} }
// DeleteInvitesKey deletes value at key k. // DeleteInvitesKey deletes value at key k.
func (st *Storage) DeleteInvitesKey(k string) { func (st *Storage) DeleteInvitesKey(k string) {
st.invitesLock.Lock() st.db.Delete(k, Invite{})
delete(st.invites, k)
st.storeInvites()
st.invitesLock.Unlock()
} }
// GetAnnouncements returns a copy of the store. // GetAnnouncements returns a copy of the store.
@ -270,6 +276,42 @@ func (st *Storage) DeleteAnnouncementsKey(k string) {
st.db.Delete(k, announcementTemplate{}) st.db.Delete(k, announcementTemplate{})
} }
// GetUserExpiries returns a copy of the store.
func (st *Storage) GetUserExpiries() []UserExpiry {
result := []UserExpiry{}
err := st.db.Find(&result, &badgerhold.Query{})
if err != nil {
// fmt.Printf("Failed to find expiries: %v\n", err)
}
return result
}
// GetUserExpiryKey returns the value stored in the store's key.
func (st *Storage) GetUserExpiryKey(k string) (UserExpiry, bool) {
result := UserExpiry{}
err := st.db.Get(k, &result)
ok := true
if err != nil {
// fmt.Printf("Failed to find expiry: %v\n", err)
ok = false
}
return result, ok
}
// SetUserExpiryKey stores value v in key k.
func (st *Storage) SetUserExpiryKey(k string, v UserExpiry) {
v.JellyfinID = k
err := st.db.Upsert(k, v)
if err != nil {
// fmt.Printf("Failed to set expiry: %v\n", err)
}
}
// DeleteUserExpiryKey deletes value at key k.
func (st *Storage) DeleteUserExpiryKey(k string) {
st.db.Delete(k, UserExpiry{})
}
type TelegramUser struct { type TelegramUser struct {
JellyfinID string `badgerhold:"key"` JellyfinID string `badgerhold:"key"`
ChatID int64 `badgerhold:"index"` ChatID int64 `badgerhold:"index"`
@ -335,6 +377,7 @@ type Profile struct {
} }
type Invite struct { type Invite struct {
Code string `badgerhold:"key"`
Created time.Time `json:"created"` Created time.Time `json:"created"`
NoLimit bool `json:"no-limit"` NoLimit bool `json:"no-limit"`
RemainingUses int `json:"remaining-uses"` RemainingUses int `json:"remaining-uses"`
@ -1036,18 +1079,16 @@ func (st *Storage) loadLangTelegram(filesystems ...fs.FS) error {
type Invites map[string]Invite type Invites map[string]Invite
func (st *Storage) loadInvites() error { func (st *Storage) loadInvites() error {
return loadJSON(st.invite_path, &st.invites) return loadJSON(st.invite_path, &st.deprecatedInvites)
} }
func (st *Storage) storeInvites() error { func (st *Storage) storeInvites() error {
return storeJSON(st.invite_path, st.invites) return storeJSON(st.invite_path, st.deprecatedInvites)
} }
func (st *Storage) loadUsers() error { func (st *Storage) loadUserExpiries() error {
st.usersLock.Lock() if st.deprecatedUserExpiries == nil {
defer st.usersLock.Unlock() st.deprecatedUserExpiries = map[string]time.Time{}
if st.users == nil {
st.users = map[string]time.Time{}
} }
temp := map[string]time.Time{} temp := map[string]time.Time{}
err := loadJSON(st.users_path, &temp) err := loadJSON(st.users_path, &temp)
@ -1055,47 +1096,47 @@ func (st *Storage) loadUsers() error {
return err return err
} }
for id, t1 := range temp { for id, t1 := range temp {
if _, ok := st.users[id]; !ok { if _, ok := st.deprecatedUserExpiries[id]; !ok {
st.users[id] = t1 st.deprecatedUserExpiries[id] = t1
} }
} }
return nil return nil
} }
func (st *Storage) storeUsers() error { func (st *Storage) storeUserExpiries() error {
return storeJSON(st.users_path, st.users) return storeJSON(st.users_path, st.deprecatedUserExpiries)
} }
func (st *Storage) loadEmails() error { func (st *Storage) loadEmails() error {
return loadJSON(st.emails_path, &st.emails) return loadJSON(st.emails_path, &st.deprecatedEmails)
} }
func (st *Storage) storeEmails() error { func (st *Storage) storeEmails() error {
return storeJSON(st.emails_path, st.emails) return storeJSON(st.emails_path, st.deprecatedEmails)
} }
func (st *Storage) loadTelegramUsers() error { func (st *Storage) loadTelegramUsers() error {
return loadJSON(st.telegram_path, &st.telegram) return loadJSON(st.telegram_path, &st.deprecatedTelegram)
} }
func (st *Storage) storeTelegramUsers() error { func (st *Storage) storeTelegramUsers() error {
return storeJSON(st.telegram_path, st.telegram) return storeJSON(st.telegram_path, st.deprecatedTelegram)
} }
func (st *Storage) loadDiscordUsers() error { func (st *Storage) loadDiscordUsers() error {
return loadJSON(st.discord_path, &st.discord) return loadJSON(st.discord_path, &st.deprecatedDiscord)
} }
func (st *Storage) storeDiscordUsers() error { func (st *Storage) storeDiscordUsers() error {
return storeJSON(st.discord_path, st.discord) return storeJSON(st.discord_path, st.deprecatedDiscord)
} }
func (st *Storage) loadMatrixUsers() error { func (st *Storage) loadMatrixUsers() error {
return loadJSON(st.matrix_path, &st.matrix) return loadJSON(st.matrix_path, &st.deprecatedMatrix)
} }
func (st *Storage) storeMatrixUsers() error { func (st *Storage) storeMatrixUsers() error {
return storeJSON(st.matrix_path, st.matrix) return storeJSON(st.matrix_path, st.deprecatedMatrix)
} }
func (st *Storage) loadCustomEmails() error { func (st *Storage) loadCustomEmails() error {
@ -1147,11 +1188,11 @@ func (st *Storage) storeOmbiTemplate() error {
} }
func (st *Storage) loadAnnouncements() error { func (st *Storage) loadAnnouncements() error {
return loadJSON(st.announcements_path, &st.announcements) return loadJSON(st.announcements_path, &st.deprecatedAnnouncements)
} }
func (st *Storage) storeAnnouncements() error { func (st *Storage) storeAnnouncements() error {
return storeJSON(st.announcements_path, st.announcements) return storeJSON(st.announcements_path, st.deprecatedAnnouncements)
} }
func (st *Storage) loadProfiles() error { func (st *Storage) loadProfiles() error {

@ -221,9 +221,6 @@ func (t *TelegramDaemon) commandLang(upd *tg.Update, sects []string, lang string
if user.ChatID == upd.Message.Chat.ID { if user.ChatID == upd.Message.Chat.ID {
user.Lang = sects[1] user.Lang = sects[1]
t.app.storage.SetTelegramKey(user.JellyfinID, user) t.app.storage.SetTelegramKey(user.JellyfinID, user)
if err := t.app.storage.storeTelegramUsers(); err != nil {
t.app.err.Printf("Failed to store Telegram users: %v", err)
}
break break
} }
} }

@ -50,13 +50,7 @@ func (rt *userDaemon) shutdown() {
} }
func (app *appContext) checkUsers() { func (app *appContext) checkUsers() {
if err := app.storage.loadUsers(); err != nil { if len(app.storage.GetUserExpiries()) == 0 {
app.err.Printf("Failed to load user expiries: %v", err)
return
}
app.storage.usersLock.Lock()
defer app.storage.usersLock.Unlock()
if len(app.storage.users) == 0 {
return return
} }
app.info.Println("Daemon: Checking for user expiry") app.info.Println("Daemon: Checking for user expiry")
@ -80,11 +74,12 @@ func (app *appContext) checkUsers() {
for _, user := range users { for _, user := range users {
userExists[user.ID] = true userExists[user.ID] = true
} }
for id, expiry := range app.storage.users { for _, expiry := range app.storage.GetUserExpiries() {
id := expiry.JellyfinID
if _, ok := userExists[id]; !ok { if _, ok := userExists[id]; !ok {
app.info.Printf("Deleting expiry for non-existent user \"%s\"", id) app.info.Printf("Deleting expiry for non-existent user \"%s\"", id)
delete(app.storage.users, id) app.storage.DeleteUserExpiryKey(expiry.JellyfinID)
} else if time.Now().After(expiry) { } else if time.Now().After(expiry.Expiry) {
found := false found := false
var user mediabrowser.User var user mediabrowser.User
for _, u := range users { for _, u := range users {
@ -96,7 +91,7 @@ func (app *appContext) checkUsers() {
} }
if !found { if !found {
app.info.Printf("Expired user already deleted, ignoring.") app.info.Printf("Expired user already deleted, ignoring.")
delete(app.storage.users, id) app.storage.DeleteUserExpiryKey(expiry.JellyfinID)
continue continue
} }
app.info.Printf("%s expired user \"%s\"", termPlural, user.Name) app.info.Printf("%s expired user \"%s\"", termPlural, user.Name)
@ -112,7 +107,7 @@ func (app *appContext) checkUsers() {
app.err.Printf("Failed to %s \"%s\" (%d): %s", mode, user.Name, status, err) app.err.Printf("Failed to %s \"%s\" (%d): %s", mode, user.Name, status, err)
continue continue
} }
delete(app.storage.users, id) app.storage.DeleteUserExpiryKey(expiry.JellyfinID)
app.jf.CacheExpiry = time.Now() app.jf.CacheExpiry = time.Now()
if contact { if contact {
if !ok { if !ok {
@ -130,8 +125,4 @@ func (app *appContext) checkUsers() {
} }
} }
} }
err = app.storage.storeUsers()
if err != nil {
app.err.Printf("Failed to store user expiries: %s", err)
}
} }

Loading…
Cancel
Save