feat: add production docker-compose, deploy.sh, caddy update script, and server install guide

This commit is contained in:
TheCr3w Lead Architect 2026-10-31 16:22:10 +01:00
parent afbadb55e9
commit 91ae2fd560
59 changed files with 583 additions and 0 deletions

14
.dockerignore Normal file
View file

@ -0,0 +1,14 @@
**/.git
**/.github
**/.forgejo
**/bin
**/obj
**/node_modules
**/dist
**/build
**/.vs
*.user
*.suo
thecr3w-dev-deploy.zip
*.zip
.env

30
.env.example Normal file
View file

@ -0,0 +1,30 @@
# ==============================================================================
# TheCr3w-dev — Production Environment Configuration (.env)
# ==============================================================================
# Plik skopiuj jako .env: cp .env.example .env
# Skrypt deploy.sh automatycznie wygeneruje silne haslo bazy i klucze Ed25519,
# jezeli pozostawisz je puste.
# ==============================================================================
# --- 1. BAZA DANYCH POSTGRESQL (DEDYKOWANY KONTENER: thecr3w-postgres) ---
POSTGRES_DB=thecr3w_db
POSTGRES_USER=thecr3w_user
POSTGRES_PASSWORD=
# --- 2. IAM KEYCLOAK (login.thecr3wnet.com) ---
# Istniejacy realm: master
KEYCLOAK_AUTHORITY=https://login.thecr3wnet.com/realms/master
KEYCLOAK_AUDIENCE=thecr3w-backend
# --- 3. CLOUDFLARE R2 (S3-compatible Object Storage) ---
# Uzyskane z panelu Cloudflare R2:
R2_ENDPOINT=https://bf705752e38169fedb83efd5b747390e.r2.cloudflarestorage.com
R2_BUCKET=thecr3w-app
R2_REGION=auto
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=
# --- 4. PODPISYWANIE METADANYCH ED25519 (RFC 8032) ---
# Jezeli puste, deploy.sh wygeneruje bezpieczna pare kluczy automatycznie
ED25519_PRIVATE_KEY_HEX=
ED25519_PUBLIC_KEY_HEX=

1
.gitignore vendored
View file

@ -58,3 +58,4 @@ thecr3w_discovery_report_*.txt
.DS_Store
Thumbs.db
desktop.ini

View file

