better-auth-studio 1.0.79-beta.3 → 1.0.79-beta.31

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
@@ -7,6 +7,7 @@ import { hex } from '@better-auth/utils/hex';
7
7
  import { scryptAsync } from '@noble/hashes/scrypt.js';
8
8
  import { Router } from 'express';
9
9
  import { createJiti } from 'jiti';
10
+ import { createRequire } from 'module';
10
11
  import { createMockAccount, createMockSession, createMockUser, createMockVerification, getAuthAdapter, } from './auth-adapter.js';
11
12
  import { possiblePaths } from './config.js';
12
13
  import { getAuthData } from './data.js';
@@ -57,6 +58,20 @@ function getStudioVersion() {
57
58
  const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
58
59
  return packageJson.version || '1.0.0';
59
60
  }
61
+ const nodeModulesPath = join(process.cwd(), 'node_modules/better-auth-studio/package.json');
62
+ if (existsSync(nodeModulesPath)) {
63
+ const packageJson = JSON.parse(readFileSync(nodeModulesPath, 'utf-8'));
64
+ return packageJson.version || '1.0.0';
65
+ }
66
+ try {
67
+ const require = createRequire(import.meta.url);
68
+ const resolvedPath = require.resolve('better-auth-studio/package.json');
69
+ if (existsSync(resolvedPath)) {
70
+ const packageJson = JSON.parse(readFileSync(resolvedPath, 'utf-8'));
71
+ return packageJson.version || '1.0.0';
72
+ }
73
+ }
74
+ catch (_resolveError) { }
60
75
  }
61
76
  catch (_error) { }
62
77
  return '1.0.0';
@@ -252,7 +267,10 @@ async function findAuthConfigPath() {
252
267
  export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter, preloadedAuthOptions, accessConfig, authInstance) {
253
268
  const isSelfHosted = !!preloadedAdapter;
254
269
  const getAuthConfigSafe = async () => {
255
- if (isSelfHosted && preloadedAuthOptions) {
270
+ if (isSelfHosted) {
271
+ return preloadedAuthOptions || authConfig || null;
272
+ }
273
+ if (preloadedAuthOptions) {
256
274
  return preloadedAuthOptions;
257
275
  }
258
276
  try {
@@ -270,7 +288,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
270
288
  catch (_error) {
271
289
  // Ignors errors
272
290
  }
273
- return null;
291
+ return authConfig || null;
274
292
  };
275
293
  const router = Router();
276
294
  const base64UrlEncode = (value) => Buffer.from(value)
@@ -476,7 +494,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
476
494
  if (!email || !password) {
477
495
  return res.status(400).json({ success: false, message: 'Email and password required' });
478
496
  }
479
- const adapter = await getAuthAdapter();
497
+ const adapter = await getAuthAdapterWithConfig();
480
498
  let signInResult = null;
481
499
  let signInError = null;
482
500
  try {
@@ -517,7 +535,6 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
517
535
  message: 'Password not configured. Please reset your password.',
518
536
  });
519
537
  }
520
- console.log({ users, credentialAccount });
521
538
  const isValidPassword = await verifyPassword(password, credentialAccount.password);
522
539
  if (!isValidPassword) {
523
540
  return res.status(401).json({ success: false, message: 'Invalid credentials' });
@@ -528,7 +545,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
528
545
  if (!allowedRoles.includes(user.role)) {
529
546
  return res.status(403).json({
530
547
  success: false,
531
- message: `Access denied. Required role: ${allowedRoles.join(' or ')}`,
548
+ message: `Access denied.`,
532
549
  userRole: user.role || 'none',
533
550
  });
534
551
  }
@@ -577,7 +594,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
577
594
  if (!allowedRoles.includes(user.role)) {
578
595
  return res.status(403).json({
579
596
  success: false,
580
- message: `Access denied. Required role: ${allowedRoles.join(' or ')}`,
597
+ message: `Access denied.`,
581
598
  userRole: user.role || 'none',
582
599
  });
583
600
  }
@@ -807,6 +824,21 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
807
824
  }
808
825
  }
809
826
  catch (_error) { }
