diff --git a/web/src/routes/api/v1/waypoint/cluster/+server.ts b/web/src/routes/api/v1/waypoint/cluster/+server.ts new file mode 100644 index 00000000..91288f88 --- /dev/null +++ b/web/src/routes/api/v1/waypoint/cluster/+server.ts @@ -0,0 +1,33 @@ +import { handleError } from "$lib/util/api_util"; +import { json, type RequestEvent } from "@sveltejs/kit"; +import { z } from "zod"; + +const WaypointClusterPointSchema = z.object({ + id: z.string().min(1), + lat: z.number().min(-90).max(90), + lon: z.number().min(-180).max(180), +}); + +const WaypointClusterSchema = z.object({ + category: z.string().length(15).or(z.literal("")).optional(), + photos: z.array(WaypointClusterPointSchema), + waypoints: z.array(WaypointClusterPointSchema), +}); + +export async function POST(event: RequestEvent) { + try { + const data = WaypointClusterSchema.parse(await event.request.json()); + const response = await event.locals.pb.send("/waypoint/cluster", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(data), + fetch: event.fetch, + }); + + return json(response); + } catch (e) { + return handleError(e); + } +} diff --git a/web/src/routes/trail/edit/[id]/+page.svelte b/web/src/routes/trail/edit/[id]/+page.svelte index 8e36a65c..c2decf89 100644 --- a/web/src/routes/trail/edit/[id]/+page.svelte +++ b/web/src/routes/trail/edit/[id]/+page.svelte @@ -77,7 +77,6 @@ import RouteEditor from "$lib/components/trail/route_editor.svelte"; import { TagCreateSchema } from "$lib/models/api/tag_schema.js"; import { convertDMSToDD } from "$lib/models/gpx/utils.js"; - import { getPb } from "$lib/pocketbase"; import { Tag } from "$lib/models/tag.js"; import { searchLocationReverse, @@ -541,24 +540,17 @@ } try { - const clusterResponse: WaypointPhotoClusterResponse = - await getPb().send("/waypoint/cluster", { - method: "POST", - headers: { - "content-type": "application/json", + const clusterResponse = await clusterWaypointPhotos({ + category: $formData.category, + photos: [ + { + id: waypointMergeCheckPhotoId, + lat: savedWaypoint.lat, + lon: savedWaypoint.lon, }, - body: JSON.stringify({ - category: $formData.category, - photos: [ - { - id: waypointMergeCheckPhotoId, - lat: savedWaypoint.lat, - lon: savedWaypoint.lon, - }, - ], - waypoints: existingWaypoints, - }), - }); + ], + waypoints: existingWaypoints, + }); const matchingCluster = clusterResponse.clusters.find( (cluster) => @@ -1323,6 +1315,36 @@ clusters: WaypointPhotoCluster[]; } + interface WaypointClusterPoint { + id: string; + lat: number; + lon: number; + } + + interface WaypointPhotoClusterRequest { + category?: string; + photos: WaypointClusterPoint[]; + waypoints: WaypointClusterPoint[]; + } + + async function clusterWaypointPhotos( + data: WaypointPhotoClusterRequest, + ): Promise { + const response = await fetch("/api/v1/waypoint/cluster", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(data), + }); + + if (!response.ok) { + throw await response.json(); + } + + return (await response.json()) as WaypointPhotoClusterResponse; + } + const waypointMergeCheckPhotoId = "__waypoint_merge_check__"; async function handleWaypointPhotoSelection() { @@ -1374,20 +1396,14 @@ let clusterResponse: WaypointPhotoClusterResponse; try { - clusterResponse = await getPb().send("/waypoint/cluster", { - method: "POST", - headers: { - "content-type": "application/json", - }, - body: JSON.stringify({ - category: $formData.category, - photos: photoCoords.map((coords) => ({ - id: coords.id, - lat: coords.latitude, - lon: coords.longitude, - })), - waypoints: getExistingWaypointClusterInputs(), - }), + clusterResponse = await clusterWaypointPhotos({ + category: $formData.category, + photos: photoCoords.map((coords) => ({ + id: coords.id, + lat: coords.latitude, + lon: coords.longitude, + })), + waypoints: getExistingWaypointClusterInputs(), }); } catch (e) { show_toast(