refactor: session-based external auth, dynamic sources, drop netease UID
Auth (external API): - ID: 16-digit random (non-sequential); Secret: SeaReport- + 32 hex - POST /auth/session: ID+Secret -> Bearer SESSION (24h, single-session, old session invalidated on re-issue, disabled client invalidates) - clientAuth now validates Bearer SESSION via api_sessions JOIN api_clients Sources (dynamic, no default, open-source friendly): - sources table + CRUD route (/api/sources, owner; delete guarded by usage) - users/user_identities.source ENUM -> VARCHAR, seeded netease/skin - register/admin create/identity bind: validate against enabled sources - UI: 来源管理 page; source dropdowns loaded dynamically everywhere (register, dashboard identity, users admin, bans), labels dynamic UID removal: - game_uid/reporter_game_uid no longer required (db default '', validations dropped, frontend fields optional) Docs: EXTERNAL-API.md session flow + new credential format; API.md updated Verified: 37 checks (syntax, session logic, source CRUD, UID removal, docs)
This commit is contained in:
@@ -30,6 +30,36 @@ const U = {
|
||||
return String(s).replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/"/g,'"').replace(/\n/g,'\\n');
|
||||
},
|
||||
|
||||
// ---- 动态来源(缓存, 注册/绑定身份/用户管理等处共用) ----
|
||||
_sourcesCache: null,
|
||||
async sources(force) {
|
||||
if (force || !this._sourcesCache) {
|
||||
try { this._sourcesCache = await API.get('/sources'); }
|
||||
catch { this._sourcesCache = []; }
|
||||
}
|
||||
return this._sourcesCache;
|
||||
},
|
||||
// 生成下拉 HTML; includeEmpty 时带"不指定"选项
|
||||
async sourcesOptions(selected, includeEmpty) {
|
||||
const list = await this.sources();
|
||||
const opts = (includeEmpty ? '<option value="">不指定</option>' : '') +
|
||||
list.map(s => `<option value="${this.esc(s.code)}" ${s.code===selected?'selected':''}>${this.esc(s.label)}</option>`).join('');
|
||||
return opts;
|
||||
},
|
||||
// code → 显示标签(找不到时显示原 code)
|
||||
async sourceLabel(code) {
|
||||
if (!code) return '-';
|
||||
const list = await this.sources();
|
||||
const hit = list.find(s => s.code === code);
|
||||
return hit ? hit.label : code;
|
||||
},
|
||||
async sourceBadge(code) {
|
||||
if (!code) return '-';
|
||||
const list = await this.sources();
|
||||
const hit = list.find(s => s.code === code);
|
||||
return `<span class="badge bg-admin">${this.esc(hit ? hit.label : code)}</span>`;
|
||||
},
|
||||
|
||||
showAlert(containerId, type, msg) {
|
||||
const ct = document.getElementById(containerId || 'page-content');
|
||||
if (!ct) return;
|
||||
|
||||
Reference in New Issue
Block a user