From 2310130e6b9a5fc476839fc937db576e5ae8d42a Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Tue, 6 Aug 2024 14:48:31 +0100 Subject: [PATCH] api clients: return data, error, no status jellyseerr already did this, but it's been standardised a little more. Mediabrowser uses it's own genericErr function and error types due to being a separate package, while jellyseerr and ombi now share errors defined in common/. --- Makefile | 2 +- api-activities.go | 6 +-- api-ombi.go | 74 ++++++++++++++++---------------- api-profiles.go | 8 ++-- api-userpage.go | 28 ++++++------- api-users.go | 82 +++++++++++++++++------------------- api.go | 31 +++++++------- auth.go | 11 ++--- common/common.go | 52 ++++++++++++++++++++++- common/go.mod | 6 ++- email.go | 21 +++++----- go.mod | 10 ++--- go.sum | 16 +++++-- housekeeping-d.go | 8 ++-- html/admin.html | 2 +- jellyseerr-d.go | 4 +- jellyseerr/jellyseerr.go | 86 ++++++++++---------------------------- logmessages/go.mod | 2 +- logmessages/logmessages.go | 10 ++++- main.go | 12 +++--- migrations.go | 12 +++--- ombi/go.mod | 2 + ombi/ombi.go | 43 +++++++++++-------- pwreset.go | 8 ++-- setup.go | 21 ++++++---- user-d.go | 6 +-- users.go | 41 +++++++----------- views.go | 31 +++++++------- 28 files changed, 327 insertions(+), 308 deletions(-) diff --git a/Makefile b/Makefile index 4aa6a08..fde2fe4 100644 --- a/Makefile +++ b/Makefile @@ -211,7 +211,7 @@ $(COPY_TARGET): $(INLINE_TARGET) $(STATIC_SRC) GO_SRC = $(shell find ./ -name "*.go") GO_TARGET = build/jfa-go -$(GO_TARGET): $(CONFIG_DESCRIPTION) $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET) $(GO_SRC) +$(GO_TARGET): $(CONFIG_DESCRIPTION) $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET) $(GO_SRC) go.mod go.sum $(info Downloading deps) $(GOBINARY) mod download $(info Building) diff --git a/api-activities.go b/api-activities.go index 8763f3d..0dc6634 100644 --- a/api-activities.go +++ b/api-activities.go @@ -144,13 +144,13 @@ func (app *appContext) GetActivities(gc *gin.Context) { if act.Type == ActivityDeletion || act.Type == ActivityCreation { resp.Activities[i].Username = act.Value resp.Activities[i].Value = "" - } else if user, status, err := app.jf.UserByID(act.UserID, false); status == 200 && err == nil { + } else if user, err := app.jf.UserByID(act.UserID, false); err == nil { resp.Activities[i].Username = user.Name } if (act.SourceType == ActivityUser || act.SourceType == ActivityAdmin) && act.Source != "" { - user, status, err := app.jf.UserByID(act.Source, false) - if status == 200 && err == nil { + user, err := app.jf.UserByID(act.Source, false) + if err == nil { resp.Activities[i].SourceUsername = user.Name } } diff --git a/api-ombi.go b/api-ombi.go index e38d172..4f5a2d1 100644 --- a/api-ombi.go +++ b/api-ombi.go @@ -1,34 +1,35 @@ package main import ( - "errors" "fmt" "net/url" "strings" "github.com/gin-gonic/gin" + "github.com/hrfee/jfa-go/common" lm "github.com/hrfee/jfa-go/logmessages" "github.com/hrfee/jfa-go/ombi" "github.com/hrfee/mediabrowser" ) -func (app *appContext) getOmbiUser(jfID string) (map[string]interface{}, int, error) { - jfUser, code, err := app.jf.UserByID(jfID, false) - if err != nil || code != 200 { - return nil, code, err +func (app *appContext) getOmbiUser(jfID string) (map[string]interface{}, error) { + jfUser, err := app.jf.UserByID(jfID, false) + if err != nil { + return nil, err } username := jfUser.Name email := "" if e, ok := app.storage.GetEmailsKey(jfID); ok { email = e.Addr } - return app.ombi.getUser(username, email) + user, err := app.ombi.getUser(username, email) + return user, err } -func (ombi *OmbiWrapper) getUser(username string, email string) (map[string]interface{}, int, error) { - ombiUsers, code, err := ombi.GetUsers() - if err != nil || code != 200 { - return nil, code, err +func (ombi *OmbiWrapper) getUser(username string, email string) (map[string]interface{}, error) { + ombiUsers, err := ombi.GetUsers() + if err != nil { + return nil, err } for _, ombiUser := range ombiUsers { ombiAddr := "" @@ -36,18 +37,19 @@ func (ombi *OmbiWrapper) getUser(username string, email string) (map[string]inte ombiAddr = a.(string) } if ombiUser["userName"].(string) == username || (ombiAddr == email && email != "") { - return ombiUser, code, err + return ombiUser, err } } - return nil, 400, errors.New(lm.NotFound) + // Gets a generic "not found" type error + return nil, common.GenericErr(404, err) } // Returns a user with the given name who has been imported from Jellyfin/Emby by Ombi -func (ombi *OmbiWrapper) getImportedUser(name string) (map[string]interface{}, int, error) { +func (ombi *OmbiWrapper) getImportedUser(name string) (map[string]interface{}, error) { // Ombi User Types: 3/4 = Emby, 5 = Jellyfin - ombiUsers, code, err := ombi.GetUsers() - if err != nil || code != 200 { - return nil, code, err + ombiUsers, err := ombi.GetUsers() + if err != nil { + return nil, err } for _, ombiUser := range ombiUsers { if ombiUser["userName"].(string) == name { @@ -60,10 +62,11 @@ func (ombi *OmbiWrapper) getImportedUser(name string) (map[string]interface{}, i } else if uType != 3 && uType != 4 { // Emby continue } - return ombiUser, code, err + return ombiUser, err } } - return nil, 400, fmt.Errorf("couldn't find user") + // Gets a generic "not found" type error + return nil, common.GenericErr(404, err) } // @Summary Get a list of Ombi users. @@ -74,8 +77,8 @@ func (ombi *OmbiWrapper) getImportedUser(name string) (map[string]interface{}, i // @Security Bearer // @tags Ombi func (app *appContext) OmbiUsers(gc *gin.Context) { - users, status, err := app.ombi.GetUsers() - if err != nil || status != 200 { + users, err := app.ombi.GetUsers() + if err != nil { app.err.Printf(lm.FailedGetUsers, lm.Ombi, err) respond(500, "Couldn't get users", gc) return @@ -110,8 +113,8 @@ func (app *appContext) SetOmbiProfile(gc *gin.Context) { respondBool(400, false, gc) return } - template, code, err := app.ombi.TemplateByID(req.ID) - if err != nil || code != 200 || len(template) == 0 { + template, err := app.ombi.TemplateByID(req.ID) + if err != nil || len(template) == 0 { app.err.Printf(lm.FailedGetUsers, lm.Ombi, err) respond(500, "Couldn't get user", gc) return @@ -147,7 +150,7 @@ type OmbiWrapper struct { *ombi.Ombi } -func (ombi *OmbiWrapper) applyProfile(user map[string]interface{}, profile map[string]interface{}) (status int, err error) { +func (ombi *OmbiWrapper) applyProfile(user map[string]interface{}, profile map[string]interface{}) (err error) { for k, v := range profile { switch v.(type) { case map[string]interface{}, []interface{}: @@ -158,22 +161,21 @@ func (ombi *OmbiWrapper) applyProfile(user map[string]interface{}, profile map[s } } } - status, err = ombi.ModifyUser(user) + err = ombi.ModifyUser(user) return } func (ombi *OmbiWrapper) ImportUser(jellyfinID string, req newUserDTO, profile Profile) (err error, ok bool) { - errors, code, err := ombi.NewUser(req.Username, req.Password, req.Email, profile.Ombi) + errors, err := ombi.NewUser(req.Username, req.Password, req.Email, profile.Ombi) var ombiUser map[string]interface{} - var status int - if err != nil || code != 200 { + if err != nil { // Check if on the off chance, Ombi's user importer has already added the account. - ombiUser, status, err = ombi.getImportedUser(req.Username) - if status == 200 && err == nil { + ombiUser, err = ombi.getImportedUser(req.Username) + if err == nil { // app.info.Println(lm.Ombi + " " + lm.UserExists) profile.Ombi["password"] = req.Password - status, err = ombi.applyProfile(ombiUser, profile.Ombi) - if status != 200 || err != nil { + err = ombi.applyProfile(ombiUser, profile.Ombi) + if err != nil { err = fmt.Errorf(lm.FailedApplyProfile, lm.Ombi, req.Username, err) } } else { @@ -189,9 +191,8 @@ func (ombi *OmbiWrapper) ImportUser(jellyfinID string, req newUserDTO, profile P func (ombi *OmbiWrapper) AddContactMethods(jellyfinID string, req newUserDTO, discord *DiscordUser, telegram *TelegramUser) (err error) { var ombiUser map[string]interface{} - var status int - ombiUser, status, err = ombi.getUser(req.Username, req.Email) - if status != 200 || err != nil { + ombiUser, err = ombi.getUser(req.Username, req.Email) + if err != nil { return } if discordEnabled || telegramEnabled { @@ -204,9 +205,8 @@ func (ombi *OmbiWrapper) AddContactMethods(jellyfinID string, req newUserDTO, di tUser = telegram.Username } var resp string - var status int - resp, status, err = ombi.SetNotificationPrefs(ombiUser, dID, tUser) - if !(status == 200 || status == 204) || err != nil { + resp, err = ombi.SetNotificationPrefs(ombiUser, dID, tUser) + if err != nil { if resp != "" { err = fmt.Errorf("%v, %s", err, resp) } diff --git a/api-profiles.go b/api-profiles.go index 40367da..0c7527f 100644 --- a/api-profiles.go +++ b/api-profiles.go @@ -84,8 +84,8 @@ func (app *appContext) CreateProfile(gc *gin.Context) { var req newProfileDTO gc.BindJSON(&req) app.jf.CacheExpiry = time.Now() - user, status, err := app.jf.UserByID(req.ID, false) - if !(status == 200 || status == 204) || err != nil { + user, err := app.jf.UserByID(req.ID, false) + if err != nil { app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err) respond(500, "Couldn't get user", gc) return @@ -98,8 +98,8 @@ func (app *appContext) CreateProfile(gc *gin.Context) { app.debug.Printf(lm.CreateProfileFromUser, user.Name) if req.Homescreen { profile.Configuration = user.Configuration - profile.Displayprefs, status, err = app.jf.GetDisplayPreferences(req.ID) - if !(status == 200 || status == 204) || err != nil { + profile.Displayprefs, err = app.jf.GetDisplayPreferences(req.ID) + if err != nil { app.err.Printf(lm.FailedGetJellyfinDisplayPrefs, req.ID, err) respond(500, "Couldn't get displayprefs", gc) return diff --git a/api-userpage.go b/api-userpage.go index c631a18..a7d9b77 100644 --- a/api-userpage.go +++ b/api-userpage.go @@ -29,8 +29,8 @@ func (app *appContext) MyDetails(gc *gin.Context) { Id: gc.GetString("jfId"), } - user, status, err := app.jf.UserByID(resp.Id, false) - if status != 200 || err != nil { + user, err := app.jf.UserByID(resp.Id, false) + if err != nil { app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err) respond(500, "Failed to get user", gc) return @@ -259,9 +259,9 @@ func (app *appContext) ModifyMyEmail(gc *gin.Context) { } if emailEnabled && app.config.Section("email_confirmation").Key("enabled").MustBool(false) { - user, status, err := app.jf.UserByID(id, false) + user, err := app.jf.UserByID(id, false) name := "" - if status == 200 && err == nil { + if err == nil { name = user.Name } app.debug.Printf(lm.EmailConfirmationRequired, id) @@ -688,20 +688,20 @@ func (app *appContext) ChangeMyPassword(gc *gin.Context) { return } } - user, status, err := app.jf.UserByID(gc.GetString("jfId"), false) - if status != 200 || err != nil { + user, err := app.jf.UserByID(gc.GetString("jfId"), false) + if err != nil { app.err.Printf(lm.FailedGetUser, gc.GetString("jfId"), lm.Jellyfin, err) respondBool(500, false, gc) return } // Authenticate as user to confirm old password. - user, status, err = app.authJf.Authenticate(user.Name, req.Old) - if status != 200 || err != nil { + user, err = app.authJf.Authenticate(user.Name, req.Old) + if err != nil { respondBool(401, false, gc) return } - status, err = app.jf.SetPassword(gc.GetString("jfId"), req.Old, req.New) - if (status != 200 && status != 204) || err != nil { + err = app.jf.SetPassword(gc.GetString("jfId"), req.Old, req.New) + if err != nil { respondBool(500, false, gc) return } @@ -716,14 +716,14 @@ func (app *appContext) ChangeMyPassword(gc *gin.Context) { if app.config.Section("ombi").Key("enabled").MustBool(false) { func() { - ombiUser, status, err := app.getOmbiUser(gc.GetString("jfId")) - if status != 200 || err != nil { + ombiUser, err := app.getOmbiUser(gc.GetString("jfId")) + if err != nil { app.err.Printf(lm.FailedGetUser, user.Name, lm.Ombi, err) return } ombiUser["password"] = req.New - status, err = app.ombi.ModifyUser(ombiUser) - if status != 200 || err != nil { + err = app.ombi.ModifyUser(ombiUser) + if err != nil { app.err.Printf(lm.FailedChangePassword, lm.Ombi, ombiUser["userName"], err) return } diff --git a/api-users.go b/api-users.go index 6e2aaef..ae0c458 100644 --- a/api-users.go +++ b/api-users.go @@ -379,9 +379,9 @@ func (app *appContext) EnableDisableUsers(gc *gin.Context) { activityType = ActivityEnabled } for _, userID := range req.Users { - user, status, err := app.jf.UserByID(userID, false) - if status != 200 || err != nil { - errors["GetUser"][user.ID] = fmt.Sprintf("%d %v", status, err) + user, err := app.jf.UserByID(userID, false) + if err != nil { + errors["GetUser"][user.ID] = err.Error() app.err.Printf(lm.FailedGetUser, user.ID, lm.Jellyfin, err) continue } @@ -440,10 +440,7 @@ func (app *appContext) DeleteUsers(gc *gin.Context) { } } for _, userID := range req.Users { - user, status, err := app.jf.UserByID(userID, false) - if status != 200 && err == nil { - err = fmt.Errorf("failed (code %d)", status) - } + user, err := app.jf.UserByID(userID, false) if err != nil { app.err.Printf(lm.FailedGetUser, user.ID, lm.Jellyfin, err) errors[userID] = err.Error() @@ -522,8 +519,8 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) { app.storage.SetUserExpiryKey(id, expiry) if messagesEnabled && req.Notify { go func(uid string, exp time.Time) { - user, status, err := app.jf.UserByID(uid, false) - if status != 200 || err != nil { + user, err := app.jf.UserByID(uid, false) + if err != nil { return } msg, err := app.email.constructExpiryAdjusted(user.Name, exp, req.Reason, app, false) @@ -655,8 +652,8 @@ func (app *appContext) Announce(gc *gin.Context) { unique := strings.Contains(req.Message, "{username}") if unique { for _, userID := range req.Users { - user, status, err := app.jf.UserByID(userID, false) - if status != 200 || err != nil { + user, err := app.jf.UserByID(userID, false) + if err != nil { app.err.Printf(lm.FailedGetUser, userID, lm.Jellyfin, err) continue } @@ -835,9 +832,9 @@ func (app *appContext) AdminPasswordReset(gc *gin.Context) { // @tags Users func (app *appContext) GetUsers(gc *gin.Context) { var resp getUsersDTO - users, status, err := app.jf.GetUsers(false) + users, err := app.jf.GetUsers(false) resp.UserList = make([]respUser, len(users)) - if !(status == 200 || status == 204) || err != nil { + if err != nil { app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err) respond(500, "Couldn't get users", gc) return @@ -910,8 +907,8 @@ func (app *appContext) GetUsers(gc *gin.Context) { func (app *appContext) SetAccountsAdmin(gc *gin.Context) { var req setAccountsAdminDTO gc.BindJSON(&req) - users, status, err := app.jf.GetUsers(false) - if !(status == 200 || status == 204) || err != nil { + users, err := app.jf.GetUsers(false) + if err != nil { app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err) respond(500, "Couldn't get users", gc) return @@ -942,8 +939,8 @@ func (app *appContext) SetAccountsAdmin(gc *gin.Context) { func (app *appContext) ModifyLabels(gc *gin.Context) { var req modifyEmailsDTO gc.BindJSON(&req) - users, status, err := app.jf.GetUsers(false) - if !(status == 200 || status == 204) || err != nil { + users, err := app.jf.GetUsers(false) + if err != nil { app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err) respond(500, "Couldn't get users", gc) return @@ -976,11 +973,11 @@ func (app *appContext) modifyEmail(jfID string, addr string) { emailStore.Addr = addr app.storage.SetEmailsKey(jfID, emailStore) if app.config.Section("ombi").Key("enabled").MustBool(false) { - ombiUser, code, err := app.getOmbiUser(jfID) - if code == 200 && err == nil { + ombiUser, err := app.getOmbiUser(jfID) + if err == nil { ombiUser["emailAddress"] = addr - code, err = app.ombi.ModifyUser(ombiUser) - if code != 200 || err != nil { + err = app.ombi.ModifyUser(ombiUser) + if err != nil { app.err.Printf(lm.FailedSetEmailAddress, lm.Ombi, jfID, err) } } @@ -1012,8 +1009,8 @@ func (app *appContext) modifyEmail(jfID string, addr string) { func (app *appContext) ModifyEmails(gc *gin.Context) { var req modifyEmailsDTO gc.BindJSON(&req) - users, status, err := app.jf.GetUsers(false) - if !(status == 200 || status == 204) || err != nil { + users, err := app.jf.GetUsers(false) + if err != nil { app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err) respond(500, "Couldn't get users", gc) return @@ -1099,8 +1096,8 @@ func (app *appContext) ApplySettings(gc *gin.Context) { } else if req.From == "user" { applyingFromType = lm.User app.jf.CacheExpiry = time.Now() - user, status, err := app.jf.UserByID(req.ID, false) - if !(status == 200 || status == 204) || err != nil { + user, err := app.jf.UserByID(req.ID, false) + if err != nil { app.err.Printf(lm.FailedGetUser, req.ID, lm.Jellyfin, err) respond(500, "Couldn't get user", gc) return @@ -1110,8 +1107,8 @@ func (app *appContext) ApplySettings(gc *gin.Context) { policy = user.Policy } if req.Homescreen { - displayprefs, status, err = app.jf.GetDisplayPreferences(req.ID) - if !(status == 200 || status == 204) || err != nil { + displayprefs, err = app.jf.GetDisplayPreferences(req.ID) + if err != nil { app.err.Printf(lm.FailedGetJellyfinDisplayPrefs, req.ID, err) respond(500, "Couldn't get displayprefs", gc) return @@ -1136,26 +1133,25 @@ func (app *appContext) ApplySettings(gc *gin.Context) { app.debug.Printf(lm.DelayingRequests, requestDelayThreshold) } for _, id := range req.ApplyTo { - var status int var err error if req.Policy { - status, err = app.jf.SetPolicy(id, policy) - if !(status == 200 || status == 204) || err != nil { - errors["policy"][id] = fmt.Sprintf("%d: %s", status, err) + err = app.jf.SetPolicy(id, policy) + if err != nil { + errors["policy"][id] = err.Error() } } if shouldDelay { time.Sleep(250 * time.Millisecond) } if req.Homescreen { - status, err = app.jf.SetConfiguration(id, configuration) + err = app.jf.SetConfiguration(id, configuration) errorString := "" - if !(status == 200 || status == 204) || err != nil { - errorString += fmt.Sprintf("Configuration %d: %v ", status, err) + if err != nil { + errorString += fmt.Sprintf("Configuration: %v", err) } else { - status, err = app.jf.SetDisplayPreferences(id, displayprefs) - if !(status == 200 || status == 204) || err != nil { - errorString += fmt.Sprintf("Displayprefs %d: %v ", status, err) + err = app.jf.SetDisplayPreferences(id, displayprefs) + if err != nil { + errorString += fmt.Sprintf("Displayprefs;) %v ", err) } } if errorString != "" { @@ -1164,18 +1160,18 @@ func (app *appContext) ApplySettings(gc *gin.Context) { } if ombi != nil { errorString := "" - user, status, err := app.getOmbiUser(id) - if status != 200 || err != nil { - errorString += fmt.Sprintf("Ombi GetUser %d: %v ", status, err) + user, err := app.getOmbiUser(id) + if err != nil { + errorString += fmt.Sprintf("Ombi GetUser: %v ", err) } else { // newUser := ombi // newUser["id"] = user["id"] // newUser["userName"] = user["userName"] // newUser["alias"] = user["alias"] // newUser["emailAddress"] = user["emailAddress"] - status, err = app.ombi.applyProfile(user, ombi) - if status != 200 || err != nil { - errorString += fmt.Sprintf("Apply %d: %v ", status, err) + err = app.ombi.applyProfile(user, ombi) + if err != nil { + errorString += fmt.Sprintf("Apply: %v ", err) } } if errorString != "" { diff --git a/api.go b/api.go index 696b587..87c1687 100644 --- a/api.go +++ b/api.go @@ -148,18 +148,18 @@ func (app *appContext) ResetSetPassword(gc *gin.Context) { userID = reset.ID username = reset.Username - status, err := app.jf.ResetPasswordAdmin(userID) - if !(status == 200 || status == 204) || err != nil { + err := app.jf.ResetPasswordAdmin(userID) + if err != nil { app.err.Printf(lm.FailedChangePassword, lm.Jellyfin, userID, err) - respondBool(status, false, gc) + respondBool(500, false, gc) return } delete(app.internalPWRs, req.PIN) } else { - resp, status, err := app.jf.ResetPassword(req.PIN) - if status != 200 || err != nil || !resp.Success { + resp, err := app.jf.ResetPassword(req.PIN) + if err != nil || !resp.Success { app.err.Printf(lm.FailedChangePassword, lm.Jellyfin, userID, err) - respondBool(status, false, gc) + respondBool(500, false, gc) return } if req.Password == "" || len(resp.UsersReset) == 0 { @@ -170,14 +170,13 @@ func (app *appContext) ResetSetPassword(gc *gin.Context) { } var user mediabrowser.User - var status int var err error if isInternal { - user, status, err = app.jf.UserByID(userID, false) + user, err = app.jf.UserByID(userID, false) } else { - user, status, err = app.jf.UserByName(username, false) + user, err = app.jf.UserByName(username, false) } - if status != 200 || err != nil { + if err != nil { app.err.Printf(lm.FailedGetUser, userID, lm.Jellyfin, err) respondBool(500, false, gc) return @@ -195,8 +194,8 @@ func (app *appContext) ResetSetPassword(gc *gin.Context) { if isInternal { prevPassword = "" } - status, err = app.jf.SetPassword(user.ID, prevPassword, req.Password) - if !(status == 200 || status == 204) || err != nil { + err = app.jf.SetPassword(user.ID, prevPassword, req.Password) + if err != nil { app.err.Printf(lm.FailedChangePassword, lm.Jellyfin, user.ID, err) respondBool(500, false, gc) return @@ -210,15 +209,15 @@ func (app *appContext) ResetSetPassword(gc *gin.Context) { respondBool(200, true, gc) return } */ - ombiUser, status, err := app.getOmbiUser(user.ID) - if status != 200 || err != nil { + ombiUser, err := app.getOmbiUser(user.ID) + if err != nil { app.err.Printf(lm.FailedGetUser, user.ID, lm.Ombi, err) respondBool(200, true, gc) return } ombiUser["password"] = req.Password - status, err = app.ombi.ModifyUser(ombiUser) - if status != 200 || err != nil { + err = app.ombi.ModifyUser(ombiUser) + if err != nil { app.err.Printf(lm.FailedChangePassword, lm.Ombi, user.ID, err) respondBool(200, true, gc) return diff --git a/auth.go b/auth.go index 6b5ea12..950ff91 100644 --- a/auth.go +++ b/auth.go @@ -2,6 +2,7 @@ package main import ( "encoding/base64" + "errors" "fmt" "os" "strings" @@ -166,19 +167,19 @@ func (app *appContext) decodeValidateLoginHeader(gc *gin.Context, userpage bool) func (app *appContext) validateJellyfinCredentials(username, password string, gc *gin.Context, userpage bool) (user mediabrowser.User, ok bool) { ok = false - user, status, err := app.authJf.Authenticate(username, password) - if status != 200 || err != nil { - if status == 401 || status == 400 { + user, err := app.authJf.Authenticate(username, password) + if err != nil { + if errors.Is(err, mediabrowser.ErrUnauthorized{}) { app.logIpInfo(gc, userpage, fmt.Sprintf(lm.FailedAuthRequest, lm.InvalidUserOrPass)) respond(401, "Unauthorized", gc) return } - if status == 403 { + if errors.Is(err, mediabrowser.ErrForbidden{}) { app.logIpInfo(gc, userpage, fmt.Sprintf(lm.FailedAuthRequest, lm.UserDisabled)) respond(403, "yourAccountWasDisabled", gc) return } - app.authLog(fmt.Sprintf(lm.FailedAuthJellyfin, app.jf.Server, status, err)) + app.authLog(fmt.Sprintf(lm.FailedAuthJellyfin, app.jf.Server, err)) respond(500, "Jellyfin error", gc) return } diff --git a/common/common.go b/common/common.go index 8d1071d..123478d 100644 --- a/common/common.go +++ b/common/common.go @@ -1,8 +1,11 @@ package common import ( + "errors" "fmt" "log" + + lm "github.com/hrfee/jfa-go/logmessages" ) // TimeoutHandler recovers from an http timeout or panic. @@ -12,7 +15,7 @@ type TimeoutHandler func() func NewTimeoutHandler(name, addr string, noFail bool) TimeoutHandler { return func() { if r := recover(); r != nil { - out := fmt.Sprintf("Failed to authenticate with %s @ \"%s\": Timed out", name, addr) + out := fmt.Sprintf(lm.FailedAuth, name, addr, 0, lm.TimedOut) if noFail { log.Print(out) } else { @@ -21,3 +24,50 @@ func NewTimeoutHandler(name, addr string, noFail bool) TimeoutHandler { } } } + +// most 404 errors are from UserNotFound, so this generic error doesn't really need any detail. +type ErrNotFound error + +type ErrUnauthorized struct{} + +func (err ErrUnauthorized) Error() string { + return lm.Unauthorized +} + +type ErrForbidden struct{} + +func (err ErrForbidden) Error() string { + return lm.Forbidden +} + +var ( + NotFound ErrNotFound = errors.New(lm.NotFound) +) + +type ErrUnknown struct { + code int +} + +func (err ErrUnknown) Error() string { + msg := fmt.Sprintf(lm.FailedGenericWithCode, err.code) + return msg +} + +// GenericErr returns an error appropriate to the given HTTP status (or actual error, if given). +func GenericErr(status int, err error) error { + if err != nil { + return err + } + switch status { + case 200, 204, 201: + return nil + case 401, 400: + return ErrUnauthorized{} + case 404: + return NotFound + case 403: + return ErrForbidden{} + default: + return ErrUnknown{code: status} + } +} diff --git a/common/go.mod b/common/go.mod index ec0c50a..6de026b 100644 --- a/common/go.mod +++ b/common/go.mod @@ -1,3 +1,7 @@ module github.com/hrfee/jfa-go/common -go 1.15 +replace github.com/hrfee/jfa-go/logmessages => ../logmessages + +go 1.22.4 + +require github.com/hrfee/jfa-go/logmessages v0.0.0-20240805130902-86c37fb4237b diff --git a/email.go b/email.go index 7246384..83cc4d2 100644 --- a/email.go +++ b/email.go @@ -968,11 +968,10 @@ func (app *appContext) getAddressOrName(jfID string) string { // returns "" if none found. returns only the first match, might be an issue if there are users with the same contact method usernames. func (app *appContext) ReverseUserSearch(address string, matchUsername, matchEmail, matchContactMethod bool) (user mediabrowser.User, ok bool) { ok = false - var status int var err error = nil if matchUsername { - user, status, err = app.jf.UserByName(address, false) - if status == 200 && err == nil { + user, err = app.jf.UserByName(address, false) + if err == nil { ok = true return } @@ -983,8 +982,8 @@ func (app *appContext) ReverseUserSearch(address string, matchUsername, matchEma err = app.storage.db.Find(&emailAddresses, badgerhold.Where("Addr").Eq(address)) if err == nil && len(emailAddresses) > 0 { for _, emailUser := range emailAddresses { - user, status, err = app.jf.UserByID(emailUser.JellyfinID, false) - if status == 200 && err == nil { + user, err = app.jf.UserByID(emailUser.JellyfinID, false) + if err == nil { ok = true return } @@ -997,8 +996,8 @@ func (app *appContext) ReverseUserSearch(address string, matchUsername, matchEma if matchContactMethod { for _, dcUser := range app.storage.GetDiscord() { if RenderDiscordUsername(dcUser) == strings.ToLower(address) { - user, status, err = app.jf.UserByID(dcUser.JellyfinID, false) - if status == 200 && err == nil { + user, err = app.jf.UserByID(dcUser.JellyfinID, false) + if err == nil { ok = true return } @@ -1009,8 +1008,8 @@ func (app *appContext) ReverseUserSearch(address string, matchUsername, matchEma err = app.storage.db.Find(&telegramUsers, badgerhold.Where("Username").Eq(tgUsername)) if err == nil && len(telegramUsers) > 0 { for _, telegramUser := range telegramUsers { - user, status, err = app.jf.UserByID(telegramUser.JellyfinID, false) - if status == 200 && err == nil { + user, err = app.jf.UserByID(telegramUser.JellyfinID, false) + if err == nil { ok = true return } @@ -1020,8 +1019,8 @@ func (app *appContext) ReverseUserSearch(address string, matchUsername, matchEma err = app.storage.db.Find(&matrixUsers, badgerhold.Where("UserID").Eq(address)) if err == nil && len(matrixUsers) > 0 { for _, matrixUser := range matrixUsers { - user, status, err = app.jf.UserByID(matrixUser.JellyfinID, false) - if status == 200 && err == nil { + user, err = app.jf.UserByID(matrixUser.JellyfinID, false) + if err == nil { ok = true return } diff --git a/go.mod b/go.mod index d464b94..655f590 100644 --- a/go.mod +++ b/go.mod @@ -33,13 +33,15 @@ require ( github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible github.com/golang-jwt/jwt v3.2.2+incompatible github.com/gomarkdown/markdown v0.0.0-20230322041520-c84983bdbf2a - github.com/hrfee/jfa-go/common v0.0.0-20240728190513-dabef831d769 + github.com/hrfee/jfa-go/common v0.0.0-20240805130902-86c37fb4237b github.com/hrfee/jfa-go/docs v0.0.0-20230626224816-f72960635dc3 github.com/hrfee/jfa-go/easyproxy v0.0.0-00010101000000-000000000000 + github.com/hrfee/jfa-go/jellyseerr v0.0.0-20240805130902-86c37fb4237b github.com/hrfee/jfa-go/linecache v0.0.0-20230626224816-f72960635dc3 github.com/hrfee/jfa-go/logger v0.0.0-20240731152135-2d066ea7cd32 - github.com/hrfee/jfa-go/ombi v0.0.0-20230626224816-f72960635dc3 - github.com/hrfee/mediabrowser v0.3.14 + github.com/hrfee/jfa-go/logmessages v0.0.0-20240805130902-86c37fb4237b + github.com/hrfee/jfa-go/ombi v0.0.0-20240805130902-86c37fb4237b + github.com/hrfee/mediabrowser v0.3.18 github.com/itchyny/timefmt-go v0.1.5 github.com/lithammer/shortuuid/v3 v3.0.7 github.com/mailgun/mailgun-go/v4 v4.9.1 @@ -92,8 +94,6 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/hrfee/jfa-go/jellyseerr v0.0.0-00010101000000-000000000000 // indirect - github.com/hrfee/jfa-go/logmessages v0.0.0-00010101000000-000000000000 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.16.6 // indirect diff --git a/go.sum b/go.sum index c48f752..ba2a61d 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -94,6 +95,7 @@ github.com/getlantern/systray v1.2.2/go.mod h1:pXFOI1wwqwYXEhLPm9ZGjS2u/vVELeIgN github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg= github.com/gin-contrib/pprof v1.4.0/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90= github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= @@ -140,6 +142,7 @@ github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogB github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -213,6 +216,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -224,10 +228,10 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hrfee/mediabrowser v0.3.13 h1:NgQNbq+JWwsP68BdWXL/rwbpfE/oO5LJ5KVkE+aNbX8= -github.com/hrfee/mediabrowser v0.3.13/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= -github.com/hrfee/mediabrowser v0.3.14 h1:/hxtwefV4pA63fyHussk00JBFry6p+uDDOumA49M1eY= -github.com/hrfee/mediabrowser v0.3.14/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= +github.com/hrfee/mediabrowser v0.3.17 h1:gerX/sxQ8V64mZ13I0zuK9hpCJnScdTpAzD2j0a7bvE= +github.com/hrfee/mediabrowser v0.3.17/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= +github.com/hrfee/mediabrowser v0.3.18 h1:6UDyae0srEVjiMG+utPQfJJp4UId6/T3WN9EDCQKRTk= +github.com/hrfee/mediabrowser v0.3.18/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= @@ -289,6 +293,7 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -351,6 +356,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= @@ -420,6 +426,7 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= @@ -455,6 +462,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/housekeeping-d.go b/housekeeping-d.go index 47123aa..f77aa55 100644 --- a/housekeeping-d.go +++ b/housekeeping-d.go @@ -16,7 +16,7 @@ func (app *appContext) clearEmails() { app.debug.Println(lm.HousekeepingEmail) emails := app.storage.GetEmails() for _, email := range emails { - _, _, err := app.jf.UserByID(email.JellyfinID, false) + _, err := app.jf.UserByID(email.JellyfinID, false) // Make sure the user doesn't exist, and no other error has occured switch err.(type) { case mediabrowser.ErrUserNotFound: @@ -32,7 +32,7 @@ func (app *appContext) clearDiscord() { app.debug.Println(lm.HousekeepingDiscord) discordUsers := app.storage.GetDiscord() for _, discordUser := range discordUsers { - user, _, err := app.jf.UserByID(discordUser.JellyfinID, false) + user, err := app.jf.UserByID(discordUser.JellyfinID, false) // Make sure the user doesn't exist, and no other error has occured switch err.(type) { case mediabrowser.ErrUserNotFound: @@ -53,7 +53,7 @@ func (app *appContext) clearMatrix() { app.debug.Println(lm.HousekeepingMatrix) matrixUsers := app.storage.GetMatrix() for _, matrixUser := range matrixUsers { - _, _, err := app.jf.UserByID(matrixUser.JellyfinID, false) + _, err := app.jf.UserByID(matrixUser.JellyfinID, false) // Make sure the user doesn't exist, and no other error has occured switch err.(type) { case mediabrowser.ErrUserNotFound: @@ -69,7 +69,7 @@ func (app *appContext) clearTelegram() { app.debug.Println(lm.HousekeepingTelegram) telegramUsers := app.storage.GetTelegram() for _, telegramUser := range telegramUsers { - _, _, err := app.jf.UserByID(telegramUser.JellyfinID, false) + _, err := app.jf.UserByID(telegramUser.JellyfinID, false) // Make sure the user doesn't exist, and no other error has occured switch err.(type) { case mediabrowser.ErrUserNotFound: diff --git a/html/admin.html b/html/admin.html index 76894a8..83a1db0 100644 --- a/html/admin.html +++ b/html/admin.html @@ -44,7 +44,7 @@