import { redirect, data } from "react-router"; import type { Route } from "./+types/new"; import { createSession, initializeSessionWithWaypoints, initializeSessionWithNoGoAreas } from "~/lib/sessions"; import { parseGpxAsync, extractWaypoints } from "@trails-cool/gpx"; import { checkRateLimit } from "~/lib/rate-limit"; export async function loader({ request }: Route.LoaderArgs) { const url = new URL(request.url); // Rate limit session creation by IP const ip = request.headers.get("x-forwarded-for") ?? "unknown"; const limit = checkRateLimit(`session-create:${ip}`, { maxRequests: 10 }); if (!limit.allowed) { throw data( { error: "Too many sessions created. Please try again later." }, { status: 429, headers: { "Retry-After": String(limit.retryAfterSeconds) }, }, ); } const callbackUrl = url.searchParams.get("callback"); const token = url.searchParams.get("token"); const returnUrl = url.searchParams.get("returnUrl"); const gpxEncoded = url.searchParams.get("gpx"); // Create a session with callback info const session = await createSession({ callbackUrl: callbackUrl ?? undefined, callbackToken: token ?? undefined, }); // Initialize with GPX waypoints if provided if (gpxEncoded) { try { const gpx = decodeURIComponent(gpxEncoded); const gpxData = await parseGpxAsync(gpx); initializeSessionWithWaypoints(session.id, extractWaypoints(gpxData)); if (gpxData.noGoAreas.length > 0) { initializeSessionWithNoGoAreas(session.id, gpxData.noGoAreas); } } catch { // Continue with empty session if GPX is invalid } } // Store returnUrl in the session URL for later const sessionUrl = returnUrl ? `/session/${session.id}?returnUrl=${encodeURIComponent(returnUrl)}` : `/session/${session.id}`; return redirect(sessionUrl); }