From 155d36e94c577c70206a5f2af285c9c8a20e86f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Tue, 24 Mar 2026 09:53:01 +0100 Subject: [PATCH] Fix Edit in Planner: create session via API, avoid URL size limit GPX data was being encoded in URL params, causing HTTP 431 (Request Header Fields Too Large). Now the Journal creates a Planner session via POST to /api/sessions with GPX in the request body, then redirects to the session URL (154 chars vs 100KB+). Also moved edit-in-planner logic to dedicated API route for proper JSON response handling. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/journal/app/routes.ts | 1 + .../routes/api.routes.$id.edit-in-planner.ts | 41 +++++++++++++++++++ apps/journal/app/routes/routes.$id.tsx | 32 ++------------- 3 files changed, 45 insertions(+), 29 deletions(-) create mode 100644 apps/journal/app/routes/api.routes.$id.edit-in-planner.ts diff --git a/apps/journal/app/routes.ts b/apps/journal/app/routes.ts index eb4200b..58bccc4 100644 --- a/apps/journal/app/routes.ts +++ b/apps/journal/app/routes.ts @@ -13,5 +13,6 @@ export default [ route("routes/:id", "routes/routes.$id.tsx"), route("routes/:id/edit", "routes/routes.$id.edit.tsx"), route("api/routes/:id/callback", "routes/api.routes.$id.callback.ts"), + route("api/routes/:id/edit-in-planner", "routes/api.routes.$id.edit-in-planner.ts"), route("users/:username", "routes/users.$username.tsx"), ] satisfies RouteConfig; diff --git a/apps/journal/app/routes/api.routes.$id.edit-in-planner.ts b/apps/journal/app/routes/api.routes.$id.edit-in-planner.ts new file mode 100644 index 0000000..212e06c --- /dev/null +++ b/apps/journal/app/routes/api.routes.$id.edit-in-planner.ts @@ -0,0 +1,41 @@ +import { data } from "react-router"; +import type { Route } from "./+types/api.routes.$id.edit-in-planner"; +import { getSessionUser } from "~/lib/auth.server"; +import { getRouteWithVersions } from "~/lib/routes.server"; +import { createRouteToken } from "~/lib/jwt.server"; + +export async function action({ params, request }: Route.ActionArgs) { + const user = await getSessionUser(request); + if (!user) return data({ error: "Not authenticated" }, { status: 401 }); + + const route = await getRouteWithVersions(params.id); + if (!route) return data({ error: "Route not found" }, { status: 404 }); + if (route.ownerId !== user.id) return data({ error: "Not authorized" }, { status: 403 }); + + const token = await createRouteToken(params.id); + const origin = process.env.ORIGIN ?? "http://localhost:3000"; + const callbackUrl = `${origin}/api/routes/${params.id}/callback`; + const plannerUrl = process.env.PLANNER_URL ?? "http://localhost:3001"; + const returnUrl = `${origin}/routes/${params.id}`; + + // Create Planner session via API (POST body, not URL params) + const sessionResp = await fetch(`${plannerUrl}/api/sessions`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + callbackUrl, + callbackToken: token, + gpx: route.gpx ?? undefined, + }), + }); + + if (!sessionResp.ok) { + return data({ error: "Failed to create Planner session" }, { status: 502 }); + } + + const session = (await sessionResp.json()) as { url: string }; + + return data({ + url: `${plannerUrl}${session.url}?returnUrl=${encodeURIComponent(returnUrl)}`, + }); +} diff --git a/apps/journal/app/routes/routes.$id.tsx b/apps/journal/app/routes/routes.$id.tsx index 13bf944..281fe60 100644 --- a/apps/journal/app/routes/routes.$id.tsx +++ b/apps/journal/app/routes/routes.$id.tsx @@ -3,7 +3,6 @@ import { data, redirect } from "react-router"; import type { Route } from "./+types/routes.$id"; import { getSessionUser } from "~/lib/auth.server"; import { getRouteWithVersions, deleteRoute, updateRoute } from "~/lib/routes.server"; -import { createRouteToken } from "~/lib/jwt.server"; export async function loader({ params, request }: Route.LoaderArgs) { @@ -75,29 +74,6 @@ export async function action({ params, request }: Route.ActionArgs) { }); } - if (intent === "edit-in-planner") { - const route = await getRouteWithVersions(params.id); - if (!route) return data({ error: "Route not found" }, { status: 404 }); - - const token = await createRouteToken(params.id); - const origin = process.env.ORIGIN ?? "http://localhost:3000"; - const callbackUrl = `${origin}/api/routes/${params.id}/callback`; - const plannerUrl = process.env.PLANNER_URL ?? "http://localhost:3001"; - - const plannerParams = new URLSearchParams({ - callback: callbackUrl, - token, - returnUrl: `${origin}/routes/${params.id}`, - }); - - // If route has GPX, include it - if (route.gpx) { - plannerParams.set("gpx", encodeURIComponent(route.gpx)); - } - - return data({ redirect: `${plannerUrl}/new?${plannerParams}` }); - } - return data({ error: "Unknown action" }, { status: 400 }); } @@ -113,12 +89,10 @@ export default function RouteDetailPage({ loaderData }: Route.ComponentProps) { const handleEditInPlanner = useCallback(async () => { setEditLoading(true); try { - const formData = new FormData(); - formData.set("intent", "edit-in-planner"); - const resp = await fetch(`/routes/${route.id}`, { method: "POST", body: formData }); + const resp = await fetch(`/api/routes/${route.id}/edit-in-planner`, { method: "POST" }); const result = await resp.json(); - if (result.redirect) { - window.location.href = result.redirect; + if (result.url) { + window.location.href = result.url; } } finally { setEditLoading(false);