fix(flow): harden proxy phone and checkout recovery paths

This commit is contained in:
朴圣佑
2026-05-03 13:58:33 +08:00
committed by QLHazyCoder
parent d851cc4d36
commit 29093e08db
34 changed files with 2958 additions and 293 deletions
+140
View File
@@ -451,6 +451,9 @@ const PERSISTED_SETTING_DEFAULTS = {
luckmailBaseUrl: DEFAULT_LUCKMAIL_BASE_URL,
luckmailEmailType: DEFAULT_LUCKMAIL_EMAIL_TYPE,
luckmailDomain: '',
luckmailUsedPurchases: {},
luckmailPreserveTagId: 0,
luckmailPreserveTagName: '保留',
};
const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS);
function normalizeLuckmailBaseUrl(value) {
@@ -462,6 +465,18 @@ function normalizeLuckmailEmailType(value) {
? String(value || '').trim()
: DEFAULT_LUCKMAIL_EMAIL_TYPE;
}
function normalizeLuckmailPurchaseId(value) {
const numeric = Number(value);
return Number.isFinite(numeric) && numeric > 0 ? String(Math.floor(numeric)) : '';
}
function normalizeLuckmailUsedPurchases(value) {
if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
return Object.entries(value).reduce((result, [key, used]) => {
const normalizedKey = normalizeLuckmailPurchaseId(key);
if (normalizedKey) result[normalizedKey] = Boolean(used);
return result;
}, {});
}
function resolveLegacyAutoStepDelaySeconds() {
return undefined;
}
@@ -487,6 +502,17 @@ return {
luckmailEmailType: 'ms_imap',
luckmailDomain: 'outlook.com',
});
const statePayload = api.buildPersistentSettingsPayload({
luckmailUsedPurchases: { 88: true, 99: false, bad: true },
luckmailPreserveTagId: '9',
luckmailPreserveTagName: ' 保留邮箱 ',
});
assert.deepStrictEqual(statePayload, {
luckmailUsedPurchases: { 88: true, 99: false },
luckmailPreserveTagId: 9,
luckmailPreserveTagName: '保留邮箱',
});
});
test('listLuckmailPurchasesByProject only keeps openai purchases', async () => {
@@ -879,3 +905,117 @@ return {
assert.deepStrictEqual(snapshot.usedMarker, { purchaseId: 456, used: true });
assert.equal(snapshot.logs.some((entry) => /已在本地标记为已用/.test(entry.message)), true);
});
test('setLuckmailPurchaseUsedState persists used map to storage.local so reload keeps it non-reusable', async () => {
const bundle = [
extractFunction('getLuckmailUsedPurchases'),
extractFunction('setLuckmailUsedPurchasesState'),
extractFunction('setLuckmailPurchaseUsedState'),
].join('\n');
const factory = new Function(`
let sessionState = {
luckmailUsedPurchases: { 7: true },
};
let persistentUpdates = [];
let sessionUpdates = [];
let broadcasts = [];
function normalizeLuckmailPurchaseId(value) {
const numeric = Number(value);
return Number.isFinite(numeric) && numeric > 0 ? String(Math.floor(numeric)) : '';
}
function normalizeLuckmailUsedPurchases(value) {
if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
return Object.entries(value).reduce((result, [key, used]) => {
const normalizedKey = normalizeLuckmailPurchaseId(key);
if (normalizedKey) result[normalizedKey] = Boolean(used);
return result;
}, {});
}
async function getState() {
return { ...sessionState };
}
async function setPersistentSettings(updates) {
persistentUpdates.push(updates);
}
async function setState(updates) {
sessionUpdates.push(updates);
sessionState = { ...sessionState, ...updates };
}
function broadcastDataUpdate(updates) {
broadcasts.push(updates);
}
${bundle}
return {
setLuckmailPurchaseUsedState,
snapshot() {
return { sessionState, persistentUpdates, sessionUpdates, broadcasts };
},
};
`);
const api = factory();
const result = await api.setLuckmailPurchaseUsedState(88, true);
const snapshot = api.snapshot();
assert.deepStrictEqual(result, { purchaseId: 88, used: true });
assert.deepStrictEqual(snapshot.sessionState.luckmailUsedPurchases, { 7: true, 88: true });
assert.deepStrictEqual(snapshot.persistentUpdates, [
{ luckmailUsedPurchases: { 7: true, 88: true } },
]);
assert.deepStrictEqual(snapshot.sessionUpdates, [
{ luckmailUsedPurchases: { 7: true, 88: true } },
]);
assert.deepStrictEqual(snapshot.broadcasts, [
{ luckmailUsedPurchases: { 7: true, 88: true } },
]);
});
test('setLuckmailPreserveTagInfo persists tag cache to storage.local', async () => {
const bundle = extractFunction('setLuckmailPreserveTagInfo');
const factory = new Function(`
let persistentUpdates = [];
let sessionUpdates = [];
let broadcasts = [];
const DEFAULT_LUCKMAIL_PRESERVE_TAG_NAME = '保留';
function normalizeLuckmailTags(tags) {
return (Array.isArray(tags) ? tags : []).map((tag) => ({
id: Number(tag?.id) || 0,
name: String(tag?.name || '').trim(),
})).filter((tag) => tag.id > 0 || tag.name);
}
async function setPersistentSettings(updates) {
persistentUpdates.push(updates);
}
async function setState(updates) {
sessionUpdates.push(updates);
}
function broadcastDataUpdate(updates) {
broadcasts.push(updates);
}
${bundle}
return {
setLuckmailPreserveTagInfo,
snapshot() {
return { persistentUpdates, sessionUpdates, broadcasts };
},
};
`);
const api = factory();
const result = await api.setLuckmailPreserveTagInfo({ id: '12', name: ' 保留邮箱 ' });
const expected = {
luckmailPreserveTagId: 12,
luckmailPreserveTagName: '保留邮箱',
};
assert.deepStrictEqual(result, expected);
assert.deepStrictEqual(api.snapshot().persistentUpdates, [expected]);
assert.deepStrictEqual(api.snapshot().sessionUpdates, [expected]);
assert.deepStrictEqual(api.snapshot().broadcasts, [expected]);
});