273 lines
8.3 KiB
Markdown
273 lines
8.3 KiB
Markdown
|
|
# Гайд использования
|
|||
|
|
|
|||
|
|
Руководство по кейсам — копируй, вставляй, проверяй. Без обдумывания.
|
|||
|
|
|
|||
|
|
## 1. Запуск
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cp .env.example .env
|
|||
|
|
docker compose -f docker-compose.dev.yml up -d --build
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Проверка:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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 (с обрезкой)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
curl -s -o /tmp/crop200.jpg \
|
|||
|
|
"http://localhost:8888/unsafe/crop:200:200/plain/https://picsum.photos/1200/800"
|
|||
|
|
# → файл 200x200
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### WebP — конвертация формата
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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 + качество
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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 — базовая проверка
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Разные размеры = разные ключи
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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 — сбросить всё
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Закешировать
|
|||
|
|
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 — конкретное изображение (все размеры)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Закешировать 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 — конкретный размер
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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.
|
|||
|
|
|
|||
|
|
### Список закешированных ключей
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
curl -s http://localhost:2019/souin-api/souin/
|
|||
|
|
# → ["GET-http-localhost:8888-/unsafe/resize:fit:200:0:0/q:80/plain/https://..."]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Purge — все методы
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Полный сброс
|
|||
|
|
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/` работает:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# С /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 — включить подпись
|
|||
|
|
|
|||
|
|
1. Сгенерировать ключи:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
openssl rand -hex 32 # → KEY
|
|||
|
|
openssl rand -hex 32 # → SALT
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. Добавить в `.env`:
|
|||
|
|
|
|||
|
|
```env
|
|||
|
|
IMGPROXY_KEY=<сгенерированный_ключ>
|
|||
|
|
IMGPROXY_SALT=<сгенерированная_соль>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. Перезапустить:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose -f docker-compose.dev.yml restart imgproxy
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
4. Проверить:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Без /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
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
5. Подписать URL:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
6. Вернуть unsigned — убрать ключи из `.env` и перезапустить.
|