// Runtime config + filesystem paths. Reads process.env once at startup. const fs = require('fs'); const path = require('path'); const PUBLIC_DIR = path.join(__dirname, 'public'); // Uploaded chat files, recordings and transcripts MUST live on the persistent volume (like the DB // and downloads). With the old in-image path, every deploy.sh rebuild wiped them — old images/files // then 404 ("broken image") while their DB rows survive. Overridable so prod points them at /data. const REC_DIR = process.env.REC_DIR || path.join(__dirname, 'recordings'); const TRANS_DIR = process.env.TRANS_DIR || path.join(__dirname, 'transcripts'); const UPLOADS_DIR = process.env.UPLOADS_DIR || path.join(__dirname, 'uploads'); // Desktop installers + auto-update feed (latest.yml). Override with DOWNLOADS_DIR to point at a // mounted volume in production; IT drops the electron-builder dist/ output here. const DOWNLOADS_DIR = process.env.DOWNLOADS_DIR || path.join(__dirname, 'downloads'); try { fs.mkdirSync(REC_DIR, { recursive: true }); } catch (e) {} try { fs.mkdirSync(TRANS_DIR, { recursive: true }); } catch (e) {} try { fs.mkdirSync(UPLOADS_DIR, { recursive: true }); } catch (e) {} try { fs.mkdirSync(DOWNLOADS_DIR, { recursive: true }); } catch (e) {} // LiveKit SFU (scales meetings past the ~5-peer mesh ceiling). Entirely optional and config-gated: // when LIVEKIT_URL/API_KEY/API_SECRET are all set the client uses LiveKit for meeting media; when // they're unset the app falls back to the built-in P2P mesh, unchanged. The API secret is used // ONLY server-side to mint per-user join tokens — it never reaches the browser. const LIVEKIT_URL = process.env.LIVEKIT_URL || ''; // wss://livekit.bizgaze.com const LIVEKIT_API_KEY = process.env.LIVEKIT_API_KEY || ''; const LIVEKIT_API_SECRET = process.env.LIVEKIT_API_SECRET || ''; const LIVEKIT_ENABLED = !!(LIVEKIT_URL && LIVEKIT_API_KEY && LIVEKIT_API_SECRET); // SMTP for outbound email (meeting invites to external participants, #4). Entirely optional and // config-gated: email is only sent when SMTP_HOST/USER/PASS are set. Credentials stay server-side. // PUBLIC_BASE_URL is the origin used to build guest meeting links in emails (e.g. https://remote.bizgaze.com). const SMTP_HOST = process.env.SMTP_HOST || ''; const SMTP_PORT = Number(process.env.SMTP_PORT || 587); const SMTP_SECURE = String(process.env.SMTP_SECURE || '').toLowerCase() === 'true' || SMTP_PORT === 465; // TLS on connect (465) vs STARTTLS const SMTP_USER = process.env.SMTP_USER || ''; const SMTP_PASS = process.env.SMTP_PASS || ''; const SMTP_FROM = process.env.SMTP_FROM || (SMTP_USER ? ('Biz Connect <' + SMTP_USER + '>') : ''); const SMTP_ENABLED = !!(SMTP_HOST && SMTP_USER && SMTP_PASS); const PUBLIC_BASE_URL = (process.env.PUBLIC_BASE_URL || 'https://remote.bizgaze.com').replace(/\/+$/, ''); module.exports = { PORT: process.env.PORT || 8090, HTTPS_PORT: process.env.HTTPS_PORT || 8443, LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET, LIVEKIT_ENABLED, SMTP_HOST, SMTP_PORT, SMTP_SECURE, SMTP_USER, SMTP_PASS, SMTP_FROM, SMTP_ENABLED, PUBLIC_BASE_URL, PUBLIC_DIR, REC_DIR, TRANS_DIR, UPLOADS_DIR, DOWNLOADS_DIR, SESSION_TTL: 1000 * 60 * 60 * 24, // 24h access-token / cookie lifetime REFRESH_TTL: 1000 * 60 * 60 * 24 * 90, // 90d refresh-token lifetime (native clients) };