import type { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' import bcrypt from 'bcrypt' import { Type } from 'typebox' import { CreateUserSchema, UserSchema } from '../schemas/auth.ts' const plugin: FastifyPluginAsyncTypebox = async (fastify) => { fastify.post( '/auth/register', { schema: { summary: 'Register', tags: ['Auth'], operationId: 'auth.register', body: CreateUserSchema, response: { 200: UserSchema, }, }, config: { skipAuth: true, }, }, async (req, reply) => { const hashed = await bcrypt.hash(req.body.password, 10) const user = await fastify.prisma.user.create({ data: { username: req.body.username, password: hashed, displayName: req.body.username, UserPreferences: { create: {}, }, }, }) const session = await fastify.lucia.createSession(user.id, {}) const cookie = fastify.lucia.createSessionCookie(session.id) reply.setCookie(cookie.name, cookie.value, cookie.attributes) return { id: user.id, username: user.username, displayName: user.username, createdAt: user.createdAt.toISOString(), } }, ) fastify.post( '/auth/login', { schema: { summary: 'Login', tags: ['Auth'], operationId: 'auth.login', body: Type.Object({ username: Type.String({ minLength: 1 }), password: Type.String({ minLength: 1 }), }), response: { 200: UserSchema, }, }, config: { skipAuth: true, }, }, async (req, reply) => { const user = await fastify.prisma.user.findFirst({ where: { username: req.body.username }, select: { id: true, username: true, displayName: true, createdAt: true, password: true, }, }) if (!user) { return reply.notFound('Incorrect username or password') } const validPassword = await bcrypt.compare(req.body.password, user.password) if (!validPassword) { return reply.notFound('Incorrect username or password') } const session = await fastify.lucia.createSession(user.id, {}) const cookie = fastify.lucia.createSessionCookie(session.id) reply.setCookie(cookie.name, cookie.value, cookie.attributes) return { ...user, createdAt: user.createdAt.toISOString(), } }, ) fastify.get( '/auth/me', { schema: { summary: 'Me', tags: ['Auth'], operationId: 'auth.me', response: { 200: UserSchema, }, }, }, async (req) => { const user = req.user! return { id: user.id, username: user.username, displayName: user.displayName, createdAt: user.createdAt.toISOString(), } }, ) fastify.post( '/auth/logout', { schema: { summary: 'Logout', tags: ['Auth'], operationId: 'auth.logout', }, }, async (req, reply) => { if (req.session) await fastify.lucia.invalidateSession(req.session.id) const blank = fastify.lucia.createBlankSessionCookie() reply.setCookie(blank.name, blank.value, blank.attributes) }, ) } export default plugin