diff --git a/lib/db/migrations/022_monitored_services_external.sql b/lib/db/migrations/022_monitored_services_external.sql new file mode 100644 index 0000000..f0f899f --- /dev/null +++ b/lib/db/migrations/022_monitored_services_external.sql @@ -0,0 +1,17 @@ +-- 022_monitored_services_external.sql +-- Optional external/domain URL for a service, used by the dashboard tile when the +-- owner is browsing remotely. LAN `url` stays the source of truth for health checks. +ALTER TABLE monitored_services ADD COLUMN external text; + +-- Backfill curated domains by id (the live instance is already seeded, so adding the +-- column alone wouldn't populate them). No-op for ids that don't exist. +UPDATE monitored_services SET external = 'https://void.hynesy.com' WHERE id = 'void-server'; +UPDATE monitored_services SET external = 'https://gramps.hynesy.com' WHERE id = 'gramps'; +UPDATE monitored_services SET external = 'https://plex.hynesy.com' WHERE id = 'plex'; +UPDATE monitored_services SET external = 'https://tdarr.hynesy.com' WHERE id = 'tdarr'; +UPDATE monitored_services SET external = 'https://sonarr.hynesy.com' WHERE id = 'sonarr'; +UPDATE monitored_services SET external = 'https://radarr.hynesy.com' WHERE id = 'radarr'; +UPDATE monitored_services SET external = 'https://bookstack.hynesy.com' WHERE id = 'bookstack'; +-- Two services previously stored their domain in `url`; normalise to LAN url + external. +UPDATE monitored_services SET url = 'http://192.168.1.230:8096', external = 'https://jellyfin.hynesy.com' WHERE id = 'jellyfin'; +UPDATE monitored_services SET url = 'http://192.168.1.230:8789', external = 'https://chaptarr.hynesy.com' WHERE id = 'chaptarr'; diff --git a/lib/db/repos/monitored_services.js b/lib/db/repos/monitored_services.js index dcff7ba..6a212fe 100644 --- a/lib/db/repos/monitored_services.js +++ b/lib/db/repos/monitored_services.js @@ -1,12 +1,12 @@ import { pool } from '../pool.js'; -const COLS = 'id, name, category, host, url, icon, check_cfg, source, enabled'; +const COLS = 'id, name, category, host, url, icon, external, check_cfg, source, enabled'; // Map a DB row to the service shape the registry/checker expect (check_cfg -> check). function toSvc(r) { return { id: r.id, name: r.name, category: r.category, host: r.host, url: r.url, - icon: r.icon, check: r.check_cfg || {}, source: r.source, enabled: r.enabled + icon: r.icon, external: r.external ?? null, check: r.check_cfg || {}, source: r.source, enabled: r.enabled }; } @@ -42,15 +42,15 @@ export async function count() { export async function create(svc) { const { id, name, category = 'other', host = null, url, icon = null, - check = {}, source = 'manual', enabled = true } = svc; + external = null, check = {}, source = 'manual', enabled = true } = svc; const { rows: [r] } = await pool.query( - `INSERT INTO monitored_services (id, name, category, host, url, icon, check_cfg, source, enabled) - VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8,$9) RETURNING ${COLS}`, - [id, name, category, host, url, icon, JSON.stringify(check), source, enabled]); + `INSERT INTO monitored_services (id, name, category, host, url, icon, external, check_cfg, source, enabled) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9,$10) RETURNING ${COLS}`, + [id, name, category, host, url, icon, external, JSON.stringify(check), source, enabled]); return toSvc(r); } -const PATCHABLE = ['name', 'category', 'host', 'url', 'icon', 'enabled']; +const PATCHABLE = ['name', 'category', 'host', 'url', 'icon', 'external', 'enabled']; export async function update(id, patch) { const sets = [], vals = []; for (const k of PATCHABLE) { @@ -75,8 +75,8 @@ export async function remove(id) { export async function upsertDiscovered(svc) { const { id, name, category = 'other', host = null, url, icon = null, check = {} } = svc; const { rows: [r] } = await pool.query( - `INSERT INTO monitored_services (id, name, category, host, url, icon, check_cfg, source, enabled) - SELECT $1,$2,$3,$4,$5,$6,$7::jsonb,'discovered',false + `INSERT INTO monitored_services (id, name, category, host, url, icon, external, check_cfg, source, enabled) + SELECT $1,$2,$3,$4,$5,$6,NULL,$7::jsonb,'discovered',false WHERE NOT EXISTS (SELECT 1 FROM monitored_services WHERE url=$5) ON CONFLICT (id) DO NOTHING RETURNING ${COLS}`, diff --git a/tests/health/registry.test.js b/tests/health/registry.test.js index 4bdf933..7499da0 100644 --- a/tests/health/registry.test.js +++ b/tests/health/registry.test.js @@ -30,4 +30,12 @@ describe('registry', () => { expect(after.every(s => s.source === 'manual' && s.enabled)).toBe(true); expect(await seedFromConfig()).toBe(0); // table not empty → no-op }); + + it('persists and returns external on create/get/update', async () => { + const id = 'ext-test'; + await services.create({ id, name: 'Ext', url: 'http://10.0.0.1', external: 'https://ext.example.com' }); + expect((await services.get(id)).external).toBe('https://ext.example.com'); + const upd = await services.update(id, { external: 'https://ext2.example.com' }); + expect(upd.external).toBe('https://ext2.example.com'); + }); });