feat(api): admin backoffice router — meshes, sessions, invites, audit
Extends the Hono adminRouter with four new read-only mesh admin modules:
meshes, sessions, invites, audit. Each ships {queries,router}.ts following
the existing users/organizations/customers pattern (paginated Drizzle
transactions, getOrderByFromSort sorting, ilike search, enum filters).
- GET /admin/meshes — paginated list with owner join + member count subquery
- GET /admin/meshes/:id — detail: members, presences, invites, last 50 audit
events (returns {mesh: null,...} shell on not-found to stay single-shape
for Hono RPC inference)
- GET /admin/sessions — live WS presences across every mesh, joined to
member/mesh for display, status + active/disconnected filters
- GET /admin/invites — invite tokens w/ mesh + createdBy user joins,
revoked/expired filters
- GET /admin/audit — mesh audit log with eventType/meshId/date filters
Summary endpoint extended: new GET /admin/summary/mesh returns
{meshes, activeMeshes, totalPresences, activePresences, messages24h}.
Messages24h derived from audit_log where event_type='message_sent'
in the past 24h.
Schemas live in packages/api/src/schema/mesh-admin.ts, re-exported from
the schema barrel. All mesh/role/transport enums mirror the DB enums
from packages/db/src/schema/mesh.ts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,23 @@ import { Hono } from "hono";
|
||||
|
||||
import { enforceAdmin, enforceAuth } from "../../middleware";
|
||||
|
||||
import { auditRouter } from "./audit/router";
|
||||
import { getMessages24hCount } from "./audit/queries";
|
||||
import { getCustomersCount } from "./customers/queries";
|
||||
import { customersRouter } from "./customers/router";
|
||||
import { invitesRouter } from "./invites/router";
|
||||
import {
|
||||
getActiveMeshesCount,
|
||||
getMeshesCount,
|
||||
} from "./meshes/queries";
|
||||
import { meshesRouter } from "./meshes/router";
|
||||
import { getOrganizationsCount } from "./organizations/queries";
|
||||
import { organizationsRouter } from "./organizations/router";
|
||||
import {
|
||||
getActivePresencesCount,
|
||||
getPresencesCount,
|
||||
} from "./sessions/queries";
|
||||
import { sessionsRouter } from "./sessions/router";
|
||||
import { getUsersCount } from "./users/queries";
|
||||
import { usersRouter } from "./users/router";
|
||||
|
||||
@@ -15,6 +28,10 @@ export const adminRouter = new Hono()
|
||||
.route("/users", usersRouter)
|
||||
.route("/organizations", organizationsRouter)
|
||||
.route("/customers", customersRouter)
|
||||
.route("/meshes", meshesRouter)
|
||||
.route("/sessions", sessionsRouter)
|
||||
.route("/invites", invitesRouter)
|
||||
.route("/audit", auditRouter)
|
||||
.get("/summary", async (c) => {
|
||||
const [users, organizations, customers] = await Promise.all([
|
||||
getUsersCount(),
|
||||
@@ -23,4 +40,22 @@ export const adminRouter = new Hono()
|
||||
]);
|
||||
|
||||
return c.json({ users, organizations, customers });
|
||||
})
|
||||
.get("/summary/mesh", async (c) => {
|
||||
const [meshes, activeMeshes, totalPresences, activePresences, messages24h] =
|
||||
await Promise.all([
|
||||
getMeshesCount(),
|
||||
getActiveMeshesCount(),
|
||||
getPresencesCount(),
|
||||
getActivePresencesCount(),
|
||||
getMessages24hCount(),
|
||||
]);
|
||||
|
||||
return c.json({
|
||||
meshes,
|
||||
activeMeshes,
|
||||
totalPresences,
|
||||
activePresences,
|
||||
messages24h,
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user