feat(agent-api): GET /api/agent/projects/:projectId/cards

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Dave 2026-09-25 22:35:22 +02:00
parent 4fef9233c8
commit 9b6c031e60

View file

@ -0,0 +1,72 @@
import { getAdminPb } from "@/lib/pb/admin";
import { requireAgentToken } from "@/lib/agent/auth";
import { jsonError, jsonOk } from "@/lib/http/json";
import type { CardRecord, ColumnRecord } from "@/lib/pb/types";
/**
* GET /api/agent/projects/:projectId/cards — every non-archived card in the
* project, any column, in the same order the board itself renders in.
*
* `?column=inbox` filters to the Inbox column only, matching `is_inbox` —
* never a literal column name, since column names are editable per project.
*/
export async function GET(
request: Request,
context: { params: Promise<{ projectId: string }> }
): Promise<Response> {
const denied = requireAgentToken(request);
if (denied) return denied;
const { projectId } = await context.params;
const pb = await getAdminPb();
const project = await pb.collection("projects").getOne(projectId).catch(() => null);
if (!project) return jsonError("Project not found", 404);
const columns = await pb.collection("columns").getFullList<ColumnRecord>({
filter: `project = "${projectId}"`,
sort: "position",
});
const columnsById = new Map(columns.map((c) => [c.id, c]));
const url = new URL(request.url);
const onlyInbox = url.searchParams.get("column") === "inbox";
let cards = await pb.collection("cards").getFullList<CardRecord>({
filter: `project = "${projectId}" && archived = false`,
});
if (onlyInbox) {
cards = cards.filter((card) => columnsById.get(card.column)?.is_inbox === true);
}
cards.sort((a, b) => {
const colA = columnsById.get(a.column)?.position ?? 0;
const colB = columnsById.get(b.column)?.position ?? 0;
return colA - colB || a.position - b.position;
});
const out = cards
.map((card) => {
const column = columnsById.get(card.column);
if (!column) return null;
return {
id: card.id,
number: card.number,
title: card.title,
description: card.description,
priority: card.priority,
target_date: card.target_date,
size_band: card.size_band,
column: {
id: column.id,
name: column.name,
is_inbox: column.is_inbox,
is_done: column.is_done,
},
};
})
.filter((c): c is NonNullable<typeof c> => c !== null);
return jsonOk(out);
}