feat(api): owner card move route using the caller's own token

This commit is contained in:
Dave 2026-09-16 10:02:51 +02:00
parent f4568e4745
commit 4649c82432

View file

@ -0,0 +1,69 @@
import { getServerPb } from "@/lib/pb/server";
import { requireSession } from "@/lib/auth/session";
import { AccessError } from "@/lib/auth/access";
import { jsonError, jsonOk } from "@/lib/http/json";
import type { CardRecord } from "@/lib/pb/types";
/**
* PATCH /api/owner/cards/:id/move — the owner drops a card in a new place.
*
* Unlike the /api/client/* routes, this one uses the caller's OWN PocketBase
* token, not a superuser's. The owner-only write rule from Plan 1a is what
* authorises the update, so a client manager reaching this route is refused by
* the database even though the handler itself is simple.
*
* That is why there is no field whitelist here: PocketBase is already
* enforcing who may write. Only the two positional fields are forwarded
* regardless, so a stray body field cannot ride along.
*/
export async function PATCH(
request: Request,
context: { params: Promise<{ id: string }> }
): Promise<Response> {
try {
const session = await requireSession();
const { id } = await context.params;
if (session.role !== "owner") {
return jsonError("Card not found", 404);
}
let body: unknown;
try {
body = await request.json();
} catch {
return jsonError("Invalid request body", 400);
}
const record = body as Record<string, unknown> | null;
const column = record?.column;
const position = record?.position;
if (typeof column !== "string" || !column) {
return jsonError("A destination column is required", 400);
}
if (typeof position !== "number" || !Number.isFinite(position)) {
return jsonError("A numeric position is required", 400);
}
const pb = await getServerPb();
const updated = await pb
.collection("cards")
.update<CardRecord>(id, { column, position });
return jsonOk({
card: {
id: updated.id,
column: updated.column,
position: updated.position,
},
});
} catch (error) {
if (error instanceof AccessError) {
return jsonError(error.message, error.status);
}
console.error("[owner-move] failed", error);
return jsonError("Could not move the card", 500);
}
}