Usuniecie skryptów i zabezpieczenie tokenem api do aktualizacji danych z jambox

This commit is contained in:
dm
2025-12-16 08:44:16 +01:00
parent 116a915cac
commit 142d289be9
11 changed files with 256 additions and 821 deletions

View File

@@ -0,0 +1,205 @@
import path from "node:path";
import fs from "node:fs/promises";
import yaml from "js-yaml";
import { XMLParser } from "fast-xml-parser";
const JAMBOX_NUMBERS_URL = "https://www.jambox.pl/xml/jamboxwliczbach.xml";
/**
* Proste auth, żeby nikt z internetu nie mógł Ci pisać po YAML.
* .env: JAMBOX_ADMIN_TOKEN="..."
*/
function isAuthorized(request) {
const expected = import.meta.env.JAMBOX_ADMIN_TOKEN;
if (!expected) return false;
const token = request.headers.get("x-admin-token");
return token === expected;
}
async function fetchXml(url) {
const res = await fetch(url);
if (!res.ok) {
throw new Error(`Błąd pobierania XML: ${res.status} ${res.statusText}`);
}
return await res.text();
}
function parseCountsNode(xmlText) {
const parser = new XMLParser({
ignoreAttributes: false,
attributeNamePrefix: "@_",
});
const json = parser.parse(xmlText);
const node = json.xml?.node ?? json.node ?? null;
if (!node) {
throw new Error("Nie znaleziono <node> w jamboxwliczbach.xml");
}
return node;
}
function getInt(node, key) {
const raw = node?.[key];
const n = Number(raw);
return Number.isFinite(n) ? n : null;
}
/**
* Mapowanie kluczy XML na source+slug (tak jak w Twoim starym skrypcie)
*/
function xmlKeyFor(source, slug, isHd) {
const s = String(source || "").toUpperCase();
const sl = String(slug || "").toLowerCase();
// EVIO: ilosc_kanalow_eviooptimum / ilosc_kanalow_hd_eviooptimum
if (s === "EVIO") {
return isHd ? `ilosc_kanalow_hd_evio${sl}` : `ilosc_kanalow_evio${sl}`;
}
// PLUS: ilosc_kanalow_korzystny / ilosc_kanalow_hd_korzystny
if (s === "PLUS") {
return isHd ? `ilosc_kanalow_hd_${sl}` : `ilosc_kanalow_${sl}`;
}
return null;
}
function findParamIndex(params, key) {
return params.findIndex(
(p) => String(p?.klucz ?? "").toLowerCase() === key.toLowerCase(),
);
}
export async function POST({ request }) {
try {
if (!isAuthorized(request)) {
return new Response(JSON.stringify({ ok: false, error: "Unauthorized" }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
const body = await request.json().catch(() => ({}));
const dryRun = body?.dryRun === true;
const YAML_PATH =
import.meta.env.JAMBOX_TV_YAML_PATH ||
path.join(process.cwd(), "src", "content", "internet-telewizja", "cards.yaml");
// 1) XML
const xml = await fetchXml(JAMBOX_NUMBERS_URL);
const node = parseCountsNode(xml);
// 2) YAML
const rawYaml = await fs.readFile(YAML_PATH, "utf8");
const doc = yaml.load(rawYaml);
if (!doc || !Array.isArray(doc.cards)) {
return new Response(JSON.stringify({ ok: false, error: "YAML: brak doc.cards" }), {
status: 400,
headers: { "Content-Type": "application/json" },
});
}
const changedItems = [];
const unchangedItems = [];
const skippedItems = [];
for (const card of doc.cards) {
const id = card?.id ?? null;
const source = String(card?.source ?? "");
const slug = String(card?.slug ?? "");
if (!source || !slug) {
skippedItems.push({ id, reason: "brak source/slug" });
continue;
}
const params = Array.isArray(card?.parametry) ? card.parametry : [];
const iCanals = findParamIndex(params, "canals");
const iHd = findParamIndex(params, "canalshd");
// nie dopisujemy na siłę, tylko aktualizujemy to co istnieje
if (iCanals === -1 && iHd === -1) {
skippedItems.push({ id, source, slug, reason: "brak canals/canalshd w parametry" });
continue;
}
const keyCanals = xmlKeyFor(source, slug, false);
const keyHd = xmlKeyFor(source, slug, true);
const xmlCanals = keyCanals ? getInt(node, keyCanals) : null;
const xmlHd = keyHd ? getInt(node, keyHd) : null;
if (xmlCanals == null && xmlHd == null) {
skippedItems.push({
id,
source,
slug,
reason: "brak wartości w XML",
keys: { keyCanals, keyHd },
});
continue;
}
const before = {
canals: iCanals !== -1 ? Number(params[iCanals]?.value) : null,
canalshd: iHd !== -1 ? Number(params[iHd]?.value) : null,
};
const after = {
canals: xmlCanals ?? before.canals,
canalshd: xmlHd ?? before.canalshd,
};
const willChange =
(after.canals != null && before.canals !== after.canals) ||
(after.canalshd != null && before.canalshd !== after.canalshd);
if (!willChange) {
unchangedItems.push({ id, source, slug, before });
continue;
}
if (!dryRun) {
if (iCanals !== -1 && after.canals != null) params[iCanals].value = after.canals;
if (iHd !== -1 && after.canalshd != null) params[iHd].value = after.canalshd;
card.parametry = params;
}
changedItems.push({ id, source, slug, before, after, keys: { keyCanals, keyHd } });
}
if (!dryRun && changedItems.length > 0) {
const newYaml = yaml.dump(doc, {
lineWidth: -1,
noRefs: true,
sortKeys: false,
});
await fs.writeFile(YAML_PATH, newYaml, "utf8");
}
return new Response(
JSON.stringify({
ok: true,
dryRun,
yamlPath: YAML_PATH,
changed: changedItems.length,
unchanged: unchangedItems.length,
skipped: skippedItems.length,
changedItems,
skippedItems,
}),
{ status: 200, headers: { "Content-Type": "application/json" } },
);
} catch (err) {
console.error("update-channels error:", err);
return new Response(JSON.stringify({ ok: false, error: String(err?.message ?? err) }), {
status: 500,
headers: { "Content-Type": "application/json" },
});
}
}