feat: 支持自定义 Z-Library 镜像并发布 v2.1.5
在 Z-Library 账户设置内增加镜像站点编辑入口,一行一个 HTTPS 地址, 自定义地址经 origin 规范化后优先于内置列表。镜像配置与凭据分开处理, 退出登录后仍保留;移除当前会话依赖的镜像时清理旧会话和 Cookie。 版本更新到 2.1.5,并补充存储、校验、IPC、界面与真实 Electron 保存测试。
This commit is contained in:
+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