fix(accounts): refresh active logins and toggle instantly

Split bulk account checks into active-only and all-configured scopes so disabled providers, Mega-Debrid accounts, and Debrid-Link keys no longer distort the normal result count. Preserve explicit full checks for diagnosing disabled credentials and surface their failed status.

Render account enablement from the optimistic settings draft, persist it with revision-safe rollback, and apply the same immediate behavior to individual and bulk switches. Extend the IPC contract, translations, changelog, and regression coverage.
This commit is contained in:
Sucukdeluxe
2026-08-15 01:14:54 +02:00
parent 64c846861b
commit 0119534e07
15 changed files with 272 additions and 81 deletions
+30 -9
View File
@@ -1,5 +1,5 @@
import type { AppSettings, DebridAccountStatus } from "../shared/types";
import { parseMegaDebridAccounts, type MegaDebridAccountEntry } from "../shared/mega-debrid-accounts";
import type { AccountCheckScope, AppSettings, DebridAccountStatus, DebridProvider } from "../shared/types";
import { getMegaDebridAccountsForMode, getMegaDebridDisabledAccountIdsForMode, parseMegaDebridAccounts, type MegaDebridAccountEntry } from "../shared/mega-debrid-accounts";
import { parseDebridLinkApiKeys, type DebridLinkApiKeyEntry } from "../shared/debrid-link-keys";
import { logger } from "./logger";
import { compactErrorText } from "./utils";
@@ -270,14 +270,35 @@ export async function checkDebridLinkKey(
export async function checkAllDebridAccounts(
settings: AppSettings,
signal?: AbortSignal,
probeRealDebridWebSession?: RealDebridSessionProbe
probeRealDebridWebSession?: RealDebridSessionProbe,
scope: AccountCheckScope = "all"
): Promise<DebridAccountStatus[]> {
const now = Date.now();
const megaAccounts = parseMegaDebridAccounts(settings.megaCredentials || "", settings.megaPassword || "");
const debridLinkKeys = parseDebridLinkApiKeys(settings.debridLinkApiKeys || "");
const now = Date.now();
const providerEnabled = (provider: DebridProvider): boolean => !(settings.disabledProviders || []).includes(provider);
const configuredMegaAccounts = parseMegaDebridAccounts(settings.megaCredentials || "", settings.megaPassword || "");
const activeMegaAccounts = (["api", "web"] as const).flatMap((mode) => {
const provider: DebridProvider = mode === "api" ? "megadebrid-api" : "megadebrid-web";
const modeEnabled = mode === "api" ? settings.megaDebridApiEnabled : settings.megaDebridWebEnabled;
if (!modeEnabled || !providerEnabled(provider) || !providerEnabled("megadebrid")) {
return [];
}
const disabledIds = new Set(getMegaDebridDisabledAccountIdsForMode(settings, mode));
return getMegaDebridAccountsForMode(settings, mode).filter((account) => !disabledIds.has(account.id));
});
const megaAccounts = scope === "all"
? configuredMegaAccounts
: [...new Map(activeMegaAccounts.map((account) => [account.id, account])).values()];
const configuredDebridLinkKeys = parseDebridLinkApiKeys(settings.debridLinkApiKeys || "");
const debridLinkKeys = scope === "all"
? configuredDebridLinkKeys
: providerEnabled("debridlink")
? configuredDebridLinkKeys.filter((key) => !(settings.debridLinkDisabledKeyIds || []).includes(key.id))
: [];
const checkRealDebrid = Boolean(settings.realDebridUseWebLogin || String(settings.token || "").trim())
&& (scope === "all" || providerEnabled("realdebrid"));
const taskFns: Array<() => Promise<DebridAccountStatus>> = [
...(settings.realDebridUseWebLogin || String(settings.token || "").trim()
...(checkRealDebrid
? [() => checkRealDebridAccount(settings, signal, now, probeRealDebridWebSession)]
: []),
...megaAccounts.map((account) => () => checkMegaDebridAccount(account, signal, now)),
@@ -286,7 +307,7 @@ export async function checkAllDebridAccounts(
const results = await runWithConcurrency(taskFns, CHECK_CONCURRENCY);
logger.info(
`Account-Check abgeschlossen: ${results.length} Accounts geprueft ` +
`Account-Check abgeschlossen: scope=${scope}, ${results.length} Accounts geprueft ` +
`(${results.filter((r) => r.valid).length} gueltig, ${results.filter((r) => r.isPremium).length} premium)`
);
return results;
+7 -4
View File
@@ -3,8 +3,9 @@ import os from "node:os";
import v8 from "node:v8";
import { app } from "electron";
import {
AddLinksPayload,
AllDebridHostInfo,
AddLinksPayload,
AccountCheckScope,
AllDebridHostInfo,
AppSettings,
AccountCommand,
AccountCommandResult,
@@ -647,18 +648,20 @@ export class AppController {
return fetchDebridLinkHostLimits(this.settings.debridLinkApiKeys, host);
}
public async checkDebridAccounts(): Promise<DebridAccountStatus[]> {
public async checkDebridAccounts(scope: AccountCheckScope = "active"): Promise<DebridAccountStatus[]> {
const statuses = sanitizeDebridAccountStatuses(
await checkAllDebridAccounts(
this.settings,
undefined,
(signal) => this.realDebridWebFallback.probeLoginState(signal)
(signal) => this.realDebridWebFallback.probeLoginState(signal),
scope
),
collectAccountStatusRedactionValues(this.settings)
);
this.manager.applyDebridAccountStatuses(statuses);
this.audit("INFO", "Debrid-Accounts geprueft", {
total: statuses.length,
scope,
valid: statuses.filter((s) => s.valid).length,
premium: statuses.filter((s) => s.isPremium).length
});
+6 -2
View File
@@ -843,8 +843,12 @@ function registerIpcHandlers(): void {
return controller.getDebridLinkHostLimits();
});
handleTrusted(IPC_CHANNELS.CHECK_DEBRID_ACCOUNTS, async () => {
return controller.checkDebridAccounts();
handleTrusted(IPC_CHANNELS.CHECK_DEBRID_ACCOUNTS, async (_event, rawScope: unknown) => {
const scope = rawScope === undefined ? "active" : validateString(rawScope, "scope");
if (scope !== "active" && scope !== "all") {
throw new Error("scope ist ungültig");
}
return controller.checkDebridAccounts(scope);
});
handleTrusted(IPC_CHANNELS.CHECK_ACCOUNT_CREDENTIALS, async (_event, rawInput: unknown) => {