@discover-cloud/shared 1.2.9 → 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/authorization/index.d.ts +1 -1
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/permission-cache.service.d.ts +104 -16
- package/dist/authorization/permission-cache.service.js +156 -143
- package/dist/authorization/permission-checker.d.ts +30 -0
- package/dist/authorization/permission-checker.js +40 -0
- package/dist/contracts/auth-service/account.dto.d.ts +30 -0
- package/dist/contracts/auth-service/account.dto.js +7 -0
- package/dist/contracts/auth-service/auth.dto.d.ts +21 -0
- package/dist/contracts/auth-service/auth.dto.js +10 -0
- package/dist/dtos/auth-service.dto.d.ts +72 -26
- package/dist/dtos/auth-service.dto.js +4 -0
- package/dist/dtos/cloud-service.dto.d.ts +51 -27
- package/dist/dtos/cloud-service.dto.js +5 -0
- package/dist/dtos/insights-service.dto.d.ts +18 -27
- package/dist/dtos/insights-service.dto.js +8 -0
- package/dist/dtos/response.dto.d.ts +7 -84
- package/dist/dtos/response.dto.js +2 -21
- package/dist/dtos/user-service.dto.d.ts +118 -13
- package/dist/enums/domain.enums.d.ts +246 -75
- package/dist/enums/domain.enums.js +305 -102
- package/dist/enums/permissions.enums.d.ts +142 -80
- package/dist/enums/permissions.enums.js +174 -133
- package/dist/errors/app-error.d.ts +13 -18
- package/dist/errors/app-error.js +14 -20
- package/dist/errors/http-errors.d.ts +105 -17
- package/dist/errors/http-errors.js +118 -33
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.js +1 -0
- package/dist/http/request-context.d.ts +8 -0
- package/dist/http/request-context.js +37 -0
- package/dist/http/service-client.d.ts +91 -38
- package/dist/http/service-client.js +102 -53
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/jwt/index.d.ts +2 -1
- package/dist/jwt/index.js +2 -1
- package/dist/jwt/machine-jwt-verifier.d.ts +21 -0
- package/dist/jwt/machine-jwt-verifier.js +84 -0
- package/dist/jwt/machine-token-client.d.ts +35 -4
- package/dist/jwt/machine-token-client.js +57 -23
- package/dist/jwt/user-jwt-verifier.d.ts +31 -0
- package/dist/jwt/user-jwt-verifier.js +101 -0
- package/dist/messaging/index.d.ts +1 -0
- package/dist/{security → messaging}/index.js +1 -1
- package/dist/messaging/rabbitmq.client.d.ts +13 -0
- package/dist/messaging/rabbitmq.client.js +127 -0
- package/dist/middleware/error-handler.middleware.d.ts +8 -28
- package/dist/middleware/error-handler.middleware.js +61 -52
- package/dist/middleware/index.d.ts +7 -5
- package/dist/middleware/index.js +7 -5
- package/dist/middleware/request-context.middleware.d.ts +17 -0
- package/dist/middleware/request-context.middleware.js +73 -0
- package/dist/middleware/require-auth.middleware.d.ts +17 -70
- package/dist/middleware/require-auth.middleware.js +24 -103
- package/dist/middleware/require-machine.middleware.d.ts +38 -0
- package/dist/middleware/require-machine.middleware.js +123 -0
- package/dist/middleware/require-org-permission-from-body.middleware.d.ts +10 -0
- package/dist/middleware/require-org-permission-from-body.middleware.js +24 -0
- package/dist/middleware/require-org-permission.middleware.d.ts +19 -0
- package/dist/middleware/require-org-permission.middleware.js +115 -0
- package/dist/middleware/require-platform-permission.middleware.d.ts +19 -0
- package/dist/middleware/require-platform-permission.middleware.js +91 -0
- package/dist/middleware/require-user.middleware.d.ts +14 -0
- package/dist/middleware/require-user.middleware.js +30 -0
- package/dist/middleware/require-workspace-permission.middleware.d.ts +20 -0
- package/dist/middleware/require-workspace-permission.middleware.js +114 -0
- package/dist/middleware/validate.middleware.d.ts +29 -29
- package/dist/middleware/validate.middleware.js +30 -32
- package/dist/types/express.types.d.ts +83 -122
- package/dist/types/express.types.js +28 -47
- package/dist/utils/date.util.d.ts +6 -0
- package/dist/utils/date.util.js +11 -0
- package/dist/utils/env.util.d.ts +6 -0
- package/dist/utils/env.util.js +19 -0
- package/dist/utils/index.d.ts +5 -4
- package/dist/utils/index.js +5 -4
- package/dist/utils/logger.util.d.ts +26 -0
- package/dist/utils/logger.util.js +53 -0
- package/dist/utils/pagination.util.d.ts +6 -0
- package/dist/utils/pagination.util.js +20 -0
- package/dist/utils/response.util.d.ts +27 -0
- package/dist/utils/response.util.js +56 -0
- package/dist/utils/slug.util.d.ts +9 -0
- package/dist/utils/slug.util.js +32 -0
- package/dist/utils/url-safety.util.d.ts +6 -0
- package/dist/utils/url-safety.util.js +72 -0
- package/package.json +3 -1
- package/dist/authorization/permissions.d.ts +0 -78
- package/dist/authorization/permissions.js +0 -174
- package/dist/context/access-context.d.ts +0 -10
- package/dist/context/access-context.js +0 -2
- package/dist/context/index.d.ts +0 -1
- package/dist/context/index.js +0 -17
- package/dist/dto/auth-service.dtos.d.ts +0 -44
- package/dist/dto/auth-service.dtos.js +0 -2
- package/dist/dto/index.d.ts +0 -3
- package/dist/dto/index.js +0 -19
- package/dist/dto/response.dtos.d.ts +0 -55
- package/dist/dto/response.dtos.js +0 -6
- package/dist/dto/user-service.dtos.d.ts +0 -50
- package/dist/dto/user-service.dtos.js +0 -2
- package/dist/enums/auth-service.enums.d.ts +0 -12
- package/dist/enums/auth-service.enums.js +0 -17
- package/dist/enums/permissions.types.d.ts +0 -12
- package/dist/enums/permissions.types.js +0 -17
- package/dist/enums/user-service.enums.d.ts +0 -32
- package/dist/enums/user-service.enums.js +0 -41
- package/dist/internal/index.d.ts +0 -4
- package/dist/internal/index.js +0 -20
- package/dist/internal/internal-jwt.service.d.ts +0 -13
- package/dist/internal/internal-jwt.service.js +0 -88
- package/dist/internal/internal-jwt.types.d.ts +0 -7
- package/dist/internal/internal-jwt.types.js +0 -2
- package/dist/internal/internal-key-manager.d.ts +0 -16
- package/dist/internal/internal-key-manager.js +0 -67
- package/dist/internal/registry.d.ts +0 -8
- package/dist/internal/registry.js +0 -34
- package/dist/internal/service-client.d.ts +0 -9
- package/dist/internal/service-client.js +0 -94
- package/dist/jwt/internal-jwt-verifier.d.ts +0 -41
- package/dist/jwt/internal-jwt-verifier.js +0 -185
- package/dist/jwt/jwt-verifier.d.ts +0 -9
- package/dist/jwt/jwt-verifier.js +0 -36
- package/dist/jwt/service-client.d.ts +0 -7
- package/dist/jwt/service-client.js +0 -87
- package/dist/middleware/authorize.d.ts +0 -3
- package/dist/middleware/authorize.js +0 -24
- package/dist/middleware/authorize.middleware.d.ts +0 -54
- package/dist/middleware/authorize.middleware.js +0 -104
- package/dist/middleware/error-handler.d.ts +0 -4
- package/dist/middleware/error-handler.js +0 -23
- package/dist/middleware/request-id.d.ts +0 -2
- package/dist/middleware/request-id.js +0 -9
- package/dist/middleware/request-id.middleware.d.ts +0 -22
- package/dist/middleware/request-id.middleware.js +0 -34
- package/dist/middleware/require-auth.d.ts +0 -10
- package/dist/middleware/require-auth.js +0 -34
- package/dist/middleware/require-human.middleware.d.ts +0 -2
- package/dist/middleware/require-human.middleware.js +0 -18
- package/dist/middleware/require-internal.middleware.d.ts +0 -18
- package/dist/middleware/require-internal.middleware.js +0 -183
- package/dist/middleware/validate.d.ts +0 -5
- package/dist/middleware/validate.js +0 -18
- package/dist/middleware/validated-merge.middleware.d.ts +0 -20
- package/dist/middleware/validated-merge.middleware.js +0 -33
- package/dist/middleware/verify-internal-jwt.d.ts +0 -7
- package/dist/middleware/verify-internal-jwt.js +0 -25
- package/dist/security/guard.d.ts +0 -10
- package/dist/security/guard.js +0 -40
- package/dist/security/index.d.ts +0 -1
- package/dist/types/express.d.ts +0 -22
- package/dist/types/express.js +0 -3
- package/dist/utils/date.utils.d.ts +0 -25
- package/dist/utils/date.utils.js +0 -30
- package/dist/utils/env.d.ts +0 -46
- package/dist/utils/env.js +0 -61
- package/dist/utils/env.utils.d.ts +0 -46
- package/dist/utils/env.utils.js +0 -61
- package/dist/utils/logger.utils.d.ts +0 -66
- package/dist/utils/logger.utils.js +0 -97
- package/dist/utils/response.d.ts +0 -4
- package/dist/utils/response.js +0 -35
- package/dist/utils/response.utils.d.ts +0 -54
- package/dist/utils/response.utils.js +0 -85
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class RabbitMQClient {
|
|
2
|
+
private connection;
|
|
3
|
+
private channel;
|
|
4
|
+
private url;
|
|
5
|
+
private exchangeName;
|
|
6
|
+
constructor(url: string);
|
|
7
|
+
connect(): Promise<void>;
|
|
8
|
+
publishEvent(routingKey: string, payload: any): Promise<void>;
|
|
9
|
+
subscribe(queueName: string, routingKey: string, handler: (payload: any) => Promise<void>, options?: {
|
|
10
|
+
prefetch?: number;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
close(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.RabbitMQClient = void 0;
|
|
37
|
+
const amqplib = __importStar(require("amqplib"));
|
|
38
|
+
class RabbitMQClient {
|
|
39
|
+
constructor(url) {
|
|
40
|
+
this.connection = null;
|
|
41
|
+
this.channel = null;
|
|
42
|
+
this.exchangeName = 'discovercloud.topic';
|
|
43
|
+
this.url = url;
|
|
44
|
+
}
|
|
45
|
+
async connect() {
|
|
46
|
+
if (this.connection && this.channel) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
this.connection = await amqplib.connect(this.url);
|
|
51
|
+
this.connection.on('error', (err) => {
|
|
52
|
+
console.error('RabbitMQ Connection Error:', err);
|
|
53
|
+
});
|
|
54
|
+
this.connection.on('close', () => {
|
|
55
|
+
console.warn('RabbitMQ Connection Closed. Reconnecting...');
|
|
56
|
+
this.connection = null;
|
|
57
|
+
this.channel = null;
|
|
58
|
+
setTimeout(() => this.connect(), 5000);
|
|
59
|
+
});
|
|
60
|
+
this.channel = await this.connection.createChannel();
|
|
61
|
+
// Ensure the main topic exchange exists
|
|
62
|
+
await this.channel.assertExchange(this.exchangeName, 'topic', { durable: true });
|
|
63
|
+
console.log('✅ Connected to RabbitMQ');
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('❌ Failed to connect to RabbitMQ:', error);
|
|
67
|
+
setTimeout(() => this.connect(), 5000);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async publishEvent(routingKey, payload) {
|
|
71
|
+
if (!this.channel) {
|
|
72
|
+
throw new Error('RabbitMQ Channel not initialized');
|
|
73
|
+
}
|
|
74
|
+
const buffer = Buffer.from(JSON.stringify(payload));
|
|
75
|
+
this.channel.publish(this.exchangeName, routingKey, buffer, {
|
|
76
|
+
persistent: true,
|
|
77
|
+
contentType: 'application/json',
|
|
78
|
+
timestamp: Date.now(),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async subscribe(queueName, routingKey, handler, options = {}) {
|
|
82
|
+
if (!this.channel) {
|
|
83
|
+
throw new Error('RabbitMQ Channel not initialized');
|
|
84
|
+
}
|
|
85
|
+
if (options.prefetch) {
|
|
86
|
+
await this.channel.prefetch(options.prefetch);
|
|
87
|
+
}
|
|
88
|
+
// Dead Letter configuration
|
|
89
|
+
const dlxName = `${this.exchangeName}.dlx`;
|
|
90
|
+
const dlqName = `${queueName}.dlq`;
|
|
91
|
+
await this.channel.assertExchange(dlxName, 'topic', { durable: true });
|
|
92
|
+
await this.channel.assertQueue(dlqName, { durable: true });
|
|
93
|
+
await this.channel.bindQueue(dlqName, dlxName, routingKey);
|
|
94
|
+
// Assert main queue with DLX bindings
|
|
95
|
+
await this.channel.assertQueue(queueName, {
|
|
96
|
+
durable: true,
|
|
97
|
+
deadLetterExchange: dlxName,
|
|
98
|
+
deadLetterRoutingKey: routingKey,
|
|
99
|
+
});
|
|
100
|
+
await this.channel.bindQueue(queueName, this.exchangeName, routingKey);
|
|
101
|
+
this.channel.consume(queueName, async (msg) => {
|
|
102
|
+
if (!msg)
|
|
103
|
+
return;
|
|
104
|
+
try {
|
|
105
|
+
const payload = JSON.parse(msg.content.toString());
|
|
106
|
+
await handler(payload);
|
|
107
|
+
this.channel.ack(msg);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.error(`Error processing message from ${queueName}:`, error);
|
|
111
|
+
// Nack the message and don't requeue, so it goes to the DLX
|
|
112
|
+
this.channel.nack(msg, false, false);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
console.log(`📡 Subscribed to queue [${queueName}] with routing key [${routingKey}]`);
|
|
116
|
+
}
|
|
117
|
+
async close() {
|
|
118
|
+
if (this.channel) {
|
|
119
|
+
await this.channel.close();
|
|
120
|
+
}
|
|
121
|
+
if (this.connection) {
|
|
122
|
+
await this.connection.close();
|
|
123
|
+
}
|
|
124
|
+
console.log('RabbitMQ connection closed.');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.RabbitMQClient = RabbitMQClient;
|
|
@@ -1,30 +1,10 @@
|
|
|
1
|
-
import { Request, Response, NextFunction } from "express";
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Centralised Express error handler. Register as the last middleware in
|
|
6
|
-
* every service's app.ts:
|
|
7
|
-
*
|
|
8
|
-
* app.use(GlobalErrorHandler.handle);
|
|
9
|
-
*
|
|
10
|
-
* Handles in order:
|
|
11
|
-
* 1. ZodError → 400 VALIDATION_ERROR with flattened field errors
|
|
12
|
-
* 2. AppError → mapped statusCode + code (details forwarded to client)
|
|
13
|
-
* 3. Unknown → 500 INTERNAL_SERVER_ERROR (internals never leaked)
|
|
14
|
-
*
|
|
15
|
-
* Logging strategy:
|
|
16
|
-
* Development — full error object printed for stack traces during development.
|
|
17
|
-
* Production — message only for AppError/Error (no stack, no details in logs).
|
|
18
|
-
* Unknown errors log the raw value at warn level.
|
|
19
|
-
* In production, a structured logger (Pino) replaces console.*
|
|
20
|
-
* in each service — these console calls are captured by stdout.
|
|
21
|
-
*
|
|
22
|
-
* Security:
|
|
23
|
-
* Unknown (unexpected) errors never expose internals to the client.
|
|
24
|
-
* AppError.details IS forwarded — callers that set details on an AppError
|
|
25
|
-
* are intentionally choosing to surface that context (e.g. validation hints).
|
|
26
|
-
* Never put secrets, stack traces, or raw DB errors in AppError.details.
|
|
2
|
+
* @file error-handler.middleware.ts
|
|
3
|
+
* @description Central Express error handler. Register as the final middleware.
|
|
27
4
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
5
|
+
import { ErrorRequestHandler } from "express";
|
|
6
|
+
import { ILogger } from "../utils";
|
|
7
|
+
/**
|
|
8
|
+
* Creates global Express error middleware.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createGlobalErrorHandler(logger?: ILogger): ErrorRequestHandler;
|
|
@@ -1,70 +1,79 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file error-handler.middleware.ts
|
|
4
|
+
* @description Central Express error handler. Register as the final middleware.
|
|
5
|
+
*/
|
|
2
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
7
|
+
exports.createGlobalErrorHandler = createGlobalErrorHandler;
|
|
4
8
|
const zod_1 = require("zod");
|
|
5
9
|
const errors_1 = require("../errors");
|
|
6
10
|
const utils_1 = require("../utils");
|
|
7
11
|
/**
|
|
8
|
-
*
|
|
9
|
-
* ────────────────────────────────────────────────
|
|
10
|
-
* Centralised Express error handler. Register as the last middleware in
|
|
11
|
-
* every service's app.ts:
|
|
12
|
-
*
|
|
13
|
-
* app.use(GlobalErrorHandler.handle);
|
|
14
|
-
*
|
|
15
|
-
* Handles in order:
|
|
16
|
-
* 1. ZodError → 400 VALIDATION_ERROR with flattened field errors
|
|
17
|
-
* 2. AppError → mapped statusCode + code (details forwarded to client)
|
|
18
|
-
* 3. Unknown → 500 INTERNAL_SERVER_ERROR (internals never leaked)
|
|
19
|
-
*
|
|
20
|
-
* Logging strategy:
|
|
21
|
-
* Development — full error object printed for stack traces during development.
|
|
22
|
-
* Production — message only for AppError/Error (no stack, no details in logs).
|
|
23
|
-
* Unknown errors log the raw value at warn level.
|
|
24
|
-
* In production, a structured logger (Pino) replaces console.*
|
|
25
|
-
* in each service — these console calls are captured by stdout.
|
|
26
|
-
*
|
|
27
|
-
* Security:
|
|
28
|
-
* Unknown (unexpected) errors never expose internals to the client.
|
|
29
|
-
* AppError.details IS forwarded — callers that set details on an AppError
|
|
30
|
-
* are intentionally choosing to surface that context (e.g. validation hints).
|
|
31
|
-
* Never put secrets, stack traces, or raw DB errors in AppError.details.
|
|
12
|
+
* Detects malformed JSON thrown by express.json().
|
|
32
13
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
14
|
+
function isMalformedJsonError(err) {
|
|
15
|
+
return (err instanceof SyntaxError &&
|
|
16
|
+
"status" in err &&
|
|
17
|
+
typeof err.status === "number" &&
|
|
18
|
+
"body" in err);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Converts any thrown value into Error.
|
|
22
|
+
*/
|
|
23
|
+
function normalizeError(err) {
|
|
24
|
+
if (err instanceof Error) {
|
|
25
|
+
return err;
|
|
26
|
+
}
|
|
27
|
+
if (typeof err === "string") {
|
|
28
|
+
return new Error(err);
|
|
29
|
+
}
|
|
30
|
+
return new Error(JSON.stringify(err));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Creates global Express error middleware.
|
|
34
|
+
*/
|
|
35
|
+
function createGlobalErrorHandler(logger = utils_1.noopLogger) {
|
|
36
|
+
return (err, req, res, _next) => {
|
|
37
|
+
const error = normalizeError(err);
|
|
38
|
+
const requestId = req.requestContext?.requestId ?? req.id;
|
|
39
|
+
/**
|
|
40
|
+
* Malformed JSON
|
|
41
|
+
*/
|
|
42
|
+
if (isMalformedJsonError(err) && err.status === 400) {
|
|
43
|
+
logger.warn({ requestId, err: error }, "Malformed JSON payload");
|
|
44
|
+
(0, utils_1.failure)(res, req, "Invalid JSON payload", "BAD_REQUEST", 400);
|
|
45
|
+
return;
|
|
47
46
|
}
|
|
48
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Validation failures
|
|
49
|
+
*/
|
|
49
50
|
if (err instanceof zod_1.ZodError) {
|
|
50
|
-
|
|
51
|
-
// (top-level). This shape is stable across Zod versions and is what
|
|
52
|
-
// clients should key on for field-level error display.
|
|
51
|
+
logger.warn({ requestId, err: error }, "Request validation failed");
|
|
53
52
|
(0, utils_1.failure)(res, req, "Validation failed", "VALIDATION_ERROR", 400, err.flatten());
|
|
54
53
|
return;
|
|
55
54
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// and is intentionally forwarded to the client.
|
|
55
|
+
/**
|
|
56
|
+
* Known application errors
|
|
57
|
+
*/
|
|
60
58
|
if (err instanceof errors_1.AppError) {
|
|
59
|
+
const payload = {
|
|
60
|
+
requestId,
|
|
61
|
+
code: err.code,
|
|
62
|
+
err: error,
|
|
63
|
+
};
|
|
64
|
+
if (err.statusCode >= 500) {
|
|
65
|
+
logger.error(payload, err.message);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
logger.warn(payload, err.message);
|
|
69
|
+
}
|
|
61
70
|
(0, utils_1.failure)(res, req, err.message, err.code, err.statusCode, err.details);
|
|
62
71
|
return;
|
|
63
72
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Unknown crashes
|
|
75
|
+
*/
|
|
76
|
+
logger.error({ requestId, err: error }, "Unhandled exception");
|
|
67
77
|
(0, utils_1.failure)(res, req, "Internal Server Error", "INTERNAL_SERVER_ERROR", 500);
|
|
68
|
-
}
|
|
78
|
+
};
|
|
69
79
|
}
|
|
70
|
-
exports.GlobalErrorHandler = GlobalErrorHandler;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export * from "./error-handler.middleware";
|
|
2
2
|
export * from "./validate.middleware";
|
|
3
|
-
export * from "./request-
|
|
4
|
-
export * from "./
|
|
5
|
-
export * from "./validated-merge.middleware";
|
|
6
|
-
export * from "./require-internal.middleware";
|
|
3
|
+
export * from "./request-context.middleware";
|
|
4
|
+
export * from "./require-machine.middleware";
|
|
7
5
|
export * from "./require-auth.middleware";
|
|
8
|
-
export * from "./require-
|
|
6
|
+
export * from "./require-user.middleware";
|
|
7
|
+
export * from "./require-org-permission.middleware";
|
|
8
|
+
export * from "./require-workspace-permission.middleware";
|
|
9
|
+
export * from "./require-platform-permission.middleware";
|
|
10
|
+
export * from "./require-org-permission-from-body.middleware";
|
package/dist/middleware/index.js
CHANGED
|
@@ -16,9 +16,11 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./error-handler.middleware"), exports);
|
|
18
18
|
__exportStar(require("./validate.middleware"), exports);
|
|
19
|
-
__exportStar(require("./request-
|
|
20
|
-
__exportStar(require("./
|
|
21
|
-
__exportStar(require("./validated-merge.middleware"), exports);
|
|
22
|
-
__exportStar(require("./require-internal.middleware"), exports);
|
|
19
|
+
__exportStar(require("./request-context.middleware"), exports);
|
|
20
|
+
__exportStar(require("./require-machine.middleware"), exports);
|
|
23
21
|
__exportStar(require("./require-auth.middleware"), exports);
|
|
24
|
-
__exportStar(require("./require-
|
|
22
|
+
__exportStar(require("./require-user.middleware"), exports);
|
|
23
|
+
__exportStar(require("./require-org-permission.middleware"), exports);
|
|
24
|
+
__exportStar(require("./require-workspace-permission.middleware"), exports);
|
|
25
|
+
__exportStar(require("./require-platform-permission.middleware"), exports);
|
|
26
|
+
__exportStar(require("./require-org-permission-from-body.middleware"), exports);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file request-context.middleware.ts
|
|
3
|
+
* @description Express middleware to assign request tracing and audit metadata context.
|
|
4
|
+
*/
|
|
5
|
+
import { Request, Response, NextFunction } from "express";
|
|
6
|
+
/**
|
|
7
|
+
* Attaches request metadata used for:
|
|
8
|
+
* - distributed tracing
|
|
9
|
+
* - audit logs
|
|
10
|
+
* - debugging correlation
|
|
11
|
+
*
|
|
12
|
+
* @param {Request} req - Express request object.
|
|
13
|
+
* @param {Response} res - Express response object.
|
|
14
|
+
* @param {NextFunction} next - Express next middleware function.
|
|
15
|
+
* @returns {void}
|
|
16
|
+
*/
|
|
17
|
+
export declare const requestContext: (req: Request, res: Response, next: NextFunction) => void;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file request-context.middleware.ts
|
|
4
|
+
* @description Express middleware to assign request tracing and audit metadata context.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.requestContext = void 0;
|
|
8
|
+
const crypto_1 = require("crypto");
|
|
9
|
+
/**
|
|
10
|
+
* Normalizes IPv4-mapped IPv6 addresses.
|
|
11
|
+
*
|
|
12
|
+
* @param {string | null} ip - Raw IP.
|
|
13
|
+
* @returns {string | null} Normalized IP.
|
|
14
|
+
*/
|
|
15
|
+
function normalizeIp(ip) {
|
|
16
|
+
if (!ip) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
if (ip === "::1") {
|
|
20
|
+
return "127.0.0.1";
|
|
21
|
+
}
|
|
22
|
+
if (ip.startsWith("::ffff:")) {
|
|
23
|
+
return ip.substring(7);
|
|
24
|
+
}
|
|
25
|
+
return ip;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolves original client IP forwarded by the API Gateway.
|
|
29
|
+
*
|
|
30
|
+
* Priority:
|
|
31
|
+
* 1. x-forwarded-for
|
|
32
|
+
* 2. x-real-ip
|
|
33
|
+
* 3. socket fallback (local development only)
|
|
34
|
+
*
|
|
35
|
+
* @param {Request} req - Express request object.
|
|
36
|
+
* @returns {string | null} Client IP address.
|
|
37
|
+
*/
|
|
38
|
+
function resolveClientIp(req) {
|
|
39
|
+
const forwarded = req.headers["x-forwarded-for"];
|
|
40
|
+
if (forwarded) {
|
|
41
|
+
const value = Array.isArray(forwarded) ? forwarded[0] : forwarded;
|
|
42
|
+
return normalizeIp(value.split(",")[0].trim());
|
|
43
|
+
}
|
|
44
|
+
const realIp = req.headers["x-real-ip"];
|
|
45
|
+
if (realIp) {
|
|
46
|
+
return normalizeIp(Array.isArray(realIp) ? realIp[0] : realIp);
|
|
47
|
+
}
|
|
48
|
+
return normalizeIp(req.socket.remoteAddress ?? null);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Attaches request metadata used for:
|
|
52
|
+
* - distributed tracing
|
|
53
|
+
* - audit logs
|
|
54
|
+
* - debugging correlation
|
|
55
|
+
*
|
|
56
|
+
* @param {Request} req - Express request object.
|
|
57
|
+
* @param {Response} res - Express response object.
|
|
58
|
+
* @param {NextFunction} next - Express next middleware function.
|
|
59
|
+
* @returns {void}
|
|
60
|
+
*/
|
|
61
|
+
const requestContext = (req, res, next) => {
|
|
62
|
+
const upstream = req.headers["x-request-id"];
|
|
63
|
+
const requestId = Array.isArray(upstream) ? upstream[0] : (upstream ?? (0, crypto_1.randomUUID)());
|
|
64
|
+
req.id = requestId;
|
|
65
|
+
req.requestContext = {
|
|
66
|
+
requestId,
|
|
67
|
+
ipAddress: resolveClientIp(req),
|
|
68
|
+
userAgent: req.headers["user-agent"] ?? null,
|
|
69
|
+
};
|
|
70
|
+
res.setHeader("x-request-id", requestId);
|
|
71
|
+
next();
|
|
72
|
+
};
|
|
73
|
+
exports.requestContext = requestContext;
|
|
@@ -1,85 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file require-auth.middleware.ts
|
|
3
|
+
* @description Authentication boundary middleware. Verifies the internal gateway JWT,
|
|
4
|
+
* builds the auth context, and gates endpoints.
|
|
5
|
+
*/
|
|
1
6
|
import { Request, Response, NextFunction } from "express";
|
|
2
|
-
import { AccountRole, GlobalPermission } from "../enums";
|
|
3
7
|
import { ILogger } from "../utils";
|
|
4
|
-
import {
|
|
8
|
+
import { UserJwtVerifier } from "../jwt/user-jwt-verifier";
|
|
5
9
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Verifies the internal JWT, resolves permissions from the Redis cache,
|
|
9
|
-
* and attaches the typed AccessContext to req.accessContext.
|
|
10
|
-
*
|
|
11
|
-
* Moved to shared so every downstream service (auth, user, cloud, insights)
|
|
12
|
-
* uses the same verified implementation rather than maintaining their own copy.
|
|
13
|
-
*
|
|
14
|
-
* Token extraction:
|
|
15
|
-
* Reads a Bearer token from the Authorization header.
|
|
16
|
-
* Does not support cookie-based auth — that is the API gateway's responsibility.
|
|
17
|
-
*
|
|
18
|
-
* On success:
|
|
19
|
-
* req.internalAuth → raw verified JWT payload (use for jti, caller, requestId)
|
|
20
|
-
* req.accessContext → clean typed context (use in controllers and services)
|
|
21
|
-
*
|
|
22
|
-
* On failure:
|
|
23
|
-
* Returns a structured error response and does NOT call next().
|
|
24
|
-
* next(err) is only called for genuinely unexpected errors (not auth failures).
|
|
25
|
-
*
|
|
26
|
-
* Inline guards (requirePermission, requireRole):
|
|
27
|
-
* Convenience methods for single-route guards. For router-level permission
|
|
28
|
-
* enforcement, prefer the standalone factories in permission.middleware.ts —
|
|
29
|
-
* they don't require a reference to this class instance.
|
|
30
|
-
*
|
|
31
|
-
* Logging:
|
|
32
|
-
* Unexpected errors are logged at "error" level with requestId for correlation.
|
|
33
|
-
* Auth failures (expired, invalid token) are intentionally not logged at error
|
|
34
|
-
* level — they are client errors, not server faults.
|
|
35
|
-
*
|
|
36
|
-
* Usage in each service's app.ts:
|
|
37
|
-
* import { RequireAuthMiddleware } from "@discover-cloud/shared";
|
|
38
|
-
*
|
|
39
|
-
* const requireAuth = new RequireAuthMiddleware(jwtVerifier, logger);
|
|
40
|
-
* router.use(requireAuth.handle);
|
|
10
|
+
* Middleware handling authentication validation.
|
|
11
|
+
* Extracts the JWT, verifies it via UserJwtVerifier, and attaches the AuthContext.
|
|
41
12
|
*/
|
|
42
13
|
export declare class RequireAuthMiddleware {
|
|
43
14
|
private readonly verifier;
|
|
44
15
|
private readonly logger;
|
|
45
|
-
constructor(verifier: InternalJwtVerifier, logger?: ILogger);
|
|
46
16
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
17
|
+
* @param {UserJwtVerifier} verifier - The token verification engine.
|
|
18
|
+
* @param {ILogger} [logger] - Optional logger instance.
|
|
49
19
|
*/
|
|
50
|
-
|
|
20
|
+
constructor(verifier: UserJwtVerifier, logger?: ILogger);
|
|
51
21
|
/**
|
|
52
|
-
*
|
|
53
|
-
* For router-level guards, prefer requireGlobalPermission() from permission.middleware.ts.
|
|
22
|
+
* Core Express middleware handler for authenticating requests.
|
|
54
23
|
*
|
|
55
|
-
*
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
* Inline role guard — use when this class instance is available at the call site.
|
|
60
|
-
*
|
|
61
|
-
* Prefer permission-based guards over role-based guards where possible.
|
|
62
|
-
* Role checks are appropriate for coarse access gates (e.g. "only admins enter this router").
|
|
63
|
-
*/
|
|
64
|
-
requireRole: (role: AccountRole) => (req: Request, res: Response, next: NextFunction) => void;
|
|
65
|
-
/**
|
|
66
|
-
* Extracts the Bearer token from the Authorization header.
|
|
67
|
-
* Returns null (not an error) if the header is absent or malformed —
|
|
68
|
-
* callers decide how to handle the missing token.
|
|
24
|
+
* @param {Request} req - Express request object.
|
|
25
|
+
* @param {Response} res - Express response object.
|
|
26
|
+
* @param {NextFunction} next - Express next function.
|
|
27
|
+
* @returns {Promise<void>}
|
|
69
28
|
*/
|
|
29
|
+
handle: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
70
30
|
private extractBearer;
|
|
71
|
-
/**
|
|
72
|
-
* Maps jose verification errors to appropriate HTTP responses.
|
|
73
|
-
*
|
|
74
|
-
* Error categories:
|
|
75
|
-
* JWTExpired → 401 TOKEN_EXPIRED (client should refresh)
|
|
76
|
-
* JWTClaimValidationFailed,
|
|
77
|
-
* JWSSignatureVerificationFailed,
|
|
78
|
-
* JWSInvalid, JWTInvalid → 401 INVALID_TOKEN (client should re-login)
|
|
79
|
-
* Anything else → 503 SERVICE_UNAVAILABLE (unexpected — log at error)
|
|
80
|
-
*
|
|
81
|
-
* Security note: generic "invalid token" messages are intentional.
|
|
82
|
-
* Never expose which specific claim failed — that aids token forgery attempts.
|
|
83
|
-
*/
|
|
84
31
|
private handleJwtError;
|
|
85
32
|
}
|