mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
db: migrate user profiles
This commit is contained in:
parent
63948a6de0
commit
a735e4ff29
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/itchyny/timefmt-go"
|
"github.com/itchyny/timefmt-go"
|
||||||
"github.com/lithammer/shortuuid/v3"
|
"github.com/lithammer/shortuuid/v3"
|
||||||
|
"github.com/timshannon/badgerhold/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (app *appContext) checkInvites() {
|
func (app *appContext) checkInvites() {
|
||||||
@ -202,7 +203,7 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if req.Profile != "" {
|
if req.Profile != "" {
|
||||||
if _, ok := app.storage.profiles[req.Profile]; ok {
|
if _, ok := app.storage.GetProfileKey(req.Profile); ok {
|
||||||
invite.Profile = req.Profile
|
invite.Profile = req.Profile
|
||||||
} else {
|
} else {
|
||||||
invite.Profile = "Default"
|
invite.Profile = "Default"
|
||||||
@ -283,17 +284,18 @@ func (app *appContext) GetInvites(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
invites = append(invites, invite)
|
invites = append(invites, invite)
|
||||||
}
|
}
|
||||||
profiles := make([]string, len(app.storage.profiles))
|
fullProfileList := app.storage.GetProfiles()
|
||||||
if len(app.storage.profiles) != 0 {
|
profiles := make([]string, len(fullProfileList))
|
||||||
profiles[0] = app.storage.defaultProfile
|
if len(profiles) != 0 {
|
||||||
|
defaultProfile := app.storage.GetDefaultProfile()
|
||||||
|
profiles[0] = defaultProfile.Name
|
||||||
i := 1
|
i := 1
|
||||||
if len(app.storage.profiles) > 1 {
|
if len(fullProfileList) > 1 {
|
||||||
for p := range app.storage.profiles {
|
app.storage.db.ForEach(badgerhold.Where("Name").Ne(profiles[0]), func(p *Profile) error {
|
||||||
if p != app.storage.defaultProfile {
|
profiles[i] = p.Name
|
||||||
profiles[i] = p
|
i++
|
||||||
i++
|
return nil
|
||||||
}
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resp := getInvitesDTO{
|
resp := getInvitesDTO{
|
||||||
@ -316,7 +318,7 @@ func (app *appContext) SetProfile(gc *gin.Context) {
|
|||||||
gc.BindJSON(&req)
|
gc.BindJSON(&req)
|
||||||
app.debug.Printf("%s: Setting profile to \"%s\"", req.Invite, req.Profile)
|
app.debug.Printf("%s: Setting profile to \"%s\"", req.Invite, req.Profile)
|
||||||
// "" means "Don't apply profile"
|
// "" means "Don't apply profile"
|
||||||
if _, ok := app.storage.profiles[req.Profile]; !ok && req.Profile != "" {
|
if _, ok := app.storage.GetProfileKey(req.Profile); !ok && req.Profile != "" {
|
||||||
app.err.Printf("%s: Profile \"%s\" not found", req.Invite, req.Profile)
|
app.err.Printf("%s: Profile \"%s\" not found", req.Invite, req.Profile)
|
||||||
respond(500, "Profile not found", gc)
|
respond(500, "Profile not found", gc)
|
||||||
return
|
return
|
||||||
|
18
api-ombi.go
18
api-ombi.go
@ -71,7 +71,7 @@ func (app *appContext) SetOmbiProfile(gc *gin.Context) {
|
|||||||
var req ombiUser
|
var req ombiUser
|
||||||
gc.BindJSON(&req)
|
gc.BindJSON(&req)
|
||||||
profileName := gc.Param("profile")
|
profileName := gc.Param("profile")
|
||||||
profile, ok := app.storage.profiles[profileName]
|
profile, ok := app.storage.GetProfileKey(profileName)
|
||||||
if !ok {
|
if !ok {
|
||||||
respondBool(400, false, gc)
|
respondBool(400, false, gc)
|
||||||
return
|
return
|
||||||
@ -83,12 +83,7 @@ func (app *appContext) SetOmbiProfile(gc *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
profile.Ombi = template
|
profile.Ombi = template
|
||||||
app.storage.profiles[profileName] = profile
|
app.storage.SetProfileKey(profileName, profile)
|
||||||
if err := app.storage.storeProfiles(); err != nil {
|
|
||||||
respond(500, "Failed to store profile", gc)
|
|
||||||
app.err.Printf("Failed to store profiles: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
respondBool(204, true, gc)
|
respondBool(204, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,17 +98,12 @@ func (app *appContext) SetOmbiProfile(gc *gin.Context) {
|
|||||||
// @tags Ombi
|
// @tags Ombi
|
||||||
func (app *appContext) DeleteOmbiProfile(gc *gin.Context) {
|
func (app *appContext) DeleteOmbiProfile(gc *gin.Context) {
|
||||||
profileName := gc.Param("profile")
|
profileName := gc.Param("profile")
|
||||||
profile, ok := app.storage.profiles[profileName]
|
profile, ok := app.storage.GetProfileKey(profileName)
|
||||||
if !ok {
|
if !ok {
|
||||||
respondBool(400, false, gc)
|
respondBool(400, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
profile.Ombi = nil
|
profile.Ombi = nil
|
||||||
app.storage.profiles[profileName] = profile
|
app.storage.SetProfileKey(profileName, profile)
|
||||||
if err := app.storage.storeProfiles(); err != nil {
|
|
||||||
respond(500, "Failed to store profile", gc)
|
|
||||||
app.err.Printf("Failed to store profiles: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
respondBool(204, true, gc)
|
respondBool(204, true, gc)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/timshannon/badgerhold/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @Summary Get a list of profiles
|
// @Summary Get a list of profiles
|
||||||
@ -13,14 +14,13 @@ import (
|
|||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @tags Profiles & Settings
|
// @tags Profiles & Settings
|
||||||
func (app *appContext) GetProfiles(gc *gin.Context) {
|
func (app *appContext) GetProfiles(gc *gin.Context) {
|
||||||
app.storage.loadProfiles()
|
|
||||||
app.debug.Println("Profiles requested")
|
app.debug.Println("Profiles requested")
|
||||||
out := getProfilesDTO{
|
out := getProfilesDTO{
|
||||||
DefaultProfile: app.storage.defaultProfile,
|
DefaultProfile: app.storage.GetDefaultProfile().Name,
|
||||||
Profiles: map[string]profileDTO{},
|
Profiles: map[string]profileDTO{},
|
||||||
}
|
}
|
||||||
for name, p := range app.storage.profiles {
|
for _, p := range app.storage.GetProfiles() {
|
||||||
out.Profiles[name] = profileDTO{
|
out.Profiles[p.Name] = profileDTO{
|
||||||
Admin: p.Admin,
|
Admin: p.Admin,
|
||||||
LibraryAccess: p.LibraryAccess,
|
LibraryAccess: p.LibraryAccess,
|
||||||
FromUser: p.FromUser,
|
FromUser: p.FromUser,
|
||||||
@ -42,20 +42,20 @@ func (app *appContext) SetDefaultProfile(gc *gin.Context) {
|
|||||||
req := profileChangeDTO{}
|
req := profileChangeDTO{}
|
||||||
gc.BindJSON(&req)
|
gc.BindJSON(&req)
|
||||||
app.info.Printf("Setting default profile to \"%s\"", req.Name)
|
app.info.Printf("Setting default profile to \"%s\"", req.Name)
|
||||||
if _, ok := app.storage.profiles[req.Name]; !ok {
|
if _, ok := app.storage.GetProfileKey(req.Name); !ok {
|
||||||
app.err.Printf("Profile not found: \"%s\"", req.Name)
|
app.err.Printf("Profile not found: \"%s\"", req.Name)
|
||||||
respond(500, "Profile not found", gc)
|
respond(500, "Profile not found", gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for name, profile := range app.storage.profiles {
|
app.storage.db.ForEach(&badgerhold.Query{}, func(profile *Profile) error {
|
||||||
if name == req.Name {
|
if profile.Name == req.Name {
|
||||||
profile.Admin = true
|
profile.Default = true
|
||||||
app.storage.profiles[name] = profile
|
|
||||||
} else {
|
} else {
|
||||||
profile.Admin = false
|
profile.Default = false
|
||||||
}
|
}
|
||||||
}
|
app.storage.SetProfileKey(profile.Name, *profile)
|
||||||
app.storage.defaultProfile = req.Name
|
return nil
|
||||||
|
})
|
||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,10 +92,7 @@ func (app *appContext) CreateProfile(gc *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.storage.loadProfiles()
|
app.storage.SetProfileKey(req.Name, profile)
|
||||||
app.storage.profiles[req.Name] = profile
|
|
||||||
app.storage.storeProfiles()
|
|
||||||
app.storage.loadProfiles()
|
|
||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,12 +107,6 @@ func (app *appContext) DeleteProfile(gc *gin.Context) {
|
|||||||
req := profileChangeDTO{}
|
req := profileChangeDTO{}
|
||||||
gc.BindJSON(&req)
|
gc.BindJSON(&req)
|
||||||
name := req.Name
|
name := req.Name
|
||||||
if _, ok := app.storage.profiles[name]; ok {
|
app.storage.DeleteProfileKey(name)
|
||||||
if app.storage.defaultProfile == name {
|
|
||||||
app.storage.defaultProfile = ""
|
|
||||||
}
|
|
||||||
delete(app.storage.profiles, name)
|
|
||||||
}
|
|
||||||
app.storage.storeProfiles()
|
|
||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
18
api-users.go
18
api-users.go
@ -269,7 +269,6 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
|||||||
success = false
|
success = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
app.storage.loadProfiles()
|
|
||||||
invite, _ := app.storage.GetInvitesKey(req.Code)
|
invite, _ := app.storage.GetInvitesKey(req.Code)
|
||||||
app.checkInvite(req.Code, true, req.Username)
|
app.checkInvite(req.Code, true, req.Username)
|
||||||
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) {
|
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) {
|
||||||
@ -301,9 +300,9 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
|||||||
if invite.Profile != "" {
|
if invite.Profile != "" {
|
||||||
app.debug.Printf("Applying settings from profile \"%s\"", invite.Profile)
|
app.debug.Printf("Applying settings from profile \"%s\"", invite.Profile)
|
||||||
var ok bool
|
var ok bool
|
||||||
profile, ok = app.storage.profiles[invite.Profile]
|
profile, ok = app.storage.GetProfileKey(invite.Profile)
|
||||||
if !ok {
|
if !ok {
|
||||||
profile = app.storage.profiles["Default"]
|
profile = app.storage.GetDefaultProfile()
|
||||||
}
|
}
|
||||||
if profile.Policy.BlockedTags != nil {
|
if profile.Policy.BlockedTags != nil {
|
||||||
app.debug.Printf("Applying policy from profile \"%s\"", invite.Profile)
|
app.debug.Printf("Applying policy from profile \"%s\"", invite.Profile)
|
||||||
@ -1008,25 +1007,24 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
|
|||||||
var displayprefs map[string]interface{}
|
var displayprefs map[string]interface{}
|
||||||
var ombi map[string]interface{}
|
var ombi map[string]interface{}
|
||||||
if req.From == "profile" {
|
if req.From == "profile" {
|
||||||
app.storage.loadProfiles()
|
|
||||||
// Check profile exists & isn't empty
|
// Check profile exists & isn't empty
|
||||||
if _, ok := app.storage.profiles[req.Profile]; !ok || app.storage.profiles[req.Profile].Policy.BlockedTags == nil {
|
profile, ok := app.storage.GetProfileKey(req.Profile)
|
||||||
|
if !ok || profile.Policy.BlockedTags == nil {
|
||||||
app.err.Printf("Couldn't find profile \"%s\" or profile was empty", req.Profile)
|
app.err.Printf("Couldn't find profile \"%s\" or profile was empty", req.Profile)
|
||||||
respond(500, "Couldn't find profile", gc)
|
respond(500, "Couldn't find profile", gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if req.Homescreen {
|
if req.Homescreen {
|
||||||
if app.storage.profiles[req.Profile].Configuration.GroupedFolders == nil || len(app.storage.profiles[req.Profile].Displayprefs) == 0 {
|
if profile.Configuration.GroupedFolders == nil || len(profile.Displayprefs) == 0 {
|
||||||
app.err.Printf("No homescreen saved in profile \"%s\"", req.Profile)
|
app.err.Printf("No homescreen saved in profile \"%s\"", req.Profile)
|
||||||
respond(500, "No homescreen template available", gc)
|
respond(500, "No homescreen template available", gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
configuration = app.storage.profiles[req.Profile].Configuration
|
configuration = profile.Configuration
|
||||||
displayprefs = app.storage.profiles[req.Profile].Displayprefs
|
displayprefs = profile.Displayprefs
|
||||||
}
|
}
|
||||||
policy = app.storage.profiles[req.Profile].Policy
|
policy = profile.Policy
|
||||||
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
||||||
profile := app.storage.profiles[req.Profile]
|
|
||||||
if profile.Ombi != nil && len(profile.Ombi) != 0 {
|
if profile.Ombi != nil && len(profile.Ombi) != 0 {
|
||||||
ombi = profile.Ombi
|
ombi = profile.Ombi
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
"ombiProfile": "Ombi user profile",
|
"ombiProfile": "Ombi user profile",
|
||||||
"ombiUserDefaultsDescription": "Create an Ombi user and configure it, then select it below. It's settings/permissions will be stored and applied to new Ombi users created by jfa-go when this profile is selected.",
|
"ombiUserDefaultsDescription": "Create an Ombi user and configure it, then select it below. It's settings/permissions will be stored and applied to new Ombi users created by jfa-go when this profile is selected.",
|
||||||
"userProfiles": "User Profiles",
|
"userProfiles": "User Profiles",
|
||||||
"userProfilesDescription": "Profiles are applied to users when they create an account. A profile include library access rights and homescreen layout.",
|
"userProfilesDescription": "Profiles are applied to users when they create an account. A profile includes library access rights and homescreen layout.",
|
||||||
"userProfilesIsDefault": "Default",
|
"userProfilesIsDefault": "Default",
|
||||||
"userProfilesLibraries": "Libraries",
|
"userProfilesLibraries": "Libraries",
|
||||||
"addProfile": "Add Profile",
|
"addProfile": "Add Profile",
|
||||||
|
@ -236,6 +236,11 @@ func migrateToBadger(app *appContext) {
|
|||||||
for k, v := range app.storage.deprecatedUserExpiries {
|
for k, v := range app.storage.deprecatedUserExpiries {
|
||||||
app.storage.SetUserExpiryKey(k, UserExpiry{Expiry: v})
|
app.storage.SetUserExpiryKey(k, UserExpiry{Expiry: v})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.storage.loadProfiles()
|
||||||
|
for k, v := range app.storage.profiles {
|
||||||
|
app.storage.SetProfileKey(k, v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate between hyphenated & non-hyphenated user IDs. Doesn't seem to happen anymore, so disabled.
|
// Migrate between hyphenated & non-hyphenated user IDs. Doesn't seem to happen anymore, so disabled.
|
||||||
|
60
storage.go
60
storage.go
@ -312,6 +312,63 @@ func (st *Storage) DeleteUserExpiryKey(k string) {
|
|||||||
st.db.Delete(k, UserExpiry{})
|
st.db.Delete(k, UserExpiry{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetProfiles returns a copy of the store.
|
||||||
|
func (st *Storage) GetProfiles() []Profile {
|
||||||
|
result := []Profile{}
|
||||||
|
err := st.db.Find(&result, &badgerhold.Query{})
|
||||||
|
if err != nil {
|
||||||
|
// fmt.Printf("Failed to find profiles: %v\n", err)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProfileKey returns the value stored in the store's key.
|
||||||
|
func (st *Storage) GetProfileKey(k string) (Profile, bool) {
|
||||||
|
result := Profile{}
|
||||||
|
err := st.db.Get(k, &result)
|
||||||
|
ok := true
|
||||||
|
if err != nil {
|
||||||
|
// fmt.Printf("Failed to find profile: %v\n", err)
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
return result, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetProfileKey stores value v in key k.
|
||||||
|
func (st *Storage) SetProfileKey(k string, v Profile) {
|
||||||
|
v.Name = k
|
||||||
|
v.Admin = v.Policy.IsAdministrator
|
||||||
|
if v.Policy.EnabledFolders != nil {
|
||||||
|
if len(v.Policy.EnabledFolders) == 0 {
|
||||||
|
v.LibraryAccess = "All"
|
||||||
|
} else {
|
||||||
|
v.LibraryAccess = strconv.Itoa(len(v.Policy.EnabledFolders))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v.FromUser == "" {
|
||||||
|
v.FromUser = "Unknown"
|
||||||
|
}
|
||||||
|
err := st.db.Upsert(k, v)
|
||||||
|
if err != nil {
|
||||||
|
// fmt.Printf("Failed to set profile: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteProfileKey deletes value at key k.
|
||||||
|
func (st *Storage) DeleteProfileKey(k string) {
|
||||||
|
st.db.Delete(k, Profile{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultProfile returns the first profile set as default, or anything available if there isn't one.
|
||||||
|
func (st *Storage) GetDefaultProfile() Profile {
|
||||||
|
defaultProfile := Profile{}
|
||||||
|
err := st.db.FindOne(&defaultProfile, badgerhold.Where("Default").Eq(true))
|
||||||
|
if err != nil {
|
||||||
|
st.db.FindOne(&defaultProfile, &badgerhold.Query{})
|
||||||
|
}
|
||||||
|
return defaultProfile
|
||||||
|
}
|
||||||
|
|
||||||
type TelegramUser struct {
|
type TelegramUser struct {
|
||||||
JellyfinID string `badgerhold:"key"`
|
JellyfinID string `badgerhold:"key"`
|
||||||
ChatID int64 `badgerhold:"index"`
|
ChatID int64 `badgerhold:"index"`
|
||||||
@ -366,7 +423,8 @@ type userPageContent struct {
|
|||||||
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
||||||
|
|
||||||
type Profile struct {
|
type Profile struct {
|
||||||
Admin bool `json:"admin,omitempty"`
|
Name string `badgerhold:"key"`
|
||||||
|
Admin bool `json:"admin,omitempty" badgerhold:"index"`
|
||||||
LibraryAccess string `json:"libraries,omitempty"`
|
LibraryAccess string `json:"libraries,omitempty"`
|
||||||
FromUser string `json:"fromUser,omitempty"`
|
FromUser string `json:"fromUser,omitempty"`
|
||||||
Policy mediabrowser.Policy `json:"policy,omitempty"`
|
Policy mediabrowser.Policy `json:"policy,omitempty"`
|
||||||
|
Loading…
Reference in New Issue
Block a user