@biggora/claude-plugins 1.2.2 → 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/README.md +2 -0
- package/package.json +1 -1
- package/registry/registry.json +15 -0
- package/specs/coding.md +6 -0
- package/src/commands/skills/add.js +63 -7
- package/src/commands/skills/list.js +23 -52
- package/src/commands/skills/remove.js +26 -27
- package/src/commands/skills/resolve.js +155 -0
- package/src/commands/skills/update.js +58 -74
- package/src/skills/nest-best-practices/SKILL.md +251 -0
- package/src/skills/nest-best-practices/references/best-practices-request-lifecycle.md +158 -0
- package/src/skills/nest-best-practices/references/cli-monorepo.md +106 -0
- package/src/skills/nest-best-practices/references/cli-overview.md +157 -0
- package/src/skills/nest-best-practices/references/core-controllers.md +165 -0
- package/src/skills/nest-best-practices/references/core-dependency-injection.md +179 -0
- package/src/skills/nest-best-practices/references/core-middleware.md +139 -0
- package/src/skills/nest-best-practices/references/core-modules.md +138 -0
- package/src/skills/nest-best-practices/references/core-providers.md +188 -0
- package/src/skills/nest-best-practices/references/faq-raw-body-hybrid.md +122 -0
- package/src/skills/nest-best-practices/references/fundamentals-circular-dependency.md +89 -0
- package/src/skills/nest-best-practices/references/fundamentals-custom-decorators.md +107 -0
- package/src/skills/nest-best-practices/references/fundamentals-dynamic-modules.md +125 -0
- package/src/skills/nest-best-practices/references/fundamentals-exception-filters.md +202 -0
- package/src/skills/nest-best-practices/references/fundamentals-execution-context.md +107 -0
- package/src/skills/nest-best-practices/references/fundamentals-guards.md +136 -0
- package/src/skills/nest-best-practices/references/fundamentals-interceptors.md +187 -0
- package/src/skills/nest-best-practices/references/fundamentals-lazy-loading.md +89 -0
- package/src/skills/nest-best-practices/references/fundamentals-lifecycle-events.md +87 -0
- package/src/skills/nest-best-practices/references/fundamentals-module-reference.md +107 -0
- package/src/skills/nest-best-practices/references/fundamentals-pipes.md +197 -0
- package/src/skills/nest-best-practices/references/fundamentals-provider-scopes.md +92 -0
- package/src/skills/nest-best-practices/references/fundamentals-testing.md +142 -0
- package/src/skills/nest-best-practices/references/graphql-overview.md +233 -0
- package/src/skills/nest-best-practices/references/graphql-resolvers-mutations.md +199 -0
- package/src/skills/nest-best-practices/references/graphql-scalars-unions-enums.md +180 -0
- package/src/skills/nest-best-practices/references/graphql-subscriptions.md +228 -0
- package/src/skills/nest-best-practices/references/microservices-grpc.md +175 -0
- package/src/skills/nest-best-practices/references/microservices-overview.md +221 -0
- package/src/skills/nest-best-practices/references/microservices-transports.md +119 -0
- package/src/skills/nest-best-practices/references/openapi-swagger.md +207 -0
- package/src/skills/nest-best-practices/references/recipes-authentication.md +97 -0
- package/src/skills/nest-best-practices/references/recipes-cqrs.md +176 -0
- package/src/skills/nest-best-practices/references/recipes-crud-generator.md +87 -0
- package/src/skills/nest-best-practices/references/recipes-documentation.md +93 -0
- package/src/skills/nest-best-practices/references/recipes-mongoose.md +153 -0
- package/src/skills/nest-best-practices/references/recipes-prisma.md +98 -0
- package/src/skills/nest-best-practices/references/recipes-terminus.md +148 -0
- package/src/skills/nest-best-practices/references/recipes-typeorm.md +122 -0
- package/src/skills/nest-best-practices/references/security-authorization.md +196 -0
- package/src/skills/nest-best-practices/references/security-cors-helmet-rate-limiting.md +204 -0
- package/src/skills/nest-best-practices/references/security-encryption-hashing.md +93 -0
- package/src/skills/nest-best-practices/references/techniques-caching.md +142 -0
- package/src/skills/nest-best-practices/references/techniques-compression-streaming-sse.md +194 -0
- package/src/skills/nest-best-practices/references/techniques-configuration.md +132 -0
- package/src/skills/nest-best-practices/references/techniques-database.md +153 -0
- package/src/skills/nest-best-practices/references/techniques-events.md +163 -0
- package/src/skills/nest-best-practices/references/techniques-fastify.md +137 -0
- package/src/skills/nest-best-practices/references/techniques-file-upload.md +140 -0
- package/src/skills/nest-best-practices/references/techniques-http-module.md +176 -0
- package/src/skills/nest-best-practices/references/techniques-logging.md +146 -0
- package/src/skills/nest-best-practices/references/techniques-mvc-serve-static.md +132 -0
- package/src/skills/nest-best-practices/references/techniques-queues.md +162 -0
- package/src/skills/nest-best-practices/references/techniques-serialization.md +158 -0
- package/src/skills/nest-best-practices/references/techniques-sessions-cookies.md +167 -0
- package/src/skills/nest-best-practices/references/techniques-task-scheduling.md +166 -0
- package/src/skills/nest-best-practices/references/techniques-validation.md +126 -0
- package/src/skills/nest-best-practices/references/techniques-versioning.md +153 -0
- package/src/skills/nest-best-practices/references/websockets-advanced.md +96 -0
- package/src/skills/nest-best-practices/references/websockets-gateways.md +215 -0
- package/src/skills/typescript-expert/SKILL.md +145 -0
- package/src/skills/typescript-expert/commands/typescript-fix.md +65 -0
- package/src/skills/typescript-expert/references/advanced-conditional-types.md +190 -0
- package/src/skills/typescript-expert/references/advanced-decorators.md +243 -0
- package/src/skills/typescript-expert/references/advanced-mapped-types.md +223 -0
- package/src/skills/typescript-expert/references/advanced-template-literals.md +209 -0
- package/src/skills/typescript-expert/references/advanced-type-guards.md +308 -0
- package/src/skills/typescript-expert/references/best-practices-patterns.md +313 -0
- package/src/skills/typescript-expert/references/best-practices-performance.md +185 -0
- package/src/skills/typescript-expert/references/best-practices-tsconfig.md +242 -0
- package/src/skills/typescript-expert/references/core-generics.md +246 -0
- package/src/skills/typescript-expert/references/core-interfaces-types.md +231 -0
- package/src/skills/typescript-expert/references/core-type-system.md +261 -0
- package/src/skills/typescript-expert/references/core-utility-types.md +235 -0
- package/src/skills/typescript-expert/references/features-ts5x.md +370 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: authorization
|
|
3
|
+
description: Role-based access control (RBAC) and CASL integration
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Authorization
|
|
7
|
+
|
|
8
|
+
Authorization determines what authenticated users can do. NestJS supports RBAC, claims-based, and policy-based authorization.
|
|
9
|
+
|
|
10
|
+
## Basic RBAC Implementation
|
|
11
|
+
|
|
12
|
+
### 1. Define Roles
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// role.enum.ts
|
|
16
|
+
export enum Role {
|
|
17
|
+
User = 'user',
|
|
18
|
+
Admin = 'admin',
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 2. Create Roles Decorator
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// roles.decorator.ts
|
|
26
|
+
import { SetMetadata } from '@nestjs/common';
|
|
27
|
+
import { Role } from './role.enum';
|
|
28
|
+
|
|
29
|
+
export const ROLES_KEY = 'roles';
|
|
30
|
+
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 3. Create Roles Guard
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// roles.guard.ts
|
|
37
|
+
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
|
38
|
+
import { Reflector } from '@nestjs/core';
|
|
39
|
+
import { Role } from './role.enum';
|
|
40
|
+
import { ROLES_KEY } from './roles.decorator';
|
|
41
|
+
|
|
42
|
+
@Injectable()
|
|
43
|
+
export class RolesGuard implements CanActivate {
|
|
44
|
+
constructor(private reflector: Reflector) {}
|
|
45
|
+
|
|
46
|
+
canActivate(context: ExecutionContext): boolean {
|
|
47
|
+
const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
|
|
48
|
+
context.getHandler(),
|
|
49
|
+
context.getClass(),
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
if (!requiredRoles) return true;
|
|
53
|
+
|
|
54
|
+
const { user } = context.switchToHttp().getRequest();
|
|
55
|
+
return requiredRoles.some((role) => user.roles?.includes(role));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 4. Apply to Routes
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
@Post()
|
|
64
|
+
@Roles(Role.Admin)
|
|
65
|
+
@UseGuards(AuthGuard, RolesGuard)
|
|
66
|
+
create(@Body() dto: CreateDto) {
|
|
67
|
+
return this.service.create(dto);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 5. Register Globally
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
@Module({
|
|
75
|
+
providers: [
|
|
76
|
+
{ provide: APP_GUARD, useClass: RolesGuard },
|
|
77
|
+
],
|
|
78
|
+
})
|
|
79
|
+
export class AppModule {}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Claims-Based Authorization
|
|
83
|
+
|
|
84
|
+
Check specific permissions instead of roles:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// permissions.decorator.ts
|
|
88
|
+
export const PERMISSIONS_KEY = 'permissions';
|
|
89
|
+
export const RequirePermissions = (...permissions: Permission[]) =>
|
|
90
|
+
SetMetadata(PERMISSIONS_KEY, permissions);
|
|
91
|
+
|
|
92
|
+
// Usage
|
|
93
|
+
@Post()
|
|
94
|
+
@RequirePermissions(Permission.CREATE_POST)
|
|
95
|
+
create() {}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## CASL Integration
|
|
99
|
+
|
|
100
|
+
For complex authorization rules:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npm install @casl/ability
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Define Abilities
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// casl-ability.factory.ts
|
|
110
|
+
import { AbilityBuilder, createMongoAbility, MongoAbility } from '@casl/ability';
|
|
111
|
+
|
|
112
|
+
export type AppAbility = MongoAbility<[Action, Subjects]>;
|
|
113
|
+
|
|
114
|
+
@Injectable()
|
|
115
|
+
export class CaslAbilityFactory {
|
|
116
|
+
createForUser(user: User) {
|
|
117
|
+
const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
|
|
118
|
+
|
|
119
|
+
if (user.isAdmin) {
|
|
120
|
+
can(Action.Manage, 'all');
|
|
121
|
+
} else {
|
|
122
|
+
can(Action.Read, 'all');
|
|
123
|
+
can(Action.Update, Article, { authorId: user.id });
|
|
124
|
+
cannot(Action.Delete, Article, { isPublished: true });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return build();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Use in Services
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
@Injectable()
|
|
136
|
+
export class ArticlesService {
|
|
137
|
+
constructor(private caslAbilityFactory: CaslAbilityFactory) {}
|
|
138
|
+
|
|
139
|
+
async update(user: User, articleId: string, dto: UpdateDto) {
|
|
140
|
+
const article = await this.findOne(articleId);
|
|
141
|
+
const ability = this.caslAbilityFactory.createForUser(user);
|
|
142
|
+
|
|
143
|
+
if (!ability.can(Action.Update, article)) {
|
|
144
|
+
throw new ForbiddenException('Cannot update this article');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return this.articlesRepository.update(articleId, dto);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Policies Guard
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
@Injectable()
|
|
156
|
+
export class PoliciesGuard implements CanActivate {
|
|
157
|
+
constructor(
|
|
158
|
+
private reflector: Reflector,
|
|
159
|
+
private caslAbilityFactory: CaslAbilityFactory,
|
|
160
|
+
) {}
|
|
161
|
+
|
|
162
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
163
|
+
const policyHandlers = this.reflector.get<PolicyHandler[]>(
|
|
164
|
+
CHECK_POLICIES_KEY,
|
|
165
|
+
context.getHandler(),
|
|
166
|
+
) || [];
|
|
167
|
+
|
|
168
|
+
const { user } = context.switchToHttp().getRequest();
|
|
169
|
+
const ability = this.caslAbilityFactory.createForUser(user);
|
|
170
|
+
|
|
171
|
+
return policyHandlers.every((handler) =>
|
|
172
|
+
typeof handler === 'function'
|
|
173
|
+
? handler(ability)
|
|
174
|
+
: handler.handle(ability),
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Usage
|
|
180
|
+
@Get()
|
|
181
|
+
@UseGuards(PoliciesGuard)
|
|
182
|
+
@CheckPolicies((ability: AppAbility) => ability.can(Action.Read, Article))
|
|
183
|
+
findAll() {}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Key Points
|
|
187
|
+
|
|
188
|
+
- Authorization is independent from authentication
|
|
189
|
+
- Use `Reflector` to access route metadata
|
|
190
|
+
- CASL provides fine-grained, attribute-based control
|
|
191
|
+
- `user.roles` should be attached by authentication guard
|
|
192
|
+
|
|
193
|
+
<!--
|
|
194
|
+
Source references:
|
|
195
|
+
- https://docs.nestjs.com/security/authorization
|
|
196
|
+
-->
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cors-helmet-rate-limiting
|
|
3
|
+
description: CORS, security headers, and rate limiting protection
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Security Middleware
|
|
7
|
+
|
|
8
|
+
Essential security measures for NestJS applications.
|
|
9
|
+
|
|
10
|
+
## CORS (Cross-Origin Resource Sharing)
|
|
11
|
+
|
|
12
|
+
Enable CORS to allow cross-origin requests:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// Simple enable
|
|
16
|
+
const app = await NestFactory.create(AppModule, { cors: true });
|
|
17
|
+
|
|
18
|
+
// Or with configuration
|
|
19
|
+
const app = await NestFactory.create(AppModule);
|
|
20
|
+
app.enableCors({
|
|
21
|
+
origin: ['https://example.com', 'https://app.example.com'],
|
|
22
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
|
23
|
+
credentials: true,
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Dynamic origin:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
app.enableCors({
|
|
31
|
+
origin: (origin, callback) => {
|
|
32
|
+
const whitelist = ['https://example.com'];
|
|
33
|
+
if (!origin || whitelist.includes(origin)) {
|
|
34
|
+
callback(null, true);
|
|
35
|
+
} else {
|
|
36
|
+
callback(new Error('Not allowed by CORS'));
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Helmet (Security Headers)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install helmet
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import helmet from 'helmet';
|
|
50
|
+
|
|
51
|
+
async function bootstrap() {
|
|
52
|
+
const app = await NestFactory.create(AppModule);
|
|
53
|
+
app.use(helmet());
|
|
54
|
+
await app.listen(3000);
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Rate Limiting
|
|
59
|
+
|
|
60
|
+
Protect against brute-force attacks:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install @nestjs/throttler
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Basic Setup
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { Module } from '@nestjs/common';
|
|
70
|
+
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
|
|
71
|
+
import { APP_GUARD } from '@nestjs/core';
|
|
72
|
+
|
|
73
|
+
@Module({
|
|
74
|
+
imports: [
|
|
75
|
+
ThrottlerModule.forRoot({
|
|
76
|
+
throttlers: [
|
|
77
|
+
{ ttl: 60000, limit: 10 }, // 10 requests per minute
|
|
78
|
+
],
|
|
79
|
+
}),
|
|
80
|
+
],
|
|
81
|
+
providers: [
|
|
82
|
+
{ provide: APP_GUARD, useClass: ThrottlerGuard },
|
|
83
|
+
],
|
|
84
|
+
})
|
|
85
|
+
export class AppModule {}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Multiple Limits
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
ThrottlerModule.forRoot({
|
|
92
|
+
throttlers: [
|
|
93
|
+
{ name: 'short', ttl: 1000, limit: 3 }, // 3 per second
|
|
94
|
+
{ name: 'medium', ttl: 10000, limit: 20 }, // 20 per 10 seconds
|
|
95
|
+
{ name: 'long', ttl: 60000, limit: 100 }, // 100 per minute
|
|
96
|
+
],
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Skip Routes
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { SkipThrottle } from '@nestjs/throttler';
|
|
104
|
+
|
|
105
|
+
@SkipThrottle()
|
|
106
|
+
@Controller('health')
|
|
107
|
+
export class HealthController {}
|
|
108
|
+
|
|
109
|
+
// Or skip specific throttlers
|
|
110
|
+
@SkipThrottle({ short: true })
|
|
111
|
+
@Get()
|
|
112
|
+
findAll() {}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Override Limits
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { Throttle } from '@nestjs/throttler';
|
|
119
|
+
|
|
120
|
+
@Throttle({ default: { limit: 3, ttl: 60000 } })
|
|
121
|
+
@Get()
|
|
122
|
+
findAll() {}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Behind Proxy
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// main.ts
|
|
129
|
+
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
|
130
|
+
app.set('trust proxy', 'loopback');
|
|
131
|
+
|
|
132
|
+
// Custom tracker for proxy
|
|
133
|
+
@Injectable()
|
|
134
|
+
export class ThrottlerBehindProxyGuard extends ThrottlerGuard {
|
|
135
|
+
protected async getTracker(req: Record<string, any>): Promise<string> {
|
|
136
|
+
return req.ips.length ? req.ips[0] : req.ip;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Redis Storage
|
|
142
|
+
|
|
143
|
+
For distributed systems:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npm install @nest-lab/throttler-storage-redis
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis';
|
|
151
|
+
|
|
152
|
+
ThrottlerModule.forRoot({
|
|
153
|
+
throttlers: [{ ttl: 60000, limit: 10 }],
|
|
154
|
+
storage: new ThrottlerStorageRedisService('redis://localhost:6379'),
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### WebSocket Rate Limiting
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
@Injectable()
|
|
162
|
+
export class WsThrottlerGuard extends ThrottlerGuard {
|
|
163
|
+
async handleRequest(requestProps: ThrottlerRequest): Promise<boolean> {
|
|
164
|
+
const { context, limit, ttl } = requestProps;
|
|
165
|
+
const client = context.switchToWs().getClient();
|
|
166
|
+
const tracker = client._socket.remoteAddress;
|
|
167
|
+
// Custom logic
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### GraphQL Rate Limiting
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
@Injectable()
|
|
177
|
+
export class GqlThrottlerGuard extends ThrottlerGuard {
|
|
178
|
+
getRequestResponse(context: ExecutionContext) {
|
|
179
|
+
const gqlCtx = GqlExecutionContext.create(context);
|
|
180
|
+
const ctx = gqlCtx.getContext();
|
|
181
|
+
return { req: ctx.req, res: ctx.res };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Time Helpers
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { seconds, minutes, hours } from '@nestjs/throttler';
|
|
190
|
+
|
|
191
|
+
ThrottlerModule.forRoot({
|
|
192
|
+
throttlers: [
|
|
193
|
+
{ ttl: seconds(30), limit: 10 },
|
|
194
|
+
{ ttl: minutes(5), limit: 100 },
|
|
195
|
+
],
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
<!--
|
|
200
|
+
Source references:
|
|
201
|
+
- https://docs.nestjs.com/security/cors
|
|
202
|
+
- https://docs.nestjs.com/security/helmet
|
|
203
|
+
- https://docs.nestjs.com/security/rate-limiting
|
|
204
|
+
-->
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: encryption-hashing
|
|
3
|
+
description: Encryption and password hashing (bcrypt, argon2) for NestJS
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Encryption and Hashing
|
|
7
|
+
|
|
8
|
+
Nest uses Node.js built-in `crypto` for encryption. For password hashing, use bcrypt or argon2.
|
|
9
|
+
|
|
10
|
+
## Encryption (crypto)
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import { createCipheriv, createDecipheriv, randomBytes, scrypt } from 'node:crypto';
|
|
14
|
+
import { promisify } from 'node:util';
|
|
15
|
+
|
|
16
|
+
const iv = randomBytes(16);
|
|
17
|
+
const key = (await promisify(scrypt)(password, 'salt', 32)) as Buffer;
|
|
18
|
+
|
|
19
|
+
// Encrypt
|
|
20
|
+
const cipher = createCipheriv('aes-256-ctr', key, iv);
|
|
21
|
+
const encrypted = Buffer.concat([
|
|
22
|
+
cipher.update(textToEncrypt),
|
|
23
|
+
cipher.final(),
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
// Decrypt
|
|
27
|
+
const decipher = createDecipheriv('aes-256-ctr', key, iv);
|
|
28
|
+
const decrypted = Buffer.concat([
|
|
29
|
+
decipher.update(encrypted),
|
|
30
|
+
decipher.final(),
|
|
31
|
+
]);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Hashing (bcrypt)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm i bcrypt
|
|
38
|
+
npm i -D @types/bcrypt
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import * as bcrypt from 'bcrypt';
|
|
43
|
+
|
|
44
|
+
// Hash password
|
|
45
|
+
const saltOrRounds = 10;
|
|
46
|
+
const hash = await bcrypt.hash(password, saltOrRounds);
|
|
47
|
+
|
|
48
|
+
// Generate salt
|
|
49
|
+
const salt = await bcrypt.genSalt();
|
|
50
|
+
|
|
51
|
+
// Verify
|
|
52
|
+
const isMatch = await bcrypt.compare(password, hash);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Hashing (argon2)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm i argon2
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import * as argon2 from 'argon2';
|
|
63
|
+
|
|
64
|
+
const hash = await argon2.hash(password);
|
|
65
|
+
const isMatch = await argon2.verify(hash, password);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Injectable Hashing Service
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
@Injectable()
|
|
72
|
+
export class HashingService {
|
|
73
|
+
async hash(password: string): Promise<string> {
|
|
74
|
+
return bcrypt.hash(password, 10);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async compare(password: string, hash: string): Promise<boolean> {
|
|
78
|
+
return bcrypt.compare(password, hash);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Key Points
|
|
84
|
+
|
|
85
|
+
- Use bcrypt or argon2 for passwords (argon2 preferred for new apps)
|
|
86
|
+
- Node.js `crypto` for encryption/decryption
|
|
87
|
+
- Never store plain passwords
|
|
88
|
+
- Use strong salts; bcrypt handles salt internally
|
|
89
|
+
|
|
90
|
+
<!--
|
|
91
|
+
Source references:
|
|
92
|
+
- https://docs.nestjs.com/security/encryption-and-hashing
|
|
93
|
+
-->
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: caching
|
|
3
|
+
description: Caching with @nestjs/cache-manager and Redis integration
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Caching
|
|
7
|
+
|
|
8
|
+
Caching improves performance by storing frequently accessed data for quick retrieval.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @nestjs/cache-manager cache-manager
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Basic Setup
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { Module } from '@nestjs/common';
|
|
20
|
+
import { CacheModule } from '@nestjs/cache-manager';
|
|
21
|
+
|
|
22
|
+
@Module({
|
|
23
|
+
imports: [CacheModule.register()],
|
|
24
|
+
})
|
|
25
|
+
export class AppModule {}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Interacting with Cache
|
|
29
|
+
|
|
30
|
+
Inject the cache manager:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { Injectable, Inject } from '@nestjs/common';
|
|
34
|
+
import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager';
|
|
35
|
+
|
|
36
|
+
@Injectable()
|
|
37
|
+
export class CatsService {
|
|
38
|
+
constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}
|
|
39
|
+
|
|
40
|
+
async getCats() {
|
|
41
|
+
// Get from cache
|
|
42
|
+
const cached = await this.cacheManager.get('cats');
|
|
43
|
+
if (cached) return cached;
|
|
44
|
+
|
|
45
|
+
const cats = await this.fetchCats();
|
|
46
|
+
|
|
47
|
+
// Set with TTL (milliseconds)
|
|
48
|
+
await this.cacheManager.set('cats', cats, 60000);
|
|
49
|
+
return cats;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async clearCache() {
|
|
53
|
+
await this.cacheManager.del('cats');
|
|
54
|
+
// Or clear all
|
|
55
|
+
await this.cacheManager.clear();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Auto-caching Responses
|
|
61
|
+
|
|
62
|
+
Use `CacheInterceptor` for automatic GET endpoint caching:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { Controller, Get, UseInterceptors } from '@nestjs/common';
|
|
66
|
+
import { CacheInterceptor, CacheKey, CacheTTL } from '@nestjs/cache-manager';
|
|
67
|
+
|
|
68
|
+
@Controller('cats')
|
|
69
|
+
@UseInterceptors(CacheInterceptor)
|
|
70
|
+
export class CatsController {
|
|
71
|
+
@Get()
|
|
72
|
+
@CacheKey('all-cats')
|
|
73
|
+
@CacheTTL(30000)
|
|
74
|
+
findAll() {
|
|
75
|
+
return this.catsService.findAll();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Global cache interceptor:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
@Module({
|
|
84
|
+
providers: [
|
|
85
|
+
{ provide: APP_INTERCEPTOR, useClass: CacheInterceptor },
|
|
86
|
+
],
|
|
87
|
+
})
|
|
88
|
+
export class AppModule {}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
CacheModule.register({
|
|
95
|
+
ttl: 5000, // Default TTL in milliseconds
|
|
96
|
+
isGlobal: true, // Available everywhere without importing
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Redis Integration
|
|
101
|
+
|
|
102
|
+
Install Redis adapter:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm install @keyv/redis
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Configure multiple stores:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { CacheModule } from '@nestjs/cache-manager';
|
|
112
|
+
import KeyvRedis from '@keyv/redis';
|
|
113
|
+
import { Keyv } from 'keyv';
|
|
114
|
+
import { CacheableMemory } from 'cacheable';
|
|
115
|
+
|
|
116
|
+
@Module({
|
|
117
|
+
imports: [
|
|
118
|
+
CacheModule.registerAsync({
|
|
119
|
+
useFactory: async () => ({
|
|
120
|
+
stores: [
|
|
121
|
+
new Keyv({ store: new CacheableMemory({ ttl: 60000 }) }),
|
|
122
|
+
new KeyvRedis('redis://localhost:6379'),
|
|
123
|
+
],
|
|
124
|
+
}),
|
|
125
|
+
}),
|
|
126
|
+
],
|
|
127
|
+
})
|
|
128
|
+
export class AppModule {}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Key Points
|
|
132
|
+
|
|
133
|
+
- Only GET endpoints are auto-cached
|
|
134
|
+
- `CacheInterceptor` doesn't work with GraphQL field resolvers
|
|
135
|
+
- Use `@CacheKey()` for custom cache keys
|
|
136
|
+
- Set `ttl: 0` for no expiration
|
|
137
|
+
- Works with WebSockets and Microservices
|
|
138
|
+
|
|
139
|
+
<!--
|
|
140
|
+
Source references:
|
|
141
|
+
- https://docs.nestjs.com/techniques/caching
|
|
142
|
+
-->
|