f5e7e966e3
- 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
59 lines
2.2 KiB
TypeScript
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.' });
|
|
}
|
|
});
|
|
};
|