refactor: replace install.sh with Python install.py
This commit is contained in:
+1
-1
@@ -44,7 +44,7 @@ secrets. Its only dependency outside the standard library is the first-party
|
|||||||
`/etc/nuntius/config.toml` on first start.
|
`/etc/nuntius/config.toml` on first start.
|
||||||
- **systemd unit** — installed inline from `docs/deployment.md` with
|
- **systemd unit** — installed inline from `docs/deployment.md` with
|
||||||
`Restart=on-failure` and `EnvironmentFile=`.
|
`Restart=on-failure` and `EnvironmentFile=`.
|
||||||
- **Install script** — `install.sh`, idempotent, handles user creation, config
|
- **Install script** — `install.py`, idempotent, handles user creation, config
|
||||||
generation, and secrets file mode `0600`.
|
generation, and secrets file mode `0600`.
|
||||||
- **docs/** — `architecture.md`, `configuration.md`, `api-reference.md`,
|
- **docs/** — `architecture.md`, `configuration.md`, `api-reference.md`,
|
||||||
`deployment.md`, `security.md`, `library-usage.md`.
|
`deployment.md`, `security.md`, `library-usage.md`.
|
||||||
|
|||||||
+1
-1
@@ -140,7 +140,7 @@ nuntius/
|
|||||||
├── examples/
|
├── examples/
|
||||||
│ └── minimal/ # Standalone usage of pkg/contactform (no HTTP server)
|
│ └── minimal/ # Standalone usage of pkg/contactform (no HTTP server)
|
||||||
├── docs/ # Architecture, configuration, API, deployment, security, library usage
|
├── docs/ # Architecture, configuration, API, deployment, security, library usage
|
||||||
├── install.sh # One-shot server installer (idempotent)
|
├── install.py # One-shot server installer (idempotent)
|
||||||
├── justfile # install, build, test, run, uninstall
|
├── justfile # install, build, test, run, uninstall
|
||||||
├── go.mod # Go 1.26, no external dependencies
|
├── go.mod # Go 1.26, no external dependencies
|
||||||
└── LICENSE # MIT
|
└── LICENSE # MIT
|
||||||
|
|||||||
Binary file not shown.
@@ -160,7 +160,7 @@ NUNTIUS_SMTP_PASSWORD=actual-app-password
|
|||||||
EnvironmentFile=/etc/nuntius/.env
|
EnvironmentFile=/etc/nuntius/.env
|
||||||
```
|
```
|
||||||
|
|
||||||
The `install.sh` helper script sets the file mode automatically.
|
The `install.py` helper script sets the file mode automatically.
|
||||||
|
|
||||||
## Defaults at a Glance
|
## Defaults at a Glance
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -37,7 +37,7 @@ rsync -avz \
|
|||||||
user@your-server:/tmp/nuntius/
|
user@your-server:/tmp/nuntius/
|
||||||
```
|
```
|
||||||
|
|
||||||
> `install.sh` lives in the repository root. The systemd unit is included inline in Option A below (you do not need to rsync it from the repo). `.env.example` is a template for the secrets file. Adjust the `rsync` source list to match what you keep checked in.
|
> `install.py` lives in the repository root. The systemd unit is included inline in Option A below (you do not need to rsync it from the repo). `.env.example` is a template for the secrets file. Adjust the `rsync` source list to match what you keep checked in.
|
||||||
|
|
||||||
## 2. Install on the Server (SSH Session)
|
## 2. Install on the Server (SSH Session)
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ sudo journalctl -u nuntius -f
|
|||||||
|
|
||||||
The service file expects the binary at `/usr/local/bin/nuntius`, the env file at `/etc/nuntius/.env`, and the data dir at `/var/lib/nuntius` (the config's `data_dir: "./data"` resolves to `/var/lib/nuntius/data` because the unit sets `WorkingDirectory=/var/lib/nuntius`). If you'd rather use `/opt/nuntius/...` or any other layout, edit the systemd unit block above before installing.
|
The service file expects the binary at `/usr/local/bin/nuntius`, the env file at `/etc/nuntius/.env`, and the data dir at `/var/lib/nuntius` (the config's `data_dir: "./data"` resolves to `/var/lib/nuntius/data` because the unit sets `WorkingDirectory=/var/lib/nuntius`). If you'd rather use `/opt/nuntius/...` or any other layout, edit the systemd unit block above before installing.
|
||||||
|
|
||||||
### Option B — `install.sh` (idempotent)
|
### Option B — `install.py` (idempotent)
|
||||||
|
|
||||||
Before running the script, copy the systemd unit from the block in Option A above into `/tmp/nuntius/nuntius.service` (the script reads it from the current working directory):
|
Before running the script, copy the systemd unit from the block in Option A above into `/tmp/nuntius/nuntius.service` (the script reads it from the current working directory):
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ ssh user@your-server
|
|||||||
cd /tmp/nuntius
|
cd /tmp/nuntius
|
||||||
# Paste the [Unit]…[Install] block from Option A into this file:
|
# Paste the [Unit]…[Install] block from Option A into this file:
|
||||||
sudo $EDITOR /tmp/nuntius/nuntius.service
|
sudo $EDITOR /tmp/nuntius/nuntius.service
|
||||||
sudo bash install.sh
|
sudo python3 install.py
|
||||||
```
|
```
|
||||||
|
|
||||||
The script is idempotent: re-running on an already-installed host is safe. It:
|
The script is idempotent: re-running on an already-installed host is safe. It:
|
||||||
|
|||||||
+1
-1
@@ -100,7 +100,7 @@ Things nuntius does **not** do:
|
|||||||
|
|
||||||
Things you should do:
|
Things you should do:
|
||||||
|
|
||||||
- Make sure `/etc/nuntius/.env` is `0600` and owned `root:nuntius` (the `install.sh` script does this for you).
|
- Make sure `/etc/nuntius/.env` is `0600` and owned `root:nuntius` (the `install.py` script does this for you).
|
||||||
- Use an app-specific password or OAuth token if your provider supports it. nuntius uses `PLAIN` auth because that is what every SMTP provider offers; the credential is the secret, not the auth mechanism.
|
- Use an app-specific password or OAuth token if your provider supports it. nuntius uses `PLAIN` auth because that is what every SMTP provider offers; the credential is the secret, not the auth mechanism.
|
||||||
- Rotate the password on the same cadence as any other production secret.
|
- Rotate the password on the same cadence as any other production secret.
|
||||||
- Restrict read access to `journalctl -u nuntius` if you do not want the client IPs in your local log files.
|
- Restrict read access to `journalctl -u nuntius` if you do not want the client IPs in your local log files.
|
||||||
|
|||||||
+263
@@ -0,0 +1,263 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# install.py — install nuntius as a systemd service.
|
||||||
|
#
|
||||||
|
# nuntius ships as a single static binary. Build it on your workstation, upload
|
||||||
|
# the binary, the systemd unit, and the .env template to the server, then run
|
||||||
|
# this script there as root from that directory:
|
||||||
|
#
|
||||||
|
# scp bin/nuntius nuntius.service .env.example user@host:/tmp/nuntius/
|
||||||
|
# ssh user@host
|
||||||
|
# cd /tmp/nuntius && sudo python3 install.py
|
||||||
|
#
|
||||||
|
# It expects ./nuntius and ./nuntius.service (and optionally ./.env.example) in
|
||||||
|
# the current working directory. Idempotent: re-running is safe — it skips the
|
||||||
|
# user, config, and .env when they already exist, and never starts the service.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from typing import NoReturn
|
||||||
|
|
||||||
|
NUNTIUS_USER = "nuntius"
|
||||||
|
BIN_DEST = "/usr/local/bin/nuntius"
|
||||||
|
CONFIG_DIR = "/etc/nuntius"
|
||||||
|
CONFIG_FILE = "/etc/nuntius/config.toml"
|
||||||
|
ENV_FILE = "/etc/nuntius/.env"
|
||||||
|
DATA_DIR = "/var/lib/nuntius"
|
||||||
|
SERVICE_DEST = "/etc/systemd/system/nuntius.service"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Coloured logging
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class _ColourFormatter(logging.Formatter):
|
||||||
|
"""Prefix each log line with a coloured level marker, mimicking the bash script."""
|
||||||
|
|
||||||
|
_COLOURS: dict[int, str] = {
|
||||||
|
logging.INFO: "\033[1;34m", # blue
|
||||||
|
logging.WARNING: "\033[1;33m", # yellow
|
||||||
|
logging.ERROR: "\033[1;31m", # red
|
||||||
|
}
|
||||||
|
_LABELS: dict[int, str] = {
|
||||||
|
logging.INFO: "==>",
|
||||||
|
logging.WARNING: "WARN:",
|
||||||
|
logging.ERROR: "ERROR:",
|
||||||
|
}
|
||||||
|
_RESET = "\033[0m"
|
||||||
|
|
||||||
|
def format(self, record: logging.LogRecord) -> str:
|
||||||
|
colour = self._COLOURS.get(record.levelno, "")
|
||||||
|
label = self._LABELS.get(record.levelno, "")
|
||||||
|
if colour and label:
|
||||||
|
return f"{colour}{label}{self._RESET} {record.getMessage()}"
|
||||||
|
return record.getMessage()
|
||||||
|
|
||||||
|
|
||||||
|
def _setup_logging() -> None:
|
||||||
|
"""Configure the root logger with coloured console output."""
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
handler.setFormatter(_ColourFormatter())
|
||||||
|
# Remove any handlers added by basicConfig or other imports.
|
||||||
|
logger.handlers.clear()
|
||||||
|
logger.addHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def _run(cmd: list[str], **kwargs: object) -> subprocess.CompletedProcess[str]:
|
||||||
|
"""Run a command via subprocess and check for failure (unless told otherwise)."""
|
||||||
|
return subprocess.run(cmd, check=True, text=True, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Install steps
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def require_root() -> None:
|
||||||
|
"""Exit if not running as root."""
|
||||||
|
if os.geteuid() != 0:
|
||||||
|
logging.error("Run as root: sudo python3 install.py")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def require_files() -> None:
|
||||||
|
"""Verify that the binary and the systemd unit are present in CWD."""
|
||||||
|
if not os.path.isfile("./nuntius"):
|
||||||
|
logging.error(
|
||||||
|
"./nuntius binary not found — copy it here first (e.g. rsync bin/nuntius)."
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
if not os.path.isfile("./nuntius.service"):
|
||||||
|
logging.error(
|
||||||
|
"./nuntius.service not found — paste the unit from docs/deployment.md (Option A)."
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def create_user() -> None:
|
||||||
|
"""Create the system user if it does not already exist."""
|
||||||
|
try:
|
||||||
|
_run(["id", NUNTIUS_USER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
logging.info("User %s already exists.", NUNTIUS_USER)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
logging.info("Creating system user %s…", NUNTIUS_USER)
|
||||||
|
_run(
|
||||||
|
[
|
||||||
|
"useradd",
|
||||||
|
"--system",
|
||||||
|
"--shell",
|
||||||
|
"/usr/sbin/nologin",
|
||||||
|
"--home-dir",
|
||||||
|
DATA_DIR,
|
||||||
|
"--user-group",
|
||||||
|
NUNTIUS_USER,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def create_data_dir() -> None:
|
||||||
|
"""Ensure the data directory exists with correct ownership and permissions."""
|
||||||
|
logging.info("Setting up %s…", DATA_DIR)
|
||||||
|
os.makedirs(DATA_DIR, exist_ok=True)
|
||||||
|
shutil.chown(DATA_DIR, user=NUNTIUS_USER, group=NUNTIUS_USER)
|
||||||
|
os.chmod(DATA_DIR, 0o750)
|
||||||
|
|
||||||
|
|
||||||
|
def install_binary() -> None:
|
||||||
|
"""Copy the nuntius binary to /usr/local/bin with mode 0755."""
|
||||||
|
logging.info("Installing binary to %s…", BIN_DEST)
|
||||||
|
shutil.copy2("./nuntius", BIN_DEST)
|
||||||
|
os.chmod(BIN_DEST, 0o755)
|
||||||
|
|
||||||
|
|
||||||
|
def install_service() -> None:
|
||||||
|
"""Copy the systemd unit file with mode 0644."""
|
||||||
|
logging.info("Installing systemd unit %s…", SERVICE_DEST)
|
||||||
|
shutil.copy2("./nuntius.service", SERVICE_DEST)
|
||||||
|
os.chmod(SERVICE_DEST, 0o644)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_config() -> None:
|
||||||
|
"""Auto-generate the TOML config by running nuntius briefly if config is absent."""
|
||||||
|
if os.path.isfile(CONFIG_FILE):
|
||||||
|
logging.info("Config %s already exists; leaving it untouched.", CONFIG_FILE)
|
||||||
|
return
|
||||||
|
|
||||||
|
logging.info("Generating %s…", CONFIG_FILE)
|
||||||
|
os.makedirs(CONFIG_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
# nuntius writes the template config on first start; run it briefly, then
|
||||||
|
# let `timeout` send SIGTERM (graceful shutdown). The write happens at
|
||||||
|
# startup, before the timeout elapses.
|
||||||
|
try:
|
||||||
|
_run(["timeout", "-k", "5s", "4s", BIN_DEST], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
# timeout exits 124 on SIGTERM, 137 on SIGKILL — both are expected.
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not os.path.isfile(CONFIG_FILE):
|
||||||
|
logging.warning("nuntius did not create %s; check the uploaded binary.", CONFIG_FILE)
|
||||||
|
|
||||||
|
# Fix ownership in case nuntius wrote files as root.
|
||||||
|
_run(["chown", "-R", f"{NUNTIUS_USER}:{NUNTIUS_USER}", DATA_DIR])
|
||||||
|
|
||||||
|
|
||||||
|
def install_env() -> None:
|
||||||
|
"""Copy .env.example to /etc/nuntius/.env if it does not already exist."""
|
||||||
|
if os.path.isfile(ENV_FILE):
|
||||||
|
logging.info("%s already exists; leaving it untouched.", ENV_FILE)
|
||||||
|
elif os.path.isfile("./.env.example"):
|
||||||
|
logging.info("Creating %s from .env.example (edit it!)…", ENV_FILE)
|
||||||
|
shutil.copy2("./.env.example", ENV_FILE)
|
||||||
|
os.chmod(ENV_FILE, 0o600)
|
||||||
|
_run(["chown", f"root:{NUNTIUS_USER}", ENV_FILE])
|
||||||
|
else:
|
||||||
|
logging.warning(
|
||||||
|
".env.example not found; skipping %s. Create it before starting.", ENV_FILE
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def enable_service() -> None:
|
||||||
|
"""Reload systemd and enable the nuntius service (does not start it)."""
|
||||||
|
logging.info("Reloading systemd and enabling nuntius…")
|
||||||
|
_run(["systemctl", "daemon-reload"])
|
||||||
|
_run(["systemctl", "enable", "nuntius.service"])
|
||||||
|
|
||||||
|
|
||||||
|
def final_notes() -> None:
|
||||||
|
"""Print post-install instructions."""
|
||||||
|
print(
|
||||||
|
f"""
|
||||||
|
============================================================
|
||||||
|
✓ nuntius installed and enabled, but NOT started.
|
||||||
|
|
||||||
|
Next manual steps:
|
||||||
|
1. Set the SMTP password:
|
||||||
|
sudo $EDITOR {ENV_FILE}
|
||||||
|
2. Edit the auto-generated config (smtp.host, smtp.user, allowed_origins):
|
||||||
|
sudo $EDITOR {CONFIG_FILE}
|
||||||
|
3. Start the service:
|
||||||
|
sudo systemctl start nuntius
|
||||||
|
sudo journalctl -u nuntius -f
|
||||||
|
4. Add the nginx location block (see README.md), then:
|
||||||
|
sudo nginx -t && sudo systemctl reload nginx
|
||||||
|
============================================================
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# CLI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def _build_parser() -> argparse.ArgumentParser:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="install.py",
|
||||||
|
description="install nuntius as a systemd service.",
|
||||||
|
epilog=(
|
||||||
|
"Idempotent: re-running is safe. The service is enabled but never started."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Main
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str] | None = None) -> NoReturn:
|
||||||
|
"""Parse args and run all install steps in order."""
|
||||||
|
_setup_logging()
|
||||||
|
parser = _build_parser()
|
||||||
|
parser.parse_args(argv)
|
||||||
|
|
||||||
|
require_root()
|
||||||
|
require_files()
|
||||||
|
create_user()
|
||||||
|
create_data_dir()
|
||||||
|
install_binary()
|
||||||
|
install_service()
|
||||||
|
generate_config()
|
||||||
|
install_env()
|
||||||
|
enable_service()
|
||||||
|
final_notes()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
-153
@@ -1,153 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# install.sh — install nuntius as a systemd service.
|
|
||||||
#
|
|
||||||
# nuntius ships as a single static binary. Build it on your workstation, upload
|
|
||||||
# the binary, the systemd unit, and the .env template to the server, then run
|
|
||||||
# this script there as root from that directory:
|
|
||||||
#
|
|
||||||
# scp bin/nuntius nuntius.service .env.example user@host:/tmp/nuntius/
|
|
||||||
# ssh user@host
|
|
||||||
# cd /tmp/nuntius && sudo bash install.sh
|
|
||||||
#
|
|
||||||
# It expects ./nuntius and ./nuntius.service (and optionally ./.env.example) in
|
|
||||||
# the current working directory. Idempotent: re-running is safe — it skips the
|
|
||||||
# user, config, and .env when they already exist, and never starts the service.
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
NUNTIUS_USER="nuntius"
|
|
||||||
BIN_DEST="/usr/local/bin/nuntius"
|
|
||||||
CONFIG_DIR="/etc/nuntius"
|
|
||||||
CONFIG_FILE="${CONFIG_DIR}/config.toml"
|
|
||||||
ENV_FILE="${CONFIG_DIR}/.env"
|
|
||||||
DATA_DIR="/var/lib/nuntius"
|
|
||||||
SERVICE_DEST="/etc/systemd/system/nuntius.service"
|
|
||||||
|
|
||||||
log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
|
|
||||||
warn() { printf '\033[1;33mWARN:\033[0m %s\n' "$*" >&2; }
|
|
||||||
die() { printf '\033[1;31mERROR:\033[0m %s\n' "$*" >&2; exit 1; }
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
cat <<'EOF'
|
|
||||||
install.sh — install nuntius as a systemd service.
|
|
||||||
|
|
||||||
Upload the binary, the systemd unit, and the .env template to the server, then
|
|
||||||
run this script there as root from that directory:
|
|
||||||
|
|
||||||
scp bin/nuntius nuntius.service .env.example user@host:/tmp/nuntius/
|
|
||||||
ssh user@host
|
|
||||||
cd /tmp/nuntius && sudo bash install.sh
|
|
||||||
|
|
||||||
Idempotent: re-running is safe. The service is enabled but never started.
|
|
||||||
EOF
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
require_root() {
|
|
||||||
[ "$(id -u)" -eq 0 ] || die "Run as root: sudo bash install.sh"
|
|
||||||
}
|
|
||||||
|
|
||||||
require_files() {
|
|
||||||
[ -f ./nuntius ] || die "./nuntius binary not found — copy it here first (e.g. rsync bin/nuntius)."
|
|
||||||
[ -f ./nuntius.service ] || \
|
|
||||||
die "./nuntius.service not found — paste the unit from docs/deployment.md (Option A)."
|
|
||||||
}
|
|
||||||
|
|
||||||
create_user() {
|
|
||||||
if id "$NUNTIUS_USER" >/dev/null 2>&1; then
|
|
||||||
log "User ${NUNTIUS_USER} already exists."
|
|
||||||
else
|
|
||||||
log "Creating system user ${NUNTIUS_USER}…"
|
|
||||||
useradd --system --shell /usr/sbin/nologin --home-dir "$DATA_DIR" --user-group "$NUNTIUS_USER"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
create_data_dir() {
|
|
||||||
log "Setting up ${DATA_DIR}…"
|
|
||||||
mkdir -p "$DATA_DIR"
|
|
||||||
chown "$NUNTIUS_USER:$NUNTIUS_USER" "$DATA_DIR"
|
|
||||||
chmod 750 "$DATA_DIR"
|
|
||||||
}
|
|
||||||
|
|
||||||
install_binary() {
|
|
||||||
log "Installing binary to ${BIN_DEST}…"
|
|
||||||
install -m 0755 ./nuntius "$BIN_DEST"
|
|
||||||
}
|
|
||||||
|
|
||||||
install_service() {
|
|
||||||
log "Installing systemd unit ${SERVICE_DEST}…"
|
|
||||||
install -m 0644 ./nuntius.service "$SERVICE_DEST"
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_config() {
|
|
||||||
if [ -f "$CONFIG_FILE" ]; then
|
|
||||||
log "Config ${CONFIG_FILE} already exists; leaving it untouched."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
log "Generating ${CONFIG_FILE}…"
|
|
||||||
mkdir -p "$CONFIG_DIR"
|
|
||||||
# nuntius writes the template config on first start; run it briefly, then
|
|
||||||
# let `timeout` send SIGTERM (graceful shutdown). No backgrounding or manual
|
|
||||||
# kill — the write happens at startup, before the timeout elapses.
|
|
||||||
timeout -k 5s 4s "$BIN_DEST" >/dev/null 2>&1 || true
|
|
||||||
[ -f "$CONFIG_FILE" ] || warn "nuntius did not create ${CONFIG_FILE}; check the uploaded binary."
|
|
||||||
chown -R "$NUNTIUS_USER:$NUNTIUS_USER" "$DATA_DIR"
|
|
||||||
}
|
|
||||||
|
|
||||||
install_env() {
|
|
||||||
if [ -f "$ENV_FILE" ]; then
|
|
||||||
log "${ENV_FILE} already exists; leaving it untouched."
|
|
||||||
elif [ -f ./.env.example ]; then
|
|
||||||
log "Creating ${ENV_FILE} from .env.example (edit it!)…"
|
|
||||||
install -m 0600 ./.env.example "$ENV_FILE"
|
|
||||||
chown "root:$NUNTIUS_USER" "$ENV_FILE"
|
|
||||||
else
|
|
||||||
warn ".env.example not found; skipping ${ENV_FILE}. Create it before starting."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
enable_service() {
|
|
||||||
log "Reloading systemd and enabling nuntius…"
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl enable nuntius.service
|
|
||||||
}
|
|
||||||
|
|
||||||
final_notes() {
|
|
||||||
cat <<'EOF'
|
|
||||||
|
|
||||||
============================================================
|
|
||||||
✓ nuntius installed and enabled, but NOT started.
|
|
||||||
|
|
||||||
Next manual steps:
|
|
||||||
1. Set the SMTP password:
|
|
||||||
sudo $EDITOR /etc/nuntius/.env
|
|
||||||
2. Edit the auto-generated config (smtp.host, smtp.user, allowed_origins):
|
|
||||||
sudo $EDITOR /etc/nuntius/config.toml
|
|
||||||
3. Start the service:
|
|
||||||
sudo systemctl start nuntius
|
|
||||||
sudo journalctl -u nuntius -f
|
|
||||||
4. Add the nginx location block (see README.md), then:
|
|
||||||
sudo nginx -t && sudo systemctl reload nginx
|
|
||||||
============================================================
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
case "${1:-}" in
|
|
||||||
-h | --help) usage ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
require_root
|
|
||||||
require_files
|
|
||||||
create_user
|
|
||||||
create_data_dir
|
|
||||||
install_binary
|
|
||||||
install_service
|
|
||||||
generate_config
|
|
||||||
install_env
|
|
||||||
enable_service
|
|
||||||
final_notes
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@"
|
|
||||||
Reference in New Issue
Block a user