mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
Merge Database Migration
Database Migration
This commit is contained in:
commit
0946b3a1da
@ -10,21 +10,20 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/itchyny/timefmt-go"
|
||||
"github.com/lithammer/shortuuid/v3"
|
||||
"github.com/timshannon/badgerhold/v4"
|
||||
)
|
||||
|
||||
func (app *appContext) checkInvites() {
|
||||
currentTime := time.Now()
|
||||
app.storage.loadInvites()
|
||||
changed := false
|
||||
for code, data := range app.storage.GetInvites() {
|
||||
for _, data := range app.storage.GetInvites() {
|
||||
expiry := data.ValidTill
|
||||
if !currentTime.After(expiry) {
|
||||
continue
|
||||
}
|
||||
app.debug.Printf("Housekeeping: Deleting old invite %s", code)
|
||||
app.debug.Printf("Housekeeping: Deleting old invite %s", data.Code)
|
||||
notify := data.Notify
|
||||
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
|
||||
app.debug.Printf("%s: Expiry notification", code)
|
||||
app.debug.Printf("%s: Expiry notification", data.Code)
|
||||
var wait sync.WaitGroup
|
||||
for address, settings := range notify {
|
||||
if !settings["notify-expiry"] {
|
||||
@ -33,9 +32,9 @@ func (app *appContext) checkInvites() {
|
||||
wait.Add(1)
|
||||
go func(addr string) {
|
||||
defer wait.Done()
|
||||
msg, err := app.email.constructExpiry(code, data, app, false)
|
||||
msg, err := app.email.constructExpiry(data.Code, data, app, false)
|
||||
if err != nil {
|
||||
app.err.Printf("%s: Failed to construct expiry notification: %v", code, err)
|
||||
app.err.Printf("%s: Failed to construct expiry notification: %v", data.Code, err)
|
||||
} else {
|
||||
// Check whether notify "address" is an email address of Jellyfin ID
|
||||
if strings.Contains(addr, "@") {
|
||||
@ -44,7 +43,7 @@ func (app *appContext) checkInvites() {
|
||||
err = app.sendByID(msg, addr)
|
||||
}
|
||||
if err != nil {
|
||||
app.err.Printf("%s: Failed to send expiry notification: %v", code, err)
|
||||
app.err.Printf("%s: Failed to send expiry notification: %v", data.Code, err)
|
||||
} else {
|
||||
app.info.Printf("Sent expiry notification to %s", addr)
|
||||
}
|
||||
@ -53,18 +52,12 @@ func (app *appContext) checkInvites() {
|
||||
}
|
||||
wait.Wait()
|
||||
}
|
||||
changed = true
|
||||
app.storage.DeleteInvitesKey(code)
|
||||
}
|
||||
if changed {
|
||||
app.storage.storeInvites()
|
||||
app.storage.DeleteInvitesKey(data.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *appContext) checkInvite(code string, used bool, username string) bool {
|
||||
currentTime := time.Now()
|
||||
app.storage.loadInvites()
|
||||
changed := false
|
||||
inv, match := app.storage.GetInvitesKey(code)
|
||||
if !match {
|
||||
return false
|
||||
@ -103,11 +96,9 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
|
||||
}
|
||||
wait.Wait()
|
||||
}
|
||||
changed = true
|
||||
match = false
|
||||
app.storage.DeleteInvitesKey(code)
|
||||
} else if used {
|
||||
changed = true
|
||||
del := false
|
||||
newInv := inv
|
||||
if newInv.RemainingUses == 1 {
|
||||
@ -122,9 +113,6 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
|
||||
app.storage.SetInvitesKey(code, newInv)
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
app.storage.storeInvites()
|
||||
}
|
||||
return match
|
||||
}
|
||||
|
||||
@ -138,7 +126,6 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
|
||||
func (app *appContext) GenerateInvite(gc *gin.Context) {
|
||||
var req generateInviteDTO
|
||||
app.debug.Println("Generating new invite")
|
||||
app.storage.loadInvites()
|
||||
gc.BindJSON(&req)
|
||||
currentTime := time.Now()
|
||||
validTill := currentTime.AddDate(0, req.Months, req.Days)
|
||||
@ -213,14 +200,13 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
|
||||
}
|
||||
}
|
||||
if req.Profile != "" {
|
||||
if _, ok := app.storage.profiles[req.Profile]; ok {
|
||||
if _, ok := app.storage.GetProfileKey(req.Profile); ok {
|
||||
invite.Profile = req.Profile
|
||||
} else {
|
||||
invite.Profile = "Default"
|
||||
}
|
||||
}
|
||||
app.storage.SetInvitesKey(inviteCode, invite)
|
||||
app.storage.storeInvites()
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
||||
@ -233,13 +219,12 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
|
||||
func (app *appContext) GetInvites(gc *gin.Context) {
|
||||
app.debug.Println("Invites requested")
|
||||
currentTime := time.Now()
|
||||
app.storage.loadInvites()
|
||||
app.checkInvites()
|
||||
var invites []inviteDTO
|
||||
for code, inv := range app.storage.GetInvites() {
|
||||
for _, inv := range app.storage.GetInvites() {
|
||||
_, months, days, hours, minutes, _ := timeDiff(inv.ValidTill, currentTime)
|
||||
invite := inviteDTO{
|
||||
Code: code,
|
||||
Code: inv.Code,
|
||||
Months: months,
|
||||
Days: days,
|
||||
Hours: hours,
|
||||
@ -277,37 +262,36 @@ func (app *appContext) GetInvites(gc *gin.Context) {
|
||||
invite.SendTo = inv.SendTo
|
||||
}
|
||||
if len(inv.Notify) != 0 {
|
||||
var address string
|
||||
// app.err.Printf("%s has notify section: %+v, you are %s\n", inv.Code, inv.Notify, gc.GetString("jfId"))
|
||||
var addressOrID string
|
||||
if app.config.Section("ui").Key("jellyfin_login").MustBool(false) {
|
||||
app.storage.loadEmails()
|
||||
if addr, ok := app.storage.GetEmailsKey(gc.GetString("jfId")); ok && addr.Addr != "" {
|
||||
address = addr.Addr
|
||||
}
|
||||
addressOrID = gc.GetString("jfId")
|
||||
} else {
|
||||
address = app.config.Section("ui").Key("email").String()
|
||||
addressOrID = app.config.Section("ui").Key("email").String()
|
||||
}
|
||||
if _, ok := inv.Notify[address]; ok {
|
||||
if _, ok = inv.Notify[address]["notify-expiry"]; ok {
|
||||
invite.NotifyExpiry = inv.Notify[address]["notify-expiry"]
|
||||
if _, ok := inv.Notify[addressOrID]; ok {
|
||||
if _, ok = inv.Notify[addressOrID]["notify-expiry"]; ok {
|
||||
invite.NotifyExpiry = inv.Notify[addressOrID]["notify-expiry"]
|
||||
}
|
||||
if _, ok = inv.Notify[address]["notify-creation"]; ok {
|
||||
invite.NotifyCreation = inv.Notify[address]["notify-creation"]
|
||||
if _, ok = inv.Notify[addressOrID]["notify-creation"]; ok {
|
||||
invite.NotifyCreation = inv.Notify[addressOrID]["notify-creation"]
|
||||
}
|
||||
}
|
||||
}
|
||||
invites = append(invites, invite)
|
||||
}
|
||||
profiles := make([]string, len(app.storage.profiles))
|
||||
if len(app.storage.profiles) != 0 {
|
||||
profiles[0] = app.storage.defaultProfile
|
||||
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(app.storage.profiles) > 1 {
|
||||
for p := range app.storage.profiles {
|
||||
if p != app.storage.defaultProfile {
|
||||
profiles[i] = p
|
||||
i++
|
||||
}
|
||||
}
|
||||
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 := getInvitesDTO{
|
||||
@ -330,7 +314,7 @@ func (app *appContext) SetProfile(gc *gin.Context) {
|
||||
gc.BindJSON(&req)
|
||||
app.debug.Printf("%s: Setting profile to \"%s\"", req.Invite, req.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)
|
||||
respond(500, "Profile not found", gc)
|
||||
return
|
||||
@ -338,7 +322,6 @@ func (app *appContext) SetProfile(gc *gin.Context) {
|
||||
inv, _ := app.storage.GetInvitesKey(req.Invite)
|
||||
inv.Profile = req.Profile
|
||||
app.storage.SetInvitesKey(req.Invite, inv)
|
||||
app.storage.storeInvites()
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
||||
@ -357,8 +340,6 @@ func (app *appContext) SetNotify(gc *gin.Context) {
|
||||
changed := false
|
||||
for code, settings := range req {
|
||||
app.debug.Printf("%s: Notification settings change requested", code)
|
||||
app.storage.loadInvites()
|
||||
app.storage.loadEmails()
|
||||
invite, ok := app.storage.GetInvitesKey(code)
|
||||
if !ok {
|
||||
app.err.Printf("%s Notification setting change failed: Invalid code", code)
|
||||
@ -401,9 +382,6 @@ func (app *appContext) SetNotify(gc *gin.Context) {
|
||||
app.storage.SetInvitesKey(code, invite)
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
app.storage.storeInvites()
|
||||
}
|
||||
}
|
||||
|
||||
// @Summary Delete an invite.
|
||||
@ -422,7 +400,6 @@ func (app *appContext) DeleteInvite(gc *gin.Context) {
|
||||
_, ok = app.storage.GetInvitesKey(req.Code)
|
||||
if ok {
|
||||
app.storage.DeleteInvitesKey(req.Code)
|
||||
app.storage.storeInvites()
|
||||
app.info.Printf("%s: Invite deleted", req.Code)
|
||||
respondBool(200, true, gc)
|
||||
return
|
||||
|
108
api-messages.go
108
api-messages.go
@ -25,18 +25,18 @@ func (app *appContext) GetCustomContent(gc *gin.Context) {
|
||||
adminLang = app.storage.lang.chosenAdminLang
|
||||
}
|
||||
list := emailListDTO{
|
||||
"UserCreated": {Name: app.storage.lang.Email[lang].UserCreated["name"], Enabled: app.storage.customEmails.UserCreated.Enabled},
|
||||
"InviteExpiry": {Name: app.storage.lang.Email[lang].InviteExpiry["name"], Enabled: app.storage.customEmails.InviteExpiry.Enabled},
|
||||
"PasswordReset": {Name: app.storage.lang.Email[lang].PasswordReset["name"], Enabled: app.storage.customEmails.PasswordReset.Enabled},
|
||||
"UserDeleted": {Name: app.storage.lang.Email[lang].UserDeleted["name"], Enabled: app.storage.customEmails.UserDeleted.Enabled},
|
||||
"UserDisabled": {Name: app.storage.lang.Email[lang].UserDisabled["name"], Enabled: app.storage.customEmails.UserDisabled.Enabled},
|
||||
"UserEnabled": {Name: app.storage.lang.Email[lang].UserEnabled["name"], Enabled: app.storage.customEmails.UserEnabled.Enabled},
|
||||
"InviteEmail": {Name: app.storage.lang.Email[lang].InviteEmail["name"], Enabled: app.storage.customEmails.InviteEmail.Enabled},
|
||||
"WelcomeEmail": {Name: app.storage.lang.Email[lang].WelcomeEmail["name"], Enabled: app.storage.customEmails.WelcomeEmail.Enabled},
|
||||
"EmailConfirmation": {Name: app.storage.lang.Email[lang].EmailConfirmation["name"], Enabled: app.storage.customEmails.EmailConfirmation.Enabled},
|
||||
"UserExpired": {Name: app.storage.lang.Email[lang].UserExpired["name"], Enabled: app.storage.customEmails.UserExpired.Enabled},
|
||||
"UserLogin": {Name: app.storage.lang.Admin[adminLang].Strings["userPageLogin"], Enabled: app.storage.userPage.Login.Enabled},
|
||||
"UserPage": {Name: app.storage.lang.Admin[adminLang].Strings["userPagePage"], Enabled: app.storage.userPage.Page.Enabled},
|
||||
"UserCreated": {Name: app.storage.lang.Email[lang].UserCreated["name"], Enabled: app.storage.MustGetCustomContentKey("UserCreated").Enabled},
|
||||
"InviteExpiry": {Name: app.storage.lang.Email[lang].InviteExpiry["name"], Enabled: app.storage.MustGetCustomContentKey("InviteExpiry").Enabled},
|
||||
"PasswordReset": {Name: app.storage.lang.Email[lang].PasswordReset["name"], Enabled: app.storage.MustGetCustomContentKey("PasswordReset").Enabled},
|
||||
"UserDeleted": {Name: app.storage.lang.Email[lang].UserDeleted["name"], Enabled: app.storage.MustGetCustomContentKey("UserDeleted").Enabled},
|
||||
"UserDisabled": {Name: app.storage.lang.Email[lang].UserDisabled["name"], Enabled: app.storage.MustGetCustomContentKey("UserDisabled").Enabled},
|
||||
"UserEnabled": {Name: app.storage.lang.Email[lang].UserEnabled["name"], Enabled: app.storage.MustGetCustomContentKey("UserEnabled").Enabled},
|
||||
"InviteEmail": {Name: app.storage.lang.Email[lang].InviteEmail["name"], Enabled: app.storage.MustGetCustomContentKey("InviteEmail").Enabled},
|
||||
"WelcomeEmail": {Name: app.storage.lang.Email[lang].WelcomeEmail["name"], Enabled: app.storage.MustGetCustomContentKey("WelcomeEmail").Enabled},
|
||||
"EmailConfirmation": {Name: app.storage.lang.Email[lang].EmailConfirmation["name"], Enabled: app.storage.MustGetCustomContentKey("EmailConfirmation").Enabled},
|
||||
"UserExpired": {Name: app.storage.lang.Email[lang].UserExpired["name"], Enabled: app.storage.MustGetCustomContentKey("UserExpired").Enabled},
|
||||
"UserLogin": {Name: app.storage.lang.Admin[adminLang].Strings["userPageLogin"], Enabled: app.storage.MustGetCustomContentKey("Login").Enabled},
|
||||
"UserPage": {Name: app.storage.lang.Admin[adminLang].Strings["userPagePage"], Enabled: app.storage.MustGetCustomContentKey("Page").Enabled},
|
||||
}
|
||||
|
||||
filter := gc.Query("filter")
|
||||
@ -50,10 +50,11 @@ func (app *appContext) GetCustomContent(gc *gin.Context) {
|
||||
gc.JSON(200, list)
|
||||
}
|
||||
|
||||
func (app *appContext) getCustomMessage(id string) *customContent {
|
||||
// No longer needed, these are stored by string keys in the database now.
|
||||
/* func (app *appContext) getCustomMessage(id string) *CustomContent {
|
||||
switch id {
|
||||
case "Announcement":
|
||||
return &customContent{}
|
||||
return &CustomContent{}
|
||||
case "UserCreated":
|
||||
return &app.storage.customEmails.UserCreated
|
||||
case "InviteExpiry":
|
||||
@ -80,45 +81,38 @@ func (app *appContext) getCustomMessage(id string) *customContent {
|
||||
return &app.storage.userPage.Page
|
||||
}
|
||||
return nil
|
||||
}
|
||||
} */
|
||||
|
||||
// @Summary Sets the corresponding custom email.
|
||||
// @Summary Sets the corresponding custom content.
|
||||
// @Produce json
|
||||
// @Param customEmails body customEmails true "Content = email (in markdown)."
|
||||
// @Param CustomContent body CustomContent true "Content = email (in markdown)."
|
||||
// @Success 200 {object} boolResponse
|
||||
// @Failure 400 {object} boolResponse
|
||||
// @Failure 500 {object} boolResponse
|
||||
// @Param id path string true "ID of email"
|
||||
// @Param id path string true "ID of content"
|
||||
// @Router /config/emails/{id} [post]
|
||||
// @Security Bearer
|
||||
// @tags Configuration
|
||||
func (app *appContext) SetCustomMessage(gc *gin.Context) {
|
||||
var req customContent
|
||||
var req CustomContent
|
||||
gc.BindJSON(&req)
|
||||
id := gc.Param("id")
|
||||
if req.Content == "" {
|
||||
respondBool(400, false, gc)
|
||||
return
|
||||
}
|
||||
message := app.getCustomMessage(id)
|
||||
if message == nil {
|
||||
message, ok := app.storage.GetCustomContentKey(id)
|
||||
if !ok {
|
||||
respondBool(400, false, gc)
|
||||
return
|
||||
}
|
||||
message.Content = req.Content
|
||||
message.Enabled = true
|
||||
if app.storage.storeCustomEmails() != nil {
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
if app.storage.storeUserPageContent() != nil {
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
app.storage.SetCustomContentKey(id, message)
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
||||
// @Summary Enable/Disable custom email.
|
||||
// @Summary Enable/Disable custom content.
|
||||
// @Produce json
|
||||
// @Success 200 {object} boolResponse
|
||||
// @Failure 400 {object} boolResponse
|
||||
@ -137,24 +131,17 @@ func (app *appContext) SetCustomMessageState(gc *gin.Context) {
|
||||
} else if s != "disable" {
|
||||
respondBool(400, false, gc)
|
||||
}
|
||||
message := app.getCustomMessage(id)
|
||||
if message == nil {
|
||||
message, ok := app.storage.GetCustomContentKey(id)
|
||||
if !ok {
|
||||
respondBool(400, false, gc)
|
||||
return
|
||||
}
|
||||
message.Enabled = enabled
|
||||
if app.storage.storeCustomEmails() != nil {
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
if app.storage.storeUserPageContent() != nil {
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
app.storage.SetCustomContentKey(id, message)
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
||||
// @Summary Returns the custom email/message (generating it if not set) and list of used variables in it.
|
||||
// @Summary Returns the custom content/message (generating it if not set) and list of used variables in it.
|
||||
// @Produce json
|
||||
// @Success 200 {object} customEmailDTO
|
||||
// @Failure 400 {object} boolResponse
|
||||
@ -174,8 +161,8 @@ func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
||||
var values map[string]interface{}
|
||||
username := app.storage.lang.Email[lang].Strings.get("username")
|
||||
emailAddress := app.storage.lang.Email[lang].Strings.get("emailAddress")
|
||||
customMessage := app.getCustomMessage(id)
|
||||
if customMessage == nil {
|
||||
customMessage, ok := app.storage.GetCustomContentKey(id)
|
||||
if !ok {
|
||||
app.err.Printf("Failed to get custom message with ID \"%s\"", id)
|
||||
respondBool(400, false, gc)
|
||||
return
|
||||
@ -280,13 +267,7 @@ func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
||||
if variables == nil {
|
||||
variables = []string{}
|
||||
}
|
||||
if app.storage.storeCustomEmails() != nil {
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
if app.storage.storeUserPageContent() != nil {
|
||||
respondBool(500, false, gc)
|
||||
}
|
||||
app.storage.SetCustomContentKey(id, customMessage)
|
||||
var mail *Message
|
||||
if id != "UserLogin" && id != "UserPage" {
|
||||
mail, err = app.email.constructTemplate("", "<div class=\"preview-content\"></div>", app)
|
||||
@ -387,11 +368,6 @@ func (app *appContext) setContactMethods(req SetContactMethodsDTO, gc *gin.Conte
|
||||
change := dcUser.Contact != req.Discord
|
||||
dcUser.Contact = req.Discord
|
||||
app.storage.SetDiscordKey(req.ID, dcUser)
|
||||
if err := app.storage.storeDiscordUsers(); err != nil {
|
||||
respondBool(500, false, gc)
|
||||
app.err.Printf("Discord: Failed to store users: %v", err)
|
||||
return
|
||||
}
|
||||
if change {
|
||||
msg := ""
|
||||
if !req.Discord {
|
||||
@ -404,11 +380,6 @@ func (app *appContext) setContactMethods(req SetContactMethodsDTO, gc *gin.Conte
|
||||
change := mxUser.Contact != req.Matrix
|
||||
mxUser.Contact = req.Matrix
|
||||
app.storage.SetMatrixKey(req.ID, mxUser)
|
||||
if err := app.storage.storeMatrixUsers(); err != nil {
|
||||
respondBool(500, false, gc)
|
||||
app.err.Printf("Matrix: Failed to store users: %v", err)
|
||||
return
|
||||
}
|
||||
if change {
|
||||
msg := ""
|
||||
if !req.Matrix {
|
||||
@ -421,11 +392,6 @@ func (app *appContext) setContactMethods(req SetContactMethodsDTO, gc *gin.Conte
|
||||
change := email.Contact != req.Email
|
||||
email.Contact = req.Email
|
||||
app.storage.SetEmailsKey(req.ID, email)
|
||||
if err := app.storage.storeEmails(); err != nil {
|
||||
respondBool(500, false, gc)
|
||||
app.err.Printf("Failed to store emails: %v", err)
|
||||
return
|
||||
}
|
||||
if change {
|
||||
msg := ""
|
||||
if !req.Email {
|
||||
@ -646,7 +612,7 @@ func (app *appContext) MatrixConnect(gc *gin.Context) {
|
||||
var req MatrixConnectUserDTO
|
||||
gc.BindJSON(&req)
|
||||
if app.storage.GetMatrix() == nil {
|
||||
app.storage.matrix = matrixStore{}
|
||||
app.storage.deprecatedMatrix = matrixStore{}
|
||||
}
|
||||
roomID, encrypted, err := app.matrix.CreateRoom(req.UserID)
|
||||
if err != nil {
|
||||
@ -662,11 +628,6 @@ func (app *appContext) MatrixConnect(gc *gin.Context) {
|
||||
Encrypted: encrypted,
|
||||
})
|
||||
app.matrix.isEncrypted[roomID] = encrypted
|
||||
if err := app.storage.storeMatrixUsers(); err != nil {
|
||||
app.err.Printf("Failed to store Matrix users: %v", err)
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
||||
@ -717,11 +678,6 @@ func (app *appContext) DiscordConnect(gc *gin.Context) {
|
||||
return
|
||||
}
|
||||
app.storage.SetDiscordKey(req.JellyfinID, user)
|
||||
if err := app.storage.storeDiscordUsers(); err != nil {
|
||||
app.err.Printf("Failed to store Discord users: %v", err)
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
linkExistingOmbiDiscordTelegram(app)
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
18
api-ombi.go
18
api-ombi.go
@ -71,7 +71,7 @@ func (app *appContext) SetOmbiProfile(gc *gin.Context) {
|
||||
var req ombiUser
|
||||
gc.BindJSON(&req)
|
||||
profileName := gc.Param("profile")
|
||||
profile, ok := app.storage.profiles[profileName]
|
||||
profile, ok := app.storage.GetProfileKey(profileName)
|
||||
if !ok {
|
||||
respondBool(400, false, gc)
|
||||
return
|
||||
@ -83,12 +83,7 @@ func (app *appContext) SetOmbiProfile(gc *gin.Context) {
|
||||
return
|
||||
}
|
||||
profile.Ombi = template
|
||||
app.storage.profiles[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
|
||||
}
|
||||
app.storage.SetProfileKey(profileName, profile)
|
||||
respondBool(204, true, gc)
|
||||
}
|
||||
|
||||
@ -103,17 +98,12 @@ func (app *appContext) SetOmbiProfile(gc *gin.Context) {
|
||||
// @tags Ombi
|
||||
func (app *appContext) DeleteOmbiProfile(gc *gin.Context) {
|
||||
profileName := gc.Param("profile")
|
||||
profile, ok := app.storage.profiles[profileName]
|
||||
profile, ok := app.storage.GetProfileKey(profileName)
|
||||
if !ok {
|
||||
respondBool(400, false, gc)
|
||||
return
|
||||
}
|
||||
profile.Ombi = nil
|
||||
app.storage.profiles[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
|
||||
}
|
||||
app.storage.SetProfileKey(profileName, profile)
|
||||
respondBool(204, true, gc)
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/timshannon/badgerhold/v4"
|
||||
)
|
||||
|
||||
// @Summary Get a list of profiles
|
||||
@ -13,14 +14,13 @@ import (
|
||||
// @Security Bearer
|
||||
// @tags Profiles & Settings
|
||||
func (app *appContext) GetProfiles(gc *gin.Context) {
|
||||
app.storage.loadProfiles()
|
||||
app.debug.Println("Profiles requested")
|
||||
out := getProfilesDTO{
|
||||
DefaultProfile: app.storage.defaultProfile,
|
||||
DefaultProfile: app.storage.GetDefaultProfile().Name,
|
||||
Profiles: map[string]profileDTO{},
|
||||
}
|
||||
for name, p := range app.storage.profiles {
|
||||
out.Profiles[name] = profileDTO{
|
||||
for _, p := range app.storage.GetProfiles() {
|
||||
out.Profiles[p.Name] = profileDTO{
|
||||
Admin: p.Admin,
|
||||
LibraryAccess: p.LibraryAccess,
|
||||
FromUser: p.FromUser,
|
||||
@ -42,20 +42,20 @@ func (app *appContext) SetDefaultProfile(gc *gin.Context) {
|
||||
req := profileChangeDTO{}
|
||||
gc.BindJSON(&req)
|
||||
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)
|
||||
respond(500, "Profile not found", gc)
|
||||
return
|
||||
}
|
||||
for name, profile := range app.storage.profiles {
|
||||
if name == req.Name {
|
||||
profile.Admin = true
|
||||
app.storage.profiles[name] = profile
|
||||
app.storage.db.ForEach(&badgerhold.Query{}, func(profile *Profile) error {
|
||||
if profile.Name == req.Name {
|
||||
profile.Default = true
|
||||
} else {
|
||||
profile.Admin = false
|
||||
profile.Default = false
|
||||
}
|
||||
}
|
||||
app.storage.defaultProfile = req.Name
|
||||
app.storage.SetProfileKey(profile.Name, *profile)
|
||||
return nil
|
||||
})
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
||||
@ -92,10 +92,7 @@ func (app *appContext) CreateProfile(gc *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
app.storage.loadProfiles()
|
||||
app.storage.profiles[req.Name] = profile
|
||||
app.storage.storeProfiles()
|
||||
app.storage.loadProfiles()
|
||||
app.storage.SetProfileKey(req.Name, profile)
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
||||
@ -110,12 +107,6 @@ func (app *appContext) DeleteProfile(gc *gin.Context) {
|
||||
req := profileChangeDTO{}
|
||||
gc.BindJSON(&req)
|
||||
name := req.Name
|
||||
if _, ok := app.storage.profiles[name]; ok {
|
||||
if app.storage.defaultProfile == name {
|
||||
app.storage.defaultProfile = ""
|
||||
}
|
||||
delete(app.storage.profiles, name)
|
||||
}
|
||||
app.storage.storeProfiles()
|
||||
app.storage.DeleteProfileKey(name)
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
@ -38,15 +38,10 @@ func (app *appContext) MyDetails(gc *gin.Context) {
|
||||
}
|
||||
resp.Disabled = user.Policy.IsDisabled
|
||||
|
||||
if exp, ok := app.storage.users[user.ID]; ok {
|
||||
resp.Expiry = exp.Unix()
|
||||
if exp, ok := app.storage.GetUserExpiryKey(user.ID); ok {
|
||||
resp.Expiry = exp.Expiry.Unix()
|
||||
}
|
||||
|
||||
app.storage.loadEmails()
|
||||
app.storage.loadDiscordUsers()
|
||||
app.storage.loadMatrixUsers()
|
||||
app.storage.loadTelegramUsers()
|
||||
|
||||
if emailEnabled {
|
||||
resp.Email = &MyDetailsContactMethodsDTO{}
|
||||
if email, ok := app.storage.GetEmailsKey(user.ID); ok && email.Addr != "" {
|
||||
@ -199,7 +194,6 @@ func (app *appContext) confirmMyAction(gc *gin.Context, key string) {
|
||||
}
|
||||
}
|
||||
|
||||
app.storage.storeEmails()
|
||||
app.info.Println("Email list modified")
|
||||
gc.Redirect(http.StatusSeeOther, "/my/account")
|
||||
return
|
||||
@ -511,8 +505,8 @@ func (app *appContext) ResetMyPassword(gc *gin.Context) {
|
||||
var pwr InternalPWR
|
||||
var err error
|
||||
|
||||
jfID := app.ReverseUserSearch(address)
|
||||
if jfID == "" {
|
||||
jfUser, ok := app.ReverseUserSearch(address)
|
||||
if !ok {
|
||||
app.debug.Printf("Ignoring PWR request: User not found")
|
||||
|
||||
for range timerWait {
|
||||
@ -521,7 +515,7 @@ func (app *appContext) ResetMyPassword(gc *gin.Context) {
|
||||
}
|
||||
return
|
||||
}
|
||||
pwr, err = app.GenInternalReset(jfID)
|
||||
pwr, err = app.GenInternalReset(jfUser.ID)
|
||||
if err != nil {
|
||||
app.err.Printf("Failed to get user from Jellyfin: %v", err)
|
||||
for range timerWait {
|
||||
@ -550,7 +544,7 @@ func (app *appContext) ResetMyPassword(gc *gin.Context) {
|
||||
return
|
||||
}
|
||||
return
|
||||
} else if err := app.sendByID(msg, jfID); err != nil {
|
||||
} else if err := app.sendByID(msg, jfUser.ID); err != nil {
|
||||
app.err.Printf("Failed to send password reset message to \"%s\": %v", address, err)
|
||||
} else {
|
||||
app.info.Printf("Sent password reset message to \"%s\"", address)
|
||||
|
181
api-users.go
181
api-users.go
@ -44,16 +44,16 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
|
||||
return
|
||||
}
|
||||
id := user.ID
|
||||
if app.storage.policy.BlockedTags != nil {
|
||||
status, err = app.jf.SetPolicy(id, app.storage.policy)
|
||||
profile := app.storage.GetDefaultProfile()
|
||||
// Check profile isn't empty
|
||||
if profile.Policy.BlockedTags != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
if app.storage.configuration.GroupedFolders != nil && len(app.storage.displayprefs) != 0 {
|
||||
status, err = app.jf.SetConfiguration(id, app.storage.configuration)
|
||||
status, err = app.jf.SetConfiguration(id, profile.Configuration)
|
||||
if (status == 200 || status == 204) && err == nil {
|
||||
status, err = app.jf.SetDisplayPreferences(id, app.storage.displayprefs)
|
||||
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)
|
||||
@ -62,18 +62,18 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
|
||||
app.jf.CacheExpiry = time.Now()
|
||||
if emailEnabled {
|
||||
app.storage.SetEmailsKey(id, EmailAddress{Addr: req.Email, Contact: true})
|
||||
app.storage.storeEmails()
|
||||
}
|
||||
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
||||
app.storage.loadOmbiTemplate()
|
||||
if len(app.storage.ombi_template) != 0 {
|
||||
errors, code, err := app.ombi.NewUser(req.Username, req.Password, req.Email, app.storage.ombi_template)
|
||||
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, ", "))
|
||||
} else {
|
||||
app.info.Println("Created Ombi user")
|
||||
}
|
||||
profile := app.storage.GetDefaultProfile()
|
||||
if profile.Ombi == nil {
|
||||
profile.Ombi = map[string]interface{}{}
|
||||
}
|
||||
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, ", "))
|
||||
} else {
|
||||
app.info.Println("Created Ombi user")
|
||||
}
|
||||
}
|
||||
if emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
|
||||
@ -130,17 +130,13 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
||||
success = false
|
||||
return
|
||||
}
|
||||
if app.config.Section("discord").Key("require_unique").MustBool(false) {
|
||||
for _, u := range app.storage.GetDiscord() {
|
||||
if discordUser.ID == u.ID {
|
||||
f = func(gc *gin.Context) {
|
||||
app.debug.Printf("%s: New user failed: Discord user already linked", req.Code)
|
||||
respond(400, "errorAccountLinked", gc)
|
||||
}
|
||||
success = false
|
||||
return
|
||||
}
|
||||
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)
|
||||
respond(400, "errorAccountLinked", gc)
|
||||
}
|
||||
success = false
|
||||
return
|
||||
}
|
||||
err := app.discord.ApplyRole(discordUser.ID)
|
||||
if err != nil {
|
||||
@ -176,17 +172,13 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
||||
success = false
|
||||
return
|
||||
}
|
||||
if app.config.Section("matrix").Key("require_unique").MustBool(false) {
|
||||
for _, u := range app.storage.GetMatrix() {
|
||||
if user.User.UserID == u.UserID {
|
||||
f = func(gc *gin.Context) {
|
||||
app.debug.Printf("%s: New user failed: Matrix user already linked", req.Code)
|
||||
respond(400, "errorAccountLinked", gc)
|
||||
}
|
||||
success = false
|
||||
return
|
||||
}
|
||||
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)
|
||||
respond(400, "errorAccountLinked", gc)
|
||||
}
|
||||
success = false
|
||||
return
|
||||
}
|
||||
matrixVerified = user.Verified
|
||||
matrixUser = *user.User
|
||||
@ -278,7 +270,6 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
||||
success = false
|
||||
return
|
||||
}
|
||||
app.storage.loadProfiles()
|
||||
invite, _ := app.storage.GetInvitesKey(req.Code)
|
||||
app.checkInvite(req.Code, true, req.Username)
|
||||
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) {
|
||||
@ -310,9 +301,9 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
||||
if invite.Profile != "" {
|
||||
app.debug.Printf("Applying settings from profile \"%s\"", invite.Profile)
|
||||
var ok bool
|
||||
profile, ok = app.storage.profiles[invite.Profile]
|
||||
profile, ok = app.storage.GetProfileKey(invite.Profile)
|
||||
if !ok {
|
||||
profile = app.storage.profiles["Default"]
|
||||
profile = app.storage.GetDefaultProfile()
|
||||
}
|
||||
if profile.Policy.BlockedTags != nil {
|
||||
app.debug.Printf("Applying policy from profile \"%s\"", invite.Profile)
|
||||
@ -335,29 +326,19 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
||||
// if app.config.Section("password_resets").Key("enabled").MustBool(false) {
|
||||
if req.Email != "" {
|
||||
app.storage.SetEmailsKey(id, EmailAddress{Addr: req.Email, Contact: true})
|
||||
app.storage.storeEmails()
|
||||
}
|
||||
expiry := time.Time{}
|
||||
if invite.UserExpiry {
|
||||
app.storage.usersLock.Lock()
|
||||
defer app.storage.usersLock.Unlock()
|
||||
expiry = time.Now().AddDate(0, invite.UserMonths, invite.UserDays).Add(time.Duration((60*invite.UserHours)+invite.UserMinutes) * time.Minute)
|
||||
app.storage.users[id] = expiry
|
||||
if err := app.storage.storeUsers(); err != nil {
|
||||
app.err.Printf("Failed to store user duration: %v", err)
|
||||
}
|
||||
app.storage.SetUserExpiryKey(id, UserExpiry{Expiry: expiry})
|
||||
}
|
||||
if discordVerified {
|
||||
discordUser.Contact = req.DiscordContact
|
||||
if app.storage.discord == nil {
|
||||
app.storage.discord = discordStore{}
|
||||
if app.storage.deprecatedDiscord == nil {
|
||||
app.storage.deprecatedDiscord = discordStore{}
|
||||
}
|
||||
app.storage.SetDiscordKey(user.ID, discordUser)
|
||||
if err := app.storage.storeDiscordUsers(); err != nil {
|
||||
app.err.Printf("Failed to store Discord users: %v", err)
|
||||
} else {
|
||||
delete(app.discord.verifiedTokens, req.DiscordPIN)
|
||||
}
|
||||
delete(app.discord.verifiedTokens, req.DiscordPIN)
|
||||
}
|
||||
if telegramVerified {
|
||||
tgUser := TelegramUser{
|
||||
@ -368,8 +349,8 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
||||
if lang, ok := app.telegram.languages[tgToken.ChatID]; ok {
|
||||
tgUser.Lang = lang
|
||||
}
|
||||
if app.storage.telegram == nil {
|
||||
app.storage.telegram = telegramStore{}
|
||||
if app.storage.deprecatedTelegram == nil {
|
||||
app.storage.deprecatedTelegram = telegramStore{}
|
||||
}
|
||||
app.telegram.DeleteVerifiedToken(req.TelegramPIN)
|
||||
app.storage.SetTelegramKey(user.ID, tgUser)
|
||||
@ -412,13 +393,10 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
||||
if matrixVerified {
|
||||
matrixUser.Contact = req.MatrixContact
|
||||
delete(app.matrix.tokens, req.MatrixPIN)
|
||||
if app.storage.matrix == nil {
|
||||
app.storage.matrix = matrixStore{}
|
||||
if app.storage.deprecatedMatrix == nil {
|
||||
app.storage.deprecatedMatrix = matrixStore{}
|
||||
}
|
||||
app.storage.SetMatrixKey(user.ID, matrixUser)
|
||||
if err := app.storage.storeMatrixUsers(); err != nil {
|
||||
app.err.Printf("Failed to store Matrix users: %v", err)
|
||||
}
|
||||
}
|
||||
if (emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "") || telegramVerified || discordVerified || matrixVerified {
|
||||
name := app.getAddressOrName(user.ID)
|
||||
@ -478,14 +456,10 @@ func (app *appContext) NewUser(gc *gin.Context) {
|
||||
respond(400, "errorNoEmail", gc)
|
||||
return
|
||||
}
|
||||
if app.config.Section("email").Key("require_unique").MustBool(false) && req.Email != "" {
|
||||
for _, email := range app.storage.GetEmails() {
|
||||
if req.Email == email.Addr {
|
||||
app.info.Printf("%s: New user failed: Email already in use", req.Code)
|
||||
respond(400, "errorEmailLinked", 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
|
||||
}
|
||||
}
|
||||
f, success := app.newUser(req, false)
|
||||
@ -641,21 +615,16 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) {
|
||||
respondBool(400, false, gc)
|
||||
return
|
||||
}
|
||||
app.storage.usersLock.Lock()
|
||||
defer app.storage.usersLock.Unlock()
|
||||
for _, id := range req.Users {
|
||||
if expiry, ok := app.storage.users[id]; ok {
|
||||
app.storage.users[id] = expiry.AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute)
|
||||
base := time.Now()
|
||||
if expiry, ok := app.storage.GetUserExpiryKey(id); ok {
|
||||
base = expiry.Expiry
|
||||
app.debug.Printf("Expiry extended for \"%s\"", id)
|
||||
} else {
|
||||
app.storage.users[id] = time.Now().AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute)
|
||||
app.debug.Printf("Created expiry for \"%s\"", id)
|
||||
}
|
||||
}
|
||||
if err := app.storage.storeUsers(); err != nil {
|
||||
app.err.Printf("Failed to store user duration: %v", err)
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
expiry := UserExpiry{Expiry: base.AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute)}
|
||||
app.storage.SetUserExpiryKey(id, expiry)
|
||||
}
|
||||
respondBool(204, true, gc)
|
||||
}
|
||||
@ -727,27 +696,21 @@ func (app *appContext) SaveAnnounceTemplate(gc *gin.Context) {
|
||||
respondBool(400, false, gc)
|
||||
return
|
||||
}
|
||||
app.storage.announcements[req.Name] = req
|
||||
if err := app.storage.storeAnnouncements(); err != nil {
|
||||
respondBool(500, false, gc)
|
||||
app.err.Printf("Failed to store announcement templates: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
app.storage.SetAnnouncementsKey(req.Name, req)
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
|
||||
// @Summary Save an announcement as a template for use or editing later.
|
||||
// @Produce json
|
||||
// @Success 200 {object} getAnnouncementsDTO
|
||||
// @Router /users/announce/template [get]
|
||||
// @Router /users/announce [get]
|
||||
// @Security Bearer
|
||||
// @tags Users
|
||||
func (app *appContext) GetAnnounceTemplates(gc *gin.Context) {
|
||||
resp := &getAnnouncementsDTO{make([]string, len(app.storage.announcements))}
|
||||
i := 0
|
||||
for name := range app.storage.announcements {
|
||||
resp.Announcements[i] = name
|
||||
i++
|
||||
resp := &getAnnouncementsDTO{make([]string, len(app.storage.GetAnnouncements()))}
|
||||
for i, a := range app.storage.GetAnnouncements() {
|
||||
resp.Announcements[i] = a.Name
|
||||
}
|
||||
gc.JSON(200, resp)
|
||||
}
|
||||
@ -762,7 +725,7 @@ func (app *appContext) GetAnnounceTemplates(gc *gin.Context) {
|
||||
// @tags Users
|
||||
func (app *appContext) GetAnnounceTemplate(gc *gin.Context) {
|
||||
name := gc.Param("name")
|
||||
if announcement, ok := app.storage.announcements[name]; ok {
|
||||
if announcement, ok := app.storage.GetAnnouncementsKey(name); ok {
|
||||
gc.JSON(200, announcement)
|
||||
return
|
||||
}
|
||||
@ -779,12 +742,7 @@ func (app *appContext) GetAnnounceTemplate(gc *gin.Context) {
|
||||
// @tags Users
|
||||
func (app *appContext) DeleteAnnounceTemplate(gc *gin.Context) {
|
||||
name := gc.Param("name")
|
||||
delete(app.storage.announcements, name)
|
||||
if err := app.storage.storeAnnouncements(); err != nil {
|
||||
respondBool(500, false, gc)
|
||||
app.err.Printf("Failed to store announcement templates: %v", err)
|
||||
return
|
||||
}
|
||||
app.storage.DeleteAnnouncementsKey(name)
|
||||
respondBool(200, false, gc)
|
||||
}
|
||||
|
||||
@ -876,8 +834,6 @@ func (app *appContext) GetUsers(gc *gin.Context) {
|
||||
adminOnly := app.config.Section("ui").Key("admin_only").MustBool(true)
|
||||
allowAll := app.config.Section("ui").Key("allow_all").MustBool(false)
|
||||
i := 0
|
||||
app.storage.usersLock.Lock()
|
||||
defer app.storage.usersLock.Unlock()
|
||||
for _, jfUser := range users {
|
||||
user := respUser{
|
||||
ID: jfUser.ID,
|
||||
@ -894,9 +850,9 @@ func (app *appContext) GetUsers(gc *gin.Context) {
|
||||
user.Label = email.Label
|
||||
user.AccountsAdmin = (app.jellyfinLogin) && (email.Admin || (adminOnly && jfUser.Policy.IsAdministrator) || allowAll)
|
||||
}
|
||||
expiry, ok := app.storage.users[jfUser.ID]
|
||||
expiry, ok := app.storage.GetUserExpiryKey(jfUser.ID)
|
||||
if ok {
|
||||
user.Expiry = expiry.Unix()
|
||||
user.Expiry = expiry.Expiry.Unix()
|
||||
}
|
||||
if tgUser, ok := app.storage.GetTelegramKey(jfUser.ID); ok {
|
||||
user.Telegram = tgUser.Username
|
||||
@ -947,10 +903,6 @@ func (app *appContext) SetAccountsAdmin(gc *gin.Context) {
|
||||
app.storage.SetEmailsKey(id, emailStore)
|
||||
}
|
||||
}
|
||||
if err := app.storage.storeEmails(); err != nil {
|
||||
app.err.Printf("Failed to store email list: %v", err)
|
||||
respondBool(500, false, gc)
|
||||
}
|
||||
app.info.Println("Email list modified")
|
||||
respondBool(204, true, gc)
|
||||
}
|
||||
@ -984,10 +936,6 @@ func (app *appContext) ModifyLabels(gc *gin.Context) {
|
||||
app.storage.SetEmailsKey(id, emailStore)
|
||||
}
|
||||
}
|
||||
if err := app.storage.storeEmails(); err != nil {
|
||||
app.err.Printf("Failed to store email list: %v", err)
|
||||
respondBool(500, false, gc)
|
||||
}
|
||||
app.info.Println("Email list modified")
|
||||
respondBool(204, true, gc)
|
||||
}
|
||||
@ -1022,7 +970,6 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
|
||||
// Auto enable contact by email for newly added addresses
|
||||
if !ok || oldEmail.Addr == "" {
|
||||
emailStore.Contact = true
|
||||
app.storage.storeEmails()
|
||||
}
|
||||
|
||||
emailStore.Addr = address
|
||||
@ -1039,7 +986,6 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
app.storage.storeEmails()
|
||||
app.info.Println("Email list modified")
|
||||
respondBool(200, true, gc)
|
||||
}
|
||||
@ -1062,25 +1008,24 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
|
||||
var displayprefs map[string]interface{}
|
||||
var ombi map[string]interface{}
|
||||
if req.From == "profile" {
|
||||
app.storage.loadProfiles()
|
||||
// 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)
|
||||
respond(500, "Couldn't find profile", gc)
|
||||
return
|
||||
}
|
||||
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)
|
||||
respond(500, "No homescreen template available", gc)
|
||||
return
|
||||
}
|
||||
configuration = app.storage.profiles[req.Profile].Configuration
|
||||
displayprefs = app.storage.profiles[req.Profile].Displayprefs
|
||||
configuration = profile.Configuration
|
||||
displayprefs = profile.Displayprefs
|
||||
}
|
||||
policy = app.storage.profiles[req.Profile].Policy
|
||||
policy = profile.Policy
|
||||
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
||||
profile := app.storage.profiles[req.Profile]
|
||||
if profile.Ombi != nil && len(profile.Ombi) != 0 {
|
||||
ombi = profile.Ombi
|
||||
}
|
||||
|
@ -157,15 +157,6 @@ func (app *appContext) loadConfig() error {
|
||||
app.MustSetValue("updates", "channel", releaseChannel)
|
||||
}
|
||||
|
||||
app.storage.customEmails_path = app.config.Section("files").Key("custom_emails").String()
|
||||
app.storage.loadCustomEmails()
|
||||
|
||||
app.MustSetValue("user_page", "enabled", "true")
|
||||
if app.config.Section("user_page").Key("enabled").MustBool(false) {
|
||||
app.storage.userPage_path = app.config.Section("files").Key("custom_user_page_content").String()
|
||||
app.storage.loadUserPageContent()
|
||||
}
|
||||
|
||||
substituteStrings = app.config.Section("jellyfin").Key("substitute_jellyfin_strings").MustString("")
|
||||
|
||||
if substituteStrings != "" {
|
||||
|
81
daemon.go
81
daemon.go
@ -7,85 +7,53 @@ import "time"
|
||||
// the user cache is fresh.
|
||||
func (app *appContext) clearEmails() {
|
||||
app.debug.Println("Housekeeping: removing unused email addresses")
|
||||
users, status, err := app.jf.GetUsers(false)
|
||||
if status != 200 || err != nil || len(users) == 0 {
|
||||
app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
|
||||
return
|
||||
}
|
||||
// Rebuild email storage to from existing users to reduce time complexity
|
||||
emails := emailStore{}
|
||||
app.storage.emailsLock.Lock()
|
||||
for _, user := range users {
|
||||
if email, ok := app.storage.GetEmailsKey(user.ID); ok {
|
||||
emails[user.ID] = email
|
||||
emails := app.storage.GetEmails()
|
||||
for _, email := range emails {
|
||||
_, status, err := app.jf.UserByID(email.JellyfinID, false)
|
||||
if status == 200 && err != nil {
|
||||
continue
|
||||
}
|
||||
app.storage.DeleteEmailsKey(email.JellyfinID)
|
||||
}
|
||||
app.storage.emails = emails
|
||||
app.storage.storeEmails()
|
||||
app.storage.emailsLock.Unlock()
|
||||
}
|
||||
|
||||
// clearDiscord does the same as clearEmails, but for Discord Users.
|
||||
func (app *appContext) clearDiscord() {
|
||||
app.debug.Println("Housekeeping: removing unused Discord IDs")
|
||||
users, status, err := app.jf.GetUsers(false)
|
||||
if status != 200 || err != nil || len(users) == 0 {
|
||||
app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
|
||||
return
|
||||
}
|
||||
// Rebuild discord storage to from existing users to reduce time complexity
|
||||
dcUsers := discordStore{}
|
||||
app.storage.discordLock.Lock()
|
||||
for _, user := range users {
|
||||
if dcUser, ok := app.storage.GetDiscordKey(user.ID); ok {
|
||||
dcUsers[user.ID] = dcUser
|
||||
discordUsers := app.storage.GetDiscord()
|
||||
for _, discordUser := range discordUsers {
|
||||
_, status, err := app.jf.UserByID(discordUser.JellyfinID, false)
|
||||
if status == 200 && err != nil {
|
||||
continue
|
||||
}
|
||||
app.storage.DeleteDiscordKey(discordUser.JellyfinID)
|
||||
}
|
||||
app.storage.discord = dcUsers
|
||||
app.storage.storeDiscordUsers()
|
||||
app.storage.discordLock.Unlock()
|
||||
}
|
||||
|
||||
// clearMatrix does the same as clearEmails, but for Matrix Users.
|
||||
func (app *appContext) clearMatrix() {
|
||||
app.debug.Println("Housekeeping: removing unused Matrix IDs")
|
||||
users, status, err := app.jf.GetUsers(false)
|
||||
if status != 200 || err != nil || len(users) == 0 {
|
||||
app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
|
||||
return
|
||||
}
|
||||
// Rebuild matrix storage to from existing users to reduce time complexity
|
||||
mxUsers := matrixStore{}
|
||||
app.storage.matrixLock.Lock()
|
||||
for _, user := range users {
|
||||
if mxUser, ok := app.storage.GetMatrixKey(user.ID); ok {
|
||||
mxUsers[user.ID] = mxUser
|
||||
matrixUsers := app.storage.GetMatrix()
|
||||
for _, matrixUser := range matrixUsers {
|
||||
_, status, err := app.jf.UserByID(matrixUser.JellyfinID, false)
|
||||
if status == 200 && err != nil {
|
||||
continue
|
||||
}
|
||||
app.storage.DeleteMatrixKey(matrixUser.JellyfinID)
|
||||
}
|
||||
app.storage.matrix = mxUsers
|
||||
app.storage.storeMatrixUsers()
|
||||
app.storage.matrixLock.Unlock()
|
||||
}
|
||||
|
||||
// clearTelegram does the same as clearEmails, but for Telegram Users.
|
||||
func (app *appContext) clearTelegram() {
|
||||
app.debug.Println("Housekeeping: removing unused Telegram IDs")
|
||||
users, status, err := app.jf.GetUsers(false)
|
||||
if status != 200 || err != nil || len(users) == 0 {
|
||||
app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
|
||||
return
|
||||
}
|
||||
// Rebuild telegram storage to from existing users to reduce time complexity
|
||||
tgUsers := telegramStore{}
|
||||
app.storage.telegramLock.Lock()
|
||||
for _, user := range users {
|
||||
if tgUser, ok := app.storage.GetTelegramKey(user.ID); ok {
|
||||
tgUsers[user.ID] = tgUser
|
||||
telegramUsers := app.storage.GetTelegram()
|
||||
for _, telegramUser := range telegramUsers {
|
||||
_, status, err := app.jf.UserByID(telegramUser.JellyfinID, false)
|
||||
if status == 200 && err != nil {
|
||||
continue
|
||||
}
|
||||
app.storage.DeleteTelegramKey(telegramUser.JellyfinID)
|
||||
}
|
||||
app.storage.telegram = tgUsers
|
||||
app.storage.storeTelegramUsers()
|
||||
app.storage.telegramLock.Unlock()
|
||||
}
|
||||
|
||||
// https://bbengfort.github.io/snippets/2016/06/26/background-work-goroutines-timer.html THANKS
|
||||
@ -148,7 +116,6 @@ func (rt *housekeepingDaemon) run() {
|
||||
break
|
||||
}
|
||||
started := time.Now()
|
||||
rt.app.storage.loadInvites()
|
||||
|
||||
for _, job := range rt.jobs {
|
||||
job(rt.app)
|
||||
|
21
discord.go
21
discord.go
@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
dg "github.com/bwmarrin/discordgo"
|
||||
"github.com/timshannon/badgerhold/v4"
|
||||
)
|
||||
|
||||
type DiscordDaemon struct {
|
||||
@ -478,11 +479,11 @@ func (d *DiscordDaemon) cmdLang(s *dg.Session, i *dg.InteractionCreate, lang str
|
||||
code := i.ApplicationCommandData().Options[0].StringValue()
|
||||
if _, ok := d.app.storage.lang.Telegram[code]; ok {
|
||||
var user DiscordUser
|
||||
for jfID, u := range d.app.storage.GetDiscord() {
|
||||
for _, u := range d.app.storage.GetDiscord() {
|
||||
if u.ID == i.Interaction.Member.User.ID {
|
||||
u.Lang = code
|
||||
lang = code
|
||||
d.app.storage.SetDiscordKey(jfID, u)
|
||||
d.app.storage.SetDiscordKey(u.JellyfinID, u)
|
||||
user = u
|
||||
break
|
||||
}
|
||||
@ -585,10 +586,10 @@ func (d *DiscordDaemon) msgLang(s *dg.Session, m *dg.MessageCreate, sects []stri
|
||||
}
|
||||
if _, ok := d.app.storage.lang.Telegram[sects[1]]; ok {
|
||||
var user DiscordUser
|
||||
for jfID, u := range d.app.storage.GetDiscord() {
|
||||
for _, u := range d.app.storage.GetDiscord() {
|
||||
if u.ID == m.Author.ID {
|
||||
u.Lang = sects[1]
|
||||
d.app.storage.SetDiscordKey(jfID, u)
|
||||
d.app.storage.SetDiscordKey(u.JellyfinID, u)
|
||||
user = u
|
||||
break
|
||||
}
|
||||
@ -708,15 +709,9 @@ func (d *DiscordDaemon) AssignedUserVerified(pin string, jfID string) (user Disc
|
||||
}
|
||||
|
||||
// UserExists returns whether or not a user with the given ID exists.
|
||||
func (d *DiscordDaemon) UserExists(id string) (ok bool) {
|
||||
ok = false
|
||||
for _, u := range d.app.storage.GetDiscord() {
|
||||
if u.ID == id {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
func (d *DiscordDaemon) UserExists(id string) bool {
|
||||
c, err := d.app.storage.db.Count(&DiscordUser{}, badgerhold.Where("ID").Eq(id))
|
||||
return err != nil || c > 0
|
||||
}
|
||||
|
||||
// DeleteVerifiedUser removes the token with the given PIN.
|
||||
|
136
email.go
136
email.go
@ -19,8 +19,10 @@ import (
|
||||
|
||||
"github.com/gomarkdown/markdown"
|
||||
"github.com/gomarkdown/markdown/html"
|
||||
"github.com/hrfee/mediabrowser"
|
||||
"github.com/itchyny/timefmt-go"
|
||||
"github.com/mailgun/mailgun-go/v4"
|
||||
"github.com/timshannon/badgerhold/v4"
|
||||
sMail "github.com/xhit/go-simple-mail/v2"
|
||||
)
|
||||
|
||||
@ -329,10 +331,11 @@ func (emailer *Emailer) constructConfirmation(code, username, key string, app *a
|
||||
}
|
||||
var err error
|
||||
template := emailer.confirmationValues(code, username, key, app, noSub)
|
||||
if app.storage.customEmails.EmailConfirmation.Enabled {
|
||||
message := app.storage.MustGetCustomContentKey("EmailConfirmation")
|
||||
if message.Enabled {
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.EmailConfirmation.Content,
|
||||
app.storage.customEmails.EmailConfirmation.Variables,
|
||||
message.Content,
|
||||
message.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
@ -412,10 +415,11 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
|
||||
}
|
||||
template := emailer.inviteValues(code, invite, app, noSub)
|
||||
var err error
|
||||
if app.storage.customEmails.InviteEmail.Enabled {
|
||||
message := app.storage.MustGetCustomContentKey("InviteEmail")
|
||||
if message.Enabled {
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.InviteEmail.Content,
|
||||
app.storage.customEmails.InviteEmail.Variables,
|
||||
message.Content,
|
||||
message.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
@ -451,10 +455,11 @@ func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appCont
|
||||
}
|
||||
var err error
|
||||
template := emailer.expiryValues(code, invite, app, noSub)
|
||||
if app.storage.customEmails.InviteExpiry.Enabled {
|
||||
message := app.storage.MustGetCustomContentKey("InviteExpiry")
|
||||
if message.Enabled {
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.InviteExpiry.Content,
|
||||
app.storage.customEmails.InviteExpiry.Variables,
|
||||
message.Content,
|
||||
message.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
@ -505,10 +510,11 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
|
||||
}
|
||||
template := emailer.createdValues(code, username, address, invite, app, noSub)
|
||||
var err error
|
||||
if app.storage.customEmails.UserCreated.Enabled {
|
||||
message := app.storage.MustGetCustomContentKey("UserCreated")
|
||||
if message.Enabled {
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.UserCreated.Content,
|
||||
app.storage.customEmails.UserCreated.Variables,
|
||||
message.Content,
|
||||
message.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
@ -578,10 +584,11 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext, noSub
|
||||
}
|
||||
template := emailer.resetValues(pwr, app, noSub)
|
||||
var err error
|
||||
if app.storage.customEmails.PasswordReset.Enabled {
|
||||
message := app.storage.MustGetCustomContentKey("PasswordReset")
|
||||
if message.Enabled {
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.PasswordReset.Content,
|
||||
app.storage.customEmails.PasswordReset.Variables,
|
||||
message.Content,
|
||||
message.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
@ -619,10 +626,11 @@ func (emailer *Emailer) constructDeleted(reason string, app *appContext, noSub b
|
||||
}
|
||||
var err error
|
||||
template := emailer.deletedValues(reason, app, noSub)
|
||||
if app.storage.customEmails.UserDeleted.Enabled {
|
||||
message := app.storage.MustGetCustomContentKey("UserDeleted")
|
||||
if message.Enabled {
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.UserDeleted.Content,
|
||||
app.storage.customEmails.UserDeleted.Variables,
|
||||
message.Content,
|
||||
message.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
@ -660,10 +668,11 @@ func (emailer *Emailer) constructDisabled(reason string, app *appContext, noSub
|
||||
}
|
||||
var err error
|
||||
template := emailer.disabledValues(reason, app, noSub)
|
||||
if app.storage.customEmails.UserDisabled.Enabled {
|
||||
message := app.storage.MustGetCustomContentKey("UserDisabled")
|
||||
if message.Enabled {
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.UserDisabled.Content,
|
||||
app.storage.customEmails.UserDisabled.Variables,
|
||||
message.Content,
|
||||
message.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
@ -701,10 +710,11 @@ func (emailer *Emailer) constructEnabled(reason string, app *appContext, noSub b
|
||||
}
|
||||
var err error
|
||||
template := emailer.enabledValues(reason, app, noSub)
|
||||
if app.storage.customEmails.UserEnabled.Enabled {
|
||||
message := app.storage.MustGetCustomContentKey("UserEnabled")
|
||||
if message.Enabled {
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.UserEnabled.Content,
|
||||
app.storage.customEmails.UserEnabled.Variables,
|
||||
message.Content,
|
||||
message.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
@ -756,7 +766,8 @@ func (emailer *Emailer) constructWelcome(username string, expiry time.Time, app
|
||||
}
|
||||
var err error
|
||||
var template map[string]interface{}
|
||||
if app.storage.customEmails.WelcomeEmail.Enabled {
|
||||
message := app.storage.MustGetCustomContentKey("WelcomeEmail")
|
||||
if message.Enabled {
|
||||
template = emailer.welcomeValues(username, expiry, app, noSub, true)
|
||||
} else {
|
||||
template = emailer.welcomeValues(username, expiry, app, noSub, false)
|
||||
@ -766,11 +777,11 @@ func (emailer *Emailer) constructWelcome(username string, expiry time.Time, app
|
||||
"date": "{yourAccountWillExpire}",
|
||||
})
|
||||
}
|
||||
if app.storage.customEmails.WelcomeEmail.Enabled {
|
||||
if message.Enabled {
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.WelcomeEmail.Content,
|
||||
app.storage.customEmails.WelcomeEmail.Variables,
|
||||
app.storage.customEmails.WelcomeEmail.Conditionals,
|
||||
message.Content,
|
||||
message.Variables,
|
||||
message.Conditionals,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
@ -801,10 +812,11 @@ func (emailer *Emailer) constructUserExpired(app *appContext, noSub bool) (*Mess
|
||||
}
|
||||
var err error
|
||||
template := emailer.userExpiredValues(app, noSub)
|
||||
if app.storage.customEmails.UserExpired.Enabled {
|
||||
message := app.storage.MustGetCustomContentKey("UserExpired")
|
||||
if message.Enabled {
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.UserExpired.Content,
|
||||
app.storage.customEmails.UserExpired.Variables,
|
||||
message.Content,
|
||||
message.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
@ -874,31 +886,63 @@ func (app *appContext) getAddressOrName(jfID string) string {
|
||||
|
||||
// ReverseUserSearch returns the jellyfin ID of the user with the given username, email, or contact method username.
|
||||
// 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) string {
|
||||
func (app *appContext) ReverseUserSearch(address string) (user mediabrowser.User, ok bool) {
|
||||
ok = false
|
||||
user, status, err := app.jf.UserByName(address, false)
|
||||
if status == 200 && err == nil {
|
||||
return user.ID
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
for id, email := range app.storage.GetEmails() {
|
||||
if strings.ToLower(address) == strings.ToLower(email.Addr) {
|
||||
return id
|
||||
emailAddresses := []EmailAddress{}
|
||||
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 {
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
for id, dcUser := range app.storage.GetDiscord() {
|
||||
// Dont know how we'd use badgerhold when we need to render each username,
|
||||
// Apart from storing the rendered name in the db.
|
||||
for _, dcUser := range app.storage.GetDiscord() {
|
||||
if RenderDiscordUsername(dcUser) == strings.ToLower(address) {
|
||||
return id
|
||||
user, status, err = app.jf.UserByID(dcUser.JellyfinID, false)
|
||||
if status == 200 && err == nil {
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
tgUsername := strings.TrimPrefix(address, "@")
|
||||
for id, tgUser := range app.storage.GetTelegram() {
|
||||
if tgUsername == tgUser.Username {
|
||||
return id
|
||||
telegramUsers := []TelegramUser{}
|
||||
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 {
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
for id, mxUser := range app.storage.GetMatrix() {
|
||||
if address == mxUser.UserID {
|
||||
return id
|
||||
matrixUsers := []MatrixUser{}
|
||||
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 {
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return
|
||||
}
|
||||
|
||||
// EmailAddressExists returns whether or not a user with the given email address exists.
|
||||
func (app *appContext) EmailAddressExists(address string) bool {
|
||||
c, err := app.storage.db.Count(&EmailAddress{}, badgerhold.Where("Addr").Eq(address))
|
||||
return err != nil || c > 0
|
||||
}
|
||||
|
14
go.mod
14
go.mod
@ -48,7 +48,12 @@ require (
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/dgraph-io/badger/v3 v3.2103.1 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201 // indirect
|
||||
github.com/getlantern/errors v1.0.3 // indirect
|
||||
@ -69,12 +74,19 @@ require (
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/go-test/deep v1.1.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/flatbuffers v2.0.0+incompatible // indirect
|
||||
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/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.13.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
@ -92,9 +104,11 @@ require (
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/timshannon/badgerhold/v4 v4.0.2 // indirect
|
||||
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.opentelemetry.io/otel v1.16.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.16.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.16.0 // indirect
|
||||
|
149
go.sum
149
go.sum
@ -1,11 +1,14 @@
|
||||
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/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/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY=
|
||||
@ -13,17 +16,39 @@ github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger/v3 v3.2103.1 h1:zaX53IRg7ycxVlkd5pYdCeFp1FynD6qBGQoQql3R3Hk=
|
||||
github.com/dgraph-io/badger/v3 v3.2103.1/go.mod h1:dULbq6ehJ5K0cGW/1TQ9iSfUk0gbSiToDWmWmTsJ53E=
|
||||
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
|
||||
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:M88ob4TyDnEqNuL3PgsE/p3bDujfspnulR+0dQWNYZs=
|
||||
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:buzQsO8HHkZX2Q45fdfGH1xejPjuDQaXH8btcYMFzPM=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||
@ -33,6 +58,7 @@ github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+ne
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
@ -131,20 +157,54 @@ github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 h1:2voWjNECnrZRbfwXxHB1/j8wa6xdKn85B5NzgVL/pTU=
|
||||
github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomarkdown/markdown v0.0.0-20230322041520-c84983bdbf2a h1:AWZzzFrqyjYlRloN6edwTLTUbKxf5flLXNuTBDm3Ews=
|
||||
github.com/gomarkdown/markdown v0.0.0-20230322041520-c84983bdbf2a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/flatbuffers v1.12.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/flatbuffers v2.0.0+incompatible h1:dicJ2oXwypfwUGnB2/TYWYEKiuk9eYQlQO/AnOHl5mI=
|
||||
github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
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/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=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@ -153,8 +213,10 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
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.8 h1:y0iBCb6jE3QKcsiCJSYva2fFPHRn4UA+sGRzoPuJ/Dk=
|
||||
github.com/hrfee/mediabrowser v0.3.8/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=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
@ -165,6 +227,11 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ=
|
||||
github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
@ -185,6 +252,7 @@ github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJV
|
||||
github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts=
|
||||
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailgun/mailgun-go/v4 v4.9.0 h1:wRbxvVQ5QObFewLxc1uVvipA16D8gxeiO+cBOca51Iw=
|
||||
github.com/mailgun/mailgun-go/v4 v4.9.0/go.mod h1:FJlF9rI5cQT+mrwujtJjPMbIVy3Ebor9bKTVsJ0QU40=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
@ -206,6 +274,8 @@ 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.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
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=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -216,6 +286,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
@ -225,6 +296,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/robert-nix/ansihtml v1.0.1 h1:VTiyQ6/+AxSJoSSLsMecnkh8i0ZqOEdiRl/odOc64fc=
|
||||
github.com/robert-nix/ansihtml v1.0.1/go.mod h1:CJwclxYaTPc2RfcxtanEACsYuTksh4yDXcNeHHKZINE=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
@ -233,10 +305,19 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
|
||||
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/steambap/captcha v1.4.1 h1:OmMdxLCWCqJvsFaFYwRpvMckIuvI6s8s1LsBrBw97P0=
|
||||
github.com/steambap/captcha v1.4.1/go.mod h1:oC9T7IfEgnrhzjDz5Djf1H7GPffCzRMbsQfFkJmhlnk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@ -276,6 +357,9 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/timshannon/badgerhold/v3 v3.0.0-20210909134927-2b6764d68c1e/go.mod h1:/Seq5xGNo8jLhSbDX3jdbeZrp4yFIpQ6/7n4TjziEWs=
|
||||
github.com/timshannon/badgerhold/v4 v4.0.2 h1:83OLY/NFnEaMnHEPd84bYtkLipVkjTsMbzQRYbk47g4=
|
||||
github.com/timshannon/badgerhold/v4 v4.0.2/go.mod h1:rh6RyXLQFsvrvcKondPQQFZnNovpRzu+gS0FlLxYuHY=
|
||||
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 h1:PM5hJF7HVfNWmCjMdEfbuOBNXSVF2cMFGgQTPdKCbwM=
|
||||
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
@ -285,6 +369,7 @@ github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/o
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
@ -296,8 +381,14 @@ github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6Fk
|
||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE=
|
||||
github.com/xhit/go-simple-mail/v2 v2.13.0 h1:OANWU9jHZrVfBkNkvLf8Ww0fexwpQVF/v/5f96fFTLI=
|
||||
github.com/xhit/go-simple-mail/v2 v2.13.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo=
|
||||
go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s=
|
||||
go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4=
|
||||
@ -320,26 +411,37 @@ go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
|
||||
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
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.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
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=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@ -347,29 +449,45 @@ golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -396,11 +514,16 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
@ -410,13 +533,37 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@ -435,6 +582,8 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
|
||||
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
|
||||
maunium.net/go/mautrix v0.15.2 h1:fUiVajeoOR92uJoSShHbCvh7uG6lDY4ZO4Mvt90LbjU=
|
||||
|
@ -79,7 +79,7 @@
|
||||
"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.",
|
||||
"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",
|
||||
"userProfilesLibraries": "Libraries",
|
||||
"addProfile": "Add Profile",
|
||||
|
56
main.go
56
main.go
@ -335,59 +335,8 @@ func start(asDaemon, firstCall bool) {
|
||||
|
||||
app.debug.Printf("Loaded config file \"%s\"", app.configPath)
|
||||
|
||||
app.debug.Println("Loading storage")
|
||||
|
||||
app.storage.invite_path = app.config.Section("files").Key("invites").String()
|
||||
if err := app.storage.loadInvites(); err != nil {
|
||||
app.err.Printf("Failed to load Invites: %v", err)
|
||||
}
|
||||
app.storage.emails_path = app.config.Section("files").Key("emails").String()
|
||||
if err := app.storage.loadEmails(); err != nil {
|
||||
app.err.Printf("Failed to load Emails: %v", err)
|
||||
err := migrateEmailStorage(app)
|
||||
if err != nil {
|
||||
app.err.Printf("Failed to migrate Email storage: %v", err)
|
||||
}
|
||||
}
|
||||
app.storage.policy_path = app.config.Section("files").Key("user_template").String()
|
||||
if err := app.storage.loadPolicy(); err != nil {
|
||||
app.err.Printf("Failed to load Policy: %v", err)
|
||||
}
|
||||
app.storage.configuration_path = app.config.Section("files").Key("user_configuration").String()
|
||||
if err := app.storage.loadConfiguration(); err != nil {
|
||||
app.err.Printf("Failed to load Configuration: %v", err)
|
||||
}
|
||||
app.storage.displayprefs_path = app.config.Section("files").Key("user_displayprefs").String()
|
||||
if err := app.storage.loadDisplayprefs(); err != nil {
|
||||
app.err.Printf("Failed to load Displayprefs: %v", err)
|
||||
}
|
||||
app.storage.users_path = app.config.Section("files").Key("users").String()
|
||||
if err := app.storage.loadUsers(); err != nil {
|
||||
app.err.Printf("Failed to load Users: %v", err)
|
||||
}
|
||||
app.storage.telegram_path = app.config.Section("files").Key("telegram_users").String()
|
||||
if err := app.storage.loadTelegramUsers(); err != nil {
|
||||
app.err.Printf("Failed to load Telegram users: %v", err)
|
||||
}
|
||||
app.storage.discord_path = app.config.Section("files").Key("discord_users").String()
|
||||
if err := app.storage.loadDiscordUsers(); err != nil {
|
||||
app.err.Printf("Failed to load Discord users: %v", err)
|
||||
}
|
||||
app.storage.matrix_path = app.config.Section("files").Key("matrix_users").String()
|
||||
if err := app.storage.loadMatrixUsers(); err != nil {
|
||||
app.err.Printf("Failed to load Matrix users: %v", err)
|
||||
}
|
||||
app.storage.announcements_path = app.config.Section("files").Key("announcements").String()
|
||||
if err := app.storage.loadAnnouncements(); err != nil {
|
||||
app.err.Printf("Failed to load announcement templates: %v", err)
|
||||
}
|
||||
|
||||
app.storage.profiles_path = app.config.Section("files").Key("user_profiles").String()
|
||||
app.storage.loadProfiles()
|
||||
|
||||
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
||||
app.storage.ombi_path = app.config.Section("files").Key("ombi_template").String()
|
||||
app.storage.loadOmbiTemplate()
|
||||
app.debug.Printf("Connecting to Ombi")
|
||||
ombiServer := app.config.Section("ombi").Key("server").String()
|
||||
app.ombi = ombi.NewOmbi(
|
||||
ombiServer,
|
||||
@ -397,6 +346,9 @@ func start(asDaemon, firstCall bool) {
|
||||
|
||||
}
|
||||
|
||||
app.storage.db_path = filepath.Join(app.dataPath, "db")
|
||||
app.ConnectDB()
|
||||
defer app.storage.db.Close()
|
||||
// Read config-base for settings on web.
|
||||
app.configBasePath = "config-base.json"
|
||||
configBase, _ := fs.ReadFile(localFS, app.configBasePath)
|
||||
|
18
matrix.go
18
matrix.go
@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gomarkdown/markdown"
|
||||
"github.com/timshannon/badgerhold/v4"
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
@ -31,11 +32,12 @@ type UnverifiedUser struct {
|
||||
}
|
||||
|
||||
type MatrixUser struct {
|
||||
RoomID string
|
||||
Encrypted bool
|
||||
UserID string
|
||||
Lang string
|
||||
Contact bool
|
||||
RoomID string
|
||||
Encrypted bool
|
||||
UserID string
|
||||
Lang string
|
||||
Contact bool
|
||||
JellyfinID string `badgerhold:"key"`
|
||||
}
|
||||
|
||||
var matrixFilter = mautrix.Filter{
|
||||
@ -268,6 +270,12 @@ func (d *MatrixDaemon) Send(message *Message, users ...MatrixUser) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// UserExists returns whether or not a user with the given User ID exists.
|
||||
func (d *MatrixDaemon) UserExists(userID string) bool {
|
||||
c, err := d.app.storage.db.Count(&MatrixUser{}, badgerhold.Where("UserID").Eq(userID))
|
||||
return err != nil || c > 0
|
||||
}
|
||||
|
||||
// User enters ID on sign-up, a PIN is sent to them. They enter it on sign-up.
|
||||
|
||||
// Message the user first, to avoid E2EE by default
|
||||
|
189
migrations.go
189
migrations.go
@ -16,26 +16,28 @@ func runMigrations(app *appContext) {
|
||||
migrateNotificationMethods(app)
|
||||
linkExistingOmbiDiscordTelegram(app)
|
||||
// migrateHyphens(app)
|
||||
migrateToBadger(app)
|
||||
}
|
||||
|
||||
// Migrate pre-0.2.0 user templates to profiles
|
||||
func migrateProfiles(app *appContext) {
|
||||
if !(app.storage.policy.BlockedTags == nil && app.storage.configuration.GroupedFolders == nil && len(app.storage.displayprefs) == 0) {
|
||||
app.info.Println("Migrating user template files to new profile format")
|
||||
app.storage.migrateToProfile()
|
||||
for _, path := range [3]string{app.storage.policy_path, app.storage.configuration_path, app.storage.displayprefs_path} {
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
dir, fname := filepath.Split(path)
|
||||
newFname := strings.Replace(fname, ".json", ".old.json", 1)
|
||||
err := os.Rename(path, filepath.Join(dir, newFname))
|
||||
if err != nil {
|
||||
app.err.Fatalf("Failed to rename %s: %s", fname, err)
|
||||
}
|
||||
if app.storage.deprecatedPolicy.BlockedTags == nil && app.storage.deprecatedConfiguration.GroupedFolders == nil && len(app.storage.deprecatedDisplayprefs) == 0 {
|
||||
return
|
||||
}
|
||||
app.info.Println("Migrating user template files to new profile format")
|
||||
app.storage.migrateToProfile()
|
||||
for _, path := range [3]string{app.storage.policy_path, app.storage.configuration_path, app.storage.displayprefs_path} {
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
dir, fname := filepath.Split(path)
|
||||
newFname := strings.Replace(fname, ".json", ".old.json", 1)
|
||||
err := os.Rename(path, filepath.Join(dir, newFname))
|
||||
if err != nil {
|
||||
app.err.Fatalf("Failed to rename %s: %s", fname, err)
|
||||
}
|
||||
}
|
||||
app.info.Println("In case of a problem, your original files have been renamed to <file>.old.json")
|
||||
app.storage.storeProfiles()
|
||||
}
|
||||
app.info.Println("In case of a problem, your original files have been renamed to <file>.old.json")
|
||||
app.storage.storeProfiles()
|
||||
}
|
||||
|
||||
// Migrate pre-0.2.5 bootstrap theme choice to a17t version.
|
||||
@ -131,7 +133,7 @@ func migrateNotificationMethods(app *appContext) error {
|
||||
return nil
|
||||
}
|
||||
changes := false
|
||||
for code, invite := range app.storage.invites {
|
||||
for code, invite := range app.storage.deprecatedInvites {
|
||||
if invite.Notify == nil {
|
||||
continue
|
||||
}
|
||||
@ -139,9 +141,9 @@ func migrateNotificationMethods(app *appContext) error {
|
||||
if !strings.Contains(address, "@") {
|
||||
continue
|
||||
}
|
||||
for id, email := range app.storage.GetEmails() {
|
||||
for _, email := range app.storage.GetEmails() {
|
||||
if email.Addr == address {
|
||||
invite.Notify[id] = notifyPrefs
|
||||
invite.Notify[email.JellyfinID] = notifyPrefs
|
||||
delete(invite.Notify, address)
|
||||
changes = true
|
||||
break
|
||||
@ -149,7 +151,7 @@ func migrateNotificationMethods(app *appContext) error {
|
||||
}
|
||||
}
|
||||
if changes {
|
||||
app.storage.invites[code] = invite
|
||||
app.storage.deprecatedInvites[code] = invite
|
||||
}
|
||||
}
|
||||
if changes {
|
||||
@ -168,16 +170,16 @@ func linkExistingOmbiDiscordTelegram(app *appContext) error {
|
||||
return nil
|
||||
}
|
||||
idList := map[string][2]string{}
|
||||
for jfID, user := range app.storage.GetDiscord() {
|
||||
idList[jfID] = [2]string{user.ID, ""}
|
||||
for _, user := range app.storage.GetDiscord() {
|
||||
idList[user.JellyfinID] = [2]string{user.ID, ""}
|
||||
}
|
||||
for jfID, user := range app.storage.GetTelegram() {
|
||||
vals, ok := idList[jfID]
|
||||
for _, user := range app.storage.GetTelegram() {
|
||||
vals, ok := idList[user.JellyfinID]
|
||||
if !ok {
|
||||
vals = [2]string{"", ""}
|
||||
}
|
||||
vals[1] = user.Username
|
||||
idList[jfID] = vals
|
||||
idList[user.JellyfinID] = vals
|
||||
}
|
||||
for jfID, ids := range idList {
|
||||
ombiUser, status, err := app.getOmbiUser(jfID)
|
||||
@ -194,6 +196,143 @@ func linkExistingOmbiDiscordTelegram(app *appContext) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MigrationStatus is just used to store whether data from JSON files has been migrated to the DB.
|
||||
type MigrationStatus struct {
|
||||
Done bool
|
||||
}
|
||||
|
||||
func loadLegacyData(app *appContext) {
|
||||
app.storage.invite_path = app.config.Section("files").Key("invites").String()
|
||||
if err := app.storage.loadInvites(); err != nil {
|
||||
app.err.Printf("LegacyData: Failed to load Invites: %v", err)
|
||||
}
|
||||
app.storage.emails_path = app.config.Section("files").Key("emails").String()
|
||||
if err := app.storage.loadEmails(); err != nil {
|
||||
app.err.Printf("LegacyData: Failed to load Emails: %v", err)
|
||||
err := migrateEmailStorage(app)
|
||||
if err != nil {
|
||||
app.err.Printf("LegacyData: Failed to migrate Email storage: %v", err)
|
||||
}
|
||||
}
|
||||
app.storage.users_path = app.config.Section("files").Key("users").String()
|
||||
if err := app.storage.loadUserExpiries(); err != nil {
|
||||
app.err.Printf("LegacyData: Failed to load Users: %v", err)
|
||||
}
|
||||
app.storage.telegram_path = app.config.Section("files").Key("telegram_users").String()
|
||||
if err := app.storage.loadTelegramUsers(); err != nil {
|
||||
app.err.Printf("LegacyData: Failed to load Telegram users: %v", err)
|
||||
}
|
||||
app.storage.discord_path = app.config.Section("files").Key("discord_users").String()
|
||||
if err := app.storage.loadDiscordUsers(); err != nil {
|
||||
app.err.Printf("LegacyData: Failed to load Discord users: %v", err)
|
||||
}
|
||||
app.storage.matrix_path = app.config.Section("files").Key("matrix_users").String()
|
||||
if err := app.storage.loadMatrixUsers(); err != nil {
|
||||
app.err.Printf("LegacyData: Failed to load Matrix users: %v", err)
|
||||
}
|
||||
app.storage.announcements_path = app.config.Section("files").Key("announcements").String()
|
||||
if err := app.storage.loadAnnouncements(); err != nil {
|
||||
app.err.Printf("LegacyData: Failed to load announcement templates: %v", err)
|
||||
}
|
||||
|
||||
app.storage.profiles_path = app.config.Section("files").Key("user_profiles").String()
|
||||
app.storage.loadProfiles()
|
||||
|
||||
app.storage.customEmails_path = app.config.Section("files").Key("custom_emails").String()
|
||||
app.storage.loadCustomEmails()
|
||||
|
||||
app.MustSetValue("user_page", "enabled", "true")
|
||||
if app.config.Section("user_page").Key("enabled").MustBool(false) {
|
||||
app.storage.userPage_path = app.config.Section("files").Key("custom_user_page_content").String()
|
||||
app.storage.loadUserPageContent()
|
||||
}
|
||||
}
|
||||
|
||||
func migrateToBadger(app *appContext) {
|
||||
// Check the DB to see if we've already migrated
|
||||
migrated := MigrationStatus{}
|
||||
app.storage.db.Get("migrated_to_db", &migrated)
|
||||
if migrated.Done {
|
||||
return
|
||||
}
|
||||
app.info.Println("Migrating to Badger(hold)")
|
||||
loadLegacyData(app)
|
||||
for k, v := range app.storage.deprecatedAnnouncements {
|
||||
app.storage.SetAnnouncementsKey(k, v)
|
||||
}
|
||||
|
||||
for jfID, v := range app.storage.deprecatedDiscord {
|
||||
app.storage.SetDiscordKey(jfID, v)
|
||||
}
|
||||
|
||||
for jfID, v := range app.storage.deprecatedTelegram {
|
||||
app.storage.SetTelegramKey(jfID, v)
|
||||
}
|
||||
|
||||
for jfID, v := range app.storage.deprecatedMatrix {
|
||||
app.storage.SetMatrixKey(jfID, v)
|
||||
}
|
||||
|
||||
for jfID, v := range app.storage.deprecatedEmails {
|
||||
app.storage.SetEmailsKey(jfID, v)
|
||||
}
|
||||
|
||||
for k, v := range app.storage.deprecatedInvites {
|
||||
app.storage.SetInvitesKey(k, v)
|
||||
}
|
||||
|
||||
for k, v := range app.storage.deprecatedUserExpiries {
|
||||
app.storage.SetUserExpiryKey(k, UserExpiry{Expiry: v})
|
||||
}
|
||||
|
||||
for k, v := range app.storage.deprecatedProfiles {
|
||||
app.storage.SetProfileKey(k, v)
|
||||
}
|
||||
|
||||
if _, ok := app.storage.GetCustomContentKey("UserCreated"); !ok {
|
||||
app.storage.SetCustomContentKey("UserCreated", app.storage.deprecatedCustomEmails.UserCreated)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("InviteExpiry"); !ok {
|
||||
app.storage.SetCustomContentKey("InviteExpiry", app.storage.deprecatedCustomEmails.InviteExpiry)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("PasswordReset"); !ok {
|
||||
app.storage.SetCustomContentKey("PasswordReset", app.storage.deprecatedCustomEmails.PasswordReset)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserDeleted"); !ok {
|
||||
app.storage.SetCustomContentKey("UserDeleted", app.storage.deprecatedCustomEmails.UserDeleted)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserDisabled"); !ok {
|
||||
app.storage.SetCustomContentKey("UserDisabled", app.storage.deprecatedCustomEmails.UserDisabled)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserEnabled"); !ok {
|
||||
app.storage.SetCustomContentKey("UserEnabled", app.storage.deprecatedCustomEmails.UserEnabled)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("InviteEmail"); !ok {
|
||||
app.storage.SetCustomContentKey("InviteEmail", app.storage.deprecatedCustomEmails.InviteEmail)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("WelcomeEmail"); !ok {
|
||||
app.storage.SetCustomContentKey("WelcomeEmail", app.storage.deprecatedCustomEmails.WelcomeEmail)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("EmailConfirmation"); !ok {
|
||||
app.storage.SetCustomContentKey("EmailConfirmation", app.storage.deprecatedCustomEmails.EmailConfirmation)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserExpired"); !ok {
|
||||
app.storage.SetCustomContentKey("UserExpired", app.storage.deprecatedCustomEmails.UserExpired)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserLogin"); !ok {
|
||||
app.storage.SetCustomContentKey("UserLogin", app.storage.deprecatedUserPageContent.Login)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserPage"); !ok {
|
||||
app.storage.SetCustomContentKey("UserPage", app.storage.deprecatedUserPageContent.Page)
|
||||
}
|
||||
|
||||
err := app.storage.db.Upsert("migrated_to_db", MigrationStatus{true})
|
||||
if err != nil {
|
||||
app.err.Fatalf("Failed to migrate to DB: %v\n", err)
|
||||
}
|
||||
app.info.Println("All data migrated to database. JSON files in the config folder can be deleted if you are sure all data is correct in the app. Create an issue if you have problems.")
|
||||
}
|
||||
|
||||
// Migrate between hyphenated & non-hyphenated user IDs. Doesn't seem to happen anymore, so disabled.
|
||||
// func migrateHyphens(app *appContext) {
|
||||
// checkVersion := func(version string) int {
|
||||
@ -212,8 +351,8 @@ func linkExistingOmbiDiscordTelegram(app *appContext) error {
|
||||
// app.jf.GetUsers(false)
|
||||
//
|
||||
// noHyphens := true
|
||||
// for id := range app.storage.GetEmails() {
|
||||
// if strings.Contains(id, "-") {
|
||||
// for _, e := range app.storage.GetEmails() {
|
||||
// if strings.Contains(e.JellyfinID, "-") {
|
||||
// noHyphens = false
|
||||
// break
|
||||
// }
|
||||
@ -255,7 +394,7 @@ func linkExistingOmbiDiscordTelegram(app *appContext) error {
|
||||
// app.storage.emails = newEmails
|
||||
// app.storage.users = newUsers
|
||||
// err = app.storage.storeEmails()
|
||||
// err2 = app.storage.storeUsers()
|
||||
// err2 = app.storage.storeUserExpiries()
|
||||
// if err != nil {
|
||||
// app.err.Fatalf("couldn't store emails.json: %v", err)
|
||||
// }
|
||||
|
@ -100,7 +100,6 @@ func pwrMonitor(app *appContext, watcher *fsnotify.Watcher) {
|
||||
app.debug.Printf("Error: %s", err)
|
||||
return
|
||||
}
|
||||
app.storage.loadEmails()
|
||||
uid := user.ID
|
||||
if uid == "" {
|
||||
app.err.Printf("Couldn't get user ID for user \"%s\"", pwr.Username)
|
||||
|
549
storage.go
549
storage.go
@ -8,11 +8,11 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hrfee/mediabrowser"
|
||||
"github.com/steambap/captcha"
|
||||
"github.com/timshannon/badgerhold/v4"
|
||||
)
|
||||
|
||||
type discordStore map[string]DiscordUser
|
||||
@ -20,224 +20,439 @@ type telegramStore map[string]TelegramUser
|
||||
type matrixStore map[string]MatrixUser
|
||||
type emailStore map[string]EmailAddress
|
||||
|
||||
type UserExpiry struct {
|
||||
JellyfinID string `badgerhold:"key"`
|
||||
Expiry time.Time
|
||||
}
|
||||
|
||||
type Storage struct {
|
||||
timePattern string
|
||||
timePattern string
|
||||
|
||||
db_path string
|
||||
db *badgerhold.Store
|
||||
|
||||
invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path, profiles_path, customEmails_path, users_path, telegram_path, discord_path, matrix_path, announcements_path, matrix_sql_path, userPage_path string
|
||||
users map[string]time.Time // Map of Jellyfin User IDs to their expiry times.
|
||||
invites Invites
|
||||
profiles map[string]Profile
|
||||
defaultProfile string
|
||||
displayprefs, ombi_template map[string]interface{}
|
||||
emails emailStore
|
||||
telegram telegramStore // Map of Jellyfin User IDs to telegram users.
|
||||
discord discordStore // Map of Jellyfin user IDs to discord users.
|
||||
matrix matrixStore // Map of Jellyfin user IDs to Matrix users.
|
||||
customEmails customEmails
|
||||
userPage userPageContent
|
||||
policy mediabrowser.Policy
|
||||
configuration mediabrowser.Configuration
|
||||
deprecatedUserExpiries map[string]time.Time // Map of Jellyfin User IDs to their expiry times.
|
||||
deprecatedInvites Invites
|
||||
deprecatedProfiles map[string]Profile
|
||||
deprecatedDisplayprefs, deprecatedOmbiTemplate map[string]interface{}
|
||||
deprecatedEmails emailStore // Map of Jellyfin User IDs to Email addresses.
|
||||
deprecatedTelegram telegramStore // Map of Jellyfin User IDs to telegram users.
|
||||
deprecatedDiscord discordStore // Map of Jellyfin user IDs to discord users.
|
||||
deprecatedMatrix matrixStore // Map of Jellyfin user IDs to Matrix users.
|
||||
deprecatedPolicy mediabrowser.Policy
|
||||
deprecatedConfiguration mediabrowser.Configuration
|
||||
deprecatedAnnouncements map[string]announcementTemplate
|
||||
deprecatedCustomEmails customEmails
|
||||
deprecatedUserPageContent userPageContent
|
||||
lang Lang
|
||||
announcements map[string]announcementTemplate
|
||||
invitesLock, usersLock, discordLock, telegramLock, matrixLock, emailsLock sync.Mutex
|
||||
}
|
||||
|
||||
func (app *appContext) ConnectDB() {
|
||||
opts := badgerhold.DefaultOptions
|
||||
opts.Dir = app.storage.db_path
|
||||
opts.ValueDir = app.storage.db_path
|
||||
db, err := badgerhold.Open(opts)
|
||||
if err != nil {
|
||||
app.err.Fatalf("Failed to open db \"%s\": %v", app.storage.db_path, err)
|
||||
}
|
||||
app.storage.db = db
|
||||
app.info.Printf("Connected to DB \"%s\"", app.storage.db_path)
|
||||
}
|
||||
|
||||
// GetEmails returns a copy of the store.
|
||||
func (st *Storage) GetEmails() emailStore {
|
||||
return st.emails
|
||||
func (st *Storage) GetEmails() []EmailAddress {
|
||||
result := []EmailAddress{}
|
||||
err := st.db.Find(&result, &badgerhold.Query{})
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find emails: %v\n", err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetEmailsKey returns the value stored in the store's key.
|
||||
func (st *Storage) GetEmailsKey(k string) (EmailAddress, bool) {
|
||||
v, ok := st.emails[k]
|
||||
return v, ok
|
||||
result := EmailAddress{}
|
||||
err := st.db.Get(k, &result)
|
||||
ok := true
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find email: %v\n", err)
|
||||
ok = false
|
||||
}
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// SetEmailsKey stores value v in key k.
|
||||
func (st *Storage) SetEmailsKey(k string, v EmailAddress) {
|
||||
st.emailsLock.Lock()
|
||||
st.emails[k] = v
|
||||
st.storeEmails()
|
||||
st.emailsLock.Unlock()
|
||||
v.JellyfinID = k
|
||||
err := st.db.Upsert(k, v)
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to set email: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteEmailKey deletes value at key k.
|
||||
func (st *Storage) DeleteEmailsKey(k string) {
|
||||
st.emailsLock.Lock()
|
||||
delete(st.emails, k)
|
||||
st.storeEmails()
|
||||
st.emailsLock.Unlock()
|
||||
st.db.Delete(k, EmailAddress{})
|
||||
}
|
||||
|
||||
// GetDiscord returns a copy of the store.
|
||||
func (st *Storage) GetDiscord() discordStore {
|
||||
if st.discord == nil {
|
||||
st.discord = discordStore{}
|
||||
func (st *Storage) GetDiscord() []DiscordUser {
|
||||
result := []DiscordUser{}
|
||||
err := st.db.Find(&result, &badgerhold.Query{})
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find users: %v\n", err)
|
||||
}
|
||||
return st.discord
|
||||
return result
|
||||
}
|
||||
|
||||
// GetDiscordKey returns the value stored in the store's key.
|
||||
func (st *Storage) GetDiscordKey(k string) (DiscordUser, bool) {
|
||||
v, ok := st.discord[k]
|
||||
return v, ok
|
||||
result := DiscordUser{}
|
||||
err := st.db.Get(k, &result)
|
||||
ok := true
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find user: %v\n", err)
|
||||
ok = false
|
||||
}
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// SetDiscordKey stores value v in key k.
|
||||
func (st *Storage) SetDiscordKey(k string, v DiscordUser) {
|
||||
st.discordLock.Lock()
|
||||
if st.discord == nil {
|
||||
st.discord = discordStore{}
|
||||
v.JellyfinID = k
|
||||
err := st.db.Upsert(k, v)
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to set user: %v\n", err)
|
||||
}
|
||||
st.discord[k] = v
|
||||
st.storeDiscordUsers()
|
||||
st.discordLock.Unlock()
|
||||
}
|
||||
|
||||
// DeleteDiscordKey deletes value at key k.
|
||||
func (st *Storage) DeleteDiscordKey(k string) {
|
||||
st.discordLock.Lock()
|
||||
delete(st.discord, k)
|
||||
st.storeDiscordUsers()
|
||||
st.discordLock.Unlock()
|
||||
st.db.Delete(k, DiscordUser{})
|
||||
}
|
||||
|
||||
// GetTelegram returns a copy of the store.
|
||||
func (st *Storage) GetTelegram() telegramStore {
|
||||
if st.telegram == nil {
|
||||
st.telegram = telegramStore{}
|
||||
func (st *Storage) GetTelegram() []TelegramUser {
|
||||
result := []TelegramUser{}
|
||||
err := st.db.Find(&result, &badgerhold.Query{})
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find users: %v\n", err)
|
||||
}
|
||||
return st.telegram
|
||||
return result
|
||||
}
|
||||
|
||||
// GetTelegramKey returns the value stored in the store's key.
|
||||
func (st *Storage) GetTelegramKey(k string) (TelegramUser, bool) {
|
||||
v, ok := st.telegram[k]
|
||||
return v, ok
|
||||
result := TelegramUser{}
|
||||
err := st.db.Get(k, &result)
|
||||
ok := true
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find user: %v\n", err)
|
||||
ok = false
|
||||
}
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// SetTelegramKey stores value v in key k.
|
||||
func (st *Storage) SetTelegramKey(k string, v TelegramUser) {
|
||||
st.telegramLock.Lock()
|
||||
if st.telegram == nil {
|
||||
st.telegram = telegramStore{}
|
||||
v.JellyfinID = k
|
||||
err := st.db.Upsert(k, v)
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to set user: %v\n", err)
|
||||
}
|
||||
st.telegram[k] = v
|
||||
st.storeTelegramUsers()
|
||||
st.telegramLock.Unlock()
|
||||
}
|
||||
|
||||
// DeleteTelegramKey deletes value at key k.
|
||||
func (st *Storage) DeleteTelegramKey(k string) {
|
||||
st.telegramLock.Lock()
|
||||
delete(st.telegram, k)
|
||||
st.storeTelegramUsers()
|
||||
st.telegramLock.Unlock()
|
||||
st.db.Delete(k, TelegramUser{})
|
||||
}
|
||||
|
||||
// GetMatrix returns a copy of the store.
|
||||
func (st *Storage) GetMatrix() matrixStore {
|
||||
if st.matrix == nil {
|
||||
st.matrix = matrixStore{}
|
||||
func (st *Storage) GetMatrix() []MatrixUser {
|
||||
result := []MatrixUser{}
|
||||
err := st.db.Find(&result, &badgerhold.Query{})
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find users: %v\n", err)
|
||||
}
|
||||
return st.matrix
|
||||
return result
|
||||
}
|
||||
|
||||
// GetMatrixKey returns the value stored in the store's key.
|
||||
func (st *Storage) GetMatrixKey(k string) (MatrixUser, bool) {
|
||||
v, ok := st.matrix[k]
|
||||
return v, ok
|
||||
result := MatrixUser{}
|
||||
err := st.db.Get(k, &result)
|
||||
ok := true
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find user: %v\n", err)
|
||||
ok = false
|
||||
}
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// SetMatrixKey stores value v in key k.
|
||||
func (st *Storage) SetMatrixKey(k string, v MatrixUser) {
|
||||
st.matrixLock.Lock()
|
||||
if st.matrix == nil {
|
||||
st.matrix = matrixStore{}
|
||||
v.JellyfinID = k
|
||||
err := st.db.Upsert(k, v)
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to set user: %v\n", err)
|
||||
}
|
||||
st.matrix[k] = v
|
||||
st.storeMatrixUsers()
|
||||
st.matrixLock.Unlock()
|
||||
}
|
||||
|
||||
// DeleteMatrixKey deletes value at key k.
|
||||
func (st *Storage) DeleteMatrixKey(k string) {
|
||||
st.matrixLock.Lock()
|
||||
delete(st.matrix, k)
|
||||
st.storeMatrixUsers()
|
||||
st.matrixLock.Unlock()
|
||||
st.db.Delete(k, MatrixUser{})
|
||||
}
|
||||
|
||||
// GetInvites returns a copy of the store.
|
||||
func (st *Storage) GetInvites() Invites {
|
||||
if st.invites == nil {
|
||||
st.invites = Invites{}
|
||||
func (st *Storage) GetInvites() []Invite {
|
||||
result := []Invite{}
|
||||
err := st.db.Find(&result, &badgerhold.Query{})
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find invites: %v\n", err)
|
||||
}
|
||||
return st.invites
|
||||
return result
|
||||
}
|
||||
|
||||
// GetInvitesKey returns the value stored in the store's key.
|
||||
func (st *Storage) GetInvitesKey(k string) (Invite, bool) {
|
||||
v, ok := st.invites[k]
|
||||
return v, ok
|
||||
result := Invite{}
|
||||
err := st.db.Get(k, &result)
|
||||
ok := true
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find invite: %v\n", err)
|
||||
ok = false
|
||||
}
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// SetInvitesKey stores value v in key k.
|
||||
func (st *Storage) SetInvitesKey(k string, v Invite) {
|
||||
st.invitesLock.Lock()
|
||||
if st.invites == nil {
|
||||
st.invites = Invites{}
|
||||
v.Code = k
|
||||
err := st.db.Upsert(k, v)
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to set invite: %v\n", err)
|
||||
}
|
||||
st.invites[k] = v
|
||||
st.storeInvites()
|
||||
st.invitesLock.Unlock()
|
||||
}
|
||||
|
||||
// DeleteInvitesKey deletes value at key k.
|
||||
func (st *Storage) DeleteInvitesKey(k string) {
|
||||
st.invitesLock.Lock()
|
||||
delete(st.invites, k)
|
||||
st.storeInvites()
|
||||
st.invitesLock.Unlock()
|
||||
st.db.Delete(k, Invite{})
|
||||
}
|
||||
|
||||
// GetAnnouncements returns a copy of the store.
|
||||
func (st *Storage) GetAnnouncements() []announcementTemplate {
|
||||
result := []announcementTemplate{}
|
||||
err := st.db.Find(&result, &badgerhold.Query{})
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find announcements: %v\n", err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetAnnouncementsKey returns the value stored in the store's key.
|
||||
func (st *Storage) GetAnnouncementsKey(k string) (announcementTemplate, bool) {
|
||||
result := announcementTemplate{}
|
||||
err := st.db.Get(k, &result)
|
||||
ok := true
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find announcement: %v\n", err)
|
||||
ok = false
|
||||
}
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// SetAnnouncementsKey stores value v in key k.
|
||||
func (st *Storage) SetAnnouncementsKey(k string, v announcementTemplate) {
|
||||
err := st.db.Upsert(k, v)
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to set announcement: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteAnnouncementsKey deletes value at key k.
|
||||
func (st *Storage) DeleteAnnouncementsKey(k string) {
|
||||
st.db.Delete(k, announcementTemplate{})
|
||||
}
|
||||
|
||||
// GetUserExpiries returns a copy of the store.
|
||||
func (st *Storage) GetUserExpiries() []UserExpiry {
|
||||
result := []UserExpiry{}
|
||||
err := st.db.Find(&result, &badgerhold.Query{})
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find expiries: %v\n", err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetUserExpiryKey returns the value stored in the store's key.
|
||||
func (st *Storage) GetUserExpiryKey(k string) (UserExpiry, bool) {
|
||||
result := UserExpiry{}
|
||||
err := st.db.Get(k, &result)
|
||||
ok := true
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find expiry: %v\n", err)
|
||||
ok = false
|
||||
}
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// SetUserExpiryKey stores value v in key k.
|
||||
func (st *Storage) SetUserExpiryKey(k string, v UserExpiry) {
|
||||
v.JellyfinID = k
|
||||
err := st.db.Upsert(k, v)
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to set expiry: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteUserExpiryKey deletes value at key k.
|
||||
func (st *Storage) DeleteUserExpiryKey(k string) {
|
||||
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
|
||||
}
|
||||
|
||||
// GetCustomContent returns a copy of the store.
|
||||
func (st *Storage) GetCustomContent() []CustomContent {
|
||||
result := []CustomContent{}
|
||||
err := st.db.Find(&result, &badgerhold.Query{})
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find custom content: %v\n", err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetCustomContentKey returns the value stored in the store's key.
|
||||
func (st *Storage) GetCustomContentKey(k string) (CustomContent, bool) {
|
||||
result := CustomContent{}
|
||||
err := st.db.Get(k, &result)
|
||||
ok := true
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to find custom content: %v\n", err)
|
||||
ok = false
|
||||
}
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// MustGetCustomContentKey returns the value stored in the store's key, or an empty value.
|
||||
func (st *Storage) MustGetCustomContentKey(k string) CustomContent {
|
||||
result := CustomContent{}
|
||||
st.db.Get(k, &result)
|
||||
return result
|
||||
}
|
||||
|
||||
// SetCustomContentKey stores value v in key k.
|
||||
func (st *Storage) SetCustomContentKey(k string, v CustomContent) {
|
||||
v.Name = k
|
||||
err := st.db.Upsert(k, v)
|
||||
if err != nil {
|
||||
// fmt.Printf("Failed to set custom content: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteCustomContentKey deletes value at key k.
|
||||
func (st *Storage) DeleteCustomContentKey(k string) {
|
||||
st.db.Delete(k, CustomContent{})
|
||||
}
|
||||
|
||||
type TelegramUser struct {
|
||||
ChatID int64
|
||||
Username string
|
||||
Lang string
|
||||
Contact bool // Whether to contact through telegram or not
|
||||
JellyfinID string `badgerhold:"key"`
|
||||
ChatID int64 `badgerhold:"index"`
|
||||
Username string `badgerhold:"index"`
|
||||
Lang string
|
||||
Contact bool // Whether to contact through telegram or not
|
||||
}
|
||||
|
||||
type DiscordUser struct {
|
||||
ChannelID string
|
||||
ID string
|
||||
Username string
|
||||
ID string `badgerhold:"index"`
|
||||
Username string `badgerhold:"index"`
|
||||
Discriminator string
|
||||
Lang string
|
||||
Contact bool
|
||||
JellyfinID string `json:"-"` // Used internally in discord.go
|
||||
JellyfinID string `json:"-" badgerhold:"key"` // Used internally in discord.go
|
||||
}
|
||||
|
||||
type EmailAddress struct {
|
||||
Addr string
|
||||
Label string // User Label.
|
||||
Contact bool
|
||||
Admin bool // Whether or not user is jfa-go admin.
|
||||
Addr string `badgerhold:"index"`
|
||||
Label string // User Label.
|
||||
Contact bool
|
||||
Admin bool // Whether or not user is jfa-go admin.
|
||||
JellyfinID string `badgerhold:"key"`
|
||||
}
|
||||
|
||||
type customEmails struct {
|
||||
UserCreated customContent `json:"userCreated"`
|
||||
InviteExpiry customContent `json:"inviteExpiry"`
|
||||
PasswordReset customContent `json:"passwordReset"`
|
||||
UserDeleted customContent `json:"userDeleted"`
|
||||
UserDisabled customContent `json:"userDisabled"`
|
||||
UserEnabled customContent `json:"userEnabled"`
|
||||
InviteEmail customContent `json:"inviteEmail"`
|
||||
WelcomeEmail customContent `json:"welcomeEmail"`
|
||||
EmailConfirmation customContent `json:"emailConfirmation"`
|
||||
UserExpired customContent `json:"userExpired"`
|
||||
UserCreated CustomContent `json:"userCreated"`
|
||||
InviteExpiry CustomContent `json:"inviteExpiry"`
|
||||
PasswordReset CustomContent `json:"passwordReset"`
|
||||
UserDeleted CustomContent `json:"userDeleted"`
|
||||
UserDisabled CustomContent `json:"userDisabled"`
|
||||
UserEnabled CustomContent `json:"userEnabled"`
|
||||
InviteEmail CustomContent `json:"inviteEmail"`
|
||||
WelcomeEmail CustomContent `json:"welcomeEmail"`
|
||||
EmailConfirmation CustomContent `json:"emailConfirmation"`
|
||||
UserExpired CustomContent `json:"userExpired"`
|
||||
}
|
||||
|
||||
type customContent struct {
|
||||
// CustomContent stores customized versions of jfa-go content, including emails and user messages.
|
||||
type CustomContent struct {
|
||||
Name string `json:"name" badgerhold:"key"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Content string `json:"content"`
|
||||
Variables []string `json:"variables,omitempty"`
|
||||
@ -245,14 +460,15 @@ type customContent struct {
|
||||
}
|
||||
|
||||
type userPageContent struct {
|
||||
Login customContent `json:"login"`
|
||||
Page customContent `json:"page"`
|
||||
Login CustomContent `json:"login"`
|
||||
Page CustomContent `json:"page"`
|
||||
}
|
||||
|
||||
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
||||
|
||||
type Profile struct {
|
||||
Admin bool `json:"admin,omitempty"`
|
||||
Name string `badgerhold:"key"`
|
||||
Admin bool `json:"admin,omitempty" badgerhold:"index"`
|
||||
LibraryAccess string `json:"libraries,omitempty"`
|
||||
FromUser string `json:"fromUser,omitempty"`
|
||||
Policy mediabrowser.Policy `json:"policy,omitempty"`
|
||||
@ -263,6 +479,7 @@ type Profile struct {
|
||||
}
|
||||
|
||||
type Invite struct {
|
||||
Code string `badgerhold:"key"`
|
||||
Created time.Time `json:"created"`
|
||||
NoLimit bool `json:"no-limit"`
|
||||
RemainingUses int `json:"remaining-uses"`
|
||||
@ -964,18 +1181,16 @@ func (st *Storage) loadLangTelegram(filesystems ...fs.FS) error {
|
||||
type Invites map[string]Invite
|
||||
|
||||
func (st *Storage) loadInvites() error {
|
||||
return loadJSON(st.invite_path, &st.invites)
|
||||
return loadJSON(st.invite_path, &st.deprecatedInvites)
|
||||
}
|
||||
|
||||
func (st *Storage) storeInvites() error {
|
||||
return storeJSON(st.invite_path, st.invites)
|
||||
return storeJSON(st.invite_path, st.deprecatedInvites)
|
||||
}
|
||||
|
||||
func (st *Storage) loadUsers() error {
|
||||
st.usersLock.Lock()
|
||||
defer st.usersLock.Unlock()
|
||||
if st.users == nil {
|
||||
st.users = map[string]time.Time{}
|
||||
func (st *Storage) loadUserExpiries() error {
|
||||
if st.deprecatedUserExpiries == nil {
|
||||
st.deprecatedUserExpiries = map[string]time.Time{}
|
||||
}
|
||||
temp := map[string]time.Time{}
|
||||
err := loadJSON(st.users_path, &temp)
|
||||
@ -983,111 +1198,111 @@ func (st *Storage) loadUsers() error {
|
||||
return err
|
||||
}
|
||||
for id, t1 := range temp {
|
||||
if _, ok := st.users[id]; !ok {
|
||||
st.users[id] = t1
|
||||
if _, ok := st.deprecatedUserExpiries[id]; !ok {
|
||||
st.deprecatedUserExpiries[id] = t1
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *Storage) storeUsers() error {
|
||||
return storeJSON(st.users_path, st.users)
|
||||
func (st *Storage) storeUserExpiries() error {
|
||||
return storeJSON(st.users_path, st.deprecatedUserExpiries)
|
||||
}
|
||||
|
||||
func (st *Storage) loadEmails() error {
|
||||
return loadJSON(st.emails_path, &st.emails)
|
||||
return loadJSON(st.emails_path, &st.deprecatedEmails)
|
||||
}
|
||||
|
||||
func (st *Storage) storeEmails() error {
|
||||
return storeJSON(st.emails_path, st.emails)
|
||||
return storeJSON(st.emails_path, st.deprecatedEmails)
|
||||
}
|
||||
|
||||
func (st *Storage) loadTelegramUsers() error {
|
||||
return loadJSON(st.telegram_path, &st.telegram)
|
||||
return loadJSON(st.telegram_path, &st.deprecatedTelegram)
|
||||
}
|
||||
|
||||
func (st *Storage) storeTelegramUsers() error {
|
||||
return storeJSON(st.telegram_path, st.telegram)
|
||||
return storeJSON(st.telegram_path, st.deprecatedTelegram)
|
||||
}
|
||||
|
||||
func (st *Storage) loadDiscordUsers() error {
|
||||
return loadJSON(st.discord_path, &st.discord)
|
||||
return loadJSON(st.discord_path, &st.deprecatedDiscord)
|
||||
}
|
||||
|
||||
func (st *Storage) storeDiscordUsers() error {
|
||||
return storeJSON(st.discord_path, st.discord)
|
||||
return storeJSON(st.discord_path, st.deprecatedDiscord)
|
||||
}
|
||||
|
||||
func (st *Storage) loadMatrixUsers() error {
|
||||
return loadJSON(st.matrix_path, &st.matrix)
|
||||
return loadJSON(st.matrix_path, &st.deprecatedMatrix)
|
||||
}
|
||||
|
||||
func (st *Storage) storeMatrixUsers() error {
|
||||
return storeJSON(st.matrix_path, st.matrix)
|
||||
return storeJSON(st.matrix_path, st.deprecatedMatrix)
|
||||
}
|
||||
|
||||
func (st *Storage) loadCustomEmails() error {
|
||||
return loadJSON(st.customEmails_path, &st.customEmails)
|
||||
return loadJSON(st.customEmails_path, &st.deprecatedCustomEmails)
|
||||
}
|
||||
|
||||
func (st *Storage) storeCustomEmails() error {
|
||||
return storeJSON(st.customEmails_path, st.customEmails)
|
||||
return storeJSON(st.customEmails_path, st.deprecatedCustomEmails)
|
||||
}
|
||||
|
||||
func (st *Storage) loadUserPageContent() error {
|
||||
return loadJSON(st.userPage_path, &st.userPage)
|
||||
return loadJSON(st.userPage_path, &st.deprecatedUserPageContent)
|
||||
}
|
||||
|
||||
func (st *Storage) storeUserPageContent() error {
|
||||
return storeJSON(st.userPage_path, st.userPage)
|
||||
return storeJSON(st.userPage_path, st.deprecatedUserPageContent)
|
||||
}
|
||||
|
||||
func (st *Storage) loadPolicy() error {
|
||||
return loadJSON(st.policy_path, &st.policy)
|
||||
return loadJSON(st.policy_path, &st.deprecatedPolicy)
|
||||
}
|
||||
|
||||
func (st *Storage) storePolicy() error {
|
||||
return storeJSON(st.policy_path, st.policy)
|
||||
return storeJSON(st.policy_path, st.deprecatedPolicy)
|
||||
}
|
||||
|
||||
func (st *Storage) loadConfiguration() error {
|
||||
return loadJSON(st.configuration_path, &st.configuration)
|
||||
return loadJSON(st.configuration_path, &st.deprecatedConfiguration)
|
||||
}
|
||||
|
||||
func (st *Storage) storeConfiguration() error {
|
||||
return storeJSON(st.configuration_path, st.configuration)
|
||||
return storeJSON(st.configuration_path, st.deprecatedConfiguration)
|
||||
}
|
||||
|
||||
func (st *Storage) loadDisplayprefs() error {
|
||||
return loadJSON(st.displayprefs_path, &st.displayprefs)
|
||||
return loadJSON(st.displayprefs_path, &st.deprecatedDisplayprefs)
|
||||
}
|
||||
|
||||
func (st *Storage) storeDisplayprefs() error {
|
||||
return storeJSON(st.displayprefs_path, st.displayprefs)
|
||||
return storeJSON(st.displayprefs_path, st.deprecatedDisplayprefs)
|
||||
}
|
||||
|
||||
func (st *Storage) loadOmbiTemplate() error {
|
||||
return loadJSON(st.ombi_path, &st.ombi_template)
|
||||
return loadJSON(st.ombi_path, &st.deprecatedOmbiTemplate)
|
||||
}
|
||||
|
||||
func (st *Storage) storeOmbiTemplate() error {
|
||||
return storeJSON(st.ombi_path, st.ombi_template)
|
||||
return storeJSON(st.ombi_path, st.deprecatedOmbiTemplate)
|
||||
}
|
||||
|
||||
func (st *Storage) loadAnnouncements() error {
|
||||
return loadJSON(st.announcements_path, &st.announcements)
|
||||
return loadJSON(st.announcements_path, &st.deprecatedAnnouncements)
|
||||
}
|
||||
|
||||
func (st *Storage) storeAnnouncements() error {
|
||||
return storeJSON(st.announcements_path, st.announcements)
|
||||
return storeJSON(st.announcements_path, st.deprecatedAnnouncements)
|
||||
}
|
||||
|
||||
func (st *Storage) loadProfiles() error {
|
||||
err := loadJSON(st.profiles_path, &st.profiles)
|
||||
for name, profile := range st.profiles {
|
||||
if profile.Default {
|
||||
st.defaultProfile = name
|
||||
}
|
||||
err := loadJSON(st.profiles_path, &st.deprecatedProfiles)
|
||||
for name, profile := range st.deprecatedProfiles {
|
||||
// if profile.Default {
|
||||
// st.defaultProfile = name
|
||||
// }
|
||||
change := false
|
||||
if profile.Policy.IsAdministrator != profile.Admin {
|
||||
change = true
|
||||
@ -1107,19 +1322,19 @@ func (st *Storage) loadProfiles() error {
|
||||
change = true
|
||||
}
|
||||
if change {
|
||||
st.profiles[name] = profile
|
||||
}
|
||||
}
|
||||
if st.defaultProfile == "" {
|
||||
for n := range st.profiles {
|
||||
st.defaultProfile = n
|
||||
st.deprecatedProfiles[name] = profile
|
||||
}
|
||||
}
|
||||
// if st.defaultProfile == "" {
|
||||
// for n := range st.deprecatedProfiles {
|
||||
// st.defaultProfile = n
|
||||
// }
|
||||
// }
|
||||
return err
|
||||
}
|
||||
|
||||
func (st *Storage) storeProfiles() error {
|
||||
return storeJSON(st.profiles_path, st.profiles)
|
||||
return storeJSON(st.profiles_path, st.deprecatedProfiles)
|
||||
}
|
||||
|
||||
func (st *Storage) migrateToProfile() error {
|
||||
@ -1127,10 +1342,10 @@ func (st *Storage) migrateToProfile() error {
|
||||
st.loadConfiguration()
|
||||
st.loadDisplayprefs()
|
||||
st.loadProfiles()
|
||||
st.profiles["Default"] = Profile{
|
||||
Policy: st.policy,
|
||||
Configuration: st.configuration,
|
||||
Displayprefs: st.displayprefs,
|
||||
st.deprecatedProfiles["Default"] = Profile{
|
||||
Policy: st.deprecatedPolicy,
|
||||
Configuration: st.deprecatedConfiguration,
|
||||
Displayprefs: st.deprecatedDisplayprefs,
|
||||
}
|
||||
return st.storeProfiles()
|
||||
}
|
||||
|
20
telegram.go
20
telegram.go
@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
tg "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||
"github.com/timshannon/badgerhold/v4"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -216,13 +217,10 @@ func (t *TelegramDaemon) commandLang(upd *tg.Update, sects []string, lang string
|
||||
}
|
||||
if _, ok := t.app.storage.lang.Telegram[sects[1]]; ok {
|
||||
t.languages[upd.Message.Chat.ID] = sects[1]
|
||||
for jfID, user := range t.app.storage.GetTelegram() {
|
||||
for _, user := range t.app.storage.GetTelegram() {
|
||||
if user.ChatID == upd.Message.Chat.ID {
|
||||
user.Lang = sects[1]
|
||||
t.app.storage.SetTelegramKey(jfID, user)
|
||||
if err := t.app.storage.storeTelegramUsers(); err != nil {
|
||||
t.app.err.Printf("Failed to store Telegram users: %v", err)
|
||||
}
|
||||
t.app.storage.SetTelegramKey(user.JellyfinID, user)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -270,15 +268,9 @@ func (t *TelegramDaemon) AssignedTokenVerified(pin string, jfID string) (token T
|
||||
}
|
||||
|
||||
// UserExists returns whether or not a user with the given username exists.
|
||||
func (t *TelegramDaemon) UserExists(username string) (ok bool) {
|
||||
ok = false
|
||||
for _, u := range t.app.storage.GetTelegram() {
|
||||
if u.Username == username {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
func (t *TelegramDaemon) UserExists(username string) bool {
|
||||
c, err := t.app.storage.db.Count(&TelegramUser{}, badgerhold.Where("Username").Eq(username))
|
||||
return err != nil || c > 0
|
||||
}
|
||||
|
||||
// DeleteVerifiedToken removes the token with the given PIN.
|
||||
|
@ -50,13 +50,7 @@ func (rt *userDaemon) shutdown() {
|
||||
}
|
||||
|
||||
func (app *appContext) checkUsers() {
|
||||
if err := app.storage.loadUsers(); err != nil {
|
||||
app.err.Printf("Failed to load user expiries: %v", err)
|
||||
return
|
||||
}
|
||||
app.storage.usersLock.Lock()
|
||||
defer app.storage.usersLock.Unlock()
|
||||
if len(app.storage.users) == 0 {
|
||||
if len(app.storage.GetUserExpiries()) == 0 {
|
||||
return
|
||||
}
|
||||
app.info.Println("Daemon: Checking for user expiry")
|
||||
@ -80,11 +74,12 @@ func (app *appContext) checkUsers() {
|
||||
for _, user := range users {
|
||||
userExists[user.ID] = true
|
||||
}
|
||||
for id, expiry := range app.storage.users {
|
||||
for _, expiry := range app.storage.GetUserExpiries() {
|
||||
id := expiry.JellyfinID
|
||||
if _, ok := userExists[id]; !ok {
|
||||
app.info.Printf("Deleting expiry for non-existent user \"%s\"", id)
|
||||
delete(app.storage.users, id)
|
||||
} else if time.Now().After(expiry) {
|
||||
app.storage.DeleteUserExpiryKey(expiry.JellyfinID)
|
||||
} else if time.Now().After(expiry.Expiry) {
|
||||
found := false
|
||||
var user mediabrowser.User
|
||||
for _, u := range users {
|
||||
@ -96,7 +91,7 @@ func (app *appContext) checkUsers() {
|
||||
}
|
||||
if !found {
|
||||
app.info.Printf("Expired user already deleted, ignoring.")
|
||||
delete(app.storage.users, id)
|
||||
app.storage.DeleteUserExpiryKey(expiry.JellyfinID)
|
||||
continue
|
||||
}
|
||||
app.info.Printf("%s expired user \"%s\"", termPlural, user.Name)
|
||||
@ -112,7 +107,7 @@ func (app *appContext) checkUsers() {
|
||||
app.err.Printf("Failed to %s \"%s\" (%d): %s", mode, user.Name, status, err)
|
||||
continue
|
||||
}
|
||||
delete(app.storage.users, id)
|
||||
app.storage.DeleteUserExpiryKey(expiry.JellyfinID)
|
||||
app.jf.CacheExpiry = time.Now()
|
||||
if contact {
|
||||
if !ok {
|
||||
@ -130,8 +125,4 @@ func (app *appContext) checkUsers() {
|
||||
}
|
||||
}
|
||||
}
|
||||
err = app.storage.storeUsers()
|
||||
if err != nil {
|
||||
app.err.Printf("Failed to store user expiries: %s", err)
|
||||
}
|
||||
}
|
||||
|
10
views.go
10
views.go
@ -224,13 +224,13 @@ func (app *appContext) MyUserPage(gc *gin.Context) {
|
||||
data["discordInviteLink"] = app.discord.inviteChannelName != ""
|
||||
}
|
||||
|
||||
pageMessages := map[string]*customContent{
|
||||
"Login": app.getCustomMessage("UserLogin"),
|
||||
"Page": app.getCustomMessage("UserPage"),
|
||||
}
|
||||
pageMessagesExist := map[string]bool{}
|
||||
pageMessages := map[string]CustomContent{}
|
||||
pageMessages["Login"], pageMessagesExist["Login"] = app.storage.GetCustomContentKey("UserLogin")
|
||||
pageMessages["Page"], pageMessagesExist["Page"] = app.storage.GetCustomContentKey("UserPage")
|
||||
|
||||
for name, msg := range pageMessages {
|
||||
if msg == nil {
|
||||
if !pageMessagesExist[name] {
|
||||
continue
|
||||
}
|
||||
data[name+"MessageEnabled"] = msg.Enabled
|
||||
|
Loading…
Reference in New Issue
Block a user