Files
MC_Report/public/js/pages/install.js
canglan 6e9101a506 fix: deploy crash + multiple bug fixes + cleanup
- server.js: fix getSiteName() returning string when not installed causing
  'getSiteName(...).then is not a function' crash on homepage (deploy blocker)
- server.js: auto-load business routes after install completes (no restart needed),
  HTML cache keyed by api_key
- install: validate db name/email/password, trigger route loading after complete
- app.js: fix forgot/reset/verify pages rendering HomePage (missing page mapping)
- verify.js: support URL token auto-verification for external registration links
- auth: new email_code template for 6-digit code, reset_password template
  (forgot-password was using verify_email template)
- upload.js: fix MP4 magic-bytes check using undefined buf variable
- tickets.js: status enum validation, anonymous submission rate limit
- security.js: XSS whitelist preserves email template HTML, strips scripts,
  blocks javascript:/data: hrefs; CORS reject returns 403
- bans.js: allow clearing reason/duration, status enum validation
- users.js: fix req.user.role ReferenceError in create user modal
- home.js: tracking results now have detail view button
- .gitignore: ignore data/ (db credentials), logs, session files, temp scripts
2026-08-16 21:21:25 +08:00

111 lines
5.7 KiB
JavaScript
Raw 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.
const InstallPage = {
step: 1,
async render() {
const status = await API.get('/install/status');
if (status.installed) { location.hash = '#/login'; return ''; }
return `<div style="max-width:600px;margin:0 auto;padding:30px 0"><div id="install-steps"></div></div>`;
},
mount() {
this.showStep(1);
},
showStep(n) {
this.step = n;
const ct = document.getElementById('install-steps');
if (n === 1) this.renderDBStep(ct);
else if (n === 2) this.renderAdminStep(ct);
},
renderDBStep(ct) {
ct.innerHTML = `
<div class="card"><div class="card-h"><i class="fas fa-database"></i> 第1步数据库配置</div><div class="card-b">
<form id="install-db">
<div class="grid-2"><div class="form-group"><label>MySQL 主机</label><input id="db-host" value="localhost"></div>
<div class="form-group"><label>端口</label><input id="db-port" value="3306" type="number"></div></div>
<div class="grid-2"><div class="form-group"><label>用户名</label><input id="db-user" value="root" required></div>
<div class="form-group"><label>密码</label><input type="password" id="db-pass"></div></div>
<div class="form-group"><label>数据库名</label><input id="db-name" value="mc_report" required><span class="help">如不存在将自动创建</span></div>
<div id="db-err" class="alert alert-e hidden"></div>
<button type="submit" class="btn btn-p"><i class="fas fa-check"></i> 测试连接并继续</button>
</form>
</div></div>
`;
document.getElementById('install-db').onsubmit = async e => {
e.preventDefault();
const btn = e.target.querySelector('button');
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 测试中...';
try {
await API.post('/install/check-db', {
host: document.getElementById('db-host').value,
port: document.getElementById('db-port').value,
user: document.getElementById('db-user').value,
password: document.getElementById('db-pass').value,
database: document.getElementById('db-name').value,
});
this.dbConfig = {
host: document.getElementById('db-host').value,
port: document.getElementById('db-port').value,
user: document.getElementById('db-user').value,
pass: document.getElementById('db-pass').value,
name: document.getElementById('db-name').value,
};
this.showStep(2);
} catch (ex) {
document.getElementById('db-err').textContent = ex.message;
document.getElementById('db-err').classList.remove('hidden');
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-check"></i> 测试连接并继续';
}
};
},
renderAdminStep(ct) {
ct.innerHTML = `
<div class="card"><div class="card-h"><i class="fas fa-user-crown"></i> 第2步创建服主账号</div><div class="card-b">
<form id="install-admin">
<div class="grid-2"><div class="form-group"><label>站点名称</label><input id="ia-sitename" value="MC举报系统"></div>
<div class="form-group"><label>站点地址</label><input id="ia-siteurl" value="${location.origin}"></div></div>
<div class="grid-2"><div class="form-group"><label>服主用户名 *</label><input id="ia-username" required placeholder="登录用"></div>
<div class="form-group"><label>邮箱 *</label><input type="email" id="ia-email" required></div></div>
<div class="grid-2"><div class="form-group"><label>密码 *</label><input type="password" id="ia-password" required minlength="6"></div>
<div class="form-group"><label>游戏名称</label><input id="ia-game" placeholder="Minecraft ID"></div></div>
<div class="form-group"><label>游戏UID</label><input id="ia-uid" placeholder="游戏唯一ID"></div>
<div id="ia-err" class="alert alert-e hidden"></div>
<div id="ia-warn" class="alert alert-i"><strong>注意:</strong>安装完成后需要重启服务器才能加载业务路由。请记好账号密码。</div>
<button type="submit" class="btn btn-p btn-lg w-full"><i class="fas fa-check"></i> 完成安装</button>
</form>
</div></div>
`;
document.getElementById('install-admin').onsubmit = async e => {
e.preventDefault();
const btn = e.target.querySelector('button');
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 安装中...';
try {
await API.post('/install/complete', {
...this.dbConfig,
db_host: this.dbConfig.host, db_port: this.dbConfig.port,
db_user: this.dbConfig.user, db_pass: this.dbConfig.pass, db_name: this.dbConfig.name,
site_name: document.getElementById('ia-sitename').value,
site_url: document.getElementById('ia-siteurl').value,
admin_username: document.getElementById('ia-username').value,
admin_password: document.getElementById('ia-password').value,
admin_email: document.getElementById('ia-email').value,
admin_game_name: document.getElementById('ia-game').value,
admin_game_uid: document.getElementById('ia-uid').value,
});
document.getElementById('install-admin').innerHTML = '<div class="alert alert-s"><h3><i class="fas fa-check-circle"></i> 安装成功!</h3><p>系统已就绪,正在跳转登录...</p></div>';
setTimeout(() => location.hash = '#/login', 1500);
} catch (ex) {
document.getElementById('ia-err').textContent = ex.message;
document.getElementById('ia-err').classList.remove('hidden');
btn.disabled = false;
btn.innerHTML = '<i class="fas fa-check"></i> 完成安装';
}
};
}
};