better-auth-studio 1.0.58 → 1.0.59-beta.10
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.d.ts.map +1 -1
- package/dist/routes.js +176 -50
- package/dist/routes.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/main-Bbsm29F_.css +1 -0
- package/public/assets/{main-CfoGc2bA.js → main-BysNKkbj.js} +144 -139
- package/public/index.html +2 -2
- package/public/assets/main-BPffJmT6.css +0 -1
package/dist/routes.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAeA,OAAO,EAA+B,MAAM,EAAE,MAAM,SAAS,CAAC;AAS9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA+D9C,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAqLhG;AAeD,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,EACtB,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CA4mLR"}
|
package/dist/routes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHmac, randomBytes } from 'node:crypto';
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, } from 'node:fs';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
5
|
// @ts-expect-error
|
|
@@ -8,6 +8,7 @@ import { scryptAsync } from '@noble/hashes/scrypt.js';
|
|
|
8
8
|
import { Router } from 'express';
|
|
9
9
|
import { createJiti } from 'jiti';
|
|
10
10
|
import { createMockAccount, createMockSession, createMockUser, createMockVerification, getAuthAdapter, } from './auth-adapter.js';
|
|
11
|
+
import { possiblePaths } from './config.js';
|
|
11
12
|
import { getAuthData } from './data.js';
|
|
12
13
|
import { initializeGeoService, resolveIPLocation, setGeoDbPath } from './geo-service.js';
|
|
13
14
|
import { detectDatabaseWithDialect } from './utils/database-detection.js';
|
|
@@ -66,7 +67,6 @@ export async function safeImportAuthConfig(authConfigPath, noCache = false) {
|
|
|
66
67
|
if (authConfigPath.endsWith('.ts')) {
|
|
67
68
|
const aliases = {};
|
|
68
69
|
const authConfigDir = dirname(authConfigPath);
|
|
69
|
-
// Find project root by looking for tsconfig.json
|
|
70
70
|
let projectDir = authConfigDir;
|
|
71
71
|
let tsconfigPath = join(projectDir, 'tsconfig.json');
|
|
72
72
|
while (!existsSync(tsconfigPath) && projectDir !== dirname(projectDir)) {
|
|
@@ -74,10 +74,8 @@ export async function safeImportAuthConfig(authConfigPath, noCache = false) {
|
|
|
74
74
|
tsconfigPath = join(projectDir, 'tsconfig.json');
|
|
75
75
|
}
|
|
76
76
|
const content = readFileSync(authConfigPath, 'utf-8');
|
|
77
|
-
// Get path aliases from tsconfig (including extends chain)
|
|
78
77
|
const { getPathAliases } = await import('./config.js');
|
|
79
78
|
const tsconfigAliases = getPathAliases(projectDir) || {};
|
|
80
|
-
// Handle relative imports
|
|
81
79
|
const relativeImportRegex = /import\s+.*?\s+from\s+['"](\.\/[^'"]+)['"]/g;
|
|
82
80
|
const dynamicImportRegex = /import\s*\(\s*['"](\.\/[^'"]+)['"]\s*\)/g;
|
|
83
81
|
const foundImports = new Set();
|
|
@@ -111,7 +109,6 @@ export async function safeImportAuthConfig(authConfigPath, noCache = false) {
|
|
|
111
109
|
const pathAliasRegex = /import\s+.*?\s+from\s+['"](\$[^'"]+)['"]/g;
|
|
112
110
|
while ((match = pathAliasRegex.exec(content)) !== null) {
|
|
113
111
|
const aliasPath = match[1];
|
|
114
|
-
// Check if we have a matching alias
|
|
115
112
|
const aliasBase = aliasPath.split('/')[0];
|
|
116
113
|
if (tsconfigAliases[aliasBase]) {
|
|
117
114
|
const remainingPath = aliasPath.replace(aliasBase, '').replace(/^\//, '');
|
|
@@ -222,14 +219,6 @@ export async function safeImportAuthConfig(authConfigPath, noCache = false) {
|
|
|
222
219
|
async function findAuthConfigPath() {
|
|
223
220
|
const { join, dirname } = await import('node:path');
|
|
224
221
|
const { existsSync } = await import('node:fs');
|
|
225
|
-
const possiblePaths = [
|
|
226
|
-
'auth.js',
|
|
227
|
-
'auth.ts',
|
|
228
|
-
'src/auth.js',
|
|
229
|
-
'src/auth.ts',
|
|
230
|
-
'lib/auth.js',
|
|
231
|
-
'lib/auth.ts',
|
|
232
|
-
];
|
|
233
222
|
for (const path of possiblePaths) {
|
|
234
223
|
const fullPath = join(process.cwd(), path);
|
|
235
224
|
if (existsSync(fullPath)) {
|
|
@@ -4354,15 +4343,44 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
|
|
|
4354
4343
|
const fields = table.fields
|
|
4355
4344
|
?.filter((f) => f.name.trim())
|
|
4356
4345
|
.map((field) => {
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4346
|
+
const attrs = [`type: "${field.type}"`];
|
|
4347
|
+
attrs.push(`required: ${field.required ? 'true' : 'false'}`);
|
|
4348
|
+
attrs.push(`unique: ${field.unique ? 'true' : 'false'}`);
|
|
4349
|
+
attrs.push('input: false');
|
|
4350
|
+
// Handle defaultValue
|
|
4351
|
+
if (field.defaultValue !== undefined &&
|
|
4352
|
+
field.defaultValue !== null &&
|
|
4353
|
+
field.defaultValue !== '') {
|
|
4354
|
+
if (field.type === 'string') {
|
|
4355
|
+
attrs.push(`defaultValue: "${field.defaultValue}"`);
|
|
4356
|
+
}
|
|
4357
|
+
else if (field.type === 'boolean') {
|
|
4358
|
+
attrs.push(`defaultValue: ${field.defaultValue === 'true' || field.defaultValue === true}`);
|
|
4359
|
+
}
|
|
4360
|
+
else if (field.type === 'number') {
|
|
4361
|
+
attrs.push(`defaultValue: ${field.defaultValue}`);
|
|
4362
|
+
}
|
|
4363
|
+
else if (field.type === 'date') {
|
|
4364
|
+
if (field.defaultValue === 'now()') {
|
|
4365
|
+
attrs.push('defaultValue: new Date()');
|
|
4366
|
+
}
|
|
4367
|
+
else {
|
|
4368
|
+
attrs.push(`defaultValue: new Date("${field.defaultValue}")`);
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
else if (field.type === 'boolean') {
|
|
4373
|
+
// Default to false for boolean if no defaultValue specified
|
|
4374
|
+
attrs.push('defaultValue: false');
|
|
4375
|
+
}
|
|
4376
|
+
const attrStr = attrs.join(',\n ');
|
|
4362
4377
|
return ` ${field.name}: {\n ${attrStr}\n }`;
|
|
4363
4378
|
})
|
|
4364
4379
|
.join(',\n') || '';
|
|
4365
|
-
|
|
4380
|
+
const tableName = table.isExtending && table.extendedTableName
|
|
4381
|
+
? table.extendedTableName.trim()
|
|
4382
|
+
: table.name.trim();
|
|
4383
|
+
return ` ${tableName}: {
|
|
4366
4384
|
fields: {
|
|
4367
4385
|
${fields}
|
|
4368
4386
|
},
|
|
@@ -4370,7 +4388,29 @@ ${fields}
|
|
|
4370
4388
|
})
|
|
4371
4389
|
.join(',\n')
|
|
4372
4390
|
: '';
|
|
4373
|
-
|
|
4391
|
+
const preserveIndentation = (code, baseIndent) => {
|
|
4392
|
+
if (!code.trim())
|
|
4393
|
+
return '';
|
|
4394
|
+
const lines = code.split('\n');
|
|
4395
|
+
const nonEmptyLines = lines.filter((line) => line.trim());
|
|
4396
|
+
if (nonEmptyLines.length === 0)
|
|
4397
|
+
return '';
|
|
4398
|
+
const minIndent = Math.min(...nonEmptyLines.map((line) => {
|
|
4399
|
+
const match = line.match(/^(\s*)/);
|
|
4400
|
+
return match ? match[1].length : 0;
|
|
4401
|
+
}));
|
|
4402
|
+
return lines
|
|
4403
|
+
.map((line) => {
|
|
4404
|
+
if (!line.trim())
|
|
4405
|
+
return '';
|
|
4406
|
+
const currentIndent = line.match(/^(\s*)/)?.[1] || '';
|
|
4407
|
+
const relativeIndent = Math.max(0, currentIndent.length - minIndent);
|
|
4408
|
+
const content = line.trim();
|
|
4409
|
+
return baseIndent + ' '.repeat(relativeIndent) + content;
|
|
4410
|
+
})
|
|
4411
|
+
.filter(Boolean)
|
|
4412
|
+
.join('\n');
|
|
4413
|
+
};
|
|
4374
4414
|
const beforeHooks = hooks
|
|
4375
4415
|
.filter((h) => h.timing === 'before')
|
|
4376
4416
|
.map((hook) => {
|
|
@@ -4390,11 +4430,11 @@ ${fields}
|
|
|
4390
4430
|
else {
|
|
4391
4431
|
matcher = `(ctx) => true`;
|
|
4392
4432
|
}
|
|
4433
|
+
const formattedHookLogic = preserveIndentation(hook.hookLogic || '// Hook logic here', ' ');
|
|
4393
4434
|
return ` {
|
|
4394
4435
|
matcher: ${matcher},
|
|
4395
4436
|
handler: createAuthMiddleware(async (ctx) => {
|
|
4396
|
-
|
|
4397
|
-
${hook.hookLogic || '// Hook logic here'}
|
|
4437
|
+
${formattedHookLogic}
|
|
4398
4438
|
}),
|
|
4399
4439
|
}`;
|
|
4400
4440
|
});
|
|
@@ -4417,27 +4457,25 @@ ${fields}
|
|
|
4417
4457
|
else {
|
|
4418
4458
|
matcher = `(ctx) => true`;
|
|
4419
4459
|
}
|
|
4460
|
+
const formattedHookLogic = preserveIndentation(hook.hookLogic || '// Hook logic here', ' ');
|
|
4420
4461
|
return ` {
|
|
4421
4462
|
matcher: ${matcher},
|
|
4422
4463
|
handler: createAuthMiddleware(async (ctx) => {
|
|
4423
|
-
|
|
4424
|
-
${hook.hookLogic || '// Hook logic here'}
|
|
4464
|
+
${formattedHookLogic}
|
|
4425
4465
|
}),
|
|
4426
4466
|
}`;
|
|
4427
4467
|
});
|
|
4428
|
-
// Generate middleware
|
|
4429
4468
|
const middlewareCode = middleware
|
|
4430
4469
|
.map((mw) => {
|
|
4470
|
+
const formattedMiddlewareLogic = preserveIndentation(mw.middlewareLogic || '// Middleware logic here', ' ');
|
|
4431
4471
|
return ` {
|
|
4432
4472
|
path: "${mw.path}",
|
|
4433
4473
|
middleware: createAuthMiddleware(async (ctx) => {
|
|
4434
|
-
|
|
4435
|
-
${mw.middlewareLogic || '// Middleware logic here'}
|
|
4474
|
+
${formattedMiddlewareLogic}
|
|
4436
4475
|
}),
|
|
4437
4476
|
}`;
|
|
4438
4477
|
})
|
|
4439
4478
|
.join(',\n');
|
|
4440
|
-
// Generate endpoints
|
|
4441
4479
|
const endpointsCode = endpoints.length > 0
|
|
4442
4480
|
? endpoints
|
|
4443
4481
|
.map((endpoint) => {
|
|
@@ -4445,27 +4483,14 @@ ${fields}
|
|
|
4445
4483
|
const sanitizedName = endpointName.replace(/[^a-zA-Z0-9]/g, '');
|
|
4446
4484
|
const endpointPath = endpoint.path?.trim() || `/${camelCaseName}/${sanitizedName}`;
|
|
4447
4485
|
const handlerLogic = endpoint.handlerLogic ||
|
|
4448
|
-
'// Endpoint handler logic here\
|
|
4449
|
-
const formattedHandlerLogic = handlerLogic
|
|
4450
|
-
.split('\n')
|
|
4451
|
-
.map((line) => {
|
|
4452
|
-
const trimmed = line.trim();
|
|
4453
|
-
if (!trimmed)
|
|
4454
|
-
return '';
|
|
4455
|
-
if (!line.startsWith(' ')) {
|
|
4456
|
-
return ' ' + trimmed;
|
|
4457
|
-
}
|
|
4458
|
-
return line;
|
|
4459
|
-
})
|
|
4460
|
-
.filter(Boolean)
|
|
4461
|
-
.join('\n');
|
|
4486
|
+
'// Endpoint handler logic here\nreturn ctx.json({ success: true });';
|
|
4487
|
+
const formattedHandlerLogic = preserveIndentation(handlerLogic, ' ');
|
|
4462
4488
|
return ` ${sanitizedName}: createAuthEndpoint(
|
|
4463
4489
|
"${endpointPath}",
|
|
4464
4490
|
{
|
|
4465
|
-
method: "${endpoint.method || 'POST'}"
|
|
4491
|
+
method: "${endpoint.method || 'POST'}",
|
|
4466
4492
|
},
|
|
4467
4493
|
async (ctx) => {
|
|
4468
|
-
// ${endpoint.name || sanitizedName}
|
|
4469
4494
|
${formattedHandlerLogic}
|
|
4470
4495
|
},
|
|
4471
4496
|
),`;
|
|
@@ -4531,14 +4556,13 @@ ${formattedHandlerLogic}
|
|
|
4531
4556
|
if (rateLimitCode) {
|
|
4532
4557
|
pluginParts.push(` rateLimit: {\n${rateLimitCode}\n }`);
|
|
4533
4558
|
}
|
|
4534
|
-
// Generate server plugin code
|
|
4535
4559
|
const imports = ['import type { BetterAuthPlugin } from "@better-auth/core"'];
|
|
4536
4560
|
if (hooks.length > 0 || middleware.length > 0 || endpoints.length > 0) {
|
|
4537
4561
|
imports.push('import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api"');
|
|
4538
4562
|
}
|
|
4539
4563
|
const serverPluginBody = pluginParts.length > 0
|
|
4540
|
-
? ` id: "${camelCaseName}"
|
|
4541
|
-
: ` id: "${camelCaseName}"
|
|
4564
|
+
? ` id: "${camelCaseName}",\n${pluginParts.join(',\n')}`
|
|
4565
|
+
: ` id: "${camelCaseName}"`;
|
|
4542
4566
|
const serverPluginCode = cleanCode(`import type { BetterAuthPlugin } from "@better-auth/core";
|
|
4543
4567
|
${imports.join('\n')}
|
|
4544
4568
|
|
|
@@ -4578,12 +4602,11 @@ import type { ${camelCaseName} } from "..";
|
|
|
4578
4602
|
|
|
4579
4603
|
export const ${camelCaseName}Client = () => {
|
|
4580
4604
|
return {
|
|
4581
|
-
id: "${camelCaseName}"
|
|
4605
|
+
id: "${camelCaseName}",
|
|
4582
4606
|
$InferServerPlugin: {} as ReturnType<typeof ${camelCaseName}>,${pathMethods ? `\n pathMethods: {\n${pathMethods}\n },` : ''}${atomListenersCode}
|
|
4583
4607
|
} satisfies BetterAuthClientPlugin;
|
|
4584
4608
|
};
|
|
4585
4609
|
`);
|
|
4586
|
-
// Generate server setup code
|
|
4587
4610
|
const serverSetupCode = cleanCode(`import { betterAuth } from "@better-auth/core";
|
|
4588
4611
|
import { ${camelCaseName} } from "./plugin/${camelCaseName}";
|
|
4589
4612
|
|
|
@@ -4601,7 +4624,6 @@ export const auth = betterAuth({
|
|
|
4601
4624
|
vue: 'better-auth/vue',
|
|
4602
4625
|
};
|
|
4603
4626
|
const frameworkImport = frameworkImportMap[clientFramework] || 'better-auth/react';
|
|
4604
|
-
// Get baseURL based on framework
|
|
4605
4627
|
const baseURLMap = {
|
|
4606
4628
|
react: 'process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"',
|
|
4607
4629
|
svelte: 'import.meta.env.PUBLIC_BETTER_AUTH_URL || "http://localhost:5173"',
|
|
@@ -4985,6 +5007,110 @@ export const authClient = createAuthClient({
|
|
|
4985
5007
|
});
|
|
4986
5008
|
}
|
|
4987
5009
|
});
|
|
5010
|
+
router.post('/api/tools/check-env-secret', async (_req, res) => {
|
|
5011
|
+
try {
|
|
5012
|
+
const envPath = join(process.cwd(), '.env');
|
|
5013
|
+
const envLocalPath = join(process.cwd(), '.env.local');
|
|
5014
|
+
const targetPath = existsSync(envLocalPath) ? envLocalPath : envPath;
|
|
5015
|
+
const envContent = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : '';
|
|
5016
|
+
const envLines = envContent.split('\n');
|
|
5017
|
+
let existingSecret;
|
|
5018
|
+
envLines.forEach((line) => {
|
|
5019
|
+
const trimmed = line.trim();
|
|
5020
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
5021
|
+
return;
|
|
5022
|
+
const match = trimmed.match(/^BETTER_AUTH_SECRET\s*=\s*(.*)$/i);
|
|
5023
|
+
if (match) {
|
|
5024
|
+
let value = match[1].trim();
|
|
5025
|
+
if (value.length >= 2) {
|
|
5026
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
5027
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
5028
|
+
value = value.slice(1, -1).trim();
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
5031
|
+
if (value && value.trim() !== '') {
|
|
5032
|
+
existingSecret = value;
|
|
5033
|
+
}
|
|
5034
|
+
}
|
|
5035
|
+
});
|
|
5036
|
+
const hasExisting = !!existingSecret && existingSecret.trim() !== '';
|
|
5037
|
+
res.json({
|
|
5038
|
+
success: true,
|
|
5039
|
+
hasExisting,
|
|
5040
|
+
existingSecret: hasExisting ? existingSecret : undefined,
|
|
5041
|
+
path: targetPath,
|
|
5042
|
+
});
|
|
5043
|
+
}
|
|
5044
|
+
catch (error) {
|
|
5045
|
+
res.status(500).json({
|
|
5046
|
+
success: false,
|
|
5047
|
+
message: error instanceof Error ? error.message : 'Failed to check secret',
|
|
5048
|
+
});
|
|
5049
|
+
}
|
|
5050
|
+
});
|
|
5051
|
+
router.post('/api/tools/write-env-secret', async (req, res) => {
|
|
5052
|
+
try {
|
|
5053
|
+
const { secret, action = 'override' } = req.body || {};
|
|
5054
|
+
if (!secret) {
|
|
5055
|
+
return res.status(400).json({
|
|
5056
|
+
success: false,
|
|
5057
|
+
message: 'Secret is required',
|
|
5058
|
+
});
|
|
5059
|
+
}
|
|
5060
|
+
const envPath = join(process.cwd(), '.env');
|
|
5061
|
+
const envLocalPath = join(process.cwd(), '.env.local');
|
|
5062
|
+
const targetPath = existsSync(envLocalPath) ? envLocalPath : envPath;
|
|
5063
|
+
const envContent = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : '';
|
|
5064
|
+
const envLines = envContent.split('\n');
|
|
5065
|
+
const envMap = new Map();
|
|
5066
|
+
const newLines = [];
|
|
5067
|
+
envLines.forEach((line, index) => {
|
|
5068
|
+
const trimmed = line.trim();
|
|
5069
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
5070
|
+
newLines.push(line);
|
|
5071
|
+
return;
|
|
5072
|
+
}
|
|
5073
|
+
const match = trimmed.match(/^([^=#]+)\s*=\s*(.*)$/);
|
|
5074
|
+
if (match) {
|
|
5075
|
+
const key = match[1].trim();
|
|
5076
|
+
if (key.toUpperCase() === 'BETTER_AUTH_SECRET') {
|
|
5077
|
+
envMap.set('BETTER_AUTH_SECRET', { line, index });
|
|
5078
|
+
if (action === 'override') {
|
|
5079
|
+
newLines.push(`BETTER_AUTH_SECRET=${secret}`);
|
|
5080
|
+
}
|
|
5081
|
+
else {
|
|
5082
|
+
newLines.push(line);
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
else {
|
|
5086
|
+
newLines.push(line);
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
5089
|
+
else {
|
|
5090
|
+
newLines.push(line);
|
|
5091
|
+
}
|
|
5092
|
+
});
|
|
5093
|
+
if (!envMap.has('BETTER_AUTH_SECRET')) {
|
|
5094
|
+
if (newLines.length > 0 && newLines[newLines.length - 1].trim() !== '') {
|
|
5095
|
+
newLines.push('');
|
|
5096
|
+
}
|
|
5097
|
+
newLines.push(`BETTER_AUTH_SECRET=${secret}`);
|
|
5098
|
+
}
|
|
5099
|
+
const newContent = newLines.join('\n');
|
|
5100
|
+
writeFileSync(targetPath, newContent, 'utf-8');
|
|
5101
|
+
res.json({
|
|
5102
|
+
success: true,
|
|
5103
|
+
message: 'Secret written successfully',
|
|
5104
|
+
path: targetPath,
|
|
5105
|
+
});
|
|
5106
|
+
}
|
|
5107
|
+
catch (error) {
|
|
5108
|
+
res.status(500).json({
|
|
5109
|
+
success: false,
|
|
5110
|
+
message: error instanceof Error ? error.message : 'Failed to write secret to .env',
|
|
5111
|
+
});
|
|
5112
|
+
}
|
|
5113
|
+
});
|
|
4988
5114
|
router.post('/api/tools/check-env-credentials', async (req, res) => {
|
|
4989
5115
|
try {
|
|
4990
5116
|
const { provider } = req.body || {};
|