Skip to content

Commit

Permalink
Super admin endpoint for database stats
Browse files Browse the repository at this point in the history
  • Loading branch information
codyebberson committed May 14, 2024
1 parent 7b3bc8c commit a43e906
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 0 deletions.
10 changes: 10 additions & 0 deletions packages/app/src/admin/SuperAdminPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export function SuperAdminPage(): JSX.Element {
.catch((err) => showNotification({ color: 'red', message: normalizeErrorString(err), autoClose: false }));
}

function getDatabaseStats(): void {
startAsyncJob(medplum, 'Get Database Stats', 'admin/super/dbstats', {});
}

return (
<Document width={600}>
<Title order={1}>Super Admin</Title>
Expand Down Expand Up @@ -159,6 +163,12 @@ export function SuperAdminPage(): JSX.Element {
<Button type="submit">Force Set Password</Button>
</Stack>
</Form>
<Divider my="lg" />
<Title order={2}>Database Stats</Title>
<p>Query current table statistics from the database.</p>
<Form onSubmit={getDatabaseStats}>
<Button type="submit">Get Database Stats</Button>
</Form>
</Document>
);
}
Expand Down
36 changes: 36 additions & 0 deletions packages/server/src/admin/super.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { rebuildR4SearchParameters } from '../seeds/searchparameters';
import { rebuildR4StructureDefinitions } from '../seeds/structuredefinitions';
import { rebuildR4ValueSets } from '../seeds/valuesets';
import { removeBullMQJobByKey } from '../workers/cron';
import { Communication } from '@medplum/fhirtypes';

export const superAdminRouter = Router();
superAdminRouter.use(authenticateRequest);
Expand Down Expand Up @@ -219,6 +220,41 @@ superAdminRouter.post(
})
);

// POST to /admin/super/dbstats
// to query database statistics.
superAdminRouter.post(
'/dbstats',
asyncWrap(async (req: Request, res: Response) => {
requireSuperAdmin();
requireAsync(req);

await sendAsyncResponse(req, res, async () => {
const systemRepo = getSystemRepo();
const client = getDatabasePool();
const sql = `
SELECT * FROM (
SELECT table_schema, table_name, pg_relation_size('"'||table_schema||'"."'||table_name||'"') AS table_size
FROM information_schema.tables
) tables
WHERE table_size > 0
ORDER BY table_size DESC;
`;

const result = await client.query(sql);

const contentString = result.rows
.map((row) => `${row.table_schema}.${row.table_name}: ${row.table_size}`)
.join('\n');

await systemRepo.createResource<Communication>({
resourceType: 'Communication',
status: 'completed',
payload: [{ contentString }],
});
});
})
);

export function requireSuperAdmin(): AuthenticatedRequestContext {
const ctx = getAuthenticatedContext();
if (!ctx.project.superAdmin) {
Expand Down

0 comments on commit a43e906

Please sign in to comment.