99 lines
2.6 KiB
TypeScript
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',
|
|
})
|