@fluyappgocore/commons-backend 1.0.204 → 1.0.208

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.
@@ -60,7 +60,13 @@ var KafkaConnection = function (brokers, clientId) {
60
60
  console.log("[events] Using RabbitMQ for " + clientId);
61
61
  return createRabbitMQAdapter(clientId);
62
62
  }
63
- var kafka = new kafkajs_1.Kafka({ clientId: clientId, brokers: brokers });
63
+ // Filter out empty/undefined broker strings
64
+ var validBrokers = brokers.filter(function (b) { return !!b && b.trim() !== ""; });
65
+ if (validBrokers.length === 0) {
66
+ console.log("[events] No Kafka brokers or RabbitMQ configured for " + clientId + " \u2014 events disabled");
67
+ return createNoopAdapter();
68
+ }
69
+ var kafka = new kafkajs_1.Kafka({ clientId: clientId, brokers: validBrokers });
64
70
  return kafka;
65
71
  };
66
72
  exports.KafkaConnection = KafkaConnection;
@@ -79,11 +85,20 @@ function createRabbitMQAdapter(clientId) {
79
85
  if (connecting)
80
86
  return [2 /*return*/, connecting];
81
87
  connecting = (function () { return __awaiter(_this, void 0, void 0, function () {
82
- var conn;
88
+ var maxRetries, attempt, conn, err_1;
83
89
  return __generator(this, function (_a) {
84
90
  switch (_a.label) {
85
- case 0: return [4 /*yield*/, amqp.connect(config_1.rabbitmqUrl)];
91
+ case 0:
92
+ maxRetries = 10;
93
+ attempt = 1;
94
+ _a.label = 1;
86
95
  case 1:
96
+ if (!(attempt <= maxRetries)) return [3 /*break*/, 7];
97
+ _a.label = 2;
98
+ case 2:
99
+ _a.trys.push([2, 4, , 6]);
100
+ return [4 /*yield*/, amqp.connect(config_1.rabbitmqUrl)];
101
+ case 3:
87
102
  conn = _a.sent();
88
103
  conn.on("error", function (err) {
89
104
  console.error("[RabbitMQ:" + clientId + "] Connection error:", err.message);
@@ -97,7 +112,23 @@ function createRabbitMQAdapter(clientId) {
97
112
  });
98
113
  connection = conn;
99
114
  connecting = null;
115
+ console.log("[RabbitMQ:" + clientId + "] Connected");
100
116
  return [2 /*return*/, conn];
117
+ case 4:
118
+ err_1 = _a.sent();
119
+ if (attempt === maxRetries) {
120
+ connecting = null;
121
+ throw err_1;
122
+ }
123
+ console.log("[RabbitMQ:" + clientId + "] Waiting for RabbitMQ... (attempt " + attempt + "/" + maxRetries + ")");
124
+ return [4 /*yield*/, new Promise(function (r) { return setTimeout(r, 3000); })];
125
+ case 5:
126
+ _a.sent();
127
+ return [3 /*break*/, 6];
128
+ case 6:
129
+ attempt++;
130
+ return [3 /*break*/, 1];
131
+ case 7: return [2 /*return*/];
101
132
  }
102
133
  });
103
134
  }); })();
@@ -402,6 +433,54 @@ function runBatchMode(channel, queueName, topic, eachBatch, autoResolve) {
402
433
  });
403
434
  });
404
435
  }
436
+ // ─── Noop adapter (no broker configured) ────────────────────────────────────
437
+ function createNoopAdapter() {
438
+ return {
439
+ producer: function () {
440
+ return {
441
+ connect: function () {
442
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
443
+ return [2 /*return*/];
444
+ }); });
445
+ },
446
+ send: function (_payload) {
447
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
448
+ return [2 /*return*/];
449
+ }); });
450
+ },
451
+ disconnect: function () {
452
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
453
+ return [2 /*return*/];
454
+ }); });
455
+ },
456
+ };
457
+ },
458
+ consumer: function (_opts) {
459
+ return {
460
+ connect: function () {
461
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
462
+ return [2 /*return*/];
463
+ }); });
464
+ },
465
+ subscribe: function (_payload) {
466
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
467
+ return [2 /*return*/];
468
+ }); });
469
+ },
470
+ run: function (_opts) {
471
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
472
+ return [2 /*return*/];
473
+ }); });
474
+ },
475
+ disconnect: function () {
476
+ return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) {
477
+ return [2 /*return*/];
478
+ }); });
479
+ },
480
+ };
481
+ },
482
+ };
483
+ }
405
484
  // ─── Message conversion ──────────────────────────────────────────────────────
