Add password-protected access flow
This commit is contained in:
@@ -4,8 +4,14 @@ const state = {
|
||||
selectedAccountId: null,
|
||||
messages: [],
|
||||
selectedMessageUid: null,
|
||||
authenticated: false,
|
||||
};
|
||||
|
||||
const appShell = document.getElementById('app-shell');
|
||||
const loginScreen = document.getElementById('login-screen');
|
||||
const loginForm = document.getElementById('login-form');
|
||||
const loginBtn = document.getElementById('login-btn');
|
||||
const logoutBtn = document.getElementById('logout-btn');
|
||||
const channelTree = document.getElementById('channel-tree');
|
||||
const channelForm = document.getElementById('channel-form');
|
||||
const accountForm = document.getElementById('account-form');
|
||||
@@ -35,10 +41,12 @@ boot();
|
||||
|
||||
async function boot() {
|
||||
bindEvents();
|
||||
await reloadBaseData();
|
||||
await restoreSession();
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
loginForm.addEventListener('submit', onLogin);
|
||||
logoutBtn.addEventListener('click', onLogout);
|
||||
composeBtn.addEventListener('click', () => openModal(composeModal));
|
||||
settingsBtn.addEventListener('click', () => openModal(settingsModal));
|
||||
closeCompose.addEventListener('click', () => closeModal(composeModal));
|
||||
@@ -56,6 +64,56 @@ function bindEvents() {
|
||||
copyExportBtn.addEventListener('click', onCopyExportAccounts);
|
||||
}
|
||||
|
||||
async function restoreSession() {
|
||||
const result = await request('/api/auth/status', {}, false);
|
||||
state.authenticated = Boolean(result.authenticated);
|
||||
updateAuthView();
|
||||
|
||||
if (state.authenticated) {
|
||||
await reloadBaseData();
|
||||
}
|
||||
}
|
||||
|
||||
function updateAuthView() {
|
||||
loginScreen.classList.toggle('hidden', state.authenticated);
|
||||
appShell.classList.toggle('hidden', !state.authenticated);
|
||||
}
|
||||
|
||||
async function onLogin(event) {
|
||||
event.preventDefault();
|
||||
const payload = Object.fromEntries(new FormData(loginForm).entries());
|
||||
loginBtn.disabled = true;
|
||||
loginBtn.textContent = '验证中...';
|
||||
|
||||
try {
|
||||
await request('/api/auth/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload),
|
||||
}, false);
|
||||
state.authenticated = true;
|
||||
updateAuthView();
|
||||
loginForm.reset();
|
||||
await reloadBaseData();
|
||||
showToast('登录成功');
|
||||
} finally {
|
||||
loginBtn.disabled = false;
|
||||
loginBtn.textContent = '进入系统';
|
||||
}
|
||||
}
|
||||
|
||||
async function onLogout() {
|
||||
await request('/api/auth/logout', { method: 'POST' }, false);
|
||||
state.authenticated = false;
|
||||
state.selectedAccountId = null;
|
||||
state.selectedMessageUid = null;
|
||||
state.messages = [];
|
||||
syncSelectionState();
|
||||
renderChannelTree();
|
||||
renderMessages();
|
||||
updateAuthView();
|
||||
showToast('已退出登录');
|
||||
}
|
||||
|
||||
function openModal(modal) {
|
||||
modal.classList.remove('hidden');
|
||||
}
|
||||
@@ -441,8 +499,9 @@ async function copyText(value) {
|
||||
document.body.removeChild(input);
|
||||
}
|
||||
|
||||
async function request(url, options = {}) {
|
||||
async function request(url, options = {}, useToast = true) {
|
||||
const response = await fetch(url, {
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(options.headers || {}),
|
||||
@@ -452,7 +511,9 @@ async function request(url, options = {}) {
|
||||
|
||||
const data = await response.json().catch(() => ({}));
|
||||
if (!response.ok) {
|
||||
showToast(data.error || '请求失败', true);
|
||||
if (useToast) {
|
||||
showToast(data.error || '请求失败', true);
|
||||
}
|
||||
throw new Error(data.error || 'Request failed');
|
||||
}
|
||||
return data;
|
||||
|
||||
Reference in New Issue
Block a user