better-auth-studio 1.0.79-beta.2 → 1.0.79-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/routes.js CHANGED
@@ -252,7 +252,10 @@ async function findAuthConfigPath() {
252
252
  export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter, preloadedAuthOptions, accessConfig, authInstance) {
253
253
  const isSelfHosted = !!preloadedAdapter;
254
254
  const getAuthConfigSafe = async () => {
255
- if (isSelfHosted && preloadedAuthOptions) {
255
+ if (isSelfHosted) {
256
+ return preloadedAuthOptions || authConfig || null;
257
+ }
258
+ if (preloadedAuthOptions) {
256
259
  return preloadedAuthOptions;
257
260
  }
258
261
  try {
@@ -270,7 +273,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
270
273
  catch (_error) {
271
274
  // Ignors errors
272
275
  }
273
- return null;
276
+ return authConfig || null;
274
277
  };
275
278
  const router = Router();
276
279
  const base64UrlEncode = (value) => Buffer.from(value)
@@ -476,7 +479,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
476
479
  if (!email || !password) {
477
480
  return res.status(400).json({ success: false, message: 'Email and password required' });
478
481
  }
479
- const adapter = await getAuthAdapter();
482
+ const adapter = await getAuthAdapterWithConfig();
480
483
  let signInResult = null;
481
484
  let signInError = null;
482
485
  try {
@@ -527,7 +530,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
527
530
  if (!allowedRoles.includes(user.role)) {
528
531
  return res.status(403).json({
529
532
  success: false,
530
- message: `Access denied. Required role: ${allowedRoles.join(' or ')}`,
533
+ message: `Access denied.`,
531
534
  userRole: user.role || 'none',
532
535
  });
533
536
  }
@@ -576,7 +579,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
576
579
  if (!allowedRoles.includes(user.role)) {
577
580
  return res.status(403).json({
578
581
  success: false,
579
- message: `Access denied. Required role: ${allowedRoles.join(' or ')}`,
582
+ message: `Access denied.`,
580
583
  userRole: user.role || 'none',
581
584
  });
582
585
  }
@@ -892,7 +895,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
892
895
  });
893
896
  router.get('/api/stats', async (_req, res) => {
894
897
  try {
895
- const stats = await getAuthData(authConfig, 'stats', undefined, configPath);
898
+ const stats = await getAuthData(authConfig, 'stats', undefined, configPath, preloadedAdapter);
896
899
  res.json(stats);
897
900
  }
898
901
  catch (_error) {
@@ -907,7 +910,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
907
910
  type: type,
908
911
  from: from,
909
912
  to: to,
910
- }, configPath);
913
+ }, configPath, preloadedAdapter);
911
914
  res.json(analytics);
912
915
  }
