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>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# 方式一:独立子域名(DNS 添加 sync.linyux.cn A 记录后使用)
|
||||
sync.linyux.cn {
|
||||
reverse_proxy 127.0.0.1:3780
|
||||
}
|
||||
|
||||
# 方式二:复用现有域名路径(并入 gitea.linyux.cn 的站点块,不要单独建站)
|
||||
# gitea.linyux.cn {
|
||||
# handle_path /sync-api/* {
|
||||
# reverse_proxy 127.0.0.1:3780
|
||||
# }
|
||||
# # ...原有 Gitea 反代配置
|
||||
# }
|
||||
+41
-11
@@ -1,6 +1,6 @@
|
||||
# 同步服务端部署指南
|
||||
# 同步服务端部署指南(Docker + Caddy 自动 HTTPS)
|
||||
|
||||
## 部署(在 124.221.192.176 上执行)
|
||||
## 一、部署同步服务
|
||||
|
||||
1. 上传 server 目录到服务器(如 ~/internlog-sync/)
|
||||
2. 启动:
|
||||
@@ -10,21 +10,51 @@
|
||||
|
||||
3. 验证:curl http://127.0.0.1:3780/api/health 应返回 {"ok":true}
|
||||
|
||||
数据保存在 ./data/(用户注册表 + 每用户数据文件),直接打包该目录即备份。
|
||||
数据保存在 ./data/(用户注册表 + 每用户数据文件),整目录复制即备份。
|
||||
|
||||
## 建议:启用 HTTPS
|
||||
## 二、Caddy 反向代理(自动申请与续期 HTTPS 证书)
|
||||
|
||||
客户端在手机浏览器使用时,建议用 nginx 反向代理复用现有证书:
|
||||
### 方式一:独立子域名(推荐)
|
||||
|
||||
location /sync-api/ {
|
||||
proxy_pass http://127.0.0.1:3780/;
|
||||
proxy_set_header Host $host;
|
||||
先在 DNS 添加 A 记录:sync.linyux.cn → 124.221.192.176
|
||||
|
||||
Caddyfile:
|
||||
|
||||
sync.linyux.cn {
|
||||
reverse_proxy 127.0.0.1:3780
|
||||
}
|
||||
|
||||
客户端同步地址填 https://gitea.linyux.cn/sync-api 即可全程加密。
|
||||
不加代理时客户端可临时填 http://124.221.192.176:3780(明文,仅建议内网或过渡期使用)。
|
||||
客户端同步地址填:https://sync.linyux.cn
|
||||
|
||||
## 接口
|
||||
### 方式二:复用现有域名路径(不新增 DNS 记录)
|
||||
|
||||
在 gitea.linyux.cn 现有 Caddy 站点块中加入 handle_path(原有 Gitea 反代配置保持不变):
|
||||
|
||||
gitea.linyux.cn {
|
||||
handle_path /sync-api/* {
|
||||
reverse_proxy 127.0.0.1:3780
|
||||
}
|
||||
# ...原有 Gitea 配置
|
||||
}
|
||||
|
||||
客户端同步地址填:https://gitea.linyux.cn/sync-api
|
||||
|
||||
### 启动 Caddy
|
||||
|
||||
- 宿主机已运行 Caddy:修改 Caddyfile 后 caddy reload 即可(参考 server/Caddyfile.example)
|
||||
- 未运行 Caddy:docker compose 里已附注释掉的 caddy 服务段,取消注释后 docker compose up -d
|
||||
|
||||
注意:证书自动申请要求 80/443 端口公网可达,且域名已解析到本服务器。
|
||||
|
||||
## 三、客户端配置
|
||||
|
||||
应用 → 设置 → 多端数据同步:
|
||||
|
||||
1. 同步服务地址:https://sync.linyux.cn(或方式二的 https://gitea.linyux.cn/sync-api)
|
||||
2. 注册云端账号(或填写已注册的账号密码)
|
||||
3. 立即同步;此后登录状态下每 5 分钟自动同步
|
||||
|
||||
## 四、接口
|
||||
|
||||
- POST /api/auth/register {username, password}
|
||||
- POST /api/auth/login {username, password}
|
||||
|
||||
@@ -7,3 +7,18 @@ services:
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
restart: unless-stopped
|
||||
|
||||
# 可选:Caddy 自动 HTTPS。若宿主机已有 Caddy/nginx 占用 80/443,请改用
|
||||
# 现有反代添加站点块(见 DEPLOY.md),不要启用本段。
|
||||
#
|
||||
# caddy:
|
||||
# image: caddy:2-alpine
|
||||
# container_name: internlog-caddy
|
||||
# ports:
|
||||
# - "80:80"
|
||||
# - "443:443"
|
||||
# volumes:
|
||||
# - ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
# - ./caddy_data:/data
|
||||
# - ./caddy_config:/config
|
||||
# restart: unless-stopped
|
||||
|
||||
@@ -531,3 +531,4 @@ src/
|
||||
- v1.11:新增记账系统——支出/收入录入(金额以分存储)、固定分类枚举、报销跟踪(待报销/已报销与待报销金额汇总)、月度汇总卡片与支出分类分布、月度流水筛选与弹窗编辑;备份格式升级至 v3 并向下兼容
|
||||
- v1.12:「今日」视图升级为「详情」——支持前后翻页、日期选择器自由切换日期,对任意一天进行查看、打卡、记录与修改
|
||||
- v1.13:多端同步与安卓应用——自建零依赖同步服务端(Docker 部署、按用户 JSON 隔离、HMAC token);客户端同步引擎(uuid 与 updated_at 补齐、镜像哈希差异、墓碑传播、记录级新者胜);设置页同步配置与自动同步;Capacitor 安卓工程脚手架与移动端 UI 适配(安全区、输入字号)
|
||||
- v1.13.1:登录会话持久化(localStorage,关闭浏览器后免登录,无需每次使用都登录);部署方案调整为 Docker + Caddy 自动 HTTPS(独立子域名与现有域名路径两种反代方式);移除 30 分钟无操作自动退出(密码库仍保留 5 分钟自动锁定与主密码门槛)
|
||||
|
||||
Reference in New Issue
Block a user