security: 改密即吊销全部旧令牌 + 登录防爆破 + COS 异地备份(环境变量开关) + 启动失败重试按钮
This commit is contained in:
+3
-3
File diff suppressed because one or more lines are too long
+2
-2
@@ -1,2 +1,2 @@
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./web-C-z1ujqe.js","./index-DT_fURyb.js","./index-2EGKD3r-.css"])))=>i.map(i=>d[i]);
|
||||
import{r as s,_ as i}from"./index-DT_fURyb.js";var t;(function(e){e.Documents="DOCUMENTS",e.Data="DATA",e.Library="LIBRARY",e.Cache="CACHE",e.External="EXTERNAL",e.ExternalStorage="EXTERNAL_STORAGE"})(t||(t={}));var a;(function(e){e.UTF8="utf8",e.ASCII="ascii",e.UTF16="utf16"})(a||(a={}));const E=s("Filesystem",{web:()=>i(()=>import("./web-C-z1ujqe.js"),__vite__mapDeps([0,1,2]),import.meta.url).then(e=>new e.FilesystemWeb)});export{t as Directory,a as Encoding,E as Filesystem};
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./web-DtE1VpZK.js","./index-D6jOKt8Z.js","./index-2EGKD3r-.css"])))=>i.map(i=>d[i]);
|
||||
import{r as s,_ as i}from"./index-D6jOKt8Z.js";var t;(function(e){e.Documents="DOCUMENTS",e.Data="DATA",e.Library="LIBRARY",e.Cache="CACHE",e.External="EXTERNAL",e.ExternalStorage="EXTERNAL_STORAGE"})(t||(t={}));var a;(function(e){e.UTF8="utf8",e.ASCII="ascii",e.UTF16="utf16"})(a||(a={}));const E=s("Filesystem",{web:()=>i(()=>import("./web-DtE1VpZK.js"),__vite__mapDeps([0,1,2]),import.meta.url).then(e=>new e.FilesystemWeb)});export{t as Directory,a as Encoding,E as Filesystem};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -8,7 +8,7 @@
|
||||
<link rel="icon" href="./icons/icon.svg" type="image/svg+xml" />
|
||||
<link rel="apple-touch-icon" href="./icons/icon-192.png" />
|
||||
<title>实习工作日志</title>
|
||||
<script type="module" crossorigin src="./assets/index-DT_fURyb.js"></script>
|
||||
<script type="module" crossorigin src="./assets/index-D6jOKt8Z.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-2EGKD3r-.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -26,7 +26,8 @@ async function main() {
|
||||
check('错误密码登录 401', bad.status === 401, bad.status)
|
||||
const ok = await post('/api/auth/login', { username: 'audit_a', password: 'aapass123' })
|
||||
check('正确密码登录 200', ok.status === 200 && !!ok.body.token, ok.status)
|
||||
const tokA = reg.body.token, tokA2 = ok.body.token
|
||||
let tokA = reg.body.token
|
||||
const tokA2 = ok.body.token
|
||||
const noAuth = await post('/api/sync', { tables: {} })
|
||||
check('无 token 同步被拒 401', noAuth.status === 401, noAuth.status)
|
||||
const regB = await post('/api/auth/register', { username: 'audit_b', password: 'bbpass123' })
|
||||
@@ -85,6 +86,10 @@ async function main() {
|
||||
check('旧密码立即失效 401', oldLogin.status === 401, oldLogin.status)
|
||||
const newLogin = await post('/api/auth/login', { username: 'audit_a', password: 'aapass456' })
|
||||
check('新密码登录 200', newLogin.status === 200, newLogin.status)
|
||||
// 改密后旧令牌立即失效(会话吊销)
|
||||
const oldTokSync = await post('/api/sync', { tables: {} }, tokA)
|
||||
check('改密后旧 token 同步被拒 401', oldTokSync.status === 401, oldTokSync.status)
|
||||
tokA = newLogin.body.token
|
||||
const chgWrong = await post('/api/auth/password', { oldPassword: 'zzz', newPassword: 'whatever1' }, tokA)
|
||||
check('错误旧密码改密被拒 400', chgWrong.status === 400, chgWrong.status)
|
||||
|
||||
@@ -123,6 +128,8 @@ async function main() {
|
||||
check('注册表仅存哈希不存明文密码', !!aRec && !JSON.stringify(aRec).includes('aapass'))
|
||||
await new Promise(r => globalThis.setTimeout(r, 400))
|
||||
check('启动自动备份已生成', fsMod.existsSync('server/data/backups/' + new Date().toISOString().slice(0, 10) + '/registry.json'))
|
||||
const hInfo = await fetch(BASE + '/api/health').then(x => x.json()).catch(() => ({}))
|
||||
check('health 暴露备份状态', hInfo && hInfo.backup === 'local-only', hInfo.backup)
|
||||
|
||||
const d1 = await post('/api/auth/delete', {}, tokA)
|
||||
check('删除账号 200', d1.status === 200, d1.body)
|
||||
|
||||
@@ -15,6 +15,12 @@ export function showFatalError(title: string, detail: string): void {
|
||||
const box = document.createElement('div')
|
||||
box.style.cssText = 'border:1px solid #E5484D;border-radius:8px;padding:12px;margin-bottom:12px;word-break:break-all;white-space:pre-wrap;'
|
||||
box.textContent = title + '\n' + detail
|
||||
// 重试入口:纯在线模式下连不上服务器时(如启动失败),一键重载而不是干等着重启应用
|
||||
const retry = document.createElement('button')
|
||||
retry.textContent = '重新加载'
|
||||
retry.style.cssText = 'margin:-4px 0 12px;padding:6px 16px;border:1px solid #E5484D;border-radius:6px;background:#FFFFFF;color:#E5484D;font-size:13px;cursor:pointer;'
|
||||
retry.addEventListener('click', () => { try { location.reload() } catch { /* 忽略 */ } })
|
||||
el.appendChild(box)
|
||||
el.appendChild(retry)
|
||||
} catch { /* 浮层失败时保持静默 */ }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user