Initial: RAPPORT-HOST Iteration 1 (proprietär)
Kommerzielle Hosting-/Abo-Plattform für Rapport-Instanzen. - React-Frontend (Vite/JSX): Landing, Register, Login, Plans, Dashboard - Node/Express-Backend: Auth (bcrypt+JWT), Stripe-Billing, Provisioning - HOST-Postgres-Schema: accounts, subscriptions, instances - Provisioning-Interface + Modell-A-Adapter (Studio im geteilten Stack) - MOCK-Modus: voller End-to-End-Flow ohne Stripe/Rapport-Stack testbar - Idempotentes Fulfillment (Upsert auf stripe_subscription_id) - docker-compose für lokale host-db; identisch auf Hetzner deploybar E2E lokal verifiziert: Register -> Checkout(mock) -> Instanz -> Idempotenz. Lizenz: proprietär (kein AGPL-Code eingebunden, nur Netzwerk-API zur Familie). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
// Zentrales Laden + Validieren der Umgebungsvariablen.
|
||||
// Liest .env (einfacher Parser, keine Dependency) und fällt sonst auf
|
||||
// process.env zurück — so läuft es lokal mit Datei und auf Hetzner mit
|
||||
// echten Env-Vars, ohne Code-Änderung.
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const envPath = path.resolve(__dirname, "..", ".env");
|
||||
|
||||
if (fs.existsSync(envPath)) {
|
||||
for (const line of fs.readFileSync(envPath, "utf8").split("\n")) {
|
||||
const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/);
|
||||
if (!m) continue;
|
||||
const key = m[1];
|
||||
let val = m[2].replace(/^["']|["']$/g, "");
|
||||
if (process.env[key] === undefined) process.env[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
const e = process.env;
|
||||
|
||||
export const env = {
|
||||
port: parseInt(e.PORT || "8787", 10),
|
||||
publicBaseUrl: (e.PUBLIC_BASE_URL || "http://localhost:5273").replace(/\/+$/, ""),
|
||||
jwtSecret: e.JWT_SECRET || "dev-insecure-secret-change-me",
|
||||
databaseUrl: e.DATABASE_URL || "postgres://rapport_host:rapport_host@localhost:55432/rapport_host",
|
||||
|
||||
stripe: {
|
||||
secretKey: e.STRIPE_SECRET_KEY || "",
|
||||
webhookSecret: e.STRIPE_WEBHOOK_SECRET || "",
|
||||
prices: {
|
||||
solo: e.STRIPE_PRICE_SOLO || "",
|
||||
studio: e.STRIPE_PRICE_STUDIO || "",
|
||||
business: e.STRIPE_PRICE_BUSINESS || "",
|
||||
},
|
||||
},
|
||||
|
||||
rapport: {
|
||||
apiUrl: e.RAPPORT_API_URL || "",
|
||||
serviceKey: e.RAPPORT_SERVICE_KEY || "",
|
||||
instanceUrlTemplate: e.RAPPORT_INSTANCE_URL_TEMPLATE || "http://localhost:8080/?studio={slug}",
|
||||
},
|
||||
};
|
||||
|
||||
// MOCK-Modus: ohne echte Stripe-/Rapport-Keys läuft alles lokal simuliert,
|
||||
// damit der End-to-End-Flow ohne externe Dienste testbar ist.
|
||||
export const stripeEnabled = !!env.stripe.secretKey && env.stripe.secretKey.startsWith("sk_");
|
||||
export const provisioningMock = !env.rapport.apiUrl || !env.rapport.serviceKey;
|
||||
|
||||
if (env.jwtSecret === "dev-insecure-secret-change-me") {
|
||||
console.warn("⚠ JWT_SECRET nicht gesetzt — unsicheres Dev-Secret in Verwendung.");
|
||||
}
|
||||
if (!stripeEnabled) console.warn("⚠ STRIPE_SECRET_KEY fehlt — Billing läuft im MOCK-Modus.");
|
||||
if (provisioningMock) console.warn("⚠ RAPPORT_API_URL/SERVICE_KEY fehlen — Provisioning läuft im MOCK-Modus.");
|
||||
Reference in New Issue
Block a user