Allow refreshing Viaplay session tokens on existing accounts.

Add Tokens vernieuwen UI and strip Bearer prefix when saving accessToken.
This commit is contained in:
Jos Vooges | STH 2026-09-22 22:54:18 +02:00
parent f19c173369
commit 1c939acd59
3 changed files with 173 additions and 53 deletions

View file

@ -72,6 +72,11 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
const [newAccessToken, setNewAccessToken] = useState("");
const [newProfileId, setNewProfileId] = useState("");
const [tokenEditId, setTokenEditId] = useState<string | null>(null);
const [editSession, setEditSession] = useState("");
const [editAccessToken, setEditAccessToken] = useState("");
const [editProfileId, setEditProfileId] = useState("");
const applySettings = useCallback(
(d: ViaplaySettings) => {
setSettings(d);
@ -186,6 +191,55 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
}
}
function openTokenEdit(account: ViaplayAccount) {
setTokenEditId((id) => (id === account.id ? null : account.id));
setEditSession("");
setEditAccessToken("");
setEditProfileId("");
setErr(null);
setMsg(null);
}
async function saveAccountTokens(account: ViaplayAccount) {
if (!editSession.trim() && !editAccessToken.trim() && !editProfileId.trim()) {
setErr("Plak minstens session, accessToken of profileId");
return;
}
setBusy(true);
setErr(null);
setMsg(null);
try {
const body: Record<string, string> = {};
if (editSession.trim()) body.session = editSession.trim();
if (editAccessToken.trim()) body.accessToken = editAccessToken.trim();
if (editProfileId.trim()) body.profileId = editProfileId.trim();
const r = await fetch(`/api/v1/admin/scripts/viaplay/accounts/${account.id}`, {
method: "PATCH",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const d = await r.json();
if (!r.ok) throw new Error(d.error?.message ?? "Tokens bijwerken mislukt");
applySettings(d);
setTokenEditId(null);
setEditSession("");
setEditAccessToken("");
setEditProfileId("");
const updated = (d.accounts as ViaplayAccount[] | undefined)?.find((a) => a.id === account.id);
const sub = updated?.subscriptionSummary;
setMsg(
sub
? `Tokens bijgewerkt · abonnement: ${sub}`
: "Tokens bijgewerkt — druk Check als abonnement nog ontbreekt"
);
} catch (e) {
setErr(String((e as Error).message ?? e));
} finally {
setBusy(false);
}
}
async function removeAccount(account: ViaplayAccount) {
if (!confirm(`Account “${account.label || account.email}” verwijderen?`)) return;
setBusy(true);
@ -377,14 +431,18 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
{accounts.map((a) => (
<li
key={a.id}
style={{
padding: "0.6rem 0",
borderBottom: "1px solid var(--border, #333)",
}}
>
<div
style={{
display: "flex",
flexWrap: "wrap",
gap: 8,
alignItems: "center",
justifyContent: "space-between",
padding: "0.6rem 0",
borderBottom: "1px solid var(--border, #333)",
}}
>
<div style={{ flex: "1 1 280px", minWidth: 0 }}>
@ -417,7 +475,9 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
</span>
)}
</div>
{a.partnerName && a.subscriptionSummary && !a.subscriptionSummary.includes(a.partnerName) ? (
{a.partnerName &&
a.subscriptionSummary &&
!a.subscriptionSummary.includes(a.partnerName) ? (
<div className="muted" style={{ fontSize: "0.8rem", marginTop: 2 }}>
Partner: {a.partnerName}
</div>
@ -433,6 +493,9 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
<button type="button" disabled={busy} onClick={() => void probeAccounts(a.id)}>
Check
</button>
<button type="button" disabled={busy} onClick={() => openTokenEdit(a)}>
{tokenEditId === a.id ? "Annuleren" : "Tokens vernieuwen"}
</button>
<button type="button" disabled={busy} onClick={() => void toggleAccount(a)}>
{a.enabled ? "Uitschakelen" : "Inschakelen"}
</button>
@ -440,6 +503,62 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
Verwijderen
</button>
</div>
</div>
{tokenEditId === a.id ? (
<div
style={{
marginTop: 10,
padding: "10px 12px",
borderRadius: 6,
border: "1px solid var(--border, #333)",
display: "grid",
gap: "0.75rem",
}}
>
<p className="muted" style={{ margin: 0, fontSize: "0.85rem" }}>
Plak verse waarden uit de browser. Lege velden blijven ongewijzigd.
</p>
<label className="field">
session
<textarea
value={editSession}
onChange={(e) => setEditSession(e.target.value)}
rows={2}
disabled={busy}
placeholder="nieuw session-cookie (optioneel)"
style={{ fontFamily: "monospace", fontSize: "0.8rem" }}
/>
</label>
<label className="field">
accessToken
<textarea
value={editAccessToken}
onChange={(e) => setEditAccessToken(e.target.value)}
rows={2}
disabled={busy}
placeholder="nieuw accessToken (voor pakketinfo vereist)"
style={{ fontFamily: "monospace", fontSize: "0.8rem" }}
/>
</label>
<label className="field">
profileId (optioneel)
<input
value={editProfileId}
onChange={(e) => setEditProfileId(e.target.value)}
disabled={busy}
placeholder="nieuw profileId"
/>
</label>
<button
type="button"
className="btn-primary"
disabled={busy}
onClick={() => void saveAccountTokens(a)}
>
Tokens opslaan &amp; Check
</button>
</div>
) : null}
</li>
))}
</ul>

View file

@ -130,7 +130,7 @@ export function createAccountStored(
const email = input.email.trim().toLowerCase();
if (!email) throw new Error("E-mail verplicht");
const session = input.session?.trim();
const accessToken = input.accessToken?.trim();
const accessToken = input.accessToken?.trim().replace(/^Bearer\s+/i, "").trim();
if (!session || !accessToken) {
throw new Error("session + accessToken verplicht");
}

View file

@ -261,7 +261,8 @@ export async function updateViaplayAccount(
sessionEnc = encryptSecret(input.session.trim(), sessionSecret);
}
if (input.accessToken?.trim()) {
accessTokenEnc = encryptSecret(input.accessToken.trim(), sessionSecret);
const token = input.accessToken.trim().replace(/^Bearer\s+/i, "").trim();
accessTokenEnc = encryptSecret(token, sessionSecret);
}
if (input.profileId !== undefined) {
profileId = input.profileId.trim() || undefined;