// POLITRAN API v1 — Node 18+ klient (fetch) + postup vydavatele. // POLITRAN_API_TOKEN=... node politran-client.mjs const BASE = (process.env.POLITRAN_API_BASE ?? 'https://politran.cz/api/v1').replace(/\/$/, ''); const TOKEN = process.env.POLITRAN_API_TOKEN; if (!TOKEN) throw new Error('POLITRAN_API_TOKEN missing'); export class PolitranApiError extends Error { constructor(status, code, message, details, requestId) { super(`${status} ${code}: ${message} (request ${requestId})`); Object.assign(this, { status, code, details: details ?? {}, requestId }); } } export async function api(method, path, { body, idempotencyKey, ifMatch, formData } = {}) { const headers = { Authorization: `Bearer ${TOKEN}`, Accept: 'application/json' }; if (body !== undefined) headers['Content-Type'] = 'application/json'; if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey; if (ifMatch) headers['If-Match'] = `"${ifMatch}"`; for (let attempt = 1; ; attempt++) { const res = await fetch(`${BASE}/${path.replace(/^\//, '')}`, { method, headers, body: formData ?? (body !== undefined ? JSON.stringify(body) : undefined), }); if (res.status === 429 && attempt < 4) { await new Promise((r) => setTimeout(r, 1000 * Number(res.headers.get('Retry-After') ?? 5))); continue; } const text = await res.text(); const json = text ? JSON.parse(text) : {}; if (!res.ok) { const e = json.error ?? {}; throw new PolitranApiError(res.status, e.code ?? `HTTP_${res.status}`, e.message ?? res.statusText, e.details, e.request_id); } return json; } } // --- Postup vydavatele ------------------------------------------------------- const me = (await api('GET', '/me')).data; if (me.side !== 'publisher') throw new Error('publisher token required'); console.log('Organisation:', me.organisation.name); // Nová / změněná oznámení od poslední synchronizace (uložte si max updated_at). const since = process.env.SINCE ?? '2026-01-01T00:00:00+02:00'; const list = (await api('GET', `/publisher/notices?updated_since=${encodeURIComponent(since)}&sort=updated_at&per_page=100`)).data; console.log(`${list.length} notices since ${since}`); for (const summary of list) { const notice = (await api('GET', `/publisher/notices/${summary.id}`)).data; console.log(notice.short_code, notice.status, notice.advertiser.name, 'actions:', notice.allowed_actions.join(',')); // Rozhodnutí — jen pokud je akce povolená. if (process.env.AUTO_PUBLISH === '1' && notice.allowed_actions.includes('publish')) { try { const published = (await api('POST', `/publisher/notices/${notice.id}/publish`)).data; console.log(' published →', published.public_url); } catch (e) { if (e instanceof PolitranApiError && e.code === 'PUBLISHER_REPORT_URL_MISSING') { console.warn(' Fill "URL for reporting incorrect information" in POLITRAN publisher settings first.'); } else throw e; } } } // Registrace webhooku (jednorázově) — secret se vrátí jen teď. if (process.env.WEBHOOK_URL) { const wh = (await api('POST', '/publisher/webhooks', { body: { name: 'Adserver', url: process.env.WEBHOOK_URL, events: ['*'] }, idempotencyKey: 'webhook-adserver-v1', })).data; console.log('Webhook secret (store it now):', wh.secret); }