feat: improve learning and platform operations
All checks were successful
CI / Validate frontend and API (push) Successful in 45s
CI / Build and publish Docker images (push) Successful in 24s

This commit is contained in:
Cauê Faleiros
2026-09-01 15:38:14 -03:00
parent 4736fb5208
commit 3d4c72e85c
20 changed files with 292 additions and 45 deletions

View File

@@ -3,6 +3,7 @@ import { z } from 'zod';
import { pool } from '../db/pool.js';
import { createRawToken, hashToken } from '../auth/account-tokens.js';
import { config } from '../config.js';
import { recordAudit } from '../audit.js';
const userParamsSchema = z.object({
userId: z.string().uuid(),
@@ -41,6 +42,18 @@ export const adminRoutes: FastifyPluginAsync = async (app) => {
return { data: result.rows[0] };
});
app.get('/audit-log', adminAccess, async () => {
const result = await pool.query(
`select a.id, a.action, a.subject_type as "subjectType", a.subject_id as "subjectId",
a.metadata, a.created_at as "createdAt", coalesce(u.display_name, 'Sistema') as "actorName"
from audit_logs a
left join users u on u.id = a.actor_id
order by a.created_at desc
limit 50`,
);
return { data: result.rows };
});
app.get('/users/:userId', adminAccess, async (request, reply) => {
const { userId } = userParamsSchema.parse(request.params);
const result = await pool.query(
@@ -63,6 +76,7 @@ export const adminRoutes: FastifyPluginAsync = async (app) => {
values ($1, $2::user_role, 'invitation', $3, now() + interval '7 days', $4)`,
[input.email, input.role, hashToken(rawToken), request.user.id],
);
await recordAudit({ actorId: request.user.id, action: 'invitation.created', subjectType: 'invitation', metadata: { email: input.email, role: input.role }, ipAddress: request.ip });
return reply.code(201).send({ data: { inviteUrl: `${config.FRONTEND_ORIGIN}/#/invite?token=${rawToken}` } });
});
@@ -76,6 +90,7 @@ export const adminRoutes: FastifyPluginAsync = async (app) => {
values ($1, $2::user_role, 'password_reset', $3, now() + interval '1 day', $4)`,
[account.rows[0].email, account.rows[0].role, hashToken(rawToken), request.user.id],
);
await recordAudit({ actorId: request.user.id, action: 'password_reset.created', subjectType: 'user', subjectId: userId, metadata: { email: account.rows[0].email }, ipAddress: request.ip });
return { data: { resetUrl: `${config.FRONTEND_ORIGIN}/#/reset-password?token=${rawToken}` } };
});
@@ -97,6 +112,7 @@ export const adminRoutes: FastifyPluginAsync = async (app) => {
);
const account = result.rows[0];
if (!account) return reply.code(404).send({ error: 'User not found' });
await recordAudit({ actorId: request.user.id, action: 'user.updated', subjectType: 'user', subjectId: userId, metadata: input, ipAddress: request.ip });
return { data: account };
});
};