Public Access
1
0
Files
leads4less/server/src/routes/account.ts
T
pguerrerox f5e7e966e3 feat: introduce app-admin authorization and audit logging
- add migrations for owner/member workspace roles and application admins

- centralize /admin access checks with DB-backed admin resolution

- audit admin analytics/billing route access

- update account/admin UI typing and env/docs for ADMIN_EMAILS fallback behavior
2026-05-25 15:25:59 +00:00

59 lines
2.2 KiB
TypeScript

import type { FastifyPluginAsync } from 'fastify';
import { ZodError, z } from 'zod';
import { requireAuth } from '../auth/middleware.js';
import { getDbPool } from '../db/pool.js';
import { buildAccountPageData, ensureWorkspaceForUser, updateUserProfile, updateWorkspaceName } from '../account/repository.js';
const updateAccountSchema = z.object({
displayName: z.string().trim().min(1).max(120).optional(),
avatarUrl: z.string().trim().url().nullable().optional().or(z.literal('')),
workspaceName: z.string().trim().min(1).max(160).optional(),
});
export const accountRoutes: FastifyPluginAsync = async (app) => {
app.get('/account/me', { preHandler: requireAuth }, async (request, reply) => {
try {
const account = await buildAccountPageData(getDbPool(), request.authUser!);
return { account };
} catch (error) {
request.log.error(error);
return reply.code(500).send({ error: 'Failed to load account page.' });
}
});
app.patch('/account/me', { preHandler: requireAuth }, async (request, reply) => {
try {
const payload = updateAccountSchema.parse(request.body);
const db = getDbPool();
const workspace = await ensureWorkspaceForUser(db, request.authUser!);
if (!workspace) {
return reply.code(500).send({ error: 'Failed to load workspace.' });
}
if (payload.workspaceName && workspace.role !== 'owner') {
return reply.code(403).send({ error: 'You do not have permission to update this workspace.' });
}
const profile = await updateUserProfile(db, request.authUser!.id, {
displayName: payload.displayName,
avatarUrl: payload.avatarUrl === '' ? null : payload.avatarUrl,
});
if (payload.workspaceName) {
await updateWorkspaceName(db, workspace.id, payload.workspaceName);
}
const account = await buildAccountPageData(db, profile);
return { account };
} catch (error) {
if (error instanceof ZodError) {
return reply.code(400).send({ error: error.issues[0]?.message || 'Invalid account payload.' });
}
request.log.error(error);
return reply.code(500).send({ error: 'Failed to update account.' });
}
});
};