You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2.8 KiB
2.8 KiB
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: "<svg>...</svg>" } }
POST /api/auth/register
Request: { username, password, confirmPassword, captchaToken, captchaText }
Response: { code: 0, data: { id: 1, username: "foo" } }
Steps:
- Zod validation (username 3-30, password 8+, passwords match)
- Verify captcha: lookup token in Map, compare text case-insensitive, delete on match
- Check username uniqueness in DB
- bcryptjs hash password (salt rounds 10)
- Insert into users table with role="user", status="active"
- Return success — no auto-login
Data Flow
- Page mounts → GET /api/auth/captcha → render SVG
- User submits → client Zod validation → POST /api/auth/register
- On success →
navigateTo('/login?registered=1') - 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.