827
+ // Get studio version - always fetch from npm registry for latest version
828
+ let studioVersion = '1.0.0';
829
+ try {
830
+ const response = await fetch('https://registry.npmjs.org/better-auth-studio/latest', {
831
+ signal: AbortSignal.timeout(2000), // 2 second timeout
832
+ });
833
+ if (response.ok) {
834
+ const data = (await response.json());
835
+ studioVersion = data.version || '1.0.0';
836
+ }
837
+ }
838
+ catch (_npmError) {
839
+ // Fallback to local version if npm fetch fails
840
+ studioVersion = getStudioVersion();
841
+ }
810
842
  if (databaseType === 'unknown' && !isSelfHosted) {
811
843
  const authConfigPath = configPath || (await findAuthConfigPath());
812
844
  if (authConfigPath) {
@@ -883,7 +915,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
883
915
  disabledPaths: effectiveConfig.disabledPaths || [],
884
916
  telemetry: effectiveConfig.telemetry,
885
917
  studio: {
886
- version: getStudioVersion(),
918
+ version: studioVersion,
887
919
  nodeVersion: process.version,
888
920
  platform: process.platform,
889
921
  uptime: process.uptime(),
@@ -893,7 +925,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
893
925
  });
894
926
  router.get('/api/stats', async (_req, res) => {
895
927
  try {
896
- const stats = await getAuthData(authConfig, 'stats', undefined, configPath);
928
+ const stats = await getAuthData(authConfig, 'stats', undefined, configPath, preloadedAdapter);
897
929
  res.json(stats);
898
930
  }
899
931
  catch (_error) {
@@ -908,7 +940,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
908
940
  type: type,
909
941
  from: from,
910
942
  to: to,
911
- }, configPath);
943
+ }, configPath, preloadedAdapter);
912
944
  res.json(analytics);
913
945
  }
914
946
  catch (_error) {
@@ -925,26 +957,17 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
925
957
  let organizationPluginEnabled = false;
926
958
  let teamsPluginEnabled = false;
927
959
  try {
928
- let betterAuthConfig = preloadedAuthOptions;
929
- if (!betterAuthConfig && !isSelfHosted) {
930
- const authConfigPath = configPath || (await findAuthConfigPath());
931
- if (authConfigPath) {
932
- const { getConfig } = await import('./config.js');
933
- betterAuthConfig = await getConfig({
934
- cwd: process.cwd(),
935
- configPath: authConfigPath,
936
- shouldThrowOnError: false,
937
- noCache: true, // Disable cache for real-time plugin checks
938
- });
939
- }
940
- }
960
+ const betterAuthConfig = preloadedAuthOptions || (await getAuthConfigSafe());
941
961
  if (betterAuthConfig) {
942
962
  const plugins = betterAuthConfig.plugins || [];
943
963
  const organizationPlugin = plugins.find((plugin) => plugin.id === 'organization');
944
964
  organizationPluginEnabled = !!organizationPlugin;
945
- teamsPluginEnabled = !!organizationPlugin?.options?.teams?.enabled;
946
965
  if (organizationPlugin) {
947
- teamsPluginEnabled = organizationPlugin.options?.teams?.enabled === true;
966
+ teamsPluginEnabled =
967
+ organizationPlugin.options?.teams?.enabled === true ||
968
+ organizationPlugin.teams?.enabled === true ||
969
+ organizationPlugin.config?.teams?.enabled === true ||
970
+ false;
948
971
  }
949
972
  }
950
973
  }
@@ -1471,7 +1494,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1471
1494
  }
1472
1495
  }
1473
1496
  catch (_adapterError) { }
