mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-11-12 21:30:10 +00:00
Compare commits
No commits in common. "b546aeb44080ca16b4fe74f8ad95be8a54ff444e" and "8e153cd92f8689dd398966997283515e00c0ea3b" have entirely different histories.
b546aeb440
...
8e153cd92f
105
api-messages.go
105
api-messages.go
@ -15,16 +15,12 @@ import (
|
|||||||
// @Router /config/emails [get]
|
// @Router /config/emails [get]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @tags Configuration
|
// @tags Configuration
|
||||||
func (app *appContext) GetCustomContent(gc *gin.Context) {
|
func (app *appContext) GetCustomEmails(gc *gin.Context) {
|
||||||
lang := gc.Query("lang")
|
lang := gc.Query("lang")
|
||||||
if _, ok := app.storage.lang.Email[lang]; !ok {
|
if _, ok := app.storage.lang.Email[lang]; !ok {
|
||||||
lang = app.storage.lang.chosenEmailLang
|
lang = app.storage.lang.chosenEmailLang
|
||||||
}
|
}
|
||||||
adminLang := lang
|
gc.JSON(200, emailListDTO{
|
||||||
if _, ok := app.storage.lang.Admin[lang]; !ok {
|
|
||||||
adminLang = app.storage.lang.chosenAdminLang
|
|
||||||
}
|
|
||||||
list := emailListDTO{
|
|
||||||
"UserCreated": {Name: app.storage.lang.Email[lang].UserCreated["name"], Enabled: app.storage.customEmails.UserCreated.Enabled},
|
"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},
|
"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},
|
"PasswordReset": {Name: app.storage.lang.Email[lang].PasswordReset["name"], Enabled: app.storage.customEmails.PasswordReset.Enabled},
|
||||||
@ -35,25 +31,13 @@ func (app *appContext) GetCustomContent(gc *gin.Context) {
|
|||||||
"WelcomeEmail": {Name: app.storage.lang.Email[lang].WelcomeEmail["name"], Enabled: app.storage.customEmails.WelcomeEmail.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},
|
"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},
|
"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},
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := gc.Query("filter")
|
|
||||||
if filter == "user" {
|
|
||||||
list = emailListDTO{"UserLogin": list["UserLogin"], "UserPage": list["UserPage"]}
|
|
||||||
} else {
|
|
||||||
delete(list, "UserLogin")
|
|
||||||
delete(list, "UserPage")
|
|
||||||
}
|
|
||||||
|
|
||||||
gc.JSON(200, list)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *appContext) getCustomMessage(id string) *customContent {
|
func (app *appContext) getCustomEmail(id string) *customEmail {
|
||||||
switch id {
|
switch id {
|
||||||
case "Announcement":
|
case "Announcement":
|
||||||
return &customContent{}
|
return &customEmail{}
|
||||||
case "UserCreated":
|
case "UserCreated":
|
||||||
return &app.storage.customEmails.UserCreated
|
return &app.storage.customEmails.UserCreated
|
||||||
case "InviteExpiry":
|
case "InviteExpiry":
|
||||||
@ -74,17 +58,13 @@ func (app *appContext) getCustomMessage(id string) *customContent {
|
|||||||
return &app.storage.customEmails.EmailConfirmation
|
return &app.storage.customEmails.EmailConfirmation
|
||||||
case "UserExpired":
|
case "UserExpired":
|
||||||
return &app.storage.customEmails.UserExpired
|
return &app.storage.customEmails.UserExpired
|
||||||
case "UserLogin":
|
|
||||||
return &app.storage.userPage.Login
|
|
||||||
case "UserPage":
|
|
||||||
return &app.storage.userPage.Page
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary Sets the corresponding custom email.
|
// @Summary Sets the corresponding custom email.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param customEmails body customEmails true "Content = email (in markdown)."
|
// @Param customEmail body customEmail true "Content = email (in markdown)."
|
||||||
// @Success 200 {object} boolResponse
|
// @Success 200 {object} boolResponse
|
||||||
// @Failure 400 {object} boolResponse
|
// @Failure 400 {object} boolResponse
|
||||||
// @Failure 500 {object} boolResponse
|
// @Failure 500 {object} boolResponse
|
||||||
@ -92,29 +72,25 @@ func (app *appContext) getCustomMessage(id string) *customContent {
|
|||||||
// @Router /config/emails/{id} [post]
|
// @Router /config/emails/{id} [post]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @tags Configuration
|
// @tags Configuration
|
||||||
func (app *appContext) SetCustomMessage(gc *gin.Context) {
|
func (app *appContext) SetCustomEmail(gc *gin.Context) {
|
||||||
var req customContent
|
var req customEmail
|
||||||
gc.BindJSON(&req)
|
gc.BindJSON(&req)
|
||||||
id := gc.Param("id")
|
id := gc.Param("id")
|
||||||
if req.Content == "" {
|
if req.Content == "" {
|
||||||
respondBool(400, false, gc)
|
respondBool(400, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message := app.getCustomMessage(id)
|
email := app.getCustomEmail(id)
|
||||||
if message == nil {
|
if email == nil {
|
||||||
respondBool(400, false, gc)
|
respondBool(400, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message.Content = req.Content
|
email.Content = req.Content
|
||||||
message.Enabled = true
|
email.Enabled = true
|
||||||
if app.storage.storeCustomEmails() != nil {
|
if app.storage.storeCustomEmails() != nil {
|
||||||
respondBool(500, false, gc)
|
respondBool(500, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if app.storage.storeUserPageContent() != nil {
|
|
||||||
respondBool(500, false, gc)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +104,7 @@ func (app *appContext) SetCustomMessage(gc *gin.Context) {
|
|||||||
// @Router /config/emails/{id}/state/{enable/disable} [post]
|
// @Router /config/emails/{id}/state/{enable/disable} [post]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @tags Configuration
|
// @tags Configuration
|
||||||
func (app *appContext) SetCustomMessageState(gc *gin.Context) {
|
func (app *appContext) SetCustomEmailState(gc *gin.Context) {
|
||||||
id := gc.Param("id")
|
id := gc.Param("id")
|
||||||
s := gc.Param("state")
|
s := gc.Param("state")
|
||||||
enabled := false
|
enabled := false
|
||||||
@ -137,24 +113,20 @@ func (app *appContext) SetCustomMessageState(gc *gin.Context) {
|
|||||||
} else if s != "disable" {
|
} else if s != "disable" {
|
||||||
respondBool(400, false, gc)
|
respondBool(400, false, gc)
|
||||||
}
|
}
|
||||||
message := app.getCustomMessage(id)
|
email := app.getCustomEmail(id)
|
||||||
if message == nil {
|
if email == nil {
|
||||||
respondBool(400, false, gc)
|
respondBool(400, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message.Enabled = enabled
|
email.Enabled = enabled
|
||||||
if app.storage.storeCustomEmails() != nil {
|
if app.storage.storeCustomEmails() != nil {
|
||||||
respondBool(500, false, gc)
|
respondBool(500, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if app.storage.storeUserPageContent() != nil {
|
|
||||||
respondBool(500, false, gc)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
respondBool(200, true, gc)
|
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 email (generating it if not set) and list of used variables in it.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} customEmailDTO
|
// @Success 200 {object} customEmailDTO
|
||||||
// @Failure 400 {object} boolResponse
|
// @Failure 400 {object} boolResponse
|
||||||
@ -163,7 +135,7 @@ func (app *appContext) SetCustomMessageState(gc *gin.Context) {
|
|||||||
// @Router /config/emails/{id} [get]
|
// @Router /config/emails/{id} [get]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @tags Configuration
|
// @tags Configuration
|
||||||
func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
func (app *appContext) GetCustomEmailTemplate(gc *gin.Context) {
|
||||||
lang := app.storage.lang.chosenEmailLang
|
lang := app.storage.lang.chosenEmailLang
|
||||||
id := gc.Param("id")
|
id := gc.Param("id")
|
||||||
var content string
|
var content string
|
||||||
@ -174,26 +146,20 @@ func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
|||||||
var values map[string]interface{}
|
var values map[string]interface{}
|
||||||
username := app.storage.lang.Email[lang].Strings.get("username")
|
username := app.storage.lang.Email[lang].Strings.get("username")
|
||||||
emailAddress := app.storage.lang.Email[lang].Strings.get("emailAddress")
|
emailAddress := app.storage.lang.Email[lang].Strings.get("emailAddress")
|
||||||
customMessage := app.getCustomMessage(id)
|
email := app.getCustomEmail(id)
|
||||||
if customMessage == nil {
|
if email == nil {
|
||||||
app.err.Printf("Failed to get custom message with ID \"%s\"", id)
|
app.err.Printf("Failed to get custom email with ID \"%s\"", id)
|
||||||
respondBool(400, false, gc)
|
respondBool(400, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if id == "WelcomeEmail" {
|
if id == "WelcomeEmail" {
|
||||||
conditionals = []string{"{yourAccountWillExpire}"}
|
conditionals = []string{"{yourAccountWillExpire}"}
|
||||||
customMessage.Conditionals = conditionals
|
email.Conditionals = conditionals
|
||||||
} else if id == "UserPage" {
|
|
||||||
variables = []string{"{username}"}
|
|
||||||
customMessage.Variables = variables
|
|
||||||
} else if id == "UserLogin" {
|
|
||||||
variables = []string{}
|
|
||||||
customMessage.Variables = variables
|
|
||||||
}
|
}
|
||||||
content = customMessage.Content
|
content = email.Content
|
||||||
noContent := content == ""
|
noContent := content == ""
|
||||||
if !noContent {
|
if !noContent {
|
||||||
variables = customMessage.Variables
|
variables = email.Variables
|
||||||
}
|
}
|
||||||
switch id {
|
switch id {
|
||||||
case "Announcement":
|
case "Announcement":
|
||||||
@ -249,14 +215,12 @@ func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
|||||||
msg, err = app.email.constructUserExpired(app, true)
|
msg, err = app.email.constructUserExpired(app, true)
|
||||||
}
|
}
|
||||||
values = app.email.userExpiredValues(app, false)
|
values = app.email.userExpiredValues(app, false)
|
||||||
case "UserLogin", "UserPage":
|
|
||||||
values = map[string]interface{}{}
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondBool(500, false, gc)
|
respondBool(500, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if noContent && id != "Announcement" && id != "UserPage" && id != "UserLogin" {
|
if noContent && id != "Announcement" {
|
||||||
content = msg.Text
|
content = msg.Text
|
||||||
variables = make([]string, strings.Count(content, "{"))
|
variables = make([]string, strings.Count(content, "{"))
|
||||||
i := 0
|
i := 0
|
||||||
@ -275,7 +239,7 @@ func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
|||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customMessage.Variables = variables
|
email.Variables = variables
|
||||||
}
|
}
|
||||||
if variables == nil {
|
if variables == nil {
|
||||||
variables = []string{}
|
variables = []string{}
|
||||||
@ -284,21 +248,10 @@ func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
|||||||
respondBool(500, false, gc)
|
respondBool(500, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if app.storage.storeUserPageContent() != nil {
|
mail, err := app.email.constructTemplate("", "<div class=\"preview-content\"></div>", app)
|
||||||
|
if err != nil {
|
||||||
respondBool(500, false, gc)
|
respondBool(500, false, gc)
|
||||||
}
|
return
|
||||||
var mail *Message
|
|
||||||
if id != "UserLogin" && id != "UserPage" {
|
|
||||||
mail, err = app.email.constructTemplate("", "<div class=\"preview-content\"></div>", app)
|
|
||||||
if err != nil {
|
|
||||||
respondBool(500, false, gc)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mail = &Message{
|
|
||||||
HTML: "<div class=\"card ~neutral dark:~d_neutral @low preview-content\"></div>",
|
|
||||||
Markdown: "<div class=\"card ~neutral dark:~d_neutral @low preview-content\"></div>",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
gc.JSON(200, customEmailDTO{Content: content, Variables: variables, Conditionals: conditionals, Values: values, HTML: mail.HTML, Plaintext: mail.Text})
|
gc.JSON(200, customEmailDTO{Content: content, Variables: variables, Conditionals: conditionals, Values: values, HTML: mail.HTML, Plaintext: mail.Text})
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ func (app *appContext) loadConfig() error {
|
|||||||
key.SetValue(key.MustString(filepath.Join(app.dataPath, (key.Name() + ".json"))))
|
key.SetValue(key.MustString(filepath.Join(app.dataPath, (key.Name() + ".json"))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails", "users", "telegram_users", "discord_users", "matrix_users", "announcements", "custom_user_page_content"} {
|
for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails", "users", "telegram_users", "discord_users", "matrix_users", "announcements"} {
|
||||||
app.config.Section("files").Key(key).SetValue(app.config.Section("files").Key(key).MustString(filepath.Join(app.dataPath, (key + ".json"))))
|
app.config.Section("files").Key(key).SetValue(app.config.Section("files").Key(key).MustString(filepath.Join(app.dataPath, (key + ".json"))))
|
||||||
}
|
}
|
||||||
for _, key := range []string{"matrix_sql"} {
|
for _, key := range []string{"matrix_sql"} {
|
||||||
@ -160,12 +160,6 @@ func (app *appContext) loadConfig() error {
|
|||||||
app.storage.customEmails_path = app.config.Section("files").Key("custom_emails").String()
|
app.storage.customEmails_path = app.config.Section("files").Key("custom_emails").String()
|
||||||
app.storage.loadCustomEmails()
|
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("")
|
substituteStrings = app.config.Section("jellyfin").Key("substitute_jellyfin_strings").MustString("")
|
||||||
|
|
||||||
if substituteStrings != "" {
|
if substituteStrings != "" {
|
||||||
|
@ -1551,14 +1551,6 @@
|
|||||||
"value": "",
|
"value": "",
|
||||||
"description": "JSON file generated by program in settings, different from email_html/email_text. See wiki for more info."
|
"description": "JSON file generated by program in settings, different from email_html/email_text. See wiki for more info."
|
||||||
},
|
},
|
||||||
"custom_user_page_content": {
|
|
||||||
"name": "Custom user page content",
|
|
||||||
"required": false,
|
|
||||||
"requires_restart": false,
|
|
||||||
"type": "text",
|
|
||||||
"value": "",
|
|
||||||
"description": "JSON file generated by program in settings, containing user page messages. See wiki for more info."
|
|
||||||
},
|
|
||||||
"telegram_users": {
|
"telegram_users": {
|
||||||
"name": "Telegram users",
|
"name": "Telegram users",
|
||||||
"required": false,
|
"required": false,
|
||||||
|
@ -13,8 +13,6 @@
|
|||||||
--border-width-2: 3px;
|
--border-width-2: 3px;
|
||||||
--border-width-4: 5px;
|
--border-width-4: 5px;
|
||||||
--border-width-8: 8px;
|
--border-width-8: 8px;
|
||||||
|
|
||||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.light {
|
.light {
|
||||||
|
4
email.go
4
email.go
@ -24,7 +24,7 @@ import (
|
|||||||
sMail "github.com/xhit/go-simple-mail/v2"
|
sMail "github.com/xhit/go-simple-mail/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var markdownRenderer = html.NewRenderer(html.RendererOptions{Flags: html.Smartypants})
|
var renderer = html.NewRenderer(html.RendererOptions{Flags: html.Smartypants})
|
||||||
|
|
||||||
// EmailClient implements email sending, right now via smtp, mailgun or a dummy client.
|
// EmailClient implements email sending, right now via smtp, mailgun or a dummy client.
|
||||||
type EmailClient interface {
|
type EmailClient interface {
|
||||||
@ -353,7 +353,7 @@ func (emailer *Emailer) constructTemplate(subject, md string, app *appContext, u
|
|||||||
subject = templateEmail(subject, []string{"{username}"}, nil, map[string]interface{}{"username": username[0]})
|
subject = templateEmail(subject, []string{"{username}"}, nil, map[string]interface{}{"username": username[0]})
|
||||||
}
|
}
|
||||||
email := &Message{Subject: subject}
|
email := &Message{Subject: subject}
|
||||||
html := markdown.ToHTML([]byte(md), nil, markdownRenderer)
|
html := markdown.ToHTML([]byte(md), nil, renderer)
|
||||||
text := stripMarkdown(md)
|
text := stripMarkdown(md)
|
||||||
message := app.config.Section("messages").Key("message").String()
|
message := app.config.Section("messages").Key("message").String()
|
||||||
var err error
|
var err error
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<meta charset="UTF-8"/>
|
<meta charset="utf-8">
|
||||||
<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">
|
||||||
|
@ -1,20 +1,11 @@
|
|||||||
<div id="modal-login" class="modal">
|
<div id="modal-login" class="modal">
|
||||||
<div class="my-[10%] row items-stretch relative mx-auto w-[40%] lg:w-[60%]">
|
<form class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3 form-login" href="">
|
||||||
{{ if index . "LoginMessageEnabled" }}
|
<span class="heading">{{ .strings.login }}</span>
|
||||||
{{ if .LoginMessageEnabled }}
|
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.username }}" id="login-user">
|
||||||
<div class="card mx-2 flex-initial w-[100%] xl:w-[35%] mb-4 xl:mb-0 ~neutral @low content">
|
<input type="password" class="field input ~neutral @high mb-4" placeholder="{{ .strings.password }}" id="login-password">
|
||||||
{{ .LoginMessageContent }}
|
<label>
|
||||||
</div>
|
<input type="submit" class="unfocused">
|
||||||
{{ end }}
|
<span class="button ~urge @low full-width center supra submit">{{ .strings.login }}</span>
|
||||||
{{ end }}
|
</label>
|
||||||
<form class="card mx-2 flex-auto form-login w-[100%] xl:w-[55%] mb-0" href="">
|
</form>
|
||||||
<span class="heading">{{ .strings.login }}</span>
|
|
||||||
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.username }}" id="login-user">
|
|
||||||
<input type="password" class="field input ~neutral @high mb-4" placeholder="{{ .strings.password }}" id="login-password">
|
|
||||||
<label>
|
|
||||||
<input type="submit" class="unfocused">
|
|
||||||
<span class="button ~urge @low full-width center supra submit">{{ .strings.login }}</span>
|
|
||||||
</label>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,7 +43,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ template "login-modal.html" . }}
|
|
||||||
{{ template "account-linking.html" . }}
|
{{ template "account-linking.html" . }}
|
||||||
<div id="notification-box"></div>
|
<div id="notification-box"></div>
|
||||||
<div class="top-4 left-4 absolute">
|
<div class="top-4 left-4 absolute">
|
||||||
@ -69,11 +68,12 @@
|
|||||||
<span class="button ~warning" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span>
|
<span class="button ~warning" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span>
|
||||||
<span class="button ~critical @low mb-4 unfocused" id="logout-button">{{ .strings.logout }}</span>
|
<span class="button ~critical @low mb-4 unfocused" id="logout-button">{{ .strings.logout }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{{ template "login-modal.html" . }}
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="card @low dark:~d_neutral mb-4" id="card-user">
|
<div class="card @low dark:~d_neutral mb-4" id="card-user">
|
||||||
<span class="heading mb-2"></span>
|
<span class="heading mb-2"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<div class="card @low dark:~d_neutral unfocused" id="card-status">
|
<div class="card @low dark:~d_neutral unfocused" id="card-status">
|
||||||
<span class="heading mb-2">{{ .strings.expiry }}</span>
|
<span class="heading mb-2">{{ .strings.expiry }}</span>
|
||||||
<aside class="aside ~warning user-expiry my-4"></aside>
|
<aside class="aside ~warning user-expiry my-4"></aside>
|
||||||
@ -83,13 +83,6 @@
|
|||||||
<span class="heading mb-2">{{ .strings.contactMethods }}</span>
|
<span class="heading mb-2">{{ .strings.contactMethods }}</span>
|
||||||
<div class="content flex justify-between flex-col h-100"></div>
|
<div class="content flex justify-between flex-col h-100"></div>
|
||||||
</div>
|
</div>
|
||||||
{{ if index . "PageMessageEnabled" }}
|
|
||||||
{{ if .PageMessageEnabled }}
|
|
||||||
<div class="card @low dark:~d_neutral content">
|
|
||||||
{{ .PageMessageContent }}
|
|
||||||
</div>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="{{ .urlBase }}/js/user.js" type="module"></script>
|
<script src="{{ .urlBase }}/js/user.js" type="module"></script>
|
||||||
|
@ -113,9 +113,7 @@
|
|||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
"searchOptions": "Search Options",
|
"searchOptions": "Search Options",
|
||||||
"matchText": "Match Text",
|
"matchText": "Match Text",
|
||||||
"jellyfinID": "Jellyfin ID",
|
"jellyfinID": "Jellyfin ID"
|
||||||
"userPageLogin": "User Page: Login",
|
|
||||||
"userPagePage": "User Page: Page"
|
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"changedEmailAddress": "Changed email address of {n}.",
|
"changedEmailAddress": "Changed email address of {n}.",
|
||||||
@ -211,4 +209,4 @@
|
|||||||
"plural": "Extended expiry for {n} users."
|
"plural": "Extended expiry for {n} users."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -61,4 +61,4 @@
|
|||||||
"plural": "{n} Days"
|
"plural": "{n} Days"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,8 +21,6 @@
|
|||||||
"sendPINDiscord": "Type {command} in {server_channel} on Discord, then send the PIN below.",
|
"sendPINDiscord": "Type {command} in {server_channel} on Discord, then send the PIN below.",
|
||||||
"matrixEnterUser": "Enter your User ID, press submit, and a PIN will be sent to you. Enter it here to continue.",
|
"matrixEnterUser": "Enter your User ID, press submit, and a PIN will be sent to you. Enter it here to continue.",
|
||||||
"welcomeUser": "Welcome, {user}!",
|
"welcomeUser": "Welcome, {user}!",
|
||||||
"addContactMethod": "Add Contact Method",
|
|
||||||
"editContactMethod": "Edit Contact Method",
|
|
||||||
"joinTheServer": "Join the server:"
|
"joinTheServer": "Join the server:"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
@ -63,4 +61,4 @@
|
|||||||
"plural": "Must have at least {n} special characters"
|
"plural": "Must have at least {n} special characters"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -249,7 +249,7 @@ func (d *MatrixDaemon) Send(message *Message, users ...MatrixUser) (err error) {
|
|||||||
md := ""
|
md := ""
|
||||||
if message.Markdown != "" {
|
if message.Markdown != "" {
|
||||||
// Convert images to links
|
// Convert images to links
|
||||||
md = string(markdown.ToHTML([]byte(strings.ReplaceAll(message.Markdown, "![", "[")), nil, markdownRenderer))
|
md = string(markdown.ToHTML([]byte(strings.ReplaceAll(message.Markdown, "![", "[")), nil, renderer))
|
||||||
}
|
}
|
||||||
content := &event.MessageEventContent{
|
content := &event.MessageEventContent{
|
||||||
MsgType: "m.text",
|
MsgType: "m.text",
|
||||||
|
@ -196,10 +196,10 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
|||||||
|
|
||||||
api.GET(p+"/config/update", app.CheckUpdate)
|
api.GET(p+"/config/update", app.CheckUpdate)
|
||||||
api.POST(p+"/config/update", app.ApplyUpdate)
|
api.POST(p+"/config/update", app.ApplyUpdate)
|
||||||
api.GET(p+"/config/emails", app.GetCustomContent)
|
api.GET(p+"/config/emails", app.GetCustomEmails)
|
||||||
api.GET(p+"/config/emails/:id", app.GetCustomMessageTemplate)
|
api.GET(p+"/config/emails/:id", app.GetCustomEmailTemplate)
|
||||||
api.POST(p+"/config/emails/:id", app.SetCustomMessage)
|
api.POST(p+"/config/emails/:id", app.SetCustomEmail)
|
||||||
api.POST(p+"/config/emails/:id/state/:state", app.SetCustomMessageState)
|
api.POST(p+"/config/emails/:id/state/:state", app.SetCustomEmailState)
|
||||||
api.GET(p+"/config", app.GetConfig)
|
api.GET(p+"/config", app.GetConfig)
|
||||||
api.POST(p+"/config", app.ModifyConfig)
|
api.POST(p+"/config", app.ModifyConfig)
|
||||||
api.POST(p+"/restart", app.restart)
|
api.POST(p+"/restart", app.restart)
|
||||||
|
70
storage.go
70
storage.go
@ -21,24 +21,23 @@ type matrixStore map[string]MatrixUser
|
|||||||
type emailStore map[string]EmailAddress
|
type emailStore map[string]EmailAddress
|
||||||
|
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
timePattern string
|
timePattern string
|
||||||
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
|
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 string
|
||||||
users map[string]time.Time // Map of Jellyfin User IDs to their expiry times.
|
users map[string]time.Time // Map of Jellyfin User IDs to their expiry times.
|
||||||
invites Invites
|
invites Invites
|
||||||
profiles map[string]Profile
|
profiles map[string]Profile
|
||||||
defaultProfile string
|
defaultProfile string
|
||||||
displayprefs, ombi_template map[string]interface{}
|
displayprefs, ombi_template map[string]interface{}
|
||||||
emails emailStore
|
emails emailStore
|
||||||
telegram telegramStore // Map of Jellyfin User IDs to telegram users.
|
telegram telegramStore // Map of Jellyfin User IDs to telegram users.
|
||||||
discord discordStore // Map of Jellyfin user IDs to discord users.
|
discord discordStore // Map of Jellyfin user IDs to discord users.
|
||||||
matrix matrixStore // Map of Jellyfin user IDs to Matrix users.
|
matrix matrixStore // Map of Jellyfin user IDs to Matrix users.
|
||||||
customEmails customEmails
|
customEmails customEmails
|
||||||
userPage userPageContent
|
policy mediabrowser.Policy
|
||||||
policy mediabrowser.Policy
|
configuration mediabrowser.Configuration
|
||||||
configuration mediabrowser.Configuration
|
lang Lang
|
||||||
lang Lang
|
announcements map[string]announcementTemplate
|
||||||
announcements map[string]announcementTemplate
|
invitesLock, usersLock, discordLock, telegramLock, matrixLock, emailsLock sync.Mutex
|
||||||
invitesLock, usersLock, discordLock, telegramLock, matrixLock, emailsLock sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEmails returns a copy of the store.
|
// GetEmails returns a copy of the store.
|
||||||
@ -173,30 +172,25 @@ type EmailAddress struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type customEmails struct {
|
type customEmails struct {
|
||||||
UserCreated customContent `json:"userCreated"`
|
UserCreated customEmail `json:"userCreated"`
|
||||||
InviteExpiry customContent `json:"inviteExpiry"`
|
InviteExpiry customEmail `json:"inviteExpiry"`
|
||||||
PasswordReset customContent `json:"passwordReset"`
|
PasswordReset customEmail `json:"passwordReset"`
|
||||||
UserDeleted customContent `json:"userDeleted"`
|
UserDeleted customEmail `json:"userDeleted"`
|
||||||
UserDisabled customContent `json:"userDisabled"`
|
UserDisabled customEmail `json:"userDisabled"`
|
||||||
UserEnabled customContent `json:"userEnabled"`
|
UserEnabled customEmail `json:"userEnabled"`
|
||||||
InviteEmail customContent `json:"inviteEmail"`
|
InviteEmail customEmail `json:"inviteEmail"`
|
||||||
WelcomeEmail customContent `json:"welcomeEmail"`
|
WelcomeEmail customEmail `json:"welcomeEmail"`
|
||||||
EmailConfirmation customContent `json:"emailConfirmation"`
|
EmailConfirmation customEmail `json:"emailConfirmation"`
|
||||||
UserExpired customContent `json:"userExpired"`
|
UserExpired customEmail `json:"userExpired"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type customContent struct {
|
type customEmail struct {
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Variables []string `json:"variables,omitempty"`
|
Variables []string `json:"variables,omitempty"`
|
||||||
Conditionals []string `json:"conditionals,omitempty"`
|
Conditionals []string `json:"conditionals,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type userPageContent struct {
|
|
||||||
Login customContent `json:"login"`
|
|
||||||
Page customContent `json:"page"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
||||||
|
|
||||||
type Profile struct {
|
type Profile struct {
|
||||||
@ -987,14 +981,6 @@ func (st *Storage) storeCustomEmails() error {
|
|||||||
return storeJSON(st.customEmails_path, st.customEmails)
|
return storeJSON(st.customEmails_path, st.customEmails)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Storage) loadUserPageContent() error {
|
|
||||||
return loadJSON(st.userPage_path, &st.userPage)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Storage) storeUserPageContent() error {
|
|
||||||
return storeJSON(st.userPage_path, st.userPage)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Storage) loadPolicy() error {
|
func (st *Storage) loadPolicy() error {
|
||||||
return loadJSON(st.policy_path, &st.policy)
|
return loadJSON(st.policy_path, &st.policy)
|
||||||
}
|
}
|
||||||
|
@ -542,7 +542,7 @@ export class settingsList {
|
|||||||
private _sections: { [name: string]: sectionPanel }
|
private _sections: { [name: string]: sectionPanel }
|
||||||
private _buttons: { [name: string]: HTMLSpanElement }
|
private _buttons: { [name: string]: HTMLSpanElement }
|
||||||
private _needsRestart: boolean = false;
|
private _needsRestart: boolean = false;
|
||||||
private _messageEditor = new MessageEditor();
|
private _emailEditor = new EmailEditor();
|
||||||
|
|
||||||
addSection = (name: string, s: Section, subButton?: HTMLElement) => {
|
addSection = (name: string, s: Section, subButton?: HTMLElement) => {
|
||||||
const section = new sectionPanel(s, name);
|
const section = new sectionPanel(s, name);
|
||||||
@ -713,7 +713,7 @@ export class settingsList {
|
|||||||
if (name in this._sections) {
|
if (name in this._sections) {
|
||||||
this._sections[name].update(settings.sections[name]);
|
this._sections[name].update(settings.sections[name]);
|
||||||
} else {
|
} else {
|
||||||
if (name == "messages" || name == "user_page") {
|
if (name == "messages") {
|
||||||
const editButton = document.createElement("div");
|
const editButton = document.createElement("div");
|
||||||
editButton.classList.add("tooltip", "left");
|
editButton.classList.add("tooltip", "left");
|
||||||
editButton.innerHTML = `
|
editButton.innerHTML = `
|
||||||
@ -724,9 +724,7 @@ export class settingsList {
|
|||||||
${window.lang.get("strings", "customizeMessages")}
|
${window.lang.get("strings", "customizeMessages")}
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
(editButton.querySelector("span.button") as HTMLSpanElement).onclick = () => {
|
(editButton.querySelector("span.button") as HTMLSpanElement).onclick = this._emailEditor.showList;
|
||||||
this._messageEditor.showList(name == "messages" ? "email" : "user");
|
|
||||||
};
|
|
||||||
this.addSection(name, settings.sections[name], editButton);
|
this.addSection(name, settings.sections[name], editButton);
|
||||||
} else if (name == "updates") {
|
} else if (name == "updates") {
|
||||||
const icon = document.createElement("span") as HTMLSpanElement;
|
const icon = document.createElement("span") as HTMLSpanElement;
|
||||||
@ -775,7 +773,7 @@ interface emailListEl {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageEditor {
|
class EmailEditor {
|
||||||
private _currentID: string;
|
private _currentID: string;
|
||||||
private _names: { [id: string]: emailListEl };
|
private _names: { [id: string]: emailListEl };
|
||||||
private _content: string;
|
private _content: string;
|
||||||
@ -886,8 +884,8 @@ class MessageEditor {
|
|||||||
// }, true);
|
// }, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
showList = (filter?: string) => {
|
showList = () => {
|
||||||
_get("/config/emails?lang=" + window.language + (filter ? "&filter=" + filter : ""), null, (req: XMLHttpRequest) => {
|
_get("/config/emails?lang=" + window.language, null, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState == 4) {
|
if (req.readyState == 4) {
|
||||||
if (req.status != 200) {
|
if (req.status != 200) {
|
||||||
window.notifications.customError("loadTemplateError", window.lang.notif("errorFailureCheckLogs"));
|
window.notifications.customError("loadTemplateError", window.lang.notif("errorFailureCheckLogs"));
|
||||||
|
@ -94,17 +94,17 @@ class ContactMethods {
|
|||||||
|
|
||||||
append = (name: string, details: MyDetailsContactMethod, icon: string, addEditFunc?: (add: boolean) => void, required?: boolean) => {
|
append = (name: string, details: MyDetailsContactMethod, icon: string, addEditFunc?: (add: boolean) => void, required?: boolean) => {
|
||||||
const row = document.createElement("div");
|
const row = document.createElement("div");
|
||||||
row.classList.add("flex", "flex-expand", "my-2", "flex-nowrap");
|
row.classList.add("row", "flex-expand", "my-2");
|
||||||
let innerHTML = `
|
let innerHTML = `
|
||||||
<div class="flex items-baseline flex-nowrap ellipsis">
|
<div class="inline align-middle">
|
||||||
<span class="shield ~urge" alt="${name}">
|
<span class="shield ~urge" alt="${name}">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
${icon}
|
${icon}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="ml-2 font-bold text-ellipsis overflow-hidden">${(details.value == "") ? window.lang.strings("notSet") : details.value}</span>
|
<span class="ml-2 font-bold">${(details.value == "") ? window.lang.strings("notSet") : details.value}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center ml-2">
|
<div class="flex items-center">
|
||||||
<button class="user-contact-enabled-disabled button ~neutral" ${details.value == "" ? "disabled" : ""}>
|
<button class="user-contact-enabled-disabled button ~neutral" ${details.value == "" ? "disabled" : ""}>
|
||||||
<input type="checkbox" class="mr-2" ${details.value == "" ? "disabled" : ""}>
|
<input type="checkbox" class="mr-2" ${details.value == "" ? "disabled" : ""}>
|
||||||
<span>${window.lang.strings("enabled")}</span>
|
<span>${window.lang.strings("enabled")}</span>
|
||||||
|
19
views.go
19
views.go
@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
"github.com/gomarkdown/markdown"
|
|
||||||
"github.com/hrfee/mediabrowser"
|
"github.com/hrfee/mediabrowser"
|
||||||
"github.com/steambap/captcha"
|
"github.com/steambap/captcha"
|
||||||
)
|
)
|
||||||
@ -204,24 +203,6 @@ func (app *appContext) MyUserPage(gc *gin.Context) {
|
|||||||
data["discordServerName"] = app.discord.serverName
|
data["discordServerName"] = app.discord.serverName
|
||||||
data["discordInviteLink"] = app.discord.inviteChannelName != ""
|
data["discordInviteLink"] = app.discord.inviteChannelName != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
pageMessages := map[string]*customContent{
|
|
||||||
"Login": app.getCustomMessage("UserLogin"),
|
|
||||||
"Page": app.getCustomMessage("UserPage"),
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, msg := range pageMessages {
|
|
||||||
if msg == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data[name+"MessageEnabled"] = msg.Enabled
|
|
||||||
if !msg.Enabled {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// We don't template here, since the username is only known after login.
|
|
||||||
data[name+"MessageContent"] = template.HTML(markdown.ToHTML([]byte(msg.Content), nil, markdownRenderer))
|
|
||||||
}
|
|
||||||
|
|
||||||
gcHTML(gc, http.StatusOK, "user.html", data)
|
gcHTML(gc, http.StatusOK, "user.html", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user