Complete security hardening: zero-migration foundation, edge headers, rate-limit atomics, body limits
Local Build and Deploy / deploy (push) Successful in 58s

- Make @/lib/safe-action re-export from foundation layer so all 13+
  existing server actions instantly get request tracing, rate limiting,
  and structured error handling without any code changes
- Add HSTS, CSP, X-Frame-Options, X-Content-Type-Options to edge proxy
  (src/proxy.ts) — ran at Cloudflare/Vercel edge for all non-asset routes
- Fix rate-limit.ts race condition: compute newCount before assignment
  to shrink the read-modify-write window; add memory-key prefix to
  avoid collisions with Redis keys
- Add request body size limit (10 MB default) to api-handler.ts with
  per-route override via maxBodyBytes option
- Remove unused imports and clean up backward-compat types
This commit is contained in:
openhands committed 2026-07-13 12:09:43 +02:00
1 parent f6ad030c5b
commit e2fc7ea1a4
6 files changed
+55 -178

No files matched your search

+4 -19
View File
@@ -1,11 +1,9 @@
import { ZodError } from "zod";
// ── Action Result Type ───────────────────────────────────────────────
import { handleActionError as foundationHandle } from "@/lib/foundation/action";
export type ActionResult<T = Record<string, unknown>> =
{ ok: true; data?: T } | { ok: false; error: string; fieldErrors?: Record<string, string[]> };
// ── Helper to build results ──────────────────────────────────────────
| { ok: true; data?: T }
| { ok: false; error: string; fieldErrors?: Record<string, string[]> };
export function actionOk<T = Record<string, unknown>>(data?: T): ActionResult<T> {
return { ok: true, data: data ?? ({} as T) };
@@ -15,8 +13,6 @@ export function actionError(message: string): ActionResult<never> {
return { ok: false, error: message };
}
// ── Throwable error ──────────────────────────────────────────────────
export class ActionError extends Error {
constructor(message: string) {
super(message);
@@ -24,8 +20,6 @@ export class ActionError extends Error {
}
}
// ── Error handler ────────────────────────────────────────────────────
export function handleActionError(error: unknown): ActionResult<never> {
if (error instanceof ZodError) {
return {
@@ -37,14 +31,5 @@ export function handleActionError(error: unknown): ActionResult<never> {
if (error instanceof Error && error.name === "ActionError") {
return { ok: false, error: error.message };
}
// Prisma P2025
if (
error instanceof Error &&
error.constructor.name === "PrismaClientKnownRequestError" &&
(error as Error & { code?: string }).code === "P2025"
) {
return { ok: false, error: "Not found" };
}
console.error("[Action error]", error);
return { ok: false, error: "Internal server error" };
return foundationHandle(error) as ActionResult<never>;
}