Shaheer Sarfaraz c4749b4211
feat: opinionated public DEMO_MODE with simulated/blocked actions, resets, and demo toasts (#87)
* feat(demo): add DEMO_MODE runtime helpers and /api/demo/info endpoint

* feat(demo): enforce simulated and blocked API actions under DEMO_MODE

* feat(demo): add deterministic seed dataset and 6-hour auto-reset

* feat(demo-ui): add demo banner and custom sonner toasts for simulated/blocked actions

* test+docs(demo): add demo mode coverage, behavior matrix, and operator docs

* formatting

* tests

* feat(demo): seed resets from typed baseline defaults

* formatting

* feat(demo): enrich baseline seed data and demo project catalog

* feat(demo): expand seeded applications and chart time ranges

* refactor(demo): split demo seed data from generation logic

* feat(demo): cap generated application history to 30 days

* feat(demo): rebalance generated job status distribution

* feat(demo-ui): make demo banner fixed and topmost

* minor fixes

* formatting

* duration revert

* durations

* feat(demo): share demo info hook, brighten demo toasts, and enforce webhook auth

* comment explaning

* formatting

* comments

* deadline builder comment
2026-02-05 16:04:04 +00:00

70 lines
1.9 KiB
TypeScript

import { logger } from "@infra/logger";
import {
DEMO_RESET_CADENCE_HOURS,
isDemoMode,
setDemoResetTimes,
} from "@server/config/demo";
import {
DEMO_BASELINE_NAME,
DEMO_BASELINE_VERSION,
} from "@server/config/demo-defaults";
import { applyDemoBaseline, buildDemoBaseline } from "./demo-seed";
const RESET_INTERVAL_MS = DEMO_RESET_CADENCE_HOURS * 60 * 60 * 1000;
let resetTimer: ReturnType<typeof setTimeout> | null = null;
let isResetRunning = false;
function computeNextReset(now: Date): Date {
return new Date(now.getTime() + RESET_INTERVAL_MS);
}
function scheduleNextReset(): void {
const now = new Date();
const nextReset = computeNextReset(now);
const delay = nextReset.getTime() - now.getTime();
setDemoResetTimes({ nextResetAt: nextReset.toISOString() });
if (resetTimer) clearTimeout(resetTimer);
resetTimer = setTimeout(() => {
void runDemoResetCycle();
}, delay);
}
export async function resetDemoData(): Promise<void> {
const baseline = buildDemoBaseline(new Date());
await applyDemoBaseline(baseline);
}
export async function runDemoResetCycle(): Promise<void> {
if (isResetRunning) return;
isResetRunning = true;
try {
await resetDemoData();
const nowIso = new Date().toISOString();
setDemoResetTimes({ lastResetAt: nowIso });
scheduleNextReset();
logger.info("Demo dataset reset completed", {
lastResetAt: nowIso,
baselineVersion: DEMO_BASELINE_VERSION,
});
} catch (error) {
logger.error("Failed to reset demo dataset", { error });
scheduleNextReset();
} finally {
isResetRunning = false;
}
}
export async function initializeDemoModeServices(): Promise<void> {
if (!isDemoMode()) return;
await runDemoResetCycle();
logger.info("Demo mode services initialized", {
resetCadenceHours: DEMO_RESET_CADENCE_HOURS,
baselineVersion: DEMO_BASELINE_VERSION,
baselineName: DEMO_BASELINE_NAME,
});
}