ace's ombi holds one Plex entry, loaded from an older server and later retyped to plex's public name: its stored machineIdentifier is not plex's, while its address answers as plex. Matching by identifier alone would leave it and add a second entry for the same server. When no entry carries the server's identifier, each entry's own address is asked for /identity, and an entry plex answers for is adopted: the bound connection laid over, and the identifier corrected (ombi builds its "view in Plex" links from it). Its name, libraries and every other choice stay. An entry that cannot be asked, or answers as another server, is left alone; nothing is guessed. Every call of the step is now bounded, since an entry may name a host that no longer answers.
67 lines
3.1 KiB
TypeScript
67 lines
3.1 KiB
TypeScript
// ombi's connections step — run once by the host after ombi's server starts, and run again whenever
|
|
// a binding or pair credential it reads changes (the container's `restart-on`, novox/hq ADR 0099).
|
|
// It brings ombi's connections to Sonarr, Radarr, Lidarr (servarr/settings.ts) and Plex
|
|
// (plex/settings.ts) in line with what the mesh bound.
|
|
//
|
|
// **A step, not a loop**, for the reason route-adapter gives: everything it does is a function of
|
|
// files the mesh writes, and the host already knows when they change. It connects to no broker.
|
|
//
|
|
// Exits non-zero when any app could not be put right — a refused credential, an unreachable app, an
|
|
// ombi that cannot reach it — so the node reports the step failed and the host runs it again on the
|
|
// next apply. It is declared last in the manifest, so its failing gates nothing else of ombi's
|
|
// (novox/hq ADR 0136). One app failing does not stop the others being put right.
|
|
//
|
|
// Reads, per provision, `<dir>/<provision>.json` (the binding) and `<dir>/<provision>.secret` (the
|
|
// pair credential), where <dir> is MESH_CONNECTIONS_DIR. Never prints a key or a token.
|
|
|
|
import { join } from "node:path";
|
|
|
|
import { PLEX_PROVISION, reconcilePlex } from "../plex/settings.js";
|
|
import { APPS, ombiReady, readBinding, readIfThere, reconcileApp, type Http, type Outcome } from "../servarr/settings.js";
|
|
|
|
const dir = process.env.MESH_CONNECTIONS_DIR ?? "/run/connections";
|
|
const url = process.env.MESH_OMBI_URL ?? "http://127.0.0.1:3579";
|
|
const apiKey = (await readIfThere(process.env.MESH_OMBI_API_KEY_FILE))?.trim() ?? process.env.MESH_OMBI_API_KEY ?? "";
|
|
const waitSeconds = Number(process.env.MESH_OMBI_WAIT_SECONDS ?? "180");
|
|
|
|
// Every call bounded: an entry ombi keeps may name a host that no longer answers, and a step that
|
|
// hangs on it holds the apply.
|
|
const http: Http = { fetch: (u, init) => fetch(u, { ...init, signal: AbortSignal.timeout(20_000) }) };
|
|
|
|
if (!apiKey) {
|
|
console.error("[ombi-connections] no ombi API key — ombi's own `api-key` secret has not been accepted");
|
|
process.exit(1);
|
|
}
|
|
const ombi = { url, apiKey };
|
|
|
|
if (!(await ombiReady(http, ombi, waitSeconds * 1000))) {
|
|
console.error(`[ombi-connections] ombi did not answer at ${url} within ${waitSeconds}s`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const inputs = async (provision: string) =>
|
|
[await readBinding(join(dir, `${provision}.json`)), await readIfThere(join(dir, `${provision}.secret`))] as const;
|
|
|
|
const outcomes: Outcome[] = [];
|
|
for (const spec of APPS) {
|
|
outcomes.push(await reconcileApp(http, ombi, spec, ...(await inputs(spec.provision))));
|
|
}
|
|
outcomes.push(await reconcilePlex(http, ombi, ...(await inputs(PLEX_PROVISION))));
|
|
|
|
let failed = 0;
|
|
for (const outcome of outcomes) {
|
|
switch (outcome.result) {
|
|
case "unchanged":
|
|
console.log(`[ombi-connections] ${outcome.app}: already as the mesh says; connection tested`);
|
|
break;
|
|
case "written":
|
|
console.log(`[ombi-connections] ${outcome.app}: wrote ${outcome.fields.join(", ")}; connection tested`);
|
|
break;
|
|
case "refused":
|
|
failed++;
|
|
console.error(`[ombi-connections] ${outcome.app}: ${outcome.problem}`);
|
|
break;
|
|
}
|
|
}
|
|
process.exitCode = failed > 0 ? 1 : 0;
|