fix: release QA hardening across processing, media, security, and CI gates (#649)

A release-readiness QA pass over the whole product. The commits split into
defects a user would hit and gates that were reporting green while measuring
nothing.

## Fixes that change behaviour

Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so
request.ip came from a client-set header and a forged X-Forwarded-For got past
the login limiter. The default is now a private-network trust list.

A transient Postgres outage stranded in-flight jobs, leaving finished output on
disk with no row pointing at it. A reconciler now resolves those rows and adopts
the bytes rather than dropping the work.

A Redis connection that moved to a new address wedged every read-blocked
consumer, so completions stopped signalling while health still answered 200.
Socket timeouts plus subscriber pings recover it.

Installing more than one AI bundle left the shared venv multi-versioned and
silently broke three tools. The installer now reconciles distributions to one
version each.

Converting an image to JXL at quality 1 through 4 returned a 500, because
libjxl 0.7 rejects the distance those values compute. The quality is floored at
what the encoder honours. A missing ffmpeg was also reported to the user as a
corrupt upload; it now says the engine is unavailable.

RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at
0.22.2, and the release scan was split so it can fail on an unfixed critical
instead of hiding it behind ignore-unfixed.

## Gates that could not fail

Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs
build; coverage discarded its whole report on any failing test; the lint gate
skipped root tests, scripts, and two workspaces; and several generated matrices
counted a host missing ffmpeg as a passing tool. Each now measures what it
claims.

Full evidence and the outstanding release items are tracked locally and are not
part of this branch.
This commit is contained in:
SnapOtter
2026-07-27 15:37:30 +08:00
committed by GitHub
parent bc32f86a07
commit d10d0f544f
855 changed files with 54564 additions and 13092 deletions
+23 -12
View File
@@ -1,8 +1,9 @@
---
description: "SnapOtter'ı Docker ile üretime dağıtın. Donanım gereksinimleri, GPU kurulumu ve Nginx, Traefik ve Cloudflare için ters proxy yapılandırmaları."
i18n_output_hash: 4fadf7841bd7
i18n_source_hash: 98172965118b
i18n_source_hash: 2a722f86da75
i18n_provenance: human
i18n_output_hash: 7ba99eabac1c
i18n_hash_version: 2
---
# Dağıtım {#deployment}
@@ -47,7 +48,7 @@ services:
# - MAX_USERS=0 # Max user accounts
# --- Networking ---
# - TRUST_PROXY=true # Trust X-Forwarded-For headers (set false if not behind a proxy)
# - TRUST_PROXY=loopback,linklocal,uniquelocal # Which peers may set the client IP via X-Forwarded-For (default shown)
# --- Bind mount permissions ---
# - PUID=1000 # Match your host user's UID (run: id -u)
@@ -82,7 +83,7 @@ services:
- SnapOtter-pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U snapotter"]
test: ["CMD-SHELL", "pg_isready -U snapotter -d snapotter"]
interval: 10s
timeout: 5s
retries: 12
@@ -170,13 +171,13 @@ services:
container_name: SnapOtter-postgres
environment:
POSTGRES_USER: snapotter
POSTGRES_PASSWORD: snapotter
POSTGRES_PASSWORD: snapotter # Yerel olmayan dağıtımlar için bunu değiştirin
POSTGRES_DB: snapotter
volumes:
- SnapOtter-pgdata:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U snapotter"]
test: ["CMD-SHELL", "pg_isready -U snapotter -d snapotter"]
interval: 10s
timeout: 5s
retries: 12
@@ -207,6 +208,8 @@ volumes:
docker compose -f docker-compose-gpu.yml up -d
```
### GPU hızlandırmayı doğrulayın {#verify-gpu-acceleration}
Günlüklerde CUDA algılamasını kontrol edin:
```bash
@@ -214,6 +217,8 @@ docker logs SnapOtter 2>&1 | head -20
# Look for: [gpu] CUDA available via torch
```
`--gpus all` ve NVIDIA Container Toolkit doğru ayarlanmış olmasına rağmen AI araçları CPU'da çalışıyorsa, etkilenen paketi (örneğin Arka Plan Kaldırma) **Ayarlar → AI Özellikleri**'nden yeniden yükleyin. Yükleyici, ONNX Runtime'ın GPU yapısını geri yükler; başka bir paket (transkripsiyon gibi) tarafından çekilen yalnızca CPU içeren bir yapı, aksi takdirde paylaşılan AI ortamında gölge oluşturabilir. Kullanıcı arayüzünden yeniden yükleme eski bir görüntüdeki GPU'yu geri yüklemezse, [sorun #490](https://github.com/snapotter-hq/SnapOtter/issues/490)'daki manuel onarıma bakın.
## Donanım Gereksinimleri {#hardware-requirements}
Bu sayılar, NVIDIA RTX 4070'li modern bir amd64 iş istasyonundan Raspberry Pi'ye kadar çeşitli sistemlerde yapılan kıyaslamalardan gelmektedir; her birinde tüm araç kataloğu çalıştırılmış ve gerçek alt sınırı bulmak için Docker kaynak limitleri taranmıştır.
@@ -436,11 +441,11 @@ Başlangıç hatası kullanılacak tam UID'yi belirtir, bu nedenle en hızlı yo
| `AUTH_ENABLED` | `true` | Oturum açma gereksinimini etkinleştir/devre dışı bırak |
| `DEFAULT_USERNAME` | `admin` | Başlangıç yönetici kullanıcı adı |
| `DEFAULT_PASSWORD` | `admin` | Başlangıç yönetici parolası (ilk oturum açmada zorunlu değişiklik) |
| `MAX_UPLOAD_SIZE_MB` | `100` | Dosya başına yükleme limiti |
| `MAX_BATCH_SIZE` | `100` | Grup isteği başına maksimum dosya |
| `MAX_UPLOAD_SIZE_MB` | `0` (sınırsız) | Dosya başına MB cinsinden yükleme limiti. İmaj `0` ile gelir; kaynaktan yapılan bir derleme 100 ile başlar |
| `MAX_BATCH_SIZE` | `0` (sınırsız) | Grup isteği başına maksimum dosya. İmaj `0` ile gelir; kaynaktan yapılan bir derleme 100 ile başlar |
| `RATE_LIMIT_PER_MIN` | `1000` | IP başına dakikada API isteği (devre dışı bırakmak için 0 ayarlayın) |
| `MAX_USERS` | `0` (sınırsız) | Maksimum kullanıcı hesabı |
| `TRUST_PROXY` | `true` | Ters proxy'den gelen X-Forwarded-For başlıklarına güven |
| `TRUST_PROXY` | `loopback,linklocal,uniquelocal` | Hangi uçların istemci IP'sini `X-Forwarded-For` üzerinden belirleyebileceği. Varsayılan olarak yalnızca özel ağlar |
| `PUID` | `999` | Bu UID olarak çalıştır (bağlama noktası izinleri için) |
| `PGID` | `999` | Bu GID olarak çalıştır (bağlama noktası izinleri için) |
| `LOG_LEVEL` | `info` | Günlük ayrıntı düzeyi: fatal, error, warn, info, debug, trace |
@@ -483,7 +488,13 @@ curl http://localhost:1349/api/v1/health
## Ters Proxy {#reverse-proxy}
SnapOtter, hız sınırlaması ve günlüğe kaydetmenin `X-Forwarded-For` başlıklarından gerçek istemci IP'sini kullanması için varsayılan olarak `TRUST_PROXY=true` ayarlar.
`TRUST_PROXY` varsayılan olarak `loopback,linklocal,uniquelocal` değerindedir; bu yüzden SnapOtter `X-Forwarded-For` başlığına yalnızca özel ağdaki bir uçtan geldiğinde inanır. Aynı makinedeki, bir Docker ağındaki ya da LAN'ınızdaki bir ters proxy kutudan çıktığı haliyle güvenilir sayılır; böylece hız sınırlaması, oturum açmadaki kaba kuvvet sınırlayıcısı, denetim günlüğü ve enterprise sürümün IP izin listesi hiçbir yapılandırma olmadan gerçek istemci IP'sini görür.
`TRUST_PROXY=true` değerini yalnızca öndeki proxy SnapOtter'a **herkese açık** bir adresten ulaşıyorsa ayarlayın; örneğin başka bir ağdaki bir bulut yük dengeleyicisi. Doğrudan açığa çıkmış bir örnekte bu değer `request.ip` alanını saldırganın denetimine bırakır, çünkü başlığı sürekli değiştiren biri her istekte taze bir hız sınırı sayacı elde eder.
İstemci IP'lerini ölçmeye girişmeden önce bilinmesi gereken iki şey var. macOS ve Windows üzerindeki Docker Desktop, yayımlanan bir bağlantı noktasını her kaynak adresini `192.168.65.1` sanal makine ağ geçidine yeniden yazan bir kullanıcı alanı proxy'si üzerinden sunar; orada `TRUST_PROXY` değerlerinin hiçbiri gerçek istemciyi geri getirmez, internete açılan her şeyi Linux üzerinde dağıtın. Ayrıca her platformda, yayımlanan bir bağlantı noktasına `localhost` üzerinden erişmek sizin istemciniz yerine köprü ağ geçidi olarak görülür; dolayısıyla localhost testi gerçek bir istemcinin nasıl ilişkilendirildiği hakkında hiçbir şey söylemez. `TRUST_PROXY` değerlerinin tam tablosu ve Docker Desktop uyarısı [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md#client-ip-resolution-trust_proxy) içinde yer alır.
Aşağıdaki her proxy için iki şey önemlidir: büyük istek gövdelerine (yüklemeler) izin verin ve yanıtları ara belleğe almayın. Yanıt arabelleğe alan bir proxy, SSE ilerlemesini keser ve daha görünür bir şekilde büyük bir dosya indirme işlemini "başlatır ancak hiçbir zaman bitirmez" çünkü proxy, aktarmadan önce tüm dosyayı tutar. SnapOtter, indirmelerde `X-Accel-Buffering: no`'yi gönderir, böylece ara belleğe alma başka bir yerde bırakılsa bile nginx bunları akışa alır, ancak nginx dışındaki proxy'lerin yanıt arabelleğe almanın açıkça devre dışı bırakılması gerekir (aşağıdaki her yapılandırmada gösterilmiştir). İndirme işlemi yarıda durursa, kontrol edilecek ilk şey öndeki ara belleğe alma proxy'sidir.
### Nginx {#nginx}
@@ -505,7 +516,7 @@ server {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# SSE support (batch progress, feature install progress)
# Ara belleğe alma yerine akış yanıtları: SSE ilerlemesi (toplu, yapay zeka, özellik yüklemeleri) ve büyük dosya indirmeleri için gereklidir.
proxy_buffering off;
proxy_read_timeout 300s;
}
@@ -549,7 +560,7 @@ images.example.com {
}
```
`flush_interval -1`, SSE ilerleme olayları (grup işleme, AI araçları, özellik kurulumları) için gerekli olan yanıt arabelleğe almayı devre dışı bırakır. Uzatılmış zaman aşımları, Caddy'nin bağlantıyı erken kapatmadan büyük dosya yüklemelerinin tamamlanmasına izin verir.
`flush_interval -1`, SSE ilerleme olayları (toplu işleme, yapay zeka araçları, özellik yüklemeleri) ve büyük dosya indirmelerinin durmak yerine akışa alınması için gerekli olan yanıt arabelleğe almayı devre dışı bırakır. Uzatılmış zaman aşımları, Caddy'nin bağlantıyı erken kapatmasına gerek kalmadan büyük dosya yüklemelerinin tamamlanmasına olanak tanır.
### Cloudflare Tunnels {#cloudflare-tunnels}