feat: 支持自定义 Z-Library 镜像并发布 v2.1.5
在 Z-Library 账户设置内增加镜像站点编辑入口,一行一个 HTTPS 地址, 自定义地址经 origin 规范化后优先于内置列表。镜像配置与凭据分开处理, 退出登录后仍保留;移除当前会话依赖的镜像时清理旧会话和 Cookie。 版本更新到 2.1.5,并补充存储、校验、IPC、界面与真实 Electron 保存测试。
This commit is contained in:
@@ -123,11 +123,32 @@ function write(creds) {
|
||||
}
|
||||
|
||||
// 清除全部(含凭据)——用于"退出登录"
|
||||
// 自定义镜像是站点配置不是凭据,退出登录后要保留,否则用户每次重新登录
|
||||
// 都得重新填一遍地址,而默认域名可能全都已经失效。
|
||||
function clear() {
|
||||
sessionCreds = null;
|
||||
const mirrors = getCustomMirrors();
|
||||
for (const fp of [getFilePath(), getCredPath(), `${getFilePath()}.tmp`, `${getCredPath()}.tmp`]) {
|
||||
try { fs.unlinkSync(fp); } catch (e) { /* ignore */ }
|
||||
}
|
||||
if (mirrors.length) {
|
||||
try { atomicWrite(getFilePath(), JSON.stringify({ customMirrors: mirrors }, null, 2)); }
|
||||
catch (e) { /* 保留失败不影响退出登录 */ }
|
||||
}
|
||||
}
|
||||
|
||||
function getCustomMirrors() {
|
||||
const meta = readMeta();
|
||||
return (meta && Array.isArray(meta.customMirrors)) ? meta.customMirrors.slice() : [];
|
||||
}
|
||||
|
||||
// 只动镜像字段:与 setSession 同理,不重写密文,避免一次读取失败清空凭据
|
||||
function setCustomMirrors(list) {
|
||||
const meta = readMeta() || {};
|
||||
const next = { ...meta };
|
||||
if (Array.isArray(list) && list.length) next.customMirrors = list.slice();
|
||||
else delete next.customMirrors;
|
||||
atomicWrite(getFilePath(), JSON.stringify(next, null, 2));
|
||||
}
|
||||
|
||||
// 只清除会话令牌,保留邮箱密码以便自动重新登录
|
||||
@@ -158,4 +179,7 @@ function setSession(userId, userKey, mirror) {
|
||||
atomicWrite(getFilePath(), JSON.stringify(next, null, 2));
|
||||
}
|
||||
|
||||
module.exports = { init, read, write, clear, clearSession, hasCreds, getSession, setSession };
|
||||
module.exports = {
|
||||
init, read, write, clear, clearSession, hasCreds, getSession, setSession,
|
||||
getCustomMirrors, setCustomMirrors
|
||||
};
|
||||
|
||||
+49
-2
@@ -43,13 +43,40 @@ function requestHeaders(mirror, includeForm = false) {
|
||||
}
|
||||
|
||||
function getMirrors() {
|
||||
const custom = (auth.read() || {}).customMirrors;
|
||||
if (Array.isArray(custom) && custom.length) {
|
||||
const custom = auth.getCustomMirrors();
|
||||
if (custom.length) {
|
||||
return custom.concat(DEFAULT_MIRRORS.filter((m) => !custom.includes(m)));
|
||||
}
|
||||
return DEFAULT_MIRRORS.slice();
|
||||
}
|
||||
|
||||
const MAX_CUSTOM_MIRRORS = 20;
|
||||
|
||||
// 镜像地址来自用户输入,会被直接拼进请求 URL,因此必须按 origin 归一:
|
||||
// 带路径、查询串或凭据的地址会让后续 apiUrl() 拼出错误甚至泄露凭据的 URL。
|
||||
function normalizeMirror(value) {
|
||||
const raw = String(value || '').trim();
|
||||
if (!raw) return '';
|
||||
let url;
|
||||
try { url = new URL(raw); } catch (e) { throw new Error(`镜像地址无效:${raw}`); }
|
||||
if (url.protocol !== 'https:') throw new Error(`镜像必须使用 HTTPS:${raw}`);
|
||||
if (url.username || url.password) throw new Error(`镜像地址不能包含账号信息:${raw}`);
|
||||
return url.origin;
|
||||
}
|
||||
|
||||
function normalizeMirrors(list) {
|
||||
if (!Array.isArray(list)) throw new Error('镜像列表格式无效');
|
||||
if (list.length > MAX_CUSTOM_MIRRORS) {
|
||||
throw new Error(`最多只能保存 ${MAX_CUSTOM_MIRRORS} 个镜像`);
|
||||
}
|
||||
const out = [];
|
||||
for (const item of list) {
|
||||
const origin = normalizeMirror(item);
|
||||
if (origin && !out.includes(origin)) out.push(origin);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function apiUrl(base, path, params = {}) {
|
||||
const u = new URL(base + path);
|
||||
for (const [k, v] of Object.entries(params)) {
|
||||
@@ -379,5 +406,25 @@ module.exports = {
|
||||
return auth.hasCreds();
|
||||
},
|
||||
|
||||
// 默认域名变动频繁且会整批失效,用户需要能自己补上可用地址而不必等新版本
|
||||
getMirrorConfig() {
|
||||
return { custom: auth.getCustomMirrors(), defaults: DEFAULT_MIRRORS.slice() };
|
||||
},
|
||||
|
||||
setMirrors(list) {
|
||||
const next = normalizeMirrors(list);
|
||||
const previous = auth.getCustomMirrors();
|
||||
auth.setCustomMirrors(next);
|
||||
// 换镜像后旧会话绑定的域名可能已不在列表里,留着会一直优先命中失效地址
|
||||
const session = auth.getSession();
|
||||
if (session && session.mirror && !getMirrors().includes(session.mirror)) {
|
||||
auth.clearSession();
|
||||
}
|
||||
for (const mirror of previous) {
|
||||
if (!next.includes(mirror)) clearCookies(mirror);
|
||||
}
|
||||
return { custom: next, defaults: DEFAULT_MIRRORS.slice() };
|
||||
},
|
||||
|
||||
setLoginTransport
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user