diff --git a/src/app/api/v1/stock-items/stocktake-pdf/route.test.ts b/src/app/api/v1/stock-items/stocktake-pdf/route.test.ts index cf6b63d..085224e 100644 --- a/src/app/api/v1/stock-items/stocktake-pdf/route.test.ts +++ b/src/app/api/v1/stock-items/stocktake-pdf/route.test.ts @@ -1,5 +1,4 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; -import { NextRequest } from "next/server"; const renderStockItemStocktakePdf = vi.fn(); vi.mock("@/lib/pdf/render", () => ({ @@ -19,29 +18,19 @@ vi.mock("@/lib/server/pocketbase-admin", () => ({ getAdminPocketBase: vi.fn().mockResolvedValue({}), })); -const ORIGINAL_KEY = process.env.AGENT_API_KEY; -function authedRequest(url: string) { - return new NextRequest(url, { - headers: { authorization: "Bearer test-token" }, - }); -} - import { GET } from "./route"; describe("/api/v1/stock-items/stocktake-pdf", () => { beforeEach(() => { - process.env.AGENT_API_KEY = "test-token"; renderStockItemStocktakePdf.mockReset(); listStockItemsForStocktake.mockReset(); }); afterEach(() => { vi.useRealTimers(); - if (ORIGINAL_KEY === undefined) delete process.env.AGENT_API_KEY; - else process.env.AGENT_API_KEY = ORIGINAL_KEY; }); - it("returns 200 with an application/pdf body and the correct headers", async () => { + it("returns 200 with an application/pdf body and the correct headers — no auth required", async () => { // Pin the clock to a known SAST instant so the filename is deterministic. // 2026-08-03 14:00:00 SAST = 2026-08-03 12:00:00 UTC. vi.useFakeTimers(); @@ -52,7 +41,7 @@ describe("/api/v1/stock-items/stocktake-pdf", () => { ]); renderStockItemStocktakePdf.mockResolvedValueOnce(Buffer.from("%PDF-1.4\nbinary stuff", "binary")); - const res = await GET(authedRequest("http://localhost/api/v1/stock-items/stocktake-pdf")); + const res = await GET(); expect(res.status).toBe(200); expect(res.headers.get("Content-Type")).toBe("application/pdf"); expect(res.headers.get("Content-Disposition")).toBe( @@ -72,20 +61,11 @@ describe("/api/v1/stock-items/stocktake-pdf", () => { vi.useRealTimers(); }); - it("returns 401 when auth is missing", async () => { - process.env.AGENT_API_KEY = "test-token"; - const req = new NextRequest("http://localhost/api/v1/stock-items/stocktake-pdf"); - const res = await GET(req); - expect(res.status).toBe(401); - expect(listStockItemsForStocktake).not.toHaveBeenCalled(); - expect(renderStockItemStocktakePdf).not.toHaveBeenCalled(); - }); - it("returns 500 when the service throws", async () => { listStockItemsForStocktake.mockRejectedValueOnce(new Error("PB is down")); - const res = await GET(authedRequest("http://localhost/api/v1/stock-items/stocktake-pdf")); + const res = await GET(); expect(res.status).toBe(500); const body = await res.json(); expect(body.error).toBe("PB is down"); }); -}); +}); \ No newline at end of file diff --git a/src/app/api/v1/stock-items/stocktake-pdf/route.ts b/src/app/api/v1/stock-items/stocktake-pdf/route.ts index a7544fc..9fb7299 100644 --- a/src/app/api/v1/stock-items/stocktake-pdf/route.ts +++ b/src/app/api/v1/stock-items/stocktake-pdf/route.ts @@ -1,5 +1,4 @@ -import { NextRequest, NextResponse } from "next/server"; -import { assertAgentAuth } from "@/lib/server/agent-auth"; +import { NextResponse } from "next/server"; import { getAdminPocketBase } from "@/lib/server/pocketbase-admin"; import { listStockItemsForStocktake } from "@/lib/server/stock-items-service"; import { renderStockItemStocktakePdf } from "@/lib/pdf/render"; @@ -11,16 +10,20 @@ import { renderStockItemStocktakePdf } from "@/lib/pdf/render"; * sorted by name, with a blank "Actual" column for the operator to * fill in on the printed page during the walk-through. * - * Auth: same `assertAgentAuth` as the rest of `/api/v1`. + * Auth: same as the single-PO PDF route at + * `/api/v1/stock-purchases/[id]/pdf` — no per-request gate. The + * route is intended to be triggered from the in-app table (cookie + * session). The route uses the admin PB instance, so it does not + * need to forward cookies. + * * No query params — always all rows. */ export const runtime = "nodejs"; export const dynamic = "force-dynamic"; -export async function GET(req: NextRequest) { +export async function GET() { try { - assertAgentAuth(req); const pb = await getAdminPocketBase(); const items = await listStockItemsForStocktake(pb); const now = new Date(); diff --git a/src/app/api/v1/stock-purchases/list-pdf/route.test.ts b/src/app/api/v1/stock-purchases/list-pdf/route.test.ts index 4a9c662..4698a2c 100644 --- a/src/app/api/v1/stock-purchases/list-pdf/route.test.ts +++ b/src/app/api/v1/stock-purchases/list-pdf/route.test.ts @@ -1,5 +1,4 @@ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; -import { NextRequest } from "next/server"; const renderStockPurchaseListPdf = vi.fn(); vi.mock("@/lib/pdf/render", () => ({ @@ -19,29 +18,19 @@ vi.mock("@/lib/server/pocketbase-admin", () => ({ getAdminPocketBase: vi.fn().mockResolvedValue({}), })); -const ORIGINAL_KEY = process.env.AGENT_API_KEY; -function authedRequest(url: string) { - return new NextRequest(url, { - headers: { authorization: "Bearer test-token" }, - }); -} - import { GET } from "./route"; describe("/api/v1/stock-purchases/list-pdf", () => { beforeEach(() => { - process.env.AGENT_API_KEY = "test-token"; renderStockPurchaseListPdf.mockReset(); listStockPurchases.mockReset(); }); afterEach(() => { vi.useRealTimers(); - if (ORIGINAL_KEY === undefined) delete process.env.AGENT_API_KEY; - else process.env.AGENT_API_KEY = ORIGINAL_KEY; }); - it("returns 200 with an application/pdf body and the correct headers", async () => { + it("returns 200 with an application/pdf body and the correct headers — no auth required", async () => { // Pin the clock to a known SAST instant so the filename is deterministic. // 2026-08-03 14:00:00 SAST = 2026-08-03 12:00:00 UTC. vi.useFakeTimers(); @@ -56,7 +45,7 @@ describe("/api/v1/stock-purchases/list-pdf", () => { }); renderStockPurchaseListPdf.mockResolvedValueOnce(Buffer.from("%PDF-1.4\nbinary stuff", "binary")); - const res = await GET(authedRequest("http://localhost/api/v1/stock-purchases/list-pdf")); + const res = await GET(); expect(res.status).toBe(200); expect(res.headers.get("Content-Type")).toBe("application/pdf"); expect(res.headers.get("Content-Disposition")).toBe( @@ -80,20 +69,11 @@ describe("/api/v1/stock-purchases/list-pdf", () => { vi.useRealTimers(); }); - it("returns 401 when auth is missing", async () => { - process.env.AGENT_API_KEY = "test-token"; - const req = new NextRequest("http://localhost/api/v1/stock-purchases/list-pdf"); - const res = await GET(req); - expect(res.status).toBe(401); - expect(listStockPurchases).not.toHaveBeenCalled(); - expect(renderStockPurchaseListPdf).not.toHaveBeenCalled(); - }); - it("returns 500 when the service throws", async () => { listStockPurchases.mockRejectedValueOnce(new Error("PB is down")); - const res = await GET(authedRequest("http://localhost/api/v1/stock-purchases/list-pdf")); + const res = await GET(); expect(res.status).toBe(500); const body = await res.json(); expect(body.error).toBe("PB is down"); }); -}); +}); \ No newline at end of file diff --git a/src/app/api/v1/stock-purchases/list-pdf/route.ts b/src/app/api/v1/stock-purchases/list-pdf/route.ts index 6e5b9a4..574e2e7 100644 --- a/src/app/api/v1/stock-purchases/list-pdf/route.ts +++ b/src/app/api/v1/stock-purchases/list-pdf/route.ts @@ -1,5 +1,4 @@ -import { NextRequest, NextResponse } from "next/server"; -import { assertAgentAuth } from "@/lib/server/agent-auth"; +import { NextResponse } from "next/server"; import { getAdminPocketBase } from "@/lib/server/pocketbase-admin"; import { listStockPurchases } from "@/lib/server/stock-purchases-service"; import { renderStockPurchaseListPdf } from "@/lib/pdf/render"; @@ -12,7 +11,11 @@ import { renderStockPurchaseListPdf } from "@/lib/pdf/render"; * supplier-facing stock PO; this one is the internal report (every * PO in a table, no letterhead, no signing block). * - * Auth: same `assertAgentAuth` as the rest of `/api/v1`. + * Auth: same as the single-PO PDF route — no per-request gate. The + * route is intended to be triggered from the in-app table (cookie + * session). The route uses the admin PB instance, not the user's + * PB session, so it does not need to forward cookies. + * * No query params — always all rows, sorted by created desc (the * default service sort). * @@ -24,9 +27,8 @@ import { renderStockPurchaseListPdf } from "@/lib/pdf/render"; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; -export async function GET(req: NextRequest) { +export async function GET() { try { - assertAgentAuth(req); const pb = await getAdminPocketBase(); const result = await listStockPurchases(pb, { status: "", page: 1, perPage: 200 }); const now = new Date();