New /api/admin/ares proxy (browser cannot reach ares.gov.cz - CORS+CSP): GET /ico/:ico and GET /search?q= map the MFCR REST API to the form shape (street incl. orientation numbers, "511 01" PSC format, DIC). Guarded by customers.create/edit + warehouse.manage; token errors map to Czech messages. Shared AresAdornment button sits in the Nazev and ICO fields of both modals: ICO mode fills directly, name mode searches with a result picker. Tested against live ARES (BOHA, ICO 22599851). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
import { FastifyInstance } from "fastify";
|
|
import { requireAnyPermission } from "../../middleware/auth";
|
|
import { success, error } from "../../utils/response";
|
|
import { aresLookupByIco, aresSearchByName } from "../../services/ares.service";
|
|
|
|
// Czech messages for the service's token errors.
|
|
const ARES_ERRORS: Record<string, { message: string; status: number }> = {
|
|
invalid_ico: { message: "IČO musí být 8 číslic", status: 400 },
|
|
not_found: {
|
|
message: "Subjekt s tímto IČO nebyl v ARES nalezen",
|
|
status: 404,
|
|
},
|
|
ares_unavailable: {
|
|
message: "Registr ARES je momentálně nedostupný, zkuste to později",
|
|
status: 502,
|
|
},
|
|
};
|
|
|
|
/**
|
|
* ARES proxy — the browser cannot call ares.gov.cz directly (CORS + CSP), so
|
|
* the customer/supplier modals go through these endpoints. Guarded by the
|
|
* permissions of the two modals that use the prefill (customer editing and
|
|
* warehouse supplier management); ARES data itself is public.
|
|
*/
|
|
export default async function aresRoutes(fastify: FastifyInstance) {
|
|
const guard = requireAnyPermission(
|
|
"customers.create",
|
|
"customers.edit",
|
|
"warehouse.manage",
|
|
);
|
|
|
|
// GET /ico/:ico — single subject by IČO
|
|
fastify.get<{ Params: { ico: string } }>(
|
|
"/ico/:ico",
|
|
{ preHandler: guard },
|
|
async (request, reply) => {
|
|
const result = await aresLookupByIco(request.params.ico);
|
|
if ("error" in result) {
|
|
const e = ARES_ERRORS[result.error];
|
|
return error(reply, e.message, e.status);
|
|
}
|
|
return success(reply, result);
|
|
},
|
|
);
|
|
|
|
// GET /search?q=… — subjects by (part of) business name, max 10
|
|
fastify.get<{ Querystring: { q?: string } }>(
|
|
"/search",
|
|
{ preHandler: guard },
|
|
async (request, reply) => {
|
|
const q = String(request.query.q ?? "").trim();
|
|
if (q.length < 2) {
|
|
return error(reply, "Zadejte alespoň 2 znaky názvu", 400);
|
|
}
|
|
const result = await aresSearchByName(q);
|
|
if ("error" in result && !Array.isArray(result)) {
|
|
const e = ARES_ERRORS[result.error];
|
|
return error(reply, e.message, e.status);
|
|
}
|
|
return success(reply, result);
|
|
},
|
|
);
|
|
}
|