Compare commits

...

47 Commits

Author SHA1 Message Date
binwiederhier
396e61cdb3 Bump go build version in CI 2023-02-14 22:00:04 -05:00
binwiederhier
dfaab8c386 Bump version 2023-02-14 21:45:03 -05:00
binwiederhier
f2f5a06be1 Bump JS deps 2023-02-14 20:58:29 -05:00
binwiederhier
8d7ff4d7db SMTP server tests 2023-02-14 20:56:02 -05:00
binwiederhier
9f052bdf8b Merge branch 'main' into smtp-lib-upgrade 2023-02-14 14:44:09 -05:00
binwiederhier
5472c8513f Release notes 2023-02-14 14:40:41 -05:00
binwiederhier
c028ec9083 Merge branch 'patch-1' 2023-02-14 14:39:34 -05:00
binwiederhier
31a87935a5 Refine iOS docs 2023-02-14 14:39:22 -05:00
binwiederhier
9e20ee35e1 Thanks to @overtone1000 and @Joachim256 for your sponsorship and donation 2023-02-12 21:13:26 -05:00
binwiederhier
0d4ef18358 Merge branch 'main' of https://hosted.weblate.org/git/ntfy/web 2023-02-12 21:11:16 -05:00
SticksDev
8bde80a3d2 Add iOS docs to the dev docs
Imports old dev docs
Also adds my currently open PR #10 on the docs to improve them.
2023-02-12 21:08:37 -05:00
Rycoh
020f561ad4 Translated using Weblate (Romanian)
Currently translated at 4.7% (9 of 189 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/ro/
2023-02-11 19:36:39 +01:00
Rycoh
432cc2003e Added translation using Weblate (Romanian) 2023-02-10 18:55:34 +01:00
binwiederhier
aea8a6d04b Thanks @IanKulin for your donation 2023-02-07 23:23:00 -05:00
binwiederhier
e449f0bda4 Examples 2023-02-07 23:22:29 -05:00
binwiederhier
ff3cb6c5cc Merge branch 'main' of github.com:binwiederhier/ntfy 2023-02-07 23:21:15 -05:00
binwiederhier
2b4f7ab56f Merge branch 'main' of https://hosted.weblate.org/git/ntfy/web 2023-02-07 23:21:09 -05:00
Philipp C. Heckel
f5a8216be6 Merge pull request #604 from Y0ngg4n/update-jellyseerr-docs
Update jellyseerr docs
2023-02-07 23:20:48 -05:00
Yonggan
3779b4a923 Update examples.md 2023-02-07 15:00:21 +01:00
Yonggan
9738e4a225 Fix identation 2023-02-07 14:04:09 +01:00
Yonggan
0905016b1f Update Jellyseerr/Overseerr docs 2023-02-07 14:03:13 +01:00
Tmpod
5f8ecfaf81 Translated using Weblate (Portuguese)
Currently translated at 100.0% (189 of 189 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/pt/
2023-02-03 14:37:52 +01:00
binwiederhier
8da46afab4 Thank you @zoic21 for your donation 2023-02-02 15:21:35 -05:00
bjornclauw
facf4684ae Translated using Weblate (Dutch)
Currently translated at 100.0% (189 of 189 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/nl/
2023-01-27 13:44:17 +01:00
binwiederhier
2624897efe Merge branch 'main' of github.com:binwiederhier/ntfy 2023-01-26 11:26:23 -05:00
binwiederhier
df6f53a161 Add Shoutrrr integration 2023-01-26 11:26:11 -05:00
Philipp C. Heckel
b941551fff Thanks to @billycao for your sponsorship 2023-01-25 22:27:47 -05:00
binwiederhier
471775ae49 Remove upx references 2023-01-24 14:57:50 -05:00
binwiederhier
3d84bdf77b Thanks to @andreapx for your donation 2023-01-24 10:32:11 -05:00
Philipp C. Heckel
8668143127 Update FUNDING.yml 2023-01-24 10:25:56 -05:00
Philipp C. Heckel
295bad59bb Merge pull request #594 from jpbaril/patch-1
Elements requiring chown to run non-root Docker
2023-01-22 07:41:24 -05:00
Jean-Philippe Baril
804ee3b298 Elements requiring chown to run non-root Docker
We also have to chown the attachments directory otherwise the docker container does not start and crashes.
BTW, all that should be automated at the container creation.
Because it took me at least an hour to understand that the only way to accomplish that chown command was to first launch the container as root, run the commands, and only then edit docker-compose.yml to add uid/gid. After that I could restart the container and it would now not crash.
2023-01-22 04:32:30 -05:00
binwiederhier
75c07221ef Added n8n-ntfy 2023-01-21 16:23:15 -05:00
binwiederhier
b82794df05 Thank you @julianlam for your sponsorship 2023-01-21 16:20:24 -05:00
binwiederhier
167656b38e Blog post 2023-01-21 15:19:52 -05:00
binwiederhier
5d81f875cb Merge branch 'main' of github.com:binwiederhier/ntfy 2023-01-21 15:17:48 -05:00
binwiederhier
6ae200e338 Added Portuguese 2023-01-21 15:17:30 -05:00
binwiederhier
ab6b902fb5 Merge branch 'main' of https://hosted.weblate.org/git/ntfy/web 2023-01-21 15:14:31 -05:00
Philipp C. Heckel
9f423b01ef Merge pull request #593 from julianlam/patch-1
Add NodeBB to integrations page
2023-01-21 15:14:25 -05:00
Julian Lam
c863c86f4c Update integrations.md
+nodebb
2023-01-21 13:57:42 -05:00
Philipp C. Heckel
2bd27a5d0b Merge pull request #588 from jamolnng/patch-1
add blog post for unRAID notifications
2023-01-19 13:23:22 -05:00
Philipp C. Heckel
cff8f88920 Update README.md 2023-01-19 12:05:26 -05:00
Jesse Laning
87f5479662 add blog post for unRAID notifications 2023-01-18 23:16:34 -05:00
ssantos
2ec13c64f3 Translated using Weblate (Portuguese)
Currently translated at 100.0% (189 of 189 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/pt/
2023-01-11 16:54:38 +01:00
Nifou
c916eeb9d7 Translated using Weblate (French)
Currently translated at 100.0% (189 of 189 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/fr/
2023-01-11 16:54:38 +01:00
Zoe
8ee85a4007 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (189 of 189 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/nb_NO/
2023-01-11 16:54:37 +01:00
binwiederhier
36c0be1097 Upgrade smtp library, but not tests 2023-01-04 09:31:32 -05:00
23 changed files with 1388 additions and 12835 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1 +1,2 @@
github: [binwiederhier]
liberapay: ntfy

View File

@@ -8,7 +8,7 @@ jobs:
name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.x'
go-version: '1.19.x'
-
name: Install node
uses: actions/setup-node@v2

View File

@@ -11,7 +11,7 @@ jobs:
name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.x'
go-version: '1.19.x'
-
name: Install node
uses: actions/setup-node@v2

View File

@@ -8,7 +8,7 @@ jobs:
name: Install Go
uses: actions/setup-go@v2
with:
go-version: '1.18.x'
go-version: '1.19.x'
-
name: Install node
uses: actions/setup-node@v2

View File

@@ -88,7 +88,6 @@ build-deps-ubuntu:
curl \
gcc-aarch64-linux-gnu \
gcc-arm-linux-gnueabi \
upx \
jq
which pip3 || sudo apt install -y python3-pip
@@ -201,7 +200,6 @@ cli-deps-static-sites:
touch server/docs/index.html server/site/app.html
cli-deps-all:
which upx || { echo "ERROR: upx not installed. On Ubuntu, run: apt install upx"; exit 1; }
go install github.com/goreleaser/goreleaser@latest
cli-deps-gcc-armv6-armv7:

View File

@@ -61,9 +61,9 @@ for the server and the Android app. Or, if you'd like to help translate 🇩🇪
</a>
## Sponsors
I have just very recently started accepting donations via [GitHub Sponsors](https://github.com/sponsors/binwiederhier).
I would be humbled if you helped me carry the server and developer account costs. Even small donations are very much
appreciated. A big fat **Thank You** to the folks already sponsoring ntfy:
I have just very recently started accepting donations via [GitHub Sponsors](https://github.com/sponsors/binwiederhier),
and [Liberapay](https://liberapay.com/ntfy). I would be humbled if you helped me carry the server and developer
account costs. Even small donations are very much appreciated. A big fat **Thank You** to the folks already sponsoring ntfy:
<a href="https://github.com/neutralinsomniac"><img src="https://github.com/neutralinsomniac.png" width="40px" /></a>
<a href="https://github.com/aspyct"><img src="https://github.com/aspyct.png" width="40px" /></a>
@@ -110,11 +110,18 @@ appreciated. A big fat **Thank You** to the folks already sponsoring ntfy:
<a href="https://github.com/biopsin"><img src="https://github.com/biopsin.png" width="40px" /></a>
<a href="https://github.com/thebino"><img src="https://github.com/thebino.png" width="40px" /></a>
<a href="https://github.com/sky4055"><img src="https://github.com/sky4055.png" width="40px" /></a>
<a href="https://github.com/julianlam"><img src="https://github.com/julianlam.png" width="40px" /></a>
<a href="https://github.com/andreapx"><img src="https://github.com/andreapx.png" width="40px" /></a>
<a href="https://github.com/billycao"><img src="https://github.com/billycao.png" width="40px" /></a>
<a href="https://github.com/zoic21"><img src="https://github.com/zoic21.png" width="40px" /></a>
<a href="https://github.com/IanKulin"><img src="https://github.com/IanKulin.png" width="40px" /></a>
<a href="https://github.com/Joachim256"><img src="https://github.com/Joachim256.png" width="40px" /></a>
<a href="https://github.com/overtone1000"><img src="https://github.com/overtone1000.png" width="40px" /></a>
I'd also like to thank JetBrains for providing their awesome [IntelliJ IDEA](https://www.jetbrains.com/idea/) to me for free,
and [DigitalOcean](https://www.digitalocean.com/) for supporting the project:
and [DigitalOcean](https://m.do.co/c/442b929528db) (*referral link*) for supporting the project:
<a href="https://www.digitalocean.com/"><img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px"></a>
<a href="https://m.do.co/c/442b929528db"><img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px"></a>
## Code of Conduct
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.

View File

@@ -92,7 +92,6 @@ sudo apt install \
gcc-arm-linux-gnueabi \
gcc-aarch64-linux-gnu \
python3-pip \
upx \
git
```
@@ -328,7 +327,76 @@ To build your own version with Firebase, you must:
```
## iOS app
The ntfy iOS app source code is available [on GitHub](https://github.com/binwiederhier/ntfy-ios).
Building the iOS app is very involved. Please report any inconsistencies or issues with it. The requirements are
strictly based off of my development on this app. There may be other versions of macOS / XCode that work.
### Requirements
1. macOS Monterey or later
1. XCode 13.2+
1. A physical iOS device (for push notifications, Firebase does not work in the XCode simulator)
1. Firebase account
1. Apple Developer license? (I forget if it's possible to do testing without purchasing the license)
### Apple setup
!!! info
I haven't had time to move the build instructions here. Please check out the repository instead.
Along with this step, the [PLIST Deployment](#plist-deployment-and-configuration) step is also required
for these changes to take effect in the iOS app.
1. [Create a new key in Apple Developer Member Center](https://developer.apple.com/account/resources/authkeys/add)
1. Select "Apple Push Notifications service (APNs)"
1. Download the newly created key (should have a file name similar to `AuthKey_ZZZZZZ.p8`, where `ZZZZZZ` is the **Key ID**)
1. Record your **Team ID** - it can be seen in the top-right corner of the page, or on your Account > Membership page
1. Next, navigate to "Project Settings" in the firebase console for your project, and select the iOS app you created. Then, click "Cloud Messaging" in the left sidebar, and scroll down to the "APNs Authentication Key" section. Click "Upload Key", and upload the key you downloaded from Apple Developer.
!!! warning
If you don't do the above setups for APNS, **notifications will not post instantly or sometimes at all**. This is because of the missing APNS key, which is required for firebase to send notifications to the iOS app. See below for a snip from the firebase docs.
If you don't have an APNs authentication key, you can still send notifications to iOS devices, but they won't be delivered
instantly. Instead, they'll be delivered when the device wakes up to check for new notifications or when your application
sends a firebase request to check for them. The time to check for new notifications can vary from a few seconds to hours,
days or even weeks. Enabling APNs authentication keys ensures that notifications are delivered instantly and is strongly
recommended.
### Firebase setup
1. If you haven't already, create a Google / Firebase account
1. Visit the [Firebase console](https://console.firebase.google.com)
1. Create a new Firebase project:
1. Enter a project name
1. Disable Google Analytics (currently iOS app does not support analytics)
1. On the "Project settings" page, add an iOS app
1. Apple bundle ID - "com.copephobia.ntfy-ios" (this can be changed to match XCode's ntfy.sh target > "Bundle Identifier" value)
1. Register the app
1. Download the config file - GoogleInfo.plist (this will need to be included in the ntfy-ios repository / XCode)
1. Generate a new service account private key for the ntfy server
1. Go to "Project settings" > "Service accounts"
1. Click "Generate new private key" to generate and download a private key to use for sending messages via the ntfy server
### ntfy server
Note that the ntfy server is not officially supported on macOS. It should, however, be able to run on macOS using these
steps:
1. If not already made, make the `/etc/ntfy/` directory and move the service account private key to that folder
1. Copy the `server/server.yml` file from the ntfy repository to `/etc/ntfy/`
1. Modify the `/etc/ntfy/server.yml` file `firebase-key-file` value to the path of the private key
1. Install go: `brew install go`
1. In the ntfy repository, run `make cli-darwin-server`.
### XCode setup
1. Follow step 4 of [https://firebase.google.com/docs/ios/setup](Add Firebase to your Apple project) to install the
`firebase-ios-sdk` in XCode, if it's not already present - you can select any packages in addition to Firebase Core / Firebase Messaging
1. Similarly, install the SQLite.swift package dependency in XCode
1. When running the debug build, ensure XCode is pointed to the connected iOS device - registering for push notifications does not work in the iOS simulators
### PLIST config
To have instant notifications/better notification delivery when using firebase, you will need to add the
`GoogleService-Info.plist` file to your project. Here's how to do that:
1. In XCode, find the NTFY app target. **Not** the NSE app target.
1. Find the Asset/ folder in the project navigator
1. Drag the `GoogleService-Info.plist` file into the Asset/ folder that you get from the firebase console. It can be
found in the "Project settings" > "General" > "Your apps" with a button labled "GoogleService-Info.plist"
After that, you should be all set!

View File

@@ -413,7 +413,8 @@ alerting:
## Jellyseerr/Overseerr webhook
Here is an example for [jellyseerr](https://github.com/Fallenbagel/jellyseerr)/[overseerr](https://overseerr.dev/) webhook
JSON payload. Remember to change the `https://requests.example.com` to your jellyseerr/overseerr URL.
JSON payload. Remember to change the `https://request.example.com` to your URL as the value of the JSON key click.
And if you're not using the request `topic`, make sure to change it in the JSON payload to your topic.
``` json
{

View File

@@ -26,37 +26,37 @@ deb/rpm packages.
=== "x86_64/amd64"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_x86_64.tar.gz
tar zxvf ntfy_1.30.1_linux_x86_64.tar.gz
sudo cp -a ntfy_1.30.1_linux_x86_64/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_1.30.1_linux_x86_64/{client,server}/*.yml /etc/ntfy
wget https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_x86_64.tar.gz
tar zxvf ntfy_1.31.0_linux_x86_64.tar.gz
sudo cp -a ntfy_1.31.0_linux_x86_64/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_1.31.0_linux_x86_64/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
```
=== "armv6"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_armv6.tar.gz
tar zxvf ntfy_1.30.1_linux_armv6.tar.gz
sudo cp -a ntfy_1.30.1_linux_armv6/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_1.30.1_linux_armv6/{client,server}/*.yml /etc/ntfy
wget https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_armv6.tar.gz
tar zxvf ntfy_1.31.0_linux_armv6.tar.gz
sudo cp -a ntfy_1.31.0_linux_armv6/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_1.31.0_linux_armv6/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
```
=== "armv7/armhf"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_armv7.tar.gz
tar zxvf ntfy_1.30.1_linux_armv7.tar.gz
sudo cp -a ntfy_1.30.1_linux_armv7/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_1.30.1_linux_armv7/{client,server}/*.yml /etc/ntfy
wget https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_armv7.tar.gz
tar zxvf ntfy_1.31.0_linux_armv7.tar.gz
sudo cp -a ntfy_1.31.0_linux_armv7/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_1.31.0_linux_armv7/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
```
=== "arm64"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_arm64.tar.gz
tar zxvf ntfy_1.30.1_linux_arm64.tar.gz
sudo cp -a ntfy_1.30.1_linux_arm64/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_1.30.1_linux_arm64/{client,server}/*.yml /etc/ntfy
wget https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_arm64.tar.gz
tar zxvf ntfy_1.31.0_linux_arm64.tar.gz
sudo cp -a ntfy_1.31.0_linux_arm64/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_1.31.0_linux_arm64/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
```
@@ -106,7 +106,7 @@ Manually installing the .deb file:
=== "x86_64/amd64"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_amd64.deb
wget https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_amd64.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
@@ -114,7 +114,7 @@ Manually installing the .deb file:
=== "armv6"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_armv6.deb
wget https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_armv6.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
@@ -122,7 +122,7 @@ Manually installing the .deb file:
=== "armv7/armhf"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_armv7.deb
wget https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_armv7.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
@@ -130,7 +130,7 @@ Manually installing the .deb file:
=== "arm64"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_arm64.deb
wget https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_arm64.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
@@ -140,28 +140,28 @@ Manually installing the .deb file:
=== "x86_64/amd64"
```bash
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_amd64.rpm
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_amd64.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
```
=== "armv6"
```bash
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_armv6.rpm
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_armv6.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
```
=== "armv7/armhf"
```bash
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_armv7.rpm
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_armv7.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
```
=== "arm64"
```bash
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_linux_arm64.rpm
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_linux_arm64.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
```
@@ -189,18 +189,18 @@ NixOS also supports [declarative setup of the ntfy server](https://search.nixos.
## macOS
The [ntfy CLI](subscribe/cli.md) (`ntfy publish` and `ntfy subscribe` only) is supported on macOS as well.
To install, please [download the tarball](https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_macOS_all.tar.gz),
To install, please [download the tarball](https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_macOS_all.tar.gz),
extract it and place it somewhere in your `PATH` (e.g. `/usr/local/bin/ntfy`).
If run as `root`, ntfy will look for its config at `/etc/ntfy/client.yml`. For all other users, it'll look for it at
`~/Library/Application Support/ntfy/client.yml` (sample included in the tarball).
```bash
curl -L https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_macOS_all.tar.gz > ntfy_1.30.1_macOS_all.tar.gz
tar zxvf ntfy_1.30.1_macOS_all.tar.gz
sudo cp -a ntfy_1.30.1_macOS_all/ntfy /usr/local/bin/ntfy
curl -L https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_macOS_all.tar.gz > ntfy_1.31.0_macOS_all.tar.gz
tar zxvf ntfy_1.31.0_macOS_all.tar.gz
sudo cp -a ntfy_1.31.0_macOS_all/ntfy /usr/local/bin/ntfy
mkdir ~/Library/Application\ Support/ntfy
cp ntfy_1.30.1_macOS_all/client/client.yml ~/Library/Application\ Support/ntfy/client.yml
cp ntfy_1.31.0_macOS_all/client/client.yml ~/Library/Application\ Support/ntfy/client.yml
ntfy --help
```
@@ -212,7 +212,7 @@ ntfy --help
## Windows
The [ntfy CLI](subscribe/cli.md) (`ntfy publish` and `ntfy subscribe` only) is supported on Windows as well.
To install, please [download the latest ZIP](https://github.com/binwiederhier/ntfy/releases/download/v1.30.1/ntfy_1.30.1_windows_x86_64.zip),
To install, please [download the latest ZIP](https://github.com/binwiederhier/ntfy/releases/download/v1.31.0/ntfy_1.31.0_windows_x86_64.zip),
extract it and place the `ntfy.exe` binary somewhere in your `%Path%`.
The default path for the client config file is at `%AppData%\ntfy\client.yml` (not created automatically, sample in the ZIP file).
@@ -287,7 +287,7 @@ services:
restart: unless-stopped
```
If using a non-root user when running the docker version, be sure to chown the server.yml, user.db, and cache.db files to the same uid/gid.
If using a non-root user when running the docker version, be sure to chown the server.yml, user.db, and cache.db files and attachments directory to the same uid/gid.
Alternatively, you may wish to build a customized Docker image that can be run with fewer command-line arguments and without delivering the configuration file separately.
```

View File

@@ -32,6 +32,7 @@ and uptime of third party servers, so use of each server is **at your own discre
- [Gatus](https://gatus.io/) ⭐ - Automated service health dashboard
- [Automatisch](https://automatisch.io/) ⭐ - Open source Zapier alternative / workflow automation tool
- [FlexGet](https://flexget.com/Plugins/Notifiers/ntfysh) ⭐ - Multipurpose automation tool for all of your media
- [Shoutrrr](https://containrrr.dev/shoutrrr/v0.7/services/ntfy/) ⭐ - Notification library for gophers and their furry friends.
- [Scrt.link](https://scrt.link/) - Share a secret
- [Platypush](https://docs.platypush.tech/platypush/plugins/ntfy.html) - Automation platform aimed to run on any device that can run Python
@@ -107,9 +108,12 @@ and uptime of third party servers, so use of each server is **at your own discre
- [ignition-ntfy-module](https://github.com/Kyvis-Labs/ignition-ntfy-module) - Adds support for sending notifications via a ntfy server to Ignition (Java)
- [maubot-ntfy](https://gitlab.com/999eagle/maubot-ntfy) - Matrix bot to subscribe to ntfy topics and send messages to Matrix (Python)
- [ntfy-wrapper](https://github.com/vict0rsch/ntfy-wrapper) - Wrapper around ntfy (Python)
- [nodebb-plugin-ntfy](https://github.com/NodeBB/nodebb-plugin-ntfy) - Push notifications for NodeBB forums
- [n8n-ntfy](https://github.com/raghavanand98/n8n-ntfy.sh) - n8n community node that lets you use ntfy in your workflows
## Blog + forum posts
- [January 2023 Developer Update](https://community.nodebb.org/topic/16908/january-2023-developer-update) - nodebb.org - 1/2023
- [Comment envoyer des notifications push sur votre téléphone facilement et gratuitement?](https://korben.info/notifications-push-telephone.html) - 1/2023
- [UnifiedPush: a decentralized, open-source push notification protocol](https://f-droid.org/en/2022/12/18/unifiedpush.html) ⭐ - 12/2022
- [ntfy setup instructions](https://docs.benjamin-altpeter.de/network/vms/1001029-ntfy/) - benjamin-altpeter.de - 12/2022
@@ -127,6 +131,7 @@ and uptime of third party servers, so use of each server is **at your own discre
- [Ntfy.sh Send push notifications to your phone via PUT/POST](https://news.ycombinator.com/item?id=33517944) ⭐ - news.ycombinator.com - 11/2022
- [Ntfy et Jeedom : un plugin](https://lunarok-domotique.com/2022/11/ntfy-et-jeedom/) - lunarok-domotique.com - 11/2022
- [Crea tu propio servidor de notificaciones con Ntfy](https://blog.parravidales.es/crea-tu-propio-servidor-de-notificaciones-con-ntfy/) - blog.parravidales.es - 11/2022
- [unRAID Notifications with ntfy.sh](https://lder.dev/posts/ntfy-Notifications-With-unRAID/) - lder.dev - 10/2022
- [Zero-cost push notifications to your phone or desktop via PUT/POST ](https://lobste.rs/s/41dq13/zero_cost_push_notifications_your_phone) - lobste.rs - 10/2022
- [A nifty push notification system: ntfy](https://jpmens.net/2022/10/30/a-nifty-push-notification-system-ntfy/) - jpmens.net - 10/2022
- [Alarmanlage der dritten Art (YouTube video)](https://www.youtube.com/watch?v=altb5QLHbaU&feature=youtu.be) - youtube.com - 10/2022

View File

@@ -2,7 +2,13 @@
Binaries for all releases can be found on the GitHub releases pages for the [ntfy server](https://github.com/binwiederhier/ntfy/releases)
and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/releases).
## ntfy server v1.31.0 (UNRELEASED)
## ntfy server v1.31.0
Released February 14, 2023
This is a tiny release before the really big release, and also the last before the big v2.0.0. The most interesting
things in this release are the new preliminary health endpoint to allow monitoring in K8s (and others), and the removal
of `upx` binary packing (which was causing erroneous virus flagging). Aside from that, the `go-smtp` library did a
breaking-change upgrade, which required some work to get working again.
**Features:**
@@ -13,12 +19,19 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
* Fix `chown` issues with RHEL-like based systems ([#566](https://github.com/binwiederhier/ntfy/issues/566)/[#565](https://github.com/binwiederhier/ntfy/pull/565), thanks to [@danieldemus](https://github.com/danieldemus))
* Removed `upx` (binary packing) for all builds due to false virus warnings ([#576](https://github.com/binwiederhier/ntfy/issues/576), thanks to [@shawnhwei](https://github.com/shawnhwei) for reporting)
* Upgraded `go-smtp` library and tests to v0.16.0 ([#569](https://github.com/binwiederhier/ntfy/issues/569))
**Documentation:**
* Add HTTP/2 and TLSv1.3 support to nginx docs ([#553](https://github.com/binwiederhier/ntfy/issues/553), thanks to [@bt90](https://github.com/bt90))
* Small wording change for `client.yml` ([#562](https://github.com/binwiederhier/ntfy/pull/562), thanks to [@fleopaulD](https://github.com/fleopaulD))
* Fix K8s install docs ([#582](https://github.com/binwiederhier/ntfy/pull/582), thanks to [@Remedan](https://github.com/Remedan))
* Updated Jellyseer docs ([#604](https://github.com/binwiederhier/ntfy/pull/604), thanks to [@Y0ngg4n](https://github.com/Y0ngg4n))
* Updated iOS developer docs ([#605](https://github.com/binwiederhier/ntfy/pull/605), thanks to [@SticksDev](https://github.com/SticksDev))
**Additional languages:**
* Portuguese (thanks to [@ssantos](https://hosted.weblate.org/user/ssantos/))
## ntfy server v1.30.1
Released December 23, 2022 🎅

34
go.mod
View File

@@ -4,22 +4,22 @@ go 1.18
require (
cloud.google.com/go/firestore v1.9.0 // indirect
cloud.google.com/go/storage v1.28.1 // indirect
cloud.google.com/go/storage v1.29.0 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/emersion/go-smtp v0.15.0
github.com/emersion/go-smtp v0.16.0
github.com/gabriel-vasile/mimetype v1.4.1
github.com/gorilla/websocket v1.5.0
github.com/mattn/go-sqlite3 v1.14.16
github.com/olebedev/when v0.0.0-20221205223600-4d190b02b8d8
github.com/stretchr/testify v1.8.1
github.com/urfave/cli/v2 v2.23.7
golang.org/x/crypto v0.4.0
golang.org/x/oauth2 v0.3.0 // indirect
github.com/urfave/cli/v2 v2.24.3
golang.org/x/crypto v0.6.0
golang.org/x/oauth2 v0.5.0 // indirect
golang.org/x/sync v0.1.0
golang.org/x/term v0.3.0
golang.org/x/term v0.5.0
golang.org/x/time v0.3.0
google.golang.org/api v0.105.0
google.golang.org/api v0.110.0
gopkg.in/yaml.v2 v2.4.0
)
@@ -28,11 +28,11 @@ require github.com/pkg/errors v0.9.1 // indirect
require firebase.google.com/go/v4 v4.10.0
require (
cloud.google.com/go v0.107.0 // indirect
cloud.google.com/go/compute v1.14.0 // indirect
cloud.google.com/go v0.109.0 // indirect
cloud.google.com/go/compute v1.18.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v0.9.0 // indirect
cloud.google.com/go/longrunning v0.3.0 // indirect
cloud.google.com/go/iam v0.10.0 // indirect
cloud.google.com/go/longrunning v0.4.1 // indirect
github.com/AlekSi/pointer v1.2.0 // indirect
github.com/MicahParks/keyfunc v1.9.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@@ -42,20 +42,20 @@ require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/appengine/v2 v2.0.2 // indirect
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
google.golang.org/grpc v1.51.0 // indirect
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc // indirect
google.golang.org/grpc v1.53.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

97
go.sum
View File

@@ -1,34 +1,25 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww=
cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I=
cloud.google.com/go/compute v1.13.0 h1:AYrLkB8NPdDRslNp4Jxmzrhdr03fUAIDbiGFjLWowoU=
cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE=
cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0=
cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo=
cloud.google.com/go/compute/metadata v0.2.2 h1:aWKAjYaBaOSrpKl57+jnS/3fJRQnxL7TvR/u1VVbt6k=
cloud.google.com/go/compute/metadata v0.2.2/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
cloud.google.com/go v0.109.0 h1:38CZoKGlCnPZjGdyj0ZfpoGae0/wgNfy5F0byyxg0Gk=
cloud.google.com/go v0.109.0/go.mod h1:2sYycXt75t/CSB5R9M2wPU1tJmire7AQZTPtITcGBVE=
cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY=
cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA=
cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE=
cloud.google.com/go/iam v0.7.0 h1:k4MuwOsS7zGJJ+QfZ5vBK8SgHBAvYN/23BWsiihJ1vs=
cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg=
cloud.google.com/go/iam v0.9.0 h1:bK6Or6mxhuL8lnj1i9j0yMo2wE/IeTO2cWlfUrf/TZs=
cloud.google.com/go/iam v0.9.0/go.mod h1:nXAECrMt2qHpF6RZUZseteD6QyanL68reN4OXPw0UWM=
cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs=
cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc=
cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI=
cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
cloud.google.com/go/iam v0.10.0 h1:fpP/gByFs6US1ma53v7VxhvbJpO2Aapng6wabJ99MuI=
cloud.google.com/go/iam v0.10.0/go.mod h1:nXAECrMt2qHpF6RZUZseteD6QyanL68reN4OXPw0UWM=
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI=
cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4=
firebase.google.com/go/v4 v4.10.0 h1:dgK/8uwfJbzc5LZK/GyRRfIkZEDObN9q0kgEXsjlXN4=
firebase.google.com/go/v4 v4.10.0/go.mod h1:m0gLwPY9fxKggizzglgCNWOGnFnVPifLpqZzo5u3e/A=
github.com/AlekSi/pointer v1.0.0/go.mod h1:1kjywbfcPFCmncIxtk6fIEub6LKrfMz3gc5QKVOSOA8=
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/MicahParks/keyfunc v1.7.0 h1:LBd4tBj6FwGs2S4GXniQbgrG0PXzIldyGDKWch8slhg=
github.com/MicahParks/keyfunc v1.7.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw=
github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o=
github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -42,8 +33,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8bnphprS1EoVRe2Q5CKCX8iDlpqjQ/Y=
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-smtp v0.15.0 h1:3+hMGMGrqP/lqd7qoxZc1hTU8LY8gHV9RFGWlqSDmP8=
github.com/emersion/go-smtp v0.15.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/emersion/go-smtp v0.16.0 h1:eB9CY9527WdEZSs5sWisTmilDX7gG+Q/2IdRcmubpa8=
github.com/emersion/go-smtp v0.16.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -86,21 +75,16 @@ github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1V
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg=
github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/olebedev/when v0.0.0-20211212231525-59bd4edcf9d6 h1:oDSPaYiL2dbjcArLrFS8ANtwgJMyOLzvQCZon+XmFsk=
github.com/olebedev/when v0.0.0-20211212231525-59bd4edcf9d6/go.mod h1:DPucAeQGDPUzYUt+NaWw6qsF5SFapWWToxEiVDh2aV0=
github.com/olebedev/when v0.0.0-20221205223600-4d190b02b8d8 h1:0uFGkScHef2Xd8g74BMHU1jFcnKEm0PzrPn4CluQ9FI=
github.com/olebedev/when v0.0.0-20221205223600-4d190b02b8d8/go.mod h1:T0THb4kP9D3NNqlvCwIG4GyUioTAzEhB4RNVzig/43E=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -111,25 +95,20 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli/v2 v2.23.6 h1:iWmtKD+prGo1nKUtLO0Wg4z9esfBM4rAV4QRLQiEmJ4=
github.com/urfave/cli/v2 v2.23.6/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/urfave/cli/v2 v2.24.3 h1:7Q1w8VN8yE0MJEHP06bv89PjYsN4IHWED2s1v/Zlfm0=
github.com/urfave/cli/v2 v2.24.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -143,15 +122,11 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU=
golang.org/x/oauth2 v0.2.0/go.mod h1:Cwn6afJ8jrQwYMxQDTpISoXmXW9I6qF6vDeuuoX3Ibs=
golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8=
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -163,19 +138,17 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -186,10 +159,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ=
google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0=
google.golang.org/api v0.105.0 h1:t6P9Jj+6XTn4U9I2wycQai6Q/Kz7iOT+QzjJ3G2V4x8=
google.golang.org/api v0.105.0/go.mod h1:qh7eD5FJks5+BcE+cjBIm6Gz8vioK7EHvnlniqXBnqI=
google.golang.org/api v0.110.0 h1:l+rh0KYUooe9JGbGVx71tbFo4SMbMTXK3I3ia2QSEeU=
google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
@@ -199,19 +170,15 @@ google.golang.org/appengine/v2 v2.0.2/go.mod h1:PkgRUWz4o1XOvbqtWTkBtCitEJ5Tp4Ho
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks=
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 h1:jmIfw8+gSvXcZSgaFAGyInDXeWzUhvYH57G/5GKMn70=
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY=
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc h1:ijGwO+0vL2hJt5gaygqP2j6PfflOBrRot0IczKbmtio=
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=

View File

@@ -34,6 +34,9 @@ type smtpBackend struct {
mu sync.Mutex
}
var _ smtp.Backend = (*smtpBackend)(nil)
var _ smtp.Session = (*smtpSession)(nil)
func newMailBackend(conf *Config, handler func(http.ResponseWriter, *http.Request)) *smtpBackend {
return &smtpBackend{
config: conf,
@@ -41,14 +44,9 @@ func newMailBackend(conf *Config, handler func(http.ResponseWriter, *http.Reques
}
}
func (b *smtpBackend) Login(state *smtp.ConnectionState, username, password string) (smtp.Session, error) {
log.Debug("%s Incoming mail, login with user %s", logSMTPPrefix(state), username)
return &smtpSession{backend: b, state: state}, nil
}
func (b *smtpBackend) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) {
log.Debug("%s Incoming mail, anonymous login", logSMTPPrefix(state))
return &smtpSession{backend: b, state: state}, nil
func (b *smtpBackend) NewSession(conn *smtp.Conn) (smtp.Session, error) {
log.Debug("%s Incoming mail", logSMTPPrefix(conn))
return &smtpSession{backend: b, conn: conn}, nil
}
func (b *smtpBackend) Counts() (total int64, success int64, failure int64) {
@@ -60,23 +58,23 @@ func (b *smtpBackend) Counts() (total int64, success int64, failure int64) {
// smtpSession is returned after EHLO.
type smtpSession struct {
backend *smtpBackend
state *smtp.ConnectionState
conn *smtp.Conn
topic string
mu sync.Mutex
}
func (s *smtpSession) AuthPlain(username, password string) error {
log.Debug("%s AUTH PLAIN (with username %s)", logSMTPPrefix(s.state), username)
func (s *smtpSession) AuthPlain(username, _ string) error {
log.Debug("%s AUTH PLAIN (with username %s)", logSMTPPrefix(s.conn), username)
return nil
}
func (s *smtpSession) Mail(from string, opts smtp.MailOptions) error {
log.Debug("%s MAIL FROM: %s (with options: %#v)", logSMTPPrefix(s.state), from, opts)
func (s *smtpSession) Mail(from string, opts *smtp.MailOptions) error {
log.Debug("%s MAIL FROM: %s (with options: %#v)", logSMTPPrefix(s.conn), from, opts)
return nil
}
func (s *smtpSession) Rcpt(to string) error {
log.Debug("%s RCPT TO: %s", logSMTPPrefix(s.state), to)
log.Debug("%s RCPT TO: %s", logSMTPPrefix(s.conn), to)
return s.withFailCount(func() error {
conf := s.backend.config
addressList, err := mail.ParseAddressList(to)
@@ -114,9 +112,9 @@ func (s *smtpSession) Data(r io.Reader) error {
return err
}
if log.IsTrace() {
log.Trace("%s DATA: %s", logSMTPPrefix(s.state), string(b))
log.Trace("%s DATA: %s", logSMTPPrefix(s.conn), string(b))
} else if log.IsDebug() {
log.Debug("%s DATA: %d byte(s)", logSMTPPrefix(s.state), len(b))
log.Debug("%s DATA: %d byte(s)", logSMTPPrefix(s.conn), len(b))
}
msg, err := mail.ReadMessage(bytes.NewReader(b))
if err != nil {
@@ -156,9 +154,9 @@ func (s *smtpSession) Data(r io.Reader) error {
func (s *smtpSession) publishMessage(m *message) error {
// Extract remote address (for rate limiting)
remoteAddr, _, err := net.SplitHostPort(s.state.RemoteAddr.String())
remoteAddr, _, err := net.SplitHostPort(s.conn.Conn().RemoteAddr().String())
if err != nil {
remoteAddr = s.state.RemoteAddr.String()
remoteAddr = s.conn.Conn().RemoteAddr().String()
}
// Call HTTP handler with fake HTTP request
@@ -198,7 +196,7 @@ func (s *smtpSession) withFailCount(fn func() error) error {
if err != nil {
// Almost all of these errors are parse errors, and user input errors.
// We do not want to spam the log with WARN messages.
log.Debug("%s Incoming mail error: %s", logSMTPPrefix(s.state), err.Error())
log.Debug("%s Incoming mail error: %s", logSMTPPrefix(s.conn), err.Error())
s.backend.failure++
}
return err

View File

@@ -1,16 +1,23 @@
package server
import (
"bufio"
"github.com/emersion/go-smtp"
"github.com/stretchr/testify/require"
"io"
"net"
"net/http"
"strings"
"testing"
"time"
)
func TestSmtpBackend_Multipart(t *testing.T) {
email := `MIME-Version: 1.0
email := `EHLO example.com
MAIL FROM: phil@example.com
RCPT TO: ntfy-mytopic@ntfy.sh
DATA
MIME-Version: 1.0
Date: Tue, 28 Dec 2021 00:30:10 +0100
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
Subject: and one more
@@ -28,20 +35,25 @@ Content-Type: text/html; charset="UTF-8"
<div dir="ltr">what&#39;s up<br clear="all"><div><br></div></div>
--000000000000f3320b05d42915c9--`
_, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
--000000000000f3320b05d42915c9--
.
`
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/mytopic", r.URL.Path)
require.Equal(t, "and one more", r.Header.Get("Title"))
require.Equal(t, "what's up", readAll(t, r.Body))
})
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
require.Nil(t, session.Rcpt("ntfy-mytopic@ntfy.sh"))
require.Nil(t, session.Data(strings.NewReader(email)))
defer s.Close()
defer c.Close()
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
}
func TestSmtpBackend_MultipartNoBody(t *testing.T) {
email := `MIME-Version: 1.0
email := `EHLO example.com
MAIL FROM: phil@example.com
RCPT TO: ntfy-emailtest@ntfy.sh
DATA
MIME-Version: 1.0
Date: Tue, 28 Dec 2021 01:33:34 +0100
Message-ID: <CAAvm7ABCDsi9vsuu0WTRXzZQBC8dXrDOLT8iCWdqrsmg@mail.gmail.com>
Subject: This email has a subject but no body
@@ -59,20 +71,25 @@ Content-Type: text/html; charset="UTF-8"
<div dir="ltr"><br></div>
--000000000000bcf4a405d429f8d4--`
_, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
--000000000000bcf4a405d429f8d4--
.
`
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/emailtest", r.URL.Path)
require.Equal(t, "", r.Header.Get("Title")) // We flipped message and body
require.Equal(t, "This email has a subject but no body", readAll(t, r.Body))
})
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
require.Nil(t, session.Rcpt("ntfy-emailtest@ntfy.sh"))
require.Nil(t, session.Data(strings.NewReader(email)))
defer s.Close()
defer c.Close()
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
}
func TestSmtpBackend_Plaintext(t *testing.T) {
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
email := `EHLO example.com
MAIL FROM: phil@example.com
RCPT TO: mytopic@ntfy.sh
DATA
Date: Tue, 28 Dec 2021 00:30:10 +0100
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
Subject: and one more
From: Phil <phil@example.com>
@@ -80,56 +97,68 @@ To: mytopic@ntfy.sh
Content-Type: text/plain; charset="UTF-8"
what's up
.
`
conf, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/mytopic", r.URL.Path)
require.Equal(t, "and one more", r.Header.Get("Title"))
require.Equal(t, "what's up", readAll(t, r.Body))
})
conf.SMTPServerAddrPrefix = ""
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
require.Nil(t, session.Data(strings.NewReader(email)))
defer s.Close()
defer c.Close()
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
}
func TestSmtpBackend_Plaintext_No_ContentType(t *testing.T) {
email := `Subject: Very short mail
email := `EHLO example.com
MAIL FROM: phil@example.com
RCPT TO: mytopic@ntfy.sh
DATA
Subject: Very short mail
what's up
.
`
conf, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/mytopic", r.URL.Path)
require.Equal(t, "Very short mail", r.Header.Get("Title"))
require.Equal(t, "what's up", readAll(t, r.Body))
})
conf.SMTPServerAddrPrefix = ""
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
require.Nil(t, session.Data(strings.NewReader(email)))
defer s.Close()
defer c.Close()
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
}
func TestSmtpBackend_Plaintext_EncodedSubject(t *testing.T) {
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
email := `EHLO example.com
MAIL FROM: phil@example.com
RCPT TO: ntfy-mytopic@ntfy.sh
DATA
Date: Tue, 28 Dec 2021 00:30:10 +0100
Subject: =?UTF-8?B?VGhyZWUgc2FudGFzIPCfjoXwn46F8J+OhQ==?=
From: Phil <phil@example.com>
To: ntfy-mytopic@ntfy.sh
Content-Type: text/plain; charset="UTF-8"
what's up
.
`
_, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "Three santas 🎅🎅🎅", r.Header.Get("Title"))
})
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
require.Nil(t, session.Rcpt("ntfy-mytopic@ntfy.sh"))
require.Nil(t, session.Data(strings.NewReader(email)))
defer s.Close()
defer c.Close()
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
}
func TestSmtpBackend_Plaintext_TooLongTruncate(t *testing.T) {
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
email := `EHLO example.com
MAIL FROM: phil@example.com
RCPT TO: mytopic@ntfy.sh
DATA
Date: Tue, 28 Dec 2021 00:30:10 +0100
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
Subject: and one more
From: Phil <phil@example.com>
@@ -148,60 +177,61 @@ so i'm gonna fill the rest of this with AAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
and with BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
that should do it
.
`
conf, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
expected := `you know this is a string.
it's a long string.
it's supposed to be longer than the max message length
@@ -214,68 +244,71 @@ so i'm gonna fill the rest of this with AAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
and with BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBB`
require.Equal(t, 4096, len(expected)) // Sanity check
require.Equal(t, expected, readAll(t, r.Body))
})
defer s.Close()
defer c.Close()
conf.SMTPServerAddrPrefix = ""
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
require.Nil(t, session.Data(strings.NewReader(email)))
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
}
func TestSmtpBackend_Unsupported(t *testing.T) {
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
email := `EHLO example.com
MAIL FROM: phil@example.com
RCPT TO: ntfy-mytopic@ntfy.sh
DATA
Date: Tue, 28 Dec 2021 00:30:10 +0100
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
Subject: and one more
From: Phil <phil@example.com>
@@ -283,34 +316,89 @@ To: mytopic@ntfy.sh
Content-Type: text/SOMETHINGELSE
what's up
.
`
conf, backend := newTestBackend(t, func(http.ResponseWriter, *http.Request) {
// Nothing.
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("This should not be called")
})
conf.SMTPServerAddrPrefix = ""
session, _ := backend.Login(fakeConnState(t, "1.2.3.4"), "user", "pass")
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
require.Equal(t, errUnsupportedContentType, session.Data(strings.NewReader(email)))
defer s.Close()
defer c.Close()
writeAndReadUntilLine(t, email, c, scanner, "554 5.0.0 Error: transaction failed, blame it on the weather: unsupported content type")
}
func newTestBackend(t *testing.T, handler func(http.ResponseWriter, *http.Request)) (*Config, *smtpBackend) {
conf := newTestConfig(t)
func TestSmtpBackend_InvalidAddress(t *testing.T) {
email := `EHLO example.com
MAIL FROM: phil@example.com
RCPT TO: unsupported@ntfy.sh
DATA
Date: Tue, 28 Dec 2021 00:30:10 +0100
Subject: and one more
From: Phil <phil@example.com>
To: mytopic@ntfy.sh
Content-Type: text/plain
what's up
.
`
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
t.Fatal("This should not be called")
})
defer s.Close()
defer c.Close()
writeAndReadUntilLine(t, email, c, scanner, "451 4.0.0 invalid address")
}
type smtpHandlerFunc func(http.ResponseWriter, *http.Request)
func newTestSMTPServer(t *testing.T, handler smtpHandlerFunc) (s *smtp.Server, c net.Conn, conf *Config, scanner *bufio.Scanner) {
conf = newTestConfig(t)
conf.SMTPServerListen = ":25"
conf.SMTPServerDomain = "ntfy.sh"
conf.SMTPServerAddrPrefix = "ntfy-"
backend := newMailBackend(conf, handler)
return conf, backend
}
func fakeConnState(t *testing.T, remoteAddr string) *smtp.ConnectionState {
ip, err := net.ResolveIPAddr("ip", remoteAddr)
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
return &smtp.ConnectionState{
Hostname: "myhostname",
LocalAddr: ip,
RemoteAddr: ip,
s = smtp.NewServer(backend)
s.Domain = conf.SMTPServerDomain
s.AllowInsecureAuth = true
go func() {
require.Nil(t, s.Serve(l))
}()
c, err = net.Dial("tcp", l.Addr().String())
if err != nil {
t.Fatal(err)
}
scanner = bufio.NewScanner(c)
return
}
func writeAndReadUntilLine(t *testing.T, email string, conn net.Conn, scanner *bufio.Scanner, expectedLine string) {
_, err := io.WriteString(conn, email)
require.Nil(t, err)
readUntilLine(t, conn, scanner, expectedLine)
}
func readUntilLine(t *testing.T, conn net.Conn, scanner *bufio.Scanner, expectedLine string) {
cancelChan := make(chan bool)
go func() {
select {
case <-cancelChan:
case <-time.After(3 * time.Second):
conn.Close()
t.Error("Failed waiting for expected output")
}
}()
var output string
for scanner.Scan() {
text := scanner.Text()
if strings.TrimSpace(text) == expectedLine {
cancelChan <- true
return
}
output += text + "\n"
//fmt.Println(text)
}
t.Fatalf("Expected line '%s' not found in output:\n%s", expectedLine, output)
}

View File

@@ -57,8 +57,8 @@ func logHTTPPrefix(v *visitor, r *http.Request) string {
return fmt.Sprintf("%s HTTP %s %s", v.ip, r.Method, requestURI)
}
func logSMTPPrefix(state *smtp.ConnectionState) string {
return fmt.Sprintf("%s/%s SMTP", state.Hostname, state.RemoteAddr.String())
func logSMTPPrefix(conn *smtp.Conn) string {
return fmt.Sprintf("%s/%s SMTP", conn.Hostname(), conn.Conn().RemoteAddr().String())
}
func renderHTTPRequest(r *http.Request) string {

13174
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
"message_bar_type_message": "Tapez un message ici",
"notifications_attachment_open_button": "Ouvrir la pièce jointe",
"notifications_attachment_link_expires": "le lien expire {{date}}",
"message_bar_error_publishing": "Notification d'erreur de publication",
"message_bar_error_publishing": "Erreur lors de la publication de la notification",
"nav_button_all_notifications": "Toutes les notifications",
"nav_button_settings": "Paramètres",
"nav_button_documentation": "Documentation",
@@ -80,7 +80,7 @@
"subscribe_dialog_login_title": "Connexion nécessaire",
"prefs_notifications_min_priority_low_and_higher": "Priorité basse et au-dessus",
"prefs_users_dialog_button_cancel": "Annuler",
"error_boundary_button_copy_stack_trace": "Copier la stack strace",
"error_boundary_button_copy_stack_trace": "Copier la trace d'appels",
"publish_dialog_attached_file_title": "Fichier joint :",
"publish_dialog_checkbox_publish_another": "Publier un autre",
"publish_dialog_attached_file_filename_placeholder": "Nom du fichier joint",
@@ -129,7 +129,7 @@
"prefs_users_table_user_header": "Utilisateur",
"prefs_users_dialog_title_edit": "Éditer l'utilisateur",
"prefs_users_dialog_button_add": "Ajouter",
"error_boundary_description": "Ceci ne devrait évidemment pas arriver. Désolé pour ça.<br/>Si vous avez une minute, merci de <githubLink>signaler ceci sur GitHub</githubLink>, ou faites-le nous savoir par <discordLink>Discord</discordLink> ou <matrixLink>Matric</matrixLink>.",
"error_boundary_description": "Ceci ne devrait évidemment pas arriver. Désolé pour ça.<br/>Si vous avez une minute, merci de <githubLink>signaler ceci sur GitHub</githubLink>, ou faites-le nous savoir par <discordLink>Discord</discordLink> ou <matrixLink>Matrix</matrixLink>.",
"prefs_users_dialog_title_add": "Ajouter un utilisateur",
"error_boundary_stack_trace": "Trace de pile d'appels",
"error_boundary_gathering_info": "Récupérer plus d'information…",

View File

@@ -102,7 +102,7 @@
"publish_dialog_topic_label": "Emnenavn",
"prefs_notifications_delete_after_one_day_description": "Merknader slettes automatisk etter én dag",
"notifications_click_copy_url_button": "Kopier lenke",
"error_boundary_title": "Oida. Ntfy krasjet.",
"error_boundary_title": "Oida, ntfy krasjet",
"publish_dialog_message_placeholder": "Skriv en melding her",
"publish_dialog_button_cancel": "Avbryt",
"prefs_notifications_min_priority_title": "Minimumsprioritet",
@@ -118,9 +118,74 @@
"prefs_users_table_base_url_header": "Tjeneste-nettadresse",
"prefs_users_dialog_button_cancel": "Avbryt",
"prefs_users_dialog_button_add": "Legg til",
"publish_dialog_chip_attach_url_label": "Legg ved fil per nettadresse",
"publish_dialog_chip_attach_url_label": "Legg til fil med nettadresse",
"publish_dialog_tags_placeholder": "Kommainndelt liste over etiketter, f.eks. advarsel, srv1-sikkerhetskopi",
"prefs_notifications_sound_description_none": "Merknader er lydløse når de mottas",
"prefs_notifications_sound_description_none": "Merknader spiller ikke lyd når de mottas",
"subscribe_dialog_subscribe_topic_placeholder": "Emnenavn, f.eks. phil_varsler",
"prefs_notifications_min_priority_default_and_higher": "Forvalgt prioritet og høyere"
"prefs_notifications_min_priority_default_and_higher": "Forvalgt prioritet og høyere",
"notifications_no_subscriptions_title": "Det ser ut til at du ikke har noen abonnementer ennå.",
"publish_dialog_attachment_limits_file_and_quota_reached": "overskrider {{fileSizeLimit}} filgrense og kvote, {{remainingBytes}} gjenstår",
"publish_dialog_attachment_limits_file_reached": "overskrider filgrensen på {{fileSizeLimit}}",
"publish_dialog_title_label": "Tittel",
"publish_dialog_title_placeholder": "Varslingstittel, f.eks. Diskplassvarsel",
"publish_dialog_topic_placeholder": "Emnenavn, f.eks. halgeir_varsler",
"publish_dialog_chip_click_label": "Klikk URL",
"publish_dialog_chip_delay_label": "Forsink leveringen",
"publish_dialog_details_examples_description": "For eksempler og en detaljert beskrivelse av alle sendefunksjoner, se <docsLink>dokumentasjonen</docsLink>.",
"publish_dialog_base_url_placeholder": "Tjeneste-URL, f.eks. https://example.com",
"alert_grant_description": "Gi nettleseren din tillatelse til å vise skrivebordsvarsler.",
"alert_not_supported_description": "Varsler støttes ikke i nettleseren din.",
"notifications_attachment_file_app": "Android-app-fil",
"notifications_no_subscriptions_description": "Klikk på \"{{linktext}}\"-koblingen for å opprette eller abonnere på et emne. Etter det kan du sende meldinger via PUT eller POST, og du vil motta varsler her.",
"notifications_actions_http_request_title": "Send HTTP {{metode}} til {{url}}",
"notifications_none_for_any_description": "For å sende varsler til et emne, bare PUT eller POST til emne-URLen. Her er et eksempel som bruker et av emnene dine.",
"notifications_more_details": "For mer informasjon, sjekk ut <websiteLink>nettstedet</websiteLink> eller <docsLink>dokumentasjonen</docsLink>.",
"publish_dialog_attachment_limits_quota_reached": "overskrider kvoten, {{remainingBytes}} gjenstår",
"publish_dialog_click_reset": "Fjern klikk-URL",
"publish_dialog_delay_placeholder": "Forsinket levering, f.eks. {{unixTimestamp}}, {{relativeTime}} eller \"{{naturalLanguage}}\" (bare på engelsk)",
"emoji_picker_search_clear": "Tøm søk",
"subscribe_dialog_subscribe_description": "Det kan hende emner ikke er passordsbeskyttet, så velg et navn som ikke er enkelt å gjette. Når du har abonnert kan du utføre PUT/POST av merknader.",
"publish_dialog_checkbox_publish_another": "Publiser enda en",
"subscribe_dialog_login_description": "Dette emnet er passordbeskyttet. Vennligst skriv inn brukernavn og passord for å abonnere.",
"prefs_notifications_sound_play": "Spill av valgt lyd",
"subscribe_dialog_error_user_not_authorized": "Bruker {{brukernavn}} ikke autorisert",
"prefs_users_delete_button": "Slett bruker",
"error_boundary_unsupported_indexeddb_description": "ntfy-nettappen trenger IndexedDB for å fungere, og nettleseren din støtter ikke IndexedDB i privat nettlesingsmodus.<br/><br/>Selv om dette er uheldig, gir det heller ikke så mye mening å bruke ntfy-nettappen i privat surfemodus uansett, fordi alt er lagret i nettleserlagringen. Du kan lese mer om det <githubLink>i denne GitHub-feilmeldingen</githubLink>, eller snakk med oss på <discordLink>Discord</discordLink> eller <matrixLink>Matrix</matrixLink>.",
"action_bar_show_menu": "Vis meny",
"action_bar_toggle_mute": "Aktiver/deaktiver notifikasjoner",
"prefs_notifications_min_priority_description_max": "Vis merknader hvis prioritet er 5 (maks.)",
"prefs_notifications_min_priority_any": "Hvilken som helst prioritet",
"prefs_notifications_min_priority_low_and_higher": "Lav prioritet og høyere",
"prefs_users_description": "Legg til/fjern brukere for dine beskyttede emner her. Vær oppmerksom på at brukernavn og passord er lagret i nettleserens lokale lagring.",
"error_boundary_description": "Dette skal åpenbart ikke skje. Beklager dette.<br/>Hvis du har et minutt, vennligst <githubLink>rapporter dette på GitHub</githubLink>, eller gi oss beskjed via <discordLink>Discord</discordLink> eller <matrixLink>Matrix</matrixLink>.",
"action_bar_logo_alt": "ntfy logo",
"message_bar_publish": "Publiser melding",
"action_bar_toggle_action_menu": "Åpne/lukk handlingsmeny",
"message_bar_show_dialog": "Vis publiseringsdialog",
"nav_button_muted": "Varsler dempet",
"nav_button_connecting": "kobler til",
"notifications_list": "Varslingsliste",
"notifications_list_item": "Varsling",
"notifications_mark_read": "Merk som lest",
"notifications_delete": "Slett",
"notifications_priority_x": "Prioritet {{prioritet}}",
"notifications_new_indicator": "Nytt varsel",
"notifications_attachment_image": "Vedlagt bilde",
"notifications_attachment_file_image": "bildefil",
"notifications_attachment_file_video": "videofil",
"notifications_attachment_file_audio": "lydfil",
"notifications_attachment_file_document": "annet dokument",
"notifications_actions_not_supported": "Handling støttes ikke i nettappen",
"notifications_none_for_topic_description": "For å sende varsler til dette emnet, bare PUT eller POST til emne-URLen.",
"publish_dialog_emoji_picker_show": "Velg emoji",
"publish_dialog_topic_reset": "Tilbakestill emne",
"publish_dialog_click_label": "Klikk URL",
"publish_dialog_email_reset": "Fjern videresending av e-post",
"publish_dialog_attach_reset": "Fjern URL vedlegg",
"publish_dialog_delay_reset": "Fjern forsinket levering",
"publish_dialog_attached_file_remove": "Fjern vedlagt fil",
"subscribe_dialog_subscribe_base_url_label": "Tjeneste-URL",
"prefs_users_table": "Brukertabell",
"prefs_users_edit_button": "Rediger bruker",
"error_boundary_unsupported_indexeddb_title": "Privat surfing støttes ikke"
}

View File

@@ -1,13 +1,13 @@
{
"action_bar_settings": "Instellingen",
"action_bar_send_test_notification": "Stuur test notificatie",
"action_bar_send_test_notification": "Verstuur testnotificatie.",
"action_bar_clear_notifications": "Wis alle notificaties",
"message_bar_type_message": "Typ hier een bericht",
"action_bar_unsubscribe": "Afmelden",
"message_bar_error_publishing": "Fout bij publiceren notificatie",
"nav_topics_title": "Geabonneerde onderwerpen",
"nav_button_settings": "Instellingen",
"alert_not_supported_description": "Notificaties worden niet ondersteund in je browser.",
"alert_not_supported_description": "Notificaties worden niet ondersteund door je browser.",
"notifications_none_for_any_title": "Je hebt nog geen notificaties ontvangen.",
"publish_dialog_tags_label": "Tags",
"publish_dialog_chip_attach_file_label": "Lokaal bestand bijvoegen",
@@ -26,20 +26,20 @@
"action_bar_show_menu": "Toon menu",
"action_bar_logo_alt": "ntfy logo",
"action_bar_toggle_mute": "Notificaties dempen/opheffen",
"action_bar_toggle_action_menu": "Actie menu openen/sluiten",
"action_bar_toggle_action_menu": "Open/Sluit actiemenu",
"message_bar_show_dialog": "Toon publicatie venster",
"message_bar_publish": "Bericht publiceren",
"nav_button_all_notifications": "Alle notificaties",
"nav_button_documentation": "Documentatie",
"nav_button_publish_message": "Notificatie publiceren",
"nav_button_subscribe": "Onderwerp abonneren",
"nav_button_subscribe": "Abonneer op onderwerp",
"nav_button_muted": "Notificaties gedempt",
"nav_button_connecting": "verbinden",
"alert_grant_title": "Notificaties zijn uitgeschakeld",
"alert_grant_description": "Geef je browser toestemming om meldingen weer te geven.",
"alert_grant_description": "Verleen je browser toestemming voor het weergeven van notificaties.",
"alert_grant_button": "Nu toestaan",
"alert_not_supported_title": "Notificaties zijn niet ondersteund",
"notifications_list": "Notificaties lijst",
"notifications_list": "Notificatielijst",
"notifications_list_item": "Notificatie",
"notifications_mark_read": "Markeer als gelezen",
"notifications_delete": "Verwijder",
@@ -59,7 +59,7 @@
"notifications_attachment_file_audio": "audiobestand",
"notifications_attachment_file_app": "Android app bestand",
"notifications_attachment_file_document": "overig document",
"notifications_click_copy_url_title": "URL naar klembord kopiëren",
"notifications_click_copy_url_title": "link URL naar klembord kopiëren",
"notifications_click_copy_url_button": "Link kopiëren",
"notifications_click_open_button": "Link openen",
"notifications_none_for_topic_description": "Om notificaties naar dit onderwerp te sturen, doe een PUT of POST naar de URL van het onderwerp.",
@@ -73,7 +73,7 @@
"publish_dialog_title_no_topic": "Notificatie publiceren",
"publish_dialog_progress_uploading": "Uploaden …",
"notifications_actions_open_url_title": "Ga naar {{url}}",
"notifications_actions_not_supported": "Deze actie is niet ondersteund in de web applicatie",
"notifications_actions_not_supported": "Actie wordt niet ondersteund in de webapplicatie",
"notifications_actions_http_request_title": "Stuur HTTP {{method}} naar {{url}}",
"notifications_none_for_topic_title": "Je hebt nog geen notificaties ontvangen voor dit onderwerp.",
"publish_dialog_priority_low": "Lage prioriteit",

View File

@@ -1 +1,191 @@
{}
{
"action_bar_clear_notifications": "Limpar todas as notificações",
"action_bar_send_test_notification": "Enviar notificação de teste",
"action_bar_unsubscribe": "Anular subscrição",
"action_bar_toggle_mute": "Ativa/Desativa notificações",
"action_bar_toggle_action_menu": "Abrir/fechar menu de ação",
"message_bar_type_message": "Escreva uma mensagem aqui",
"message_bar_error_publishing": "Erro ao publicar notificação",
"message_bar_publish": "Publicar mensagem",
"nav_topics_title": "Tópicos subscritos",
"nav_button_all_notifications": "Todas notificações",
"nav_button_settings": "Configurações",
"nav_button_documentation": "Documentação",
"nav_button_publish_message": "Publicar notificação",
"nav_button_subscribe": "Subscrever tópico",
"nav_button_muted": "Notificações desativadas",
"nav_button_connecting": "A ligar",
"alert_grant_title": "As notificações estão desativadas",
"alert_grant_description": "Conceder permissão ao seu navegador para mostrar notificações.",
"alert_not_supported_title": "Notificações não suportadas",
"notifications_list": "Lista de notificações",
"alert_not_supported_description": "As notificações não são suportadas pelo seu navegador.",
"notifications_list_item": "Notificação",
"notifications_mark_read": "Marcar como lido",
"notifications_delete": "Apagar",
"notifications_copied_to_clipboard": "Copiado para a área de transferência",
"notifications_tags": "Etiquetas",
"notifications_priority_x": "Prioridade {{priority}}",
"notifications_new_indicator": "Nova notificação",
"notifications_attachment_image": "Imagem anexada",
"notifications_attachment_copy_url_title": "Copiar URL do anexo para a área de transferência",
"notifications_attachment_copy_url_button": "Copiar URL",
"notifications_attachment_open_title": "Ir para {{url}}",
"notifications_attachment_link_expired": "a ligação de transferência expirou",
"notifications_attachment_open_button": "Abrir anexo",
"notifications_attachment_link_expires": "a ligação expira em {{date}}",
"notifications_attachment_file_image": "ficheiro de imagem",
"notifications_attachment_file_video": "ficheiro de vídeo",
"notifications_attachment_file_audio": "ficheiro de áudio",
"notifications_attachment_file_app": "ficheiro apk Android",
"notifications_attachment_file_document": "outros documentos",
"notifications_click_copy_url_title": "Copiar URL da ligação para a área de transferência",
"notifications_click_copy_url_button": "Copiar ligação",
"notifications_click_open_button": "Abrir ligação",
"notifications_actions_open_url_title": "Ir para {{url}}",
"notifications_actions_not_supported": "Ação não suportada na app web",
"notifications_actions_http_request_title": "Enviar HTTP {{method}} para {{url}}",
"notifications_none_for_topic_title": "Ainda não recebeu nenhuma notificação deste tópico.",
"notifications_none_for_topic_description": "Para enviar notificações deste tópico, basta usar os métodos PUT ou POST no URL do tópico.",
"notifications_none_for_any_title": "Ainda não recebeu nenhuma notificação.",
"notifications_none_for_any_description": "Para enviar notificações dum tópico, basta usar os métodos PUT ou POST no URL do tópico. Eis um exemplo usando um dos seus tópicos.",
"notifications_no_subscriptions_title": "Parece que ainda não tem nenhuma inscrição.",
"notifications_no_subscriptions_description": "Clique na ligação \"{{linktext}}\" para criar ou subscrever um tópico. Depois, poderá enviar mensagens via PUT ou POST e receberá notificações aqui.",
"notifications_example": "Exemplo",
"notifications_more_details": "Para mais informações, aceda ao <websiteLink>site</websiteLink> ou à <docsLink>documentação</docsLink>.",
"notifications_loading": "A carregar notificações…",
"publish_dialog_title_topic": "Publicar em {{topic}}",
"publish_dialog_title_no_topic": "Publicar notificação",
"publish_dialog_progress_uploading": "A enviar …",
"publish_dialog_progress_uploading_detail": "A enviar {{loaded}}/{{total}} ({{percent}}%)…",
"publish_dialog_message_published": "Notificação publicada",
"publish_dialog_attachment_limits_file_and_quota_reached": "excede limite de ficheiro de {{fileSizeLimit}} e cota, {{remainingBytes}} restante(s)",
"publish_dialog_attachment_limits_quota_reached": "excede a cota, {{remainingBytes}} restante(s)",
"publish_dialog_priority_min": "Prioridade mínima",
"publish_dialog_priority_low": "Prioridade baixa",
"publish_dialog_priority_default": "Prioridade padrão",
"publish_dialog_priority_high": "Prioridade alta",
"publish_dialog_base_url_label": "URL de serviço",
"publish_dialog_base_url_placeholder": "URL de serviço, por exemplo: https://exemplo.com",
"publish_dialog_topic_label": "Nome do tópico",
"publish_dialog_topic_placeholder": "Nome do tópico, por exemplo: \"avisos_do_filipe\"",
"publish_dialog_topic_reset": "Limpar tópico",
"publish_dialog_title_placeholder": "Título da notificação, por exemplo: \"Alerta de espaço em disco\"",
"publish_dialog_message_label": "Mensagem",
"publish_dialog_message_placeholder": "Escreva uma mensagem aqui",
"publish_dialog_tags_label": "Etiquetas",
"publish_dialog_tags_placeholder": "Lista de etiquetas, separadas por vírgula, por exemplo: aviso, srv1-backup",
"publish_dialog_priority_label": "Prioridade",
"publish_dialog_click_label": "URL de clique",
"publish_dialog_click_placeholder": "URL que é aberto quando a notificação é clicada",
"publish_dialog_click_reset": "Remover URL de clique",
"publish_dialog_email_label": "Email",
"publish_dialog_filename_placeholder": "Nome do ficheiro anexado",
"publish_dialog_email_placeholder": "Endereça para o qual encaminhar a notificação, por exemplo: filipe@exemplo.com",
"publish_dialog_email_reset": "Remover encaminhamento por email",
"publish_dialog_attach_label": "URL de anexo",
"publish_dialog_attach_placeholder": "Anexar ficheiro por URL, por exemplo: https://f-droid.org/F-Droid.apk",
"publish_dialog_attach_reset": "Remover URL de anexo",
"publish_dialog_filename_label": "Nome do ficheiro",
"publish_dialog_delay_label": "Atraso",
"publish_dialog_delay_placeholder": "Atraso na entrega, por exemplo \"{{{unixTimestamp}}\", \"{{relativeTime}}\", ou \"{{naturalLanguage}}\" (apenas em Inglês)",
"publish_dialog_other_features": "Outras funcionalidades:",
"publish_dialog_chip_click_label": "URL de clique",
"publish_dialog_chip_topic_label": "Alterar tópico",
"publish_dialog_details_examples_description": "Para obter exemplos e uma descrição detalhada de todas as funcionalidades de envio, consulte a <docsLink>documentação</docsLink>.",
"publish_dialog_button_cancel_sending": "Cancelar o envio",
"publish_dialog_attached_file_filename_placeholder": "Nome do ficheiro anexado",
"publish_dialog_attached_file_remove": "Remover ficheiro anexado",
"emoji_picker_search_clear": "Limpar pesquisa",
"subscribe_dialog_subscribe_description": "Os tópicos podem não ser protegidos por palavra-passe, por isso escolha um nome que não seja fácil de adivinhar. Uma vez subscrito, pode usar os métodos PUT/POST para publicar notificações.",
"subscribe_dialog_subscribe_use_another_label": "Usar outro servidor",
"subscribe_dialog_error_user_not_authorized": "Utilizador {{username}} não autorizado",
"prefs_notifications_min_priority_description_max": "Mostrar notificações se prioridade for 5 (máxima)",
"prefs_notifications_delete_after_one_week": "Após uma semana",
"prefs_notifications_delete_after_one_month": "Após um mês",
"prefs_notifications_delete_after_never_description": "As notificações nunca serão eliminadas automaticamente",
"prefs_notifications_delete_after_one_week_description": "As notificações serão eliminadas automaticamente após uma semana",
"prefs_notifications_delete_after_one_month_description": "As notificações serão eliminadas automaticamente após um mês",
"prefs_users_dialog_username_label": "Utilizador, por exemplo: \"filipe\"",
"prefs_users_dialog_password_label": "Palavra-passe",
"prefs_users_dialog_button_cancel": "Cancelar",
"prefs_users_dialog_button_add": "Adicionar",
"error_boundary_description": "Obviamente, isto não devia acontecer, lamentamos o sucedido.<br/>Se tiver um minuto, por favor <githubLink>relate isto no GitHub</githubLink>, ou informe-nos através de <discordLink>Discord</discordLink> ou <matrixLink>Matrix</matrixLink>.",
"error_boundary_stack_trace": "Erro (\"stack trace\")",
"error_boundary_gathering_info": "A recolher mais informações …",
"error_boundary_unsupported_indexeddb_title": "Navegação anónima não suportada",
"error_boundary_unsupported_indexeddb_description": "A aplicação web ntfy necessita da \"IndexedDB\" para funcionar e o seu navegador não a suporta no modo de navegação privada.<br/><br/>Embora isso seja inconveniente, também não faz muito sentido usar a aplicação no modo de navegação privada de qualquer maneira, visto que tudo é guardado no armazenamento do navegador. Pode ler mais sobre isso <githubLink>nesta questão no GitHub</githubLink>, ou falar connosco por <discordLink>Discord</discordLink> ou <matrixLink>Matrix</matrixLink>.",
"action_bar_show_menu": "Mostrar menu",
"action_bar_logo_alt": "logótipo do ntfy",
"action_bar_settings": "Configurações",
"message_bar_show_dialog": "Mostrar caixa de publicação",
"alert_grant_button": "Conceder agora",
"publish_dialog_attachment_limits_file_reached": "excede o limite de ficheiro de {{fileSizeLimit}}",
"publish_dialog_emoji_picker_show": "Escolher emoji",
"publish_dialog_priority_max": "Prioridade máxima",
"publish_dialog_title_label": "Título",
"publish_dialog_delay_reset": "Remover atraso de entrega",
"publish_dialog_chip_email_label": "Encaminhar para email",
"publish_dialog_chip_attach_url_label": "Anexar ficheiro por URL",
"publish_dialog_chip_attach_file_label": "Anexar ficheiro local",
"publish_dialog_chip_delay_label": "Atraso de entrega",
"publish_dialog_button_cancel": "Cancelar",
"publish_dialog_button_send": "Enviar",
"publish_dialog_checkbox_publish_another": "Publicar outra",
"publish_dialog_attached_file_title": "Ficheiro anexado:",
"publish_dialog_drop_file_here": "Arraste o ficheiro para aqui",
"emoji_picker_search_placeholder": "Pesquisar emoji",
"subscribe_dialog_subscribe_title": "Subscrever tópico",
"subscribe_dialog_subscribe_topic_placeholder": "Nome do tópico, por exemplo: \"alertas_do_filipe\"",
"subscribe_dialog_subscribe_base_url_label": "URL de serviço",
"subscribe_dialog_subscribe_button_cancel": "Cancelar",
"subscribe_dialog_subscribe_button_subscribe": "Subscrever",
"subscribe_dialog_login_title": "Autenticação necessária",
"subscribe_dialog_login_description": "Esse tópico é protegido por palavra-passe. Por favor insira um nome de utilizador e palavra-passe para subscrever.",
"subscribe_dialog_login_username_label": "Nome, por exemplo: \"filipe\"",
"subscribe_dialog_login_password_label": "Palavra-passe",
"subscribe_dialog_login_button_back": "Voltar",
"subscribe_dialog_login_button_login": "Autenticar",
"subscribe_dialog_error_user_anonymous": "anónimo",
"prefs_notifications_title": "Notificações",
"prefs_notifications_sound_title": "Som de notificações",
"prefs_notifications_sound_description_none": "Notificações não reproduzem nenhum som quando chegam",
"prefs_notifications_sound_description_some": "Notificações reproduzem som {{sound}} quando chegam",
"prefs_notifications_sound_no_sound": "Sem som",
"prefs_notifications_sound_play": "Reproduzir som selecionado",
"prefs_notifications_min_priority_title": "Prioridade mínima",
"prefs_notifications_min_priority_description_any": "A mostrar todas as notificações, independentemente da prioridade",
"prefs_notifications_min_priority_description_x_or_higher": "Mostrar notificações se prioridade for {{number}} ({{name}}) ou acima",
"prefs_notifications_min_priority_any": "Qualquer prioridade",
"prefs_notifications_min_priority_low_and_higher": "Prioridade baixa e acima",
"prefs_notifications_min_priority_default_and_higher": "Prioridade padrão e acima",
"prefs_notifications_min_priority_high_and_higher": "Prioridade alta e acima",
"prefs_notifications_min_priority_max_only": "Apenas prioridade máxima",
"prefs_notifications_delete_after_title": "Eliminar notificações",
"prefs_notifications_delete_after_never": "Nunca",
"prefs_notifications_delete_after_three_hours": "Após três horas",
"prefs_notifications_delete_after_one_day": "Após um dia",
"prefs_notifications_delete_after_three_hours_description": "As notificações serão eliminadas automaticamente após três horas",
"prefs_notifications_delete_after_one_day_description": "As notificações serão eliminadas automaticamente após um dia",
"prefs_users_title": "Gerir utilizadores",
"prefs_users_description": "Adicionar/remover utilizadores aos seus tópicos protegidos. Note que o utilizador e palavra-passe são guardados no armazenamento local do navegador.",
"prefs_users_table": "Tabela de utilizadores",
"prefs_users_add_button": "Adicionar utilizador",
"prefs_users_edit_button": "Editar utilizador",
"prefs_users_delete_button": "Apagar utilizador",
"prefs_users_table_user_header": "Utilizador",
"prefs_users_table_base_url_header": "URL de serviço",
"prefs_users_dialog_title_add": "Adicionar utilizador",
"prefs_users_dialog_title_edit": "Editar utilizador",
"prefs_users_dialog_base_url_label": "URL de serviço, por exemplo: https://ntfy.sh",
"prefs_users_dialog_button_save": "Gravar",
"prefs_appearance_title": "Aparência",
"prefs_appearance_language_title": "Idioma",
"priority_min": "mínima",
"priority_low": "baixa",
"priority_default": "padrão",
"priority_high": "alta",
"priority_max": "máxima",
"error_boundary_title": "Oh não, o ntfy parou de funcionar",
"error_boundary_button_copy_stack_trace": "Copiar erro (\"stack trace\")"
}

View File

@@ -0,0 +1,11 @@
{
"action_bar_show_menu": "Afișează meniu",
"action_bar_send_test_notification": "Trimite notificare de probă",
"action_bar_clear_notifications": "Șterge toate notificările",
"action_bar_settings": "Setări",
"action_bar_unsubscribe": "Dezabonare",
"action_bar_logo_alt": "logo-ul ntfy",
"action_bar_toggle_mute": "Oprire/activare notificări",
"message_bar_type_message": "Scrie un mesaj aici",
"message_bar_error_publishing": "Eroare la publicarea notificării"
}

View File

@@ -463,6 +463,7 @@ const Language = () => {
<MenuItem value="nl">Nederlands</MenuItem>
<MenuItem value="nb_NO">Norsk bokmål</MenuItem>
<MenuItem value="uk">Українська</MenuItem>
<MenuItem value="pt">Português</MenuItem>
<MenuItem value="pt_BR">Português (Brasil)</MenuItem>
<MenuItem value="pl">Polski</MenuItem>
<MenuItem value="ru">Русский</MenuItem>