import { DEFAULT_CONFIG_PATH } from './config.ts'; const SERVICE_FILE_PATH = '/etc/systemd/system/uptimerunner.service'; export class UptimeRunnerSystemd { private readonly serviceTemplate = `[Unit] Description=uptime.link Runner Agent After=network-online.target Wants=network-online.target [Service] ExecStart=/usr/local/bin/uptimerunner run --config ${DEFAULT_CONFIG_PATH} Restart=always RestartSec=10 User=root Group=root Environment=PATH=/usr/bin:/usr/local/bin WorkingDirectory=/opt/uptimerunner [Install] WantedBy=multi-user.target `; public async install(): Promise { this.assertRoot(); await Deno.writeTextFile(SERVICE_FILE_PATH, this.serviceTemplate); await run('systemctl', ['daemon-reload']); await run('systemctl', ['enable', 'uptimerunner.service']); console.log(`Service installed: ${SERVICE_FILE_PATH}`); } public async uninstall(): Promise { this.assertRoot(); await run('systemctl', ['disable', '--now', 'uptimerunner.service']).catch(() => null); await Deno.remove(SERVICE_FILE_PATH).catch(() => null); await run('systemctl', ['daemon-reload']); console.log('Service removed.'); } public async start(): Promise { await run('systemctl', ['start', 'uptimerunner.service']); } public async stop(): Promise { await run('systemctl', ['stop', 'uptimerunner.service']); } public async restart(): Promise { await run('systemctl', ['restart', 'uptimerunner.service']); } public async status(): Promise { await run('systemctl', ['status', 'uptimerunner.service', '--no-pager']); } public async logs(): Promise { await run('journalctl', ['-u', 'uptimerunner.service', '-n', '120', '--no-pager']); } private assertRoot(): void { if (Deno.uid && Deno.uid() !== 0) { throw new Error('This service command must run as root.'); } } } async function run(commandArg: string, argsArg: string[]): Promise { const command = new Deno.Command(commandArg, { args: argsArg, stdout: 'inherit', stderr: 'inherit', }); const output = await command.output(); if (!output.success) { throw new Error(`${commandArg} ${argsArg.join(' ')} exited with ${output.code}`); } }