fix(accounts): separate Debrid-Link username and email

Persist the username and email returned by the Debrid-Link account endpoint as distinct identity fields and project them into their matching account-table columns. Migrate cached legacy statuses that stored a username in the email field, preserve both fields through renderer validation and sanitization, and cover API responses with and without an email address.
This commit is contained in:
Sucukdeluxe
2026-08-15 02:11:59 +02:00
parent 90fc77f203
commit d91e2a4ab9
10 changed files with 99 additions and 42 deletions
+17 -8
View File
@@ -81,14 +81,23 @@ describe("checkMegaDebridAccount", () => {
});
describe("checkDebridLinkKey", () => {
it("reports valid + premium from premiumLeft seconds", async () => {
const premiumLeft = 60 * 24 * 60 * 60;
mockFetchOnce(200, { success: true, value: { username: "u", accountType: 1, premiumLeft } });
const st = await checkDebridLinkKey(debridLinkKey(), undefined, NOW);
expect(st.valid).toBe(true);
expect(st.isPremium).toBe(true);
expect(st.premiumUntilMs).toBe(NOW + premiumLeft * 1000);
});
it("reports valid + premium from premiumLeft seconds", async () => {
const premiumLeft = 60 * 24 * 60 * 60;
mockFetchOnce(200, { success: true, value: { username: "u", email: "u@example.test", accountType: 1, premiumLeft } });
const st = await checkDebridLinkKey(debridLinkKey(), undefined, NOW);
expect(st.valid).toBe(true);
expect(st.isPremium).toBe(true);
expect(st.premiumUntilMs).toBe(NOW + premiumLeft * 1000);
expect(st.username).toBe("u");
expect(st.email).toBe("u@example.test");
});
it("does not present a Debrid-Link username as an email address", async () => {
mockFetchOnce(200, { success: true, value: { username: "xsucukde5", accountType: 0, premiumLeft: 0 } });
const st = await checkDebridLinkKey(debridLinkKey(), undefined, NOW);
expect(st.username).toBe("xsucukde5");
expect(st.email).toBeUndefined();
});
it("reports valid but free (premiumLeft 0, accountType 0)", async () => {
mockFetchOnce(200, { success: true, value: { username: "u", accountType: 0, premiumLeft: 0 } });
+2
View File
@@ -15,6 +15,7 @@ describe("renderer settings validation", () => {
valid: true,
isPremium: true,
premiumUntilMs: null,
username: "web-user",
message: "Premium aktiv",
checkedAt: 1_700_000_000_000
}
@@ -22,6 +23,7 @@ describe("renderer settings validation", () => {
const rendererSettings = createRendererSettings(current);
expect(rendererSettings.debridAccountStatuses["svc-realdebrid"]).toHaveProperty("email", undefined);
expect(rendererSettings.debridAccountStatuses["svc-realdebrid"]).toHaveProperty("username", "web-user");
const validated = validateRendererSettingsUpdate(rendererSettings, current);
expect(validated.debridAccountStatuses).toEqual(rendererSettings.debridAccountStatuses);
+4 -2
View File
@@ -153,10 +153,10 @@ function accountSources(): AccountRowSource[] {
mode: "API-Key",
icon: "./provider-icons/debrid-link.ico",
enabled: true,
status: { state: "free", message: "Free Account", premiumUntilMs: null },
status: { state: "free", message: "Free Account", premiumUntilMs: null, username: "xsucukde5" },
dailyLimitBytes: 0,
dailyUsageBytes: 0,
username: "free-user",
username: "",
credentialKind: "api-key",
canCheck: true
},
@@ -352,6 +352,8 @@ describe("settings model", () => {
expect(rows[0].username).toBe("stored-user");
expect(rows[0].email).toBe("verified@example.test");
expect(rows[0].credential).toBe("••••••");
expect(rows[1].username).toBe("xsucukde5");
expect(rows[1].email).toBe("—");
expect(rows[1].credential).toBe("API-Key");
expect(rows.map((row) => row.status.tone)).toEqual(["ok", "free", "invalid", "unknown", "disabled"]);
expect(rows.map((row) => row.status.text)).toEqual([
+29 -1
View File
@@ -673,7 +673,8 @@ describe("settings storage", () => {
valid: true,
isPremium: true,
premiumUntilMs: checkedAt + 1000,
email: "web-user",
username: "web-user",
email: "w***r@example.test",
message: "Premium aktiv",
checkedAt
}
@@ -684,9 +685,36 @@ describe("settings storage", () => {
accountId: "svc-realdebrid",
provider: "realdebrid",
valid: true,
username: "web-user",
email: "w***r@example.test",
checkedAt
});
});
it("migrates a legacy Debrid-Link username out of the email field", () => {
const [key] = parseDebridLinkApiKeys("dl-key-one");
const normalized = normalizeSettings({
...defaultSettings(),
debridLinkApiKeys: "dl-key-one",
debridAccountStatuses: {
[key.id]: {
accountId: key.id,
provider: "debridlink",
label: "Key 1",
maskedLogin: key.masked,
valid: true,
isPremium: false,
premiumUntilMs: 0,
email: "xsucukde5",
message: "Kein Premium (Free)",
checkedAt: Date.now()
}
}
});
expect(normalized.debridAccountStatuses[key.id].username).toBe("xsucukde5");
expect(normalized.debridAccountStatuses[key.id].email).toBeUndefined();
});
it("defaults AllDebrid web login to disabled and normalizes the flag", () => {
expect(defaultSettings().allDebridUseWebLogin).toBe(false);