cc upt
This commit is contained in:
@@ -186,7 +186,12 @@ Pokryté oblasti:
|
|||||||
|
|
||||||
Aplikace poslouchá na `0.0.0.0` a portu `PORT` s výchozí hodnotou `3000`. `ROOT_PATH` se používá pro dokumentaci a testovací requesty za reverse proxy, například `/apps/sap-bo`.
|
Aplikace poslouchá na `0.0.0.0` a portu `PORT` s výchozí hodnotou `3000`. `ROOT_PATH` se používá pro dokumentaci a testovací requesty za reverse proxy, například `/apps/sap-bo`.
|
||||||
|
|
||||||
`/docs` je Swagger UI napojené na `/openapi.json`. V Swaggeru použij `Authorize` pro vyplnění povinných `X-SAP-B1-*` hlaviček a potom `Try it out` u konkrétní operace. OpenAPI `servers` respektuje `ROOT_PATH`, takže za AppFactory proxy volá například `/apps/sap-bo/api/business-partners`, ne root doménu.
|
`/docs` je Swagger UI, které načítá OpenAPI dokument přes relativní endpoint
|
||||||
|
`openapi.json` (servírovaný jako `/docs/openapi.json`, alias `/openapi.json`). Díky tomu
|
||||||
|
se dokument načte lokálně i za AppFactory proxy bez hardcodování prefixu. V Swaggeru použij
|
||||||
|
`Authorize` pro vyplnění povinných `X-SAP-B1-*` hlaviček a potom `Try it out` u konkrétní
|
||||||
|
operace. OpenAPI `servers` se nastaví z `ROOT_PATH` (fallback `X-Forwarded-Prefix`, jinak
|
||||||
|
`/`), takže za proxy volá například `/apps/sap-bo/api/business-partners`, ne root doménu.
|
||||||
|
|
||||||
## TODO ověřit v konkrétní instalaci SAP Business One
|
## TODO ověřit v konkrétní instalaci SAP Business One
|
||||||
|
|
||||||
|
|||||||
@@ -49,9 +49,20 @@ HTTP API hlavičky:
|
|||||||
## Reverse proxy a Swagger
|
## Reverse proxy a Swagger
|
||||||
|
|
||||||
Aplikace běží za AppFactory proxy na `/apps/<app-id>` (Caddy `handle_path` prefix před
|
Aplikace běží za AppFactory proxy na `/apps/<app-id>` (Caddy `handle_path` prefix před
|
||||||
předáním do containeru odstraní). Proto:
|
předáním do containeru odstraní, takže container vidí routy bez prefixu a z requestu
|
||||||
|
veřejnou cestu nelze odvodit). Stejný přístup jako sousední služba `idoklad`:
|
||||||
|
|
||||||
- OpenAPI `servers` obsahuje `ROOT_PATH` (`/apps/<app-id>`), takže Swagger `Try it out`
|
- OpenAPI dokument se servíruje **pod stejným `/docs` prefixem** jako UI na
|
||||||
volá `…/apps/<app-id>/api/<resource>`.
|
`GET /docs/openapi.json` (plus alias `GET /openapi.json` pro přímý přístup).
|
||||||
- Swagger UI načítá OpenAPI dokument z `ROOT_PATH + /openapi.json`, ne z kořene domény.
|
- Swagger UI načítá dokument přes **relativní** endpoint `openapi.json`, takže se
|
||||||
- Lokálně bez `ROOT_PATH` se vše chová relativně ke kořeni (`/openapi.json`, server `/`).
|
v prohlížeči vyhodnotí jako `{prefix}/docs/openapi.json` lokálně i za proxy, bez
|
||||||
|
hardcodování `/apps/<app-id>`.
|
||||||
|
- `servers[0].url` se nastaví z `ROOT_PATH` (fallback hlavička `X-Forwarded-Prefix`,
|
||||||
|
nakonec `/`), takže Swagger `Try it out` volá `…/apps/<app-id>/api/<resource>`.
|
||||||
|
|
||||||
|
| Kontrola | Lokálně (bez ROOT_PATH) | S `ROOT_PATH=/apps/sap-bo` |
|
||||||
|
|---|---|---|
|
||||||
|
| `GET /health` | 200 | 200 |
|
||||||
|
| `GET /docs` (Swagger UI) | 200 | 200 |
|
||||||
|
| `GET /docs/openapi.json` | 200 | 200 |
|
||||||
|
| `servers[0].url` v OpenAPI | `/` | `/apps/sap-bo` |
|
||||||
|
|||||||
+26
-28
@@ -581,11 +581,23 @@ function openApiDocument(basePath = "") {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function swaggerOptions(basePath = "") {
|
function resolveBasePath(req: Request): string {
|
||||||
|
// Behind the AppFactory reverse proxy the public prefix is /apps/<app-id>, but the
|
||||||
|
// handle_path route strips it before the request reaches this container, so it cannot be
|
||||||
|
// read from the request path. ROOT_PATH is the sanctioned source; X-Forwarded-Prefix is a
|
||||||
|
// fallback for proxies that forward it. Empty when running without a proxy.
|
||||||
|
const forwardedPrefix = getHeader(req, "x-forwarded-prefix");
|
||||||
|
return (rootPath || forwardedPrefix || "").replace(/\/+$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function swaggerUiOptions() {
|
||||||
return {
|
return {
|
||||||
customSiteTitle: "SAP Business One connector API",
|
customSiteTitle: "SAP Business One connector API",
|
||||||
swaggerOptions: {
|
swaggerOptions: {
|
||||||
url: `${basePath}/openapi.json`.replace("//", "/"),
|
// Relative endpoint (same approach as the sibling idoklad service): the browser resolves
|
||||||
|
// it against the docs page, i.e. {prefix}/docs/openapi.json, so the spec loads locally and
|
||||||
|
// behind the AppFactory proxy without hardcoding the /apps/<app-id> prefix.
|
||||||
|
url: "openapi.json",
|
||||||
displayRequestDuration: true,
|
displayRequestDuration: true,
|
||||||
persistAuthorization: true,
|
persistAuthorization: true,
|
||||||
tryItOutEnabled: true,
|
tryItOutEnabled: true,
|
||||||
@@ -604,36 +616,22 @@ app.get("/health", (_req, res) => {
|
|||||||
res.json({ status: "ok" });
|
res.json({ status: "ok" });
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/openapi.json", (_req, res) => {
|
// OpenAPI document served under the same /docs prefix as the UI so the relative UI endpoint
|
||||||
res.json(openApiDocument(rootPath));
|
// resolves correctly locally and behind the reverse-proxy prefix. servers[0].url advertises the
|
||||||
|
// public prefix (ROOT_PATH) so Swagger UI "Try it out" targets {prefix}/api/..., not the host root.
|
||||||
|
app.get("/docs/openapi.json", (req, res) => {
|
||||||
|
res.json(openApiDocument(resolveBasePath(req)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Documented root alias for direct access to the OpenAPI document.
|
||||||
|
app.get("/openapi.json", (req, res) => {
|
||||||
|
res.json(openApiDocument(resolveBasePath(req)));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use("/docs", swaggerUi.serve, swaggerUi.setup(undefined, swaggerUiOptions()));
|
||||||
|
|
||||||
addResourceEndpoints();
|
addResourceEndpoints();
|
||||||
|
|
||||||
// Behind the AppFactory reverse proxy the container is reached via /apps/<app-id> and
|
|
||||||
// the prefix is stripped (handle_path) before requests arrive here. The Swagger UI page is
|
|
||||||
// therefore served at the public /apps/<app-id>/docs, so the OpenAPI document URL and the
|
|
||||||
// "Try it out" base path must carry ROOT_PATH; otherwise the browser would resolve them
|
|
||||||
// against the bare origin and bypass the app.
|
|
||||||
app.use("/docs", swaggerUi.serveFiles(undefined, swaggerOptions(rootPath)), swaggerUi.setup(undefined, swaggerOptions(rootPath)));
|
|
||||||
|
|
||||||
if (rootPath) {
|
|
||||||
app.get(rootPath, (_req, res) => {
|
|
||||||
res.json(serviceMetadata());
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get(rootPath + "/health", (_req, res) => {
|
|
||||||
res.json({ status: "ok" });
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get(rootPath + "/openapi.json", (_req, res) => {
|
|
||||||
res.json(openApiDocument(rootPath));
|
|
||||||
});
|
|
||||||
|
|
||||||
addResourceEndpoints(rootPath);
|
|
||||||
app.use(rootPath + "/docs", swaggerUi.serveFiles(undefined, swaggerOptions(rootPath)), swaggerUi.setup(undefined, swaggerOptions(rootPath)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
app.listen(port, "0.0.0.0", () => {
|
app.listen(port, "0.0.0.0", () => {
|
||||||
console.log(serviceId + " listening on port " + port);
|
console.log(serviceId + " listening on port " + port);
|
||||||
|
|||||||
Reference in New Issue
Block a user