better-auth-studio 1.0.59-beta.1 → 1.0.59-beta.11
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 +173 -46
- package/dist/routes.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/main-Bbsm29F_.css +1 -0
- package/public/assets/{main-BH6u-YKB.js → main-RRrATaXc.js} +161 -146
- package/public/index.html +2 -2
- package/public/assets/main-KBM4GS4q.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,26 +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
4468
|
const middlewareCode = middleware
|
|
4429
4469
|
.map((mw) => {
|
|
4470
|
+
const formattedMiddlewareLogic = preserveIndentation(mw.middlewareLogic || '// Middleware logic here', ' ');
|
|
4430
4471
|
return ` {
|
|
4431
4472
|
path: "${mw.path}",
|
|
4432
4473
|
middleware: createAuthMiddleware(async (ctx) => {
|
|
4433
|
-
|
|
4434
|
-
${mw.middlewareLogic || '// Middleware logic here'}
|
|
4474
|
+
${formattedMiddlewareLogic}
|
|
4435
4475
|
}),
|
|
4436
4476
|
}`;
|
|
4437
4477
|
})
|
|
4438
4478
|
.join(',\n');
|
|
4439
|
-
// Generate endpoints
|
|
4440
4479
|
const endpointsCode = endpoints.length > 0
|
|
4441
4480
|
? endpoints
|
|
4442
4481
|
.map((endpoint) => {
|
|
@@ -4444,27 +4483,14 @@ ${fields}
|
|
|
4444
4483
|
const sanitizedName = endpointName.replace(/[^a-zA-Z0-9]/g, '');
|
|
4445
4484
|
const endpointPath = endpoint.path?.trim() || `/${camelCaseName}/${sanitizedName}`;
|
|
4446
4485
|
const handlerLogic = endpoint.handlerLogic ||
|
|
4447
|
-
'// Endpoint handler logic here\
|
|
4448
|
-
const formattedHandlerLogic = handlerLogic
|
|
4449
|
-
.split('\n')
|
|
4450
|
-
.map((line) => {
|
|
4451
|
-
const trimmed = line.trim();
|
|
4452
|
-
if (!trimmed)
|
|
4453
|
-
return '';
|
|
4454
|
-
if (!line.startsWith(' ')) {
|
|
4455
|
-
return ' ' + trimmed;
|
|
4456
|
-
}
|
|
4457
|
-
return line;
|
|
4458
|
-
})
|
|
4459
|
-
.filter(Boolean)
|
|
4460
|
-
.join('\n');
|
|
4486
|
+
'// Endpoint handler logic here\nreturn ctx.json({ success: true });';
|
|
4487
|
+
const formattedHandlerLogic = preserveIndentation(handlerLogic, ' ');
|
|
4461
4488
|
return ` ${sanitizedName}: createAuthEndpoint(
|
|
4462
4489
|
"${endpointPath}",
|
|
4463
4490
|
{
|
|
4464
4491
|
method: "${endpoint.method || 'POST'}",
|
|
4465
4492
|
},
|
|
4466
4493
|
async (ctx) => {
|
|
4467
|
-
// ${endpoint.name || sanitizedName}
|
|
4468
4494
|
${formattedHandlerLogic}
|
|
4469
4495
|
},
|
|
4470
4496
|
),`;
|
|
@@ -4530,7 +4556,6 @@ ${formattedHandlerLogic}
|
|
|
4530
4556
|
if (rateLimitCode) {
|
|
4531
4557
|
pluginParts.push(` rateLimit: {\n${rateLimitCode}\n }`);
|
|
4532
4558
|
}
|
|
4533
|
-
// Generate server plugin code
|
|
4534
4559
|
const imports = ['import type { BetterAuthPlugin } from "@better-auth/core"'];
|
|
4535
4560
|
if (hooks.length > 0 || middleware.length > 0 || endpoints.length > 0) {
|
|
4536
4561
|
imports.push('import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api"');
|
|
@@ -4577,12 +4602,11 @@ import type { ${camelCaseName} } from "..";
|
|
|
4577
4602
|
|
|
4578
4603
|
export const ${camelCaseName}Client = () => {
|
|
4579
4604
|
return {
|
|
4580
|
-
id: "${camelCaseName}"
|
|
4605
|
+
id: "${camelCaseName}",
|
|
4581
4606
|
$InferServerPlugin: {} as ReturnType<typeof ${camelCaseName}>,${pathMethods ? `\n pathMethods: {\n${pathMethods}\n },` : ''}${atomListenersCode}
|
|
4582
4607
|
} satisfies BetterAuthClientPlugin;
|
|
4583
4608
|
};
|
|
4584
4609
|
`);
|
|
4585
|
-
// Generate server setup code
|
|
4586
4610
|
const serverSetupCode = cleanCode(`import { betterAuth } from "@better-auth/core";
|
|
4587
4611
|
import { ${camelCaseName} } from "./plugin/${camelCaseName}";
|
|
4588
4612
|
|
|
@@ -4600,7 +4624,6 @@ export const auth = betterAuth({
|
|
|
4600
4624
|
vue: 'better-auth/vue',
|
|
4601
4625
|
};
|
|
4602
4626
|
const frameworkImport = frameworkImportMap[clientFramework] || 'better-auth/react';
|
|
4603
|
-
// Get baseURL based on framework
|
|
4604
4627
|
const baseURLMap = {
|
|
4605
4628
|
react: 'process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"',
|
|
4606
4629
|
svelte: 'import.meta.env.PUBLIC_BETTER_AUTH_URL || "http://localhost:5173"',
|
|
@@ -4984,6 +5007,110 @@ export const authClient = createAuthClient({
|
|
|
4984
5007
|
});
|
|
4985
5008
|
}
|
|
4986
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
|
+
});
|
|
4987
5114
|
router.post('/api/tools/check-env-credentials', async (req, res) => {
|
|
4988
5115
|
try {
|
|
4989
5116
|
const { provider } = req.body || {};
|