T14 fixup: route ordering + UUID guard for /api/agents/:id
Verification after commit d4c02a3 found Hono matching /:id BEFORE /activity
when the bare /api/agents/activity request hit the API — Postgres returned a
500 cast error because id='activity' was not a valid UUID.
Two changes:
1. Move GET /api/agents/activity registration to BEFORE GET /:id so Hono's
matcher picks the static path before the param path.
2. Add a UUID-format guard at the top of GET /api/agents/:id — returns
404 when id is not a UUID. Defense in depth: prevents future 500s if
a similarly-shaped static route collides with /:id.
Parent: t_e1cbd87d
This commit is contained in:
@@ -100,11 +100,35 @@ agentRoutes.post("/", async (c) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// GET /api/agents/activity — All activity (bare path, no agent filter)
|
||||||
|
agentRoutes.get("/activity", async (c) => {
|
||||||
|
try {
|
||||||
|
await requireAuth(c);
|
||||||
|
const items = await db.select()
|
||||||
|
.from(agentActivity)
|
||||||
|
.orderBy(desc(agentActivity.createdAt))
|
||||||
|
.limit(100);
|
||||||
|
return c.json({ items, totalItems: items.length });
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AuthError) return c.json({ error: { code: error.code, message: error.message } }, error.status as any);
|
||||||
|
console.error("[agents] GET /activity error:", error);
|
||||||
|
return c.json({ error: { code: "INTERNAL_ERROR", message: "Failed to get activity" } }, 500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// GET /api/agents/:id — Read
|
// GET /api/agents/:id — Read
|
||||||
agentRoutes.get("/:id", async (c) => {
|
agentRoutes.get("/:id", async (c) => {
|
||||||
try {
|
try {
|
||||||
await requireAuth(c);
|
await requireAuth(c);
|
||||||
const id = c.req.param("id");
|
const id = c.req.param("id");
|
||||||
|
// Guard: /:id must be a UUID. Hono matches /:id before /activity when the
|
||||||
|
// param path was registered first; without this guard we get a Postgres
|
||||||
|
// "invalid input syntax for type uuid" 500 on /api/agents/activity.
|
||||||
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) {
|
||||||
|
return c.json({ error: { code: "NOT_FOUND", message: "Agent not found" } }, 404);
|
||||||
|
}
|
||||||
const [agent] = await db.select().from(agents).where(eq(agents.id, id)).limit(1);
|
const [agent] = await db.select().from(agents).where(eq(agents.id, id)).limit(1);
|
||||||
if (!agent) return c.json({ error: { code: "NOT_FOUND", message: "Agent not found" } }, 404);
|
if (!agent) return c.json({ error: { code: "NOT_FOUND", message: "Agent not found" } }, 404);
|
||||||
return c.json(agent);
|
return c.json(agent);
|
||||||
@@ -224,22 +248,6 @@ agentRoutes.get("/:id/permissions", async (c) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// GET /api/agents/activity — All activity (bare path, no agent filter)
|
|
||||||
agentRoutes.get("/activity", async (c) => {
|
|
||||||
try {
|
|
||||||
await requireAuth(c);
|
|
||||||
const items = await db.select()
|
|
||||||
.from(agentActivity)
|
|
||||||
.orderBy(desc(agentActivity.createdAt))
|
|
||||||
.limit(100);
|
|
||||||
return c.json({ items, totalItems: items.length });
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof AuthError) return c.json({ error: { code: error.code, message: error.message } }, error.status as any);
|
|
||||||
console.error("[agents] GET /activity error:", error);
|
|
||||||
return c.json({ error: { code: "INTERNAL_ERROR", message: "Failed to get activity" } }, 500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// GET /api/agents/:id/activity — Agent activity log (or all if id=_all)
|
// GET /api/agents/:id/activity — Agent activity log (or all if id=_all)
|
||||||
agentRoutes.get("/:id/activity", async (c) => {
|
agentRoutes.get("/:id/activity", async (c) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user