|
|
|
@ -10,6 +10,7 @@ import (
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
"github.com/golang-jwt/jwt"
|
|
|
|
|
"github.com/hrfee/jfa-go/jellyseerr"
|
|
|
|
|
lm "github.com/hrfee/jfa-go/logmessages"
|
|
|
|
|
"github.com/hrfee/mediabrowser"
|
|
|
|
|
"github.com/lithammer/shortuuid/v3"
|
|
|
|
|
"github.com/timshannon/badgerhold/v4"
|
|
|
|
@ -36,14 +37,14 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
|
|
|
|
|
gc.BindJSON(&req)
|
|
|
|
|
existingUser, _, _ := app.jf.UserByName(req.Username, false)
|
|
|
|
|
if existingUser.Name != "" {
|
|
|
|
|
msg := fmt.Sprintf("User already exists named %s", req.Username)
|
|
|
|
|
app.info.Printf("%s New user failed: %s", req.Username, msg)
|
|
|
|
|
msg := lm.UserExists
|
|
|
|
|
app.info.Printf(lm.FailedCreateUser, lm.Jellyfin, req.Username, msg)
|
|
|
|
|
respondUser(401, false, false, msg, gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
user, status, err := app.jf.NewUser(req.Username, req.Password)
|
|
|
|
|
if !(status == 200 || status == 204) || err != nil {
|
|
|
|
|
app.err.Printf("%s New user failed (%d): %v", req.Username, status, err)
|
|
|
|
|
app.err.Printf(lm.FailedCreateUser, lm.Jellyfin, req.Username, err)
|
|
|
|
|
respondUser(401, false, false, err.Error(), gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -64,19 +65,19 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
|
|
|
|
|
if p, ok := app.storage.GetProfileKey(req.Profile); ok {
|
|
|
|
|
profile = p
|
|
|
|
|
} else {
|
|
|
|
|
app.debug.Printf("Couldn't find profile \"%s\", using default", req.Profile)
|
|
|
|
|
app.debug.Printf(lm.FailedGetProfile+lm.FallbackToDefault, req.Profile)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status, err = app.jf.SetPolicy(id, profile.Policy)
|
|
|
|
|
if !(status == 200 || status == 204 || err == nil) {
|
|
|
|
|
app.err.Printf("%s: Failed to set user policy (%d): %v", req.Username, status, err)
|
|
|
|
|
app.err.Printf(lm.FailedApplyTemplate, "policy", lm.Jellyfin, req.Username, err)
|
|
|
|
|
}
|
|
|
|
|
status, err = app.jf.SetConfiguration(id, profile.Configuration)
|
|
|
|
|
if (status == 200 || status == 204) && err == nil {
|
|
|
|
|
status, err = app.jf.SetDisplayPreferences(id, profile.Displayprefs)
|
|
|
|
|
}
|
|
|
|
|
if !((status == 200 || status == 204) && err == nil) {
|
|
|
|
|
app.err.Printf("%s: Failed to set configuration template (%d): %v", req.Username, status, err)
|
|
|
|
|
app.err.Printf(lm.FailedApplyTemplate, "configuration", lm.Jellyfin, req.Username, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
app.jf.CacheExpiry = time.Now()
|
|
|
|
@ -89,48 +90,47 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
|
|
|
|
|
}
|
|
|
|
|
errors, code, err := app.ombi.NewUser(req.Username, req.Password, req.Email, profile.Ombi)
|
|
|
|
|
if err != nil || code != 200 {
|
|
|
|
|
app.err.Printf("Failed to create Ombi user (%d): %v", code, err)
|
|
|
|
|
app.debug.Printf("Errors reported by Ombi: %s", strings.Join(errors, ", "))
|
|
|
|
|
app.err.Printf(lm.FailedCreateUser, lm.Ombi, req.Username, err)
|
|
|
|
|
app.debug.Printf(lm.AdditionalOmbiErrors, strings.Join(errors, ", "))
|
|
|
|
|
} else {
|
|
|
|
|
app.info.Println("Created Ombi user")
|
|
|
|
|
app.info.Printf(lm.CreateUser, lm.Ombi, req.Username)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if app.config.Section("jellyseerr").Key("enabled").MustBool(false) {
|
|
|
|
|
// Gets existing user (not possible) or imports the given user.
|
|
|
|
|
_, err := app.js.MustGetUser(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to create Jellyseerr user: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedCreateUser, lm.Jellyseerr, req.Username, err)
|
|
|
|
|
} else {
|
|
|
|
|
app.info.Println("Created Jellyseerr user")
|
|
|
|
|
app.info.Printf(lm.CreateUser, lm.Jellyseerr, req.Username)
|
|
|
|
|
}
|
|
|
|
|
err = app.js.ApplyTemplateToUser(id, profile.Jellyseerr.User)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to apply Jellyseerr user template: %v\n", err)
|
|
|
|
|
app.err.Printf(lm.FailedApplyTemplate, "user", lm.Jellyseerr, req.Username, err)
|
|
|
|
|
}
|
|
|
|
|
err = app.js.ApplyNotificationsTemplateToUser(id, profile.Jellyseerr.Notifications)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to apply Jellyseerr notifications template: %v\n", err)
|
|
|
|
|
app.err.Printf(lm.FailedApplyTemplate, "notifications", lm.Jellyseerr, req.Username, err)
|
|
|
|
|
}
|
|
|
|
|
if emailEnabled {
|
|
|
|
|
err = app.js.ModifyUser(id, map[jellyseerr.UserField]any{jellyseerr.FieldEmail: req.Email})
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to set Jellyseerr email address: %v\n", err)
|
|
|
|
|
app.err.Printf(lm.FailedSetEmailAddress, lm.Jellyseerr, id, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
|
|
|
|
|
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email)
|
|
|
|
|
msg, err := app.email.constructWelcome(req.Username, time.Time{}, app, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("%s: Failed to construct welcome email: %v", req.Username, err)
|
|
|
|
|
app.err.Printf(lm.FailedConstructWelcomeMessage, id, err)
|
|
|
|
|
respondUser(500, true, false, err.Error(), gc)
|
|
|
|
|
return
|
|
|
|
|
} else if err := app.email.send(msg, req.Email); err != nil {
|
|
|
|
|
app.err.Printf("%s: Failed to send welcome email: %v", req.Username, err)
|
|
|
|
|
app.err.Printf(lm.FailedSendWelcomeMessage, req.Username, req.Email, err)
|
|
|
|
|
respondUser(500, true, false, err.Error(), gc)
|
|
|
|
|
return
|
|
|
|
|
} else {
|
|
|
|
|
app.info.Printf("%s: Sent welcome email to %s", req.Username, req.Email)
|
|
|
|
|
app.info.Printf(lm.SentWelcomeMessage, req.Username, req.Email)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
respondUser(200, true, true, "", gc)
|
|
|
|
@ -143,8 +143,8 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
existingUser, _, _ := app.jf.UserByName(req.Username, false)
|
|
|
|
|
if existingUser.Name != "" {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
msg := fmt.Sprintf("User %s already exists", req.Username)
|
|
|
|
|
app.info.Printf("%s: New user failed: %s", req.Code, msg)
|
|
|
|
|
msg := lm.UserExists
|
|
|
|
|
app.info.Printf(lm.FailedCreateUser, lm.Jellyfin, req.Username, msg)
|
|
|
|
|
respond(401, "errorUserExists", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -156,7 +156,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
if req.DiscordPIN == "" {
|
|
|
|
|
if app.config.Section("discord").Key("required").MustBool(false) {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.debug.Printf("%s: New user failed: Discord verification not completed", req.Code)
|
|
|
|
|
app.info.Printf(lm.FailedLinkUser, lm.Discord, "?", req.Code, lm.AccountUnverified)
|
|
|
|
|
respond(401, "errorDiscordVerification", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -166,7 +166,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
discordUser, discordVerified = app.discord.UserVerified(req.DiscordPIN)
|
|
|
|
|
if !discordVerified {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.debug.Printf("%s: New user failed: Discord PIN was invalid", req.Code)
|
|
|
|
|
app.info.Printf(lm.FailedLinkUser, lm.Discord, "?", req.Code, fmt.Sprintf(lm.InvalidPIN, req.DiscordPIN))
|
|
|
|
|
respond(401, "errorInvalidPIN", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -174,7 +174,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
}
|
|
|
|
|
if app.config.Section("discord").Key("require_unique").MustBool(false) && app.discord.UserExists(discordUser.ID) {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.debug.Printf("%s: New user failed: Discord user already linked", req.Code)
|
|
|
|
|
app.debug.Printf(lm.FailedLinkUser, lm.Discord, discordUser.ID, req.Code, lm.AccountLinked)
|
|
|
|
|
respond(400, "errorAccountLinked", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -183,7 +183,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
err := app.discord.ApplyRole(discordUser.ID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.err.Printf("%s: New user failed: Failed to set member role: %v", req.Code, err)
|
|
|
|
|
app.err.Printf(lm.FailedLinkUser, lm.Discord, discordUser.ID, req.Code, fmt.Sprintf(lm.FailedSetDiscordMemberRole, err))
|
|
|
|
|
respond(401, "error", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -197,7 +197,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
if req.MatrixPIN == "" {
|
|
|
|
|
if app.config.Section("matrix").Key("required").MustBool(false) {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.debug.Printf("%s: New user failed: Matrix verification not completed", req.Code)
|
|
|
|
|
app.info.Printf(lm.FailedLinkUser, lm.Matrix, "?", req.Code, lm.AccountUnverified)
|
|
|
|
|
respond(401, "errorMatrixVerification", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -208,7 +208,11 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
if !ok || !user.Verified {
|
|
|
|
|
matrixVerified = false
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.debug.Printf("%s: New user failed: Matrix PIN was invalid", req.Code)
|
|
|
|
|
uid := ""
|
|
|
|
|
if ok {
|
|
|
|
|
uid = user.User.UserID
|
|
|
|
|
}
|
|
|
|
|
app.info.Printf(lm.FailedLinkUser, lm.Matrix, uid, req.Code, fmt.Sprintf(lm.InvalidPIN, req.MatrixPIN))
|
|
|
|
|
respond(401, "errorInvalidPIN", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -216,7 +220,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
}
|
|
|
|
|
if app.config.Section("matrix").Key("require_unique").MustBool(false) && app.matrix.UserExists(user.User.UserID) {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.debug.Printf("%s: New user failed: Matrix user already linked", req.Code)
|
|
|
|
|
app.debug.Printf(lm.FailedLinkUser, lm.Matrix, user.User.UserID, req.Code, lm.AccountLinked)
|
|
|
|
|
respond(400, "errorAccountLinked", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -233,7 +237,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
if req.TelegramPIN == "" {
|
|
|
|
|
if app.config.Section("telegram").Key("required").MustBool(false) {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.debug.Printf("%s: New user failed: Telegram verification not completed", req.Code)
|
|
|
|
|
app.info.Printf(lm.FailedLinkUser, lm.Telegram, "?", req.Code, lm.AccountUnverified)
|
|
|
|
|
respond(401, "errorTelegramVerification", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -243,7 +247,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
tgToken, telegramVerified = app.telegram.TokenVerified(req.TelegramPIN)
|
|
|
|
|
if !telegramVerified {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.debug.Printf("%s: New user failed: Telegram PIN was invalid", req.Code)
|
|
|
|
|
app.info.Printf(lm.FailedLinkUser, lm.Telegram, tgToken.Username, req.Code, fmt.Sprintf(lm.InvalidPIN, req.TelegramPIN))
|
|
|
|
|
respond(401, "errorInvalidPIN", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -251,7 +255,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
}
|
|
|
|
|
if app.config.Section("telegram").Key("require_unique").MustBool(false) && app.telegram.UserExists(tgToken.Username) {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.debug.Printf("%s: New user failed: Telegram user already linked", req.Code)
|
|
|
|
|
app.debug.Printf(lm.FailedLinkUser, lm.Telegram, tgToken.Username, req.Code, lm.AccountLinked)
|
|
|
|
|
respond(400, "errorAccountLinked", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -270,7 +274,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
key, err := tk.SignedString([]byte(os.Getenv("JFA_SECRET")))
|
|
|
|
|
if err != nil {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.info.Printf("Failed to generate confirmation token: %v", err)
|
|
|
|
|
app.info.Printf(lm.FailedSignJWT, err)
|
|
|
|
|
respond(500, "errorUnknown", gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -288,15 +292,15 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
app.ConfirmationKeys[req.Code] = cKeys
|
|
|
|
|
app.confirmationKeysLock.Unlock()
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.debug.Printf("%s: Email confirmation required", req.Code)
|
|
|
|
|
app.debug.Printf(lm.EmailConfirmationRequired, req.Username)
|
|
|
|
|
respond(401, "confirmEmail", gc)
|
|
|
|
|
msg, err := app.email.constructConfirmation(req.Code, req.Username, key, app, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("%s: Failed to construct confirmation email: %v", req.Code, err)
|
|
|
|
|
app.err.Printf(lm.FailedConstructConfirmationEmail, req.Code, err)
|
|
|
|
|
} else if err := app.email.send(msg, req.Email); err != nil {
|
|
|
|
|
app.err.Printf("%s: Failed to send user confirmation email: %v", req.Code, err)
|
|
|
|
|
app.err.Printf(lm.FailedSendConfirmationEmail, req.Code, req.Email, err)
|
|
|
|
|
} else {
|
|
|
|
|
app.info.Printf("%s: Sent user confirmation email to \"%s\"", req.Code, req.Email)
|
|
|
|
|
app.err.Printf(lm.SentConfirmationEmail, req.Code, req.Email)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -306,7 +310,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
user, status, err := app.jf.NewUser(req.Username, req.Password)
|
|
|
|
|
if !(status == 200 || status == 204) || err != nil {
|
|
|
|
|
f = func(gc *gin.Context) {
|
|
|
|
|
app.err.Printf("%s New user failed (%d): %v", req.Code, status, err)
|
|
|
|
|
app.err.Printf(lm.FailedCreateUser, lm.Jellyfin, req.Username, err)
|
|
|
|
|
respond(401, app.storage.lang.Admin[app.storage.lang.chosenAdminLang].Notifications.get("errorUnknown"), gc)
|
|
|
|
|
}
|
|
|
|
|
success = false
|
|
|
|
@ -320,7 +324,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
go func(addr string) {
|
|
|
|
|
msg, err := app.email.constructCreated(req.Code, req.Username, req.Email, invite, app, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("%s: Failed to construct user creation notification: %v", req.Code, err)
|
|
|
|
|
app.err.Printf(lm.FailedConstructCreationAdmin, req.Code, err)
|
|
|
|
|
} else {
|
|
|
|
|
// Check whether notify "addr" is an email address of Jellyfin ID
|
|
|
|
|
if strings.Contains(addr, "@") {
|
|
|
|
@ -329,9 +333,9 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
err = app.sendByID(msg, addr)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("%s: Failed to send user creation notification: %v", req.Code, err)
|
|
|
|
|
app.err.Printf(lm.FailedSendCreationAdmin, req.Code, addr, err)
|
|
|
|
|
} else {
|
|
|
|
|
app.info.Printf("Sent user creation notification to %s", addr)
|
|
|
|
|
app.info.Printf(lm.SentCreationAdmin, req.Code, addr)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}(address)
|
|
|
|
@ -373,24 +377,22 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
|
|
|
|
|
var profile Profile
|
|
|
|
|
if invite.Profile != "" {
|
|
|
|
|
app.debug.Printf("Applying settings from profile \"%s\"", invite.Profile)
|
|
|
|
|
app.debug.Printf(lm.ApplyProfile, invite.Profile)
|
|
|
|
|
var ok bool
|
|
|
|
|
profile, ok = app.storage.GetProfileKey(invite.Profile)
|
|
|
|
|
if !ok {
|
|
|
|
|
profile = app.storage.GetDefaultProfile()
|
|
|
|
|
}
|
|
|
|
|
app.debug.Printf("Applying policy from profile \"%s\"", invite.Profile)
|
|
|
|
|
status, err = app.jf.SetPolicy(id, profile.Policy)
|
|
|
|
|
if !((status == 200 || status == 204) && err == nil) {
|
|
|
|
|
app.err.Printf("%s: Failed to set user policy (%d): %v", req.Code, status, err)
|
|
|
|
|
app.err.Printf(lm.FailedApplyTemplate, "policy", lm.Jellyfin, id, err)
|
|
|
|
|
}
|
|
|
|
|
app.debug.Printf("Applying homescreen from profile \"%s\"", invite.Profile)
|
|
|
|
|
status, err = app.jf.SetConfiguration(id, profile.Configuration)
|
|
|
|
|
if (status == 200 || status == 204) && err == nil {
|
|
|
|
|
status, err = app.jf.SetDisplayPreferences(id, profile.Displayprefs)
|
|
|
|
|
}
|
|
|
|
|
if !((status == 200 || status == 204) && err == nil) {
|
|
|
|
|
app.err.Printf("%s: Failed to set configuration template (%d): %v", req.Code, status, err)
|
|
|
|
|
app.err.Printf(lm.FailedApplyTemplate, "configuration", lm.Jellyfin, id, err)
|
|
|
|
|
}
|
|
|
|
|
if app.config.Section("user_page").Key("enabled").MustBool(false) && app.config.Section("user_page").Key("referrals").MustBool(false) && profile.ReferralTemplateKey != "" {
|
|
|
|
|
emailStore.ReferralTemplateKey = profile.ReferralTemplateKey
|
|
|
|
@ -454,23 +456,23 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
// Check if on the off chance, Ombi's user importer has already added the account.
|
|
|
|
|
ombiUser, status, err = app.getOmbiImportedUser(req.Username)
|
|
|
|
|
if status == 200 && err == nil {
|
|
|
|
|
app.info.Println("Found existing Ombi user, applying changes")
|
|
|
|
|
app.info.Println(lm.Ombi + " " + lm.UserExists)
|
|
|
|
|
accountExists = true
|
|
|
|
|
template["password"] = req.Password
|
|
|
|
|
status, err = app.applyOmbiProfile(ombiUser, template)
|
|
|
|
|
if status != 200 || err != nil {
|
|
|
|
|
app.err.Printf("Failed to modify existing Ombi user (%d): %v\n", status, err)
|
|
|
|
|
app.err.Printf(lm.FailedApplyProfile, lm.Ombi, req.Username, err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
app.info.Printf("Failed to create Ombi user (%d): %s", code, err)
|
|
|
|
|
app.debug.Printf("Errors reported by Ombi: %s", strings.Join(errors, ", "))
|
|
|
|
|
app.info.Printf(lm.FailedCreateUser, lm.Ombi, req.Username, err)
|
|
|
|
|
app.debug.Printf(lm.AdditionalOmbiErrors, strings.Join(errors, ", "))
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ombiUser, status, err = app.getOmbiUser(id)
|
|
|
|
|
if status != 200 || err != nil {
|
|
|
|
|
app.err.Printf("Failed to get Ombi user (%d): %v", status, err)
|
|
|
|
|
app.err.Printf(lm.FailedGetUser, id, lm.Ombi, err)
|
|
|
|
|
} else {
|
|
|
|
|
app.info.Println("Created Ombi user")
|
|
|
|
|
app.info.Println(lm.CreateUser, lm.Ombi, id)
|
|
|
|
|
accountExists = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -487,13 +489,11 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
}
|
|
|
|
|
resp, status, err := app.ombi.SetNotificationPrefs(ombiUser, dID, tUser)
|
|
|
|
|
if !(status == 200 || status == 204) || err != nil {
|
|
|
|
|
app.err.Printf("Failed to link Telegram/Discord to Ombi (%d): %v", status, err)
|
|
|
|
|
app.debug.Printf("Response: %v", resp)
|
|
|
|
|
app.err.Printf(lm.FailedSyncContactMethods, lm.Ombi, err)
|
|
|
|
|
app.debug.Printf(lm.AdditionalOmbiErrors, resp)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
app.debug.Printf("Skipping Ombi: Profile \"%s\" was empty", invite.Profile)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if invite.Profile != "" && app.config.Section("jellyseerr").Key("enabled").MustBool(false) {
|
|
|
|
@ -501,23 +501,23 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
// Gets existing user (not possible) or imports the given user.
|
|
|
|
|
_, err := app.js.MustGetUser(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to create Jellyseerr user: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedCreateUser, lm.Jellyseerr, id, err)
|
|
|
|
|
} else {
|
|
|
|
|
app.info.Println("Created Jellyseerr user")
|
|
|
|
|
app.info.Printf(lm.CreateUser, lm.Jellyseerr, id)
|
|
|
|
|
}
|
|
|
|
|
err = app.js.ApplyTemplateToUser(id, profile.Jellyseerr.User)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to apply Jellyseerr user template: %v\n", err)
|
|
|
|
|
app.err.Printf(lm.FailedApplyTemplate, "user", lm.Jellyseerr, id, err)
|
|
|
|
|
}
|
|
|
|
|
err = app.js.ApplyNotificationsTemplateToUser(id, profile.Jellyseerr.Notifications)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to apply Jellyseerr notifications template: %v\n", err)
|
|
|
|
|
app.err.Printf(lm.FailedApplyTemplate, "notifications", lm.Jellyseerr, id, err)
|
|
|
|
|
}
|
|
|
|
|
contactMethods := map[jellyseerr.NotificationsField]any{}
|
|
|
|
|
if emailEnabled {
|
|
|
|
|
err = app.js.ModifyMainUserSettings(id, jellyseerr.MainUserSettings{Email: req.Email})
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to set Jellyseerr email address: %v\n", err)
|
|
|
|
|
app.err.Printf(lm.FailedSetEmailAddress, lm.Jellyseerr, id, err)
|
|
|
|
|
} else {
|
|
|
|
|
contactMethods[jellyseerr.FieldEmailEnabled] = req.EmailContact
|
|
|
|
|
}
|
|
|
|
@ -534,11 +534,9 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
if emailEnabled || discordVerified || telegramVerified {
|
|
|
|
|
err := app.js.ModifyNotifications(id, contactMethods)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to sync contact methods with Jellyseerr: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedSyncContactMethods, lm.Jellyseerr, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
app.debug.Printf("Skipping Jellyseerr: Profile \"%s\" was empty", invite.Profile)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if matrixVerified {
|
|
|
|
@ -551,14 +549,13 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
}
|
|
|
|
|
if (emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "") || telegramVerified || discordVerified || matrixVerified {
|
|
|
|
|
name := app.getAddressOrName(user.ID)
|
|
|
|
|
app.debug.Printf("%s: Sending welcome message to %s", req.Username, name)
|
|
|
|
|
msg, err := app.email.constructWelcome(req.Username, expiry, app, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("%s: Failed to construct welcome message: %v", req.Username, err)
|
|
|
|
|
app.err.Printf(lm.FailedConstructWelcomeMessage, id, err)
|
|
|
|
|
} else if err := app.sendByID(msg, user.ID); err != nil {
|
|
|
|
|
app.err.Printf("%s: Failed to send welcome message: %v", req.Username, err)
|
|
|
|
|
app.err.Printf(lm.FailedSendWelcomeMessage, id, req.Email, err)
|
|
|
|
|
} else {
|
|
|
|
|
app.info.Printf("%s: Sent welcome message to \"%s\"", req.Username, name)
|
|
|
|
|
app.info.Printf(lm.SentWelcomeMessage, id, req.Email)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
app.jf.CacheExpiry = time.Now()
|
|
|
|
@ -576,14 +573,13 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|
|
|
|
func (app *appContext) NewUser(gc *gin.Context) {
|
|
|
|
|
var req newUserDTO
|
|
|
|
|
gc.BindJSON(&req)
|
|
|
|
|
app.debug.Printf("%s: New user attempt", req.Code)
|
|
|
|
|
if app.config.Section("captcha").Key("enabled").MustBool(false) && !app.verifyCaptcha(req.Code, req.CaptchaID, req.CaptchaText, false) {
|
|
|
|
|
app.info.Printf("%s: New user failed: Captcha Incorrect", req.Code)
|
|
|
|
|
app.info.Printf(lm.FailedCreateUser, lm.Jellyfin, req.Username, lm.IncorrectCaptcha)
|
|
|
|
|
respond(400, "errorCaptcha", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if !app.checkInvite(req.Code, false, "") {
|
|
|
|
|
app.info.Printf("%s New user failed: invalid code", req.Code)
|
|
|
|
|
app.info.Printf(lm.FailedCreateUser, lm.Jellyfin, req.Username, fmt.Sprintf(lm.InvalidInviteCode, req.Code))
|
|
|
|
|
respond(401, "errorInvalidCode", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -597,18 +593,15 @@ func (app *appContext) NewUser(gc *gin.Context) {
|
|
|
|
|
}
|
|
|
|
|
if !valid {
|
|
|
|
|
// 200 bcs idk what i did in js
|
|
|
|
|
app.info.Printf("%s: New user failed: Invalid password", req.Code)
|
|
|
|
|
gc.JSON(200, validation)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if emailEnabled {
|
|
|
|
|
if app.config.Section("email").Key("required").MustBool(false) && !strings.Contains(req.Email, "@") {
|
|
|
|
|
app.info.Printf("%s: New user failed: Email Required", req.Code)
|
|
|
|
|
respond(400, "errorNoEmail", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if app.config.Section("email").Key("require_unique").MustBool(false) && req.Email != "" && app.EmailAddressExists(req.Email) {
|
|
|
|
|
app.info.Printf("%s: New user failed: Email already in use", req.Code)
|
|
|
|
|
respond(400, "errorEmailLinked", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -653,7 +646,7 @@ func (app *appContext) EnableDisableUsers(gc *gin.Context) {
|
|
|
|
|
msg, err = app.email.constructDisabled(req.Reason, app, false)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to construct account enabled/disabled emails: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedConstructEnableDisableMessage, "?", err)
|
|
|
|
|
sendMail = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -665,14 +658,14 @@ func (app *appContext) EnableDisableUsers(gc *gin.Context) {
|
|
|
|
|
user, status, err := app.jf.UserByID(userID, false)
|
|
|
|
|
if status != 200 || err != nil {
|
|
|
|
|
errors["GetUser"][userID] = fmt.Sprintf("%d %v", status, err)
|
|
|
|
|
app.err.Printf("Failed to get user \"%s\" (%d): %v", userID, status, err)
|
|
|
|
|
app.err.Printf(lm.FailedGetUser, userID, lm.Jellyfin, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
user.Policy.IsDisabled = !req.Enabled
|
|
|
|
|
status, err = app.jf.SetPolicy(userID, user.Policy)
|
|
|
|
|
if !(status == 200 || status == 204) || err != nil {
|
|
|
|
|
errors["SetPolicy"][userID] = fmt.Sprintf("%d %v", status, err)
|
|
|
|
|
app.err.Printf("Failed to set policy for user \"%s\" (%d): %v", userID, status, err)
|
|
|
|
|
app.err.Printf(lm.FailedApplyTemplate, "policy", lm.Jellyfin, userID, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -687,7 +680,7 @@ func (app *appContext) EnableDisableUsers(gc *gin.Context) {
|
|
|
|
|
|
|
|
|
|
if sendMail && req.Notify {
|
|
|
|
|
if err := app.sendByID(msg, userID); err != nil {
|
|
|
|
|
app.err.Printf("Failed to send account enabled/disabled email: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedSendEnableDisableMessage, userID, "?", err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -720,7 +713,7 @@ func (app *appContext) DeleteUsers(gc *gin.Context) {
|
|
|
|
|
if sendMail {
|
|
|
|
|
msg, err = app.email.constructDeleted(req.Reason, app, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to construct account deletion emails: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedConstructDeletionMessage, "?", err)
|
|
|
|
|
sendMail = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -731,7 +724,7 @@ func (app *appContext) DeleteUsers(gc *gin.Context) {
|
|
|
|
|
if id, ok := ombiUser["id"]; ok {
|
|
|
|
|
status, err := app.ombi.DeleteUser(id.(string))
|
|
|
|
|
if err != nil || status != 200 {
|
|
|
|
|
app.err.Printf("Failed to delete ombi user (%d): %v", status, err)
|
|
|
|
|
app.err.Printf(lm.FailedDeleteUser, lm.Ombi, userID, err)
|
|
|
|
|
errors[userID] = fmt.Sprintf("Ombi: %d %v, ", status, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -765,14 +758,14 @@ func (app *appContext) DeleteUsers(gc *gin.Context) {
|
|
|
|
|
|
|
|
|
|
if sendMail && req.Notify {
|
|
|
|
|
if err := app.sendByID(msg, userID); err != nil {
|
|
|
|
|
app.err.Printf("Failed to send account deletion email: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedSendDeletionMessage, userID, "?", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
app.jf.CacheExpiry = time.Now()
|
|
|
|
|
if len(errors) == len(req.Users) {
|
|
|
|
|
respondBool(500, false, gc)
|
|
|
|
|
app.err.Printf("Account deletion failed: %s", errors[req.Users[0]])
|
|
|
|
|
app.err.Printf(lm.FailedDeleteUsers, lm.Jellyfin, errors[req.Users[0]])
|
|
|
|
|
return
|
|
|
|
|
} else if len(errors) != 0 {
|
|
|
|
|
gc.JSON(500, errors)
|
|
|
|
@ -801,10 +794,8 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) {
|
|
|
|
|
base := time.Now()
|
|
|
|
|
if expiry, ok := app.storage.GetUserExpiryKey(id); ok {
|
|
|
|
|
base = expiry.Expiry
|
|
|
|
|
app.debug.Printf("Expiry extended for \"%s\"", id)
|
|
|
|
|
} else {
|
|
|
|
|
app.debug.Printf("Created expiry for \"%s\"", id)
|
|
|
|
|
}
|
|
|
|
|
app.debug.Printf(lm.ExtendCreateExpiry, id)
|
|
|
|
|
expiry := UserExpiry{}
|
|
|
|
|
if req.Timestamp != 0 {
|
|
|
|
|
expiry.Expiry = time.Unix(req.Timestamp, 0)
|
|
|
|
@ -820,11 +811,11 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) {
|
|
|
|
|
}
|
|
|
|
|
msg, err := app.email.constructExpiryAdjusted(user.Name, exp, req.Reason, app, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("%s: Failed to construct expiry adjustment notification: %v", uid, err)
|
|
|
|
|
app.err.Printf(lm.FailedConstructExpiryAdjustmentMessage, uid, err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if err := app.sendByID(msg, uid); err != nil {
|
|
|
|
|
app.err.Printf("%s: Failed to send expiry adjustment notification: %v", uid, err)
|
|
|
|
|
app.err.Printf(lm.FailedSendExpiryAdjustmentMessage, uid, "?", err)
|
|
|
|
|
}
|
|
|
|
|
}(id, expiry.Expiry)
|
|
|
|
|
}
|
|
|
|
@ -867,17 +858,17 @@ func (app *appContext) EnableReferralForUsers(gc *gin.Context) {
|
|
|
|
|
profile, ok := app.storage.GetProfileKey(source)
|
|
|
|
|
err := app.storage.db.Get(profile.ReferralTemplateKey, &baseInv)
|
|
|
|
|
if !ok || profile.ReferralTemplateKey == "" || err != nil {
|
|
|
|
|
app.debug.Printf("Couldn't find template to source from")
|
|
|
|
|
app.debug.Printf(lm.FailedGetReferralTemplate, profile.ReferralTemplateKey, err)
|
|
|
|
|
respondBool(400, false, gc)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
app.debug.Printf("Found referral template in profile: %+v\n", profile.ReferralTemplateKey)
|
|
|
|
|
app.debug.Printf(lm.GetReferralTemplate, profile.ReferralTemplateKey)
|
|
|
|
|
} else if mode == "invite" {
|
|
|
|
|
// Get the invite, and modify it to turn it into a referral
|
|
|
|
|
err := app.storage.db.Get(source, &baseInv)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.debug.Printf("Couldn't find invite to source from")
|
|
|
|
|
app.debug.Printf(lm.InvalidInviteCode, source)
|
|
|
|
|
respondBool(400, false, gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -949,33 +940,34 @@ func (app *appContext) Announce(gc *gin.Context) {
|
|
|
|
|
for _, userID := range req.Users {
|
|
|
|
|
user, status, err := app.jf.UserByID(userID, false)
|
|
|
|
|
if status != 200 || err != nil {
|
|
|
|
|
app.err.Printf("Failed to get user with ID \"%s\" (%d): %v", userID, status, err)
|
|
|
|
|
app.err.Printf(lm.FailedGetUser, userID, lm.Jellyfin, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
msg, err := app.email.constructTemplate(req.Subject, req.Message, app, user.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to construct announcement message: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedConstructAnnouncementMessage, userID, err)
|
|
|
|
|
respondBool(500, false, gc)
|
|
|
|
|
return
|
|
|
|
|
} else if err := app.sendByID(msg, userID); err != nil {
|
|
|
|
|
app.err.Printf("Failed to send announcement message: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedSendAnnouncementMessage, userID, "?", err)
|
|
|
|
|
respondBool(500, false, gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
app.info.Printf(lm.SentAnnouncementMessage, userID, "?")
|
|
|
|
|
} else {
|
|
|
|
|
msg, err := app.email.constructTemplate(req.Subject, req.Message, app)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to construct announcement messages: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedConstructAnnouncementMessage, "*", err)
|
|
|
|
|
respondBool(500, false, gc)
|
|
|
|
|
return
|
|
|
|
|
} else if err := app.sendByID(msg, req.Users...); err != nil {
|
|
|
|
|
app.err.Printf("Failed to send announcement messages: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedSendAnnouncementMessage, "*", "?", err)
|
|
|
|
|
respondBool(500, false, gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
app.info.Printf(lm.SentAnnouncementMessage, "*", "?")
|
|
|
|
|
}
|
|
|
|
|
app.info.Println("Sent announcement messages")
|
|
|
|
|
respondBool(200, true, gc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1063,7 +1055,6 @@ func (app *appContext) AdminPasswordReset(gc *gin.Context) {
|
|
|
|
|
var req AdminPasswordResetDTO
|
|
|
|
|
gc.BindJSON(&req)
|
|
|
|
|
if req.Users == nil || len(req.Users) == 0 {
|
|
|
|
|
app.debug.Println("Ignoring empty request for PWR")
|
|
|
|
|
respondBool(400, false, gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -1074,7 +1065,7 @@ func (app *appContext) AdminPasswordReset(gc *gin.Context) {
|
|
|
|
|
for _, id := range req.Users {
|
|
|
|
|
pwr, err = app.GenInternalReset(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to get user from Jellyfin: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedGetUser, id, lm.Jellyfin, err)
|
|
|
|
|
respondBool(500, false, gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -1100,13 +1091,13 @@ func (app *appContext) AdminPasswordReset(gc *gin.Context) {
|
|
|
|
|
}, app, false,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to construct password reset message for \"%s\": %v", pwr.Username, err)
|
|
|
|
|
app.err.Printf(lm.FailedConstructPWRMessage, id, err)
|
|
|
|
|
respondBool(500, false, gc)
|
|
|
|
|
return
|
|
|
|
|
} else if err := app.sendByID(msg, id); err != nil {
|
|
|
|
|
app.err.Printf("Failed to send password reset message to \"%s\": %v", sendAddress, err)
|
|
|
|
|
app.err.Printf(lm.FailedSendPWRMessage, id, sendAddress, err)
|
|
|
|
|
} else {
|
|
|
|
|
app.info.Printf("Sent password reset message to \"%s\"", sendAddress)
|
|
|
|
|
app.info.Printf(lm.SentPWRMessage, id, sendAddress)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1125,12 +1116,11 @@ func (app *appContext) AdminPasswordReset(gc *gin.Context) {
|
|
|
|
|
// @Security Bearer
|
|
|
|
|
// @tags Users
|
|
|
|
|
func (app *appContext) GetUsers(gc *gin.Context) {
|
|
|
|
|
app.debug.Println("Users requested")
|
|
|
|
|
var resp getUsersDTO
|
|
|
|
|
users, status, err := app.jf.GetUsers(false)
|
|
|
|
|
resp.UserList = make([]respUser, len(users))
|
|
|
|
|
if !(status == 200 || status == 204) || err != nil {
|
|
|
|
|
app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
|
|
|
|
|
app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err)
|
|
|
|
|
respond(500, "Couldn't get users", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -1202,10 +1192,9 @@ func (app *appContext) GetUsers(gc *gin.Context) {
|
|
|
|
|
func (app *appContext) SetAccountsAdmin(gc *gin.Context) {
|
|
|
|
|
var req setAccountsAdminDTO
|
|
|
|
|
gc.BindJSON(&req)
|
|
|
|
|
app.debug.Println("Admin modification requested")
|
|
|
|
|
users, status, err := app.jf.GetUsers(false)
|
|
|
|
|
if !(status == 200 || status == 204) || err != nil {
|
|
|
|
|
app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
|
|
|
|
|
app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err)
|
|
|
|
|
respond(500, "Couldn't get users", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -1218,9 +1207,9 @@ func (app *appContext) SetAccountsAdmin(gc *gin.Context) {
|
|
|
|
|
}
|
|
|
|
|
emailStore.Admin = admin
|
|
|
|
|
app.storage.SetEmailsKey(id, emailStore)
|
|
|
|
|
app.info.Printf(lm.UserAdminAdjusted, id, admin)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
app.info.Println("Email list modified")
|
|
|
|
|
respondBool(204, true, gc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1235,10 +1224,9 @@ func (app *appContext) SetAccountsAdmin(gc *gin.Context) {
|
|
|
|
|
func (app *appContext) ModifyLabels(gc *gin.Context) {
|
|
|
|
|
var req modifyEmailsDTO
|
|
|
|
|
gc.BindJSON(&req)
|
|
|
|
|
app.debug.Println("Label modification requested")
|
|
|
|
|
users, status, err := app.jf.GetUsers(false)
|
|
|
|
|
if !(status == 200 || status == 204) || err != nil {
|
|
|
|
|
app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
|
|
|
|
|
app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err)
|
|
|
|
|
respond(500, "Couldn't get users", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -1250,10 +1238,10 @@ func (app *appContext) ModifyLabels(gc *gin.Context) {
|
|
|
|
|
emailStore = oldEmail
|
|
|
|
|
}
|
|
|
|
|
emailStore.Label = label
|
|
|
|
|
app.debug.Println(lm.UserLabelAdjusted, id, label)
|
|
|
|
|
app.storage.SetEmailsKey(id, emailStore)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
app.info.Println("Email list modified")
|
|
|
|
|
respondBool(204, true, gc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1275,21 +1263,21 @@ func (app *appContext) modifyEmail(jfID string, addr string) {
|
|
|
|
|
ombiUser["emailAddress"] = addr
|
|
|
|
|
code, err = app.ombi.ModifyUser(ombiUser)
|
|
|
|
|
if code != 200 || err != nil {
|
|
|
|
|
app.err.Printf("%s: Failed to change ombi email address (%d): %v", ombiUser["userName"].(string), code, err)
|
|
|
|
|
app.err.Printf(lm.FailedSetEmailAddress, lm.Ombi, jfID, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if app.config.Section("jellyseerr").Key("enabled").MustBool(false) {
|
|
|
|
|
err := app.js.ModifyMainUserSettings(jfID, jellyseerr.MainUserSettings{Email: addr})
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to set Jellyseerr email address: %v\n", err)
|
|
|
|
|
app.err.Printf(lm.FailedSetEmailAddress, lm.Jellyseerr, jfID, err)
|
|
|
|
|
} else if contactPrefChanged {
|
|
|
|
|
contactMethods := map[jellyseerr.NotificationsField]any{
|
|
|
|
|
jellyseerr.FieldEmailEnabled: true,
|
|
|
|
|
}
|
|
|
|
|
err := app.js.ModifyNotifications(jfID, contactMethods)
|
|
|
|
|
if err != nil {
|
|
|
|
|
app.err.Printf("Failed to sync contact methods with Jellyseerr: %v", err)
|
|
|
|
|
app.err.Printf(lm.FailedSyncContactMethods, lm.Jellyseerr, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1306,10 +1294,9 @@ func (app *appContext) modifyEmail(jfID string, addr string) {
|
|
|
|
|
func (app *appContext) ModifyEmails(gc *gin.Context) {
|
|
|
|
|
var req modifyEmailsDTO
|
|
|
|
|
gc.BindJSON(&req)
|
|
|
|
|
app.debug.Println("Email modification requested")
|
|
|
|
|
users, status, err := app.jf.GetUsers(false)
|
|
|
|
|
if !(status == 200 || status == 204) || err != nil {
|
|
|
|
|
app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
|
|
|
|
|
app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err)
|
|
|
|
|
respond(500, "Couldn't get users", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -1318,6 +1305,8 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
|
|
|
|
|
if address, ok := req[id]; ok {
|
|
|
|
|
app.modifyEmail(id, address)
|
|
|
|
|
|
|
|
|
|
app.info.Printf(lm.UserEmailAdjusted, gc.GetString("jfId"))
|
|
|
|
|
|
|
|
|
|
activityType := ActivityContactLinked
|
|
|
|
|
if address == "" {
|
|
|
|
|
activityType = ActivityContactUnlinked
|
|
|
|
@ -1332,7 +1321,6 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
|
|
|
|
|
}, gc, false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
app.info.Println("Email list modified")
|
|
|
|
|
respondBool(200, true, gc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1348,7 +1336,8 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
|
|
|
|
|
app.info.Println("User settings change requested")
|
|
|
|
|
var req userSettingsDTO
|
|
|
|
|
gc.BindJSON(&req)
|
|
|
|
|
applyingFrom := "profile"
|
|
|
|
|
applyingFromType := lm.Profile
|
|
|
|
|
applyingFromSource := "?"
|
|
|
|
|
var policy mediabrowser.Policy
|
|
|
|
|
var configuration mediabrowser.Configuration
|
|
|
|
|
var displayprefs map[string]interface{}
|
|
|
|
@ -1359,18 +1348,21 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
|
|
|
|
|
// Check profile exists & isn't empty
|
|
|
|
|
profile, ok := app.storage.GetProfileKey(req.Profile)
|
|
|
|
|
if !ok {
|
|
|
|
|
app.err.Printf("Couldn't find profile \"%s\" or profile was empty", req.Profile)
|
|
|
|
|
app.err.Printf(lm.FailedGetProfile, req.Profile)
|
|
|
|
|
respond(500, "Couldn't find profile", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
applyingFromSource = req.Profile
|
|
|
|
|
if req.Homescreen {
|
|
|
|
|
if !profile.Homescreen {
|
|
|
|
|
app.err.Printf("No homescreen saved in profile \"%s\"", req.Profile)
|
|
|
|
|
if profile.Homescreen {
|
|
|
|
|
configuration = profile.Configuration
|
|
|
|
|
displayprefs = profile.Displayprefs
|
|
|
|
|
} else {
|
|
|
|
|
req.Homescreen = false
|
|
|
|
|
app.err.Printf(lm.ProfileNoHomescreen, req.Profile)
|
|
|
|
|
respond(500, "No homescreen template available", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
configuration = profile.Configuration
|
|
|
|
|
displayprefs = profile.Displayprefs
|
|
|
|
|
}
|
|
|
|
|
if req.Policy {
|
|
|
|
|
policy = profile.Policy
|
|
|
|
@ -1387,29 +1379,29 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if req.From == "user" {
|
|
|
|
|
applyingFrom = "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 {
|
|
|
|
|
app.err.Printf("Failed to get user from Jellyfin (%d): %v", status, err)
|
|
|
|
|
app.err.Printf(lm.FailedGetUser, req.ID, lm.Jellyfin, err)
|
|
|
|
|
respond(500, "Couldn't get user", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
applyingFrom = "\"" + user.Name + "\""
|
|
|
|
|
applyingFromSource = user.Name
|
|
|
|
|
if req.Policy {
|
|
|
|
|
policy = user.Policy
|
|
|
|
|
}
|
|
|
|
|
if req.Homescreen {
|
|
|
|
|
displayprefs, status, err = app.jf.GetDisplayPreferences(req.ID)
|
|
|
|
|
if !(status == 200 || status == 204) || err != nil {
|
|
|
|
|
app.err.Printf("Failed to get DisplayPrefs (%d): %v", status, err)
|
|
|
|
|
app.err.Printf(lm.FailedGetJellyfinDisplayPrefs, req.ID, err)
|
|
|
|
|
respond(500, "Couldn't get displayprefs", gc)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
configuration = user.Configuration
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
app.info.Printf("Applying settings to %d user(s) from %s", len(req.ApplyTo), applyingFrom)
|
|
|
|
|
app.info.Printf(lm.ApplyingTemplatesFrom, applyingFromType, applyingFromSource, len(req.ApplyTo))
|
|
|
|
|
errors := errorListDTO{
|
|
|
|
|
"policy": map[string]string{},
|
|
|
|
|
"homescreen": map[string]string{},
|
|
|
|
@ -1420,9 +1412,10 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
|
|
|
|
|
and can crash and mess up its database. Issue #160 says this occurs when more
|
|
|
|
|
than 100 users are modified. A delay totalling 500ms between requests is used
|
|
|
|
|
if so. */
|
|
|
|
|
var shouldDelay bool = len(req.ApplyTo) >= 100
|
|
|
|
|
const requestDelayThreshold = 100
|
|
|
|
|
var shouldDelay bool = len(req.ApplyTo) >= requestDelayThreshold
|
|
|
|
|
if shouldDelay {
|
|
|
|
|
app.debug.Println("Adding delay between requests for large batch")
|
|
|
|
|
app.debug.Printf(lm.DelayingRequests, requestDelayThreshold)
|
|
|
|
|
}
|
|
|
|
|
for _, id := range req.ApplyTo {
|
|
|
|
|
var status int
|
|
|
|
|