feat: 登录会话持久化 + Caddy 反代部署方案
This commit is contained in:
+4
-18
@@ -21,24 +21,12 @@ import { categoryLabel, type Attendance, type LogItem, type Memo } from './db/sc
|
||||
|
||||
const toasts = useToasts()
|
||||
|
||||
// ---- 用户会话 ----
|
||||
// ---- 用户会话(持久化登录,退出走顶栏按钮)----
|
||||
const session = ref<Session | null>(getSession())
|
||||
const IDLE_LOGOUT_MS = 30 * 60 * 1000
|
||||
let sessionIdleTimer: number | null = null
|
||||
|
||||
function armSessionTimer() {
|
||||
if (sessionIdleTimer !== null) window.clearTimeout(sessionIdleTimer)
|
||||
if (!session.value) return
|
||||
sessionIdleTimer = window.setTimeout(() => { doLogout(true) }, IDLE_LOGOUT_MS)
|
||||
}
|
||||
function onActivity() {
|
||||
if (session.value) armSessionTimer()
|
||||
}
|
||||
async function onLogin(s: Session) {
|
||||
session.value = s
|
||||
location.hash = '#/calendar'
|
||||
route.value = 'calendar'
|
||||
armSessionTimer()
|
||||
await refreshClock()
|
||||
await refreshTip()
|
||||
startSyncTimer()
|
||||
@@ -49,12 +37,12 @@ async function onLogin(s: Session) {
|
||||
showToast('已迁移原有数据 ' + mt + ' 条')
|
||||
}
|
||||
}
|
||||
async function doLogout(auto: boolean) {
|
||||
async function doLogout() {
|
||||
await logoutUser()
|
||||
session.value = null
|
||||
route.value = 'calendar'
|
||||
location.hash = '#/calendar'
|
||||
showToast(auto ? '长时间未操作已自动退出' : '已退出登录')
|
||||
showToast('已退出登录')
|
||||
}
|
||||
|
||||
// ---- 自动同步:登录后与每 5 分钟,静默执行 ----
|
||||
@@ -212,10 +200,8 @@ async function refreshTip() {
|
||||
|
||||
onMounted(async () => {
|
||||
window.addEventListener('keydown', onGlobalKey)
|
||||
window.addEventListener('click', onActivity)
|
||||
if (session.value) {
|
||||
openUserDatabase(session.value.userId)
|
||||
armSessionTimer()
|
||||
await refreshClock()
|
||||
await refreshTip()
|
||||
startSyncTimer()
|
||||
@@ -268,7 +254,7 @@ onMounted(async () => {
|
||||
填写本周周记
|
||||
</button>
|
||||
<span class="user-chip num">{{ session?.username }}</span>
|
||||
<button class="btn btn-sm" @click="doLogout(false)">退出</button>
|
||||
<button class="btn btn-sm" @click="doLogout">退出</button>
|
||||
<button class="btn btn-primary btn-sm" :disabled="clockDone" @click="doClock">
|
||||
<Icon :name="clockDone ? 'check-circle' : 'clock'" :size="14" />
|
||||
<span class="num">{{ clockLabel }}</span>
|
||||
|
||||
@@ -31,10 +31,11 @@ export interface Session {
|
||||
username: string
|
||||
}
|
||||
|
||||
// 会话存 sessionStorage:关闭标签页即退出,刷新页面保持登录
|
||||
// 会话持久化到 localStorage:关闭浏览器后仍保持登录,无需每次使用都登录
|
||||
// 安全边界:密码库主密码不随会话保存,每次启动仍需解锁
|
||||
export function getSession(): Session | null {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(SESSION_KEY)
|
||||
const raw = localStorage.getItem(SESSION_KEY)
|
||||
if (!raw) return null
|
||||
const s = JSON.parse(raw)
|
||||
if (s && s.userId && s.username) return s as Session
|
||||
@@ -42,10 +43,10 @@ export function getSession(): Session | null {
|
||||
return null
|
||||
}
|
||||
function setSession(s: Session): void {
|
||||
sessionStorage.setItem(SESSION_KEY, JSON.stringify(s))
|
||||
localStorage.setItem(SESSION_KEY, JSON.stringify(s))
|
||||
}
|
||||
function clearSession(): void {
|
||||
sessionStorage.removeItem(SESSION_KEY)
|
||||
localStorage.removeItem(SESSION_KEY)
|
||||
}
|
||||
|
||||
export function openUserDatabase(userId: string): void {
|
||||
|
||||
@@ -347,7 +347,7 @@ async function doImport() {
|
||||
<div class="ai-grid">
|
||||
<div class="rule-item">
|
||||
<label>同步服务地址</label>
|
||||
<input class="input" v-model="syncBase" placeholder="如 http://124.221.192.176:3780" />
|
||||
<input class="input" v-model="syncBase" placeholder="如 https://sync.linyux.cn 或 https://gitea.linyux.cn/sync-api" />
|
||||
</div>
|
||||
<div class="rule-item">
|
||||
<label>云端用户名</label>
|
||||
|
||||
Reference in New Issue
Block a user