feat: add production docker-compose, deploy.sh, caddy update script, and server install guide
This commit is contained in:
parent
afbadb55e9
commit
91ae2fd560
59 changed files with 583 additions and 0 deletions
14
.dockerignore
Normal file
14
.dockerignore
Normal 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
30
.env.example
Normal 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
1
.gitignore
vendored
|
|
@ -58,3 +58,4 @@ thecr3w_discovery_report_*.txt
|
|||
.DS_Store
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
|
|
|
|||
|
|
@ -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
106
SERVER_INSTALL_GUIDE.md
Normal 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
|
||||
```
|
||||
|
|
@ -125,3 +125,4 @@ public static class Ed25519Service
|
|||
return Convert.ToHexString(hash).ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,3 +56,4 @@ public static class ManifestSigner
|
|||
return Ed25519Service.ComputeSha256(canonicalBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,3 +52,4 @@ public class TheCr3wDbContext : DbContext
|
|||
.HasIndex(m => new { m.EnvironmentId, m.Status });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,3 +89,4 @@ public class ExternalDeploymentService : IExternalDeploymentService
|
|||
return string.Equals(actualFingerprint.Trim(), expectedFingerprint.Trim(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,3 +78,4 @@ public class RollbackStrategyConfig
|
|||
[JsonPropertyName("target_immutable_digest")]
|
||||
public string TargetImmutableDigest { get; set; } = "none";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,3 +65,4 @@ public enum SyncProtocol
|
|||
Ftps,
|
||||
Ftp
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,3 +55,4 @@ public class ReleaseMetadata
|
|||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? SignatureEd25519 { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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\"}"
|
||||
|
||||
|
|
|
|||
|
|
@ -80,3 +80,4 @@ jobs:
|
|||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer ${{ secrets.THECR3W_API_TOKEN }}" \
|
||||
-d @manifest.json
|
||||
|
||||
|
|
|
|||
|
|
@ -24,3 +24,4 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
echo "Publikacja wydania Windows do Cloudflare R2 i Native Update API..."
|
||||
|
||||
|
|
|
|||
|
|
@ -425,3 +425,4 @@ components:
|
|||
type: boolean
|
||||
correlation_id:
|
||||
type: string
|
||||
|
||||
|
|
|
|||
|
|
@ -604,3 +604,4 @@ components:
|
|||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
|
|
|
|||
|
|
@ -131,3 +131,4 @@
|
|||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,3 +90,4 @@
|
|||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
||||
|
|
|
|||
110
deploy.sh
Normal file
110
deploy.sh
Normal 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."
|
||||
|
|
@ -168,3 +168,4 @@ public class DeploymentExecutor
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
90
docker-compose.yml
Normal file
90
docker-compose.yml
Normal 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
|
||||
|
|
@ -43,3 +43,4 @@ CoroutineScope(Dispatchers.IO).launch {
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -74,3 +74,4 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|||
};
|
||||
});
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -46,3 +46,4 @@
|
|||
- **Rozwiązanie:**
|
||||
- Ponowne wywołanie endpointu pobierania w celu wygenerowania świeżego URL.
|
||||
- Weryfikacja synchronizacji NTP na serwerze: `timedatectl status`.
|
||||
|
||||
|
|
|
|||
|
|
@ -48,3 +48,4 @@ if (update.UpdateAvailable)
|
|||
TheCr3wUpdater.LaunchInstallerAndExit(tempPath, "/SILENT");
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@
|
|||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -20,3 +20,4 @@
|
|||
"vite": "^6.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -427,3 +427,4 @@ export const App: React.FC = () => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
||||
|
|
|
|||
|
|
@ -8,3 +8,4 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -59,3 +59,4 @@ export interface AuditEventItem {
|
|||
success: boolean;
|
||||
ipAddress?: string;
|
||||
}
|
||||
|
||||
|
|
|
|||
1
frontend/admin-panel/src/vite-env.d.ts
vendored
1
frontend/admin-panel/src/vite-env.d.ts
vendored
|
|
@ -1 +1,2 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
|
|
|
|||
|
|
@ -19,3 +19,4 @@
|
|||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@ export default defineConfig({
|
|||
port: 3001
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -11,3 +11,4 @@
|
|||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -315,3 +315,4 @@ export const App: React.FC = () => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -314,3 +314,4 @@ body {
|
|||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,3 +8,4 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -45,3 +45,4 @@ export async function getDownloadUrl(appId: string, releaseId: string): Promise<
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,3 +29,4 @@ export interface DownloadUrlResponse {
|
|||
sha256: string;
|
||||
signature_ed25519: string;
|
||||
}
|
||||
|
||||
|
|
|
|||
1
frontend/download-center/src/vite-env.d.ts
vendored
1
frontend/download-center/src/vite-env.d.ts
vendored
|
|
@ -1 +1,2 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
|
|
|
|||
|
|
@ -6,3 +6,4 @@ export default defineConfig({
|
|||
port: 3000
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -23,3 +23,4 @@ HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
|
|||
CMD wget -qO- http://localhost:80/ || exit 1
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
||||
|
|
|
|||
|
|
@ -23,3 +23,4 @@ HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
|
|||
CMD wget -qO- http://localhost:80/ || exit 1
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
|
|
|
|||
|
|
@ -34,3 +34,4 @@ services:
|
|||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
|
|
|
|||
90
infrastructure/docker/docker-compose.prod.yml
Normal file
90
infrastructure/docker/docker-compose.prod.yml
Normal 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
|
||||
|
|
@ -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
91
scripts/update_caddy.sh
Normal 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}"
|
||||
|
|
@ -174,3 +174,4 @@ class TheCr3wUpdater(
|
|||
return digest.digest().joinToString("") { "%02x".format(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -130,3 +130,4 @@ public class TheCr3wUpdater
|
|||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,3 +41,4 @@ public class ExternalDeploymentTests
|
|||
Assert.True(_service.ValidateHostKey("SHA256:abc123actual", "SHA256:abc123actual"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue