feat: добавить Image Gateway с кешем Souin
- добавлена сборка Caddy с Souin, Otter и NutsDB - добавлена конфигурация dev, prod и test Docker Compose - настроено кеширование через Otter L1 и NutsDB L2 - добавлены e2e-тесты Bun для кеша, restart и purge - добавлена документация по запуску, API кеша и тестам
This commit is contained in:
147
docs/cache-api.md
Normal file
147
docs/cache-api.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# API кеша (Souin)
|
||||
|
||||
## Обзор
|
||||
|
||||
Кеширование реализовано через Souin plugin для Caddy. Обработанные изображения кешируются с TTL = 1 год.
|
||||
|
||||
Souin API доступен через **Caddy admin API** (порт 2019). Это отдельный endpoint, не доступный извне через основной порт.
|
||||
|
||||
## Заголовки
|
||||
|
||||
### Cache-Status
|
||||
|
||||
Каждый ответ содержит заголовок `Cache-Status`:
|
||||
|
||||
```
|
||||
Cache-Status: Souin; hit; ttl=31535999; key=GET-...; detail=DEFAULT
|
||||
```
|
||||
|
||||
| Значение | Описание |
|
||||
|---|---|
|
||||
| `hit` | Ответ из кеша |
|
||||
| `fwd=uri-miss; stored` | Первый запрос, результат закеширован |
|
||||
| `fwd=uri-miss; detail=UNCACHEABLE-*` | Запрос не может быть закеширован |
|
||||
|
||||
### Cache-Control
|
||||
|
||||
imgproxy возвращает:
|
||||
|
||||
```
|
||||
Cache-Control: max-age=31536000, public
|
||||
```
|
||||
|
||||
Souin использует этот заголовок для определения TTL.
|
||||
|
||||
## Souin API
|
||||
|
||||
API доступно через Caddy admin API на порту `2019`.
|
||||
|
||||
### Список закешированных ключей
|
||||
|
||||
```bash
|
||||
curl http://localhost:2019/souin-api/souin/
|
||||
```
|
||||
|
||||
Ответ — JSON-массив ключей:
|
||||
|
||||
```json
|
||||
[
|
||||
"GET-http-localhost:8888-/unsafe/resize:fit:800:0:0/q:80/plain/https://example.com/photo.jpg",
|
||||
"GET-http-localhost:8888-/unsafe/resize:fit:200:0:0/q:60/plain/https://example.com/photo.jpg"
|
||||
]
|
||||
```
|
||||
|
||||
### Purge по ключу (regex)
|
||||
|
||||
```bash
|
||||
curl -X PURGE "http://localhost:2019/souin-api/souin/.*example.com/photo.jpg"
|
||||
```
|
||||
|
||||
`regex` — регулярное выражение для поиска ключей кеша. Используйте `$` в конце для точного совпадения.
|
||||
|
||||
Примеры:
|
||||
|
||||
```bash
|
||||
# Purge конкретного размера
|
||||
curl -X PURGE "http://localhost:2019/souin-api/souin/.*resize:fit:800.*photo\.jpg$"
|
||||
|
||||
# Purge всех вариантов одного изображения
|
||||
curl -X PURGE "http://localhost:2019/souin-api/souin/.*example.com/photo.jpg"
|
||||
|
||||
# Purge всех изображений домена
|
||||
curl -X PURGE "http://localhost:2019/souin-api/souin/.*example\.com.*"
|
||||
```
|
||||
|
||||
Ответ: `204 No Content` — успешно.
|
||||
|
||||
### Purge всего кеша
|
||||
|
||||
```bash
|
||||
curl -X PURGE http://localhost:2019/souin-api/souin/flush
|
||||
```
|
||||
|
||||
Ответ: `204 No Content` — успешно.
|
||||
|
||||
## Формат ключа кеша
|
||||
|
||||
Souin формирует ключ кеша из HTTP-метода, схемы, хоста и пути:
|
||||
|
||||
```
|
||||
{METHOD}-{scheme}-{host}:{port}-{path}
|
||||
```
|
||||
|
||||
Пример:
|
||||
|
||||
```
|
||||
GET-http-localhost:8888-/unsafe/resize:fit:800:0:0/q:80/plain/https://example.com/photo.jpg
|
||||
```
|
||||
|
||||
При purge по regex ищите совпадение по частям пути.
|
||||
|
||||
## Примеры использования
|
||||
|
||||
### curl
|
||||
|
||||
```bash
|
||||
# Закешировать изображение
|
||||
curl -s -o /dev/null -w "status: %{http_code}, time: %{time_total}s\n" \
|
||||
"http://localhost:8888/unsafe/resize:fit:800:0:0/q:80/plain/https://example.com/photo.jpg"
|
||||
# → status: 200, time: 0.570s (MISS)
|
||||
|
||||
# Повторный запрос
|
||||
curl -s -o /dev/null -w "status: %{http_code}, time: %{time_total}s\n" \
|
||||
"http://localhost:8888/unsafe/resize:fit:800:0:0/q:80/plain/https://example.com/photo.jpg"
|
||||
# → status: 200, time: 0.001s (HIT)
|
||||
|
||||
# Purge
|
||||
curl -X PURGE "http://localhost:2019/souin-api/souin/.*example.com/photo.jpg"
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
|
||||
```ts
|
||||
async function purgeCache(imageUrl: string) {
|
||||
const response = await fetch(
|
||||
`http://localhost:2019/souin-api/souin/.*${escapeRegex(imageUrl)}`,
|
||||
{ method: 'PURGE' }
|
||||
)
|
||||
return response.ok
|
||||
}
|
||||
|
||||
function escapeRegex(str: string): string {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
}
|
||||
```
|
||||
|
||||
## Безопасность
|
||||
|
||||
Caddy admin API по умолчанию доступен только на `localhost:2019`. В production:
|
||||
|
||||
- Порт 2019 не должен быть открыт наружу
|
||||
- Можно настроить `admin off` в Caddyfile и использовать альтернативный доступ
|
||||
- Или ограничить через firewall
|
||||
|
||||
## Ограничения
|
||||
|
||||
- **In-memory storage** — по умолчанию кеш хранится в памяти. При перезапуске Caddy кеш теряется
|
||||
- Для production рекомендуется подключить дисковый storage (Badger, NutsDB) через дополнительный плагин
|
||||
Reference in New Issue
Block a user