forked from bai/curriculum-project-hub
feat(hub): add org sessions and usage admin APIs
List project agent sessions/runs and aggregate AgentRun cost and token usage by project and optional folder for org admins.
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
import { registerAccessRoutes } from "./accessRoutes.js";
|
||||
import { registerExplorerRoutes } from "./explorerRoutes.js";
|
||||
import { registerMembersRoutes } from "./membersRoutes.js";
|
||||
import { registerSessionsAndUsageRoutes } from "./sessionsRoutes.js";
|
||||
import { registerTeamsRoutes } from "./teamsRoutes.js";
|
||||
|
||||
export interface OrgRouteConfig {
|
||||
@@ -96,4 +97,8 @@ export async function registerOrgRoutes(app: FastifyInstance, config: OrgRouteCo
|
||||
prisma: config.prisma,
|
||||
sessionSecret: config.sessionSecret,
|
||||
});
|
||||
await registerSessionsAndUsageRoutes(app, {
|
||||
prisma: config.prisma,
|
||||
sessionSecret: config.sessionSecret,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import type { PrismaClient } from "@prisma/client";
|
||||
import type { FastifyInstance } from "fastify";
|
||||
import { getSessionDetail, listProjectSessions } from "../../org/sessions.js";
|
||||
import { getOrgUsage, getProjectUsage } from "../../org/usage.js";
|
||||
import { requireOrgRole, type GuardDeps } from "../auth/guards.js";
|
||||
import { handleRouteError } from "../errors.js";
|
||||
|
||||
export async function registerSessionsAndUsageRoutes(
|
||||
app: FastifyInstance,
|
||||
config: { readonly prisma: PrismaClient; readonly sessionSecret: string },
|
||||
): Promise<void> {
|
||||
const guardDeps: GuardDeps = { prisma: config.prisma, sessionSecret: config.sessionSecret };
|
||||
|
||||
app.get("/api/org/:orgSlug/projects/:projectId/sessions", async (request, reply) => {
|
||||
try {
|
||||
const { orgSlug, projectId } = request.params as { orgSlug: string; projectId: string };
|
||||
const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug });
|
||||
if (auth === null) return;
|
||||
const q = request.query as { limit?: string };
|
||||
const limit = q.limit !== undefined ? Number(q.limit) : undefined;
|
||||
return {
|
||||
sessions: await listProjectSessions(config.prisma, {
|
||||
organizationId: auth.organization.id,
|
||||
projectId,
|
||||
...(limit !== undefined && Number.isFinite(limit) ? { limit } : {}),
|
||||
}),
|
||||
};
|
||||
} catch (err) {
|
||||
return handleRouteError(reply, err);
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/org/:orgSlug/sessions/:sessionId", async (request, reply) => {
|
||||
try {
|
||||
const { orgSlug, sessionId } = request.params as { orgSlug: string; sessionId: string };
|
||||
const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug });
|
||||
if (auth === null) return;
|
||||
return await getSessionDetail(config.prisma, {
|
||||
organizationId: auth.organization.id,
|
||||
sessionId,
|
||||
});
|
||||
} catch (err) {
|
||||
return handleRouteError(reply, err);
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/org/:orgSlug/usage", async (request, reply) => {
|
||||
try {
|
||||
const { orgSlug } = request.params as { orgSlug: string };
|
||||
const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug });
|
||||
if (auth === null) return;
|
||||
const q = request.query as { from?: string; to?: string; folderId?: string };
|
||||
return await getOrgUsage(config.prisma, {
|
||||
organizationId: auth.organization.id,
|
||||
...(q.from !== undefined && q.from !== "" ? { from: new Date(q.from) } : {}),
|
||||
...(q.to !== undefined && q.to !== "" ? { to: new Date(q.to) } : {}),
|
||||
...(q.folderId !== undefined && q.folderId !== "" ? { folderId: q.folderId } : {}),
|
||||
});
|
||||
} catch (err) {
|
||||
return handleRouteError(reply, err);
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/org/:orgSlug/projects/:projectId/usage", async (request, reply) => {
|
||||
try {
|
||||
const { orgSlug, projectId } = request.params as { orgSlug: string; projectId: string };
|
||||
const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug });
|
||||
if (auth === null) return;
|
||||
const q = request.query as { from?: string; to?: string };
|
||||
return await getProjectUsage(config.prisma, {
|
||||
organizationId: auth.organization.id,
|
||||
projectId,
|
||||
...(q.from !== undefined && q.from !== "" ? { from: new Date(q.from) } : {}),
|
||||
...(q.to !== undefined && q.to !== "" ? { to: new Date(q.to) } : {}),
|
||||
});
|
||||
} catch (err) {
|
||||
return handleRouteError(reply, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user