83 lines
2.9 KiB
TypeScript
83 lines
2.9 KiB
TypeScript
|
|
/**
|
|||
|
|
* Auth Routes – Registration, Login, Logout, Profile
|
|||
|
|
*/
|
|||
|
|
import type { FastifyInstance } from 'fastify';
|
|||
|
|
import type { AuthService } from '../auth/AuthService.js';
|
|||
|
|
|
|||
|
|
export function registerAuthRoutes(fastify: FastifyInstance, authService: AuthService) {
|
|||
|
|
// Register new user
|
|||
|
|
fastify.post('/api/auth/register', async (request, reply) => {
|
|||
|
|
const { email, password, name, role } = request.body as {
|
|||
|
|
email?: string; password?: string; name?: string; role?: string;
|
|||
|
|
};
|
|||
|
|
if (!email || !password || !name) {
|
|||
|
|
return reply.code(400).send({ error: 'Email, password and name are required' });
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
const result = await authService.register(email, password, name, role);
|
|||
|
|
return reply.code(201).send(result);
|
|||
|
|
} catch (err: any) { return reply.code(409).send({ error: err.message });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Login
|
|||
|
|
fastify.post('/api/auth/login', async (request, reply) => {
|
|||
|
|
const { email, password } = request.body as { email?: string; password?: string };
|
|||
|
|
if (!email || !password) {
|
|||
|
|
return reply.code(400).send({ error: 'Email and password are required' });
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
const result = await authService.login(email, password);
|
|||
|
|
return reply.send(result);
|
|||
|
|
} catch {
|
|||
|
|
return reply.code(401).send({ error: 'Invalid credentials' });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Logout
|
|||
|
|
fastify.post('/api/auth/logout', async (request, reply) => {
|
|||
|
|
const token = extractToken(request);
|
|||
|
|
if (token) {
|
|||
|
|
authService.destroySession(token);
|
|||
|
|
}
|
|||
|
|
return reply.code(204).send();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Get current user profile
|
|||
|
|
fastify.get('/api/auth/me', async (request, reply) => {
|
|||
|
|
const token = extractToken(request);
|
|||
|
|
if (!token) return reply.code(401).send({ error: 'Not authenticated' });
|
|||
|
|
const user = authService.getUserFromSession(token);
|
|||
|
|
if (!user) return reply.code(401).send({ error: 'Invalid or expired session' });
|
|||
|
|
const { password_hash, ...safe } = user;
|
|||
|
|
return safe;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Change password
|
|||
|
|
fastify.patch('/api/auth/password', async (request, reply) => {
|
|||
|
|
const token = extractToken(request);
|
|||
|
|
if (!token) return reply.code(401).send({ error: 'Not authenticated' });
|
|||
|
|
const user = authService.getUserFromSession(token);
|
|||
|
|
if (!user) return reply.code(401).send({ error: 'Invalid or expired session' });
|
|||
|
|
|
|||
|
|
const { oldPassword, newPassword } = request.body as { oldPassword?: string; newPassword?: string };
|
|||
|
|
if (!oldPassword || !newPassword) {
|
|||
|
|
return reply.code(400).send({ error: 'oldPassword and newPassword are required' });
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
await authService.changePassword(user.id, oldPassword, newPassword);
|
|||
|
|
return reply.code(204).send();
|
|||
|
|
} catch (err: any) {
|
|||
|
|
return reply.code(400).send({ error: err.message });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function extractToken(request: any): string | null {
|
|||
|
|
const auth = request.headers?.authorization;
|
|||
|
|
if (auth && auth.startsWith('Bearer ')) {
|
|||
|
|
return auth.slice(7);
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|