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/cli.js +0 -0
- package/dist/core/handler.d.ts +0 -7
- package/dist/core/handler.d.ts.map +1 -1
- package/dist/core/handler.js +202 -28
- package/dist/core/handler.js.map +1 -1
- package/dist/data.d.ts +1 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +3 -7
- package/dist/data.js.map +1 -1
- package/dist/public/assets/main-Bs-6ysD-.css +1 -0
- package/dist/public/assets/main-D7qZvFYK.js +1155 -0
- package/dist/public/index.html +2 -2
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +282 -116
- package/dist/routes.js.map +1 -1
- package/dist/utils/paths.d.ts +2 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +12 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +31 -31
- package/public/assets/main-Bs-6ysD-.css +1 -0
- package/public/assets/main-D7qZvFYK.js +1155 -0
- package/public/index.html +2 -2
- package/scripts/download-geolite2.js +35 -0
- package/scripts/generate-default-db.js +462 -0
- package/scripts/postinstall.js +98 -0
- package/dist/public/assets/main-3NIBCudD.js +0 -1155
- package/dist/public/assets/main-DbXDm13A.css +0 -1
- package/frontend/package.json +0 -64
- package/public/assets/main-3NIBCudD.js +0 -1155
- package/public/assets/main-DbXDm13A.css +0 -1
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
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
|
-
|
|
1534
|
-
|
|
1560
|
+
plugins: pluginInfo,
|
|
1561
|
+
configPath: isSelfHosted ? null : configPath || null,
|
|
1562
|
+
totalPlugins: pluginInfo.length,
|
|
1535
1563
|
});
|
|
1536
1564
|
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
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
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
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
|
-
|
|
1554
|
-
if (!auth) {
|
|
1613
|
+
catch (_error) {
|
|
1555
1614
|
return res.json({
|
|
1556
1615
|
plugins: [],
|
|
1557
|
-
error: '
|
|
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
|
|
2169
|
-
if (!
|
|
2224
|
+
const fileAuthInstance = authModule?.auth || authModule?.default;
|
|
2225
|
+
if (!fileAuthInstance?.$context) {
|
|
2170
2226
|
return null;
|
|
2171
2227
|
}
|
|
2172
|
-
const context = await
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
2826
|
+
teams = await adapter.findMany({
|
|
2741
2827
|
model: 'team',
|
|
2742
2828
|
where: [{ field: 'organizationId', value: orgId }],
|
|
2743
2829
|
limit: 10000,
|
|
2744
2830
|
});
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
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:
|
|
2860
|
+
memberCount: memberCount,
|
|
2762
2861
|
};
|
|
2763
|
-
}
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
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 (
|
|
2772
|
-
res.status(500).json({
|
|
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
|
|
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:
|
|
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:
|
|
3057
|
+
error: errorMessage,
|
|
2897
3058
|
});
|
|
2898
3059
|
}
|
|
2899
3060
|
}
|
|
3061
|
+
const successCount = results.filter((r) => r.success).length;
|
|
2900
3062
|
res.json({
|
|
2901
|
-
success:
|
|
2902
|
-
message: `Added ${
|
|
3063
|
+
success: results.some((r) => r.success),
|
|
3064
|
+
message: `Added ${successCount} member${successCount !== 1 ? 's' : ''}`,
|
|
2903
3065
|
results,
|
|
2904
3066
|
});
|
|
2905
3067
|
}
|
|
2906
|
-
catch (
|
|
2907
|
-
res.status(500).json({
|
|
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) {
|