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) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-03-24 09:53:01 +01:00 committed by GitHub
parent 8559b89be6
commit 155d36e94c
3 changed files with 45 additions and 29 deletions

View file

@ -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;

View file

@ -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)}`,
});
}

View file

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