route.js 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. import { listFiles } from "@/lib/storage";
  2. import { getSession } from "@/lib/auth/session";
  3. import { canAccessBranch } from "@/lib/auth/permissions";
  4. import {
  5. withErrorHandling,
  6. json,
  7. badRequest,
  8. unauthorized,
  9. forbidden,
  10. } from "@/lib/api/errors";
  11. import { mapStorageReadError } from "@/lib/api/storageErrors";
  12. /**
  13. * Next.js Route Handler caching configuration (RHL-006):
  14. *
  15. * We force this route to execute dynamically on every request.
  16. *
  17. * Reasons:
  18. * - NAS contents can change at any time (new scans).
  19. * - Auth/RBAC-protected responses must not be cached/shared across users.
  20. * - We rely on a small storage-layer TTL micro-cache instead of Next route caching.
  21. */
  22. export const dynamic = "force-dynamic";
  23. /**
  24. * GET /api/files?branch=&year=&month=&day=
  25. *
  26. * Happy-path response must remain unchanged:
  27. * { "branch":"NL01", "year":"2024", "month":"10", "day":"23", "files":[...] }
  28. */
  29. export const GET = withErrorHandling(
  30. async function GET(request) {
  31. const session = await getSession();
  32. if (!session) {
  33. throw unauthorized("AUTH_UNAUTHENTICATED", "Unauthorized");
  34. }
  35. const { searchParams } = new URL(request.url);
  36. // Query params are required for this endpoint.
  37. const branch = searchParams.get("branch");
  38. const year = searchParams.get("year");
  39. const month = searchParams.get("month");
  40. const day = searchParams.get("day");
  41. const missing = [];
  42. if (!branch) missing.push("branch");
  43. if (!year) missing.push("year");
  44. if (!month) missing.push("month");
  45. if (!day) missing.push("day");
  46. if (missing.length > 0) {
  47. throw badRequest(
  48. "VALIDATION_MISSING_QUERY",
  49. "Missing required query parameter(s)",
  50. { params: missing }
  51. );
  52. }
  53. if (!canAccessBranch(session, branch)) {
  54. throw forbidden("AUTH_FORBIDDEN_BRANCH", "Forbidden");
  55. }
  56. try {
  57. const files = await listFiles(branch, year, month, day);
  58. return json({ branch, year, month, day, files }, 200);
  59. } catch (err) {
  60. throw await mapStorageReadError(err, {
  61. details: { branch, year, month, day },
  62. });
  63. }
  64. },
  65. { logPrefix: "[api/files]" }
  66. );