mirror of
https://github.com/binwiederhier/ntfy.git
synced 2024-11-01 09:31:18 +01:00
475 lines
21 KiB
Markdown
475 lines
21 KiB
Markdown
# 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
|
|
The ntfy server source code is available [on GitHub](https://github.com/binwiederhier/ntfy). The codebase for the
|
|
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
|
|
[main.go](https://github.com/binwiederhier/ntfy/blob/main/main.go), and the meat you're likely interested in is
|
|
in [server.go](https://github.com/binwiederhier/ntfy/blob/main/server/server.go). Notably, the server uses a
|
|
[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 [Vite](https://vitejs.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/test on Gitpod
|
|
To get a quick working development environment you can use [Gitpod](https://gitpod.io), an in-browser IDE
|
|
that makes it easy to develop ntfy without having to set up a desktop IDE. For any real development,
|
|
I do suggest a proper IDE like [IntelliJ IDEA](https://www.jetbrains.com/idea/).
|
|
|
|
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/binwiederhier/ntfy)
|
|
|
|
### 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.19.1.linux-amd64.tar.gz
|
|
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.19.1.linux-amd64.tar.gz
|
|
export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin
|
|
go version # verifies that it worked
|
|
```
|
|
|
|
Install [GoReleaser](https://goreleaser.com/) (see [official instructions](https://goreleaser.com/install/)):
|
|
``` shell
|
|
go install github.com/goreleaser/goreleaser@latest
|
|
goreleaser -v # verifies that it worked
|
|
```
|
|
|
|
Install [nodejs](https://nodejs.org/en/) (see [official instructions](https://nodejs.org/en/download/package-manager/)):
|
|
``` shell
|
|
curl -fsSL https://deb.nodesource.com/setup_18.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 \
|
|
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 cli-linux-amd64 - Build server/client binary (amd64, no web app or docs)
|
|
make install-linux-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 arm64),
|
|
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 a Docker image only for Linux
|
|
|
|
This is useful to test the final build with web app, docs, and server without any dependencies locally
|
|
|
|
``` shell
|
|
$ make docker-dev
|
|
$ docker run --rm -p 80:80 binwiederhier/ntfy:dev serve
|
|
```
|
|
|
|
### Build the ntfy binary
|
|
To build only the `ntfy` binary **without the web app or documentation**, use the `make cli-...` targets:
|
|
|
|
``` shell
|
|
$ make
|
|
Build server & client (using GoReleaser, not release version):
|
|
make cli - Build server & client (all architectures)
|
|
make cli-linux-amd64 - Build server & client (Linux, amd64 only)
|
|
make cli-linux-armv6 - Build server & client (Linux, armv6 only)
|
|
make cli-linux-armv7 - Build server & client (Linux, armv7 only)
|
|
make cli-linux-arm64 - Build server & client (Linux, arm64 only)
|
|
make cli-windows-amd64 - Build client (Windows, amd64 only)
|
|
make cli-darwin-all - Build client (macOS, arm64+amd64 universal binary)
|
|
```
|
|
|
|
So if you're on an amd64/x86_64-based machine, you may just want to run `make cli-linux-amd64` during testing. On a modern
|
|
system, this shouldn't take longer than 5-10 seconds. I often combine it with `install-linux-amd64` so I can run the binary
|
|
right away:
|
|
|
|
``` shell
|
|
$ make cli-linux-amd64 install-linux-amd64
|
|
$ ntfy serve
|
|
```
|
|
|
|
**During development of the main app, you can also just use `go run main.go`**, as long as you run
|
|
`make cli-deps-static-sites`at least once and `CGO_ENABLED=1`:
|
|
|
|
``` shell
|
|
$ export CGO_ENABLED=1
|
|
$ make cli-deps-static-sites
|
|
$ go run main.go serve
|
|
2022/03/18 08:43:55 Listening on :2586[http]
|
|
...
|
|
```
|
|
|
|
If you don't run `cli-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 `cli-deps-static-sites`
|
|
target creates dummy files that ensure that you'll be able to build.
|
|
|
|
While not officially supported (or released), you can build and run the server **on macOS** as well. Simply run
|
|
`make cli-darwin-server` to build a binary, or `go run main.go serve` (see above) to run it.
|
|
|
|
### 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 cli` (or `make cli-linux-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
|
|
```
|
|
|
|
### Testing Web Push locally
|
|
|
|
Reference: <https://stackoverflow.com/questions/34160509/options-for-testing-service-workers-via-http>
|
|
|
|
#### With the dev servers
|
|
|
|
1. Get web push keys `go run main.go web-push generate-keys`
|
|
|
|
2. Run the server with web push enabled
|
|
|
|
```sh
|
|
go run main.go \
|
|
--log-level debug \
|
|
serve \
|
|
--web-push-enabled \
|
|
--web-push-public-key KEY \
|
|
--web-push-private-key KEY \
|
|
--web-push-subscriptions-file=/tmp/subscriptions.db
|
|
```
|
|
|
|
3. In `web/public/config.js`:
|
|
|
|
- Set `base_url` to `http://localhost`, This is required as web push can only be used with the server matching the `base_url`.
|
|
|
|
- Set the `web_push_public_key` correctly.
|
|
|
|
4. Run `ENABLE_DEV_PWA=1 npm run start` - this enables the dev service worker
|
|
|
|
5. Set your browser to allow testing service workers insecurely:
|
|
|
|
- Chrome:
|
|
|
|
Open Chrome with special flags allowing insecure localhost service worker testing insecurely
|
|
|
|
```sh
|
|
# for example, macOS
|
|
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
|
|
--user-data-dir=/tmp/foo \
|
|
--unsafely-treat-insecure-origin-as-secure=http://localhost:3000,http://localhost
|
|
```
|
|
|
|
- Firefox:
|
|
|
|
See here: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
|
|
|
|
> Note: On Firefox, for testing you can run service workers over HTTP (insecurely); simply check the Enable Service Workers over HTTP (when toolbox is open) option in the Firefox Devtools options/gear menu
|
|
|
|
- Safari, iOS:
|
|
|
|
There doesn't seem to be a good way to do this currently. The only way is to serve a valid HTTPS certificate.
|
|
|
|
This is beyond the scope of this guide, but you can try `mkcert`, a number of reverse proxies such as Traefik and Caddy,
|
|
or tunneling software such as [Cloudflare Tunnels][cloudflare_tunnels] or ngrok.
|
|
|
|
[cloudflare_tunnels]: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/do-more-with-tunnels/trycloudflare/
|
|
|
|
6. Open <http://localhost:3000/>
|
|
#### With a built package
|
|
|
|
1. Run `make web-build`
|
|
|
|
2. Follow steps 1, 2, 4 and 5 from "With the dev servers"
|
|
|
|
3. Open <http://localhost/>
|
|
### 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
|
|
The ntfy Android app source code is available [on GitHub](https://github.com/binwiederhier/ntfy-android).
|
|
The Android app has two flavors:
|
|
|
|
* **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
|
|
|
|
### 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:
|
|
|
|
=== "via HTTPS"
|
|
``` shell
|
|
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.
|
|
|
|
### 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 [values.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/values.xml)
|
|
if you're self-hosting the server. Then run:
|
|
```
|
|
# Remove Google dependencies (FCM)
|
|
sed -i -e '/google-services/d' build.gradle
|
|
sed -i -e '/google-services/d' app/build.gradle
|
|
|
|
# To build an unsigned .apk (app/build/outputs/apk/fdroid/*.apk)
|
|
./gradlew assembleFdroidRelease
|
|
|
|
# To build a bundle .aab (app/fdroid/release/*.aab)
|
|
./gradlew bundleFdroidRelease
|
|
```
|
|
|
|
### 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:
|
|
|
|
* Create a Firebase/FCM account
|
|
* Place your account file at `app/google-services.json`
|
|
* And change `app_base_url` in [values.xml](https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/values/values.xml)
|
|
* Then run:
|
|
```
|
|
# To build an unsigned .apk (app/build/outputs/apk/play/*.apk)
|
|
./gradlew assemblePlayRelease
|
|
|
|
# To build a bundle .aab (app/play/release/*.aab)
|
|
./gradlew bundlePlayRelease
|
|
```
|
|
|
|
## iOS app
|
|
Building the iOS app is very involved. Please report any inconsistencies or issues with it. The requirements are
|
|
strictly based off of my development on this app. There may be other versions of macOS / XCode that work.
|
|
|
|
### Requirements
|
|
1. macOS Monterey or later
|
|
1. XCode 13.2+
|
|
1. A physical iOS device (for push notifications, Firebase does not work in the XCode simulator)
|
|
1. Firebase account
|
|
1. Apple Developer license? (I forget if it's possible to do testing without purchasing the license)
|
|
|
|
### Apple setup
|
|
|
|
!!! info
|
|
Along with this step, the [PLIST Deployment](#plist-deployment-and-configuration) step is also required
|
|
for these changes to take effect in the iOS app.
|
|
|
|
1. [Create a new key in Apple Developer Member Center](https://developer.apple.com/account/resources/authkeys/add)
|
|
1. Select "Apple Push Notifications service (APNs)"
|
|
1. Download the newly created key (should have a file name similar to `AuthKey_ZZZZZZ.p8`, where `ZZZZZZ` is the **Key ID**)
|
|
1. Record your **Team ID** - it can be seen in the top-right corner of the page, or on your Account > Membership page
|
|
1. Next, navigate to "Project Settings" in the firebase console for your project, and select the iOS app you created. Then, click "Cloud Messaging" in the left sidebar, and scroll down to the "APNs Authentication Key" section. Click "Upload Key", and upload the key you downloaded from Apple Developer.
|
|
|
|
!!! warning
|
|
If you don't do the above setups for APNS, **notifications will not post instantly or sometimes at all**. This is because of the missing APNS key, which is required for firebase to send notifications to the iOS app. See below for a snip from the firebase docs.
|
|
|
|
If you don't have an APNs authentication key, you can still send notifications to iOS devices, but they won't be delivered
|
|
instantly. Instead, they'll be delivered when the device wakes up to check for new notifications or when your application
|
|
sends a firebase request to check for them. The time to check for new notifications can vary from a few seconds to hours,
|
|
days or even weeks. Enabling APNs authentication keys ensures that notifications are delivered instantly and is strongly
|
|
recommended.
|
|
|
|
### Firebase setup
|
|
|
|
1. If you haven't already, create a Google / Firebase account
|
|
1. Visit the [Firebase console](https://console.firebase.google.com)
|
|
1. Create a new Firebase project:
|
|
1. Enter a project name
|
|
1. Disable Google Analytics (currently iOS app does not support analytics)
|
|
1. On the "Project settings" page, add an iOS app
|
|
1. Apple bundle ID - "com.copephobia.ntfy-ios" (this can be changed to match XCode's ntfy.sh target > "Bundle Identifier" value)
|
|
1. Register the app
|
|
1. Download the config file - GoogleInfo.plist (this will need to be included in the ntfy-ios repository / XCode)
|
|
1. Generate a new service account private key for the ntfy server
|
|
1. Go to "Project settings" > "Service accounts"
|
|
1. Click "Generate new private key" to generate and download a private key to use for sending messages via the ntfy server
|
|
|
|
### ntfy server
|
|
Note that the ntfy server is not officially supported on macOS. It should, however, be able to run on macOS using these
|
|
steps:
|
|
|
|
1. If not already made, make the `/etc/ntfy/` directory and move the service account private key to that folder
|
|
1. Copy the `server/server.yml` file from the ntfy repository to `/etc/ntfy/`
|
|
1. Modify the `/etc/ntfy/server.yml` file `firebase-key-file` value to the path of the private key
|
|
1. Install go: `brew install go`
|
|
1. In the ntfy repository, run `make cli-darwin-server`.
|
|
|
|
### XCode setup
|
|
|
|
1. Follow step 4 of [https://firebase.google.com/docs/ios/setup](Add Firebase to your Apple project) to install the
|
|
`firebase-ios-sdk` in XCode, if it's not already present - you can select any packages in addition to Firebase Core / Firebase Messaging
|
|
1. Similarly, install the SQLite.swift package dependency in XCode
|
|
1. When running the debug build, ensure XCode is pointed to the connected iOS device - registering for push notifications does not work in the iOS simulators
|
|
|
|
### PLIST config
|
|
To have instant notifications/better notification delivery when using firebase, you will need to add the
|
|
`GoogleService-Info.plist` file to your project. Here's how to do that:
|
|
|
|
1. In XCode, find the NTFY app target. **Not** the NSE app target.
|
|
1. Find the Asset/ folder in the project navigator
|
|
1. Drag the `GoogleService-Info.plist` file into the Asset/ folder that you get from the firebase console. It can be
|
|
found in the "Project settings" > "General" > "Your apps" with a button labled "GoogleService-Info.plist"
|
|
|
|
After that, you should be all set!
|