CI / test (push) Successful in 2m15s
- Clean up both sc.exe and nssm services before install - Wait 1s after remove for SCM to release - Retry up to 3x with 3s delay if 'marked for deletion' - Added import time for sleep calls
277 lines
7.9 KiB
V
277 lines
7.9 KiB
V
module main
|
|
|
|
import os
|
|
import time
|
|
|
|
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 {
|
|
name := 'ntfy-bridge'
|
|
args := '--quiet --config "${abs_config}"'
|
|
|
|
// Find nssm
|
|
mut nssm_exe := ''
|
|
dir := os.dir(exe_path)
|
|
candidate := os.join_path(dir, 'nssm.exe')
|
|
if os.exists(candidate) {
|
|
nssm_exe = candidate
|
|
} else {
|
|
r := os.execute('where nssm 2>nul')
|
|
if r.exit_code == 0 && r.output.contains('nssm') {
|
|
nssm_exe = 'nssm'
|
|
}
|
|
}
|
|
|
|
if nssm_exe == '' {
|
|
eprintln('nssm.exe not found. Windows requires NSSM to run ntfy-bridge as a service.')
|
|
eprintln('')
|
|
eprintln('Install NSSM (Non-Sucking Service Manager):')
|
|
eprintln(' scoop install nssm')
|
|
eprintln(' OR download from https://nssm.cc/download')
|
|
eprintln(' OR place nssm.exe next to ntfy-bridge.exe')
|
|
exit(1)
|
|
}
|
|
println(' Using nssm: ${nssm_exe}')
|
|
|
|
// Remove previous installs (both sc.exe and nssm)
|
|
os.execute('sc stop ${name} 2>nul')
|
|
os.execute('sc delete ${name} 2>nul')
|
|
os.execute('"${nssm_exe}" stop ${name} 2>nul')
|
|
os.execute('"${nssm_exe}" remove ${name} confirm 2>nul')
|
|
|
|
// Wait for SCM to release any "marked for deletion" state
|
|
time.sleep(1 * time.second)
|
|
|
|
// Install the service (retry up to 3 times for "marked for deletion")
|
|
mut installed := false
|
|
for attempt := 1; attempt <= 3; attempt++ {
|
|
r := os.execute('"${nssm_exe}" install ${name} "${exe_path}" ${args}')
|
|
if r.exit_code == 0 {
|
|
installed = true
|
|
break
|
|
}
|
|
if r.output.contains('marked for deletion') {
|
|
println(' Service still marked for deletion, waiting...')
|
|
time.sleep(3 * time.second)
|
|
continue
|
|
}
|
|
eprintln('nssm install failed: ${r.output}')
|
|
exit(1)
|
|
}
|
|
if !installed {
|
|
eprintln('nssm install failed after 3 attempts. Close services.msc and try again.')
|
|
exit(1)
|
|
}
|
|
println(' Service registered.')
|
|
|
|
// Configure service
|
|
os.execute('"${nssm_exe}" set ${name} AppStdout C:\\ProgramData\\ntfy-bridge\\service.log')
|
|
os.execute('"${nssm_exe}" set ${name} AppStderr C:\\ProgramData\\ntfy-bridge\\service.log')
|
|
os.execute('"${nssm_exe}" set ${name} AppRotateFiles 1')
|
|
os.execute('"${nssm_exe}" set ${name} AppRotateSeconds 86400')
|
|
os.execute('"${nssm_exe}" set ${name} AppRotateBytes 1048576')
|
|
os.execute('"${nssm_exe}" set ${name} DisplayName "ntfy-bridge Notification Hub"')
|
|
os.execute('"${nssm_exe}" set ${name} Description "Homelab notification hub"')
|
|
os.execute('"${nssm_exe}" set ${name} Start SERVICE_AUTO_START')
|
|
|
|
// Create log directory
|
|
os.mkdir_all('C:\\ProgramData\\ntfy-bridge') or {}
|
|
|
|
// Start the service
|
|
r2 := os.execute('"${nssm_exe}" start ${name}')
|
|
if r2.exit_code != 0 {
|
|
println(' Warning: nssm start ${name} failed — start manually.')
|
|
} else {
|
|
println(' Service started successfully.')
|
|
}
|
|
println('')
|
|
println('Service commands:')
|
|
println(' nssm status ${name}')
|
|
println(' nssm start ${name}')
|
|
println(' nssm stop ${name}')
|
|
println(' nssm restart ${name}')
|
|
println(' nssm remove ${name} confirm (uninstall)')
|
|
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'
|
|
// Find nssm
|
|
exe_path := os.executable()
|
|
mut nssm_exe := ''
|
|
dir := os.dir(exe_path)
|
|
candidate := os.join_path(dir, 'nssm.exe')
|
|
if os.exists(candidate) {
|
|
nssm_exe = candidate
|
|
} else {
|
|
r := os.execute('where nssm 2>nul')
|
|
if r.exit_code == 0 && r.output.contains('nssm') {
|
|
nssm_exe = 'nssm'
|
|
}
|
|
}
|
|
|
|
if nssm_exe != '' {
|
|
os.execute('"${nssm_exe}" stop ${name}')
|
|
r := os.execute('"${nssm_exe}" remove ${name} confirm')
|
|
if r.exit_code != 0 {
|
|
return error('nssm remove failed: ${r.output}')
|
|
}
|
|
} else {
|
|
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 {
|
|
// Find nssm
|
|
exe_path := os.executable()
|
|
mut nssm_exe := ''
|
|
dir := os.dir(exe_path)
|
|
candidate := os.join_path(dir, 'nssm.exe')
|
|
if os.exists(candidate) {
|
|
nssm_exe = candidate
|
|
} else {
|
|
r := os.execute('where nssm 2>nul')
|
|
if r.exit_code == 0 && r.output.contains('nssm') {
|
|
nssm_exe = 'nssm'
|
|
}
|
|
}
|
|
|
|
if nssm_exe != '' {
|
|
r := os.execute('"${nssm_exe}" status ntfy-bridge')
|
|
if r.exit_code == 0 { println(r.output) } else { println('Not installed.') }
|
|
} else {
|
|
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.')
|
|
}
|