docs/cms-api.md describes the endpoints TYPO3 serves to MyE&P and the chatbot (/api/hotel, /api/product, /api/calendar-hotels, /api/content/*): key, parameters, responses, errors. It is the other side of MyE&P's API consumer guide. docs/calendars.md covers the price calendar, the Belegungskalender and the iCal feed: which product gets which, the MyE&P endpoints behind them, caching, troubleshooting, and the rule that a product's calendar hotel must carry the BusPro code, not a group house's CMS code (which broke three example trips). The README replaces the TYPO3 base distribution boilerplate and links both. The plugin renaming SQL in ep_products' README is obsolete since the migration. The cmsApiKey description names /api/calendar-hotels. Co-Authored-By: Claude Opus 5.5 <[email protected]>
102 lines
4.4 KiB
Markdown
102 lines
4.4 KiB
Markdown
# CMS API
|
|
|
|
Endpoints TYPO3 serves to other systems: **MyE&P** (hotel and product content, calendar hotels)
|
|
and the **chatbot** (content feed). They are answered by `EP\EpProducts\Middleware\ApiMiddleware`
|
|
before the page resolver, so they do not depend on a page and are never page-cached.
|
|
|
|
The other side of these contracts is documented in MyE&P's `docs/api-consumer-guide.md`
|
|
(`EP/myep`).
|
|
|
|
## Authentication
|
|
|
|
All endpoints need the shared key `cmsApiKey` from the `ep_products` extension configuration
|
|
(Settings → Extension Configuration → ep_products, tab ContentFeed):
|
|
|
|
- header `X-Api-Key: <key>` (preferred, query strings end up in access logs), or
|
|
- query parameter `?key=<key>`.
|
|
|
|
A missing or wrong key answers `403 {"message": "Invalid or missing api key"}`. An empty
|
|
`cmsApiKey` denies every request. MyE&P sends the key as `APP_CMS_API_KEY`; both sides must be
|
|
changed together.
|
|
|
|
## `GET /api/hotel?hotel=<code>`
|
|
|
|
Content of one hotel, for MyE&P's group house pages and booking flow.
|
|
|
|
- `hotel`: the TYPO3 hotel code or one of its matchcode aliases. In MyE&P this is the
|
|
accommodation's **CMS code** (e.g. `DGSGRP`), which can differ from its calendar code.
|
|
- `400` without `hotel`, `404 {"message": "Hotel not found"}` for an unknown code.
|
|
|
|
```json
|
|
{
|
|
"name": "…", "code": "…",
|
|
"description": "…", "features": "…", "room_types": "…",
|
|
"additional_information": "…", "address": "…",
|
|
"images": {"original": [{"url": "…", "alt": "…", "title": "…"}], "resized": […]},
|
|
"icons": […],
|
|
"region": {"name": "…", "latitude": …, "longitude": …, "webcam": "…", "ski_area": "…",
|
|
"ski_area_extended": "…", "description": "…", "news": "…", "length": …,
|
|
"altitude": …, "lifts": …, "images": {…}, "region_maps": [{"url": "…", "alt": "…", "title": "…"}]}
|
|
}
|
|
```
|
|
|
|
## `GET /api/product?product=<code>[&hotel=<code>]`
|
|
|
|
Content of one product, optionally with a hotel.
|
|
|
|
- `product`: the product code. `400` without it, `404 {"message": "Product not found"}` for an
|
|
unknown code.
|
|
- `hotel` (optional): adds `hotel` with the same content as `/api/hotel`.
|
|
|
|
```json
|
|
{
|
|
"product": {"name": "…", "code": "…", "images": {…}},
|
|
"country": {"name": "…"},
|
|
"region": {…},
|
|
"city": {"name": "…"},
|
|
"hotel": {…}
|
|
}
|
|
```
|
|
|
|
## `GET /api/calendar-hotels`
|
|
|
|
The calendar hotel of every visible product: the hotel codes whose availability TYPO3 shows
|
|
(iCal feed, availability calendar, price calendar). MyE&P syncs its contingent snapshot for
|
|
these codes in addition to its accommodations, so hotels offered only in winter, which have no
|
|
accommodation in MyE&P, have availability too. See [calendars.md](calendars.md).
|
|
|
|
- Only products and hotels that are not hidden or deleted; hotels without a code are left out.
|
|
- Sorted by code; products by uid. Names have line breaks collapsed.
|
|
- `season`: `"s"` (summer), `"w"` (winter) or `null`.
|
|
|
|
```json
|
|
[
|
|
{"code": "DKI", "name": "Sportclub Schwendi", "products": [
|
|
{"uid": 155, "name": "Sportclub Schwendi", "season": "w"},
|
|
{"uid": 362, "name": "Gruppenhaus Schwendi", "season": "s"}
|
|
]}
|
|
]
|
|
```
|
|
|
|
Every code in this list should be a **BusPro calendar code**. A group house's content record
|
|
(`…GRP`, `…SO`) set as a product's calendar hotel puts its CMS code in here, which bpn-connect does
|
|
not know (see [calendars.md](calendars.md#the-calendar-hotel-field)).
|
|
|
|
## `GET /api/content/manifest` and `GET /api/content/records?type=<type>&uids=<uids>`
|
|
|
|
The content feed for the chatbot (`EP\EpProducts\ContentFeed\ContentFeedApi`).
|
|
|
|
- `manifest`: everything the feed publishes (pages of the configured sites and records), with a
|
|
modification timestamp per item. Cached for 10 minutes.
|
|
- `records`: up to 100 records of one type per request. `type` is one of `product`, `hotel`,
|
|
`region`, `country`, `city`, `concept`, `news`; `uids` comma-separated. Uids that are no longer
|
|
eligible are left out, not answered with an error (the consumer reads them as deleted).
|
|
- `400` for an unknown type, no uids or more than 100 uids; `404` for other paths.
|
|
- `503` with `Retry-After: 3600` when the feed is switched off (`contentFeedEnabled = 0`, the kill
|
|
switch for when the host is struggling), `503` when `contentFeedSites` is empty. The consumer
|
|
keeps what it has on a 503; nothing is deleted on the chatbot side.
|
|
|
|
Configuration (extension configuration `ep_products`, tab ContentFeed): `contentFeedEnabled`,
|
|
`contentFeedSites`, `contentFeedLanguages`, `contentFeedExcludedPages`,
|
|
`contentFeedNewsDetailPages`.
|