From 63948a6de0283dbdc0610d8a93e5b2ba9ce029fb Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Sat, 24 Jun 2023 19:13:05 +0100 Subject: [PATCH] db: migrate invites, user expiry some fixes to stuff in there too, probably --- api-invites.go | 55 +++++++------------- api-messages.go | 27 +--------- api-userpage.go | 5 +- api-users.go | 61 ++++++---------------- main.go | 7 +-- migrations.go | 58 +++++++++++++-------- storage.go | 133 +++++++++++++++++++++++++++++++----------------- telegram.go | 3 -- userdaemon.go | 23 +++------ 9 files changed, 168 insertions(+), 204 deletions(-) diff --git a/api-invites.go b/api-invites.go index 268fdac..61e6772 100644 --- a/api-invites.go +++ b/api-invites.go @@ -15,16 +15,15 @@ import ( func (app *appContext) checkInvites() { currentTime := time.Now() app.storage.loadInvites() - changed := false - for code, data := range app.storage.GetInvites() { + for _, data := range app.storage.GetInvites() { expiry := data.ValidTill if !currentTime.After(expiry) { continue } - app.debug.Printf("Housekeeping: Deleting old invite %s", code) + app.debug.Printf("Housekeeping: Deleting old invite %s", data.Code) notify := data.Notify 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 for address, settings := range notify { if !settings["notify-expiry"] { @@ -33,9 +32,9 @@ func (app *appContext) checkInvites() { wait.Add(1) go func(addr string) { 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 { - 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 { // Check whether notify "address" is an email address of Jellyfin ID if strings.Contains(addr, "@") { @@ -44,7 +43,7 @@ func (app *appContext) checkInvites() { err = app.sendByID(msg, addr) } 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 { app.info.Printf("Sent expiry notification to %s", addr) } @@ -53,18 +52,13 @@ func (app *appContext) checkInvites() { } wait.Wait() } - changed = true - app.storage.DeleteInvitesKey(code) - } - if changed { - app.storage.storeInvites() + app.storage.DeleteInvitesKey(data.Code) } } func (app *appContext) checkInvite(code string, used bool, username string) bool { currentTime := time.Now() app.storage.loadInvites() - changed := false inv, match := app.storage.GetInvitesKey(code) if !match { return false @@ -103,11 +97,9 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool } wait.Wait() } - changed = true match = false app.storage.DeleteInvitesKey(code) } else if used { - changed = true del := false newInv := inv if newInv.RemainingUses == 1 { @@ -122,9 +114,6 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool app.storage.SetInvitesKey(code, newInv) } } - if changed { - app.storage.storeInvites() - } return match } @@ -220,7 +209,6 @@ func (app *appContext) GenerateInvite(gc *gin.Context) { } } app.storage.SetInvitesKey(inviteCode, invite) - app.storage.storeInvites() respondBool(200, true, gc) } @@ -236,10 +224,10 @@ func (app *appContext) GetInvites(gc *gin.Context) { app.storage.loadInvites() app.checkInvites() var invites []inviteDTO - for code, inv := range app.storage.GetInvites() { + for _, inv := range app.storage.GetInvites() { _, months, days, hours, minutes, _ := timeDiff(inv.ValidTill, currentTime) invite := inviteDTO{ - Code: code, + Code: inv.Code, Months: months, Days: days, Hours: hours, @@ -277,21 +265,19 @@ func (app *appContext) GetInvites(gc *gin.Context) { invite.SendTo = inv.SendTo } 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) { - app.storage.loadEmails() - if addr, ok := app.storage.GetEmailsKey(gc.GetString("jfId")); ok && addr.Addr != "" { - address = addr.Addr - } + addressOrID = gc.GetString("jfId") } 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[address]["notify-expiry"]; ok { - invite.NotifyExpiry = inv.Notify[address]["notify-expiry"] + if _, ok := inv.Notify[addressOrID]; ok { + if _, ok = inv.Notify[addressOrID]["notify-expiry"]; ok { + invite.NotifyExpiry = inv.Notify[addressOrID]["notify-expiry"] } - if _, ok = inv.Notify[address]["notify-creation"]; ok { - invite.NotifyCreation = inv.Notify[address]["notify-creation"] + if _, ok = inv.Notify[addressOrID]["notify-creation"]; ok { + 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.Profile = req.Profile app.storage.SetInvitesKey(req.Invite, inv) - app.storage.storeInvites() respondBool(200, true, gc) } @@ -401,9 +386,6 @@ func (app *appContext) SetNotify(gc *gin.Context) { app.storage.SetInvitesKey(code, invite) } } - if changed { - app.storage.storeInvites() - } } // @Summary Delete an invite. @@ -422,7 +404,6 @@ func (app *appContext) DeleteInvite(gc *gin.Context) { _, ok = app.storage.GetInvitesKey(req.Code) if ok { app.storage.DeleteInvitesKey(req.Code) - app.storage.storeInvites() app.info.Printf("%s: Invite deleted", req.Code) respondBool(200, true, gc) return diff --git a/api-messages.go b/api-messages.go index c571eb7..b4ea494 100644 --- a/api-messages.go +++ b/api-messages.go @@ -387,11 +387,6 @@ func (app *appContext) setContactMethods(req SetContactMethodsDTO, gc *gin.Conte change := dcUser.Contact != req.Discord dcUser.Contact = req.Discord 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 { msg := "" if !req.Discord { @@ -404,11 +399,6 @@ func (app *appContext) setContactMethods(req SetContactMethodsDTO, gc *gin.Conte change := mxUser.Contact != req.Matrix mxUser.Contact = req.Matrix 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 { msg := "" if !req.Matrix { @@ -421,11 +411,6 @@ func (app *appContext) setContactMethods(req SetContactMethodsDTO, gc *gin.Conte change := email.Contact != req.Email email.Contact = req.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 { msg := "" if !req.Email { @@ -646,7 +631,7 @@ func (app *appContext) MatrixConnect(gc *gin.Context) { var req MatrixConnectUserDTO gc.BindJSON(&req) if app.storage.GetMatrix() == nil { - app.storage.matrix = matrixStore{} + app.storage.deprecatedMatrix = matrixStore{} } roomID, encrypted, err := app.matrix.CreateRoom(req.UserID) if err != nil { @@ -662,11 +647,6 @@ func (app *appContext) MatrixConnect(gc *gin.Context) { Encrypted: 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) } @@ -717,11 +697,6 @@ func (app *appContext) DiscordConnect(gc *gin.Context) { return } 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) respondBool(200, true, gc) } diff --git a/api-userpage.go b/api-userpage.go index c2b65f1..780a4ee 100644 --- a/api-userpage.go +++ b/api-userpage.go @@ -38,8 +38,8 @@ func (app *appContext) MyDetails(gc *gin.Context) { } resp.Disabled = user.Policy.IsDisabled - if exp, ok := app.storage.users[user.ID]; ok { - resp.Expiry = exp.Unix() + if exp, ok := app.storage.GetUserExpiryKey(user.ID); ok { + resp.Expiry = exp.Expiry.Unix() } 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") gc.Redirect(http.StatusSeeOther, "/my/account") return diff --git a/api-users.go b/api-users.go index a09273c..71d7b30 100644 --- a/api-users.go +++ b/api-users.go @@ -62,7 +62,6 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) { app.jf.CacheExpiry = time.Now() if emailEnabled { app.storage.SetEmailsKey(id, EmailAddress{Addr: req.Email, Contact: true}) - app.storage.storeEmails() } if app.config.Section("ombi").Key("enabled").MustBool(false) { 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 req.Email != "" { app.storage.SetEmailsKey(id, EmailAddress{Addr: req.Email, Contact: true}) - app.storage.storeEmails() } expiry := time.Time{} 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) - app.storage.users[id] = expiry - if err := app.storage.storeUsers(); err != nil { - app.err.Printf("Failed to store user duration: %v", err) - } + app.storage.SetUserExpiryKey(id, UserExpiry{Expiry: expiry}) } if discordVerified { discordUser.Contact = req.DiscordContact - if app.storage.discord == nil { - app.storage.discord = discordStore{} + if app.storage.deprecatedDiscord == nil { + app.storage.deprecatedDiscord = discordStore{} } app.storage.SetDiscordKey(user.ID, discordUser) - if err := app.storage.storeDiscordUsers(); err != nil { - app.err.Printf("Failed to store Discord users: %v", err) - } else { - delete(app.discord.verifiedTokens, req.DiscordPIN) - } + delete(app.discord.verifiedTokens, req.DiscordPIN) } if telegramVerified { 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 { tgUser.Lang = lang } - if app.storage.telegram == nil { - app.storage.telegram = telegramStore{} + if app.storage.deprecatedTelegram == nil { + app.storage.deprecatedTelegram = telegramStore{} } app.telegram.DeleteVerifiedToken(req.TelegramPIN) app.storage.SetTelegramKey(user.ID, tgUser) @@ -404,13 +393,10 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc if matrixVerified { matrixUser.Contact = req.MatrixContact delete(app.matrix.tokens, req.MatrixPIN) - if app.storage.matrix == nil { - app.storage.matrix = matrixStore{} + if app.storage.deprecatedMatrix == nil { + app.storage.deprecatedMatrix = matrixStore{} } 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 { name := app.getAddressOrName(user.ID) @@ -629,21 +615,16 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) { respondBool(400, false, gc) return } - app.storage.usersLock.Lock() - defer app.storage.usersLock.Unlock() for _, id := range req.Users { - if expiry, ok := app.storage.users[id]; ok { - app.storage.users[id] = expiry.AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute) + base := time.Now() + if expiry, ok := app.storage.GetUserExpiryKey(id); ok { + base = expiry.Expiry app.debug.Printf("Expiry extended for \"%s\"", id) } 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) } - } - if err := app.storage.storeUsers(); err != nil { - app.err.Printf("Failed to store user duration: %v", err) - respondBool(500, false, gc) - return + expiry := UserExpiry{Expiry: base.AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute)} + app.storage.SetUserExpiryKey(id, expiry) } 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) allowAll := app.config.Section("ui").Key("allow_all").MustBool(false) i := 0 - app.storage.usersLock.Lock() - defer app.storage.usersLock.Unlock() for _, jfUser := range users { user := respUser{ ID: jfUser.ID, @@ -871,9 +850,9 @@ func (app *appContext) GetUsers(gc *gin.Context) { user.Label = email.Label 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 { - user.Expiry = expiry.Unix() + user.Expiry = expiry.Expiry.Unix() } if tgUser, ok := app.storage.GetTelegramKey(jfUser.ID); ok { user.Telegram = tgUser.Username @@ -924,10 +903,6 @@ func (app *appContext) SetAccountsAdmin(gc *gin.Context) { 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") respondBool(204, true, gc) } @@ -961,10 +936,6 @@ func (app *appContext) ModifyLabels(gc *gin.Context) { 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") respondBool(204, true, gc) } @@ -999,7 +970,6 @@ func (app *appContext) ModifyEmails(gc *gin.Context) { // Auto enable contact by email for newly added addresses if !ok || oldEmail.Addr == "" { emailStore.Contact = true - app.storage.storeEmails() } emailStore.Addr = address @@ -1016,7 +986,6 @@ func (app *appContext) ModifyEmails(gc *gin.Context) { } } } - app.storage.storeEmails() app.info.Println("Email list modified") respondBool(200, true, gc) } diff --git a/main.go b/main.go index 94961b1..955ddbd 100644 --- a/main.go +++ b/main.go @@ -362,7 +362,7 @@ func start(asDaemon, firstCall bool) { app.err.Printf("Failed to load Displayprefs: %v", err) } 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.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.ConnectDB() 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. app.configBasePath = "config-base.json" configBase, _ := fs.ReadFile(localFS, app.configBasePath) diff --git a/migrations.go b/migrations.go index 7ce4a6e..2a476da 100644 --- a/migrations.go +++ b/migrations.go @@ -16,26 +16,28 @@ func runMigrations(app *appContext) { migrateNotificationMethods(app) linkExistingOmbiDiscordTelegram(app) // migrateHyphens(app) + migrateToBadger(app) } // Migrate pre-0.2.0 user templates to profiles func migrateProfiles(app *appContext) { - 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") - app.storage.migrateToProfile() - for _, path := range [3]string{app.storage.policy_path, app.storage.configuration_path, app.storage.displayprefs_path} { - if _, err := os.Stat(path); !os.IsNotExist(err) { - dir, fname := filepath.Split(path) - newFname := strings.Replace(fname, ".json", ".old.json", 1) - err := os.Rename(path, filepath.Join(dir, newFname)) - if err != nil { - app.err.Fatalf("Failed to rename %s: %s", fname, err) - } + if app.storage.policy.BlockedTags == nil && app.storage.configuration.GroupedFolders == nil && len(app.storage.displayprefs) == 0 { + return + } + app.info.Println("Migrating user template files to new profile format") + app.storage.migrateToProfile() + for _, path := range [3]string{app.storage.policy_path, app.storage.configuration_path, app.storage.displayprefs_path} { + if _, err := os.Stat(path); !os.IsNotExist(err) { + dir, fname := filepath.Split(path) + newFname := strings.Replace(fname, ".json", ".old.json", 1) + 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 .old.json") - app.storage.storeProfiles() } + app.info.Println("In case of a problem, your original files have been renamed to .old.json") + app.storage.storeProfiles() } // Migrate pre-0.2.5 bootstrap theme choice to a17t version. @@ -131,7 +133,7 @@ func migrateNotificationMethods(app *appContext) error { return nil } changes := false - for code, invite := range app.storage.invites { + for code, invite := range app.storage.deprecatedInvites { if invite.Notify == nil { continue } @@ -149,7 +151,7 @@ func migrateNotificationMethods(app *appContext) error { } } if changes { - app.storage.invites[code] = invite + app.storage.deprecatedInvites[code] = invite } } if changes { @@ -195,31 +197,45 @@ func linkExistingOmbiDiscordTelegram(app *appContext) error { } 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.storage.loadAnnouncements() - for k, v := range app.storage.announcements { + for k, v := range app.storage.deprecatedAnnouncements { app.storage.SetAnnouncementsKey(k, v) } app.storage.loadDiscordUsers() - for jfID, v := range app.storage.discord { + for jfID, v := range app.storage.deprecatedDiscord { app.storage.SetDiscordKey(jfID, v) } app.storage.loadTelegramUsers() - for jfID, v := range app.storage.telegram { + for jfID, v := range app.storage.deprecatedTelegram { app.storage.SetTelegramKey(jfID, v) } app.storage.loadMatrixUsers() - for jfID, v := range app.storage.matrix { + for jfID, v := range app.storage.deprecatedMatrix { app.storage.SetMatrixKey(jfID, v) } app.storage.loadEmails() - for jfID, v := range app.storage.emails { + for jfID, v := range app.storage.deprecatedEmails { 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. @@ -283,7 +299,7 @@ func migrateToBadger(app *appContext) { // app.storage.emails = newEmails // app.storage.users = newUsers // err = app.storage.storeEmails() -// err2 = app.storage.storeUsers() +// err2 = app.storage.storeUserExpiries() // if err != nil { // app.err.Fatalf("couldn't store emails.json: %v", err) // } diff --git a/storage.go b/storage.go index ba31b40..503dafd 100644 --- a/storage.go +++ b/storage.go @@ -8,7 +8,6 @@ import ( "path/filepath" "strconv" "strings" - "sync" "time" "github.com/hrfee/mediabrowser" @@ -21,6 +20,11 @@ type telegramStore map[string]TelegramUser type matrixStore map[string]MatrixUser type emailStore map[string]EmailAddress +type UserExpiry struct { + JellyfinID string `badgerhold:"key"` + Expiry time.Time +} + type Storage struct { timePattern string @@ -28,22 +32,21 @@ type Storage struct { 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 - users map[string]time.Time // Map of Jellyfin User IDs to their expiry times. - invites Invites + deprecatedUserExpiries map[string]time.Time // Map of Jellyfin User IDs to their expiry times. + deprecatedInvites Invites profiles map[string]Profile defaultProfile string displayprefs, ombi_template map[string]interface{} - emails emailStore // Map of Jellyfin User IDs to Email addresses. - telegram telegramStore // Map of Jellyfin User IDs to telegram users. - discord discordStore // Map of Jellyfin user IDs to discord users. - matrix matrixStore // Map of Jellyfin user IDs to Matrix users. + deprecatedEmails emailStore // Map of Jellyfin User IDs to Email addresses. + deprecatedTelegram telegramStore // Map of Jellyfin User IDs to telegram users. + deprecatedDiscord discordStore // Map of Jellyfin user IDs to discord users. + deprecatedMatrix matrixStore // Map of Jellyfin user IDs to Matrix users. customEmails customEmails userPage userPageContent policy mediabrowser.Policy configuration mediabrowser.Configuration lang Lang - announcements map[string]announcementTemplate - invitesLock, usersLock, discordLock, telegramLock, matrixLock, emailsLock sync.Mutex + deprecatedAnnouncements map[string]announcementTemplate } func (app *appContext) ConnectDB() { @@ -203,36 +206,39 @@ func (st *Storage) DeleteMatrixKey(k string) { } // GetInvites returns a copy of the store. -func (st *Storage) GetInvites() Invites { - if st.invites == nil { - st.invites = Invites{} +func (st *Storage) GetInvites() []Invite { + result := []Invite{} + 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. func (st *Storage) GetInvitesKey(k string) (Invite, bool) { - v, ok := st.invites[k] - return v, ok + result := Invite{} + 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. func (st *Storage) SetInvitesKey(k string, v Invite) { - st.invitesLock.Lock() - if st.invites == nil { - st.invites = Invites{} + v.Code = k + err := st.db.Upsert(k, v) + 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. func (st *Storage) DeleteInvitesKey(k string) { - st.invitesLock.Lock() - delete(st.invites, k) - st.storeInvites() - st.invitesLock.Unlock() + st.db.Delete(k, Invite{}) } // GetAnnouncements returns a copy of the store. @@ -270,6 +276,42 @@ func (st *Storage) DeleteAnnouncementsKey(k string) { 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 { JellyfinID string `badgerhold:"key"` ChatID int64 `badgerhold:"index"` @@ -335,6 +377,7 @@ type Profile struct { } type Invite struct { + Code string `badgerhold:"key"` Created time.Time `json:"created"` NoLimit bool `json:"no-limit"` RemainingUses int `json:"remaining-uses"` @@ -1036,18 +1079,16 @@ func (st *Storage) loadLangTelegram(filesystems ...fs.FS) error { type Invites map[string]Invite func (st *Storage) loadInvites() error { - return loadJSON(st.invite_path, &st.invites) + return loadJSON(st.invite_path, &st.deprecatedInvites) } func (st *Storage) storeInvites() error { - return storeJSON(st.invite_path, st.invites) + return storeJSON(st.invite_path, st.deprecatedInvites) } -func (st *Storage) loadUsers() error { - st.usersLock.Lock() - defer st.usersLock.Unlock() - if st.users == nil { - st.users = map[string]time.Time{} +func (st *Storage) loadUserExpiries() error { + if st.deprecatedUserExpiries == nil { + st.deprecatedUserExpiries = map[string]time.Time{} } temp := map[string]time.Time{} err := loadJSON(st.users_path, &temp) @@ -1055,47 +1096,47 @@ func (st *Storage) loadUsers() error { return err } for id, t1 := range temp { - if _, ok := st.users[id]; !ok { - st.users[id] = t1 + if _, ok := st.deprecatedUserExpiries[id]; !ok { + st.deprecatedUserExpiries[id] = t1 } } return nil } -func (st *Storage) storeUsers() error { - return storeJSON(st.users_path, st.users) +func (st *Storage) storeUserExpiries() error { + return storeJSON(st.users_path, st.deprecatedUserExpiries) } func (st *Storage) loadEmails() error { - return loadJSON(st.emails_path, &st.emails) + return loadJSON(st.emails_path, &st.deprecatedEmails) } func (st *Storage) storeEmails() error { - return storeJSON(st.emails_path, st.emails) + return storeJSON(st.emails_path, st.deprecatedEmails) } func (st *Storage) loadTelegramUsers() error { - return loadJSON(st.telegram_path, &st.telegram) + return loadJSON(st.telegram_path, &st.deprecatedTelegram) } func (st *Storage) storeTelegramUsers() error { - return storeJSON(st.telegram_path, st.telegram) + return storeJSON(st.telegram_path, st.deprecatedTelegram) } func (st *Storage) loadDiscordUsers() error { - return loadJSON(st.discord_path, &st.discord) + return loadJSON(st.discord_path, &st.deprecatedDiscord) } func (st *Storage) storeDiscordUsers() error { - return storeJSON(st.discord_path, st.discord) + return storeJSON(st.discord_path, st.deprecatedDiscord) } func (st *Storage) loadMatrixUsers() error { - return loadJSON(st.matrix_path, &st.matrix) + return loadJSON(st.matrix_path, &st.deprecatedMatrix) } func (st *Storage) storeMatrixUsers() error { - return storeJSON(st.matrix_path, st.matrix) + return storeJSON(st.matrix_path, st.deprecatedMatrix) } func (st *Storage) loadCustomEmails() error { @@ -1147,11 +1188,11 @@ func (st *Storage) storeOmbiTemplate() 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 { - return storeJSON(st.announcements_path, st.announcements) + return storeJSON(st.announcements_path, st.deprecatedAnnouncements) } func (st *Storage) loadProfiles() error { diff --git a/telegram.go b/telegram.go index ca5fd69..d5f5a67 100644 --- a/telegram.go +++ b/telegram.go @@ -221,9 +221,6 @@ func (t *TelegramDaemon) commandLang(upd *tg.Update, sects []string, lang string if user.ChatID == upd.Message.Chat.ID { user.Lang = sects[1] 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 } } diff --git a/userdaemon.go b/userdaemon.go index 4db091f..de0e0ce 100644 --- a/userdaemon.go +++ b/userdaemon.go @@ -50,13 +50,7 @@ func (rt *userDaemon) shutdown() { } func (app *appContext) checkUsers() { - if err := app.storage.loadUsers(); err != nil { - 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 { + if len(app.storage.GetUserExpiries()) == 0 { return } app.info.Println("Daemon: Checking for user expiry") @@ -80,11 +74,12 @@ func (app *appContext) checkUsers() { for _, user := range users { 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 { app.info.Printf("Deleting expiry for non-existent user \"%s\"", id) - delete(app.storage.users, id) - } else if time.Now().After(expiry) { + app.storage.DeleteUserExpiryKey(expiry.JellyfinID) + } else if time.Now().After(expiry.Expiry) { found := false var user mediabrowser.User for _, u := range users { @@ -96,7 +91,7 @@ func (app *appContext) checkUsers() { } if !found { app.info.Printf("Expired user already deleted, ignoring.") - delete(app.storage.users, id) + app.storage.DeleteUserExpiryKey(expiry.JellyfinID) continue } 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) continue } - delete(app.storage.users, id) + app.storage.DeleteUserExpiryKey(expiry.JellyfinID) app.jf.CacheExpiry = time.Now() if contact { 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) - } }