@fluyappgocore/commons-backend 1.0.205 → 1.0.210
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/build/helpers/dialectHelper.d.ts +34 -0
- package/build/helpers/dialectHelper.js +206 -0
- package/build/helpers/index.d.ts +2 -0
- package/build/helpers/index.js +8 -0
- package/build/helpers/multiDialectMigrator.d.ts +15 -0
- package/build/helpers/multiDialectMigrator.js +32 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/middlewares/error.middleware.d.ts +1 -3
- package/build/middlewares/error.middleware.js +1 -0
- package/build/middlewares/index.d.ts +1 -0
- package/build/middlewares/index.js +1 -0
- package/build/middlewares/licenseGuard.middleware.d.ts +56 -0
- package/build/middlewares/licenseGuard.middleware.js +263 -0
- package/package.json +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare class DialectHelper {
|
|
2
|
+
private dialect;
|
|
3
|
+
constructor(dialectOrSequelize: string | {
|
|
4
|
+
getDialect(): string;
|
|
5
|
+
});
|
|
6
|
+
get isPostgres(): boolean;
|
|
7
|
+
get isMssql(): boolean;
|
|
8
|
+
get isMysql(): boolean;
|
|
9
|
+
get dialectName(): string;
|
|
10
|
+
now(): string;
|
|
11
|
+
bool(val: boolean): string;
|
|
12
|
+
qi(name: string): string;
|
|
13
|
+
jsonType(): string;
|
|
14
|
+
autoIncrement(): string;
|
|
15
|
+
textArrayType(): string;
|
|
16
|
+
ilike(): string;
|
|
17
|
+
booleanType(): string;
|
|
18
|
+
textType(): string;
|
|
19
|
+
jsonValue(column: string, path: string): string;
|
|
20
|
+
jsonQuery(column: string, path: string): string;
|
|
21
|
+
jsonSet(column: string, path: string, value: string): string;
|
|
22
|
+
upsertIgnore(table: string, columns: string[], values: string[][], conflictColumn: string): string;
|
|
23
|
+
upsertUpdate(table: string, columns: string[], values: string[], conflictColumn: string, updateColumns: string[]): string;
|
|
24
|
+
returning(columns?: string[]): string;
|
|
25
|
+
limit(n: number): string;
|
|
26
|
+
limitClause(n: number): string;
|
|
27
|
+
timestampDefault(): string;
|
|
28
|
+
createIndexIfNotExists(table: string, indexName: string, columns: string[], unique?: boolean): string;
|
|
29
|
+
concat(...parts: string[]): string;
|
|
30
|
+
toTimestamp(value: string): string;
|
|
31
|
+
}
|
|
32
|
+
export declare function createDialectHelper(dialectOrSequelize: string | {
|
|
33
|
+
getDialect(): string;
|
|
34
|
+
}): DialectHelper;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDialectHelper = exports.DialectHelper = void 0;
|
|
4
|
+
var DialectHelper = /** @class */ (function () {
|
|
5
|
+
function DialectHelper(dialectOrSequelize) {
|
|
6
|
+
this.dialect = typeof dialectOrSequelize === 'string'
|
|
7
|
+
? dialectOrSequelize
|
|
8
|
+
: dialectOrSequelize.getDialect();
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(DialectHelper.prototype, "isPostgres", {
|
|
11
|
+
get: function () { return this.dialect === 'postgres'; },
|
|
12
|
+
enumerable: false,
|
|
13
|
+
configurable: true
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(DialectHelper.prototype, "isMssql", {
|
|
16
|
+
get: function () { return this.dialect === 'mssql'; },
|
|
17
|
+
enumerable: false,
|
|
18
|
+
configurable: true
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(DialectHelper.prototype, "isMysql", {
|
|
21
|
+
get: function () { return this.dialect === 'mysql' || this.dialect === 'mariadb'; },
|
|
22
|
+
enumerable: false,
|
|
23
|
+
configurable: true
|
|
24
|
+
});
|
|
25
|
+
Object.defineProperty(DialectHelper.prototype, "dialectName", {
|
|
26
|
+
get: function () { return this.dialect; },
|
|
27
|
+
enumerable: false,
|
|
28
|
+
configurable: true
|
|
29
|
+
});
|
|
30
|
+
DialectHelper.prototype.now = function () {
|
|
31
|
+
if (this.isMssql)
|
|
32
|
+
return 'GETDATE()';
|
|
33
|
+
return 'NOW()'; // postgres + mysql
|
|
34
|
+
};
|
|
35
|
+
DialectHelper.prototype.bool = function (val) {
|
|
36
|
+
if (this.isMssql)
|
|
37
|
+
return val ? '1' : '0';
|
|
38
|
+
if (this.isMysql)
|
|
39
|
+
return val ? '1' : '0';
|
|
40
|
+
return val ? 'true' : 'false'; // postgres
|
|
41
|
+
};
|
|
42
|
+
DialectHelper.prototype.qi = function (name) {
|
|
43
|
+
if (this.isMssql)
|
|
44
|
+
return "[" + name + "]";
|
|
45
|
+
if (this.isMysql)
|
|
46
|
+
return "`" + name + "`";
|
|
47
|
+
return "\"" + name + "\""; // postgres
|
|
48
|
+
};
|
|
49
|
+
DialectHelper.prototype.jsonType = function () {
|
|
50
|
+
if (this.isMssql)
|
|
51
|
+
return 'NVARCHAR(MAX)';
|
|
52
|
+
if (this.isMysql)
|
|
53
|
+
return 'JSON';
|
|
54
|
+
return 'JSONB'; // postgres
|
|
55
|
+
};
|
|
56
|
+
DialectHelper.prototype.autoIncrement = function () {
|
|
57
|
+
if (this.isMssql)
|
|
58
|
+
return 'INT IDENTITY(1,1)';
|
|
59
|
+
if (this.isMysql)
|
|
60
|
+
return 'INT AUTO_INCREMENT';
|
|
61
|
+
return 'SERIAL'; // postgres
|
|
62
|
+
};
|
|
63
|
+
DialectHelper.prototype.textArrayType = function () {
|
|
64
|
+
if (this.isPostgres)
|
|
65
|
+
return 'TEXT[]';
|
|
66
|
+
return 'NVARCHAR(MAX)'; // mssql + mysql (store as JSON string)
|
|
67
|
+
};
|
|
68
|
+
DialectHelper.prototype.ilike = function () {
|
|
69
|
+
return this.isPostgres ? 'ILIKE' : 'LIKE'; // mssql + mysql use LIKE
|
|
70
|
+
};
|
|
71
|
+
DialectHelper.prototype.booleanType = function () {
|
|
72
|
+
if (this.isMssql)
|
|
73
|
+
return 'BIT';
|
|
74
|
+
if (this.isMysql)
|
|
75
|
+
return 'TINYINT(1)';
|
|
76
|
+
return 'BOOLEAN'; // postgres
|
|
77
|
+
};
|
|
78
|
+
DialectHelper.prototype.textType = function () {
|
|
79
|
+
if (this.isMssql)
|
|
80
|
+
return 'NVARCHAR(MAX)';
|
|
81
|
+
if (this.isMysql)
|
|
82
|
+
return 'LONGTEXT';
|
|
83
|
+
return 'TEXT'; // postgres
|
|
84
|
+
};
|
|
85
|
+
DialectHelper.prototype.jsonValue = function (column, path) {
|
|
86
|
+
if (this.isMssql)
|
|
87
|
+
return "JSON_VALUE(" + this.qi(column) + ", '$." + path + "')";
|
|
88
|
+
if (this.isMysql)
|
|
89
|
+
return "JSON_UNQUOTE(JSON_EXTRACT(" + this.qi(column) + ", '$." + path + "'))";
|
|
90
|
+
return this.qi(column) + "->>'" + path + "'"; // postgres
|
|
91
|
+
};
|
|
92
|
+
DialectHelper.prototype.jsonQuery = function (column, path) {
|
|
93
|
+
if (this.isMssql)
|
|
94
|
+
return "JSON_QUERY(" + this.qi(column) + ", '$." + path + "')";
|
|
95
|
+
if (this.isMysql)
|
|
96
|
+
return "JSON_EXTRACT(" + this.qi(column) + ", '$." + path + "')";
|
|
97
|
+
return this.qi(column) + "->'" + path + "'"; // postgres
|
|
98
|
+
};
|
|
99
|
+
DialectHelper.prototype.jsonSet = function (column, path, value) {
|
|
100
|
+
if (this.isMssql)
|
|
101
|
+
return "JSON_MODIFY(" + this.qi(column) + ", '$." + path + "', " + value + ")";
|
|
102
|
+
if (this.isMysql)
|
|
103
|
+
return "JSON_SET(" + this.qi(column) + ", '$." + path + "', " + value + ")";
|
|
104
|
+
return "jsonb_set(" + this.qi(column) + ", '{" + path.replace(/\./g, ',') + "}', " + value + ")"; // postgres
|
|
105
|
+
};
|
|
106
|
+
DialectHelper.prototype.upsertIgnore = function (table, columns, values, conflictColumn) {
|
|
107
|
+
var _this = this;
|
|
108
|
+
var colList = columns.map(function (c) { return _this.qi(c); }).join(', ');
|
|
109
|
+
if (this.isMssql) {
|
|
110
|
+
return values.map(function (row) {
|
|
111
|
+
var conflictIdx = columns.indexOf(conflictColumn);
|
|
112
|
+
var whereClause = _this.qi(conflictColumn) + " = " + row[conflictIdx];
|
|
113
|
+
var valList = row.join(', ');
|
|
114
|
+
return "IF NOT EXISTS (SELECT 1 FROM " + _this.qi(table) + " WHERE " + whereClause + ")\n INSERT INTO " + _this.qi(table) + " (" + colList + ") VALUES (" + valList + ");";
|
|
115
|
+
}).join('\n');
|
|
116
|
+
}
|
|
117
|
+
if (this.isMysql) {
|
|
118
|
+
var valRows_1 = values.map(function (row) { return "(" + row.join(', ') + ")"; }).join(',\n ');
|
|
119
|
+
return "INSERT IGNORE INTO " + this.qi(table) + " (" + colList + ") VALUES\n " + valRows_1 + ";";
|
|
120
|
+
}
|
|
121
|
+
// postgres
|
|
122
|
+
var valRows = values.map(function (row) { return "(" + row.join(', ') + ")"; }).join(',\n ');
|
|
123
|
+
return "INSERT INTO " + this.qi(table) + " (" + colList + ") VALUES\n " + valRows + "\n ON CONFLICT (" + this.qi(conflictColumn) + ") DO NOTHING;";
|
|
124
|
+
};
|
|
125
|
+
DialectHelper.prototype.upsertUpdate = function (table, columns, values, conflictColumn, updateColumns) {
|
|
126
|
+
var _this = this;
|
|
127
|
+
var colList = columns.map(function (c) { return _this.qi(c); }).join(', ');
|
|
128
|
+
var valList = values.join(', ');
|
|
129
|
+
if (this.isMssql) {
|
|
130
|
+
var updateSet_1 = updateColumns.map(function (c) {
|
|
131
|
+
var idx = columns.indexOf(c);
|
|
132
|
+
return _this.qi(c) + " = " + values[idx];
|
|
133
|
+
}).join(', ');
|
|
134
|
+
return "MERGE " + this.qi(table) + " AS target\nUSING (SELECT " + values.map(function (v, i) { return v + " AS " + _this.qi(columns[i]); }).join(', ') + ") AS source\nON target." + this.qi(conflictColumn) + " = source." + this.qi(conflictColumn) + "\nWHEN MATCHED THEN UPDATE SET " + updateSet_1 + "\nWHEN NOT MATCHED THEN INSERT (" + colList + ") VALUES (" + valList + ");";
|
|
135
|
+
}
|
|
136
|
+
if (this.isMysql) {
|
|
137
|
+
var updateSet_2 = updateColumns.map(function (c) { return _this.qi(c) + " = VALUES(" + _this.qi(c) + ")"; }).join(', ');
|
|
138
|
+
return "INSERT INTO " + this.qi(table) + " (" + colList + ") VALUES (" + valList + ")\nON DUPLICATE KEY UPDATE " + updateSet_2 + ";";
|
|
139
|
+
}
|
|
140
|
+
// postgres
|
|
141
|
+
var updateSet = updateColumns.map(function (c) { return _this.qi(c) + " = EXCLUDED." + _this.qi(c); }).join(', ');
|
|
142
|
+
return "INSERT INTO " + this.qi(table) + " (" + colList + ") VALUES (" + valList + ")\nON CONFLICT (" + this.qi(conflictColumn) + ") DO UPDATE SET " + updateSet + ";";
|
|
143
|
+
};
|
|
144
|
+
DialectHelper.prototype.returning = function (columns) {
|
|
145
|
+
var _this = this;
|
|
146
|
+
if (columns === void 0) { columns = ['*']; }
|
|
147
|
+
if (this.isMssql || this.isMysql)
|
|
148
|
+
return '';
|
|
149
|
+
return "RETURNING " + columns.map(function (c) { return _this.qi(c); }).join(', '); // postgres only
|
|
150
|
+
};
|
|
151
|
+
DialectHelper.prototype.limit = function (n) {
|
|
152
|
+
return this.isMssql ? "TOP " + n : ''; // postgres + mysql use LIMIT clause
|
|
153
|
+
};
|
|
154
|
+
DialectHelper.prototype.limitClause = function (n) {
|
|
155
|
+
if (this.isMssql)
|
|
156
|
+
return '';
|
|
157
|
+
return "LIMIT " + n; // postgres + mysql
|
|
158
|
+
};
|
|
159
|
+
DialectHelper.prototype.timestampDefault = function () {
|
|
160
|
+
if (this.isMssql)
|
|
161
|
+
return 'DEFAULT GETDATE()';
|
|
162
|
+
return 'DEFAULT NOW()'; // postgres + mysql
|
|
163
|
+
};
|
|
164
|
+
// Create index with IF NOT EXISTS support
|
|
165
|
+
DialectHelper.prototype.createIndexIfNotExists = function (table, indexName, columns, unique) {
|
|
166
|
+
var _this = this;
|
|
167
|
+
if (unique === void 0) { unique = false; }
|
|
168
|
+
var uniqueStr = unique ? 'UNIQUE ' : '';
|
|
169
|
+
var colList = columns.map(function (c) { return _this.qi(c); }).join(', ');
|
|
170
|
+
if (this.isMssql) {
|
|
171
|
+
return "IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = '" + indexName + "')\nCREATE " + uniqueStr + "INDEX " + this.qi(indexName) + " ON " + this.qi(table) + " (" + colList + ");";
|
|
172
|
+
}
|
|
173
|
+
if (this.isMysql) {
|
|
174
|
+
// MySQL doesn't support IF NOT EXISTS for CREATE INDEX, use procedure
|
|
175
|
+
return "CREATE " + uniqueStr + "INDEX " + this.qi(indexName) + " ON " + this.qi(table) + " (" + colList + ");";
|
|
176
|
+
}
|
|
177
|
+
// postgres
|
|
178
|
+
return "CREATE " + uniqueStr + "INDEX IF NOT EXISTS " + this.qi(indexName) + " ON " + this.qi(table) + " (" + colList + ");";
|
|
179
|
+
};
|
|
180
|
+
// Concatenation
|
|
181
|
+
DialectHelper.prototype.concat = function () {
|
|
182
|
+
var parts = [];
|
|
183
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
184
|
+
parts[_i] = arguments[_i];
|
|
185
|
+
}
|
|
186
|
+
if (this.isMssql)
|
|
187
|
+
return parts.join(' + ');
|
|
188
|
+
if (this.isMysql)
|
|
189
|
+
return "CONCAT(" + parts.join(', ') + ")";
|
|
190
|
+
return parts.join(' || '); // postgres
|
|
191
|
+
};
|
|
192
|
+
// String to timestamp
|
|
193
|
+
DialectHelper.prototype.toTimestamp = function (value) {
|
|
194
|
+
if (this.isMssql)
|
|
195
|
+
return "CAST(" + value + " AS DATETIME2)";
|
|
196
|
+
if (this.isMysql)
|
|
197
|
+
return "CAST(" + value + " AS DATETIME)";
|
|
198
|
+
return value + "::TIMESTAMP"; // postgres
|
|
199
|
+
};
|
|
200
|
+
return DialectHelper;
|
|
201
|
+
}());
|
|
202
|
+
exports.DialectHelper = DialectHelper;
|
|
203
|
+
function createDialectHelper(dialectOrSequelize) {
|
|
204
|
+
return new DialectHelper(dialectOrSequelize);
|
|
205
|
+
}
|
|
206
|
+
exports.createDialectHelper = createDialectHelper;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getMultiDialectGlobs = exports.createDialectHelper = exports.DialectHelper = void 0;
|
|
4
|
+
var dialectHelper_1 = require("./dialectHelper");
|
|
5
|
+
Object.defineProperty(exports, "DialectHelper", { enumerable: true, get: function () { return dialectHelper_1.DialectHelper; } });
|
|
6
|
+
Object.defineProperty(exports, "createDialectHelper", { enumerable: true, get: function () { return dialectHelper_1.createDialectHelper; } });
|
|
7
|
+
var multiDialectMigrator_1 = require("./multiDialectMigrator");
|
|
8
|
+
Object.defineProperty(exports, "getMultiDialectGlobs", { enumerable: true, get: function () { return multiDialectMigrator_1.getMultiDialectGlobs; } });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface MigratorConfig {
|
|
2
|
+
migrationsPath: string;
|
|
3
|
+
dialect: string;
|
|
4
|
+
serviceName: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Returns the glob patterns for multi-dialect migrations.
|
|
8
|
+
* If common/ or {dialect}/ folders exist, returns those paths.
|
|
9
|
+
* Otherwise falls back to flat migrations/*.{ts,js} (backward compatible).
|
|
10
|
+
*/
|
|
11
|
+
export declare function getMultiDialectGlobs(config: MigratorConfig): {
|
|
12
|
+
globs: string[];
|
|
13
|
+
isMultiDialect: boolean;
|
|
14
|
+
};
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getMultiDialectGlobs = void 0;
|
|
7
|
+
var path_1 = __importDefault(require("path"));
|
|
8
|
+
var fs_1 = __importDefault(require("fs"));
|
|
9
|
+
/**
|
|
10
|
+
* Returns the glob patterns for multi-dialect migrations.
|
|
11
|
+
* If common/ or {dialect}/ folders exist, returns those paths.
|
|
12
|
+
* Otherwise falls back to flat migrations/*.{ts,js} (backward compatible).
|
|
13
|
+
*/
|
|
14
|
+
function getMultiDialectGlobs(config) {
|
|
15
|
+
var migrationsPath = config.migrationsPath, dialect = config.dialect, serviceName = config.serviceName;
|
|
16
|
+
var commonDir = path_1.default.join(migrationsPath, 'common');
|
|
17
|
+
var dialectDir = path_1.default.join(migrationsPath, dialect);
|
|
18
|
+
var hasCommon = fs_1.default.existsSync(commonDir);
|
|
19
|
+
var hasDialect = fs_1.default.existsSync(dialectDir);
|
|
20
|
+
if (hasCommon || hasDialect) {
|
|
21
|
+
var globs = [];
|
|
22
|
+
if (hasCommon)
|
|
23
|
+
globs.push(path_1.default.join(commonDir, '*.{ts,js}'));
|
|
24
|
+
if (hasDialect)
|
|
25
|
+
globs.push(path_1.default.join(dialectDir, '*.{ts,js}'));
|
|
26
|
+
console.log("[" + serviceName + "] Multi-dialect migrations: common/" + (hasCommon ? '✓' : '✗') + " " + dialect + "/" + (hasDialect ? '✓' : '✗'));
|
|
27
|
+
return { globs: globs, isMultiDialect: true };
|
|
28
|
+
}
|
|
29
|
+
console.log("[" + serviceName + "] Legacy flat migrations");
|
|
30
|
+
return { globs: [path_1.default.join(migrationsPath, '*.{ts,js}')], isMultiDialect: false };
|
|
31
|
+
}
|
|
32
|
+
exports.getMultiDialectGlobs = getMultiDialectGlobs;
|
package/build/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from "./middlewares";
|
|
|
5
5
|
export * from "./events";
|
|
6
6
|
export * from "./dtos";
|
|
7
7
|
export * from "./lib";
|
|
8
|
+
export * from "./helpers";
|
|
8
9
|
export { BacklogManager } from './classes/BacklogManager';
|
|
9
10
|
export { BacklogMonitor } from './classes/BacklogMonitor';
|
|
10
11
|
export { Listener } from './events/baseListener';
|
package/build/index.js
CHANGED
|
@@ -18,6 +18,7 @@ __exportStar(require("./middlewares"), exports);
|
|
|
18
18
|
__exportStar(require("./events"), exports);
|
|
19
19
|
__exportStar(require("./dtos"), exports);
|
|
20
20
|
__exportStar(require("./lib"), exports);
|
|
21
|
+
__exportStar(require("./helpers"), exports);
|
|
21
22
|
// Classes
|
|
22
23
|
var BacklogManager_1 = require("./classes/BacklogManager");
|
|
23
24
|
Object.defineProperty(exports, "BacklogManager", { enumerable: true, get: function () { return BacklogManager_1.BacklogManager; } });
|
|
@@ -1,3 +1 @@
|
|
|
1
|
-
|
|
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';
|
|
@@ -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;
|