docs(readme): reverse-proxy requirements for SSE streaming paths
The AI chat SSE endpoints (POST /api/ai-chat/stream, GET /api/ai-chat/runs/<chatId>/stream, POST /api/shares/ai/stream) must bypass response buffering AND compression at every proxy in front of the app — a compressing proxy silently buffers SSE frames until the response closes (pending request, tokens arriving in one burst, reloaded tabs degrading to polling). Document the affected paths, the DevTools tell (Content-Encoding on text/event-stream), and concrete nginx/Traefik configuration in both READMEs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -125,6 +125,32 @@ Gitmost follows the upstream Docmost setup. See the Docmost
|
||||
[documentation](https://docmost.com/docs) for self-hosting and development instructions; replace the
|
||||
`docmost/docmost` image with `ghcr.io/vvzvlad/gitmost` where applicable.
|
||||
|
||||
### Reverse proxy: SSE streaming paths
|
||||
|
||||
The AI agent streams its answers over Server-Sent Events. These endpoints produce a
|
||||
long-lived `text/event-stream` response and **must bypass response buffering AND response
|
||||
compression** at every proxy in front of the app:
|
||||
|
||||
- `POST /api/ai-chat/stream` — the live agent turn stream
|
||||
- `GET /api/ai-chat/runs/<chatId>/stream` — attach/resume of a detached agent run
|
||||
(`AI_CHAT_RESUMABLE_STREAM`)
|
||||
- `POST /api/shares/ai/stream` — the anonymous public-share assistant
|
||||
|
||||
A buffering or compressing proxy does not break these with an error — it silently ruins them:
|
||||
the request hangs in `pending`, tokens stop streaming and arrive in one burst when the turn
|
||||
ends, or a reloaded tab falls back to coarse polling. The tell in DevTools is a
|
||||
`Content-Encoding: gzip/zstd` response header on a `text/event-stream` response.
|
||||
|
||||
The server already sends `X-Accel-Buffering: no` (honored by nginx unless ignored), but
|
||||
compression middleware is applied by proxy configuration, not headers:
|
||||
|
||||
- **nginx** — `proxy_buffering off; proxy_cache off; gzip off;` for these locations, e.g.
|
||||
`location ~ ^/api/(ai-chat/(stream$|runs/.+/stream$)|shares/ai/) { ... }`
|
||||
- **Traefik** — route these paths through a dedicated router **without** the `compress`
|
||||
middleware (a `compress` middleware buffers SSE frames until the response closes), e.g.
|
||||
``PathPrefix(`/api/ai-chat/stream`) || PathPrefix(`/api/ai-chat/runs/`)``. Belt-and-braces:
|
||||
`traefik.http.middlewares.<name>.compress.excludedcontenttypes: text/event-stream`.
|
||||
|
||||
## Migration from Docmost
|
||||
|
||||
Gitmost's database schema is a **strict superset** of Docmost's. Every Gitmost-specific migration
|
||||
|
||||
@@ -126,6 +126,32 @@ Gitmost повторяет процесс установки upstream-Docmost.
|
||||
смотрите в [документации](https://docmost.com/docs) Docmost; где это применимо, заменяйте образ
|
||||
`docmost/docmost` на `ghcr.io/vvzvlad/gitmost`.
|
||||
|
||||
### Reverse proxy: SSE-стриминговые пути
|
||||
|
||||
AI-агент стримит ответы через Server-Sent Events. Эти эндпоинты отдают долгоживущий
|
||||
`text/event-stream`-ответ и **обязаны обходить буферизацию И сжатие ответов** на каждом
|
||||
прокси перед приложением:
|
||||
|
||||
- `POST /api/ai-chat/stream` — живой стрим хода агента
|
||||
- `GET /api/ai-chat/runs/<chatId>/stream` — подключение/резюм detached-рана
|
||||
(`AI_CHAT_RESUMABLE_STREAM`)
|
||||
- `POST /api/shares/ai/stream` — анонимный ассистент публичных шар
|
||||
|
||||
Буферизующий или сжимающий прокси не ломает эти пути с ошибкой — он тихо их портит:
|
||||
запрос висит в `pending`, токены не стримятся и вываливаются одним куском в конце хода,
|
||||
а перезагруженная вкладка падает в грубый поллинг. Диагностический признак в DevTools —
|
||||
заголовок `Content-Encoding: gzip/zstd` на ответе с `text/event-stream`.
|
||||
|
||||
Сервер уже шлёт `X-Accel-Buffering: no` (nginx учитывает его по умолчанию), но
|
||||
compression-мидлвари управляются конфигом прокси, а не заголовками:
|
||||
|
||||
- **nginx** — `proxy_buffering off; proxy_cache off; gzip off;` для этих location,
|
||||
например `location ~ ^/api/(ai-chat/(stream$|runs/.+/stream$)|shares/ai/) { ... }`
|
||||
- **Traefik** — вести эти пути через отдельный роутер **без** `compress`-мидлвари
|
||||
(compress буферизует SSE-кадры до закрытия ответа), например
|
||||
``PathPrefix(`/api/ai-chat/stream`) || PathPrefix(`/api/ai-chat/runs/`)``. Для надёжности:
|
||||
`traefik.http.middlewares.<name>.compress.excludedcontenttypes: text/event-stream`.
|
||||
|
||||
## Миграция с Docmost
|
||||
|
||||
Схема БД Gitmost — это **строгий superset** схемы Docmost. Все Gitmost-специфичные миграции только
|
||||
|
||||
Reference in New Issue
Block a user