metatroncubeswdev 075b79f6b2 Hosted Quorla: sign-up, Stripe billing, one workspace per agency
- Control centre (QUORLA_CONTROL=true on app.quorlasocial.com): public /signup → Stripe Checkout,
  verified Stripe webhook (paid, updated, cancelled, payment failed), Admin → Agencies
  (approve without Stripe, suspend, resume, billing portal, resend welcome, remove)
- deploy/provision-tenants.sh (cron): one container per agency with its own .env and data,
  nginx map for <agency>.quorlasocial.com, upgrades agencies after a rebuild, archives removed data
- New workspaces: first admin only via the emailed one-time setup link (SETUP_TOKEN)
- Meta system user token can be saved in Settings (encrypted) for workspaces that can't edit .env
- docs/multi-tenant.md: DNS, wildcard cert, nginx, sudoers, cron, Stripe setup; tests for the control centre

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:35:23 -04:00

41 lines
1.8 KiB
JavaScript

// Paths and settings shared by the dashboard backend.
// Secrets come from the project .env (same file the social-manager connector uses).
import path from "node:path";
import fs from "node:fs";
import crypto from "node:crypto";
import { loadEnv, PROJECT_ROOT } from "../../connector/src/env.js";
loadEnv();
export { PROJECT_ROOT };
export const CLIENTS_DIR = path.join(PROJECT_ROOT, "clients");
export const TEMPLATE_DIR = path.join(CLIENTS_DIR, "_template");
export const DATA_DIR = process.env.QUORLA_DASHBOARD_DATA || path.join(PROJECT_ROOT, "data", "dashboard"); // override for tests
export const BRANDING_DIR = path.join(PROJECT_ROOT, "branding");
export const WEB_DIST = path.join(PROJECT_ROOT, "dashboard", "web", "dist");
fs.mkdirSync(DATA_DIR, { recursive: true });
export const PORT = Number(process.env.DASHBOARD_PORT || 4310);
export const AGENCY_NAME = process.env.AGENCY_NAME || "Metatroncube";
// Session signing secret. In production set DASHBOARD_SECRET; otherwise a random one is
// generated per process (everyone is logged out on restart).
export const SESSION_SECRET = process.env.DASHBOARD_SECRET || crypto.randomBytes(32).toString("hex");
export const SESSION_HOURS = Number(process.env.DASHBOARD_SESSION_HOURS || 12);
export const COOKIE_SECURE = process.env.DASHBOARD_COOKIE_SECURE === "true";
export const ODOO = {
url: (process.env.ODOO_URL || "").replace(/\/$/, ""),
db: process.env.ODOO_DATABASE || process.env.ODOO_DB || "",
username: process.env.ODOO_USERNAME || "",
secret: process.env.ODOO_API_KEY || process.env.ODOO_PASSWORD || "",
allowWrites: process.env.ODOO_ALLOW_WRITES === "true",
};
// AI runs (audits, plans, reports) go through the Claude Agent SDK.
export const AI = {
model: process.env.DASHBOARD_AI_MODEL || "claude-opus-5-5",
maxTurns: Number(process.env.DASHBOARD_AI_MAX_TURNS || 80),
};