- добавлена сборка Caddy с Souin, Otter и NutsDB - добавлена конфигурация dev, prod и test Docker Compose - настроено кеширование через Otter L1 и NutsDB L2 - добавлены e2e-тесты Bun для кеша, restart и purge - добавлена документация по запуску, API кеша и тестам
8.3 KiB
8.3 KiB
Гайд использования
Руководство по кейсам — копируй, вставляй, проверяй. Без обдумывания.
1. Запуск
cp .env.example .env
docker compose -f docker-compose.dev.yml up -d --build
Проверка:
curl -s -o /dev/null -w "%{http_code}" \
"http://localhost:8888/unsafe/resize:fit:100:0:0/q:80/plain/https://picsum.photos/200/200"
# → 200
2. Обработка изображений
Resize — вписать в ширину 800px
curl -s -o /tmp/fit800.jpg \
"http://localhost:8888/unsafe/resize:fit:800:0:0/q:80/plain/https://picsum.photos/1200/800"
# → файл /tmp/fit800.jpg, ширина = 800px
Resize — вписать в квадрат 200x200
curl -s -o /tmp/fit200.jpg \
"http://localhost:8888/unsafe/resize:fit:200:200:0/q:80/plain/https://picsum.photos/1200/800"
# → файл 200x?, пропорции сохранены
Resize — заполнить 400x300 (с обрезкой)
curl -s -o /tmp/fill400.jpg \
"http://localhost:8888/unsafe/resize:fill:400:300:0/g:ce/q:80/plain/https://picsum.photos/1200/800"
# → файл точно 400x300, лишнее обрезано по центру
Crop — обрезка 200x200
curl -s -o /tmp/crop200.jpg \
"http://localhost:8888/unsafe/crop:200:200/plain/https://picsum.photos/1200/800"
# → файл 200x200
WebP — конвертация формата
curl -s -D - -o /tmp/webp.webp \
"http://localhost:8888/unsafe/format:webp/q:80/plain/https://picsum.photos/800/600" \
2>&1 | grep Content-Type
# → Content-Type: image/webp
AVIF
curl -s -D - -o /tmp/avif.avif \
"http://localhost:8888/unsafe/format:avif/q:80/plain/https://picsum.photos/800/600" \
2>&1 | grep Content-Type
# → Content-Type: image/avif
Качество — сравнить q:10 vs q:100
curl -s -o /tmp/q10.jpg \
"http://localhost:8888/unsafe/resize:fit:400:0:0/q:10/plain/https://picsum.photos/800/600"
curl -s -o /tmp/q100.jpg \
"http://localhost:8888/unsafe/resize:fit:400:0:0/q:100/plain/https://picsum.photos/800/600"
ls -la /tmp/q10.jpg /tmp/q100.jpg
# → q10 ~4KB, q100 ~50KB — разница ~12x
Комбинированные — resize + WebP + качество
curl -s -o /tmp/combined.webp \
"http://localhost:8888/unsafe/resize:fit:800:0:0/g:ce/q:75/format:webp/plain/https://picsum.photos/1200/800"
# → 800px по ширине, WebP, качество 75
base64url — закодированный source URL
B64=$(echo -n "https://picsum.photos/800/600" | base64 -w0 | tr '+/' '-_' | tr -d '=')
curl -s -o /tmp/b64.jpg "http://localhost:8888/unsafe/resize:fit:100:0:0/q:80/$B64"
# → работает как plain/, но без /plain/ префикса
3. Кеширование
MISS → HIT — базовая проверка
URL="http://localhost:8888/unsafe/resize:fit:100:0:0/q:80/plain/https://picsum.photos/400/300"
# Первый запрос — MISS (обработка через imgproxy)
curl -s -o /dev/null -D - -w "\ntime: %{time_total}s\n" "$URL" | grep -E "Cache-Status|time"
# → Cache-Status: Souin; fwd=uri-miss; stored
# → time: 0.5s
# Второй запрос — HIT (из кеша)
curl -s -o /dev/null -D - -w "\ntime: %{time_total}s\n" "$URL" | grep -E "Cache-Status|time"
# → Cache-Status: Souin; hit; ttl=31535999; detail=DEFAULT
# → time: 0.001s
Что значит Cache-Status
Cache-Status: Souin; hit; ... → из кеша
Cache-Status: Souin; fwd=uri-miss; stored; key=GET-... → первый запрос, закешировано
Cache-Status: Souin; fwd=uri-miss; detail=UNCACHEABLE-... → не закешировано (ошибка upstream)
Разные размеры = разные ключи
SRC="https://picsum.photos/id/42/800/600"
curl -s -o /dev/null "http://localhost:8888/unsafe/resize:fit:200:0:0/q:80/plain/$SRC"
curl -s -o /dev/null "http://localhost:8888/unsafe/resize:fit:400:0:0/q:80/plain/$SRC"
curl -s "http://localhost:2019/souin-api/souin/"
# → 2 ключа: ...resize:fit:200... и ...resize:fit:400...
4. Purge кеша
Все purge-запросы идут через Caddy Admin API на порту 2019.
Purge — сбросить всё
# Закешировать
curl -s -o /dev/null "http://localhost:8888/unsafe/resize:fit:100:0:0/q:80/plain/https://picsum.photos/400/300"
# Purge
curl -s -w "status: %{http_code}\n" -X PURGE http://localhost:2019/souin-api/souin/flush
# → status: 204
# Проверить — снова MISS
curl -s -o /dev/null -D - "http://localhost:8888/unsafe/resize:fit:100:0:0/q:80/plain/https://picsum.photos/400/300" \
| grep Cache-Status
# → Cache-Status: Souin; fwd=uri-miss; stored
Purge — конкретное изображение (все размеры)
# Закешировать 2 размера
SRC="https://picsum.photos/id/77/800/600"
curl -s -o /dev/null "http://localhost:8888/unsafe/resize:fit:200:0:0/q:80/plain/$SRC"
curl -s -o /dev/null "http://localhost:8888/unsafe/resize:fit:400:0:0/q:80/plain/$SRC"
# Purge по regex (id/77)
curl -s -w "status: %{http_code}\n" -X PURGE "http://localhost:2019/souin-api/souin/.*id/77.*"
# → status: 204
# Оба размера сброшены
Purge — конкретный размер
curl -s -w "status: %{http_code}\n" -X PURGE \
"http://localhost:2019/souin-api/souin/.*resize:fit:200.*id/77.*$"
# → status: 204 — только 200px сброшен, 400px остался
5. Caddy Admin API (Souin)
Порт 2019, только localhost.
Список закешированных ключей
curl -s http://localhost:2019/souin-api/souin/
# → ["GET-http-localhost:8888-/unsafe/resize:fit:200:0:0/q:80/plain/https://..."]
Purge — все методы
# Полный сброс
curl -X PURGE http://localhost:2019/souin-api/souin/flush
# → 204
# По regex
curl -X PURGE "http://localhost:2019/souin-api/souin/.*example.com/photo.jpg"
# → 204
# Точное совпадение (с $ в конце)
curl -X PURGE "http://localhost:2019/souin-api/souin/.*resize:fit:800.*photo\.jpg$"
# → 204
6. Signed vs Unsigned
Unsigned — по умолчанию
Ключи не заданы → /unsafe/ работает:
# С /unsafe/ → работает
curl -s -o /dev/null -w "%{http_code}" \
"http://localhost:8888/unsafe/resize:fit:100:0:0/q:80/plain/https://picsum.photos/200/200"
# → 200
# Без /unsafe/ → тоже работает (ключи не заданы, подпись не проверяется)
curl -s -o /dev/null -w "%{http_code}" \
"http://localhost:8888/resize:fit:100:0:0/q:80/plain/https://picsum.photos/200/200"
# → 200
Signed — включить подпись
- Сгенерировать ключи:
openssl rand -hex 32 # → KEY
openssl rand -hex 32 # → SALT
- Добавить в
.env:
IMGPROXY_KEY=<сгенерированный_ключ>
IMGPROXY_SALT=<сгенерированная_соль>
- Перезапустить:
docker compose -f docker-compose.dev.yml restart imgproxy
- Проверить:
# Без /unsafe/ и без подписи → ошибка
curl -s -o /dev/null -w "%{http_code}" \
"http://localhost:8888/resize:fit:100:0:0/q:80/plain/https://picsum.photos/200/200"
# → 403
# С /unsafe/ → тоже ошибка (ключи заданы, unsafe запрещён)
curl -s -o /dev/null -w "%{http_code}" \
"http://localhost:8888/unsafe/resize:fit:100:0:0/q:80/plain/https://picsum.photos/200/200"
# → 403
- Подписать URL:
KEY="<сгенерированный_ключ>"
SALT="<сгенерированная_соль>"
PATH_URL="/resize:fit:100:0:0/q:80/plain/https://picsum.photos/200/200"
SIG=$(echo -n "$SALT" | xxd -r -p | openssl dgst -sha256 -hmac "$(echo -n "$KEY" | xxd -r -p)" -binary | base64 | tr '+/' '-_' | tr -d '=' | head -c 32)
curl -s -o /dev/null -w "%{http_code}" "http://localhost:8888/${SIG}${PATH_URL}"
# → 200
- Вернуть unsigned — убрать ключи из
.envи перезапустить.