nest-authme 1.2.3 → 1.3.0
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/cli.js +211 -28
- package/dist/cli.js.map +1 -1
- package/dist/generator/templates/jwt/auth.service.ts.hbs +11 -7
- package/dist/generator/templates/prisma/schema.prisma.models.hbs +47 -0
- package/dist/gui/index.js +275 -107
- package/dist/gui/index.js.map +1 -1
- package/dist/gui/orchestrator.js +273 -105
- package/dist/gui/orchestrator.js.map +1 -1
- package/dist/gui/server.js +275 -107
- package/dist/gui/server.js.map +1 -1
- package/dist/index.js +178 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Injectable, UnauthorizedException{{#if features.resetPassword}}, BadRequestException{{/if}}{{#if features.emailVerification}}, NotFoundException{{/if}}{{#if features.accountLockout}}, ForbiddenException{{/if}} } from '@nestjs/common';
|
|
2
|
-
{{#if (or features.emailVerification features.resetPassword)}}
|
|
2
|
+
{{#if (or features.emailVerification features.resetPassword features.refreshTokens)}}
|
|
3
3
|
import * as crypto from 'crypto';
|
|
4
4
|
{{/if}}
|
|
5
5
|
import { JwtService } from '@nestjs/jwt';
|
|
@@ -57,7 +57,7 @@ export class AuthService {
|
|
|
57
57
|
* Validate user credentials
|
|
58
58
|
*/
|
|
59
59
|
async validateUser(email: string, password: string): Promise<any> {
|
|
60
|
-
const user = await this.usersService.findByEmail(email);
|
|
60
|
+
const user = await this.usersService.findByEmail(email.toLowerCase());
|
|
61
61
|
if (!user) {
|
|
62
62
|
return null;
|
|
63
63
|
}
|
|
@@ -152,7 +152,7 @@ export class AuthService {
|
|
|
152
152
|
const hashedPassword = await bcrypt.hash(registerDto.password, bcryptRounds);
|
|
153
153
|
|
|
154
154
|
const user = await this.usersService.create({
|
|
155
|
-
email: registerDto.email,
|
|
155
|
+
email: registerDto.email.toLowerCase(),
|
|
156
156
|
{{#if features.useUsername}}
|
|
157
157
|
username: registerDto.username,
|
|
158
158
|
{{/if}}
|
|
@@ -244,6 +244,10 @@ export class AuthService {
|
|
|
244
244
|
* Verify email with token
|
|
245
245
|
*/
|
|
246
246
|
async verifyEmail(token: string): Promise<{ message: string }> {
|
|
247
|
+
if (!token) {
|
|
248
|
+
throw new NotFoundException('Invalid verification token');
|
|
249
|
+
}
|
|
250
|
+
|
|
247
251
|
const user = await this.usersService.findByVerificationToken(token);
|
|
248
252
|
if (!user) {
|
|
249
253
|
throw new NotFoundException('Invalid verification token');
|
|
@@ -261,7 +265,7 @@ export class AuthService {
|
|
|
261
265
|
{{else}}
|
|
262
266
|
async resendVerification(email: string): Promise<{ verificationToken: string; message: string }> {
|
|
263
267
|
{{/if}}
|
|
264
|
-
const user = await this.usersService.findByEmail(email);
|
|
268
|
+
const user = await this.usersService.findByEmail(email.toLowerCase());
|
|
265
269
|
if (!user) {
|
|
266
270
|
throw new NotFoundException('User not found');
|
|
267
271
|
}
|
|
@@ -294,7 +298,7 @@ export class AuthService {
|
|
|
294
298
|
*/
|
|
295
299
|
{{#if features.emailService}}
|
|
296
300
|
async forgotPassword(forgotPasswordDto: ForgotPasswordDto): Promise<{ message: string }> {
|
|
297
|
-
const user = await this.usersService.findByEmail(forgotPasswordDto.email);
|
|
301
|
+
const user = await this.usersService.findByEmail(forgotPasswordDto.email.toLowerCase());
|
|
298
302
|
if (!user) {
|
|
299
303
|
// Return success even if user not found to prevent email enumeration
|
|
300
304
|
return { message: 'If the email exists, a password reset link has been sent' };
|
|
@@ -311,7 +315,7 @@ export class AuthService {
|
|
|
311
315
|
}
|
|
312
316
|
{{else}}
|
|
313
317
|
async forgotPassword(forgotPasswordDto: ForgotPasswordDto): Promise<{ resetToken: string; message: string }> {
|
|
314
|
-
const user = await this.usersService.findByEmail(forgotPasswordDto.email);
|
|
318
|
+
const user = await this.usersService.findByEmail(forgotPasswordDto.email.toLowerCase());
|
|
315
319
|
if (!user) {
|
|
316
320
|
// Return success even if user not found to prevent email enumeration
|
|
317
321
|
return { resetToken: '', message: 'If the email exists, a reset token has been generated' };
|
|
@@ -356,7 +360,7 @@ export class AuthService {
|
|
|
356
360
|
*/
|
|
357
361
|
private async createRefreshToken(userId: string): Promise<any> {
|
|
358
362
|
const token = this.jwtService.sign(
|
|
359
|
-
{ sub: userId },
|
|
363
|
+
{ sub: userId, jti: crypto.randomBytes(16).toString('hex') },
|
|
360
364
|
{ expiresIn: '{{jwt.refreshExpiration}}' }
|
|
361
365
|
);
|
|
362
366
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
model User {
|
|
2
|
+
id String @id @default(uuid())
|
|
3
|
+
email String @unique
|
|
4
|
+
{{#if features.useUsername}}
|
|
5
|
+
username String @unique
|
|
6
|
+
{{/if}}
|
|
7
|
+
password String{{#if oauth}}?{{/if}}
|
|
8
|
+
{{#if oauth}}
|
|
9
|
+
{{#if oauth.google}}
|
|
10
|
+
googleId String? @unique
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if oauth.github}}
|
|
13
|
+
githubId String? @unique
|
|
14
|
+
{{/if}}
|
|
15
|
+
{{/if}}
|
|
16
|
+
{{#if rbac.enabled}}
|
|
17
|
+
roles String[] @default(["User"])
|
|
18
|
+
{{/if}}
|
|
19
|
+
{{#if features.emailVerification}}
|
|
20
|
+
isEmailVerified Boolean @default(false)
|
|
21
|
+
emailVerificationToken String?
|
|
22
|
+
{{/if}}
|
|
23
|
+
{{#if features.resetPassword}}
|
|
24
|
+
passwordResetToken String?
|
|
25
|
+
passwordResetExpires DateTime?
|
|
26
|
+
{{/if}}
|
|
27
|
+
{{#if features.accountLockout}}
|
|
28
|
+
failedLoginAttempts Int @default(0)
|
|
29
|
+
lockUntil DateTime?
|
|
30
|
+
{{/if}}
|
|
31
|
+
{{#if features.refreshTokens}}
|
|
32
|
+
refreshTokens RefreshToken[]
|
|
33
|
+
{{/if}}
|
|
34
|
+
createdAt DateTime @default(now())
|
|
35
|
+
updatedAt DateTime @updatedAt
|
|
36
|
+
}
|
|
37
|
+
{{#if features.refreshTokens}}
|
|
38
|
+
|
|
39
|
+
model RefreshToken {
|
|
40
|
+
id String @id @default(uuid())
|
|
41
|
+
token String @unique
|
|
42
|
+
userId String
|
|
43
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
44
|
+
expiresAt DateTime
|
|
45
|
+
createdAt DateTime @default(now())
|
|
46
|
+
}
|
|
47
|
+
{{/if}}
|
package/dist/gui/index.js
CHANGED
|
@@ -37,6 +37,103 @@ var init_cjs_shims = __esm({
|
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
// src/generator/template-engine.ts
|
|
41
|
+
var import_handlebars, path2, fs2, TemplateEngine;
|
|
42
|
+
var init_template_engine = __esm({
|
|
43
|
+
"src/generator/template-engine.ts"() {
|
|
44
|
+
"use strict";
|
|
45
|
+
init_cjs_shims();
|
|
46
|
+
import_handlebars = __toESM(require("handlebars"));
|
|
47
|
+
path2 = __toESM(require("path"));
|
|
48
|
+
fs2 = __toESM(require("fs-extra"));
|
|
49
|
+
TemplateEngine = class {
|
|
50
|
+
handlebars;
|
|
51
|
+
templateCache;
|
|
52
|
+
templatesDir;
|
|
53
|
+
constructor(templatesDir) {
|
|
54
|
+
this.handlebars = import_handlebars.default.create();
|
|
55
|
+
this.templateCache = /* @__PURE__ */ new Map();
|
|
56
|
+
this.templatesDir = templatesDir || path2.join(__dirname, "generator", "templates");
|
|
57
|
+
this.registerHelpers();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Register Handlebars helpers
|
|
61
|
+
*/
|
|
62
|
+
registerHelpers() {
|
|
63
|
+
this.handlebars.registerHelper("eq", (a, b) => a === b);
|
|
64
|
+
this.handlebars.registerHelper("ne", (a, b) => a !== b);
|
|
65
|
+
this.handlebars.registerHelper("or", (a, b) => a || b);
|
|
66
|
+
this.handlebars.registerHelper("and", (a, b) => a && b);
|
|
67
|
+
this.handlebars.registerHelper("includes", (arr, item) => arr?.includes(item));
|
|
68
|
+
this.handlebars.registerHelper("capitalize", (str) => {
|
|
69
|
+
if (!str) return "";
|
|
70
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
71
|
+
});
|
|
72
|
+
this.handlebars.registerHelper("lowercase", (str) => {
|
|
73
|
+
if (!str) return "";
|
|
74
|
+
return str.toLowerCase();
|
|
75
|
+
});
|
|
76
|
+
this.handlebars.registerHelper("uppercase", (str) => {
|
|
77
|
+
if (!str) return "";
|
|
78
|
+
return str.toUpperCase();
|
|
79
|
+
});
|
|
80
|
+
this.handlebars.registerHelper("camelCase", (str) => {
|
|
81
|
+
if (!str) return "";
|
|
82
|
+
return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
83
|
+
});
|
|
84
|
+
this.handlebars.registerHelper("pascalCase", (str) => {
|
|
85
|
+
if (!str) return "";
|
|
86
|
+
const camel = str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
87
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Render a template with context
|
|
92
|
+
*/
|
|
93
|
+
async render(templatePath, context) {
|
|
94
|
+
const template = await this.loadTemplate(templatePath);
|
|
95
|
+
return template(context);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Load a template (with caching)
|
|
99
|
+
*/
|
|
100
|
+
async loadTemplate(templatePath) {
|
|
101
|
+
if (this.templateCache.has(templatePath)) {
|
|
102
|
+
return this.templateCache.get(templatePath);
|
|
103
|
+
}
|
|
104
|
+
const fullPath = path2.join(this.templatesDir, templatePath);
|
|
105
|
+
if (!await fs2.pathExists(fullPath)) {
|
|
106
|
+
throw new Error(`Template not found: ${templatePath}`);
|
|
107
|
+
}
|
|
108
|
+
const source = await fs2.readFile(fullPath, "utf-8");
|
|
109
|
+
const compiled = this.handlebars.compile(source);
|
|
110
|
+
this.templateCache.set(templatePath, compiled);
|
|
111
|
+
return compiled;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Clear template cache
|
|
115
|
+
*/
|
|
116
|
+
clearCache() {
|
|
117
|
+
this.templateCache.clear();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// src/config/config-builder.ts
|
|
124
|
+
function buildTemplateContext(config) {
|
|
125
|
+
return {
|
|
126
|
+
...config
|
|
127
|
+
// Add any additional computed properties here
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
var init_config_builder = __esm({
|
|
131
|
+
"src/config/config-builder.ts"() {
|
|
132
|
+
"use strict";
|
|
133
|
+
init_cjs_shims();
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
40
137
|
// src/installer/ast-updater.ts
|
|
41
138
|
var import_ts_morph, fs4, AppModuleUpdater;
|
|
42
139
|
var init_ast_updater = __esm({
|
|
@@ -426,13 +523,139 @@ var init_main_ts_updater = __esm({
|
|
|
426
523
|
}
|
|
427
524
|
});
|
|
428
525
|
|
|
526
|
+
// src/installer/prisma-schema-updater.ts
|
|
527
|
+
var fs6, import_execa, PrismaSchemaUpdater;
|
|
528
|
+
var init_prisma_schema_updater = __esm({
|
|
529
|
+
"src/installer/prisma-schema-updater.ts"() {
|
|
530
|
+
"use strict";
|
|
531
|
+
init_cjs_shims();
|
|
532
|
+
fs6 = __toESM(require("fs-extra"));
|
|
533
|
+
import_execa = require("execa");
|
|
534
|
+
init_template_engine();
|
|
535
|
+
init_config_builder();
|
|
536
|
+
PrismaSchemaUpdater = class {
|
|
537
|
+
constructor(schemaPath) {
|
|
538
|
+
this.schemaPath = schemaPath;
|
|
539
|
+
}
|
|
540
|
+
backupPath = null;
|
|
541
|
+
/**
|
|
542
|
+
* Update prisma/schema.prisma with auth models
|
|
543
|
+
*/
|
|
544
|
+
async update(config) {
|
|
545
|
+
const exists = await fs6.pathExists(this.schemaPath);
|
|
546
|
+
if (!exists) {
|
|
547
|
+
return {
|
|
548
|
+
updated: false,
|
|
549
|
+
message: 'prisma/schema.prisma not found. Run "npx prisma init" first, then re-run nest-authme.',
|
|
550
|
+
skippedModels: []
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
const existingContent = await fs6.readFile(this.schemaPath, "utf-8");
|
|
554
|
+
const skippedModels = [];
|
|
555
|
+
const hasUser = /^\s*model\s+User\s*\{/m.test(existingContent);
|
|
556
|
+
const hasRefreshToken = /^\s*model\s+RefreshToken\s*\{/m.test(existingContent);
|
|
557
|
+
if (hasUser) {
|
|
558
|
+
skippedModels.push("User");
|
|
559
|
+
}
|
|
560
|
+
if (hasRefreshToken && config.features.refreshTokens) {
|
|
561
|
+
skippedModels.push("RefreshToken");
|
|
562
|
+
}
|
|
563
|
+
const needsUser = !hasUser;
|
|
564
|
+
const needsRefreshToken = config.features.refreshTokens && !hasRefreshToken;
|
|
565
|
+
if (!needsUser && !needsRefreshToken) {
|
|
566
|
+
return {
|
|
567
|
+
updated: false,
|
|
568
|
+
message: `Models already exist in schema.prisma: ${skippedModels.join(", ")}. Please check that your existing models have all required auth fields.`,
|
|
569
|
+
skippedModels
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
const templateEngine = new TemplateEngine();
|
|
573
|
+
const context = buildTemplateContext(config);
|
|
574
|
+
let modelsContent = await templateEngine.render("prisma/schema.prisma.models.hbs", context);
|
|
575
|
+
if (hasUser) {
|
|
576
|
+
modelsContent = modelsContent.replace(/model\s+User\s*\{[^}]*\}\n?/s, "");
|
|
577
|
+
}
|
|
578
|
+
if (hasRefreshToken) {
|
|
579
|
+
modelsContent = modelsContent.replace(/model\s+RefreshToken\s*\{[^}]*\}\n?/s, "");
|
|
580
|
+
}
|
|
581
|
+
modelsContent = modelsContent.replace(/\n{3,}/g, "\n\n").trim();
|
|
582
|
+
if (!modelsContent) {
|
|
583
|
+
return {
|
|
584
|
+
updated: false,
|
|
585
|
+
message: "No new models to add.",
|
|
586
|
+
skippedModels
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
await this.createBackup();
|
|
590
|
+
try {
|
|
591
|
+
const separator = "\n\n// === Auth models (added by nest-authme) ===\n\n";
|
|
592
|
+
const updatedContent = existingContent.trimEnd() + separator + modelsContent + "\n";
|
|
593
|
+
await fs6.writeFile(this.schemaPath, updatedContent, "utf-8");
|
|
594
|
+
await this.tryPrismaFormat();
|
|
595
|
+
const addedModels = [];
|
|
596
|
+
if (needsUser) addedModels.push("User");
|
|
597
|
+
if (needsRefreshToken) addedModels.push("RefreshToken");
|
|
598
|
+
let message = `Added ${addedModels.join(", ")} model(s) to prisma/schema.prisma`;
|
|
599
|
+
if (skippedModels.length > 0) {
|
|
600
|
+
message += `. Skipped existing: ${skippedModels.join(", ")} (check for missing auth fields)`;
|
|
601
|
+
}
|
|
602
|
+
return {
|
|
603
|
+
updated: true,
|
|
604
|
+
message,
|
|
605
|
+
skippedModels
|
|
606
|
+
};
|
|
607
|
+
} catch (error) {
|
|
608
|
+
await this.restoreBackup();
|
|
609
|
+
throw error;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Try to run prisma format for consistent indentation
|
|
614
|
+
*/
|
|
615
|
+
async tryPrismaFormat() {
|
|
616
|
+
try {
|
|
617
|
+
await (0, import_execa.execa)("npx", ["prisma", "format"], {
|
|
618
|
+
cwd: this.schemaPath.replace(/[/\\]prisma[/\\]schema\.prisma$/, ""),
|
|
619
|
+
timeout: 15e3
|
|
620
|
+
});
|
|
621
|
+
} catch {
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Create backup of schema.prisma
|
|
626
|
+
*/
|
|
627
|
+
async createBackup() {
|
|
628
|
+
this.backupPath = `${this.schemaPath}.backup`;
|
|
629
|
+
await fs6.copy(this.schemaPath, this.backupPath);
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Restore backup on failure
|
|
633
|
+
*/
|
|
634
|
+
async restoreBackup() {
|
|
635
|
+
if (this.backupPath && await fs6.pathExists(this.backupPath)) {
|
|
636
|
+
await fs6.copy(this.backupPath, this.schemaPath, { overwrite: true });
|
|
637
|
+
await fs6.remove(this.backupPath);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Clean up backup after success
|
|
642
|
+
*/
|
|
643
|
+
async cleanupBackup() {
|
|
644
|
+
if (this.backupPath && await fs6.pathExists(this.backupPath)) {
|
|
645
|
+
await fs6.remove(this.backupPath);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
|
|
429
652
|
// src/installer/package-updater.ts
|
|
430
|
-
var
|
|
653
|
+
var fs7, PackageUpdater;
|
|
431
654
|
var init_package_updater = __esm({
|
|
432
655
|
"src/installer/package-updater.ts"() {
|
|
433
656
|
"use strict";
|
|
434
657
|
init_cjs_shims();
|
|
435
|
-
|
|
658
|
+
fs7 = __toESM(require("fs-extra"));
|
|
436
659
|
PackageUpdater = class {
|
|
437
660
|
constructor(packageJsonPath) {
|
|
438
661
|
this.packageJsonPath = packageJsonPath;
|
|
@@ -444,7 +667,7 @@ var init_package_updater = __esm({
|
|
|
444
667
|
async update(config) {
|
|
445
668
|
await this.createBackup();
|
|
446
669
|
try {
|
|
447
|
-
const packageJson = await
|
|
670
|
+
const packageJson = await fs7.readJSON(this.packageJsonPath);
|
|
448
671
|
const deps = this.getDependencies(config);
|
|
449
672
|
packageJson.dependencies = {
|
|
450
673
|
...packageJson.dependencies,
|
|
@@ -458,7 +681,7 @@ var init_package_updater = __esm({
|
|
|
458
681
|
packageJson.devDependencies = this.sortObject(
|
|
459
682
|
packageJson.devDependencies
|
|
460
683
|
);
|
|
461
|
-
await
|
|
684
|
+
await fs7.writeJSON(this.packageJsonPath, packageJson, { spaces: 2 });
|
|
462
685
|
} catch (error) {
|
|
463
686
|
await this.restoreBackup();
|
|
464
687
|
throw error;
|
|
@@ -540,23 +763,23 @@ var init_package_updater = __esm({
|
|
|
540
763
|
*/
|
|
541
764
|
async createBackup() {
|
|
542
765
|
this.backupPath = `${this.packageJsonPath}.backup`;
|
|
543
|
-
await
|
|
766
|
+
await fs7.copy(this.packageJsonPath, this.backupPath);
|
|
544
767
|
}
|
|
545
768
|
/**
|
|
546
769
|
* Restore backup
|
|
547
770
|
*/
|
|
548
771
|
async restoreBackup() {
|
|
549
|
-
if (this.backupPath && await
|
|
550
|
-
await
|
|
551
|
-
await
|
|
772
|
+
if (this.backupPath && await fs7.pathExists(this.backupPath)) {
|
|
773
|
+
await fs7.copy(this.backupPath, this.packageJsonPath, { overwrite: true });
|
|
774
|
+
await fs7.remove(this.backupPath);
|
|
552
775
|
}
|
|
553
776
|
}
|
|
554
777
|
/**
|
|
555
778
|
* Clean up backup
|
|
556
779
|
*/
|
|
557
780
|
async cleanupBackup() {
|
|
558
|
-
if (this.backupPath && await
|
|
559
|
-
await
|
|
781
|
+
if (this.backupPath && await fs7.pathExists(this.backupPath)) {
|
|
782
|
+
await fs7.remove(this.backupPath);
|
|
560
783
|
}
|
|
561
784
|
}
|
|
562
785
|
};
|
|
@@ -564,12 +787,12 @@ var init_package_updater = __esm({
|
|
|
564
787
|
});
|
|
565
788
|
|
|
566
789
|
// src/installer/dependency-installer.ts
|
|
567
|
-
var
|
|
790
|
+
var import_execa2, import_detect_package_manager, DependencyInstaller;
|
|
568
791
|
var init_dependency_installer = __esm({
|
|
569
792
|
"src/installer/dependency-installer.ts"() {
|
|
570
793
|
"use strict";
|
|
571
794
|
init_cjs_shims();
|
|
572
|
-
|
|
795
|
+
import_execa2 = require("execa");
|
|
573
796
|
import_detect_package_manager = require("detect-package-manager");
|
|
574
797
|
DependencyInstaller = class {
|
|
575
798
|
/**
|
|
@@ -579,7 +802,7 @@ var init_dependency_installer = __esm({
|
|
|
579
802
|
const packageManager = await this.detectPackageManager(cwd);
|
|
580
803
|
console.log(`\u{1F4E6} Installing dependencies with ${packageManager}...`);
|
|
581
804
|
try {
|
|
582
|
-
await (0,
|
|
805
|
+
await (0, import_execa2.execa)(packageManager, ["install"], {
|
|
583
806
|
cwd,
|
|
584
807
|
stdio: "inherit"
|
|
585
808
|
});
|
|
@@ -596,7 +819,7 @@ var init_dependency_installer = __esm({
|
|
|
596
819
|
async installCapture(cwd) {
|
|
597
820
|
const packageManager = await this.detectPackageManager(cwd);
|
|
598
821
|
try {
|
|
599
|
-
const result = await (0,
|
|
822
|
+
const result = await (0, import_execa2.execa)(packageManager, ["install"], {
|
|
600
823
|
cwd,
|
|
601
824
|
stdio: "pipe"
|
|
602
825
|
});
|
|
@@ -627,7 +850,8 @@ __export(installer_exports, {
|
|
|
627
850
|
AppModuleUpdater: () => AppModuleUpdater,
|
|
628
851
|
DependencyInstaller: () => DependencyInstaller,
|
|
629
852
|
MainTsUpdater: () => MainTsUpdater,
|
|
630
|
-
PackageUpdater: () => PackageUpdater
|
|
853
|
+
PackageUpdater: () => PackageUpdater,
|
|
854
|
+
PrismaSchemaUpdater: () => PrismaSchemaUpdater
|
|
631
855
|
});
|
|
632
856
|
var init_installer = __esm({
|
|
633
857
|
"src/installer/index.ts"() {
|
|
@@ -635,6 +859,7 @@ var init_installer = __esm({
|
|
|
635
859
|
init_cjs_shims();
|
|
636
860
|
init_ast_updater();
|
|
637
861
|
init_main_ts_updater();
|
|
862
|
+
init_prisma_schema_updater();
|
|
638
863
|
init_package_updater();
|
|
639
864
|
init_dependency_installer();
|
|
640
865
|
}
|
|
@@ -653,7 +878,7 @@ init_cjs_shims();
|
|
|
653
878
|
init_cjs_shims();
|
|
654
879
|
var http = __toESM(require("http"));
|
|
655
880
|
var path6 = __toESM(require("path"));
|
|
656
|
-
var
|
|
881
|
+
var fs9 = __toESM(require("fs-extra"));
|
|
657
882
|
|
|
658
883
|
// src/analyzer/index.ts
|
|
659
884
|
init_cjs_shims();
|
|
@@ -858,88 +1083,12 @@ function buildConfig(answers, projectName, sourceRoot, detectedORM, detectedDB)
|
|
|
858
1083
|
init_cjs_shims();
|
|
859
1084
|
var import_events = require("events");
|
|
860
1085
|
var path5 = __toESM(require("path"));
|
|
861
|
-
var
|
|
1086
|
+
var fs8 = __toESM(require("fs-extra"));
|
|
862
1087
|
|
|
863
1088
|
// src/generator/generator.ts
|
|
864
1089
|
init_cjs_shims();
|
|
865
1090
|
var path4 = __toESM(require("path"));
|
|
866
|
-
|
|
867
|
-
// src/generator/template-engine.ts
|
|
868
|
-
init_cjs_shims();
|
|
869
|
-
var import_handlebars = __toESM(require("handlebars"));
|
|
870
|
-
var path2 = __toESM(require("path"));
|
|
871
|
-
var fs2 = __toESM(require("fs-extra"));
|
|
872
|
-
var TemplateEngine = class {
|
|
873
|
-
handlebars;
|
|
874
|
-
templateCache;
|
|
875
|
-
templatesDir;
|
|
876
|
-
constructor(templatesDir) {
|
|
877
|
-
this.handlebars = import_handlebars.default.create();
|
|
878
|
-
this.templateCache = /* @__PURE__ */ new Map();
|
|
879
|
-
this.templatesDir = templatesDir || path2.join(__dirname, "generator", "templates");
|
|
880
|
-
this.registerHelpers();
|
|
881
|
-
}
|
|
882
|
-
/**
|
|
883
|
-
* Register Handlebars helpers
|
|
884
|
-
*/
|
|
885
|
-
registerHelpers() {
|
|
886
|
-
this.handlebars.registerHelper("eq", (a, b) => a === b);
|
|
887
|
-
this.handlebars.registerHelper("ne", (a, b) => a !== b);
|
|
888
|
-
this.handlebars.registerHelper("or", (a, b) => a || b);
|
|
889
|
-
this.handlebars.registerHelper("and", (a, b) => a && b);
|
|
890
|
-
this.handlebars.registerHelper("includes", (arr, item) => arr?.includes(item));
|
|
891
|
-
this.handlebars.registerHelper("capitalize", (str) => {
|
|
892
|
-
if (!str) return "";
|
|
893
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
894
|
-
});
|
|
895
|
-
this.handlebars.registerHelper("lowercase", (str) => {
|
|
896
|
-
if (!str) return "";
|
|
897
|
-
return str.toLowerCase();
|
|
898
|
-
});
|
|
899
|
-
this.handlebars.registerHelper("uppercase", (str) => {
|
|
900
|
-
if (!str) return "";
|
|
901
|
-
return str.toUpperCase();
|
|
902
|
-
});
|
|
903
|
-
this.handlebars.registerHelper("camelCase", (str) => {
|
|
904
|
-
if (!str) return "";
|
|
905
|
-
return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
906
|
-
});
|
|
907
|
-
this.handlebars.registerHelper("pascalCase", (str) => {
|
|
908
|
-
if (!str) return "";
|
|
909
|
-
const camel = str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
910
|
-
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
911
|
-
});
|
|
912
|
-
}
|
|
913
|
-
/**
|
|
914
|
-
* Render a template with context
|
|
915
|
-
*/
|
|
916
|
-
async render(templatePath, context) {
|
|
917
|
-
const template = await this.loadTemplate(templatePath);
|
|
918
|
-
return template(context);
|
|
919
|
-
}
|
|
920
|
-
/**
|
|
921
|
-
* Load a template (with caching)
|
|
922
|
-
*/
|
|
923
|
-
async loadTemplate(templatePath) {
|
|
924
|
-
if (this.templateCache.has(templatePath)) {
|
|
925
|
-
return this.templateCache.get(templatePath);
|
|
926
|
-
}
|
|
927
|
-
const fullPath = path2.join(this.templatesDir, templatePath);
|
|
928
|
-
if (!await fs2.pathExists(fullPath)) {
|
|
929
|
-
throw new Error(`Template not found: ${templatePath}`);
|
|
930
|
-
}
|
|
931
|
-
const source = await fs2.readFile(fullPath, "utf-8");
|
|
932
|
-
const compiled = this.handlebars.compile(source);
|
|
933
|
-
this.templateCache.set(templatePath, compiled);
|
|
934
|
-
return compiled;
|
|
935
|
-
}
|
|
936
|
-
/**
|
|
937
|
-
* Clear template cache
|
|
938
|
-
*/
|
|
939
|
-
clearCache() {
|
|
940
|
-
this.templateCache.clear();
|
|
941
|
-
}
|
|
942
|
-
};
|
|
1091
|
+
init_template_engine();
|
|
943
1092
|
|
|
944
1093
|
// src/generator/file-writer.ts
|
|
945
1094
|
init_cjs_shims();
|
|
@@ -1017,16 +1166,8 @@ var FileWriter = class {
|
|
|
1017
1166
|
}
|
|
1018
1167
|
};
|
|
1019
1168
|
|
|
1020
|
-
// src/config/config-builder.ts
|
|
1021
|
-
init_cjs_shims();
|
|
1022
|
-
function buildTemplateContext(config) {
|
|
1023
|
-
return {
|
|
1024
|
-
...config
|
|
1025
|
-
// Add any additional computed properties here
|
|
1026
|
-
};
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
1169
|
// src/generator/generator.ts
|
|
1170
|
+
init_config_builder();
|
|
1030
1171
|
var Generator = class {
|
|
1031
1172
|
templateEngine;
|
|
1032
1173
|
fileWriter;
|
|
@@ -1144,8 +1285,7 @@ var Generator = class {
|
|
|
1144
1285
|
} else if (config.orm === "prisma") {
|
|
1145
1286
|
plan.push(
|
|
1146
1287
|
{ template: "prisma/prisma.service.ts.hbs", output: `${config.sourceRoot}/prisma/prisma.service.ts` },
|
|
1147
|
-
{ template: "prisma/prisma.module.ts.hbs", output: `${config.sourceRoot}/prisma/prisma.module.ts` }
|
|
1148
|
-
{ template: "prisma/schema.prisma.additions.hbs", output: "prisma-schema-additions.prisma" }
|
|
1288
|
+
{ template: "prisma/prisma.module.ts.hbs", output: `${config.sourceRoot}/prisma/prisma.module.ts` }
|
|
1149
1289
|
);
|
|
1150
1290
|
}
|
|
1151
1291
|
if (config.features.emailService) {
|
|
@@ -1171,6 +1311,8 @@ var Generator = class {
|
|
|
1171
1311
|
};
|
|
1172
1312
|
|
|
1173
1313
|
// src/gui/orchestrator.ts
|
|
1314
|
+
init_template_engine();
|
|
1315
|
+
init_config_builder();
|
|
1174
1316
|
var GuiOrchestrator = class extends import_events.EventEmitter {
|
|
1175
1317
|
constructor(_cwd) {
|
|
1176
1318
|
super();
|
|
@@ -1191,7 +1333,7 @@ var GuiOrchestrator = class extends import_events.EventEmitter {
|
|
|
1191
1333
|
for (const spec of plan) {
|
|
1192
1334
|
const content = await templateEngine.render(spec.template, context);
|
|
1193
1335
|
const fullPath = path5.join(projectInfo.root, spec.output);
|
|
1194
|
-
const exists = await
|
|
1336
|
+
const exists = await fs8.pathExists(fullPath);
|
|
1195
1337
|
files.push({
|
|
1196
1338
|
path: spec.output,
|
|
1197
1339
|
content,
|
|
@@ -1203,6 +1345,12 @@ var GuiOrchestrator = class extends import_events.EventEmitter {
|
|
|
1203
1345
|
{ path: `${config.sourceRoot}/main.ts`, description: "Add global JWT guard, ValidationPipe, Swagger setup" },
|
|
1204
1346
|
{ path: "package.json", description: "Add authentication dependencies" }
|
|
1205
1347
|
];
|
|
1348
|
+
if (config.orm === "prisma") {
|
|
1349
|
+
modifiedFiles.push({
|
|
1350
|
+
path: "prisma/schema.prisma",
|
|
1351
|
+
description: "Append User and RefreshToken models"
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1206
1354
|
return {
|
|
1207
1355
|
files,
|
|
1208
1356
|
modifiedFiles,
|
|
@@ -1237,6 +1385,26 @@ var GuiOrchestrator = class extends import_events.EventEmitter {
|
|
|
1237
1385
|
errors.push(msg);
|
|
1238
1386
|
return { success: false, filesCreated: result.filesCreated, filesSkipped: result.filesSkipped, errors, warnings };
|
|
1239
1387
|
}
|
|
1388
|
+
if (config.orm === "prisma") {
|
|
1389
|
+
this.emitProgress("prisma-schema", "Updating prisma/schema.prisma...", "started");
|
|
1390
|
+
try {
|
|
1391
|
+
const { PrismaSchemaUpdater: PrismaSchemaUpdater2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
|
|
1392
|
+
const schemaPath = path5.join(projectInfo.root, "prisma", "schema.prisma");
|
|
1393
|
+
const prismaUpdater = new PrismaSchemaUpdater2(schemaPath);
|
|
1394
|
+
const prismaResult = await prismaUpdater.update(config);
|
|
1395
|
+
if (prismaResult.updated) {
|
|
1396
|
+
await prismaUpdater.cleanupBackup();
|
|
1397
|
+
this.emitProgress("prisma-schema", prismaResult.message, "completed");
|
|
1398
|
+
} else {
|
|
1399
|
+
this.emitProgress("prisma-schema", prismaResult.message, "warning");
|
|
1400
|
+
warnings.push(prismaResult.message);
|
|
1401
|
+
}
|
|
1402
|
+
} catch (error) {
|
|
1403
|
+
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
1404
|
+
this.emitProgress("prisma-schema", "Could not update prisma/schema.prisma", "warning", msg);
|
|
1405
|
+
warnings.push("Could not update prisma/schema.prisma - add models manually");
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1240
1408
|
this.emitProgress("ast-main-ts", "Updating main.ts...", "started");
|
|
1241
1409
|
try {
|
|
1242
1410
|
const { MainTsUpdater: MainTsUpdater2 } = await Promise.resolve().then(() => (init_installer(), installer_exports));
|
|
@@ -1396,7 +1564,7 @@ data: ${JSON.stringify(data)}
|
|
|
1396
1564
|
];
|
|
1397
1565
|
for (const candidate of candidates) {
|
|
1398
1566
|
try {
|
|
1399
|
-
const html =
|
|
1567
|
+
const html = fs9.readFileSync(candidate, "utf-8");
|
|
1400
1568
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1401
1569
|
res.end(html);
|
|
1402
1570
|
return;
|