返回 DeepSeek-Reasonix
validation.ts
根目录 / workers / accounts / src / lib / validation.ts
1 import type { Context } from "hono";
2 import { z } from "zod";
3 import type { AppEnv } from "../env";
4 import { MAX_PASSWORD, MIN_PASSWORD } from "../config";
5 import { ApiError } from "../http/errors";
6
7 const email = z.string().trim().toLowerCase().email().max(254);
8 const password = z.string().min(MIN_PASSWORD, `Password must be at least ${MIN_PASSWORD} characters.`).max(MAX_PASSWORD);
9
10 export const RegisterSchema = z.object({
11 email,
12 password,
13 displayName: z.string().trim().max(80).optional(),
14 });
15
16 export const LoginSchema = z.object({
17 email,
18 password: z.string().min(1).max(MAX_PASSWORD),
19 });
20
21 export const ForgotSchema = z.object({ email });
22 export const ResendSchema = z.object({ email });
23
24 export const ResetSchema = z.object({
25 token: z.string().min(10).max(256),
26 password,
27 });
28
29 export const VerifyQuerySchema = z.object({
30 token: z.string().min(10).max(256),
31 });
32
33 export const ProfileSchema = z
34 .object({
35 displayName: z.string().trim().max(80).optional(),
36 bio: z.string().trim().max(500).optional(),
37 avatarUrl: z.union([z.string().url().max(500), z.literal("")]).optional(),
38 handle: z.string().trim().min(3).max(30).optional(),
39 })
40 .strict();
41
42 export const PasswordChangeSchema = z.object({
43 currentPassword: z.string().min(1).max(MAX_PASSWORD),
44 newPassword: password,
45 });
46
47 const userCode = z.string().trim().min(4).max(20);
48
49 export const DevicePollSchema = z.object({ deviceCode: z.string().min(10).max(256) });
50 export const DeviceApproveSchema = z.object({ userCode });
51 export const DeviceCodeQuerySchema = z.object({ userCode });
52
53 function firstIssue(error: z.ZodError): string {
54 const issue = error.issues[0];
55 if (!issue) return "Some fields are invalid.";
56 const path = issue.path.join(".");
57 return path ? `${path}: ${issue.message}` : issue.message;
58 }
59
60 export async function parseBody<S extends z.ZodTypeAny>(c: Context<AppEnv>, schema: S): Promise<z.infer<S>> {
61 let raw: unknown;
62 try {
63 raw = await c.req.json();
64 } catch {
65 throw new ApiError(400, "invalid_json", "Request body must be valid JSON.");
66 }
67 const result = schema.safeParse(raw);
68 if (!result.success) throw new ApiError(422, "invalid_input", firstIssue(result.error));
69 return result.data;
70 }
71
72 export function parseQuery<S extends z.ZodTypeAny>(c: Context<AppEnv>, schema: S): z.infer<S> {
73 const params = Object.fromEntries(new URL(c.req.url).searchParams);
74 const result = schema.safeParse(params);
75 if (!result.success) throw new ApiError(422, "invalid_input", firstIssue(result.error));
76 return result.data;
77 }
78
78 lines TYPESCRIPT