Files
admin c7fd9d45a2
CI (SIAX Cloud) / integration (push) Has been skipped
masterplan-lock / check (pull_request) Successful in 3s
masterplan-lock / check (push) Successful in 3s
CI (SIAX Cloud) / security (push) Successful in 15s
Security Scan (SIAX Cloud) / security (push) Successful in 14s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 18s
CI (SIAX Cloud) / quality (pull_request) Successful in 31s
CI (SIAX Cloud) / quality (push) Successful in 40s
CI (SIAX Cloud) / security (pull_request) Successful in 42s
CI (SIAX Cloud) / integration (pull_request) Successful in 45s
fix(prod): map SiaxError in the 7 legacy money routes — DEPENDENCY_UNAVAILABLE/503 instead of opaque 500 (0pt#53)
getSqlForDurableStore throws SiaxError(DEPENDENCY_UNAVAILABLE, httpStatus 503,
retryable) when a money-bearing store would fall back to an in-memory Map in a
production runtime (the durable-store guard). The /api/0pt/* surface already
maps it via src/http/siax-error-response.ts; the seven legacy routes outside
the middleware matcher did not — the documented 503 leaked as Next's opaque
500 with no body.

- app/api/siax-error-mapping.ts: shared isSiaxError + siaxErrorResponseForLegacy
- orders (POST/GET), orders/[id] (GET/PATCH), subscriptions (GET/POST),
  payments/deposit, payments/release, admin/metrics: try/catch -> mapper,
  each route's own typed error contract preserved (idempotency conflict 409 etc.)
- webhooks/stripe: store outage -> 503 received:false reason:store_unavailable
  so Stripe's delivery retry path stays open (never a silent received:true)
- tests/api/legacy-routes-siax-error-mapping.test.ts: pins the 503 + body
2026-09-22 04:33:57 +02:00

30 lines
1.2 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { createSubscription, listSubscriptions } from "@/lib/subscriptions-store";
import { isSiaxError, siaxErrorResponseForLegacy } from "../siax-error-mapping";
// GET /api/subscriptions — feeds list. POST — convert an order to a recurring feed.
export async function GET() {
try {
return NextResponse.json(await listSubscriptions());
} catch (err: unknown) {
// 0pt#53: durable-store failure -> documented DEPENDENCY_UNAVAILABLE/503.
if (isSiaxError(err)) return siaxErrorResponseForLegacy(err);
throw err;
}
}
export async function POST(req: NextRequest) {
const parsed = await req.json().catch(() => ({}));
const body = parsed && typeof parsed === "object" ? parsed : {};
if (!body.order_id) {
return NextResponse.json({ error: { code: "bad_request", message: "order_id krävs" } }, { status: 400 });
}
try {
const sub = await createSubscription(body.order_id, body.cadence ?? "weekly", body.webhook_url ?? null);
return NextResponse.json(sub, { status: 201 });
} catch (err: unknown) {
if (isSiaxError(err)) return siaxErrorResponseForLegacy(err);
throw err;
}
}