mirror of
https://github.com/hrfee/jfa-go.git
synced 2025-01-01 14:00:12 +00:00
Compare commits
5 Commits
5c87d109a3
...
6ffdd4dad7
Author | SHA1 | Date | |
---|---|---|---|
6ffdd4dad7 | |||
98d59ba4e0 | |||
938523c18b | |||
cc4e12c405 | |||
eb406ef951 |
253
api.go
253
api.go
@ -126,7 +126,7 @@ func (app *appContext) checkInvites() {
|
|||||||
wait.Add(1)
|
wait.Add(1)
|
||||||
go func(addr string) {
|
go func(addr string) {
|
||||||
defer wait.Done()
|
defer wait.Done()
|
||||||
msg, err := app.email.constructExpiry(code, data, app)
|
msg, err := app.email.constructExpiry(code, data, app, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Printf("%s: Failed to construct expiry notification: %s", code, err)
|
app.err.Printf("%s: Failed to construct expiry notification: %s", code, err)
|
||||||
} else if err := app.email.send(msg, addr); err != nil {
|
} else if err := app.email.send(msg, addr); err != nil {
|
||||||
@ -163,7 +163,7 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
|
|||||||
for address, settings := range notify {
|
for address, settings := range notify {
|
||||||
if settings["notify-expiry"] {
|
if settings["notify-expiry"] {
|
||||||
go func() {
|
go func() {
|
||||||
msg, err := app.email.constructExpiry(code, inv, app)
|
msg, err := app.email.constructExpiry(code, inv, app, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Printf("%s: Failed to construct expiry notification: %s", code, err)
|
app.err.Printf("%s: Failed to construct expiry notification: %s", code, err)
|
||||||
} else if err := app.email.send(msg, address); err != nil {
|
} else if err := app.email.send(msg, address); err != nil {
|
||||||
@ -295,7 +295,7 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
if emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
|
if emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
|
||||||
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email)
|
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email)
|
||||||
msg, err := app.email.constructWelcome(req.Username, app)
|
msg, err := app.email.constructWelcome(req.Username, app, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Printf("%s: Failed to construct welcome email: %s", req.Username, err)
|
app.err.Printf("%s: Failed to construct welcome email: %s", req.Username, err)
|
||||||
respondUser(500, true, false, err.Error(), gc)
|
respondUser(500, true, false, err.Error(), gc)
|
||||||
@ -351,7 +351,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
|||||||
f = func(gc *gin.Context) {
|
f = func(gc *gin.Context) {
|
||||||
app.debug.Printf("%s: Email confirmation required", req.Code)
|
app.debug.Printf("%s: Email confirmation required", req.Code)
|
||||||
respond(401, "confirmEmail", gc)
|
respond(401, "confirmEmail", gc)
|
||||||
msg, err := app.email.constructConfirmation(req.Code, req.Username, key, app)
|
msg, err := app.email.constructConfirmation(req.Code, req.Username, key, app, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Printf("%s: Failed to construct confirmation email: %s", req.Code, err)
|
app.err.Printf("%s: Failed to construct confirmation email: %s", req.Code, err)
|
||||||
} else if err := app.email.send(msg, req.Email); err != nil {
|
} else if err := app.email.send(msg, req.Email); err != nil {
|
||||||
@ -380,7 +380,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
|||||||
for address, settings := range invite.Notify {
|
for address, settings := range invite.Notify {
|
||||||
if settings["notify-creation"] {
|
if settings["notify-creation"] {
|
||||||
go func() {
|
go func() {
|
||||||
msg, err := app.email.constructCreated(req.Code, req.Username, req.Email, invite, app)
|
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: %s", req.Code, err)
|
app.err.Printf("%s: Failed to construct user creation notification: %s", req.Code, err)
|
||||||
} else if err := app.email.send(msg, address); err != nil {
|
} else if err := app.email.send(msg, address); err != nil {
|
||||||
@ -436,7 +436,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
|||||||
}
|
}
|
||||||
if emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
|
if emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
|
||||||
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email)
|
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email)
|
||||||
msg, err := app.email.constructWelcome(req.Username, app)
|
msg, err := app.email.constructWelcome(req.Username, app, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Printf("%s: Failed to construct welcome email: %s", req.Username, err)
|
app.err.Printf("%s: Failed to construct welcome email: %s", req.Username, err)
|
||||||
} else if err := app.email.send(msg, req.Email); err != nil {
|
} else if err := app.email.send(msg, req.Email); err != nil {
|
||||||
@ -516,7 +516,7 @@ func (app *appContext) Announce(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
addresses = append(addresses, addr.(string))
|
addresses = append(addresses, addr.(string))
|
||||||
}
|
}
|
||||||
msg, err := app.email.constructAnnouncement(req.Subject, req.Message, app)
|
msg, err := app.email.constructTemplate(req.Subject, req.Message, app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Printf("Failed to construct announcement emails: %s", err)
|
app.err.Printf("Failed to construct announcement emails: %s", err)
|
||||||
respondBool(500, false, gc)
|
respondBool(500, false, gc)
|
||||||
@ -576,7 +576,7 @@ func (app *appContext) DeleteUsers(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
if len(addresses) != 0 {
|
if len(addresses) != 0 {
|
||||||
go func(reason string, addresses []string) {
|
go func(reason string, addresses []string) {
|
||||||
msg, err := app.email.constructDeleted(reason, app)
|
msg, err := app.email.constructDeleted(reason, app, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Printf("Failed to construct account deletion emails: %s", err)
|
app.err.Printf("Failed to construct account deletion emails: %s", err)
|
||||||
} else if err := app.email.send(msg, addresses...); err != nil {
|
} else if err := app.email.send(msg, addresses...); err != nil {
|
||||||
@ -638,7 +638,7 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
|
|||||||
if emailEnabled && req.Email != "" && app.config.Section("invite_emails").Key("enabled").MustBool(false) {
|
if emailEnabled && req.Email != "" && app.config.Section("invite_emails").Key("enabled").MustBool(false) {
|
||||||
app.debug.Printf("%s: Sending invite email", inviteCode)
|
app.debug.Printf("%s: Sending invite email", inviteCode)
|
||||||
invite.Email = req.Email
|
invite.Email = req.Email
|
||||||
msg, err := app.email.constructInvite(inviteCode, invite, app)
|
msg, err := app.email.constructInvite(inviteCode, invite, app, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
invite.Email = fmt.Sprintf("Failed to send to %s", req.Email)
|
invite.Email = fmt.Sprintf("Failed to send to %s", req.Email)
|
||||||
app.err.Printf("%s: Failed to construct invite email: %s", inviteCode, err)
|
app.err.Printf("%s: Failed to construct invite email: %s", inviteCode, err)
|
||||||
@ -1269,6 +1269,239 @@ func (app *appContext) ModifyConfig(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Summary Get a list of email names and IDs.
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} emailListDTO
|
||||||
|
// @Router /config/emails [get]
|
||||||
|
// @tags Configuration
|
||||||
|
func (app *appContext) GetEmails(gc *gin.Context) {
|
||||||
|
gc.JSON(200, emailListDTO{
|
||||||
|
"UserCreated": app.storage.lang.Email["en-us"].UserCreated["name"],
|
||||||
|
"InviteExpiry": app.storage.lang.Email["en-us"].InviteExpiry["name"],
|
||||||
|
"PasswordReset": app.storage.lang.Email["en-us"].PasswordReset["name"],
|
||||||
|
"UserDeleted": app.storage.lang.Email["en-us"].UserDeleted["name"],
|
||||||
|
"InviteEmail": app.storage.lang.Email["en-us"].InviteEmail["name"],
|
||||||
|
"WelcomeEmail": app.storage.lang.Email["en-us"].WelcomeEmail["name"],
|
||||||
|
"EmailConfirmation": app.storage.lang.Email["en-us"].EmailConfirmation["name"],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Sets the corresponding custom email.
|
||||||
|
// @Produce json
|
||||||
|
// @Param customEmail body customEmail true "Content = email (in markdown)."
|
||||||
|
// @Success 200 {object} boolResponse
|
||||||
|
// @Failure 400 {object} boolResponse
|
||||||
|
// @Failure 500 {object} boolResponse
|
||||||
|
// @Router /config/emails/{id} [post]
|
||||||
|
// @tags Configuration
|
||||||
|
func (app *appContext) SetEmail(gc *gin.Context) {
|
||||||
|
var req customEmail
|
||||||
|
gc.BindJSON(&req)
|
||||||
|
id := gc.Param("id")
|
||||||
|
if req.Content == "" {
|
||||||
|
respondBool(400, false, gc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if id == "UserCreated" {
|
||||||
|
app.storage.customEmails.UserCreated.Content = req.Content
|
||||||
|
app.storage.customEmails.UserCreated.Enabled = true
|
||||||
|
} else if id == "InviteExpiry" {
|
||||||
|
app.storage.customEmails.InviteExpiry.Content = req.Content
|
||||||
|
app.storage.customEmails.InviteExpiry.Enabled = true
|
||||||
|
} else if id == "PasswordReset" {
|
||||||
|
app.storage.customEmails.PasswordReset.Content = req.Content
|
||||||
|
app.storage.customEmails.PasswordReset.Enabled = true
|
||||||
|
} else if id == "UserDeleted" {
|
||||||
|
app.storage.customEmails.UserDeleted.Content = req.Content
|
||||||
|
app.storage.customEmails.UserDeleted.Enabled = true
|
||||||
|
} else if id == "InviteEmail" {
|
||||||
|
app.storage.customEmails.InviteEmail.Content = req.Content
|
||||||
|
app.storage.customEmails.InviteEmail.Enabled = true
|
||||||
|
} else if id == "WelcomeEmail" {
|
||||||
|
app.storage.customEmails.WelcomeEmail.Content = req.Content
|
||||||
|
app.storage.customEmails.WelcomeEmail.Enabled = true
|
||||||
|
} else if id == "EmailConfirmation" {
|
||||||
|
app.storage.customEmails.EmailConfirmation.Content = req.Content
|
||||||
|
app.storage.customEmails.EmailConfirmation.Enabled = true
|
||||||
|
} else {
|
||||||
|
respondBool(400, false, gc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if app.storage.storeCustomEmails() != nil {
|
||||||
|
respondBool(500, false, gc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
respondBool(200, true, gc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Enable/Disable custom email.
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} boolResponse
|
||||||
|
// @Failure 400 {object} boolResponse
|
||||||
|
// @Failure 500 {object} boolResponse
|
||||||
|
// @Router /config/emails/{id}/{enable/disable} [post]
|
||||||
|
// @tags Configuration
|
||||||
|
func (app *appContext) SetEmailState(gc *gin.Context) {
|
||||||
|
id := gc.Param("id")
|
||||||
|
s := gc.Param("state")
|
||||||
|
enabled := false
|
||||||
|
if s == "enable" {
|
||||||
|
enabled = true
|
||||||
|
} else if s != "disable" {
|
||||||
|
respondBool(400, false, gc)
|
||||||
|
}
|
||||||
|
if id == "UserCreated" {
|
||||||
|
app.storage.customEmails.UserCreated.Enabled = enabled
|
||||||
|
} else if id == "InviteExpiry" {
|
||||||
|
app.storage.customEmails.InviteExpiry.Enabled = enabled
|
||||||
|
} else if id == "PasswordReset" {
|
||||||
|
app.storage.customEmails.PasswordReset.Enabled = enabled
|
||||||
|
} else if id == "UserDeleted" {
|
||||||
|
app.storage.customEmails.UserDeleted.Enabled = enabled
|
||||||
|
} else if id == "InviteEmail" {
|
||||||
|
app.storage.customEmails.InviteEmail.Enabled = enabled
|
||||||
|
} else if id == "WelcomeEmail" {
|
||||||
|
app.storage.customEmails.WelcomeEmail.Enabled = enabled
|
||||||
|
} else if id == "EmailConfirmation" {
|
||||||
|
app.storage.customEmails.EmailConfirmation.Enabled = enabled
|
||||||
|
} else {
|
||||||
|
respondBool(400, false, gc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if app.storage.storeCustomEmails() != nil {
|
||||||
|
respondBool(500, false, gc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
respondBool(200, true, gc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Returns the boilerplate email and list of used variables in it.
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} customEmail
|
||||||
|
// @Failure 400 {object} boolResponse
|
||||||
|
// @Failure 500 {object} boolResponse
|
||||||
|
// @Router /config/emails/{id} [get]
|
||||||
|
// @tags Configuration
|
||||||
|
func (app *appContext) GetEmail(gc *gin.Context) {
|
||||||
|
id := gc.Param("id")
|
||||||
|
var content string
|
||||||
|
var err error
|
||||||
|
var msg *Email
|
||||||
|
var variables []string
|
||||||
|
var writeVars func(variables []string)
|
||||||
|
newEmail := false
|
||||||
|
if id == "UserCreated" {
|
||||||
|
content = app.storage.customEmails.UserCreated.Content
|
||||||
|
if content == "" {
|
||||||
|
newEmail = true
|
||||||
|
msg, err = app.email.constructCreated("", "", "", Invite{}, app, true)
|
||||||
|
content = msg.text
|
||||||
|
} else {
|
||||||
|
variables = app.storage.customEmails.UserCreated.Variables
|
||||||
|
}
|
||||||
|
writeVars = func(variables []string) { app.storage.customEmails.UserCreated.Variables = variables }
|
||||||
|
// app.storage.customEmails.UserCreated = content
|
||||||
|
} else if id == "InviteExpiry" {
|
||||||
|
content = app.storage.customEmails.InviteExpiry.Content
|
||||||
|
if content == "" {
|
||||||
|
newEmail = true
|
||||||
|
msg, err = app.email.constructExpiry("", Invite{}, app, true)
|
||||||
|
content = msg.text
|
||||||
|
} else {
|
||||||
|
variables = app.storage.customEmails.InviteExpiry.Variables
|
||||||
|
}
|
||||||
|
writeVars = func(variables []string) { app.storage.customEmails.InviteExpiry.Variables = variables }
|
||||||
|
// app.storage.customEmails.InviteExpiry = content
|
||||||
|
} else if id == "PasswordReset" {
|
||||||
|
content = app.storage.customEmails.PasswordReset.Content
|
||||||
|
if content == "" {
|
||||||
|
newEmail = true
|
||||||
|
msg, err = app.email.constructReset(PasswordReset{}, app, true)
|
||||||
|
content = msg.text
|
||||||
|
} else {
|
||||||
|
variables = app.storage.customEmails.PasswordReset.Variables
|
||||||
|
}
|
||||||
|
writeVars = func(variables []string) { app.storage.customEmails.PasswordReset.Variables = variables }
|
||||||
|
// app.storage.customEmails.PasswordReset = content
|
||||||
|
} else if id == "UserDeleted" {
|
||||||
|
content = app.storage.customEmails.UserDeleted.Content
|
||||||
|
if content == "" {
|
||||||
|
newEmail = true
|
||||||
|
msg, err = app.email.constructDeleted("", app, true)
|
||||||
|
content = msg.text
|
||||||
|
} else {
|
||||||
|
variables = app.storage.customEmails.UserDeleted.Variables
|
||||||
|
}
|
||||||
|
writeVars = func(variables []string) { app.storage.customEmails.UserDeleted.Variables = variables }
|
||||||
|
// app.storage.customEmails.UserDeleted = content
|
||||||
|
} else if id == "InviteEmail" {
|
||||||
|
content = app.storage.customEmails.InviteEmail.Content
|
||||||
|
if content == "" {
|
||||||
|
newEmail = true
|
||||||
|
msg, err = app.email.constructInvite("", Invite{}, app, true)
|
||||||
|
content = msg.text
|
||||||
|
} else {
|
||||||
|
variables = app.storage.customEmails.InviteEmail.Variables
|
||||||
|
}
|
||||||
|
writeVars = func(variables []string) { app.storage.customEmails.InviteEmail.Variables = variables }
|
||||||
|
// app.storage.customEmails.InviteEmail = content
|
||||||
|
} else if id == "WelcomeEmail" {
|
||||||
|
content = app.storage.customEmails.WelcomeEmail.Content
|
||||||
|
if content == "" {
|
||||||
|
newEmail = true
|
||||||
|
msg, err = app.email.constructWelcome("", app, true)
|
||||||
|
content = msg.text
|
||||||
|
} else {
|
||||||
|
variables = app.storage.customEmails.WelcomeEmail.Variables
|
||||||
|
}
|
||||||
|
writeVars = func(variables []string) { app.storage.customEmails.WelcomeEmail.Variables = variables }
|
||||||
|
// app.storage.customEmails.WelcomeEmail = content
|
||||||
|
} else if id == "EmailConfirmation" {
|
||||||
|
content = app.storage.customEmails.EmailConfirmation.Content
|
||||||
|
if content == "" {
|
||||||
|
newEmail = true
|
||||||
|
msg, err = app.email.constructConfirmation("", "", "", app, true)
|
||||||
|
content = msg.text
|
||||||
|
} else {
|
||||||
|
variables = app.storage.customEmails.EmailConfirmation.Variables
|
||||||
|
}
|
||||||
|
writeVars = func(variables []string) { app.storage.customEmails.EmailConfirmation.Variables = variables }
|
||||||
|
// app.storage.customEmails.EmailConfirmation = content
|
||||||
|
} else {
|
||||||
|
respondBool(400, false, gc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
respondBool(500, false, gc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if newEmail {
|
||||||
|
variables = make([]string, strings.Count(content, "{"))
|
||||||
|
i := 0
|
||||||
|
found := false
|
||||||
|
buf := ""
|
||||||
|
for _, c := range content {
|
||||||
|
if !found && c != '{' && c != '}' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
buf += string(c)
|
||||||
|
if c == '}' {
|
||||||
|
found = false
|
||||||
|
variables[i] = buf
|
||||||
|
buf = ""
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeVars(variables)
|
||||||
|
}
|
||||||
|
if app.storage.storeCustomEmails() != nil {
|
||||||
|
respondBool(500, false, gc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gc.JSON(200, customEmail{Content: content, Variables: variables})
|
||||||
|
}
|
||||||
|
|
||||||
// @Summary Logout by deleting refresh token from cookies.
|
// @Summary Logout by deleting refresh token from cookies.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} boolResponse
|
// @Success 200 {object} boolResponse
|
||||||
@ -1291,7 +1524,7 @@ func (app *appContext) Logout(gc *gin.Context) {
|
|||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} langDTO
|
// @Success 200 {object} langDTO
|
||||||
// @Failure 500 {object} stringResponse
|
// @Failure 500 {object} stringResponse
|
||||||
// @Router /lang [get]
|
// @Router /lang/{page} [get]
|
||||||
// @tags Other
|
// @tags Other
|
||||||
func (app *appContext) GetLanguages(gc *gin.Context) {
|
func (app *appContext) GetLanguages(gc *gin.Context) {
|
||||||
page := gc.Param("page")
|
page := gc.Param("page")
|
||||||
|
12
config.go
12
config.go
@ -32,11 +32,12 @@ func (app *appContext) loadConfig() error {
|
|||||||
app.config.Section("jellyfin").Key("public_server").SetValue(app.config.Section("jellyfin").Key("public_server").MustString(app.config.Section("jellyfin").Key("server").String()))
|
app.config.Section("jellyfin").Key("public_server").SetValue(app.config.Section("jellyfin").Key("public_server").MustString(app.config.Section("jellyfin").Key("server").String()))
|
||||||
|
|
||||||
for _, key := range app.config.Section("files").Keys() {
|
for _, key := range app.config.Section("files").Keys() {
|
||||||
if key.Name() != "html_templates" {
|
if name := key.Name(); name != "html_templates" && name != "lang_files" {
|
||||||
|
fmt.Println(name)
|
||||||
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"} {
|
for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails"} {
|
||||||
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"))))
|
||||||
}
|
}
|
||||||
app.URLBase = strings.TrimSuffix(app.config.Section("ui").Key("url_base").MustString(""), "/")
|
app.URLBase = strings.TrimSuffix(app.config.Section("ui").Key("url_base").MustString(""), "/")
|
||||||
@ -63,8 +64,8 @@ func (app *appContext) loadConfig() error {
|
|||||||
app.config.Section("welcome_email").Key("email_html").SetValue(app.config.Section("welcome_email").Key("email_html").MustString("jfa-go:" + "welcome.html"))
|
app.config.Section("welcome_email").Key("email_html").SetValue(app.config.Section("welcome_email").Key("email_html").MustString("jfa-go:" + "welcome.html"))
|
||||||
app.config.Section("welcome_email").Key("email_text").SetValue(app.config.Section("welcome_email").Key("email_text").MustString("jfa-go:" + "welcome.txt"))
|
app.config.Section("welcome_email").Key("email_text").SetValue(app.config.Section("welcome_email").Key("email_text").MustString("jfa-go:" + "welcome.txt"))
|
||||||
|
|
||||||
app.config.Section("announcement_email").Key("email_html").SetValue(app.config.Section("announcement_email").Key("email_html").MustString("jfa-go:" + "announcement.html"))
|
app.config.Section("template_email").Key("email_html").SetValue(app.config.Section("template_email").Key("email_html").MustString("jfa-go:" + "template.html"))
|
||||||
app.config.Section("announcement_email").Key("email_text").SetValue(app.config.Section("announcement_email").Key("email_text").MustString("jfa-go:" + "announcement.txt"))
|
app.config.Section("template_email").Key("email_text").SetValue(app.config.Section("template_email").Key("email_text").MustString("jfa-go:" + "template.txt"))
|
||||||
|
|
||||||
app.config.Section("jellyfin").Key("version").SetValue(VERSION)
|
app.config.Section("jellyfin").Key("version").SetValue(VERSION)
|
||||||
app.config.Section("jellyfin").Key("device").SetValue("jfa-go")
|
app.config.Section("jellyfin").Key("device").SetValue("jfa-go")
|
||||||
@ -76,6 +77,9 @@ func (app *appContext) loadConfig() error {
|
|||||||
emailEnabled = true
|
emailEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.storage.customEmails_path = app.config.Section("files").Key("custom_emails").String()
|
||||||
|
app.storage.loadCustomEmails()
|
||||||
|
|
||||||
substituteStrings = app.config.Section("jellyfin").Key("substitute_jellyfin_strings").MustString("")
|
substituteStrings = app.config.Section("jellyfin").Key("substitute_jellyfin_strings").MustString("")
|
||||||
|
|
||||||
oldFormLang := app.config.Section("ui").Key("language").MustString("")
|
oldFormLang := app.config.Section("ui").Key("language").MustString("")
|
||||||
|
@ -853,6 +853,14 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"value": "",
|
"value": "",
|
||||||
"description": "The path to a directory which following the same form as the internal 'lang/' directory. See GitHub for more info."
|
"description": "The path to a directory which following the same form as the internal 'lang/' directory. See GitHub for more info."
|
||||||
|
},
|
||||||
|
"custom_emails": {
|
||||||
|
"name": "Custom email content",
|
||||||
|
"required": false,
|
||||||
|
"requires_restart": false,
|
||||||
|
"type": "text",
|
||||||
|
"value": "",
|
||||||
|
"description": "JSON file generated by program in settings, different from email_html/email_text. See wiki for more info."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
285
email.go
285
email.go
@ -18,7 +18,6 @@ import (
|
|||||||
jEmail "github.com/jordan-wright/email"
|
jEmail "github.com/jordan-wright/email"
|
||||||
"github.com/knz/strtime"
|
"github.com/knz/strtime"
|
||||||
"github.com/mailgun/mailgun-go/v4"
|
"github.com/mailgun/mailgun-go/v4"
|
||||||
stripmd "github.com/writeas/go-strip-markdown"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// implements email sending, right now via smtp or mailgun.
|
// implements email sending, right now via smtp or mailgun.
|
||||||
@ -212,36 +211,58 @@ func (emailer *Emailer) construct(app *appContext, section, keyFragment string,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructConfirmation(code, username, key string, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructConfirmation(code, username, key string, app *appContext, noSub bool) (*Email, error) {
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: app.config.Section("email_confirmation").Key("subject").MustString(emailer.lang.EmailConfirmation.get("title")),
|
subject: app.config.Section("email_confirmation").Key("subject").MustString(emailer.lang.EmailConfirmation.get("title")),
|
||||||
}
|
}
|
||||||
message := app.config.Section("email").Key("message").String()
|
|
||||||
inviteLink := app.config.Section("invite_emails").Key("url_base").String()
|
|
||||||
inviteLink = fmt.Sprintf("%s/%s?key=%s", inviteLink, code, key)
|
|
||||||
var err error
|
var err error
|
||||||
email.html, email.text, err = emailer.construct(app, "email_confirmation", "email_", map[string]interface{}{
|
template := map[string]interface{}{
|
||||||
"helloUser": emailer.lang.Strings.template("helloUser", tmpl{"username": username}),
|
|
||||||
"clickBelow": emailer.lang.EmailConfirmation.get("clickBelow"),
|
"clickBelow": emailer.lang.EmailConfirmation.get("clickBelow"),
|
||||||
"ifItWasNotYou": emailer.lang.Strings.get("ifItWasNotYou"),
|
"ifItWasNotYou": emailer.lang.Strings.get("ifItWasNotYou"),
|
||||||
"urlVal": inviteLink,
|
|
||||||
"confirmEmail": emailer.lang.EmailConfirmation.get("confirmEmail"),
|
"confirmEmail": emailer.lang.EmailConfirmation.get("confirmEmail"),
|
||||||
"message": message,
|
"message": "",
|
||||||
})
|
"username": username,
|
||||||
|
}
|
||||||
|
if noSub {
|
||||||
|
template["helloUser"] = emailer.lang.Strings.get("helloUser")
|
||||||
|
empty := []string{"confirmationURL"}
|
||||||
|
for _, v := range empty {
|
||||||
|
template[v] = "{" + v + "}"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message := app.config.Section("email").Key("message").String()
|
||||||
|
inviteLink := app.config.Section("invite_emails").Key("url_base").String()
|
||||||
|
inviteLink = fmt.Sprintf("%s/%s?key=%s", inviteLink, code, key)
|
||||||
|
template["helloUser"] = emailer.lang.Strings.template("helloUser", tmpl{"username": username})
|
||||||
|
template["confirmationURL"] = inviteLink
|
||||||
|
template["message"] = message
|
||||||
|
}
|
||||||
|
if app.storage.customEmails.EmailConfirmation.Enabled {
|
||||||
|
content := app.storage.customEmails.EmailConfirmation.Content
|
||||||
|
for _, v := range app.storage.customEmails.EmailConfirmation.Variables {
|
||||||
|
replaceWith, ok := template[v[1:len(v)-1]]
|
||||||
|
if ok {
|
||||||
|
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
email, err = emailer.constructTemplate(email.subject, content, app)
|
||||||
|
} else {
|
||||||
|
email.html, email.text, err = emailer.construct(app, "email_confirmation", "email_", template)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return email, nil
|
return email, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructAnnouncement(subject, md string, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructTemplate(subject, md string, app *appContext) (*Email, error) {
|
||||||
email := &Email{subject: subject}
|
email := &Email{subject: subject}
|
||||||
renderer := html.NewRenderer(html.RendererOptions{Flags: html.Smartypants})
|
renderer := html.NewRenderer(html.RendererOptions{Flags: html.Smartypants})
|
||||||
html := markdown.ToHTML([]byte(md), nil, renderer)
|
html := markdown.ToHTML([]byte(md), nil, renderer)
|
||||||
text := strings.TrimPrefix(strings.TrimSuffix(stripmd.Strip(md), "</p>"), "<p>")
|
text := stripMarkdown(md)
|
||||||
message := app.config.Section("email").Key("message").String()
|
message := app.config.Section("email").Key("message").String()
|
||||||
var err error
|
var err error
|
||||||
email.html, email.text, err = emailer.construct(app, "announcement_email", "email_", map[string]interface{}{
|
email.html, email.text, err = emailer.construct(app, "template_email", "email_", map[string]interface{}{
|
||||||
"text": template.HTML(html),
|
"text": template.HTML(html),
|
||||||
"plaintext": text,
|
"plaintext": text,
|
||||||
"message": message,
|
"message": message,
|
||||||
@ -252,7 +273,7 @@ func (emailer *Emailer) constructAnnouncement(subject, md string, app *appContex
|
|||||||
return email, nil
|
return email, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructInvite(code string, invite Invite, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructInvite(code string, invite Invite, app *appContext, noSub bool) (*Email, error) {
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: app.config.Section("email_confirmation").Key("subject").MustString(emailer.lang.InviteEmail.get("title")),
|
subject: app.config.Section("email_confirmation").Key("subject").MustString(emailer.lang.InviteEmail.get("title")),
|
||||||
}
|
}
|
||||||
@ -261,120 +282,251 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
|
|||||||
message := app.config.Section("email").Key("message").String()
|
message := app.config.Section("email").Key("message").String()
|
||||||
inviteLink := app.config.Section("invite_emails").Key("url_base").String()
|
inviteLink := app.config.Section("invite_emails").Key("url_base").String()
|
||||||
inviteLink = fmt.Sprintf("%s/%s", inviteLink, code)
|
inviteLink = fmt.Sprintf("%s/%s", inviteLink, code)
|
||||||
var err error
|
template := map[string]interface{}{
|
||||||
email.html, email.text, err = emailer.construct(app, "invite_emails", "email_", map[string]interface{}{
|
|
||||||
"hello": emailer.lang.InviteEmail.get("hello"),
|
"hello": emailer.lang.InviteEmail.get("hello"),
|
||||||
"youHaveBeenInvited": emailer.lang.InviteEmail.get("youHaveBeenInvited"),
|
"youHaveBeenInvited": emailer.lang.InviteEmail.get("youHaveBeenInvited"),
|
||||||
"toJoin": emailer.lang.InviteEmail.get("toJoin"),
|
"toJoin": emailer.lang.InviteEmail.get("toJoin"),
|
||||||
"inviteExpiry": emailer.lang.InviteEmail.template("inviteExpiry", tmpl{"date": d, "time": t, "expiresInMinutes": expiresIn}),
|
|
||||||
"linkButton": emailer.lang.InviteEmail.get("linkButton"),
|
"linkButton": emailer.lang.InviteEmail.get("linkButton"),
|
||||||
"invite_link": inviteLink,
|
"message": "",
|
||||||
"message": message,
|
"date": d,
|
||||||
})
|
"time": t,
|
||||||
|
"expiresInMinutes": expiresIn,
|
||||||
|
}
|
||||||
|
if noSub {
|
||||||
|
template["inviteExpiry"] = emailer.lang.InviteEmail.get("inviteExpiry")
|
||||||
|
empty := []string{"inviteURL"}
|
||||||
|
for _, v := range empty {
|
||||||
|
template[v] = "{" + v + "}"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
template["inviteExpiry"] = emailer.lang.InviteEmail.template("inviteExpiry", tmpl{"date": d, "time": t, "expiresInMinutes": expiresIn})
|
||||||
|
template["inviteURL"] = inviteLink
|
||||||
|
template["message"] = message
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if app.storage.customEmails.InviteEmail.Enabled {
|
||||||
|
content := app.storage.customEmails.InviteEmail.Content
|
||||||
|
for _, v := range app.storage.customEmails.InviteEmail.Variables {
|
||||||
|
replaceWith, ok := template[v[1:len(v)-1]]
|
||||||
|
if ok {
|
||||||
|
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
email, err = emailer.constructTemplate(email.subject, content, app)
|
||||||
|
} else {
|
||||||
|
email.html, email.text, err = emailer.construct(app, "invite_emails", "email_", template)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return email, nil
|
return email, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appContext, noSub bool) (*Email, error) {
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: emailer.lang.InviteExpiry.get("title"),
|
subject: emailer.lang.InviteExpiry.get("title"),
|
||||||
}
|
}
|
||||||
expiry := app.formatDatetime(invite.ValidTill)
|
expiry := app.formatDatetime(invite.ValidTill)
|
||||||
var err error
|
template := map[string]interface{}{
|
||||||
email.html, email.text, err = emailer.construct(app, "notifications", "expiry_", map[string]interface{}{
|
|
||||||
"inviteExpired": emailer.lang.InviteExpiry.get("inviteExpired"),
|
"inviteExpired": emailer.lang.InviteExpiry.get("inviteExpired"),
|
||||||
"expiredAt": emailer.lang.InviteExpiry.template("expiredAt", tmpl{"code": "\"" + code + "\"", "time": expiry}),
|
|
||||||
"notificationNotice": emailer.lang.InviteExpiry.get("notificationNotice"),
|
"notificationNotice": emailer.lang.InviteExpiry.get("notificationNotice"),
|
||||||
})
|
"code": "\"" + code + "\"",
|
||||||
|
"time": expiry,
|
||||||
|
}
|
||||||
|
if noSub {
|
||||||
|
template["expiredAt"] = emailer.lang.InviteExpiry.get("expiredAt")
|
||||||
|
} else {
|
||||||
|
template["expiredAt"] = emailer.lang.InviteExpiry.template("expiredAt", tmpl{"code": template["code"].(string), "time": template["time"].(string)})
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if app.storage.customEmails.InviteExpiry.Enabled {
|
||||||
|
content := app.storage.customEmails.InviteExpiry.Content
|
||||||
|
for _, v := range app.storage.customEmails.InviteExpiry.Variables {
|
||||||
|
replaceWith, ok := template[v[1:len(v)-1]]
|
||||||
|
if ok {
|
||||||
|
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
email, err = emailer.constructTemplate(email.subject, content, app)
|
||||||
|
} else {
|
||||||
|
email.html, email.text, err = emailer.construct(app, "notifications", "expiry_", template)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return email, nil
|
return email, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructCreated(code, username, address string, invite Invite, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructCreated(code, username, address string, invite Invite, app *appContext, noSub bool) (*Email, error) {
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: emailer.lang.UserCreated.get("title"),
|
subject: emailer.lang.UserCreated.get("title"),
|
||||||
}
|
}
|
||||||
created := app.formatDatetime(invite.Created)
|
template := map[string]interface{}{
|
||||||
var tplAddress string
|
"nameString": emailer.lang.Strings.get("name"),
|
||||||
if app.config.Section("email").Key("no_username").MustBool(false) {
|
"addressString": emailer.lang.Strings.get("emailAddress"),
|
||||||
tplAddress = "n/a"
|
"timeString": emailer.lang.UserCreated.get("time"),
|
||||||
|
"notificationNotice": "",
|
||||||
|
"code": "\"" + code + "\"",
|
||||||
|
}
|
||||||
|
if noSub {
|
||||||
|
template["aUserWasCreated"] = emailer.lang.UserCreated.get("aUserWasCreated")
|
||||||
|
empty := []string{"name", "address", "time"}
|
||||||
|
for _, v := range empty {
|
||||||
|
template[v] = "{" + v + "}"
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tplAddress = address
|
created := app.formatDatetime(invite.Created)
|
||||||
|
var tplAddress string
|
||||||
|
if app.config.Section("email").Key("no_username").MustBool(false) {
|
||||||
|
tplAddress = "n/a"
|
||||||
|
} else {
|
||||||
|
tplAddress = address
|
||||||
|
}
|
||||||
|
template["aUserWasCreated"] = emailer.lang.UserCreated.template("aUserWasCreated", tmpl{"code": template["code"].(string)})
|
||||||
|
template["name"] = username
|
||||||
|
template["address"] = tplAddress
|
||||||
|
template["time"] = created
|
||||||
|
template["notificationNotice"] = emailer.lang.UserCreated.get("notificationNotice")
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
email.html, email.text, err = emailer.construct(app, "notifications", "created_", map[string]interface{}{
|
if app.storage.customEmails.UserCreated.Enabled {
|
||||||
"aUserWasCreated": emailer.lang.UserCreated.template("aUserWasCreated", tmpl{"code": "\"" + code + "\""}),
|
content := app.storage.customEmails.UserCreated.Content
|
||||||
"name": emailer.lang.Strings.get("name"),
|
for _, v := range app.storage.customEmails.UserCreated.Variables {
|
||||||
"address": emailer.lang.Strings.get("emailAddress"),
|
replaceWith, ok := template[v[1:len(v)-1]]
|
||||||
"time": emailer.lang.UserCreated.get("time"),
|
if ok {
|
||||||
"nameVal": username,
|
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||||
"addressVal": tplAddress,
|
}
|
||||||
"timeVal": created,
|
}
|
||||||
"notificationNotice": emailer.lang.UserCreated.get("notificationNotice"),
|
email, err = emailer.constructTemplate(email.subject, content, app)
|
||||||
})
|
} else {
|
||||||
|
email.html, email.text, err = emailer.construct(app, "notifications", "created_", template)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return email, nil
|
return email, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext, noSub bool) (*Email, error) {
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: app.config.Section("password_resets").Key("subject").MustString(emailer.lang.PasswordReset.get("title")),
|
subject: app.config.Section("password_resets").Key("subject").MustString(emailer.lang.PasswordReset.get("title")),
|
||||||
}
|
}
|
||||||
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
|
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
|
||||||
message := app.config.Section("email").Key("message").String()
|
message := app.config.Section("email").Key("message").String()
|
||||||
var err error
|
template := map[string]interface{}{
|
||||||
email.html, email.text, err = emailer.construct(app, "password_resets", "email_", map[string]interface{}{
|
|
||||||
"helloUser": emailer.lang.Strings.template("helloUser", tmpl{"username": pwr.Username}),
|
|
||||||
"someoneHasRequestedReset": emailer.lang.PasswordReset.get("someoneHasRequestedReset"),
|
"someoneHasRequestedReset": emailer.lang.PasswordReset.get("someoneHasRequestedReset"),
|
||||||
"ifItWasYou": emailer.lang.PasswordReset.get("ifItWasYou"),
|
"ifItWasYou": emailer.lang.PasswordReset.get("ifItWasYou"),
|
||||||
"codeExpiry": emailer.lang.PasswordReset.template("codeExpiry", tmpl{"date": d, "time": t, "expiresInMinutes": expiresIn}),
|
|
||||||
"ifItWasNotYou": emailer.lang.Strings.get("ifItWasNotYou"),
|
"ifItWasNotYou": emailer.lang.Strings.get("ifItWasNotYou"),
|
||||||
"pin": emailer.lang.PasswordReset.get("pin"),
|
"pinString": emailer.lang.PasswordReset.get("pin"),
|
||||||
"pinVal": pwr.Pin,
|
"message": "",
|
||||||
"message": message,
|
"username": pwr.Username,
|
||||||
})
|
"date": d,
|
||||||
|
"time": t,
|
||||||
|
"expiresInMinutes": expiresIn,
|
||||||
|
}
|
||||||
|
if noSub {
|
||||||
|
template["helloUser"] = emailer.lang.Strings.get("helloUser")
|
||||||
|
template["codeExpiry"] = emailer.lang.PasswordReset.get("codeExpiry")
|
||||||
|
empty := []string{"pin"}
|
||||||
|
for _, v := range empty {
|
||||||
|
template[v] = "{" + v + "}"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
template["helloUser"] = emailer.lang.Strings.template("helloUser", tmpl{"username": pwr.Username})
|
||||||
|
template["codeExpiry"] = emailer.lang.PasswordReset.template("codeExpiry", tmpl{"date": d, "time": t, "expiresInMinutes": expiresIn})
|
||||||
|
template["pin"] = pwr.Pin
|
||||||
|
template["message"] = message
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if app.storage.customEmails.PasswordReset.Enabled {
|
||||||
|
content := app.storage.customEmails.PasswordReset.Content
|
||||||
|
for _, v := range app.storage.customEmails.PasswordReset.Variables {
|
||||||
|
replaceWith, ok := template[v[1:len(v)-1]]
|
||||||
|
if ok {
|
||||||
|
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
email, err = emailer.constructTemplate(email.subject, content, app)
|
||||||
|
} else {
|
||||||
|
email.html, email.text, err = emailer.construct(app, "password_resets", "email_", template)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return email, nil
|
return email, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructDeleted(reason string, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructDeleted(reason string, app *appContext, noSub bool) (*Email, error) {
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: app.config.Section("deletion").Key("subject").MustString(emailer.lang.UserDeleted.get("title")),
|
subject: app.config.Section("deletion").Key("subject").MustString(emailer.lang.UserDeleted.get("title")),
|
||||||
}
|
}
|
||||||
var err error
|
template := map[string]interface{}{
|
||||||
email.html, email.text, err = emailer.construct(app, "deletion", "email_", map[string]interface{}{
|
|
||||||
"yourAccountWasDeleted": emailer.lang.UserDeleted.get("yourAccountWasDeleted"),
|
"yourAccountWasDeleted": emailer.lang.UserDeleted.get("yourAccountWasDeleted"),
|
||||||
"reason": emailer.lang.UserDeleted.get("reason"),
|
"reasonString": emailer.lang.UserDeleted.get("reason"),
|
||||||
"reasonVal": reason,
|
"message": "",
|
||||||
})
|
}
|
||||||
|
if noSub {
|
||||||
|
empty := []string{"reason"}
|
||||||
|
for _, v := range empty {
|
||||||
|
template[v] = "{" + v + "}"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
template["reason"] = reason
|
||||||
|
template["message"] = app.config.Section("email").Key("message").String()
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if app.storage.customEmails.UserDeleted.Enabled {
|
||||||
|
content := app.storage.customEmails.UserDeleted.Content
|
||||||
|
for _, v := range app.storage.customEmails.UserDeleted.Variables {
|
||||||
|
replaceWith, ok := template[v[1:len(v)-1]]
|
||||||
|
if ok {
|
||||||
|
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
email, err = emailer.constructTemplate(email.subject, content, app)
|
||||||
|
} else {
|
||||||
|
email.html, email.text, err = emailer.construct(app, "deletion", "email_", template)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return email, nil
|
return email, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructWelcome(username string, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructWelcome(username string, app *appContext, noSub bool) (*Email, error) {
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: app.config.Section("welcome_email").Key("subject").MustString(emailer.lang.WelcomeEmail.get("title")),
|
subject: app.config.Section("welcome_email").Key("subject").MustString(emailer.lang.WelcomeEmail.get("title")),
|
||||||
}
|
}
|
||||||
|
template := map[string]interface{}{
|
||||||
|
"welcome": emailer.lang.WelcomeEmail.get("welcome"),
|
||||||
|
"youCanLoginWith": emailer.lang.WelcomeEmail.get("youCanLoginWith"),
|
||||||
|
"jellyfinURLString": emailer.lang.WelcomeEmail.get("jellyfinURL"),
|
||||||
|
"usernameString": emailer.lang.Strings.get("username"),
|
||||||
|
"message": "",
|
||||||
|
}
|
||||||
|
if noSub {
|
||||||
|
empty := []string{"jellyfinURL", "username"}
|
||||||
|
for _, v := range empty {
|
||||||
|
template[v] = "{" + v + "}"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
template["jellyfinURL"] = app.config.Section("jellyfin").Key("public_server").String()
|
||||||
|
template["username"] = username
|
||||||
|
template["message"] = app.config.Section("email").Key("message").String()
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
email.html, email.text, err = emailer.construct(app, "welcome_email", "email_", map[string]interface{}{
|
if app.storage.customEmails.WelcomeEmail.Enabled {
|
||||||
"welcome": emailer.lang.WelcomeEmail.get("welcome"),
|
content := app.storage.customEmails.WelcomeEmail.Content
|
||||||
"youCanLoginWith": emailer.lang.WelcomeEmail.get("youCanLoginWith"),
|
for _, v := range app.storage.customEmails.WelcomeEmail.Variables {
|
||||||
"jellyfinURL": emailer.lang.WelcomeEmail.get("jellyfinURL"),
|
replaceWith, ok := template[v[1:len(v)-1]]
|
||||||
"jellyfinURLVal": app.config.Section("jellyfin").Key("public_server").String(),
|
if ok {
|
||||||
"username": emailer.lang.Strings.get("username"),
|
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||||
"usernameVal": username,
|
}
|
||||||
"message": app.config.Section("email").Key("message").String(),
|
}
|
||||||
})
|
email, err = emailer.constructTemplate(email.subject, content, app)
|
||||||
|
} else {
|
||||||
|
email.html, email.text, err = emailer.construct(app, "welcome_email", "email_", template)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -383,5 +535,6 @@ func (emailer *Emailer) constructWelcome(username string, app *appContext) (*Ema
|
|||||||
|
|
||||||
// calls the send method in the underlying emailClient.
|
// calls the send method in the underlying emailClient.
|
||||||
func (emailer *Emailer) send(email *Email, address ...string) error {
|
func (emailer *Emailer) send(email *Email, address ...string) error {
|
||||||
|
fmt.Printf("%+v\n", email)
|
||||||
return emailer.sender.send(emailer.fromName, emailer.fromAddr, email, address...)
|
return emailer.sender.send(emailer.fromName, emailer.fromAddr, email, address...)
|
||||||
}
|
}
|
||||||
|
8
lang.go
8
lang.go
@ -120,14 +120,18 @@ func (ls *setupLangs) getOptions() [][2]string {
|
|||||||
type langSection map[string]string
|
type langSection map[string]string
|
||||||
type tmpl map[string]string
|
type tmpl map[string]string
|
||||||
|
|
||||||
func (el langSection) template(field string, vals tmpl) string {
|
func templateString(text string, vals tmpl) string {
|
||||||
text := el.get(field)
|
|
||||||
for key, val := range vals {
|
for key, val := range vals {
|
||||||
text = strings.ReplaceAll(text, "{"+key+"}", val)
|
text = strings.ReplaceAll(text, "{"+key+"}", val)
|
||||||
}
|
}
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (el langSection) template(field string, vals tmpl) string {
|
||||||
|
text := el.get(field)
|
||||||
|
return templateString(text, vals)
|
||||||
|
}
|
||||||
|
|
||||||
func (el langSection) format(field string, vals ...string) string {
|
func (el langSection) format(field string, vals ...string) string {
|
||||||
text := el.get(field)
|
text := el.get(field)
|
||||||
for _, val := range vals {
|
for _, val := range vals {
|
||||||
|
@ -8,21 +8,21 @@
|
|||||||
},
|
},
|
||||||
"userCreated": {
|
"userCreated": {
|
||||||
"title": "Mitteilung: Benutzer erstellt",
|
"title": "Mitteilung: Benutzer erstellt",
|
||||||
"aUserWasCreated": "Ein Benutzer wurde unter Verwendung des Codes {n] erstellt.",
|
"aUserWasCreated": "Ein Benutzer wurde unter Verwendung des Codes {code} erstellt.",
|
||||||
"time": "Zeit",
|
"time": "Zeit",
|
||||||
"notificationNotice": "Hinweis: Benachrichtigungs-E-Mails können auf dem Administrator-Dashboard umgeschalten werden."
|
"notificationNotice": "Hinweis: Benachrichtigungs-E-Mails können auf dem Administrator-Dashboard umgeschalten werden."
|
||||||
},
|
},
|
||||||
"inviteExpiry": {
|
"inviteExpiry": {
|
||||||
"title": "Mitteilung: Invite abgelaufen",
|
"title": "Mitteilung: Invite abgelaufen",
|
||||||
"inviteExpired": "Invite abgelaufen.",
|
"inviteExpired": "Invite abgelaufen.",
|
||||||
"expiredAt": "Code {code} lief um {code} ab.",
|
"expiredAt": "Code {code} lief um {time} ab.",
|
||||||
"notificationNotice": "Hinweis: Benachrichtigungs-E-Mails können auf dem Administrator-Dashboard umgeschalten werden."
|
"notificationNotice": "Hinweis: Benachrichtigungs-E-Mails können auf dem Administrator-Dashboard umgeschalten werden."
|
||||||
},
|
},
|
||||||
"passwordReset": {
|
"passwordReset": {
|
||||||
"title": "Passwortzurücksetzung angefordert - Jellyfin",
|
"title": "Passwortzurücksetzung angefordert - Jellyfin",
|
||||||
"someoneHasRequestedReset": "Jemand hat vor kurzem eine Passwortzurücksetzung auf Jellyfin angefordert.",
|
"someoneHasRequestedReset": "Jemand hat vor kurzem eine Passwortzurücksetzung auf Jellyfin angefordert.",
|
||||||
"ifItWasYou": "Wenn du das warst, gib die PIN unten in die Eingabeaufforderung ein.",
|
"ifItWasYou": "Wenn du das warst, gib die PIN unten in die Eingabeaufforderung ein.",
|
||||||
"codeExpiry": "Der Code wird am {time}, um [n} UTC ablaufen, was in {date} ist.",
|
"codeExpiry": "Der Code wird am {date}, um {time} UTC ablaufen, was in {expiresInMinutes} ist.",
|
||||||
"pin": "PIN"
|
"pin": "PIN"
|
||||||
},
|
},
|
||||||
"userDeleted": {
|
"userDeleted": {
|
||||||
@ -35,7 +35,7 @@
|
|||||||
"hello": "Hallo",
|
"hello": "Hallo",
|
||||||
"youHaveBeenInvited": "Du wurdest zu Jellyfin eingeladen.",
|
"youHaveBeenInvited": "Du wurdest zu Jellyfin eingeladen.",
|
||||||
"toJoin": "Um beizutreten, folge dem untenstehenden Link.",
|
"toJoin": "Um beizutreten, folge dem untenstehenden Link.",
|
||||||
"inviteExpiry": "Dieser Invite wird am {time}; um {expiresInMinutes} ablaufen, was in {date} ist, also handle schnell.",
|
"inviteExpiry": "Dieser Invite wird am {date}; um {time} ablaufen, was in {expiresInMinutes} ist, also handle schnell.",
|
||||||
"linkButton": "Richte dein Konto ein"
|
"linkButton": "Richte dein Konto ein"
|
||||||
},
|
},
|
||||||
"welcomeEmail": {
|
"welcomeEmail": {
|
||||||
|
@ -7,18 +7,21 @@
|
|||||||
"helloUser": "Hi {username},"
|
"helloUser": "Hi {username},"
|
||||||
},
|
},
|
||||||
"userCreated": {
|
"userCreated": {
|
||||||
|
"name": "User creation",
|
||||||
"title": "Notice: User created",
|
"title": "Notice: User created",
|
||||||
"aUserWasCreated": "A user was created using code {code}.",
|
"aUserWasCreated": "A user was created using code {code}.",
|
||||||
"time": "Time",
|
"time": "Time",
|
||||||
"notificationNotice": "Note: Notification emails can be toggled on the admin dashboard."
|
"notificationNotice": "Note: Notification emails can be toggled on the admin dashboard."
|
||||||
},
|
},
|
||||||
"inviteExpiry": {
|
"inviteExpiry": {
|
||||||
|
"name": "Invite expiry",
|
||||||
"title": "Notice: Invite expired",
|
"title": "Notice: Invite expired",
|
||||||
"inviteExpired": "Invite expired.",
|
"inviteExpired": "Invite expired.",
|
||||||
"expiredAt": "Code {code} expired at {time}.",
|
"expiredAt": "Code {code} expired at {time}.",
|
||||||
"notificationNotice": "Note: Notification emails can be toggled on the admin dashboard."
|
"notificationNotice": "Note: Notification emails can be toggled on the admin dashboard."
|
||||||
},
|
},
|
||||||
"passwordReset": {
|
"passwordReset": {
|
||||||
|
"name": "Password reset",
|
||||||
"title": "Password reset requested - Jellyfin",
|
"title": "Password reset requested - Jellyfin",
|
||||||
"someoneHasRequestedReset": "Someone has recently requested a password reset on Jellyfin.",
|
"someoneHasRequestedReset": "Someone has recently requested a password reset on Jellyfin.",
|
||||||
"ifItWasYou": "If this was you, enter the pin below into the prompt.",
|
"ifItWasYou": "If this was you, enter the pin below into the prompt.",
|
||||||
@ -26,11 +29,13 @@
|
|||||||
"pin": "PIN"
|
"pin": "PIN"
|
||||||
},
|
},
|
||||||
"userDeleted": {
|
"userDeleted": {
|
||||||
|
"name": "User deletion",
|
||||||
"title": "Your account was deleted - Jellyfin",
|
"title": "Your account was deleted - Jellyfin",
|
||||||
"yourAccountWasDeleted": "Your Jellyfin account was deleted.",
|
"yourAccountWasDeleted": "Your Jellyfin account was deleted.",
|
||||||
"reason": "Reason"
|
"reason": "Reason"
|
||||||
},
|
},
|
||||||
"inviteEmail": {
|
"inviteEmail": {
|
||||||
|
"name": "Invite email",
|
||||||
"title": "Invite - Jellyfin",
|
"title": "Invite - Jellyfin",
|
||||||
"hello": "Hi",
|
"hello": "Hi",
|
||||||
"youHaveBeenInvited": "You've been invited to Jellyfin.",
|
"youHaveBeenInvited": "You've been invited to Jellyfin.",
|
||||||
@ -39,12 +44,14 @@
|
|||||||
"linkButton": "Setup your account"
|
"linkButton": "Setup your account"
|
||||||
},
|
},
|
||||||
"welcomeEmail": {
|
"welcomeEmail": {
|
||||||
|
"name": "Welcome email",
|
||||||
"title": "Welcome to Jellyfin",
|
"title": "Welcome to Jellyfin",
|
||||||
"welcome": "Welcome to Jellyfin!",
|
"welcome": "Welcome to Jellyfin!",
|
||||||
"youCanLoginWith": "You can login with the details below",
|
"youCanLoginWith": "You can login with the details below",
|
||||||
"jellyfinURL": "URL"
|
"jellyfinURL": "URL"
|
||||||
},
|
},
|
||||||
"emailConfirmation": {
|
"emailConfirmation": {
|
||||||
|
"name": "Confirmation email",
|
||||||
"title": "Confirm your email - Jellyfin",
|
"title": "Confirm your email - Jellyfin",
|
||||||
"clickBelow": "Click the link below to confirm your email address and start using Jellyfin.",
|
"clickBelow": "Click the link below to confirm your email address and start using Jellyfin.",
|
||||||
"confirmEmail": "Confirm Email"
|
"confirmEmail": "Confirm Email"
|
||||||
|
@ -64,7 +64,7 @@
|
|||||||
<p>{{ .clickBelow }}</p>
|
<p>{{ .clickBelow }}</p>
|
||||||
<p>{{ .ifItWasNotYou }}</p>
|
<p>{{ .ifItWasNotYou }}</p>
|
||||||
</mj-text>
|
</mj-text>
|
||||||
<mj-button mj-class="blue bold" href="{{ .urlVal }}">{{ .confirmEmail }}</mj-button>
|
<mj-button mj-class="blue bold" href="{{ .confirmationURL }}">{{ .confirmEmail }}</mj-button>
|
||||||
</mj-column>
|
</mj-column>
|
||||||
</mj-section>
|
</mj-section>
|
||||||
<mj-section mj-class="bg2">
|
<mj-section mj-class="bg2">
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
{{ .clickBelow }}
|
{{ .clickBelow }}
|
||||||
{{ .ifItWasNotYou }}
|
{{ .ifItWasNotYou }}
|
||||||
|
|
||||||
{{ .urlVal }}
|
{{ .confirmationURL }}
|
||||||
|
|
||||||
{{ .message }}
|
{{ .message }}
|
||||||
|
@ -64,14 +64,14 @@
|
|||||||
</mj-text>
|
</mj-text>
|
||||||
<mj-table mj-class="text" container-background-color="#242424">
|
<mj-table mj-class="text" container-background-color="#242424">
|
||||||
<tr style="text-align: left;">
|
<tr style="text-align: left;">
|
||||||
<th>{{ .name }}</th>
|
<th>{{ .nameString }}</th>
|
||||||
<th>{{ .address }}</th>
|
<th>{{ .addressString }}</th>
|
||||||
<th>{{ .time }}</th>
|
<th>{{ .timeString }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="font-style: italic; text-align: left; color: rgb(153,153,153);">
|
<tr style="font-style: italic; text-align: left; color: rgb(153,153,153);">
|
||||||
<th>{{ .nameVal }}</th>
|
<th>{{ .name }}</th>
|
||||||
<th>{{ .addressVal }}</th>
|
<th>{{ .address }}</th>
|
||||||
<th>{{ .timeVal }}</th>
|
<th>{{ .time }}</th>
|
||||||
</mj-table>
|
</mj-table>
|
||||||
</mj-column>
|
</mj-column>
|
||||||
</mj-section>
|
</mj-section>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{{ .aUserWasCreated }}
|
{{ .aUserWasCreated }}
|
||||||
|
|
||||||
{{ .name }}: {{ .nameVal }}
|
{{ .nameString }}: {{ .name }}
|
||||||
{{ .address }}: {{ .addressVal }}
|
{{ .addressString }}: {{ .address }}
|
||||||
{{ .time }}: {{ .timeVal }}
|
{{ .timeString }}: {{ .time }}
|
||||||
|
|
||||||
{{ .notificationNotice }}
|
{{ .notificationNotice }}
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
<mj-column>
|
<mj-column>
|
||||||
<mj-text mj-class="text" font-size="16px" font-family="Noto Sans, Helvetica, Arial, sans-serif">
|
<mj-text mj-class="text" font-size="16px" font-family="Noto Sans, Helvetica, Arial, sans-serif">
|
||||||
<h3>{{ .yourAccountWasDeleted }}</h3>
|
<h3>{{ .yourAccountWasDeleted }}</h3>
|
||||||
<p>{{ .reason }}: <i>{{ .reasonVal }}</i></p>
|
<p>{{ .reasonString }}: <i>{{ .reason }}</i></p>
|
||||||
</mj-text>
|
</mj-text>
|
||||||
</mj-column>
|
</mj-column>
|
||||||
</mj-section>
|
</mj-section>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{{ .yourAccountWasDeleted }}
|
{{ .yourAccountWasDeleted }}
|
||||||
{{ .reason }}: {{ .reasonVal }}
|
{{ .reasonString }}: {{ .reason }}
|
||||||
|
|
||||||
{{ .message }}
|
{{ .message }}
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
<p>{{ .codeExpiry }}</p>
|
<p>{{ .codeExpiry }}</p>
|
||||||
<p>{{ .ifItWasNotYou }}</p>
|
<p>{{ .ifItWasNotYou }}</p>
|
||||||
</mj-text>
|
</mj-text>
|
||||||
<mj-button mj-class="blue bold"><mj-raw>{{ .pinVal }}</mj-raw></mj-button>
|
<mj-button mj-class="blue bold"><mj-raw>{{ .pin }}</mj-raw></mj-button>
|
||||||
</mj-column>
|
</mj-column>
|
||||||
</mj-section>
|
</mj-section>
|
||||||
<mj-section mj-class="bg2">
|
<mj-section mj-class="bg2">
|
||||||
|
@ -5,6 +5,6 @@
|
|||||||
{{ .codeExpiry }}
|
{{ .codeExpiry }}
|
||||||
{{ .ifItWasNotYou }}
|
{{ .ifItWasNotYou }}
|
||||||
|
|
||||||
{{ .pin }}: {{ .pinVal }}
|
{{ .pinString }}: {{ .pin }}
|
||||||
|
|
||||||
{{ .message }}
|
{{ .message }}
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
<p>{{ .toJoin }}</p>
|
<p>{{ .toJoin }}</p>
|
||||||
<p>{{ .inviteExpiry }}</p>
|
<p>{{ .inviteExpiry }}</p>
|
||||||
</mj-text>
|
</mj-text>
|
||||||
<mj-button mj-class="blue bold" href="{{ .invite_link }}">{{ .linkButton }}</mj-button>
|
<mj-button mj-class="blue bold" href="{{ .inviteURL }}">{{ .linkButton }}</mj-button>
|
||||||
</mj-column>
|
</mj-column>
|
||||||
</mj-section>
|
</mj-section>
|
||||||
<mj-section mj-class="bg2">
|
<mj-section mj-class="bg2">
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
{{ .toJoin }}
|
{{ .toJoin }}
|
||||||
{{ .inviteExpiry }}
|
{{ .inviteExpiry }}
|
||||||
|
|
||||||
{{ .invite_link }}
|
{{ .inviteURL }}
|
||||||
|
|
||||||
{{ .message }}
|
{{ .message }}
|
||||||
|
@ -62,8 +62,8 @@
|
|||||||
<mj-text mj-class="text" font-size="16px" font-family="Noto Sans, Helvetica, Arial, sans-serif">
|
<mj-text mj-class="text" font-size="16px" font-family="Noto Sans, Helvetica, Arial, sans-serif">
|
||||||
<h3>{{ .welcome }}</h3>
|
<h3>{{ .welcome }}</h3>
|
||||||
<p>{{ .youCanLoginWith }}:</p>
|
<p>{{ .youCanLoginWith }}:</p>
|
||||||
{{ .jellyfinURL }}: <a href="{{ .jellyfinURLVal }}">{{ .jellyfinURLVal }}</a>
|
{{ .jellyfinURLString }}: <a href="{{ .jellyfinURLVal }}">{{ .jellyfinURL }}</a>
|
||||||
<p>{{ .username }}: <i>{{ .usernameVal }}</i></p>
|
<p>{{ .usernameString }}: <i>{{ .username }}</i></p>
|
||||||
</mj-text>
|
</mj-text>
|
||||||
</mj-column>
|
</mj-column>
|
||||||
</mj-section>
|
</mj-section>
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
{{ .youCanLoginWith }}:
|
{{ .youCanLoginWith }}:
|
||||||
|
|
||||||
{{ .jellyfinURL }}: {{ .jellyfinURLVal }}
|
{{ .jellyfinURLString }}: {{ .jellyfinURL }}
|
||||||
{{ .username }}: {{ .usernameVal }}
|
{{ .usernameString }}: {{ .username }}
|
||||||
|
|
||||||
|
|
||||||
{{ .message }}
|
{{ .message }}
|
||||||
|
@ -174,3 +174,9 @@ type settings struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type langDTO map[string]string
|
type langDTO map[string]string
|
||||||
|
|
||||||
|
type emailListDTO map[string]string
|
||||||
|
|
||||||
|
type emailSetDTO struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
@ -81,7 +81,7 @@ func pwrMonitor(app *appContext, watcher *fsnotify.Watcher) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
address = addr.(string)
|
address = addr.(string)
|
||||||
msg, err := app.email.constructReset(pwr, app)
|
msg, err := app.email.constructReset(pwr, app, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.err.Printf("Failed to construct password reset email for %s", pwr.Username)
|
app.err.Printf("Failed to construct password reset email for %s", pwr.Username)
|
||||||
app.debug.Printf("%s: Error: %s", pwr.Username, err)
|
app.debug.Printf("%s: Error: %s", pwr.Username, err)
|
||||||
|
@ -139,6 +139,10 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
|||||||
// api.POST(p + "/setDefaults", app.SetDefaults)
|
// api.POST(p + "/setDefaults", app.SetDefaults)
|
||||||
api.POST(p+"/users/settings", app.ApplySettings)
|
api.POST(p+"/users/settings", app.ApplySettings)
|
||||||
api.POST(p+"/users/announce", app.Announce)
|
api.POST(p+"/users/announce", app.Announce)
|
||||||
|
api.GET(p+"/config/emails", app.GetEmails)
|
||||||
|
api.GET(p+"/config/emails/:id", app.GetEmail)
|
||||||
|
api.POST(p+"/config/emails/:id", app.SetEmail)
|
||||||
|
api.POST(p+"/config/emails/:id/:state", app.SetEmailState)
|
||||||
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)
|
||||||
|
43
storage.go
43
storage.go
@ -14,15 +14,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
timePattern string
|
timePattern string
|
||||||
invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path, profiles_path string
|
invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path, profiles_path, customEmails_path string
|
||||||
invites Invites
|
invites Invites
|
||||||
profiles map[string]Profile
|
profiles map[string]Profile
|
||||||
defaultProfile string
|
defaultProfile string
|
||||||
emails, displayprefs, ombi_template map[string]interface{}
|
emails, displayprefs, ombi_template map[string]interface{}
|
||||||
policy mediabrowser.Policy
|
customEmails customEmails
|
||||||
configuration mediabrowser.Configuration
|
policy mediabrowser.Policy
|
||||||
lang Lang
|
configuration mediabrowser.Configuration
|
||||||
|
lang Lang
|
||||||
|
}
|
||||||
|
|
||||||
|
type customEmails struct {
|
||||||
|
UserCreated customEmail `json:"userCreated"`
|
||||||
|
InviteExpiry customEmail `json:"inviteExpiry"`
|
||||||
|
PasswordReset customEmail `json:"passwordReset"`
|
||||||
|
UserDeleted customEmail `json:"userDeleted"`
|
||||||
|
InviteEmail customEmail `json:"inviteEmail"`
|
||||||
|
WelcomeEmail customEmail `json:"welcomeEmail"`
|
||||||
|
EmailConfirmation customEmail `json:"emailConfirmation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type customEmail struct {
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Variables []string `json:"variables,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
||||||
@ -394,6 +411,14 @@ func (st *Storage) storeEmails() error {
|
|||||||
return storeJSON(st.emails_path, st.emails)
|
return storeJSON(st.emails_path, st.emails)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (st *Storage) loadCustomEmails() error {
|
||||||
|
return loadJSON(st.customEmails_path, &st.customEmails)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *Storage) storeCustomEmails() error {
|
||||||
|
return storeJSON(st.customEmails_path, st.customEmails)
|
||||||
|
}
|
||||||
|
|
||||||
func (st *Storage) loadPolicy() error {
|
func (st *Storage) loadPolicy() error {
|
||||||
return loadJSON(st.policy_path, &st.policy)
|
return loadJSON(st.policy_path, &st.policy)
|
||||||
}
|
}
|
||||||
|
53
stripmd.go
Normal file
53
stripmd.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
stripmd "github.com/writeas/go-strip-markdown"
|
||||||
|
)
|
||||||
|
|
||||||
|
func stripMarkdown(md string) string {
|
||||||
|
// Search for markdown-formatted urls, and replace them with just the url, then use a library to strip any traces of markdown. You'll need some eyebleach after this.
|
||||||
|
foundOpenSquare := false
|
||||||
|
openSquare := -1
|
||||||
|
openBracket := -1
|
||||||
|
closeBracket := -1
|
||||||
|
openSquares := []int{}
|
||||||
|
closeBrackets := []int{}
|
||||||
|
links := []string{}
|
||||||
|
foundOpen := false
|
||||||
|
for i, c := range md {
|
||||||
|
if !foundOpenSquare && !foundOpen && c != '[' && c != ']' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c == '[' && md[i-1] != '!' {
|
||||||
|
foundOpenSquare = true
|
||||||
|
openSquare = i
|
||||||
|
} else if c == ']' {
|
||||||
|
if md[i+1] == '(' {
|
||||||
|
foundOpenSquare = false
|
||||||
|
foundOpen = true
|
||||||
|
openBracket = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if c == ')' {
|
||||||
|
closeBracket = i
|
||||||
|
openSquares = append(openSquares, openSquare)
|
||||||
|
closeBrackets = append(closeBrackets, closeBracket)
|
||||||
|
links = append(links, md[openBracket+1:closeBracket])
|
||||||
|
openBracket = -1
|
||||||
|
closeBracket = -1
|
||||||
|
openSquare = -1
|
||||||
|
foundOpenSquare = false
|
||||||
|
foundOpen = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fullLinks := make([]string, len(openSquares))
|
||||||
|
for i := range openSquares {
|
||||||
|
fullLinks[i] = md[openSquares[i] : closeBrackets[i]+1]
|
||||||
|
}
|
||||||
|
for i, _ := range openSquares {
|
||||||
|
md = strings.Replace(md, fullLinks[i], links[i], 1)
|
||||||
|
}
|
||||||
|
return strings.TrimPrefix(strings.TrimSuffix(stripmd.Strip(md), "</p>"), "<p>")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user