// bazarr's Servarr step — run once by the host after bazarr's server starts, and run again whenever a // binding or pair credential it reads changes (the container's `restart-on`, novox/hq ADR 0099). // // **A step, not a loop**: 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, a // bazarr that would not keep the settings — so the node reports the step failed and the host runs it // again on the next apply. Declared last in the manifest, so its failing gates nothing else of // bazarr's (novox/hq ADR 0136). // // Reads, per app, `/.json` (the binding) and `/.secret` (the pair // credential), where is MESH_SERVARR_DIR; and bazarr's own key from bazarr's own config.yaml // under MESH_BAZARR_CONFIG_DIR. Never prints a key. import { join } from "node:path"; import { apiKeyFromConfigDir } from "../apikey.js"; import { APPS, bazarrReady, readBinding, readIfThere, reconcileApp, type Http } from "./settings.js"; const dir = process.env.MESH_SERVARR_DIR ?? "/run/servarr"; const url = process.env.MESH_BAZARR_URL ?? "http://127.0.0.1:6767"; const waitSeconds = Number(process.env.MESH_BAZARR_WAIT_SECONDS ?? "180"); const http: Http = { fetch: (u, init) => fetch(u, init) }; const bazarrUp = await bazarrReady(http, { url, apiKey: "" }, waitSeconds * 1000); if (!bazarrUp) { console.error(`[bazarr-servarr] bazarr did not answer at ${url} within ${waitSeconds}s`); process.exit(1); } // Read after bazarr answers: on a first start bazarr writes its config.yaml, key included, as it boots. const apiKey = apiKeyFromConfigDir(process.env.MESH_BAZARR_CONFIG_DIR); if (!apiKey) { console.error("[bazarr-servarr] no bazarr API key in bazarr's config/config.yaml under MESH_BAZARR_CONFIG_DIR"); process.exit(1); } const bazarr = { url, apiKey }; let failed = 0; for (const spec of APPS) { const outcome = await reconcileApp( http, bazarr, spec, await readBinding(join(dir, `${spec.provision}.json`)), await readIfThere(join(dir, `${spec.provision}.secret`)), ); switch (outcome.result) { case "unchanged": console.log(`[bazarr-servarr] ${outcome.app}: already as the mesh says; key taken by ${outcome.app}`); break; case "written": console.log(`[bazarr-servarr] ${outcome.app}: wrote ${outcome.fields.join(", ")}; key taken by ${outcome.app}`); break; case "refused": failed++; console.error(`[bazarr-servarr] ${outcome.app}: ${outcome.problem}`); break; } } process.exitCode = failed > 0 ? 1 : 0;