# Registration Page Design ## Overview Add a registration page at `/register` with username, password, confirm password, and SVG captcha. On success, redirect to `/login` with a query param. Login page is out of scope. ## Stack - Nuxt 4.4 + Nuxt UI 4.6 + Tailwind v4 - Drizzle ORM + SQLite (users table already exists) - bcryptjs (already installed), svg-captcha, zod (already installed) ## Files ### New | File | Purpose | |---|---| | `app/pages/register.vue` | Registration page component | | `server/api/auth/captcha.get.ts` | Generate SVG captcha, return token + svg | | `server/api/auth/register.post.ts` | Validate input, create user | | `server/utils/auth/captcha.ts` | Server-side captcha generation/validation | | `server/utils/auth/validation.ts` | Zod schemas for registration input | ### Modified | File | Change | |---|---| | `app/layouts/default.vue` | Wire header button to `/register` | ## API Endpoints ### GET /api/auth/captcha Generates a captcha, stores token→text in a server-side Map (5-min expiry), returns the SVG and token. ``` Response: { code: 0, data: { token: "uuid", svg: "..." } } ``` ### POST /api/auth/register ``` Request: { username, password, confirmPassword, captchaToken, captchaText } Response: { code: 0, data: { id: 1, username: "foo" } } ``` Steps: 1. Zod validation (username 3-30, password 8+, passwords match) 2. Verify captcha: lookup token in Map, compare text case-insensitive, delete on match 3. Check username uniqueness in DB 4. bcryptjs hash password (salt rounds 10) 5. Insert into users table with role="user", status="active" 6. Return success — no auto-login ## Data Flow 1. Page mounts → GET /api/auth/captcha → render SVG 2. User submits → client Zod validation → POST /api/auth/register 3. On success → `navigateTo('/login?registered=1')` 4. On error → show toast, keep form state; if captcha fail, auto-refresh captcha ## Error Handling Client-side: UForm + Zod validation for empty fields, length, password match. Server-side: | Case | Message | |---|---| | Zod validation fail | "密码至少需要8个字符" | | Captcha wrong | "验证码错误" | | Captcha expired | "验证码已过期" | | Username taken | "用户名已存在" | | Server error | "注册失败,请稍后重试" | Captcha is consumed (deleted from Map) on match to prevent replay. If registration fails after captcha check, a new captcha is fetched. ## Form Fields - **Username** — required, 3-30 characters, unique in DB - **Password** — required, min 8 characters, masked input with toggle - **Confirm Password** — required, must match password - **Captcha** — SVG image + text input, refresh button ## Layout Card-centered form (380px max-width), single column. Header already has the "登录/注册" button. Form shows a "Already have an account? Log in" link at the bottom.