boardx / boardx/workspacex

🔒 需人类签核:用户个人资料自助服务 —— 改姓名/头像/密码 + 查看多组织 + 自己的活动记录(零契约操作)

Open
#638 2 comments 0 reactions 0 assignees View on GitHub
backlog enhancement
Dominant language
TypeScript
Stars
0
Forks
0
Avg merge
1h 7m
Merged PRs (30d)
969

Description

## 背景

人类直接要求(非勘探触发):需要用户个人资料(改姓名/头像、改密码、查看/管理自己所属的多个组织、查看自己的活动/审计记录)。

## 实测:现状全景

| 子项 | 现状 |
|---|---|
| 改姓名 | ❌ 零契约操作。`credentials.display_name` 字段存在(`0010-auth-credentials-sessions.sql`),但没有任何 `UPDATE` 路径——本仓「本模型没有独立的 users 表」的设计原则下,改名就是改这一列 |
| 改头像 | ❌ 更彻底:`credentials` 表**没有头像列**,需要迁移新增 |
| 改密码(已登录状态) | ❌ 零契约操作。**注意与找回密码不同**:现有的是 `passwordReset`(未登录、走邮箱令牌),没有"已登录用户主动改密码"这条 |
| 查看/管理所属多个组织 | 🟡 **部分已有**:`login.out.orgIds`(一串 id)+ `switchOrganization` 操作已存在(F22 一账号多组织)。**缺的只是"看到每个组织的名字"**——而这条今晚已经在 #596 的处置里用 `resolveIdentity` 按组织多次调用绕过了,形状是 N+1,**不需要新契约**,UI 层面复用即可 |
| 查看自己的活动/审计记录 | ❌ 零自助端点。已有的 `adminAuditRead`(`chat.ts:349`)是**管理员**审计读,不是"我看我自己" |

## 契约草案(登记待签核,未实现,未新增任何文件)

```ts
// packages/contracts/src/identity.ts 或新文件 profile.ts(待人类定放哪)

updateOwnProfile: {
method: "PATCH", path: "/identity/me",
in: z.object({
displayName: z.string().min(1).optional(),
avatarUrl: z.string().url().optional(), // 需要 credentials 表新增列,见下
}).strict(),
out: z.object({ displayName: z.string(), avatarUrl: z.string().nullable() }).strict(),
err: ["INVALID_INPUT"] as const,
},

changeOwnPassword: {
method: "POST", path: "/identity/me/password",
in: z.object({
currentPassword: z.string().min(1), // 主动改密必须验证当前密码,防会话劫持后静默改密
newPassword: PasswordPolicy, // 复用 auth.ts 已有的密码策略单一事实源
}).strict(),
out: z.object({ changed: z.literal(true) }).strict(),
err: ["CURRENT_PASSWORD_INVALID", "PASSWORD_POLICY_VIOLATION"] as const,
},

listOwnActivity: {
method: "GET", path: "/identity/me/activity",
in: z.object({ cursor: z.string().nullable(), limit: z.number().int().min(1).max(100) }).strict(),
out: z.object({
events: z.array(z.object({
eventId: z.string(), kind: z.string(), occurredAt: z.string(), summary: z.string(),
})),
nextCursor: z.string().nullable(),
}).strict(),
err: [] as const,
},
```

## ⚠ 需要人类裁决的点,不只是"批不批"

1. **头像需要迁移**:`credentials` 表加 `avatar_url text` 列。**存 URL 还是存文件**(走已有的 artifact/asset 上传链路)是产品决定,不是我能定的实现细节——两者的错误处理、大小限制、审计口径都不同。
2. **改密码要不要吊销其他会话**:改密后是否强制登出其他设备,这是安全策略决定,建议参考 `password-reset.ts` 里"只吊销当前会话"那条先例(如果有)还是"全部吊销"。
3. **活动记录的口径**:`listOwnActivity` 读哪张表?如果复用 `provenance_events`(`adminAuditRead` 读的那张),需要确认按 `actorUserId` 过滤是否已有索引,否则全表扫会是性能问题。

## 范围收窄(本轮不做,需单独排期)

- **多组织管理**(离开某个组织、转让所有权等):本条只做"查看",管理动作范围更大,明确排除。
- 按人类裁决,团队 CRUD 单独登记(见另一个 issue)。

## 出处
coord-agent-auth 受人类直接指派勘探(非例行巡检触发)。**未实现,仅登记待签核。**

Contributor guide

No contributing guide indexed for this repository

Research direction

First resolve the human decisions on avatar storage, session revocation, and the activity data source. Then inspect packages/contracts/src/identity.ts, auth.ts, chat.ts:349, password-reset.ts, and 0010-auth-credentials-sessions.sql. The work is done when the approved profile, password-change, and activity scope is implemented with its required migration and contract changes.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, authentication, authorization, databases, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.