import type { FastifyPluginAsyncTypebox } from '@fastify/type-provider-typebox' import bcrypt from 'bcrypt' import { CreateUserPayloadSchema, LoginPayloadSchema, UserSchema } from '../plugins/schemas/auth.ts' import { TypeboxRef } from '../utils/typebox-ref.ts' const plugin: FastifyPluginAsyncTypebox = async (fastify) => { fastify.post( '/auth/register', { schema: { summary: 'Register', tags: ['Auth'], operationId: 'auth.register', body: TypeboxRef(CreateUserPayloadSchema), response: { 200: TypeboxRef(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: TypeboxRef(LoginPayloadSchema), response: { 200: TypeboxRef(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: TypeboxRef(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