913
916
  catch (_error) {
@@ -924,19 +927,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
924
927
  let organizationPluginEnabled = false;
925
928
  let teamsPluginEnabled = false;
926
929
  try {
927
- let betterAuthConfig = preloadedAuthOptions;
928
- if (!betterAuthConfig && !isSelfHosted) {
929
- const authConfigPath = configPath || (await findAuthConfigPath());
930
- if (authConfigPath) {
931
- const { getConfig } = await import('./config.js');
932
- betterAuthConfig = await getConfig({
933
- cwd: process.cwd(),
934
- configPath: authConfigPath,
935
- shouldThrowOnError: false,
936
- noCache: true, // Disable cache for real-time plugin checks
937
- });
938
- }
939
- }
930
+ const betterAuthConfig = preloadedAuthOptions || (await getAuthConfigSafe());
940
931
  if (betterAuthConfig) {
941
932
  const plugins = betterAuthConfig.plugins || [];
942
933
  const organizationPlugin = plugins.find((plugin) => plugin.id === 'organization');
@@ -1470,7 +1461,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1470
1461
  }
1471
1462
  }
1472
1463
  catch (_adapterError) { }
1473
- const result = await getAuthData(authConfig, 'users', { page, limit, search }, configPath);
1464
+ const result = await getAuthData(authConfig, 'users', { page, limit, search }, configPath, preloadedAdapter);
1474
1465
  const transformedUsers = (result.data || []).map((user) => ({
1475
1466
  id: user.id,
1476
1467
  email: user.email,
@@ -1495,7 +1486,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1495
1486
  try {
1496
1487
  const page = parseInt(req.query.page, 10) || 1;
1497
1488
  const limit = parseInt(req.query.limit, 10) || 20;
1498
- const sessions = await getAuthData(authConfig, 'sessions', { page, limit }, configPath);
1489
+ const sessions = await getAuthData(authConfig, 'sessions', { page, limit }, configPath, preloadedAdapter);
1499
1490
  res.json(sessions);
1500
1491
  }
1501
1492
  catch (_error) {
@@ -1504,7 +1495,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1504
1495
  });
1505
1496
  router.get('/api/providers', async (_req, res) => {
1506
1497
  try {
1507
- const providers = await getAuthData(authConfig, 'providers', undefined, configPath);
1498
+ const providers = await getAuthData(authConfig, 'providers', undefined, configPath, preloadedAdapter);
1508
1499
  res.json(providers);
1509
1500
  }
1510
1501
  catch (_error) {
@@ -1514,7 +1505,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1514
1505
  router.delete('/api/users/:id', async (req, res) => {
1515
1506
  try {
1516
1507
  const { id } = req.params;
1517
- await getAuthData(authConfig, 'deleteUser', { id }, configPath);
1508
+ await getAuthData(authConfig, 'deleteUser', { id }, configPath, preloadedAdapter);
1518
1509
  res.json({ success: true });
1519
1510
  }
1520
1511
  catch (_error) {
@@ -1523,60 +1514,82 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1523
1514
  });
1524
1515
  router.get('/api/plugins', async (_req, res) => {
1525
1516
  try {
1526
- const authConfigPath = configPath
1527
- ? join(process.cwd(), configPath)
1528
- : await findAuthConfigPath();
1529
- if (!authConfigPath) {
1517
+ const betterAuthConfig = preloadedAuthOptions || (await getAuthConfigSafe());
1518
+ if (betterAuthConfig) {
1519
+ const plugins = betterAuthConfig.plugins || [];
1520
+ const pluginInfo = plugins.map((plugin) => ({
1521
+ id: plugin.id,
1522
+ name: plugin.name || plugin.id,
1523
+ description: plugin.description || `${plugin.id} plugin for Better Auth`,
1524
+ enabled: true,
1525
+ }));
1530
1526
  return res.json({
1531
- plugins: [],
1532
- error: 'No auth config found',
1533
- configPath: null,
1527
+ plugins: pluginInfo,
1528
+ configPath: isSelfHosted ? null : configPath || null,
1529
+ totalPlugins: pluginInfo.length,
1534
1530
  });
1535
1531
  }
1536
- try {
1537
- let authModule;
1538
- try {
1539
- authModule = await safeImportAuthConfig(authConfigPath, true); // Disable cache for real-time plugin checks
1532
+ if (!isSelfHosted) {
1533
+ const authConfigPath = configPath
1534
+ ? join(process.cwd(), configPath)
1535
+ : await findAuthConfigPath();
1536
+ if (!authConfigPath) {
1537
+ return res.json({
1538
+ plugins: [],
1539
+ error: 'No auth config found',
1540
+ configPath: null,
1541
+ });
1540
1542
  }
1541
- catch (_importError) {
1542
- const content = readFileSync(authConfigPath, 'utf-8');
1543
- authModule = {
1544
- auth: {
1545
- options: {
1546
- _content: content,
1547
- plugins: [],
1543
+ try {
1544
+ let authModule;
1545
+ try {
1546
+ authModule = await safeImportAuthConfig(authConfigPath, true); // Disable cache for real-time plugin checks
1547
+ }
1548
+ catch (_importError) {
1549
+ const content = readFileSync(authConfigPath, 'utf-8');
1550
+ authModule = {
1551
+ auth: {
1552
+ options: {
1553
+ _content: content,
1554
+ plugins: [],
1555
+ },
1548
1556
  },
1549
- },
1550
- };
1557
+ };
1558
+ }
1559
+ const auth = authModule.auth || authModule.default;
1560
+ if (!auth) {
1561
+ return res.json({
1562
+ plugins: [],
1563
+ error: 'No auth export found',
1564
+ configPath: authConfigPath,
1565
+ });
1566
+ }
1567
+ const plugins = auth.options?.plugins || [];
1568
+ const pluginInfo = plugins.map((plugin) => ({
1569
+ id: plugin.id,
1570
+ name: plugin.name || plugin.id,
1571
+ description: plugin.description || `${plugin.id} plugin for Better Auth`,
1572
+ enabled: true,
1573
+ }));
1574
+ return res.json({
1575
+ plugins: pluginInfo,
1576
+ configPath: authConfigPath,
1577
+ totalPlugins: pluginInfo.length,
1578
+ });
1551
1579
  }
1552
- const auth = authModule.auth || authModule.default;
1553
- if (!auth) {
1580
+ catch (_error) {
1554
1581
  return res.json({
1555
1582
  plugins: [],
1556
- error: 'No auth export found',
1583
+ error: 'Failed to load auth config - import failed and regex extraction unavailable',
1557
1584
  configPath: authConfigPath,
1558
1585
  });
1559
1586
  }
1560
- const plugins = auth.options?.plugins || [];
1561
- const pluginInfo = plugins.map((plugin) => ({
1562
- id: plugin.id,
1563
- name: plugin.name || plugin.id,
1564
- description: plugin.description || `${plugin.id} plugin for Better Auth`,
1565
- enabled: true,
1566
- }));
1567
- res.json({
1568
- plugins: pluginInfo,
1569
- configPath: authConfigPath,
1570
- totalPlugins: pluginInfo.length,
1571
- });
1572
- }
1573
- catch (_error) {
1574
- res.json({
1575
- plugins: [],
1576
- error: 'Failed to load auth config - import failed and regex extraction unavailable',
1577
- configPath: authConfigPath,
1578
- });
1579
1587
  }
1588
+ return res.json({
1589
+ plugins: [],
1590
+ error: 'No auth config found',
1591
+ configPath: null,
1592
+ });
1580
1593
  }
1581
1594
  catch (_error) {
1582
1595
  res.status(500).json({ error: 'Failed to fetch plugins' });
@@ -1584,6 +1597,13 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1584
1597
  });
1585
1598
  router.get('/api/database/info', async (_req, res) => {
1586
1599
  try {
1600
+ if (isSelfHosted && preloadedAuthOptions) {
1601
+ const database = preloadedAuthOptions.database;
1602
+ return res.json({
1603
+ database: database,
1604
+ configPath: null,
1605
+ });
1606
+ }
1587
1607
  const authConfigPath = configPath || (await findAuthConfigPath());
1588
1608
  if (!authConfigPath) {
1589
1609
  return res.json({
@@ -1977,7 +1997,6 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1977
1997
  });
1978
1998
  }
1979
1999
  });
1980
- // Database Detection endpoint - Auto-detect database from installed packages
1981
2000
  router.get('/api/database/detect', async (_req, res) => {
1982
2001
  try {
1983
2002
  const detectedDb = await detectDatabaseWithDialect();
@@ -2046,7 +2065,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2046
2065
  });
2047
2066
  router.post('/api/admin/ban-user', async (req, res) => {
2048
2067
  try {
2049
- const auth = await getAuthConfigSafe();
2068
+ const auth = preloadedAuthOptions || (await getAuthConfigSafe());
2050
2069
  if (!auth) {
2051
2070
  return res.status(400).json({
2052
2071
  success: false,
@@ -2085,7 +2104,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2085
2104
  });
2086
2105
  router.post('/api/admin/unban-user', async (req, res) => {
2087
2106
  try {
2088
- const auth = await getAuthConfigSafe();
2107
+ const auth = preloadedAuthOptions || (await getAuthConfigSafe());
2089
2108
  if (!auth) {
2090
2109
  return res.status(400).json({
2091
2110
  success: false,
@@ -2124,7 +2143,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2124
2143
  });
2125
2144
  router.get('/api/admin/status', async (_req, res) => {
2126
2145
  try {
2127
- const betterAuthConfig = await getAuthConfigSafe();
2146
+ const betterAuthConfig = preloadedAuthOptions || (await getAuthConfigSafe());
2128
2147
  if (!betterAuthConfig) {
2129
2148
  return res.json({
2130
2149
  enabled: false,
@@ -2148,8 +2167,6 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2148
2167
  });
2149
2168
  }
2150
2169
  });
2151
- // Database Schema Visualization endpoint
2152
- // Now uses loadContextTables to dynamically load schema from Better Auth context
2153
2170
  const CONTEXT_CORE_TABLES = new Set(['user', 'session', 'account', 'verification']);
2154
2171
  async function resolveSchemaConfigPath() {
2155
2172
  if (configPath) {
@@ -2159,16 +2176,23 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2159
2176
  }
2160
2177
  async function loadContextTables() {
2161
2178
  try {
2179
+ if (isSelfHosted && authInstance?.$context) {
2180
+ try {
2181
+ const context = await authInstance.$context;
2182
+ return context?.tables || null;
2183
+ }
2184
+ catch (_error) { }
2185
+ }
2162
2186
  const authConfigPath = await resolveSchemaConfigPath();
2163
2187
  if (!authConfigPath) {
2164
2188
  return null;
2165
2189
  }
2166
2190
  const authModule = await safeImportAuthConfig(authConfigPath);
2167
- const authInstance = authModule?.auth || authModule?.default;
2168
- if (!authInstance?.$context) {
2191
+ const fileAuthInstance = authModule?.auth || authModule?.default;
2192
+ if (!fileAuthInstance?.$context) {
2169
2193
  return null;
2170
2194
  }
2171
- const context = await authInstance.$context;
2195
+ const context = await fileAuthInstance.$context;
2172
2196
  return context?.tables || null;
2173
2197
  }
2174
2198
  catch (_error) {
@@ -2292,14 +2316,11 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2292
2316
  if (!schema) {
2293
2317
  return null;
2294
2318
  }
2295
- // Filter by plugin origin if plugins are specified
2296
2319
  if (selectedPlugins && selectedPlugins.length > 0) {
2297
2320
  schema.tables = schema.tables.filter((table) => {
2298
- // Include core tables
2299
2321
  if (CONTEXT_CORE_TABLES.has(table.name)) {
2300
2322
  return true;
2301
2323
  }
2302
- // Include tables from selected plugins
2303
2324
  return selectedPlugins.includes(table.origin);
2304
2325
  });
2305
2326
  }
@@ -2351,12 +2372,13 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2351
2372
  });
2352
2373
  router.get('/api/plugins/teams/status', async (_req, res) => {
2353
2374
  try {
2354
- const betterAuthConfig = await getAuthConfigSafe();
2375
+ // Prioritize preloadedAuthOptions for self-hosted mode
2376
+ const betterAuthConfig = preloadedAuthOptions || (await getAuthConfigSafe());
2355
2377
  if (!betterAuthConfig) {
2356
2378
  return res.json({
2357
2379
  enabled: false,
2358
2380
  error: 'No auth config found',
2359
- configPath: null,
2381
+ configPath: isSelfHosted ? null : configPath || null,
2360
2382
  });
2361
2383
  }
2362
2384
  const plugins = betterAuthConfig.plugins || [];
@@ -2365,14 +2387,14 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2365
2387
  const teamsEnabled = organizationPlugin.options?.teams?.enabled === true;
2366
2388
  return res.json({
2367
2389
  enabled: teamsEnabled,
2368
- configPath: configPath || null,
2390
+ configPath: isSelfHosted ? null : configPath || null,
2369
2391
  organizationPlugin: organizationPlugin || null,
2370
2392
  });
2371
2393
  }
2372
2394
  else {
2373
2395
  return res.json({
2374
2396
  enabled: false,
2375
- configPath: configPath || null,
2397
+ configPath: isSelfHosted ? null : configPath || null,
2376
2398
  organizationPlugin: null,
2377
2399
  error: 'Organization plugin not found',
2378
2400
  });
@@ -2730,6 +2752,67 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2730
2752
  res.status(500).json({ error: 'Failed to create invitation' });
2731
2753
  }
2732
2754
  });
2755
+ router.get('/api/teams', async (req, res) => {
2756
+ try {
2757
+ const adapter = await getAuthAdapterWithConfig();
2758
+ if (adapter && typeof adapter.findMany === 'function') {
2759
+ try {
2760
+ const teams = await adapter.findMany({
2761
+ model: 'team',
2762
+ limit: 10000,
2763
+ });
2764
+ const transformedTeams = await Promise.all((teams || []).map(async (team) => {
2765
+ if (!adapter.findMany) {
2766
+ return null;
2767
+ }
2768
+ // Fetch organization details
2769
+ let organization = null;
2770
+ try {
2771
+ if (team.organizationId) {
2772
+ const orgs = await adapter.findMany({
2773
+ model: 'organization',
2774
+ where: [{ field: 'id', value: team.organizationId }],
2775
+ limit: 1,
2776
+ });
2777
+ organization = orgs && orgs.length > 0 ? orgs[0] : null;
2778
+ }
2779
+ }
2780
+ catch (_error) { }
2781
+ // Fetch team members count
2782
+ const teamMembers = await adapter.findMany({
2783
+ model: 'teamMember',
2784
+ where: [{ field: 'teamId', value: team.id }],
2785
+ limit: 10000,
2786
+ });
2787
+ return {
2788
+ id: team.id,
2789
+ name: team.name,
2790
+ organizationId: team.organizationId,
2791
+ metadata: team.metadata,
2792
+ createdAt: team.createdAt,
2793
+ updatedAt: team.updatedAt,
2794
+ memberCount: teamMembers ? teamMembers.length : 0,
2795
+ organization: organization
2796
+ ? {
2797
+ id: organization.id,
2798
+ name: organization.name,
2799
+ slug: organization.slug,
2800
+ }
2801
+ : null,
2802
+ };
2803
+ }));
2804
+ const validTeams = transformedTeams.filter((team) => team !== null);
2805
+ res.json({ success: true, teams: validTeams });
2806
+ return;
2807
+ }
2808
+ catch (_error) { }
2809
+ }
2810
+ res.json({ success: true, teams: [] });
2811
+ }
2812
+ catch (_error) {
2813
+ res.status(500).json({ error: 'Failed to fetch teams' });
2814
+ }
2815
+ });
2733
2816
  router.get('/api/organizations/:orgId/teams', async (req, res) => {
2734
2817
  try {
2735
2818
  const { orgId } = req.params;
@@ -2973,7 +3056,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2973
3056
  });
2974
3057
  router.get('/api/plugins/organization/status', async (_req, res) => {
2975
3058
  try {
2976
- const betterAuthConfig = await getAuthConfigSafe();
3059
+ const betterAuthConfig = preloadedAuthOptions || (await getAuthConfigSafe());
2977
3060
  if (!betterAuthConfig) {
2978
3061
  return res.json({
2979
3062
  enabled: false,
@@ -3105,7 +3188,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
3105
3188
  try {
3106
3189
  const { id } = req.params;
3107
3190
  const userData = req.body;
3108
- const updatedUser = await getAuthData(authConfig, 'updateUser', { id, userData }, configPath);
3191
+ const updatedUser = await getAuthData(authConfig, 'updateUser', { id, userData }, configPath, preloadedAdapter);
3109
3192
  res.json({ success: true, user: updatedUser });
3110
3193
  }
3111
3194
  catch (_error) {