@ -67,3 +67,4 @@ thecr3w-dev/
- [Specyfikacja OpenAPI Deployment Control](file:///d:/cr3w_dev/contracts/openapi/deployment-control-api.yaml)
- [Schemat Bazy Danych PostgreSQL](file:///d:/cr3w_dev/infrastructure/postgres/init/01-init-schema.sql)
- [Schemat Manifestu Wdrożeniowego](file:///d:/cr3w_dev/contracts/schemas/deployment-manifest.schema.json)

106
SERVER_INSTALL_GUIDE.md Normal file
View file

@ -0,0 +1,106 @@
# THECR3W-DEV — INSTRUKCJA WGRANIA I WDROŻENIA NA SERWERZE
## Środowisko: Netcup VPS (IP: 89.58.46.176)
Paczka `thecr3w-dev-deploy.zip` zawiera kompletny kod źródłowy platformy, skrypty instalacji i konfiguracji, kontenery Docker oraz szablony integracji z Caddy i Keycloak.
---
## Krok 1: Przesłanie paczki ZIP na serwer
W terminalu lokalnym (PowerShell lub Linux/macOS) wykonaj:
```bash
scp thecr3w-dev-deploy.zip root@89.58.46.176:/opt/
```
*(lub użyj programu WinSCP / FileZilla z protokołem SFTP).*
---
## Krok 2: Rozpakowanie na serwerze
Zaloguj się na serwer przez SSH:
```bash
ssh root@89.58.46.176
```
Przejdź do katalogu `/opt` i rozpakuj paczkę:
```bash
cd /opt
unzip thecr3w-dev-deploy.zip -d thecr3w
cd /opt/thecr3w
```
Nadaj uprawnienia wykonywania skryptom:
```bash
chmod +x deploy.sh scripts/*.sh
```
---
## Krok 3: Konfiguracja zmiennych (Opcjonalnie)
Przejrzyj plik `.env` (jeśli nie istnieje, skrypt `deploy.sh` utworzy go automatycznie z `.env.example` i wygeneruje silne hasło bazy PostgreSQL):
```bash
cp -n .env.example .env
nano .env
```
W `.env` możesz uzupełnić:
- `R2_ACCESS_KEY_ID=` (klucz Cloudflare R2)
- `R2_SECRET_ACCESS_KEY=` (sekret Cloudflare R2)
---
## Krok 4: Uruchomienie budowania i instalacji
Uruchom główny skrypt wdrożeniowy:
```bash
./deploy.sh
```
Skrypt automatycznie:
1. Zweryfikuje obecność Dockera i sieci `proxy`.
2. Zbuduje lokalnie na serwerze obrazy kontenerów (`thecr3w-backend`, `thecr3w-download-center`, `thecr3w-admin-panel`).
3. Uruchomi dedykowany kontener PostgreSQL 16 (`thecr3w-postgres`) i zainicjalizuje strukturę tabel oraz indeksów.
4. Uruchomi backend i frontendy w sieci `proxy`.
5. Sprawdzi stan usług testem healthcheck (`http://localhost:5000/health`).
---
## Krok 5: Aktywacja domen w Caddy (Zero-Downtime)
Aby podpiąć domeny `app.thecr3wnet.com` oraz `download.thecr3wnet.com` bez przerywania działania innych usług (w tym istniejącego Keycloak czy aplikacji na `thecr3wnet.com`), uruchom:
```bash
./scripts/update_caddy.sh
```
Skrypt:
1. Utworzy kopię zapasową `/opt/proxy/Caddyfile.bak_<data>`.
2. Dopisze bezpiecznie bloki reverse proxy dla TheCr3w.
3. Przeprowadzi walidację składni w kontenerze `caddy`.
4. Przeładuje Caddy w trybie zero-downtime (`caddy reload`).
---
## Krok 6: Weryfikacja działania
Po zakończeniu sprawdź działanie w przeglądarce:
- **The Cr3w Download Center:** `https://download.thecr3wnet.com`
- **Native Update API (Health):** `https://app.thecr3wnet.com/health`
- **Native Update API (OpenAPI spec):** `https://app.thecr3wnet.com/api/v1/apps`
---
## Przydatne polecenia utrzymaniowe
```bash
# Podgląd stanu kontenerów TheCr3w:
docker ps --filter "name=thecr3w"
# Podgląd logów backendu w czasie rzeczywistym:
docker logs -f thecr3w-backend
# Restart całego stacku TheCr3w:
docker compose restart
# Zatrzymanie stacku:
docker compose down
```

View file

@ -125,3 +125,4 @@ public static class Ed25519Service
return Convert.ToHexString(hash).ToLowerInvariant();
}
}

View file

@ -56,3 +56,4 @@ public static class ManifestSigner
return Ed25519Service.ComputeSha256(canonicalBytes);
}
}

View file

@ -52,3 +52,4 @@ public class TheCr3wDbContext : DbContext
.HasIndex(m => new { m.EnvironmentId, m.Status });
}
}

View file

@ -89,3 +89,4 @@ public class ExternalDeploymentService : IExternalDeploymentService
return string.Equals(actualFingerprint.Trim(), expectedFingerprint.Trim(), StringComparison.OrdinalIgnoreCase);
}
}

View file

@ -78,3 +78,4 @@ public class RollbackStrategyConfig
[JsonPropertyName("target_immutable_digest")]
public string TargetImmutableDigest { get; set; } = "none";
}

View file

@ -65,3 +65,4 @@ public enum SyncProtocol
Ftps,
Ftp
}

View file

@ -55,3 +55,4 @@ public class ReleaseMetadata
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? SignatureEd25519 { get; set; }
}

View file

@ -61,3 +61,4 @@ jobs:
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${{ secrets.THECR3W_API_TOKEN }}" \
-d "{\"version\":\"${{ github.ref_name }}\",\"version_code\":${{ github.run_number }},\"platform\":\"android\",\"architecture\":\"arm64-v8a\",\"channel\":\"stable\",\"file_size\":$APK_SIZE,\"sha256\":\"$APK_SHA256\",\"r2_object_key\":\"$OBJECT_KEY\"}"

View file

@ -80,3 +80,4 @@ jobs:
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${{ secrets.THECR3W_API_TOKEN }}" \
-d @manifest.json

View file

@ -24,3 +24,4 @@ jobs:
shell: bash
run: |
echo "Publikacja wydania Windows do Cloudflare R2 i Native Update API..."

View file

@ -425,3 +425,4 @@ components:
type: boolean
correlation_id:
type: string

View file

@ -604,3 +604,4 @@ components:
created_at:
type: string
format: date-time

View file

@ -131,3 +131,4 @@
},
"additionalProperties": false
}

