binwiederhier 1 год назад
Родитель
Сommit
a04f2f9c9a
3 измененных файлов с 72 добавлено и 42 удалено
  1. 31 28
      docs/publish.md
  2. 25 14
      server/server.go
  3. 16 0
      server/server_test.go

+ 31 - 28
docs/publish.md

@@ -938,6 +938,37 @@ Here's an example with a custom message, tags and a priority:
     file_get_contents('https://ntfy.sh/mywebhook/publish?message=Webhook+triggered&priority=high&tags=warning,skull');
     ```
 
+
+## JSON templating
+Some services let you specify a webhook URL but do not let you modify the webhook body (e.g. GitHub, Grafana). Instead of using a separate
+bridge program to parse the webhook body into the format ntfy expects, you can include a templated message and/or a templated title
+which will be populated based on the fields of the webhook body (so long as the webhook body is valid JSON).
+
+Enable templating by setting the `X-Template` header (or its aliases `Template` or `tpl`) to `yes`, or (more appropriately for webhooks)
+by setting the `?template=yes` query parameter. Then, include templates in your message and/or title by including paths to the
+appropriate JSON fields surrounded by `${` and `}`, e.g. `${alert.title}` or `${error.desc}`, depending on your JSON payload.
+
+Please refer to the [GJSON docs](https://github.com/tidwall/gjson/blob/master/SYNTAX.md) for supported JSON path syntax, as well as 
+[gjson.dev](https://gjson.dev/) to test your templates.
+
+=== "HTTP"
+    ``` http
+    POST /mytopic HTTP/1.1
+    Host: ntfy.sh
+    X-Message: Error message: ${error.desc}
+    X-Title: ${hostname}: A ${error.level} error has occurred
+    X-Template: yes
+
+    {"hostname": "philipp-pc", "error": {"level": "severe", "desc": "Disk has run out of space"}}
+    ```
+
+The example above would send a notification with a title "philipp-pc: A severe error has occurred" and a message "Error message: Disk has run out of space".
+
+For Grafana webhooks, you might find it helpful to use the headers `X-Title: Grafana alert: ${title}` and `X-Message: ${message}`.
+Alternatively, you can include the params in the webhook URL. For example, by
+appending `?template=yes&title=Grafana alert: ${title}&message=${message}` to the URL.
+
+
 ## Publish as JSON
 _Supported on:_ :material-android: :material-apple: :material-firefox:
 
@@ -3557,34 +3588,6 @@ ntfy server plays the role of the Push Gateway, as well as the Push Provider. Un
 !!! info
     This is not a generic Matrix Push Gateway. It only works in combination with UnifiedPush and ntfy.
 
-### Message and Title Templates
-Some services let you specify a webhook URL but do not let you modify the webhook body (e.g., Grafana). Instead of using a separate
-bridge program to parse the webhook body into the format ntfy expects, you can include a templated message and/or a templated title 
-which will be populated based on the fields of the webhook body (so long as the webhook body is valid JSON).
-
-Enable templating by setting the `X-Template` header (or its aliases `Template` or `tpl`) to "yes". Then, include templates
-in your message and/or title (no other fields can be filled with a template at this time) by including paths to the
-appropriate JSON fields surrounded by `${` and `}`. See an example below.
-See [GJSON docs](https://github.com/tidwall/gjson/blob/master/SYNTAX.md) for supported JSON path syntax.
-[https://gjson.dev/](https://gjson.dev/) is a great resource for testing your templates.
-
-=== "HTTP"
-    ``` http
-    POST /mytopic HTTP/1.1
-    Host: ntfy.sh
-    X-Message: Error message: ${error.desc}
-    X-Title: ${hostname}: A ${error.level} error has occurred
-    X-Template: yes
-
-    {"hostname": "philipp-pc", "error": {"level": "severe", "desc": "Disk has run out of space"}}
-    ```
-
-The example above would send a notification with a title "philipp-pc: A severe error has occurred" and a message "Error message: Disk has run out of space".
-
-For Grafana webhooks, you might find it helpful to use the headers `X-Title: Grafana alert: ${title}` and `X-Message: ${message}`.
-Alternatively, you can include the params in the webhook URL. For example, by
-appending `?template=yes&title=Grafana alert: ${title}&message=${message}` to the URL.
-
 ## Public topics
 Obviously all topics on ntfy.sh are public, but there are a few designated topics that are used in examples, and topics
 that you can use to try out what [authentication and access control](#authentication) looks like.

+ 25 - 14
server/server.go

@@ -23,13 +23,13 @@ import (
 	"strconv"
 	"strings"
 	"sync"
+	"text/template"
 	"time"
 	"unicode/utf8"
 
 	"github.com/emersion/go-smtp"
 	"github.com/gorilla/websocket"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
-	"github.com/tidwall/gjson"
 	"golang.org/x/sync/errgroup"
 	"heckel.io/ntfy/v2/log"
 	"heckel.io/ntfy/v2/user"
@@ -1095,32 +1095,43 @@ func (s *Server) handleBodyAsTextMessage(m *message, body *util.PeekedReadCloser
 }
 
 func (s *Server) handleBodyAsTemplatedTextMessage(m *message, body *util.PeekedReadCloser) error {
-	body, err := util.Peek(body, jsonBodyBytesLimit)
+	body, err := util.Peek(body, max(s.config.MessageSizeLimit, jsonBodyBytesLimit))
 	if err != nil {
 		return err
 	} else if body.LimitReached {
 		return errHTTPEntityTooLargeJSONBody
 	}
 	peekedBody := strings.TrimSpace(string(body.PeekedBytes))
-	if !gjson.Valid(peekedBody) {
-		return errHTTPBadRequestTemplatedMessageNotJSON
-	}
-	m.Message = replaceGJSONTemplate(m.Message, peekedBody)
-	m.Title = replaceGJSONTemplate(m.Title, peekedBody)
+	m.Message = replaceTemplate(m.Message, peekedBody)
+	m.Title = replaceTemplate(m.Title, peekedBody)
 	if len(m.Message) > s.config.MessageSizeLimit {
 		return errHTTPBadRequestTemplatedMessageTooLarge
 	}
 	return nil
 }
 
-func replaceGJSONTemplate(template string, source string) string {
-	matches := templateVarRegex.FindAllStringSubmatch(template, -1)
-	for _, m := range matches {
-		if result := gjson.Get(source, m[1]); result.Exists() {
-			template = strings.ReplaceAll(template, fmt.Sprintf(templateVarFormat, m[1]), result.String())
-		}
+func replaceTemplate(tpl string, source string) string {
+	rendered, err := replaceTemplateInternal(tpl, source)
+	if err != nil {
+		return "<invalid template>"
+	}
+	return rendered
+}
+
+func replaceTemplateInternal(tpl string, source string) (string, error) {
+	var data any
+	if err := json.Unmarshal([]byte(source), &data); err != nil {
+		return "", err
+	}
+	t, err := template.New("").Parse(tpl)
+	if err != nil {
+		return "", err
+	}
+	var buf bytes.Buffer
+	if err := t.Execute(&buf, data); err != nil {
+		return "", err
 	}
-	return template
+	return buf.String(), nil
 }
 
 func (s *Server) handleBodyAsAttachment(r *http.Request, v *visitor, m *message, body *util.PeekedReadCloser) error {

Разница между файлами не показана из-за своего большого размера
+ 16 - 0
server/server_test.go


Некоторые файлы не были показаны из-за большого количества измененных файлов