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:
@@ -2,6 +2,17 @@ const RegisterPage = {
|
||||
captcha: null,
|
||||
codeId: null,
|
||||
|
||||
async mount() {
|
||||
// 动态加载来源下拉(不设默认来源)
|
||||
U.sources().then(list => {
|
||||
const sel = document.getElementById('rs');
|
||||
if (sel) {
|
||||
sel.innerHTML = '<option value="">不指定</option>' + list.map(s => `<option value="${U.esc(s.code)}">${U.esc(s.label)}</option>`).join('');
|
||||
this.toggleSource();
|
||||
}
|
||||
}).catch(()=>{});
|
||||
},
|
||||
|
||||
async render() {
|
||||
try { this.captcha = await API.get('/captcha'); } catch { this.captcha = null; }
|
||||
return `
|
||||
@@ -15,11 +26,11 @@ const RegisterPage = {
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="form-group"><label>帐号来源</label><select id="rs" onchange="RegisterPage.toggleSource()"><option value="netease">网易端</option><option value="skin">皮肤站</option></select></div>
|
||||
<div class="form-group"><label>帐号来源</label><select id="rs" onchange="RegisterPage.toggleSource()"><option value="">加载中...</option></select></div>
|
||||
<div class="form-group"><label id="rs-label1">游戏名称</label><input id="rgn" required placeholder="Minecraft ID"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="uid-group"><label id="rs-label2">网易UID</label><input id="rgu" placeholder="仅网易端必填"></div>
|
||||
<div class="form-group" id="uid-group" style="display:none"><label id="rs-label2">UID(可选)</label><input id="rgu" placeholder="选填"></div>
|
||||
|
||||
${this.captcha ? `
|
||||
<div class="form-group"><label>人机验证</label>
|
||||
@@ -55,9 +66,10 @@ const RegisterPage = {
|
||||
toggleSource() {
|
||||
const src = document.getElementById('rs').value;
|
||||
document.getElementById('rs-label1').textContent = '游戏名称';
|
||||
document.getElementById('rs-label2').textContent = src === 'netease' ? '网易UID' : '皮肤站UUID(自动获取)';
|
||||
document.getElementById('uid-group').style.display = src === 'netease' ? '' : 'none';
|
||||
document.getElementById('rgu').required = src === 'netease';
|
||||
const isSkin = src === 'skin';
|
||||
document.getElementById('rs-label2').textContent = isSkin ? '皮肤站UUID(可选)' : 'UID(可选)';
|
||||
document.getElementById('uid-group').style.display = isSkin ? 'none' : '';
|
||||
document.getElementById('rgu').required = false;
|
||||
},
|
||||
|
||||
async refreshCaptcha() {
|
||||
|
||||
Reference in New Issue
Block a user