Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59ec2de8bd | ||
|
|
d154d3936d | ||
|
|
5125aac91c | ||
|
|
62512b7a1a | ||
|
|
b67d9fc85d | ||
|
|
59b341dfb8 | ||
|
|
e2834a7c4d | ||
|
|
2280031a80 | ||
|
|
de1b97bbce | ||
|
|
3b4a4108e5 | ||
|
|
dc1c0ddd4e | ||
|
|
182e21a9c3 | ||
|
|
d4fe2052c7 | ||
|
|
202051bbbf | ||
|
|
a693975526 | ||
|
|
4cd4e890fe | ||
|
|
5dc8031ec9 | ||
|
|
03ad5dcff6 | ||
|
|
5f508e1839 | ||
|
|
c5642799df | ||
|
|
5a99fe8ba2 | ||
|
|
ee0f448d86 | ||
|
|
a222f64ee4 | ||
|
|
140daec0d3 | ||
|
|
b409c89d3b | ||
|
|
806893962c | ||
|
|
14d3c5e93e | ||
|
|
37e14b13a4 | ||
|
|
d7fa51be2c | ||
|
|
a3e28e71aa | ||
|
|
35cef8386c | ||
|
|
38072c9cdd | ||
|
|
13d741b89e | ||
|
|
21fc1245eb | ||
|
|
2511ba7627 | ||
|
|
23547f4504 | ||
|
|
e6f19d050f | ||
|
|
3ec8084450 | ||
|
|
2edb722c0e | ||
|
|
1f75498dca | ||
|
|
ab19c4d688 | ||
|
|
15265d9ef3 | ||
|
|
2839a7228f | ||
|
|
c2036975fa | ||
|
|
7aa0f87376 | ||
|
|
df372d1a7e | ||
|
|
6cd31502e7 | ||
|
|
bade88079f | ||
|
|
20ab05afc8 | ||
|
|
5b10f51af1 | ||
|
|
470d11f442 | ||
|
|
4952f0fbd2 |
@@ -4,7 +4,7 @@ before:
|
|||||||
- go mod tidy
|
- go mod tidy
|
||||||
builds:
|
builds:
|
||||||
-
|
-
|
||||||
id: ntfy
|
id: ntfy_amd64
|
||||||
binary: ntfy
|
binary: ntfy
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=1 # required for go-sqlite3
|
- CGO_ENABLED=1 # required for go-sqlite3
|
||||||
@@ -28,9 +28,8 @@ builds:
|
|||||||
goos: [linux]
|
goos: [linux]
|
||||||
goarch: [arm]
|
goarch: [arm]
|
||||||
goarm: [7]
|
goarm: [7]
|
||||||
hooks:
|
# No "upx", since it causes random core dumps, see
|
||||||
post:
|
# https://github.com/binwiederhier/ntfy/issues/191#issuecomment-1083406546
|
||||||
- upx "{{ .Path }}" # apt install upx
|
|
||||||
-
|
-
|
||||||
id: ntfy_arm64
|
id: ntfy_arm64
|
||||||
binary: ntfy
|
binary: ntfy
|
||||||
@@ -42,9 +41,8 @@ builds:
|
|||||||
- "-linkmode=external -extldflags=-static -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
|
- "-linkmode=external -extldflags=-static -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
|
||||||
goos: [linux]
|
goos: [linux]
|
||||||
goarch: [arm64]
|
goarch: [arm64]
|
||||||
hooks:
|
# No "upx", since it causes random core dumps, see
|
||||||
post:
|
# https://github.com/binwiederhier/ntfy/issues/191#issuecomment-1083406546
|
||||||
- upx "{{ .Path }}" # apt install upx
|
|
||||||
nfpms:
|
nfpms:
|
||||||
-
|
-
|
||||||
package_name: ntfy
|
package_name: ntfy
|
||||||
|
|||||||
195
Makefile
195
Makefile
@@ -3,55 +3,87 @@ VERSION := $(shell git describe --tag)
|
|||||||
.PHONY:
|
.PHONY:
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "Typical commands:"
|
@echo "Typical commands (more see below):"
|
||||||
@echo " make check - Run all tests, vetting/formatting checks and linters"
|
@echo " make build - Build web app, documentation and server/client (sloowwww)"
|
||||||
@echo " make fmt build-snapshot install - Build latest and install to local system"
|
@echo " make server-amd64 - Build server/client binary (amd64, no web app or docs)"
|
||||||
|
@echo " make install-amd64 - Install ntfy binary to /usr/bin/ntfy (amd64)"
|
||||||
|
@echo " make web - Build the web app"
|
||||||
|
@echo " make docs - Build the documentation"
|
||||||
|
@echo " make check - Run all tests, vetting/formatting checks and linters"
|
||||||
|
@echo
|
||||||
|
@echo "Build everything:"
|
||||||
|
@echo " make build - Build web app, documentation and server/client"
|
||||||
|
@echo " make clean - Clean build/dist folders"
|
||||||
|
@echo
|
||||||
|
@echo "Build server & client (not release version):"
|
||||||
|
@echo " make server - Build server & client (all architectures)"
|
||||||
|
@echo " make server-amd64 - Build server & client (amd64 only)"
|
||||||
|
@echo " make server-armv7 - Build server & client (armv7 only)"
|
||||||
|
@echo " make server-arm64 - Build server & client (arm64 only)"
|
||||||
|
@echo
|
||||||
|
@echo "Build web app:"
|
||||||
|
@echo " make web - Build the web app"
|
||||||
|
@echo " make web-deps - Install web app dependencies (npm install the universe)"
|
||||||
|
@echo " make web-build - Actually build the web app"
|
||||||
|
@echo
|
||||||
|
@echo "Build documentation:"
|
||||||
|
@echo " make docs - Build the documentation"
|
||||||
|
@echo " make docs-deps - Install Python dependencies (pip3 install)"
|
||||||
|
@echo " make docs-build - Actually build the documentation"
|
||||||
@echo
|
@echo
|
||||||
@echo "Test/check:"
|
@echo "Test/check:"
|
||||||
@echo " make test - Run tests"
|
@echo " make test - Run tests"
|
||||||
@echo " make race - Run tests with -race flag"
|
@echo " make race - Run tests with -race flag"
|
||||||
@echo " make coverage - Run tests and show coverage"
|
@echo " make coverage - Run tests and show coverage"
|
||||||
@echo " make coverage-html - Run tests and show coverage (as HTML)"
|
@echo " make coverage-html - Run tests and show coverage (as HTML)"
|
||||||
@echo " make coverage-upload - Upload coverage results to codecov.io"
|
@echo " make coverage-upload - Upload coverage results to codecov.io"
|
||||||
@echo
|
@echo
|
||||||
@echo "Lint/format:"
|
@echo "Lint/format:"
|
||||||
@echo " make fmt - Run 'go fmt'"
|
@echo " make fmt - Run 'go fmt'"
|
||||||
@echo " make fmt-check - Run 'go fmt', but don't change anything"
|
@echo " make fmt-check - Run 'go fmt', but don't change anything"
|
||||||
@echo " make vet - Run 'go vet'"
|
@echo " make vet - Run 'go vet'"
|
||||||
@echo " make lint - Run 'golint'"
|
@echo " make lint - Run 'golint'"
|
||||||
@echo " make staticcheck - Run 'staticcheck'"
|
@echo " make staticcheck - Run 'staticcheck'"
|
||||||
@echo
|
@echo
|
||||||
@echo "Build:"
|
@echo "Releasing:"
|
||||||
@echo " make build - Build"
|
@echo " make release - Create a release"
|
||||||
@echo " make build-snapshot - Build snapshot"
|
@echo " make release-snapshot - Create a test release"
|
||||||
@echo " make build-simple - Build (using go build, without goreleaser)"
|
|
||||||
@echo " make clean - Clean build folder"
|
|
||||||
@echo
|
|
||||||
@echo "Releasing (requires goreleaser):"
|
|
||||||
@echo " make release - Create a release"
|
|
||||||
@echo " make release-snapshot - Create a test release"
|
|
||||||
@echo
|
@echo
|
||||||
@echo "Install locally (requires sudo):"
|
@echo "Install locally (requires sudo):"
|
||||||
@echo " make install - Copy binary from dist/ to /usr/bin"
|
@echo " make install-amd64 - Copy amd64 binary from dist/ to /usr/bin/ntfy"
|
||||||
@echo " make install-deb - Install .deb from dist/"
|
@echo " make install-armv7 - Copy armv7 binary from dist/ to /usr/bin/ntfy"
|
||||||
@echo " make install-lint - Install golint"
|
@echo " make install-arm64 - Copy arm64 binary from dist/ to /usr/bin/ntfy"
|
||||||
|
@echo " make install-deb-amd64 - Install .deb from dist/ (amd64 only)"
|
||||||
|
@echo " make install-deb-armv7 - Install .deb from dist/ (armv7 only)"
|
||||||
|
@echo " make install-deb-arm64 - Install .deb from dist/ (arm64 only)"
|
||||||
|
|
||||||
|
|
||||||
|
# Building everything
|
||||||
|
|
||||||
|
clean: .PHONY
|
||||||
|
rm -rf dist build server/docs server/site
|
||||||
|
|
||||||
|
build: web docs server
|
||||||
|
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
|
docs: docs-deps docs-build
|
||||||
|
|
||||||
docs-deps: .PHONY
|
docs-deps: .PHONY
|
||||||
pip3 install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
|
|
||||||
docs: docs-deps
|
docs-build: .PHONY
|
||||||
mkdocs build
|
mkdocs build
|
||||||
|
|
||||||
|
|
||||||
# Web app
|
# Web app
|
||||||
|
|
||||||
|
web: web-deps web-build
|
||||||
|
|
||||||
web-deps:
|
web-deps:
|
||||||
cd web \
|
cd web && npm install
|
||||||
&& npm install \
|
# If this fails for .svg files, optimizes them with svgo
|
||||||
&& node_modules/svgo/bin/svgo src/img/*.svg
|
|
||||||
|
|
||||||
web-build:
|
web-build:
|
||||||
cd web \
|
cd web \
|
||||||
@@ -63,7 +95,37 @@ web-build:
|
|||||||
../server/site/config.js \
|
../server/site/config.js \
|
||||||
../server/site/asset-manifest.json
|
../server/site/asset-manifest.json
|
||||||
|
|
||||||
web: web-deps web-build
|
|
||||||
|
# Main server/client build
|
||||||
|
|
||||||
|
server: server-deps
|
||||||
|
goreleaser build --snapshot --rm-dist --debug
|
||||||
|
|
||||||
|
server-amd64: server-deps-static-sites
|
||||||
|
goreleaser build --snapshot --rm-dist --debug --id ntfy_amd64
|
||||||
|
|
||||||
|
server-armv7: server-deps-static-sites server-deps-gcc-armv7
|
||||||
|
goreleaser build --snapshot --rm-dist --debug --id ntfy_armv7
|
||||||
|
|
||||||
|
server-arm64: server-deps-static-sites server-deps-gcc-arm64
|
||||||
|
goreleaser build --snapshot --rm-dist --debug --id ntfy_arm64
|
||||||
|
|
||||||
|
server-deps: server-deps-static-sites server-deps-all server-deps-gcc
|
||||||
|
|
||||||
|
server-deps-gcc: server-deps-gcc-armv7 server-deps-gcc-arm64
|
||||||
|
|
||||||
|
server-deps-static-sites:
|
||||||
|
mkdir -p server/docs server/site
|
||||||
|
touch server/docs/index.html server/site/app.html
|
||||||
|
|
||||||
|
server-deps-all:
|
||||||
|
which upx || { echo "ERROR: upx not installed. On Ubuntu, run: apt install upx"; exit 1; }
|
||||||
|
|
||||||
|
server-deps-gcc-armv7:
|
||||||
|
which arm-linux-gnueabi-gcc || { echo "ERROR: ARMv7 cross compiler not installed. On Ubuntu, run: apt install gcc-arm-linux-gnueabi"; exit 1; }
|
||||||
|
|
||||||
|
server-deps-gcc-arm64:
|
||||||
|
which aarch64-linux-gnu-gcc || { echo "ERROR: ARM64 cross compiler not installed. On Ubuntu, run: apt install gcc-aarch64-linux-gnu"; exit 1; }
|
||||||
|
|
||||||
|
|
||||||
# Test/check targets
|
# Test/check targets
|
||||||
@@ -114,64 +176,51 @@ staticcheck: .PHONY
|
|||||||
rm -rf build/staticcheck
|
rm -rf build/staticcheck
|
||||||
|
|
||||||
|
|
||||||
# Building targets
|
|
||||||
|
|
||||||
build-deps: docs web
|
|
||||||
which arm-linux-gnueabi-gcc || { echo "ERROR: ARMv6/v7 cross compiler not installed. On Ubuntu, run: apt install gcc-arm-linux-gnueabi"; exit 1; }
|
|
||||||
which aarch64-linux-gnu-gcc || { echo "ERROR: ARM64 cross compiler not installed. On Ubuntu, run: apt install gcc-aarch64-linux-gnu"; exit 1; }
|
|
||||||
|
|
||||||
build: build-deps
|
|
||||||
goreleaser build --rm-dist --debug
|
|
||||||
|
|
||||||
build-snapshot: build-deps
|
|
||||||
goreleaser build --snapshot --rm-dist --debug
|
|
||||||
|
|
||||||
build-simple: clean
|
|
||||||
mkdir -p dist/ntfy_linux_amd64 server/docs server/site
|
|
||||||
touch server/docs/index.html
|
|
||||||
touch server/site/app.html
|
|
||||||
export CGO_ENABLED=1
|
|
||||||
go build \
|
|
||||||
-o dist/ntfy_linux_amd64/ntfy \
|
|
||||||
-tags sqlite_omit_load_extension,osusergo,netgo \
|
|
||||||
-ldflags \
|
|
||||||
"-linkmode=external -extldflags=-static -s -w -X main.version=$(VERSION) -X main.commit=$(shell git rev-parse --short HEAD) -X main.date=$(shell date +%s)"
|
|
||||||
|
|
||||||
clean: .PHONY
|
|
||||||
rm -rf dist build server/docs server/site
|
|
||||||
|
|
||||||
|
|
||||||
# Releasing targets
|
# Releasing targets
|
||||||
|
|
||||||
|
release: release-deps
|
||||||
|
goreleaser release --rm-dist --debug
|
||||||
|
|
||||||
|
release-snapshot: release-deps
|
||||||
|
goreleaser release --snapshot --skip-publish --rm-dist --debug
|
||||||
|
|
||||||
|
release-deps: clean server-deps release-check-tags docs web check
|
||||||
|
|
||||||
release-check-tags:
|
release-check-tags:
|
||||||
$(eval LATEST_TAG := $(shell git describe --abbrev=0 --tags | cut -c2-))
|
$(eval LATEST_TAG := $(shell git describe --abbrev=0 --tags | cut -c2-))
|
||||||
if ! grep -q $(LATEST_TAG) docs/install.md; then\
|
if ! grep -q $(LATEST_TAG) docs/install.md; then\
|
||||||
echo "ERROR: Must update docs/install.md with latest tag first.";\
|
echo "ERROR: Must update docs/install.md with latest tag first.";\
|
||||||
exit 1;\
|
exit 1;\
|
||||||
fi
|
fi
|
||||||
if grep -q XXXXX docs/releases.md; then\
|
|
||||||
echo "ERROR: Must update docs/releases.md, found XXXXX.";\
|
|
||||||
exit 1;\
|
|
||||||
fi
|
|
||||||
if ! grep -q $(LATEST_TAG) docs/releases.md; then\
|
if ! grep -q $(LATEST_TAG) docs/releases.md; then\
|
||||||
echo "ERROR: Must update docs/releases.mdwith latest tag first.";\
|
echo "ERROR: Must update docs/releases.md with latest tag first.";\
|
||||||
exit 1;\
|
exit 1;\
|
||||||
fi
|
fi
|
||||||
|
|
||||||
release: build-deps release-check-tags check
|
|
||||||
goreleaser release --rm-dist --debug
|
|
||||||
|
|
||||||
release-snapshot: build-deps
|
|
||||||
goreleaser release --snapshot --skip-publish --rm-dist --debug
|
|
||||||
|
|
||||||
|
|
||||||
# Installing targets
|
# Installing targets
|
||||||
|
|
||||||
install:
|
install-amd64: remove-binary
|
||||||
sudo rm -f /usr/bin/ntfy
|
sudo cp -a dist/ntfy_amd64_linux_amd64/ntfy /usr/bin/ntfy
|
||||||
sudo cp -a dist/ntfy_linux_amd64/ntfy /usr/bin/ntfy
|
|
||||||
|
|
||||||
install-deb:
|
install-armv7: remove-binary
|
||||||
|
sudo cp -a dist/ntfy_armv7_linux_armv7/ntfy /usr/bin/ntfy
|
||||||
|
|
||||||
|
install-arm64: remove-binary
|
||||||
|
sudo cp -a dist/ntfy_arm64_linux_arm64/ntfy /usr/bin/ntfy
|
||||||
|
|
||||||
|
remove-binary:
|
||||||
|
sudo rm -f /usr/bin/ntfy
|
||||||
|
|
||||||
|
install-amd64-deb: purge-package
|
||||||
|
sudo dpkg -i dist/ntfy_*_linux_amd64.deb
|
||||||
|
|
||||||
|
install-armv7-deb: purge-package
|
||||||
|
sudo dpkg -i dist/ntfy_*_linux_armv7.deb
|
||||||
|
|
||||||
|
install-arm64-deb: purge-package
|
||||||
|
sudo dpkg -i dist/ntfy_*_linux_arm64.deb
|
||||||
|
|
||||||
|
purge-package:
|
||||||
sudo systemctl stop ntfy || true
|
sudo systemctl stop ntfy || true
|
||||||
sudo apt-get purge ntfy || true
|
sudo apt-get purge ntfy || true
|
||||||
sudo dpkg -i dist/ntfy_*_linux_amd64.deb
|
|
||||||
|
|||||||
@@ -34,7 +34,14 @@ too.
|
|||||||
[Building](https://ntfy.sh/docs/develop/)
|
[Building](https://ntfy.sh/docs/develop/)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
I welcome any and all contributions. Just create a PR or an issue.
|
I welcome any and all contributions. Just create a PR or an issue. To contribute code, check out
|
||||||
|
the [build instructions](https://ntfy.sh/docs/develop/) for the server and the Android app.
|
||||||
|
Or, if you'd like to help translate 🇩🇪 🇺🇸 🇧🇬, you can start immediately in
|
||||||
|
[Hosted Weblate](https://hosted.weblate.org/projects/ntfy/).
|
||||||
|
|
||||||
|
<a href="https://hosted.weblate.org/engage/ntfy/">
|
||||||
|
<img src="https://hosted.weblate.org/widgets/ntfy/-/multi-blue.svg" alt="Translation status" />
|
||||||
|
</a>
|
||||||
|
|
||||||
## Contact me
|
## Contact me
|
||||||
You can directly contact me **[on Discord](https://discord.gg/cT7ECsZj9w)** or [on Matrix](https://matrix.to/#/#ntfy:matrix.org)
|
You can directly contact me **[on Discord](https://discord.gg/cT7ECsZj9w)** or [on Matrix](https://matrix.to/#/#ntfy:matrix.org)
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ statuspage.io (though these days most services also support webhooks and HTTP ca
|
|||||||
To configure the SMTP server, you must at least set `smtp-server-listen` and `smtp-server-domain`:
|
To configure the SMTP server, you must at least set `smtp-server-listen` and `smtp-server-domain`:
|
||||||
|
|
||||||
* `smtp-server-listen` defines the IP address and port the SMTP server will listen on, e.g. `:25` or `1.2.3.4:25`
|
* `smtp-server-listen` defines the IP address and port the SMTP server will listen on, e.g. `:25` or `1.2.3.4:25`
|
||||||
* `smtp-server-domain` is the e-mail domain, e.g. `ntfy.sh`
|
* `smtp-server-domain` is the e-mail domain, e.g. `ntfy.sh` (must be identical to MX record, see below)
|
||||||
* `smtp-server-addr-prefix` is an optional prefix for the e-mail addresses to prevent spam. If set to `ntfy-`, for instance,
|
* `smtp-server-addr-prefix` is an optional prefix for the e-mail addresses to prevent spam. If set to `ntfy-`, for instance,
|
||||||
only e-mails to `ntfy-$topic@ntfy.sh` will be accepted. If this is not set, all emails to `$topic@ntfy.sh` will be
|
only e-mails to `ntfy-$topic@ntfy.sh` will be accepted. If this is not set, all emails to `$topic@ntfy.sh` will be
|
||||||
accepted (which may obviously be a spam problem).
|
accepted (which may obviously be a spam problem).
|
||||||
@@ -369,6 +369,42 @@ configured (in [Amazon Route 53](https://aws.amazon.com/route53/)):
|
|||||||
<figcaption>DNS records for incoming mail</figcaption>
|
<figcaption>DNS records for incoming mail</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
You can check if everything is working correctly by sending an email as raw SMTP via `nc`. Create a text file, e.g.
|
||||||
|
`email.txt`
|
||||||
|
|
||||||
|
```
|
||||||
|
EHLO example.com
|
||||||
|
MAIL FROM: phil@example.com
|
||||||
|
RCPT TO: ntfy-mytopic@ntfy.sh
|
||||||
|
DATA
|
||||||
|
Subject: Email for you
|
||||||
|
Content-Type: text/plain; charset="UTF-8"
|
||||||
|
|
||||||
|
Hello from 🇩🇪
|
||||||
|
.
|
||||||
|
```
|
||||||
|
|
||||||
|
And then send the mail via `nc` like this. If you see any lines starting with `451`, those are errors from the
|
||||||
|
ntfy server. Read them carefully.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cat email.txt | nc -N ntfy.sh 25
|
||||||
|
220 ntfy.sh ESMTP Service Ready
|
||||||
|
250-Hello example.com
|
||||||
|
...
|
||||||
|
250 2.0.0 Roger, accepting mail from <phil@example.com>
|
||||||
|
250 2.0.0 I'll make sure <ntfy-mytopic@ntfy.sh> gets this
|
||||||
|
```
|
||||||
|
|
||||||
|
As for the DNS setup, be sure to verify that `dig MX` and `dig A` are returning results similar to this:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dig MX ntfy.sh +short
|
||||||
|
10 mx1.ntfy.sh.
|
||||||
|
$ dig A mx1.ntfy.sh +short
|
||||||
|
3.139.215.220
|
||||||
|
```
|
||||||
|
|
||||||
## Behind a proxy (TLS, etc.)
|
## Behind a proxy (TLS, etc.)
|
||||||
!!! warning
|
!!! warning
|
||||||
If you are running ntfy behind a proxy, you must set the `behind-proxy` flag. Otherwise, all visitors are
|
If you are running ntfy behind a proxy, you must set the `behind-proxy` flag. Otherwise, all visitors are
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ This page is used to list deprecation notices for ntfy. Deprecated commands and
|
|||||||
In future versions of the Android app, instant delivery connections and connections to self-hosted servers will
|
In future versions of the Android app, instant delivery connections and connections to self-hosted servers will
|
||||||
be using the WebSockets protocol. This potentially requires [configuration changes in your proxy](https://ntfy.sh/docs/config/#nginxapache2caddy).
|
be using the WebSockets protocol. This potentially requires [configuration changes in your proxy](https://ntfy.sh/docs/config/#nginxapache2caddy).
|
||||||
|
|
||||||
|
Due to [reports of varying battery consumption](https://github.com/binwiederhier/ntfy/issues/190) (which entirely
|
||||||
|
seems to depend on the phone), JSON HTTP stream support will not be removed. Instead, I'll just flip the default to
|
||||||
|
WebSocket in June.
|
||||||
|
|
||||||
### Android app: Using `since=<timestamp>` instead of `since=<id>`
|
### Android app: Using `since=<timestamp>` instead of `since=<id>`
|
||||||
> Active since 2022-02-27, behavior will change in **May 2022**
|
> Active since 2022-02-27, behavior will change in **May 2022**
|
||||||
|
|
||||||
|
|||||||
301
docs/develop.md
301
docs/develop.md
@@ -1,51 +1,287 @@
|
|||||||
# Building
|
# Development
|
||||||
|
Hurray 🥳 🎉, you are interested in writing code for ntfy! **That's awesome.** 😎
|
||||||
|
|
||||||
|
I tried my very best to write up detailed instructions, but if at any point in time you run into issues, don't
|
||||||
|
hesitate to **contact me on [Discord](https://discord.gg/cT7ECsZj9w) or [Matrix](https://matrix.to/#/#ntfy:matrix.org)**.
|
||||||
|
|
||||||
## ntfy server
|
## ntfy server
|
||||||
The ntfy server source code is available [on GitHub](https://github.com/binwiederhier/ntfy).
|
The ntfy server source code is available [on GitHub](https://github.com/binwiederhier/ntfy). The codebase for the
|
||||||
To quickly build on amd64, you can use `make build-simple`:
|
server consists of three components:
|
||||||
|
|
||||||
```
|
* **The main server/client** is written in [Go](https://go.dev/) (so you'll need Go). Its main entrypoint is at
|
||||||
git clone git@github.com:binwiederhier/ntfy.git
|
[main.go](https://github.com/binwiederhier/ntfy/blob/main/main.go), and the meat you're likely interested in is
|
||||||
cd ntfy
|
in [server.go](https://github.com/binwiederhier/ntfy/blob/main/server/server.go). Notably, the server uses a
|
||||||
make build-simple
|
[SQLite](https://sqlite.org) library called [go-sqlite3](https://github.com/mattn/go-sqlite3), which requires
|
||||||
|
[Cgo](https://go.dev/blog/cgo) and `CGO_ENABLED=1` to be set. Otherwise things will not work (see below).
|
||||||
|
* **The documentation** is generated by [MkDocs](https://www.mkdocs.org/) and [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/),
|
||||||
|
which is written in [Python](https://www.python.org/). You'll need Python and MkDocs (via `pip`) only if you want to
|
||||||
|
build the docs.
|
||||||
|
* **The web app** is written in [React](https://reactjs.org/), using [MUI](https://mui.com/). It uses [Create React App](https://create-react-app.dev/)
|
||||||
|
to build the production build. If you want to modify the web app, you need [nodejs](https://nodejs.org/en/) (for `npm`)
|
||||||
|
and install all the 100,000 dependencies (*sigh*).
|
||||||
|
|
||||||
|
All of these components are built and then **baked into one binary**.
|
||||||
|
|
||||||
|
### Navigating the code
|
||||||
|
Code:
|
||||||
|
|
||||||
|
* [main.go](https://github.com/binwiederhier/ntfy/blob/main/main.go) - Main entrypoint into the CLI, for both server and client
|
||||||
|
* [cmd/](https://github.com/binwiederhier/ntfy/tree/main/cmd) - CLI commands, such as `serve` or `publish`
|
||||||
|
* [server/](https://github.com/binwiederhier/ntfy/tree/main/server) - The meat of the server logic
|
||||||
|
* [docs/](https://github.com/binwiederhier/ntfy/tree/main/docs) - The [MkDocs](https://www.mkdocs.org/) documentation, also see `mkdocs.yml`
|
||||||
|
* [web/](https://github.com/binwiederhier/ntfy/tree/main/web) - The [React](https://reactjs.org/) application, also see `web/package.json`
|
||||||
|
|
||||||
|
Build related:
|
||||||
|
|
||||||
|
* [Makefile](https://github.com/binwiederhier/ntfy/blob/main/Makefile) - Main entrypoint for all things related to building
|
||||||
|
* [.goreleaser.yml](https://github.com/binwiederhier/ntfy/blob/main/.goreleaser.yml) - Describes all build outputs (for [GoReleaser](https://goreleaser.com/))
|
||||||
|
* [go.mod](https://github.com/binwiederhier/ntfy/blob/main/go.mod) - Go modules dependency file
|
||||||
|
* [mkdocs.yml](https://github.com/binwiederhier/ntfy/blob/main/mkdocs.yml) - Config file for the docs (for [MkDocs](https://www.mkdocs.org/))
|
||||||
|
* [web/package.json](https://github.com/binwiederhier/ntfy/blob/main/web/package.json) - Build and dependency file for web app (for npm)
|
||||||
|
|
||||||
|
|
||||||
|
The `web/` and `docs/` folder are the sources for web app and documentation. During the build process,
|
||||||
|
the generated output is copied to `server/site` (web app and landing page) and `server/docs` (documentation).
|
||||||
|
|
||||||
|
### Build requirements
|
||||||
|
|
||||||
|
* [Go](https://go.dev/) (required for main server)
|
||||||
|
* [gcc](https://gcc.gnu.org/) (required main server, for SQLite cgo-based bindings)
|
||||||
|
* [Make](https://www.gnu.org/software/make/) (required for convenience)
|
||||||
|
* [libsqlite3/libsqlite3-dev](https://www.sqlite.org/) (required for main server, for SQLite cgo-based bindings)
|
||||||
|
* [GoReleaser](https://goreleaser.com/) (required for a proper main server build)
|
||||||
|
* [Python](https://www.python.org/) (for `pip`, only to build the docs)
|
||||||
|
* [nodejs](https://nodejs.org/en/) (for `npm`, only to build the web app)
|
||||||
|
|
||||||
|
### Install dependencies
|
||||||
|
These steps **assume Ubuntu**. Steps may vary on different Linux distributions.
|
||||||
|
|
||||||
|
First, install [Go](https://go.dev/) (see [official instructions](https://go.dev/doc/install)):
|
||||||
|
``` shell
|
||||||
|
wget https://go.dev/dl/go1.18.linux-amd64.tar.gz
|
||||||
|
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.18.linux-amd64.tar.gz
|
||||||
|
export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin
|
||||||
|
go version # verifies that it worked
|
||||||
```
|
```
|
||||||
|
|
||||||
That'll generate a statically linked binary in `dist/ntfy_linux_amd64/ntfy`.
|
Install [GoReleaser](https://goreleaser.com/) (see [official instructions](https://goreleaser.com/install/)):
|
||||||
|
``` shell
|
||||||
For all other platforms (including Docker), and for production or other snapshot builds, you should use the amazingly
|
go install github.com/goreleaser/goreleaser@latest
|
||||||
awesome [GoReleaser](https://goreleaser.com/) make targets:
|
goreleaser -v # verifies that it worked
|
||||||
|
|
||||||
```
|
|
||||||
Build:
|
|
||||||
make build - Build
|
|
||||||
make build-snapshot - Build snapshot
|
|
||||||
make build-simple - Build (using go build, without goreleaser)
|
|
||||||
make clean - Clean build folder
|
|
||||||
|
|
||||||
Releasing (requires goreleaser):
|
|
||||||
make release - Create a release
|
|
||||||
make release-snapshot - Create a test release
|
|
||||||
```
|
```
|
||||||
|
|
||||||
There are currently no platform-specific make targets, so they will build for all platforms (which may take a while).
|
Install [nodejs](https://nodejs.org/en/) (see [official instructions](https://nodejs.org/en/download/package-manager/)):
|
||||||
|
``` shell
|
||||||
|
curl -fsSL https://deb.nodesource.com/setup_17.x | sudo -E bash -
|
||||||
|
sudo apt-get install -y nodejs
|
||||||
|
npm -v # verifies that it worked
|
||||||
|
```
|
||||||
|
|
||||||
|
Then install a few other things required:
|
||||||
|
``` shell
|
||||||
|
sudo apt install \
|
||||||
|
build-essential \
|
||||||
|
libsqlite3-dev \
|
||||||
|
gcc-arm-linux-gnueabi \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
python3-pip \
|
||||||
|
upx \
|
||||||
|
git
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check out code
|
||||||
|
Now check out via git from the [GitHub repository](https://github.com/binwiederhier/ntfy):
|
||||||
|
|
||||||
|
=== "via HTTPS"
|
||||||
|
``` shell
|
||||||
|
git clone https://github.com/binwiederhier/ntfy.git
|
||||||
|
cd ntfy
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "via SSH"
|
||||||
|
``` shell
|
||||||
|
git clone git@github.com:binwiederhier/ntfy.git
|
||||||
|
cd ntfy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build all the things
|
||||||
|
Now you can finally build everything. There are tons of `make` targets, so maybe just review what's there first
|
||||||
|
by typing `make`:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ make
|
||||||
|
Typical commands (more see below):
|
||||||
|
make build - Build web app, documentation and server/client (sloowwww)
|
||||||
|
make server-amd64 - Build server/client binary (amd64, no web app or docs)
|
||||||
|
make install-amd64 - Install ntfy binary to /usr/bin/ntfy (amd64)
|
||||||
|
make web - Build the web app
|
||||||
|
make docs - Build the documentation
|
||||||
|
make check - Run all tests, vetting/formatting checks and linters
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to build the **ntfy binary including web app and docs for all supported architectures** (amd64, armv7, and amd64),
|
||||||
|
you can simply run `make build`:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ make build
|
||||||
|
...
|
||||||
|
# This builds web app, docs, and the ntfy binary (for amd64, armv7 and arm64).
|
||||||
|
# This will be SLOW (5+ minutes on my laptop on the first run). Maybe look at the other make targets?
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll see all the outputs in the `dist/` folder afterwards:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
$ find dist
|
||||||
|
dist
|
||||||
|
dist/metadata.json
|
||||||
|
dist/ntfy_arm64_linux_arm64
|
||||||
|
dist/ntfy_arm64_linux_arm64/ntfy
|
||||||
|
dist/ntfy_armv7_linux_arm_7
|
||||||
|
dist/ntfy_armv7_linux_arm_7/ntfy
|
||||||
|
dist/ntfy_amd64_linux_amd64
|
||||||
|
dist/ntfy_amd64_linux_amd64/ntfy
|
||||||
|
dist/config.yaml
|
||||||
|
dist/artifacts.json
|
||||||
|
```
|
||||||
|
|
||||||
|
If you also want to build the **Debian/RPM packages and the Docker images for all supported architectures**, you can
|
||||||
|
use the `make release-snapshot` target:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ make release-snapshot
|
||||||
|
...
|
||||||
|
# This will be REALLY SLOW (sometimes 5+ minutes on my laptop)
|
||||||
|
```
|
||||||
|
|
||||||
|
During development, you may want to be more picky and build only certain things. Here are a few examples.
|
||||||
|
|
||||||
|
### Build the ntfy binary
|
||||||
|
To build only the `ntfy` binary **without the web app or documentation**, use the `make server-...` targets:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ make
|
||||||
|
Build server & client (not release version):
|
||||||
|
make server - Build server & client (all architectures)
|
||||||
|
make server-amd64 - Build server & client (amd64 only)
|
||||||
|
make server-armv7 - Build server & client (armv7 only)
|
||||||
|
make server-arm64 - Build server & client (arm64 only)
|
||||||
|
```
|
||||||
|
|
||||||
|
So if you're on an amd64/x86_64-based machine, you may just want to run `make server-amd64` during testing. On a modern
|
||||||
|
system, this shouldn't take longer than 5-10 seconds. I often combine it with `install-amd64` so I can run the binary
|
||||||
|
right away:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ make server-amd64 install-amd64
|
||||||
|
$ ntfy serve
|
||||||
|
```
|
||||||
|
|
||||||
|
**During development of the main app, you can also just use `go run main.go`**, as long as you run
|
||||||
|
`make server-deps-static-sites`at least once and `CGO_ENABLED=1`:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ export CGO_ENABLED=1
|
||||||
|
$ make server-deps-static-sites
|
||||||
|
$ go run main.go serve
|
||||||
|
2022/03/18 08:43:55 Listening on :2586[http]
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't run `server-deps-static-sites`, you may see an error *`pattern ...: no matching files found`*:
|
||||||
|
```
|
||||||
|
$ go run main.go serve
|
||||||
|
server/server.go:85:13: pattern docs: no matching files found
|
||||||
|
```
|
||||||
|
|
||||||
|
This is because we use `go:embed` to embed the documentation and web app, so the Go code expects files to be
|
||||||
|
present at `server/docs` and `server/site`. If they are not, you'll see the above error. The `server-deps-static-sites`
|
||||||
|
target creates dummy files that ensures that you'll be able to build.
|
||||||
|
|
||||||
|
|
||||||
|
### Build the web app
|
||||||
|
The sources for the web app live in `web/`. As long as you have `npm` installed (see above), building the web app
|
||||||
|
is really simple. Just type `make web` and you're in business:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ make web
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
This will build the web app using Create React App and then **copy the production build to the `server/site` folder**, so
|
||||||
|
that when you `make server` (or `make server-amd64`, ...), you will have the web app included in the `ntfy` binary.
|
||||||
|
|
||||||
|
If you're developing on the web app, it's best to just `cd web` and run `npm start` manually. This will open your browser
|
||||||
|
at `http://127.0.0.1:3000` with the web app, and as you edit the source files, they will be recompiled and the browser
|
||||||
|
will automatically refresh:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ cd web
|
||||||
|
$ npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build the docs
|
||||||
|
The sources for the docs live in `docs/`. Similarly to the web app, you can simply run `make docs` to build the
|
||||||
|
documentation. As long as you have `mkdocs` installed (see above), this should work fine:
|
||||||
|
|
||||||
|
``` shell
|
||||||
|
$ make docs
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are changing the documentation, you should be running `mkdocs serve` directly. This will build the documentation,
|
||||||
|
serve the files at `http://127.0.0.1:8000/`, and rebuild every time you save the source files:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mkdocs serve
|
||||||
|
INFO - Building documentation...
|
||||||
|
INFO - Cleaning site directory
|
||||||
|
INFO - Documentation built in 5.53 seconds
|
||||||
|
INFO - [16:28:14] Serving on http://127.0.0.1:8000/
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can navigate to http://127.0.0.1:8000/ and whenever you change a markdown file in your text editor it'll automatically update.
|
||||||
|
|
||||||
## Android app
|
## Android app
|
||||||
The ntfy Android app source code is available [on GitHub](https://github.com/binwiederhier/ntfy-android).
|
The ntfy Android app source code is available [on GitHub](https://github.com/binwiederhier/ntfy-android).
|
||||||
The Android app has two flavors:
|
The Android app has two flavors:
|
||||||
|
|
||||||
* **Google Play:** The `play` flavor includes Firebase (FCM) and requires a Firebase account
|
* **Google Play:** The `play` flavor includes [Firebase (FCM)](https://firebase.google.com/) and requires a Firebase account
|
||||||
* **F-Droid:** The `fdroid` flavor does not include Firebase or Google dependencies
|
* **F-Droid:** The `fdroid` flavor does not include Firebase or Google dependencies
|
||||||
|
|
||||||
|
### Navigating the code
|
||||||
|
* [main/](https://github.com/binwiederhier/ntfy-android/tree/main/app/src/main) - Main Android app source code
|
||||||
|
* [play/](https://github.com/binwiederhier/ntfy-android/tree/main/app/src/play) - Google Play / Firebase specific code
|
||||||
|
* [fdroid/](https://github.com/binwiederhier/ntfy-android/tree/main/app/src/fdroid) - F-Droid Firebase stubs
|
||||||
|
* [build.gradle](https://github.com/binwiederhier/ntfy-android/blob/main/app/build.gradle) - Main build file
|
||||||
|
|
||||||
|
### IDE/Environment
|
||||||
|
You should download [Android Studio](https://developer.android.com/studio) (or [IntelliJ IDEA](https://www.jetbrains.com/idea/)
|
||||||
|
with the relevant Android plugins). Everything else will just be a pain for you. Do yourself a favor. 😀
|
||||||
|
|
||||||
|
### Check out the code
|
||||||
First check out the repository:
|
First check out the repository:
|
||||||
|
|
||||||
```
|
=== "via HTTPS"
|
||||||
git clone git@github.com:binwiederhier/ntfy-android.git
|
``` shell
|
||||||
cd ntfy-android
|
git clone https://github.com/binwiederhier/ntfy-android.git
|
||||||
```
|
cd ntfy-android
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "via SSH"
|
||||||
|
``` shell
|
||||||
|
git clone git@github.com:binwiederhier/ntfy-android.git
|
||||||
|
cd ntfy-android
|
||||||
|
```
|
||||||
|
|
||||||
Then either follow the steps for building with or without Firebase.
|
Then either follow the steps for building with or without Firebase.
|
||||||
|
|
||||||
### Building without Firebase (F-Droid flavor)
|
### Build F-Droid flavor (no FCM)
|
||||||
|
!!! info
|
||||||
|
I do build the ntfy Android app using IntelliJ IDEA (Android Studio), so I don't know if these Gradle commands will
|
||||||
|
work without issues. Please give me feedback if it does/doesn't work for you.
|
||||||
|
|
||||||
Without Firebase, you may want to still change the default `app_base_url` in [strings.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/strings.xml)
|
Without Firebase, you may want to still change the default `app_base_url` in [strings.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/strings.xml)
|
||||||
if you're self-hosting the server. Then run:
|
if you're self-hosting the server. Then run:
|
||||||
```
|
```
|
||||||
@@ -56,8 +292,13 @@ if you're self-hosting the server. Then run:
|
|||||||
./gradlew bundleFdroidRelease
|
./gradlew bundleFdroidRelease
|
||||||
```
|
```
|
||||||
|
|
||||||
### Building with Firebase (FCM, Google Play flavor)
|
### Build Play flavor (FCM)
|
||||||
|
!!! info
|
||||||
|
I do build the ntfy Android app using IntelliJ IDEA (Android Studio), so I don't know if these Gradle commands will
|
||||||
|
work without issues. Please give me feedback if it does/doesn't work for you.
|
||||||
|
|
||||||
To build your own version with Firebase, you must:
|
To build your own version with Firebase, you must:
|
||||||
|
|
||||||
* Create a Firebase/FCM account
|
* Create a Firebase/FCM account
|
||||||
* Place your account file at `app/google-services.json`
|
* Place your account file at `app/google-services.json`
|
||||||
* And change `app_base_url` in [strings.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/strings.xml)
|
* And change `app_base_url` in [strings.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/strings.xml)
|
||||||
|
|||||||
242
docs/examples.md
242
docs/examples.md
@@ -124,3 +124,245 @@ GitHub have been hopeless. In case it ever becomes available, I want to know imm
|
|||||||
*/6 * * * * if curl -s https://api.github.com/users/ntfy | grep "Not Found"; then curl -d "github.com/ntfy is available" -H "Tags: tada" -H "Prio: high" ntfy.sh/my-alerts; fi
|
*/6 * * * * if curl -s https://api.github.com/users/ntfy | grep "Not Found"; then curl -d "github.com/ntfy is available" -H "Tags: tada" -H "Prio: high" ntfy.sh/my-alerts; fi
|
||||||
~
|
~
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Download notifications (Sonarr, Radarr, Lidarr, Readarr, Prowlarr, SABnzbd)
|
||||||
|
It's possible to use custom scripts for all the *arr services, plus SABnzbd. Notifications for downloads, warnings, grabs etc.
|
||||||
|
Some simple bash scripts to achieve this are kindly provided in [nickexyz's repository](https://github.com/nickexyz/ntfy-shellscripts).
|
||||||
|
|
||||||
|
## Node-RED
|
||||||
|
You can use the HTTP request node to send messages with [Node-RED](https://nodered.org), some examples:
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Example: Send a message (click to expand)</summary>
|
||||||
|
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "c956e688cc74ad8e",
|
||||||
|
"type": "http request",
|
||||||
|
"z": "fabdd7a3.4045a",
|
||||||
|
"name": "ntfy.sh",
|
||||||
|
"method": "POST",
|
||||||
|
"ret": "txt",
|
||||||
|
"paytoqs": "ignore",
|
||||||
|
"url": "https://ntfy.sh/mytopic",
|
||||||
|
"tls": "",
|
||||||
|
"persist": false,
|
||||||
|
"proxy": "",
|
||||||
|
"authType": "",
|
||||||
|
"senderr": false,
|
||||||
|
"credentials":
|
||||||
|
{
|
||||||
|
"user": "",
|
||||||
|
"password": ""
|
||||||
|
},
|
||||||
|
"x": 590,
|
||||||
|
"y": 3160,
|
||||||
|
"wires":
|
||||||
|
[
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "32ee1eade51fae50",
|
||||||
|
"type": "function",
|
||||||
|
"z": "fabdd7a3.4045a",
|
||||||
|
"name": "data",
|
||||||
|
"func": "msg.payload = \"Something happened\";\nmsg.headers = {};\nmsg.headers['tags'] = 'house';\nmsg.headers['X-Title'] = 'Home Assistant';\n\nreturn msg;",
|
||||||
|
"outputs": 1,
|
||||||
|
"noerr": 0,
|
||||||
|
"initialize": "",
|
||||||
|
"finalize": "",
|
||||||
|
"libs": [],
|
||||||
|
"x": 470,
|
||||||
|
"y": 3160,
|
||||||
|
"wires":
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"c956e688cc74ad8e"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b287e59cd2311815",
|
||||||
|
"type": "inject",
|
||||||
|
"z": "fabdd7a3.4045a",
|
||||||
|
"name": "Manual start",
|
||||||
|
"props":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"p": "payload"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"p": "topic",
|
||||||
|
"vt": "str"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repeat": "",
|
||||||
|
"crontab": "",
|
||||||
|
"once": false,
|
||||||
|
"onceDelay": "20",
|
||||||
|
"topic": "",
|
||||||
|
"payload": "",
|
||||||
|
"payloadType": "date",
|
||||||
|
"x": 330,
|
||||||
|
"y": 3160,
|
||||||
|
"wires":
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"32ee1eade51fae50"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Example: Send a picture (click to expand)</summary>
|
||||||
|
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "d135a13eadeb9d6d",
|
||||||
|
"type": "http request",
|
||||||
|
"z": "fabdd7a3.4045a",
|
||||||
|
"name": "Download image",
|
||||||
|
"method": "GET",
|
||||||
|
"ret": "bin",
|
||||||
|
"paytoqs": "ignore",
|
||||||
|
"url": "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png",
|
||||||
|
"tls": "",
|
||||||
|
"persist": false,
|
||||||
|
"proxy": "",
|
||||||
|
"authType": "",
|
||||||
|
"senderr": false,
|
||||||
|
"credentials":
|
||||||
|
{
|
||||||
|
"user": "",
|
||||||
|
"password": ""
|
||||||
|
},
|
||||||
|
"x": 490,
|
||||||
|
"y": 3320,
|
||||||
|
"wires":
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"6e75bc41d2ec4a03"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6e75bc41d2ec4a03",
|
||||||
|
"type": "function",
|
||||||
|
"z": "fabdd7a3.4045a",
|
||||||
|
"name": "data",
|
||||||
|
"func": "msg.payload = msg.payload;\nmsg.headers = {};\nmsg.headers['tags'] = 'house';\nmsg.headers['X-Title'] = 'Home Assistant - Picture';\n\nreturn msg;",
|
||||||
|
"outputs": 1,
|
||||||
|
"noerr": 0,
|
||||||
|
"initialize": "",
|
||||||
|
"finalize": "",
|
||||||
|
"libs": [],
|
||||||
|
"x": 650,
|
||||||
|
"y": 3320,
|
||||||
|
"wires":
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"eb160615b6ceda98"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "eb160615b6ceda98",
|
||||||
|
"type": "http request",
|
||||||
|
"z": "fabdd7a3.4045a",
|
||||||
|
"name": "ntfy.sh",
|
||||||
|
"method": "PUT",
|
||||||
|
"ret": "bin",
|
||||||
|
"paytoqs": "ignore",
|
||||||
|
"url": "https://ntfy.sh/mytopic",
|
||||||
|
"tls": "",
|
||||||
|
"persist": false,
|
||||||
|
"proxy": "",
|
||||||
|
"authType": "",
|
||||||
|
"senderr": false,
|
||||||
|
"credentials":
|
||||||
|
{
|
||||||
|
"user": "",
|
||||||
|
"password": ""
|
||||||
|
},
|
||||||
|
"x": 770,
|
||||||
|
"y": 3320,
|
||||||
|
"wires":
|
||||||
|
[
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5b8dbf15c8a7a3a5",
|
||||||
|
"type": "inject",
|
||||||
|
"z": "fabdd7a3.4045a",
|
||||||
|
"name": "Manual start",
|
||||||
|
"props":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"p": "payload"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"p": "topic",
|
||||||
|
"vt": "str"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repeat": "",
|
||||||
|
"crontab": "",
|
||||||
|
"once": false,
|
||||||
|
"onceDelay": "20",
|
||||||
|
"topic": "",
|
||||||
|
"payload": "",
|
||||||
|
"payloadType": "date",
|
||||||
|
"x": 310,
|
||||||
|
"y": 3320,
|
||||||
|
"wires":
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"d135a13eadeb9d6d"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Gatus service health check
|
||||||
|
|
||||||
|
An example for a custom alert with <a href="https://github.com/TwiN/gatus">Gatus</a>
|
||||||
|
```
|
||||||
|
alerting:
|
||||||
|
custom:
|
||||||
|
url: "https://ntfy.sh"
|
||||||
|
method: "POST"
|
||||||
|
body: |
|
||||||
|
{
|
||||||
|
"topic": "mytopic",
|
||||||
|
"message": "[ENDPOINT_NAME] - [ALERT_DESCRIPTION]",
|
||||||
|
"title": "Gatus",
|
||||||
|
"tags": ["[ALERT_TRIGGERED_OR_RESOLVED]"],
|
||||||
|
"priority": 3
|
||||||
|
}
|
||||||
|
default-alert:
|
||||||
|
enabled: true
|
||||||
|
description: "health check failed"
|
||||||
|
send-on-resolved: true
|
||||||
|
failure-threshold: 3
|
||||||
|
success-threshold: 3
|
||||||
|
placeholders:
|
||||||
|
ALERT_TRIGGERED_OR_RESOLVED:
|
||||||
|
TRIGGERED: "warning"
|
||||||
|
RESOLVED: "white_check_mark"
|
||||||
|
```
|
||||||
|
|||||||
@@ -26,23 +26,29 @@ deb/rpm packages.
|
|||||||
|
|
||||||
=== "x86_64/amd64"
|
=== "x86_64/amd64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.18.0/ntfy_1.18.0_linux_x86_64.tar.gz
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.19.0/ntfy_1.19.0_linux_x86_64.tar.gz
|
||||||
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
tar zxvf ntfy_1.19.0_linux_x86_64.tar.gz
|
||||||
sudo ./ntfy serve
|
sudo cp -a ntfy_1.19.0_linux_x86_64/ntfy /usr/bin/ntfy
|
||||||
|
sudo mkdir /etc/ntfy && sudo cp ntfy_1.19.0_linux_x86_64/{client,server}/*.yml /etc/ntfy
|
||||||
|
sudo ntfy serve
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "armv7/armhf"
|
=== "armv7/armhf"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.18.0/ntfy_1.18.0_linux_armv7.tar.gz
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.19.0/ntfy_1.19.0_linux_armv7.tar.gz
|
||||||
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
tar zxvf ntfy_1.19.0_linux_armv7.tar.gz
|
||||||
sudo ./ntfy serve
|
sudo cp -a ntfy_1.19.0_linux_armv7/ntfy /usr/bin/ntfy
|
||||||
|
sudo mkdir /etc/ntfy && sudo cp ntfy_1.19.0_linux_armv7/{client,server}/*.yml /etc/ntfy
|
||||||
|
sudo ntfy serve
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "arm64"
|
=== "arm64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.18.0/ntfy_1.18.0_linux_arm64.tar.gz
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.19.0/ntfy_1.19.0_linux_arm64.tar.gz
|
||||||
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
tar zxvf ntfy_1.19.0_linux_arm64.tar.gz
|
||||||
sudo ./ntfy serve
|
sudo cp -a ntfy_1.19.0_linux_arm64/ntfy /usr/bin/ntfy
|
||||||
|
sudo mkdir /etc/ntfy && sudo cp ntfy_1.19.0_linux_arm64/{client,server}/*.yml /etc/ntfy
|
||||||
|
sudo ntfy serve
|
||||||
```
|
```
|
||||||
|
|
||||||
## Debian/Ubuntu repository
|
## Debian/Ubuntu repository
|
||||||
@@ -88,7 +94,7 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "x86_64/amd64"
|
=== "x86_64/amd64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.18.0/ntfy_1.18.0_linux_amd64.deb
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.19.0/ntfy_1.19.0_linux_amd64.deb
|
||||||
sudo dpkg -i ntfy_*.deb
|
sudo dpkg -i ntfy_*.deb
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
@@ -96,7 +102,7 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "armv7/armhf"
|
=== "armv7/armhf"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.18.0/ntfy_1.18.0_linux_armv7.deb
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.19.0/ntfy_1.19.0_linux_armv7.deb
|
||||||
sudo dpkg -i ntfy_*.deb
|
sudo dpkg -i ntfy_*.deb
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
@@ -104,7 +110,7 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "arm64"
|
=== "arm64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.18.0/ntfy_1.18.0_linux_arm64.deb
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.19.0/ntfy_1.19.0_linux_arm64.deb
|
||||||
sudo dpkg -i ntfy_*.deb
|
sudo dpkg -i ntfy_*.deb
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
@@ -114,21 +120,21 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "x86_64/amd64"
|
=== "x86_64/amd64"
|
||||||
```bash
|
```bash
|
||||||
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.18.0/ntfy_1.18.0_linux_amd64.rpm
|
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.19.0/ntfy_1.19.0_linux_amd64.rpm
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "armv7/armhf"
|
=== "armv7/armhf"
|
||||||
```bash
|
```bash
|
||||||
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.18.0/ntfy_1.18.0_linux_armv7.rpm
|
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.19.0/ntfy_1.19.0_linux_armv7.rpm
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "arm64"
|
=== "arm64"
|
||||||
```bash
|
```bash
|
||||||
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.18.0/ntfy_1.18.0_linux_arm64.rpm
|
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.19.0/ntfy_1.19.0_linux_arm64.rpm
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
```
|
```
|
||||||
@@ -188,13 +194,3 @@ COPY server.yml /etc/ntfy/server.yml
|
|||||||
ENTRYPOINT ["ntfy", "serve"]
|
ENTRYPOINT ["ntfy", "serve"]
|
||||||
```
|
```
|
||||||
This image can be pushed to a container registry and shipped independently. All that's needed when running it is mapping ntfy's port to a host port.
|
This image can be pushed to a container registry and shipped independently. All that's needed when running it is mapping ntfy's port to a host port.
|
||||||
|
|
||||||
## Go
|
|
||||||
To install via Go, simply run:
|
|
||||||
```bash
|
|
||||||
go install heckel.io/ntfy@latest
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! info
|
|
||||||
Please [let me know](https://github.com/binwiederhier/ntfy/issues) if there are any issues with this installation
|
|
||||||
method. The SQLite bindings require CGO and it works for me, but I have the feeling it may not work for everyone.
|
|
||||||
|
|||||||
143
docs/publish.md
143
docs/publish.md
@@ -36,6 +36,11 @@ Here's an example showing how to publish a simple message using a POST request:
|
|||||||
strings.NewReader("Backup successful 😀"))
|
strings.NewReader("Backup successful 😀"))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri https://ntfy.sh/topic -Body "Backup successful 😀" -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.sh/mytopic",
|
requests.post("https://ntfy.sh/mytopic",
|
||||||
@@ -117,6 +122,16 @@ a [title](#message-title), and [tag messages](#tags-emojis) 🥳 🎉. Here's an
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.sh/phil_alerts"
|
||||||
|
$headers = @{ Title="Unauthorized access detected"
|
||||||
|
Priority="urgent"
|
||||||
|
Tags="warning,skull" }
|
||||||
|
$body = "Remote access to phils-laptop detected. Act right away."
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $body -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.sh/phil_alerts",
|
requests.post("https://ntfy.sh/phil_alerts",
|
||||||
@@ -191,6 +206,14 @@ you can set the `X-Title` header (or any of its aliases: `Title`, `ti`, or `t`).
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.sh/controversial"
|
||||||
|
$headers = @{ Title="Dogs are better than cats" }
|
||||||
|
$body = "Oh my ..."
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $body -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.sh/controversial",
|
requests.post("https://ntfy.sh/controversial",
|
||||||
@@ -222,13 +245,13 @@ notification sounds and vibration patterns on your phone to map to these priorit
|
|||||||
|
|
||||||
The following priorities exist:
|
The following priorities exist:
|
||||||
|
|
||||||
| Priority | Icon | ID | Name | Description |
|
| Priority | Icon | ID | Name | Description |
|
||||||
|---|---|---|---|---|
|
|----------------------|--------------------------------------------|-----|----------------|--------------------------------------------------------------------------------------------------------|
|
||||||
| Max priority |  | `5` | `max`/`urgent` | Really long vibration bursts, default notification sound with a pop-over notification. |
|
| Max priority |  | `5` | `max`/`urgent` | Really long vibration bursts, default notification sound with a pop-over notification. |
|
||||||
| High priority |  | `4` | `high` | Long vibration burst, default notification sound with a pop-over notification. |
|
| High priority |  | `4` | `high` | Long vibration burst, default notification sound with a pop-over notification. |
|
||||||
| **Default priority** | *(none)* | `3` | `default` | Short default vibration and sound. Default notification behavior. |
|
| **Default priority** | *(none)* | `3` | `default` | Short default vibration and sound. Default notification behavior. |
|
||||||
| Low priority |  |`2` | `low` | No vibration or sound. Notification will not visibly show up until notification drawer is pulled down. |
|
| Low priority |  | `2` | `low` | No vibration or sound. Notification will not visibly show up until notification drawer is pulled down. |
|
||||||
| Min priority |  | `1` | `min` | No vibration or sound. The notification will be under the fold in "Other notifications". |
|
| Min priority |  | `1` | `min` | No vibration or sound. The notification will be under the fold in "Other notifications". |
|
||||||
|
|
||||||
You can set the priority with the header `X-Priority` (or any of its aliases: `Priority`, `prio`, or `p`).
|
You can set the priority with the header `X-Priority` (or any of its aliases: `Priority`, `prio`, or `p`).
|
||||||
|
|
||||||
@@ -271,6 +294,14 @@ You can set the priority with the header `X-Priority` (or any of its aliases: `P
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.sh/phil_alerts"
|
||||||
|
$headers = @{ Priority="5" }
|
||||||
|
$body = "An urgent message"
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $body -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.sh/phil_alerts",
|
requests.post("https://ntfy.sh/phil_alerts",
|
||||||
@@ -382,6 +413,14 @@ them with a comma, e.g. `tag1,tag2,tag3`.
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.sh/backups"
|
||||||
|
$headers = @{ Tags="warning,mailsrv13,daily-backup" }
|
||||||
|
$body = "Backup of mailsrv13 failed"
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $body -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.sh/backups",
|
requests.post("https://ntfy.sh/backups",
|
||||||
@@ -464,6 +503,14 @@ to be delivered in 3 days, it'll remain in the cache for 3 days and 12 hours. Al
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.sh/hello"
|
||||||
|
$headers = @{ At="tomorrow, 10am" }
|
||||||
|
$body = "Good morning"
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $body -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.sh/hello",
|
requests.post("https://ntfy.sh/hello",
|
||||||
@@ -538,6 +585,11 @@ For instance, assuming your topic is `mywebhook`, you can simply call `/mywebhoo
|
|||||||
http.Get("https://ntfy.sh/mywebhook/trigger")
|
http.Get("https://ntfy.sh/mywebhook/trigger")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
Invoke-RestMethod -Method 'Get' -Uri "ntfy.sh/mywebhook/trigger"
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.get("https://ntfy.sh/mywebhook/trigger")
|
requests.get("https://ntfy.sh/mywebhook/trigger")
|
||||||
@@ -582,6 +634,11 @@ Here's an example with a custom message, tags and a priority:
|
|||||||
http.Get("https://ntfy.sh/mywebhook/publish?message=Webhook+triggered&priority=high&tags=warning,skull")
|
http.Get("https://ntfy.sh/mywebhook/publish?message=Webhook+triggered&priority=high&tags=warning,skull")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
Invoke-RestMethod -Method 'Get' -Uri "ntfy.sh/mywebhook/publish?message=Webhook+triggered&priority=high&tags=warning,skull"
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.get("https://ntfy.sh/mywebhook/publish?message=Webhook+triggered&priority=high&tags=warning,skull")
|
requests.get("https://ntfy.sh/mywebhook/publish?message=Webhook+triggered&priority=high&tags=warning,skull")
|
||||||
@@ -604,7 +661,8 @@ the example.
|
|||||||
To publish as JSON, you must **PUT/POST to the ntfy root URL**, not to the topic URL. Be sure to check that you're
|
To publish as JSON, you must **PUT/POST to the ntfy root URL**, not to the topic URL. Be sure to check that you're
|
||||||
POST-ing to `https://ntfy.sh/` (correct), and not to `https://ntfy.sh/mytopic` (incorrect).
|
POST-ing to `https://ntfy.sh/` (correct), and not to `https://ntfy.sh/mytopic` (incorrect).
|
||||||
|
|
||||||
Here's an example using all supported parameters. The `topic` parameter is the only required one:
|
Here's an example using most supported parameters. Check the table below for a complete list. The `topic` parameter
|
||||||
|
is the only required one:
|
||||||
|
|
||||||
=== "Command line (curl)"
|
=== "Command line (curl)"
|
||||||
```
|
```
|
||||||
@@ -675,6 +733,22 @@ Here's an example using all supported parameters. The `topic` parameter is the o
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.sh"
|
||||||
|
$body = @{
|
||||||
|
"topic"="powershell"
|
||||||
|
"title"="Low disk space alert"
|
||||||
|
"message"="Disk space is low at 5.1 GB"
|
||||||
|
"priority"=4
|
||||||
|
"attach"="https://filesrv.lan/space.jpg"
|
||||||
|
"filename"="diskspace.jpg"
|
||||||
|
"tags"=@("warning","cd")
|
||||||
|
"click"= "https://homecamera.lan/xasds1h2xsSsa/"
|
||||||
|
} | ConvertTo-Json
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Body $body -ContentType "application/json" -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.sh/",
|
requests.post("https://ntfy.sh/",
|
||||||
@@ -725,7 +799,8 @@ all the supported fields:
|
|||||||
| `click` | - | *URL* | `https://example.com` | Website opened when notification is [clicked](#click-action) |
|
| `click` | - | *URL* | `https://example.com` | Website opened when notification is [clicked](#click-action) |
|
||||||
| `attach` | - | *URL* | `https://example.com/file.jpg` | URL of an attachment, see [attach via URL](#attach-file-from-url) |
|
| `attach` | - | *URL* | `https://example.com/file.jpg` | URL of an attachment, see [attach via URL](#attach-file-from-url) |
|
||||||
| `filename` | - | *string* | `file.jpg` | File name of the attachment |
|
| `filename` | - | *string* | `file.jpg` | File name of the attachment |
|
||||||
|
| `delay` | - | *string* | `30min`, `9am` | Timestamp or duration for delayed delivery |
|
||||||
|
| `email` | - | *e-mail address* | `phil@example.com` | E-mail address for e-mail notifications |
|
||||||
|
|
||||||
## Click action
|
## Click action
|
||||||
You can define which URL to open when a notification is clicked. This may be useful if your notification is related
|
You can define which URL to open when a notification is clicked. This may be useful if your notification is related
|
||||||
@@ -774,6 +849,14 @@ Here's an example that will open Reddit when the notification is clicked:
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.sh/reddit_alerts"
|
||||||
|
$headers = @{ Click="https://www.reddit.com/message/messages" }
|
||||||
|
$body = "New messages on Reddit"
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $body -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.sh/reddit_alerts",
|
requests.post("https://ntfy.sh/reddit_alerts",
|
||||||
@@ -936,6 +1019,13 @@ Here's an example showing how to attach an APK file:
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.sh/mydownloads"
|
||||||
|
$headers = @{ Attach="https://f-droid.org/F-Droid.apk" }
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.put("https://ntfy.sh/mydownloads",
|
requests.put("https://ntfy.sh/mydownloads",
|
||||||
@@ -1025,6 +1115,17 @@ that, your IP address appears in the e-mail body. This is to prevent abuse.
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.sh/alerts"
|
||||||
|
$headers = @{ Title"="Low disk space alert"
|
||||||
|
Priority="high"
|
||||||
|
Tags="warning,skull,backup-host,ssh-login")
|
||||||
|
Email="phil@example.com" }
|
||||||
|
$body = "Unknown login from 5.31.23.83 to backups.example.com"
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Body $body -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.sh/alerts",
|
requests.post("https://ntfy.sh/alerts",
|
||||||
@@ -1135,6 +1236,14 @@ Here's a simple example:
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.example.com/mysecrets"
|
||||||
|
$headers = @{ Authorization="Basic cGhpbDpteXBhc3M=" }
|
||||||
|
$body = "Look ma, with auth"
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Body $body -Headers $headers -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.example.com/mysecrets",
|
requests.post("https://ntfy.example.com/mysecrets",
|
||||||
@@ -1210,6 +1319,14 @@ are still delivered to connected subscribers, but [`since=`](subscribe/api.md#fe
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.sh/mytopic"
|
||||||
|
$headers = @{ Cache="no" }
|
||||||
|
$body = "This message won't be stored server-side"
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Body $body -Headers $headers -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.sh/mytopic",
|
requests.post("https://ntfy.sh/mytopic",
|
||||||
@@ -1282,6 +1399,14 @@ to `no`. This will instruct the server not to forward messages to Firebase.
|
|||||||
http.DefaultClient.Do(req)
|
http.DefaultClient.Do(req)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== "PowerShell"
|
||||||
|
``` powershell
|
||||||
|
$uri = "https://ntfy.sh/mytopic"
|
||||||
|
$headers = @{ Firebase="no" }
|
||||||
|
$body = "This message won't be forwarded to FCM"
|
||||||
|
Invoke-RestMethod -Method 'Post' -Uri $uri -Body $body -Headers $headers -UseBasicParsing
|
||||||
|
```
|
||||||
|
|
||||||
=== "Python"
|
=== "Python"
|
||||||
``` python
|
``` python
|
||||||
requests.post("https://ntfy.sh/mytopic",
|
requests.post("https://ntfy.sh/mytopic",
|
||||||
|
|||||||
@@ -2,12 +2,92 @@
|
|||||||
Binaries for all releases can be found on the GitHub releases pages for the [ntfy server](https://github.com/binwiederhier/ntfy/releases)
|
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).
|
and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/releases).
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
## ntfy Android app v1.11.0 (UNRELEASED)
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
|
||||||
|
* Download attachments to cache folder ([#181](https://github.com/binwiederhier/ntfy/issues/181))
|
||||||
|
* Regularly delete attachments for deleted notifications ([#142](https://github.com/binwiederhier/ntfy/issues/142))
|
||||||
|
* Translations to different languages ([#188](https://github.com/binwiederhier/ntfy/issues/188), thanks to
|
||||||
|
[@StoyanDimitrov](https://github.com/StoyanDimitrov) for initiating things)
|
||||||
|
|
||||||
|
**Bugs:**
|
||||||
|
|
||||||
|
* IllegalStateException: Failed to build unique file ([#177](https://github.com/binwiederhier/ntfy/issues/177), thanks to [@Fallenbagel](https://github.com/Fallenbagel) for reporting)
|
||||||
|
* SQLiteConstraintException: Crash during UP registration ([#185](https://github.com/binwiederhier/ntfy/issues/185))
|
||||||
|
* Refresh preferences screen after settings import (#183, thanks to [@cmeis](https://github.com/cmeis) for reporting)
|
||||||
|
|
||||||
|
**Translations:**
|
||||||
|
|
||||||
|
* English language improvements (thanks to [@comradekingu](https://github.com/comradekingu))
|
||||||
|
* Bulgarian (thanks to [@StoyanDimitrov](https://github.com/StoyanDimitrov))
|
||||||
|
* Chinese/Simplified (thanks to [@poi](https://hosted.weblate.org/user/poi) and [@PeterCxy](https://hosted.weblate.org/user/PeterCxy))
|
||||||
|
* Dutch (*incomplete*, thanks to [@diony](https://hosted.weblate.org/user/diony))
|
||||||
|
* French (*incomplete*, thanks to [@Kusoneko](https://kusoneko.moe/))
|
||||||
|
* German (thanks to [@cmeis](https://github.com/cmeis))
|
||||||
|
* Italian (thanks to [@theTranslator](https://hosted.weblate.org/user/theTranslator/))
|
||||||
|
* Norwegian (*incomplete*, thanks to [@comradekingu](https://github.com/comradekingu))
|
||||||
|
* Portuguese/Brazil (thanks to [@LW](https://hosted.weblate.org/user/LW/))
|
||||||
|
* Spanish (thanks to [@rogeliodh](https://github.com/rogeliodh))
|
||||||
|
* Turkish (thanks to [@ersen](https://ersen.moe/))
|
||||||
|
|
||||||
|
**Thanks:**
|
||||||
|
|
||||||
|
* Many thanks to [@cmeis](https://github.com/cmeis), [@Fallenbagel](https://github.com/Fallenbagel), [@Joeharrison94](https://github.com/Joeharrison94),
|
||||||
|
and [@rogeliodh](https://github.com/rogeliodh) for input on the new attachment logic, and for testing the release
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
## ntfy server v1.19.0
|
||||||
|
Released Mar 30, 2022
|
||||||
|
|
||||||
|
**Bugs:**
|
||||||
|
|
||||||
|
* Do not pack binary with `upx` for armv7/arm64 due to `illegal instruction` errors ([#191](https://github.com/binwiederhier/ntfy/issues/191), thanks to [@iexos](https://github.com/iexos))
|
||||||
|
* Do not allow comma in topic name in publish via GET endpoint (no ticket)
|
||||||
|
* Add "Access-Control-Allow-Origin: *" for attachments (no ticket, thanks to @FrameXX)
|
||||||
|
* Make pruning run again in web app ([#186](https://github.com/binwiederhier/ntfy/issues/186))
|
||||||
|
* Added missing params `delay` and `email` to publish as JSON body (no ticket)
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
|
||||||
|
* Improved [e-mail publishing](config.md#e-mail-publishing) documentation
|
||||||
|
|
||||||
|
## ntfy server v1.18.1
|
||||||
|
Released Mar 21, 2022
|
||||||
|
_This release ships no features or bug fixes. It's merely a documentation update._
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
|
||||||
|
* Overhaul of [developer documentation](https://ntfy.sh/docs/develop/)
|
||||||
|
* PowerShell examples for [publish documentation](https://ntfy.sh/docs/publish/) ([#138](https://github.com/binwiederhier/ntfy/issues/138), thanks to [@Joeharrison94](https://github.com/Joeharrison94))
|
||||||
|
* Additional examples for [NodeRED, Gatus, Sonarr, Radarr, ...](https://ntfy.sh/docs/examples/) (thanks to [@nickexyz](https://github.com/nickexyz))
|
||||||
|
* Fixes in developer instructions (thanks to [@Fallenbagel](https://github.com/Fallenbagel) for reporting)
|
||||||
|
|
||||||
|
## ntfy Android app v1.10.0
|
||||||
|
Released Mar 21, 2022
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
|
||||||
|
* Support for UnifiedPush 2.0 specification (bytes messages, [#130](https://github.com/binwiederhier/ntfy/issues/130))
|
||||||
|
* Export/import settings and subscriptions ([#115](https://github.com/binwiederhier/ntfy/issues/115), thanks [@cmeis](https://github.com/cmeis) for reporting)
|
||||||
|
* Open "Click" link when tapping notification ([#110](https://github.com/binwiederhier/ntfy/issues/110), thanks [@cmeis](https://github.com/cmeis) for reporting)
|
||||||
|
* JSON stream deprecation banner ([#164](https://github.com/binwiederhier/ntfy/issues/164))
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
|
||||||
|
* Display locale-specific times, with AM/PM or 24h format ([#140](https://github.com/binwiederhier/ntfy/issues/140), thanks [@hl2guide](https://github.com/hl2guide) for reporting)
|
||||||
|
|
||||||
## ntfy server v1.18.0
|
## ntfy server v1.18.0
|
||||||
Released Mar 16, 2022
|
Released Mar 16, 2022
|
||||||
|
|
||||||
**Features:**
|
**Features:**
|
||||||
|
|
||||||
* Publish messages as JSON ([#133](https://github.com/binwiederhier/ntfy/issues/133), thanks [@cmeis](https://github.com/cmeis) for reporting)
|
* [Publish messages as JSON](https://ntfy.sh/docs/publish/#publish-as-json) ([#133](https://github.com/binwiederhier/ntfy/issues/133),
|
||||||
|
thanks [@cmeis](https://github.com/cmeis) for reporting, thanks to [@Joeharrison94](https://github.com/Joeharrison94) and
|
||||||
|
[@Fallenbagel](https://github.com/Fallenbagel) for testing)
|
||||||
|
|
||||||
**Bug fixes:**
|
**Bug fixes:**
|
||||||
|
|
||||||
@@ -17,20 +97,8 @@ Released Mar 16, 2022
|
|||||||
|
|
||||||
**Deprecations:**
|
**Deprecations:**
|
||||||
|
|
||||||
* Removed the ability to run server as `ntfy serve` as per [deprecation](deprecations.md)
|
* Removed the ability to run server as `ntfy` (as opposed to `ntfy serve`) as per [deprecation](deprecations.md)
|
||||||
|
|
||||||
<!--
|
|
||||||
## ntfy Android app v1.10.0 (UNRELEASED)
|
|
||||||
|
|
||||||
**Features:**
|
|
||||||
|
|
||||||
* Support for UnifiedPush 2.0 specification (bytes messages, [#130](https://github.com/binwiederhier/ntfy/issues/130))
|
|
||||||
* Export/import settings and subscriptions ([#115](https://github.com/binwiederhier/ntfy/issues/115), thanks [@cmeis](https://github.com/cmeis) for reporting)
|
|
||||||
|
|
||||||
**Bug fixes:**
|
|
||||||
|
|
||||||
* Display locale-specific times, with AM/PM or 24h format ([#140](https://github.com/binwiederhier/ntfy/issues/140), thanks [@hl2guide](https://github.com/hl2guide) for reporting)
|
|
||||||
-->
|
|
||||||
## ntfy server v1.17.1
|
## ntfy server v1.17.1
|
||||||
Released Mar 12, 2022
|
Released Mar 12, 2022
|
||||||
|
|
||||||
|
|||||||
4
docs/static/css/extra.css
vendored
4
docs/static/css/extra.css
vendored
@@ -8,6 +8,10 @@
|
|||||||
width: unset !important;
|
width: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.md-sidebar {
|
||||||
|
width: 12.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
.md-typeset h4 {
|
.md-typeset h4 {
|
||||||
font-weight: 500 !important;
|
font-weight: 500 !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
|
|||||||
BIN
docs/static/img/nodered-message.png
vendored
Normal file
BIN
docs/static/img/nodered-message.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
BIN
docs/static/img/nodered-picture.png
vendored
Normal file
BIN
docs/static/img/nodered-picture.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.9 KiB |
10
go.mod
10
go.mod
@@ -15,7 +15,7 @@ require (
|
|||||||
github.com/olebedev/when v0.0.0-20211212231525-59bd4edcf9d6
|
github.com/olebedev/when v0.0.0-20211212231525-59bd4edcf9d6
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/urfave/cli/v2 v2.4.0
|
github.com/urfave/cli/v2 v2.4.0
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064
|
||||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect
|
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||||
@@ -28,7 +28,7 @@ require (
|
|||||||
cloud.google.com/go v0.100.2 // indirect
|
cloud.google.com/go v0.100.2 // indirect
|
||||||
cloud.google.com/go/compute v1.5.0 // indirect
|
cloud.google.com/go/compute v1.5.0 // indirect
|
||||||
cloud.google.com/go/iam v0.3.0 // indirect
|
cloud.google.com/go/iam v0.3.0 // indirect
|
||||||
github.com/AlekSi/pointer v1.0.0 // indirect
|
github.com/AlekSi/pointer v1.2.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac // indirect
|
github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
@@ -40,12 +40,12 @@ require (
|
|||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
go.opencensus.io v0.23.0 // indirect
|
go.opencensus.io v0.23.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
|
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106 // indirect
|
google.golang.org/genproto v0.0.0-20220322021311-435b647f9ef2 // indirect
|
||||||
google.golang.org/grpc v1.45.0 // indirect
|
google.golang.org/grpc v1.45.0 // indirect
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
google.golang.org/protobuf v1.28.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
17
go.sum
17
go.sum
@@ -61,8 +61,9 @@ cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKu
|
|||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4=
|
firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4=
|
||||||
firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs=
|
firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs=
|
||||||
github.com/AlekSi/pointer v1.0.0 h1:KWCWzsvFxNLcmM5XmiqHsGTTsuwZMsLFwWF9Y+//bNE=
|
|
||||||
github.com/AlekSi/pointer v1.0.0/go.mod h1:1kjywbfcPFCmncIxtk6fIEub6LKrfMz3gc5QKVOSOA8=
|
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 v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
|
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
|
||||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
@@ -244,8 +245,9 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
|
||||||
|
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@@ -317,6 +319,7 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
|
|||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
@@ -406,8 +409,9 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
|
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
|
||||||
|
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -604,8 +608,8 @@ google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2
|
|||||||
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||||
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||||
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||||
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106 h1:ErU+UA6wxadoU8nWrsy5MZUVBs75K17zUCsUCIfrXCE=
|
google.golang.org/genproto v0.0.0-20220322021311-435b647f9ef2 h1:3n0D2NdPGm0g0wrVJzXJWW5CBOoqgGBkDX9cRMJHZAY=
|
||||||
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
google.golang.org/genproto v0.0.0-20220322021311-435b647f9ef2/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
@@ -648,8 +652,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
|||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
|
||||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||||
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
# The documentation uses 'mkdocs', which is written in Python
|
# The documentation uses 'mkdocs', which is written in Python
|
||||||
|
|
||||||
# See https://github.com/squidfunk/mkdocs-material/issues/2030
|
|
||||||
jinja2>=2.11.1
|
|
||||||
|
|
||||||
# mkdocs
|
|
||||||
mkdocs
|
|
||||||
mkdocs-material
|
mkdocs-material
|
||||||
mkdocs-minify-plugin
|
mkdocs-minify-plugin
|
||||||
|
|||||||
@@ -55,15 +55,15 @@ type handleFunc func(http.ResponseWriter, *http.Request, *visitor) error
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// If changed, don't forget to update Android App and auth_sqlite.go
|
// If changed, don't forget to update Android App and auth_sqlite.go
|
||||||
topicRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`) // No /!
|
topicRegex = regexp.MustCompile(`^[-_A-Za-z0-9]{1,64}$`) // No /!
|
||||||
topicPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}$`) // Regex must match JS & Android app!
|
topicPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}$`) // Regex must match JS & Android app!
|
||||||
extTopicPathRegex = regexp.MustCompile(`^/[^/]+\.[^/]+/[-_A-Za-z0-9]{1,64}$`) // Extended topic path, for web-app, e.g. /example.com/mytopic
|
externalTopicPathRegex = regexp.MustCompile(`^/[^/]+\.[^/]+/[-_A-Za-z0-9]{1,64}$`) // Extended topic path, for web-app, e.g. /example.com/mytopic
|
||||||
jsonPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/json$`)
|
jsonPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/json$`)
|
||||||
ssePathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/sse$`)
|
ssePathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/sse$`)
|
||||||
rawPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/raw$`)
|
rawPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/raw$`)
|
||||||
wsPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/ws$`)
|
wsPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/ws$`)
|
||||||
authPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/auth$`)
|
authPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/auth$`)
|
||||||
publishPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/(publish|send|trigger)$`)
|
publishPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}/(publish|send|trigger)$`)
|
||||||
|
|
||||||
webConfigPath = "/config.js"
|
webConfigPath = "/config.js"
|
||||||
staticRegex = regexp.MustCompile(`^/static/.+`)
|
staticRegex = regexp.MustCompile(`^/static/.+`)
|
||||||
@@ -293,7 +293,7 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit
|
|||||||
return s.limitRequests(s.authRead(s.handleSubscribeWS))(w, r, v)
|
return s.limitRequests(s.authRead(s.handleSubscribeWS))(w, r, v)
|
||||||
} else if r.Method == http.MethodGet && authPathRegex.MatchString(r.URL.Path) {
|
} else if r.Method == http.MethodGet && authPathRegex.MatchString(r.URL.Path) {
|
||||||
return s.limitRequests(s.authRead(s.handleTopicAuth))(w, r, v)
|
return s.limitRequests(s.authRead(s.handleTopicAuth))(w, r, v)
|
||||||
} else if r.Method == http.MethodGet && (topicPathRegex.MatchString(r.URL.Path) || extTopicPathRegex.MatchString(r.URL.Path)) {
|
} else if r.Method == http.MethodGet && (topicPathRegex.MatchString(r.URL.Path) || externalTopicPathRegex.MatchString(r.URL.Path)) {
|
||||||
return s.handleTopic(w, r)
|
return s.handleTopic(w, r)
|
||||||
}
|
}
|
||||||
return errHTTPNotFound
|
return errHTTPNotFound
|
||||||
@@ -380,6 +380,7 @@ func (s *Server) handleFile(w http.ResponseWriter, r *http.Request, v *visitor)
|
|||||||
return errHTTPTooManyRequestsAttachmentBandwidthLimit
|
return errHTTPTooManyRequestsAttachmentBandwidthLimit
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", stat.Size()))
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", stat.Size()))
|
||||||
|
w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
|
||||||
f, err := os.Open(file)
|
f, err := os.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -1134,6 +1135,12 @@ func (s *Server) transformBodyJSON(next handleFunc) handleFunc {
|
|||||||
if m.Click != "" {
|
if m.Click != "" {
|
||||||
r.Header.Set("X-Click", m.Click)
|
r.Header.Set("X-Click", m.Click)
|
||||||
}
|
}
|
||||||
|
if m.Email != "" {
|
||||||
|
r.Header.Set("X-Email", m.Email)
|
||||||
|
}
|
||||||
|
if m.Delay != "" {
|
||||||
|
r.Header.Set("X-Delay", m.Delay)
|
||||||
|
}
|
||||||
return next(w, r, v)
|
return next(w, r, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,6 +203,14 @@ func TestServer_PublishPriority(t *testing.T) {
|
|||||||
require.Equal(t, 40007, toHTTPError(t, response.Body.String()).Code)
|
require.Equal(t, 40007, toHTTPError(t, response.Body.String()).Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServer_PublishGETOnlyOneTopic(t *testing.T) {
|
||||||
|
// This tests a bug that allowed publishing topics with a comma in the name (no ticket)
|
||||||
|
|
||||||
|
s := newTestServer(t, newTestConfig(t))
|
||||||
|
response := request(t, s, "GET", "/mytopic,mytopic2/publish?m=hi", "", nil)
|
||||||
|
require.Equal(t, 404, response.Code)
|
||||||
|
}
|
||||||
|
|
||||||
func TestServer_PublishNoCache(t *testing.T) {
|
func TestServer_PublishNoCache(t *testing.T) {
|
||||||
s := newTestServer(t, newTestConfig(t))
|
s := newTestServer(t, newTestConfig(t))
|
||||||
|
|
||||||
@@ -706,6 +714,12 @@ func (t *testMailer) Send(from, to string, m *message) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *testMailer) Count() int {
|
||||||
|
t.mu.Lock()
|
||||||
|
defer t.mu.Unlock()
|
||||||
|
return t.count
|
||||||
|
}
|
||||||
|
|
||||||
func TestServer_PublishTooRequests_Defaults(t *testing.T) {
|
func TestServer_PublishTooRequests_Defaults(t *testing.T) {
|
||||||
s := newTestServer(t, newTestConfig(t))
|
s := newTestServer(t, newTestConfig(t))
|
||||||
for i := 0; i < 60; i++ {
|
for i := 0; i < 60; i++ {
|
||||||
@@ -865,7 +879,8 @@ func TestServer_PublishUnifiedPushText(t *testing.T) {
|
|||||||
func TestServer_PublishAsJSON(t *testing.T) {
|
func TestServer_PublishAsJSON(t *testing.T) {
|
||||||
s := newTestServer(t, newTestConfig(t))
|
s := newTestServer(t, newTestConfig(t))
|
||||||
body := `{"topic":"mytopic","message":"A message","title":"a title\nwith lines","tags":["tag1","tag 2"],` +
|
body := `{"topic":"mytopic","message":"A message","title":"a title\nwith lines","tags":["tag1","tag 2"],` +
|
||||||
`"not-a-thing":"ok", "attach":"http://google.com","filename":"google.pdf", "click":"http://ntfy.sh","priority":4}`
|
`"not-a-thing":"ok", "attach":"http://google.com","filename":"google.pdf", "click":"http://ntfy.sh","priority":4,` +
|
||||||
|
`"delay":"30min"}`
|
||||||
response := request(t, s, "PUT", "/", body, nil)
|
response := request(t, s, "PUT", "/", body, nil)
|
||||||
require.Equal(t, 200, response.Code)
|
require.Equal(t, 200, response.Code)
|
||||||
|
|
||||||
@@ -878,6 +893,22 @@ func TestServer_PublishAsJSON(t *testing.T) {
|
|||||||
require.Equal(t, "google.pdf", m.Attachment.Name)
|
require.Equal(t, "google.pdf", m.Attachment.Name)
|
||||||
require.Equal(t, "http://ntfy.sh", m.Click)
|
require.Equal(t, "http://ntfy.sh", m.Click)
|
||||||
require.Equal(t, 4, m.Priority)
|
require.Equal(t, 4, m.Priority)
|
||||||
|
require.True(t, m.Time > time.Now().Unix()+29*60)
|
||||||
|
require.True(t, m.Time < time.Now().Unix()+31*60)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServer_PublishAsJSON_WithEmail(t *testing.T) {
|
||||||
|
mailer := &testMailer{}
|
||||||
|
s := newTestServer(t, newTestConfig(t))
|
||||||
|
s.mailer = mailer
|
||||||
|
body := `{"topic":"mytopic","message":"A message","email":"phil@example.com"}`
|
||||||
|
response := request(t, s, "PUT", "/", body, nil)
|
||||||
|
require.Equal(t, 200, response.Code)
|
||||||
|
|
||||||
|
m := toMessage(t, response.Body.String())
|
||||||
|
require.Equal(t, "mytopic", m.Topic)
|
||||||
|
require.Equal(t, "A message", m.Message)
|
||||||
|
require.Equal(t, 1, mailer.Count())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_PublishAsJSON_Invalid(t *testing.T) {
|
func TestServer_PublishAsJSON_Invalid(t *testing.T) {
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ type publishMessage struct {
|
|||||||
Click string `json:"click"`
|
Click string `json:"click"`
|
||||||
Attach string `json:"attach"`
|
Attach string `json:"attach"`
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Delay string `json:"delay"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// messageEncoder is a function that knows how to encode a message
|
// messageEncoder is a function that knows how to encode a message
|
||||||
|
|||||||
2776
web/package-lock.json
generated
2776
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -22,8 +22,7 @@
|
|||||||
"react-router-dom": "^6.2.2",
|
"react-router-dom": "^6.2.2",
|
||||||
"react-scripts": "^5.0.0",
|
"react-scripts": "^5.0.0",
|
||||||
"stacktrace-gps": "^3.0.4",
|
"stacktrace-gps": "^3.0.4",
|
||||||
"stacktrace-js": "^2.0.2",
|
"stacktrace-js": "^2.0.2"
|
||||||
"svgo": "^2.8.0"
|
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a, a:visited {
|
a, a:visited {
|
||||||
color: #3a9784;
|
color: #338574;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
@@ -114,7 +114,7 @@ code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.anchor .anchorLink:hover {
|
.anchor .anchorLink:hover {
|
||||||
color: #3a9784;
|
color: #338574;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ figcaption {
|
|||||||
/* Header */
|
/* Header */
|
||||||
|
|
||||||
#header {
|
#header {
|
||||||
background: #3a9784;
|
background: #338574;
|
||||||
height: 130px;
|
height: 130px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,4 @@ class Poller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const poller = new Poller();
|
const poller = new Poller();
|
||||||
poller.startWorker();
|
|
||||||
|
|
||||||
export default poller;
|
export default poller;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import prefs from "./Prefs";
|
import prefs from "./Prefs";
|
||||||
import subscriptionManager from "./SubscriptionManager";
|
import subscriptionManager from "./SubscriptionManager";
|
||||||
|
|
||||||
const delayMillis = 15000; // 15 seconds
|
const delayMillis = 25000; // 25 seconds
|
||||||
const intervalMillis = 1800000; // 30 minutes
|
const intervalMillis = 1800000; // 30 minutes
|
||||||
|
|
||||||
class Pruner {
|
class Pruner {
|
||||||
@@ -35,6 +35,4 @@ class Pruner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pruner = new Pruner();
|
const pruner = new Pruner();
|
||||||
pruner.startWorker();
|
|
||||||
|
|
||||||
export default pruner;
|
export default pruner;
|
||||||
|
|||||||
@@ -121,13 +121,16 @@ const SettingsIcons = (props) => {
|
|||||||
"Titles are optional, did you know that?",
|
"Titles are optional, did you know that?",
|
||||||
"ntfy is open source, and will always be free. Cool, right?",
|
"ntfy is open source, and will always be free. Cool, right?",
|
||||||
"I don't really like apples",
|
"I don't really like apples",
|
||||||
"My favorite TV show is The Wire. You should watch it!"
|
"My favorite TV show is The Wire. You should watch it!",
|
||||||
|
"You can attach files and URLs to messages too",
|
||||||
|
"You can delay messages up to 3 days"
|
||||||
])[0];
|
])[0];
|
||||||
|
const nowSeconds = Math.round(Date.now()/1000);
|
||||||
const message = shuffle([
|
const message = shuffle([
|
||||||
`Hello friend, this is a test notification from ntfy web. It's ${formatShortDateTime(Date.now())} right now. Is that early or late?`,
|
`Hello friend, this is a test notification from ntfy web. It's ${formatShortDateTime(nowSeconds)} right now. Is that early or late?`,
|
||||||
`So I heard you like ntfy? If that's true, go to GitHub and star it, or to the Play store and rate it. Thanks! Oh yeah, this is a test notification.`,
|
`So I heard you like ntfy? If that's true, go to GitHub and star it, or to the Play store and rate it. Thanks! Oh yeah, this is a test notification.`,
|
||||||
`It's almost like you want to hear what I have to say. I'm not even a machine. I'm just a sentence that Phil typed on a random Thursday.`,
|
`It's almost like you want to hear what I have to say. I'm not even a machine. I'm just a sentence that Phil typed on a random Thursday.`,
|
||||||
`Alright then, it's ${formatShortDateTime(Date.now())} already. Boy oh boy, where did the time go? I hope you're alright, friend.`,
|
`Alright then, it's ${formatShortDateTime(nowSeconds)} already. Boy oh boy, where did the time go? I hope you're alright, friend.`,
|
||||||
`There are nine million bicycles in Beijing That's a fact; It's a thing we can't deny. I wonder if that's true ...`,
|
`There are nine million bicycles in Beijing That's a fact; It's a thing we can't deny. I wonder if that's true ...`,
|
||||||
`I'm really excited that you're trying out ntfy. Did you know that there are a few public topics, such as ntfy.sh/stats and ntfy.sh/announcements.`,
|
`I'm really excited that you're trying out ntfy. Did you know that there are a few public topics, such as ntfy.sh/stats and ntfy.sh/announcements.`,
|
||||||
`It's interesting to hear what people use ntfy for. I've heard people talk about using it for so many cool things. What do you use it for?`
|
`It's interesting to hear what people use ntfy for. I've heard people talk about using it for so many cool things. What do you use it for?`
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {BrowserRouter, Outlet, Route, Routes, useOutletContext, useParams} from
|
|||||||
import {expandUrl} from "../app/utils";
|
import {expandUrl} from "../app/utils";
|
||||||
import ErrorBoundary from "./ErrorBoundary";
|
import ErrorBoundary from "./ErrorBoundary";
|
||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import {useAutoSubscribe, useConnectionListeners, useLocalStorageMigration} from "./hooks";
|
import {useAutoSubscribe, useConnectionListeners, useBackgroundProcesses} from "./hooks";
|
||||||
|
|
||||||
// TODO add drag and drop
|
// TODO add drag and drop
|
||||||
// TODO races when two tabs are open
|
// TODO races when two tabs are open
|
||||||
@@ -67,7 +67,7 @@ const Layout = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
useConnectionListeners(subscriptions, users);
|
useConnectionListeners(subscriptions, users);
|
||||||
useLocalStorageMigration();
|
useBackgroundProcesses();
|
||||||
useEffect(() => updateTitle(newNotificationsCount), [newNotificationsCount]);
|
useEffect(() => updateTitle(newNotificationsCount), [newNotificationsCount]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import notifier from "../app/Notifier";
|
|||||||
import routes from "./routes";
|
import routes from "./routes";
|
||||||
import connectionManager from "../app/ConnectionManager";
|
import connectionManager from "../app/ConnectionManager";
|
||||||
import poller from "../app/Poller";
|
import poller from "../app/Poller";
|
||||||
|
import pruner from "../app/Pruner";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wire connectionManager and subscriptionManager so that subscriptions are updated when the connection
|
* Wire connectionManager and subscriptionManager so that subscriptions are updated when the connection
|
||||||
@@ -67,29 +68,13 @@ export const useAutoSubscribe = (subscriptions, selected) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Migrate the 'topics' item in localStorage to the subscriptionManager. This is only done once to migrate away
|
* Start the poller and the pruner. This is done in a side effect as opposed to just in Pruner.js
|
||||||
* from the old web UI.
|
* and Poller.js, because side effect imports are not a thing in JS, and "Optimize imports" cleans
|
||||||
|
* up "unused" imports. See https://github.com/binwiederhier/ntfy/issues/186.
|
||||||
*/
|
*/
|
||||||
export const useLocalStorageMigration = () => {
|
export const useBackgroundProcesses = () => {
|
||||||
const [hasRun, setHasRun] = useState(false);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasRun) {
|
poller.startWorker();
|
||||||
return;
|
pruner.startWorker();
|
||||||
}
|
|
||||||
const topicsStr = localStorage.getItem("topics");
|
|
||||||
if (topicsStr) {
|
|
||||||
const topics = JSON.parse(topicsStr).filter(topic => topic !== "");
|
|
||||||
if (topics.length > 0) {
|
|
||||||
(async () => {
|
|
||||||
for (const topic of topics) {
|
|
||||||
const baseUrl = window.location.origin;
|
|
||||||
const subscription = await subscriptionManager.add(baseUrl, topic);
|
|
||||||
poller.pollInBackground(subscription); // Dangle!
|
|
||||||
}
|
|
||||||
localStorage.removeItem("topics");
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setHasRun(true);
|
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user