1474
- const result = await getAuthData(authConfig, 'users', { page, limit, search }, configPath);
1497
+ const result = await getAuthData(authConfig, 'users', { page, limit, search }, configPath, preloadedAdapter);
1475
1498
  const transformedUsers = (result.data || []).map((user) => ({
1476
1499
  id: user.id,
1477
1500
  email: user.email,
@@ -1496,7 +1519,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1496
1519
  try {
1497
1520
  const page = parseInt(req.query.page, 10) || 1;
1498
1521
  const limit = parseInt(req.query.limit, 10) || 20;
1499
- const sessions = await getAuthData(authConfig, 'sessions', { page, limit }, configPath);
1522
+ const sessions = await getAuthData(authConfig, 'sessions', { page, limit }, configPath, preloadedAdapter);
1500
1523
  res.json(sessions);
1501
1524
  }
1502
1525
  catch (_error) {
@@ -1505,7 +1528,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1505
1528
  });
1506
1529
  router.get('/api/providers', async (_req, res) => {
1507
1530
  try {
1508
- const providers = await getAuthData(authConfig, 'providers', undefined, configPath);
1531
+ const providers = await getAuthData(authConfig, 'providers', undefined, configPath, preloadedAdapter);
1509
1532
  res.json(providers);
1510
1533
  }
1511
1534
  catch (_error) {
@@ -1515,7 +1538,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1515
1538
  router.delete('/api/users/:id', async (req, res) => {
1516
1539
  try {
1517
1540
  const { id } = req.params;
1518
- await getAuthData(authConfig, 'deleteUser', { id }, configPath);
1541
+ await getAuthData(authConfig, 'deleteUser', { id }, configPath, preloadedAdapter);
1519
1542
  res.json({ success: true });
1520
1543
  }
1521
1544
  catch (_error) {
@@ -1524,60 +1547,82 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1524
1547
  });
1525
1548
  router.get('/api/plugins', async (_req, res) => {
1526
1549
  try {
1527
- const authConfigPath = configPath
1528
- ? join(process.cwd(), configPath)
1529
- : await findAuthConfigPath();
1530
- if (!authConfigPath) {
1550
+ const betterAuthConfig = preloadedAuthOptions || (await getAuthConfigSafe());
1551
+ if (betterAuthConfig) {
1552
+ const plugins = betterAuthConfig.plugins || [];
1553
+ const pluginInfo = plugins.map((plugin) => ({
1554
+ id: plugin.id,
1555
+ name: plugin.name || plugin.id,
1556
+ description: plugin.description || `${plugin.id} plugin for Better Auth`,
1557
+ enabled: true,
1558
+ }));
1531
1559
  return res.json({
1532
- plugins: [],
1533
- error: 'No auth config found',
1534
- configPath: null,
1560
+ plugins: pluginInfo,
1561
+ configPath: isSelfHosted ? null : configPath || null,
1562
+ totalPlugins: pluginInfo.length,
1535
1563
  });
1536
1564
  }
1537
- try {
1538
- let authModule;
1539
- try {
1540
- authModule = await safeImportAuthConfig(authConfigPath, true); // Disable cache for real-time plugin checks
1565
+ if (!isSelfHosted) {
1566
+ const authConfigPath = configPath
1567
+ ? join(process.cwd(), configPath)
1568
+ : await findAuthConfigPath();
1569
+ if (!authConfigPath) {
1570
+ return res.json({
1571
+ plugins: [],
1572
+ error: 'No auth config found',
1573
+ configPath: null,
1574
+ });
1541
1575
  }
1542
- catch (_importError) {
1543
- const content = readFileSync(authConfigPath, 'utf-8');
1544
- authModule = {
1545
- auth: {
1546
- options: {
1547
- _content: content,
1548
- plugins: [],
1576
+ try {
1577
+ let authModule;
1578
+ try {
1579
+ authModule = await safeImportAuthConfig(authConfigPath, true); // Disable cache for real-time plugin checks
1580
+ }
1581
+ catch (_importError) {
1582
+ const content = readFileSync(authConfigPath, 'utf-8');
1583
+ authModule = {
1584
+ auth: {
1585
+ options: {
1586
+ _content: content,
1587
+ plugins: [],
1588
+ },
1549
1589
  },
1550
- },
1551
- };
1590
+ };
1591
+ }
1592
+ const auth = authModule.auth || authModule.default;
1593
+ if (!auth) {
1594
+ return res.json({
1595
+ plugins: [],
1596
+ error: 'No auth export found',
1597
+ configPath: authConfigPath,
1598
+ });
1599
+ }
1600
+ const plugins = auth.options?.plugins || [];
1601
+ const pluginInfo = plugins.map((plugin) => ({
1602
+ id: plugin.id,
1603
+ name: plugin.name || plugin.id,
1604
+ description: plugin.description || `${plugin.id} plugin for Better Auth`,
1605
+ enabled: true,
1606
+ }));
1607
+ return res.json({
1608
+ plugins: pluginInfo,
1609
+ configPath: authConfigPath,
1610
+ totalPlugins: pluginInfo.length,
1611
+ });
1552
1612
  }
1553
- const auth = authModule.auth || authModule.default;
1554
- if (!auth) {
1613
+ catch (_error) {
1555
1614
  return res.json({
1556
1615
  plugins: [],
1557
- error: 'No auth export found',
1616
+ error: 'Failed to load auth config - import failed and regex extraction unavailable',
1558
1617
  configPath: authConfigPath,
1559
1618
  });
1560
1619
  }
1561
- const plugins = auth.options?.plugins || [];
1562
- const pluginInfo = plugins.map((plugin) => ({
1563
- id: plugin.id,
1564
- name: plugin.name || plugin.id,
1565
- description: plugin.description || `${plugin.id} plugin for Better Auth`,
1566
- enabled: true,
1567
- }));
1568
- res.json({
1569
- plugins: pluginInfo,
1570
- configPath: authConfigPath,
1571
- totalPlugins: pluginInfo.length,
1572
- });
1573
- }
1574
- catch (_error) {
1575
- res.json({
1576
- plugins: [],
1577
- error: 'Failed to load auth config - import failed and regex extraction unavailable',
1578
- configPath: authConfigPath,
1579
- });
1580
1620
  }
1621
+ return res.json({
1622
+ plugins: [],
1623
+ error: 'No auth config found',
1624
+ configPath: null,
1625
+ });
1581
1626
  }
1582
1627
  catch (_error) {
1583
1628
  res.status(500).json({ error: 'Failed to fetch plugins' });
@@ -1585,6 +1630,13 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1585
1630
  });
1586
1631
  router.get('/api/database/info', async (_req, res) => {
1587
1632
  try {
1633
+ if (isSelfHosted && preloadedAuthOptions) {
1634
+ const database = preloadedAuthOptions.database;
1635
+ return res.json({
1636
+ database: database,
1637
+ configPath: null,
1638
+ });
1639
+ }
1588
1640
  const authConfigPath = configPath || (await findAuthConfigPath());
1589
1641
  if (!authConfigPath) {
1590
1642
  return res.json({
@@ -1978,7 +2030,6 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
1978
2030
  });
1979
2031
  }
1980
2032
  });
1981
- // Database Detection endpoint - Auto-detect database from installed packages
1982
2033
  router.get('/api/database/detect', async (_req, res) => {
1983
2034
  try {
1984
2035
  const detectedDb = await detectDatabaseWithDialect();
@@ -2047,7 +2098,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2047
2098
  });
2048
2099
  router.post('/api/admin/ban-user', async (req, res) => {
2049
2100
  try {
2050
- const auth = await getAuthConfigSafe();
2101
+ const auth = preloadedAuthOptions || (await getAuthConfigSafe());
2051
2102
  if (!auth) {
2052
2103
  return res.status(400).json({
2053
2104
  success: false,
@@ -2086,7 +2137,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2086
2137
  });
2087
2138
  router.post('/api/admin/unban-user', async (req, res) => {
2088
2139
  try {
2089
- const auth = await getAuthConfigSafe();
2140
+ const auth = preloadedAuthOptions || (await getAuthConfigSafe());
2090
2141
  if (!auth) {
2091
2142
  return res.status(400).json({
2092
2143
  success: false,
@@ -2125,7 +2176,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2125
2176
  });
2126
2177
  router.get('/api/admin/status', async (_req, res) => {
2127
2178
  try {
2128
- const betterAuthConfig = await getAuthConfigSafe();
2179
+ const betterAuthConfig = preloadedAuthOptions || (await getAuthConfigSafe());
2129
2180
  if (!betterAuthConfig) {
2130
2181
  return res.json({
2131
2182
  enabled: false,
@@ -2149,8 +2200,6 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2149
2200
  });
2150
2201
  }
2151
2202
  });
2152
- // Database Schema Visualization endpoint
2153
- // Now uses loadContextTables to dynamically load schema from Better Auth context
2154
2203
  const CONTEXT_CORE_TABLES = new Set(['user', 'session', 'account', 'verification']);
2155
2204
  async function resolveSchemaConfigPath() {
2156
2205
  if (configPath) {
@@ -2160,16 +2209,23 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2160
2209
  }
2161
2210
  async function loadContextTables() {
2162
2211
  try {
2212
+ if (isSelfHosted && authInstance?.$context) {
2213
+ try {
2214
+ const context = await authInstance.$context;
2215
+ return context?.tables || null;
2216
+ }
2217
+ catch (_error) { }
2218
+ }
2163
2219
  const authConfigPath = await resolveSchemaConfigPath();
2164
2220
  if (!authConfigPath) {
2165
2221
  return null;
2166
2222
  }
2167
2223
  const authModule = await safeImportAuthConfig(authConfigPath);
2168
- const authInstance = authModule?.auth || authModule?.default;
2169
- if (!authInstance?.$context) {
2224
+ const fileAuthInstance = authModule?.auth || authModule?.default;
2225
+ if (!fileAuthInstance?.$context) {
2170
2226
  return null;
2171
2227
  }
2172
- const context = await authInstance.$context;
2228
+ const context = await fileAuthInstance.$context;
2173
2229
  return context?.tables || null;
2174
2230
  }
2175
2231
  catch (_error) {
@@ -2293,14 +2349,11 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2293
2349
  if (!schema) {
2294
2350
  return null;
2295
2351
  }
2296
- // Filter by plugin origin if plugins are specified
2297
2352
  if (selectedPlugins && selectedPlugins.length > 0) {
2298
2353
  schema.tables = schema.tables.filter((table) => {
2299
- // Include core tables
2300
2354
  if (CONTEXT_CORE_TABLES.has(table.name)) {
2301
2355
  return true;
2302
2356
  }
2303
- // Include tables from selected plugins
2304
2357
  return selectedPlugins.includes(table.origin);
2305
2358
  });
2306
2359
  }
@@ -2352,28 +2405,46 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2352
2405
  });
2353
2406
  router.get('/api/plugins/teams/status', async (_req, res) => {
2354
2407
  try {
2355
- const betterAuthConfig = await getAuthConfigSafe();
2408
+ const betterAuthConfig = preloadedAuthOptions || (await getAuthConfigSafe());
2356
2409
  if (!betterAuthConfig) {
2357
2410
  return res.json({
2358
2411
  enabled: false,
2359
2412
  error: 'No auth config found',
2360
- configPath: null,
2413
+ configPath: isSelfHosted ? null : configPath || null,
2361
2414
  });
2362
2415
  }
2363
2416
  const plugins = betterAuthConfig.plugins || [];
2364
2417
  const organizationPlugin = plugins.find((plugin) => plugin.id === 'organization');
2365
2418
  if (organizationPlugin) {
2366
- const teamsEnabled = organizationPlugin.options?.teams?.enabled === true;
2419
+ let teamsEnabled = false;
2420
+ if (organizationPlugin.options?.teams?.enabled === true) {
2421
+ teamsEnabled = true;
2422
+ }
2423
+ else if (organizationPlugin.teams?.enabled === true) {
2424
+ teamsEnabled = true;
2425
+ }
2426
+ else if (organizationPlugin.config?.teams?.enabled === true) {
2427
+ teamsEnabled = true;
2428
+ }
2429
+ else if (organizationPlugin.options?.teams &&
2430
+ typeof organizationPlugin.options.teams === 'object') {
2431
+ teamsEnabled = organizationPlugin.options.teams.enabled === true;
2432
+ }
2433
+ else if (organizationPlugin.teams && typeof organizationPlugin.teams === 'object') {
2434
+ teamsEnabled = organizationPlugin.teams.enabled === true;
2435
+ }
2436
+ const teamSchema = organizationPlugin.schema;
2437
+ teamsEnabled = 'team' in teamSchema;
2367
2438
  return res.json({
2368
2439
  enabled: teamsEnabled,
2369
- configPath: configPath || null,
2440
+ configPath: isSelfHosted ? null : configPath || null,
2370
2441
  organizationPlugin: organizationPlugin || null,
2371
2442
  });
2372
2443
  }
2373
2444
  else {
2374
2445
  return res.json({
2375
2446
  enabled: false,
2376
- configPath: configPath || null,
2447
+ configPath: isSelfHosted ? null : configPath || null,
2377
2448
  organizationPlugin: null,
2378
2449
  error: 'Organization plugin not found',
2379
2450
  });
@@ -2735,22 +2806,50 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2735
2806
  try {
2736
2807
  const { orgId } = req.params;
2737
2808
  const adapter = await getAuthAdapterWithConfig();
2738
- if (adapter && typeof adapter.findMany === 'function') {
2809
+ if (!adapter) {
2810
+ return res.status(500).json({
2811
+ success: false,
2812
+ error: 'Auth adapter not available',
2813
+ teams: [],
2814
+ });
2815
+ }
2816
+ if (typeof adapter.findMany !== 'function') {
2817
+ return res.status(500).json({
2818
+ success: false,
2819
+ error: 'Adapter findMany method not available',
2820
+ teams: [],
2821
+ });
2822
+ }
2823
+ try {
2824
+ let teams = [];
2739
2825
  try {
2740
- const teams = await adapter.findMany({
2826
+ teams = await adapter.findMany({
2741
2827
  model: 'team',
2742
2828
  where: [{ field: 'organizationId', value: orgId }],
2743
2829
  limit: 10000,
2744
2830
  });
2745
- const transformedTeams = await Promise.all((teams || []).map(async (team) => {
2746
- if (!adapter.findMany) {
2747
- return null;
2831
+ }
2832
+ catch (whereError) {
2833
+ const allTeams = await adapter.findMany({
2834
+ model: 'team',
2835
+ limit: 10000,
2836
+ });
2837
+ teams = (allTeams || []).filter((team) => team.organizationId === orgId);
2838
+ }
2839
+ if (!teams || teams.length === 0) {
2840
+ return res.json({ success: true, teams: [] });
2841
+ }
2842
+ const transformedTeams = await Promise.all((teams || []).map(async (team) => {
2843
+ try {
2844
+ let memberCount = 0;
2845
+ if (adapter.findMany) {
2846
+ const teamMembers = await adapter.findMany({
2847
+ model: 'teamMember',
2848
+ where: [{ field: 'teamId', value: team.id }],
2849
+ limit: 10000,
2850
+ });
2851
+ memberCount = teamMembers ? teamMembers.length : 0;
2748
2852
  }
2749
- const teamMembers = await adapter.findMany({
2750
- model: 'teamMember',
2751
- where: [{ field: 'teamId', value: team.id }],
2752
- limit: 10000,
2753
- });
2754
2853
  return {
2755
2854
  id: team.id,
2756
2855
  name: team.name,
@@ -2758,18 +2857,38 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2758
2857
  metadata: team.metadata,
2759
2858
  createdAt: team.createdAt,
2760
2859
  updatedAt: team.updatedAt,
2761
- memberCount: teamMembers ? teamMembers.length : 0,
2860
+ memberCount: memberCount,
2762
2861
  };
2763
- }));
2764
- res.json({ success: true, teams: transformedTeams });
2765
- return;
2766
- }
2767
- catch (_error) { }
2862
+ }
2863
+ catch (_error) {
2864
+ return {
2865
+ id: team.id,
2866
+ name: team.name,
2867
+ organizationId: team.organizationId,
2868
+ metadata: team.metadata,
2869
+ createdAt: team.createdAt,
2870
+ updatedAt: team.updatedAt,
2871
+ memberCount: 0,
2872
+ };
2873
+ }
2874
+ }));
2875
+ const validTeams = transformedTeams.filter((team) => team !== null);
2876
+ return res.json({ success: true, teams: validTeams });
2877
+ }
2878
+ catch (error) {
2879
+ return res.json({
2880
+ success: true,
2881
+ teams: [],
2882
+ error: error?.message || 'Failed to fetch teams',
2883
+ });
2768
2884
  }
2769
- res.json({ success: true, teams: [] });
2770
2885
  }
2771
- catch (_error) {
2772
- res.status(500).json({ error: 'Failed to fetch teams' });
2886
+ catch (error) {
2887
+ res.status(500).json({
2888
+ success: false,
2889
+ error: 'Failed to fetch teams',
2890
+ message: error?.message || 'Unknown error',
2891
+ });
2773
2892
  }
2774
2893
  });
2775
2894
  router.post('/api/organizations/:orgId/teams', async (req, res) => {
@@ -2794,7 +2913,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2794
2913
  if (!adapter.create) {
2795
2914
  return res.status(500).json({ error: 'Adapter create method not available' });
2796
2915
  }
2797
- await adapter.create({
2916
+ const teamResult = await adapter.create({
2798
2917
  model: 'team',
2799
2918
  data: {
2800
2919
  name: teamData.name,
@@ -2803,6 +2922,9 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2803
2922
  updatedAt: teamData.updatedAt,
2804
2923
  },
2805
2924
  });
2925
+ if (!teamResult) {
2926
+ return res.status(500).json({ error: 'Failed to create team' });
2927
+ }
2806
2928
  res.json({ success: true, team });
2807
2929
  }
2808
2930
  catch (_error) {
@@ -2872,39 +2994,83 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2872
2994
  return res.status(400).json({ error: 'userIds array is required' });
2873
2995
  }
2874
2996
  const adapter = await getAuthAdapterWithConfig();
2875
- if (!adapter || !adapter.create) {
2997
+ if (!adapter) {
2876
2998
  return res.status(500).json({ error: 'Adapter not available' });
2877
2999
  }
3000
+ if (!adapter.create) {
3001
+ return res.status(500).json({ error: 'Adapter create method not available' });
3002
+ }
2878
3003
  const results = [];
2879
3004
  for (const userId of userIds) {
2880
3005
  try {
3006
+ let existingMember = null;
3007
+ if (adapter.findMany) {
3008
+ try {
3009
+ const existing = await adapter.findMany({
3010
+ model: 'teamMember',
3011
+ where: [
3012
+ { field: 'teamId', value: teamId },
3013
+ { field: 'userId', value: userId },
3014
+ ],
3015
+ limit: 1,
3016
+ });
3017
+ existingMember = existing && existing.length > 0 ? existing[0] : null;
3018
+ }
3019
+ catch (_findError) {
3020
+ // if where clause isn't working.
3021
+ try {
3022
+ const allMembers = await adapter.findMany({
3023
+ model: 'teamMember',
3024
+ limit: 10000,
3025
+ });
3026
+ existingMember = (allMembers || []).find((m) => m.teamId === teamId && m.userId === userId);
3027
+ }
3028
+ catch (_fallbackError) { }
3029
+ }
3030
+ }
3031
+ if (existingMember) {
3032
+ results.push({
3033
+ success: false,
3034
+ userId,
3035
+ error: 'User is already a member of this team',
3036
+ });
3037
+ continue;
3038
+ }
3039
+ const now = new Date();
2881
3040
  await adapter.create({
2882
3041
  model: 'teamMember',
2883
3042
  data: {
2884
3043
  teamId,
2885
3044
  userId,
2886
3045
  role: 'member',
2887
- createdAt: new Date(),
3046
+ createdAt: now,
3047
+ updatedAt: now,
2888
3048
  },
2889
3049
  });
2890
3050
  results.push({ success: true, userId });
2891
3051
  }
2892
3052
  catch (error) {
3053
+ const errorMessage = error?.message || error?.toString() || 'Unknown error';
2893
3054
  results.push({
2894
3055
  success: false,
2895
3056
  userId,
2896
- error: error instanceof Error ? error.message : 'Unknown error',
3057
+ error: errorMessage,
2897
3058
  });
2898
3059
  }
2899
3060
  }
3061
+ const successCount = results.filter((r) => r.success).length;
2900
3062
  res.json({
2901
- success: true,
2902
- message: `Added ${results.filter((r) => r.success).length} members`,
3063
+ success: results.some((r) => r.success),
3064
+ message: `Added ${successCount} member${successCount !== 1 ? 's' : ''}`,
2903
3065
  results,
2904
3066
  });
2905
3067
  }
2906
- catch (_error) {
2907
- res.status(500).json({ error: 'Failed to add team members' });
3068
+ catch (error) {
3069
+ res.status(500).json({
3070
+ success: false,
3071
+ error: 'Failed to add team members',
3072
+ message: error?.message || 'Unknown error',
3073
+ });
2908
3074
  }
2909
3075
  });
2910
3076
  router.delete('/api/team-members/:id', async (req, res) => {
@@ -2974,7 +3140,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
2974
3140
  });
2975
3141
  router.get('/api/plugins/organization/status', async (_req, res) => {
2976
3142
  try {
2977
- const betterAuthConfig = await getAuthConfigSafe();
3143
+ const betterAuthConfig = preloadedAuthOptions || (await getAuthConfigSafe());
2978
3144
  if (!betterAuthConfig) {
2979
3145
  return res.json({
2980
3146
  enabled: false,
@@ -3106,7 +3272,7 @@ export function createRoutes(authConfig, configPath, geoDbPath, preloadedAdapter
3106
3272
  try {
3107
3273
  const { id } = req.params;
3108
3274
  const userData = req.body;
3109
- const updatedUser = await getAuthData(authConfig, 'updateUser', { id, userData }, configPath);
3275
+ const updatedUser = await getAuthData(authConfig, 'updateUser', { id, userData }, configPath, preloadedAdapter);
3110
3276
  res.json({ success: true, user: updatedUser });
3111
3277
  }
3112
3278
  catch (_error) {