better-auth-studio 1.0.58-beta.4 → 1.0.59-beta.1
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 +310 -2
- package/dist/routes.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/{main-tRS3LNnj.js → main-BH6u-YKB.js} +128 -119
- package/public/assets/main-KBM4GS4q.css +1 -0
- 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,CAi8KR"}
|
package/dist/routes.js
CHANGED
|
@@ -4331,6 +4331,316 @@ 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
|
+
const middlewareCode = middleware
|
|
4429
|
+
.map((mw) => {
|
|
4430
|
+
return ` {
|
|
4431
|
+
path: "${mw.path}",
|
|
4432
|
+
middleware: createAuthMiddleware(async (ctx) => {
|
|
4433
|
+
// ${mw.name || 'Middleware'}
|
|
4434
|
+
${mw.middlewareLogic || '// Middleware logic here'}
|
|
4435
|
+
}),
|
|
4436
|
+
}`;
|
|
4437
|
+
})
|
|
4438
|
+
.join(',\n');
|
|
4439
|
+
// Generate endpoints
|
|
4440
|
+
const endpointsCode = endpoints.length > 0
|
|
4441
|
+
? endpoints
|
|
4442
|
+
.map((endpoint) => {
|
|
4443
|
+
const endpointName = endpoint.name?.trim() || `endpoint${endpoints.indexOf(endpoint) + 1}`;
|
|
4444
|
+
const sanitizedName = endpointName.replace(/[^a-zA-Z0-9]/g, '');
|
|
4445
|
+
const endpointPath = endpoint.path?.trim() || `/${camelCaseName}/${sanitizedName}`;
|
|
4446
|
+
const handlerLogic = endpoint.handlerLogic ||
|
|
4447
|
+
'// Endpoint handler logic here\n return ctx.json({ success: true });';
|
|
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');
|
|
4461
|
+
return ` ${sanitizedName}: createAuthEndpoint(
|
|
4462
|
+
"${endpointPath}",
|
|
4463
|
+
{
|
|
4464
|
+
method: "${endpoint.method || 'POST'}",
|
|
4465
|
+
},
|
|
4466
|
+
async (ctx) => {
|
|
4467
|
+
// ${endpoint.name || sanitizedName}
|
|
4468
|
+
${formattedHandlerLogic}
|
|
4469
|
+
},
|
|
4470
|
+
),`;
|
|
4471
|
+
})
|
|
4472
|
+
.join('\n')
|
|
4473
|
+
: '';
|
|
4474
|
+
const rateLimitCode = rateLimit
|
|
4475
|
+
? (() => {
|
|
4476
|
+
const rl = rateLimit;
|
|
4477
|
+
let pathMatcher = '';
|
|
4478
|
+
if (rl.pathType === 'exact') {
|
|
4479
|
+
pathMatcher = `(path: string) => path === "${rl.path}"`;
|
|
4480
|
+
}
|
|
4481
|
+
else if (rl.pathType === 'prefix') {
|
|
4482
|
+
pathMatcher = `(path: string) => path.startsWith("${rl.path}")`;
|
|
4483
|
+
}
|
|
4484
|
+
else if (rl.pathType === 'regex') {
|
|
4485
|
+
pathMatcher = `(path: string) => new RegExp("${rl.path.replace(/"/g, '\\"')}").test(path)`;
|
|
4486
|
+
}
|
|
4487
|
+
else {
|
|
4488
|
+
pathMatcher = `(path: string) => true`;
|
|
4489
|
+
}
|
|
4490
|
+
const windowValue = rl.window && rl.window > 0 ? rl.window : 15 * 60 * 1000;
|
|
4491
|
+
const maxValue = rl.max && rl.max > 0 ? rl.max : 100;
|
|
4492
|
+
return ` window: ${windowValue},
|
|
4493
|
+
max: ${maxValue},
|
|
4494
|
+
pathMatcher: ${pathMatcher}`;
|
|
4495
|
+
})()
|
|
4496
|
+
: '';
|
|
4497
|
+
const cleanCode = (code) => {
|
|
4498
|
+
return code
|
|
4499
|
+
.split('\n')
|
|
4500
|
+
.map((line) => line.trimEnd())
|
|
4501
|
+
.filter((line, index, arr) => {
|
|
4502
|
+
if (line === '' && arr[index + 1] === '')
|
|
4503
|
+
return false;
|
|
4504
|
+
return true;
|
|
4505
|
+
})
|
|
4506
|
+
.join('\n')
|
|
4507
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
4508
|
+
.trim();
|
|
4509
|
+
};
|
|
4510
|
+
const pluginParts = [];
|
|
4511
|
+
if (schemaCode) {
|
|
4512
|
+
pluginParts.push(` schema: {\n${schemaCode}\n }`);
|
|
4513
|
+
}
|
|
4514
|
+
if (beforeHooks.length > 0 || afterHooks.length > 0) {
|
|
4515
|
+
const hooksParts = [];
|
|
4516
|
+
if (beforeHooks.length > 0) {
|
|
4517
|
+
hooksParts.push(` before: [\n${beforeHooks.join(',\n')}\n ]`);
|
|
4518
|
+
}
|
|
4519
|
+
if (afterHooks.length > 0) {
|
|
4520
|
+
hooksParts.push(` after: [\n${afterHooks.join(',\n')}\n ]`);
|
|
4521
|
+
}
|
|
4522
|
+
pluginParts.push(` hooks: {\n${hooksParts.join(',\n')}\n }`);
|
|
4523
|
+
}
|
|
4524
|
+
if (middlewareCode) {
|
|
4525
|
+
pluginParts.push(` middlewares: [\n${middlewareCode}\n ]`);
|
|
4526
|
+
}
|
|
4527
|
+
if (endpointsCode) {
|
|
4528
|
+
pluginParts.push(` endpoints: {\n${endpointsCode}\n }`);
|
|
4529
|
+
}
|
|
4530
|
+
if (rateLimitCode) {
|
|
4531
|
+
pluginParts.push(` rateLimit: {\n${rateLimitCode}\n }`);
|
|
4532
|
+
}
|
|
4533
|
+
// Generate server plugin code
|
|
4534
|
+
const imports = ['import type { BetterAuthPlugin } from "@better-auth/core"'];
|
|
4535
|
+
if (hooks.length > 0 || middleware.length > 0 || endpoints.length > 0) {
|
|
4536
|
+
imports.push('import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api"');
|
|
4537
|
+
}
|
|
4538
|
+
const serverPluginBody = pluginParts.length > 0
|
|
4539
|
+
? ` id: "${camelCaseName}",\n${pluginParts.join(',\n')}`
|
|
4540
|
+
: ` id: "${camelCaseName}"`;
|
|
4541
|
+
const serverPluginCode = cleanCode(`import type { BetterAuthPlugin } from "@better-auth/core";
|
|
4542
|
+
${imports.join('\n')}
|
|
4543
|
+
|
|
4544
|
+
${description ? `/**\n * ${description.replace(/\n/g, '\n * ')}\n */` : ''}
|
|
4545
|
+
export const ${camelCaseName} = (options?: Record<string, any>) => {
|
|
4546
|
+
return {
|
|
4547
|
+
${serverPluginBody}
|
|
4548
|
+
} satisfies BetterAuthPlugin;
|
|
4549
|
+
};
|
|
4550
|
+
`);
|
|
4551
|
+
const pathMethods = endpoints.length > 0
|
|
4552
|
+
? endpoints
|
|
4553
|
+
.map((endpoint) => {
|
|
4554
|
+
const endpointPath = endpoint.path?.trim() || '';
|
|
4555
|
+
const method = endpoint.method || 'POST';
|
|
4556
|
+
return ` "${endpointPath}": "${method}"`;
|
|
4557
|
+
})
|
|
4558
|
+
.join(',\n')
|
|
4559
|
+
: '';
|
|
4560
|
+
const sessionAffectingPaths = endpoints
|
|
4561
|
+
.filter((endpoint) => {
|
|
4562
|
+
const path = endpoint.path?.trim() || '';
|
|
4563
|
+
return path.includes('/sign-in') || path.includes('/sign-up');
|
|
4564
|
+
})
|
|
4565
|
+
.map((endpoint) => {
|
|
4566
|
+
const endpointPath = endpoint.path?.trim() || '';
|
|
4567
|
+
return ` {
|
|
4568
|
+
matcher: (path) => path === "${endpointPath}",
|
|
4569
|
+
signal: "$sessionSignal",
|
|
4570
|
+
}`;
|
|
4571
|
+
});
|
|
4572
|
+
const atomListenersCode = sessionAffectingPaths.length > 0
|
|
4573
|
+
? `\n atomListeners: [\n${sessionAffectingPaths.join(',\n')}\n ],`
|
|
4574
|
+
: '';
|
|
4575
|
+
const clientPluginCode = cleanCode(`import type { BetterAuthClientPlugin } from "@better-auth/core";
|
|
4576
|
+
import type { ${camelCaseName} } from "..";
|
|
4577
|
+
|
|
4578
|
+
export const ${camelCaseName}Client = () => {
|
|
4579
|
+
return {
|
|
4580
|
+
id: "${camelCaseName}" as const,
|
|
4581
|
+
$InferServerPlugin: {} as ReturnType<typeof ${camelCaseName}>,${pathMethods ? `\n pathMethods: {\n${pathMethods}\n },` : ''}${atomListenersCode}
|
|
4582
|
+
} satisfies BetterAuthClientPlugin;
|
|
4583
|
+
};
|
|
4584
|
+
`);
|
|
4585
|
+
// Generate server setup code
|
|
4586
|
+
const serverSetupCode = cleanCode(`import { betterAuth } from "@better-auth/core";
|
|
4587
|
+
import { ${camelCaseName} } from "./plugin/${camelCaseName}";
|
|
4588
|
+
|
|
4589
|
+
export const auth = betterAuth({
|
|
4590
|
+
// ... your existing config
|
|
4591
|
+
plugins: [
|
|
4592
|
+
${camelCaseName}(),
|
|
4593
|
+
],
|
|
4594
|
+
});
|
|
4595
|
+
`);
|
|
4596
|
+
const frameworkImportMap = {
|
|
4597
|
+
react: 'better-auth/react',
|
|
4598
|
+
svelte: 'better-auth/svelte',
|
|
4599
|
+
solid: 'better-auth/solid',
|
|
4600
|
+
vue: 'better-auth/vue',
|
|
4601
|
+
};
|
|
4602
|
+
const frameworkImport = frameworkImportMap[clientFramework] || 'better-auth/react';
|
|
4603
|
+
// Get baseURL based on framework
|
|
4604
|
+
const baseURLMap = {
|
|
4605
|
+
react: 'process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"',
|
|
4606
|
+
svelte: 'import.meta.env.PUBLIC_BETTER_AUTH_URL || "http://localhost:5173"',
|
|
4607
|
+
solid: 'import.meta.env.PUBLIC_BETTER_AUTH_URL || "http://localhost:5173"',
|
|
4608
|
+
vue: 'import.meta.env.PUBLIC_BETTER_AUTH_URL || "http://localhost:5173"',
|
|
4609
|
+
};
|
|
4610
|
+
const baseURL = baseURLMap[clientFramework] ||
|
|
4611
|
+
'process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"';
|
|
4612
|
+
const clientSetupCode = cleanCode(`import { createAuthClient } from "${frameworkImport}";
|
|
4613
|
+
import { ${camelCaseName}Client } from "./plugin/${camelCaseName}/client";
|
|
4614
|
+
|
|
4615
|
+
export const authClient = createAuthClient({
|
|
4616
|
+
baseURL: ${baseURL},
|
|
4617
|
+
plugins: [
|
|
4618
|
+
${camelCaseName}Client(),
|
|
4619
|
+
],
|
|
4620
|
+
});
|
|
4621
|
+
`);
|
|
4622
|
+
return res.json({
|
|
4623
|
+
success: true,
|
|
4624
|
+
plugin: {
|
|
4625
|
+
name: sanitizedName,
|
|
4626
|
+
server: serverPluginCode,
|
|
4627
|
+
client: clientPluginCode,
|
|
4628
|
+
serverSetup: serverSetupCode,
|
|
4629
|
+
clientSetup: clientSetupCode,
|
|
4630
|
+
filePaths: {
|
|
4631
|
+
server: `plugin/${camelCaseName}/index.ts`,
|
|
4632
|
+
client: `plugin/${camelCaseName}/client/index.ts`,
|
|
4633
|
+
serverSetup: 'auth.ts',
|
|
4634
|
+
clientSetup: 'auth-client.ts',
|
|
4635
|
+
},
|
|
4636
|
+
},
|
|
4637
|
+
});
|
|
4638
|
+
}
|
|
4639
|
+
catch (error) {
|
|
4640
|
+
const message = error instanceof Error ? error.message : 'Failed to generate plugin';
|
|
4641
|
+
res.status(500).json({ success: false, error: message });
|
|
4642
|
+
}
|
|
4643
|
+
});
|
|
4334
4644
|
router.post('/api/tools/export', async (req, res) => {
|
|
4335
4645
|
try {
|
|
4336
4646
|
const { tables, format, limit } = req.body;
|
|
@@ -4344,7 +4654,6 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
|
|
|
4344
4654
|
return res.status(500).json({ success: false, error: 'Auth adapter not available' });
|
|
4345
4655
|
}
|
|
4346
4656
|
const exportData = {};
|
|
4347
|
-
// Fetch data from each table
|
|
4348
4657
|
for (const tableName of tables) {
|
|
4349
4658
|
try {
|
|
4350
4659
|
const data = await adapter.findMany({
|
|
@@ -4354,7 +4663,6 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
|
|
|
4354
4663
|
exportData[tableName] = data || [];
|
|
4355
4664
|
}
|
|
4356
4665
|
catch (error) {
|
|
4357
|
-
// If table doesn't exist or can't be accessed, skip it
|
|
4358
4666
|
exportData[tableName] = [];
|
|
4359
4667
|
}
|
|
4360
4668
|
}
|