Files
orbit/internal/transport/httpserver/console/login.html

38 lines
5.2 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark">
<title>登录 Orbit · 星轨控制台</title>
<link rel="stylesheet" href="/console/styles.css">
<style>
html,body{min-height:100%;margin:0}body{display:grid;place-items:center;background:#06101e;color:#dceafb;font-family:ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.ambient{position:fixed;pointer-events:none}.login-shell{position:relative;width:min(430px,calc(100% - 40px));padding:38px;border:1px solid rgba(156,183,218,.15);border-radius:24px;background:linear-gradient(145deg,rgba(18,37,62,.95),rgba(7,19,34,.97));box-shadow:0 26px 80px rgba(0,0,0,.35)}.login-brand{display:flex;align-items:center;gap:13px;margin-bottom:34px}.login-brand-mark{display:grid;grid-template-columns:repeat(3,5px);align-items:end;gap:3px;width:35px;height:35px;padding:9px;border:1px solid rgba(98,230,212,.5);border-radius:13px;background:rgba(98,230,212,.08)}.login-brand-mark i{display:block;border-radius:4px;background:#62e6d4}.login-brand-mark i:nth-child(1){height:11px}.login-brand-mark i:nth-child(2){height:20px}.login-brand-mark i:nth-child(3){height:15px}.login-brand strong{display:block;font-size:20px;letter-spacing:.02em}.login-brand small{display:block;margin-top:3px;color:#7189a3;font-size:10px}.login-kicker{color:#62e6d4;font-size:10px;letter-spacing:.22em;font-weight:700}.login-title{margin:9px 0 9px;font-size:29px;letter-spacing:-.04em}.login-copy{margin:0 0 28px;color:#8095ad;font-size:12px;line-height:1.7}.login-form{display:grid;gap:15px}.login-field span{display:block;margin-bottom:8px;color:#91a6bc;font-size:11px}.login-field input{box-sizing:border-box;width:100%;padding:13px 14px;border:1px solid rgba(156,183,218,.18);border-radius:11px;outline:0;background:rgba(3,12,24,.58);color:#e5f2ff;font:inherit;font-size:13px}.login-field input:focus{border-color:rgba(98,230,212,.72);box-shadow:0 0 0 3px rgba(98,230,212,.09)}.login-submit{margin-top:6px;padding:13px 16px;border:0;border-radius:11px;background:#62e6d4;color:#05262a;font:700 13px inherit;cursor:pointer;box-shadow:0 12px 25px rgba(98,230,212,.16)}.login-submit:hover{background:#8ff2e6}.login-submit:disabled{opacity:.62;cursor:wait}.login-error{min-height:16px;margin:1px 0 0;color:#ff9baa;font-size:11px}.login-foot{margin:26px 0 0;color:#5e7895;font-size:10px;line-height:1.6}
</style>
</head>
<body>
<div class="ambient ambient-one"></div><div class="ambient ambient-two"></div>
<main class="login-shell">
<div class="login-brand"><div class="login-brand-mark" aria-hidden="true"><i></i><i></i><i></i></div><div><strong>Orbit</strong><small>星轨控制台</small></div></div>
<div class="login-kicker">CONTROL PLANE</div>
<h1 class="login-title">登录控制台</h1>
<p class="login-copy">使用你的控制台账号进入任务、定时、流程和结果交付页面。</p>
<form class="login-form" id="login-form" autocomplete="on">
<label class="login-field"><span>用户名</span><input id="username" name="username" type="text" autocomplete="username" maxlength="128" required autofocus></label>
<label class="login-field"><span>密码</span><input id="password" name="password" type="password" autocomplete="current-password" maxlength="1024" required></label>
<p class="login-error" id="login-error" role="alert"></p>
<button class="login-submit" id="login-submit" type="submit">登录 Orbit <span>→</span></button>
</form>
<p class="login-foot">会话由服务端保存和校验,浏览器不会读取业务凭据。</p>
</main>
<script>
(() => {
const form = document.getElementById('login-form'); const username = document.getElementById('username'); const password = document.getElementById('password'); const submit = document.getElementById('login-submit'); const error = document.getElementById('login-error');
const params = new URLSearchParams(location.search); let returnTo = params.get('return') || '/console/'; if (!returnTo.startsWith('/console') || returnTo.startsWith('//') || returnTo.startsWith('/console/login')) returnTo = '/console/';
fetch('/console/auth/session', { headers: { Accept: 'application/json' }, cache: 'no-store' }).then(response => response.json()).then(body => { if (body.authenticated) location.replace(returnTo); }).catch(() => {});
form.addEventListener('submit', async event => { event.preventDefault(); error.textContent = ''; submit.disabled = true; submit.textContent = '正在登录…'; try { const response = await fetch('/console/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, body: JSON.stringify({ username: username.value.trim(), password: password.value }) }); const body = await response.json().catch(() => ({})); if (!response.ok) throw new Error(body?.error?.message || '登录失败,请检查用户名和密码'); location.replace(returnTo); } catch (loginError) { error.textContent = loginError.message || '登录失败,请稍后重试'; password.select(); } finally { submit.disabled = false; submit.textContent = '登录 Orbit →'; } });
})();
</script>
</body>
</html>