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
+3
-31
@@ -32,6 +32,7 @@ import {
|
||||
refForEntry,
|
||||
type SettingsEntry,
|
||||
} from './model-landing.js'
|
||||
import { readTextOrEmpty, writeHomeFile } from './home-files.js'
|
||||
import { rateLimit } from './middleware/rate-limit.js'
|
||||
import { authRoutes } from './routes/auth.js'
|
||||
import { adminRoutes } from './routes/admin.js'
|
||||
@@ -120,37 +121,8 @@ export async function buildServer(config: ServerConfig): Promise<FastifyInstance
|
||||
await mkdir(managedDir, { recursive: true })
|
||||
await writeFile(join(managedDir, userId + '.json'), JSON.stringify(m), { mode: 0o600 })
|
||||
}
|
||||
const readTextOrEmpty = async (file: string): Promise<string> => {
|
||||
try {
|
||||
return await readFile(file, 'utf8')
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 写 home 里的配置文件:备份(落**平台目录**)→ 写 → chown 给 home 属主。
|
||||
* 实例以 dsh-<uid> 身份运行,root 写的 600 文件它读不了 ⇒ 最后一步不能省。
|
||||
*/
|
||||
const writeHomeFile = async (homeDir: string, file: string, text: string): Promise<void> => {
|
||||
try {
|
||||
const bakDir = process.env.DSH_PLATFORM_BACKUP_DIR ?? '/opt/dsh/backups'
|
||||
await mkdir(bakDir, { recursive: true })
|
||||
const label = basename(file).replace(/^\./, '').replace(/\.ya?ml$/, '')
|
||||
// ⚠️ 带上 home 的**父目录名**(= 用户 id):只写 basename 的话每个人都是 "home",
|
||||
// 备份文件互相看不出是谁的(旧实现就是这个毛病:credentials-home-*.yaml)。
|
||||
const who = basename(dirname(homeDir))
|
||||
writeFileSync(join(bakDir, `${label}-${who}-${Date.now()}.yaml`), text, { mode: 0o600 })
|
||||
} catch {
|
||||
/* 备份失败不阻断 */
|
||||
}
|
||||
await writeFile(file, text, { mode: 0o600 })
|
||||
try {
|
||||
const st = await stat(homeDir)
|
||||
await chown(file, st.uid, st.gid)
|
||||
} catch {
|
||||
/* chown 失败(非 root 运行等)不阻断 */
|
||||
}
|
||||
}
|
||||
// `readTextOrEmpty` / `writeHomeFile` 已抽到 `./home-files.js`(2026-09-15:语言偏好
|
||||
// 持久化也要用同一套「写 home 文件」语义 —— 与其复制一份,不如共用;约束见该模块头注释)。
|
||||
|
||||
/** 平台共享条目(admin 配的、已启用的那些)—— 只在该用户开关打开、且他不是那个 admin 时纳入。 */
|
||||
const sharedLandingRows = async (userId: string): Promise<CredentialLandingRow[]> => {
|
||||
|
||||
Reference in new issue
Block a user