87 lines
2.4 KiB
TypeScript
87 lines
2.4 KiB
TypeScript
import { Hono } from 'hono';
|
|||
|
|
import { jsonValidator } from '../../shared/validation/index.js';
|
||
|
|
import { rateLimiter } from 'hono-rate-limiter';
|
||
|
|
import { env } from '../../config/index.js';
|
||
|
|
import { getClientIp } from '../../lib/clientIp.js';
|
||
|
|
import { created, success } from '../../lib/response.js';
|
||
|
|
import { requireAuth } from '../../middleware/auth.js';
|
||
|
|
import { loginSchema, registerSchema } from './auth.schemas.js';
|
||
|
|
import {
|
||
|
|
createGuest,
|
||
|
|
login,
|
||
|
|
logout,
|
||
|
|
register,
|
||
|
|
toPublicUser,
|
||
|
|
} from './auth.service.js';
|
||
|
|
import { prisma } from '../../lib/prisma.js';
|
||
|
|
|
||
|
|
const authAbuseLimiter = rateLimiter({
|
||
|
|
windowMs: env.RATE_LIMIT_WINDOW_MS,
|
||
|
|
limit: env.AUTH_RATE_LIMIT_MAX,
|
||
|
|
standardHeaders: 'draft-6',
|
||
|
|
keyGenerator: (c) => `auth:${getClientIp(c)}`,
|
||
|
|
handler: (c) =>
|
||
|
|
c.json(
|
||
|
|
{
|
||
|
|
success: false,
|
||
|
|
error: {
|
||
|
|
code: 'RATE_LIMIT_EXCEEDED',
|
||
|
|
message: 'Too many requests',
|
||
|
|
},
|
||
|
|
meta: {
|
||
|
|
requestId: c.get('requestId') ?? 'unknown',
|
||
|
|
timestamp: new Date().toISOString(),
|
||
|
|
},
|
||
|
|
},
|
||
|
|
429,
|
||
|
|
),
|
||
|
|
});
|
||
|
|
|
||
|
|
export function createAuthRoutes() {
|
||
|
|
const routes = new Hono();
|
||
|
|
|
||
|
|
routes.post('/register', authAbuseLimiter, jsonValidator(registerSchema), async (c) => {
|
||
|
|
const { email, password } = c.req.valid('json');
|
||
|
|
const result = await register(email, password);
|
||
|
|
return created(c, result);
|
||
|
|
});
|
||
|
|
|
||
|
|
routes.post('/login', authAbuseLimiter, jsonValidator(loginSchema), async (c) => {
|
||
|
|
const { email, password } = c.req.valid('json');
|
||
|
|
const result = await login(email, password);
|
||
|
|
return success(c, result);
|
||
|
|
});
|
||
|
|
|
||
|
|
routes.post('/guest', authAbuseLimiter, async (c) => {
|
||
|
|
const result = await createGuest();
|
||
|
|
return created(c, result);
|
||
|
|
});
|
||
|
|
|
||
|
|
routes.get('/me', requireAuth, async (c) => {
|
||
|
|
const authUser = c.get('authUser');
|
||
|
|
const user = await prisma.user.findUnique({
|
||
|
|
where: { id: authUser.id },
|
||
|
|
include: { profile: true, identities: true },
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!user) {
|
||
|
|
return success(c, {
|
||
|
|
id: authUser.externalId,
|
||
|
|
email: authUser.email,
|
||
|
|
isGuest: authUser.isGuest,
|
||
|
|
profileComplete: false,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
return success(c, toPublicUser(user));
|
||
|
|
});
|
||
|
|
|
||
|
|
routes.post('/logout', requireAuth, async (c) => {
|
||
|
|
const authUser = c.get('authUser');
|
||
|
|
await logout(authUser.sessionId);
|
||
|
|
return success(c, { loggedOut: true });
|
||
|
|
});
|
||
|
|
|
||
|
|
return routes;
|
||
|
|
}
|