mirror of
https://github.com/hrfee/jfa-go.git
synced 2026-01-18 16:47:42 +01:00
emails: fix and confirm function of all emails
both custom and standard emails tested, quite a few fixes made, including to an old bug with admin notifs.
This commit is contained in:
@@ -124,7 +124,7 @@ func (app *appContext) deleteExpiredInvite(data Invite) {
|
||||
|
||||
func (app *appContext) sendAdminExpiryNotification(data Invite) *sync.WaitGroup {
|
||||
notify := data.Notify
|
||||
if !emailEnabled || !app.config.Section("notifications").Key("enabled").MustBool(false) || len(notify) != 0 {
|
||||
if !emailEnabled || !app.config.Section("notifications").Key("enabled").MustBool(false) || len(notify) == 0 {
|
||||
return nil
|
||||
}
|
||||
var wait sync.WaitGroup
|
||||
@@ -283,7 +283,7 @@ func (app *appContext) GetInviteCount(gc *gin.Context) {
|
||||
// @Summary Get the number of invites stored in the database that have been used (but are still valid).
|
||||
// @Produce json
|
||||
// @Success 200 {object} PageCountDTO
|
||||
// @Router /invites/count [get]
|
||||
// @Router /invites/count/used [get]
|
||||
// @Security Bearer
|
||||
// @tags Invites
|
||||
func (app *appContext) GetInviteUsedCount(gc *gin.Context) {
|
||||
|
||||
@@ -148,11 +148,11 @@ func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
||||
case "PasswordReset":
|
||||
msg, err = app.email.constructReset(PasswordReset{}, true)
|
||||
case "UserDeleted":
|
||||
msg, err = app.email.constructDeleted("", true)
|
||||
msg, err = app.email.constructDeleted("", "", true)
|
||||
case "UserDisabled":
|
||||
msg, err = app.email.constructDisabled("", true)
|
||||
msg, err = app.email.constructDisabled("", "", true)
|
||||
case "UserEnabled":
|
||||
msg, err = app.email.constructEnabled("", true)
|
||||
msg, err = app.email.constructEnabled("", "", true)
|
||||
case "UserExpiryAdjusted":
|
||||
msg, err = app.email.constructExpiryAdjusted("", time.Time{}, "", true)
|
||||
case "ExpiryReminder":
|
||||
@@ -164,7 +164,7 @@ func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
||||
case "EmailConfirmation":
|
||||
msg, err = app.email.constructConfirmation("", "", "", true)
|
||||
case "UserExpired":
|
||||
msg, err = app.email.constructUserExpired(true)
|
||||
msg, err = app.email.constructUserExpired("", true)
|
||||
case "Announcement":
|
||||
case "UserPage":
|
||||
case "UserLogin":
|
||||
@@ -181,14 +181,13 @@ func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
var mail *Message
|
||||
var mail *Message = nil
|
||||
if contentInfo.ContentType == CustomMessage {
|
||||
mail = &Message{}
|
||||
err = app.email.construct(EmptyCustomContent, CustomContent{
|
||||
mail, err = app.email.construct(EmptyCustomContent, CustomContent{
|
||||
Name: EmptyCustomContent.Name,
|
||||
Enabled: true,
|
||||
Content: "<div class=\"preview-content\"></div>",
|
||||
}, map[string]any{}, mail)
|
||||
}, map[string]any{})
|
||||
if err != nil {
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
|
||||
53
api-users.go
53
api-users.go
@@ -380,19 +380,6 @@ func (app *appContext) EnableDisableUsers(gc *gin.Context) {
|
||||
"SetPolicy": map[string]string{},
|
||||
}
|
||||
sendMail := messagesEnabled
|
||||
var msg *Message
|
||||
var err error
|
||||
if sendMail {
|
||||
if req.Enabled {
|
||||
msg, err = app.email.constructEnabled(req.Reason, false)
|
||||
} else {
|
||||
msg, err = app.email.constructDisabled(req.Reason, false)
|
||||
}
|
||||
if err != nil {
|
||||
app.err.Printf(lm.FailedConstructEnableDisableMessage, "?", err)
|
||||
sendMail = false
|
||||
}
|
||||
}
|
||||
activityType := ActivityDisabled
|
||||
if req.Enabled {
|
||||
activityType = ActivityEnabled
|
||||
@@ -404,6 +391,18 @@ func (app *appContext) EnableDisableUsers(gc *gin.Context) {
|
||||
app.err.Printf(lm.FailedGetUser, user.ID, lm.Jellyfin, err)
|
||||
continue
|
||||
}
|
||||
var msg *Message
|
||||
if sendMail {
|
||||
if req.Enabled {
|
||||
msg, err = app.email.constructEnabled(user.Name, req.Reason, false)
|
||||
} else {
|
||||
msg, err = app.email.constructDisabled(user.Name, req.Reason, false)
|
||||
}
|
||||
if err != nil {
|
||||
app.err.Printf(lm.FailedConstructEnableDisableMessage, "?", err)
|
||||
sendMail = false
|
||||
}
|
||||
}
|
||||
err, _, _ = app.SetUserDisabled(user, !req.Enabled)
|
||||
if err != nil {
|
||||
errors["SetPolicy"][user.ID] = err.Error()
|
||||
@@ -449,15 +448,6 @@ func (app *appContext) DeleteUsers(gc *gin.Context) {
|
||||
gc.BindJSON(&req)
|
||||
errors := map[string]string{}
|
||||
sendMail := messagesEnabled
|
||||
var msg *Message
|
||||
var err error
|
||||
if sendMail {
|
||||
msg, err = app.email.constructDeleted(req.Reason, false)
|
||||
if err != nil {
|
||||
app.err.Printf(lm.FailedConstructDeletionMessage, "?", err)
|
||||
sendMail = false
|
||||
}
|
||||
}
|
||||
for _, userID := range req.Users {
|
||||
user, err := app.jf.UserByID(userID, false)
|
||||
if err != nil {
|
||||
@@ -465,6 +455,15 @@ func (app *appContext) DeleteUsers(gc *gin.Context) {
|
||||
errors[userID] = err.Error()
|
||||
}
|
||||
|
||||
var msg *Message = nil
|
||||
if sendMail {
|
||||
msg, err = app.email.constructDeleted(user.Name, req.Reason, false)
|
||||
if err != nil {
|
||||
app.err.Printf(lm.FailedConstructDeletionMessage, "?", err)
|
||||
sendMail = false
|
||||
}
|
||||
}
|
||||
|
||||
deleted := false
|
||||
err, deleted = app.DeleteUser(user)
|
||||
if err != nil {
|
||||
@@ -677,11 +676,10 @@ func (app *appContext) Announce(gc *gin.Context) {
|
||||
app.err.Printf(lm.FailedGetUser, userID, lm.Jellyfin, err)
|
||||
continue
|
||||
}
|
||||
msg := &Message{}
|
||||
err = app.email.construct(AnnouncementCustomContent(req.Subject), CustomContent{
|
||||
msg, err := app.email.construct(AnnouncementCustomContent(req.Subject), CustomContent{
|
||||
Enabled: true,
|
||||
Content: req.Message,
|
||||
}, map[string]any{"username": user.Name}, msg)
|
||||
}, map[string]any{"username": user.Name})
|
||||
if err != nil {
|
||||
app.err.Printf(lm.FailedConstructAnnouncementMessage, userID, err)
|
||||
respondBool(500, false, gc)
|
||||
@@ -694,11 +692,10 @@ func (app *appContext) Announce(gc *gin.Context) {
|
||||
}
|
||||
// app.info.Printf(lm.SentAnnouncementMessage, "*", "?")
|
||||
} else {
|
||||
msg := &Message{}
|
||||
err := app.email.construct(AnnouncementCustomContent(req.Subject), CustomContent{
|
||||
msg, err := app.email.construct(AnnouncementCustomContent(req.Subject), CustomContent{
|
||||
Enabled: true,
|
||||
Content: req.Message,
|
||||
}, map[string]any{"username": ""}, msg)
|
||||
}, map[string]any{"username": ""})
|
||||
if err != nil {
|
||||
app.err.Printf(lm.FailedConstructAnnouncementMessage, "*", err)
|
||||
respondBool(500, false, gc)
|
||||
|
||||
4
args.go
4
args.go
@@ -31,6 +31,7 @@ func (app *appContext) loadArgs(firstCall bool) {
|
||||
SWAGGER = flag.Bool("swagger", false, "Enable swagger at /swagger/index.html")
|
||||
|
||||
flag.BoolVar(&NO_API_AUTH_DO_NOT_USE, "disable-api-auth-do-not-use", false, "Disables API authentication. DO NOT USE!")
|
||||
flag.StringVar(&NO_API_AUTH_FORCE_JFID, "disable-api-auth-force-jf-id", "", "Assume given JFID when API auth is disabled.")
|
||||
|
||||
flag.Parse()
|
||||
if *help {
|
||||
@@ -52,11 +53,14 @@ func (app *appContext) loadArgs(firstCall bool) {
|
||||
|
||||
if NO_API_AUTH_DO_NOT_USE && *DEBUG {
|
||||
NO_API_AUTH_DO_NOT_USE = false
|
||||
forceJfID := NO_API_AUTH_FORCE_JFID
|
||||
NO_API_AUTH_FORCE_JFID = ""
|
||||
buf := bufio.NewReader(os.Stdin)
|
||||
app.err.Print(lm.NoAPIAuthPrompt)
|
||||
sentence, err := buf.ReadBytes('\n')
|
||||
if err == nil && strings.ContainsRune(string(sentence), 'y') {
|
||||
NO_API_AUTH_DO_NOT_USE = true
|
||||
NO_API_AUTH_FORCE_JFID = forceJfID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
auth.go
11
auth.go
@@ -40,8 +40,12 @@ func (app *appContext) logIpErr(gc *gin.Context, user bool, out string) {
|
||||
}
|
||||
|
||||
func (app *appContext) webAuth() gin.HandlerFunc {
|
||||
if NO_API_AUTH_DO_NOT_USE {
|
||||
return app.bogusAuthenticate
|
||||
} else {
|
||||
return app.authenticate
|
||||
}
|
||||
}
|
||||
|
||||
func (app *appContext) authLog(v any) { app.debug.PrintfCustomLevel(4, lm.FailedAuthRequest, v) }
|
||||
|
||||
@@ -138,6 +142,13 @@ func (app *appContext) authenticate(gc *gin.Context) {
|
||||
gc.Next()
|
||||
}
|
||||
|
||||
// bogusAuthenticate is for use with NO_API_AUTH_DO_NOT_USE, it sets the jfId/userId value from NO_API_AUTH_FORCE_JF_ID.
|
||||
func (app *appContext) bogusAuthenticate(gc *gin.Context) {
|
||||
gc.Set("jfId", NO_API_AUTH_FORCE_JFID)
|
||||
gc.Set("userId", NO_API_AUTH_FORCE_JFID)
|
||||
gc.Next()
|
||||
}
|
||||
|
||||
func checkToken(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("Unexpected signing method %v", token.Header["alg"])
|
||||
|
||||
@@ -245,7 +245,7 @@ var customContent = map[string]CustomContentInfo{
|
||||
"reason",
|
||||
),
|
||||
Placeholders: defaultVals(map[string]any{
|
||||
"newExpiry": "",
|
||||
"newExpiry": "01/01/01 00:00",
|
||||
"reason": "Reason",
|
||||
}),
|
||||
SourceFile: ContentSourceFileInfo{
|
||||
|
||||
99
email.go
99
email.go
@@ -246,14 +246,21 @@ type templ interface {
|
||||
Execute(wr io.Writer, data interface{}) error
|
||||
}
|
||||
|
||||
func (emailer *Emailer) construct(contentInfo CustomContentInfo, cc CustomContent, data map[string]any, msg *Message) error {
|
||||
func (emailer *Emailer) construct(contentInfo CustomContentInfo, cc CustomContent, data map[string]any) (*Message, error) {
|
||||
msg := &Message{
|
||||
Subject: contentInfo.Subject(emailer.config, &emailer.lang),
|
||||
}
|
||||
// Template the subject for bonus points
|
||||
if subject, err := templateEmail(msg.Subject, contentInfo.Variables, contentInfo.Conditionals, data); err == nil {
|
||||
msg.Subject = subject
|
||||
}
|
||||
if cc.Enabled {
|
||||
// Use template email, rather than the built-in's email file.
|
||||
contentInfo.SourceFile = customContent["TemplateEmail"].SourceFile
|
||||
content, err := templateEmail(cc.Content, contentInfo.Variables, contentInfo.Conditionals, data)
|
||||
if err != nil {
|
||||
emailer.err.Printf(lm.FailedConstructCustomContent, msg.Subject, err)
|
||||
return err
|
||||
return msg, err
|
||||
}
|
||||
html := markdown.ToHTML([]byte(content), nil, markdownRenderer)
|
||||
text := stripMarkdown(content)
|
||||
@@ -268,10 +275,6 @@ func (emailer *Emailer) construct(contentInfo CustomContentInfo, cc CustomConten
|
||||
data = templateData
|
||||
}
|
||||
var err error = nil
|
||||
// Template the subject for bonus points
|
||||
if subject, err := templateEmail(msg.Subject, contentInfo.Variables, contentInfo.Conditionals, data); err == nil {
|
||||
msg.Subject = subject
|
||||
}
|
||||
|
||||
var tpl templ
|
||||
msg.Text = ""
|
||||
@@ -313,7 +316,7 @@ func (emailer *Emailer) construct(contentInfo CustomContentInfo, cc CustomConten
|
||||
tpl, err = textTemplate.ParseFS(filesystem, fpath)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading from fs path \"%s\": %v", fpath, err)
|
||||
return msg, fmt.Errorf("error reading from fs path \"%s\": %v", fpath, err)
|
||||
}
|
||||
// For constructTemplate, if "md" is found in data it's used in stead of "text".
|
||||
foundMarkdown := false
|
||||
@@ -326,7 +329,7 @@ func (emailer *Emailer) construct(contentInfo CustomContentInfo, cc CustomConten
|
||||
var tplData bytes.Buffer
|
||||
err = tpl.Execute(&tplData, data)
|
||||
if err != nil {
|
||||
return err
|
||||
return msg, err
|
||||
}
|
||||
if foundMarkdown {
|
||||
data["plaintext"], data["md"] = data["md"], data["plaintext"]
|
||||
@@ -339,10 +342,10 @@ func (emailer *Emailer) construct(contentInfo CustomContentInfo, cc CustomConten
|
||||
msg.Markdown = tplData.String()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func (emailer *Emailer) baseValues(name string, username string, placeholders bool, values map[string]any) (CustomContentInfo, map[string]any, *Message) {
|
||||
func (emailer *Emailer) baseValues(name string, username string, placeholders bool, values map[string]any) (CustomContentInfo, map[string]any) {
|
||||
contentInfo := customContent[name]
|
||||
template := map[string]any{
|
||||
"username": username,
|
||||
@@ -355,17 +358,14 @@ func (emailer *Emailer) baseValues(name string, username string, placeholders bo
|
||||
template[v] = "{" + v + "}"
|
||||
}
|
||||
}
|
||||
email := &Message{
|
||||
Subject: contentInfo.Subject(emailer.config, &emailer.lang),
|
||||
}
|
||||
return contentInfo, template, email
|
||||
return contentInfo, template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructConfirmation(code, username, key string, placeholders bool) (*Message, error) {
|
||||
if placeholders {
|
||||
username = "{username}"
|
||||
}
|
||||
contentInfo, template, msg := emailer.baseValues("EmailConfirmation", username, placeholders, map[string]any{
|
||||
contentInfo, template := emailer.baseValues("EmailConfirmation", username, placeholders, map[string]any{
|
||||
"helloUser": emailer.lang.Strings.template("helloUser", tmpl{"username": username}),
|
||||
"clickBelow": emailer.lang.EmailConfirmation.get("clickBelow"),
|
||||
"ifItWasNotYou": emailer.lang.Strings.get("ifItWasNotYou"),
|
||||
@@ -381,15 +381,14 @@ func (emailer *Emailer) constructConfirmation(code, username, key string, placeh
|
||||
template["confirmationURL"] = inviteLink
|
||||
}
|
||||
cc := emailer.storage.MustGetCustomContentKey(contentInfo.Name)
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructInvite(invite Invite, placeholders bool) (*Message, error) {
|
||||
expiry := invite.ValidTill
|
||||
d, t, expiresIn := emailer.formatExpiry(expiry, false)
|
||||
inviteLink := fmt.Sprintf("%s%s/%s", ExternalURI(nil), PAGES.Form, invite.Code)
|
||||
contentInfo, template, msg := emailer.baseValues("InviteEmail", "", placeholders, map[string]any{
|
||||
contentInfo, template := emailer.baseValues("InviteEmail", "", placeholders, map[string]any{
|
||||
"hello": emailer.lang.InviteEmail.get("hello"),
|
||||
"youHaveBeenInvited": emailer.lang.InviteEmail.get("youHaveBeenInvited"),
|
||||
"toJoin": emailer.lang.InviteEmail.get("toJoin"),
|
||||
@@ -404,13 +403,12 @@ func (emailer *Emailer) constructInvite(invite Invite, placeholders bool) (*Mess
|
||||
template["inviteExpiry"] = emailer.lang.InviteEmail.template("inviteExpiry", template)
|
||||
}
|
||||
cc := emailer.storage.MustGetCustomContentKey(contentInfo.Name)
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructExpiry(invite Invite, placeholders bool) (*Message, error) {
|
||||
expiry := formatDatetime(invite.ValidTill)
|
||||
contentInfo, template, msg := emailer.baseValues("InviteExpiry", "", placeholders, map[string]any{
|
||||
contentInfo, template := emailer.baseValues("InviteExpiry", "", placeholders, map[string]any{
|
||||
"inviteExpired": emailer.lang.InviteExpiry.get("inviteExpired"),
|
||||
"notificationNotice": emailer.lang.InviteExpiry.get("notificationNotice"),
|
||||
"expiredAt": emailer.lang.InviteExpiry.get("expiredAt"),
|
||||
@@ -421,14 +419,13 @@ func (emailer *Emailer) constructExpiry(invite Invite, placeholders bool) (*Mess
|
||||
template["expiredAt"] = emailer.lang.InviteExpiry.template("expiredAt", template)
|
||||
}
|
||||
cc := emailer.storage.MustGetCustomContentKey(contentInfo.Name)
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructCreated(username, address string, when time.Time, invite Invite, placeholders bool) (*Message, error) {
|
||||
// NOTE: This was previously invite.Created, not sure why.
|
||||
created := formatDatetime(when)
|
||||
contentInfo, template, msg := emailer.baseValues("UserCreated", username, placeholders, map[string]any{
|
||||
contentInfo, template := emailer.baseValues("UserCreated", username, placeholders, map[string]any{
|
||||
"aUserWasCreated": emailer.lang.UserCreated.get("aUserWasCreated"),
|
||||
"nameString": emailer.lang.Strings.get("name"),
|
||||
"addressString": emailer.lang.Strings.get("emailAddress"),
|
||||
@@ -446,8 +443,7 @@ func (emailer *Emailer) constructCreated(username, address string, when time.Tim
|
||||
}
|
||||
}
|
||||
cc := emailer.storage.MustGetCustomContentKey(contentInfo.Name)
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructReset(pwr PasswordReset, placeholders bool) (*Message, error) {
|
||||
@@ -456,7 +452,7 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, placeholders bool) (*M
|
||||
}
|
||||
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true)
|
||||
linkResetEnabled := emailer.config.Section("password_resets").Key("link_reset").MustBool(false)
|
||||
contentInfo, template, msg := emailer.baseValues("PasswordReset", pwr.Username, placeholders, map[string]any{
|
||||
contentInfo, template := emailer.baseValues("PasswordReset", pwr.Username, placeholders, map[string]any{
|
||||
"helloUser": emailer.lang.Strings.template("helloUser", tmpl{"username": pwr.Username}),
|
||||
"someoneHasRequestedReset": emailer.lang.PasswordReset.get("someoneHasRequestedReset"),
|
||||
"ifItWasYou": emailer.lang.PasswordReset.get("ifItWasYou"),
|
||||
@@ -487,50 +483,49 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, placeholders bool) (*M
|
||||
}
|
||||
}
|
||||
cc := emailer.storage.MustGetCustomContentKey(contentInfo.Name)
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructDeleted(reason string, placeholders bool) (*Message, error) {
|
||||
func (emailer *Emailer) constructDeleted(username, reason string, placeholders bool) (*Message, error) {
|
||||
if placeholders {
|
||||
username = "{username}"
|
||||
reason = "{reason}"
|
||||
}
|
||||
contentInfo, template, msg := emailer.baseValues("UserDeleted", "", placeholders, map[string]any{
|
||||
contentInfo, template := emailer.baseValues("UserDeleted", username, placeholders, map[string]any{
|
||||
"yourAccountWas": emailer.lang.UserDeleted.get("yourAccountWasDeleted"),
|
||||
"reasonString": emailer.lang.Strings.get("reason"),
|
||||
"reason": reason,
|
||||
})
|
||||
cc := emailer.storage.MustGetCustomContentKey(contentInfo.Name)
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructDisabled(reason string, placeholders bool) (*Message, error) {
|
||||
func (emailer *Emailer) constructDisabled(username, reason string, placeholders bool) (*Message, error) {
|
||||
if placeholders {
|
||||
username = "{username}"
|
||||
reason = "{reason}"
|
||||
}
|
||||
contentInfo, template, msg := emailer.baseValues("UserDeleted", "", placeholders, map[string]any{
|
||||
contentInfo, template := emailer.baseValues("UserDisabled", username, placeholders, map[string]any{
|
||||
"yourAccountWas": emailer.lang.UserDisabled.get("yourAccountWasDisabled"),
|
||||
"reasonString": emailer.lang.Strings.get("reason"),
|
||||
"reason": reason,
|
||||
})
|
||||
cc := emailer.storage.MustGetCustomContentKey(contentInfo.Name)
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructEnabled(reason string, placeholders bool) (*Message, error) {
|
||||
func (emailer *Emailer) constructEnabled(username, reason string, placeholders bool) (*Message, error) {
|
||||
if placeholders {
|
||||
username = "{username}"
|
||||
reason = "{reason}"
|
||||
}
|
||||
contentInfo, template, msg := emailer.baseValues("UserDeleted", "", placeholders, map[string]any{
|
||||
contentInfo, template := emailer.baseValues("UserEnabled", username, placeholders, map[string]any{
|
||||
"yourAccountWas": emailer.lang.UserEnabled.get("yourAccountWasEnabled"),
|
||||
"reasonString": emailer.lang.Strings.get("reason"),
|
||||
"reason": reason,
|
||||
})
|
||||
cc := emailer.storage.MustGetCustomContentKey(contentInfo.Name)
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructExpiryAdjusted(username string, expiry time.Time, reason string, placeholders bool) (*Message, error) {
|
||||
@@ -538,7 +533,7 @@ func (emailer *Emailer) constructExpiryAdjusted(username string, expiry time.Tim
|
||||
username = "{username}"
|
||||
}
|
||||
exp := formatDatetime(expiry)
|
||||
contentInfo, template, msg := emailer.baseValues("UserExpiryAdjusted", username, placeholders, map[string]any{
|
||||
contentInfo, template := emailer.baseValues("UserExpiryAdjusted", username, placeholders, map[string]any{
|
||||
"helloUser": emailer.lang.Strings.template("helloUser", tmpl{"username": username}),
|
||||
"yourExpiryWasAdjusted": emailer.lang.UserExpiryAdjusted.get("yourExpiryWasAdjusted"),
|
||||
"ifPreviouslyDisabled": emailer.lang.UserExpiryAdjusted.get("ifPreviouslyDisabled"),
|
||||
@@ -554,8 +549,7 @@ func (emailer *Emailer) constructExpiryAdjusted(username string, expiry time.Tim
|
||||
})
|
||||
}
|
||||
}
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructExpiryReminder(username string, expiry time.Time, placeholders bool) (*Message, error) {
|
||||
@@ -563,7 +557,7 @@ func (emailer *Emailer) constructExpiryReminder(username string, expiry time.Tim
|
||||
username = "{username}"
|
||||
}
|
||||
d, t, expiresIn := emailer.formatExpiry(expiry, false)
|
||||
contentInfo, template, msg := emailer.baseValues("ExpiryReminder", username, placeholders, map[string]any{
|
||||
contentInfo, template := emailer.baseValues("ExpiryReminder", username, placeholders, map[string]any{
|
||||
"helloUser": emailer.lang.Strings.template("helloUser", tmpl{"username": username}),
|
||||
"yourAccountIsDueToExpire": emailer.lang.ExpiryReminder.get("yourAccountIsDueToExpire"),
|
||||
"expiresIn": expiresIn,
|
||||
@@ -576,8 +570,7 @@ func (emailer *Emailer) constructExpiryReminder(username string, expiry time.Tim
|
||||
template["yourAccountIsDueToExpire"] = emailer.lang.ExpiryReminder.template("yourAccountIsDueToExpire", template)
|
||||
}
|
||||
}
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructWelcome(username string, expiry time.Time, placeholders bool) (*Message, error) {
|
||||
@@ -586,7 +579,7 @@ func (emailer *Emailer) constructWelcome(username string, expiry time.Time, plac
|
||||
username = "{username}"
|
||||
exp = "{yourAccountWillExpire}"
|
||||
}
|
||||
contentInfo, template, msg := emailer.baseValues("WelcomeEmail", username, placeholders, map[string]any{
|
||||
contentInfo, template := emailer.baseValues("WelcomeEmail", username, placeholders, map[string]any{
|
||||
"welcome": emailer.lang.WelcomeEmail.get("welcome"),
|
||||
"youCanLoginWith": emailer.lang.WelcomeEmail.get("youCanLoginWith"),
|
||||
"jellyfinURLString": emailer.lang.WelcomeEmail.get("jellyfinURL"),
|
||||
@@ -604,18 +597,16 @@ func (emailer *Emailer) constructWelcome(username string, expiry time.Time, plac
|
||||
template["yourAccountWillExpire"] = exp
|
||||
}
|
||||
}
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructUserExpired(placeholders bool) (*Message, error) {
|
||||
contentInfo, template, msg := emailer.baseValues("UserExpired", "", placeholders, map[string]any{
|
||||
func (emailer *Emailer) constructUserExpired(username string, placeholders bool) (*Message, error) {
|
||||
contentInfo, template := emailer.baseValues("UserExpired", username, placeholders, map[string]any{
|
||||
"yourAccountHasExpired": emailer.lang.UserExpired.get("yourAccountHasExpired"),
|
||||
"contactTheAdmin": emailer.lang.UserExpired.get("contactTheAdmin"),
|
||||
})
|
||||
cc := emailer.storage.MustGetCustomContentKey(contentInfo.Name)
|
||||
err := emailer.construct(contentInfo, cc, template, msg)
|
||||
return msg, err
|
||||
return emailer.construct(contentInfo, cc, template)
|
||||
}
|
||||
|
||||
// calls the send method in the underlying emailClient.
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
type GenericDaemon struct {
|
||||
Stopped bool
|
||||
ShutdownChannel chan string
|
||||
TriggerChannel chan bool
|
||||
Interval time.Duration
|
||||
period time.Duration
|
||||
jobs []func(app *appContext)
|
||||
@@ -27,6 +28,7 @@ func NewGenericDaemon(interval time.Duration, app *appContext, jobs ...func(app
|
||||
d := GenericDaemon{
|
||||
Stopped: false,
|
||||
ShutdownChannel: make(chan string),
|
||||
TriggerChannel: make(chan bool),
|
||||
Interval: interval,
|
||||
period: interval,
|
||||
app: app,
|
||||
@@ -46,6 +48,8 @@ func (d *GenericDaemon) run() {
|
||||
case <-d.ShutdownChannel:
|
||||
d.ShutdownChannel <- "Down"
|
||||
return
|
||||
case <-d.TriggerChannel:
|
||||
break
|
||||
case <-time.After(d.period):
|
||||
break
|
||||
}
|
||||
@@ -61,6 +65,10 @@ func (d *GenericDaemon) run() {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *GenericDaemon) Trigger() {
|
||||
d.TriggerChannel <- true
|
||||
}
|
||||
|
||||
func (d *GenericDaemon) Shutdown() {
|
||||
d.Stopped = true
|
||||
d.ShutdownChannel <- "Down"
|
||||
|
||||
13
main.go
13
main.go
@@ -123,6 +123,7 @@ type appContext struct {
|
||||
telegram *TelegramDaemon
|
||||
discord *DiscordDaemon
|
||||
matrix *MatrixDaemon
|
||||
housekeepingDaemon, userDaemon *GenericDaemon
|
||||
contactMethods []ContactMethodLinker
|
||||
LoggerSet
|
||||
host string
|
||||
@@ -505,13 +506,13 @@ func start(asDaemon, firstCall bool) {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
invDaemon := newHousekeepingDaemon(time.Duration(60*time.Second), app)
|
||||
go invDaemon.run()
|
||||
defer invDaemon.Shutdown()
|
||||
app.housekeepingDaemon = newHousekeepingDaemon(time.Duration(60*time.Second), app)
|
||||
go app.housekeepingDaemon.run()
|
||||
defer app.housekeepingDaemon.Shutdown()
|
||||
|
||||
userDaemon := newUserDaemon(time.Duration(60*time.Second), app)
|
||||
go userDaemon.run()
|
||||
defer userDaemon.Shutdown()
|
||||
app.userDaemon = newUserDaemon(time.Duration(60*time.Second), app)
|
||||
go app.userDaemon.run()
|
||||
defer app.userDaemon.Shutdown()
|
||||
|
||||
var jellyseerrDaemon *GenericDaemon
|
||||
if app.config.Section("jellyseerr").Key("enabled").MustBool(false) && app.config.Section("jellyseerr").Key("import_existing").MustBool(false) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
var (
|
||||
// Disables authentication for the API. Do not use!
|
||||
NO_API_AUTH_DO_NOT_USE = false
|
||||
NO_API_AUTH_FORCE_JFID = ""
|
||||
)
|
||||
|
||||
// loads HTML templates. If [files]/html_templates is set, alternative files inside the directory are loaded in place of the internal templates.
|
||||
@@ -188,11 +189,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
||||
}
|
||||
|
||||
var api *gin.RouterGroup
|
||||
if NO_API_AUTH_DO_NOT_USE && *DEBUG {
|
||||
api = router.Group("/")
|
||||
} else {
|
||||
api = router.Group("/", app.webAuth())
|
||||
}
|
||||
|
||||
for _, p := range routePrefixes {
|
||||
var user *gin.RouterGroup
|
||||
@@ -244,6 +241,8 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
||||
api.POST(p+"/config", app.ModifyConfig)
|
||||
api.POST(p+"/restart", app.restart)
|
||||
api.GET(p+"/logs", app.GetLog)
|
||||
api.POST(p+"/tasks/housekeeping", func(gc *gin.Context) { app.housekeepingDaemon.Trigger(); gc.Status(http.StatusNoContent) })
|
||||
api.POST(p+"/tasks/users", func(gc *gin.Context) { app.userDaemon.Trigger(); gc.Status(http.StatusNoContent) })
|
||||
api.POST(p+"/backups", app.CreateBackup)
|
||||
api.GET(p+"/backups/:fname", app.GetBackup)
|
||||
api.GET(p+"/backups", app.GetBackups)
|
||||
|
||||
@@ -1126,9 +1126,9 @@ class MessageEditor {
|
||||
this._variables.innerHTML = innerHTML
|
||||
let buttons = this._variables.querySelectorAll("span.button") as NodeListOf<HTMLSpanElement>;
|
||||
for (let i = 0; i < this._templ.variables.length; i++) {
|
||||
buttons[i].innerHTML = `<span class="font-mono bg-inherit">` + this._templ.variables[i] + `</span>`;
|
||||
buttons[i].innerHTML = `<span class="font-mono bg-inherit">` + "{" + this._templ.variables[i] + "}" + `</span>`;
|
||||
buttons[i].onclick = () => {
|
||||
insertText(this._textArea, this._templ.variables[i]);
|
||||
insertText(this._textArea, "{" + this._templ.variables[i] + "}");
|
||||
this.loadPreview();
|
||||
// this._timeout = setTimeout(this.loadPreview, this._finishInterval);
|
||||
}
|
||||
@@ -1146,9 +1146,9 @@ class MessageEditor {
|
||||
this._conditionals.innerHTML = innerHTML
|
||||
buttons = this._conditionals.querySelectorAll("span.button") as NodeListOf<HTMLSpanElement>;
|
||||
for (let i = 0; i < this._templ.conditionals.length; i++) {
|
||||
buttons[i].innerHTML = `<span class="font-mono bg-inherit">{if ` + this._templ.conditionals[i].slice(1) + `</span>`;
|
||||
buttons[i].innerHTML = `<span class="font-mono bg-inherit">{if ` + this._templ.conditionals[i] + "}" + `</span>`;
|
||||
buttons[i].onclick = () => {
|
||||
insertText(this._textArea, "{if " + this._templ.conditionals[i].slice(1) + "{endif}");
|
||||
insertText(this._textArea, "{if " + this._templ.conditionals[i] + "}" + "{endif}");
|
||||
this.loadPreview();
|
||||
// this._timeout = setTimeout(this.loadPreview, this._finishInterval);
|
||||
}
|
||||
@@ -1162,9 +1162,9 @@ class MessageEditor {
|
||||
let content = this._textArea.value;
|
||||
if (this._templ.variables) {
|
||||
for (let variable of this._templ.variables) {
|
||||
let value = this._templ.values[variable.slice(1, -1)];
|
||||
if (value === undefined) { value = variable; }
|
||||
content = content.replace(new RegExp(variable, "g"), value);
|
||||
let value = this._templ.values[variable];
|
||||
if (value === undefined) { value = "{" + variable + "}"; }
|
||||
content = content.replace(new RegExp("{" + variable + "}", "g"), value);
|
||||
}
|
||||
}
|
||||
if (this._templ.html == "") {
|
||||
|
||||
@@ -173,7 +173,7 @@ func (app *appContext) checkUsers(remindBeforeExpiry *DayTimerSet) {
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
msg, err := app.email.constructUserExpired(false)
|
||||
msg, err := app.email.constructUserExpired(user.Name, false)
|
||||
if err != nil {
|
||||
app.err.Printf(lm.FailedConstructExpiryMessage, user.ID, err)
|
||||
} else if err := app.sendByID(msg, user.ID); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user