JessieExcel/api/routes/auth.ts
2026-03-25 01:54:12 +08:00

97 lines
2.6 KiB
TypeScript

/**
* This is a user authentication API route demo.
* Handle user registration, login, token management, etc.
*/
import { Router, type Request, type Response } from 'express'
import bcrypt from 'bcryptjs'
import { z } from 'zod'
import { signToken, toPublicUser } from '../auth.js'
import { createUser, findUserByUsername } from '../db.js'
import { requireAuth } from '../middleware/requireAuth.js'
import type { AuthedRequest } from '../middleware/requireAuth.js'
import type { Role } from '../../shared/types.js'
const router = Router()
/**
* User Login
* POST /api/auth/register
*/
const registerSchema = z.object({
username: z.string().min(3).max(32),
password: z.string().min(6).max(64),
email: z.string().email().optional(),
})
router.post('/register', async (req: Request, res: Response): Promise<void> => {
const parsed = registerSchema.safeParse(req.body)
if (!parsed.success) {
res.status(400).json({ success: false, error: 'BAD_REQUEST' })
return
}
const existed = await findUserByUsername(parsed.data.username)
if (existed) {
res.status(409).json({ success: false, error: 'USERNAME_TAKEN' })
return
}
const user = await createUser({
username: parsed.data.username,
password: parsed.data.password,
role: 'sales',
email: parsed.data.email,
})
const token = signToken(user)
res.json({ success: true, data: { token, user } })
})
/**
* User Login
* POST /api/auth/login
*/
const loginSchema = z.object({
username: z.string().min(1),
password: z.string().min(1),
})
router.post('/login', async (req: Request, res: Response): Promise<void> => {
const parsed = loginSchema.safeParse(req.body)
if (!parsed.success) {
res.status(400).json({ success: false, error: 'BAD_REQUEST' })
return
}
const user = await findUserByUsername(parsed.data.username)
if (!user || user.status !== 'active') {
res.status(401).json({ success: false, error: 'INVALID_CREDENTIALS' })
return
}
const ok = await bcrypt.compare(parsed.data.password, user.passwordHash)
if (!ok) {
res.status(401).json({ success: false, error: 'INVALID_CREDENTIALS' })
return
}
const pub = toPublicUser(user)
const token = signToken(pub)
res.json({ success: true, data: { token, user: pub } })
})
/**
* User Logout
* POST /api/auth/logout
*/
router.post('/logout', async (_req: Request, res: Response): Promise<void> => {
res.json({ success: true })
})
router.get('/me', requireAuth, async (req: Request, res: Response): Promise<void> => {
const user = (req as AuthedRequest).user
res.json({ success: true, data: user })
})
export default router