mirror of
				https://github.com/binwiederhier/ntfy.git
				synced 2025-10-31 13:02:24 +01:00 
			
		
		
		
	Bla
This commit is contained in:
		
							parent
							
								
									9247dac50d
								
							
						
					
					
						commit
						a04f2f9c9a
					
				
					 3 changed files with 85 additions and 42 deletions
				
			
		|  | @ -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'); |     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 | ## Publish as JSON | ||||||
| _Supported on:_ :material-android: :material-apple: :material-firefox: | _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 | !!! info | ||||||
|     This is not a generic Matrix Push Gateway. It only works in combination with UnifiedPush and ntfy. |     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 | ## Public topics | ||||||
| Obviously all topics on ntfy.sh are public, but there are a few designated topics that are used in examples, and 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. | that you can use to try out what [authentication and access control](#authentication) looks like. | ||||||
|  |  | ||||||
|  | @ -23,13 +23,13 @@ import ( | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
|  | 	"text/template" | ||||||
| 	"time" | 	"time" | ||||||
| 	"unicode/utf8" | 	"unicode/utf8" | ||||||
| 
 | 
 | ||||||
| 	"github.com/emersion/go-smtp" | 	"github.com/emersion/go-smtp" | ||||||
| 	"github.com/gorilla/websocket" | 	"github.com/gorilla/websocket" | ||||||
| 	"github.com/prometheus/client_golang/prometheus/promhttp" | 	"github.com/prometheus/client_golang/prometheus/promhttp" | ||||||
| 	"github.com/tidwall/gjson" |  | ||||||
| 	"golang.org/x/sync/errgroup" | 	"golang.org/x/sync/errgroup" | ||||||
| 	"heckel.io/ntfy/v2/log" | 	"heckel.io/ntfy/v2/log" | ||||||
| 	"heckel.io/ntfy/v2/user" | 	"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 { | 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 { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} else if body.LimitReached { | 	} else if body.LimitReached { | ||||||
| 		return errHTTPEntityTooLargeJSONBody | 		return errHTTPEntityTooLargeJSONBody | ||||||
| 	} | 	} | ||||||
| 	peekedBody := strings.TrimSpace(string(body.PeekedBytes)) | 	peekedBody := strings.TrimSpace(string(body.PeekedBytes)) | ||||||
| 	if !gjson.Valid(peekedBody) { | 	m.Message = replaceTemplate(m.Message, peekedBody) | ||||||
| 		return errHTTPBadRequestTemplatedMessageNotJSON | 	m.Title = replaceTemplate(m.Title, peekedBody) | ||||||
| 	} |  | ||||||
| 	m.Message = replaceGJSONTemplate(m.Message, peekedBody) |  | ||||||
| 	m.Title = replaceGJSONTemplate(m.Title, peekedBody) |  | ||||||
| 	if len(m.Message) > s.config.MessageSizeLimit { | 	if len(m.Message) > s.config.MessageSizeLimit { | ||||||
| 		return errHTTPBadRequestTemplatedMessageTooLarge | 		return errHTTPBadRequestTemplatedMessageTooLarge | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func replaceGJSONTemplate(template string, source string) string { | func replaceTemplate(tpl string, source string) string { | ||||||
| 	matches := templateVarRegex.FindAllStringSubmatch(template, -1) | 	rendered, err := replaceTemplateInternal(tpl, source) | ||||||
| 	for _, m := range matches { | 	if err != nil { | ||||||
| 		if result := gjson.Get(source, m[1]); result.Exists() { | 		return "<invalid template>" | ||||||
| 			template = strings.ReplaceAll(template, fmt.Sprintf(templateVarFormat, m[1]), result.String()) |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	return 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 buf.String(), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Server) handleBodyAsAttachment(r *http.Request, v *visitor, m *message, body *util.PeekedReadCloser) error { | func (s *Server) handleBodyAsAttachment(r *http.Request, v *visitor, m *message, body *util.PeekedReadCloser) error { | ||||||
|  |  | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 binwiederhier
						binwiederhier