Migrate to env-driven templates and slavic-pulse feature set.
Replace hardcoded HTML with .env generation, YouTube thumbnail backgrounds, responsive FHD/vertical intro, hardened nginx, and updated Dokploy deploy flow. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,3 +7,7 @@ node_modules/
|
||||
.env
|
||||
.venv/
|
||||
docker-compose-example/
|
||||
docker-compose*.yml
|
||||
dev.sh
|
||||
index.html
|
||||
site.webmanifest
|
||||
|
||||
+35
-3
@@ -1,8 +1,40 @@
|
||||
# Agencja AI — Environment Configuration (Local Development)
|
||||
# Agencja AI — Environment Configuration
|
||||
# Copy to .env and fill in your values.
|
||||
# Never commit .env with real credentials to version control.
|
||||
#
|
||||
# Po zmianie wartości uruchom: ./dev.sh config (lub ./dev.sh restart)
|
||||
|
||||
# --- Frontend Server ---
|
||||
# --- Frontend Server (TYLKO lokalny dev — nie używaj w Dokploy) ---
|
||||
# Port na hoście (localhost). W kontenerze nginx zawsze nasłuchuje na 80.
|
||||
# Mapowanie w docker-compose.yml: ${FRONTEND_PORT}:80
|
||||
FRONTEND_PORT=8081
|
||||
|
||||
# --- Strona (meta, SEO, PWA) ---
|
||||
SITE_TITLE="Agencja AI"
|
||||
SITE_SHORT_NAME="Agencja AI"
|
||||
SITE_BASE_URL=https://agencja-ai.com
|
||||
SITE_DESCRIPTION="Lokalne modele LLM, własny serwer AI (Proxmox, TrueNAS, Ubuntu), RAG, Ollama, Open WebUI, bezpieczny self-hosting i wdrożenia biznesowe lokalnej AI."
|
||||
# Krótki opis pod og:description (Facebook, LinkedIn) — bez HTML; puste = SITE_DESCRIPTION
|
||||
FACEBOOK_DESCRIPTION="Lokalne modele LLM (Llama 3, Mistral, Command-R, Phi-3, DeepSeek i inne). Budowa własnego serwera AI na bazie Proxmox, TrueNAS SCALE oraz Ubuntu. Optymalizacja GPU w środowisku Linux (Ubuntu, OpenSUSE, Proxmox). Technologia RAG – jak podpiąć Twoją firmową wiedzę do lokalnego AI. Prywatne instancje Ollama, Open WebUI, AnythingLLM, PrivateGPT. Bezpieczny self-hosting – od konfiguracji Proxmox VE, przez TrueNAS, po produkcyjne wdrożenia. Porównania sprzętu, zużycie prądu, chłodzenie i realne koszty posiadania własnego AI. Automatyzacje, integracje i zaawansowane zastosowania biznesowe lokalnej AI."
|
||||
TWITTER_DESCRIPTION="Lokalne modele LLM (Llama 3, Mistral, Command-R, Phi-3, DeepSeek i inne). Budowa własnego serwera AI na bazie Proxmox, TrueNAS SCALE oraz Ubuntu. Optymalizacja GPU w środowisku Linux (Ubuntu, OpenSUSE, Proxmox). Technologia RAG – jak podpiąć Twoją firmową wiedzę do lokalnego AI. Prywatne instancje Ollama, Open WebUI, AnythingLLM, PrivateGPT. Bezpieczny self-hosting – od konfiguracji Proxmox VE, przez TrueNAS, po produkcyjne wdrożenia. Porównania sprzętu, zużycie prądu, chłodzenie i realne koszty posiadania własnego AI. Automatyzacje, integracje i zaawansowane zastosowania biznesowe lokalnej AI."
|
||||
SITE_OG_IMAGE_ALT="Agencja AI — lokalna sztuczna inteligencja i self-hosting"
|
||||
THEME_COLOR="#000000"
|
||||
|
||||
# --- Ikony (ścieżki względem roota strony) ---
|
||||
FAVICON_48=/assets/imgs/favico/48x48.png
|
||||
FAVICON_180=/assets/imgs/favico/180x180.png
|
||||
FAVICON_512=/assets/imgs/favico/512x512.png
|
||||
|
||||
# --- YouTube ---
|
||||
# Short FHD 16:9 (landscape / szeroki viewport) — ID z adresu youtube.com/watch?v=...
|
||||
VIDEO_ID_FHD=kujXXtoHUbc
|
||||
POST_VIDEO_REDIRECT_URL_FHD=
|
||||
# Short vertical 9:16 (portrait / wąski viewport) — puste = fallback do FHD
|
||||
VIDEO_ID_VERTICAL=
|
||||
POST_VIDEO_REDIRECT_URL_VERTICAL=
|
||||
# Zachowanie po zakończeniu intro (domyślnie dla obu formatów):
|
||||
# true = przekieruj na POST_VIDEO_REDIRECT_URL_* (YouTube / inny link)
|
||||
# false = zostaw miniaturkę YouTube jako tło strony (bez przekierowania)
|
||||
POST_VIDEO_REDIRECT_ENABLED=false
|
||||
# Opcjonalnie osobno dla każdego formatu (puste = użyj POST_VIDEO_REDIRECT_ENABLED):
|
||||
# POST_VIDEO_REDIRECT_ENABLED_FHD=false
|
||||
# POST_VIDEO_REDIRECT_ENABLED_VERTICAL=false
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
.env
|
||||
*.env.local
|
||||
|
||||
# Generated from .env at dev/build time
|
||||
index.html
|
||||
site.webmanifest
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
.venv/
|
||||
|
||||
+12
-6
@@ -1,19 +1,25 @@
|
||||
FROM nginx:1.27-alpine AS production
|
||||
|
||||
RUN apk add --no-cache gettext
|
||||
|
||||
# Remove default nginx config
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Copy nginx config from project root
|
||||
# Copy nginx configs from project root
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Copy web files (index.html + assets)
|
||||
COPY index.html /usr/share/nginx/html/index.html
|
||||
COPY site.webmanifest /usr/share/nginx/html/site.webmanifest
|
||||
# Copy static assets
|
||||
COPY assets/ /usr/share/nginx/html/assets/
|
||||
|
||||
# Templates + generator (rendered at container start)
|
||||
COPY index.html.template site.webmanifest.template generate-from-env.sh /opt/agencja-ai/
|
||||
RUN chmod +x /opt/agencja-ai/generate-from-env.sh
|
||||
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.d/99-generate-from-env.sh
|
||||
RUN chmod +x /docker-entrypoint.d/99-generate-from-env.sh
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget -qO- http://localhost:80/ || exit 1
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
+25
-2
@@ -10,9 +10,24 @@ server {
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;
|
||||
gzip_min_length 1024;
|
||||
|
||||
# Block hidden files and sensitive extensions
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
return 404;
|
||||
}
|
||||
|
||||
location ~* \.(sh|template|env|yml|yaml|md|git|bak|sql|log)$ {
|
||||
deny all;
|
||||
return 404;
|
||||
}
|
||||
|
||||
# Main page
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
location = / {
|
||||
try_files /index.html =404;
|
||||
}
|
||||
|
||||
location = /index.html {
|
||||
add_header Cache-Control "no-cache";
|
||||
}
|
||||
|
||||
# Web app manifest
|
||||
@@ -29,7 +44,15 @@ server {
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Everything else → 404 (no fallback to index.html)
|
||||
location / {
|
||||
return 404;
|
||||
}
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options SAMEORIGIN always;
|
||||
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' '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;
|
||||
}
|
||||
|
||||
@@ -10,12 +10,22 @@ if [[ ! -f .env ]]; then
|
||||
echo "Utworzono .env z .env.example"
|
||||
fi
|
||||
|
||||
generate_from_env() {
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source .env
|
||||
set +a
|
||||
chmod +x generate-from-env.sh
|
||||
./generate-from-env.sh .
|
||||
}
|
||||
|
||||
case "${1:-up}" in
|
||||
up)
|
||||
generate_from_env
|
||||
$COMPOSE up -d
|
||||
echo ""
|
||||
echo "Dev server: http://localhost:${FRONTEND_PORT:-8081}"
|
||||
echo "Edytuj pliki → F5 w przeglądarce (bez rebuildu)."
|
||||
echo "Edytuj .env → ./dev.sh config → F5 w przeglądarce."
|
||||
;;
|
||||
down)
|
||||
$COMPOSE down
|
||||
@@ -24,14 +34,19 @@ case "${1:-up}" in
|
||||
$COMPOSE logs -f
|
||||
;;
|
||||
restart)
|
||||
generate_from_env
|
||||
$COMPOSE restart
|
||||
;;
|
||||
config)
|
||||
generate_from_env
|
||||
echo "Wygenerowano index.html i site.webmanifest z .env."
|
||||
;;
|
||||
reload-nginx)
|
||||
docker exec agencja-ai-com-frontend nginx -s reload
|
||||
echo "Nginx przeładowany (po zmianie default.conf)."
|
||||
;;
|
||||
*)
|
||||
echo "Użycie: $0 [up|down|logs|restart|reload-nginx]"
|
||||
echo "Użycie: $0 [up|down|logs|restart|reload-nginx|config]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
# Agencja AI — Environment Configuration (Dokploy)
|
||||
# Copy to .env before deploy if needed.
|
||||
# Never commit .env with real credentials to version control.
|
||||
# Po zmianie wartości uruchom redeploy w Dokploy.
|
||||
|
||||
# --- Frontend Server ---
|
||||
# Port publikowany na hoście. W kontenerze nginx zawsze nasłuchuje na 80.
|
||||
# Mapowanie w docker-compose.yml: ${FRONTEND_PORT}:80
|
||||
# Dokploy/Traefik dodatkowo routuje po FQDN do wewnętrznego portu 80 kontenera.
|
||||
FRONTEND_PORT=8081
|
||||
|
||||
# --- Strona (meta, SEO, PWA) ---
|
||||
SITE_TITLE="Agencja AI"
|
||||
SITE_SHORT_NAME="Agencja AI"
|
||||
SITE_BASE_URL=https://agencja-ai.com
|
||||
SITE_DESCRIPTION="Lokalne modele LLM, własny serwer AI (Proxmox, TrueNAS, Ubuntu), RAG, Ollama, Open WebUI, bezpieczny self-hosting i wdrożenia biznesowe lokalnej AI."
|
||||
FACEBOOK_DESCRIPTION="Lokalne modele LLM (Llama 3, Mistral, Command-R, Phi-3, DeepSeek i inne). Budowa własnego serwera AI na bazie Proxmox, TrueNAS SCALE oraz Ubuntu. Optymalizacja GPU w środowisku Linux (Ubuntu, OpenSUSE, Proxmox). Technologia RAG – jak podpiąć Twoją firmową wiedzę do lokalnego AI. Prywatne instancje Ollama, Open WebUI, AnythingLLM, PrivateGPT. Bezpieczny self-hosting – od konfiguracji Proxmox VE, przez TrueNAS, po produkcyjne wdrożenia. Porównania sprzętu, zużycie prądu, chłodzenie i realne koszty posiadania własnego AI. Automatyzacje, integracje i zaawansowane zastosowania biznesowe lokalnej AI."
|
||||
TWITTER_DESCRIPTION="Lokalne modele LLM (Llama 3, Mistral, Command-R, Phi-3, DeepSeek i inne). Budowa własnego serwera AI na bazie Proxmox, TrueNAS SCALE oraz Ubuntu. Optymalizacja GPU w środowisku Linux (Ubuntu, OpenSUSE, Proxmox). Technologia RAG – jak podpiąć Twoją firmową wiedzę do lokalnego AI. Prywatne instancje Ollama, Open WebUI, AnythingLLM, PrivateGPT. Bezpieczny self-hosting – od konfiguracji Proxmox VE, przez TrueNAS, po produkcyjne wdrożenia. Porównania sprzętu, zużycie prądu, chłodzenie i realne koszty posiadania własnego AI. Automatyzacje, integracje i zaawansowane zastosowania biznesowe lokalnej AI."
|
||||
SITE_OG_IMAGE_ALT="Agencja AI — lokalna sztuczna inteligencja i self-hosting"
|
||||
THEME_COLOR=#000000
|
||||
|
||||
# --- Ikony (ścieżki względem roota strony) ---
|
||||
FAVICON_48=/assets/imgs/favico/48x48.png
|
||||
FAVICON_180=/assets/imgs/favico/180x180.png
|
||||
FAVICON_512=/assets/imgs/favico/512x512.png
|
||||
|
||||
# --- YouTube ---
|
||||
VIDEO_ID_FHD=kujXXtoHUbc
|
||||
POST_VIDEO_REDIRECT_URL_FHD=
|
||||
VIDEO_ID_VERTICAL=
|
||||
POST_VIDEO_REDIRECT_URL_VERTICAL=
|
||||
POST_VIDEO_REDIRECT_ENABLED=false
|
||||
|
||||
@@ -1,59 +1,139 @@
|
||||
# Agencja AI - Frontend (Dokploy Deployment)
|
||||
|
||||
Strona internetowa ze statycznym obrazem tła od pierwszej klatki i krótkim intro wideo streamowanym z YouTube (bez widocznych ramek playera) po załadowaniu.
|
||||
Statyczna strona z intro YouTube (short FHD 16:9 lub vertical 9:16 — wybór po formacie viewportu). Tło przed filmem to miniaturka z YouTube. Konfiguracja w `.env`, HTML generowany przy starcie kontenera.
|
||||
|
||||
## Struktura projektu
|
||||
|
||||
```
|
||||
site-agencja-ai-com/
|
||||
├── docker-compose.yml # Lokalny dev (build: context .)
|
||||
├── Dockerfile # Kontener nginx 1.27 Alpine
|
||||
├── default.conf # Konfiguracja nginx
|
||||
├── index.html # Strona ze streamem YouTube
|
||||
├── assets/imgs/www-bg-agencja-ai-com-001.jpg # Obraz tła (367 KB)
|
||||
├── assets/imgs/favico/ # Favicony (48, 180, 512 px)
|
||||
├── site.webmanifest # Manifest PWA / Android
|
||||
├── .dockerignore # Ignorowane pliki przy budowie
|
||||
├── .gitignore # Git ignore
|
||||
├── docker-compose.yml # Lokalny dev (build z Dockerfile)
|
||||
├── Dockerfile
|
||||
├── default.conf
|
||||
├── nginx.conf
|
||||
├── index.html.template # Szablon strony
|
||||
├── generate-from-env.sh # Generator HTML z .env
|
||||
├── assets/imgs/favico/ # Favicony
|
||||
├── site.webmanifest.template
|
||||
└── docker-compose-example/ # Pliki deploymentowe Dokploy
|
||||
├── docker-compose.yml # Dokploy: image + git clone
|
||||
├── .env # Wymagany przez Dokploy (pusty)
|
||||
└── README.md # Ten plik
|
||||
├── docker-compose.yml
|
||||
├── .env.example
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Dwa podejścia do deployu
|
||||
## Dokploy deployment
|
||||
|
||||
### 1. Lokalny development (docker-compose.yml w root)
|
||||
|
||||
Używa `build:` z Dockerfile — buduje obraz lokalnie. Port hosta ustawiasz w `.env`:
|
||||
|
||||
```bash
|
||||
cp .env.example .env # opcjonalnie zmień FRONTEND_PORT
|
||||
cd /home/tomasz-syn-grzegorza/Pulpit/cursor/site-agencja-ai-com/
|
||||
docker compose up -d --build
|
||||
# Strona na http://localhost:${FRONTEND_PORT} (domyślnie 8081)
|
||||
```
|
||||
|
||||
### 2. Dokploy deployment (docker-compose-example/)
|
||||
|
||||
Używa `image:` + runtime git clone — nie wymaga lokalnego build contextu, bo Dokploy kopiuje tylko `docker-compose.yml` i `.env` na serwer.
|
||||
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 → wklej zawartość `docker-compose-example/docker-compose.yml`
|
||||
2. Wklej `.env` z tego samego katalogu (ustaw `FRONTEND_PORT` jeśli potrzebny)
|
||||
3. Ustaw FQDN w UI (np. `agencja-ai.com`)
|
||||
4. Deploy
|
||||
1. Nowa aplikacja (Compose) → wklej `docker-compose-example/docker-compose.yml`
|
||||
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):
|
||||
- `agencja-ai.com`
|
||||
- `www.agencja-ai.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**
|
||||
5. Deploy
|
||||
|
||||
**Porty:** `FRONTEND_PORT` z `.env` mapuje port hosta na nginx w kontenerze (`${FRONTEND_PORT}:80`). Wewnątrz kontenera nginx zawsze nasłuchuje na porcie 80 (`default.conf`). Dokploy/Traefik routuje po FQDN do tego samego wewnętrznego portu 80.
|
||||
**Porty:** compose mapuje `${FRONTEND_PORT}:80`. Traefik/Dokploy dodatkowo routuje po FQDN do wewnętrznego portu 80 kontenera.
|
||||
|
||||
**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. **Obraz tła od startu** - Lokalny JPG widoczny od pierwszej klatki; loader znika po załadowaniu obrazka
|
||||
2. **Intro wideo YouTube** - Standardowy embed YouTube (`kujXXtoHUbc`) jako nakładka po załadowaniu playera
|
||||
3. **Po zakończeniu filmu** - Warstwa wideo znika, obraz JPG zostaje
|
||||
4. **Safety timeout 15s** - Jeśli YouTube nie załaduje się w czasie → strona zostaje na obrazku tła
|
||||
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
|
||||
|
||||
## Modyfikacja
|
||||
## Konfiguracja (.env)
|
||||
|
||||
- **Inny film:** zmień `VIDEO_ID` w `index.html`
|
||||
- **Inny port hosta:** zmień `FRONTEND_PORT` w `.env` (nginx w kontenerze zostaje na porcie 80)
|
||||
| Zmienna | Opis |
|
||||
|---------|------|
|
||||
| `FRONTEND_PORT` | Port na hoście (np. `8081`) |
|
||||
| `SITE_TITLE`, `SITE_BASE_URL`, `SITE_DESCRIPTION` | Meta i SEO |
|
||||
| `FACEBOOK_DESCRIPTION`, `TWITTER_DESCRIPTION` | Opisy social media |
|
||||
| `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 |
|
||||
|
||||
## Lokalny development
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
./dev.sh up
|
||||
# Strona na http://localhost:${FRONTEND_PORT} (domyślnie 8081)
|
||||
# Edytuj .env → ./dev.sh config → F5 w przeglądarce
|
||||
```
|
||||
|
||||
Produkcyjny build lokalnie:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
## Hardening bezpieczeństwa (wbudowany)
|
||||
|
||||
- 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
|
||||
- CSP z `'unsafe-inline'` dla skryptu YouTube (inline JS w `index.html`)
|
||||
- `server_tokens off` — ukryta wersja nginx
|
||||
|
||||
## Weryfikacja po deployu
|
||||
|
||||
```bash
|
||||
# Strona działa
|
||||
curl -sI https://agencja-ai.com/ | head -5
|
||||
|
||||
# Wrażliwe pliki niedostępne (404)
|
||||
curl -sI https://agencja-ai.com/.env | grep HTTP
|
||||
curl -sI https://agencja-ai.com/generate-from-env.sh | grep HTTP
|
||||
curl -sI https://agencja-ai.com/random-path | grep HTTP
|
||||
|
||||
# Nagłówki bezpieczeństwa
|
||||
curl -sI https://agencja-ai.com/ | grep -iE 'content-security-policy|referrer-policy|permissions-policy'
|
||||
|
||||
# W kontenerze (na serwerze)
|
||||
docker logs agencja-ai-com-frontend --tail 30
|
||||
docker exec agencja-ai-com-frontend ls /app 2>&1 # powinno: No such file
|
||||
docker exec agencja-ai-com-frontend head -3 /usr/share/nginx/html/index.html
|
||||
```
|
||||
|
||||
## Rozwiązywanie problemów
|
||||
|
||||
### Kontener `Restarting` / unhealthy
|
||||
|
||||
```bash
|
||||
docker logs agencja-ai-com-frontend --tail 50
|
||||
```
|
||||
|
||||
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 (przy starcie ponownie klonuje repo i generuje HTML)
|
||||
|
||||
### Błąd certyfikatu SSL na telefonie („serwer nie potrafi udowodnić…”)
|
||||
|
||||
**Diagnoza (stan typowy):**
|
||||
- `https://agencja-ai.com` — certyfikat Let's Encrypt OK
|
||||
- `https://www.agencja-ai.com` — **self-signed** (Traefik default, CN=`*`) → błąd na Androidzie / iPhone / WebView Facebooka
|
||||
|
||||
**Naprawa w Dokploy (zalecane):**
|
||||
1. **Domains** → **Add Domain**
|
||||
2. Host: `www.agencja-ai.com`, Port: `80`, HTTPS: włączone, Certificate: **letsencrypt**
|
||||
3. Deploy / poczekaj ~30 s na wystawienie certyfikatu
|
||||
4. Sprawdź: `curl -I https://www.agencja-ai.com` (powinno być HTTP 200 lub 301, bez błędu SSL)
|
||||
5. (Opcjonalnie) **Advanced → Redirects** → **www to non-www**
|
||||
|
||||
**Facebook / in-app browser:** linki w postach powinny wskazywać `https://agencja-ai.com` (bez www).
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# ============================================================
|
||||
# Agencja AI - Docker Compose (Dokploy / manual)
|
||||
# ============================================================
|
||||
# Dokploy-compatible pattern: uses pre-built image + runtime git clone.
|
||||
# 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. This approach matches
|
||||
# other working apps on our servers (e.g. wdj-hermes-frontend-matrix).
|
||||
# build context with a Dockerfile does not work.
|
||||
#
|
||||
# Port hosta: FRONTEND_PORT z .env (domyślnie 8080) → nginx w kontenerze na porcie 80.
|
||||
# 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.
|
||||
# ============================================================
|
||||
|
||||
@@ -15,19 +17,31 @@ services:
|
||||
image: nginx:1.27-alpine
|
||||
container_name: agencja-ai-com-frontend
|
||||
restart: unless-stopped
|
||||
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-8080}:80"
|
||||
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
command: >
|
||||
sh -c "
|
||||
apk add --no-cache git &&
|
||||
sh -ce "
|
||||
echo '[agencja-ai] Installing git + gettext...' &&
|
||||
apk add --no-cache git gettext &&
|
||||
echo '[agencja-ai] Cloning repo...' &&
|
||||
rm -rf /app &&
|
||||
git clone https://gitea.expert-it.agency-ai.dev/tomasz-syn-grzegorza/site-agencja-ai-com.git /app &&
|
||||
cp /app/index.html /usr/share/nginx/html/index.html &&
|
||||
cp /app/site.webmanifest /usr/share/nginx/html/site.webmanifest &&
|
||||
git clone --depth 1 https://gitea.expert-it.agency-ai.dev/tomasz-syn-grzegorza/site-agencja-ai-com.git /app &&
|
||||
echo '[agencja-ai] 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 '[agencja-ai] Copying assets and nginx config...' &&
|
||||
cp -r /app/assets /usr/share/nginx/html/assets &&
|
||||
rm /etc/nginx/conf.d/default.conf &&
|
||||
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 '[agencja-ai] Removing cloned repo from container...' &&
|
||||
rm -rf /app &&
|
||||
echo '[agencja-ai] Starting nginx...' &&
|
||||
nginx -g 'daemon off;'
|
||||
"
|
||||
|
||||
@@ -36,7 +50,7 @@ services:
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 45s
|
||||
start_period: 60s
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
|
||||
@@ -5,6 +5,8 @@ services:
|
||||
frontend:
|
||||
image: nginx:1.27-alpine
|
||||
build: !reset null
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- ./index.html:/usr/share/nginx/html/index.html:ro
|
||||
- ./site.webmanifest:/usr/share/nginx/html/site.webmanifest:ro
|
||||
|
||||
@@ -8,5 +8,7 @@ services:
|
||||
dockerfile: Dockerfile
|
||||
container_name: agencja-ai-com-frontend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-8080}:80"
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
TEMPLATE_DIR=/opt/agencja-ai /opt/agencja-ai/generate-from-env.sh /usr/share/nginx/html
|
||||
rm -rf /opt/agencja-ai
|
||||
unset GITEA_REPO_URL FRONTEND_PORT 2>/dev/null || true
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/bin/sh
|
||||
# Generuje index.html i site.webmanifest z zmiennych środowiskowych (.env).
|
||||
set -eu
|
||||
|
||||
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
||||
TEMPLATE_DIR="${TEMPLATE_DIR:-$SCRIPT_DIR}"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
if [ -n "${1:-}" ]; then
|
||||
OUTPUT_DIR="$1"
|
||||
elif [ -d /usr/share/nginx/html ]; then
|
||||
OUTPUT_DIR="/usr/share/nginx/html"
|
||||
else
|
||||
OUTPUT_DIR="."
|
||||
fi
|
||||
|
||||
# --- Domyślne wartości szablonu ---
|
||||
SITE_TITLE="${SITE_TITLE:-Agencja AI}"
|
||||
SITE_SHORT_NAME="${SITE_SHORT_NAME:-Agencja AI}"
|
||||
SITE_BASE_URL="${SITE_BASE_URL:-https://agencja-ai.com}"
|
||||
SITE_DESCRIPTION="${SITE_DESCRIPTION:-Lokalne modele LLM, własny serwer AI (Proxmox, TrueNAS, Ubuntu), RAG, Ollama, Open WebUI, bezpieczny self-hosting i wdrożenia biznesowe lokalnej AI.}"
|
||||
FACEBOOK_DESCRIPTION="${FACEBOOK_DESCRIPTION:-Lokalne modele LLM (Llama 3, Mistral, Command-R, Phi-3, DeepSeek i inne). Budowa własnego serwera AI na bazie Proxmox, TrueNAS SCALE oraz Ubuntu. Optymalizacja GPU w środowisku Linux (Ubuntu, OpenSUSE, Proxmox). Technologia RAG – jak podpiąć Twoją firmową wiedzę do lokalnego AI. Prywatne instancje Ollama, Open WebUI, AnythingLLM, PrivateGPT. Bezpieczny self-hosting – od konfiguracji Proxmox VE, przez TrueNAS, po produkcyjne wdrożenia. Porównania sprzętu, zużycie prądu, chłodzenie i realne koszty posiadania własnego AI. Automatyzacje, integracje i zaawansowane zastosowania biznesowe lokalnej AI.}"
|
||||
TWITTER_DESCRIPTION="${TWITTER_DESCRIPTION:-$FACEBOOK_DESCRIPTION}"
|
||||
SITE_OG_IMAGE_ALT="${SITE_OG_IMAGE_ALT:-Agencja AI — lokalna sztuczna inteligencja i self-hosting}"
|
||||
THEME_COLOR="${THEME_COLOR:-#000000}"
|
||||
|
||||
FAVICON_48="${FAVICON_48:-/assets/imgs/favico/48x48.png}"
|
||||
FAVICON_180="${FAVICON_180:-/assets/imgs/favico/180x180.png}"
|
||||
FAVICON_512="${FAVICON_512:-/assets/imgs/favico/512x512.png}"
|
||||
|
||||
VIDEO_ID_FHD="${VIDEO_ID_FHD:-${VIDEO_ID:-kujXXtoHUbc}}"
|
||||
VIDEO_ID_VERTICAL="${VIDEO_ID_VERTICAL:-}"
|
||||
POST_VIDEO_REDIRECT_URL_FHD="${POST_VIDEO_REDIRECT_URL_FHD:-${POST_VIDEO_REDIRECT_URL:-}}"
|
||||
POST_VIDEO_REDIRECT_URL_VERTICAL="${POST_VIDEO_REDIRECT_URL_VERTICAL:-}"
|
||||
POST_VIDEO_REDIRECT_ENABLED="${POST_VIDEO_REDIRECT_ENABLED:-false}"
|
||||
POST_VIDEO_REDIRECT_ENABLED_FHD="${POST_VIDEO_REDIRECT_ENABLED_FHD:-$POST_VIDEO_REDIRECT_ENABLED}"
|
||||
POST_VIDEO_REDIRECT_ENABLED_VERTICAL="${POST_VIDEO_REDIRECT_ENABLED_VERTICAL:-$POST_VIDEO_REDIRECT_ENABLED}"
|
||||
|
||||
OG_IMAGE_URL="https://img.youtube.com/vi/${VIDEO_ID_FHD}/maxresdefault.jpg"
|
||||
|
||||
escape_js() {
|
||||
printf '%s' "$1" | sed "s/'/\\\\'/g"
|
||||
}
|
||||
|
||||
parse_bool_js() {
|
||||
case "$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" in
|
||||
true|1|yes|on) printf 'true' ;;
|
||||
*) printf 'false' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
VIDEO_ID_FHD_JS="$(escape_js "$VIDEO_ID_FHD")"
|
||||
VIDEO_ID_VERTICAL_JS="$(escape_js "$VIDEO_ID_VERTICAL")"
|
||||
POST_VIDEO_REDIRECT_URL_FHD_JS="$(escape_js "$POST_VIDEO_REDIRECT_URL_FHD")"
|
||||
POST_VIDEO_REDIRECT_URL_VERTICAL_JS="$(escape_js "$POST_VIDEO_REDIRECT_URL_VERTICAL")"
|
||||
POST_VIDEO_REDIRECT_ENABLED_FHD_JS="$(parse_bool_js "$POST_VIDEO_REDIRECT_ENABLED_FHD")"
|
||||
POST_VIDEO_REDIRECT_ENABLED_VERTICAL_JS="$(parse_bool_js "$POST_VIDEO_REDIRECT_ENABLED_VERTICAL")"
|
||||
|
||||
export SITE_TITLE SITE_SHORT_NAME SITE_BASE_URL SITE_DESCRIPTION FACEBOOK_DESCRIPTION TWITTER_DESCRIPTION SITE_OG_IMAGE_ALT
|
||||
export THEME_COLOR FAVICON_48 FAVICON_180 FAVICON_512 OG_IMAGE_URL
|
||||
export VIDEO_ID_FHD_JS VIDEO_ID_VERTICAL_JS POST_VIDEO_REDIRECT_URL_FHD_JS POST_VIDEO_REDIRECT_URL_VERTICAL_JS POST_VIDEO_REDIRECT_ENABLED_FHD_JS POST_VIDEO_REDIRECT_ENABLED_VERTICAL_JS
|
||||
|
||||
TEMPLATE_VARS='${SITE_TITLE} ${SITE_SHORT_NAME} ${SITE_BASE_URL} ${SITE_DESCRIPTION} ${FACEBOOK_DESCRIPTION} ${TWITTER_DESCRIPTION} ${SITE_OG_IMAGE_ALT} ${THEME_COLOR} ${FAVICON_48} ${FAVICON_180} ${FAVICON_512} ${OG_IMAGE_URL} ${VIDEO_ID_FHD_JS} ${VIDEO_ID_VERTICAL_JS} ${POST_VIDEO_REDIRECT_URL_FHD_JS} ${POST_VIDEO_REDIRECT_URL_VERTICAL_JS} ${POST_VIDEO_REDIRECT_ENABLED_FHD_JS} ${POST_VIDEO_REDIRECT_ENABLED_VERTICAL_JS}'
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
envsubst "$TEMPLATE_VARS" < "$TEMPLATE_DIR/index.html.template" > "$OUTPUT_DIR/index.html"
|
||||
envsubst "$TEMPLATE_VARS" < "$TEMPLATE_DIR/site.webmanifest.template" > "$OUTPUT_DIR/site.webmanifest"
|
||||
@@ -3,29 +3,28 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Agencja AI</title>
|
||||
<!-- SITE_BASE_URL: https://agencja-ai.com -->
|
||||
<meta name="description" content="Lokalne modele LLM, własny serwer AI (Proxmox, TrueNAS, Ubuntu), RAG, Ollama, Open WebUI, bezpieczny self-hosting i wdrożenia biznesowe lokalnej AI.">
|
||||
<link rel="canonical" href="https://agencja-ai.com/">
|
||||
<title>${SITE_TITLE}</title>
|
||||
<meta name="description" content="${SITE_DESCRIPTION}">
|
||||
<link rel="canonical" href="${SITE_BASE_URL}/">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:site_name" content="Agencja AI">
|
||||
<meta property="og:site_name" content="${SITE_TITLE}">
|
||||
<meta property="og:locale" content="pl_PL">
|
||||
<meta property="og:title" content="Agencja AI">
|
||||
<meta property="og:url" content="https://agencja-ai.com/">
|
||||
<meta property="og:image" content="https://agencja-ai.com/assets/imgs/www-bg-agencja-ai-com-001.jpg">
|
||||
<meta property="og:title" content="${SITE_TITLE}">
|
||||
<meta property="og:url" content="${SITE_BASE_URL}/">
|
||||
<meta property="og:image" content="${OG_IMAGE_URL}">
|
||||
<meta property="og:image:type" content="image/jpeg">
|
||||
<meta property="og:image:alt" content="Agencja AI — lokalna sztuczna inteligencja i self-hosting">
|
||||
<meta property="og:description" content="Lokalne modele LLM (Llama 3, Mistral, Command-R, Phi-3, DeepSeek i inne). Budowa własnego serwera AI na bazie Proxmox, TrueNAS SCALE oraz Ubuntu. Optymalizacja GPU w środowisku Linux (Ubuntu, OpenSUSE, Proxmox). Technologia RAG – jak podpiąć Twoją firmową wiedzę do lokalnego AI. Prywatne instancje Ollama, Open WebUI, AnythingLLM, PrivateGPT. Bezpieczny self-hosting – od konfiguracji Proxmox VE, przez TrueNAS, po produkcyjne wdrożenia. Porównania sprzętu, zużycie prądu, chłodzenie i realne koszty posiadania własnego AI. Automatyzacje, integracje i zaawansowane zastosowania biznesowe lokalnej AI.">
|
||||
<meta property="og:image:alt" content="${SITE_OG_IMAGE_ALT}">
|
||||
<meta property="og:description" content="${FACEBOOK_DESCRIPTION}">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Agencja AI">
|
||||
<meta name="twitter:image" content="https://agencja-ai.com/assets/imgs/www-bg-agencja-ai-com-001.jpg">
|
||||
<meta name="twitter:description" content="Lokalne modele LLM (Llama 3, Mistral, Command-R, Phi-3, DeepSeek i inne). Budowa własnego serwera AI na bazie Proxmox, TrueNAS SCALE oraz Ubuntu. Optymalizacja GPU w środowisku Linux (Ubuntu, OpenSUSE, Proxmox). Technologia RAG – jak podpiąć Twoją firmową wiedzę do lokalnego AI. Prywatne instancje Ollama, Open WebUI, AnythingLLM, PrivateGPT. Bezpieczny self-hosting – od konfiguracji Proxmox VE, przez TrueNAS, po produkcyjne wdrożenia. Porównania sprzętu, zużycie prądu, chłodzenie i realne koszty posiadania własnego AI. Automatyzacje, integracje i zaawansowane zastosowania biznesowe lokalnej AI.">
|
||||
<link rel="icon" type="image/png" sizes="48x48" href="/assets/imgs/favico/48x48.png">
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="/assets/imgs/favico/512x512.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/assets/imgs/favico/180x180.png">
|
||||
<meta name="twitter:title" content="${SITE_TITLE}">
|
||||
<meta name="twitter:image" content="${OG_IMAGE_URL}">
|
||||
<meta name="twitter:description" content="${TWITTER_DESCRIPTION}">
|
||||
<link rel="icon" type="image/png" sizes="48x48" href="${FAVICON_48}">
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="${FAVICON_512}">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${FAVICON_180}">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<link rel="preload" as="image" href="/assets/imgs/www-bg-agencja-ai-com-001.jpg">
|
||||
<meta name="theme-color" content="${THEME_COLOR}">
|
||||
<link rel="preconnect" href="https://img.youtube.com">
|
||||
<link rel="preconnect" href="https://www.youtube.com">
|
||||
<link rel="preconnect" href="https://www.google.com">
|
||||
<style>
|
||||
@@ -146,7 +145,7 @@
|
||||
<div id="stage-root">
|
||||
<div id="stage">
|
||||
<div id="stage-image" class="stage-layer">
|
||||
<img src="/assets/imgs/www-bg-agencja-ai-com-001.jpg" alt="">
|
||||
<img id="stage-bg" src="" alt="${SITE_OG_IMAGE_ALT}">
|
||||
</div>
|
||||
<div id="stage-video" class="stage-layer">
|
||||
<div id="player"></div>
|
||||
@@ -155,29 +154,84 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const VIDEO_ID = 'kujXXtoHUbc';
|
||||
const BG_IMAGE = '/assets/imgs/www-bg-agencja-ai-com-001.jpg';
|
||||
const VIDEO_CONFIGS = {
|
||||
fhd: {
|
||||
id: '${VIDEO_ID_FHD_JS}',
|
||||
ratio: 16 / 9,
|
||||
redirectUrl: '${POST_VIDEO_REDIRECT_URL_FHD_JS}',
|
||||
redirectEnabled: ${POST_VIDEO_REDIRECT_ENABLED_FHD_JS}
|
||||
},
|
||||
vertical: {
|
||||
id: '${VIDEO_ID_VERTICAL_JS}',
|
||||
ratio: 9 / 16,
|
||||
redirectUrl: '${POST_VIDEO_REDIRECT_URL_VERTICAL_JS}',
|
||||
redirectEnabled: ${POST_VIDEO_REDIRECT_ENABLED_VERTICAL_JS}
|
||||
}
|
||||
};
|
||||
let player;
|
||||
let activeFormat;
|
||||
let activeConfig;
|
||||
|
||||
const loader = document.getElementById('loader');
|
||||
const stageVideo = document.getElementById('stage-video');
|
||||
const stage = document.getElementById('stage');
|
||||
const VIDEO_RATIO = 16 / 9;
|
||||
|
||||
function isInAppBrowser() {
|
||||
const ua = navigator.userAgent || '';
|
||||
return /FBAN|FBAV|Instagram|Line\/|Twitter|LinkedInApp|wv\)/i.test(ua);
|
||||
}
|
||||
|
||||
function hideLoader() {
|
||||
loader.classList.add('hidden');
|
||||
}
|
||||
|
||||
function youtubeThumbnailUrl(videoId, quality) {
|
||||
return 'https://img.youtube.com/vi/' + videoId + '/' + (quality || 'maxresdefault') + '.jpg';
|
||||
}
|
||||
|
||||
function updateStageBackground() {
|
||||
const img = document.getElementById('stage-bg');
|
||||
if (!img || !activeConfig || !activeConfig.id) return;
|
||||
delete img.dataset.fallback;
|
||||
img.onerror = function () {
|
||||
if (!img.dataset.fallback) {
|
||||
img.dataset.fallback = '1';
|
||||
img.src = youtubeThumbnailUrl(activeConfig.id, 'hqdefault');
|
||||
}
|
||||
};
|
||||
img.src = youtubeThumbnailUrl(activeConfig.id);
|
||||
}
|
||||
|
||||
function getVideoFormat() {
|
||||
const w = stage.clientWidth || window.innerWidth;
|
||||
const h = stage.clientHeight || window.innerHeight;
|
||||
return w >= h ? 'fhd' : 'vertical';
|
||||
}
|
||||
|
||||
function resolveConfig(format) {
|
||||
const config = VIDEO_CONFIGS[format];
|
||||
if (config.id) return config;
|
||||
const fallbackKey = format === 'fhd' ? 'vertical' : 'fhd';
|
||||
const fallback = VIDEO_CONFIGS[fallbackKey];
|
||||
if (fallback.id) return fallback;
|
||||
return config;
|
||||
}
|
||||
|
||||
function sizeVideoCover() {
|
||||
const iframe = document.querySelector('#stage-video iframe');
|
||||
if (!stage || !iframe) return;
|
||||
if (!stage || !iframe || !activeConfig) return;
|
||||
|
||||
const ratio = activeConfig.ratio;
|
||||
const w = stage.clientWidth;
|
||||
const h = stage.clientHeight;
|
||||
let iw, ih;
|
||||
|
||||
if (w / h > VIDEO_RATIO) {
|
||||
if (w / h > ratio) {
|
||||
iw = w;
|
||||
ih = w / VIDEO_RATIO;
|
||||
ih = w / ratio;
|
||||
} else {
|
||||
ih = h;
|
||||
iw = h * VIDEO_RATIO;
|
||||
iw = h * ratio;
|
||||
}
|
||||
|
||||
iframe.style.width = iw + 'px';
|
||||
@@ -189,14 +243,27 @@
|
||||
iframe.style.margin = '0';
|
||||
}
|
||||
|
||||
activeFormat = getVideoFormat();
|
||||
activeConfig = resolveConfig(activeFormat);
|
||||
updateStageBackground();
|
||||
|
||||
const stageBg = document.getElementById('stage-bg');
|
||||
const bgPreload = new Image();
|
||||
bgPreload.onload = () => loader.classList.add('hidden');
|
||||
bgPreload.src = BG_IMAGE;
|
||||
bgPreload.onload = hideLoader;
|
||||
bgPreload.onerror = hideLoader;
|
||||
if (stageBg && stageBg.src) {
|
||||
bgPreload.src = stageBg.src;
|
||||
} else {
|
||||
hideLoader();
|
||||
}
|
||||
|
||||
const tag = document.createElement('script');
|
||||
tag.src = 'https://www.youtube.com/iframe_api';
|
||||
tag.onerror = hideLoader;
|
||||
document.head.appendChild(tag);
|
||||
|
||||
setTimeout(hideLoader, isInAppBrowser() ? 4000 : 15000);
|
||||
|
||||
function enableVideoSound() {
|
||||
if (!player || typeof player.unMute !== 'function') return;
|
||||
player.unMute();
|
||||
@@ -204,13 +271,15 @@
|
||||
}
|
||||
|
||||
function onYouTubeIframeAPIReady() {
|
||||
if (!activeConfig || !activeConfig.id) return;
|
||||
|
||||
player = new YT.Player('player', {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
videoId: VIDEO_ID,
|
||||
videoId: activeConfig.id,
|
||||
playerVars: {
|
||||
autoplay: 1,
|
||||
mute: 0,
|
||||
mute: 1,
|
||||
controls: 0,
|
||||
showinfo: 0,
|
||||
modestbranding: 1,
|
||||
@@ -231,24 +300,38 @@
|
||||
|
||||
function onPlayerReady(event) {
|
||||
sizeVideoCover();
|
||||
enableVideoSound();
|
||||
stageVideo.classList.add('active');
|
||||
event.target.playVideo();
|
||||
if (!isInAppBrowser()) {
|
||||
enableVideoSound();
|
||||
}
|
||||
}
|
||||
|
||||
function onPlayerStateChange(event) {
|
||||
if (event.data === YT.PlayerState.ENDED) {
|
||||
stageVideo.classList.remove('active');
|
||||
if (activeConfig.redirectEnabled && activeConfig.redirectUrl) {
|
||||
window.location.href = activeConfig.redirectUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('resize', sizeVideoCover);
|
||||
function handleResize() {
|
||||
const newFormat = getVideoFormat();
|
||||
if (newFormat !== activeFormat) {
|
||||
activeFormat = newFormat;
|
||||
activeConfig = resolveConfig(activeFormat);
|
||||
updateStageBackground();
|
||||
if (player && activeConfig.id) {
|
||||
player.loadVideoById(activeConfig.id);
|
||||
}
|
||||
}
|
||||
sizeVideoCover();
|
||||
}
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
document.getElementById('stage-root').addEventListener('pointerdown', enableVideoSound, { once: true });
|
||||
|
||||
setTimeout(() => {
|
||||
loader.classList.add('hidden');
|
||||
}, 15000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
keepalive_timeout 15;
|
||||
client_body_timeout 10;
|
||||
client_header_timeout 10;
|
||||
send_timeout 10;
|
||||
|
||||
server_tokens off;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
@@ -1,24 +1,24 @@
|
||||
{
|
||||
"name": "Agencja AI",
|
||||
"short_name": "Agencja AI",
|
||||
"name": "${SITE_TITLE}",
|
||||
"short_name": "${SITE_SHORT_NAME}",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/imgs/favico/48x48.png",
|
||||
"src": "${FAVICON_48}",
|
||||
"sizes": "48x48",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/assets/imgs/favico/180x180.png",
|
||||
"src": "${FAVICON_180}",
|
||||
"sizes": "180x180",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/assets/imgs/favico/512x512.png",
|
||||
"src": "${FAVICON_512}",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#000000"
|
||||
"theme_color": "${THEME_COLOR}",
|
||||
"background_color": "${THEME_COLOR}"
|
||||
}
|
||||
Reference in New Issue
Block a user