better-auth-studio 1.0.58-beta.3 → 1.0.58
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 +313 -3
- package/dist/routes.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/main-BPffJmT6.css +1 -0
- package/public/assets/{main-tRS3LNnj.js → main-CfoGc2bA.js} +128 -119
- package/public/index.html +2 -2
- package/public/assets/main-D93LWsqh.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":"AAOA,OAAO,EAA+B,MAAM,EAAE,MAAM,SAAS,CAAC;AAS9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA8D9C,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAyLhG;AAwBD,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,EACtB,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAOA,OAAO,EAA+B,MAAM,EAAE,MAAM,SAAS,CAAC;AAS9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA8D9C,wBAAsB,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAyLhG;AAwBD,wBAAgB,YAAY,CAC1B,UAAU,EAAE,UAAU,EACtB,UAAU,CAAC,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAk8KR"}
|
package/dist/routes.js
CHANGED
|
@@ -4331,6 +4331,317 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
|
|
|
4331
4331
|
res.status(500).json({ success: false, error: 'Failed to generate token' });
|
|
4332
4332
|
}
|
|
4333
4333
|
});
|
|
4334
|
+
router.post('/api/tools/plugin-generator', async (req, res) => {
|
|
4335
|
+
try {
|
|
4336
|
+
const { pluginName, description, clientFramework = 'react', tables = [], hooks = [], middleware = [], endpoints = [], rateLimit, } = req.body || {};
|
|
4337
|
+
if (!pluginName || typeof pluginName !== 'string' || pluginName.trim().length === 0) {
|
|
4338
|
+
return res.status(400).json({ success: false, error: 'Plugin name is required' });
|
|
4339
|
+
}
|
|
4340
|
+
const validNameRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
4341
|
+
if (!validNameRegex.test(pluginName.trim())) {
|
|
4342
|
+
return res.status(400).json({
|
|
4343
|
+
success: false,
|
|
4344
|
+
error: 'Plugin name must be a valid JavaScript identifier (letters, numbers, _, $)',
|
|
4345
|
+
});
|
|
4346
|
+
}
|
|
4347
|
+
const sanitizedName = pluginName.trim();
|
|
4348
|
+
const camelCaseName = sanitizedName.charAt(0).toLowerCase() + sanitizedName.slice(1);
|
|
4349
|
+
const pascalCaseName = sanitizedName.charAt(0).toUpperCase() + sanitizedName.slice(1);
|
|
4350
|
+
// Generate schema
|
|
4351
|
+
const schemaCode = tables.length > 0
|
|
4352
|
+
? tables
|
|
4353
|
+
.map((table) => {
|
|
4354
|
+
const fields = table.fields
|
|
4355
|
+
?.filter((f) => f.name.trim())
|
|
4356
|
+
.map((field) => {
|
|
4357
|
+
let attrStr = `type: "${field.type}"`;
|
|
4358
|
+
if (field.required)
|
|
4359
|
+
attrStr += ',\n required: true';
|
|
4360
|
+
if (field.unique)
|
|
4361
|
+
attrStr += ',\n unique: true';
|
|
4362
|
+
return ` ${field.name}: {\n ${attrStr}\n }`;
|
|
4363
|
+
})
|
|
4364
|
+
.join(',\n') || '';
|
|
4365
|
+
return ` ${table.name}: {
|
|
4366
|
+
fields: {
|
|
4367
|
+
${fields}
|
|
4368
|
+
},
|
|
4369
|
+
}`;
|
|
4370
|
+
})
|
|
4371
|
+
.join(',\n')
|
|
4372
|
+
: '';
|
|
4373
|
+
// Generate hooks
|
|
4374
|
+
const beforeHooks = hooks
|
|
4375
|
+
.filter((h) => h.timing === 'before')
|
|
4376
|
+
.map((hook) => {
|
|
4377
|
+
let matcher = '';
|
|
4378
|
+
if (hook.action === 'sign-up') {
|
|
4379
|
+
matcher = `(ctx) => ctx.path.startsWith("/sign-up")`;
|
|
4380
|
+
}
|
|
4381
|
+
else if (hook.action === 'sign-in') {
|
|
4382
|
+
matcher = `(ctx) => ctx.path.startsWith("/sign-in")`;
|
|
4383
|
+
}
|
|
4384
|
+
else if (hook.action === 'custom' && hook.customPath) {
|
|
4385
|
+
matcher = `(ctx) => ctx.path === "${hook.customPath}"`;
|
|
4386
|
+
if (hook.customMatcher) {
|
|
4387
|
+
matcher = `(ctx) => ctx.path === "${hook.customPath}" && (${hook.customMatcher})`;
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
else {
|
|
4391
|
+
matcher = `(ctx) => true`;
|
|
4392
|
+
}
|
|
4393
|
+
return ` {
|
|
4394
|
+
matcher: ${matcher},
|
|
4395
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
4396
|
+
// ${hook.name || `${hook.timing} ${hook.action} hook`}
|
|
4397
|
+
${hook.hookLogic || '// Hook logic here'}
|
|
4398
|
+
}),
|
|
4399
|
+
}`;
|
|
4400
|
+
});
|
|
4401
|
+
const afterHooks = hooks
|
|
4402
|
+
.filter((h) => h.timing === 'after')
|
|
4403
|
+
.map((hook) => {
|
|
4404
|
+
let matcher = '';
|
|
4405
|
+
if (hook.action === 'sign-up') {
|
|
4406
|
+
matcher = `(ctx) => ctx.path.startsWith("/sign-up")`;
|
|
4407
|
+
}
|
|
4408
|
+
else if (hook.action === 'sign-in') {
|
|
4409
|
+
matcher = `(ctx) => ctx.path.startsWith("/sign-in")`;
|
|
4410
|
+
}
|
|
4411
|
+
else if (hook.action === 'custom' && hook.customPath) {
|
|
4412
|
+
matcher = `(ctx) => ctx.path === "${hook.customPath}"`;
|
|
4413
|
+
if (hook.customMatcher) {
|
|
4414
|
+
matcher = `(ctx) => ctx.path === "${hook.customPath}" && (${hook.customMatcher})`;
|
|
4415
|
+
}
|
|
4416
|
+
}
|
|
4417
|
+
else {
|
|
4418
|
+
matcher = `(ctx) => true`;
|
|
4419
|
+
}
|
|
4420
|
+
return ` {
|
|
4421
|
+
matcher: ${matcher},
|
|
4422
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
4423
|
+
// ${hook.name || `${hook.timing} ${hook.action} hook`}
|
|
4424
|
+
${hook.hookLogic || '// Hook logic here'}
|
|
4425
|
+
}),
|
|
4426
|
+
}`;
|
|
4427
|
+
});
|
|
4428
|
+
// Generate middleware
|
|
4429
|
+
const middlewareCode = middleware
|
|
4430
|
+
.map((mw) => {
|
|
4431
|
+
return ` {
|
|
4432
|
+
path: "${mw.path}",
|
|
4433
|
+
middleware: createAuthMiddleware(async (ctx) => {
|
|
4434
|
+
// ${mw.name || 'Middleware'}
|
|
4435
|
+
${mw.middlewareLogic || '// Middleware logic here'}
|
|
4436
|
+
}),
|
|
4437
|
+
}`;
|
|
4438
|
+
})
|
|
4439
|
+
.join(',\n');
|
|
4440
|
+
// Generate endpoints
|
|
4441
|
+
const endpointsCode = endpoints.length > 0
|
|
4442
|
+
? endpoints
|
|
4443
|
+
.map((endpoint) => {
|
|
4444
|
+
const endpointName = endpoint.name?.trim() || `endpoint${endpoints.indexOf(endpoint) + 1}`;
|
|
4445
|
+
const sanitizedName = endpointName.replace(/[^a-zA-Z0-9]/g, '');
|
|
4446
|
+
const endpointPath = endpoint.path?.trim() || `/${camelCaseName}/${sanitizedName}`;
|
|
4447
|
+
const handlerLogic = endpoint.handlerLogic ||
|
|
4448
|
+
'// Endpoint handler logic here\n return ctx.json({ success: true });';
|
|
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');
|
|
4462
|
+
return ` ${sanitizedName}: createAuthEndpoint(
|
|
4463
|
+
"${endpointPath}",
|
|
4464
|
+
{
|
|
4465
|
+
method: "${endpoint.method || 'POST'}" as const,
|
|
4466
|
+
},
|
|
4467
|
+
async (ctx) => {
|
|
4468
|
+
// ${endpoint.name || sanitizedName}
|
|
4469
|
+
${formattedHandlerLogic}
|
|
4470
|
+
},
|
|
4471
|
+
),`;
|
|
4472
|
+
})
|
|
4473
|
+
.join('\n')
|
|
4474
|
+
: '';
|
|
4475
|
+
const rateLimitCode = rateLimit
|
|
4476
|
+
? (() => {
|
|
4477
|
+
const rl = rateLimit;
|
|
4478
|
+
let pathMatcher = '';
|
|
4479
|
+
if (rl.pathType === 'exact') {
|
|
4480
|
+
pathMatcher = `(path: string) => path === "${rl.path}"`;
|
|
4481
|
+
}
|
|
4482
|
+
else if (rl.pathType === 'prefix') {
|
|
4483
|
+
pathMatcher = `(path: string) => path.startsWith("${rl.path}")`;
|
|
4484
|
+
}
|
|
4485
|
+
else if (rl.pathType === 'regex') {
|
|
4486
|
+
pathMatcher = `(path: string) => new RegExp("${rl.path.replace(/"/g, '\\"')}").test(path)`;
|
|
4487
|
+
}
|
|
4488
|
+
else {
|
|
4489
|
+
pathMatcher = `(path: string) => true`;
|
|
4490
|
+
}
|
|
4491
|
+
const windowValue = rl.window && rl.window > 0 ? rl.window : 15 * 60 * 1000;
|
|
4492
|
+
const maxValue = rl.max && rl.max > 0 ? rl.max : 100;
|
|
4493
|
+
return ` window: ${windowValue},
|
|
4494
|
+
max: ${maxValue},
|
|
4495
|
+
pathMatcher: ${pathMatcher}`;
|
|
4496
|
+
})()
|
|
4497
|
+
: '';
|
|
4498
|
+
const cleanCode = (code) => {
|
|
4499
|
+
return code
|
|
4500
|
+
.split('\n')
|
|
4501
|
+
.map((line) => line.trimEnd())
|
|
4502
|
+
.filter((line, index, arr) => {
|
|
4503
|
+
if (line === '' && arr[index + 1] === '')
|
|
4504
|
+
return false;
|
|
4505
|
+
return true;
|
|
4506
|
+
})
|
|
4507
|
+
.join('\n')
|
|
4508
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
4509
|
+
.trim();
|
|
4510
|
+
};
|
|
4511
|
+
const pluginParts = [];
|
|
4512
|
+
if (schemaCode) {
|
|
4513
|
+
pluginParts.push(` schema: {\n${schemaCode}\n }`);
|
|
4514
|
+
}
|
|
4515
|
+
if (beforeHooks.length > 0 || afterHooks.length > 0) {
|
|
4516
|
+
const hooksParts = [];
|
|
4517
|
+
if (beforeHooks.length > 0) {
|
|
4518
|
+
hooksParts.push(` before: [\n${beforeHooks.join(',\n')}\n ]`);
|
|
4519
|
+
}
|
|
4520
|
+
if (afterHooks.length > 0) {
|
|
4521
|
+
hooksParts.push(` after: [\n${afterHooks.join(',\n')}\n ]`);
|
|
4522
|
+
}
|
|
4523
|
+
pluginParts.push(` hooks: {\n${hooksParts.join(',\n')}\n }`);
|
|
4524
|
+
}
|
|
4525
|
+
if (middlewareCode) {
|
|
4526
|
+
pluginParts.push(` middlewares: [\n${middlewareCode}\n ]`);
|
|
4527
|
+
}
|
|
4528
|
+
if (endpointsCode) {
|
|
4529
|
+
pluginParts.push(` endpoints: {\n${endpointsCode}\n }`);
|
|
4530
|
+
}
|
|
4531
|
+
if (rateLimitCode) {
|
|
4532
|
+
pluginParts.push(` rateLimit: {\n${rateLimitCode}\n }`);
|
|
4533
|
+
}
|
|
4534
|
+
// Generate server plugin code
|
|
4535
|
+
const imports = ['import type { BetterAuthPlugin } from "@better-auth/core"'];
|
|
4536
|
+
if (hooks.length > 0 || middleware.length > 0 || endpoints.length > 0) {
|
|
4537
|
+
imports.push('import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api"');
|
|
4538
|
+
}
|
|
4539
|
+
const serverPluginBody = pluginParts.length > 0
|
|
4540
|
+
? ` id: "${camelCaseName}" as const,\n${pluginParts.join(',\n')}`
|
|
4541
|
+
: ` id: "${camelCaseName}" as const`;
|
|
4542
|
+
const serverPluginCode = cleanCode(`import type { BetterAuthPlugin } from "@better-auth/core";
|
|
4543
|
+
${imports.join('\n')}
|
|
4544
|
+
|
|
4545
|
+
${description ? `/**\n * ${description.replace(/\n/g, '\n * ')}\n */` : ''}
|
|
4546
|
+
export const ${camelCaseName} = (options?: Record<string, any>) => {
|
|
4547
|
+
return {
|
|
4548
|
+
${serverPluginBody}
|
|
4549
|
+
} satisfies BetterAuthPlugin;
|
|
4550
|
+
};
|
|
4551
|
+
`);
|
|
4552
|
+
const pathMethods = endpoints.length > 0
|
|
4553
|
+
? endpoints
|
|
4554
|
+
.map((endpoint) => {
|
|
4555
|
+
const endpointPath = endpoint.path?.trim() || '';
|
|
4556
|
+
const method = endpoint.method || 'POST';
|
|
4557
|
+
return ` "${endpointPath}": "${method}"`;
|
|
4558
|
+
})
|
|
4559
|
+
.join(',\n')
|
|
4560
|
+
: '';
|
|
4561
|
+
const sessionAffectingPaths = endpoints
|
|
4562
|
+
.filter((endpoint) => {
|
|
4563
|
+
const path = endpoint.path?.trim() || '';
|
|
4564
|
+
return path.includes('/sign-in') || path.includes('/sign-up');
|
|
4565
|
+
})
|
|
4566
|
+
.map((endpoint) => {
|
|
4567
|
+
const endpointPath = endpoint.path?.trim() || '';
|
|
4568
|
+
return ` {
|
|
4569
|
+
matcher: (path) => path === "${endpointPath}",
|
|
4570
|
+
signal: "$sessionSignal",
|
|
4571
|
+
}`;
|
|
4572
|
+
});
|
|
4573
|
+
const atomListenersCode = sessionAffectingPaths.length > 0
|
|
4574
|
+
? `\n atomListeners: [\n${sessionAffectingPaths.join(',\n')}\n ],`
|
|
4575
|
+
: '';
|
|
4576
|
+
const clientPluginCode = cleanCode(`import type { BetterAuthClientPlugin } from "@better-auth/core";
|
|
4577
|
+
import type { ${camelCaseName} } from "..";
|
|
4578
|
+
|
|
4579
|
+
export const ${camelCaseName}Client = () => {
|
|
4580
|
+
return {
|
|
4581
|
+
id: "${camelCaseName}" as const,
|
|
4582
|
+
$InferServerPlugin: {} as ReturnType<typeof ${camelCaseName}>,${pathMethods ? `\n pathMethods: {\n${pathMethods}\n },` : ''}${atomListenersCode}
|
|
4583
|
+
} satisfies BetterAuthClientPlugin;
|
|
4584
|
+
};
|
|
4585
|
+
`);
|
|
4586
|
+
// Generate server setup code
|
|
4587
|
+
const serverSetupCode = cleanCode(`import { betterAuth } from "@better-auth/core";
|
|
4588
|
+
import { ${camelCaseName} } from "./plugin/${camelCaseName}";
|
|
4589
|
+
|
|
4590
|
+
export const auth = betterAuth({
|
|
4591
|
+
// ... your existing config
|
|
4592
|
+
plugins: [
|
|
4593
|
+
${camelCaseName}(),
|
|
4594
|
+
],
|
|
4595
|
+
});
|
|
4596
|
+
`);
|
|
4597
|
+
const frameworkImportMap = {
|
|
4598
|
+
react: 'better-auth/react',
|
|
4599
|
+
svelte: 'better-auth/svelte',
|
|
4600
|
+
solid: 'better-auth/solid',
|
|
4601
|
+
vue: 'better-auth/vue',
|
|
4602
|
+
};
|
|
4603
|
+
const frameworkImport = frameworkImportMap[clientFramework] || 'better-auth/react';
|
|
4604
|
+
// Get baseURL based on framework
|
|
4605
|
+
const baseURLMap = {
|
|
4606
|
+
react: 'process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"',
|
|
4607
|
+
svelte: 'import.meta.env.PUBLIC_BETTER_AUTH_URL || "http://localhost:5173"',
|
|
4608
|
+
solid: 'import.meta.env.PUBLIC_BETTER_AUTH_URL || "http://localhost:5173"',
|
|
4609
|
+
vue: 'import.meta.env.PUBLIC_BETTER_AUTH_URL || "http://localhost:5173"',
|
|
4610
|
+
};
|
|
4611
|
+
const baseURL = baseURLMap[clientFramework] ||
|
|
4612
|
+
'process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"';
|
|
4613
|
+
const clientSetupCode = cleanCode(`import { createAuthClient } from "${frameworkImport}";
|
|
4614
|
+
import { ${camelCaseName}Client } from "./plugin/${camelCaseName}/client";
|
|
4615
|
+
|
|
4616
|
+
export const authClient = createAuthClient({
|
|
4617
|
+
baseURL: ${baseURL},
|
|
4618
|
+
plugins: [
|
|
4619
|
+
${camelCaseName}Client(),
|
|
4620
|
+
],
|
|
4621
|
+
});
|
|
4622
|
+
`);
|
|
4623
|
+
return res.json({
|
|
4624
|
+
success: true,
|
|
4625
|
+
plugin: {
|
|
4626
|
+
name: sanitizedName,
|
|
4627
|
+
server: serverPluginCode,
|
|
4628
|
+
client: clientPluginCode,
|
|
4629
|
+
serverSetup: serverSetupCode,
|
|
4630
|
+
clientSetup: clientSetupCode,
|
|
4631
|
+
filePaths: {
|
|
4632
|
+
server: `plugin/${camelCaseName}/index.ts`,
|
|
4633
|
+
client: `plugin/${camelCaseName}/client/index.ts`,
|
|
4634
|
+
serverSetup: 'auth.ts',
|
|
4635
|
+
clientSetup: 'auth-client.ts',
|
|
4636
|
+
},
|
|
4637
|
+
},
|
|
4638
|
+
});
|
|
4639
|
+
}
|
|
4640
|
+
catch (error) {
|
|
4641
|
+
const message = error instanceof Error ? error.message : 'Failed to generate plugin';
|
|
4642
|
+
res.status(500).json({ success: false, error: message });
|
|
4643
|
+
}
|
|
4644
|
+
});
|
|
4334
4645
|
router.post('/api/tools/export', async (req, res) => {
|
|
4335
4646
|
try {
|
|
4336
4647
|
const { tables, format, limit } = req.body;
|
|
@@ -4344,7 +4655,6 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
|
|
|
4344
4655
|
return res.status(500).json({ success: false, error: 'Auth adapter not available' });
|
|
4345
4656
|
}
|
|
4346
4657
|
const exportData = {};
|
|
4347
|
-
// Fetch data from each table
|
|
4348
4658
|
for (const tableName of tables) {
|
|
4349
4659
|
try {
|
|
4350
4660
|
const data = await adapter.findMany({
|
|
@@ -4354,7 +4664,6 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
|
|
|
4354
4664
|
exportData[tableName] = data || [];
|
|
4355
4665
|
}
|
|
4356
4666
|
catch (error) {
|
|
4357
|
-
// If table doesn't exist or can't be accessed, skip it
|
|
4358
4667
|
exportData[tableName] = [];
|
|
4359
4668
|
}
|
|
4360
4669
|
}
|
|
@@ -4707,7 +5016,8 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
|
|
|
4707
5016
|
let value = match[2].trim();
|
|
4708
5017
|
// Remove surrounding quotes if present (handles both single and double quotes)
|
|
4709
5018
|
if (value.length >= 2) {
|
|
4710
|
-
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
5019
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
5020
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
4711
5021
|
value = value.slice(1, -1).trim();
|
|
4712
5022
|
}
|
|
4713
5023
|
}
|