diff --git a/apps/admin-ui/src/app/settings/page.tsx b/apps/admin-ui/src/app/settings/page.tsx index 2fbc68d..f8eece3 100644 --- a/apps/admin-ui/src/app/settings/page.tsx +++ b/apps/admin-ui/src/app/settings/page.tsx @@ -8,8 +8,33 @@ interface AddonTokenRow { 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"; export default function SettingsPage() { @@ -18,6 +43,11 @@ export default function SettingsPage() { 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 loadTokens = useCallback(() => { fetch("/api/v1/admin/addon-tokens", { credentials: "include" }) @@ -69,9 +99,131 @@ export default function SettingsPage() { 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 ( <>