dashboard.ts156 lines · main
1/**
2 * Yellow Authentication dashboard data — all from Doltgres briven-engine.
3 */
4
5import { getEnginePool } from './db.js';
6import {
7 BRIVEN_ENGINE_ID,
8 getAuthCoreRecipeMeta,
9 isAuthCoreInitialized,
10 probeBrivenEngine,
11} from './engine.js';
12import { BRIVEN_ENGINE_RECIPE_CATALOG, getRecipePhase } from './recipes.js';
13import { listBrivenEngineUsers } from './users.js';
14
15export type BrivenEngineDashboard = {
16 engine: typeof BRIVEN_ENGINE_ID;
17 storage: 'doltgres';
18 database: 'briven_engine';
19 ok: boolean;
20 message: string;
21 counts: {
22 users: number;
23 sessions: number;
24 tenants: number;
25 thirdPartyLinks: number;
26 passwordlessCodesActive: number;
27 };
28 methods: {
29 emailPassword: boolean;
30 passwordlessEmail: boolean;
31 passwordlessSms: boolean;
32 google: boolean;
33 github: boolean;
34 webauthn: boolean;
35 mfa: boolean;
36 };
37 recentUsers: Array<{
38 id: string;
39 emails: string[];
40 phoneNumbers: string[];
41 tenantId?: string;
42 timeJoined: number;
43 }>;
44 recipesLoaded: string[];
45 recipePhase: number | null;
46};
47
48export async function getBrivenEngineDashboard(opts?: {
49 tenantId?: string;
50 projectId?: string;
51}): Promise<BrivenEngineDashboard & { projectId?: string; tenantId?: string }> {
52 const probe = await probeBrivenEngine();
53 const meta = getAuthCoreRecipeMeta();
54 const phase = getRecipePhase();
55 const names = new Set(meta.names.length ? meta.names : BRIVEN_ENGINE_RECIPE_CATALOG.filter((r) => r.phase <= phase).map((r) => r.id));
56
57 const baseMethods = {
58 emailPassword: names.has('emailpassword'),
59 passwordlessEmail: names.has('passwordless'),
60 passwordlessSms: names.has('passwordless'),
61 google: names.has('thirdparty'),
62 github: names.has('thirdparty'),
63 webauthn: names.has('webauthn'),
64 mfa: names.has('multifactorauth'),
65 };
66
67 if (!isAuthCoreInitialized() || !probe.ok) {
68 return {
69 engine: BRIVEN_ENGINE_ID,
70 storage: 'doltgres',
71 database: 'briven_engine',
72 ok: false,
73 message: probe.message,
74 counts: {
75 users: 0,
76 sessions: 0,
77 tenants: 0,
78 thirdPartyLinks: 0,
79 passwordlessCodesActive: 0,
80 },
81 methods: baseMethods,
82 recentUsers: [],
83 recipesLoaded: meta.names,
84 recipePhase: meta.phase,
85 projectId: opts?.projectId,
86 tenantId: opts?.tenantId,
87 };
88 }
89
90 const pool = getEnginePool();
91 const tid = opts?.tenantId;
92 const [usersC, sessionsC, tenantsC, linksC, codesC] = await Promise.all([
93 tid
94 ? pool.query(
95 `SELECT COUNT(*)::int AS n FROM be_users WHERE tenant_id = $1`,
96 [tid],
97 )
98 : pool.query(`SELECT COUNT(*)::int AS n FROM be_users`),
99 tid
100 ? pool.query(
101 `SELECT COUNT(*)::int AS n FROM be_sessions WHERE expires_at > NOW() AND tenant_id = $1`,
102 [tid],
103 )
104 : pool.query(
105 `SELECT COUNT(*)::int AS n FROM be_sessions WHERE expires_at > NOW()`,
106 ),
107 pool.query(`SELECT COUNT(*)::int AS n FROM be_tenants`),
108 tid
109 ? pool.query(
110 `SELECT COUNT(*)::int AS n FROM be_third_party_links WHERE tenant_id = $1`,
111 [tid],
112 )
113 : pool.query(`SELECT COUNT(*)::int AS n FROM be_third_party_links`),
114 tid
115 ? pool.query(
116 `SELECT COUNT(*)::int AS n FROM be_passwordless_codes WHERE expires_at > NOW() AND tenant_id = $1`,
117 [tid],
118 )
119 : pool.query(
120 `SELECT COUNT(*)::int AS n FROM be_passwordless_codes WHERE expires_at > NOW()`,
121 ),
122 ]);
123
124 const listed = await listBrivenEngineUsers({
125 limit: 20,
126 tenantId: tid,
127 });
128
129 return {
130 engine: BRIVEN_ENGINE_ID,
131 storage: 'doltgres',
132 database: 'briven_engine',
133 ok: true,
134 message: tid
135 ? 'dashboard data for one Auth project (tenant)'
136 : 'dashboard data from Doltgres',
137 counts: {
138 users: (usersC.rows[0] as { n: number }).n,
139 sessions: (sessionsC.rows[0] as { n: number }).n,
140 tenants: (tenantsC.rows[0] as { n: number }).n,
141 thirdPartyLinks: (linksC.rows[0] as { n: number }).n,
142 passwordlessCodesActive: (codesC.rows[0] as { n: number }).n,
143 },
144 methods: baseMethods,
145 recentUsers: listed.users.map((u) => ({
146 id: u.id,
147 emails: u.emails,
148 phoneNumbers: u.phoneNumbers,
149 timeJoined: u.timeJoined,
150 })),
151 recipesLoaded: meta.names,
152 recipePhase: meta.phase,
153 projectId: opts?.projectId,
154 tenantId: opts?.tenantId,
155 };
156}