feat(api): 账户资料与密码安全

This commit is contained in:
R524809
2026-09-03 16:56:20 +08:00
parent 46d98ab11e
commit a5b89ddb1d
13 changed files with 552 additions and 14 deletions
+40
View File
@@ -10,13 +10,32 @@ export const loginRequestSchema = z.object({
email: z.string().trim().email().max(254),
password: z.string().min(1),
});
export const updateCurrentUserRequestSchema = z
.object({
display_name: z.string().trim().min(1).max(80).nullable().optional(),
avatar_asset_id: z.uuid().nullable().optional(),
})
.strict()
.refine((value) => Object.keys(value).length > 0, {
message: '至少需要提交一个可修改字段',
});
export const changePasswordRequestSchema = z
.object({
old_password: z.string().min(1),
password: z.string().min(1),
password_confirmation: z.string().min(1),
})
.strict();
export const authUserSchema = z.object({
id: z.uuid(),
email: z.string().email(),
display_name: z.string().nullable(),
// 头像文件由素材模块管理,账户接口只提供稳定素材引用,绝不保存对象存储 URL。
avatar_asset_id: z.uuid().nullable(),
email_verified_at: z.iso.datetime().nullable(),
created_at: z.iso.datetime(),
revision: z.number().int().positive(),
});
export const workspaceSchema = z.object({
id: z.uuid(),
@@ -29,8 +48,29 @@ export const authSessionResponseSchema = z.object({
user: authUserSchema,
workspace: workspaceSchema,
});
export const authRefreshResponseSchema = z.object({
access_token: z.string(),
access_token_expires_in: z.number().int().positive(),
});
export const authMeResponseSchema = z.object({
user: authUserSchema,
workspace: workspaceSchema,
platforms: z.array(
z.object({ id: z.uuid(), code: z.string(), name: z.string() }),
),
suggested_context: z.object({
platform_id: z.uuid().nullable(),
default_store: z.null(),
}),
});
export type RegisterRequest = z.infer<typeof registerRequestSchema>;
export type LoginRequest = z.infer<typeof loginRequestSchema>;
export type UpdateCurrentUserRequest = z.infer<
typeof updateCurrentUserRequestSchema
>;
export type ChangePasswordRequest = z.infer<typeof changePasswordRequestSchema>;
export type AuthUser = z.infer<typeof authUserSchema>;
export type Workspace = z.infer<typeof workspaceSchema>;
export type AuthSessionResponse = z.infer<typeof authSessionResponseSchema>;
export type AuthRefreshResponse = z.infer<typeof authRefreshResponseSchema>;
export type AuthMeResponse = z.infer<typeof authMeResponseSchema>;
@@ -0,0 +1,9 @@
-- 用户头像是账户资料的一部分;素材文件仍由受工作空间隔离的 media_assets 管理。
SET search_path TO atlas, public;
ALTER TABLE users
ADD COLUMN avatar_asset_id UUID REFERENCES media_assets(id) ON DELETE SET NULL,
ADD COLUMN revision INTEGER NOT NULL DEFAULT 1 CHECK (revision > 0);
CREATE INDEX ix_users_avatar_asset ON users (avatar_asset_id)
WHERE avatar_asset_id IS NOT NULL;