import type { LoaderFunctionArgs } from "@remix-run/node"; import type { Method } from "@prisma/client"; import { authenticate } from "../shopify.server"; import { resolveAvailabilityRequest } from "../services/availability-request.server"; // Public endpoint, reachable only through Shopify's App Proxy (signature // verified by authenticate.public.appProxy) — this is what the storefront // Theme App Extension calls. Requests to https://{shop}/apps/scheduling/* // forward here because shopify.app.toml's [app_proxy].url already includes // the /apps/scheduling prefix, so this file's path (apps.scheduling.*) // mirrors the shop-facing URL exactly. The actual resolution logic lives in // services/availability-request.server.ts, shared with the POS route // (pos.scheduling.availability.tsx) — same pool, same code, different auth. const VALID_METHODS = new Set(["SHIPPING", "LOCAL_DELIVERY", "PICKUP"]); export const loader = async ({ request }: LoaderFunctionArgs) => { const { session } = await authenticate.public.appProxy(request); if (!session) { return Response.json({ error: "Shop not found" }, { status: 404 }); } const url = new URL(request.url); const methodParam = url.searchParams.get("method"); if (!methodParam || !VALID_METHODS.has(methodParam as Method)) { return Response.json({ error: "Invalid or missing method" }, { status: 400 }); } const result = await resolveAvailabilityRequest(session.shop, { method: methodParam as Method, locationId: url.searchParams.get("locationId") || undefined, postalCode: url.searchParams.get("postalCode") || undefined, address: url.searchParams.get("address") || undefined, days: Number(url.searchParams.get("days")) || undefined, }); if (!result.locationId) { return Response.json(result, { status: result.error === "No active location configured" ? 404 : 200 }); } return Response.json(result); };