| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- /**
- * briven-engine dashboard workspace — projects the operator can manage,
- * with Auth on/off based on be_tenants (Doltgres).
- */
- import { listProjectsForUser } from '../projects.js';
- import { getEnginePool } from './db.js';
- import { isAuthCoreInitialized } from './engine.js';
- import { ensureBrivenEngineTenant } from './multitenancy.js';
- import { getBrivenEngineProjectConfig } from './project-config.js';
- import { mapProjectToAuthCore } from './project-map.js';
- export type BrivenEngineWorkspaceProject = {
- id: string;
- slug: string;
- name: string;
- authEnabled: boolean;
- tenantId: string | null;
- providers: {
- emailPassword: boolean;
- magicLink: boolean;
- emailOtp: boolean;
- passkey: boolean;
- } | null;
- error?: boolean;
- };
- /**
- * Enable Auth for a project = create briven-engine tenant island on Doltgres.
- * Idempotent. Re-enables if previously soft-disabled.
- */
- export async function enableBrivenEngineAuth(projectId: string): Promise<{
- ok: boolean;
- engine: 'briven-engine';
- projectId: string;
- tenantId: string;
- authEnabled: boolean;
- created: boolean;
- message?: string;
- storage: 'doltgres';
- }> {
- const result = await ensureBrivenEngineTenant(projectId);
- if (result.ok) {
- try {
- const pool = getEnginePool();
- // Clear soft-disable so Auth is on again (users/data stay intact).
- await pool.query(
- `UPDATE be_tenants SET disabled_at = NULL
- WHERE tenant_id = $1 OR project_id = $2`,
- [result.tenantId, result.projectId],
- );
- } catch {
- /* column may not exist yet on very old engines — treat as enabled */
- }
- }
- return {
- ok: result.ok,
- engine: 'briven-engine',
- projectId: result.projectId,
- tenantId: result.tenantId,
- authEnabled: result.ok,
- created: result.created,
- message: result.message,
- storage: 'doltgres',
- };
- }
- /**
- * Turn Auth off for a project without deleting end-users or credentials.
- * Soft-disable: tenant stays, disabled_at is set; app login should treat Auth as off.
- */
- export async function disableBrivenEngineAuth(projectId: string): Promise<{
- ok: boolean;
- engine: 'briven-engine';
- projectId: string;
- tenantId: string;
- authEnabled: boolean;
- message?: string;
- storage: 'doltgres';
- }> {
- const map = mapProjectToAuthCore(projectId);
- const base = {
- engine: 'briven-engine' as const,
- storage: 'doltgres' as const,
- projectId: map.projectId,
- tenantId: map.tenantId,
- };
- if (!isAuthCoreInitialized()) {
- return {
- ...base,
- ok: false,
- authEnabled: false,
- message: 'briven-engine not ready on Doltgres',
- };
- }
- try {
- const pool = getEnginePool();
- // Ensure soft-disable column exists (older engines may not have run migration).
- try {
- await pool.query(`ALTER TABLE be_tenants ADD COLUMN disabled_at TIMESTAMPTZ`);
- } catch {
- /* already exists or unsupported — continue */
- }
- const existing = await pool.query(
- `SELECT tenant_id FROM be_tenants
- WHERE tenant_id = $1 OR project_id = $2
- LIMIT 1`,
- [map.tenantId, map.projectId],
- );
- if (!existing.rowCount) {
- return {
- ...base,
- ok: true,
- authEnabled: false,
- message: 'Auth was already off for this project',
- };
- }
- try {
- await pool.query(
- `UPDATE be_tenants SET disabled_at = NOW()
- WHERE tenant_id = $1 OR project_id = $2`,
- [map.tenantId, map.projectId],
- );
- } catch (err) {
- // Fallback if disabled_at column missing: leave row (still "on") and report.
- const message = err instanceof Error ? err.message : String(err);
- return {
- ...base,
- ok: false,
- authEnabled: true,
- message: `could not disable Auth: ${message}`,
- };
- }
- return {
- ...base,
- ok: true,
- authEnabled: false,
- message:
- 'Auth disabled for this project. User data is kept — enable Auth again anytime.',
- };
- } catch (err) {
- return {
- ...base,
- ok: false,
- authEnabled: true,
- message: err instanceof Error ? err.message : String(err),
- };
- }
- }
- /**
- * Whether Auth is on for a project (tenant row exists and not soft-disabled).
- */
- export async function isBrivenEngineAuthEnabled(
- projectId: string,
- ): Promise<boolean> {
- if (!isAuthCoreInitialized()) return false;
- try {
- const map = mapProjectToAuthCore(projectId);
- const pool = getEnginePool();
- // Prefer disabled_at IS NULL; if column missing, any tenant row means on.
- try {
- const res = await pool.query(
- `SELECT 1 FROM be_tenants
- WHERE (tenant_id = $1 OR project_id = $2)
- AND disabled_at IS NULL
- LIMIT 1`,
- [map.tenantId, map.projectId],
- );
- return Boolean(res.rowCount && res.rowCount > 0);
- } catch {
- const res = await pool.query(
- `SELECT 1 FROM be_tenants
- WHERE tenant_id = $1 OR project_id = $2
- LIMIT 1`,
- [map.tenantId, map.projectId],
- );
- return Boolean(res.rowCount && res.rowCount > 0);
- }
- } catch {
- return false;
- }
- }
- function markEnabled(
- set: Set<string>,
- projectId: string | null | undefined,
- ): void {
- if (!projectId) return;
- set.add(projectId);
- set.add(projectId.toLowerCase());
- }
- /**
- * All projects the user can see + Auth on/off from briven-engine.
- */
- export async function listBrivenEngineWorkspace(
- userId: string,
- ): Promise<{ engine: 'briven-engine'; projects: BrivenEngineWorkspaceProject[] }> {
- const projects = await listProjectsForUser(userId);
- // Build maps first — Auth on = be_tenants row for that project's tenant_id.
- const maps = projects
- .map((p) => {
- try {
- return mapProjectToAuthCore(p.id);
- } catch {
- return null;
- }
- })
- .filter((m): m is NonNullable<typeof m> => m != null);
- const tenantToProject = new Map(maps.map((m) => [m.tenantId, m.projectId]));
- let enabledProjectIds = new Set<string>();
- if (isAuthCoreInitialized() && maps.length > 0) {
- try {
- const pool = getEnginePool();
- // Active Auth only: tenant row and not soft-disabled.
- let res;
- try {
- res = await pool.query(
- `SELECT project_id, tenant_id FROM be_tenants
- WHERE disabled_at IS NULL`,
- );
- } catch {
- res = await pool.query(`SELECT project_id, tenant_id FROM be_tenants`);
- }
- for (const row of res.rows as Array<{
- project_id: string;
- tenant_id: string;
- }>) {
- markEnabled(enabledProjectIds, row.project_id);
- const viaTenant = tenantToProject.get(row.tenant_id);
- markEnabled(enabledProjectIds, viaTenant);
- // Also match tenant_id → project when project_id column is stale/mismatched
- if (row.tenant_id.startsWith('proj-')) {
- const fromTenant = tenantToProject.get(row.tenant_id);
- markEnabled(enabledProjectIds, fromTenant);
- }
- }
- } catch {
- enabledProjectIds = new Set();
- }
- }
- const rows: BrivenEngineWorkspaceProject[] = await Promise.all(
- projects.map(async (p) => {
- const name = (p as { name?: string | null }).name?.trim() || p.slug;
- let tenantId: string | null = null;
- try {
- tenantId = mapProjectToAuthCore(p.id).tenantId;
- } catch {
- tenantId = null;
- }
- let authEnabled =
- enabledProjectIds.has(p.id) ||
- enabledProjectIds.has(p.id.toLowerCase());
- // Direct probe when batch missed (should be rare).
- if (!authEnabled && isAuthCoreInitialized()) {
- authEnabled = await isBrivenEngineAuthEnabled(p.id);
- }
- if (!authEnabled) {
- return {
- id: p.id,
- slug: p.slug,
- name,
- authEnabled: false,
- // Do not show mapped tenant as if Auth were on
- tenantId: null,
- providers: null,
- };
- }
- try {
- const config = await getBrivenEngineProjectConfig(p.id);
- return {
- id: p.id,
- slug: p.slug,
- name,
- authEnabled: true,
- tenantId: config.tenantId,
- providers: {
- emailPassword: config.recipes.emailPassword,
- magicLink: config.recipes.passwordless,
- emailOtp: config.recipes.passwordless,
- passkey: config.recipes.webauthn,
- },
- };
- } catch {
- return {
- id: p.id,
- slug: p.slug,
- name,
- authEnabled: true,
- tenantId,
- providers: {
- emailPassword: true,
- magicLink: true,
- emailOtp: true,
- passkey: true,
- },
- };
- }
- }),
- );
- return { engine: 'briven-engine', projects: rows };
- }
|