Docs docs docs
This commit is contained in:
		
							parent
							
								
									762333c28f
								
							
						
					
					
						commit
						034c81288c
					
				
					 11 changed files with 346 additions and 141 deletions
				
			
		|  | @ -25,7 +25,7 @@ var cmdPublish = &cli.Command{ | |||
| 		&cli.StringFlag{Name: "delay", Aliases: []string{"at", "in", "D"}, Usage: "delay/schedule message"}, | ||||
| 		&cli.StringFlag{Name: "click", Aliases: []string{"U"}, Usage: "URL to open when notification is clicked"}, | ||||
| 		&cli.StringFlag{Name: "attach", Aliases: []string{"a"}, Usage: "URL to send as an external attachment"}, | ||||
| 		&cli.StringFlag{Name: "filename", Aliases: []string{"n"}, Usage: "Filename for the attachment"}, | ||||
| 		&cli.StringFlag{Name: "filename", Aliases: []string{"name", "n"}, Usage: "Filename for the attachment"}, | ||||
| 		&cli.StringFlag{Name: "file", Aliases: []string{"f"}, Usage: "File to upload as an attachment"}, | ||||
| 		&cli.StringFlag{Name: "email", Aliases: []string{"e-mail", "mail", "e"}, Usage: "also send to e-mail address"}, | ||||
| 		&cli.BoolFlag{Name: "no-cache", Aliases: []string{"C"}, Usage: "do not cache message server-side"}, | ||||
|  |  | |||
|  | @ -36,13 +36,13 @@ Subscribers can retrieve cached messaging using the [`poll=1` parameter](subscri | |||
| [`since=` parameter](subscribe/api.md#fetch-cached-messages). | ||||
| 
 | ||||
| ## Attachments | ||||
| If desired, you may allow users to upload and [attach files to notifications](publish.md#attachments-send-files). To enable | ||||
| If desired, you may allow users to upload and [attach files to notifications](publish.md#attachments). To enable | ||||
| this feature, you have to simply configure an attachment cache directory and a base URL (`attachment-cache-dir`, `base-url`).  | ||||
| Once these options are set and the directory is writable by the server user, you can upload attachments via PUT. | ||||
| 
 | ||||
| By default, attachments are stored in the disk-case **for only 3 hours**. The main reason for this is to avoid legal issues | ||||
| and such when hosting user controlled content. Typically, this is more than enough time for the user (or the phone) to download  | ||||
| the file. The following config options are relevant to attachments: | ||||
| By default, attachments are stored in the disk-cache **for only 3 hours**. The main reason for this is to avoid legal issues | ||||
| and such when hosting user controlled content. Typically, this is more than enough time for the user (or the auto download  | ||||
| feature) to download the file. The following config options are relevant to attachments: | ||||
| 
 | ||||
| * `base-url` is the root URL for the ntfy server; this is needed for the generated attachment URLs | ||||
| * `attachment-cache-dir` is the cache directory for attached files | ||||
|  | @ -356,8 +356,15 @@ request every 10s (defined by `visitor-request-limit-replenish`) | |||
| * `visitor-request-limit-replenish` is the rate at which the bucket is refilled (one request per x). Defaults to 10s. | ||||
| 
 | ||||
| ### Attachment limits | ||||
| Aside from the global file size and total attachment cache limits (see [above](#attachments)), there are two relevant  | ||||
| per-visitor limits: | ||||
| 
 | ||||
| XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx | ||||
| * `visitor-attachment-total-size-limit` is the total storage limit used for attachments per visitor. It defaults to 100M. | ||||
|   The per-visitor storage is automatically decreased as attachments expire. External attachments (attached via `X-Attach`,  | ||||
|   see [publishing docs](publish.md#attachments)) do not count here.  | ||||
| * `visitor-attachment-daily-bandwidth-limit` is the total daily attachment download/upload bandwidth limit per visitor,  | ||||
|   including PUT and GET requests. This is to protect your precious bandwidth from abuse, since egress costs money in | ||||
|   most cloud providers. This defaults to 500M. | ||||
| 
 | ||||
| ### E-mail limits | ||||
| Similarly to the request limit, there is also an e-mail limit (only relevant if [e-mail notifications](#e-mail-notifications)  | ||||
|  | @ -471,7 +478,7 @@ CLI option (e.g. `--listen-http :80`. Here's a list of all available options. Al | |||
| variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`). | ||||
| 
 | ||||
| | Config option                              | Env variable                                    | Format           | Default | Description                                                                                                                                                                                                                     | | ||||
| |---|---|---|---|---| | ||||
| |--------------------------------------------|-------------------------------------------------|------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ||||
| | `base-url`                                 | `NTFY_BASE_URL`                                 | *URL*            | -       | Public facing base URL of the service (e.g. `https://ntfy.sh`)                                                                                                                                                                  | | ||||
| | `listen-http`                              | `NTFY_LISTEN_HTTP`                              | `[host]:port`    | `:80`   | Listen address for the HTTP web server                                                                                                                                                                                          | | ||||
| | `listen-https`                             | `NTFY_LISTEN_HTTPS`                             | `[host]:port`    | -       | Listen address for the HTTPS web server. If set, you also need to set `key-file` and `cert-file`.                                                                                                                               | | ||||
|  | @ -480,12 +487,11 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`). | |||
| | `firebase-key-file`                        | `NTFY_FIREBASE_KEY_FILE`                        | *filename*       | -       | If set, also publish messages to a Firebase Cloud Messaging (FCM) topic for your app. This is optional and only required to save battery when using the Android app. See [Firebase (FCM](#firebase-fcm).                        | | ||||
| | `cache-file`                               | `NTFY_CACHE_FILE`                               | *filename*       | -       | If set, messages are cached in a local SQLite database instead of only in-memory. This allows for service restarts without losing messages in support of the since= parameter. See [message cache](#message-cache).             | | ||||
| | `cache-duration`                           | `NTFY_CACHE_DURATION`                           | *duration*       | 12h     | Duration for which messages will be buffered before they are deleted. This is required to support the `since=...` and `poll=1` parameter. Set this to `0` to disable the cache entirely.                                        | | ||||
| | `behind-proxy`                             | `NTFY_BEHIND_PROXY`                             | *bool*           | false   | If set, the X-Forwarded-For header is used to determine the visitor IP address instead of the remote address of the connection.                                                                                                 | | ||||
| | `attachment-cache-dir`                     | `NTFY_ATTACHMENT_CACHE_DIR`                     | *directory*      | -       | Cache directory for attached files. To enable attachments, this has to be set.                                                                                                                                                  | | ||||
| | `attachment-total-size-limit`              | `NTFY_ATTACHMENT_TOTAL_SIZE_LIMIT`              | *size*           | 5G      | Limit of the on-disk attachment cache directory. If the limits is exceeded, new attachments will be rejected.                                                                                                                   | | ||||
| | `attachment-file-size-limit`               | `NTFY_ATTACHMENT_FILE_SIZE_LIMIT`               | *size*           | 15M     | Per-file attachment size limit (e.g. 300k, 2M, 100M). Larger attachment will be rejected.                                                                                                                                       | | ||||
| | `attachment-expiry-duration`               | `NTFY_ATTACHMENT_EXPIRY_DURATION`               | *duration*       | 3h      | Duration after which uploaded attachments will be deleted (e.g. 3h, 20h). Strongly affects `visitor-attachment-total-size-limit`.                                                                                               | | ||||
| | `keepalive-interval` | `NTFY_KEEPALIVE_INTERVAL` | *duration* | 55s | Interval in which keepalive messages are sent to the client. This is to prevent intermediaries closing the connection for inactivity. Note that the Android app has a hardcoded timeout at 77s, so it should be less than that. | | ||||
| | `manager-interval` | `$NTFY_MANAGER_INTERVAL` | *duration* | 1m | Interval in which the manager prunes old messages, deletes topics and prints the stats. | | ||||
| | `smtp-sender-addr`                         | `NTFY_SMTP_SENDER_ADDR`                         | `host:port`      | -       | SMTP server address to allow email sending                                                                                                                                                                                      | | ||||
| | `smtp-sender-user`                         | `NTFY_SMTP_SENDER_USER`                         | *string*         | -       | SMTP user; only used if e-mail sending is enabled                                                                                                                                                                               | | ||||
| | `smtp-sender-pass`                         | `NTFY_SMTP_SENDER_PASS`                         | *string*         | -       | SMTP password; only used if e-mail sending is enabled                                                                                                                                                                           | | ||||
|  | @ -493,15 +499,16 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`). | |||
| | `smtp-server-listen`                       | `NTFY_SMTP_SERVER_LISTEN`                       | `[ip]:port`      | -       | Defines the IP address and port the SMTP server will listen on, e.g. `:25` or `1.2.3.4:25`                                                                                                                                      | | ||||
| | `smtp-server-domain`                       | `NTFY_SMTP_SERVER_DOMAIN`                       | *domain name*    | -       | SMTP server e-mail domain, e.g. `ntfy.sh`                                                                                                                                                                                       | | ||||
| | `smtp-server-addr-prefix`                  | `NTFY_SMTP_SERVER_ADDR_PREFIX`                  | `[ip]:port`      | -       | Optional prefix for the e-mail addresses to prevent spam, e.g. `ntfy-`                                                                                                                                                          | | ||||
| | `keepalive-interval`                       | `NTFY_KEEPALIVE_INTERVAL`                       | *duration*       | 55s     | Interval in which keepalive messages are sent to the client. This is to prevent intermediaries closing the connection for inactivity. Note that the Android app has a hardcoded timeout at 77s, so it should be less than that. | | ||||
| | `manager-interval`                         | `$NTFY_MANAGER_INTERVAL`                        | *duration*       | 1m      | Interval in which the manager prunes old messages, deletes topics and prints the stats.                                                                                                                                         | | ||||
| | `global-topic-limit`                       | `NTFY_GLOBAL_TOPIC_LIMIT`                       | *number*         | 15,000  | Rate limiting: Total number of topics before the server rejects new topics.                                                                                                                                                     | | ||||
| | `visitor-subscription-limit`               | `NTFY_VISITOR_SUBSCRIPTION_LIMIT`               | *number*         | 30      | Rate limiting: Number of subscriptions per visitor (IP address)                                                                                                                                                                 | | ||||
| | `visitor-attachment-total-size-limit` | `NTFY_VISITOR_ATTACHMENT_TOTAL_SIZE_LIMIT` | *size* | 100M | Total storage limit used for attachments per visitor, for all attachments combined. Storage is freed after attachments expire. See `attachment-expiry-duration`. | | ||||
| | `visitor-attachment-daily-bandwidth-limit` | `NTFY_VISITOR_ATTACHMENT_DAILY_BANDWIDTH_LIMIT` | *size* | 500M | Total daily attachment download/upload traffic limit per visitor. This is to protect your bandwidth costs from exploding. | | ||||
| | `visitor-request-limit-burst` | `NTFY_VISITOR_REQUEST_LIMIT_BURST` | *number* | 60 | Allowed GET/PUT/POST requests per second, per visitor. This setting is the initial bucket of requests each visitor has | | ||||
| | `visitor-request-limit-replenish` | `NTFY_VISITOR_REQUEST_LIMIT_REPLENISH` | *duration* | 10s | Strongly related to `visitor-request-limit-burst`: The rate at which the bucket is refilled | | ||||
| | `visitor-email-limit-burst` | `NTFY_VISITOR_EMAIL_LIMIT_BURST` | *number* | 16 | Initial limit of e-mails per visitor | | ||||
| | `visitor-email-limit-replenish` | `NTFY_VISITOR_EMAIL_LIMIT_REPLENISH` | *duration* | 1h | Strongly related to `visitor-email-limit-burst`: The rate at which the bucket is refilled | | ||||
| | `behind-proxy` | `NTFY_BEHIND_PROXY` | *bool* | false | If set, the X-Forwarded-For header is used to determine the visitor IP address instead of the remote address of the connection. | | ||||
| | `visitor-attachment-total-size-limit`      | `NTFY_VISITOR_ATTACHMENT_TOTAL_SIZE_LIMIT`      | *size*           | 100M    | Rate limiting: Total storage limit used for attachments per visitor, for all attachments combined. Storage is freed after attachments expire. See `attachment-expiry-duration`.                                                 | | ||||
| | `visitor-attachment-daily-bandwidth-limit` | `NTFY_VISITOR_ATTACHMENT_DAILY_BANDWIDTH_LIMIT` | *size*           | 500M    | Rate limiting: Total daily attachment download/upload traffic limit per visitor. This is to protect your bandwidth costs from exploding.                                                                                        | | ||||
| | `visitor-request-limit-burst`              | `NTFY_VISITOR_REQUEST_LIMIT_BURST`              | *number*         | 60      | Rate limiting: Allowed GET/PUT/POST requests per second, per visitor. This setting is the initial bucket of requests each visitor has                                                                                           | | ||||
| | `visitor-request-limit-replenish`          | `NTFY_VISITOR_REQUEST_LIMIT_REPLENISH`          | *duration*       | 10s     | Rate limiting: Strongly related to `visitor-request-limit-burst`: The rate at which the bucket is refilled                                                                                                                      | | ||||
| | `visitor-email-limit-burst`                | `NTFY_VISITOR_EMAIL_LIMIT_BURST`                | *number*         | 16      | Rate limiting:Initial limit of e-mails per visitor                                                                                                                                                                              | | ||||
| | `visitor-email-limit-replenish`            | `NTFY_VISITOR_EMAIL_LIMIT_REPLENISH`            | *duration*       | 1h      | Rate limiting: Strongly related to `visitor-email-limit-burst`: The rate at which the bucket is refilled                                                                                                                        | | ||||
| 
 | ||||
| The format for a *duration* is: `<number>(smh)`, e.g. 30s, 20m or 1h.    | ||||
| The format for a *size* is: `<number>(GMK)`, e.g. 1G, 200M or 4000k. | ||||
|  |  | |||
							
								
								
									
										150
									
								
								docs/publish.md
									
										
									
									
									
								
							
							
						
						
									
										150
									
								
								docs/publish.md
									
										
									
									
									
								
							|  | @ -659,26 +659,33 @@ Here's an example that will open Reddit when the notification is clicked: | |||
|     ])); | ||||
|     ``` | ||||
| 
 | ||||
| ## Attachments (send files) | ||||
| ## Attachments | ||||
| You can send images and other files to your phone as attachments to a notification. The attachments are then downloaded | ||||
| onto your phone (depending on size and setting automatically), and can be used from the Downloads folder. | ||||
| 
 | ||||
| There are two different ways to send attachments, either via PUT or by passing an external URL.   | ||||
| There are two different ways to send attachments:  | ||||
| 
 | ||||
| **Upload attachments from your computer**: To send an attachment from your computer as a file, you can send it as the  | ||||
| PUT request body. If a message is greater than the maximum message size or consists of non-UTF-8 characters, the ntfy  | ||||
| server will automatically detect the mime type and size, and send the message as an attachment file.  | ||||
| * sending [a local file](#attach-local-file) via PUT, e.g. from `~/Flowers/flower.jpg` or `ringtone.mp3` | ||||
| * or by [passing an external URL](#attach-file-from-a-url) as an attachment, e.g. `https://f-droid.org/F-Droid.apk`  | ||||
| 
 | ||||
| You can optionally pass a filename (or force attachment mode for small text-messages) by passing the `X-Filename` header | ||||
| or query parameter (or any of its aliases `Filename`, `File` or `f`).  | ||||
| ### Attach local file | ||||
| To send an attachment from your computer as a file, you can send it as the PUT request body. If a message is greater  | ||||
| than the maximum message size (4,096 bytes) or consists of non UTF-8 characters, the ntfy server will automatically  | ||||
| detect the mime type and size, and send the message as an attachment file. To send smaller text-only messages or files  | ||||
| as attachments, you must pass a filename by passing the `X-Filename` header or query parameter (or any of its aliases  | ||||
| `Filename`, `File` or `f`).  | ||||
| 
 | ||||
| By default, and how ntfy.sh is configured, the **max attachment size is 15 MB** (with 100 MB total per visitor).  | ||||
| Attachments **expire after 3 hours**, which typically is plenty of time for the user to download it, or for the Android app | ||||
| to auto-download it. Please also check out the [other limits below](#limitations). | ||||
| 
 | ||||
| Here's an example showing how to upload an image: | ||||
| 
 | ||||
| 
 | ||||
| === "Command line (curl)" | ||||
|     ``` | ||||
|     curl \ | ||||
|         -T flower.jpg \ | ||||
|         -H "Filename: flower.jpg" \ | ||||
|         ntfy.sh/flowers | ||||
|     ``` | ||||
| 
 | ||||
|  | @ -693,6 +700,7 @@ Here's an example showing how to upload an image: | |||
|     ``` http | ||||
|     PUT /flowers HTTP/1.1 | ||||
|     Host: ntfy.sh | ||||
|     Filename: flower.jpg | ||||
| 
 | ||||
|     <binary JPEG data> | ||||
|     ``` | ||||
|  | @ -701,7 +709,8 @@ Here's an example showing how to upload an image: | |||
|     ``` javascript | ||||
|     fetch('https://ntfy.sh/flowers', { | ||||
|         method: 'PUT', | ||||
|         body: document.getElementById("file").files[0] | ||||
|         body: document.getElementById("file").files[0], | ||||
|         headers: { 'Filename': 'flower.jpg' } | ||||
|     }) | ||||
|     ``` | ||||
| 
 | ||||
|  | @ -709,45 +718,109 @@ Here's an example showing how to upload an image: | |||
|     ``` go | ||||
|     file, _ := os.Open("flower.jpg") | ||||
|     req, _ := http.NewRequest("PUT", "https://ntfy.sh/flowers", file) | ||||
|     req.Header.Set("Filename", "flower.jpg") | ||||
|     http.DefaultClient.Do(req) | ||||
|     ``` | ||||
| 
 | ||||
| === "Python" | ||||
|     ``` python | ||||
|     requests.put("https://ntfy.sh/flowers", | ||||
|         data=open("flower.jpg", 'rb')) | ||||
|         data=open("flower.jpg", 'rb'), | ||||
|         headers={ "Filename": "flower.jpg" }) | ||||
|     ``` | ||||
| 
 | ||||
| === "PHP" | ||||
|     ``` php-inline | ||||
|     file_get_contents('https://ntfy.sh/reddit_alerts', false, stream_context_create([ | ||||
|     file_get_contents('https://ntfy.sh/flowers', false, stream_context_create([ | ||||
|         'http' => [ | ||||
|             'method' => 'PUT', | ||||
|             'content' => XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXxx  | ||||
|             'header' => | ||||
|                 "Content-Type: application/octet-stream\r\n" . // Does not matter | ||||
|                 "Filename: flower.jpg", | ||||
|             'content' => file_get_contents('flower.jpg') // Dangerous for large files  | ||||
|         ] | ||||
|     ])); | ||||
|     ``` | ||||
| 
 | ||||
| Here's what that looks like on Android: | ||||
| 
 | ||||
| <figure markdown> | ||||
|   { width=500 } | ||||
|   <figcaption>Image attachment sent from a local file</figcaption> | ||||
| </figure> | ||||
| 
 | ||||
| ### Attach file from a URL | ||||
| Instead of sending a local file to your phone, you can use an external URL to specify where the attachment is hosted. | ||||
| This could be a Google Drive or Dropbox link, or any other publicly available URL. The ntfy server will briefly probe | ||||
| the URL to retrieve type and size for you. Since the files are externally hosted, the expiration or size limits from  | ||||
| above do not apply here. | ||||
| 
 | ||||
| To attach an external file, simple pass the `X-Attach` header or query parameter (or any of its aliases `Attach` or `a`) | ||||
| to specify the attachment URL. It can be any type of file. | ||||
| 
 | ||||
| Here's an example showing how to upload an image: | ||||
| 
 | ||||
| === "Command line (curl)" | ||||
|     ``` | ||||
| - Uploaded attachment | ||||
| - External attachment | ||||
| - Preview without attachment  | ||||
| 
 | ||||
| 
 | ||||
| # Upload and send attachment with custom message and filename | ||||
|     curl \ | ||||
|     -T flower.jpg \ | ||||
|     -H "Message: Here's a flower for you" \ | ||||
|     -H "Filename: flower.jpg" \ | ||||
|     ntfy.sh/howdy | ||||
| 
 | ||||
| # Send external attachment from other URL, with custom message  | ||||
| curl \ | ||||
|     -H "Attachment: https://example.com/files.zip" \ | ||||
|     "ntfy.sh/howdy?m=Important+documents+attached" | ||||
| 
 | ||||
|         -X POST \ | ||||
|         -H "Attach: https://f-droid.org/F-Droid.apk" \ | ||||
|         ntfy.sh/mydownloads | ||||
|     ``` | ||||
| 
 | ||||
| === "ntfy CLI" | ||||
|     ``` | ||||
|     ntfy publish \ | ||||
|         --attach="https://f-droid.org/F-Droid.apk" \ | ||||
|         mydownloads | ||||
|     ``` | ||||
| 
 | ||||
| === "HTTP" | ||||
|     ``` http | ||||
|     POST /mydownloads HTTP/1.1 | ||||
|     Host: ntfy.sh | ||||
|     Attach: https://f-droid.org/F-Droid.apk | ||||
|     ``` | ||||
| 
 | ||||
| === "JavaScript" | ||||
|     ``` javascript | ||||
|     fetch('https://ntfy.sh/mydownloads', { | ||||
|         method: 'POST', | ||||
|         headers: { 'Attach': 'https://f-droid.org/F-Droid.apk' } | ||||
|     }) | ||||
|     ``` | ||||
| 
 | ||||
| === "Go" | ||||
|     ``` go | ||||
|     req, _ := http.NewRequest("POST", "https://ntfy.sh/mydownloads", file) | ||||
|     req.Header.Set("Attach", "https://f-droid.org/F-Droid.apk") | ||||
|     http.DefaultClient.Do(req) | ||||
|     ``` | ||||
| 
 | ||||
| === "Python" | ||||
|     ``` python | ||||
|     requests.put("https://ntfy.sh/mydownloads", | ||||
|         headers={ "Attach": "https://f-droid.org/F-Droid.apk" }) | ||||
|     ``` | ||||
| 
 | ||||
| === "PHP" | ||||
|     ``` php-inline | ||||
|     file_get_contents('https://ntfy.sh/mydownloads', false, stream_context_create([ | ||||
|         'http' => [ | ||||
|         'method' => 'PUT', | ||||
|         'header' => | ||||
|             "Content-Type: text/plain\r\n" . // Does not matter | ||||
|             "Attach: https://f-droid.org/F-Droid.apk", | ||||
|         ] | ||||
|     ])); | ||||
|     ``` | ||||
| 
 | ||||
| <figure markdown> | ||||
|   { width=500 } | ||||
|   <figcaption>File attachment sent from an external URL</figcaption> | ||||
| </figure> | ||||
| 
 | ||||
| 
 | ||||
| ## E-mail notifications | ||||
| You can forward messages to e-mail by specifying an address in the header. This can be useful for messages that  | ||||
| you'd like to persist longer, or to blast-notify yourself on all possible channels.  | ||||
|  | @ -1029,16 +1102,19 @@ parameter (or any of its aliases `unifiedpush` or `up`) to `1` to [disable Fireb | |||
| option is equivalent to `Firebase: no`, but was introduced to allow future flexibility. | ||||
| 
 | ||||
| ## Limitations | ||||
| There are a few limitations to the API to prevent abuse and to keep the server healthy. Most of them you won't run into, | ||||
| There are a few limitations to the API to prevent abuse and to keep the server healthy. Almost all of these settings  | ||||
| are configurable via the server side [rate limiting settings](config.md#rate-limiting). Most of these limits you won't run into, | ||||
| but just in case, let's list them all: | ||||
| 
 | ||||
| | Limit                     | Description                                                                                                                                                               | | ||||
| |---|---| | ||||
| | **Message length** | Each message can be up to 4,096 bytes long. Longer messages are truncated. | | ||||
| | **Requests** | By default, the server is configured to allow 60 requests at once, and then refills the your allowed requests bucket at a rate of one request per 10 seconds. You can read more about this in the [rate limiting](config.md#rate-limiting) section. | | ||||
| | **E-mails** | By default, the server is configured to allow sending 16 e-mails at once, and then refills the your allowed e-mail bucket at a rate of one per hour. You can read more about this in the [rate limiting](config.md#rate-limiting) section. | | ||||
| | **Subscription limits** | By default, the server allows each visitor to keep 30 connections to the server open. | | ||||
| | **Bandwidth** | By default, the server allows 500 MB of GET/PUT/POST traffic for attachments per visitor in a 24 hour period. Traffic exceeding that is rejected. | | ||||
| |---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ||||
| | **Message length**        | Each message can be up to 4,096 bytes long. Longer messages are treated as [attachments](#attachments).                                                                   | | ||||
| | **Requests**              | By default, the server is configured to allow 60 requests per visitor at once, and then refills the your allowed requests bucket at a rate of one request per 10 seconds. | | ||||
| | **E-mails**               | By default, the server is configured to allow sending 16 e-mails per visitor at once, and then refills the your allowed e-mail bucket at a rate of one per hour.          | | ||||
| | **Subscription limit**    | By default, the server allows each visitor to keep 30 connections to the server open.                                                                                     | | ||||
| | **Attachment size limit** | By default, the server allows attachments up to 15 MB in size, up to 100 MB in total per visitor and up to 5 GB across all visitors.                                      | | ||||
| | **Attachment expiry**     | By default, the server deletes attachments after 3 hours and thereby frees up space from the total visitor attachment limit.                                              | | ||||
| | **Attachment bandwidth**  | By default, the server allows 500 MB of GET/PUT/POST traffic for attachments per visitor in a 24 hour period. Traffic exceeding that is rejected.                         | | ||||
| | **Total number of topics** | By default, the server is configured to allow 15,000 topics. The ntfy.sh server has higher limits though.                                                                 | | ||||
| 
 | ||||
| ## List of all parameters | ||||
|  | @ -1053,8 +1129,8 @@ and can be passed as **HTTP headers** or **query parameters in the URL**. They a | |||
| | `X-Tags` | `Tags`, `Tag`, `ta` | [Tags and emojis](#tags-emojis) | | ||||
| | `X-Delay` | `Delay`, `X-At`, `At`, `X-In`, `In` | Timestamp or duration for [delayed delivery](#scheduled-delivery) | | ||||
| | `X-Click` | `Click` | URL to open when [notification is clicked](#click-action) | | ||||
| | `X-Attach` | `Attach`, `a` | URL to send as an [attachment](#attachments-send-files), as an alternative to PUT/POST-ing an attachment | | ||||
| | `X-Filename` | `Filename`, `file`, `f` | Optional [attachment](#attachments-send-files) filename, as it appears in the client | | ||||
| | `X-Attach` | `Attach`, `a` | URL to send as an [attachment](#attachments), as an alternative to PUT/POST-ing an attachment | | ||||
| | `X-Filename` | `Filename`, `file`, `f` | Optional [attachment](#attachments) filename, as it appears in the client | | ||||
| | `X-Email` | `X-E-Mail`, `Email`, `E-Mail`, `mail`, `e` | E-mail address for [e-mail notifications](#e-mail-notifications) | | ||||
| | `X-Cache` | `Cache` | Allows disabling [message caching](#message-caching) | | ||||
| | `X-Firebase` | `Firebase` | Allows disabling [sending to Firebase](#disable-firebase) | | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								docs/static/img/android-screenshot-attachment-file.png
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/static/img/android-screenshot-attachment-file.png
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 52 KiB | 
							
								
								
									
										
											BIN
										
									
								
								docs/static/img/android-screenshot-attachment-image.png
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/static/img/android-screenshot-attachment-image.png
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 156 KiB | 
|  | @ -131,7 +131,8 @@ func (c *memCache) AttachmentsSize(owner string) (int64, error) { | |||
| 	var size int64 | ||||
| 	for topic := range c.messages { | ||||
| 		for _, m := range c.messages[topic] { | ||||
| 			if m.Attachment != nil && m.Attachment.Owner == owner { | ||||
| 			counted := m.Attachment != nil && m.Attachment.Owner == owner && m.Attachment.Expires > time.Now().Unix() | ||||
| 			if counted { | ||||
| 				size += m.Attachment.Size | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
|  | @ -25,6 +25,10 @@ func TestMemCache_Prune(t *testing.T) { | |||
| 	testCachePrune(t, newMemCache()) | ||||
| } | ||||
| 
 | ||||
| func TestMemCache_Attachments(t *testing.T) { | ||||
| 	testCacheAttachments(t, newMemCache()) | ||||
| } | ||||
| 
 | ||||
| func TestMemCache_NopCache(t *testing.T) { | ||||
| 	c := newNopCache() | ||||
| 	assert.Nil(t, c.AddMessage(newDefaultMessage("mytopic", "my message"))) | ||||
|  |  | |||
|  | @ -29,6 +29,10 @@ func TestSqliteCache_Prune(t *testing.T) { | |||
| 	testCachePrune(t, newSqliteTestCache(t)) | ||||
| } | ||||
| 
 | ||||
| func TestSqliteCache_Attachments(t *testing.T) { | ||||
| 	testCacheAttachments(t, newSqliteTestCache(t)) | ||||
| } | ||||
| 
 | ||||
| func TestSqliteCache_Migration_From0(t *testing.T) { | ||||
| 	filename := newSqliteTestCacheFile(t) | ||||
| 	db, err := sql.Open("sqlite3", filename) | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| package server | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| ) | ||||
|  | @ -13,71 +13,71 @@ func testCacheMessages(t *testing.T, c cache) { | |||
| 	m2 := newDefaultMessage("mytopic", "my other message") | ||||
| 	m2.Time = 2 | ||||
| 
 | ||||
| 	assert.Nil(t, c.AddMessage(m1)) | ||||
| 	assert.Nil(t, c.AddMessage(newDefaultMessage("example", "my example message"))) | ||||
| 	assert.Nil(t, c.AddMessage(m2)) | ||||
| 	require.Nil(t, c.AddMessage(m1)) | ||||
| 	require.Nil(t, c.AddMessage(newDefaultMessage("example", "my example message"))) | ||||
| 	require.Nil(t, c.AddMessage(m2)) | ||||
| 
 | ||||
| 	// Adding invalid | ||||
| 	assert.Equal(t, errUnexpectedMessageType, c.AddMessage(newKeepaliveMessage("mytopic"))) // These should not be added! | ||||
| 	assert.Equal(t, errUnexpectedMessageType, c.AddMessage(newOpenMessage("example")))      // These should not be added! | ||||
| 	require.Equal(t, errUnexpectedMessageType, c.AddMessage(newKeepaliveMessage("mytopic"))) // These should not be added! | ||||
| 	require.Equal(t, errUnexpectedMessageType, c.AddMessage(newOpenMessage("example")))      // These should not be added! | ||||
| 
 | ||||
| 	// mytopic: count | ||||
| 	count, err := c.MessageCount("mytopic") | ||||
| 	assert.Nil(t, err) | ||||
| 	assert.Equal(t, 2, count) | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, 2, count) | ||||
| 
 | ||||
| 	// mytopic: since all | ||||
| 	messages, _ := c.Messages("mytopic", sinceAllMessages, false) | ||||
| 	assert.Equal(t, 2, len(messages)) | ||||
| 	assert.Equal(t, "my message", messages[0].Message) | ||||
| 	assert.Equal(t, "mytopic", messages[0].Topic) | ||||
| 	assert.Equal(t, messageEvent, messages[0].Event) | ||||
| 	assert.Equal(t, "", messages[0].Title) | ||||
| 	assert.Equal(t, 0, messages[0].Priority) | ||||
| 	assert.Nil(t, messages[0].Tags) | ||||
| 	assert.Equal(t, "my other message", messages[1].Message) | ||||
| 	require.Equal(t, 2, len(messages)) | ||||
| 	require.Equal(t, "my message", messages[0].Message) | ||||
| 	require.Equal(t, "mytopic", messages[0].Topic) | ||||
| 	require.Equal(t, messageEvent, messages[0].Event) | ||||
| 	require.Equal(t, "", messages[0].Title) | ||||
| 	require.Equal(t, 0, messages[0].Priority) | ||||
| 	require.Nil(t, messages[0].Tags) | ||||
| 	require.Equal(t, "my other message", messages[1].Message) | ||||
| 
 | ||||
| 	// mytopic: since none | ||||
| 	messages, _ = c.Messages("mytopic", sinceNoMessages, false) | ||||
| 	assert.Empty(t, messages) | ||||
| 	require.Empty(t, messages) | ||||
| 
 | ||||
| 	// mytopic: since 2 | ||||
| 	messages, _ = c.Messages("mytopic", sinceTime(time.Unix(2, 0)), false) | ||||
| 	assert.Equal(t, 1, len(messages)) | ||||
| 	assert.Equal(t, "my other message", messages[0].Message) | ||||
| 	require.Equal(t, 1, len(messages)) | ||||
| 	require.Equal(t, "my other message", messages[0].Message) | ||||
| 
 | ||||
| 	// example: count | ||||
| 	count, err = c.MessageCount("example") | ||||
| 	assert.Nil(t, err) | ||||
| 	assert.Equal(t, 1, count) | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, 1, count) | ||||
| 
 | ||||
| 	// example: since all | ||||
| 	messages, _ = c.Messages("example", sinceAllMessages, false) | ||||
| 	assert.Equal(t, "my example message", messages[0].Message) | ||||
| 	require.Equal(t, "my example message", messages[0].Message) | ||||
| 
 | ||||
| 	// non-existing: count | ||||
| 	count, err = c.MessageCount("doesnotexist") | ||||
| 	assert.Nil(t, err) | ||||
| 	assert.Equal(t, 0, count) | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, 0, count) | ||||
| 
 | ||||
| 	// non-existing: since all | ||||
| 	messages, _ = c.Messages("doesnotexist", sinceAllMessages, false) | ||||
| 	assert.Empty(t, messages) | ||||
| 	require.Empty(t, messages) | ||||
| } | ||||
| 
 | ||||
| func testCacheTopics(t *testing.T, c cache) { | ||||
| 	assert.Nil(t, c.AddMessage(newDefaultMessage("topic1", "my example message"))) | ||||
| 	assert.Nil(t, c.AddMessage(newDefaultMessage("topic2", "message 1"))) | ||||
| 	assert.Nil(t, c.AddMessage(newDefaultMessage("topic2", "message 2"))) | ||||
| 	assert.Nil(t, c.AddMessage(newDefaultMessage("topic2", "message 3"))) | ||||
| 	require.Nil(t, c.AddMessage(newDefaultMessage("topic1", "my example message"))) | ||||
| 	require.Nil(t, c.AddMessage(newDefaultMessage("topic2", "message 1"))) | ||||
| 	require.Nil(t, c.AddMessage(newDefaultMessage("topic2", "message 2"))) | ||||
| 	require.Nil(t, c.AddMessage(newDefaultMessage("topic2", "message 3"))) | ||||
| 
 | ||||
| 	topics, err := c.Topics() | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	assert.Equal(t, 2, len(topics)) | ||||
| 	assert.Equal(t, "topic1", topics["topic1"].ID) | ||||
| 	assert.Equal(t, "topic2", topics["topic2"].ID) | ||||
| 	require.Equal(t, 2, len(topics)) | ||||
| 	require.Equal(t, "topic1", topics["topic1"].ID) | ||||
| 	require.Equal(t, "topic2", topics["topic2"].ID) | ||||
| } | ||||
| 
 | ||||
| func testCachePrune(t *testing.T, c cache) { | ||||
|  | @ -90,23 +90,23 @@ func testCachePrune(t *testing.T, c cache) { | |||
| 	m3 := newDefaultMessage("another_topic", "and another one") | ||||
| 	m3.Time = 1 | ||||
| 
 | ||||
| 	assert.Nil(t, c.AddMessage(m1)) | ||||
| 	assert.Nil(t, c.AddMessage(m2)) | ||||
| 	assert.Nil(t, c.AddMessage(m3)) | ||||
| 	assert.Nil(t, c.Prune(time.Unix(2, 0))) | ||||
| 	require.Nil(t, c.AddMessage(m1)) | ||||
| 	require.Nil(t, c.AddMessage(m2)) | ||||
| 	require.Nil(t, c.AddMessage(m3)) | ||||
| 	require.Nil(t, c.Prune(time.Unix(2, 0))) | ||||
| 
 | ||||
| 	count, err := c.MessageCount("mytopic") | ||||
| 	assert.Nil(t, err) | ||||
| 	assert.Equal(t, 1, count) | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, 1, count) | ||||
| 
 | ||||
| 	count, err = c.MessageCount("another_topic") | ||||
| 	assert.Nil(t, err) | ||||
| 	assert.Equal(t, 0, count) | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, 0, count) | ||||
| 
 | ||||
| 	messages, err := c.Messages("mytopic", sinceAllMessages, false) | ||||
| 	assert.Nil(t, err) | ||||
| 	assert.Equal(t, 1, len(messages)) | ||||
| 	assert.Equal(t, "my other message", messages[0].Message) | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, 1, len(messages)) | ||||
| 	require.Equal(t, "my other message", messages[0].Message) | ||||
| } | ||||
| 
 | ||||
| func testCacheMessagesTagsPrioAndTitle(t *testing.T, c cache) { | ||||
|  | @ -114,12 +114,12 @@ func testCacheMessagesTagsPrioAndTitle(t *testing.T, c cache) { | |||
| 	m.Tags = []string{"tag1", "tag2"} | ||||
| 	m.Priority = 5 | ||||
| 	m.Title = "some title" | ||||
| 	assert.Nil(t, c.AddMessage(m)) | ||||
| 	require.Nil(t, c.AddMessage(m)) | ||||
| 
 | ||||
| 	messages, _ := c.Messages("mytopic", sinceAllMessages, false) | ||||
| 	assert.Equal(t, []string{"tag1", "tag2"}, messages[0].Tags) | ||||
| 	assert.Equal(t, 5, messages[0].Priority) | ||||
| 	assert.Equal(t, "some title", messages[0].Title) | ||||
| 	require.Equal(t, []string{"tag1", "tag2"}, messages[0].Tags) | ||||
| 	require.Equal(t, 5, messages[0].Priority) | ||||
| 	require.Equal(t, "some title", messages[0].Title) | ||||
| } | ||||
| 
 | ||||
| func testCacheMessagesScheduled(t *testing.T, c cache) { | ||||
|  | @ -130,20 +130,93 @@ func testCacheMessagesScheduled(t *testing.T, c cache) { | |||
| 	m3.Time = time.Now().Add(time.Minute).Unix() // earlier than m2! | ||||
| 	m4 := newDefaultMessage("mytopic2", "message 4") | ||||
| 	m4.Time = time.Now().Add(time.Minute).Unix() | ||||
| 	assert.Nil(t, c.AddMessage(m1)) | ||||
| 	assert.Nil(t, c.AddMessage(m2)) | ||||
| 	assert.Nil(t, c.AddMessage(m3)) | ||||
| 	require.Nil(t, c.AddMessage(m1)) | ||||
| 	require.Nil(t, c.AddMessage(m2)) | ||||
| 	require.Nil(t, c.AddMessage(m3)) | ||||
| 
 | ||||
| 	messages, _ := c.Messages("mytopic", sinceAllMessages, false) // exclude scheduled | ||||
| 	assert.Equal(t, 1, len(messages)) | ||||
| 	assert.Equal(t, "message 1", messages[0].Message) | ||||
| 	require.Equal(t, 1, len(messages)) | ||||
| 	require.Equal(t, "message 1", messages[0].Message) | ||||
| 
 | ||||
| 	messages, _ = c.Messages("mytopic", sinceAllMessages, true) // include scheduled | ||||
| 	assert.Equal(t, 3, len(messages)) | ||||
| 	assert.Equal(t, "message 1", messages[0].Message) | ||||
| 	assert.Equal(t, "message 3", messages[1].Message) // Order! | ||||
| 	assert.Equal(t, "message 2", messages[2].Message) | ||||
| 	require.Equal(t, 3, len(messages)) | ||||
| 	require.Equal(t, "message 1", messages[0].Message) | ||||
| 	require.Equal(t, "message 3", messages[1].Message) // Order! | ||||
| 	require.Equal(t, "message 2", messages[2].Message) | ||||
| 
 | ||||
| 	messages, _ = c.MessagesDue() | ||||
| 	assert.Empty(t, messages) | ||||
| 	require.Empty(t, messages) | ||||
| } | ||||
| 
 | ||||
| func testCacheAttachments(t *testing.T, c cache) { | ||||
| 	expires1 := time.Now().Add(-4 * time.Hour).Unix() | ||||
| 	m := newDefaultMessage("mytopic", "flower for you") | ||||
| 	m.ID = "m1" | ||||
| 	m.Attachment = &attachment{ | ||||
| 		Name:    "flower.jpg", | ||||
| 		Type:    "image/jpeg", | ||||
| 		Size:    5000, | ||||
| 		Expires: expires1, | ||||
| 		URL:     "https://ntfy.sh/file/AbDeFgJhal.jpg", | ||||
| 		Owner:   "1.2.3.4", | ||||
| 	} | ||||
| 	require.Nil(t, c.AddMessage(m)) | ||||
| 
 | ||||
| 	expires2 := time.Now().Add(2 * time.Hour).Unix() // Future | ||||
| 	m = newDefaultMessage("mytopic", "sending you a car") | ||||
| 	m.ID = "m2" | ||||
| 	m.Attachment = &attachment{ | ||||
| 		Name:    "car.jpg", | ||||
| 		Type:    "image/jpeg", | ||||
| 		Size:    10000, | ||||
| 		Expires: expires2, | ||||
| 		URL:     "https://ntfy.sh/file/aCaRURL.jpg", | ||||
| 		Owner:   "1.2.3.4", | ||||
| 	} | ||||
| 	require.Nil(t, c.AddMessage(m)) | ||||
| 
 | ||||
| 	expires3 := time.Now().Add(1 * time.Hour).Unix() // Future | ||||
| 	m = newDefaultMessage("another-topic", "sending you another car") | ||||
| 	m.ID = "m3" | ||||
| 	m.Attachment = &attachment{ | ||||
| 		Name:    "another-car.jpg", | ||||
| 		Type:    "image/jpeg", | ||||
| 		Size:    20000, | ||||
| 		Expires: expires3, | ||||
| 		URL:     "https://ntfy.sh/file/zakaDHFW.jpg", | ||||
| 		Owner:   "1.2.3.4", | ||||
| 	} | ||||
| 	require.Nil(t, c.AddMessage(m)) | ||||
| 
 | ||||
| 	messages, err := c.Messages("mytopic", sinceAllMessages, false) | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, 2, len(messages)) | ||||
| 
 | ||||
| 	require.Equal(t, "flower for you", messages[0].Message) | ||||
| 	require.Equal(t, "flower.jpg", messages[0].Attachment.Name) | ||||
| 	require.Equal(t, "image/jpeg", messages[0].Attachment.Type) | ||||
| 	require.Equal(t, int64(5000), messages[0].Attachment.Size) | ||||
| 	require.Equal(t, expires1, messages[0].Attachment.Expires) | ||||
| 	require.Equal(t, "https://ntfy.sh/file/AbDeFgJhal.jpg", messages[0].Attachment.URL) | ||||
| 	require.Equal(t, "1.2.3.4", messages[0].Attachment.Owner) | ||||
| 
 | ||||
| 	require.Equal(t, "sending you a car", messages[1].Message) | ||||
| 	require.Equal(t, "car.jpg", messages[1].Attachment.Name) | ||||
| 	require.Equal(t, "image/jpeg", messages[1].Attachment.Type) | ||||
| 	require.Equal(t, int64(10000), messages[1].Attachment.Size) | ||||
| 	require.Equal(t, expires2, messages[1].Attachment.Expires) | ||||
| 	require.Equal(t, "https://ntfy.sh/file/aCaRURL.jpg", messages[1].Attachment.URL) | ||||
| 	require.Equal(t, "1.2.3.4", messages[1].Attachment.Owner) | ||||
| 
 | ||||
| 	size, err := c.AttachmentsSize("1.2.3.4") | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, int64(30000), size) | ||||
| 
 | ||||
| 	size, err = c.AttachmentsSize("5.6.7.8") | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, int64(0), size) | ||||
| 
 | ||||
| 	ids, err := c.AttachmentsExpired() | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, []string{"m1"}, ids) | ||||
| } | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ | |||
| # | ||||
| # You can disable the cache entirely by setting this to 0. | ||||
| # | ||||
| # cache-duration: 12h | ||||
| # cache-duration: "12h" | ||||
| 
 | ||||
| # If set, the X-Forwarded-For header is used to determine the visitor IP address | ||||
| # instead of the remote address of the connection. | ||||
|  | @ -46,6 +46,19 @@ | |||
| # | ||||
| # behind-proxy: false | ||||
| 
 | ||||
| # If enabled, clients can attach files to notifications as attachments. Minimum settings to enable attachments | ||||
| # are "attachment-cache-dir" and "base-url". | ||||
| # | ||||
| # - attachment-cache-dir is the cache directory for attached files | ||||
| # - attachment-total-size-limit is the limit of the on-disk attachment cache directory (total size) | ||||
| # - attachment-file-size-limit is the per-file attachment size limit (e.g. 300k, 2M, 100M) | ||||
| # - attachment-expiry-duration is the duration after which uploaded attachments will be deleted (e.g. 3h, 20h) | ||||
| # | ||||
| # attachment-cache-dir: | ||||
| # attachment-total-size-limit: "5G" | ||||
| # attachment-file-size-limit: "15M" | ||||
| # attachment-expiry-duration: "3h" | ||||
| 
 | ||||
| # If enabled, allow outgoing e-mail notifications via the 'X-Email' header. If this header is set, | ||||
| # messages will additionally be sent out as e-mail using an external SMTP server. As of today, only | ||||
| # SMTP servers with plain text auth and STARTLS are supported. Please also refer to the rate limiting settings | ||||
|  | @ -78,12 +91,12 @@ | |||
| # | ||||
| # Note that the Android app has a hardcoded timeout at 77s, so it should be less than that. | ||||
| # | ||||
| # keepalive-interval: 30s | ||||
| # keepalive-interval: "30s" | ||||
| 
 | ||||
| # Interval in which the manager prunes old messages, deletes topics | ||||
| # and prints the stats. | ||||
| # | ||||
| # manager-interval: 1m | ||||
| # manager-interval: "1m" | ||||
| 
 | ||||
| # Rate limiting: Total number of topics before the server rejects new topics. | ||||
| # | ||||
|  | @ -98,11 +111,18 @@ | |||
| # - visitor-request-limit-replenish is the rate at which the bucket is refilled | ||||
| # | ||||
| # visitor-request-limit-burst: 60 | ||||
| # visitor-request-limit-replenish: 10s | ||||
| # visitor-request-limit-replenish: "10s" | ||||
| 
 | ||||
| # Rate limiting: Allowed emails per visitor: | ||||
| # - visitor-email-limit-burst is the initial bucket of emails each visitor has | ||||
| # - visitor-email-limit-replenish is the rate at which the bucket is refilled | ||||
| # | ||||
| # visitor-email-limit-burst: 16 | ||||
| # visitor-email-limit-replenish: 1h | ||||
| # visitor-email-limit-replenish: "1h" | ||||
| 
 | ||||
| # Rate limiting: Attachment size and bandwidth limits per visitor: | ||||
| # - visitor-attachment-total-size-limit is the total storage limit used for attachments per visitor | ||||
| # - visitor-attachment-daily-bandwidth-limit is the total daily attachment download/upload traffic limit per visitor | ||||
| # | ||||
| # visitor-attachment-total-size-limit: "100M" | ||||
| # visitor-attachment-daily-bandwidth-limit: "500M" | ||||
|  |  | |||
|  | @ -699,12 +699,21 @@ func TestServer_PublishAttachment(t *testing.T) { | |||
| 	require.Equal(t, 200, response.Code) | ||||
| 	require.Equal(t, "5000", response.Header().Get("Content-Length")) | ||||
| 	require.Equal(t, content, response.Body.String()) | ||||
| 
 | ||||
| 	// Slightly unrelated cross-test: make sure we add an owner for internal attachments | ||||
| 	size, err := s.cache.AttachmentsSize("9.9.9.9") // See request() | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, int64(5000), size) | ||||
| } | ||||
| 
 | ||||
| func TestServer_PublishAttachmentShortWithFilename(t *testing.T) { | ||||
| 	s := newTestServer(t, newTestConfig(t)) | ||||
| 	c := newTestConfig(t) | ||||
| 	c.BehindProxy = true | ||||
| 	s := newTestServer(t, c) | ||||
| 	content := "this is an ATTACHMENT" | ||||
| 	response := request(t, s, "PUT", "/mytopic?f=myfile.txt", content, nil) | ||||
| 	response := request(t, s, "PUT", "/mytopic?f=myfile.txt", content, map[string]string{ | ||||
| 		"X-Forwarded-For": "1.2.3.4", | ||||
| 	}) | ||||
| 	msg := toMessage(t, response.Body.String()) | ||||
| 	require.Equal(t, "myfile.txt", msg.Attachment.Name) | ||||
| 	require.Equal(t, "text/plain; charset=utf-8", msg.Attachment.Type) | ||||
|  | @ -719,6 +728,11 @@ func TestServer_PublishAttachmentShortWithFilename(t *testing.T) { | |||
| 	require.Equal(t, 200, response.Code) | ||||
| 	require.Equal(t, "21", response.Header().Get("Content-Length")) | ||||
| 	require.Equal(t, content, response.Body.String()) | ||||
| 
 | ||||
| 	// Slightly unrelated cross-test: make sure we add an owner for internal attachments | ||||
| 	size, err := s.cache.AttachmentsSize("1.2.3.4") | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, int64(21), size) | ||||
| } | ||||
| 
 | ||||
| func TestServer_PublishAttachmentExternalWithoutFilename(t *testing.T) { | ||||
|  | @ -734,6 +748,11 @@ func TestServer_PublishAttachmentExternalWithoutFilename(t *testing.T) { | |||
| 	require.Equal(t, int64(0), msg.Attachment.Expires) | ||||
| 	require.Equal(t, "https://upload.wikimedia.org/wikipedia/commons/f/fd/Pink_flower.jpg", msg.Attachment.URL) | ||||
| 	require.Equal(t, "", msg.Attachment.Owner) | ||||
| 
 | ||||
| 	// Slightly unrelated cross-test: make sure we don't add an owner for external attachments | ||||
| 	size, err := s.cache.AttachmentsSize("127.0.0.1") | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, int64(0), size) | ||||
| } | ||||
| 
 | ||||
| func TestServer_PublishAttachmentExternalWithFilename(t *testing.T) { | ||||
|  | @ -914,6 +933,7 @@ func request(t *testing.T, s *Server, method, url, body string, headers map[stri | |||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	req.RemoteAddr = "9.9.9.9" // Used for tests | ||||
| 	for k, v := range headers { | ||||
| 		req.Header.Set(k, v) | ||||
| 	} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue