feat(health): service registry loader + seed config (fresh titles)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
11
config/services.json
Normal file
11
config/services.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{ "id": "void-server", "name": "Void 2.0", "category": "agents", "host": "ct311", "url": "http://192.168.1.216:3000", "icon": "void", "check": { "type": "http", "path": "/health" } },
|
||||||
|
{ "id": "ollama", "name": "Ollama", "category": "agents", "host": "ct102", "url": "http://192.168.1.185:11434", "icon": "ollama" },
|
||||||
|
{ "id": "gitea", "name": "Gitea", "category": "infrastructure", "host": "ct105", "url": "http://192.168.1.223:3000", "icon": "gitea" },
|
||||||
|
{ "id": "pihole", "name": "Pi-hole", "category": "infrastructure", "host": "ct106", "url": "http://192.168.1.140/admin", "icon": "pi-hole" },
|
||||||
|
{ "id": "bookstack", "name": "BookStack", "category": "infrastructure", "host": "ct104", "url": "http://192.168.1.213", "icon": "bookstack" },
|
||||||
|
{ "id": "plex", "name": "Plex", "category": "media", "host": "ct100", "url": "http://192.168.1.230:32400/web", "icon": "plex" },
|
||||||
|
{ "id": "sonarr", "name": "Sonarr", "category": "media", "host": "ct100", "url": "http://192.168.1.230:8989", "icon": "sonarr" },
|
||||||
|
{ "id": "radarr", "name": "Radarr", "category": "media", "host": "ct100", "url": "http://192.168.1.230:7878", "icon": "radarr" },
|
||||||
|
{ "id": "qbittorrent", "name": "qBittorrent", "category": "media", "host": "ct100", "url": "http://192.168.1.230:8080", "icon": "qbittorrent" }
|
||||||
|
]
|
||||||
30
lib/health/registry.js
Normal file
30
lib/health/registry.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const CONFIG = path.join(__dirname, '../../config/services.json');
|
||||||
|
export const CATEGORY_ORDER = ['agents', 'infrastructure', 'media', 'other'];
|
||||||
|
|
||||||
|
let cache = null;
|
||||||
|
export function load() {
|
||||||
|
if (!cache) cache = JSON.parse(readFileSync(CONFIG, 'utf8'));
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
export function _reset() { cache = null; } // tests
|
||||||
|
|
||||||
|
export function iconSlug(svc) {
|
||||||
|
return (svc.icon || svc.name).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function grouped(services) {
|
||||||
|
const map = new Map();
|
||||||
|
for (const s of services) {
|
||||||
|
const cat = CATEGORY_ORDER.includes(s.category) ? s.category : 'other';
|
||||||
|
if (!map.has(cat)) map.set(cat, []);
|
||||||
|
map.get(cat).push(s);
|
||||||
|
}
|
||||||
|
return [...CATEGORY_ORDER, ...[...map.keys()].filter(c => !CATEGORY_ORDER.includes(c))]
|
||||||
|
.filter(c => map.has(c))
|
||||||
|
.map(category => ({ category, services: map.get(category) }));
|
||||||
|
}
|
||||||
17
tests/health/registry.test.js
Normal file
17
tests/health/registry.test.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { load, grouped, iconSlug, CATEGORY_ORDER } from '../../lib/health/registry.js';
|
||||||
|
|
||||||
|
describe('registry', () => {
|
||||||
|
it('loads the seed config', () => { expect(load().length).toBeGreaterThan(0); });
|
||||||
|
it('derives an icon slug from icon or name', () => {
|
||||||
|
expect(iconSlug({ name: 'Open WebUI' })).toBe('open-webui');
|
||||||
|
expect(iconSlug({ name: 'Plex', icon: 'plex' })).toBe('plex');
|
||||||
|
});
|
||||||
|
it('groups in agents→infrastructure→media order', () => {
|
||||||
|
const g = grouped(load());
|
||||||
|
const cats = g.map(x => x.category);
|
||||||
|
const ai = cats.indexOf('agents'), mi = cats.indexOf('media');
|
||||||
|
expect(ai).toBeLessThan(mi);
|
||||||
|
expect(CATEGORY_ORDER[0]).toBe('agents');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user