release: ship v2.0.23 queue and account reliability fixes

Normalize RapidGator aliases, stabilize active queue ordering, preserve user-controlled package expansion, and align compact queue status presentation.

Separate Mega-Debrid API and Web credential pools, migrate legacy credentials and disabled states safely, refresh live account availability without restart, honor disabled credential persistence, and invalidate Web sessions without allowing stale in-flight, retry, or queued logins to restore old cookies.

Expand regression coverage for account isolation, legacy migration, session races, live scheduler refresh, update notes, and responsive queue behavior.
This commit is contained in:
Sucukdeluxe
2026-08-11 18:31:01 +02:00
parent b5b1ff88e5
commit a6e6d2d439
36 changed files with 2343 additions and 1376 deletions
+108 -15
View File
@@ -2,7 +2,8 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { parseDebridLinkApiKeys } from "../src/shared/debrid-link-keys";
import { parseDebridLinkApiKeys } from "../src/shared/debrid-link-keys";
import { getMegaDebridAccountId } from "../src/shared/mega-debrid-accounts";
import { getProviderUsageDayKey } from "../src/shared/provider-daily-limits";
import { AppSettings } from "../src/shared/types";
import { defaultSettings } from "../src/main/constants";
@@ -44,11 +45,32 @@ describe("settings storage", () => {
expect(normalizeSettings({ ...normalized, columnOrder: ["name", "speed"] }).columnOrder).toEqual(["name", "speed"]);
});
it("uses English for new installations and preserves only supported languages", () => {
it("uses English for new installations and preserves only supported languages", () => {
expect(defaultSettings().language).toBe("en");
expect(normalizeSettings({ ...defaultSettings(), language: "de" }).language).toBe("de");
expect(normalizeSettings({ ...defaultSettings(), language: "fr" as "en" }).language).toBe("en");
});
expect(normalizeSettings({ ...defaultSettings(), language: "fr" as "en" }).language).toBe("en");
});
it("migrates a legacy shared Mega-Debrid pool into only the preferred mode", () => {
const legacy = { ...defaultSettings() } as Partial<AppSettings>;
legacy.megaCredentials = "legacy@example.test:legacy-pass";
legacy.megaLogin = "legacy@example.test";
legacy.megaPassword = "legacy-pass";
legacy.megaDebridApiEnabled = true;
legacy.megaDebridWebEnabled = true;
legacy.megaDebridPreferApi = true;
delete legacy.megaDebridApiCredentials;
delete legacy.megaDebridWebCredentials;
delete legacy.megaDebridApiDisabledAccountIds;
delete legacy.megaDebridWebDisabledAccountIds;
const normalized = normalizeSettings(legacy as AppSettings);
expect(normalized.megaDebridApiCredentials).toBe("legacy@example.test:legacy-pass");
expect(normalized.megaDebridWebCredentials).toBe("");
expect(normalized.megaDebridApiEnabled).toBe(true);
expect(normalized.megaDebridWebEnabled).toBe(false);
});
it("keeps German for existing settings files created before language selection existed", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-store-"));
@@ -109,17 +131,23 @@ describe("settings storage", () => {
saveSettings(paths, {
...defaultSettings(),
rememberToken: false,
token: "rd-token",
megaLogin: "mega-user",
megaPassword: "mega-pass",
bestToken: "best-token",
token: "rd-token",
megaLogin: "mega-user",
megaPassword: "mega-pass",
megaCredentials: "mega-user:mega-pass",
megaDebridApiCredentials: "mega-user:mega-pass",
megaDebridWebCredentials: "web-user:web-pass",
bestToken: "best-token",
allDebridToken: "all-token"
});
const raw = JSON.parse(fs.readFileSync(paths.configFile, "utf8")) as Record<string, unknown>;
expect(raw.token).toBe("");
expect(raw.megaLogin).toBe("");
expect(raw.megaPassword).toBe("");
expect(raw.megaPassword).toBe("");
expect(raw.megaCredentials).toBe("");
expect(raw.megaDebridApiCredentials).toBe("");
expect(raw.megaDebridWebCredentials).toBe("");
expect(raw.bestToken).toBe("");
expect(raw.allDebridToken).toBe("");
@@ -127,7 +155,10 @@ describe("settings storage", () => {
expect(loaded.rememberToken).toBe(false);
expect(loaded.token).toBe("");
expect(loaded.megaLogin).toBe("");
expect(loaded.megaPassword).toBe("");
expect(loaded.megaPassword).toBe("");
expect(loaded.megaCredentials).toBe("");
expect(loaded.megaDebridApiCredentials).toBe("");
expect(loaded.megaDebridWebCredentials).toBe("");
expect(loaded.bestToken).toBe("");
expect(loaded.allDebridToken).toBe("");
});
@@ -275,7 +306,7 @@ describe("settings storage", () => {
expect(webNormalized.hosterRouting.rapidgator).toBe("megadebrid-web");
});
it("migriert eine pre-v1.6.90-Config (Mega-Creds, beide Enable-Flags fehlen) zu aktiviertem Mega-Debrid statt es still auf false zu setzen", () => {
it("migriert eine pre-v1.6.90-Config (Mega-Creds, beide Enable-Flags fehlen) zu aktiviertem Mega-Debrid statt es still auf false zu setzen", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-store-"));
tempDirs.push(dir);
const paths = createStoragePaths(dir);
@@ -304,10 +335,72 @@ describe("settings storage", () => {
fs.writeFileSync(paths2.configFile, JSON.stringify(legacyWeb), "utf8");
const loadedWeb = loadSettings(paths2);
expect(loadedWeb.megaDebridApiEnabled).toBe(false);
expect(loadedWeb.megaDebridWebEnabled).toBe(true);
});
it("re-aktiviert KEINE bewusst deaktivierten Mega-Flags und migriert nicht ohne Mega-Creds", () => {
expect(loadedWeb.megaDebridWebEnabled).toBe(true);
});
it("migrates an explicitly Web-only legacy account even when API remains preferred", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-store-"));
tempDirs.push(dir);
const paths = createStoragePaths(dir);
fs.writeFileSync(paths.configFile, JSON.stringify({
rememberToken: true,
megaCredentials: "web-user:web-pass",
megaDebridPreferApi: true,
megaDebridApiEnabled: false,
megaDebridWebEnabled: true
}), "utf8");
const loaded = loadSettings(paths);
expect(loaded.megaDebridApiCredentials).toBe("");
expect(loaded.megaDebridWebCredentials).toBe("web-user:web-pass");
expect(loaded.megaDebridApiEnabled).toBe(false);
expect(loaded.megaDebridWebEnabled).toBe(true);
});
it("migrates legacy disabled Mega-Debrid accounts into the selected mode", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-store-"));
tempDirs.push(dir);
const paths = createStoragePaths(dir);
const accountId = getMegaDebridAccountId("disabled-user");
fs.writeFileSync(paths.configFile, JSON.stringify({
rememberToken: true,
megaCredentials: "disabled-user:disabled-pass",
megaDebridPreferApi: true,
megaDebridApiEnabled: true,
megaDebridWebEnabled: false,
megaDebridDisabledAccountIds: [accountId]
}), "utf8");
const loaded = loadSettings(paths);
expect(loaded.megaDebridApiDisabledAccountIds).toEqual([accountId]);
expect(loaded.megaDebridWebDisabledAccountIds).toEqual([]);
});
it("preserves explicit mode-specific disabled Mega-Debrid accounts", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-store-"));
tempDirs.push(dir);
const paths = createStoragePaths(dir);
const accountId = getMegaDebridAccountId("shared-user");
fs.writeFileSync(paths.configFile, JSON.stringify({
rememberToken: true,
megaDebridApiCredentials: "shared-user:api-pass",
megaDebridWebCredentials: "shared-user:web-pass",
megaDebridApiEnabled: true,
megaDebridWebEnabled: true,
megaDebridDisabledAccountIds: [accountId],
megaDebridApiDisabledAccountIds: [],
megaDebridWebDisabledAccountIds: [accountId]
}), "utf8");
const loaded = loadSettings(paths);
expect(loaded.megaDebridApiDisabledAccountIds).toEqual([]);
expect(loaded.megaDebridWebDisabledAccountIds).toEqual([accountId]);
});
it("re-aktiviert KEINE bewusst deaktivierten Mega-Flags und migriert nicht ohne Mega-Creds", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-store-"));
tempDirs.push(dir);
const paths = createStoragePaths(dir);