feat: Tailscale sync jobs
All checks were successful
CI / test (pull_request) Successful in 14s
CI / build-dev (pull_request) Has been skipped

Adds a background job system that polls the Tailscale API on a configurable
interval and syncs tailscale status and IPs to instances by hostname match.

- New config table (key/value) in SQLite for persistent server-side settings
- New server/jobs.js: runTailscaleSync + restartJobs scheduler
- GET/PUT /api/config — read and write Tailscale settings; API key masked as **REDACTED** on GET
- POST /api/jobs/tailscale/run — immediate manual sync
- Settings modal: new Tailscale Sync section with enable toggle, tailnet, API key, poll interval, Save + Run Now buttons, last-run status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-28 17:11:40 -04:00
parent 31a5090f4f
commit 47e9c4faf7
8 changed files with 308 additions and 0 deletions

View File

@@ -53,6 +53,11 @@ function createSchema() {
changed_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_history_vmid ON instance_history(vmid);
CREATE TABLE IF NOT EXISTS config (
key TEXT PRIMARY KEY,
value TEXT NOT NULL DEFAULT ''
);
`);
}
@@ -187,6 +192,18 @@ export function getAllHistory() {
return db.prepare('SELECT * FROM instance_history ORDER BY vmid, changed_at').all();
}
export function getConfig(key, defaultVal = '') {
const row = db.prepare('SELECT value FROM config WHERE key = ?').get(key);
return row ? row.value : defaultVal;
}
export function setConfig(key, value) {
db.prepare(
`INSERT INTO config (key, value) VALUES (?, ?)
ON CONFLICT(key) DO UPDATE SET value = excluded.value`
).run(key, String(value));
}
// ── Test helpers ──────────────────────────────────────────────────────────────
export function _resetForTest() {