Merge branch 'main' of ssh://gitea.meghsakha.com:22222/Benjamin_Boenisch/breakpilot-core
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m22s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 37s
CI / test-python-voice (push) Successful in 40s
CI / test-bqas (push) Successful in 39s
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m22s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 37s
CI / test-python-voice (push) Successful in 40s
CI / test-bqas (push) Successful in 39s
This commit is contained in:
@@ -2,29 +2,29 @@
|
||||
|
||||
## Entwicklungsumgebung (WICHTIG - IMMER ZUERST LESEN)
|
||||
|
||||
### Zwei-Rechner-Setup + Coolify
|
||||
### Zwei-Rechner-Setup + Orca
|
||||
|
||||
| Geraet | Rolle | Aufgaben |
|
||||
|--------|-------|----------|
|
||||
| **MacBook** | Entwicklung | Claude Terminal, Code-Entwicklung, Browser (Frontend-Tests) |
|
||||
| **Mac Mini** | Lokaler Server | Docker fuer lokale Dev/Tests (NICHT fuer Production!) |
|
||||
| **Coolify** | Production | Automatisches Build + Deploy bei Push auf gitea |
|
||||
| **Orca** | Production | Automatisches Build + Deploy bei Push auf gitea |
|
||||
|
||||
**WICHTIG:** Code wird direkt auf dem MacBook in diesem Repo bearbeitet. Production-Deployment laeuft automatisch ueber Coolify.
|
||||
**WICHTIG:** Code wird direkt auf dem MacBook in diesem Repo bearbeitet. Production-Deployment laeuft automatisch ueber Orca.
|
||||
|
||||
### Entwicklungsworkflow (CI/CD — Coolify)
|
||||
### Entwicklungsworkflow (CI/CD — Orca)
|
||||
|
||||
```bash
|
||||
# 1. Code auf MacBook bearbeiten (dieses Verzeichnis)
|
||||
# 2. Committen und zu BEIDEN Remotes pushen:
|
||||
git push origin main && git push gitea main
|
||||
git push origin main
|
||||
|
||||
# 3. FERTIG! Push auf gitea triggert automatisch:
|
||||
# - Gitea Actions: Tests
|
||||
# - Coolify: Build → Deploy
|
||||
# - Orca: Build → Deploy
|
||||
```
|
||||
|
||||
**NIEMALS** manuell in Coolify auf "Redeploy" klicken — Gitea Actions triggert Coolify automatisch.
|
||||
**NIEMALS** manuell in Orca auf "Redeploy" klicken — Gitea Actions triggert Orca automatisch.
|
||||
**IMMER auf `main` pushen** — sowohl origin als auch gitea.
|
||||
|
||||
### Post-Push Deploy-Monitoring (PFLICHT nach jedem Push auf gitea)
|
||||
@@ -39,7 +39,7 @@ git push origin main && git push gitea main
|
||||
```
|
||||
3. Sobald ALLE Endpoints healthy sind, dem User im Chat melden:
|
||||
**"Deploy abgeschlossen! Du kannst jetzt testen."**
|
||||
4. Falls nach 5 Minuten noch nicht healthy → Fehlermeldung mit Hinweis auf Coolify-Logs.
|
||||
4. Falls nach 5 Minuten noch nicht healthy → Fehlermeldung mit Hinweis auf Orca-Logs.
|
||||
|
||||
### Lokale Entwicklung (Mac Mini — optional, nur Dev/Tests)
|
||||
|
||||
@@ -80,8 +80,8 @@ networks:
|
||||
|
||||
| Repo | Deployment | Trigger |
|
||||
|------|-----------|---------|
|
||||
| **breakpilot-core** | Coolify (automatisch) | Push auf gitea main |
|
||||
| **breakpilot-compliance** | Coolify (automatisch) | Push auf gitea main |
|
||||
| **breakpilot-core** | Orca (automatisch) | Push auf gitea main |
|
||||
| **breakpilot-compliance** | Orca (automatisch) | Push auf gitea main |
|
||||
| **breakpilot-lehrer** | Mac Mini (lokal) | Manuell docker compose |
|
||||
|
||||
---
|
||||
@@ -252,8 +252,8 @@ ssh macmini "/usr/local/bin/docker logs -f bp-core-control-pipeline"
|
||||
### Deployment (CI/CD — Standardweg)
|
||||
|
||||
```bash
|
||||
# Committen und pushen → Coolify deployt automatisch:
|
||||
git push origin main && git push gitea main
|
||||
# Committen und pushen → Orca deployt automatisch:
|
||||
git push origin main
|
||||
```
|
||||
|
||||
### Lokale Docker-Befehle (Mac Mini — nur Dev/Tests)
|
||||
@@ -278,11 +278,11 @@ ssh macmini "/usr/local/bin/docker ps --filter name=bp-core"
|
||||
|
||||
```bash
|
||||
# Zu BEIDEN Remotes pushen (PFLICHT!):
|
||||
git push origin main && git push gitea main
|
||||
git push origin main
|
||||
|
||||
# Remotes:
|
||||
# origin: lokale Gitea (macmini:3003)
|
||||
# gitea: gitea.meghsakha.com
|
||||
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -58,7 +58,7 @@ Blocks on: type errors, lint violations, **build failures**.
|
||||
|-------|-------|------|
|
||||
| Pre-push (local) | Claude runs | Lint + type check + unit tests + build |
|
||||
| CI (Gitea Actions) | Automatic on push | Same + integration tests + contract tests |
|
||||
| Deploy (Coolify) | Automatic after CI | Docker build + health check |
|
||||
| Deploy (Orca) | Automatic after CI | Docker build + health check |
|
||||
|
||||
Local checks catch 90% of CI failures in seconds. CI is the safety net, not the first line of defense.
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ jobs:
|
||||
cd pitch-deck
|
||||
SHORT_SHA=$(git rev-parse --short HEAD)
|
||||
docker build \
|
||||
--build-arg GIT_SHA=${SHORT_SHA} \
|
||||
-t registry.meghsakha.com/breakpilot/pitch-deck:latest \
|
||||
-t registry.meghsakha.com/breakpilot/pitch-deck:${SHORT_SHA} \
|
||||
.
|
||||
|
||||
@@ -7,10 +7,10 @@ BreakPilot verwendet zwei Umgebungen:
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ Development │───── git push ────▶│ Production │
|
||||
│ (Mac Mini) │ │ (Coolify) │
|
||||
│ (Mac Mini) │ │ (Orca) │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
Lokale Automatisch
|
||||
Entwicklung via Coolify
|
||||
Entwicklung via Orca
|
||||
```
|
||||
|
||||
## Umgebungen
|
||||
@@ -32,21 +32,21 @@ BreakPilot verwendet zwei Umgebungen:
|
||||
ssh macmini "cd ~/Projekte/breakpilot-core && /usr/local/bin/docker compose up -d"
|
||||
```
|
||||
|
||||
### Production (Coolify)
|
||||
### Production (Orca)
|
||||
|
||||
**Zweck:** Live-System
|
||||
|
||||
| Eigenschaft | Wert |
|
||||
|-------------|------|
|
||||
| Git Branch | `main` |
|
||||
| Deployment | Coolify (automatisch bei Push auf gitea) |
|
||||
| Deployment | Orca (automatisch bei Push auf gitea) |
|
||||
| Database | Externe PostgreSQL (TLS) |
|
||||
| Debug | Deaktiviert |
|
||||
|
||||
**Deploy:**
|
||||
```bash
|
||||
git push origin main && git push gitea main
|
||||
# Coolify baut und deployt automatisch
|
||||
# Orca baut und deployt automatisch
|
||||
```
|
||||
|
||||
## Docker Compose Architektur
|
||||
@@ -54,10 +54,10 @@ git push origin main && git push gitea main
|
||||
```
|
||||
docker-compose.yml ← Basis-Konfiguration (lokal, arm64)
|
||||
│
|
||||
└── docker-compose.coolify.yml ← Production Override (amd64)
|
||||
└── docker-compose.orca.yml ← Production Override (amd64)
|
||||
```
|
||||
|
||||
Coolify verwendet automatisch beide Compose-Files fuer den Production-Build.
|
||||
Orca verwendet automatisch beide Compose-Files fuer den Production-Build.
|
||||
|
||||
## Secrets Management
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ Uebersicht ueber den Deployment-Prozess fuer BreakPilot.
|
||||
|
||||
| Repo | Deployment | Trigger | Compose File |
|
||||
|------|-----------|---------|--------------|
|
||||
| **breakpilot-core** | Coolify (automatisch) | Push auf `coolify` Branch | `docker-compose.coolify.yml` |
|
||||
| **breakpilot-compliance** | Coolify (automatisch) | Push auf `main` Branch | `docker-compose.yml` + `docker-compose.coolify.yml` |
|
||||
| **breakpilot-core** | Orca (automatisch) | Push auf `orca` Branch | `docker-compose.orca.yml` |
|
||||
| **breakpilot-compliance** | Orca (automatisch) | Push auf `main` Branch | `docker-compose.yml` + `docker-compose.orca.yml` |
|
||||
| **breakpilot-lehrer** | Mac Mini (lokal) | Manuell `docker compose` | `docker-compose.yml` |
|
||||
|
||||
## Deployment-Architektur
|
||||
@@ -16,7 +16,7 @@ Uebersicht ueber den Deployment-Prozess fuer BreakPilot.
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Entwickler-MacBook │
|
||||
│ │
|
||||
│ breakpilot-core/ → git push gitea coolify │
|
||||
│ breakpilot-core/ → git push gitea orca │
|
||||
│ breakpilot-compliance/ → git push gitea main │
|
||||
│ breakpilot-lehrer/ → git push + ssh macmini docker ... │
|
||||
│ │
|
||||
@@ -26,11 +26,11 @@ Uebersicht ueber den Deployment-Prozess fuer BreakPilot.
|
||||
│ │
|
||||
▼ ▼
|
||||
┌───────────────────────────┐ ┌───────────────────────────┐
|
||||
│ Coolify (Production) │ │ Mac Mini (Lokal/Dev) │
|
||||
│ Orca (Production) │ │ Mac Mini (Lokal/Dev) │
|
||||
│ │ │ │
|
||||
│ Gitea Actions │ │ breakpilot-lehrer │
|
||||
│ ├── Tests │ │ ├── studio-v2 │
|
||||
│ └── Coolify API Deploy │ │ ├── klausur-service │
|
||||
│ └── Orca API Deploy │ │ ├── klausur-service │
|
||||
│ │ │ ├── backend-lehrer │
|
||||
│ Core Services: │ │ └── voice-service │
|
||||
│ ├── consent-service │ │ │
|
||||
@@ -47,23 +47,23 @@ Uebersicht ueber den Deployment-Prozess fuer BreakPilot.
|
||||
└───────────────────────────┘ └───────────────────────────┘
|
||||
```
|
||||
|
||||
## breakpilot-core → Coolify
|
||||
## breakpilot-core → Orca
|
||||
|
||||
### Pipeline
|
||||
|
||||
```yaml
|
||||
# .gitea/workflows/deploy-coolify.yml
|
||||
# .gitea/workflows/deploy-orca.yml
|
||||
on:
|
||||
push:
|
||||
branches: [coolify]
|
||||
branches: [orca]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Deploy via Coolify API
|
||||
# Triggert Coolify Build + Deploy ueber API
|
||||
# Secrets: COOLIFY_API_TOKEN, COOLIFY_RESOURCE_UUID, COOLIFY_BASE_URL
|
||||
- name: Deploy via Orca API
|
||||
# Triggert Orca Build + Deploy ueber API
|
||||
# Secrets: ORCA_API_TOKEN, ORCA_RESOURCE_UUID, ORCA_BASE_URL
|
||||
```
|
||||
|
||||
### Workflow
|
||||
@@ -74,13 +74,13 @@ jobs:
|
||||
git push origin main && git push gitea main
|
||||
|
||||
# 3. Fuer Production-Deploy:
|
||||
git push gitea coolify
|
||||
git push gitea orca
|
||||
|
||||
# 4. Status pruefen:
|
||||
# https://gitea.meghsakha.com/Benjamin_Boenisch/breakpilot-core/actions
|
||||
```
|
||||
|
||||
### Coolify-deployed Services
|
||||
### Orca-deployed Services
|
||||
|
||||
| Service | Container | Beschreibung |
|
||||
|---------|-----------|--------------|
|
||||
@@ -91,7 +91,7 @@ git push gitea coolify
|
||||
| paddleocr-service | bp-core-paddleocr | OCR Engine (x86_64) |
|
||||
| health-aggregator | bp-core-health | Health-Check Aggregator |
|
||||
|
||||
## breakpilot-compliance → Coolify
|
||||
## breakpilot-compliance → Orca
|
||||
|
||||
### Pipeline
|
||||
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
### Workflow
|
||||
|
||||
```bash
|
||||
# Committen und pushen → Coolify deployt automatisch:
|
||||
# Committen und pushen → Orca deployt automatisch:
|
||||
git push origin main && git push gitea main
|
||||
|
||||
# CI-Status pruefen:
|
||||
@@ -154,8 +154,8 @@ Workflows liegen in jedem Repo unter `.gitea/workflows/`:
|
||||
|
||||
| Repo | Workflow | Branch | Aktion |
|
||||
|------|----------|--------|--------|
|
||||
| breakpilot-core | `deploy-coolify.yml` | `coolify` | Coolify API Deploy |
|
||||
| breakpilot-compliance | `ci.yaml` | `main` | Tests + Coolify Deploy |
|
||||
| breakpilot-core | `deploy-orca.yml` | `orca` | Orca API Deploy |
|
||||
| breakpilot-compliance | `ci.yaml` | `main` | Tests + Orca Deploy |
|
||||
|
||||
### Runner-Token erneuern
|
||||
|
||||
@@ -181,7 +181,7 @@ ssh macmini "/usr/local/bin/docker logs -f bp-core-gitea-runner"
|
||||
|
||||
## Health Checks
|
||||
|
||||
### Production (Coolify)
|
||||
### Production (Orca)
|
||||
|
||||
```bash
|
||||
# Core PaddleOCR
|
||||
@@ -229,14 +229,14 @@ ssh macmini "docker compose build --no-cache <service>"
|
||||
|
||||
## Rollback
|
||||
|
||||
### Coolify
|
||||
### Orca
|
||||
|
||||
Ein Redeploy mit einem aelteren Commit kann durch Zuruecksetzen des Branches ausgeloest werden:
|
||||
|
||||
```bash
|
||||
# Branch auf vorherigen Commit zuruecksetzen und pushen
|
||||
git reset --hard <previous-commit>
|
||||
git push gitea coolify --force
|
||||
git push gitea orca --force
|
||||
```
|
||||
|
||||
### Lokal (Mac Mini)
|
||||
|
||||
@@ -16,8 +16,8 @@ BreakPilot besteht aus drei unabhaengigen Projekten:
|
||||
|
||||
| Repo | Deployment | Trigger |
|
||||
|------|-----------|---------|
|
||||
| **breakpilot-core** | Coolify (automatisch) | Push auf gitea main |
|
||||
| **breakpilot-compliance** | Coolify (automatisch) | Push auf gitea main |
|
||||
| **breakpilot-core** | Orca (automatisch) | Push auf gitea main |
|
||||
| **breakpilot-compliance** | Orca (automatisch) | Push auf gitea main |
|
||||
| **breakpilot-lehrer** | Mac Mini (lokal) | Manuell docker compose |
|
||||
|
||||
## Core Services
|
||||
|
||||
@@ -12,6 +12,10 @@ RUN npm install
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Embed git commit hash into build
|
||||
ARG GIT_SHA=dev
|
||||
ENV GIT_SHA=$GIT_SHA
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { finanzplanToFMResults } from '@/lib/finanzplan/adapter'
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { scenarioId, source } = body
|
||||
const { scenarioId, source, force } = body
|
||||
|
||||
// If source=finanzplan, use the Finanzplan engine instead
|
||||
if (source === 'finanzplan') {
|
||||
@@ -28,8 +28,8 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const client = await pool.connect()
|
||||
try {
|
||||
// Fast path: return cached results if they exist (avoid expensive recompute + 60 inserts)
|
||||
const cached = await client.query(
|
||||
// Fast path: return cached results if they exist (skip when force=true)
|
||||
const cached = force ? { rows: [] } : await client.query(
|
||||
'SELECT * FROM pitch_fm_results WHERE scenario_id = $1 ORDER BY month',
|
||||
[scenarioId]
|
||||
)
|
||||
|
||||
@@ -39,7 +39,9 @@ export async function GET(
|
||||
query += ' ORDER BY sort_order'
|
||||
|
||||
const { rows } = await pool.query(query, params)
|
||||
return NextResponse.json({ sheet: sheetName, rows })
|
||||
return NextResponse.json({ sheet: sheetName, rows }, {
|
||||
headers: { 'Cache-Control': 'no-store' },
|
||||
})
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: String(error) }, { status: 500 })
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ export async function GET() {
|
||||
sheets,
|
||||
scenarios: scenarios.rows,
|
||||
months: { start: '2026-01', end: '2030-12', count: 60, founding: '2026-08' },
|
||||
}, {
|
||||
headers: { 'Cache-Control': 'no-store' },
|
||||
})
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: String(error) }, { status: 500 })
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import Link from 'next/link'
|
||||
import { ArrowLeft, Save } from 'lucide-react'
|
||||
import { ArrowLeft, RefreshCw, Save } from 'lucide-react'
|
||||
|
||||
interface Assumption {
|
||||
id: string
|
||||
@@ -36,6 +36,7 @@ export default function EditScenarioPage() {
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [edits, setEdits] = useState<Record<string, string>>({})
|
||||
const [savingId, setSavingId] = useState<string | null>(null)
|
||||
const [recomputing, setRecomputing] = useState(false)
|
||||
const [toast, setToast] = useState<string | null>(null)
|
||||
|
||||
function flashToast(msg: string) {
|
||||
@@ -56,6 +57,17 @@ export default function EditScenarioPage() {
|
||||
|
||||
useEffect(() => { if (scenarioId) load() }, [scenarioId])
|
||||
|
||||
async function forceRecompute() {
|
||||
setRecomputing(true)
|
||||
const res = await fetch('/api/financial-model/compute', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ scenarioId, force: true }),
|
||||
})
|
||||
setRecomputing(false)
|
||||
flashToast(res.ok ? 'Recomputed successfully' : 'Recompute failed')
|
||||
}
|
||||
|
||||
function setEdit(id: string, val: string) {
|
||||
setEdits(prev => ({ ...prev, [id]: val }))
|
||||
}
|
||||
@@ -108,17 +120,28 @@ export default function EditScenarioPage() {
|
||||
<ArrowLeft className="w-4 h-4" /> Back to scenarios
|
||||
</Link>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-1">
|
||||
<div className="w-4 h-4 rounded-full" style={{ backgroundColor: scenario.color }} />
|
||||
<h1 className="text-2xl font-semibold text-white">{scenario.name}</h1>
|
||||
{scenario.is_default && (
|
||||
<span className="text-[9px] px-1.5 py-0.5 rounded bg-indigo-500/20 text-indigo-300 uppercase font-semibold">
|
||||
Default
|
||||
</span>
|
||||
)}
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-1">
|
||||
<div className="w-4 h-4 rounded-full" style={{ backgroundColor: scenario.color }} />
|
||||
<h1 className="text-2xl font-semibold text-white">{scenario.name}</h1>
|
||||
{scenario.is_default && (
|
||||
<span className="text-[9px] px-1.5 py-0.5 rounded bg-indigo-500/20 text-indigo-300 uppercase font-semibold">
|
||||
Default
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{scenario.description && <p className="text-sm text-white/50">{scenario.description}</p>}
|
||||
</div>
|
||||
{scenario.description && <p className="text-sm text-white/50">{scenario.description}</p>}
|
||||
<button
|
||||
onClick={forceRecompute}
|
||||
disabled={recomputing}
|
||||
className="flex items-center gap-2 text-sm px-3 py-1.5 rounded-lg bg-white/[0.06] hover:bg-white/[0.1] text-white/70 hover:text-white disabled:opacity-40 disabled:cursor-wait transition-colors"
|
||||
title="Clear cache and recompute financial model results"
|
||||
>
|
||||
<RefreshCw className={`w-3.5 h-3.5 ${recomputing ? 'animate-spin' : ''}`} />
|
||||
{recomputing ? 'Computing…' : 'Force Recompute'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
|
||||
@@ -92,6 +92,11 @@ export default function AdminShell({ admin, children }: AdminShellProps) {
|
||||
<div className="px-3 py-2 mb-2">
|
||||
<div className="text-sm font-medium text-white/90 truncate">{admin.name}</div>
|
||||
<div className="text-xs text-white/40 truncate">{admin.email}</div>
|
||||
<div className="mt-1.5 flex items-center gap-1.5">
|
||||
<span className="text-[9px] font-mono bg-white/[0.06] text-white/30 px-1.5 py-0.5 rounded">
|
||||
{process.env.NEXT_PUBLIC_GIT_SHA ?? 'dev'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={logout}
|
||||
|
||||
@@ -178,8 +178,8 @@ export default function AIPipelineSlide({ lang }: AIPipelineSlideProps) {
|
||||
color: 'text-purple-400',
|
||||
title: de ? 'CI/CD & Testing' : 'CI/CD & Testing',
|
||||
items: de
|
||||
? ['Gitea Actions: Lint → Tests → Validierung bei jedem Push', 'Go-Tests (AI SDK) + Python-Tests (Backend + Pipeline)', 'Coolify Auto-Deploy mit Health-Check-Monitoring', 'arm64 → amd64 Cross-Build für Hetzner Production']
|
||||
: ['Gitea Actions: Lint → Tests → Validation on every push', 'Go tests (AI SDK) + Python tests (Backend + Pipeline)', 'Coolify auto-deploy with health check monitoring', 'arm64 → amd64 cross-build for Hetzner production'],
|
||||
? ['Gitea Actions: Lint → Tests → Validierung bei jedem Push', 'Go-Tests (AI SDK) + Python-Tests (Backend + Pipeline)', 'Orca Auto-Deploy mit Health-Check-Monitoring', 'arm64 → amd64 Cross-Build für Hetzner Production']
|
||||
: ['Gitea Actions: Lint → Tests → Validation on every push', 'Go tests (AI SDK) + Python tests (Backend + Pipeline)', 'Orca auto-deploy with health check monitoring', 'arm64 → amd64 cross-build for Hetzner production'],
|
||||
},
|
||||
{
|
||||
icon: Zap,
|
||||
|
||||
@@ -61,8 +61,12 @@ function formatCell(v: number | undefined): string {
|
||||
return Math.round(v).toLocaleString('de-DE', { maximumFractionDigits: 0 })
|
||||
}
|
||||
|
||||
interface FpScenario { id: string; name: string; is_default: boolean }
|
||||
|
||||
export default function FinanzplanSlide({ lang, investorId, preferredScenarioId }: FinanzplanSlideProps) {
|
||||
const [sheets, setSheets] = useState<SheetMeta[]>([])
|
||||
const [scenarios, setScenarios] = useState<FpScenario[]>([])
|
||||
const [selectedScenarioId, setSelectedScenarioId] = useState<string>('')
|
||||
const [activeSheet, setActiveSheet] = useState<string>('personalkosten')
|
||||
const [rows, setRows] = useState<SheetRow[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
@@ -77,19 +81,26 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId
|
||||
[fm.activeResults],
|
||||
)
|
||||
|
||||
// Determine fp_scenario_id from the active FM scenario name
|
||||
const fpScenarioParam = fm.activeScenario?.name?.toLowerCase().includes('wandeldarlehen')
|
||||
? '?scenarioId=c0000000-0000-0000-0000-000000000200'
|
||||
: ''
|
||||
|
||||
// Load sheet list
|
||||
// Load sheet list + scenarios
|
||||
useEffect(() => {
|
||||
fetch('/api/finanzplan')
|
||||
fetch('/api/finanzplan', { cache: 'no-store' })
|
||||
.then(r => r.json())
|
||||
.then(data => setSheets(data.sheets || []))
|
||||
.then(data => {
|
||||
setSheets(data.sheets || [])
|
||||
const scens: FpScenario[] = data.scenarios || []
|
||||
setScenarios(scens)
|
||||
// Pick default scenario on first load
|
||||
if (!selectedScenarioId) {
|
||||
const def = scens.find(s => s.is_default) ?? scens[0]
|
||||
if (def) setSelectedScenarioId(def.id)
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const scenarioParam = selectedScenarioId ? `?scenarioId=${selectedScenarioId}` : ''
|
||||
|
||||
// Load sheet data
|
||||
const loadSheet = useCallback(async (name: string) => {
|
||||
if (name === 'kpis' || name === 'charts') {
|
||||
@@ -99,12 +110,12 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId
|
||||
}
|
||||
setLoading(true)
|
||||
try {
|
||||
const r = await fetch(`/api/finanzplan/${name}${fpScenarioParam}`)
|
||||
const r = await fetch(`/api/finanzplan/${name}${scenarioParam}`, { cache: 'no-store' })
|
||||
const data = await r.json()
|
||||
setRows(data.rows || [])
|
||||
} catch { /* ignore */ }
|
||||
setLoading(false)
|
||||
}, [fpScenarioParam])
|
||||
}, [scenarioParam])
|
||||
|
||||
useEffect(() => { loadSheet(activeSheet) }, [activeSheet, loadSheet])
|
||||
|
||||
@@ -112,7 +123,7 @@ export default function FinanzplanSlide({ lang, investorId, preferredScenarioId
|
||||
const handleCompute = async () => {
|
||||
setComputing(true)
|
||||
try {
|
||||
await fetch('/api/finanzplan/compute', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' })
|
||||
await fetch('/api/finanzplan/compute', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ scenarioId: selectedScenarioId || undefined }) })
|
||||
await loadSheet(activeSheet)
|
||||
} catch { /* ignore */ }
|
||||
setComputing(false)
|
||||
|
||||
@@ -692,8 +692,8 @@ export const PRESENTER_FAQ: FAQEntry[] = [
|
||||
keywords: ['syseleven', 'hetzner', 'cloud', 'hosting', 'infrastruktur', 'infrastructure', 'server', 'rechenzentrum', 'data center', 'wo laufen', 'where hosted'],
|
||||
question_de: 'Auf welcher Infrastruktur laeuft die Plattform?',
|
||||
question_en: 'What infrastructure does the platform run on?',
|
||||
answer_de: 'Unsere Plattform laeuft zu 100 Prozent auf europaeischer Cloud-Infrastruktur — ohne einen einzigen US-Anbieter. Fuer LLM-Inferenz und KI-Workloads nutzen wir SysEleven, einen BSI-C5-zertifizierten deutschen Cloud-Provider mit GPU-Kapazitaet. Fuer Datenbanken, Vektorspeicher und Anwendungslogik setzen wir auf Hetzner — ebenfalls deutsch, ISO 27001-zertifiziert und deutlich kostenguenstiger als AWS oder Azure. Das CI/CD laeuft ueber Gitea Actions mit automatischem Deploy via Coolify auf Hetzner. Diese Kombination gibt uns einen strukturellen Kostenvorteil bei voller EU-Datensouveraenitaet.',
|
||||
answer_en: 'Our platform runs 100 percent on European cloud infrastructure — without a single US provider. For LLM inference and AI workloads we use SysEleven, a BSI C5-certified German cloud provider with GPU capacity. For databases, vector storage and application logic we rely on Hetzner — also German, ISO 27001-certified and significantly more cost-effective than AWS or Azure. CI/CD runs via Gitea Actions with automatic deploy via Coolify on Hetzner. This combination gives us a structural cost advantage with full EU data sovereignty.',
|
||||
answer_de: 'Unsere Plattform laeuft zu 100 Prozent auf europaeischer Cloud-Infrastruktur — ohne einen einzigen US-Anbieter. Fuer LLM-Inferenz und KI-Workloads nutzen wir SysEleven, einen BSI-C5-zertifizierten deutschen Cloud-Provider mit GPU-Kapazitaet. Fuer Datenbanken, Vektorspeicher und Anwendungslogik setzen wir auf Hetzner — ebenfalls deutsch, ISO 27001-zertifiziert und deutlich kostenguenstiger als AWS oder Azure. Das CI/CD laeuft ueber Gitea Actions mit automatischem Deploy via Orca auf Hetzner. Diese Kombination gibt uns einen strukturellen Kostenvorteil bei voller EU-Datensouveraenitaet.',
|
||||
answer_en: 'Our platform runs 100 percent on European cloud infrastructure — without a single US provider. For LLM inference and AI workloads we use SysEleven, a BSI C5-certified German cloud provider with GPU capacity. For databases, vector storage and application logic we rely on Hetzner — also German, ISO 27001-certified and significantly more cost-effective than AWS or Azure. CI/CD runs via Gitea Actions with automatic deploy via Orca on Hetzner. This combination gives us a structural cost advantage with full EU data sovereignty.',
|
||||
goto_slide: 'annex-architecture',
|
||||
priority: 8,
|
||||
},
|
||||
|
||||
@@ -534,8 +534,8 @@ export const PRESENTER_SCRIPT: SlideScript[] = [
|
||||
duration: 40,
|
||||
paragraphs: [
|
||||
{
|
||||
text_de: 'Engineering Deep Dive: Über 500.000 Zeilen Code, 45 Container, 65 Compliance-Module. Tech-Stack: Go, Python, TypeScript mit Next.js. CI/CD über Gitea Actions mit automatischem Deploy via Coolify auf Hetzner.',
|
||||
text_en: 'Engineering deep dive: Over 500,000 lines of code, 45 containers, 65 compliance modules. Tech stack: Go, Python, TypeScript with Next.js. CI/CD via Gitea Actions with automatic deploy via Coolify on Hetzner.',
|
||||
text_de: 'Engineering Deep Dive: Über 500.000 Zeilen Code, 45 Container, 65 Compliance-Module. Tech-Stack: Go, Python, TypeScript mit Next.js. CI/CD über Gitea Actions mit automatischem Deploy via Orca auf Hetzner.',
|
||||
text_en: 'Engineering deep dive: Over 500,000 lines of code, 45 containers, 65 compliance modules. Tech stack: Go, Python, TypeScript with Next.js. CI/CD via Gitea Actions with automatic deploy via Orca on Hetzner.',
|
||||
pause_after: 2000,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: 'standalone',
|
||||
env: {
|
||||
NEXT_PUBLIC_GIT_SHA: process.env.GIT_SHA || 'dev',
|
||||
},
|
||||
reactStrictMode: true,
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
|
||||
Reference in New Issue
Block a user