Files
MC_Report/public/js/pages/external-api.js
canglan effc262382 fix: site name in top logo + server-list public + external API docs page
- app.js: set #logo-text (top bar) from window.__SITE_NAME__ - the
  top-right site name stayed default because only #site-title and
  #sidebar-title were set
- security.js: apiKeyGuard whitelist /api/polls/server-list so the
  ticket create page can load sub-servers (was 401 without api key)
- external-api.js: management page now only creates/manages clients,
  removed inline endpoint table
- new external-api-docs.js: full API doc page (auth flow, all 11
  endpoints, request/response examples, Node sample) at
  #/external-api-docs, linked from the management page
- index.html: load external-api-docs.js
2026-08-22 02:18:29 +08:00

89 lines
4.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* MC Report System
* Copyright (C) 2026 Sea Network Technology Studio
* Author: CangLan <admin@sea-studio.top>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const ExternalApiPage = {
async render() { return '<div class="loading"><i class="fas fa-spinner"></i></div>'; },
async mount() {
document.getElementById('page-title').textContent = '外部API';
document.getElementById('page-actions').innerHTML = `
<button class="btn btn-o btn-sm" onclick="App.navigate('external-api-docs')"><i class="fas fa-book"></i> 接口文档</button>
<button class="btn btn-p btn-sm" onclick="ExternalApiPage.showCreate()"><i class="fas fa-plus"></i> 创建客户端</button>`;
await this.load();
},
async load() {
const ct = document.getElementById('page-content');
try {
const clients = await API.get('/external/clients');
ct.innerHTML = `
<div class="card"><div class="card-h"><i class="fas fa-key"></i> API 客户端</div><div class="card-b">
${clients.length === 0 ? '<div class="empty"><i class="fas fa-key"></i><p>暂无客户端</p></div>' : `<div class="table-wrap"><table><thead><tr><th>#</th><th>名称</th><th>Client ID</th><th>状态</th><th>创建时间</th><th>操作</th></tr></thead><tbody>${clients.map(c => `
<tr>
<td>${c.id}</td>
<td>${U.esc(c.name)}</td>
<td><code style="font-size:11px">${U.esc(c.client_id)}</code></td>
<td>${c.active ? '<span class="badge bg-resolved">启用</span>' : '<span class="badge bg-rejected">停用</span>'}</td>
<td class="ts">${U.date(c.created_at)}</td>
<td>
<button class="btn btn-o btn-sm" onclick="ExternalApiPage.toggle(${c.id},${c.active?0:1})">${c.active?'停用':'启用'}</button>
<button class="btn btn-d btn-sm" onclick="ExternalApiPage.del(${c.id})"><i class="fas fa-trash"></i></button>
</td>
</tr>`).join('')}</tbody></table></div>`}
<div class="alert alert-i" style="margin-top:12px"><b>使用流程:</b> 创建客户端后, 用 <code>Client ID</code> + <code>Secret</code> 调 <code>POST /api/external/auth/session</code> 换取 SESSION, 之后所有接口用 <code>Authorization: Bearer &lt;SESSION&gt;</code>。完整说明见右上角「接口文档」。</div>
</div></div>
`;
} catch (e) { ct.innerHTML = `<div class="alert alert-e">${e.message}</div>`; }
},
async showCreate() {
U.modal('创建 API 客户端', `
<form id="apikey-form">
<div class="form-group"><label>名称 *</label><input id="ak-name" required placeholder="如QQ官方机器人"></div>
<div class="modal-f"><button type="button" class="btn btn-o" data-action="App:closeModal">取消</button><button type="submit" class="btn btn-p">创建</button></div>
</form>
`);
document.getElementById('apikey-form').onsubmit = async e => {
e.preventDefault();
try {
const r = await API.post('/external/clients', { name: document.getElementById('ak-name').value });
App.closeModal();
U.modal('客户端已创建', `
<p>请立即保存以下凭据(secret 只显示一次):</p>
<div class="alert alert-i" style="word-break:break-all;font-family:monospace;font-size:12px">
<div>Client ID: <b>${U.esc(r.client_id)}</b></div>
<div style="margin-top:6px">Secret: <b>${U.esc(r.secret)}</b></div>
</div>
<div class="modal-f"><button type="button" class="btn btn-p" data-action="App:closeModal">我已保存</button></div>
`);
this.load();
} catch(ex){alert(ex.message);}
};
},
async toggle(id, active) {
try { await API.put(`/external/clients/${id}`, { active: !!active }); this.load(); } catch(ex){alert(ex.message);}
},
async del(id) {
if (!await U.confirm('确认删除该客户端?')) return;
try { await API.delete(`/external/clients/${id}`); this.load(); } catch(ex){alert(ex.message);}
}
};