trails/apps/planner/app/routes/new.tsx
Ullrich Schäfer 4b711abaec
Add split export: Export Route vs Export Plan
Split button with default "Export GPX" (track only) and dropdown:
- Export Route: clean track for use in any app
- Export Plan: track + waypoints + no-go areas in GPX extensions
  (trails:planning namespace) for reimporting into the planner

Also:
- Add no-go area serialization to generateGpx (GPX extensions)
- Parse no-go areas from GPX extensions on import
- Initialize no-go areas in Yjs session from imported GPX
- Save to Journal now exports track only (consistent with Export Route)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:52:39 +01:00

54 lines
1.8 KiB
TypeScript

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);
}