repo/installer/src/metadata.ts
Ezequiel C. 187379de4e multistreaming: scenes/composition, installer build support, service updates
- multistreaming (new): RTMP ingest + multi-platform fan-out with pluggable providers (Twitch/YouTube/Kick/custom), zero-knowledge key vaults, Authelia OIDC auth, shared rooms with editor/streamer roles, single-use invites, per-account streaming grants, and scenes & composition (grid/PiP layouts, text/image overlays, per-output audio routing).
- installer: support Dockerfile build in metadata (not just image) and RSA key generation for the Authelia OIDC JWKS.
- authelia: add OIDC provider with portainer + multistreaming clients (public + PKCE).
- services: remove allprox; add nginx-proxy-manager and portainer; update lldap; regenerate catalog.
2026-09-02 21:18:25 +02:00

57 lines
1.9 KiB
TypeScript

import type { ServiceMetadata } from "./types.ts";
export interface ValidationIssue {
path: string;
message: string;
}
export function parseAndValidate(raw: string, fallbackId: string): ServiceMetadata {
let data: unknown;
try {
data = JSON.parse(raw);
} catch (e) {
throw new Error(`metadata.json is not valid JSON: ${(e as Error).message}`);
}
if (typeof data !== "object" || data === null || Array.isArray(data)) {
throw new Error("metadata.json must be a JSON object");
}
const meta = data as ServiceMetadata;
const issues = validateMetadata(meta, fallbackId);
if (issues.length > 0) {
const detail = issues.map((i) => `${i.path}: ${i.message}`).join("; ");
throw new Error(`invalid metadata.json (${detail})`);
}
meta.id = meta.id || fallbackId;
return meta;
}
export function validateMetadata(meta: ServiceMetadata, fallbackId: string): ValidationIssue[] {
const issues: ValidationIssue[] = [];
const id = meta.id || fallbackId;
if (!meta.name || typeof meta.name !== "string") {
issues.push({ path: "name", message: "required string" });
}
if (!meta.description || typeof meta.description !== "string") {
issues.push({ path: "description", message: "required string" });
}
if (!meta.version || typeof meta.version !== "string") {
issues.push({ path: "version", message: "required string" });
}
if (!meta.compose || typeof meta.compose !== "object" || Array.isArray(meta.compose)) {
issues.push({ path: "compose", message: "required object (docker-compose service spec)" });
} else if (!meta.compose.image && !meta.compose.build) {
issues.push({
path: "compose",
message: "must define `image` (pull an image) or `build` (build from a Dockerfile)",
});
}
if (!/^[a-z0-9][a-z0-9-]*$/.test(id)) {
issues.push({ path: "id", message: "must be kebab-case and match the folder name" });
}
return issues;
}