406
485
  function toKafkaMessage(amqpMsg) {
407
486
  var _a, _b, _c;
@@ -1,3 +1 @@
1
- import { NextFunction, Request, Response } from 'express';
2
- import { HttpException } from '../exceptions';
3
- export declare const errorMiddleware: (error: HttpException, request: Request, response: Response, next: NextFunction) => void;
1
+ export declare const errorMiddleware: any;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.errorMiddleware = void 0;
4
+ // Typed as 'any' to avoid Express version conflicts across microservices
4
5
  var errorMiddleware = function (error, request, response, next) {
5
6
  var status = error.status || 500;
6
7
  var message = error.message || 'Something went wrong';
@@ -1,3 +1,4 @@
1
1
  export * from './auth.middleware';
2
2
  export * from './error.middleware';
3
3
  export * from './validation.middleware';
4
+ export * from './licenseGuard.middleware';
@@ -13,3 +13,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
13
13
  __exportStar(require("./auth.middleware"), exports);
14
14
  __exportStar(require("./error.middleware"), exports);
15
15
  __exportStar(require("./validation.middleware"), exports);
16
+ __exportStar(require("./licenseGuard.middleware"), exports);
@@ -0,0 +1,56 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ /**
3
+ * License feature guard middleware for microservices.
4
+ * Caches license data from the licensing server and checks feature flags.
5
+ *
6
+ * Usage in any MS route:
7
+ * router.get("/analytics", authFBMiddleware, licenseGuard("analytics-summary"), controller)
8
+ *
9
+ * Requires LICENSE_URL and INSTALLATION_UUID env vars.
10
+ * Falls back to allowing access if license server is unreachable.
11
+ */
12
+ interface LicenseCache {
13
+ valid: boolean;
14
+ readOnly: boolean;
15
+ blocked: boolean;
16
+ features: Record<string, boolean>;
17
+ limits: {
18
+ maxBranches: number;
19
+ maxAgents: number;
20
+ maxServices: number;
21
+ };
22
+ tier: string;
23
+ fetchedAt: number;
24
+ }
25
+ /**
26
+ * Check if a license feature is enabled.
27
+ * Can be used programmatically without middleware.
28
+ */
29
+ export declare function isLicenseFeatureEnabled(featureKey: string): Promise<boolean>;
30
+ /**
31
+ * Get cached license limits.
32
+ */
33
+ export declare function getLicenseLimits(): Promise<LicenseCache["limits"] | null>;
34
+ /**
35
+ * Express middleware that blocks access if a license feature is not enabled.
36
+ */
37
+ export declare function licenseGuard(...requiredFeatures: string[]): (_req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
38
+ /**
39
+ * Blocks write operations (POST/PUT/DELETE) when license is in READONLY mode.
40
+ * Use on all mutation routes. GET requests are always allowed in readonly.
41
+ */
42
+ export declare function licenseWriteGuard(): (req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
43
+ /**
44
+ * Blocks ALL access (including login) when license is BLOCKED.
45
+ * Use on auth/login routes.
46
+ */
47
+ export declare function licenseLoginGuard(): (_req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
48
+ /**
49
+ * Get current license status (for use in controllers).
50
+ */
51
+ export declare function getLicenseStatus(): Promise<{
52
+ valid: boolean;
53
+ readOnly: boolean;
54
+ blocked: boolean;
55
+ } | null>;
56
+ export {};
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (_) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.getLicenseStatus = exports.licenseLoginGuard = exports.licenseWriteGuard = exports.licenseGuard = exports.getLicenseLimits = exports.isLicenseFeatureEnabled = void 0;
40
+ var cache = null;
41
+ var CACHE_TTL = 1000 * 60 * 60; // 1 hour
42
+ function fetchLicense() {
43
+ return __awaiter(this, void 0, void 0, function () {
44
+ var licenseUrl, installationUuid, controller_1, timeout_1, res, data, _a;
45
+ return __generator(this, function (_b) {
46
+ switch (_b.label) {
47
+ case 0:
48
+ licenseUrl = process.env.LICENSE_URL || "";
49
+ installationUuid = process.env.INSTALLATION_UUID || process.env.ENTITY_UUID || "";
50
+ if (!licenseUrl || !installationUuid)
51
+ return [2 /*return*/, null];
52
+ _b.label = 1;
53
+ case 1:
54
+ _b.trys.push([1, 4, , 5]);
55
+ controller_1 = new AbortController();
56
+ timeout_1 = setTimeout(function () { return controller_1.abort(); }, 5000);
57
+ return [4 /*yield*/, fetch(licenseUrl + "/api/license/validate/" + installationUuid, { signal: controller_1.signal }).finally(function () { return clearTimeout(timeout_1); })];
58
+ case 2:
59
+ res = _b.sent();
60
+ if (!res.ok)
61
+ return [2 /*return*/, null];
62
+ return [4 /*yield*/, res.json()];
63
+ case 3:
64
+ data = _b.sent();
65
+ return [2 /*return*/, {
66
+ valid: data.valid,
67
+ readOnly: data.readOnly || false,
68
+ blocked: data.blocked || false,
69
+ features: data.features || {},
70
+ limits: data.limits || {},
71
+ tier: data.tier || "BASIC",
72
+ fetchedAt: Date.now(),
73
+ }];
74
+ case 4:
75
+ _a = _b.sent();
76
+ return [2 /*return*/, null];
77
+ case 5: return [2 /*return*/];
78
+ }
79
+ });
80
+ });
81
+ }
82
+ function getCachedLicense() {
83
+ return __awaiter(this, void 0, void 0, function () {
84
+ var fresh;
85
+ return __generator(this, function (_a) {
86
+ switch (_a.label) {
87
+ case 0:
88
+ if (cache && (Date.now() - cache.fetchedAt) < CACHE_TTL)
89
+ return [2 /*return*/, cache];
90
+ return [4 /*yield*/, fetchLicense()];
91
+ case 1:
92
+ fresh = _a.sent();
93
+ if (fresh)
94
+ cache = fresh;
95
+ return [2 /*return*/, cache];
96
+ }
97
+ });
98
+ });
99
+ }
100
+ /**
101
+ * Check if a license feature is enabled.
102
+ * Can be used programmatically without middleware.
103
+ */
104
+ function isLicenseFeatureEnabled(featureKey) {
105
+ return __awaiter(this, void 0, void 0, function () {
106
+ var license;
107
+ return __generator(this, function (_a) {
108
+ switch (_a.label) {
109
+ case 0: return [4 /*yield*/, getCachedLicense()];
110
+ case 1:
111
+ license = _a.sent();
112
+ if (!license)
113
+ return [2 /*return*/, true]; // Allow if can't check
114
+ if (Object.keys(license.features).length === 0)
115
+ return [2 /*return*/, true]; // No restrictions
116
+ return [2 /*return*/, license.features[featureKey] === true];
117
+ }
118
+ });
119
+ });
120
+ }
121
+ exports.isLicenseFeatureEnabled = isLicenseFeatureEnabled;
122
+ /**
123
+ * Get cached license limits.
124
+ */
125
+ function getLicenseLimits() {
126
+ return __awaiter(this, void 0, void 0, function () {
127
+ var license;
128
+ return __generator(this, function (_a) {
129
+ switch (_a.label) {
130
+ case 0: return [4 /*yield*/, getCachedLicense()];
131
+ case 1:
132
+ license = _a.sent();
133
+ return [2 /*return*/, (license === null || license === void 0 ? void 0 : license.limits) || null];
134
+ }
135
+ });
136
+ });
137
+ }
138
+ exports.getLicenseLimits = getLicenseLimits;
139
+ /**
140
+ * Express middleware that blocks access if a license feature is not enabled.
141
+ */
142
+ function licenseGuard() {
143
+ var _this = this;
144
+ var requiredFeatures = [];
145
+ for (var _i = 0; _i < arguments.length; _i++) {
146
+ requiredFeatures[_i] = arguments[_i];
147
+ }
148
+ return function (_req, res, next) { return __awaiter(_this, void 0, void 0, function () {
149
+ var license, _i, requiredFeatures_1, feature;
150
+ return __generator(this, function (_a) {
151
+ switch (_a.label) {
152
+ case 0: return [4 /*yield*/, getCachedLicense()];
153
+ case 1:
154
+ license = _a.sent();
155
+ // If can't check license, allow (don't block users)
156
+ if (!license)
157
+ return [2 /*return*/, next()];
158
+ // If license is not valid (expired/revoked), block
159
+ if (!license.valid) {
160
+ return [2 /*return*/, res.status(403).json({
161
+ error: "Licencia no valida",
162
+ code: "LICENSE_INVALID",
163
+ })];
164
+ }
165
+ // If no features defined in license, allow everything
166
+ if (Object.keys(license.features).length === 0)
167
+ return [2 /*return*/, next()];
168
+ // Check all required features
169
+ for (_i = 0, requiredFeatures_1 = requiredFeatures; _i < requiredFeatures_1.length; _i++) {
170
+ feature = requiredFeatures_1[_i];
171
+ if (license.features[feature] !== true) {
172
+ return [2 /*return*/, res.status(403).json({
173
+ error: "Modulo \"" + feature + "\" no habilitado en su licencia",
174
+ code: "FEATURE_DISABLED",
175
+ feature: feature,
176
+ tier: license.tier,
177
+ })];
178
+ }
179
+ }
180
+ next();
181
+ return [2 /*return*/];
182
+ }
183
+ });
184
+ }); };
185
+ }
186
+ exports.licenseGuard = licenseGuard;
187
+ /**
188
+ * Blocks write operations (POST/PUT/DELETE) when license is in READONLY mode.
189
+ * Use on all mutation routes. GET requests are always allowed in readonly.
190
+ */
191
+ function licenseWriteGuard() {
192
+ var _this = this;
193
+ return function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
194
+ var license;
195
+ return __generator(this, function (_a) {
196
+ switch (_a.label) {
197
+ case 0:
198
+ // Only block write methods
199
+ if (req.method === "GET" || req.method === "HEAD" || req.method === "OPTIONS")
200
+ return [2 /*return*/, next()];
201
+ return [4 /*yield*/, getCachedLicense()];
202
+ case 1:
203
+ license = _a.sent();
204
+ if (!license)
205
+ return [2 /*return*/, next()]; // Can't check → allow
206
+ if (license.blocked) {
207
+ return [2 /*return*/, res.status(403).json({
208
+ error: "Licencia expirada. Acceso bloqueado.",
209
+ code: "LICENSE_BLOCKED",
210
+ })];
211
+ }
212
+ if (license.readOnly) {
213
+ return [2 /*return*/, res.status(403).json({
214
+ error: "Licencia en modo solo lectura. No se permiten cambios.",
215
+ code: "LICENSE_READONLY",
216
+ })];
217
+ }
218
+ next();
219
+ return [2 /*return*/];
220
+ }
221
+ });
222
+ }); };
223
+ }
224
+ exports.licenseWriteGuard = licenseWriteGuard;
225
+ /**
226
+ * Blocks ALL access (including login) when license is BLOCKED.
227
+ * Use on auth/login routes.
228
+ */
229
+ function licenseLoginGuard() {
230
+ var _this = this;
231
+ return function (_req, res, next) { return __awaiter(_this, void 0, void 0, function () {
232
+ var license;
233
+ return __generator(this, function (_a) {
234
+ switch (_a.label) {
235
+ case 0: return [4 /*yield*/, getCachedLicense()];
236
+ case 1:
237
+ license = _a.sent();
238
+ if (!license)
239
+ return [2 /*return*/, next()]; // Can't check → allow
240
+ if (license.blocked) {
241
+ return [2 /*return*/, res.status(403).json({
242
+ error: "Licencia expirada. El acceso ha sido suspendido. Contacte al administrador.",
243
+ code: "LICENSE_BLOCKED",
244
+ })];
245
+ }
246
+ next();
247
+ return [2 /*return*/];
248
+ }
249
+ });
250
+ }); };
251
+ }
252
+ exports.licenseLoginGuard = licenseLoginGuard;
253
+ /**
254
+ * Get current license status (for use in controllers).
255
+ */
256
+ function getLicenseStatus() {
257
+ return __awaiter(this, void 0, void 0, function () {
258
+ return __generator(this, function (_a) {
259
+ return [2 /*return*/, getCachedLicense()];
260
+ });
261
+ });
262
+ }
263
+ exports.getLicenseStatus = getLicenseStatus;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluyappgocore/commons-backend",
3
- "version": "1.0.204",
3
+ "version": "1.0.208",
4
4
  "description": "",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",