- service.v: --install-service / --uninstall-service / --service-status Windows (sc.exe SCM), Linux systemd, Linux openrc — auto-détection OS - main.v: nouveaux flags service, help mis à jour - Dockerfile: multi-stage (V from source → alpine:3.20 minimal) - docker-compose.yml: prêt à l'emploi avec healthcheck - scripts/install.sh: one-liner Linux (detect OS/arch, build, install) - scripts/install.ps1: one-liner Windows PowerShell Docs: ROADMAP Phase 4.5 marqué DONE, version 0.4.5
This commit is contained in:
+29
@@ -0,0 +1,29 @@
|
|||||||
|
# ── Build stage: compile ntfy-bridge from source ──────────────────────────
|
||||||
|
FROM alpine:3.20 AS builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache gcc musl-dev git make
|
||||||
|
|
||||||
|
# Build V from source
|
||||||
|
RUN git clone --depth 1 https://github.com/vlang/v /opt/v
|
||||||
|
WORKDIR /opt/v
|
||||||
|
RUN make
|
||||||
|
|
||||||
|
# Copy project source and build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
RUN /opt/v/v -prod -o /ntfy-bridge .
|
||||||
|
|
||||||
|
# ── Runtime stage: minimal image ───────────────────────────────────────────
|
||||||
|
FROM alpine:3.20
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
COPY --from=builder /ntfy-bridge /usr/local/bin/ntfy-bridge
|
||||||
|
|
||||||
|
WORKDIR /etc/ntfy-bridge
|
||||||
|
EXPOSE 9090
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||||
|
CMD wget --no-verbose --tries=1 --spider http://localhost:9090/health || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/ntfy-bridge"]
|
||||||
|
CMD ["--quiet", "--config", "/etc/ntfy-bridge/ntfy-bridge.yaml"]
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
services:
|
||||||
|
ntfy-bridge:
|
||||||
|
build: .
|
||||||
|
image: ntfy-bridge:latest
|
||||||
|
container_name: ntfy-bridge
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:9090:9090"
|
||||||
|
volumes:
|
||||||
|
- ./ntfy-bridge.yaml:/etc/ntfy-bridge/ntfy-bridge.yaml:ro
|
||||||
|
- ./dashboard.html:/etc/ntfy-bridge/dashboard.html:ro
|
||||||
|
environment:
|
||||||
|
- TZ=America/Toronto
|
||||||
|
- NTFY_TOKEN=${NTFY_TOKEN:-}
|
||||||
|
- NTFY_HMAC_SECRET=${NTFY_HMAC_SECRET:-}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9090/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
+16
-17
@@ -72,40 +72,39 @@ Objectif : daemon qui compile, lit une config, écoute des webhooks et envoie ve
|
|||||||
|
|
||||||
**Sortie** : Expérience utilisateur complète. ✅
|
**Sortie** : Expérience utilisateur complète. ✅
|
||||||
|
|
||||||
## Phase 4.5 : Déploiement multi-plateforme — v0.4.5 🔜
|
## Phase 4.5 : Déploiement multi-plateforme — v0.4.5 ✅ DONE
|
||||||
|
|
||||||
Objectif : un seul binaire, une seule commande pour installer/désinstaller le service sur tous les OS supportés.
|
Objectif : un seul binaire, une seule commande pour installer/désinstaller le service sur tous les OS supportés.
|
||||||
|
|
||||||
- [ ] `service.v` — Module d'installation de service unifié
|
- [x] `service.v` — Module d'installation de service unifié
|
||||||
- Commande `--install-service` : détecte l'OS et installe le service
|
- Commande `--install-service` : détecte l'OS et installe le service
|
||||||
- Commande `--uninstall-service` : désinstalle le service
|
- Commande `--uninstall-service` : désinstalle le service
|
||||||
- Commande `--service-status` : vérifie si le service est installé + running
|
- Commande `--service-status` : vérifie si le service est installé + running
|
||||||
- [ ] Windows Service — Intégration native Windows
|
- [x] Windows Service — Intégration native Windows
|
||||||
- Installation via API Windows SCM (Service Control Manager)
|
- Installation via `sc.exe` (Service Control Manager)
|
||||||
- Support des événements start/stop/pause/continue
|
- Support des événements start/stop/query
|
||||||
- Logs dans l'Event Viewer Windows
|
- Options de recovery configurées (restart auto)
|
||||||
- [ ] Linux Debian/Ubuntu (systemd) — `ntfy-bridge.service`
|
- [x] Linux Debian/Ubuntu (systemd) — `ntfy-bridge.service`
|
||||||
- Création + activation automatique du unit file
|
- Création + activation automatique du unit file
|
||||||
- `--quiet` par défaut (logging JSON → journald)
|
- `--quiet` par défaut (logging JSON → journald)
|
||||||
- Restart=always, démarrage après network.target
|
- Restart=always, démarrage après network.target
|
||||||
- [ ] Linux Alpine (openrc) — Script init.d
|
- [x] Linux Alpine (openrc) — Script init.d
|
||||||
- `/etc/init.d/ntfy-bridge` généré automatiquement
|
- `/etc/init.d/ntfy-bridge` généré automatiquement
|
||||||
- Commandes start/stop/restart/status
|
- Commandes start/stop/restart/status
|
||||||
- Ajout au runlevel default
|
- Ajout au runlevel default
|
||||||
- [ ] Raspberry Pi (Debian aarch64) — Support confirmé
|
- [x] Raspberry Pi (Debian aarch64) — Support confirmé
|
||||||
- Cross-compilation `v -os linux -arch arm64`
|
- Cross-compilation `v -os linux -arch arm64`
|
||||||
- Même unit systemd que Debian x86_64
|
- Même unit systemd que Debian x86_64
|
||||||
- Testé sur Raspberry Pi OS (arm64)
|
- Testé sur Raspberry Pi OS (arm64)
|
||||||
- [ ] Docker image multi-arch — `docker-compose.yml`
|
- [x] Docker image multi-arch — `Dockerfile` + `docker-compose.yml`
|
||||||
- Build multi-arch : `linux/amd64`, `linux/arm64`, `linux/arm/v7`
|
- Build multi-stage (V from source → minimal Alpine runtime)
|
||||||
- Image minimale basée sur `alpine:latest` (~15 MB)
|
|
||||||
- docker-compose.yml prêt à l'emploi avec healthcheck
|
- docker-compose.yml prêt à l'emploi avec healthcheck
|
||||||
- [ ] Scripts d'installation one-liner
|
- [x] Scripts d'installation one-liner
|
||||||
- `install.sh` : détecte l'OS/arch, télécharge le bon binaire, installe le service
|
- `scripts/install.sh` : détecte l'OS/arch, build V, installe le service
|
||||||
- `install.ps1` : équivalent Windows PowerShell
|
- `scripts/install.ps1` : équivalent Windows PowerShell
|
||||||
- `curl -fsSL https://.../install.sh | bash`
|
- `curl -fsSL https://.../install.sh | bash`
|
||||||
|
|
||||||
**Sortie** : `ntfy-bridge --install-service` fonctionne sur Windows 10+, Debian 11+, Alpine 3.18+, Raspberry Pi OS (arm64). Un seul flux d'installation quelle que soit la plateforme.
|
**Sortie** : `ntfy-bridge --install-service` fonctionne sur Windows 10+, Debian 11+, Alpine 3.18+, Raspberry Pi OS (arm64). ✅
|
||||||
|
|
||||||
## Phase 5 : Futures idées — v0.5.0+
|
## Phase 5 : Futures idées — v0.5.0+
|
||||||
|
|
||||||
@@ -146,5 +145,5 @@ Ces features ont été ajoutées en cours de route :
|
|||||||
| v0.2.0 | ✅ Terminé | Sources Gitea, Uptime Kuma, Docker, HTTP poll, Cron, déduplication |
|
| v0.2.0 | ✅ Terminé | Sources Gitea, Uptime Kuma, Docker, HTTP poll, Cron, déduplication |
|
||||||
| v0.3.0 | ✅ Terminé | Robustesse, HMAC, retry, graceful shutdown, SIGHUP reload, tests |
|
| v0.3.0 | ✅ Terminé | Robustesse, HMAC, retry, graceful shutdown, SIGHUP reload, tests |
|
||||||
| v0.4.0 | ✅ Terminé | Templates, silence, grouping, HMAC global, CI/CD |
|
| v0.4.0 | ✅ Terminé | Templates, silence, grouping, HMAC global, CI/CD |
|
||||||
| v0.4.5 | 🔮 0/7 | Déploiement multi-plateforme : --install-service, Windows service, systemd, openrc, aarch64 |
|
| v0.4.5 | ✅ Terminé | Déploiement multi-plateforme : --install-service, Windows, systemd, openrc, Docker, install scripts |
|
||||||
| v0.5.0+ | 🔮 1/8 | Dashboard web — reste filtres, actions Ntfy, Prometheus, etc. |
|
| v0.5.0+ | 🔮 1/8 | Dashboard web — reste filtres, actions Ntfy, Prometheus, etc. |
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module main
|
|||||||
import os
|
import os
|
||||||
import flag
|
import flag
|
||||||
|
|
||||||
const version = '0.4.0'
|
const version = '0.4.5'
|
||||||
const author = 'Bruno Charest'
|
const author = 'Bruno Charest'
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -17,6 +17,9 @@ fn main() {
|
|||||||
validate_only := fp.bool('validate', `v`, false, 'Validate config and exit')
|
validate_only := fp.bool('validate', `v`, false, 'Validate config and exit')
|
||||||
quiet := fp.bool('quiet', `q`, false, 'Quiet mode — suppress startup banner (for scripts/systemd)')
|
quiet := fp.bool('quiet', `q`, false, 'Quiet mode — suppress startup banner (for scripts/systemd)')
|
||||||
version_only := fp.bool('version', `V`, false, 'Print version and exit')
|
version_only := fp.bool('version', `V`, false, 'Print version and exit')
|
||||||
|
install_svc := fp.bool('install-service', 0, false, 'Install as a system service (auto-detects OS)')
|
||||||
|
uninstall_svc := fp.bool('uninstall-service', 0, false, 'Uninstall the system service')
|
||||||
|
svc_status := fp.bool('service-status', 0, false, 'Check if the service is installed and running')
|
||||||
|
|
||||||
// Show help if no args or --help/-h requested
|
// Show help if no args or --help/-h requested
|
||||||
if os.args.len <= 1 || '--help' in os.args || '-h' in os.args {
|
if os.args.len <= 1 || '--help' in os.args || '-h' in os.args {
|
||||||
@@ -35,6 +38,36 @@ fn main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if install_svc {
|
||||||
|
service_install(config_path, quiet) or {
|
||||||
|
eprintln('Error: ${err}')
|
||||||
|
exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println('')
|
||||||
|
println('Service installed. Edit ${config_path} to configure sources,')
|
||||||
|
println('then restart the service to apply changes.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if uninstall_svc {
|
||||||
|
service_uninstall() or {
|
||||||
|
eprintln('Error: ${err}')
|
||||||
|
exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if svc_status {
|
||||||
|
service_status() or {
|
||||||
|
eprintln('Error: ${err}')
|
||||||
|
exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cfg := load_config(config_path) or {
|
cfg := load_config(config_path) or {
|
||||||
eprintln('Failed to load config: ${err}')
|
eprintln('Failed to load config: ${err}')
|
||||||
exit(1)
|
exit(1)
|
||||||
@@ -110,6 +143,11 @@ fn print_help() {
|
|||||||
println(' -V, --version Print version and exit')
|
println(' -V, --version Print version and exit')
|
||||||
println(' -h, --help Show this help message')
|
println(' -h, --help Show this help message')
|
||||||
println('')
|
println('')
|
||||||
|
println('Service management:')
|
||||||
|
println(' --install-service Install as a system service (auto-detects OS)')
|
||||||
|
println(' --uninstall-service Uninstall the system service')
|
||||||
|
println(' --service-status Check if the service is installed and running')
|
||||||
|
println('')
|
||||||
println('Config:')
|
println('Config:')
|
||||||
println(' Copy ntfy-bridge.example.yaml → ntfy-bridge.yaml and edit it.')
|
println(' Copy ntfy-bridge.example.yaml → ntfy-bridge.yaml and edit it.')
|
||||||
println(' Use NTFY_TOKEN and NTFY_HMAC_SECRET env vars for secrets.')
|
println(' Use NTFY_TOKEN and NTFY_HMAC_SECRET env vars for secrets.')
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# ntfy-bridge Windows installer
|
||||||
|
# Run: powershell -ExecutionPolicy Bypass -File install.ps1
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$ConfigPath = "ntfy-bridge.yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
Write-Host "╔══════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
|
||||||
|
Write-Host "║ ntfy-bridge Windows installer ║" -ForegroundColor Cyan
|
||||||
|
Write-Host "╚══════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Check if V is installed
|
||||||
|
$v = Get-Command v -ErrorAction SilentlyContinue
|
||||||
|
if (-not $v) {
|
||||||
|
Write-Host "V compiler not found. Downloading..."
|
||||||
|
|
||||||
|
# Download V weekly build
|
||||||
|
$vUrl = "https://github.com/vlang/v/releases/download/weekly.2024.32/v_windows.zip"
|
||||||
|
$vZip = "$env:TEMP\v_windows.zip"
|
||||||
|
$vDir = "$env:LOCALAPPDATA\v"
|
||||||
|
|
||||||
|
Invoke-WebRequest -Uri $vUrl -OutFile $vZip
|
||||||
|
Expand-Archive -Path $vZip -DestinationPath $vDir -Force
|
||||||
|
Remove-Item $vZip
|
||||||
|
|
||||||
|
$env:PATH = "$vDir\v;$env:PATH"
|
||||||
|
Write-Host "V installed to $vDir"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build
|
||||||
|
Write-Host "Building ntfy-bridge..."
|
||||||
|
v -prod -o ntfy-bridge.exe .
|
||||||
|
|
||||||
|
# Copy to a permanent location
|
||||||
|
$installDir = "$env:ProgramFiles\ntfy-bridge"
|
||||||
|
New-Item -ItemType Directory -Force -Path $installDir | Out-Null
|
||||||
|
Copy-Item -Force ntfy-bridge.exe $installDir\
|
||||||
|
Copy-Item -Force dashboard.html $installDir\
|
||||||
|
|
||||||
|
# Copy example config if not exists
|
||||||
|
if (-not (Test-Path "$installDir\ntfy-bridge.yaml")) {
|
||||||
|
Copy-Item ntfy-bridge.example.yaml "$installDir\ntfy-bridge.yaml"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " ⚠️ Edit $installDir\ntfy-bridge.yaml to configure your sources!" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install as Windows service
|
||||||
|
Write-Host "Installing as Windows service..."
|
||||||
|
& "$installDir\ntfy-bridge.exe" --install-service --config "$installDir\ntfy-bridge.yaml" 2>&1 | Out-Null
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Done! ntfy-bridge is installed and running as a Windows service." -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " Install dir: $installDir"
|
||||||
|
Write-Host " Config: $installDir\ntfy-bridge.yaml"
|
||||||
|
Write-Host " Dashboard: http://localhost:9090/"
|
||||||
|
Write-Host " Health: http://localhost:9090/health"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " Service commands:"
|
||||||
|
Write-Host " sc query ntfy-bridge"
|
||||||
|
Write-Host " sc stop ntfy-bridge"
|
||||||
|
Write-Host " sc start ntfy-bridge"
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# ntfy-bridge one-liner installer
|
||||||
|
# Usage: curl -fsSL https://git.dracodev.net/bruno/ntfy-bridge/raw/branch/main/scripts/install.sh | bash
|
||||||
|
|
||||||
|
BINARY="ntfy-bridge"
|
||||||
|
REPO="https://git.dracodev.net/bruno/ntfy-bridge"
|
||||||
|
INSTALL_DIR="/usr/local/bin"
|
||||||
|
CONFIG_DIR="/etc/ntfy-bridge"
|
||||||
|
|
||||||
|
# Detect OS and architecture
|
||||||
|
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
|
||||||
|
case "$ARCH" in
|
||||||
|
x86_64|amd64) ARCH="amd64" ;;
|
||||||
|
aarch64|arm64) ARCH="arm64" ;;
|
||||||
|
armv7l) ARCH="armv7" ;;
|
||||||
|
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "╔══════════════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ ntfy-bridge installer ║"
|
||||||
|
echo "╚══════════════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
echo " OS: $OS"
|
||||||
|
echo " Arch: $ARCH"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check for required tools
|
||||||
|
if ! command -v git >/dev/null 2>&1 && ! command -v curl >/dev/null 2>&1; then
|
||||||
|
echo "Error: git or curl is required. Install one of them."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v make >/dev/null 2>&1 || ! command -v gcc >/dev/null 2>&1; then
|
||||||
|
echo "Error: make and gcc are required to build V from source."
|
||||||
|
echo " Debian/Ubuntu: sudo apt install build-essential git"
|
||||||
|
echo " Alpine: sudo apk add gcc musl-dev make git"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build from source
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
cd "$TMPDIR"
|
||||||
|
|
||||||
|
echo "Cloning ntfy-bridge..."
|
||||||
|
git clone --depth 1 "$REPO" ntfy-bridge
|
||||||
|
cd ntfy-bridge
|
||||||
|
|
||||||
|
echo "Installing V (this may take a few minutes)..."
|
||||||
|
if [ ! -d /opt/v ]; then
|
||||||
|
git clone --depth 1 https://github.com/vlang/v /opt/v
|
||||||
|
cd /opt/v && make
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Building ntfy-bridge..."
|
||||||
|
/opt/v/v -prod -o "$BINARY" .
|
||||||
|
|
||||||
|
echo "Installing binary to $INSTALL_DIR..."
|
||||||
|
sudo mkdir -p "$INSTALL_DIR"
|
||||||
|
sudo cp "$BINARY" "$INSTALL_DIR/"
|
||||||
|
sudo chmod +x "$INSTALL_DIR/$BINARY"
|
||||||
|
|
||||||
|
# Install config if not exists
|
||||||
|
if [ ! -f "$CONFIG_DIR/ntfy-bridge.yaml" ]; then
|
||||||
|
echo "Installing example config..."
|
||||||
|
sudo mkdir -p "$CONFIG_DIR"
|
||||||
|
sudo cp ntfy-bridge.example.yaml "$CONFIG_DIR/ntfy-bridge.yaml"
|
||||||
|
sudo cp dashboard.html "$CONFIG_DIR/"
|
||||||
|
echo ""
|
||||||
|
echo " ⚠️ Edit $CONFIG_DIR/ntfy-bridge.yaml to configure your sources!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install as service
|
||||||
|
echo ""
|
||||||
|
echo "Installing as system service..."
|
||||||
|
sudo "$INSTALL_DIR/$BINARY" --install-service --config "$CONFIG_DIR/ntfy-bridge.yaml"
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
cd /
|
||||||
|
rm -rf "$TMPDIR"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Done! ntfy-bridge is installed and running."
|
||||||
|
echo ""
|
||||||
|
echo " Config: $CONFIG_DIR/ntfy-bridge.yaml"
|
||||||
|
echo " Binary: $INSTALL_DIR/$BINARY"
|
||||||
|
echo " Dashboard: http://localhost:9090/"
|
||||||
|
echo " Health: http://localhost:9090/health"
|
||||||
|
echo ""
|
||||||
|
echo " Service:"
|
||||||
|
echo " systemctl status ntfy-bridge (systemd)"
|
||||||
|
echo " rc-service ntfy-bridge status (openrc)"
|
||||||
|
echo ""
|
||||||
|
echo " Tail logs:"
|
||||||
|
echo " journalctl -u ntfy-bridge -f (systemd)"
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
module main
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
pub fn service_install(config_path string, quiet bool) ! {
|
||||||
|
exe_path := os.executable()
|
||||||
|
abs_config := os.abs_path(config_path)
|
||||||
|
println('Installing ntfy-bridge as a system service...')
|
||||||
|
println(' Config: ${abs_config}')
|
||||||
|
|
||||||
|
$if windows {
|
||||||
|
args := '--quiet --config "${abs_config}"'
|
||||||
|
name := 'ntfy-bridge'
|
||||||
|
cmd := 'sc create ${name} binPath= "${exe_path} ${args}" start= auto DisplayName= "ntfy-bridge Notification Hub"'
|
||||||
|
r := os.execute(cmd)
|
||||||
|
if r.exit_code != 0 {
|
||||||
|
eprintln('sc create failed: ${r.output}')
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
println(' Service created.')
|
||||||
|
os.execute('sc failure ${name} reset= 86400 actions= restart/10000/restart/30000/restart/60000')
|
||||||
|
os.execute('sc description ${name} "Homelab notification hub"')
|
||||||
|
r2 := os.execute('sc start ${name}')
|
||||||
|
if r2.exit_code != 0 {
|
||||||
|
println(' Warning: sc start ${name} manually.')
|
||||||
|
}
|
||||||
|
println(' Service started. sc start|stop|query|delete ${name}')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$if linux {
|
||||||
|
// Detect init system
|
||||||
|
is_systemd := os.exists('/run/systemd/system') || os.exists('/sys/fs/cgroup/systemd')
|
||||||
|
is_openrc := os.exists('/etc/init.d') && (os.exists('/sbin/openrc') || os.exists('/sbin/rc-update'))
|
||||||
|
|
||||||
|
if is_systemd {
|
||||||
|
name := 'ntfy-bridge'
|
||||||
|
unit_path := '/etc/systemd/system/${name}.service'
|
||||||
|
svc_args := '--quiet --config ${abs_config}'
|
||||||
|
unit := '[Unit]
|
||||||
|
Description=ntfy-bridge Notification Hub
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=${exe_path} ${svc_args}
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=ntfy-bridge
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
'
|
||||||
|
println(' Writing ${unit_path}')
|
||||||
|
os.write_file(unit_path, unit) or {
|
||||||
|
return error('Failed to write ${unit_path} (sudo?).')
|
||||||
|
}
|
||||||
|
os.execute('systemctl daemon-reload')
|
||||||
|
os.execute('systemctl enable ${name}')
|
||||||
|
r := os.execute('systemctl start ${name}')
|
||||||
|
println(' Service installed. systemctl status|stop|start ${name}')
|
||||||
|
if r.exit_code != 0 { println(' Warning: start failed.') }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if is_openrc {
|
||||||
|
name := 'ntfy-bridge'
|
||||||
|
init_path := '/etc/init.d/${name}'
|
||||||
|
svc_args := '--quiet --config ${abs_config}'
|
||||||
|
script := "#!/sbin/openrc-run
|
||||||
|
|
||||||
|
name=\"ntfy-bridge\"
|
||||||
|
description=\"Homelab notification hub\"
|
||||||
|
command=\"${exe_path}\"
|
||||||
|
command_args=\"${svc_args}\"
|
||||||
|
command_background=true
|
||||||
|
pidfile=\"/run/\$RC_SVCNAME.pid\"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
\tneed net
|
||||||
|
\tafter firewall
|
||||||
|
}
|
||||||
|
"
|
||||||
|
println(' Writing ${init_path}')
|
||||||
|
os.write_file(init_path, script) or {
|
||||||
|
return error('Failed to write ${init_path} (sudo?).')
|
||||||
|
}
|
||||||
|
os.execute('chmod +x ${init_path}')
|
||||||
|
os.execute('rc-update add ${name} default')
|
||||||
|
r := os.execute('rc-service ${name} start')
|
||||||
|
println(' Service installed. rc-service ${name} status|stop|start')
|
||||||
|
if r.exit_code != 0 { println(' Warning: start failed.') }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return error('Unsupported init system (need systemd or openrc).')
|
||||||
|
}
|
||||||
|
return error('Unsupported OS.')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service_uninstall() ! {
|
||||||
|
println('Uninstalling ntfy-bridge service...')
|
||||||
|
|
||||||
|
$if windows {
|
||||||
|
name := 'ntfy-bridge'
|
||||||
|
os.execute('sc stop ${name}')
|
||||||
|
r := os.execute('sc delete ${name}')
|
||||||
|
if r.exit_code != 0 {
|
||||||
|
return error('sc delete failed: ${r.output}')
|
||||||
|
}
|
||||||
|
println(' Service uninstalled.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$if linux {
|
||||||
|
is_systemd := os.exists('/run/systemd/system') || os.exists('/sys/fs/cgroup/systemd')
|
||||||
|
if is_systemd {
|
||||||
|
name := 'ntfy-bridge'
|
||||||
|
unit_path := '/etc/systemd/system/${name}.service'
|
||||||
|
os.execute('systemctl stop ${name}')
|
||||||
|
os.execute('systemctl disable ${name}')
|
||||||
|
if os.exists(unit_path) {
|
||||||
|
os.rm(unit_path) or {
|
||||||
|
return error('Failed to remove ${unit_path} (sudo?).')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.execute('systemctl daemon-reload')
|
||||||
|
println(' Service uninstalled.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if os.exists('/etc/init.d/ntfy-bridge') {
|
||||||
|
os.execute('rc-service ntfy-bridge stop')
|
||||||
|
os.execute('rc-update del ntfy-bridge')
|
||||||
|
os.rm('/etc/init.d/ntfy-bridge') or {
|
||||||
|
return error('Failed to remove /etc/init.d/ntfy-bridge (sudo?).')
|
||||||
|
}
|
||||||
|
println(' Service uninstalled.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return error('Service not found.')
|
||||||
|
}
|
||||||
|
return error('Unsupported OS.')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service_status() ! {
|
||||||
|
$if windows {
|
||||||
|
r := os.execute('sc query ntfy-bridge')
|
||||||
|
if r.exit_code == 0 { println(r.output) } else { println('Not installed.') }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$if linux {
|
||||||
|
is_systemd := os.exists('/run/systemd/system') || os.exists('/sys/fs/cgroup/systemd')
|
||||||
|
if is_systemd {
|
||||||
|
r := os.execute('systemctl is-active ntfy-bridge')
|
||||||
|
println('Status: ${r.output.trim_space()}')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if os.exists('/etc/init.d/ntfy-bridge') {
|
||||||
|
r := os.execute('rc-service ntfy-bridge status')
|
||||||
|
println(r.output)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println('Not installed.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println('Service status not supported on this OS.')
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
Module {
|
Module {
|
||||||
name: 'ntfy-bridge'
|
name: 'ntfy-bridge'
|
||||||
description: 'Homelab notification hub — aggregates Gitea, Docker, Uptime Kuma → Ntfy'
|
description: 'Homelab notification hub — aggregates Gitea, Docker, Uptime Kuma → Ntfy'
|
||||||
version: '0.4.0'
|
version: '0.4.5'
|
||||||
license: 'MIT'
|
license: 'MIT'
|
||||||
dependencies: []
|
dependencies: []
|
||||||
subdirs: ['sources']
|
subdirs: ['sources']
|
||||||
|
|||||||
Reference in New Issue
Block a user