nestjs-ddd-cli 2.2.0 → 3.2.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/README.md +247 -408
- package/ddd.schema.json +111 -0
- package/dist/commands/aggregate-validator.d.ts +9 -0
- package/dist/commands/aggregate-validator.js +953 -0
- package/dist/commands/aggregate-validator.js.map +1 -0
- package/dist/commands/ai-assist.d.ts +8 -0
- package/dist/commands/ai-assist.js +337 -0
- package/dist/commands/ai-assist.js.map +1 -0
- package/dist/commands/api-contracts.d.ts +9 -0
- package/dist/commands/api-contracts.js +1368 -0
- package/dist/commands/api-contracts.js.map +1 -0
- package/dist/commands/api-docs.d.ts +8 -0
- package/dist/commands/api-docs.js +408 -0
- package/dist/commands/api-docs.js.map +1 -0
- package/dist/commands/api-versioning.d.ts +11 -0
- package/dist/commands/api-versioning.js +643 -0
- package/dist/commands/api-versioning.js.map +1 -0
- package/dist/commands/audit-logging.d.ts +9 -0
- package/dist/commands/audit-logging.js +1129 -0
- package/dist/commands/audit-logging.js.map +1 -0
- package/dist/commands/batch-generate.d.ts +10 -0
- package/dist/commands/batch-generate.js +405 -0
- package/dist/commands/batch-generate.js.map +1 -0
- package/dist/commands/caching-strategies.d.ts +9 -0
- package/dist/commands/caching-strategies.js +874 -0
- package/dist/commands/caching-strategies.js.map +1 -0
- package/dist/commands/code-analyzer.d.ts +42 -0
- package/dist/commands/code-analyzer.js +474 -0
- package/dist/commands/code-analyzer.js.map +1 -0
- package/dist/commands/database-seeding.d.ts +6 -0
- package/dist/commands/database-seeding.js +621 -0
- package/dist/commands/database-seeding.js.map +1 -0
- package/dist/commands/db-optimization.d.ts +7 -0
- package/dist/commands/db-optimization.js +687 -0
- package/dist/commands/db-optimization.js.map +1 -0
- package/dist/commands/dependency-graph.d.ts +6 -0
- package/dist/commands/dependency-graph.js +329 -0
- package/dist/commands/dependency-graph.js.map +1 -0
- package/dist/commands/doctor-enhanced.d.ts +22 -0
- package/dist/commands/doctor-enhanced.js +543 -0
- package/dist/commands/doctor-enhanced.js.map +1 -0
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.js +151 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/env-manager.d.ts +6 -0
- package/dist/commands/env-manager.js +419 -0
- package/dist/commands/env-manager.js.map +1 -0
- package/dist/commands/event-sourcing-full.d.ts +10 -0
- package/dist/commands/event-sourcing-full.js +1107 -0
- package/dist/commands/event-sourcing-full.js.map +1 -0
- package/dist/commands/feature-flags.d.ts +9 -0
- package/dist/commands/feature-flags.js +824 -0
- package/dist/commands/feature-flags.js.map +1 -0
- package/dist/commands/filter-dsl.d.ts +10 -0
- package/dist/commands/filter-dsl.js +1407 -0
- package/dist/commands/filter-dsl.js.map +1 -0
- package/dist/commands/generate-all.js +485 -32
- package/dist/commands/generate-all.js.map +1 -1
- package/dist/commands/generate-deployment.d.ts +8 -0
- package/dist/commands/generate-deployment.js +746 -0
- package/dist/commands/generate-deployment.js.map +1 -0
- package/dist/commands/generate-domain-service.d.ts +14 -0
- package/dist/commands/generate-domain-service.js +796 -0
- package/dist/commands/generate-domain-service.js.map +1 -0
- package/dist/commands/generate-entity.js +82 -24
- package/dist/commands/generate-entity.js.map +1 -1
- package/dist/commands/generate-from-schema.d.ts +56 -0
- package/dist/commands/generate-from-schema.js +222 -0
- package/dist/commands/generate-from-schema.js.map +1 -0
- package/dist/commands/generate-orchestrator.d.ts +14 -0
- package/dist/commands/generate-orchestrator.js +887 -0
- package/dist/commands/generate-orchestrator.js.map +1 -0
- package/dist/commands/generate-repository.d.ts +14 -0
- package/dist/commands/generate-repository.js +1019 -0
- package/dist/commands/generate-repository.js.map +1 -0
- package/dist/commands/generate-shared.d.ts +4 -0
- package/dist/commands/generate-shared.js +388 -0
- package/dist/commands/generate-shared.js.map +1 -0
- package/dist/commands/generate-value-object.d.ts +32 -0
- package/dist/commands/generate-value-object.js +700 -0
- package/dist/commands/generate-value-object.js.map +1 -0
- package/dist/commands/graphql-subscriptions.d.ts +6 -0
- package/dist/commands/graphql-subscriptions.js +607 -0
- package/dist/commands/graphql-subscriptions.js.map +1 -0
- package/dist/commands/graphql-types.d.ts +5 -0
- package/dist/commands/graphql-types.js +423 -0
- package/dist/commands/graphql-types.js.map +1 -0
- package/dist/commands/health-probes-advanced.d.ts +6 -0
- package/dist/commands/health-probes-advanced.js +655 -0
- package/dist/commands/health-probes-advanced.js.map +1 -0
- package/dist/commands/i18n-setup.d.ts +10 -0
- package/dist/commands/i18n-setup.js +677 -0
- package/dist/commands/i18n-setup.js.map +1 -0
- package/dist/commands/init-config.d.ts +6 -0
- package/dist/commands/init-config.js +370 -0
- package/dist/commands/init-config.js.map +1 -0
- package/dist/commands/init-project.js +56 -6
- package/dist/commands/init-project.js.map +1 -1
- package/dist/commands/interactive-scaffold.d.ts +5 -0
- package/dist/commands/interactive-scaffold.js +271 -0
- package/dist/commands/interactive-scaffold.js.map +1 -0
- package/dist/commands/metrics-prometheus.d.ts +6 -0
- package/dist/commands/metrics-prometheus.js +681 -0
- package/dist/commands/metrics-prometheus.js.map +1 -0
- package/dist/commands/migration-engine.d.ts +6 -0
- package/dist/commands/migration-engine.js +446 -0
- package/dist/commands/migration-engine.js.map +1 -0
- package/dist/commands/migration.d.ts +12 -0
- package/dist/commands/migration.js +484 -0
- package/dist/commands/migration.js.map +1 -0
- package/dist/commands/monorepo.d.ts +8 -0
- package/dist/commands/monorepo.js +483 -0
- package/dist/commands/monorepo.js.map +1 -0
- package/dist/commands/multi-database.d.ts +5 -0
- package/dist/commands/multi-database.js +439 -0
- package/dist/commands/multi-database.js.map +1 -0
- package/dist/commands/observability-tracing.d.ts +10 -0
- package/dist/commands/observability-tracing.js +740 -0
- package/dist/commands/observability-tracing.js.map +1 -0
- package/dist/commands/openapi-export.d.ts +8 -0
- package/dist/commands/openapi-export.js +359 -0
- package/dist/commands/openapi-export.js.map +1 -0
- package/dist/commands/perf-analyzer.d.ts +8 -0
- package/dist/commands/perf-analyzer.js +423 -0
- package/dist/commands/perf-analyzer.js.map +1 -0
- package/dist/commands/rate-limiting.d.ts +10 -0
- package/dist/commands/rate-limiting.js +953 -0
- package/dist/commands/rate-limiting.js.map +1 -0
- package/dist/commands/recipe-plugin.d.ts +56 -0
- package/dist/commands/recipe-plugin.js +315 -0
- package/dist/commands/recipe-plugin.js.map +1 -0
- package/dist/commands/recipe.d.ts +6 -0
- package/dist/commands/recipe.js +3941 -0
- package/dist/commands/recipe.js.map +1 -0
- package/dist/commands/recipes/elasticsearch.recipe.d.ts +1 -0
- package/dist/commands/recipes/elasticsearch.recipe.js +761 -0
- package/dist/commands/recipes/elasticsearch.recipe.js.map +1 -0
- package/dist/commands/recipes/event-sourcing.recipe.d.ts +1 -0
- package/dist/commands/recipes/event-sourcing.recipe.js +889 -0
- package/dist/commands/recipes/event-sourcing.recipe.js.map +1 -0
- package/dist/commands/recipes/index.d.ts +7 -0
- package/dist/commands/recipes/index.js +24 -0
- package/dist/commands/recipes/index.js.map +1 -0
- package/dist/commands/recipes/message-queue.recipe.d.ts +1 -0
- package/dist/commands/recipes/message-queue.recipe.js +706 -0
- package/dist/commands/recipes/message-queue.recipe.js.map +1 -0
- package/dist/commands/recipes/middleware.recipe.d.ts +1 -0
- package/dist/commands/recipes/middleware.recipe.js +383 -0
- package/dist/commands/recipes/middleware.recipe.js.map +1 -0
- package/dist/commands/recipes/multi-tenancy.recipe.d.ts +1 -0
- package/dist/commands/recipes/multi-tenancy.recipe.js +520 -0
- package/dist/commands/recipes/multi-tenancy.recipe.js.map +1 -0
- package/dist/commands/recipes/oauth2.recipe.d.ts +1 -0
- package/dist/commands/recipes/oauth2.recipe.js +472 -0
- package/dist/commands/recipes/oauth2.recipe.js.map +1 -0
- package/dist/commands/recipes/websocket.recipe.d.ts +1 -0
- package/dist/commands/recipes/websocket.recipe.js +453 -0
- package/dist/commands/recipes/websocket.recipe.js.map +1 -0
- package/dist/commands/resilience-patterns.d.ts +13 -0
- package/dist/commands/resilience-patterns.js +1029 -0
- package/dist/commands/resilience-patterns.js.map +1 -0
- package/dist/commands/security-patterns.d.ts +11 -0
- package/dist/commands/security-patterns.js +2233 -0
- package/dist/commands/security-patterns.js.map +1 -0
- package/dist/commands/template-debug.d.ts +27 -0
- package/dist/commands/template-debug.js +388 -0
- package/dist/commands/template-debug.js.map +1 -0
- package/dist/commands/test-factory-full.d.ts +9 -0
- package/dist/commands/test-factory-full.js +1570 -0
- package/dist/commands/test-factory-full.js.map +1 -0
- package/dist/commands/test-scaffold.d.ts +7 -0
- package/dist/commands/test-scaffold.js +621 -0
- package/dist/commands/test-scaffold.js.map +1 -0
- package/dist/index.js +1088 -0
- package/dist/index.js.map +1 -1
- package/dist/templates/ai-context/CLAUDE.md.hbs +158 -0
- package/dist/templates/ai-context/conventions.md.hbs +154 -0
- package/dist/templates/command/create-command.hbs +6 -14
- package/dist/templates/command/delete-command.hbs +19 -0
- package/dist/templates/command/update-command.hbs +24 -0
- package/dist/templates/controller/controller.hbs +64 -17
- package/dist/templates/dto/create-dto.hbs +29 -5
- package/dist/templates/dto/filter-dto.hbs +52 -0
- package/dist/templates/dto/filter-query.dto.hbs +148 -0
- package/dist/templates/dto/paginated-response.dto.hbs +29 -0
- package/dist/templates/dto/pagination-query.dto.hbs +30 -0
- package/dist/templates/dto/response-dto.hbs +38 -0
- package/dist/templates/dto/update-dto.hbs +11 -0
- package/dist/templates/entity/entity.hbs +32 -1
- package/dist/templates/event/domain-event.hbs +33 -7
- package/dist/templates/event/event-handler.hbs +40 -0
- package/dist/templates/exception/base-exceptions.hbs +69 -0
- package/dist/templates/exception/entity-not-found.exception.hbs +7 -0
- package/dist/templates/mapper/mapper.hbs +49 -24
- package/dist/templates/module/module.hbs +34 -10
- package/dist/templates/orm-entity/orm-entity.hbs +63 -12
- package/dist/templates/prisma/prisma-mapper.hbs +71 -0
- package/dist/templates/prisma/prisma-repository.hbs +114 -0
- package/dist/templates/prisma/prisma-schema.hbs +20 -0
- package/dist/templates/prisma/prisma-service.hbs +51 -0
- package/dist/templates/query/get-all.query.hbs +50 -0
- package/dist/templates/query/get-by-id.query.hbs +31 -0
- package/dist/templates/repository/repository.hbs +55 -13
- package/dist/templates/resolver/graphql-input.hbs +54 -0
- package/dist/templates/resolver/graphql-type.hbs +58 -0
- package/dist/templates/resolver/pagination-args.hbs +33 -0
- package/dist/templates/resolver/resolver.hbs +62 -0
- package/dist/templates/shared/prisma-query-builder.util.hbs +189 -0
- package/dist/templates/shared/query-builder.util.hbs +218 -0
- package/dist/templates/test/controller.spec.hbs +124 -0
- package/dist/templates/test/repository.spec.hbs +158 -0
- package/dist/templates/test/usecase.spec.hbs +116 -0
- package/dist/templates/usecase/create-usecase.hbs +19 -7
- package/dist/templates/usecase/delete-usecase.hbs +17 -0
- package/dist/templates/usecase/update-usecase.hbs +31 -0
- package/dist/utils/config.utils.d.ts +45 -0
- package/dist/utils/config.utils.js +211 -0
- package/dist/utils/config.utils.js.map +1 -0
- package/dist/utils/error.utils.d.ts +145 -0
- package/dist/utils/error.utils.js +422 -0
- package/dist/utils/error.utils.js.map +1 -0
- package/dist/utils/field.utils.d.ts +54 -0
- package/dist/utils/field.utils.js +389 -0
- package/dist/utils/field.utils.js.map +1 -0
- package/dist/utils/file.utils.d.ts +19 -8
- package/dist/utils/file.utils.js +135 -4
- package/dist/utils/file.utils.js.map +1 -1
- package/dist/utils/idempotency.utils.d.ts +123 -0
- package/dist/utils/idempotency.utils.js +444 -0
- package/dist/utils/idempotency.utils.js.map +1 -0
- package/dist/utils/naming.utils.js +26 -3
- package/dist/utils/naming.utils.js.map +1 -1
- package/dist/utils/performance.utils.d.ts +37 -0
- package/dist/utils/performance.utils.js +158 -0
- package/dist/utils/performance.utils.js.map +1 -0
- package/dist/utils/relation.utils.d.ts +92 -0
- package/dist/utils/relation.utils.js +388 -0
- package/dist/utils/relation.utils.js.map +1 -0
- package/dist/utils/rollback.utils.d.ts +49 -0
- package/dist/utils/rollback.utils.js +306 -0
- package/dist/utils/rollback.utils.js.map +1 -0
- package/dist/utils/schema.utils.d.ts +123 -0
- package/dist/utils/schema.utils.js +419 -0
- package/dist/utils/schema.utils.js.map +1 -0
- package/dist/utils/security.utils.d.ts +57 -0
- package/dist/utils/security.utils.js +315 -0
- package/dist/utils/security.utils.js.map +1 -0
- package/dist/utils/template-engine.utils.d.ts +80 -0
- package/dist/utils/template-engine.utils.js +463 -0
- package/dist/utils/template-engine.utils.js.map +1 -0
- package/dist/utils/validation-registry.utils.d.ts +160 -0
- package/dist/utils/validation-registry.utils.js +526 -0
- package/dist/utils/validation-registry.utils.js.map +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.applyMiddlewareRecipe = applyMiddlewareRecipe;
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const file_utils_1 = require("../../utils/file.utils");
|
|
43
|
+
async function applyMiddlewareRecipe(basePath) {
|
|
44
|
+
const sharedPath = path.join(basePath, 'src/shared');
|
|
45
|
+
const middlewarePath = path.join(sharedPath, 'middleware');
|
|
46
|
+
const guardsPath = path.join(sharedPath, 'guards');
|
|
47
|
+
const interceptorsPath = path.join(sharedPath, 'interceptors');
|
|
48
|
+
await (0, file_utils_1.ensureDir)(middlewarePath);
|
|
49
|
+
await (0, file_utils_1.ensureDir)(guardsPath);
|
|
50
|
+
await (0, file_utils_1.ensureDir)(interceptorsPath);
|
|
51
|
+
// Request Logger Middleware
|
|
52
|
+
const loggerMiddlewareContent = `import { Injectable, NestMiddleware, Logger } from "@nestjs/common";
|
|
53
|
+
import { Request, Response, NextFunction } from "express";
|
|
54
|
+
import { v4 as uuid } from "uuid";
|
|
55
|
+
|
|
56
|
+
@Injectable()
|
|
57
|
+
export class RequestLoggerMiddleware implements NestMiddleware {
|
|
58
|
+
private readonly logger = new Logger("HTTP");
|
|
59
|
+
|
|
60
|
+
use(req: Request, res: Response, next: NextFunction) {
|
|
61
|
+
const requestId = (req.headers["x-request-id"] as string) || uuid();
|
|
62
|
+
const startTime = Date.now();
|
|
63
|
+
const { method, originalUrl, ip } = req;
|
|
64
|
+
const userAgent = req.get("user-agent") || "";
|
|
65
|
+
|
|
66
|
+
// Attach request ID to request object
|
|
67
|
+
(req as any).requestId = requestId;
|
|
68
|
+
res.setHeader("X-Request-Id", requestId);
|
|
69
|
+
|
|
70
|
+
// Log request
|
|
71
|
+
this.logger.log(\`--> \${method} \${originalUrl} [ID: \${requestId}]\`);
|
|
72
|
+
|
|
73
|
+
// Log response when finished
|
|
74
|
+
res.on("finish", () => {
|
|
75
|
+
const duration = Date.now() - startTime;
|
|
76
|
+
const { statusCode } = res;
|
|
77
|
+
const contentLength = res.get("content-length") || 0;
|
|
78
|
+
|
|
79
|
+
const logLevel = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "log";
|
|
80
|
+
|
|
81
|
+
this.logger[\`\${logLevel}\`](
|
|
82
|
+
\`<-- \${method} \${originalUrl} \${statusCode} \${duration}ms \${contentLength}b [ID: \${requestId}]\`
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
next();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
await (0, file_utils_1.writeFile)(path.join(middlewarePath, 'request-logger.middleware.ts'), loggerMiddlewareContent);
|
|
91
|
+
// Correlation ID Middleware
|
|
92
|
+
const correlationMiddlewareContent = `import { Injectable, NestMiddleware } from "@nestjs/common";
|
|
93
|
+
import { Request, Response, NextFunction } from "express";
|
|
94
|
+
import { v4 as uuid } from "uuid";
|
|
95
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
96
|
+
|
|
97
|
+
export interface CorrelationContext {
|
|
98
|
+
correlationId: string;
|
|
99
|
+
requestId: string;
|
|
100
|
+
userId?: string;
|
|
101
|
+
tenantId?: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const correlationStorage = new AsyncLocalStorage<CorrelationContext>();
|
|
105
|
+
|
|
106
|
+
@Injectable()
|
|
107
|
+
export class CorrelationIdMiddleware implements NestMiddleware {
|
|
108
|
+
use(req: Request, res: Response, next: NextFunction) {
|
|
109
|
+
const correlationId = (req.headers["x-correlation-id"] as string) || uuid();
|
|
110
|
+
const requestId = (req.headers["x-request-id"] as string) || uuid();
|
|
111
|
+
|
|
112
|
+
const context: CorrelationContext = {
|
|
113
|
+
correlationId,
|
|
114
|
+
requestId,
|
|
115
|
+
userId: (req as any).user?.id,
|
|
116
|
+
tenantId: (req as any).tenantId,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
res.setHeader("X-Correlation-Id", correlationId);
|
|
120
|
+
res.setHeader("X-Request-Id", requestId);
|
|
121
|
+
|
|
122
|
+
correlationStorage.run(context, () => next());
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function getCorrelationContext(): CorrelationContext | undefined {
|
|
127
|
+
return correlationStorage.getStore();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function getCorrelationId(): string | undefined {
|
|
131
|
+
return correlationStorage.getStore()?.correlationId;
|
|
132
|
+
}
|
|
133
|
+
`;
|
|
134
|
+
await (0, file_utils_1.writeFile)(path.join(middlewarePath, 'correlation-id.middleware.ts'), correlationMiddlewareContent);
|
|
135
|
+
// Permission Guard
|
|
136
|
+
const permissionGuardContent = `import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
|
|
137
|
+
import { Reflector } from "@nestjs/core";
|
|
138
|
+
|
|
139
|
+
export const PERMISSIONS_KEY = "permissions";
|
|
140
|
+
export const Permissions = (...permissions: string[]) =>
|
|
141
|
+
Reflect.metadata(PERMISSIONS_KEY, permissions);
|
|
142
|
+
|
|
143
|
+
@Injectable()
|
|
144
|
+
export class PermissionGuard implements CanActivate {
|
|
145
|
+
constructor(private reflector: Reflector) {}
|
|
146
|
+
|
|
147
|
+
canActivate(context: ExecutionContext): boolean {
|
|
148
|
+
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
|
|
149
|
+
PERMISSIONS_KEY,
|
|
150
|
+
[context.getHandler(), context.getClass()]
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
if (!requiredPermissions || requiredPermissions.length === 0) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const { user } = context.switchToHttp().getRequest();
|
|
158
|
+
|
|
159
|
+
if (!user) {
|
|
160
|
+
throw new ForbiddenException("User not authenticated");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const userPermissions: string[] = user.permissions || [];
|
|
164
|
+
const hasPermission = requiredPermissions.every((permission) =>
|
|
165
|
+
userPermissions.includes(permission)
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
if (!hasPermission) {
|
|
169
|
+
throw new ForbiddenException(
|
|
170
|
+
\`Missing required permissions: \${requiredPermissions.join(", ")}\`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
`;
|
|
178
|
+
await (0, file_utils_1.writeFile)(path.join(guardsPath, 'permission.guard.ts'), permissionGuardContent);
|
|
179
|
+
// Resource Owner Guard
|
|
180
|
+
const resourceOwnerGuardContent = `import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
|
|
181
|
+
import { Reflector } from "@nestjs/core";
|
|
182
|
+
|
|
183
|
+
export const RESOURCE_OWNER_KEY = "resource_owner";
|
|
184
|
+
export const ResourceOwner = (paramName: string = "id", userField: string = "userId") =>
|
|
185
|
+
Reflect.metadata(RESOURCE_OWNER_KEY, { paramName, userField });
|
|
186
|
+
|
|
187
|
+
@Injectable()
|
|
188
|
+
export class ResourceOwnerGuard implements CanActivate {
|
|
189
|
+
constructor(private reflector: Reflector) {}
|
|
190
|
+
|
|
191
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
192
|
+
const config = this.reflector.get<{ paramName: string; userField: string }>(
|
|
193
|
+
RESOURCE_OWNER_KEY,
|
|
194
|
+
context.getHandler()
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
if (!config) return true;
|
|
198
|
+
|
|
199
|
+
const request = context.switchToHttp().getRequest();
|
|
200
|
+
const user = request.user;
|
|
201
|
+
|
|
202
|
+
if (!user) {
|
|
203
|
+
throw new ForbiddenException("User not authenticated");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Admin bypass
|
|
207
|
+
if (user.roles?.includes("admin")) return true;
|
|
208
|
+
|
|
209
|
+
const resourceId = request.params[config.paramName];
|
|
210
|
+
const userId = user.id;
|
|
211
|
+
|
|
212
|
+
// You should inject and use your repository here
|
|
213
|
+
// This is a placeholder - implement actual resource lookup
|
|
214
|
+
const resource = request.resource; // Assume loaded by previous middleware/interceptor
|
|
215
|
+
|
|
216
|
+
if (resource && resource[config.userField] !== userId) {
|
|
217
|
+
throw new ForbiddenException("You do not own this resource");
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
`;
|
|
224
|
+
await (0, file_utils_1.writeFile)(path.join(guardsPath, 'resource-owner.guard.ts'), resourceOwnerGuardContent);
|
|
225
|
+
// Transform Response Interceptor
|
|
226
|
+
const transformInterceptorContent = `import {
|
|
227
|
+
Injectable,
|
|
228
|
+
NestInterceptor,
|
|
229
|
+
ExecutionContext,
|
|
230
|
+
CallHandler,
|
|
231
|
+
} from "@nestjs/common";
|
|
232
|
+
import { Observable } from "rxjs";
|
|
233
|
+
import { map } from "rxjs/operators";
|
|
234
|
+
import { Reflector } from "@nestjs/core";
|
|
235
|
+
|
|
236
|
+
export interface StandardResponse<T> {
|
|
237
|
+
success: boolean;
|
|
238
|
+
data: T;
|
|
239
|
+
meta?: {
|
|
240
|
+
timestamp: string;
|
|
241
|
+
requestId?: string;
|
|
242
|
+
[key: string]: any;
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export const SKIP_TRANSFORM_KEY = "skip_transform";
|
|
247
|
+
export const SkipTransform = () => Reflect.metadata(SKIP_TRANSFORM_KEY, true);
|
|
248
|
+
|
|
249
|
+
@Injectable()
|
|
250
|
+
export class TransformResponseInterceptor<T>
|
|
251
|
+
implements NestInterceptor<T, StandardResponse<T>>
|
|
252
|
+
{
|
|
253
|
+
constructor(private reflector: Reflector) {}
|
|
254
|
+
|
|
255
|
+
intercept(
|
|
256
|
+
context: ExecutionContext,
|
|
257
|
+
next: CallHandler
|
|
258
|
+
): Observable<StandardResponse<T>> {
|
|
259
|
+
const skipTransform = this.reflector.get<boolean>(
|
|
260
|
+
SKIP_TRANSFORM_KEY,
|
|
261
|
+
context.getHandler()
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
if (skipTransform) {
|
|
265
|
+
return next.handle();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const request = context.switchToHttp().getRequest();
|
|
269
|
+
|
|
270
|
+
return next.handle().pipe(
|
|
271
|
+
map((data) => ({
|
|
272
|
+
success: true,
|
|
273
|
+
data,
|
|
274
|
+
meta: {
|
|
275
|
+
timestamp: new Date().toISOString(),
|
|
276
|
+
requestId: request.requestId || request.headers["x-request-id"],
|
|
277
|
+
},
|
|
278
|
+
}))
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
`;
|
|
283
|
+
await (0, file_utils_1.writeFile)(path.join(interceptorsPath, 'transform-response.interceptor.ts'), transformInterceptorContent);
|
|
284
|
+
// Timing Interceptor
|
|
285
|
+
const timingInterceptorContent = `import {
|
|
286
|
+
Injectable,
|
|
287
|
+
NestInterceptor,
|
|
288
|
+
ExecutionContext,
|
|
289
|
+
CallHandler,
|
|
290
|
+
Logger,
|
|
291
|
+
} from "@nestjs/common";
|
|
292
|
+
import { Observable } from "rxjs";
|
|
293
|
+
import { tap } from "rxjs/operators";
|
|
294
|
+
|
|
295
|
+
@Injectable()
|
|
296
|
+
export class TimingInterceptor implements NestInterceptor {
|
|
297
|
+
private readonly logger = new Logger("Timing");
|
|
298
|
+
|
|
299
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
300
|
+
const request = context.switchToHttp().getRequest();
|
|
301
|
+
const response = context.switchToHttp().getResponse();
|
|
302
|
+
const { method, url } = request;
|
|
303
|
+
const startTime = Date.now();
|
|
304
|
+
|
|
305
|
+
return next.handle().pipe(
|
|
306
|
+
tap({
|
|
307
|
+
next: () => {
|
|
308
|
+
const duration = Date.now() - startTime;
|
|
309
|
+
response.setHeader("X-Response-Time", \`\${duration}ms\`);
|
|
310
|
+
|
|
311
|
+
if (duration > 1000) {
|
|
312
|
+
this.logger.warn(\`Slow request: \${method} \${url} took \${duration}ms\`);
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
error: () => {
|
|
316
|
+
const duration = Date.now() - startTime;
|
|
317
|
+
response.setHeader("X-Response-Time", \`\${duration}ms\`);
|
|
318
|
+
},
|
|
319
|
+
})
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
`;
|
|
324
|
+
await (0, file_utils_1.writeFile)(path.join(interceptorsPath, 'timing.interceptor.ts'), timingInterceptorContent);
|
|
325
|
+
// Timeout Interceptor
|
|
326
|
+
const timeoutInterceptorContent = `import {
|
|
327
|
+
Injectable,
|
|
328
|
+
NestInterceptor,
|
|
329
|
+
ExecutionContext,
|
|
330
|
+
CallHandler,
|
|
331
|
+
RequestTimeoutException,
|
|
332
|
+
} from "@nestjs/common";
|
|
333
|
+
import { Observable, throwError, TimeoutError } from "rxjs";
|
|
334
|
+
import { catchError, timeout } from "rxjs/operators";
|
|
335
|
+
import { Reflector } from "@nestjs/core";
|
|
336
|
+
|
|
337
|
+
export const TIMEOUT_KEY = "request_timeout";
|
|
338
|
+
export const SetTimeout = (ms: number) => Reflect.metadata(TIMEOUT_KEY, ms);
|
|
339
|
+
|
|
340
|
+
@Injectable()
|
|
341
|
+
export class TimeoutInterceptor implements NestInterceptor {
|
|
342
|
+
constructor(private reflector: Reflector) {}
|
|
343
|
+
|
|
344
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
345
|
+
const timeoutMs =
|
|
346
|
+
this.reflector.get<number>(TIMEOUT_KEY, context.getHandler()) ||
|
|
347
|
+
parseInt(process.env.REQUEST_TIMEOUT || "30000", 10);
|
|
348
|
+
|
|
349
|
+
return next.handle().pipe(
|
|
350
|
+
timeout(timeoutMs),
|
|
351
|
+
catchError((err) => {
|
|
352
|
+
if (err instanceof TimeoutError) {
|
|
353
|
+
return throwError(
|
|
354
|
+
() => new RequestTimeoutException(\`Request timeout after \${timeoutMs}ms\`)
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return throwError(() => err);
|
|
358
|
+
})
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
`;
|
|
363
|
+
await (0, file_utils_1.writeFile)(path.join(interceptorsPath, 'timeout.interceptor.ts'), timeoutInterceptorContent);
|
|
364
|
+
// Index exports
|
|
365
|
+
await (0, file_utils_1.writeFile)(path.join(middlewarePath, 'index.ts'), `export * from "./request-logger.middleware";
|
|
366
|
+
export * from "./correlation-id.middleware";
|
|
367
|
+
`);
|
|
368
|
+
await (0, file_utils_1.writeFile)(path.join(guardsPath, 'index.ts'), `export * from "./permission.guard";
|
|
369
|
+
export * from "./resource-owner.guard";
|
|
370
|
+
`);
|
|
371
|
+
await (0, file_utils_1.writeFile)(path.join(interceptorsPath, 'index.ts'), `export * from "./transform-response.interceptor";
|
|
372
|
+
export * from "./timing.interceptor";
|
|
373
|
+
export * from "./timeout.interceptor";
|
|
374
|
+
`);
|
|
375
|
+
console.log(chalk_1.default.green(' ✓ Request Logger Middleware'));
|
|
376
|
+
console.log(chalk_1.default.green(' ✓ Correlation ID Middleware with AsyncLocalStorage'));
|
|
377
|
+
console.log(chalk_1.default.green(' ✓ Permission Guard with @Permissions decorator'));
|
|
378
|
+
console.log(chalk_1.default.green(' ✓ Resource Owner Guard'));
|
|
379
|
+
console.log(chalk_1.default.green(' ✓ Transform Response Interceptor'));
|
|
380
|
+
console.log(chalk_1.default.green(' ✓ Timing Interceptor'));
|
|
381
|
+
console.log(chalk_1.default.green(' ✓ Timeout Interceptor'));
|
|
382
|
+
}
|
|
383
|
+
//# sourceMappingURL=middleware.recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.recipe.js","sourceRoot":"","sources":["../../../src/commands/recipes/middleware.recipe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function applyMultiTenancyRecipe(basePath: string): Promise<void>;
|