better-auth-studio 1.0.59-beta.8 → 1.0.59

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.
@@ -1 +1 @@
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;AA8D9C,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,CAsmLR"}
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,CAkzLR"}
package/dist/routes.js CHANGED
@@ -8,10 +8,10 @@ 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';
14
- import { possiblePaths } from './config.js';
15
15
  const config = {
16
16
  N: 16384,
17
17
  r: 16,
@@ -4348,7 +4348,9 @@ export function createRoutes(authConfig, configPath, geoDbPath) {
4348
4348
  attrs.push(`unique: ${field.unique ? 'true' : 'false'}`);
4349
4349
  attrs.push('input: false');
4350
4350
  // Handle defaultValue
4351
- if (field.defaultValue !== undefined && field.defaultValue !== null && field.defaultValue !== '') {
4351
+ if (field.defaultValue !== undefined &&
4352
+ field.defaultValue !== null &&
4353
+ field.defaultValue !== '') {
4352
4354
  if (field.type === 'string') {
4353
4355
  attrs.push(`defaultValue: "${field.defaultValue}"`);
4354
4356
  }
@@ -4390,15 +4392,15 @@ ${fields}
4390
4392
  if (!code.trim())
4391
4393
  return '';
4392
4394
  const lines = code.split('\n');
4393
- const nonEmptyLines = lines.filter(line => line.trim());
4395
+ const nonEmptyLines = lines.filter((line) => line.trim());
4394
4396
  if (nonEmptyLines.length === 0)
4395
4397
  return '';
4396
- const minIndent = Math.min(...nonEmptyLines.map(line => {
4398
+ const minIndent = Math.min(...nonEmptyLines.map((line) => {
4397
4399
  const match = line.match(/^(\s*)/);
4398
4400
  return match ? match[1].length : 0;
4399
4401
  }));
4400
4402
  return lines
4401
- .map(line => {
4403
+ .map((line) => {
4402
4404
  if (!line.trim())
4403
4405
  return '';
4404
4406
  const currentIndent = line.match(/^(\s*)/)?.[1] || '';
@@ -5120,14 +5122,11 @@ export const authClient = createAuthClient({
5120
5122
  }
5121
5123
  const envPath = join(process.cwd(), '.env');
5122
5124
  const envLocalPath = join(process.cwd(), '.env.local');
5123
- // Try .env.local first, then .env
5124
5125
  const targetPath = existsSync(envLocalPath) ? envLocalPath : envPath;
5125
5126
  const envContent = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : '';
5126
- // Generate environment variable names
5127
5127
  const providerUpper = provider.toUpperCase();
5128
5128
  const clientIdKey = `${providerUpper}_CLIENT_ID`;
5129
5129
  const clientSecretKey = `${providerUpper}_CLIENT_SECRET`;
5130
- // Parse existing .env file
5131
5130
  const envLines = envContent.split('\n');
5132
5131
  const existingCredentials = {};
5133
5132
  envLines.forEach((line) => {
@@ -5289,6 +5288,194 @@ export const authClient = createAuthClient({
5289
5288
  });
5290
5289
  }
5291
5290
  });
5291
+ router.post('/api/tools/apply-email-template', async (req, res) => {
5292
+ try {
5293
+ const { subject, html, templateId } = req.body || {};
5294
+ if (!subject || !html || !templateId) {
5295
+ return res
5296
+ .status(400)
5297
+ .json({ success: false, message: 'templateId, subject and html are required' });
5298
+ }
5299
+ const authPath = join(process.cwd(), 'examples/nextjs/prisma/lib/auth.ts');
5300
+ if (!existsSync(authPath)) {
5301
+ return res.status(404).json({ success: false, message: 'auth.ts not found' });
5302
+ }
5303
+ let fileContent = readFileSync(authPath, 'utf-8');
5304
+ const escapedSubject = subject.replace(/`/g, '\\`').replace(/\${/g, '\\${');
5305
+ const escapedHtml = html.replace(/`/g, '\\`').replace(/\${/g, '\\${');
5306
+ if (!fileContent.includes("from 'resend'")) {
5307
+ fileContent = `import { Resend } from 'resend';\n` + fileContent;
5308
+ }
5309
+ if (!fileContent.includes('const resend = new Resend(')) {
5310
+ const importBlockEnd = fileContent.indexOf('\n', fileContent.lastIndexOf('import'));
5311
+ if (importBlockEnd > -1) {
5312
+ fileContent =
5313
+ fileContent.slice(0, importBlockEnd + 1) +
5314
+ `const resend = new Resend(process.env.RESEND_API_KEY || '');\n` +
5315
+ fileContent.slice(importBlockEnd + 1);
5316
+ }
5317
+ else {
5318
+ fileContent =
5319
+ `const resend = new Resend(process.env.RESEND_API_KEY || '');\n` + fileContent;
5320
+ }
5321
+ }
5322
+ const handlers = {
5323
+ 'password-reset': {
5324
+ regex: /sendResetPassword\s*:\s*async\s*\([^)]*\)\s*=>\s*\{[\s\S]*?\},?/,
5325
+ fn: `sendResetPassword: async ({ user, url, token }, request) => {
5326
+ const subject = \\\`${escapedSubject}\\\`
5327
+ .replace(/{{user.name}}/g, user?.name || '')
5328
+ .replace(/{{user.email}}/g, user?.email || '');
5329
+
5330
+ const html = \\\`${escapedHtml}\\\`
5331
+ .replace(/{{user.name}}/g, user?.name || '')
5332
+ .replace(/{{user.email}}/g, user?.email || '')
5333
+ .replace(/{{url}}/g, url || '')
5334
+ .replace(/{{token}}/g, token || '');
5335
+
5336
+ void sendEmail({
5337
+ to: user.email,
5338
+ subject,
5339
+ html,
5340
+ });
5341
+ }`,
5342
+ },
5343
+ 'email-verification': {
5344
+ regex: /sendVerificationEmail\s*:\s*async\s*\([^)]*\)\s*=>\s*\{[\s\S]*?\},?/,
5345
+ fn: `sendVerificationEmail: async ({ user, url, token }, request) => {
5346
+ const subject = \\\`${escapedSubject}\\\`
5347
+ .replace(/{{user.name}}/g, user?.name || '')
5348
+ .replace(/{{user.email}}/g, user?.email || '');
5349
+
5350
+ const html = \\\`${escapedHtml}\\\`
5351
+ .replace(/{{user.name}}/g, user?.name || '')
5352
+ .replace(/{{user.email}}/g, user?.email || '')
5353
+ .replace(/{{url}}/g, url || '')
5354
+ .replace(/{{token}}/g, token || '');
5355
+
5356
+ void sendEmail({
5357
+ to: user.email,
5358
+ subject,
5359
+ html,
5360
+ });
5361
+ }`,
5362
+ },
5363
+ 'magic-link': {
5364
+ regex: /sendMagicLink\s*:\s*async\s*\([^)]*\)\s*=>\s*\{[\s\S]*?\},?/,
5365
+ fn: `sendMagicLink: async ({ email, token, url }, ctx) => {
5366
+ const subject = \\\`${escapedSubject}\\\`
5367
+ .replace(/{{user.email}}/g, email || '');
5368
+
5369
+ const html = \\\`${escapedHtml}\\\`
5370
+ .replace(/{{user.email}}/g, email || '')
5371
+ .replace(/{{url}}/g, url || '')
5372
+ .replace(/{{token}}/g, token || '');
5373
+
5374
+ void sendEmail({
5375
+ to: email,
5376
+ subject,
5377
+ html,
5378
+ });
5379
+ }`,
5380
+ },
5381
+ 'org-invitation': {
5382
+ regex: /sendInvitationEmail\s*:\s*async\s*\([^)]*\)\s*=>\s*\{[\s\S]*?\},?/,
5383
+ fn: `sendInvitationEmail: async ({ data, request }: {
5384
+ data: {
5385
+ invitation: {
5386
+ id: string;
5387
+ organizationId: string;
5388
+ email: string;
5389
+ role: string;
5390
+ status: "pending" | "accepted" | "rejected" | "canceled";
5391
+ inviterId: string;
5392
+ expiresAt: Date;
5393
+ createdAt: Date;
5394
+ teamId?: string | null | undefined;
5395
+ };
5396
+ organization: { name?: string; slug?: string };
5397
+ inviter: { user: { name?: string; email?: string } };
5398
+ };
5399
+ request?: Request;
5400
+ }) => {
5401
+ const { invitation, organization, inviter } = data;
5402
+ const baseUrl = process.env.BETTER_AUTH_URL || 'http://localhost:3000';
5403
+ const url = \\\`\\\${baseUrl}/accept-invitation?id=\\\${invitation.id}\\\`;
5404
+
5405
+ const subject = \\\`${escapedSubject}\\\`
5406
+ .replace(/{{organization.name}}/g, organization?.name || '')
5407
+ .replace(/{{invitation.role}}/g, invitation.role || '')
5408
+ .replace(/{{inviter.user.name}}/g, inviter?.user?.name || '')
5409
+ .replace(/{{inviter.user.email}}/g, inviter?.user?.email || '')
5410
+ .replace(/{{invitation.email}}/g, invitation.email || '');
5411
+
5412
+ const html = \\\`${escapedHtml}\\\`
5413
+ .replace(/{{invitation.url}}/g, url)
5414
+ .replace(/{{invitation.role}}/g, invitation.role || '')
5415
+ .replace(/{{organization.name}}/g, organization?.name || '')
5416
+ .replace(/{{organization.slug}}/g, organization?.slug || '')
5417
+ .replace(/{{inviter.user.name}}/g, inviter?.user?.name || '')
5418
+ .replace(/{{inviter.user.email}}/g, inviter?.user?.email || '')
5419
+ .replace(/{{invitation.email}}/g, invitation.email || '')
5420
+ .replace(/{{invitation.expiresAt}}/g, invitation.expiresAt?.toLocaleString() || '')
5421
+ .replace(/{{expiresIn}}/g, invitation.expiresAt ? \\\`\\\${Math.ceil((invitation.expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24))} days\\\` : '');
5422
+
5423
+ void sendEmail({
5424
+ to: invitation.email,
5425
+ subject,
5426
+ html,
5427
+ });
5428
+ }`,
5429
+ },
5430
+ };
5431
+ const handler = handlers[templateId];
5432
+ if (!handler) {
5433
+ return res
5434
+ .status(400)
5435
+ .json({ success: false, message: 'Unsupported templateId for apply' });
5436
+ }
5437
+ if (!fileContent.includes('const sendEmail = async')) {
5438
+ const insertPoint = fileContent.indexOf('export const auth');
5439
+ fileContent =
5440
+ fileContent.slice(0, insertPoint) +
5441
+ `const sendEmail = async ({ to, subject, text, html }: { to: string; subject: string; text?: string; html?: string }) => {\n console.log(\`Sending email to \${to} | \${subject}\`);\n if (text) console.log('Text content:', text);\n if (html) console.log('HTML content:', html);\n};\n\n` +
5442
+ fileContent.slice(insertPoint);
5443
+ }
5444
+ if (handler.regex.test(fileContent)) {
5445
+ fileContent = fileContent.replace(handler.regex, `${handler.fn},`);
5446
+ }
5447
+ else {
5448
+ if (templateId === 'org-invitation') {
5449
+ const orgPluginRegex = /organization\(\s*\{\s*/;
5450
+ if (!orgPluginRegex.test(fileContent)) {
5451
+ return res.status(400).json({
5452
+ success: false,
5453
+ message: 'organization plugin config not found in auth.ts',
5454
+ });
5455
+ }
5456
+ fileContent = fileContent.replace(orgPluginRegex, (m) => `${m}${handler.fn},\n `);
5457
+ }
5458
+ else {
5459
+ const emailAndPasswordRegex = /emailAndPassword\s*:\s*\{\s*/;
5460
+ if (!emailAndPasswordRegex.test(fileContent)) {
5461
+ return res.status(400).json({
5462
+ success: false,
5463
+ message: 'emailAndPassword config not found in auth.ts',
5464
+ });
5465
+ }
5466
+ fileContent = fileContent.replace(emailAndPasswordRegex, (m) => `${m}${handler.fn},\n `);
5467
+ }
5468
+ }
5469
+ writeFileSync(authPath, fileContent, 'utf-8');
5470
+ res.json({ success: true, path: authPath });
5471
+ }
5472
+ catch (error) {
5473
+ res.status(500).json({
5474
+ success: false,
5475
+ message: error?.message || 'Failed to apply invitation template',
5476
+ });
5477
+ }
5478
+ });
5292
5479
  return router;
5293
5480
  }
5294
5481
  //# sourceMappingURL=routes.js.map