From b8bb8eecd20cd05eb4738af692d53beae3dc1ef0 Mon Sep 17 00:00:00 2001 From: tomasz-syn-grzegorza Date: Sat, 27 Jun 2026 17:25:28 +0200 Subject: [PATCH] Fix Dokploy deploy to match agencja-ai runtime clone pattern. Revert compose to public git clone with env-based HTML generation, allow inline scripts in CSP for YouTube player, and use window.location.origin for embed origin. Co-authored-by: Cursor --- .env.example | 2 +- default.conf | 2 +- docker-compose-example/.env.example | 10 ++-- docker-compose-example/README.md | 66 +++++++++++------------ docker-compose-example/docker-compose.yml | 44 +++++++++++---- index.html.template | 3 +- 6 files changed, 76 insertions(+), 51 deletions(-) diff --git a/.env.example b/.env.example index 064f97f..34a3370 100644 --- a/.env.example +++ b/.env.example @@ -17,7 +17,7 @@ SITE_DESCRIPTION="Lokalne modele LLM, własny serwer AI (Proxmox, TrueNAS, Ubunt FACEBOOK_DESCRIPTION="Polub nas na YouTube i wesprzyj fundację — linki na slavic-pulse.com" TWITTER_DESCRIPTION="Lokalne modele LLM, własny serwer AI (Proxmox, TrueNAS, Ubuntu), RAG, Ollama, Open WebUI, bezpieczny self-hosting i wdrożenia biznesowe lokalnej AI." SITE_OG_IMAGE_ALT="Slavic Pulse" -THEME_COLOR=#000000 +THEME_COLOR="#000000" # --- Ikony (ścieżki względem roota strony) --- FAVICON_48=/assets/imgs/favico/48x48.png diff --git a/default.conf b/default.conf index 4b5a82f..d9a754a 100644 --- a/default.conf +++ b/default.conf @@ -54,5 +54,5 @@ server { add_header X-Content-Type-Options nosniff always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always; - add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://www.youtube.com https://www.gstatic.com; style-src 'self' 'unsafe-inline'; img-src 'self' https://img.youtube.com data:; frame-src https://www.youtube.com; connect-src 'self' https://www.youtube.com; base-uri 'self'; form-action 'none'; object-src 'none'; upgrade-insecure-requests;" always; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.youtube.com https://www.gstatic.com; style-src 'self' 'unsafe-inline'; img-src 'self' https://img.youtube.com data:; frame-src https://www.youtube.com; connect-src 'self' https://www.youtube.com; base-uri 'self'; form-action 'none'; object-src 'none'; upgrade-insecure-requests;" always; } diff --git a/docker-compose-example/.env.example b/docker-compose-example/.env.example index c11eddb..6a44d88 100644 --- a/docker-compose-example/.env.example +++ b/docker-compose-example/.env.example @@ -2,8 +2,12 @@ # Copy to .env before deploy if needed. # Never commit .env with real credentials to version control. # -# Tylko zmienne runtime strony — bez GITEA_REPO_URL, bez FRONTEND_PORT. -# Dokploy buduje obraz z Dockerfile; repo klonowane jest na hoście przez Dokploy. +# Dokploy kopiuje tylko docker-compose.yml + .env (jak site-agencja-ai-com). +# Repo publiczne — brak GITEA_REPO_URL. + +# --- Frontend Server --- +# Port publikowany na hoście. W kontenerze nginx zawsze nasłuchuje na 80. +FRONTEND_PORT=8082 # --- Strona (meta, SEO, PWA) --- SITE_TITLE="Slavic Pulse" @@ -13,7 +17,7 @@ SITE_DESCRIPTION="Lokalne modele LLM, własny serwer AI (Proxmox, TrueNAS, Ubunt FACEBOOK_DESCRIPTION="Polub nas na YouTube i wesprzyj fundację — linki na slavic-pulse.com" TWITTER_DESCRIPTION="Lokalne modele LLM, własny serwer AI (Proxmox, TrueNAS, Ubuntu), RAG, Ollama, Open WebUI, bezpieczny self-hosting i wdrożenia biznesowe lokalnej AI." SITE_OG_IMAGE_ALT="Slavic Pulse" -THEME_COLOR=#000000 +THEME_COLOR="#000000" # --- Ikony (ścieżki względem roota strony) --- FAVICON_48=/assets/imgs/favico/48x48.png diff --git a/docker-compose-example/README.md b/docker-compose-example/README.md index d656386..ab31e64 100644 --- a/docker-compose-example/README.md +++ b/docker-compose-example/README.md @@ -22,45 +22,44 @@ site-slavic-pulse-com/ ## Dokploy deployment +**Ten sam model co `site-agencja-ai-com`:** Dokploy kopiuje na serwer **tylko** `docker-compose.yml` + `.env` — nie ma tam `Dockerfile` ani pełnego repo. Kontener przy starcie robi `git clone` publicznego repo, generuje HTML z `.env` i uruchamia nginx. + **Kroki w Dokploy UI:** 1. Nowa aplikacja (Compose) → wklej `docker-compose-example/docker-compose.yml` -2. Wklej `.env` (na podstawie `.env.example`) — ustaw `VIDEO_ID_*`, opisy SEO +2. Wklej `.env` (na podstawie `.env.example`) — ustaw `FRONTEND_PORT`, `VIDEO_ID_*`, opisy SEO 3. **Domains** → dodaj **oba** hosty (Container Port = `80`, HTTPS = Let's Encrypt): - `slavic-pulse.com` - `www.slavic-pulse.com` ← **wymagane** — bez tego www dostaje domyślny cert Traefika (CN=*, self-signed) i telefony pokazują „serwer nie potrafi udowodnić tożsamości” -4. (Opcjonalnie) **Advanced → Redirects** → preset **www → non-www**, żeby `www` przekierowywało na apex -5. Traefik routuje ruch HTTPS → port 80 kontenera (bez mapowania portu na hosta) -6. Deploy — Dokploy klonuje repo na hoście i **buduje obraz z Dockerfile** (brak git clone w kontenerze) +4. (Opcjonalnie) **Advanced → Redirects** → preset **www → non-www** +5. Deploy -**Porty:** nie używaj `ports:` w compose — Traefik/Dokploy łączy się z wewnętrznym `:80`. -W `docker ps` kolumna PORTS może być pusta — to normalne przy routingu przez Traefik. +**Porty:** compose mapuje `${FRONTEND_PORT}:80` (jak agencja-ai). Traefik/Dokploy dodatkowo routuje po FQDN do wewnętrznego portu 80 kontenera. -**Gitea Provider w Dokploy** (OAuth) — autoryzuje Dokploy do listowania repo i deployu z UI. -Repo klonowane jest na **hoście** przez Dokploy; kontener nie potrzebuje tokena GITEA ani `git clone` w runtime. +**Repo musi być publiczne** w Gitea — clone bez tokenu. Po `git push` zrób redeploy, żeby kontener pobrał nowy kod. ## Jak to działa -1. **Build** — Dokploy buduje obraz z `Dockerfile` (nginx + assets + szablony) -2. **Start kontenera** — entrypoint generuje `index.html` i `site.webmanifest` ze zmiennych `.env`, usuwa szablony z dysku -3. **Tło** — miniaturka YouTube (`img.youtube.com`) dopasowana do FHD/vertical -4. **Intro wideo** — YouTube IFrame API, autoplay -5. **Po filmie** — przekierowanie (jeśli `POST_VIDEO_REDIRECT_ENABLED=true`) lub tło z miniaturki +1. **Start kontenera** — `git clone` publicznego repo do `/app` +2. **`generate-from-env.sh`** — buduje `index.html` i `site.webmanifest` ze zmiennych `.env` +3. **Kopiowanie** — `assets/`, `default.conf`, `nginx.conf` → nginx +4. **Cleanup** — `rm -rf /app` (brak pełnego repo na dysku kontenera) +5. **Intro wideo** — YouTube IFrame API, autoplay; po filmie redirect lub tło ## Konfiguracja (.env) | Zmienna | Opis | |---------|------| +| `FRONTEND_PORT` | Port na hoście (np. `8082`) | | `VIDEO_ID_FHD` / `VIDEO_ID_VERTICAL` | ID shortów YouTube | | `POST_VIDEO_REDIRECT_URL_*` | URL po zakończeniu filmu | | `POST_VIDEO_REDIRECT_ENABLED` | `true` = redirect, `false` = zostaje tło | -**Nie dodawaj do produkcyjnego `.env`:** `GITEA_REPO_URL`, `FRONTEND_PORT` — nie są używane w tym modelu deployu. - ## Hardening bezpieczeństwa (wbudowany) -- Build z Dockerfile zamiast runtime git clone — brak tokenów w kontenerze +- Repo publiczne — brak tokena GITEA w kontenerze +- Po starcie `/app` usuwane — brak compose/README/szablonów na dysku - nginx: whitelist ścieżek, blokada `.env`/`.sh`/`.template`, nagłówki CSP/Referrer-Policy -- Szablony i generator usuwane z kontenera po wygenerowaniu HTML +- CSP z `'unsafe-inline'` dla skryptu YouTube (inline JS w `index.html`) - `server_tokens off` — ukryta wersja nginx ## Weryfikacja po deployu @@ -77,34 +76,35 @@ curl -sI https://slavic-pulse.com/random-path | grep HTTP # Nagłówki bezpieczeństwa curl -sI https://slavic-pulse.com/ | grep -iE 'content-security-policy|referrer-policy|permissions-policy' -# Brak wersji nginx w Server -curl -sI https://slavic-pulse.com/ | grep -i server - # W kontenerze (na serwerze) -docker exec slavic-pulse-com-frontend ls /opt/slavic-pulse 2>&1 # powinno: No such file -docker inspect slavic-pulse-com-frontend --format '{{range .Config.Env}}{{println .}}{{end}}' | grep -i gitea # brak wyniku +docker logs slavic-pulse-com-frontend --tail 30 +docker exec slavic-pulse-com-frontend ls /app 2>&1 # powinno: No such file +docker exec slavic-pulse-com-frontend head -3 /usr/share/nginx/html/index.html ``` ## Rozwiązywanie problemów -### Brak portów w `docker ps` (kolumna PORTS pusta) +### Kontener `Restarting` / unhealthy -Przy Traefik/Dokploy bez `ports:` w compose — **to oczekiwane**. -Sprawdź w Dokploy UI: domena → Container Port = **80**, kontener `Up (healthy)`. - -### Kontener nie startuje / unhealthy - -**Diagnostyka na serwerze:** ```bash docker logs slavic-pulse-com-frontend --tail 50 ``` -Typowe przyczyny: błąd builda Dockerfile, brak `.env`, błąd w `generate-from-env.sh`. +Typowe przyczyny: +- Repo **prywatne** → ustaw jako Public w Gitea +- Brak lub błędny `.env` w Dokploy +- Błąd w `generate-from-env.sh` (sprawdź logi) + +### Strona czarna / loader bez końca + +- Sprawdź konsolę przeglądarki (CSP / YouTube) +- Upewnij się, że `VIDEO_ID_*` są poprawne w `.env` +- Po zmianie `default.conf` lub szablonu: `git push` + redeploy ### Zmiana konfiguracji po deployu 1. Edytuj `.env` w Dokploy -2. Redeploy / restart kontenera (entrypoint regeneruje HTML z nowych zmiennych) +2. Redeploy / restart kontenera (przy starcie ponownie klonuje repo i generuje HTML) ### Błąd certyfikatu SSL na telefonie („serwer nie potrafi udowodnić…”) @@ -121,8 +121,8 @@ Strona wysyła `Strict-Transport-Security: includeSubDomains`, więc po wizycie 4. Sprawdź: `curl -I https://www.slavic-pulse.com` (powinno być HTTP 200 lub 301, bez błędu SSL) 5. (Opcjonalnie) **Advanced → Redirects** → **www to non-www** -**Alternatywa w DNS (OVH):** zamiast rekordu A dla `www` ustaw przekierowanie HTTP na `https://slavic-pulse.com` w panelu domeny — wtedy ruch www nie trafia na serwer bez certyfikatu. +**Alternatywa w DNS (OVH):** zamiast rekordu A dla `www` ustaw przekierowanie HTTP na `https://slavic-pulse.com` w panelu domeny. -**Facebook / in-app browser:** linki w postach powinny wskazywać `https://slavic-pulse.com` (bez www). Po naprawie www problem znika także dla użytkowników, którzy wpiszą www ręcznie. +**Facebook / in-app browser:** linki w postach powinny wskazywać `https://slavic-pulse.com` (bez www). **Odnowienie certyfikatu:** Let's Encrypt odnawia cert automatycznie przez Traefik. Jeśli cert wygasa, w Dokploy sprawdź logi Traefik i czy port 80 jest dostępny z internetu (HTTP-01 challenge). diff --git a/docker-compose-example/docker-compose.yml b/docker-compose-example/docker-compose.yml index d38195e..cc9a04d 100644 --- a/docker-compose-example/docker-compose.yml +++ b/docker-compose-example/docker-compose.yml @@ -1,34 +1,56 @@ # ============================================================ # Slavic Pulse - Docker Compose (Dokploy / manual) # ============================================================ -# Dokploy + Traefik: NIE mapuj portu na hosta (ports:). -# W UI Dokploy ustaw domenę i Container Port = 80 — Traefik routuje po FQDN. -# Wewnętrznie nginx nasłuchuje na :80 (expose poniżej). +# Dokploy-compatible pattern: pre-built nginx image + runtime git clone. +# Dokploy only copies docker-compose.yml + .env to the server, so local +# build context with a Dockerfile does not work (same as site-agencja-ai-com). # -# Build z Dockerfile — Dokploy klonuje repo na hoście i buduje obraz. -# Brak runtime git clone w kontenerze (hardening bezpieczeństwa). +# Repo publiczne — clone bez tokenu. HTML generowany z .env przy starcie. +# Po setupie /app jest usuwane (brak pełnego repo na dysku kontenera). +# +# Port hosta: FRONTEND_PORT z .env → nginx w kontenerze na porcie 80. +# Dokploy/Traefik routuje po FQDN do wewnętrznego portu 80 kontenera. # ============================================================ services: frontend: - build: - context: .. - dockerfile: Dockerfile + image: nginx:1.27-alpine container_name: slavic-pulse-com-frontend restart: unless-stopped - expose: - - "80" + ports: + - "${FRONTEND_PORT:-8080}:80" env_file: - .env + command: > + sh -ce " + echo '[slavic-pulse] Installing git + gettext...' && + apk add --no-cache git gettext && + echo '[slavic-pulse] Cloning repo...' && + rm -rf /app && + git clone --depth 1 https://gitea.expert-it.agency-ai.dev/tomasz-syn-grzegorza/site-slavic-pulse-com.git /app && + echo '[slavic-pulse] Generating index.html from env...' && + chmod +x /app/generate-from-env.sh && + TEMPLATE_DIR=/app /app/generate-from-env.sh /usr/share/nginx/html && + echo '[slavic-pulse] Copying assets and nginx config...' && + cp -r /app/assets /usr/share/nginx/html/assets && + rm -f /etc/nginx/conf.d/default.conf && + cp /app/default.conf /etc/nginx/conf.d/default.conf && + cp /app/nginx.conf /etc/nginx/nginx.conf && + echo '[slavic-pulse] Removing cloned repo from container...' && + rm -rf /app && + echo '[slavic-pulse] Starting nginx...' && + nginx -g 'daemon off;' + " + healthcheck: test: ["CMD", "wget", "-qO-", "http://127.0.0.1:80/"] interval: 30s timeout: 10s retries: 3 - start_period: 15s + start_period: 60s deploy: resources: diff --git a/index.html.template b/index.html.template index 58653fb..ec19caa 100644 --- a/index.html.template +++ b/index.html.template @@ -168,7 +168,6 @@ redirectEnabled: ${POST_VIDEO_REDIRECT_ENABLED_VERTICAL_JS} } }; - const SITE_ORIGIN = '${SITE_BASE_URL}'; let player; let activeFormat; let activeConfig; @@ -290,7 +289,7 @@ disablekb: 1, iv_load_policy: 3, cc_load_policy: 0, - origin: SITE_ORIGIN.replace(/\/$/, '') + origin: window.location.origin }, events: { onReady: onPlayerReady,