fix(accounts): keep invalid accounts disabled

Require the silent activation refresh to return a valid account state before persisting the enabled setting. Invalid or expired sessions now roll back the optimistic toggle and cannot trigger an interactive login from active downloads.
This commit is contained in:
Sucukdeluxe
2026-08-15 21:43:30 +02:00
parent 82303dc94a
commit 31560cfbe0
3 changed files with 21 additions and 7 deletions
+4 -4
View File
@@ -2861,7 +2861,7 @@ export function App(): ReactElement {
const persistAccountToggle = async ( const persistAccountToggle = async (
nextDraft: RendererSettingsDraft, nextDraft: RendererSettingsDraft,
refreshBeforePersist?: () => Promise<void> refreshBeforePersist?: () => ReturnType<typeof window.rd.checkAccountCredentials>
): Promise<RendererSettings> => { ): Promise<RendererSettings> => {
const previousDraft = settingsDraft; const previousDraft = settingsDraft;
const previousDirty = settingsDirtyRef.current; const previousDirty = settingsDirtyRef.current;
@@ -2905,7 +2905,7 @@ export function App(): ReactElement {
}; };
await persistAccountToggle( await persistAccountToggle(
nextDraft, nextDraft,
enabled ? async () => { await window.rd.checkAccountCredentials({ kind: "debridlink-api", accountId: key.id }); } : undefined enabled ? () => window.rd.checkAccountCredentials({ kind: "debridlink-api", accountId: key.id }) : undefined
); );
showToast( showToast(
enabled enabled
@@ -2953,7 +2953,7 @@ export function App(): ReactElement {
megaDebridApiDisabledAccountIds: apiDisabledIds, megaDebridApiDisabledAccountIds: apiDisabledIds,
megaDebridWebDisabledAccountIds: webDisabledIds megaDebridWebDisabledAccountIds: webDisabledIds
}, enabled }, enabled
? async () => { await window.rd.checkAccountCredentials({ kind, accountId }); } ? () => window.rd.checkAccountCredentials({ kind, accountId })
: undefined : undefined
); );
showToast(enabled ? "Account aktiviert" : "Account deaktiviert", 2000); showToast(enabled ? "Account aktiviert" : "Account deaktiviert", 2000);
@@ -3010,7 +3010,7 @@ export function App(): ReactElement {
disabledProviders: nextState.disabledProviders, disabledProviders: nextState.disabledProviders,
realDebridDisabledAccountIds: nextState.disabledAccountIds realDebridDisabledAccountIds: nextState.disabledAccountIds
}, enabled }, enabled
? async () => { await window.rd.checkAccountCredentials({ kind, accountId }); } ? () => window.rd.checkAccountCredentials({ kind, accountId })
: undefined : undefined
); );
showToast(enabled ? "Account aktiviert" : "Account deaktiviert", 2000); showToast(enabled ? "Account aktiviert" : "Account deaktiviert", 2000);
+5 -2
View File
@@ -146,11 +146,14 @@ export async function runOptimisticAccountUpdate<T>(
} }
export async function runAccountEnableRefresh<T>( export async function runAccountEnableRefresh<T>(
refresh: (() => Promise<void>) | undefined, refresh: (() => Promise<{ valid: boolean; message?: string }>) | undefined,
persist: () => Promise<T> persist: () => Promise<T>
): Promise<T> { ): Promise<T> {
if (refresh) { if (refresh) {
await refresh(); const status = await refresh();
if (!status.valid) {
throw new Error(status.message || "Accountprüfung fehlgeschlagen");
}
} }
return persist(); return persist();
} }
+12 -1
View File
@@ -141,7 +141,7 @@ describe("account activation refresh", () => {
const events: string[] = []; const events: string[] = [];
await runAccountEnableRefresh( await runAccountEnableRefresh(
async () => { events.push("check"); }, async () => { events.push("check"); return { valid: true, message: "Premium aktiv" }; },
async () => { events.push("persist"); } async () => { events.push("persist"); }
); );
@@ -158,4 +158,15 @@ describe("account activation refresh", () => {
expect(events).toEqual(["persist"]); expect(events).toEqual(["persist"]);
}); });
it("does not enable an account whose silent refresh is invalid", async () => {
const events: string[] = [];
await expect(runAccountEnableRefresh(
async () => { events.push("check"); return { valid: false, message: "Sitzung abgelaufen" }; },
async () => { events.push("persist"); }
)).rejects.toThrow("Sitzung abgelaufen");
expect(events).toEqual(["check"]);
});
}); });