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/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 +285 -117
- 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 +6 -5
- 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
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
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
|
-
|
|
1534
|
-
|
|
1562
|
+
plugins: pluginInfo,
|
|
1563
|
+
configPath: isSelfHosted ? null : configPath || null,
|
|
1564
|
+
totalPlugins: pluginInfo.length,
|
|
1535
1565
|
});
|
|
1536
1566
|
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
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
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
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
|
-
|
|
1554
|
-
if (!auth) {
|
|
1615
|
+
catch (_error) {
|
|
1555
1616
|
return res.json({
|
|
1556
1617
|
plugins: [],
|
|
1557
|
-
error: '
|
|
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
|
|
2169
|
-
if (!
|
|
2226
|
+
const fileAuthInstance = authModule?.auth || authModule?.default;
|
|
2227
|
+
if (!fileAuthInstance?.$context) {
|
|
2170
2228
|
return null;
|
|
2171
2229
|
}
|
|
2172
|
-
const context = await
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
2828
|
+
teams = await adapter.findMany({
|
|
2741
2829
|
model: 'team',
|
|
2742
2830
|
where: [{ field: 'organizationId', value: orgId }],
|
|
2743
2831
|
limit: 10000,
|
|
2744
2832
|
});
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
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:
|
|
2862
|
+
memberCount: memberCount,
|
|
2762
2863
|
};
|
|
2763
|
-
}
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
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 (
|
|
2772
|
-
res.status(500).json({
|
|
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
|
|
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:
|
|
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:
|
|
3059
|
+
error: errorMessage,
|
|
2897
3060
|
});
|
|
2898
3061
|
}
|
|
2899
3062
|
}
|
|
3063
|
+
const successCount = results.filter((r) => r.success).length;
|
|
2900
3064
|
res.json({
|
|
2901
|
-
success:
|
|
2902
|
-
message: `Added ${
|
|
3065
|
+
success: results.some((r) => r.success),
|
|
3066
|
+
message: `Added ${successCount} member${successCount !== 1 ? 's' : ''}`,
|
|
2903
3067
|
results,
|
|
2904
3068
|
});
|
|
2905
3069
|
}
|
|
2906
|
-
catch (
|
|
2907
|
-
res.status(500).json({
|
|
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) {
|