2022-03-22 14:58:39 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-07-31 17:49:52 +00:00
|
|
|
"fmt"
|
2022-03-22 14:58:39 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
2024-07-31 17:49:52 +00:00
|
|
|
lm "github.com/hrfee/jfa-go/logmessages"
|
2023-06-24 20:29:16 +00:00
|
|
|
"github.com/timshannon/badgerhold/v4"
|
2022-03-22 14:58:39 +00:00
|
|
|
)
|
|
|
|
|
2024-08-21 19:35:08 +00:00
|
|
|
// @Summary Get the names of all available profile.
|
|
|
|
// @Produce json
|
|
|
|
// @Success 200 {object} getProfileNamesDTO
|
|
|
|
// @Router /profiles/names [get]
|
|
|
|
// @Security Bearer
|
|
|
|
// @tags Profiles & Settings
|
|
|
|
func (app *appContext) GetProfileNames(gc *gin.Context) {
|
|
|
|
fullProfileList := app.storage.GetProfiles()
|
|
|
|
profiles := make([]string, len(fullProfileList))
|
|
|
|
if len(profiles) != 0 {
|
|
|
|
defaultProfile := app.storage.GetDefaultProfile()
|
|
|
|
profiles[0] = defaultProfile.Name
|
|
|
|
i := 1
|
|
|
|
if len(fullProfileList) > 1 {
|
|
|
|
app.storage.db.ForEach(badgerhold.Where("Name").Ne(profiles[0]), func(p *Profile) error {
|
|
|
|
profiles[i] = p.Name
|
|
|
|
i++
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
resp := getProfileNamesDTO{
|
|
|
|
Profiles: profiles,
|
|
|
|
}
|
|
|
|
gc.JSON(200, resp)
|
|
|
|
}
|
|
|
|
|
|
|
|
// @Summary Get all available profiles, indexed by their names.
|
2022-03-22 14:58:39 +00:00
|
|
|
// @Produce json
|
|
|
|
// @Success 200 {object} getProfilesDTO
|
|
|
|
// @Router /profiles [get]
|
|
|
|
// @Security Bearer
|
|
|
|
// @tags Profiles & Settings
|
|
|
|
func (app *appContext) GetProfiles(gc *gin.Context) {
|
|
|
|
out := getProfilesDTO{
|
2023-06-24 20:29:16 +00:00
|
|
|
DefaultProfile: app.storage.GetDefaultProfile().Name,
|
2022-03-22 14:58:39 +00:00
|
|
|
Profiles: map[string]profileDTO{},
|
|
|
|
}
|
2023-09-06 21:00:44 +00:00
|
|
|
referralsEnabled := app.config.Section("user_page").Key("referrals").MustBool(false)
|
|
|
|
baseInv := Invite{}
|
2023-06-24 20:29:16 +00:00
|
|
|
for _, p := range app.storage.GetProfiles() {
|
2023-09-06 21:00:44 +00:00
|
|
|
pdto := profileDTO{
|
|
|
|
Admin: p.Admin,
|
|
|
|
LibraryAccess: p.LibraryAccess,
|
|
|
|
FromUser: p.FromUser,
|
|
|
|
Ombi: p.Ombi != nil,
|
2024-07-30 15:36:59 +00:00
|
|
|
Jellyseerr: p.Jellyseerr.Enabled,
|
2023-09-06 21:00:44 +00:00
|
|
|
ReferralsEnabled: false,
|
2022-03-22 14:58:39 +00:00
|
|
|
}
|
2023-09-06 21:00:44 +00:00
|
|
|
if referralsEnabled {
|
|
|
|
err := app.storage.db.Get(p.ReferralTemplateKey, &baseInv)
|
|
|
|
if p.ReferralTemplateKey != "" && err == nil {
|
|
|
|
pdto.ReferralsEnabled = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out.Profiles[p.Name] = pdto
|
2022-03-22 14:58:39 +00:00
|
|
|
}
|
|
|
|
gc.JSON(200, out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// @Summary Set the default profile to use.
|
|
|
|
// @Produce json
|
|
|
|
// @Param profileChangeDTO body profileChangeDTO true "Default profile object"
|
|
|
|
// @Success 200 {object} boolResponse
|
|
|
|
// @Failure 500 {object} stringResponse
|
|
|
|
// @Router /profiles/default [post]
|
|
|
|
// @Security Bearer
|
|
|
|
// @tags Profiles & Settings
|
|
|
|
func (app *appContext) SetDefaultProfile(gc *gin.Context) {
|
|
|
|
req := profileChangeDTO{}
|
|
|
|
gc.BindJSON(&req)
|
2024-07-31 17:49:52 +00:00
|
|
|
app.info.Printf(lm.SetDefaultProfile, req.Name)
|
2023-06-24 20:29:16 +00:00
|
|
|
if _, ok := app.storage.GetProfileKey(req.Name); !ok {
|
2024-07-31 17:49:52 +00:00
|
|
|
msg := fmt.Sprintf(lm.FailedGetProfile, req.Name)
|
|
|
|
app.err.Println(msg)
|
|
|
|
respond(500, msg, gc)
|
2022-03-22 14:58:39 +00:00
|
|
|
return
|
|
|
|
}
|
2023-06-24 20:29:16 +00:00
|
|
|
app.storage.db.ForEach(&badgerhold.Query{}, func(profile *Profile) error {
|
|
|
|
if profile.Name == req.Name {
|
|
|
|
profile.Default = true
|
2022-03-22 14:58:39 +00:00
|
|
|
} else {
|
2023-06-24 20:29:16 +00:00
|
|
|
profile.Default = false
|
2022-03-22 14:58:39 +00:00
|
|
|
}
|
2023-06-24 20:29:16 +00:00
|
|
|
app.storage.SetProfileKey(profile.Name, *profile)
|
|
|
|
return nil
|
|
|
|
})
|
2022-03-22 14:58:39 +00:00
|
|
|
respondBool(200, true, gc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// @Summary Create a profile based on a Jellyfin user's settings.
|
|
|
|
// @Produce json
|
|
|
|
// @Param newProfileDTO body newProfileDTO true "New profile object"
|
|
|
|
// @Success 200 {object} boolResponse
|
|
|
|
// @Failure 500 {object} stringResponse
|
|
|
|
// @Router /profiles [post]
|
|
|
|
// @Security Bearer
|
|
|
|
// @tags Profiles & Settings
|
|
|
|
func (app *appContext) CreateProfile(gc *gin.Context) {
|
|
|
|
var req newProfileDTO
|
|
|
|
gc.BindJSON(&req)
|
|
|
|
app.jf.CacheExpiry = time.Now()
|
2024-08-06 13:48:31 +00:00
|
|
|
user, err := app.jf.UserByID(req.ID, false)
|
|
|
|
if err != nil {
|
2024-07-31 17:49:52 +00:00
|
|
|
app.err.Printf(lm.FailedGetUsers, lm.Jellyfin, err)
|
2022-03-22 14:58:39 +00:00
|
|
|
respond(500, "Couldn't get user", gc)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
profile := Profile{
|
2023-06-26 11:57:16 +00:00
|
|
|
FromUser: user.Name,
|
|
|
|
Policy: user.Policy,
|
|
|
|
Homescreen: req.Homescreen,
|
2022-03-22 14:58:39 +00:00
|
|
|
}
|
2024-07-31 17:49:52 +00:00
|
|
|
app.debug.Printf(lm.CreateProfileFromUser, user.Name)
|
2022-03-22 14:58:39 +00:00
|
|
|
if req.Homescreen {
|
|
|
|
profile.Configuration = user.Configuration
|
2024-08-06 13:48:31 +00:00
|
|
|
profile.Displayprefs, err = app.jf.GetDisplayPreferences(req.ID)
|
|
|
|
if err != nil {
|
2024-07-31 17:49:52 +00:00
|
|
|
app.err.Printf(lm.FailedGetJellyfinDisplayPrefs, req.ID, err)
|
2022-03-22 14:58:39 +00:00
|
|
|
respond(500, "Couldn't get displayprefs", gc)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2023-06-24 20:29:16 +00:00
|
|
|
app.storage.SetProfileKey(req.Name, profile)
|
2023-10-11 11:00:38 +00:00
|
|
|
// Refresh discord bots, profile list
|
|
|
|
if discordEnabled {
|
|
|
|
app.discord.UpdateCommands()
|
|
|
|
}
|
2022-03-22 14:58:39 +00:00
|
|
|
respondBool(200, true, gc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// @Summary Delete an existing profile
|
|
|
|
// @Produce json
|
|
|
|
// @Param profileChangeDTO body profileChangeDTO true "Delete profile object"
|
|
|
|
// @Success 200 {object} boolResponse
|
|
|
|
// @Router /profiles [delete]
|
|
|
|
// @Security Bearer
|
|
|
|
// @tags Profiles & Settings
|
|
|
|
func (app *appContext) DeleteProfile(gc *gin.Context) {
|
|
|
|
req := profileChangeDTO{}
|
|
|
|
gc.BindJSON(&req)
|
|
|
|
name := req.Name
|
2023-06-24 20:29:16 +00:00
|
|
|
app.storage.DeleteProfileKey(name)
|
2022-03-22 14:58:39 +00:00
|
|
|
respondBool(200, true, gc)
|
|
|
|
}
|
2023-09-06 21:00:44 +00:00
|
|
|
|
|
|
|
// @Summary Enable referrals for a profile, sourced from the given invite by its code.
|
|
|
|
// @Produce json
|
|
|
|
// @Param profile path string true "name of profile to enable referrals for."
|
|
|
|
// @Param invite path string true "invite code to create referral template from."
|
2023-11-10 15:07:29 +00:00
|
|
|
// @Param useExpiry path string true "with-expiry or none."
|
2023-09-06 21:00:44 +00:00
|
|
|
// @Success 200 {object} boolResponse
|
|
|
|
// @Failure 400 {object} stringResponse
|
|
|
|
// @Failure 500 {object} stringResponse
|
2023-11-10 15:07:29 +00:00
|
|
|
// @Router /profiles/referral/{profile}/{invite}/{useExpiry} [post]
|
2023-09-06 21:00:44 +00:00
|
|
|
// @Security Bearer
|
|
|
|
// @tags Profiles & Settings
|
|
|
|
func (app *appContext) EnableReferralForProfile(gc *gin.Context) {
|
|
|
|
profileName := gc.Param("profile")
|
|
|
|
invCode := gc.Param("invite")
|
2023-11-10 15:07:29 +00:00
|
|
|
useExpiry := gc.Param("useExpiry") == "with-expiry"
|
2023-09-06 21:00:44 +00:00
|
|
|
inv, ok := app.storage.GetInvitesKey(invCode)
|
|
|
|
if !ok {
|
|
|
|
respond(400, "Invalid invite code", gc)
|
2024-07-31 17:49:52 +00:00
|
|
|
app.err.Printf(lm.InvalidInviteCode, invCode)
|
2023-09-06 21:00:44 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
profile, ok := app.storage.GetProfileKey(profileName)
|
|
|
|
if !ok {
|
|
|
|
respond(400, "Invalid profile", gc)
|
2024-07-31 17:49:52 +00:00
|
|
|
app.err.Printf(lm.FailedGetProfile, profileName)
|
2023-09-06 21:00:44 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate new code for referral template
|
2023-10-11 10:30:28 +00:00
|
|
|
inv.Code = GenerateInviteCode()
|
2023-11-10 15:07:29 +00:00
|
|
|
expiryDelta := inv.ValidTill.Sub(inv.Created)
|
2023-09-06 21:00:44 +00:00
|
|
|
inv.Created = time.Now()
|
2023-11-10 15:07:29 +00:00
|
|
|
if useExpiry {
|
|
|
|
inv.ValidTill = inv.Created.Add(expiryDelta)
|
|
|
|
} else {
|
|
|
|
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
|
|
|
|
}
|
2023-09-06 21:00:44 +00:00
|
|
|
inv.IsReferral = true
|
2023-11-10 15:07:29 +00:00
|
|
|
inv.UseReferralExpiry = useExpiry
|
2023-09-06 21:00:44 +00:00
|
|
|
// Since this is a template for multiple users, ReferrerJellyfinID is not set.
|
|
|
|
// inv.ReferrerJellyfinID = ...
|
|
|
|
|
|
|
|
app.storage.SetInvitesKey(inv.Code, inv)
|
|
|
|
|
|
|
|
profile.ReferralTemplateKey = inv.Code
|
|
|
|
|
|
|
|
app.storage.SetProfileKey(profile.Name, profile)
|
|
|
|
|
|
|
|
respondBool(200, true, gc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// @Summary Disable referrals for a profile, and removes the referral template. no-op if not enabled.
|
|
|
|
// @Produce json
|
|
|
|
// @Param profile path string true "name of profile to enable referrals for."
|
|
|
|
// @Success 200 {object} boolResponse
|
|
|
|
// @Router /profiles/referral/{profile} [delete]
|
|
|
|
// @Security Bearer
|
|
|
|
// @tags Profiles & Settings
|
|
|
|
func (app *appContext) DisableReferralForProfile(gc *gin.Context) {
|
2023-09-06 21:12:36 +00:00
|
|
|
profileName := gc.Param("profile")
|
|
|
|
profile, ok := app.storage.GetProfileKey(profileName)
|
|
|
|
if !ok {
|
|
|
|
respondBool(200, true, gc)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
app.storage.DeleteInvitesKey(profile.ReferralTemplateKey)
|
|
|
|
|
|
|
|
profile.ReferralTemplateKey = ""
|
|
|
|
|
|
|
|
app.storage.SetProfileKey(profileName, profile)
|
|
|
|
|
2023-09-06 21:00:44 +00:00
|
|
|
respondBool(200, true, gc)
|
|
|
|
}
|