gitea源码

mailer.go 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package templates
  4. import (
  5. "context"
  6. "fmt"
  7. "html/template"
  8. "regexp"
  9. "strings"
  10. "sync/atomic"
  11. texttmpl "text/template"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/util"
  15. )
  16. type MailTemplates struct {
  17. TemplateNames []string
  18. BodyTemplates *template.Template
  19. SubjectTemplates *texttmpl.Template
  20. }
  21. var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}\s*$`)
  22. // mailSubjectTextFuncMap returns functions for injecting to text templates, it's only used for mail subject
  23. func mailSubjectTextFuncMap() texttmpl.FuncMap {
  24. return texttmpl.FuncMap{
  25. "dict": dict,
  26. "Eval": evalTokens,
  27. "EllipsisString": util.EllipsisDisplayString,
  28. "AppName": func() string {
  29. return setting.AppName
  30. },
  31. "AppDomain": func() string { // documented in mail-templates.md
  32. return setting.Domain
  33. },
  34. }
  35. }
  36. func buildSubjectBodyTemplate(stpl *texttmpl.Template, btpl *template.Template, name string, content []byte) error {
  37. // Split template into subject and body
  38. var subjectContent []byte
  39. bodyContent := content
  40. loc := mailSubjectSplit.FindIndex(content)
  41. if loc != nil {
  42. subjectContent = content[0:loc[0]]
  43. bodyContent = content[loc[1]:]
  44. }
  45. if _, err := stpl.New(name).Parse(string(subjectContent)); err != nil {
  46. return fmt.Errorf("failed to parse template [%s/subject]: %w", name, err)
  47. }
  48. if _, err := btpl.New(name).Parse(string(bodyContent)); err != nil {
  49. return fmt.Errorf("failed to parse template [%s/body]: %w", name, err)
  50. }
  51. return nil
  52. }
  53. // LoadMailTemplates provides the templates required for sending notification mails.
  54. func LoadMailTemplates(ctx context.Context, loadedTemplates *atomic.Pointer[MailTemplates]) {
  55. assetFS := AssetFS()
  56. refreshTemplates := func(firstRun bool) {
  57. var templateNames []string
  58. subjectTemplates := texttmpl.New("")
  59. bodyTemplates := template.New("")
  60. subjectTemplates.Funcs(mailSubjectTextFuncMap())
  61. bodyTemplates.Funcs(NewFuncMap())
  62. if !firstRun {
  63. log.Trace("Reloading mail templates")
  64. }
  65. assetPaths, err := ListMailTemplateAssetNames(assetFS)
  66. if err != nil {
  67. log.Error("Failed to list mail templates: %v", err)
  68. return
  69. }
  70. for _, assetPath := range assetPaths {
  71. content, layerName, err := assetFS.ReadLayeredFile(assetPath)
  72. if err != nil {
  73. log.Warn("Failed to read mail template %s by %s: %v", assetPath, layerName, err)
  74. continue
  75. }
  76. tmplName := strings.TrimPrefix(strings.TrimSuffix(assetPath, ".tmpl"), "mail/")
  77. if firstRun {
  78. log.Trace("Adding mail template %s: %s by %s", tmplName, assetPath, layerName)
  79. }
  80. templateNames = append(templateNames, tmplName)
  81. if err = buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content); err != nil {
  82. if firstRun {
  83. log.Fatal("Failed to parse mail template, err: %v", err)
  84. }
  85. log.Error("Failed to parse mail template, err: %v", err)
  86. }
  87. }
  88. loaded := &MailTemplates{
  89. TemplateNames: templateNames,
  90. BodyTemplates: bodyTemplates,
  91. SubjectTemplates: subjectTemplates,
  92. }
  93. loadedTemplates.Store(loaded)
  94. }
  95. refreshTemplates(true)
  96. if !setting.IsProd {
  97. // Now subjectTemplates and bodyTemplates are both synchronized
  98. // thus it is safe to call refresh from a different goroutine
  99. go assetFS.WatchLocalChanges(ctx, func() {
  100. refreshTemplates(false)
  101. })
  102. }
  103. }