mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-26 19:10:10 +00:00
Compare commits
6 Commits
e9e361ae60
...
e44d11c58c
Author | SHA1 | Date | |
---|---|---|---|
e44d11c58c | |||
48a2058e81 | |||
cd98e51ea9 | |||
fbbb03a47d | |||
6f5fc0948a | |||
05d473dc97 |
@ -26,18 +26,19 @@ func (app *appContext) GetCustomContent(gc *gin.Context) {
|
|||||||
adminLang = app.storage.lang.chosenAdminLang
|
adminLang = app.storage.lang.chosenAdminLang
|
||||||
}
|
}
|
||||||
list := emailListDTO{
|
list := emailListDTO{
|
||||||
"UserCreated": {Name: app.storage.lang.Email[lang].UserCreated["name"], Enabled: app.storage.MustGetCustomContentKey("UserCreated").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},
|
"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},
|
"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},
|
"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},
|
"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},
|
"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},
|
"UserExpiryAdjusted": {Name: app.storage.lang.Email[lang].UserExpiryAdjusted["name"], Enabled: app.storage.MustGetCustomContentKey("UserExpiryAdjusted").Enabled},
|
||||||
"WelcomeEmail": {Name: app.storage.lang.Email[lang].WelcomeEmail["name"], Enabled: app.storage.MustGetCustomContentKey("WelcomeEmail").Enabled},
|
"InviteEmail": {Name: app.storage.lang.Email[lang].InviteEmail["name"], Enabled: app.storage.MustGetCustomContentKey("InviteEmail").Enabled},
|
||||||
"EmailConfirmation": {Name: app.storage.lang.Email[lang].EmailConfirmation["name"], Enabled: app.storage.MustGetCustomContentKey("EmailConfirmation").Enabled},
|
"WelcomeEmail": {Name: app.storage.lang.Email[lang].WelcomeEmail["name"], Enabled: app.storage.MustGetCustomContentKey("WelcomeEmail").Enabled},
|
||||||
"UserExpired": {Name: app.storage.lang.Email[lang].UserExpired["name"], Enabled: app.storage.MustGetCustomContentKey("UserExpired").Enabled},
|
"EmailConfirmation": {Name: app.storage.lang.Email[lang].EmailConfirmation["name"], Enabled: app.storage.MustGetCustomContentKey("EmailConfirmation").Enabled},
|
||||||
"UserLogin": {Name: app.storage.lang.Admin[adminLang].Strings["userPageLogin"], Enabled: app.storage.MustGetCustomContentKey("UserLogin").Enabled},
|
"UserExpired": {Name: app.storage.lang.Email[lang].UserExpired["name"], Enabled: app.storage.MustGetCustomContentKey("UserExpired").Enabled},
|
||||||
"UserPage": {Name: app.storage.lang.Admin[adminLang].Strings["userPagePage"], Enabled: app.storage.MustGetCustomContentKey("UserPage").Enabled},
|
"UserLogin": {Name: app.storage.lang.Admin[adminLang].Strings["userPageLogin"], Enabled: app.storage.MustGetCustomContentKey("UserLogin").Enabled},
|
||||||
|
"UserPage": {Name: app.storage.lang.Admin[adminLang].Strings["userPagePage"], Enabled: app.storage.MustGetCustomContentKey("UserPage").Enabled},
|
||||||
}
|
}
|
||||||
|
|
||||||
filter := gc.Query("filter")
|
filter := gc.Query("filter")
|
||||||
@ -51,39 +52,6 @@ func (app *appContext) GetCustomContent(gc *gin.Context) {
|
|||||||
gc.JSON(200, list)
|
gc.JSON(200, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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{}
|
|
||||||
case "UserCreated":
|
|
||||||
return &app.storage.customEmails.UserCreated
|
|
||||||
case "InviteExpiry":
|
|
||||||
return &app.storage.customEmails.InviteExpiry
|
|
||||||
case "PasswordReset":
|
|
||||||
return &app.storage.customEmails.PasswordReset
|
|
||||||
case "UserDeleted":
|
|
||||||
return &app.storage.customEmails.UserDeleted
|
|
||||||
case "UserDisabled":
|
|
||||||
return &app.storage.customEmails.UserDisabled
|
|
||||||
case "UserEnabled":
|
|
||||||
return &app.storage.customEmails.UserEnabled
|
|
||||||
case "InviteEmail":
|
|
||||||
return &app.storage.customEmails.InviteEmail
|
|
||||||
case "WelcomeEmail":
|
|
||||||
return &app.storage.customEmails.WelcomeEmail
|
|
||||||
case "EmailConfirmation":
|
|
||||||
return &app.storage.customEmails.EmailConfirmation
|
|
||||||
case "UserExpired":
|
|
||||||
return &app.storage.customEmails.UserExpired
|
|
||||||
case "UserLogin":
|
|
||||||
return &app.storage.userPage.Login
|
|
||||||
case "UserPage":
|
|
||||||
return &app.storage.userPage.Page
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
} */
|
|
||||||
|
|
||||||
// @Summary Sets the corresponding custom content.
|
// @Summary Sets the corresponding custom content.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param CustomContent body CustomContent true "Content = email (in markdown)."
|
// @Param CustomContent body CustomContent true "Content = email (in markdown)."
|
||||||
@ -217,6 +185,11 @@ func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
|||||||
msg, err = app.email.constructEnabled("", app, true)
|
msg, err = app.email.constructEnabled("", app, true)
|
||||||
}
|
}
|
||||||
values = app.email.deletedValues(app.storage.lang.Email[lang].Strings.get("reason"), app, false)
|
values = app.email.deletedValues(app.storage.lang.Email[lang].Strings.get("reason"), app, false)
|
||||||
|
case "UserExpiryAdjusted":
|
||||||
|
if noContent {
|
||||||
|
msg, err = app.email.constructExpiryAdjusted("", time.Time{}, "", app, true)
|
||||||
|
}
|
||||||
|
values = app.email.expiryAdjustedValues(username, time.Now(), app.storage.lang.Email[lang].Strings.get("reason"), app, false, true)
|
||||||
case "InviteEmail":
|
case "InviteEmail":
|
||||||
if noContent {
|
if noContent {
|
||||||
msg, err = app.email.constructInvite("", Invite{}, app, true)
|
msg, err = app.email.constructInvite("", Invite{}, app, true)
|
||||||
|
30
api-users.go
30
api-users.go
@ -293,24 +293,24 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool, gc *gin.Context)
|
|||||||
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) {
|
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) {
|
||||||
for address, settings := range invite.Notify {
|
for address, settings := range invite.Notify {
|
||||||
if settings["notify-creation"] {
|
if settings["notify-creation"] {
|
||||||
go func() {
|
go func(addr string) {
|
||||||
msg, err := app.email.constructCreated(req.Code, req.Username, req.Email, invite, app, false)
|
msg, err := app.email.constructCreated(req.Code, req.Username, req.Email, invite, app, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Printf("%s: Failed to construct user creation notification: %v", req.Code, err)
|
app.err.Printf("%s: Failed to construct user creation notification: %v", req.Code, err)
|
||||||
} else {
|
} else {
|
||||||
// Check whether notify "address" is an email address of Jellyfin ID
|
// Check whether notify "addr" is an email address of Jellyfin ID
|
||||||
if strings.Contains(address, "@") {
|
if strings.Contains(addr, "@") {
|
||||||
err = app.email.send(msg, address)
|
err = app.email.send(msg, addr)
|
||||||
} else {
|
} else {
|
||||||
err = app.sendByID(msg, address)
|
err = app.sendByID(msg, addr)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Printf("%s: Failed to send user creation notification: %v", req.Code, err)
|
app.err.Printf("%s: Failed to send user creation notification: %v", req.Code, err)
|
||||||
} else {
|
} else {
|
||||||
app.info.Printf("Sent user creation notification to %s", address)
|
app.info.Printf("Sent user creation notification to %s", addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}(address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -739,6 +739,22 @@ func (app *appContext) ExtendExpiry(gc *gin.Context) {
|
|||||||
expiry.Expiry = base.AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute)
|
expiry.Expiry = base.AddDate(0, req.Months, req.Days).Add(time.Duration(((60 * req.Hours) + req.Minutes)) * time.Minute)
|
||||||
}
|
}
|
||||||
app.storage.SetUserExpiryKey(id, expiry)
|
app.storage.SetUserExpiryKey(id, expiry)
|
||||||
|
if messagesEnabled && req.Notify {
|
||||||
|
go func(uid string, exp time.Time) {
|
||||||
|
user, status, err := app.jf.UserByID(uid, false)
|
||||||
|
if status != 200 || err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg, err := app.email.constructExpiryAdjusted(user.Name, exp, req.Reason, app, false)
|
||||||
|
if err != nil {
|
||||||
|
app.err.Printf("%s: Failed to construct expiry adjustment notification: %v", uid, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := app.sendByID(msg, uid); err != nil {
|
||||||
|
app.err.Printf("%s: Failed to send expiry adjustment notification: %v", uid, err)
|
||||||
|
}
|
||||||
|
}(id, expiry.Expiry)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
respondBool(204, true, gc)
|
respondBool(204, true, gc)
|
||||||
}
|
}
|
||||||
|
@ -105,6 +105,9 @@ func (app *appContext) loadConfig() error {
|
|||||||
app.MustSetValue("user_expiry", "email_html", "jfa-go:"+"user-expired.html")
|
app.MustSetValue("user_expiry", "email_html", "jfa-go:"+"user-expired.html")
|
||||||
app.MustSetValue("user_expiry", "email_text", "jfa-go:"+"user-expired.txt")
|
app.MustSetValue("user_expiry", "email_text", "jfa-go:"+"user-expired.txt")
|
||||||
|
|
||||||
|
app.MustSetValue("user_expiry", "adjustment_email_html", "jfa-go:"+"expiry-adjusted.html")
|
||||||
|
app.MustSetValue("user_expiry", "adjustment_email_text", "jfa-go:"+"expiry-adjusted.txt")
|
||||||
|
|
||||||
app.MustSetValue("matrix", "topic", "Jellyfin notifications")
|
app.MustSetValue("matrix", "topic", "Jellyfin notifications")
|
||||||
app.MustSetValue("matrix", "show_on_reg", "true")
|
app.MustSetValue("matrix", "show_on_reg", "true")
|
||||||
|
|
||||||
|
@ -1713,7 +1713,7 @@
|
|||||||
"order": [],
|
"order": [],
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "User Expiry",
|
"name": "User Expiry",
|
||||||
"description": "When set on an invite, users will be deleted or disabled a specified amount of time after they create their account."
|
"description": "When set on an invite, users will be deleted or disabled a specified amount of time after they create their account. Expiries can also be set and extended for invididual users, optionally with a message why."
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"behaviour": {
|
"behaviour": {
|
||||||
@ -1765,6 +1765,35 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"value": "",
|
"value": "",
|
||||||
"description": "Path to custom email in plain text"
|
"description": "Path to custom email in plain text"
|
||||||
|
},
|
||||||
|
"adjustment_subject": {
|
||||||
|
"name": "Adjustment: email subject",
|
||||||
|
"required": false,
|
||||||
|
"requires_restart": false,
|
||||||
|
"depends_true": "messages|enabled",
|
||||||
|
"type": "text",
|
||||||
|
"value": "",
|
||||||
|
"description": "Subject of adjustment emails, sent optionally when setting/extending an expiry."
|
||||||
|
},
|
||||||
|
"adjustment_email_html": {
|
||||||
|
"name": "Adjustment: Custom email (HTML)",
|
||||||
|
"required": false,
|
||||||
|
"requires_restart": false,
|
||||||
|
"advanced": true,
|
||||||
|
"depends_true": "messages|enabled",
|
||||||
|
"type": "text",
|
||||||
|
"value": "",
|
||||||
|
"description": "Path to custom email html"
|
||||||
|
},
|
||||||
|
"adjustment_email_text": {
|
||||||
|
"name": "Adjustment: Custom email (plaintext)",
|
||||||
|
"required": false,
|
||||||
|
"requires_restart": false,
|
||||||
|
"advanced": true,
|
||||||
|
"depends_true": "messages|enabled",
|
||||||
|
"type": "text",
|
||||||
|
"value": "",
|
||||||
|
"description": "Path to custom email in plain text"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
66
email.go
66
email.go
@ -741,6 +741,72 @@ func (emailer *Emailer) constructEnabled(reason string, app *appContext, noSub b
|
|||||||
return email, nil
|
return email, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (emailer *Emailer) expiryAdjustedValues(username string, expiry time.Time, reason string, app *appContext, noSub bool, custom bool) map[string]interface{} {
|
||||||
|
template := map[string]interface{}{
|
||||||
|
"yourExpiryWasAdjusted": emailer.lang.UserExpiryAdjusted.get("yourExpiryWasAdjusted"),
|
||||||
|
"ifPreviouslyDisabled": emailer.lang.UserExpiryAdjusted.get("ifPreviouslyDisabled"),
|
||||||
|
"reasonString": emailer.lang.Strings.get("reason"),
|
||||||
|
"newExpiry": "",
|
||||||
|
"message": "",
|
||||||
|
}
|
||||||
|
if noSub {
|
||||||
|
template["helloUser"] = emailer.lang.Strings.get("helloUser")
|
||||||
|
empty := []string{"reason", "newExpiry"}
|
||||||
|
for _, v := range empty {
|
||||||
|
template[v] = "{" + v + "}"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
template["reason"] = reason
|
||||||
|
template["message"] = app.config.Section("messages").Key("message").String()
|
||||||
|
template["helloUser"] = emailer.lang.Strings.template("helloUser", tmpl{"username": username})
|
||||||
|
exp := app.formatDatetime(expiry)
|
||||||
|
if !expiry.IsZero() {
|
||||||
|
if custom {
|
||||||
|
template["newExpiry"] = exp
|
||||||
|
} else if !expiry.IsZero() {
|
||||||
|
template["newExpiry"] = emailer.lang.UserExpiryAdjusted.template("newExpiry", tmpl{
|
||||||
|
"date": exp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return template
|
||||||
|
}
|
||||||
|
|
||||||
|
func (emailer *Emailer) constructExpiryAdjusted(username string, expiry time.Time, reason string, app *appContext, noSub bool) (*Message, error) {
|
||||||
|
email := &Message{
|
||||||
|
Subject: app.config.Section("user_expiry").Key("adjustment_subject").MustString(emailer.lang.UserExpiryAdjusted.get("title")),
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
var template map[string]interface{}
|
||||||
|
message := app.storage.MustGetCustomContentKey("UserExpiryAdjusted")
|
||||||
|
if message.Enabled {
|
||||||
|
template = emailer.expiryAdjustedValues(username, expiry, reason, app, noSub, true)
|
||||||
|
} else {
|
||||||
|
template = emailer.expiryAdjustedValues(username, expiry, reason, app, noSub, false)
|
||||||
|
}
|
||||||
|
if noSub {
|
||||||
|
template["newExpiry"] = emailer.lang.UserExpiryAdjusted.template("newExpiry", tmpl{
|
||||||
|
"date": "{newExpiry}",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if message.Enabled {
|
||||||
|
content := templateEmail(
|
||||||
|
message.Content,
|
||||||
|
message.Variables,
|
||||||
|
nil,
|
||||||
|
template,
|
||||||
|
)
|
||||||
|
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||||
|
} else {
|
||||||
|
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "user_expiry", "adjustment_email_", template)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return email, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) welcomeValues(username string, expiry time.Time, app *appContext, noSub bool, custom bool) map[string]interface{} {
|
func (emailer *Emailer) welcomeValues(username string, expiry time.Time, app *appContext, noSub bool, custom bool) map[string]interface{} {
|
||||||
template := map[string]interface{}{
|
template := map[string]interface{}{
|
||||||
"welcome": emailer.lang.WelcomeEmail.get("welcome"),
|
"welcome": emailer.lang.WelcomeEmail.get("welcome"),
|
||||||
|
6
go.mod
6
go.mod
@ -18,6 +18,7 @@ replace github.com/hrfee/jfa-go/easyproxy => ./easyproxy
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bwmarrin/discordgo v0.27.1
|
github.com/bwmarrin/discordgo v0.27.1
|
||||||
|
github.com/dgraph-io/badger/v3 v3.2103.5
|
||||||
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
||||||
github.com/fatih/color v1.15.0
|
github.com/fatih/color v1.15.0
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
@ -30,10 +31,11 @@ require (
|
|||||||
github.com/gomarkdown/markdown v0.0.0-20230322041520-c84983bdbf2a
|
github.com/gomarkdown/markdown v0.0.0-20230322041520-c84983bdbf2a
|
||||||
github.com/hrfee/jfa-go/common v0.0.0-20230626224816-f72960635dc3
|
github.com/hrfee/jfa-go/common v0.0.0-20230626224816-f72960635dc3
|
||||||
github.com/hrfee/jfa-go/docs v0.0.0-20230626224816-f72960635dc3
|
github.com/hrfee/jfa-go/docs v0.0.0-20230626224816-f72960635dc3
|
||||||
|
github.com/hrfee/jfa-go/easyproxy v0.0.0-00010101000000-000000000000
|
||||||
github.com/hrfee/jfa-go/linecache v0.0.0-20230626224816-f72960635dc3
|
github.com/hrfee/jfa-go/linecache v0.0.0-20230626224816-f72960635dc3
|
||||||
github.com/hrfee/jfa-go/logger v0.0.0-20230626224816-f72960635dc3
|
github.com/hrfee/jfa-go/logger v0.0.0-20230626224816-f72960635dc3
|
||||||
github.com/hrfee/jfa-go/ombi v0.0.0-20230626224816-f72960635dc3
|
github.com/hrfee/jfa-go/ombi v0.0.0-20230626224816-f72960635dc3
|
||||||
github.com/hrfee/mediabrowser v0.3.12
|
github.com/hrfee/mediabrowser v0.3.13
|
||||||
github.com/itchyny/timefmt-go v0.1.5
|
github.com/itchyny/timefmt-go v0.1.5
|
||||||
github.com/lithammer/shortuuid/v3 v3.0.7
|
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||||
github.com/mailgun/mailgun-go/v4 v4.9.1
|
github.com/mailgun/mailgun-go/v4 v4.9.1
|
||||||
@ -54,7 +56,6 @@ require (
|
|||||||
github.com/cespare/xxhash v1.1.0 // indirect
|
github.com/cespare/xxhash v1.1.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/dgraph-io/badger/v3 v3.2103.5 // indirect
|
|
||||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
@ -87,7 +88,6 @@ require (
|
|||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
github.com/hrfee/jfa-go/easyproxy v0.0.0-00010101000000-000000000000 // indirect
|
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.16.6 // indirect
|
github.com/klauspost/compress v1.16.6 // indirect
|
||||||
|
13
go.sum
13
go.sum
@ -224,8 +224,8 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
|||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
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/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/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hrfee/mediabrowser v0.3.12 h1:fqDxt1be3e+ZNjAtlKc8MTqg7peo6fuGCrk2wOXo20k=
|
github.com/hrfee/mediabrowser v0.3.13 h1:NgQNbq+JWwsP68BdWXL/rwbpfE/oO5LJ5KVkE+aNbX8=
|
||||||
github.com/hrfee/mediabrowser v0.3.12/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U=
|
github.com/hrfee/mediabrowser v0.3.13/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
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 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
|
||||||
github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
|
github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
|
||||||
@ -394,8 +394,6 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
|
|||||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
|
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
|
||||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE=
|
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/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA=
|
github.com/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA=
|
||||||
github.com/xhit/go-simple-mail/v2 v2.16.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98=
|
github.com/xhit/go-simple-mail/v2 v2.16.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
@ -437,8 +435,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
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-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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
|
||||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
|
||||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@ -478,8 +474,6 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
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.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.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
|
||||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
|
||||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@ -523,8 +517,6 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
|
||||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
@ -536,7 +528,6 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
|
||||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
@ -554,7 +554,7 @@
|
|||||||
<div class="card @low dark:~d_neutral">
|
<div class="card @low dark:~d_neutral">
|
||||||
<span class="heading">{{ .strings.create }}</span>
|
<span class="heading">{{ .strings.create }}</span>
|
||||||
<div class="flex flex-col md:flex-row gap-3 mt-2" id="create-inv">
|
<div class="flex flex-col md:flex-row gap-3 mt-2" id="create-inv">
|
||||||
<div class="card ~neutral @low flex flex-col gap-2 w-1/2">
|
<div class="card ~neutral @low flex flex-col gap-2 grow">
|
||||||
<div class="flex flex-row gap-2">
|
<div class="flex flex-row gap-2">
|
||||||
<label class="w-1/2">
|
<label class="w-1/2">
|
||||||
<input type="radio" name="duration" class="unfocused" id="radio-inv-duration" checked>
|
<input type="radio" name="duration" class="unfocused" id="radio-inv-duration" checked>
|
||||||
@ -662,7 +662,7 @@
|
|||||||
<input type="text" id="create-user-label" class="input ~neutral @low">
|
<input type="text" id="create-user-label" class="input ~neutral @low">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low flex flex-col justify-between gap-2 w-1/2">
|
<div class="card ~neutral @low flex flex-col justify-between gap-2 grow">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<label class="label supra" for="create-uses">{{ .strings.inviteNumberOfUses }}</label>
|
<label class="label supra" for="create-uses">{{ .strings.inviteNumberOfUses }}</label>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="Description" content="jfa-go, a better way to manage Jellyfin users.">
|
<meta name="Description" content="jfa-go, a better way to manage Jellyfin users.">
|
||||||
<meta name="color-scheme" content="dark light">
|
<meta name="color-scheme" content="dark light">
|
||||||
|
<meta name="robots" content="noindex">
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ .urlBase }}/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="{{ .urlBase }}/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="{{ .urlBase }}/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="{{ .urlBase }}/favicon-32x32.png">
|
||||||
|
25
lang.go
25
lang.go
@ -93,18 +93,19 @@ func (ls *emailLangs) getOptions() [][2]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type emailLang struct {
|
type emailLang struct {
|
||||||
Meta langMeta `json:"meta"`
|
Meta langMeta `json:"meta"`
|
||||||
Strings langSection `json:"strings"`
|
Strings langSection `json:"strings"`
|
||||||
UserCreated langSection `json:"userCreated"`
|
UserCreated langSection `json:"userCreated"`
|
||||||
InviteExpiry langSection `json:"inviteExpiry"`
|
InviteExpiry langSection `json:"inviteExpiry"`
|
||||||
PasswordReset langSection `json:"passwordReset"`
|
PasswordReset langSection `json:"passwordReset"`
|
||||||
UserDeleted langSection `json:"userDeleted"`
|
UserDeleted langSection `json:"userDeleted"`
|
||||||
UserDisabled langSection `json:"userDisabled"`
|
UserDisabled langSection `json:"userDisabled"`
|
||||||
UserEnabled langSection `json:"userEnabled"`
|
UserEnabled langSection `json:"userEnabled"`
|
||||||
InviteEmail langSection `json:"inviteEmail"`
|
UserExpiryAdjusted langSection `json:"userExpiryAdjusted"`
|
||||||
WelcomeEmail langSection `json:"welcomeEmail"`
|
InviteEmail langSection `json:"inviteEmail"`
|
||||||
EmailConfirmation langSection `json:"emailConfirmation"`
|
WelcomeEmail langSection `json:"welcomeEmail"`
|
||||||
UserExpired langSection `json:"userExpired"`
|
EmailConfirmation langSection `json:"emailConfirmation"`
|
||||||
|
UserExpired langSection `json:"userExpired"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type setupLangs map[string]setupLang
|
type setupLangs map[string]setupLang
|
||||||
|
@ -45,6 +45,13 @@
|
|||||||
"title": "Your account has been re-enabled - Jellyfin",
|
"title": "Your account has been re-enabled - Jellyfin",
|
||||||
"yourAccountWasEnabled": "Your account was re-enabled."
|
"yourAccountWasEnabled": "Your account was re-enabled."
|
||||||
},
|
},
|
||||||
|
"userExpiryAdjusted": {
|
||||||
|
"name": "Expiry adjusted",
|
||||||
|
"title": "Account expiry adjusted - Jellyfin",
|
||||||
|
"yourExpiryWasAdjusted": "Your account's expiry date has been adjusted.",
|
||||||
|
"ifPreviouslyDisabled": "If your account was previously disabled, it may have been re-enabled.",
|
||||||
|
"newExpiry": "Your account will now expire on {date}."
|
||||||
|
},
|
||||||
"inviteEmail": {
|
"inviteEmail": {
|
||||||
"name": "Invite email",
|
"name": "Invite email",
|
||||||
"title": "Invite - Jellyfin",
|
"title": "Invite - Jellyfin",
|
||||||
@ -74,4 +81,4 @@
|
|||||||
"yourAccountHasExpired": "Your account has expired.",
|
"yourAccountHasExpired": "Your account has expired.",
|
||||||
"contactTheAdmin": "Contact the administrator for more info."
|
"contactTheAdmin": "Contact the administrator for more info."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
83
mail/expiry-adjusted.mjml
Normal file
83
mail/expiry-adjusted.mjml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<mjml>
|
||||||
|
<mj-head>
|
||||||
|
<mj-raw>
|
||||||
|
<meta name="color-scheme" content="light dark">
|
||||||
|
<meta name="supported-color-schemes" content="light dark">
|
||||||
|
</mj-raw>
|
||||||
|
<mj-style>
|
||||||
|
:root {
|
||||||
|
Color-scheme: light dark;
|
||||||
|
supported-color-schemes: light dark;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
Color-scheme: dark;
|
||||||
|
.body {
|
||||||
|
background: #242424 !important;
|
||||||
|
background-color: #242424 !important;
|
||||||
|
}
|
||||||
|
[data-ogsc] .body {
|
||||||
|
background: #242424 !important;
|
||||||
|
background-color: #242424 !important;
|
||||||
|
}
|
||||||
|
[data-ogsb] .body {
|
||||||
|
background: #242424 !important;
|
||||||
|
background-color: #242424 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
Color-scheme: dark;
|
||||||
|
.body {
|
||||||
|
background: #242424 !important;
|
||||||
|
background-color: #242424 !important;
|
||||||
|
}
|
||||||
|
[data-ogsc] .body {
|
||||||
|
background: #242424 !important;
|
||||||
|
background-color: #242424 !important;
|
||||||
|
}
|
||||||
|
[data-ogsb] .body {
|
||||||
|
background: #242424 !important;
|
||||||
|
background-color: #242424 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</mj-style>
|
||||||
|
<mj-attributes>
|
||||||
|
<mj-class name="bg" background-color="#101010" />
|
||||||
|
<mj-class name="bg2" background-color="#242424" />
|
||||||
|
<mj-class name="text" color="#cacaca" />
|
||||||
|
<mj-class name="bold" color="rgba(255,255,255,0.87)" />
|
||||||
|
<mj-class name="secondary" color="rgb(153,153,153)" />
|
||||||
|
<mj-class name="blue" background-color="rgb(0,164,220)" />
|
||||||
|
</mj-attributes>
|
||||||
|
<mj-font name="Quicksand" href="https://fonts.googleapis.com/css2?family=Quicksand" />
|
||||||
|
<mj-font name="Noto Sans" href="https://fonts.googleapis.com/css2?family=Noto+Sans" />
|
||||||
|
</mj-head>
|
||||||
|
<mj-body>
|
||||||
|
<mj-section mj-class="bg2">
|
||||||
|
<mj-column>
|
||||||
|
<mj-text mj-class="bold" font-size="25px" font-family="Quicksand, Noto Sans, Helvetica, Arial, sans-serif"> {{ .jellyfin }} </mj-text>
|
||||||
|
</mj-column>
|
||||||
|
</mj-section>
|
||||||
|
<mj-section mj-class="bg">
|
||||||
|
<mj-column>
|
||||||
|
<mj-text mj-class="text" font-size="16px" font-family="Noto Sans, Helvetica, Arial, sans-serif">
|
||||||
|
<p>{{ .helloUser }}</p>
|
||||||
|
|
||||||
|
<h3>{{ .yourExpiryWasAdjusted }}</h3>
|
||||||
|
|
||||||
|
<p>{{ .ifPreviouslyDisabled }}</p>
|
||||||
|
|
||||||
|
<h4>{{ .newExpiry }}</h4>
|
||||||
|
|
||||||
|
<p>{{ .reasonString }}: <i>{{ .reason }}</i></p>
|
||||||
|
</mj-text>
|
||||||
|
</mj-column>
|
||||||
|
</mj-section>
|
||||||
|
<mj-section mj-class="bg2">
|
||||||
|
<mj-column>
|
||||||
|
<mj-text mj-class="secondary" font-style="italic" font-size="14px">
|
||||||
|
{{ .message }}
|
||||||
|
</mj-text>
|
||||||
|
</mj-column>
|
||||||
|
</mj-section>
|
||||||
|
</body>
|
||||||
|
</mjml>
|
11
mail/expiry-adjusted.txt
Normal file
11
mail/expiry-adjusted.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{{ .helloUser }}
|
||||||
|
|
||||||
|
{{ .yourExpiryWasAdjusted }}
|
||||||
|
|
||||||
|
{{ .ifPreviouslyDisabled }}
|
||||||
|
|
||||||
|
{{ .newExpiry }}
|
||||||
|
|
||||||
|
{{ .reasonString }}: {{ .reason }}
|
||||||
|
|
||||||
|
{{ .message }}
|
@ -17,6 +17,7 @@ func runMigrations(app *appContext) {
|
|||||||
linkExistingOmbiDiscordTelegram(app)
|
linkExistingOmbiDiscordTelegram(app)
|
||||||
// migrateHyphens(app)
|
// migrateHyphens(app)
|
||||||
migrateToBadger(app)
|
migrateToBadger(app)
|
||||||
|
intialiseCustomContent(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate pre-0.2.0 user templates to profiles
|
// Migrate pre-0.2.0 user templates to profiles
|
||||||
@ -329,6 +330,8 @@ func migrateToBadger(app *appContext) {
|
|||||||
app.storage.SetCustomContentKey("UserPage", app.storage.deprecatedUserPageContent.Page)
|
app.storage.SetCustomContentKey("UserPage", app.storage.deprecatedUserPageContent.Page)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom content not present here was added post-badger.
|
||||||
|
|
||||||
err := app.storage.db.Upsert("migrated_to_db", MigrationStatus{true})
|
err := app.storage.db.Upsert("migrated_to_db", MigrationStatus{true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Fatalf("Failed to migrate to DB: %v\n", err)
|
app.err.Fatalf("Failed to migrate to DB: %v\n", err)
|
||||||
@ -336,6 +339,53 @@ func migrateToBadger(app *appContext) {
|
|||||||
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.")
|
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.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simply creates an emply CC template if not imn the DB already.
|
||||||
|
// Add new CC types here!
|
||||||
|
func intialiseCustomContent(app *appContext) {
|
||||||
|
emptyCC := CustomContent{
|
||||||
|
Enabled: false,
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("UserCreated"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("UserCreated", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("InviteExpiry"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("InviteExpiry", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("PasswordReset"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("PasswordReset", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("UserDeleted"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("UserDeleted", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("UserDisabled"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("UserDisabled", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("UserEnabled"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("UserEnabled", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("InviteEmail"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("InviteEmail", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("WelcomeEmail"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("WelcomeEmail", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("EmailConfirmation"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("EmailConfirmation", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("UserExpired"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("UserExpired", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("UserLogin"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("UserLogin", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("UserPage"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("UserPage", emptyCC)
|
||||||
|
}
|
||||||
|
if _, ok := app.storage.GetCustomContentKey("UserExpiryAdjusted"); !ok {
|
||||||
|
app.storage.SetCustomContentKey("UserExpiryAdjusted", emptyCC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Migrate between hyphenated & non-hyphenated user IDs. Doesn't seem to happen anymore, so disabled.
|
// Migrate between hyphenated & non-hyphenated user IDs. Doesn't seem to happen anymore, so disabled.
|
||||||
// func migrateHyphens(app *appContext) {
|
// func migrateHyphens(app *appContext) {
|
||||||
// checkVersion := func(version string) int {
|
// checkVersion := func(version string) int {
|
||||||
|
14
models.go
14
models.go
@ -261,12 +261,14 @@ type customEmailDTO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type extendExpiryDTO struct {
|
type extendExpiryDTO struct {
|
||||||
Users []string `json:"users"` // List of user IDs to apply to.
|
Users []string `json:"users"` // List of user IDs to apply to.
|
||||||
Months int `json:"months" example:"1"` // Number of months to add.
|
Months int `json:"months" example:"1"` // Number of months to add.
|
||||||
Days int `json:"days" example:"1"` // Number of days to add.
|
Days int `json:"days" example:"1"` // Number of days to add.
|
||||||
Hours int `json:"hours" example:"2"` // Number of hours to add.
|
Hours int `json:"hours" example:"2"` // Number of hours to add.
|
||||||
Minutes int `json:"minutes" example:"3"` // Number of minutes to add.
|
Minutes int `json:"minutes" example:"3"` // Number of minutes to add.
|
||||||
Timestamp int64 `json:"timestamp"` // Optional, exact time to expire at. Overrides other fields.
|
Timestamp int64 `json:"timestamp"` // Optional, exact time to expire at. Overrides other fields.
|
||||||
|
Notify bool `json:"notify"` // Whether to message the user(s) about the change.
|
||||||
|
Reason string `json:"reason" example:"i felt like it"` // Reason for adjustment.
|
||||||
}
|
}
|
||||||
|
|
||||||
type checkUpdateDTO struct {
|
type checkUpdateDTO struct {
|
||||||
|
1
package-lock.json
generated
1
package-lock.json
generated
@ -15,7 +15,6 @@
|
|||||||
"any-date-parser": "^1.5.4",
|
"any-date-parser": "^1.5.4",
|
||||||
"browserslist": "^4.21.7",
|
"browserslist": "^4.21.7",
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"cheerio": "^1.0.0-rc.12",
|
||||||
"esbuild": "^0.18.20",
|
|
||||||
"fs-cheerio": "^3.0.0",
|
"fs-cheerio": "^3.0.0",
|
||||||
"inline-source": "^8.0.2",
|
"inline-source": "^8.0.2",
|
||||||
"jsdom": "^22.1.0",
|
"jsdom": "^22.1.0",
|
||||||
|
23
storage.go
23
storage.go
@ -610,16 +610,17 @@ type EmailAddress struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type customEmails struct {
|
type customEmails struct {
|
||||||
UserCreated CustomContent `json:"userCreated"`
|
UserCreated CustomContent `json:"userCreated"`
|
||||||
InviteExpiry CustomContent `json:"inviteExpiry"`
|
InviteExpiry CustomContent `json:"inviteExpiry"`
|
||||||
PasswordReset CustomContent `json:"passwordReset"`
|
PasswordReset CustomContent `json:"passwordReset"`
|
||||||
UserDeleted CustomContent `json:"userDeleted"`
|
UserDeleted CustomContent `json:"userDeleted"`
|
||||||
UserDisabled CustomContent `json:"userDisabled"`
|
UserDisabled CustomContent `json:"userDisabled"`
|
||||||
UserEnabled CustomContent `json:"userEnabled"`
|
UserEnabled CustomContent `json:"userEnabled"`
|
||||||
InviteEmail CustomContent `json:"inviteEmail"`
|
UserExpiryAdjusted CustomContent `json:"userExpiryAdjusted"`
|
||||||
WelcomeEmail CustomContent `json:"welcomeEmail"`
|
InviteEmail CustomContent `json:"inviteEmail"`
|
||||||
EmailConfirmation CustomContent `json:"emailConfirmation"`
|
WelcomeEmail CustomContent `json:"welcomeEmail"`
|
||||||
UserExpired CustomContent `json:"userExpired"`
|
EmailConfirmation CustomContent `json:"emailConfirmation"`
|
||||||
|
UserExpired CustomContent `json:"userExpired"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomContent stores customized versions of jfa-go content, including emails and user messages.
|
// CustomContent stores customized versions of jfa-go content, including emails and user messages.
|
||||||
@ -1225,6 +1226,7 @@ func (st *Storage) loadLangEmail(filesystems ...fs.FS) error {
|
|||||||
patchLang(&lang.UserDeleted, &fallback.UserDeleted, &english.UserDeleted)
|
patchLang(&lang.UserDeleted, &fallback.UserDeleted, &english.UserDeleted)
|
||||||
patchLang(&lang.UserDisabled, &fallback.UserDisabled, &english.UserDisabled)
|
patchLang(&lang.UserDisabled, &fallback.UserDisabled, &english.UserDisabled)
|
||||||
patchLang(&lang.UserEnabled, &fallback.UserEnabled, &english.UserEnabled)
|
patchLang(&lang.UserEnabled, &fallback.UserEnabled, &english.UserEnabled)
|
||||||
|
patchLang(&lang.UserExpiryAdjusted, &fallback.UserExpiryAdjusted, &english.UserExpiryAdjusted)
|
||||||
patchLang(&lang.InviteEmail, &fallback.InviteEmail, &english.InviteEmail)
|
patchLang(&lang.InviteEmail, &fallback.InviteEmail, &english.InviteEmail)
|
||||||
patchLang(&lang.WelcomeEmail, &fallback.WelcomeEmail, &english.WelcomeEmail)
|
patchLang(&lang.WelcomeEmail, &fallback.WelcomeEmail, &english.WelcomeEmail)
|
||||||
patchLang(&lang.EmailConfirmation, &fallback.EmailConfirmation, &english.EmailConfirmation)
|
patchLang(&lang.EmailConfirmation, &fallback.EmailConfirmation, &english.EmailConfirmation)
|
||||||
@ -1239,6 +1241,7 @@ func (st *Storage) loadLangEmail(filesystems ...fs.FS) error {
|
|||||||
patchLang(&lang.UserDeleted, &english.UserDeleted)
|
patchLang(&lang.UserDeleted, &english.UserDeleted)
|
||||||
patchLang(&lang.UserDisabled, &english.UserDisabled)
|
patchLang(&lang.UserDisabled, &english.UserDisabled)
|
||||||
patchLang(&lang.UserEnabled, &english.UserEnabled)
|
patchLang(&lang.UserEnabled, &english.UserEnabled)
|
||||||
|
patchLang(&lang.UserExpiryAdjusted, &english.UserExpiryAdjusted)
|
||||||
patchLang(&lang.InviteEmail, &english.InviteEmail)
|
patchLang(&lang.InviteEmail, &english.InviteEmail)
|
||||||
patchLang(&lang.WelcomeEmail, &english.WelcomeEmail)
|
patchLang(&lang.WelcomeEmail, &english.WelcomeEmail)
|
||||||
patchLang(&lang.EmailConfirmation, &english.EmailConfirmation)
|
patchLang(&lang.EmailConfirmation, &english.EmailConfirmation)
|
||||||
|
@ -1684,22 +1684,25 @@ export class accountsList {
|
|||||||
applyList.push(id);
|
applyList.push(id);
|
||||||
}
|
}
|
||||||
this._enableExpiryReason.classList.add("unfocused");
|
this._enableExpiryReason.classList.add("unfocused");
|
||||||
|
this._enableExpiryNotify.parentElement.classList.remove("unfocused");
|
||||||
|
this._enableExpiryNotify.checked = false;
|
||||||
|
this._enableExpiryReason.value = "";
|
||||||
let header: string;
|
let header: string;
|
||||||
if (enableUser) {
|
if (enableUser) {
|
||||||
header = window.lang.quantity("reEnableUsers", list.length);
|
header = window.lang.quantity("reEnableUsers", list.length);
|
||||||
this._enableExpiryNotify.parentElement.classList.remove("unfocused");
|
|
||||||
this._enableExpiryNotify.checked = false;
|
|
||||||
this._enableExpiryReason.value = "";
|
|
||||||
} else if (this._settingExpiry) {
|
} else if (this._settingExpiry) {
|
||||||
header = window.lang.quantity("setExpiry", list.length);
|
header = window.lang.quantity("setExpiry", list.length);
|
||||||
this._enableExpiryNotify.parentElement.classList.add("unfocused");
|
// this._enableExpiryNotify.parentElement.classList.add("unfocused");
|
||||||
} else {
|
} else {
|
||||||
header = window.lang.quantity("extendExpiry", applyList.length);
|
header = window.lang.quantity("extendExpiry", applyList.length);
|
||||||
this._enableExpiryNotify.parentElement.classList.add("unfocused");
|
// this._enableExpiryNotify.parentElement.classList.add("unfocused");
|
||||||
}
|
}
|
||||||
document.getElementById("header-extend-expiry").textContent = header;
|
document.getElementById("header-extend-expiry").textContent = header;
|
||||||
const extend = () => {
|
const extend = () => {
|
||||||
let send = { "users": applyList, "timestamp": 0 }
|
let send = { "users": applyList, "timestamp": 0, "notify": this._enableExpiryNotify.checked }
|
||||||
|
if (this._enableExpiryNotify.checked) {
|
||||||
|
send["reason"] = this._enableExpiryReason.value;
|
||||||
|
}
|
||||||
if (this._usingExtendExpiryTextInput) {
|
if (this._usingExtendExpiryTextInput) {
|
||||||
let date = (Date as any).fromString(this._extendExpiryTextInput.value) as Date;
|
let date = (Date as any).fromString(this._extendExpiryTextInput.value) as Date;
|
||||||
send["timestamp"] = Math.floor(date.getTime() / 1000);
|
send["timestamp"] = Math.floor(date.getTime() / 1000);
|
||||||
@ -1728,7 +1731,7 @@ export class accountsList {
|
|||||||
this._extendExpiryForm.onsubmit = (event: Event) => {
|
this._extendExpiryForm.onsubmit = (event: Event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (enableUser) {
|
if (enableUser) {
|
||||||
this._enableDisableUsers(applyList, true, this._enableExpiryNotify.checked, this._enableExpiryNotify ? this._enableExpiryReason.value : null, (req: XMLHttpRequest) => {
|
this._enableDisableUsers(applyList, true, this._enableExpiryNotify.checked, this._enableExpiryNotify.checked ? this._enableExpiryReason.value : null, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState == 4) {
|
if (req.readyState == 4) {
|
||||||
if (req.status != 200 && req.status != 204) {
|
if (req.status != 200 && req.status != 204) {
|
||||||
window.modals.extendExpiry.close();
|
window.modals.extendExpiry.close();
|
||||||
|
Loading…
Reference in New Issue
Block a user