Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 1x 1x 16x 16x 1x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 39x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 3x 6x 39x 39x | import { AppError } from "@ontrack/backend-common";
import type { FastifyPluginAsync } from "fastify";
import type { AppConfig } from "../config.js";
import { resolveTrackGeocode } from "../lib/route-layout.js";
import { assertServiceToken } from "../lib/service-token.js";
const reverseResponseSchema = {
type: "object",
required: ["locality", "routeLayoutCode", "startLocality", "endLocality"],
additionalProperties: false,
properties: {
locality: { type: "string" },
routeLayoutCode: { type: "string", enum: ["CIRCULAR", "LINEAR"] },
startLocality: { type: "string" },
endLocality: { type: "string" },
},
} as const;
function parseCoordinate(
value: unknown,
field: "lat" | "lon",
min: number,
max: number,
): number {
const raw = Array.isArray(value) ? value[0] : value;
const parsed =
typeof raw === "string" || typeof raw === "number" ? Number.parseFloat(String(raw)) : Number.NaN;
if (!Number.isFinite(parsed) || parsed < min || parsed > max) {
throw new AppError(400, "INVALID_COORDINATES", `Invalid ${field}`);
}
return parsed;
}
export const geocodeRoutes: FastifyPluginAsync<{ config: AppConfig }> = async (app, opts) => {
const { config } = opts;
/** Reverse geocode старта и финиша GPX + тип маршрута (круговой/линейный). */
app.get(
"/geocode/reverse",
{
schema: {
querystring: {
type: "object",
required: ["startLat", "startLon", "endLat", "endLon"],
additionalProperties: false,
properties: {
startLat: { type: "string" },
startLon: { type: "string" },
endLat: { type: "string" },
endLon: { type: "string" },
},
},
response: {
200: reverseResponseSchema,
},
},
},
async (request, reply) => {
const token = request.headers["x-service-token"];
assertServiceToken(Array.isArray(token) ? token[0] : token, config.serviceToken);
const query = request.query as {
startLat?: string;
startLon?: string;
endLat?: string;
endLon?: string;
};
const startLat = parseCoordinate(query.startLat, "lat", -90, 90);
const startLon = parseCoordinate(query.startLon, "lon", -180, 180);
const endLat = parseCoordinate(query.endLat, "lat", -90, 90);
const endLon = parseCoordinate(query.endLon, "lon", -180, 180);
const result = await resolveTrackGeocode(startLat, startLon, endLat, endLon, {
baseUrl: config.nominatimBaseUrl,
userAgent: config.nominatimUserAgent,
});
return reply.send(result);
},
);
};
|