Initial: RAPPORT-STACK — All-in-One Compose (Supabase + Website + HOST)
Ein 'docker compose up' bringt die komplette Hosting-Plattform hoch: - include ../SERVER-CONTAINER (db/auth/rest/realtime/storage/kong/app) - host-db: eigene Postgres für RAPPORT-HOST - host: Node-Backend + gebündelte Hugo-Website (Dockerfile.host, multi-stage) provisioniert Kunden-Instanzen über Kong in den Supabase-Stack Eine .env für lokal UND Hetzner (Domains/Keys per Env). host-Image baut + läuft verifiziert: Website (/,/hosting/,/login/,/admin/) + API + E2E-Flow (register→checkout→admin) aus dem Container. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# RAPPORT-STACK — eine Konfiguration für lokal UND Hetzner.
|
||||
# Kopiere nach .env und ersetze die CHANGE-ME-Werte. .env niemals committen.
|
||||
#
|
||||
# Secrets generieren: openssl rand -hex 32
|
||||
# ANON_KEY / SERVICE_ROLE_KEY aus JWT_SECRET ableiten:
|
||||
# cd ../SERVER-CONTAINER && node scripts/generate-keys.mjs $JWT_SECRET
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
# ═══ Rapport-Backend (Supabase-Stack) ═══
|
||||
POSTGRES_PASSWORD=CHANGE-ME-min-32-zeichen
|
||||
JWT_SECRET=CHANGE-ME-min-32-zeichen
|
||||
ANON_KEY=CHANGE-ME-aus-jwt-secret
|
||||
SERVICE_ROLE_KEY=CHANGE-ME-aus-jwt-secret
|
||||
|
||||
# URLs des Rapport-Stacks
|
||||
# Lokal: SITE_URL=http://localhost:8080 · API_EXTERNAL_URL=http://localhost:8000
|
||||
# Prod: SITE_URL=https://app.rapport.studio · API_EXTERNAL_URL=https://api.rapport.studio
|
||||
SITE_URL=http://localhost:8080
|
||||
API_EXTERNAL_URL=http://localhost:8000
|
||||
|
||||
# Ports des Rapport-Stacks
|
||||
APP_PORT=8080
|
||||
KONG_HTTP_PORT=8000
|
||||
KONG_HTTPS_PORT=8443
|
||||
DB_PORT=5432
|
||||
|
||||
# Rapport-Frontend-Image-Tag (aus Gitea-Registry)
|
||||
RAPPORT_APP_TAG=main
|
||||
|
||||
# SMTP (leer = Test-Mailserver / Mails landen lokal)
|
||||
SMTP_HOST=
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=
|
||||
SMTP_PASS=
|
||||
SMTP_SENDER_NAME=Rapport
|
||||
SMTP_ADMIN_EMAIL=admin@rapport.local
|
||||
|
||||
# ═══ RAPPORT-HOST (Hosting-Plattform) ═══
|
||||
HOST_PORT=8787
|
||||
# Lokal: http://localhost:8787 · Prod: https://host.rapport.studio
|
||||
PUBLIC_BASE_URL=http://localhost:8787
|
||||
|
||||
# Eigenes JWT-Secret für HOST-Kundenkonten (NICHT das Stack-JWT_SECRET).
|
||||
HOST_JWT_SECRET=CHANGE-ME-min-32-zeichen
|
||||
# Passwort für den Betreiber-Bereich /admin.
|
||||
ADMIN_PASSWORD=CHANGE-ME-admin-passwort
|
||||
|
||||
# Eigene HOST-Datenbank (Container-intern; Default reicht meist).
|
||||
HOST_DB_USER=rapport_host
|
||||
HOST_DB_PASSWORD=rapport_host
|
||||
HOST_DB_NAME=rapport_host
|
||||
|
||||
# Provisioning in den Rapport-Stack. Default greift containerintern auf Kong zu.
|
||||
RAPPORT_API_URL=http://kong:8000
|
||||
# URL-Vorlage für die fertige Kunden-Instanz ({slug} wird ersetzt).
|
||||
# Lokal: http://localhost:8080/?studio={slug}
|
||||
# Prod: https://app.rapport.studio/?studio={slug}
|
||||
RAPPORT_INSTANCE_URL_TEMPLATE=http://localhost:8080/?studio={slug}
|
||||
|
||||
# ═══ Stripe (optional; solange CHANGE-ME → MOCK-Modus, kein echtes Geld) ═══
|
||||
STRIPE_SECRET_KEY=sk_test_CHANGE-ME
|
||||
STRIPE_WEBHOOK_SECRET=whsec_CHANGE-ME
|
||||
STRIPE_PRICE_SOLO=price_CHANGE-ME
|
||||
STRIPE_PRICE_STUDIO=price_CHANGE-ME
|
||||
STRIPE_PRICE_BUSINESS=price_CHANGE-ME
|
||||
@@ -0,0 +1,3 @@
|
||||
.env
|
||||
*.log
|
||||
.DS_Store
|
||||
@@ -0,0 +1,31 @@
|
||||
# RAPPORT-HOST-Image — bündelt das Node-Backend MIT der gebauten Hugo-Website.
|
||||
# Haupt-Build-Kontext: RAPPORT-HOST. Zusatz-Kontext `website`: RAPPORT-WEBSITE.
|
||||
|
||||
# ── Stage 1: Hugo-Website bauen ──────────────────────────────────────────────
|
||||
FROM hugomods/hugo:exts AS web
|
||||
WORKDIR /src
|
||||
COPY --from=website . /src
|
||||
# Frisch bauen (alte public/resources ignorieren), Ausgabe nach /public.
|
||||
RUN rm -rf public resources && hugo --gc --baseURL / --destination /public
|
||||
|
||||
# ── Stage 2: Node-Backend ────────────────────────────────────────────────────
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
|
||||
# Nur Server-Dependencies installieren (Layer-Caching).
|
||||
COPY server/package.json server/package-lock.json* ./server/
|
||||
RUN cd server && npm install --omit=dev --no-audit --no-fund
|
||||
|
||||
# Backend-Code + die gebaute Website.
|
||||
COPY server ./server
|
||||
COPY package.json ./
|
||||
COPY --from=web /public ./website-public
|
||||
|
||||
# Das Backend liest WEBSITE_PUBLIC_DIR; hier auf die gebündelte Website zeigen.
|
||||
ENV WEBSITE_PUBLIC_DIR=/app/website-public
|
||||
ENV PORT=8787
|
||||
EXPOSE 8787
|
||||
|
||||
# Beim Start: HOST-Schema migrieren, dann Server. (host-db ist via compose
|
||||
# depends_on healthy.)
|
||||
CMD ["sh", "-c", "node server/migrate.js && node server/index.js"]
|
||||
@@ -0,0 +1,69 @@
|
||||
# RAPPORT-STACK
|
||||
|
||||
> Alles-in-einem: der komplette RAPPORT-Hosting-Betrieb als **ein** Compose-Stack.
|
||||
> Ein `docker compose up` startet Backend, Rapport-Frontend, Marketing-Website,
|
||||
> Login/Konto und die Betreiber-Plattform.
|
||||
|
||||
## Was drin ist
|
||||
|
||||
| Schicht | Herkunft | Container |
|
||||
|---|---|---|
|
||||
| Rapport-Backend (Supabase) | `../SERVER-CONTAINER` (via `include`) | db, auth, rest, realtime, storage, kong |
|
||||
| Rapport-Frontend | `../SERVER-CONTAINER` | app |
|
||||
| HOST-Datenbank | dieses Repo | host-db |
|
||||
| Marketing + Login + Konto + Admin + API | `../RAPPORT-WEBSITE` (gebaut) + `../RAPPORT-HOST` (Node) | host |
|
||||
|
||||
Der `host`-Container bündelt die **gebaute Hugo-Website** und das **Node-Backend**
|
||||
in einem Image (siehe `Dockerfile.host`). Er provisioniert Kunden-Instanzen über
|
||||
Kong in den Supabase-Stack.
|
||||
|
||||
> **Voraussetzung:** Die drei Schwester-Repos liegen neben diesem
|
||||
> (`~/RAPPORT/SERVER-CONTAINER`, `~/RAPPORT/RAPPORT-WEBSITE`, `~/RAPPORT/RAPPORT-HOST`)
|
||||
> — der Build referenziert sie als Build-Kontexte.
|
||||
|
||||
## Start (lokal)
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
# Secrets setzen: openssl rand -hex 32 für POSTGRES_PASSWORD/JWT_SECRET/HOST_JWT_SECRET
|
||||
# ANON_KEY + SERVICE_ROLE_KEY: cd ../SERVER-CONTAINER && node scripts/generate-keys.mjs <JWT_SECRET>
|
||||
# Rapport-Migrations einmal holen: cd ../SERVER-CONTAINER && ./scripts/sync-migrations.sh
|
||||
|
||||
docker compose up -d
|
||||
docker compose ps # alle healthy?
|
||||
```
|
||||
|
||||
Erreichbar:
|
||||
- **Hosting-Plattform** (Marketing/Login/Konto/Admin): http://localhost:8787
|
||||
- **Rapport-App** (Kunden-Instanzen): http://localhost:8080
|
||||
- **Rapport-API** (Kong): http://localhost:8000
|
||||
|
||||
Admin-Bereich: http://localhost:8787/admin/ (Passwort = `ADMIN_PASSWORD`).
|
||||
|
||||
## Produktion (Hetzner)
|
||||
|
||||
Gleicher Stack, andere `.env`:
|
||||
- Domains in `SITE_URL`, `API_EXTERNAL_URL`, `PUBLIC_BASE_URL`,
|
||||
`RAPPORT_INSTANCE_URL_TEMPLATE`
|
||||
- echte `STRIPE_*`-Keys
|
||||
- davor ein Reverse-Proxy (Caddy/Nginx Proxy Manager) für TLS auf
|
||||
`host.…` (→ 8787) und `app.…` (→ 8080)
|
||||
|
||||
## Aufbau
|
||||
|
||||
```
|
||||
RAPPORT-STACK/
|
||||
├── docker-compose.yml include SERVER-CONTAINER + host-db + host
|
||||
├── Dockerfile.host Hugo-Website-Build + Node-Backend (multi-stage)
|
||||
├── .env.example eine Config für lokal + Hetzner
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Hinweise
|
||||
|
||||
- **host** baut bei `docker compose build` die Website frisch aus
|
||||
`../RAPPORT-WEBSITE`. Nach Website-Änderungen: `docker compose build host`.
|
||||
- Bei Schema-/Migrations-Änderungen im App-Repo:
|
||||
`cd ../SERVER-CONTAINER && ./scripts/sync-migrations.sh && docker compose up -d`
|
||||
- Lizenz: dieses Repo bündelt AGPL-Komponenten (Rapport) UND das proprietäre
|
||||
RAPPORT-HOST. Das Compose-Setup selbst ist Infrastruktur-Glue.
|
||||
@@ -0,0 +1,78 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# RAPPORT-STACK — Alles-in-einem: Supabase-Backend + Rapport-Frontend +
|
||||
# RAPPORT-WEBSITE (Marketing/Login) + RAPPORT-HOST (Hosting-Plattform).
|
||||
#
|
||||
# Ein Befehl bringt die komplette Hosting-Plattform hoch:
|
||||
# cp .env.example .env # Secrets setzen (siehe Kommentare dort)
|
||||
# docker compose up -d
|
||||
#
|
||||
# Läuft lokal UND auf Hetzner — Unterschiede nur über .env (Domains, Keys).
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
# Der komplette Supabase-Stack (db, auth, rest, realtime, storage, kong, app)
|
||||
# wird aus dem Schwester-Repo wiederverwendet — nicht dupliziert.
|
||||
include:
|
||||
- path: ../SERVER-CONTAINER/docker-compose.yml
|
||||
|
||||
services:
|
||||
# ════════════════════════════════════════════════════════════════════════
|
||||
# host-db — eigene Postgres für RAPPORT-HOST (Konten/Abos/Instanzen).
|
||||
# Bewusst GETRENNT von der Rapport-Kunden-DB (db).
|
||||
# ════════════════════════════════════════════════════════════════════════
|
||||
host-db:
|
||||
image: postgres:16-alpine
|
||||
container_name: rapport-host-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${HOST_DB_USER:-rapport_host}
|
||||
POSTGRES_PASSWORD: ${HOST_DB_PASSWORD:-rapport_host}
|
||||
POSTGRES_DB: ${HOST_DB_NAME:-rapport_host}
|
||||
volumes:
|
||||
- host-db-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "${HOST_DB_USER:-rapport_host}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════
|
||||
# host — RAPPORT-HOST Node-Backend, das ZUGLEICH die gebaute RAPPORT-WEBSITE
|
||||
# ausliefert (Marketing + Login + Konto + Admin). Provisioniert Kunden-
|
||||
# Instanzen in den Supabase-Stack über Kong.
|
||||
# ════════════════════════════════════════════════════════════════════════
|
||||
host:
|
||||
build:
|
||||
# Haupt-Kontext = RAPPORT-HOST; die Website kommt als benannter Zusatz-
|
||||
# Kontext rein (so liegen beide Repos im selben Build, ohne Duplikat).
|
||||
context: ../RAPPORT-HOST
|
||||
dockerfile: ../RAPPORT-STACK/Dockerfile.host
|
||||
additional_contexts:
|
||||
website: ../RAPPORT-WEBSITE
|
||||
container_name: rapport-host
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
host-db:
|
||||
condition: service_healthy
|
||||
kong:
|
||||
condition: service_started
|
||||
environment:
|
||||
PORT: 8787
|
||||
PUBLIC_BASE_URL: ${PUBLIC_BASE_URL:-http://localhost:8787}
|
||||
JWT_SECRET: ${HOST_JWT_SECRET}
|
||||
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
|
||||
DATABASE_URL: postgres://${HOST_DB_USER:-rapport_host}:${HOST_DB_PASSWORD:-rapport_host}@host-db:5432/${HOST_DB_NAME:-rapport_host}
|
||||
# Provisioning in den Rapport-Stack (über Kong, service_role = der Stack-Key)
|
||||
RAPPORT_API_URL: ${RAPPORT_API_URL:-http://kong:8000}
|
||||
RAPPORT_SERVICE_KEY: ${SERVICE_ROLE_KEY}
|
||||
RAPPORT_INSTANCE_URL_TEMPLATE: ${RAPPORT_INSTANCE_URL_TEMPLATE:-http://localhost:8080/?studio={slug}}
|
||||
# Stripe (optional; CHANGE-ME → MOCK-Modus)
|
||||
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-}
|
||||
STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:-}
|
||||
STRIPE_PRICE_SOLO: ${STRIPE_PRICE_SOLO:-}
|
||||
STRIPE_PRICE_STUDIO: ${STRIPE_PRICE_STUDIO:-}
|
||||
STRIPE_PRICE_BUSINESS: ${STRIPE_PRICE_BUSINESS:-}
|
||||
ports:
|
||||
- "${HOST_PORT:-8787}:8787"
|
||||
|
||||
volumes:
|
||||
host-db-data:
|
||||
Reference in New Issue
Block a user