brf/server/plugins/auth.ts

99 lines
2.6 KiB
TypeScript

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<User>) => 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<ANY> }) {
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',
})