{"openapi":"3.1.0","info":{"title":"SourcePage API","description":"Dohledá přímý sken stránky podle tištěného čísla stránky napříč podporovanými\ndigitálními zdroji. Pokud má odpověď `citation_source_url`, může klient přes SourcePage\nkompletně nahradit přímé volání Kramerius API.\n\n## Použití\n\n- Veřejný endpoint přes gateway: `POST /sourcepage/api/` (routuje se na `POST /resolve`),\n  JSON body s `Content-Type: application/json`.\n- `GET /sourcepage/api/` bez parametrů zobrazí interaktivní dokumentaci (Swagger UI);\n  GET/OPTIONS s query parametry nebo body se zamítá (`405 method_not_allowed`).\n- Request hlavičky odpovídají internímu backendu (`X-Requested-With: XMLHttpRequest`,\n  `Accept: application/json`); klienti mohou použít běžné fetch hlavičky.\n\n## Povinná pole\n\n- `input` — URL, URN, OAI identifikátor nebo Wikidata QID.\n- `page` — tištěné číslo stránky na skenu (`24`, `III`, `5a`, …).\n\n## Podporované zdroje a povolené vstupy\n\n- **Archive.org** — URL `https://archive.org/details/{item}/page/...`, Wikidata `P724`.\n- **Digitale Sammlungen (BSB, Mnichov)** — `https://digitale-sammlungen.de/de/view/{bsbid}?page=N`,\n  URN `urn:nbn:de:bvb:{full-urn}`.\n- **Wienbibliothek Digital** — `https://www.digital.wienbibliothek.at/wbrndg/content/titleinfo/{id}`,\n  OAI `oai:www.digital.wienbibliothek.at:{id}`, URN `urn:nbn:at:AT-WBR-{id}`.\n- **Sammlungen UB Frankfurt** — `https://sammlungen.ub.uni-frankfurt.de/freimann/content/titleinfo/{id}`,\n  URN `urn:nbn:de:hebis:30:{id}`, Wikidata `P11981`.\n- **SBC – Śląska Biblioteka Cyfrowa (dLibra, Wrocław)** — `https://www.sbc.org.pl/dlibra/publication/{id}`\n  a `oai:www.sbc.org.pl:{id}`. PDF pipeline: při prvním dotazu API stáhne PDF dokumentu,\n  seskládá trvalý OCR page index a stránku extrahuje podle tištěného čísla; první request\n  u nového dokumentu může trvat i minuty (klient má ukazovat průběh, PHP proxy má delší timeout).\n- **ÖNB ANNO** — `https://anno.onb.ac.at/cgi-content/anno?aid={aid}&datum={YYYYMMDD}[&seite={N}]`;\n  při Wikidata vstupu (`P9258`) je pole `date` povinné (`YYYYMMDD` nebo `YYYY-MM-DD`).\n- **ÖNB Viewer** — `https://viewer.onb.ac.at/{id}/`.\n- **Kramerius** — `https://ndk.cz/uuid/uuid:{page_uuid}` (`page_uuid` = UUID stránky);\n  Wikidata `P13032`. Jako IIIF odkaz vrací `https://iiif.digitalniknihovna.cz/nkp/uuid:{document_uuid}`.\n- **ÖNB REC** — `https://data.onb.ac.at/rec/{id}`: resolver pokračuje jen tehdy, když vede\n  k obrázku (viewer, ABO nebo IIIF manifest); jinak skončí `all_sources_failed`.\n\nExplicitně blokované zdroje (`unsupported_source`): `wbc.poznan.pl`, `obc.opole.pl`\n(dokud nemají použitelný stránkový náhled/deep-link).\n\n## Vstupní formáty\n\n- URL podporovaného zdroje (výše).\n- `oai:{domain}:{id}` — Wienbibliothek Digital + SBC.\n- `urn:nbn:...` — překlad přes `nbn-resolving.org` na cílové URL zdroje.\n- Wikidata QID (`Q12345` nebo URL položky) — resolver zkouší priority chain\n  Wikidata property; u ANNO (`P9258`) je v requestu povinná hodnota `date`.\n  Prosté číslo bez specifikace zdroje API zamítá jako nejednoznačné.\n\n## Sémantika výsledku\n\n- `ok=true`, `selection_required=false` + `results[0]` — použitelné přímo klientem.\n- `ok=false` — vždy obsahuje `error` a `error_code`.\n- Legacy režim umí odpovědět i `status: \"selection_required\"` (manuální výběr stránky).\n\n## Chybové kódy (`error_code`)\n\nBackend:\n- 400: `missing_required_params`, `invalid_date`, `invalid_urn`\n- 404: `upstream_not_found`, `printed_page_not_found`, `urn_resolve_failed`,\n  `missing_page_numbers`, `missing_canvases`, `all_sources_failed`, `kramerius_result_not_found`\n- 405: `method_not_allowed` (GET/OPTIONS s parametry nebo body)\n- 422: `unsupported_source`\n- 502: `invalid_upstream_json`, `missing_canvas_image`, `invalid_kramerius_response`\n\nPHP proxy navíc: `cors_origin_not_allowed` (403), `sourcepage_api_unavailable` (502),\n`invalid_sourcepage_response` (502) — když backend neodpovídá JSONem.\n\n## CORS\n\n- Výchozí whitelist: `tools.daelba.eu`, `tools.daelba.cz`, `localhost`/`127.0.0.1`\n  na portech `8080`, `8081`, `8002` a `https://www.wikidata.org`.\n- Konfigurace prostředím: `CORS_MODE` (`whitelist`/`blacklist`), `CORS_ALLOWED_ORIGINS`,\n  příp. `CORS_BLOCKED_ORIGINS` (csv), `CORS_ALLOW_CREDENTIALS`.\n- Bez whitelistu originů je CORS vypnuté; při `credentials=true` se wildcard `*` nepoužije.\n- Vary hlavičky: runtime `Origin`; preflight `Origin, Access-Control-Request-Method,\n  Access-Control-Request-Headers`.\n\n### curl smoke testy CORS\n\n```bash\nBASE=\"http://localhost:8002\"\n# POST z povoleného originu\ncurl -i -X POST \"$BASE/resolve\" -H \"Origin: https://tools.daelba.eu\"   -H \"Content-Type: application/json\" -d '{\"input\":\"Q96613911\",\"page\":\"140\"}'\n# Preflight\ncurl -i -X OPTIONS \"$BASE/resolve\" -H \"Origin: https://tools.daelba.eu\"   -H \"Access-Control-Request-Method: POST\" -H \"Access-Control-Request-Headers: Content-Type\"\n# Blokovaný origin (očekáváno 403 bez CORS hlaviček)\ncurl -i -X POST \"$BASE/resolve\" -H \"Origin: https://evil.example\"   -H \"Content-Type: application/json\" -d '{\"input\":\"Q96613911\",\"page\":\"140\"}'\n```\n\n## Testy\n\n- Resolver PHP testy: `php sourcepage/tests/resolve-response-utils-test.php`\n- Python testy: `sourcepage/tests/test_resolve.py`, `test_wikidata_resolver.py`,\n  `test_sbc_pdf_pipeline.py`\n- CORS lze ověřit curl příkazy uvedenými výše v sekci CORS\n","version":"1.2.0"},"paths":{"/health":{"get":{"tags":["monitoring"],"summary":"Kontrola živosti služby","description":"Vrátí stav služby pro liveness proby. Odpověď je vždy `ok=true, status=200`.","operationId":"health_health_get","responses":{"200":{"description":"Služba je živá","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Health Get"},"example":{"ok":true,"status":200}}}}}}},"/metrics":{"get":{"tags":["monitoring"],"summary":"Prometheus metriky","description":"Vrátí metriky ve formátu Prometheus (text/plain; version=0.0.4).","operationId":"metrics_metrics_get","responses":{"200":{"description":"Metriky ve formátu Prometheus","content":{"application/json":{"schema":{}},"text/plain":{"schema":{"type":"string"}}}}}}},"/resolve":{"get":{"tags":["resolve"],"summary":"Dotaz na metadata stránky (URL/http query mód)","description":"Verze pro GET requesty — metadata o stávajících resolve mechanismech. Vrací seznam podporovaných parametrů, typy vstupů a struktury odpovědi.","operationId":"resolve_get_docs_resolve_get","responses":{"200":{"description":"Metadata o resolve endpointu","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Resolve Get Docs Resolve Get"},"example":{"ok":true,"version":"1.2.0","supported_sources":["kramerius","archive_org","digitale_sammlungen","sbc_dlibra","wienbibliothek","frankfurt","onb_viewer","onb_anno","onb_data"],"error_codes":["missing_required_params","invalid_date","unsupported_source","all_sources_failed","upstream_not_found","invalid_upstream_json"]}}}}}},"post":{"tags":["resolve"],"summary":"Vyřešení odkazu na stránku ve zdrojovém dokumentu","description":"Přijme JSON payload a vrátí metadata stránky z libovolného podporovaného zdroje (Kramerius, Archive.org, Digitale Sammlungen, SBC, Wienbibliothek, Frankfurt, ÖNB, Wikisource). Zdroj se detekuje automaticky podle `input` (URL, UUID, Wikidata QID, OAI nebo URN).","operationId":"resolve_resolve_post","parameters":[{"name":"input","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Zdrojový odkaz: URL, UUID, Wikidata QID nebo OAI identifikátor.","examples":["https://ndk.cz/view/uuid:abc123"],"title":"Input"},"description":"Zdrojový odkaz: URL, UUID, Wikidata QID nebo OAI identifikátor."},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Číslo stránky nebo rozsah (např. `14` nebo `14–31`).","examples":["14"],"title":"Page"},"description":"Číslo stránky nebo rozsah (např. `14` nebo `14–31`)."},{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Datum čísla ve formátu YYYYMMDD nebo YYYY-MM-DD.","examples":["19240515"],"title":"Date"},"description":"Datum čísla ve formátu YYYYMMDD nebo YYYY-MM-DD."}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Payload"},"examples":{"uuid_lookup":{"summary":"UUID lookup (Kramerius přímý odkaz)","value":{"input":"https://ndk.cz/view/uuid:35d64c24-9cf2-408b-9147-935f1323be20"}},"wikidata_lookup":{"summary":"Wikidata QID lookup","value":{"input":"Q96613911","page":"140","date":"1849-04-01"}},"archive_org_lookup":{"summary":"Archive.org lookup","value":{"input":"https://archive.org/details/abc123","page":"42"}},"oai_lookup":{"summary":"OAI identifikátor (Wienbibliothek)","value":{"input":"oai:www.digital.wienbibliothek.at:12345"}}}}}},"responses":{"200":{"description":"Úspěšné vyřešení.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Resolve Resolve Post"},"example":{"ok":true,"status":200,"input":"https://ndk.cz/view/uuid:abc123","results":[{"source_type":"kramerius","page":"14","viewer_url":"..."}]}}}},"400":{"description":"Chybějící povinné parametry nebo neplatný formát (např. date)."},"404":{"description":"Zdroj/stránka nebyla nalezena nebo selhaly všechny zdroje."},"422":{"description":"Nepodporovaný zdroj nebo neplatná stránka."},"502":{"description":"Nadřazená služba nedostupná."}}}},"/":{"post":{"tags":["resolve"],"summary":"Vyřešení odkazu (alias /resolve)","description":"Alias endpointu `/resolve` pro zpětnou kompatibilitu. Přijímá stejný JSON payload.","operationId":"resolve_root__post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Úspěšné vyřešení, viz /resolve.","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Resolve Root  Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"resolve","description":"Hlavní endpoint pro dohledání metadata stránky."},{"name":"monitoring","description":"Liveness a Prometheus metriky."}]}