mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-24 21:36:39 +02:00
feat: adapt codebase for IP-based HTTP deployment on 172.20.10.59
Switch from hostname+HTTPS (insight-dev.xinion.lan) to IP+HTTP (172.20.10.59) for alpha/dev deployment without DNS. Key changes: - Cookie secure/sameSite flags environment-conditional (fixes HTTP auth) - docker-compose.yml: remove HTTPS, update host rules, reduce PG memory - Traefik: disable TLS, update CORS/CSP for HTTP - Add Prisma init migration (7 tables) and admin seed script - Generate package-lock.json for npm ci in Docker builds - Update all documentation for IP-based access Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
10f291cdda
commit
5412ae137a
17 changed files with 14970 additions and 105 deletions
|
|
@ -8,8 +8,8 @@
|
||||||
# --- Allgemein ---
|
# --- Allgemein ---
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
APP_PORT=3000
|
APP_PORT=3000
|
||||||
APP_URL=https://insight-dev.xinion.lan
|
APP_URL=http://172.20.10.59
|
||||||
FRONTEND_URL=https://insight-dev.xinion.lan
|
FRONTEND_URL=http://172.20.10.59
|
||||||
LOG_LEVEL=info
|
LOG_LEVEL=info
|
||||||
|
|
||||||
# --- PostgreSQL ---
|
# --- PostgreSQL ---
|
||||||
|
|
@ -41,7 +41,7 @@ JWT_ISSUER=insight-platform
|
||||||
BCRYPT_COST=12
|
BCRYPT_COST=12
|
||||||
|
|
||||||
# --- CORS ---
|
# --- CORS ---
|
||||||
CORS_ORIGINS=https://insight-dev.xinion.lan
|
CORS_ORIGINS=http://172.20.10.59
|
||||||
|
|
||||||
# --- Rate Limiting ---
|
# --- Rate Limiting ---
|
||||||
THROTTLE_TTL=60000
|
THROTTLE_TTL=60000
|
||||||
|
|
|
||||||
25
README.md
25
README.md
|
|
@ -63,9 +63,9 @@ cp .env.example .env
|
||||||
### 3. JWT-Schluessel generieren
|
### 3. JWT-Schluessel generieren
|
||||||
```bash
|
```bash
|
||||||
# RS256 Schluessel fuer JWT-Signierung
|
# RS256 Schluessel fuer JWT-Signierung
|
||||||
mkdir -p packages/core-service/keys
|
mkdir -p keys
|
||||||
openssl genpkey -algorithm RSA -out packages/core-service/keys/jwt-private.pem -pkeyopt rsa_keygen_bits:2048
|
openssl genpkey -algorithm RSA -out keys/jwt-private.pem -pkeyopt rsa_keygen_bits:2048
|
||||||
openssl rsa -pubout -in packages/core-service/keys/jwt-private.pem -out packages/core-service/keys/jwt-public.pem
|
openssl rsa -pubout -in keys/jwt-private.pem -out keys/jwt-public.pem
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Services starten
|
### 4. Services starten
|
||||||
|
|
@ -77,23 +77,24 @@ docker compose up -d
|
||||||
docker compose -f docker-compose.yml -f docker-compose.observability.yml up -d
|
docker compose -f docker-compose.yml -f docker-compose.observability.yml up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Datenbank-Migration
|
### 5. Datenbank-Migration + Seed
|
||||||
```bash
|
```bash
|
||||||
# Core-Schema
|
# Core-Schema migrieren
|
||||||
docker compose exec core npx prisma migrate deploy --schema=./prisma/core.schema.prisma
|
docker compose run --rm core npx prisma migrate deploy --schema=./prisma/core.schema.prisma
|
||||||
|
|
||||||
# Tenant-Schema (wird beim Onboarding automatisch ausgefuehrt)
|
# Admin-User anlegen
|
||||||
|
docker compose run --rm core npx ts-node prisma/seed.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. Health-Checks pruefen
|
### 6. Health-Checks pruefen
|
||||||
```bash
|
```bash
|
||||||
curl http://localhost:3000/health # Core-Service
|
curl http://172.20.10.59/health
|
||||||
curl http://localhost:8080 # Frontend
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7. Erster Login
|
### 7. Erster Login
|
||||||
- URL: https://insight-dev.xinion.lan (oder http://localhost)
|
- URL: `http://172.20.10.59`
|
||||||
- Initialer Admin-Account wird beim ersten Start via Seed-Script angelegt
|
- Admin: `admin@xinion.de` / `ChangeMe123!`
|
||||||
|
- Passwort nach erstem Login aendern!
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -101,7 +102,7 @@ curl http://localhost:8080 # Frontend
|
||||||
|
|
||||||
| Service | Port (intern) | URL (extern via Traefik) | Beschreibung |
|
| Service | Port (intern) | URL (extern via Traefik) | Beschreibung |
|
||||||
|---------------|---------------|----------------------------------|------------------------|
|
|---------------|---------------|----------------------------------|------------------------|
|
||||||
| Traefik | 80/443 | https://insight-dev.xinion.lan | API Gateway |
|
| Traefik | 80 | http://172.20.10.59 | API Gateway |
|
||||||
| Core-Service | 3000 | /api/v1/* | NestJS Backend |
|
| Core-Service | 3000 | /api/v1/* | NestJS Backend |
|
||||||
| Frontend | 8080 | /* | React App |
|
| Frontend | 8080 | /* | React App |
|
||||||
| PostgreSQL | 5432 | - | Datenbank |
|
| PostgreSQL | 5432 | - | Datenbank |
|
||||||
|
|
|
||||||
76
Summarize.md
76
Summarize.md
|
|
@ -67,11 +67,11 @@
|
||||||
**Erstellte Dateien:**
|
**Erstellte Dateien:**
|
||||||
|
|
||||||
1. **`docker-compose.yml`** - Alle Basis-Services:
|
1. **`docker-compose.yml`** - Alle Basis-Services:
|
||||||
- Traefik 3 (API Gateway, Reverse Proxy, TLS, Rate Limiting)
|
- Traefik 3 (API Gateway, Reverse Proxy, Rate Limiting)
|
||||||
- PostgreSQL 16-alpine (mit Performance-Tuning fuer 8GB RAM)
|
- PostgreSQL 16-alpine (Performance-Tuning: 1GB shared_buffers, 4GB cache)
|
||||||
- PgBouncer (Connection Pooling, Transaction Mode)
|
- PgBouncer (Connection Pooling, Transaction Mode)
|
||||||
- Redis 7-alpine (Cache, Sessions, Token-Revocation)
|
- Redis 7-alpine (Cache, Sessions, Token-Revocation)
|
||||||
- step-ca (Interne Certificate Authority fuer mTLS)
|
- step-ca (Interne Certificate Authority fuer mTLS - geplant)
|
||||||
- Core-Service (NestJS) mit Traefik-Labels
|
- Core-Service (NestJS) mit Traefik-Labels
|
||||||
- Frontend (React) mit Traefik-Labels
|
- Frontend (React) mit Traefik-Labels
|
||||||
- 3 isolierte Docker-Netzwerke (insight-web, insight-db, insight-cache)
|
- 3 isolierte Docker-Netzwerke (insight-web, insight-db, insight-cache)
|
||||||
|
|
@ -87,7 +87,7 @@
|
||||||
- PostgreSQL Exporter (DB-Metriken)
|
- PostgreSQL Exporter (DB-Metriken)
|
||||||
|
|
||||||
3. **Konfigurationsdateien:**
|
3. **Konfigurationsdateien:**
|
||||||
- `config/traefik/dynamic/tls.yml` - TLS-Konfiguration
|
- `config/traefik/dynamic/tls.yml` - TLS deaktiviert (Alpha/Dev)
|
||||||
- `config/traefik/dynamic/middlewares.yml` - Security-Headers, CORS, Compression
|
- `config/traefik/dynamic/middlewares.yml` - Security-Headers, CORS, Compression
|
||||||
- `config/prometheus/prometheus.yml` - Scrape-Konfiguration
|
- `config/prometheus/prometheus.yml` - Scrape-Konfiguration
|
||||||
- `config/loki/loki.yml` - Log-Storage-Konfiguration
|
- `config/loki/loki.yml` - Log-Storage-Konfiguration
|
||||||
|
|
@ -113,7 +113,7 @@
|
||||||
- `TotpService`: TOTP 2FA (Google Authenticator kompatibel)
|
- `TotpService`: TOTP 2FA (Google Authenticator kompatibel)
|
||||||
- `LoginDto`: Validierung mit class-validator
|
- `LoginDto`: Validierung mit class-validator
|
||||||
- Account-Lockout nach 5 Fehlversuchen (15 Min Sperre)
|
- Account-Lockout nach 5 Fehlversuchen (15 Min Sperre)
|
||||||
- Refresh-Token als HttpOnly/Secure/SameSite=Strict Cookie
|
- Refresh-Token als HttpOnly Cookie (secure/sameSite umgebungsabhaengig)
|
||||||
- Token-Rotation mit Redis-basierter Familien-Erkennung
|
- Token-Rotation mit Redis-basierter Familien-Erkennung
|
||||||
|
|
||||||
2. **Users-Modul** (`src/core/users/`)
|
2. **Users-Modul** (`src/core/users/`)
|
||||||
|
|
@ -145,7 +145,7 @@
|
||||||
6. **Config:**
|
6. **Config:**
|
||||||
- `validateConfig()` mit class-validator fuer Umgebungsvariablen
|
- `validateConfig()` mit class-validator fuer Umgebungsvariablen
|
||||||
|
|
||||||
#### 6. Prisma-Schemas
|
#### 6. Prisma-Schemas & Migration
|
||||||
|
|
||||||
1. **`core.schema.prisma`** (platform_core Datenbank):
|
1. **`core.schema.prisma`** (platform_core Datenbank):
|
||||||
- `User` - Plattform-Benutzer (mit Login-Tracking, 2FA)
|
- `User` - Plattform-Benutzer (mit Login-Tracking, 2FA)
|
||||||
|
|
@ -161,6 +161,14 @@
|
||||||
- `Activity` - CRM-Aktivitaeten (Notiz, Anruf, E-Mail, Meeting, Task)
|
- `Activity` - CRM-Aktivitaeten (Notiz, Anruf, E-Mail, Meeting, Task)
|
||||||
- Referenz-Schema fuer Sprint 2 (CRM-Modul)
|
- Referenz-Schema fuer Sprint 2 (CRM-Modul)
|
||||||
|
|
||||||
|
3. **Erste Migration erstellt** (`prisma/migrations/20260308000000_init/`)
|
||||||
|
- 7 Tabellen, alle Indizes und Foreign Keys
|
||||||
|
- `migration_lock.toml` fuer Prisma
|
||||||
|
|
||||||
|
4. **Seed-Script erstellt** (`prisma/seed.ts`)
|
||||||
|
- Erstellt Platform-Admin: `admin@xinion.de` / `ChangeMe123!`
|
||||||
|
- Bcrypt Cost 12, Rolle: PLATFORM_ADMIN
|
||||||
|
|
||||||
#### 7. React Frontend-Shell
|
#### 7. React Frontend-Shell
|
||||||
|
|
||||||
**Projekt-Setup:**
|
**Projekt-Setup:**
|
||||||
|
|
@ -211,6 +219,46 @@
|
||||||
- SSH-Deploy auf insight-dev-01
|
- SSH-Deploy auf insight-dev-01
|
||||||
- Health-Check Verifizierung
|
- Health-Check Verifizierung
|
||||||
|
|
||||||
|
#### 9. IP-basierte Deployment-Anpassung (HTTP statt HTTPS)
|
||||||
|
|
||||||
|
**Grund:** Kein DNS-Eintrag vorhanden, Zugriff nur ueber IP 172.20.10.59.
|
||||||
|
|
||||||
|
**Geaenderte Dateien:**
|
||||||
|
|
||||||
|
1. **`auth.controller.ts`** - Cookie secure/sameSite umgebungsabhaengig
|
||||||
|
- `secure: true` -> `secure: process.env.NODE_ENV === 'production'`
|
||||||
|
- `sameSite: 'strict'` -> `isProduction ? 'strict' : 'lax'`
|
||||||
|
- Betrifft `setRefreshTokenCookie()` und `logout()`
|
||||||
|
|
||||||
|
2. **`docker-compose.yml`** - HTTP + IP Umstellung
|
||||||
|
- HTTPS-Redirect entfernt
|
||||||
|
- TLS-Entrypoint deaktiviert, Port 443 entfernt
|
||||||
|
- Alle Host-Rules: `insight-dev.xinion.lan` -> `172.20.10.59`
|
||||||
|
- Alle Entrypoints: `websecure` -> `web`
|
||||||
|
- URL-Defaults auf `http://172.20.10.59`
|
||||||
|
- PostgreSQL Memory reduziert (1GB/4GB/256MB fuer 8GB RAM VM)
|
||||||
|
- JWT-Keys Volume-Mount hinzugefuegt: `./keys:/app/keys:ro`
|
||||||
|
|
||||||
|
3. **`config/traefik/dynamic/tls.yml`** - TLS-Konfiguration deaktiviert
|
||||||
|
|
||||||
|
4. **`config/traefik/dynamic/middlewares.yml`**
|
||||||
|
- HSTS-Headers entfernt
|
||||||
|
- CSP: `wss://insight-dev.xinion.lan` -> `ws://172.20.10.59`
|
||||||
|
- CORS: `https://insight-dev.xinion.lan` -> `http://172.20.10.59`
|
||||||
|
|
||||||
|
5. **`main.ts`** - CORS-Fallback auf `http://172.20.10.59`
|
||||||
|
|
||||||
|
6. **`env.validation.ts`** - APP_URL Default auf `http://172.20.10.59`
|
||||||
|
|
||||||
|
7. **`.env.example`** - Alle URLs auf `http://172.20.10.59`
|
||||||
|
|
||||||
|
8. **`package-lock.json`** - Generiert fuer core-service und frontend (npm ci braucht diese)
|
||||||
|
|
||||||
|
9. **Dokumentation aktualisiert:**
|
||||||
|
- `docs/INFRASTRUCTURE.md` - HTTP statt HTTPS, IP statt DNS
|
||||||
|
- `docs/ACCESS.md` - Ports, URLs, Default-Zugangsdaten
|
||||||
|
- `README.md` - Setup-Anleitung, URLs, Seed-Befehle
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Naechste Schritte
|
### Naechste Schritte
|
||||||
|
|
@ -222,15 +270,21 @@
|
||||||
- [x] Prisma-Schemas erstellen (core + tenant)
|
- [x] Prisma-Schemas erstellen (core + tenant)
|
||||||
- [x] React Frontend-Shell implementieren
|
- [x] React Frontend-Shell implementieren
|
||||||
- [x] CI/CD Pipelines (.forgejo/workflows/) definieren
|
- [x] CI/CD Pipelines (.forgejo/workflows/) definieren
|
||||||
|
- [x] Codebase auf HTTP + IP (172.20.10.59) umstellen
|
||||||
|
- [x] Seed-Script erstellen (admin@xinion.de)
|
||||||
|
- [x] Prisma-Migration erstellen (init)
|
||||||
|
- [x] package-lock.json generieren
|
||||||
|
- [x] Dokumentation aktualisieren
|
||||||
|
- [ ] Commit & Push auf develop
|
||||||
|
- [ ] LVM-Festplatte auf Server erweitern (60GB -> voll nutzbar)
|
||||||
- [ ] Docker + Docker Compose auf insight-dev-01 installieren
|
- [ ] Docker + Docker Compose auf insight-dev-01 installieren
|
||||||
- [ ] .env-Datei auf Server anlegen (echte Passwoerter)
|
- [ ] Repo klonen, .env + JWT-Keys auf Server erstellen
|
||||||
- [ ] JWT RS256 Schluessel generieren (fuer Token-Signierung)
|
- [ ] Services starten, Migration + Seed ausfuehren
|
||||||
- [ ] Erste Prisma-Migration ausfuehren
|
|
||||||
- [ ] Platform-Admin User anlegen (Seed)
|
|
||||||
- [ ] Erster End-to-End Test (Login -> Dashboard)
|
- [ ] Erster End-to-End Test (Login -> Dashboard)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Offene Fragen / Abhaengigkeiten
|
### Offene Fragen / Abhaengigkeiten
|
||||||
|
|
||||||
- DNS-Eintrag `insight-dev.xinion.lan` muss auf 172.20.10.59 zeigen
|
- DNS-Eintrag `insight-dev.xinion.lan` wird spaeter eingerichtet (dann HTTPS aktivieren)
|
||||||
|
- LVM auf Server muss erweitert werden (60GB Disk, nur ~56GB sichtbar)
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,6 @@ http:
|
||||||
browserXssFilter: true
|
browserXssFilter: true
|
||||||
contentTypeNosniff: true
|
contentTypeNosniff: true
|
||||||
frameDeny: true
|
frameDeny: true
|
||||||
stsIncludeSubdomains: true
|
|
||||||
stsPreload: true
|
|
||||||
stsSeconds: 31536000
|
|
||||||
customFrameOptionsValue: "SAMEORIGIN"
|
customFrameOptionsValue: "SAMEORIGIN"
|
||||||
referrerPolicy: "strict-origin-when-cross-origin"
|
referrerPolicy: "strict-origin-when-cross-origin"
|
||||||
contentSecurityPolicy: >-
|
contentSecurityPolicy: >-
|
||||||
|
|
@ -21,7 +18,7 @@ http:
|
||||||
style-src 'self' 'unsafe-inline';
|
style-src 'self' 'unsafe-inline';
|
||||||
img-src 'self' data: blob:;
|
img-src 'self' data: blob:;
|
||||||
font-src 'self';
|
font-src 'self';
|
||||||
connect-src 'self' wss://insight-dev.xinion.lan;
|
connect-src 'self' ws://172.20.10.59;
|
||||||
frame-ancestors 'self';
|
frame-ancestors 'self';
|
||||||
|
|
||||||
# CORS fuer API
|
# CORS fuer API
|
||||||
|
|
@ -40,7 +37,7 @@ http:
|
||||||
- X-Tenant-ID
|
- X-Tenant-ID
|
||||||
- X-Request-ID
|
- X-Request-ID
|
||||||
accessControlAllowOriginList:
|
accessControlAllowOriginList:
|
||||||
- "https://insight-dev.xinion.lan"
|
- "http://172.20.10.59"
|
||||||
accessControlMaxAge: 86400
|
accessControlMaxAge: 86400
|
||||||
accessControlAllowCredentials: true
|
accessControlAllowCredentials: true
|
||||||
addVaryHeader: true
|
addVaryHeader: true
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,2 @@
|
||||||
# ============================================================
|
# TLS-Konfiguration deaktiviert fuer Alpha/Dev (IP-basierter HTTP-Zugang).
|
||||||
# Traefik - Dynamische TLS-Konfiguration
|
# Wird reaktiviert wenn DNS + HTTPS eingerichtet wird.
|
||||||
# ============================================================
|
|
||||||
# Fuer die Alpha-Phase verwenden wir ein selbst-signiertes
|
|
||||||
# Zertifikat. Spaeter wird step-ca als ACME-Provider genutzt.
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
tls:
|
|
||||||
options:
|
|
||||||
default:
|
|
||||||
minVersion: VersionTLS12
|
|
||||||
cipherSuites:
|
|
||||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
|
||||||
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
|
||||||
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
|
||||||
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|
|
||||||
sniStrict: false
|
|
||||||
|
|
||||||
stores:
|
|
||||||
default:
|
|
||||||
defaultGeneratedCert:
|
|
||||||
resolver: default
|
|
||||||
domain:
|
|
||||||
main: "insight-dev.xinion.lan"
|
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,8 @@ services:
|
||||||
# API & Dashboard
|
# API & Dashboard
|
||||||
- "--api.dashboard=true"
|
- "--api.dashboard=true"
|
||||||
- "--api.insecure=true"
|
- "--api.insecure=true"
|
||||||
# Entrypoints
|
# Entrypoints (nur HTTP fuer Alpha/Dev mit IP-Zugang)
|
||||||
- "--entrypoints.web.address=:80"
|
- "--entrypoints.web.address=:80"
|
||||||
- "--entrypoints.websecure.address=:443"
|
|
||||||
# HTTP -> HTTPS Redirect
|
|
||||||
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
|
|
||||||
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
|
|
||||||
# Docker Provider
|
# Docker Provider
|
||||||
- "--providers.docker=true"
|
- "--providers.docker=true"
|
||||||
- "--providers.docker.exposedbydefault=false"
|
- "--providers.docker.exposedbydefault=false"
|
||||||
|
|
@ -58,8 +54,6 @@ services:
|
||||||
# File Provider (fuer dynamische Konfiguration)
|
# File Provider (fuer dynamische Konfiguration)
|
||||||
- "--providers.file.directory=/etc/traefik/dynamic"
|
- "--providers.file.directory=/etc/traefik/dynamic"
|
||||||
- "--providers.file.watch=true"
|
- "--providers.file.watch=true"
|
||||||
# TLS (self-signed fuer Dev)
|
|
||||||
- "--entrypoints.websecure.http.tls=true"
|
|
||||||
# Logging
|
# Logging
|
||||||
- "--log.level=INFO"
|
- "--log.level=INFO"
|
||||||
- "--accesslog=true"
|
- "--accesslog=true"
|
||||||
|
|
@ -70,7 +64,6 @@ services:
|
||||||
- "--entrypoints.metrics.address=:8082"
|
- "--entrypoints.metrics.address=:8082"
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "443:443"
|
|
||||||
- "8080:8080" # Dashboard (nur intern)
|
- "8080:8080" # Dashboard (nur intern)
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
|
@ -80,10 +73,10 @@ services:
|
||||||
- insight-web
|
- insight-web
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
# Dashboard Route (nur intern)
|
# Dashboard Route (nur intern, via Port 8080)
|
||||||
- "traefik.http.routers.dashboard.rule=Host(`traefik.insight-dev.xinion.lan`)"
|
- "traefik.http.routers.dashboard.rule=Host(`172.20.10.59`) && PathPrefix(`/dashboard`)"
|
||||||
- "traefik.http.routers.dashboard.service=api@internal"
|
- "traefik.http.routers.dashboard.service=api@internal"
|
||||||
- "traefik.http.routers.dashboard.entrypoints=websecure"
|
- "traefik.http.routers.dashboard.entrypoints=web"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "traefik", "healthcheck"]
|
test: ["CMD", "traefik", "healthcheck"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
|
|
@ -118,13 +111,13 @@ services:
|
||||||
command:
|
command:
|
||||||
- "postgres"
|
- "postgres"
|
||||||
- "-c"
|
- "-c"
|
||||||
- "shared_buffers=2GB"
|
- "shared_buffers=1GB"
|
||||||
- "-c"
|
- "-c"
|
||||||
- "effective_cache_size=6GB"
|
- "effective_cache_size=4GB"
|
||||||
- "-c"
|
- "-c"
|
||||||
- "work_mem=16MB"
|
- "work_mem=16MB"
|
||||||
- "-c"
|
- "-c"
|
||||||
- "maintenance_work_mem=512MB"
|
- "maintenance_work_mem=256MB"
|
||||||
- "-c"
|
- "-c"
|
||||||
- "max_connections=200"
|
- "max_connections=200"
|
||||||
- "-c"
|
- "-c"
|
||||||
|
|
@ -225,8 +218,8 @@ services:
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: ${NODE_ENV:-development}
|
NODE_ENV: ${NODE_ENV:-development}
|
||||||
APP_PORT: ${APP_PORT:-3000}
|
APP_PORT: ${APP_PORT:-3000}
|
||||||
APP_URL: ${APP_URL:-https://insight-dev.xinion.lan}
|
APP_URL: ${APP_URL:-http://172.20.10.59}
|
||||||
FRONTEND_URL: ${FRONTEND_URL:-https://insight-dev.xinion.lan}
|
FRONTEND_URL: ${FRONTEND_URL:-http://172.20.10.59}
|
||||||
LOG_LEVEL: ${LOG_LEVEL:-info}
|
LOG_LEVEL: ${LOG_LEVEL:-info}
|
||||||
# Database (via PgBouncer)
|
# Database (via PgBouncer)
|
||||||
DATABASE_URL: "postgresql://${DB_USER:-insight}:${DB_PASSWORD}@pgbouncer:6432/${DB_NAME:-platform_core}"
|
DATABASE_URL: "postgresql://${DB_USER:-insight}:${DB_PASSWORD}@pgbouncer:6432/${DB_NAME:-platform_core}"
|
||||||
|
|
@ -245,10 +238,12 @@ services:
|
||||||
# Bcrypt
|
# Bcrypt
|
||||||
BCRYPT_COST: ${BCRYPT_COST:-12}
|
BCRYPT_COST: ${BCRYPT_COST:-12}
|
||||||
# CORS
|
# CORS
|
||||||
CORS_ORIGINS: ${CORS_ORIGINS:-https://insight-dev.xinion.lan}
|
CORS_ORIGINS: ${CORS_ORIGINS:-http://172.20.10.59}
|
||||||
# Rate Limiting
|
# Rate Limiting
|
||||||
THROTTLE_TTL: ${THROTTLE_TTL:-60000}
|
THROTTLE_TTL: ${THROTTLE_TTL:-60000}
|
||||||
THROTTLE_LIMIT: ${THROTTLE_LIMIT:-200}
|
THROTTLE_LIMIT: ${THROTTLE_LIMIT:-200}
|
||||||
|
volumes:
|
||||||
|
- ./keys:/app/keys:ro
|
||||||
networks:
|
networks:
|
||||||
- insight-web
|
- insight-web
|
||||||
- insight-db
|
- insight-db
|
||||||
|
|
@ -261,13 +256,13 @@ services:
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
# API Routing
|
# API Routing
|
||||||
- "traefik.http.routers.core-api.rule=Host(`insight-dev.xinion.lan`) && PathPrefix(`/api`)"
|
- "traefik.http.routers.core-api.rule=Host(`172.20.10.59`) && PathPrefix(`/api`)"
|
||||||
- "traefik.http.routers.core-api.entrypoints=websecure"
|
- "traefik.http.routers.core-api.entrypoints=web"
|
||||||
- "traefik.http.routers.core-api.service=core-api"
|
- "traefik.http.routers.core-api.service=core-api"
|
||||||
- "traefik.http.services.core-api.loadbalancer.server.port=3000"
|
- "traefik.http.services.core-api.loadbalancer.server.port=3000"
|
||||||
# Health-Endpunkt (ohne Auth)
|
# Health-Endpunkt (ohne Auth)
|
||||||
- "traefik.http.routers.core-health.rule=Host(`insight-dev.xinion.lan`) && Path(`/health`)"
|
- "traefik.http.routers.core-health.rule=Host(`172.20.10.59`) && Path(`/health`)"
|
||||||
- "traefik.http.routers.core-health.entrypoints=websecure"
|
- "traefik.http.routers.core-health.entrypoints=web"
|
||||||
- "traefik.http.routers.core-health.service=core-api"
|
- "traefik.http.routers.core-health.service=core-api"
|
||||||
# Rate Limiting Middleware
|
# Rate Limiting Middleware
|
||||||
- "traefik.http.middlewares.api-ratelimit.ratelimit.average=100"
|
- "traefik.http.middlewares.api-ratelimit.ratelimit.average=100"
|
||||||
|
|
@ -295,8 +290,8 @@ services:
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
# Frontend Routing (Catch-All nach API)
|
# Frontend Routing (Catch-All nach API)
|
||||||
- "traefik.http.routers.frontend.rule=Host(`insight-dev.xinion.lan`)"
|
- "traefik.http.routers.frontend.rule=Host(`172.20.10.59`)"
|
||||||
- "traefik.http.routers.frontend.entrypoints=websecure"
|
- "traefik.http.routers.frontend.entrypoints=web"
|
||||||
- "traefik.http.routers.frontend.service=frontend"
|
- "traefik.http.routers.frontend.service=frontend"
|
||||||
- "traefik.http.routers.frontend.priority=1"
|
- "traefik.http.routers.frontend.priority=1"
|
||||||
- "traefik.http.services.frontend.loadbalancer.server.port=8080"
|
- "traefik.http.services.frontend.loadbalancer.server.port=8080"
|
||||||
|
|
|
||||||
|
|
@ -118,10 +118,11 @@ docker compose restart core
|
||||||
|
|
||||||
## 4. Service-Ports (auf der VM)
|
## 4. Service-Ports (auf der VM)
|
||||||
|
|
||||||
|
> **Alpha/Dev:** Kein HTTPS, kein DNS. Zugriff via `http://172.20.10.59`
|
||||||
|
|
||||||
| Service | Interner Port | Externer Port | URL |
|
| Service | Interner Port | Externer Port | URL |
|
||||||
|-----------------|---------------|---------------|----------------------------------|
|
|-----------------|---------------|---------------|----------------------------------|
|
||||||
| Traefik (HTTP) | 80 | 80 | http://insight-dev.xinion.lan |
|
| Traefik (HTTP) | 80 | 80 | http://172.20.10.59 |
|
||||||
| Traefik (HTTPS) | 443 | 443 | https://insight-dev.xinion.lan |
|
|
||||||
| Traefik Dashboard | 8080 | - | Nur intern |
|
| Traefik Dashboard | 8080 | - | Nur intern |
|
||||||
| Core-Service | 3000 | - | Via Traefik: /api/v1/* |
|
| Core-Service | 3000 | - | Via Traefik: /api/v1/* |
|
||||||
| Frontend | 8080 | - | Via Traefik: /* |
|
| Frontend | 8080 | - | Via Traefik: /* |
|
||||||
|
|
@ -210,7 +211,20 @@ Laufende Anwendung
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. Wichtige Befehle
|
## 9. Default-Zugangsdaten (Alpha/Dev)
|
||||||
|
|
||||||
|
> **WICHTIG:** Diese Zugangsdaten gelten nur fuer die Ersteinrichtung!
|
||||||
|
> Passwoerter muessen nach dem ersten Login geaendert werden.
|
||||||
|
|
||||||
|
| Service | User / E-Mail | Passwort |
|
||||||
|
|-------------------|------------------------|--------------------|
|
||||||
|
| Plattform-Admin | `admin@xinion.de` | `ChangeMe123!` |
|
||||||
|
| Grafana | `admin` | Siehe `.env` |
|
||||||
|
| Traefik Dashboard | `admin` | Siehe `.env` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Wichtige Befehle
|
||||||
|
|
||||||
### Vom MacBook aus
|
### Vom MacBook aus
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -220,6 +234,9 @@ git push origin develop
|
||||||
# SSH auf Server
|
# SSH auf Server
|
||||||
ssh -i .keys/deploy_ed25519 deploy@172.20.10.59
|
ssh -i .keys/deploy_ed25519 deploy@172.20.10.59
|
||||||
|
|
||||||
|
# Plattform oeffnen
|
||||||
|
open http://172.20.10.59
|
||||||
|
|
||||||
# Grafana oeffnen (SSH-Tunnel)
|
# Grafana oeffnen (SSH-Tunnel)
|
||||||
ssh -L 3001:localhost:3001 -i .keys/deploy_ed25519 deploy@172.20.10.59
|
ssh -L 3001:localhost:3001 -i .keys/deploy_ed25519 deploy@172.20.10.59
|
||||||
# Dann im Browser: http://localhost:3001
|
# Dann im Browser: http://localhost:3001
|
||||||
|
|
@ -234,10 +251,13 @@ docker compose up -d
|
||||||
docker compose -f docker-compose.yml -f docker-compose.observability.yml up -d
|
docker compose -f docker-compose.yml -f docker-compose.observability.yml up -d
|
||||||
|
|
||||||
# Health-Check
|
# Health-Check
|
||||||
curl http://localhost:3000/health
|
curl http://172.20.10.59/health
|
||||||
|
|
||||||
# Datenbank-Migration
|
# Datenbank-Migration
|
||||||
docker compose exec core npx prisma migrate deploy
|
docker compose run --rm core npx prisma migrate deploy --schema=./prisma/core.schema.prisma
|
||||||
|
|
||||||
|
# Admin-User seeden
|
||||||
|
docker compose run --rm core npx ts-node prisma/seed.ts
|
||||||
|
|
||||||
# Logs folgen
|
# Logs folgen
|
||||||
docker compose logs -f --tail=100
|
docker compose logs -f --tail=100
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,14 @@ Alle Services werden als Docker-Container betrieben.
|
||||||
- SSH: nur Key-basiert (`PasswordAuthentication no`)
|
- SSH: nur Key-basiert (`PasswordAuthentication no`)
|
||||||
- Firewall (ufw):
|
- Firewall (ufw):
|
||||||
- Port 22 (SSH) - nur internes Netzwerk
|
- Port 22 (SSH) - nur internes Netzwerk
|
||||||
- Port 80 (HTTP -> Redirect auf HTTPS)
|
- Port 80 (HTTP) - Webzugang (kein HTTPS in Alpha/Dev)
|
||||||
- Port 443 (HTTPS)
|
|
||||||
- Alle anderen Ports: DENY
|
- Alle anderen Ports: DENY
|
||||||
- Automatische Sicherheitsupdates: `unattended-upgrades` aktiviert
|
- Automatische Sicherheitsupdates: `unattended-upgrades` aktiviert
|
||||||
- Fail2ban fuer SSH-Brute-Force-Schutz
|
- Fail2ban fuer SSH-Brute-Force-Schutz
|
||||||
|
|
||||||
|
> **Hinweis:** In der Alpha/Dev-Phase wird kein HTTPS verwendet.
|
||||||
|
> Zugriff erfolgt ueber `http://172.20.10.59` (IP-basiert, kein DNS).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Software auf der VM
|
## 3. Software auf der VM
|
||||||
|
|
@ -54,11 +56,11 @@ Alles laeuft in Containern.
|
||||||
```
|
```
|
||||||
Internet / Internes Netz
|
Internet / Internes Netz
|
||||||
|
|
|
|
||||||
[ Port 80/443 ]
|
[ Port 80 ]
|
||||||
|
|
|
|
||||||
+-------v--------+
|
+-------v--------+
|
||||||
| Traefik | API Gateway, SSL-Terminierung,
|
| Traefik | API Gateway, Reverse Proxy,
|
||||||
| (Gateway) | Rate Limiting, mTLS-Terminierung
|
| (Gateway) | Rate Limiting
|
||||||
+---+-------+----+
|
+---+-------+----+
|
||||||
| |
|
| |
|
||||||
+---------+ +---------+
|
+---------+ +---------+
|
||||||
|
|
@ -88,12 +90,12 @@ Alles laeuft in Containern.
|
||||||
| `insight-db` | Core-Service <-> PgBouncer <-> PostgreSQL (intern) |
|
| `insight-db` | Core-Service <-> PgBouncer <-> PostgreSQL (intern) |
|
||||||
| `insight-cache`| Core-Service <-> Redis (intern) |
|
| `insight-cache`| Core-Service <-> Redis (intern) |
|
||||||
|
|
||||||
### mTLS (step-ca)
|
### mTLS (step-ca) - geplant fuer Produktion
|
||||||
|
|
||||||
Alle interne Kommunikation zwischen Containern wird ueber mTLS abgesichert.
|
> **Status:** mTLS ist in der Alpha/Dev-Phase deaktiviert.
|
||||||
step-ca (Smallstep) fungiert als interne Certificate Authority.
|
> step-ca wird spaeter fuer interne Container-Kommunikation eingesetzt.
|
||||||
|
|
||||||
| Komponente | Zertifikat |
|
| Komponente | Zertifikat (geplant) |
|
||||||
|---------------|-------------------------------|
|
|---------------|-------------------------------|
|
||||||
| Traefik | Wildcard fuer externe Domain |
|
| Traefik | Wildcard fuer externe Domain |
|
||||||
| Core-Service | `core-service.insight.local` |
|
| Core-Service | `core-service.insight.local` |
|
||||||
|
|
@ -108,7 +110,7 @@ step-ca (Smallstep) fungiert als interne Certificate Authority.
|
||||||
|
|
||||||
| Service | Image | Port (intern) | Port (extern) | Beschreibung |
|
| Service | Image | Port (intern) | Port (extern) | Beschreibung |
|
||||||
|---------------|--------------------------------|---------------|---------------|-------------------------------|
|
|---------------|--------------------------------|---------------|---------------|-------------------------------|
|
||||||
| `traefik` | traefik:3 | 80, 443, 8080 | 80, 443 | API Gateway, Reverse Proxy |
|
| `traefik` | traefik:3 | 80, 8080 | 80 | API Gateway, Reverse Proxy |
|
||||||
| `core` | insight-core:latest | 3000 | - | NestJS Backend |
|
| `core` | insight-core:latest | 3000 | - | NestJS Backend |
|
||||||
| `frontend` | insight-frontend:latest | 8080 | - | React App (Nginx served) |
|
| `frontend` | insight-frontend:latest | 8080 | - | React App (Nginx served) |
|
||||||
| `postgres` | postgres:16-alpine | 5432 | - | Datenbank |
|
| `postgres` | postgres:16-alpine | 5432 | - | Datenbank |
|
||||||
|
|
@ -149,12 +151,22 @@ PostgreSQL-Server
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8. DNS / Domains
|
## 8. Netzwerk / Zugriff
|
||||||
|
|
||||||
|
> **Alpha/Dev-Phase:** Kein DNS, Zugriff ueber IP-Adresse.
|
||||||
|
> HTTPS wird spaeter mit DNS-Eintrag aktiviert.
|
||||||
|
|
||||||
|
| Zugriff | URL | Zweck |
|
||||||
|
|----------------------------|--------------------------------|-------------------------------|
|
||||||
|
| Frontend + API | `http://172.20.10.59` | Entwicklungs-Plattform |
|
||||||
|
| API-Endpunkte | `http://172.20.10.59/api/v1/*` | REST API |
|
||||||
|
| Git-Server | `git.xinion.lan` | Git Repository & CI/CD |
|
||||||
|
|
||||||
|
### Spaeter (mit DNS):
|
||||||
|
|
||||||
| Eintrag | Ziel | Zweck |
|
| Eintrag | Ziel | Zweck |
|
||||||
|----------------------------|--------------------|-------------------------------|
|
|----------------------------|--------------------|-------------------------------|
|
||||||
| `insight-dev.xinion.lan` | VM-IP | Entwicklungs-Frontend |
|
| `insight-dev.xinion.lan` | VM-IP | Entwicklungs-Frontend (HTTPS) |
|
||||||
| `api.insight-dev.xinion.lan` | VM-IP | API-Endpunkt |
|
|
||||||
| `git.xinion.lan` | Forgejo-Server | Git Repository & CI/CD |
|
| `git.xinion.lan` | Forgejo-Server | Git Repository & CI/CD |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
10925
packages/core-service/package-lock.json
generated
Normal file
10925
packages/core-service/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -22,7 +22,8 @@
|
||||||
"prisma:generate": "prisma generate --schema=prisma/core.schema.prisma",
|
"prisma:generate": "prisma generate --schema=prisma/core.schema.prisma",
|
||||||
"prisma:migrate": "prisma migrate dev --schema=prisma/core.schema.prisma",
|
"prisma:migrate": "prisma migrate dev --schema=prisma/core.schema.prisma",
|
||||||
"prisma:migrate:deploy": "prisma migrate deploy --schema=prisma/core.schema.prisma",
|
"prisma:migrate:deploy": "prisma migrate deploy --schema=prisma/core.schema.prisma",
|
||||||
"prisma:studio": "prisma studio --schema=prisma/core.schema.prisma"
|
"prisma:studio": "prisma studio --schema=prisma/core.schema.prisma",
|
||||||
|
"prisma:seed": "ts-node prisma/seed.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^10.4.0",
|
"@nestjs/common": "^10.4.0",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "users" (
|
||||||
|
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||||
|
"email" VARCHAR(255) NOT NULL,
|
||||||
|
"first_name" VARCHAR(100) NOT NULL,
|
||||||
|
"last_name" VARCHAR(100) NOT NULL,
|
||||||
|
"role" VARCHAR(50) NOT NULL DEFAULT 'USER',
|
||||||
|
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"two_factor_enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"last_login" TIMESTAMP(3),
|
||||||
|
"failed_login_attempts" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"last_failed_login" TIMESTAMP(3),
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "auth_providers" (
|
||||||
|
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||||
|
"user_id" UUID NOT NULL,
|
||||||
|
"provider" VARCHAR(50) NOT NULL,
|
||||||
|
"provider_id" VARCHAR(255),
|
||||||
|
"password_hash" VARCHAR(255),
|
||||||
|
"totp_secret" VARCHAR(255),
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "auth_providers_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "tenants" (
|
||||||
|
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||||
|
"name" VARCHAR(200) NOT NULL,
|
||||||
|
"slug" VARCHAR(50) NOT NULL,
|
||||||
|
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"settings" JSONB NOT NULL DEFAULT '{}',
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "tenants_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "tenant_memberships" (
|
||||||
|
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||||
|
"user_id" UUID NOT NULL,
|
||||||
|
"tenant_id" UUID NOT NULL,
|
||||||
|
"tenant_role" VARCHAR(50) NOT NULL DEFAULT 'MEMBER',
|
||||||
|
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "tenant_memberships_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "modules" (
|
||||||
|
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||||
|
"key" VARCHAR(50) NOT NULL,
|
||||||
|
"name" VARCHAR(100) NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"version" VARCHAR(20) NOT NULL DEFAULT '1.0.0',
|
||||||
|
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "modules_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "tenant_modules" (
|
||||||
|
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||||
|
"tenant_id" UUID NOT NULL,
|
||||||
|
"module_id" UUID NOT NULL,
|
||||||
|
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"config" JSONB NOT NULL DEFAULT '{}',
|
||||||
|
"activated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "tenant_modules_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "audit_logs" (
|
||||||
|
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||||
|
"user_id" UUID,
|
||||||
|
"action" VARCHAR(100) NOT NULL,
|
||||||
|
"entity" VARCHAR(100) NOT NULL,
|
||||||
|
"entity_id" VARCHAR(255),
|
||||||
|
"details" JSONB,
|
||||||
|
"ip_address" VARCHAR(45),
|
||||||
|
"user_agent" TEXT,
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "audit_logs_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "auth_providers_user_id_provider_key" ON "auth_providers"("user_id", "provider");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "tenants_slug_key" ON "tenants"("slug");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "tenant_memberships_user_id_tenant_id_key" ON "tenant_memberships"("user_id", "tenant_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "modules_key_key" ON "modules"("key");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "tenant_modules_tenant_id_module_id_key" ON "tenant_modules"("tenant_id", "module_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "audit_logs_user_id_idx" ON "audit_logs"("user_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "audit_logs_action_idx" ON "audit_logs"("action");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "audit_logs_entity_entity_id_idx" ON "audit_logs"("entity", "entity_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "audit_logs_created_at_idx" ON "audit_logs"("created_at");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "auth_providers" ADD CONSTRAINT "auth_providers_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "tenant_memberships" ADD CONSTRAINT "tenant_memberships_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "tenant_memberships" ADD CONSTRAINT "tenant_memberships_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "tenant_modules" ADD CONSTRAINT "tenant_modules_tenant_id_fkey" FOREIGN KEY ("tenant_id") REFERENCES "tenants"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "tenant_modules" ADD CONSTRAINT "tenant_modules_module_id_fkey" FOREIGN KEY ("module_id") REFERENCES "modules"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "audit_logs" ADD CONSTRAINT "audit_logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (i.e. Git)
|
||||||
|
provider = "postgresql"
|
||||||
65
packages/core-service/prisma/seed.ts
Normal file
65
packages/core-service/prisma/seed.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import * as bcrypt from 'bcrypt';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seed-Script: Erstellt den initialen Platform-Admin User.
|
||||||
|
*
|
||||||
|
* Ausfuehrung:
|
||||||
|
* npx ts-node prisma/seed.ts
|
||||||
|
*
|
||||||
|
* WICHTIG: Passwort nach erstem Login aendern!
|
||||||
|
*/
|
||||||
|
|
||||||
|
const prisma = new PrismaClient({
|
||||||
|
datasources: {
|
||||||
|
db: {
|
||||||
|
url: process.env.DATABASE_URL_DIRECT || process.env.DATABASE_URL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
const email = 'admin@xinion.de';
|
||||||
|
|
||||||
|
// Pruefen ob Admin bereits existiert
|
||||||
|
const existing = await prisma.user.findUnique({ where: { email } });
|
||||||
|
if (existing) {
|
||||||
|
console.log(`Admin-User ${email} existiert bereits. Seed uebersprungen.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Passwort hashen (Bcrypt Cost 12 gemaess Sicherheitsregel)
|
||||||
|
const passwordHash = await bcrypt.hash('ChangeMe123!', 12);
|
||||||
|
|
||||||
|
// Admin-User anlegen
|
||||||
|
const user = await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
email,
|
||||||
|
firstName: 'Platform',
|
||||||
|
lastName: 'Admin',
|
||||||
|
role: 'PLATFORM_ADMIN',
|
||||||
|
isActive: true,
|
||||||
|
authProvider: {
|
||||||
|
create: {
|
||||||
|
provider: 'LOCAL',
|
||||||
|
passwordHash,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Platform-Admin erstellt: ${user.email} (ID: ${user.id})`);
|
||||||
|
console.log('');
|
||||||
|
console.log('Zugangsdaten:');
|
||||||
|
console.log(` E-Mail: ${email}`);
|
||||||
|
console.log(' Passwort: ChangeMe123!');
|
||||||
|
console.log('');
|
||||||
|
console.log('WICHTIG: Passwort nach erstem Login aendern!');
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((e: Error) => {
|
||||||
|
console.error('Seed fehlgeschlagen:', e.message);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.finally(() => prisma.$disconnect());
|
||||||
|
|
@ -27,7 +27,7 @@ class EnvironmentVariables {
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
APP_URL = 'https://insight-dev.xinion.lan';
|
APP_URL = 'http://172.20.10.59';
|
||||||
|
|
||||||
// Datenbank
|
// Datenbank
|
||||||
@IsString()
|
@IsString()
|
||||||
|
|
|
||||||
|
|
@ -96,10 +96,11 @@ export class AuthController {
|
||||||
await this.authService.logout(user, refreshToken);
|
await this.authService.logout(user, refreshToken);
|
||||||
|
|
||||||
// Refresh-Token Cookie loeschen
|
// Refresh-Token Cookie loeschen
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
res.clearCookie('refresh_token', {
|
res.clearCookie('refresh_token', {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: true,
|
secure: isProduction,
|
||||||
sameSite: 'strict',
|
sameSite: isProduction ? 'strict' : 'lax',
|
||||||
path: '/api/v1/auth',
|
path: '/api/v1/auth',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -107,14 +108,17 @@ export class AuthController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setzt das Refresh-Token als HttpOnly, Secure, SameSite=Strict Cookie.
|
* Setzt das Refresh-Token als HttpOnly Cookie.
|
||||||
|
* Secure + SameSite=Strict nur in Produktion (HTTPS).
|
||||||
|
* In Development (HTTP) wird Secure deaktiviert und SameSite=Lax gesetzt.
|
||||||
*/
|
*/
|
||||||
private setRefreshTokenCookie(res: Response, refreshToken: string): void {
|
private setRefreshTokenCookie(res: Response, refreshToken: string): void {
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
res.cookie('refresh_token', refreshToken, {
|
res.cookie('refresh_token', refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: true, // Nur HTTPS
|
secure: isProduction,
|
||||||
sameSite: 'strict',
|
sameSite: isProduction ? 'strict' : 'lax',
|
||||||
path: '/api/v1/auth', // Nur fuer Auth-Endpunkte
|
path: '/api/v1/auth',
|
||||||
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 Tage
|
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 Tage
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ async function bootstrap(): Promise<void> {
|
||||||
|
|
||||||
// CORS
|
// CORS
|
||||||
const corsOrigins = process.env.CORS_ORIGINS?.split(',') ?? [
|
const corsOrigins = process.env.CORS_ORIGINS?.split(',') ?? [
|
||||||
'https://insight-dev.xinion.lan',
|
'http://172.20.10.59',
|
||||||
];
|
];
|
||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: corsOrigins,
|
origin: corsOrigins,
|
||||||
|
|
|
||||||
3664
packages/frontend/package-lock.json
generated
Normal file
3664
packages/frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue