mirror of
https://github.com/binwiederhier/ntfy.git
synced 2025-11-29 19:59:59 +01:00
Tempalte dir
This commit is contained in:
parent
2a468493f9
commit
93e14b73bb
3 changed files with 62 additions and 3 deletions
|
|
@ -107,6 +107,7 @@ var flagsServe = append(
|
|||
altsrc.NewStringFlag(&cli.StringFlag{Name: "web-push-startup-queries", Aliases: []string{"web_push_startup_queries"}, EnvVars: []string{"NTFY_WEB_PUSH_STARTUP_QUERIES"}, Usage: "queries run when the web push database is initialized"}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "web-push-expiry-duration", Aliases: []string{"web_push_expiry_duration"}, EnvVars: []string{"NTFY_WEB_PUSH_EXPIRY_DURATION"}, Value: util.FormatDuration(server.DefaultWebPushExpiryDuration), Usage: "automatically expire unused subscriptions after this time"}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "web-push-expiry-warning-duration", Aliases: []string{"web_push_expiry_warning_duration"}, EnvVars: []string{"NTFY_WEB_PUSH_EXPIRY_WARNING_DURATION"}, Value: util.FormatDuration(server.DefaultWebPushExpiryWarningDuration), Usage: "send web push warning notification after this time before expiring unused subscriptions"}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "template-directory", Aliases: []string{"template_directory"}, EnvVars: []string{"NTFY_TEMPLATE_DIRECTORY"}, Usage: "directory to load named templates from"}),
|
||||
)
|
||||
|
||||
var cmdServe = &cli.Command{
|
||||
|
|
@ -205,6 +206,7 @@ func execServe(c *cli.Context) error {
|
|||
metricsListenHTTP := c.String("metrics-listen-http")
|
||||
enableMetrics := c.Bool("enable-metrics") || metricsListenHTTP != ""
|
||||
profileListenHTTP := c.String("profile-listen-http")
|
||||
templateDirectory := c.String("template-directory")
|
||||
|
||||
// Convert durations
|
||||
cacheDuration, err := util.ParseDuration(cacheDurationStr)
|
||||
|
|
@ -461,6 +463,7 @@ func execServe(c *cli.Context) error {
|
|||
conf.WebPushStartupQueries = webPushStartupQueries
|
||||
conf.WebPushExpiryDuration = webPushExpiryDuration
|
||||
conf.WebPushExpiryWarningDuration = webPushExpiryWarningDuration
|
||||
conf.TemplateDirectory = templateDirectory
|
||||
conf.Version = c.App.Version
|
||||
|
||||
// Set up hot-reloading of config
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ type Config struct {
|
|||
WebPushExpiryDuration time.Duration
|
||||
WebPushExpiryWarningDuration time.Duration
|
||||
Version string // injected by App
|
||||
TemplateDirectory string // Directory to load named templates from
|
||||
}
|
||||
|
||||
// NewConfig instantiates a default new server config
|
||||
|
|
@ -257,5 +258,6 @@ func NewConfig() *Config {
|
|||
WebPushEmailAddress: "",
|
||||
WebPushExpiryDuration: DefaultWebPushExpiryDuration,
|
||||
WebPushExpiryWarningDuration: DefaultWebPushExpiryWarningDuration,
|
||||
TemplateDirectory: "",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ type Server struct {
|
|||
metricsHandler http.Handler // Handles /metrics if enable-metrics set, and listen-metrics-http not set
|
||||
closeChan chan bool
|
||||
mu sync.RWMutex
|
||||
templates map[string]*template.Template // Loaded named templates
|
||||
}
|
||||
|
||||
// handleFunc extends the normal http.HandlerFunc to be able to easily return errors
|
||||
|
|
@ -222,8 +223,16 @@ func New(conf *Config) (*Server, error) {
|
|||
messagesHistory: []int64{messages},
|
||||
visitors: make(map[string]*visitor),
|
||||
stripe: stripe,
|
||||
templates: make(map[string]*template.Template),
|
||||
}
|
||||
s.priceCache = util.NewLookupCache(s.fetchStripePrices, conf.StripePriceCacheDuration)
|
||||
if conf.TemplateDirectory != "" {
|
||||
tmpls, err := loadTemplatesFromDir(conf.TemplateDirectory)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load templates from %s: %w", conf.TemplateDirectory, err)
|
||||
}
|
||||
s.templates = tmpls
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
|
@ -1113,10 +1122,10 @@ func (s *Server) handleBodyAsTemplatedTextMessage(m *message, body *util.PeekedR
|
|||
return errHTTPEntityTooLargeJSONBody
|
||||
}
|
||||
peekedBody := strings.TrimSpace(string(body.PeekedBytes))
|
||||
if m.Message, err = replaceTemplate(m.Message, peekedBody); err != nil {
|
||||
if m.Message, err = s.replaceTemplate(m.Message, peekedBody); err != nil {
|
||||
return err
|
||||
}
|
||||
if m.Title, err = replaceTemplate(m.Title, peekedBody); err != nil {
|
||||
if m.Title, err = s.replaceTemplate(m.Title, peekedBody); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(m.Message) > s.config.MessageSizeLimit {
|
||||
|
|
@ -1125,10 +1134,26 @@ func (s *Server) handleBodyAsTemplatedTextMessage(m *message, body *util.PeekedR
|
|||
return nil
|
||||
}
|
||||
|
||||
func replaceTemplate(tpl string, source string) (string, error) {
|
||||
func (s *Server) replaceTemplate(tpl string, source string) (string, error) {
|
||||
if templateDisallowedRegex.MatchString(tpl) {
|
||||
return "", errHTTPBadRequestTemplateDisallowedFunctionCalls
|
||||
}
|
||||
if strings.HasPrefix(tpl, "@") {
|
||||
name := strings.TrimPrefix(tpl, "@")
|
||||
t, ok := s.templates[name]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("template '@%s' not found", name)
|
||||
}
|
||||
var data any
|
||||
if err := json.Unmarshal([]byte(source), &data); err != nil {
|
||||
return "", errHTTPBadRequestTemplateMessageNotJSON
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := t.Execute(util.NewTimeoutWriter(&buf, templateMaxExecutionTime), data); err != nil {
|
||||
return "", errHTTPBadRequestTemplateExecuteFailed
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
var data any
|
||||
if err := json.Unmarshal([]byte(source), &data); err != nil {
|
||||
return "", errHTTPBadRequestTemplateMessageNotJSON
|
||||
|
|
@ -2061,3 +2086,32 @@ func (s *Server) updateAndWriteStats(messagesCount int64) {
|
|||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func loadTemplatesFromDir(dir string) (map[string]*template.Template, error) {
|
||||
templates := make(map[string]*template.Template)
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
if !strings.HasSuffix(name, ".tmpl") {
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(dir, name)
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read template %s: %w", name, err)
|
||||
}
|
||||
tmpl, err := template.New(name).Funcs(sprig.FuncMap()).Parse(string(content))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse template %s: %w", name, err)
|
||||
}
|
||||
base := strings.TrimSuffix(name, ".tmpl")
|
||||
templates[base] = tmpl
|
||||
}
|
||||
return templates, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue