From 211c3fa9cb369529ff03a4da454eebd8d6bf2953 Mon Sep 17 00:00:00 2001 From: Assclaw Date: Sat, 27 Jun 2026 04:40:45 +0000 Subject: [PATCH] Handle GrizzlyFlix signup mail and Seerr sync failures --- api-users.go | 42 ++++++++++++++++++++++++---------------- config/config-base.yaml | 17 ++++++++++++++++ jellyseerr-d.go | 14 ++++++++++++-- jellyseerr/jellyseerr.go | 2 +- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/api-users.go b/api-users.go index 3a1592c..aafd724 100644 --- a/api-users.go +++ b/api-users.go @@ -189,32 +189,40 @@ func (app *appContext) NewUserFromInvite(gc *gin.Context) { respond(500, "errorUnknown", gc) return } - if app.ConfirmationKeys == nil { - app.ConfirmationKeys = map[string]map[string]ConfirmationKey{} - } - cKeys, ok := app.ConfirmationKeys[req.Code] - if !ok { - cKeys = map[string]ConfirmationKey{} - } - cKeys[key] = ConfirmationKey{ - newUserDTO: req, - completeContactMethods: completeContactMethods, - } - app.confirmationKeysLock.Lock() - app.ConfirmationKeys[req.Code] = cKeys - app.confirmationKeysLock.Unlock() - app.debug.Printf(lm.EmailConfirmationRequired, req.Username) - respond(401, "confirmEmail", gc) msg, err := app.email.constructConfirmation(req.Code, req.Username, key, false) if err != nil { app.err.Printf(lm.FailedConstructConfirmationEmail, req.Code, err) + respond(500, "errorUnknown", gc) + return } else if err := app.email.send(msg, req.Email); err != nil { app.err.Printf(lm.FailedSendConfirmationEmail, req.Code, req.Email, err) + if !app.config.Section("email_confirmation").Key("allow_account_creation_on_send_failure").MustBool(false) { + respond(500, "errorUnknown", gc) + return + } + app.debug.Printf("Email confirmation send failed for \"%s\"; continuing account creation because email_confirmation.allow_account_creation_on_send_failure is enabled", req.Username) } else { app.debug.Printf(lm.SentConfirmationEmail, req.Code, req.Email) + if app.ConfirmationKeys == nil { + app.ConfirmationKeys = map[string]map[string]ConfirmationKey{} + } + cKeys, ok := app.ConfirmationKeys[req.Code] + if !ok { + cKeys = map[string]ConfirmationKey{} + } + cKeys[key] = ConfirmationKey{ + newUserDTO: req, + completeContactMethods: completeContactMethods, + } + app.confirmationKeysLock.Lock() + app.ConfirmationKeys[req.Code] = cKeys + app.confirmationKeysLock.Unlock() + + app.debug.Printf(lm.EmailConfirmationRequired, req.Username) + respond(401, "confirmEmail", gc) + return } - return } } diff --git a/config/config-base.yaml b/config/config-base.yaml index bc5fcc4..2dcb767 100644 --- a/config/config-base.yaml +++ b/config/config-base.yaml @@ -1436,6 +1436,15 @@ sections: description: Jellyseerr requires email addresses to be unique. If this is not the case, you may see errors in jfa-go's logs. You can require unique addresses in Settings > Email. + - setting: ignore_csrf_email_update_errors + name: Ignore CSRF email update errors + advanced: true + type: bool + value: true + depends_true: enabled + description: Some Seerr builds allow API-key reads/imports but reject email or + notification writes with CSRF protection. If enabled, jfa-go logs those write + rejections at debug level while still importing/linking users. - section: backups meta: name: Backups @@ -1514,6 +1523,14 @@ sections: requires_restart: true type: bool value: false + - setting: allow_account_creation_on_send_failure + name: Continue if confirmation email fails + advanced: true + type: bool + value: false + description: If enabled, account creation continues when the confirmation email + cannot be sent. Use this only when signups should keep working during temporary + mail relay failures. - setting: subject name: Email subject type: text diff --git a/jellyseerr-d.go b/jellyseerr-d.go index e25e102..fcc46a8 100644 --- a/jellyseerr-d.go +++ b/jellyseerr-d.go @@ -14,6 +14,10 @@ type JellyseerrInitialSyncStatus struct { Done bool } +func isJellyseerrCSRFFailure(err error) bool { + return err != nil && strings.Contains(strings.ToLower(err.Error()), "invalid csrf token") +} + // Ensure the Jellyseerr cache is up to date before calling. func (app *appContext) SynchronizeJellyseerrUser(jfID string) { user, imported, err := app.js.GetOrImportUser(jfID, true) @@ -35,7 +39,9 @@ func (app *appContext) SynchronizeJellyseerrUser(jfID string) { if ok && email.Addr != "" && user.Email != email.Addr { err = app.js.ModifyMainUserSettings(jfID, jellyseerr.MainUserSettings{Email: email.Addr}) if err != nil { - if strings.Contains(err.Error(), "INVALID_EMAIL") { + if isJellyseerrCSRFFailure(err) && app.config.Section("jellyseerr").Key("ignore_csrf_email_update_errors").MustBool(true) { + app.debug.Printf("Skipping Jellyseerr email update for user \"%s\": Jellyseerr rejected the API-key write with CSRF protection", jfID) + } else if strings.Contains(err.Error(), "INVALID_EMAIL") { app.err.Printf(lm.FailedSetEmailAddress, lm.Jellyseerr, jfID, err.Error()+"\""+email.Addr+"\"") } else { app.err.Printf(lm.FailedSetEmailAddress, lm.Jellyseerr, jfID, err) @@ -63,7 +69,11 @@ func (app *appContext) SynchronizeJellyseerrUser(jfID string) { if len(contactMethods) != 0 { err := app.js.ModifyNotifications(jfID, contactMethods) if err != nil { - app.err.Printf(lm.FailedSyncContactMethods, lm.Jellyseerr, err) + if isJellyseerrCSRFFailure(err) && app.config.Section("jellyseerr").Key("ignore_csrf_email_update_errors").MustBool(true) { + app.debug.Printf("Skipping Jellyseerr notification update for user \"%s\": Jellyseerr rejected the API-key write with CSRF protection", jfID) + } else { + app.err.Printf(lm.FailedSyncContactMethods, lm.Jellyseerr, err) + } } } } diff --git a/jellyseerr/jellyseerr.go b/jellyseerr/jellyseerr.go index f781d93..f5dc94c 100644 --- a/jellyseerr/jellyseerr.go +++ b/jellyseerr/jellyseerr.go @@ -430,7 +430,7 @@ func (js *Jellyseerr) ModifyNotifications(jfID string, conf map[NotificationsFie switch v.(type) { case string: conf[FieldDiscord] = []string{v.(string)} - } + } } u, err := js.getUser(jfID) if err != nil {