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
+34 -22
View File
@@ -226,7 +226,9 @@ export class MegaWebFallback {
private getCredentials: () => MegaCredentials;
private sessions = new Map<string, { cookie: string; setAt: number }>();
private sessions = new Map<string, { cookie: string; setAt: number }>();
private sessionGeneration = 0;
public constructor(getCredentials: () => MegaCredentials) {
this.getCredentials = getCredentials;
@@ -241,18 +243,19 @@ export class MegaWebFallback {
const creds = (account && account.login.trim() && account.password.trim())
? account
: this.getCredentials();
if (!creds.login.trim() || !creds.password.trim()) {
return null;
}
const key = creds.login.trim().toLowerCase();
return this.runExclusive(async () => {
throwIfAborted(overallSignal);
let cookie = await this.ensureSession(key, creds.login, creds.password, overallSignal);
let generated = await this.generate(link, cookie, overallSignal);
if (!generated) {
this.sessions.delete(key);
cookie = await this.ensureSession(key, creds.login, creds.password, overallSignal);
if (!creds.login.trim() || !creds.password.trim()) {
return null;
}
const key = creds.login.trim().toLowerCase();
const sessionGeneration = this.sessionGeneration;
return this.runExclusive(async () => {
throwIfAborted(overallSignal);
let cookie = await this.ensureSession(key, creds.login, creds.password, overallSignal, sessionGeneration);
let generated = await this.generate(link, cookie, overallSignal);
if (!generated) {
this.sessions.delete(key);
cookie = await this.ensureSession(key, creds.login, creds.password, overallSignal, sessionGeneration);
generated = await this.generate(link, cookie, overallSignal);
if (!generated) {
return null;
@@ -267,19 +270,28 @@ export class MegaWebFallback {
}, key, overallSignal);
}
private async ensureSession(key: string, login: string, password: string, signal?: AbortSignal): Promise<string> {
private async ensureSession(
key: string,
login: string,
password: string,
signal?: AbortSignal,
generation = this.sessionGeneration
): Promise<string> {
const existing = this.sessions.get(key);
if (existing && existing.cookie && Date.now() - existing.setAt <= 20 * 60 * 1000) {
return existing.cookie;
}
const cookie = await this.login(login, password, signal);
this.sessions.set(key, { cookie, setAt: Date.now() });
return cookie;
}
public invalidateSession(): void {
this.sessions.clear();
}
const cookie = await this.login(login, password, signal);
if (generation === this.sessionGeneration) {
this.sessions.set(key, { cookie, setAt: Date.now() });
}
return cookie;
}
public invalidateSession(): void {
this.sessionGeneration += 1;
this.sessions.clear();
}
private async runExclusive<T>(job: () => Promise<T>, key: string, signal?: AbortSignal): Promise<T> {
const queuedAt = Date.now();