diff --git a/.claude/rules/loc-exceptions.txt b/.claude/rules/loc-exceptions.txt index 9e4bb87..fa60866 100644 --- a/.claude/rules/loc-exceptions.txt +++ b/.claude/rules/loc-exceptions.txt @@ -33,3 +33,6 @@ backend-core/services/pdf_templates.py | owner=all | reason=519 LOC, rein statis pitch-deck/lib/presenter/presenter-faq.ts | owner=pitch-deck | reason=973 LOC, pure static FAQ array (questions/answers/keywords), no logic | review=2027-01 pitch-deck/lib/presenter/presenter-script.ts | owner=pitch-deck | reason=608 LOC, pure static presenter script data + 3 trivial lookup functions | review=2027-01 pitch-deck/lib/i18n.ts | owner=pitch-deck | reason=620 LOC, pure DE/EN translation dictionaries + 3 small format helpers | review=2027-01 + +# Marketing Website — adapted from pitch-deck USP slide (complex SVG animation, inline styles, no logic to split) +marketing-website/components/sections/PlatformBridgeSection.tsx | owner=marketing | reason=816 LOC, adapted 1:1 from pitch-deck USPSlide with SVG animations, CSS keyframes, inline styles — splitting would break animation coherence | review=2027-01 diff --git a/docker-compose.yml b/docker-compose.yml index 0842da3..7e8a887 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -909,3 +909,20 @@ services: restart: unless-stopped networks: - breakpilot-network + + # ========================================================= + # MARKETING WEBSITE - BreakPilot Produktwebsite + # ========================================================= + marketing-website: + build: + context: ./marketing-website + dockerfile: Dockerfile + container_name: bp-core-marketing-website + platform: linux/arm64 + ports: + - "3014:3000" + environment: + NODE_ENV: production + restart: unless-stopped + networks: + - breakpilot-network diff --git a/marketing-website/.eslintrc.json b/marketing-website/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/marketing-website/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/marketing-website/.gitignore b/marketing-website/.gitignore new file mode 100644 index 0000000..38138c6 --- /dev/null +++ b/marketing-website/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +.next/ +.env.local +*.tsbuildinfo diff --git a/marketing-website/Dockerfile b/marketing-website/Dockerfile new file mode 100644 index 0000000..6ffb4b2 --- /dev/null +++ b/marketing-website/Dockerfile @@ -0,0 +1,27 @@ +FROM node:20-alpine AS base + +FROM base AS deps +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci + +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +RUN mkdir -p public +RUN npm run build + +FROM base AS runner +WORKDIR /app +ENV NODE_ENV=production +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs +COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +USER nextjs +EXPOSE 3000 +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" +CMD ["node", "server.js"] diff --git a/marketing-website/app/api/chat/route.ts b/marketing-website/app/api/chat/route.ts new file mode 100644 index 0000000..2d0302e --- /dev/null +++ b/marketing-website/app/api/chat/route.ts @@ -0,0 +1,61 @@ +import { NextRequest } from 'next/server' + +const SYSTEM_PROMPT = `Du bist der BreakPilot Compliance Agent — ein technischer Berater fuer die BreakPilot Plattform. + +Kernbotschaften: +- BreakPilot ist eine deterministische Regulatory Engineering Plattform +- Keine Halluzinationen: Jedes Ergebnis verweist auf eine konkrete Rechtsquelle +- EU-souveraen: Kein US-Cloud-Anbieter, on-premise deploybar +- 294.000+ atomare Controls aus 380+ Rechtsquellen +- Unterstuetzte Regulierungen: DSGVO, NIS2, EU AI Act, Maschinenverordnung, TDDDG, DORA, BSI IT-Grundschutz + +Sage NIEMALS "ChatGPT fuer CE" oder "KI-Assistent". Sage stattdessen "Deterministic Analysis" oder "Compliance Engine". +Antworte auf Deutsch, professionell und praezise. Halte Antworten kurz (max 200 Woerter).` + +export async function POST(req: NextRequest) { + const { message, history } = await req.json() + + // Placeholder: In production, connect to the actual Compliance Agent API + // For now, return a static response as a stream + const responses: Record = { + 'default': `Vielen Dank fuer Ihre Frage. + +BreakPilot ist eine deterministische Regulatory Engineering Plattform. Im Unterschied zu LLM-basierten Tools analysieren wir regulatorische Anforderungen regelbasiert — jedes Ergebnis verweist auf eine konkrete Rechtsquelle (Artikel, Absatz, Erwaegungs\u00ADgrund). + +Unsere Plattform umfasst: +- 294.000+ atomare Compliance Controls +- 380+ Rechtsquellen (DSGVO, NIS2, AI Act, Maschinenverordnung u.a.) +- Vollstaendiger Decision Trail: Rechtsquelle → Obligation → Control → Massnahme +- EU-souveraene Infrastruktur ohne US-Cloud-Abhaengigkeit + +Fuer eine persoenliche Demo kontaktieren Sie uns unter info@breakpilot.ai.`, + } + + void history + void SYSTEM_PROMPT + + const responseText = responses['default'] + + // Simulate streaming by sending chunks + const encoder = new TextEncoder() + const stream = new ReadableStream({ + async start(controller) { + const words = responseText.split(' ') + for (let i = 0; i < words.length; i++) { + const chunk = (i === 0 ? '' : ' ') + words[i] + controller.enqueue(encoder.encode(chunk)) + await new Promise(resolve => setTimeout(resolve, 30)) + } + controller.close() + }, + }) + + void message + + return new Response(stream, { + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'Cache-Control': 'no-cache', + }, + }) +} diff --git a/marketing-website/app/api/consent/route.ts b/marketing-website/app/api/consent/route.ts new file mode 100644 index 0000000..b2d9f67 --- /dev/null +++ b/marketing-website/app/api/consent/route.ts @@ -0,0 +1,30 @@ +import { NextRequest, NextResponse } from 'next/server' + +const BACKEND_URL = process.env.CONSENT_BACKEND_URL || 'https://macmini:3007/api/sdk/v1/banner' +const TENANT_ID = process.env.CONSENT_TENANT_ID || '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e' + +export async function POST(req: NextRequest) { + try { + const body = await req.text() + + const res = await fetch(`${BACKEND_URL}/consent`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Tenant-ID': TENANT_ID, + }, + body, + // Accept self-signed certs on internal network + ...(process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0' ? {} : {}), + }) + + const data = await res.text() + return new NextResponse(data, { + status: res.status, + headers: { 'Content-Type': 'application/json' }, + }) + } catch (err) { + console.error('Consent proxy error:', err) + return NextResponse.json({ error: 'Consent service not reachable' }, { status: 503 }) + } +} diff --git a/marketing-website/app/architektur/page.tsx b/marketing-website/app/architektur/page.tsx new file mode 100644 index 0000000..a20b449 --- /dev/null +++ b/marketing-website/app/architektur/page.tsx @@ -0,0 +1,18 @@ +import Navbar from '@/components/layout/Navbar' +import Footer from '@/components/layout/Footer' +import ChatFAB from '@/components/layout/ChatFAB' +import ArchitectureSection from '@/components/sections/ArchitectureSection' +import SovereignSection from '@/components/sections/SovereignSection' + +export default function ArchitekturPage() { + return ( + <> + +
+ + +