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 | 1x 1x 1x 26x 26x 26x 26x | import { AppError } from "@ontrack/backend-common";
import type { FastifyPluginAsync } from "fastify";
import type { AppConfig } from "../config.js";
import { requireVerifiedUser, type IdTokenVerifier } from "../lib/firebase-auth.js";
import { throwIfUpstreamFailed } from "../lib/http-client.js";
export const geoRoutes: FastifyPluginAsync<{
config: AppConfig;
verifyIdToken: IdTokenVerifier;
}> = async (app, opts) => {
const { config, verifyIdToken } = opts;
/**
* Анализ GPX: симплификация трека (RDP) + метрики (длина, набор высоты, bbox).
* Принимает multipart с полем `file` (как /track), проксирует сырой GPX во
* внутренний geo-service с X-Service-Token. Только Bearer + emailVerified.
*/
app.post("/analyze", async (request, reply) => {
await requireVerifiedUser(request, verifyIdToken);
const file = await request.file();
if (!file) {
throw new AppError(400, "FILE_REQUIRED", "Expected a multipart 'file' field with a GPX track");
}
const buffer = await file.toBuffer();
const tolerance = (request.query as { tolerance?: string }).tolerance;
const search =
tolerance && /^\d+(\.\d+)?$/.test(tolerance) ? `?tolerance=${tolerance}` : "";
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), config.upstreamTimeoutMs);
try {
const response = await fetch(`${config.geoServiceUrl}/v1/geo/analyze${search}`, {
method: "POST",
headers: {
"x-service-token": config.serviceToken,
"content-type": "application/gpx+xml",
},
body: new Uint8Array(buffer),
signal: controller.signal,
});
const body = Buffer.from(await response.arrayBuffer());
throwIfUpstreamFailed(response.status, body);
const ct = response.headers.get("content-type");
if (ct) {
reply.header("Content-Type", ct);
}
return reply.code(response.status).send(body);
} catch (error) {
if (error instanceof AppError) {
throw error;
}
if (error instanceof Error && error.name === "AbortError") {
throw new AppError(504, "UPSTREAM_TIMEOUT", "Upstream request timed out");
}
throw new AppError(502, "UPSTREAM_UNAVAILABLE", "Upstream service unavailable");
} finally {
clearTimeout(timeout);
}
});
};
|