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

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