cb738a8e9a
Co-authored-by: Cursor <cursoragent@cursor.com>
80 lines
2.8 KiB
Markdown
80 lines
2.8 KiB
Markdown
# Changelog: 主动 JWT 续期 —— 消除文件管理器双刷新
|
||
|
||
**日期**: 2026-06-02
|
||
**类型**: 修复 (Bug Fix)
|
||
**影响范围**: frontend/src/stores/auth.ts · frontend/src/api/index.ts
|
||
|
||
---
|
||
|
||
## 变更摘要
|
||
|
||
实现客户端 JWT access token 提前续期机制,彻底消除文件管理器"进目录双刷新"问题。
|
||
|
||
---
|
||
|
||
## 动机
|
||
|
||
每次 access token 过期后,`api/index.ts` 的 `fetchAuthed()` 会:
|
||
1. 先发出一次业务请求 → 服务端返回 401
|
||
2. 触发 `/auth/refresh` 获取新 token
|
||
3. 用新 token 重发同一业务请求
|
||
|
||
导致每次进入目录产生 2 次 `/api/sync/browse` 请求,页面可见"双刷新"(`_browseSeq` 虽可防止旧数据覆盖,但无法消除 2 次请求本身)。
|
||
|
||
---
|
||
|
||
## 方案
|
||
|
||
### `frontend/src/stores/auth.ts`
|
||
|
||
- 新增 `decodeJwtExp(jwt)` 工具函数:不验签地从 JWT Payload 解码 `exp` 字段(秒)
|
||
- 新增 `tokenExp` computed:返回当前 token 的过期 unix 秒,无 token 时为 0
|
||
- 新增 `isTokenExpiringSoon(thresholdSec = 300)` 方法:当距过期 < `thresholdSec` 秒时返回 true
|
||
|
||
### `frontend/src/api/index.ts`
|
||
|
||
在 `fetchAuthed()` 发出实际请求之前加入一个提前续期分支:
|
||
|
||
```typescript
|
||
if (!_retry && !AUTH_SKIP_REFRESH.has(path) && auth.token && auth.isTokenExpiringSoon(300)) {
|
||
await tryRefreshSession()
|
||
}
|
||
```
|
||
|
||
若 token 将在 5 分钟内过期,先刷新再发请求,完全跳过"401 → 重试"路径。
|
||
原有的 401 兜底重试逻辑保留不变(处理极端并发或时钟偏差场景)。
|
||
|
||
---
|
||
|
||
## 涉及文件
|
||
|
||
| 文件 | 变更类型 |
|
||
|------|---------|
|
||
| `frontend/src/stores/auth.ts` | 新增 `decodeJwtExp` / `tokenExp` / `isTokenExpiringSoon` |
|
||
| `frontend/src/api/index.ts` | 在 `fetchAuthed` 中新增提前续期分支 |
|
||
|
||
---
|
||
|
||
## 是否需要迁移 / 重启
|
||
|
||
- **前端**: 需重新 `vite build` 后部署到服务器
|
||
- **后端**: 无变更,无需重启
|
||
- **数据库**: 无变更
|
||
|
||
---
|
||
|
||
## 追加修复(2026-06-02 续)
|
||
|
||
- `FilesPage.vue`:`v-select` 在 `onMounted` 赋值 `selectedServer` 时会触发 `@update:model-value` → `onServerChange` 再次 `browse()`;增加 `_initing` 守卫
|
||
- `FilesPage.vue`:`pageReady` 避免首屏短暂显示「请先选择服务器」
|
||
- `FilesPage.vue`:同路径并发 `browse()` 合并为单次 in-flight 请求
|
||
- 部署须执行 `deploy/prune_frontend_assets.py` 清理服务器上残留的旧 hash chunk(否则浏览器可能仍加载旧 `FilesPage-*.js`)
|
||
|
||
## 验证方式
|
||
|
||
1. 登录后等待 access token 接近过期(或手动修改 token exp 为近期时间)
|
||
2. 进入文件管理器浏览目录
|
||
3. 打开 DevTools Network 过滤 `/sync/browse`
|
||
4. 预期:每次进目录只有 **1 条** `/sync/browse` 请求,不再出现 401 后重试的第 2 条
|
||
5. 空目录区不应再闪现「请先选择服务器」(已选服务器时)
|