Files
screenclipper/apps/frontend/lib/apiDaemon.ts
T
ayrisdevandClaude Sonnet 5 43ccbd9ec7 feat: interaktif panel kontrolleri (Faz 3)
Yayın durdurma, kanal duraklat/devam (tekli+toplu), şimdi kontrol et,
manuel URL ile zorla kayıt başlatma, canlı player+log önizleme, oturum
süre sayacı, başarısız/takılı render için tekrar dene ve iptal, poll
aralığı + ham segment TTL'yi panelden canlı değiştirme, cookie bayatlık
uyarısı, Telegram bildirim aç/kapa, ve kullanım/maliyet özeti (/stats).

api-daemon'ın yeni state-değiştiren endpoint'leri (stop/force-start/
render/cancel) INTERNAL_API_TOKEN ile korunuyor — bu daemon'ın portu
Coolify'de public'e açık olduğu için korumasız bırakmak güvenlik açığı
olurdu.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 13:38:47 +03:00

47 lines
1.7 KiB
TypeScript

export interface ChannelStatus {
id: string;
name: string;
isActive: boolean;
lastCheckedAt: string | null;
recording: boolean;
activeSessionId: string | null;
lastPollError: string | null;
}
export const API_DAEMON_URL = process.env.API_DAEMON_URL ?? "http://localhost:4001";
/** POSTs to a state-changing api-daemon endpoint with the shared internal auth header (see INTERNAL_API_TOKEN in .env.example). */
export async function postToApiDaemon<T = unknown>(path: string, body?: unknown): Promise<T> {
const res = await fetch(`${API_DAEMON_URL}${path}`, {
method: "POST",
headers: {
"content-type": "application/json",
...(process.env.INTERNAL_API_TOKEN ? { authorization: `Bearer ${process.env.INTERNAL_API_TOKEN}` } : {}),
},
body: body ? JSON.stringify(body) : undefined,
cache: "no-store",
});
if (!res.ok) {
const text = await res.text().catch(() => "");
throw new Error(`api-daemon ${path} failed (${res.status}): ${text.slice(0, 300)}`);
}
return res.json() as Promise<T>;
}
/**
* lastPollError only exists in api-daemon's in-memory map (not persisted to
* the DB), so the panel fetches it live over HTTP rather than via Prisma.
* Failures here (api-daemon down, network hiccup) shouldn't break the page —
* they just mean live-status fields fall back to unknown.
*/
export async function fetchChannelStatuses(): Promise<Map<string, ChannelStatus>> {
try {
const res = await fetch(`${API_DAEMON_URL}/status`, { cache: "no-store" });
if (!res.ok) return new Map();
const data = (await res.json()) as { channels: ChannelStatus[] };
return new Map(data.channels.map((c) => [c.id, c]));
} catch {
return new Map();
}
}