feat: add superadmin management
All checks were successful
CI / Validate frontend and API (push) Successful in 1m59s
CI / Build and publish Docker images (push) Successful in 22s

This commit is contained in:
Cauê Faleiros
2026-09-01 09:54:45 -03:00
parent 2dfca33758
commit a5055238b9
21 changed files with 366 additions and 74 deletions

View File

@@ -2,6 +2,7 @@ import fastifyJwt from '@fastify/jwt';
import type { FastifyPluginAsync, preHandlerHookHandler } from 'fastify';
import fastifyPlugin from 'fastify-plugin';
import { config } from '../config.js';
import { pool } from '../db/pool.js';
export type UserRole = 'student' | 'instructor' | 'admin';
@@ -29,20 +30,35 @@ declare module 'fastify' {
const registerAuth: FastifyPluginAsync = async (app) => {
await app.register(fastifyJwt, { secret: config.JWT_SECRET });
app.decorate('authenticate', async (request, reply) => {
const verifyActiveUser = async (request: { jwtVerify: () => Promise<void>; user: AuthUser }, reply: { code: (status: number) => { send: (payload: object) => unknown } }) => {
try {
await request.jwtVerify();
} catch {
return reply.code(401).send({ error: 'Authentication required' });
reply.code(401).send({ error: 'Authentication required' });
return false;
}
const result = await pool.query<{ role: UserRole; display_name: string }>(
'select role, display_name from users where id = $1 and is_active = true',
[request.user.id],
);
const account = result.rows[0];
if (!account) {
reply.code(401).send({ error: 'This account is no longer active' });
return false;
}
request.user.role = account.role;
request.user.name = account.display_name;
return true;
};
app.decorate('authenticate', async (request, reply) => {
await verifyActiveUser(request, reply);
});
app.decorate('requireRoles', (roles: UserRole[]) => async (request, reply) => {
try {
await request.jwtVerify();
} catch {
return reply.code(401).send({ error: 'Authentication required' });
}
if (!(await verifyActiveUser(request, reply))) return;
if (!roles.includes(request.user.role)) {
return reply.code(403).send({ error: 'Insufficient permissions' });