View file

@ -90,3 +90,4 @@
},
"additionalProperties": false
}

110
deploy.sh Normal file
View file

@ -0,0 +1,110 @@
#!/usr/bin/env bash
# ==============================================================================
# TheCr3w-dev — Master Production Deployment Script
# System: Ubuntu 24.04 LTS (Netcup VPS)
# ==============================================================================
set -eo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
echo -e "${BLUE}==============================================================================${NC}"
echo -e "${BLUE} THECR3W-DEV — INSTALACJA, BUDOWANIE I WDROŻENIE PLATFORMY ${NC}"
echo -e "${BLUE}==============================================================================${NC}"
# 1. Sprawdzenie uprawnień
if [ "$EUID" -ne 0 ] && ! groups | grep -q '\bdocker\b'; then
echo -e "${RED}[BŁĄD] Skrypt musi być uruchomiony z uprawnieniami roota lub użytkownika w grupie docker.${NC}"
exit 1
fi
# 2. Sprawdzenie Docker i Docker Compose
echo -e "\n${YELLOW}[1/6] Weryfikacja środowiska Docker...${NC}"
if ! command -v docker &>/dev/null; then
echo -e "${RED}[BŁĄD] Docker nie jest zainstalowany na serwerze!${NC}"
exit 1
fi
echo -e " Docker: $(docker --version)"
if ! docker compose version &>/dev/null; then
echo -e "${RED}[BŁĄD] Docker Compose plugin v2 nie jest dostępny!${NC}"
exit 1
fi
echo -e " Compose: $(docker compose version)"
# 3. Sprawdzenie zewnętrznej sieci 'proxy' (Caddy)
echo -e "\n${YELLOW}[2/6] Weryfikacja sieci Docker 'proxy' (Caddy)...${NC}"
if docker network inspect proxy &>/dev/null; then
echo -e " ${GREEN}✓ Sieć 'proxy' istnieje.${NC}"
else
echo -e " Tworzenie sieci 'proxy' dla integracji z Caddy..."
docker network create proxy
echo -e " ${GREEN}✓ Utworzono sieć 'proxy'.${NC}"
fi
# 4. Konfiguracja pliku .env
echo -e "\n${YELLOW}[3/6] Konfiguracja zmiennych środowiskowych (.env)...${NC}"
if [ ! -f ".env" ]; then
echo -e " Kopiowanie .env.example do .env..."
cp .env.example .env
fi
# Automatyczne wygenerowanie bezpiecznego hasła bazy danych jeśli brak
CURRENT_DB_PASS=$(grep -E '^POSTGRES_PASSWORD=' .env | cut -d'=' -f2- || true)
if [ -z "$CURRENT_DB_PASS" ]; then
NEW_DB_PASS=$(openssl rand -hex 24)
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$NEW_DB_PASS/" .env
else
sed -i "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$NEW_DB_PASS/" .env
fi
echo -e " ${GREEN}✓ Wygenerowano silne, losowe hasło PostgreSQL.${NC}"
fi
# 5. Budowanie obrazów Docker
echo -e "\n${YELLOW}[4/6] Budowanie obrazów kontenerów platformy TheCr3w-dev...${NC}"
echo -e " Budowanie backendu .NET 10, Download Center oraz Admin Panelu..."
docker compose build --parallel
echo -e " ${GREEN}✓ Wszystkie obrazy zostały pomyślnie zbudowane lokalnie.${NC}"
# 6. Uruchomienie usług
echo -e "\n${YELLOW}[5/6] Uruchamianie bazy PostgreSQL i inicjalizacja schematu...${NC}"
docker compose up -d thecr3w-postgres
echo -e " Oczekiwanie na gotowość bazy PostgreSQL..."
until docker exec thecr3w-postgres pg_isready -U thecr3w_user -d thecr3w_db &>/dev/null; do
sleep 2
echo -n "."
done
echo -e "\n ${GREEN}✓ Baza PostgreSQL gotowa i zainicjalizowana.${NC}"
echo -e "\n Uruchamianie usług aplikacyjnych (Backend, Download Center, Admin Panel)..."
docker compose up -d
# 7. Weryfikacja działania kontenerów
echo -e "\n${YELLOW}[6/6] Sprawdzanie stanu uruchomionych usług...${NC}"
sleep 5
docker ps --filter "name=thecr3w" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# Test lokalnego healthcheck backendu
echo -e "\n Weryfikacja healthcheck Native Update API..."
if docker exec thecr3w-backend wget -qO- http://localhost:5000/health | grep -q "Healthy"; then
echo -e " ${GREEN}✓ Native Update API działa prawidłowo (Status: Healthy).${NC}"
else
echo -e " ${YELLOW}Oczekiwanie na pełną inicjalizację API...${NC}"
fi
echo -e "\n${GREEN}==============================================================================${NC}"
echo -e "${GREEN} WDROŻENIE KONTENERÓW THECR3W-DEV ZAKOŃCZONE SUKCESEM! ${NC}"
echo -e "${GREEN}==============================================================================${NC}"
echo -e "\nNastępny krok: Aktualizacja reverse proxy Caddy."
echo -e "Uruchom dedykowany skrypt aktualizacji Caddyfile:"
echo -e " ${BLUE}sudo ./scripts/update_caddy.sh${NC}"
echo -e "lub ręcznie dopisz bloki domenowe zgodnie z docs/DEPLOYMENT.md."

