"use client"; import { useCallback, useEffect, useState } from "react"; import { Nav, useAuth } from "@/components/Nav"; interface AddonTokenRow { id: string; label: string | null; revoked: boolean; createdAt: string; hasIptv?: boolean; iptvOk?: boolean; } interface IptvForm { label: string; baseUrl: string; username: string; password: string; enableLive: boolean; enableVod: boolean; enableSeries: boolean; lastOkAt?: string | null; lastError?: string | null; hasPassword?: boolean; } const emptyIptv = (): IptvForm => ({ label: "", baseUrl: "", username: "", password: "", enableLive: true, enableVod: true, enableSeries: true, }); const MASTER_URL = "https://master.vonas.nl"; interface ShelfRow { id: string; name: string; slug: string; kind: "MOVIE" | "SERIES"; sortOrder: number; enabled: boolean; scanRootCount: number; mediaFileCount: number; } export default function SettingsPage() { useAuth(); const [tokens, setTokens] = useState([]); const [addonToken, setAddonToken] = useState(""); const [installUrl, setInstallUrl] = useState(""); const [label, setLabel] = useState("Stremio"); const [iptvTokenId, setIptvTokenId] = useState(null); const [iptv, setIptv] = useState(emptyIptv()); const [iptvBusy, setIptvBusy] = useState(false); const [iptvMsg, setIptvMsg] = useState(null); const [iptvErr, setIptvErr] = useState(null); const [shelves, setShelves] = useState([]); const [newShelfName, setNewShelfName] = useState(""); const [newShelfKind, setNewShelfKind] = useState<"MOVIE" | "SERIES">("MOVIE"); const loadTokens = useCallback(() => { fetch("/api/v1/admin/addon-tokens", { credentials: "include" }) .then((r) => r.json()) .then((d) => setTokens(d.tokens ?? [])); }, []); const loadShelves = useCallback(() => { fetch("/api/v1/admin/library/shelves", { credentials: "include" }) .then((r) => r.json()) .then((d) => setShelves(d.shelves ?? [])); }, []); useEffect(() => { loadTokens(); loadShelves(); }, [loadTokens, loadShelves]); async function createShelf() { if (!newShelfName.trim()) return; const res = await fetch("/api/v1/admin/library/shelves", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: newShelfName.trim(), kind: newShelfKind }), }); if (!res.ok) { const data = await res.json().catch(() => ({})); alert(data.error?.message ?? "Aanmaken mislukt"); return; } setNewShelfName(""); loadShelves(); } async function deleteShelf(id: string, name: string) { if (!confirm(`Plank "${name}" verwijderen?`)) return; const res = await fetch(`/api/v1/admin/library/shelves/${id}`, { method: "DELETE", credentials: "include", }); if (!res.ok) { const data = await res.json().catch(() => ({})); alert(data.error?.message ?? "Verwijderen mislukt"); return; } loadShelves(); } async function createAddonToken() { const res = await fetch("/api/v1/admin/addon-tokens", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ label }), }); const data = await res.json(); if (!res.ok) { alert(data.error?.message ?? "Aanmaken mislukt"); return; } setAddonToken(data.token); setInstallUrl(`${MASTER_URL}/stremio/${data.token}/manifest.json`); loadTokens(); } async function revokeToken(id: string) { if (!confirm("Addon deactiveren? Bestaande Stremio-installaties stoppen met werken.")) return; await fetch(`/api/v1/admin/addon-tokens/${id}/revoke`, { method: "POST", credentials: "include", }); loadTokens(); } async function activateToken(id: string) { await fetch(`/api/v1/admin/addon-tokens/${id}/activate`, { method: "POST", credentials: "include", }); loadTokens(); } async function deleteToken(id: string) { if (!confirm("Addon definitief verwijderen? Dit kan niet ongedaan worden gemaakt.")) return; await fetch(`/api/v1/admin/addon-tokens/${id}`, { method: "DELETE", credentials: "include", }); if (iptvTokenId === id) { setIptvTokenId(null); setIptv(emptyIptv()); } loadTokens(); } async function openIptv(tokenId: string) { setIptvTokenId(tokenId); setIptvMsg(null); setIptvErr(null); setIptvBusy(true); try { const res = await fetch(`/api/v1/admin/addon-tokens/${tokenId}/iptv`, { credentials: "include", }); const data = await res.json(); if (!res.ok) { setIptvErr(data.error?.message ?? "Laden mislukt"); setIptv(emptyIptv()); return; } if (data.iptv) { setIptv({ label: data.iptv.label ?? "", baseUrl: data.iptv.baseUrl ?? "", username: data.iptv.username ?? "", password: "", enableLive: data.iptv.enableLive ?? true, enableVod: data.iptv.enableVod ?? true, enableSeries: data.iptv.enableSeries ?? true, lastOkAt: data.iptv.lastOkAt, lastError: data.iptv.lastError, hasPassword: data.iptv.hasPassword, }); } else { setIptv(emptyIptv()); } } finally { setIptvBusy(false); } } async function saveIptv() { if (!iptvTokenId) return; setIptvBusy(true); setIptvMsg(null); setIptvErr(null); try { const res = await fetch(`/api/v1/admin/addon-tokens/${iptvTokenId}/iptv`, { method: "PUT", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ label: iptv.label || undefined, baseUrl: iptv.baseUrl, username: iptv.username, password: iptv.password || undefined, enableLive: iptv.enableLive, enableVod: iptv.enableVod, enableSeries: iptv.enableSeries, }), }); const data = await res.json(); if (!res.ok) { setIptvErr(data.error?.message ?? "Opslaan mislukt"); return; } setIptvMsg( `Verbonden · ${data.iptv?.liveCategories ?? 0} live-bouquets. Herlaad de Stremio-addon om Live TV te zien.` ); setIptv((s) => ({ ...s, password: "", hasPassword: true, lastError: null })); loadTokens(); await openIptv(iptvTokenId); } finally { setIptvBusy(false); } } async function testIptv() { if (!iptvTokenId) return; setIptvBusy(true); setIptvMsg(null); setIptvErr(null); try { const res = await fetch(`/api/v1/admin/addon-tokens/${iptvTokenId}/iptv/test`, { method: "POST", credentials: "include", }); const data = await res.json(); if (!res.ok) { setIptvErr(data.error?.message ?? "Test mislukt"); return; } setIptvMsg( `OK · status ${data.status}` + (data.expDate ? ` · exp ${data.expDate}` : "") + ` · live ${data.liveCategories} · films ${data.vodCategories} · series ${data.seriesCategories}` ); loadTokens(); } finally { setIptvBusy(false); } } async function deleteIptv() { if (!iptvTokenId) return; if (!confirm("IPTV-userline ontkoppelen van deze addon?")) return; setIptvBusy(true); setIptvErr(null); try { await fetch(`/api/v1/admin/addon-tokens/${iptvTokenId}/iptv`, { method: "DELETE", credentials: "include", }); setIptv(emptyIptv()); setIptvMsg("IPTV ontkoppeld"); loadTokens(); } finally { setIptvBusy(false); } } const activeToken = tokens.find((t) => t.id === iptvTokenId); return ( <>