115 lines
3.8 KiB
HTML
115 lines
3.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>写作后台 - 登录</title>
|
|
<style>
|
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
body{min-height:100vh;display:flex;align-items:center;justify-content:center;background:#fcfaf5;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
|
.login-card{background:#fff;border-radius:16px;box-shadow:0 4px 24px rgba(34,61,102,.08);padding:48px 40px;width:100%;max-width:380px}
|
|
h1{font-size:20px;color:#1d3d6c;margin-bottom:4px;font-weight:700}
|
|
.sub{font-size:13px;color:#999;margin-bottom:32px}
|
|
.field{margin-bottom:20px}
|
|
.field label{display:block;font-size:13px;color:#223d66;margin-bottom:6px;font-weight:500}
|
|
.field input{width:100%;padding:10px 14px;border:1.5px solid #e4dfd2;border-radius:8px;font-size:14px;color:#223d66;background:#fcfaf5;outline:none;transition:border .2s}
|
|
.field input:focus{border-color:#1d3d6c}
|
|
.btn{width:100%;padding:12px;background:#1d3d6c;color:#fff;border:none;border-radius:8px;font-size:15px;font-weight:600;cursor:pointer;transition:opacity .2s;margin-top:4px}
|
|
.btn:hover{opacity:.85}
|
|
.btn:active{opacity:.7}
|
|
.btn:disabled{opacity:.5;cursor:not-allowed}
|
|
.error{background:#fef2f2;color:#dc2626;font-size:13px;padding:10px 14px;border-radius:8px;margin-bottom:16px;display:none}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="login-card">
|
|
<h1>写作后台</h1>
|
|
<p class="sub">优世界 · Blog Admin</p>
|
|
<div class="error" id="err">用户名或密码错误</div>
|
|
<form id="form">
|
|
<div class="field">
|
|
<label>用户名</label>
|
|
<input type="text" id="user" autocomplete="username" autofocus required>
|
|
</div>
|
|
<div class="field">
|
|
<label>密码</label>
|
|
<input type="password" id="pass" autocomplete="current-password" required>
|
|
</div>
|
|
<button class="btn" type="submit" id="btn">登 录</button>
|
|
</form>
|
|
</div>
|
|
<script>
|
|
document.getElementById('form').addEventListener('submit', async function(e) {
|
|
e.preventDefault();
|
|
var btn = document.getElementById('btn');
|
|
var err = document.getElementById('err');
|
|
btn.disabled = true;
|
|
btn.textContent = '登录中...';
|
|
err.style.display = 'none';
|
|
|
|
var u = document.getElementById('user').value;
|
|
var p = document.getElementById('pass').value;
|
|
var tok = btoa(u + ':' + p);
|
|
|
|
try {
|
|
// 验证凭据
|
|
var res = await fetch('/auth/check', {
|
|
headers: { 'Authorization': 'Basic ' + tok }
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error('auth failed');
|
|
}
|
|
|
|
// 存储凭据
|
|
localStorage.setItem('wauth', tok);
|
|
|
|
// 注册 Service Worker
|
|
if ('serviceWorker' in navigator) {
|
|
var reg = await navigator.serviceWorker.register('/sw.js');
|
|
// 等待 SW 激活
|
|
if (reg.installing) {
|
|
await new Promise(function(resolve) {
|
|
reg.installing.addEventListener('statechange', function() {
|
|
if (this.state === 'activated') resolve();
|
|
});
|
|
});
|
|
}
|
|
// 发送 token 给 SW
|
|
var sw = reg.active || reg.waiting || reg.installing;
|
|
if (sw) {
|
|
sw.postMessage({ type: 'SET_TOKEN', token: tok });
|
|
}
|
|
// 确保 SW 控制当前页面
|
|
if (navigator.serviceWorker.controller) {
|
|
navigator.serviceWorker.controller.postMessage({ type: 'SET_TOKEN', token: tok });
|
|
}
|
|
}
|
|
|
|
// 跳转到主页
|
|
location.href = '/';
|
|
} catch (err2) {
|
|
err.style.display = 'block';
|
|
btn.disabled = false;
|
|
btn.textContent = '登 录';
|
|
}
|
|
});
|
|
|
|
// 已登录则验证后跳转
|
|
(function(){
|
|
var tok = localStorage.getItem('wauth');
|
|
if (!tok) return;
|
|
fetch('/auth/check', { headers: { 'Authorization': 'Basic ' + tok } }).then(function(r) {
|
|
if (r.ok) {
|
|
location.href = '/';
|
|
} else {
|
|
localStorage.removeItem('wauth');
|
|
}
|
|
}).catch(function() {
|
|
localStorage.removeItem('wauth');
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|