nestjs-prisma-cli 1.0.10 → 1.0.12
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/README.md +9 -9
- package/bin/index.js +8 -8
- package/package.json +1 -1
- package/template/.github/workflows/deploy-to-ecr.yml +1 -1
- package/template/src/modules/auth/auth.controller.ts +35 -35
- package/template/src/modules/auth/auth.module.ts +16 -7
- package/template/src/modules/auth/auth.service.ts +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# NestJS + Prisma Project Generator
|
|
2
2
|
|
|
3
3
|
[](#license)
|
|
4
4
|
|
|
@@ -6,23 +6,23 @@ A CLI tool to quickly scaffold a **NestJS + Prisma** project with pre-configured
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## NestJS Prisma Generator CLI
|
|
10
10
|
|
|
11
11
|
A CLI tool to quickly scaffold a **NestJS + Prisma** project with built-in support for:
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
13
|
+
- Prisma ORM with migrations and seeding
|
|
14
|
+
- Swagger (OpenAPI) setup
|
|
15
|
+
- Authentication boilerplate
|
|
16
|
+
- AWS S3 integration
|
|
17
|
+
- Logging via Winston (rotating logs and DB persistence)
|
|
18
18
|
|
|
19
19
|
Compatible with **Node.js >=18** and **NestJS v10+**.
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## Installation
|
|
24
24
|
|
|
25
|
-
###
|
|
25
|
+
### Quick Start Guide
|
|
26
26
|
|
|
27
27
|
Once the CLI is installed, you can use the following commands:
|
|
28
28
|
|
package/bin/index.js
CHANGED
|
@@ -193,17 +193,17 @@ async function main() {
|
|
|
193
193
|
sqlserver: `sqlserver://localhost:1433;database=${dbSafeName};user=sa;password=your_password;encrypt=false`,
|
|
194
194
|
};
|
|
195
195
|
|
|
196
|
-
const envContent = `DATABASE_URL
|
|
196
|
+
const envContent = `DATABASE_URL=${defaultUrlMap[database.toLowerCase()]}
|
|
197
197
|
|
|
198
|
-
JWT_ACCESS_SECRET=
|
|
199
|
-
JWT_REFRESH_SECRET=
|
|
200
|
-
JWT_ACCESS_EXPIRATION_TIME=
|
|
201
|
-
JWT_REFRESH_EXPIRATION_TIME=
|
|
198
|
+
JWT_ACCESS_SECRET=JWT_ACCESS_SECRET
|
|
199
|
+
JWT_REFRESH_SECRET=JWT_REFRESH_SECRET
|
|
200
|
+
JWT_ACCESS_EXPIRATION_TIME=1d
|
|
201
|
+
JWT_REFRESH_EXPIRATION_TIME=30d
|
|
202
202
|
|
|
203
|
-
AWS_ACCESS_KEY_ID=
|
|
204
|
-
AWS_SECRET_ACCESS_KEY=
|
|
203
|
+
AWS_ACCESS_KEY_ID=AWS_ACCESS_KEY_ID
|
|
204
|
+
AWS_SECRET_ACCESS_KEY=AWS_SECRET_ACCESS_KEY
|
|
205
205
|
AWS_REGION=ap-southeast-1
|
|
206
|
-
S3_BUCKET=
|
|
206
|
+
S3_BUCKET=S3_BUCKET
|
|
207
207
|
|
|
208
208
|
NODE_ENV=dev
|
|
209
209
|
PROJECT_NAME=${dbSafeName}
|
package/package.json
CHANGED
|
@@ -4,84 +4,84 @@ import {
|
|
|
4
4
|
Body,
|
|
5
5
|
HttpCode,
|
|
6
6
|
UnauthorizedException,
|
|
7
|
-
} from
|
|
8
|
-
import { ApiBody, ApiTags } from
|
|
9
|
-
import { AuthService } from
|
|
10
|
-
import { Public } from
|
|
11
|
-
import { JwtService } from
|
|
12
|
-
import { LoginDto } from
|
|
7
|
+
} from "@nestjs/common";
|
|
8
|
+
import { ApiBody, ApiTags } from "@nestjs/swagger";
|
|
9
|
+
import { AuthService } from "./auth.service";
|
|
10
|
+
import { Public } from "../../common/decorator/public.decorator";
|
|
11
|
+
import { JwtService } from "@nestjs/jwt";
|
|
12
|
+
import { LoginDto } from "./dto/login.dto";
|
|
13
13
|
|
|
14
|
-
@ApiTags(
|
|
14
|
+
@ApiTags("Authentication")
|
|
15
15
|
@Controller()
|
|
16
16
|
export class AuthController {
|
|
17
17
|
constructor(
|
|
18
18
|
private readonly authService: AuthService,
|
|
19
|
-
private readonly jwtService: JwtService
|
|
19
|
+
private readonly jwtService: JwtService
|
|
20
20
|
) {}
|
|
21
21
|
|
|
22
22
|
@Public()
|
|
23
|
-
@Post(
|
|
24
|
-
@ApiBody({ description:
|
|
23
|
+
@Post("login")
|
|
24
|
+
@ApiBody({ description: "Login credentials", type: LoginDto })
|
|
25
25
|
async login(@Body() body: LoginDto) {
|
|
26
26
|
if (!body || !body.userId || !body.password) {
|
|
27
|
-
throw new UnauthorizedException(
|
|
27
|
+
throw new UnauthorizedException("Missing login credentials");
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const user = await this.authService.validateUser(
|
|
31
31
|
body.userId,
|
|
32
|
-
body.password
|
|
32
|
+
body.password
|
|
33
33
|
);
|
|
34
34
|
return this.authService.login(user);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
@Public()
|
|
38
|
-
@Post(
|
|
38
|
+
@Post("refresh")
|
|
39
39
|
@ApiBody({
|
|
40
|
-
description:
|
|
40
|
+
description: "Provide your userId and refreshToken",
|
|
41
41
|
schema: {
|
|
42
|
-
type:
|
|
42
|
+
type: "object",
|
|
43
43
|
properties: {
|
|
44
|
-
userId: { type:
|
|
45
|
-
refreshToken: { type:
|
|
44
|
+
userId: { type: "string", example: "USER_20250815001" },
|
|
45
|
+
refreshToken: { type: "string", example: "xxxx.yyyy.zzzz" },
|
|
46
46
|
},
|
|
47
|
-
required: [
|
|
47
|
+
required: ["userId", "refreshToken"],
|
|
48
48
|
},
|
|
49
49
|
})
|
|
50
50
|
async refresh(@Body() body: { userId: string; refreshToken: string }) {
|
|
51
51
|
if (!body || !body.userId || !body.refreshToken) {
|
|
52
|
-
throw new UnauthorizedException(
|
|
52
|
+
throw new UnauthorizedException("Missing login credentials");
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
let payload: any;
|
|
56
|
-
const modifiedUserId = body.userId.replace(/\s+/g,
|
|
56
|
+
const modifiedUserId = body.userId.replace(/\s+/g, "");
|
|
57
57
|
|
|
58
58
|
try {
|
|
59
59
|
payload = this.jwtService.verify(body.refreshToken, {
|
|
60
60
|
secret: process.env.JWT_REFRESH_SECRET,
|
|
61
61
|
});
|
|
62
62
|
} catch (err) {
|
|
63
|
-
if (err.name ===
|
|
64
|
-
throw new UnauthorizedException(
|
|
63
|
+
if (err.name === "TokenExpiredError") {
|
|
64
|
+
throw new UnauthorizedException("Refresh Token expired");
|
|
65
65
|
}
|
|
66
|
-
throw new UnauthorizedException(
|
|
66
|
+
throw new UnauthorizedException("Invalid refresh token");
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
if (String(payload.userId).trim() !== modifiedUserId) {
|
|
70
|
-
throw new UnauthorizedException(
|
|
70
|
+
throw new UnauthorizedException("Unauthorized access");
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
const user = await this.authService.getUserByUserId(modifiedUserId);
|
|
74
74
|
|
|
75
75
|
if (!user) {
|
|
76
|
-
throw new UnauthorizedException(
|
|
76
|
+
throw new UnauthorizedException("User not found");
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const newAccessToken = this.jwtService.sign(
|
|
80
80
|
{ sub: user.id, userId: user.userId },
|
|
81
81
|
{
|
|
82
82
|
secret: process.env.JWT_ACCESS_SECRET,
|
|
83
|
-
expiresIn: process.env.JWT_ACCESS_EXPIRATION_TIME,
|
|
84
|
-
}
|
|
83
|
+
expiresIn: process.env.JWT_ACCESS_EXPIRATION_TIME as any,
|
|
84
|
+
}
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
return {
|
|
@@ -91,16 +91,16 @@ export class AuthController {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
@Public()
|
|
94
|
-
@Post(
|
|
94
|
+
@Post("validate")
|
|
95
95
|
@HttpCode(200)
|
|
96
96
|
@ApiBody({
|
|
97
|
-
description:
|
|
97
|
+
description: "Provide the access token to validate",
|
|
98
98
|
schema: {
|
|
99
|
-
type:
|
|
99
|
+
type: "object",
|
|
100
100
|
properties: {
|
|
101
|
-
token: { type:
|
|
101
|
+
token: { type: "string", example: "xxxx.yyyy.zzzz" },
|
|
102
102
|
},
|
|
103
|
-
required: [
|
|
103
|
+
required: ["token"],
|
|
104
104
|
},
|
|
105
105
|
})
|
|
106
106
|
async validate(@Body() body: { token: string }) {
|
|
@@ -110,10 +110,10 @@ export class AuthController {
|
|
|
110
110
|
});
|
|
111
111
|
return payload;
|
|
112
112
|
} catch (err) {
|
|
113
|
-
if (err.name ===
|
|
114
|
-
throw new UnauthorizedException(
|
|
113
|
+
if (err.name === "TokenExpiredError") {
|
|
114
|
+
throw new UnauthorizedException("Token expired");
|
|
115
115
|
}
|
|
116
|
-
throw new UnauthorizedException(
|
|
116
|
+
throw new UnauthorizedException("Invalid token");
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Module } from '@nestjs/common';
|
|
2
2
|
import { AuthController } from './auth.controller';
|
|
3
3
|
import { AuthService } from './auth.service';
|
|
4
|
-
import { JwtModule } from '@nestjs/jwt';
|
|
4
|
+
import { JwtModule, JwtModuleOptions } from '@nestjs/jwt';
|
|
5
5
|
import { JwtStrategy } from './jwt/jwt.strategy';
|
|
6
6
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
7
7
|
|
|
@@ -11,12 +11,21 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
|
11
11
|
JwtModule.registerAsync({
|
|
12
12
|
imports: [ConfigModule],
|
|
13
13
|
inject: [ConfigService],
|
|
14
|
-
useFactory: async (configService: ConfigService) =>
|
|
15
|
-
secret
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
useFactory: async (configService: ConfigService): Promise<JwtModuleOptions> => {
|
|
15
|
+
const secret = configService.get<string>('JWT_ACCESS_SECRET');
|
|
16
|
+
const expiresIn = configService.get<string>('JWT_ACCESS_EXPIRATION_TIME');
|
|
17
|
+
|
|
18
|
+
if (!secret || !expiresIn) {
|
|
19
|
+
throw new Error('Missing JWT environment variables');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
secret,
|
|
24
|
+
signOptions: {
|
|
25
|
+
expiresIn: expiresIn as any,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
},
|
|
20
29
|
}),
|
|
21
30
|
],
|
|
22
31
|
controllers: [AuthController],
|
|
@@ -36,12 +36,12 @@ export class AuthService {
|
|
|
36
36
|
|
|
37
37
|
const accessToken = this.jwtService.sign(payload, {
|
|
38
38
|
secret: process.env.JWT_ACCESS_SECRET,
|
|
39
|
-
expiresIn: process.env.JWT_ACCESS_EXPIRATION_TIME,
|
|
39
|
+
expiresIn: process.env.JWT_ACCESS_EXPIRATION_TIME as any,
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
const refreshToken = this.jwtService.sign(payload, {
|
|
43
43
|
secret: process.env.JWT_REFRESH_SECRET,
|
|
44
|
-
expiresIn: process.env.JWT_REFRESH_EXPIRATION_TIME,
|
|
44
|
+
expiresIn: process.env.JWT_REFRESH_EXPIRATION_TIME as any,
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
return {
|