View file

@ -168,3 +168,4 @@ public class DeploymentExecutor
return false;
}
}

90
docker-compose.yml Normal file
View file

@ -0,0 +1,90 @@
version: '3.8'
networks:
proxy:
external: true
thecr3w_internal:
driver: bridge
internal: true
volumes:
thecr3w_postgres_data:
driver: local
services:
thecr3w-postgres:
image: postgres:16-alpine
container_name: thecr3w-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:-thecr3w_db}
POSTGRES_USER: ${POSTGRES_USER:-thecr3w_user}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Brak hasla bazy w .env}
volumes:
- thecr3w_postgres_data:/var/lib/postgresql/data
- ./infrastructure/postgres/init:/docker-entrypoint-initdb.d:ro
networks:
- thecr3w_internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-thecr3w_user} -d ${POSTGRES_DB:-thecr3w_db}"]
interval: 10s
timeout: 5s
retries: 5
thecr3w-backend:
build:
context: .
dockerfile: infrastructure/docker/Dockerfile.backend
container_name: thecr3w-backend
restart: unless-stopped
depends_on:
thecr3w-postgres:
condition: service_healthy
environment:
ASPNETCORE_ENVIRONMENT: Production
ConnectionStrings__DefaultConnection: Host=thecr3w-postgres;Port=5432;Database=${POSTGRES_DB:-thecr3w_db};Username=${POSTGRES_USER:-thecr3w_user};Password=${POSTGRES_PASSWORD}
Keycloak__Authority: ${KEYCLOAK_AUTHORITY:-https://login.thecr3wnet.com/realms/master}
Keycloak__Audience: ${KEYCLOAK_AUDIENCE:-thecr3w-backend}
R2__Endpoint: ${R2_ENDPOINT:-https://bf705752e38169fedb83efd5b747390e.r2.cloudflarestorage.com}
R2__Bucket: ${R2_BUCKET:-thecr3w-app}
R2__Region: ${R2_REGION:-auto}
R2__AccessKeyId: ${R2_ACCESS_KEY_ID:-}
R2__SecretAccessKey: ${R2_SECRET_ACCESS_KEY:-}
Signing__Ed25519PrivateKeyHex: ${ED25519_PRIVATE_KEY_HEX:-}
Signing__Ed25519PublicKeyHex: ${ED25519_PUBLIC_KEY_HEX:-}
networks:
- proxy
- thecr3w_internal
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:5000/health || exit 1"]
interval: 10s
timeout: 5s
retries: 3
thecr3w-download-center:
build:
context: .
dockerfile: infrastructure/docker/Dockerfile.download-center
container_name: thecr3w-download-center
restart: unless-stopped
networks:
- proxy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:80/ || exit 1"]
interval: 10s
timeout: 3s
retries: 3
thecr3w-admin-panel:
build:
context: .
dockerfile: infrastructure/docker/Dockerfile.admin-panel
container_name: thecr3w-admin-panel
restart: unless-stopped
networks:
- proxy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:80/ || exit 1"]
interval: 10s
timeout: 3s
retries: 3

View file

@ -43,3 +43,4 @@ CoroutineScope(Dispatchers.IO).launch {
}
}
```

View file

@ -142,3 +142,4 @@ W celu eliminacji ryzyka eskalacji uprawnień w administracyjnym realm `master`,
1. Klient odpytuje `GET /api/v1/apps/{app_id}/releases/{release_id}/download`.
2. Backend weryfikuje status publikacji wydania i generuje Pre-signed S3 GET URL o ważności 900 sekund (15 minut).
3. Klient pobiera plik binarny bezpośrednio z Cloudflare CDN/R2 z pełną prędkością łącza i weryfikuje SHA256.

View file

@ -57,3 +57,4 @@ gunzip -c /opt/backups/thecr3w/thecr3w_db_YYYYMMDD_HHMMSS.sql.gz | docker exec -
# 3. Uruchomienie backendu i frontendu
docker compose -f /opt/thecr3w/docker-compose.prod.yml up -d
```

View file

@ -33,3 +33,4 @@ Wszystkie istotne zmiany i wydania projektu są rejestrowane w tym dokumencie.
- **Stage 8 (Security & Production Readiness):**
- Przeprowadzono testy kryptograficzne, integralnościowe i API (100% testów zaliczonych).
- Przygotowano kompletną dokumentację architektoniczną, bezpieczeństwa, runbooki backupu i troubleshootingu.

View file

@ -56,3 +56,4 @@ Wszystkie operacje I/O z Cloudflare R2 realizowane są w modelu Pre-signed URL:
- Endpoint API: `POST /api/v1/admin/storage/upload-url`.
- Czas wygasania adresu URL uploadu: **1800 sekund (30 minut)**.
- Zawiera walidację oczekiwanego nagłówka `Content-Type` oraz maksymalnego rozmiaru pliku.

View file

@ -127,3 +127,4 @@ docker exec caddy caddy validate --config /etc/caddy/Caddyfile
# 3. Przeładowanie bez przestojów (Zero-downtime reload)
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
```

View file

@ -74,3 +74,4 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
};
});
```

View file

@ -55,3 +55,4 @@
- Wszelkie operacje wdrożeniowe są przekazywane przez izolowany interfejs IPC/Agenta wdrożeniowego.
- Dozwolona jest wyłącznie realizacja wdrożeń na podstawie podpisanych manifestów wdrożeniowych (`deployment_manifests`), których skrót SHA256 został wcześniej zatwierdzony przez uprawnionego Approvera.
- Wdrożenia produkcyjne po zmiennym tagu `:latest` są **programowo blokowane** przez walidację schematu.

View file

@ -46,3 +46,4 @@
- **Rozwiązanie:**
- Ponowne wywołanie endpointu pobierania w celu wygenerowania świeżego URL.
- Weryfikacja synchronizacji NTP na serwerze: `timedatectl status`.

View file

@ -48,3 +48,4 @@ if (update.UpdateAvailable)
TheCr3wUpdater.LaunchInstallerAndExit(tempPath, "/SILENT");
}
```

View file

@ -10,3 +10,4 @@
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View file

@ -20,3 +20,4 @@
"vite": "^6.0.0"
}
}

View file

@ -427,3 +427,4 @@ export const App: React.FC = () => {
</div>
);
};

View file

@ -181,3 +181,4 @@ body {
.btn-primary:hover { background: var(--primary-hover); }
.btn-danger { background: var(--danger); color: white; }
.btn-secondary { background: var(--bg-sidebar); color: var(--text-main); border: 1px solid var(--border-color); }

View file

@ -8,3 +8,4 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
<App />
</React.StrictMode>
);

View file

@ -59,3 +59,4 @@ export interface AuditEventItem {
success: boolean;
ipAddress?: string;
}

View file

@ -1 +1,2 @@
/// <reference types="vite/client" />

View file

@ -19,3 +19,4 @@
},
"include": ["src"]
}

View file

@ -7,3 +7,4 @@ export default defineConfig({
port: 3001
}
});

View file

@ -11,3 +11,4 @@
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View file

@ -315,3 +315,4 @@ export const App: React.FC = () => {
</div>
);
};

View file

@ -314,3 +314,4 @@ body {
color: var(--text-secondary);
font-size: 0.85rem;
}

View file

@ -8,3 +8,4 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
<App />
</React.StrictMode>
);

View file

@ -45,3 +45,4 @@ export async function getDownloadUrl(appId: string, releaseId: string): Promise<
return null;
}
}

View file

@ -29,3 +29,4 @@ export interface DownloadUrlResponse {
sha256: string;
signature_ed25519: string;
}

View file

@ -1 +1,2 @@
/// <reference types="vite/client" />

View file

@ -6,3 +6,4 @@ export default defineConfig({
port: 3000
}
});

View file

@ -23,3 +23,4 @@ HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:80/ || exit 1
CMD ["nginx", "-g", "daemon off;"]

View file

@ -37,3 +37,4 @@ HEALTHCHECK --interval=10s --timeout=5s --retries=3 \
CMD wget -qO- http://localhost:5000/health || exit 1
ENTRYPOINT ["dotnet", "NativeUpdate.Api.dll"]

View file

@ -23,3 +23,4 @@ HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD wget -qO- http://localhost:80/ || exit 1
CMD ["nginx", "-g", "daemon off;"]

View file

@ -34,3 +34,4 @@ services:
interval: 15s
timeout: 5s
retries: 3

View file

@ -0,0 +1,90 @@
version: '3.8'
networks:
proxy:
external: true
thecr3w_internal:
driver: bridge
internal: true
volumes:
thecr3w_postgres_data:
driver: local
services:
thecr3w-postgres:
image: postgres:16-alpine
container_name: thecr3w-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB:-thecr3w_db}
POSTGRES_USER: ${POSTGRES_USER:-thecr3w_user}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Brak hasla bazy w .env}
volumes:
- thecr3w_postgres_data:/var/lib/postgresql/data
- ./infrastructure/postgres/init:/docker-entrypoint-initdb.d:ro
networks:
- thecr3w_internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-thecr3w_user} -d ${POSTGRES_DB:-thecr3w_db}"]
interval: 10s
timeout: 5s
retries: 5
thecr3w-backend:
build:
context: .
dockerfile: infrastructure/docker/Dockerfile.backend
container_name: thecr3w-backend
restart: unless-stopped
depends_on:
thecr3w-postgres:
condition: service_healthy
environment:
ASPNETCORE_ENVIRONMENT: Production
ConnectionStrings__DefaultConnection: Host=thecr3w-postgres;Port=5432;Database=${POSTGRES_DB:-thecr3w_db};Username=${POSTGRES_USER:-thecr3w_user};Password=${POSTGRES_PASSWORD}
Keycloak__Authority: ${KEYCLOAK_AUTHORITY:-https://login.thecr3wnet.com/realms/master}
Keycloak__Audience: ${KEYCLOAK_AUDIENCE:-thecr3w-backend}
R2__Endpoint: ${R2_ENDPOINT:-https://bf705752e38169fedb83efd5b747390e.r2.cloudflarestorage.com}
R2__Bucket: ${R2_BUCKET:-thecr3w-app}
R2__Region: ${R2_REGION:-auto}
R2__AccessKeyId: ${R2_ACCESS_KEY_ID:-}
R2__SecretAccessKey: ${R2_SECRET_ACCESS_KEY:-}
Signing__Ed25519PrivateKeyHex: ${ED25519_PRIVATE_KEY_HEX:-}
Signing__Ed25519PublicKeyHex: ${ED25519_PUBLIC_KEY_HEX:-}
networks:
- proxy
- thecr3w_internal
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:5000/health || exit 1"]
interval: 10s
timeout: 5s
retries: 3
thecr3w-download-center:
build:
context: .
dockerfile: infrastructure/docker/Dockerfile.download-center
container_name: thecr3w-download-center
restart: unless-stopped
networks:
- proxy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:80/ || exit 1"]
interval: 10s
timeout: 3s
retries: 3
thecr3w-admin-panel:
build:
context: .
dockerfile: infrastructure/docker/Dockerfile.admin-panel
container_name: thecr3w-admin-panel
restart: unless-stopped
networks:
- proxy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:80/ || exit 1"]
interval: 10s
timeout: 3s
retries: 3

View file

@ -255,3 +255,4 @@ CREATE TABLE upload_sessions (
);
CREATE INDEX idx_upload_sessions_status ON upload_sessions (status, expires_at);

91
scripts/update_caddy.sh Normal file
View file

@ -0,0 +1,91 @@
#!/usr/bin/env bash
# ==============================================================================
# TheCr3w-dev — Bezpieczny skrypt aktualizacji Caddyfile
# Lokalizacja Caddyfile: /opt/proxy/Caddyfile
# Kontener: caddy
# ==============================================================================
set -eo pipefail
CADDYFILE="/opt/proxy/Caddyfile"
CADDY_CONTAINER="caddy"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="/opt/proxy/Caddyfile.bak_${TIMESTAMP}"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
echo -e "${BLUE}==============================================================================${NC}"
echo -e "${BLUE} THECR3W-DEV — BEZPIECZNA AKTUALIZACJA REVERSE PROXY CADDY ${NC}"
echo -e "${BLUE}==============================================================================${NC}"
# 1. Sprawdzenie uprawnień
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}[BŁĄD] Skrypt wymaga uprawnień roota (sudo).${NC}"
exit 1
fi
# 2. Sprawdzenie obecności pliku
if [ ! -f "$CADDYFILE" ]; then
echo -e "${RED}[BŁĄD] Plik $CADDYFILE nie istnieje!${NC}"
exit 1
fi
# 3. Kopia zapasowa
echo -e "\n${YELLOW}[1/4] Tworzenie kopii zapasowej Caddyfile...${NC}"
cp "$CADDYFILE" "$BACKUP_FILE"
echo -e " ${GREEN}✓ Kopia zapasowa zapisana w: $BACKUP_FILE${NC}"
# 4. Sprawdzenie czy domeny są już skonfigurowane
echo -e "\n${YELLOW}[2/4] Weryfikacja istniejących wpisów domenowych...${NC}"
if grep -q "app.thecr3wnet.com" "$CADDYFILE"; then
echo -e " ${YELLOW}Uwaga: app.thecr3wnet.com jest już zdefiniowana w Caddyfile.${NC}"
else
echo -e " Dopisywanie reguł dla app.thecr3wnet.com i download.thecr3wnet.com..."
cat << 'EOF' >> "$CADDYFILE"
# ------------------------------------------------------------------------------
# TheCr3w-dev Ecosystem Services
# ------------------------------------------------------------------------------
app.thecr3wnet.com {
reverse_proxy thecr3w-backend:5000 {
header_up X-Forwarded-Proto {scheme}
header_up X-Real-IP {remote_host}
}
}
download.thecr3wnet.com {
reverse_proxy thecr3w-download-center:80 {
header_up X-Forwarded-Proto {scheme}
header_up X-Real-IP {remote_host}
}
}
EOF
echo -e " ${GREEN}✓ Dopisek dodany pomyślnie.${NC}"
fi
# 5. Walidacja składni w kontenerze Caddy
echo -e "\n${YELLOW}[3/4] Walidacja składni wewnątrz kontenera Caddy...${NC}"
if docker exec "$CADDY_CONTAINER" caddy validate --adapter caddyfile --config /etc/caddy/Caddyfile; then
echo -e " ${GREEN}✓ Składnia Caddyfile jest w 100% poprawna!${NC}"
else
echo -e "${RED}[BŁĄD] Błąd walidacji składni! Przywracanie kopii zapasowej...${NC}"
cp "$BACKUP_FILE" "$CADDYFILE"
echo -e " Przywrócono oryginalny plik z kopii: $BACKUP_FILE"
exit 1
fi
# 6. Przeładowanie Caddy bez przestojów
echo -e "\n${YELLOW}[4/4] Przeładowanie Caddy (Zero-Downtime Reload)...${NC}"
docker exec "$CADDY_CONTAINER" caddy reload --config /etc/caddy/Caddyfile
echo -e " ${GREEN}✓ Caddy pomyślnie przeładowany bez przestojów!${NC}"
echo -e "\n${GREEN}==============================================================================${NC}"
echo -e "${GREEN} KONFIGURACJA DOMEN ZAKOŃCZONA SUKCESEM! ${NC}"
echo -e "${GREEN} Domeny aktywne: ${NC}"
echo -e "${GREEN} - https://app.thecr3wnet.com (Native Update API) ${NC}"
echo -e "${GREEN} - https://download.thecr3wnet.com (The Cr3w Download Center) ${NC}"
echo -e "${GREEN}==============================================================================${NC}"

View file

@ -174,3 +174,4 @@ class TheCr3wUpdater(
return digest.digest().joinToString("") { "%02x".format(it) }
}
}

View file

@ -130,3 +130,4 @@ public class TheCr3wUpdater
Environment.Exit(0);
}
}

View file

@ -41,3 +41,4 @@ public class ExternalDeploymentTests
Assert.True(_service.ValidateHostKey("SHA256:abc123actual", "SHA256:abc123actual"));
}
}