128 lines
3.7 KiB
TypeScript
128 lines
3.7 KiB
TypeScript
import { Router, type Request, type Response } from 'express'
|
|
import { z } from 'zod'
|
|
import { AuthService } from './auth.service'
|
|
import { authMiddleware } from '../../shared/middlewares/auth.middleware'
|
|
|
|
const service = new AuthService()
|
|
|
|
// ─── Schemas de validação ─────────────────────────────────────────────────────
|
|
|
|
const registerSchema = z.object({
|
|
name: z.string().min(2).max(100),
|
|
email: z.string().email(),
|
|
password: z.string().min(8).max(128),
|
|
planId: z.string().uuid().optional(),
|
|
})
|
|
|
|
const loginSchema = z.object({
|
|
email: z.string().email(),
|
|
password: z.string().min(1),
|
|
})
|
|
|
|
const refreshSchema = z.object({
|
|
refreshToken: z.string().min(1),
|
|
})
|
|
|
|
const changePasswordSchema = z.object({
|
|
currentPassword: z.string().min(1),
|
|
newPassword: z.string().min(8).max(128),
|
|
})
|
|
|
|
// ─── Helper para erros de validação / serviço ─────────────────────────────────
|
|
|
|
function handleError(res: Response, err: unknown) {
|
|
if (err instanceof z.ZodError) {
|
|
res.status(400).json({ error: 'Dados inválidos', details: err.flatten().fieldErrors })
|
|
return
|
|
}
|
|
const e = err as Error & { statusCode?: number }
|
|
res.status(e.statusCode ?? 500).json({ error: e.message ?? 'Erro interno' })
|
|
}
|
|
|
|
// ─── Router ───────────────────────────────────────────────────────────────────
|
|
|
|
export const authRouter = Router()
|
|
|
|
/**
|
|
* POST /api/auth/register
|
|
* Cria novo tenant (usuário com role USER).
|
|
* Retorna access + refresh token e dados do usuário.
|
|
*/
|
|
authRouter.post('/register', async (req: Request, res: Response) => {
|
|
try {
|
|
const body = registerSchema.parse(req.body) as any
|
|
const result = await service.register(body)
|
|
res.status(201).json(result)
|
|
} catch (err) {
|
|
handleError(res, err)
|
|
}
|
|
})
|
|
|
|
/**
|
|
* POST /api/auth/login
|
|
* Autentica usuário. Retorna access + refresh token.
|
|
*/
|
|
authRouter.post('/login', async (req: Request, res: Response) => {
|
|
try {
|
|
const body = loginSchema.parse(req.body) as any
|
|
const result = await service.login(body)
|
|
res.json(result)
|
|
} catch (err) {
|
|
handleError(res, err)
|
|
}
|
|
})
|
|
|
|
/**
|
|
* POST /api/auth/refresh
|
|
* Renova o access token usando o refresh token.
|
|
*/
|
|
authRouter.post('/refresh', async (req: Request, res: Response) => {
|
|
try {
|
|
const { refreshToken } = refreshSchema.parse(req.body)
|
|
const result = await service.refresh(refreshToken)
|
|
res.json(result)
|
|
} catch (err) {
|
|
handleError(res, err)
|
|
}
|
|
})
|
|
|
|
/**
|
|
* POST /api/auth/logout
|
|
* Revoga o refresh token do usuário autenticado.
|
|
*/
|
|
authRouter.post('/logout', authMiddleware, async (req: Request, res: Response) => {
|
|
try {
|
|
await service.logout(req.tenantId!)
|
|
res.json({ message: 'Sessão encerrada' })
|
|
} catch (err) {
|
|
handleError(res, err)
|
|
}
|
|
})
|
|
|
|
/**
|
|
* GET /api/auth/me
|
|
* Retorna dados do usuário autenticado + dias restantes do trial.
|
|
*/
|
|
authRouter.get('/me', authMiddleware, async (req: Request, res: Response) => {
|
|
try {
|
|
const user = await service.me(req.tenantId!)
|
|
res.json(user)
|
|
} catch (err) {
|
|
handleError(res, err)
|
|
}
|
|
})
|
|
|
|
/**
|
|
* PATCH /api/auth/password
|
|
* Altera a senha do usuário autenticado.
|
|
*/
|
|
authRouter.patch('/password', authMiddleware, async (req: Request, res: Response) => {
|
|
try {
|
|
const body = changePasswordSchema.parse(req.body) as any
|
|
await service.changePassword(req.tenantId!, body)
|
|
res.json({ message: 'Senha alterada com sucesso' })
|
|
} catch (err) {
|
|
handleError(res, err)
|
|
}
|
|
})
|