import type { FastifyPluginCallback, FastifyRequest } from 'fastify' import fp from 'fastify-plugin' import type { Selectable } from 'kysely' import { jsonArrayFrom } from 'kysely/helpers/postgres' import changePassword from './auth/routes/change_password.ts' import resetPassword from './auth/routes/reset_password.ts' import login from './auth/routes/login.ts' import logout from './auth/routes/logout.ts' import register from './auth/routes/register.ts' import verifyEmail from './auth/routes/verify_email.ts' import type { User } from '../../shared/types.db.ts' const userPromiseSymbol = Symbol('user') const serialize = (user: Selectable) => Promise.resolve(user.id) const auth: FastifyPluginCallback<{ prefix?: string }> = (fastify, options, done) => { const prefix = options.prefix || '' const { db } = fastify fastify.decorate('auth', (request, reply, done) => { if (!request.session.userId) return reply.status(401).send() done() }) fastify.decorateRequest( 'login', function login(user) { return serialize(user).then((result) => { this.session.set('userId', result) return this.session.save().catch(console.error) }) }, ['session'], ) fastify.decorateRequest( 'logout', function logout() { return this.session.destroy() }, ['session'], ) fastify.decorateRequest('getUser', function getUser(this: FastifyRequest & { [userPromiseSymbol]?: Promise }) { if (!this.session || !this.session.userId) { return Promise.resolve(null) } if (!this[userPromiseSymbol]) { this[userPromiseSymbol] = db .selectFrom('user') .selectAll() .select((eb) => jsonArrayFrom( eb.selectFrom('role as r').innerJoin('users_roles as ur', 'ur.roleId', 'r.id').select(['id', 'name']), ).as('roles'), ) .where('id', '=', this.session.userId) .executeTakeFirstOrThrow() .then(({ password: _, ...rest }) => rest) } return this[userPromiseSymbol] }) fastify.decorateRequest( 'user', { getter() { return this.getUser() }, }, ['getUser'], ) fastify.get(`${prefix}/current`, function (request) { return request.user }) fastify.post(prefix + '/change-password', changePassword) fastify.post(prefix + '/reset-password', resetPassword) fastify.get(prefix + '/verify-email', verifyEmail) fastify.post(prefix + '/login', login) fastify.get(prefix + '/logout', logout) fastify.post(prefix + '/register', register) done() } export default fp(auth, { name: 'auth', fastify: '5.x', })