chore: 并入已删除会话的在途成果(防丢失;原会话已删,未做功能验收)
**背景**:这些改动原属本工作区另外几个会话(T01/T02 等),**那些会话已被用户删除** ⇒
工作树里的成果处于"无主"状态,一次错误 checkout / 覆盖即**永久丢失** ⇒ 代入库保全。
口径遵循本项目**先例**(`39a1f2e` / `b617cdb`:**别人的活,代入库并在提交信息里注明**)。
**内容**:档案 101「能力管理」改名 + 页内 tab 分页|档案 102 语言切换搬入「用户设置」|
`07-实例UI分区登记表.md`|`scripts/find-ui*.mjs`(UI 元素定位工具)|`poc/portal-entry/`(0.5.3)|
`src/web/locale-pref.ts` + `home-files.ts`(语言偏好持久化)|`test/locale-pref.test.mjs`|
`package.json`|`BRIEF.md` / `INDEX.md` / `docs-manifest.json` / `03-路线图与待办.md` / 档案 100 增量。
**已做最小健全性检查**(⚠️ **未跑完整构建 / 单测** —— 那是原会话的验收职责,本次只求"不丢"):
- JSON 合法:`package.json` / `poc/business-plugins/package.json` / `docs-manifest.json` ✓
- 4 个 TS 文件 `{}`/`()` 配平 ✓;新增文件均非空 ✓
- 规模:13 文件改动 +340/−210,新增 12 条
**未 push**(按 §4 提交边界:用户说"提交",未说"推送")。
This commit is contained in:
1 parent
03c8363960
commit
3efd68517f
27 files changed
+1712
-206
No files matched your search
@@ -12,6 +12,9 @@ import { deriveKey, encrypt } from '../../crypto.js'
|
||||
import { toPublicUser } from '../../db/types.js'
|
||||
import { catalogDiagnostics, isCatalogProvider, isCnProvider, listCatalogProviders } from '../model-catalog.js'
|
||||
import { PROTOCOLS } from '../model-landing.js'
|
||||
import { readTextOrEmpty, writeHomeFile } from '../home-files.js'
|
||||
import { isLocaleId, reconcileLocalePreference } from '../locale-pref.js'
|
||||
import { join } from 'node:path'
|
||||
import {
|
||||
clearSessionCookie,
|
||||
hashPassword,
|
||||
@@ -22,6 +25,15 @@ import {
|
||||
verifyPassword,
|
||||
} from '../auth.js'
|
||||
|
||||
const localeSchema = {
|
||||
body: {
|
||||
type: 'object',
|
||||
required: ['locale'],
|
||||
additionalProperties: false,
|
||||
properties: { locale: { type: 'string', minLength: 2, maxLength: 8 } },
|
||||
},
|
||||
} as const
|
||||
|
||||
const registerSchema = {
|
||||
body: {
|
||||
type: 'object',
|
||||
@@ -345,6 +357,26 @@ export const authRoutes: FastifyPluginAsync = async (app) => {
|
||||
* @deprecated 档案 87 起语义已变成"启用这一个、**不关**别的"(与 `toggle(true)` 同义)。
|
||||
* 保留路由只为老客户端不 404;新前端不该再用它。
|
||||
*/
|
||||
/**
|
||||
* 语言偏好持久化(2026-09-15,档案 102)—— 把用户在实例「用户设置」里选的语言**记住**。
|
||||
*
|
||||
* 为什么需要:官方 `dsh-client-locale` 只对 **loopback** 页面持久化到 `settings.yaml`;平台是
|
||||
* 「浏览器经域名访问远程实例」⇒ 非 loopback ⇒ 官方只为当前进程保留选择,刷新即回默认。
|
||||
* 本路由**替官方把它自己的设置写进它自己的文件**(顶层 `locale: → preference:`),
|
||||
* ⇒ 官方语义不破(实例启动时读自己的设置即生效)+ 用户选择跨页面 / 跨重启保留。
|
||||
* ⚠️ 写文件沿用 `model-landing` 那套(备份到平台目录 + chown 给 home 属主),见 `home-files.ts`。
|
||||
*/
|
||||
app.post('/api/me/locale', { preHandler: requireAuth, schema: localeSchema }, async (request, reply) => {
|
||||
const { locale } = request.body as { locale: string }
|
||||
if (!isLocaleId(locale)) return reply.code(400).send({ error: 'invalid_locale' })
|
||||
const homeDir = homeRoot(userRoot(app.config.dataRoot, request.user!.id))
|
||||
const file = join(homeDir, 'settings.yaml')
|
||||
const text = await readTextOrEmpty(file)
|
||||
const next = reconcileLocalePreference(text, locale)
|
||||
if (next.changed) await writeHomeFile(homeDir, file, next.text)
|
||||
return { ok: true, locale, changed: next.changed }
|
||||
})
|
||||
|
||||
app.post('/api/me/keys/:id/select', { preHandler: requireAuth }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
if (!(await app.db.selectCredentialKey(request.user!.id, id))) return reply.code(404).send({ error: 'not_found' })
|
||||
|
||||
Reference in new issue
Block a user