Archived
chore: 清理前端与容器脚本中指向已删台账的过时编号
模板/样式/脚本的注释里散落着「第 N 条」「F59」「P3-43」这类编号,它们指向的 修复验收台账与交接文档已在本轮删除——留着既检索不到、也对不上号,还让后来读 注释的人以为存在一份可查的编号清单。本次只动注释,正文零改动。 覆盖范围(全仓一致): - templates/index.html 163 处、static/site_auth.js 55 处,共 218 处编号整段摘除 (含「第 N 条(Px):」前缀与「(Fxx 短标签)」括号)。 - 容器/配置 16 处:docker-compose.yml、entrypoint.sh(F59 / 第 93 条 / 第 97 条)、 Dockerfile(T7 / 第 97 条)、.dockerignore(T1 / 第 97 条 / 第 55 条 / P2-9)、 .env.example(F59 / 第 100 条 / 「详见交接文档 F11」这条失效指引)、.gitignore(P3-41)。 - Python 4 处:core/config.py(D-1)、services/tasks.py(O-5×2)、services/_shared.py(O-5) ——上一轮 Python 侧清理的漏网。 为什么规则要收窄(两类误伤,实测踩到): 1. 标签首字母不能用泛化的 [A-Z]:X5 是 QQ 浏览器内核、ES5 是 JS 版本,都不是编号。 泛化后会把「微信 XWeb/X5、QQ 内置」改成「微信 XWeb/、QQ 内置」,把 「Chromium <87 的老 X5 不识别 inset」改成「老 不识别」。现按本仓真实用过的 任务族白名单收窄(A B C D F O P R S T U),X5/ES5 原样保留。 2. 括号内「编号 + 长说明」不能整段删:「(F51 只防了切歌,这里补同歌翻页)」 整段删掉会连说明一起丢;现只摘编号、保留说明文字。 为什么同步改黄金契约快照:题头注释属于模板内容,改了注释首页字节数必然变 (466261 → 464333),tests/golden/responses.json 的 index 条目按既定流程刷新 html_len/html_sha256;并在 tests/test_golden.py 补一段说明,写清该条目记的是 「当前模板」而非 Flask 期数值、以及有意改模板后如何重算 (html_len = 文件字节数 - 1,sha256 = 文件去掉末尾换行后的哈希)。 验证(全部通过): - 与 HEAD 比对:剥离全部注释后的正文逐行一致(唯一差异是删掉了一处行尾空注释), 行数不变、每行半角括号/引号数量不变 → 代码与字符串零改动; - node --check 通过(静态 JS + index.html 内联脚本),<style> 块花括号平衡; - 真实 uvicorn 冒烟:/health /user/status /api/topen → 200、/admin/site/backup/list → 403, /static/site_auth.js 响应体与本地文件 sha1 相同,/ 无「第 N 条」残留; - ruff All checks passed;pyright 0 errors / 0 warnings;pytest 144 passed。
This commit is contained in:
1 parent
6288a98ecc
commit
da3d013e9c
13 files changed
+246
-242
No files matched your search
+56
-56
@@ -5,7 +5,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* P2-11:Promise.finally 老内核 polyfill(与 index.html 同一份标准实现——
|
||||
/* Promise.finally 老内核 polyfill(与 index.html 同一份标准实现——
|
||||
index.html 的脚本先加载,这里再兜底一次确保独立引用时也安全) */
|
||||
if (!Promise.prototype.finally) {
|
||||
Promise.prototype.finally = function (cb) {
|
||||
@@ -71,7 +71,7 @@
|
||||
return r.json().then(function (d) { if (timer) clearTimeout(timer); return d; },
|
||||
function (e) { if (timer) clearTimeout(timer); throw e; });
|
||||
}).catch(function () {
|
||||
// F9:默认失败兜底——网络断/超时/非 JSON 响应不再产生 unhandledrejection
|
||||
// 默认失败兜底——网络断/超时/非 JSON 响应不再产生 unhandledrejection
|
||||
// 噪音(喂 fe_error 上报通道),统一转为标准失败对象走调用方既有的 toast
|
||||
if (timer) clearTimeout(timer);
|
||||
return { status: 0, success: false, message: '网络异常,请检查连接后重试' };
|
||||
@@ -92,9 +92,9 @@
|
||||
|
||||
/* ---------------- 设备标识 ---------------- */
|
||||
function getDeviceId() {
|
||||
/* F-7(第 79 条):与 index.html 的 getDeviceId 统一为同一键/同一值——
|
||||
/* 与 index.html 的 getDeviceId 统一为同一键/同一值——
|
||||
注册指纹与下载任务/心跳的设备号不再各走一套;
|
||||
内存缓存兜底 localStorage 不可写时同一页面内保持稳定(F108 同款)。 */
|
||||
内存缓存兜底 localStorage 不可写时同一页面内保持稳定。 */
|
||||
if (window.__siteDeviceIdCache) return window.__siteDeviceIdCache;
|
||||
var KEY = 'netease_device_id';
|
||||
var d = '';
|
||||
@@ -111,8 +111,8 @@ function getDeviceId() {
|
||||
var user = null;
|
||||
var durationText = '';
|
||||
var ann = null;
|
||||
var smtpOk = false; // F-8.2:fail-closed——/auth/me 返回前不显示注册入口(防止未配置 SMTP 的窗口期误开)
|
||||
var registerCredits = 3; // F-6.1:注册赠送次数由 /auth/me 下发,文案不再硬编码
|
||||
var smtpOk = false; // fail-closed——/auth/me 返回前不显示注册入口(防止未配置 SMTP 的窗口期误开)
|
||||
var registerCredits = 3; // 注册赠送次数由 /auth/me 下发,文案不再硬编码
|
||||
|
||||
/* ---------------- 弹窗 ---------------- */
|
||||
function openModal(id) { var m = $('#' + id); if (m) m.classList.add('show'); }
|
||||
@@ -129,9 +129,9 @@ function getDeviceId() {
|
||||
['goto-register', 'goto-register2'].forEach(function (id) {
|
||||
var a = $('#' + id); if (a) a.hidden = !showRegister;
|
||||
});
|
||||
// F-8.1:两个弹窗(登录/注册)里的试用提示都要受管——原 querySelector 只命中第一个
|
||||
// 两个弹窗(登录/注册)里的试用提示都要受管——原 querySelector 只命中第一个
|
||||
$$('.auth-trial-box').forEach(function (box) { box.hidden = !showRegister; });
|
||||
$$('.reg-gift-num').forEach(function (el) { el.textContent = registerCredits; }); // F-6.1
|
||||
$$('.reg-gift-num').forEach(function (el) { el.textContent = registerCredits; });
|
||||
var guest = $('#auth-guest-area'), uarea = $('#auth-user-area');
|
||||
if (guest && uarea) {
|
||||
if (user) {
|
||||
@@ -163,12 +163,12 @@ function getDeviceId() {
|
||||
durationText = d.duration_text || '';
|
||||
ann = d.announcement || null;
|
||||
smtpOk = !!d.smtp_configured;
|
||||
// F-6.1:注册赠送次数动态下发(/auth/me),文案不再硬编码
|
||||
// 注册赠送次数动态下发(/auth/me),文案不再硬编码
|
||||
if (typeof d.register_credits === 'number') registerCredits = d.register_credits;
|
||||
renderAuthArea();
|
||||
renderUserCenter();
|
||||
maybeShowAnnouncement();
|
||||
// A1:确认登录后再恢复下载任务(游客调任务接口会 401 误弹登录框)
|
||||
// 确认登录后再恢复下载任务(游客调任务接口会 401 误弹登录框)
|
||||
if (user && typeof window.SITE_TASKS_BOOTSTRAP === 'function') {
|
||||
try { window.SITE_TASKS_BOOTSTRAP(); } catch (e) {}
|
||||
}
|
||||
@@ -190,14 +190,14 @@ function getDeviceId() {
|
||||
showExpiredModal();
|
||||
return;
|
||||
}
|
||||
// F53:会话失效时清掉内存里的旧用户状态再引导登录
|
||||
// 会话失效时清掉内存里的旧用户状态再引导登录
|
||||
//(原实现保留旧 user,页面继续显示"已登录"却处处 401)
|
||||
user = null;
|
||||
durationText = '';
|
||||
renderAuthArea();
|
||||
renderUserCenter();
|
||||
authToastOnce(msg || '登录已失效,请重新登录');
|
||||
// 第 87 条:登录弹窗已打开且正处于找回/两步验证视图时,不复位视图——
|
||||
// 登录弹窗已打开且正处于找回/两步验证视图时,不复位视图——
|
||||
// 未登录期间后台任务轮询/心跳会持续吃 401 反复进入本函数,若每次都把
|
||||
// 弹窗拽回登录视图,管理员正在输入的验证码会被打断、票据被清空
|
||||
var _lm = document.getElementById('login-modal');
|
||||
@@ -209,7 +209,7 @@ function getDeviceId() {
|
||||
hideForgotView();
|
||||
hideAdmin2faView();
|
||||
}
|
||||
// 第 98 条:会话失效弹登录窗前,先关掉可能叠开的注册弹窗(双遮罩错乱),
|
||||
// 会话失效弹登录窗前,先关掉可能叠开的注册弹窗(双遮罩错乱),
|
||||
// 已 show 则短路——401 风暴(1.5s 轮询)期间不反复重开
|
||||
var _reg = document.getElementById('register-modal');
|
||||
if (_reg && _reg.classList.contains('show')) closeModal('register-modal');
|
||||
@@ -265,14 +265,14 @@ function getDeviceId() {
|
||||
durationText = d.duration_text || '';
|
||||
renderAuthArea();
|
||||
renderUserCenter();
|
||||
// A1:登录成功后立即恢复本设备的进行中任务
|
||||
// 登录成功后立即恢复本设备的进行中任务
|
||||
if (user && typeof window.SITE_TASKS_BOOTSTRAP === 'function') {
|
||||
try { window.SITE_TASKS_BOOTSTRAP(); } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------- 登录 / 注册弹窗 ---------------- */
|
||||
// 第 81 条:注册唯一入口——导航按钮与登录弹窗两处链接(全站仅有的 3 条打开
|
||||
// 注册唯一入口——导航按钮与登录弹窗两处链接(全站仅有的 3 条打开
|
||||
// 路径)统一经此:未同意协议 → cookieGate('register') 弹「隐私与协议设置」
|
||||
// 注册模式(16 秒强制阅读计时)并中止;已同意 → 直接进注册弹窗。
|
||||
// 提交侧 doRegister 另有 fail-closed 兜底闸,程序化打开也绕不过。
|
||||
@@ -294,7 +294,7 @@ function getDeviceId() {
|
||||
['login-close', 'register-close', 'announce-close'].forEach(function (id) {
|
||||
var b = $('#' + id);
|
||||
if (b) b.addEventListener('click', function () {
|
||||
if (id.indexOf('login') === 0 && window.resetAuthViews) window.resetAuthViews(); // 第 87 条:关闭侧复位
|
||||
if (id.indexOf('login') === 0 && window.resetAuthViews) window.resetAuthViews(); // 关闭侧复位
|
||||
closeModal(id.indexOf('login') === 0 ? 'login-modal' : id.indexOf('register') === 0 ? 'register-modal' : 'announce-modal');
|
||||
});
|
||||
});
|
||||
@@ -302,7 +302,7 @@ function getDeviceId() {
|
||||
var m = $('#' + p[0]);
|
||||
if (m) m.addEventListener('click', function (e) {
|
||||
if (e.target === m) {
|
||||
if (p[0] === 'login-modal' && window.resetAuthViews) window.resetAuthViews(); // 第 87 条:关闭侧复位
|
||||
if (p[0] === 'login-modal' && window.resetAuthViews) window.resetAuthViews(); // 关闭侧复位
|
||||
closeModal(p[0]);
|
||||
}
|
||||
});
|
||||
@@ -312,12 +312,12 @@ function getDeviceId() {
|
||||
if (g2) g2.addEventListener('click', function () { closeModal('login-modal'); openRegisterFlow(); });
|
||||
var gl1 = $('#goto-login');
|
||||
if (gl1) gl1.addEventListener('click', function () { closeModal('register-modal'); hideForgotView(); hideAdmin2faView(); openModal('login-modal'); });
|
||||
// 第 86 条:找回密码入口/返回(F5.1 只交付 DOM 漏了绑定)
|
||||
// 找回密码入口/返回(只交付 DOM 漏了绑定)
|
||||
var gf = $('#goto-forgot');
|
||||
if (gf) gf.addEventListener('click', showForgotView);
|
||||
var fbl = $('#forgot-back-login');
|
||||
if (fbl) fbl.addEventListener('click', hideForgotView);
|
||||
// 第 86 条:管理员两步验证视图的返回链接
|
||||
// 管理员两步验证视图的返回链接
|
||||
var a2b = $('#a2fa-back');
|
||||
if (a2b) a2b.addEventListener('click', hideAdmin2faView);
|
||||
|
||||
@@ -334,7 +334,7 @@ function getDeviceId() {
|
||||
api('/auth/login', { username: username, password: password })
|
||||
.then(function (data) {
|
||||
if (data.status !== 200) throw new Error(data.message || '登录失败');
|
||||
// 第 86 条:管理员密码验证通过——转两步验证视图(服务端未建会话)
|
||||
// 管理员密码验证通过——转两步验证视图(服务端未建会话)
|
||||
if (data.data && data.data.admin_2fa) {
|
||||
showAdmin2faView(data.data.ticket, data.data.email_masked);
|
||||
toast('密码验证通过,请完成邮箱验证', 'success');
|
||||
@@ -358,7 +358,7 @@ function getDeviceId() {
|
||||
/* ---------------- 注册 ---------------- */
|
||||
var _REG_EMAIL_RE = /^[\w.%+-]+@[\w.-]+\.[A-Za-z]{2,}$/;
|
||||
var _REG_NAME_RE = /^[A-Za-z0-9_\u4e00-\u9fa5]{3,20}$/;
|
||||
// F5.1:注册弹窗内常驻错误行(长文案 2.6s toast 看不完)
|
||||
// 注册弹窗内常驻错误行(长文案 2.6s toast 看不完)
|
||||
function regShowError(msg) {
|
||||
toast(msg, 'error');
|
||||
var line = $('#reg-error-line');
|
||||
@@ -391,8 +391,8 @@ function getDeviceId() {
|
||||
}
|
||||
|
||||
/* ---------------- 找回密码 ---------------- */
|
||||
// 第 84 条:F5.1 只交付了找回视图 DOM 未实现 JS——此处补齐全链路。
|
||||
// 后端 /auth/forgot_password(第 79 条)已就绪:验证码原子消费、防枚举
|
||||
// 只交付了找回视图 DOM 未实现 JS——此处补齐全链路。
|
||||
// 后端 /auth/forgot_password 已就绪:验证码原子消费、防枚举
|
||||
// 统一文案、login_seq 踢全会话、5 次/分/IP 限流;发码复用 /auth/send_code。
|
||||
function forgotShowError(msg) {
|
||||
toast(msg, 'error');
|
||||
@@ -470,7 +470,7 @@ function getDeviceId() {
|
||||
.finally(function () { btn.disabled = false; btn.textContent = '重置密码'; });
|
||||
}
|
||||
|
||||
/* ---------------- 管理员两步验证(第 86 条) ----------------
|
||||
/* ---------------- 管理员两步验证 ----------------
|
||||
* Step1 用户名+密码 → 服务端签发票据(不建会话);本视图完成
|
||||
* Step2 绑定邮箱比对(触发发码)与 Step3 验证码登录。普通用户不经过此流程。 */
|
||||
var _a2faTicket = '';
|
||||
@@ -495,7 +495,7 @@ function getDeviceId() {
|
||||
_a2faTicket = ticket || '';
|
||||
var mask = $('#a2fa-email-mask');
|
||||
if (mask) mask.textContent = emailMask || '-';
|
||||
// 第 87 条:清掉上次会话残留的输入(错码/旧邮箱不再被原样重复提交)
|
||||
// 清掉上次会话残留的输入(错码/旧邮箱不再被原样重复提交)
|
||||
var ae = $('#a2fa-email'), ac = $('#a2fa-code');
|
||||
if (ae) ae.value = '';
|
||||
if (ac) ac.value = '';
|
||||
@@ -515,7 +515,7 @@ function getDeviceId() {
|
||||
a2faClearError();
|
||||
a2faStopTimer();
|
||||
}
|
||||
// 第 87 条:关闭侧统一复位——登录弹窗的 X/遮罩/Escape/doLogout 关闭路径都调用,
|
||||
// 关闭侧统一复位——登录弹窗的 X/遮罩/Escape/doLogout 关闭路径都调用,
|
||||
// 不再只依赖"打开侧纪律";同时供 index.html 的 Escape 通用分支使用
|
||||
window.resetAuthViews = function () { hideForgotView(); hideAdmin2faView(); };
|
||||
function doA2faSendCode() {
|
||||
@@ -527,7 +527,7 @@ function getDeviceId() {
|
||||
api('/auth/admin_login/email', { ticket: _a2faTicket, email: email })
|
||||
.then(function (data) {
|
||||
if (data.status === 401) {
|
||||
// 第 87 条:先复位视图再提示——错误行随视图隐藏,此处 toast 是唯一可达提示
|
||||
// 先复位视图再提示——错误行随视图隐藏,此处 toast 是唯一可达提示
|
||||
hideAdmin2faView();
|
||||
a2faShowError(data.message || '请重新登录');
|
||||
return;
|
||||
@@ -558,7 +558,7 @@ function getDeviceId() {
|
||||
api('/auth/admin_login/verify', { ticket: _a2faTicket, code: code })
|
||||
.then(function (data) {
|
||||
if (data.status === 401) {
|
||||
// 第 87 条:先复位视图再提示(同上,错误行随视图隐藏)
|
||||
// 先复位视图再提示(同上,错误行随视图隐藏)
|
||||
hideAdmin2faView();
|
||||
a2faShowError(data.message || '请重新登录');
|
||||
return;
|
||||
@@ -582,7 +582,7 @@ function getDeviceId() {
|
||||
var email = ($('#reg-email') || {}).value || '';
|
||||
var code = ($('#reg-code') || {}).value || '';
|
||||
regClearError();
|
||||
// 第 81 条:提交侧兜底闸(入口已统一走 openRegisterFlow,此处防任何程序化
|
||||
// 提交侧兜底闸(入口已统一走 openRegisterFlow,此处防任何程序化
|
||||
// 打开路径绕过)。fail-closed:协议闸缺失视为未同意。注册语境只查协议同意
|
||||
// (功能性 Cookie 不拦注册,拦解析/下载时另有提示);未同意时闸内已自动
|
||||
// 弹「隐私与协议设置」注册模式并 toast,此处不再重复提示
|
||||
@@ -591,7 +591,7 @@ function getDeviceId() {
|
||||
return;
|
||||
}
|
||||
if (!window.SITE_COOKIE_GATE('register')) return;
|
||||
// F1.1:前端即时校验(口径与后端一致,省一次网络往返)
|
||||
// 前端即时校验(口径与后端一致,省一次网络往返)
|
||||
if (!username || !password || !email || !code) { regShowError('请填写完整注册信息'); return; }
|
||||
if (!_REG_NAME_RE.test(username)) { regShowError('用户名需 3-20 位,仅支持中文、字母、数字、下划线,且不能纯下划线'); return; }
|
||||
if (!_REG_EMAIL_RE.test(email.trim())) { regShowError('邮箱格式不正确'); return; }
|
||||
@@ -619,10 +619,10 @@ function getDeviceId() {
|
||||
api('/auth/logout', {}).then(function () {
|
||||
user = null; durationText = '';
|
||||
renderAuthArea();
|
||||
renderUserCenter(); // 第 83 条:同步个人中心 gate/content 显隐,与其他登出路径一致
|
||||
if (window.resetAuthViews) window.resetAuthViews(); // 第 87 条:登出复位登录弹窗视图
|
||||
renderUserCenter(); // 同步个人中心 gate/content 显隐,与其他登出路径一致
|
||||
if (window.resetAuthViews) window.resetAuthViews(); // 登出复位登录弹窗视图
|
||||
closeModal('register-modal'); closeModal('login-modal');
|
||||
// 第 102 条(U15 拍板):退出连带清除个人网易云登录(index.html 注册的钩子:
|
||||
// 退出连带清除个人网易云登录(index.html 注册的钩子:
|
||||
// 清 localStorage 的个人 Cookie/解析账号选择并复位账号栏)
|
||||
if (typeof window.SITE_ON_LOGOUT === 'function') {
|
||||
try { window.SITE_ON_LOGOUT(); } catch (e) {}
|
||||
@@ -680,7 +680,7 @@ function getDeviceId() {
|
||||
.then(function (data) {
|
||||
if (data.status !== 200) throw new Error(data.message || '兑换失败');
|
||||
durationText = (data.data && data.data.duration_text) || durationText;
|
||||
// F69:同步更新内存余额(批量下载预检读的是 user.duration_seconds,
|
||||
// 同步更新内存余额(批量下载预检读的是 user.duration_seconds,
|
||||
// 原实现只改显示文字,预检仍用充值前的旧值)
|
||||
if (user && data.data && typeof data.data.duration_seconds === 'number') {
|
||||
user.duration_seconds = data.data.duration_seconds;
|
||||
@@ -700,7 +700,7 @@ function getDeviceId() {
|
||||
|
||||
/* ---------------- 公告 ---------------- */
|
||||
function maybeShowAnnouncement(force) {
|
||||
// F81:手动"查看公告"(force=true)无条件展示;
|
||||
// 手动"查看公告"(force=true)无条件展示;
|
||||
// 自动弹出仍按 popup_mode 与 once 已读版本判断
|
||||
if (!force && (!ann || !ann.content || ann.popup_mode === 'off')) return;
|
||||
if (!force && ann && ann.popup_mode === 'once') {
|
||||
@@ -717,7 +717,7 @@ function getDeviceId() {
|
||||
var t = $('#announce-title'), c = $('#announce-content');
|
||||
if (t) t.textContent = ann.title || '系统公告';
|
||||
if (c) c.innerHTML = String(ann.content).split('\n').map(function (l) { return escapeHtml(l); }).join('<br>');
|
||||
// 第 98 条:首访 Cookie 协议窗显示中不叠加公告弹窗(双遮罩错乱);
|
||||
// 首访 Cookie 协议窗显示中不叠加公告弹窗(双遮罩错乱);
|
||||
// 跳过时不记已读,下次进入页面再弹
|
||||
var _cm = document.getElementById('cookie-modal');
|
||||
if (_cm && _cm.classList.contains('show')) return;
|
||||
@@ -747,7 +747,7 @@ function getDeviceId() {
|
||||
|
||||
function uaBindSide() {
|
||||
$$('#admin-side .admin-side-item[data-view]').forEach(function (item) {
|
||||
// 第 97 条(P3):侧栏项补键盘语义(F109 同型——tabindex/role + Enter/空格激活)
|
||||
// 侧栏项补键盘语义(同型——tabindex/role + Enter/空格激活)
|
||||
item.setAttribute('tabindex', '0');
|
||||
item.setAttribute('role', 'button');
|
||||
item.addEventListener('click', function () {
|
||||
@@ -767,7 +767,7 @@ function getDeviceId() {
|
||||
});
|
||||
var lo = $('#ua-logout');
|
||||
if (lo) lo.addEventListener('click', doLogout);
|
||||
// 第 97 条(P3):返回/退出两个无 data-view 的侧栏项同样补键盘语义
|
||||
// 返回/退出两个无 data-view 的侧栏项同样补键盘语义
|
||||
[back, lo].forEach(function (el) {
|
||||
if (!el) return;
|
||||
el.setAttribute('tabindex', '0');
|
||||
@@ -776,7 +776,7 @@ function getDeviceId() {
|
||||
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); el.click(); }
|
||||
});
|
||||
});
|
||||
/* ---- 播放 API 密钥(第 78 条) ---- */
|
||||
/* ---- 播放 API 密钥 ---- */
|
||||
var pkShow = $('#ua-playkey-show');
|
||||
var pkInput = $('#ua-playkey-value');
|
||||
function pkCopyToClipboard(text) {
|
||||
@@ -891,12 +891,12 @@ function getDeviceId() {
|
||||
var isAdmin = u.role === 'admin';
|
||||
var disabled = (u.status === 0);
|
||||
var banned = (u.status === 2);
|
||||
// 第 97 条(P3):数值字段同过转义(纵深防御——后端口径若变为字符串
|
||||
// 数值字段同过转义(纵深防御——后端口径若变为字符串
|
||||
// 不再形成管理后台存储型 XSS 面,与同表 username/email 口径对齐)
|
||||
var uid = escapeHtml(String(u.id));
|
||||
var dur = escapeHtml(String(u.duration_seconds));
|
||||
var cred = u.duration_seconds < 0 ? '不限' : (dur + ' 次');
|
||||
var dev = [u.register_ip, u.trial_device].filter(Boolean).join(' · ') || '-'; // R5-79:IP 优先显示
|
||||
var dev = [u.register_ip, u.trial_device].filter(Boolean).join(' · ') || '-'; // IP 优先显示
|
||||
if (u.last_province) dev += ' · ' + u.last_province;
|
||||
return '<tr' + ((disabled || banned) ? ' style="opacity:.55;"' : '') + '>' +
|
||||
'<td>' + uid + '</td>' +
|
||||
@@ -925,7 +925,7 @@ function getDeviceId() {
|
||||
.then(function (d) { toast(d.message || (d.status === 200 ? '已修改' : '修改失败'), d.status === 200 ? 'success' : 'error'); if (d.status === 200) uaLoadUsers(); });
|
||||
});
|
||||
});
|
||||
// T3:管理员自助改密(需验证旧密码;改完其他设备全部下线)
|
||||
// 管理员自助改密(需验证旧密码;改完其他设备全部下线)
|
||||
$$('[data-uself]', body).forEach(function (b) {
|
||||
b.addEventListener('click', function () {
|
||||
var oldpwd = prompt('请输入当前密码:', '');
|
||||
@@ -990,7 +990,7 @@ function getDeviceId() {
|
||||
}
|
||||
|
||||
/* ---- 全局 Cookie ---- */
|
||||
var uaQrTimer = null, uaUnikey = '', uaQrGen = 0; // T2:轮询代次令牌(删除 Cookie 后作废旧轮询)
|
||||
var uaQrTimer = null, uaUnikey = '', uaQrGen = 0; // 轮询代次令牌(删除 Cookie 后作废旧轮询)
|
||||
function uaLoadCookieStatus() {
|
||||
api('/admin/cookie/status', {}).then(function (data) {
|
||||
var el = $('#ua-cookie-status');
|
||||
@@ -1013,13 +1013,13 @@ function getDeviceId() {
|
||||
}
|
||||
if (tip) tip.textContent = '请使用网易云音乐手机 APP 扫码';
|
||||
if (uaQrTimer) clearInterval(uaQrTimer);
|
||||
uaQrGen++; // T2:新轮询代次
|
||||
uaQrGen++; // 新轮询代次
|
||||
uaQrTimer = setInterval(uaQrPoll, 3000);
|
||||
});
|
||||
}
|
||||
function uaQrPoll() {
|
||||
if (!uaUnikey) { if (uaQrTimer) clearInterval(uaQrTimer); return; }
|
||||
var myGen = uaQrGen; // T2:本次轮询所属代次
|
||||
var myGen = uaQrGen; // 本次轮询所属代次
|
||||
api('/admin/qr/check', { unikey: uaUnikey }).then(function (data) {
|
||||
if (myGen !== uaQrGen) return; // 期间已删除 Cookie/重开扫码,丢弃迟到响应
|
||||
var tip = $('#ua-qr-tip');
|
||||
@@ -1045,7 +1045,7 @@ function getDeviceId() {
|
||||
function uaCookieDelete() {
|
||||
if (!window.confirm('确定删除全局 Cookie?删除后需重新扫码登录。')) return;
|
||||
api('/admin/cookie/delete', {}).then(function (data) {
|
||||
// T2:作废在途扫码轮询——迟到的 803 成功响应不得把刚删除的 Cookie 写回
|
||||
// 作废在途扫码轮询——迟到的 803 成功响应不得把刚删除的 Cookie 写回
|
||||
if (uaQrTimer) clearInterval(uaQrTimer);
|
||||
uaUnikey = '';
|
||||
uaQrGen++;
|
||||
@@ -1099,7 +1099,7 @@ function getDeviceId() {
|
||||
var total = (data.data && data.data.total) || 0;
|
||||
var pages = (data.data && data.data.pages) || 1;
|
||||
uaLogPage = (data.data && data.data.page) || 1;
|
||||
window._uaLogPages = pages; // P3-43:next 按钮按真实页数封顶
|
||||
window._uaLogPages = pages; // next 按钮按真实页数封顶
|
||||
var el;
|
||||
if ((el = $('#ua-log-page'))) el.textContent = uaLogPage;
|
||||
if ((el = $('#ua-log-pages'))) el.textContent = pages;
|
||||
@@ -1147,7 +1147,7 @@ function getDeviceId() {
|
||||
uaLoadCards(0);
|
||||
});
|
||||
}
|
||||
// 第 81 条:卡密管理操作列(禁用/启用/删除)+ 分页(F85 后端已支持,
|
||||
// 卡密管理操作列(禁用/启用/删除)+ 分页(后端已支持,
|
||||
// 不分页则超出最新 100 张的旧卡密在界面上永远管理不到)
|
||||
var _uaCardPage = 0;
|
||||
var _uaCardTotal = 0;
|
||||
@@ -1312,7 +1312,7 @@ function getDeviceId() {
|
||||
uaLoadDb();
|
||||
})
|
||||
.catch(function (err) {
|
||||
// 第 97 条(P2):全站唯一绕过统一请求封装的调用此前无失败兜底——
|
||||
// 全站唯一绕过统一请求封装的调用此前无失败兜底——
|
||||
// 网络失败/非 JSON 响应时管理员毫无感知还制造 unhandledrejection 噪音
|
||||
toast((err && err.message) || '备份上传失败,请检查网络后重试', 'error');
|
||||
});
|
||||
@@ -1323,7 +1323,7 @@ function getDeviceId() {
|
||||
bindAuthModal();
|
||||
var s1 = $('#login-submit'); if (s1) s1.addEventListener('click', doLogin);
|
||||
var s2 = $('#login-password'); if (s2) s2.addEventListener('keydown', function (e) { if (e.key === 'Enter') doLogin(); });
|
||||
// F-3.1:回车提交覆盖全部输入框(登录用户名框 / 注册四框)
|
||||
// 回车提交覆盖全部输入框(登录用户名框 / 注册四框)
|
||||
var s2u = $('#login-username'); if (s2u) s2u.addEventListener('keydown', function (e) { if (e.key === 'Enter') doLogin(); });
|
||||
var s3 = $('#reg-submit'); if (s3) s3.addEventListener('click', doRegister);
|
||||
var s4 = $('#reg-send-code'); if (s4) s4.addEventListener('click', sendCode);
|
||||
@@ -1331,7 +1331,7 @@ function getDeviceId() {
|
||||
var el = $(sel);
|
||||
if (el) el.addEventListener('keydown', function (e) { if (e.key === 'Enter') doRegister(); });
|
||||
});
|
||||
// F-4.1:密码明文切换(登录/注册/找回)
|
||||
// 密码明文切换(登录/注册/找回)
|
||||
[['#login-pwd-eye', '#login-password'], ['#reg-pwd-eye', '#reg-password'], ['#forgot-pwd-eye', '#forgot-password']].forEach(function (pair) {
|
||||
var eye = $(pair[0]), input = $(pair[1]);
|
||||
if (!eye || !input) return;
|
||||
@@ -1341,7 +1341,7 @@ function getDeviceId() {
|
||||
eye.textContent = show ? '隐藏' : '显示';
|
||||
});
|
||||
});
|
||||
// F-2.1:发码后修改邮箱 → 提前终止倒计时并恢复按钮,提示需重发
|
||||
// 发码后修改邮箱 → 提前终止倒计时并恢复按钮,提示需重发
|
||||
var regEmail = $('#reg-email');
|
||||
if (regEmail) regEmail.addEventListener('input', function () {
|
||||
var btn = $('#reg-send-code');
|
||||
@@ -1351,7 +1351,7 @@ function getDeviceId() {
|
||||
toast('邮箱已修改,请重新发送验证码', 'info');
|
||||
}
|
||||
});
|
||||
// 第 84 条:找回视图——发码/提交/回车/改邮箱终止倒计时(对齐注册交互)
|
||||
// 找回视图——发码/提交/回车/改邮箱终止倒计时(对齐注册交互)
|
||||
var fsc = $('#forgot-send-code'); if (fsc) fsc.addEventListener('click', doForgotSendCode);
|
||||
var fsub = $('#forgot-submit'); if (fsub) fsub.addEventListener('click', doForgotReset);
|
||||
['#forgot-email', '#forgot-code', '#forgot-password'].forEach(function (sel) {
|
||||
@@ -1367,7 +1367,7 @@ function getDeviceId() {
|
||||
toast('邮箱已修改,请重新发送验证码', 'info');
|
||||
}
|
||||
});
|
||||
// 第 86 条:管理员两步验证——发码/验证提交/回车提交
|
||||
// 管理员两步验证——发码/验证提交/回车提交
|
||||
var asc = $('#a2fa-send'); if (asc) asc.addEventListener('click', doA2faSendCode);
|
||||
var asb = $('#a2fa-submit'); if (asb) asb.addEventListener('click', doA2faVerify);
|
||||
['#a2fa-email', '#a2fa-code'].forEach(function (sel) {
|
||||
@@ -1386,7 +1386,7 @@ function getDeviceId() {
|
||||
else { var t = $('#tab-usercenter'); if (t) t.click(); }
|
||||
});
|
||||
var ucRedeem = $('#uc-card-redeem'); if (ucRedeem) ucRedeem.addEventListener('click', doRedeem);
|
||||
var ucAnn = $('#uc-ann-view'); if (ucAnn) ucAnn.addEventListener('click', function () { maybeShowAnnouncement(true); }); // F81:手动查看强制展示
|
||||
var ucAnn = $('#uc-ann-view'); if (ucAnn) ucAnn.addEventListener('click', function () { maybeShowAnnouncement(true); }); // 手动查看强制展示
|
||||
var ucLogout = $('#uc-logout'); if (ucLogout) ucLogout.addEventListener('click', doLogout);
|
||||
|
||||
uaBindSide();
|
||||
@@ -1400,7 +1400,7 @@ function getDeviceId() {
|
||||
var b8 = $('#ua-log-tab-ok'); if (b8) b8.addEventListener('click', function () { uaLogsType = 'success'; uaLogPage = 1; uaLoadLogs(); });
|
||||
var b9 = $('#ua-log-tab-fail'); if (b9) b9.addEventListener('click', function () { uaLogsType = 'fail'; uaLogPage = 1; uaLoadLogs(); });
|
||||
var blogPrev = $('#ua-log-prev'); if (blogPrev) blogPrev.addEventListener('click', function () { if (uaLogPage > 1) { uaLogPage--; uaLoadLogs(); } });
|
||||
// P3-43:next 补边界(原实现可翻到空页——前端无 pages 时放宽为 999 防呆)
|
||||
// next 补边界(原实现可翻到空页——前端无 pages 时放宽为 999 防呆)
|
||||
var blogNext = $('#ua-log-next'); if (blogNext) blogNext.addEventListener('click', function () { var maxP = window._uaLogPages || 999; if (uaLogPage < maxP) { uaLogPage++; uaLoadLogs(); } });
|
||||
var blogClear = $('#ua-log-clear'); if (blogClear) blogClear.addEventListener('click', uaClearLogs);
|
||||
var b10 = $('#ua-card-gen'); if (b10) b10.addEventListener('click', uaGenCards);
|
||||
|
||||
Reference in new issue
Block a user