"""Vstupní bod aplikace pplcplapi. Stateless FastAPI služba běžící v AppFactory za reverse proxy `/apps/`. Multi-tenant proxy nad PPL CPL API (Create Package Label): tvorba zásilek a tisk etiket, tracking, objednávky svozu, číselníky, výdejní místa. Přihlašovací údaje PPL se předávají per-request v X- hlavičkách, nikdy se neukládají ani nelogují. Jedinou výjimkou je in-memory cache OAuth tokenů (PPL limituje vydávání tokenů na 12/min), klíčovaná hashem údajů. """ import os from contextlib import asynccontextmanager from fastapi import FastAPI from .config import APP_NAME, APP_VERSION, ROOT_PATH from .cpl_client import close_client from .errors import register_exception_handlers from .logging_config import configure_logging from .routers import codelists, customer, lookups, meta, orders, proxy, shipments configure_logging() DESCRIPTION = """ Proxy nad **PPL CPL API** (Create Package Label) — tvorba zásilek a etiket, tracking, objednávky svozu, číselníky, výdejní místa a našeptávač adres. ### Přihlašovací údaje (hlavičky) Secrets se předávají v hlavičkách u každého requestu — nikdy v těle ani v URL: - `X-Client-Id` — PPL CPL ClientId - `X-Client-Secret` — PPL CPL ClientSecret - `X-Environment` — volitelně `production` (default) nebo `test` Služba si sama vyžádá a cachuje OAuth Bearer token (platnost 30 min, PPL limit 12 tokenů/min) a dodržuje minimální rozestup requestů 40 ms. ### Asynchronní tok zásilek CPL API zpracovává zásilky i objednávky dávkově: `POST /shipments/batch` vrátí `batchId` → stav se sleduje přes `GET /shipments/batch/{batchId}` → etikety přes `GET /shipments/batch/{batchId}/labels`. Pro jednoduché použití slouží `POST /shipments/create-and-wait` (a `POST /orders/create-and-wait`), které celý tok provedou v jednom requestu. ### Diakritika CPL API přijímá pouze Latin znaky bez diakritiky. Texty v tělech create requestů se defaultně transliterují (`Jiří` → `Jiri`); vypnout lze query parametrem `transliterate=false`. ### Generická proxy Cokoliv, co nemá vlastní endpoint: `/proxy/{cesta}` předá request 1:1 na CPL API s doplněnou autentizací (např. `GET /proxy/codelist/product?Limit=10&Offset=0`). ### Chyby JSON `{error, message, detail}` — `detail` obsahuje problem+json z PPL. 400 = validační chyba, 401 = chybějící/odmítnuté přihlašovací údaje, 403 = chybí oprávnění, 404 = záznam neexistuje, 429 = rate limit, 502 = výpadek PPL. """ @asynccontextmanager async def lifespan(app: FastAPI): yield await close_client() app = FastAPI( title=APP_NAME, version=APP_VERSION, description=DESCRIPTION, root_path=ROOT_PATH, lifespan=lifespan, ) register_exception_handlers(app) app.include_router(meta.router) app.include_router(shipments.router) app.include_router(orders.router) app.include_router(codelists.router) app.include_router(lookups.router) app.include_router(customer.router) app.include_router(proxy.router) if __name__ == "__main__": import uvicorn uvicorn.run( "app.main:app", host="0.0.0.0", port=int(os.getenv("PORT", "8000")), )