mvc-common-toolkit 1.43.10 → 1.44.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/src/constants.d.ts +59 -0
- package/dist/src/constants.js +69 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/gateways/alibaba-cloud-gateway.d.ts +30 -0
- package/dist/src/gateways/alibaba-cloud-gateway.js +120 -0
- package/dist/src/gateways/alibaba-cloud-gateway.js.map +1 -0
- package/dist/src/gateways/http-audit-gateway.d.ts +20 -0
- package/dist/src/gateways/http-audit-gateway.js +76 -0
- package/dist/src/gateways/http-audit-gateway.js.map +1 -0
- package/dist/src/gateways/index.d.ts +5 -0
- package/dist/src/gateways/index.js +22 -0
- package/dist/src/gateways/index.js.map +1 -0
- package/dist/src/gateways/internal-auth-gateway.d.ts +8 -0
- package/dist/src/gateways/internal-auth-gateway.js +40 -0
- package/dist/src/gateways/internal-auth-gateway.js.map +1 -0
- package/dist/src/gateways/stdout-audit-gateway.d.ts +7 -0
- package/dist/src/gateways/stdout-audit-gateway.js +25 -0
- package/dist/src/gateways/stdout-audit-gateway.js.map +1 -0
- package/dist/src/gateways/webhook-audit-gateway.d.ts +14 -0
- package/dist/src/gateways/webhook-audit-gateway.js +27 -0
- package/dist/src/gateways/webhook-audit-gateway.js.map +1 -0
- package/dist/src/interfaces.d.ts +218 -0
- package/dist/src/interfaces.js +3 -0
- package/dist/src/interfaces.js.map +1 -0
- package/dist/src/models/audit-log.d.ts +77 -0
- package/dist/src/models/audit-log.js +92 -0
- package/dist/src/models/audit-log.js.map +1 -0
- package/dist/src/models/index.d.ts +1 -0
- package/dist/src/models/index.js +18 -0
- package/dist/src/models/index.js.map +1 -0
- package/dist/src/pkg/array-helper.d.ts +1 -0
- package/dist/src/pkg/array-helper.js +12 -0
- package/dist/src/pkg/array-helper.js.map +1 -0
- package/dist/src/pkg/bcrypt-helper.d.ts +2 -0
- package/dist/src/pkg/bcrypt-helper.js +36 -0
- package/dist/src/pkg/bcrypt-helper.js.map +1 -0
- package/dist/src/pkg/crypto-helper.d.ts +2 -0
- package/dist/src/pkg/crypto-helper.js +16 -0
- package/dist/src/pkg/crypto-helper.js.map +1 -0
- package/dist/src/pkg/encryption-helper.d.ts +18 -0
- package/dist/src/pkg/encryption-helper.js +89 -0
- package/dist/src/pkg/encryption-helper.js.map +1 -0
- package/dist/src/pkg/encryption-helper.spec.d.ts +1 -0
- package/dist/src/pkg/encryption-helper.spec.js +238 -0
- package/dist/src/pkg/encryption-helper.spec.js.map +1 -0
- package/dist/src/pkg/filter-helper.d.ts +2 -0
- package/dist/src/pkg/filter-helper.js +102 -0
- package/dist/src/pkg/filter-helper.js.map +1 -0
- package/dist/src/pkg/filter-helper.spec.d.ts +1 -0
- package/dist/src/pkg/filter-helper.spec.js +94 -0
- package/dist/src/pkg/filter-helper.spec.js.map +1 -0
- package/dist/src/pkg/geoip-helper.d.ts +2 -0
- package/dist/src/pkg/geoip-helper.js +32 -0
- package/dist/src/pkg/geoip-helper.js.map +1 -0
- package/dist/src/pkg/hash-helper.d.ts +1 -0
- package/dist/src/pkg/hash-helper.js +37 -0
- package/dist/src/pkg/hash-helper.js.map +1 -0
- package/dist/src/pkg/http-request-utils.d.ts +4 -0
- package/dist/src/pkg/http-request-utils.js +55 -0
- package/dist/src/pkg/http-request-utils.js.map +1 -0
- package/dist/src/pkg/index.d.ts +19 -0
- package/dist/src/pkg/index.js +46 -0
- package/dist/src/pkg/index.js.map +1 -0
- package/dist/src/pkg/key-helper.d.ts +2 -0
- package/dist/src/pkg/key-helper.js +20 -0
- package/dist/src/pkg/key-helper.js.map +1 -0
- package/dist/src/pkg/logger.d.ts +9 -0
- package/dist/src/pkg/logger.js +23 -0
- package/dist/src/pkg/logger.js.map +1 -0
- package/dist/src/pkg/object-helper.d.ts +2 -0
- package/dist/src/pkg/object-helper.js +37 -0
- package/dist/src/pkg/object-helper.js.map +1 -0
- package/dist/src/pkg/paginated-cache-registry.d.ts +8 -0
- package/dist/src/pkg/paginated-cache-registry.js +23 -0
- package/dist/src/pkg/paginated-cache-registry.js.map +1 -0
- package/dist/src/pkg/query-helper.d.ts +3 -0
- package/dist/src/pkg/query-helper.js +60 -0
- package/dist/src/pkg/query-helper.js.map +1 -0
- package/dist/src/pkg/referral-tree-utils.d.ts +33 -0
- package/dist/src/pkg/referral-tree-utils.js +71 -0
- package/dist/src/pkg/referral-tree-utils.js.map +1 -0
- package/dist/src/pkg/scripts/index.d.ts +1 -0
- package/dist/src/pkg/scripts/index.js +28 -0
- package/dist/src/pkg/scripts/index.js.map +1 -0
- package/dist/src/pkg/scripts/lua.d.ts +10 -0
- package/dist/src/pkg/scripts/lua.js +109 -0
- package/dist/src/pkg/scripts/lua.js.map +1 -0
- package/dist/src/pkg/sort-helper.d.ts +3 -0
- package/dist/src/pkg/sort-helper.js +18 -0
- package/dist/src/pkg/sort-helper.js.map +1 -0
- package/dist/src/pkg/string-utils.d.ts +10 -0
- package/dist/src/pkg/string-utils.js +79 -0
- package/dist/src/pkg/string-utils.js.map +1 -0
- package/dist/src/pkg/task-helper.d.ts +2 -0
- package/dist/src/pkg/task-helper.js +30 -0
- package/dist/src/pkg/task-helper.js.map +1 -0
- package/dist/src/pkg/trading-pair-helper.d.ts +9 -0
- package/dist/src/pkg/trading-pair-helper.js +44 -0
- package/dist/src/pkg/trading-pair-helper.js.map +1 -0
- package/dist/src/pkg/trading-pair-helper.spec.d.ts +1 -0
- package/dist/src/pkg/trading-pair-helper.spec.js +132 -0
- package/dist/src/pkg/trading-pair-helper.spec.js.map +1 -0
- package/dist/src/pkg/workflow/delayed-task-registry.d.ts +10 -0
- package/dist/src/pkg/workflow/delayed-task-registry.js +67 -0
- package/dist/src/pkg/workflow/delayed-task-registry.js.map +1 -0
- package/dist/src/pkg/workflow/delayed-task.d.ts +18 -0
- package/dist/src/pkg/workflow/delayed-task.js +95 -0
- package/dist/src/pkg/workflow/delayed-task.js.map +1 -0
- package/dist/src/pkg/workflow/index.d.ts +5 -0
- package/dist/src/pkg/workflow/index.js +22 -0
- package/dist/src/pkg/workflow/index.js.map +1 -0
- package/dist/src/pkg/workflow/processing-milestone.d.ts +18 -0
- package/dist/src/pkg/workflow/processing-milestone.js +39 -0
- package/dist/src/pkg/workflow/processing-milestone.js.map +1 -0
- package/dist/src/pkg/workflow/retry-task.d.ts +24 -0
- package/dist/src/pkg/workflow/retry-task.js +89 -0
- package/dist/src/pkg/workflow/retry-task.js.map +1 -0
- package/dist/src/pkg/workflow/retry-task.spec.d.ts +1 -0
- package/dist/src/pkg/workflow/retry-task.spec.js +145 -0
- package/dist/src/pkg/workflow/retry-task.spec.js.map +1 -0
- package/dist/src/pkg/workflow/sync-taskqueue.d.ts +32 -0
- package/dist/src/pkg/workflow/sync-taskqueue.js +108 -0
- package/dist/src/pkg/workflow/sync-taskqueue.js.map +1 -0
- package/dist/src/pkg/worksheet.utils.d.ts +27 -0
- package/dist/src/pkg/worksheet.utils.js +116 -0
- package/dist/src/pkg/worksheet.utils.js.map +1 -0
- package/dist/src/services/audit-service.d.ts +7 -0
- package/dist/src/services/audit-service.js +32 -0
- package/dist/src/services/audit-service.js.map +1 -0
- package/dist/src/services/excel.service.d.ts +25 -0
- package/dist/src/services/excel.service.js +95 -0
- package/dist/src/services/excel.service.js.map +1 -0
- package/dist/src/services/http-service.d.ts +7 -0
- package/dist/src/services/http-service.js +67 -0
- package/dist/src/services/http-service.js.map +1 -0
- package/dist/src/services/index.d.ts +8 -0
- package/dist/src/services/index.js +25 -0
- package/dist/src/services/index.js.map +1 -0
- package/dist/src/services/kafka-service.d.ts +15 -0
- package/dist/src/services/kafka-service.js +68 -0
- package/dist/src/services/kafka-service.js.map +1 -0
- package/dist/src/services/mailer-service.d.ts +15 -0
- package/dist/src/services/mailer-service.js +44 -0
- package/dist/src/services/mailer-service.js.map +1 -0
- package/dist/src/services/paginated-cache.d.ts +16 -0
- package/dist/src/services/paginated-cache.js +115 -0
- package/dist/src/services/paginated-cache.js.map +1 -0
- package/dist/src/services/paginated-cache.spec.d.ts +1 -0
- package/dist/src/services/paginated-cache.spec.js +284 -0
- package/dist/src/services/paginated-cache.spec.js.map +1 -0
- package/dist/src/services/redis-service.d.ts +33 -0
- package/dist/src/services/redis-service.js +230 -0
- package/dist/src/services/redis-service.js.map +1 -0
- package/dist/src/services/security-service.d.ts +11 -0
- package/dist/src/services/security-service.js +68 -0
- package/dist/src/services/security-service.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/constants.ts +66 -0
- package/src/gateways/alibaba-cloud-gateway.ts +127 -0
- package/src/gateways/http-audit-gateway.ts +104 -0
- package/src/gateways/index.ts +5 -0
- package/src/gateways/internal-auth-gateway.ts +42 -0
- package/src/gateways/stdout-audit-gateway.ts +23 -0
- package/src/gateways/webhook-audit-gateway.ts +33 -0
- package/src/interfaces.ts +304 -0
- package/src/models/audit-log.ts +126 -0
- package/src/models/index.ts +1 -0
- package/src/pkg/array-helper.ts +7 -0
- package/src/pkg/bcrypt-helper.ts +9 -0
- package/src/pkg/crypto-helper.ts +18 -0
- package/src/pkg/encryption-helper.spec.ts +423 -0
- package/src/pkg/encryption-helper.ts +155 -0
- package/src/pkg/filter-helper.spec.ts +105 -0
- package/src/pkg/filter-helper.ts +139 -0
- package/src/pkg/geoip-helper.ts +5 -0
- package/src/pkg/hash-helper.ts +12 -0
- package/src/pkg/http-request-utils.ts +75 -0
- package/src/pkg/index.ts +19 -0
- package/src/pkg/key-helper.ts +20 -0
- package/src/pkg/logger.ts +23 -0
- package/src/pkg/object-helper.ts +42 -0
- package/src/pkg/paginated-cache-registry.ts +25 -0
- package/src/pkg/query-helper.ts +79 -0
- package/src/pkg/referral-tree-utils.ts +165 -0
- package/src/pkg/scripts/index.ts +1 -0
- package/src/pkg/scripts/lua.ts +112 -0
- package/src/pkg/sort-helper.ts +19 -0
- package/src/pkg/string-utils.ts +104 -0
- package/src/pkg/task-helper.ts +25 -0
- package/src/pkg/trading-pair-helper.spec.ts +146 -0
- package/src/pkg/trading-pair-helper.ts +78 -0
- package/src/pkg/workflow/delayed-task-registry.ts +54 -0
- package/src/pkg/workflow/delayed-task.ts +106 -0
- package/src/pkg/workflow/index.ts +5 -0
- package/src/pkg/workflow/processing-milestone.ts +54 -0
- package/src/pkg/workflow/retry-task.spec.ts +194 -0
- package/src/pkg/workflow/retry-task.ts +119 -0
- package/src/pkg/workflow/sync-taskqueue.ts +118 -0
- package/src/pkg/worksheet.utils.ts +178 -0
- package/src/services/audit-service.ts +22 -0
- package/src/services/excel.service.ts +103 -0
- package/src/services/http-service.ts +71 -0
- package/src/services/index.ts +8 -0
- package/src/services/kafka-service.ts +81 -0
- package/src/services/mailer-service.ts +43 -0
- package/src/services/paginated-cache.spec.ts +519 -0
- package/src/services/paginated-cache.ts +122 -0
- package/src/services/redis-service.ts +238 -0
- package/src/services/security-service.ts +80 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
randomBytes,
|
|
3
|
+
pbkdf2Sync,
|
|
4
|
+
createCipheriv,
|
|
5
|
+
createDecipheriv,
|
|
6
|
+
} from "crypto";
|
|
7
|
+
|
|
8
|
+
const PBKDF2_ITERATIONS = 310_000; // adjust upward over time
|
|
9
|
+
const KEY_LEN = 32; // 256-bit AES key
|
|
10
|
+
const IV_LEN = 12; // standard for GCM
|
|
11
|
+
const SALT_LEN = 16; // at least 16 bytes
|
|
12
|
+
|
|
13
|
+
interface EncryptedPayload {
|
|
14
|
+
iv: string; // hex
|
|
15
|
+
salt: string; // hex
|
|
16
|
+
ciphertext: string;
|
|
17
|
+
tag: string; // auth tag (hex)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface EncryptedPayload2FA {
|
|
21
|
+
iv: string;
|
|
22
|
+
saltUser: string;
|
|
23
|
+
saltServer: string;
|
|
24
|
+
ciphertext: string;
|
|
25
|
+
tag: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Encrypt a UTF-8 string using password-based AES-256-GCM
|
|
30
|
+
*/
|
|
31
|
+
export function encrypt(text: string, password: string): EncryptedPayload {
|
|
32
|
+
const salt = randomBytes(SALT_LEN);
|
|
33
|
+
const key = pbkdf2Sync(password, salt, PBKDF2_ITERATIONS, KEY_LEN, "sha256");
|
|
34
|
+
|
|
35
|
+
const iv = randomBytes(IV_LEN);
|
|
36
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
37
|
+
|
|
38
|
+
const ciphertext = Buffer.concat([
|
|
39
|
+
cipher.update(text, "utf8"),
|
|
40
|
+
cipher.final(),
|
|
41
|
+
]);
|
|
42
|
+
const tag = cipher.getAuthTag();
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
iv: iv.toString("hex"),
|
|
46
|
+
salt: salt.toString("hex"),
|
|
47
|
+
ciphertext: ciphertext.toString("hex"),
|
|
48
|
+
tag: tag.toString("hex"),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Decrypt payload produced by `encrypt`
|
|
54
|
+
*/
|
|
55
|
+
export function decrypt(payload: EncryptedPayload, password: string): string {
|
|
56
|
+
const { iv, salt, ciphertext, tag } = payload;
|
|
57
|
+
|
|
58
|
+
const key = pbkdf2Sync(
|
|
59
|
+
Buffer.from(password),
|
|
60
|
+
Buffer.from(salt, "hex"),
|
|
61
|
+
PBKDF2_ITERATIONS,
|
|
62
|
+
KEY_LEN,
|
|
63
|
+
"sha256"
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(iv, "hex"));
|
|
67
|
+
decipher.setAuthTag(Buffer.from(tag, "hex"));
|
|
68
|
+
|
|
69
|
+
const plaintext = Buffer.concat([
|
|
70
|
+
decipher.update(Buffer.from(ciphertext, "hex")),
|
|
71
|
+
decipher.final(),
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
return plaintext.toString("utf8");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function deriveKey(password: string, salt: Buffer): Buffer {
|
|
78
|
+
return pbkdf2Sync(password, salt, PBKDF2_ITERATIONS, KEY_LEN, "sha256");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function encryptWithTwoFactors(
|
|
82
|
+
plaintext: string,
|
|
83
|
+
userPassword: string,
|
|
84
|
+
serverSecret: string
|
|
85
|
+
): EncryptedPayload2FA {
|
|
86
|
+
const saltUser = randomBytes(SALT_LEN);
|
|
87
|
+
const saltServer = randomBytes(SALT_LEN);
|
|
88
|
+
|
|
89
|
+
const userKey = deriveKey(userPassword, saltUser);
|
|
90
|
+
const serverKey = deriveKey(serverSecret, saltServer);
|
|
91
|
+
|
|
92
|
+
// Final AES key: XOR of both keys
|
|
93
|
+
const finalKey = Buffer.alloc(KEY_LEN);
|
|
94
|
+
for (let i = 0; i < KEY_LEN; i++) {
|
|
95
|
+
finalKey[i] = userKey[i] ^ serverKey[i];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const iv = randomBytes(IV_LEN);
|
|
99
|
+
const cipher = createCipheriv("aes-256-gcm", finalKey, iv);
|
|
100
|
+
|
|
101
|
+
const ciphertext = Buffer.concat([
|
|
102
|
+
cipher.update(plaintext, "utf8"),
|
|
103
|
+
cipher.final(),
|
|
104
|
+
]);
|
|
105
|
+
const tag = cipher.getAuthTag();
|
|
106
|
+
|
|
107
|
+
// Cleanup sensitive buffers
|
|
108
|
+
userKey.fill(0);
|
|
109
|
+
serverKey.fill(0);
|
|
110
|
+
finalKey.fill(0);
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
iv: iv.toString("hex"),
|
|
114
|
+
saltUser: saltUser.toString("hex"),
|
|
115
|
+
saltServer: saltServer.toString("hex"),
|
|
116
|
+
ciphertext: ciphertext.toString("hex"),
|
|
117
|
+
tag: tag.toString("hex"),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function decryptWithTwoFactors(
|
|
122
|
+
payload: EncryptedPayload2FA,
|
|
123
|
+
userPassword: string,
|
|
124
|
+
serverSecret: string
|
|
125
|
+
): string {
|
|
126
|
+
const userKey = deriveKey(userPassword, Buffer.from(payload.saltUser, "hex"));
|
|
127
|
+
const serverKey = deriveKey(
|
|
128
|
+
serverSecret,
|
|
129
|
+
Buffer.from(payload.saltServer, "hex")
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const finalKey = Buffer.alloc(KEY_LEN);
|
|
133
|
+
for (let i = 0; i < KEY_LEN; i++) {
|
|
134
|
+
finalKey[i] = userKey[i] ^ serverKey[i];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const decipher = createDecipheriv(
|
|
138
|
+
"aes-256-gcm",
|
|
139
|
+
finalKey,
|
|
140
|
+
Buffer.from(payload.iv, "hex")
|
|
141
|
+
);
|
|
142
|
+
decipher.setAuthTag(Buffer.from(payload.tag, "hex"));
|
|
143
|
+
|
|
144
|
+
const plaintext = Buffer.concat([
|
|
145
|
+
decipher.update(Buffer.from(payload.ciphertext, "hex")),
|
|
146
|
+
decipher.final(),
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
// Cleanup sensitive buffers
|
|
150
|
+
userKey.fill(0);
|
|
151
|
+
serverKey.fill(0);
|
|
152
|
+
finalKey.fill(0);
|
|
153
|
+
|
|
154
|
+
return plaintext.toString("utf8");
|
|
155
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
|
|
3
|
+
import { FILTER_OPERATOR } from "../constants";
|
|
4
|
+
import { IFilter } from "../interfaces";
|
|
5
|
+
import { toSimpleMongooseFilter } from "./filter-helper";
|
|
6
|
+
|
|
7
|
+
describe("filter helper", () => {
|
|
8
|
+
describe("simple mongoose filter", () => {
|
|
9
|
+
it("should parse simple equal filters correctly", () => {
|
|
10
|
+
const filters: IFilter[] = [
|
|
11
|
+
{
|
|
12
|
+
field: "name",
|
|
13
|
+
operator: FILTER_OPERATOR.EQUAL,
|
|
14
|
+
value: "andy",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
field: "email",
|
|
18
|
+
operator: FILTER_OPERATOR.EQUAL,
|
|
19
|
+
value: "andy@gmail.com",
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const parsedFilter = toSimpleMongooseFilter(filters);
|
|
24
|
+
|
|
25
|
+
expect(parsedFilter.name).to.be.equal("andy");
|
|
26
|
+
expect(parsedFilter.email).to.be.equal("andy@gmail.com");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should parse more complex filters correctly", () => {
|
|
30
|
+
const filters: IFilter[] = [
|
|
31
|
+
{
|
|
32
|
+
field: "name",
|
|
33
|
+
operator: FILTER_OPERATOR.INS_STARTS_WITH,
|
|
34
|
+
value: "andy",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
field: "email",
|
|
38
|
+
operator: FILTER_OPERATOR.INS_ENDS_WITH,
|
|
39
|
+
value: "andy@gmail.com",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
field: "age",
|
|
43
|
+
operator: FILTER_OPERATOR.IN,
|
|
44
|
+
value: [20, 30, 40],
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const parsedFilter = toSimpleMongooseFilter(filters);
|
|
49
|
+
|
|
50
|
+
expect(parsedFilter.name).to.be.deep.equal({
|
|
51
|
+
$regex: "^andy.*$",
|
|
52
|
+
$options: "i",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(parsedFilter.email).to.be.deep.equal({
|
|
56
|
+
$regex: ".*andy@gmail.com$",
|
|
57
|
+
$options: "i",
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(parsedFilter.age).to.be.deep.equal([20, 30, 40]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should parse OR operator correctly", () => {
|
|
64
|
+
const filters: IFilter[] = [
|
|
65
|
+
{
|
|
66
|
+
field: null,
|
|
67
|
+
operator: FILTER_OPERATOR.OR,
|
|
68
|
+
value: [
|
|
69
|
+
{
|
|
70
|
+
field: "name",
|
|
71
|
+
operator: FILTER_OPERATOR.EQUAL,
|
|
72
|
+
value: "andy",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
field: "name",
|
|
76
|
+
operator: FILTER_OPERATOR.EQUAL,
|
|
77
|
+
value: "devstic",
|
|
78
|
+
},
|
|
79
|
+
] as IFilter[],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
field: "email",
|
|
83
|
+
operator: FILTER_OPERATOR.INS_LIKE,
|
|
84
|
+
value: "andy@gmail.com",
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const parsedFilter = toSimpleMongooseFilter(filters);
|
|
89
|
+
|
|
90
|
+
expect(parsedFilter.$or).to.be.deep.equal([
|
|
91
|
+
{
|
|
92
|
+
name: "andy",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "devstic",
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
expect(parsedFilter.email).to.be.deep.equal({
|
|
100
|
+
$regex: ".*andy@gmail.com.*",
|
|
101
|
+
$options: "i",
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { FILTER_OPERATOR } from "../constants";
|
|
2
|
+
import { IFilter } from "../interfaces";
|
|
3
|
+
|
|
4
|
+
export const toSimpleMongooseFilter = (
|
|
5
|
+
filters: IFilter[]
|
|
6
|
+
): Record<string, any> => {
|
|
7
|
+
return filters.reduce((agg, current) => {
|
|
8
|
+
switch (current.operator) {
|
|
9
|
+
case FILTER_OPERATOR.EQUAL:
|
|
10
|
+
agg[current.field] = current.value;
|
|
11
|
+
|
|
12
|
+
break;
|
|
13
|
+
case FILTER_OPERATOR.NOT_EQUAL:
|
|
14
|
+
agg[current.field] = {
|
|
15
|
+
$ne: current.value,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
break;
|
|
19
|
+
|
|
20
|
+
case FILTER_OPERATOR.NOT:
|
|
21
|
+
agg[current.field] = {
|
|
22
|
+
$ne: current.value,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
break;
|
|
26
|
+
|
|
27
|
+
case FILTER_OPERATOR.LIKE:
|
|
28
|
+
agg[current.field] = {
|
|
29
|
+
$regex: `.*${current.value}.*`,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
break;
|
|
33
|
+
|
|
34
|
+
case FILTER_OPERATOR.INS_LIKE:
|
|
35
|
+
agg[current.field] = {
|
|
36
|
+
$regex: `.*${current.value}.*`,
|
|
37
|
+
$options: "i",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
break;
|
|
41
|
+
|
|
42
|
+
case FILTER_OPERATOR.STARTS_WITH:
|
|
43
|
+
agg[current.field] = {
|
|
44
|
+
$regex: `^${current.value}.*$`,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
break;
|
|
48
|
+
case FILTER_OPERATOR.ENDS_WITH:
|
|
49
|
+
agg[current.field] = {
|
|
50
|
+
$regex: `.*${current.value}$`,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
break;
|
|
54
|
+
case FILTER_OPERATOR.INS_STARTS_WITH:
|
|
55
|
+
agg[current.field] = {
|
|
56
|
+
$regex: `^${current.value}.*$`,
|
|
57
|
+
$options: "i",
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
break;
|
|
61
|
+
case FILTER_OPERATOR.INS_ENDS_WITH:
|
|
62
|
+
agg[current.field] = {
|
|
63
|
+
$regex: `.*${current.value}$`,
|
|
64
|
+
$options: "i",
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
break;
|
|
68
|
+
case FILTER_OPERATOR.GREATER_THAN:
|
|
69
|
+
agg[current.field] = {
|
|
70
|
+
$gt: current.value,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
break;
|
|
74
|
+
|
|
75
|
+
case FILTER_OPERATOR.GREATER_THAN_OR_EQUAL:
|
|
76
|
+
agg[current.field] = {
|
|
77
|
+
$gte: current.value,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
break;
|
|
81
|
+
|
|
82
|
+
case FILTER_OPERATOR.LESS_THAN:
|
|
83
|
+
agg[current.field] = {
|
|
84
|
+
$lt: current.value,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case FILTER_OPERATOR.LESS_THAN_OR_EQUAL:
|
|
90
|
+
agg[current.field] = {
|
|
91
|
+
$lte: current.value,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
case FILTER_OPERATOR.IN:
|
|
97
|
+
agg[current.field] = current.value;
|
|
98
|
+
|
|
99
|
+
break;
|
|
100
|
+
|
|
101
|
+
case FILTER_OPERATOR.NOT_IN:
|
|
102
|
+
agg[current.field] = {
|
|
103
|
+
$nin: current.value,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
break;
|
|
107
|
+
|
|
108
|
+
case FILTER_OPERATOR.OR:
|
|
109
|
+
const parsedOr =
|
|
110
|
+
current.value?.map((filterCondition: IFilter) =>
|
|
111
|
+
toSimpleMongooseFilter([filterCondition])
|
|
112
|
+
) || [];
|
|
113
|
+
|
|
114
|
+
if (!parsedOr.length) {
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
agg.$or = parsedOr;
|
|
119
|
+
|
|
120
|
+
break;
|
|
121
|
+
|
|
122
|
+
case FILTER_OPERATOR.NOR:
|
|
123
|
+
const parsedNor =
|
|
124
|
+
current.value?.map((filterCondition: IFilter) =>
|
|
125
|
+
toSimpleMongooseFilter([filterCondition])
|
|
126
|
+
) || [];
|
|
127
|
+
|
|
128
|
+
if (!parsedNor.length) {
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
agg.$nor = parsedNor;
|
|
133
|
+
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return agg;
|
|
138
|
+
}, {} as Record<string, any>);
|
|
139
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as crypto from "crypto";
|
|
2
|
+
|
|
3
|
+
import jsonS = require("fast-json-stable-stringify");
|
|
4
|
+
|
|
5
|
+
export const generateHashFromJSON = (data: Record<string, any>): string => {
|
|
6
|
+
if (!data || typeof data !== "object") {
|
|
7
|
+
throw new Error("Input must be a non-null object");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const jsonString = jsonS(data);
|
|
11
|
+
return crypto.createHash("sha256").update(jsonString).digest("hex");
|
|
12
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export function constructUrlQueryString(
|
|
2
|
+
url: string,
|
|
3
|
+
queryStringData: any,
|
|
4
|
+
options?: { encodeUrl?: boolean }
|
|
5
|
+
): string {
|
|
6
|
+
if (!queryStringData) {
|
|
7
|
+
return url;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const encoder = options?.encodeUrl
|
|
11
|
+
? encodeURIComponent
|
|
12
|
+
: (value: any) => value;
|
|
13
|
+
|
|
14
|
+
const rawQueryString = [];
|
|
15
|
+
for (const props in queryStringData) {
|
|
16
|
+
const propValue = queryStringData[props];
|
|
17
|
+
if (propValue === undefined || propValue === null) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (typeof propValue === "object") {
|
|
22
|
+
rawQueryString.push(`${props}=${encoder(JSON.stringify(propValue))}`);
|
|
23
|
+
} else {
|
|
24
|
+
rawQueryString.push(`${props}=${encoder(propValue)}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return `${url}?${rawQueryString.join("&")}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const MASK_FIELDS = ["password", "secret"];
|
|
32
|
+
|
|
33
|
+
export function maskSensitiveData(
|
|
34
|
+
data: any,
|
|
35
|
+
maskFields: string[] = MASK_FIELDS
|
|
36
|
+
): Record<string, any> {
|
|
37
|
+
// It is a string, number, or whatever. We don't process
|
|
38
|
+
if (typeof data !== "object") {
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Data is an object
|
|
43
|
+
if (!Array.isArray(data)) {
|
|
44
|
+
const maskedData = Object.assign({}, data || {});
|
|
45
|
+
const keys = Object.keys(data);
|
|
46
|
+
|
|
47
|
+
keys.forEach((key) => {
|
|
48
|
+
let value = data[key];
|
|
49
|
+
|
|
50
|
+
if (!value) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (typeof value === "object") {
|
|
55
|
+
value = maskSensitiveData(value, maskFields);
|
|
56
|
+
|
|
57
|
+
maskedData[key] = value;
|
|
58
|
+
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Key is not sensitive
|
|
63
|
+
if (!maskFields.includes(key)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
maskedData[key] = "***";
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return maskedData;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Data is an array
|
|
74
|
+
return data.map((i) => maskSensitiveData(i, maskFields));
|
|
75
|
+
}
|
package/src/pkg/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export * as bcryptHelper from "./bcrypt-helper";
|
|
2
|
+
export * as httpRequestUtils from "./http-request-utils";
|
|
3
|
+
export * as objectHelper from "./object-helper";
|
|
4
|
+
export * as queryHelper from "./query-helper";
|
|
5
|
+
export * as stringUtils from "./string-utils";
|
|
6
|
+
export * as worksheetUtils from "./worksheet.utils";
|
|
7
|
+
export * as cryptoHelpers from "./crypto-helper";
|
|
8
|
+
export * as sortHelpers from "./sort-helper";
|
|
9
|
+
export * as filterHelpers from "./filter-helper";
|
|
10
|
+
export * as loggers from "./logger";
|
|
11
|
+
export * as workflows from "./workflow";
|
|
12
|
+
export * as geoipHelpers from "./geoip-helper";
|
|
13
|
+
export * as arrayHelpers from "./array-helper";
|
|
14
|
+
export * as referralTreeHelpers from "./referral-tree-utils";
|
|
15
|
+
export * as timeoutHelper from "./task-helper";
|
|
16
|
+
export * as cacheHelper from "./paginated-cache-registry";
|
|
17
|
+
export * as encryptionHelper from "./encryption-helper";
|
|
18
|
+
export * as scripts from "./scripts";
|
|
19
|
+
export * as tradingPairHelper from "./trading-pair-helper";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const versionPlaceholder = '_version';
|
|
2
|
+
|
|
3
|
+
// Helper functions for versioned cache keys
|
|
4
|
+
export const getVersionedCacheKey = (key: string, version: number) => {
|
|
5
|
+
if (key.includes(versionPlaceholder)) {
|
|
6
|
+
return key.replace(versionPlaceholder, version.toString());
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return `${key}:version:${version}`;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const getVersionCacheKeyForKey = (key: string) => {
|
|
13
|
+
if (key.includes(versionPlaceholder)) {
|
|
14
|
+
const indexOfPlaceholder = key.indexOf(versionPlaceholder);
|
|
15
|
+
|
|
16
|
+
return key.substring(0, indexOfPlaceholder);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return `${key}:version`;
|
|
20
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import pino from "pino";
|
|
2
|
+
|
|
3
|
+
import { Logger } from "../interfaces";
|
|
4
|
+
|
|
5
|
+
export class PinoLogger implements Logger {
|
|
6
|
+
protected _logger: pino.Logger;
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
this._logger = pino();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public info(message: string, ...args: any[]): void {
|
|
13
|
+
return this._logger.info(message, ...args);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public error(message: string, ...args: any[]): void {
|
|
17
|
+
return this._logger.error(message, ...args);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public warn(message: string, ...args: any[]): void {
|
|
21
|
+
return this._logger.warn(message, ...args);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function removeUndefinedProps(data: any): any {
|
|
2
|
+
const allKeys = Object.keys(data);
|
|
3
|
+
|
|
4
|
+
return allKeys.reduce((aggregated, current) => {
|
|
5
|
+
if (data[current] !== undefined) {
|
|
6
|
+
aggregated[current] = data[current];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return aggregated;
|
|
10
|
+
}, {});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const tryParseStringIntoCorrectData = (data: string): any => {
|
|
14
|
+
if (!data?.length) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
// Parse boolean
|
|
20
|
+
if (data === "true") {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (data === "false") {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Parse number
|
|
29
|
+
const dataAsNumber = Number(data);
|
|
30
|
+
|
|
31
|
+
if (!Number.isNaN(dataAsNumber)) {
|
|
32
|
+
return dataAsNumber;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Parse object
|
|
36
|
+
const parsedObject = JSON.parse(data);
|
|
37
|
+
|
|
38
|
+
return parsedObject;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
return data;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CacheService, IPaginatedDataCache } from "../interfaces";
|
|
2
|
+
import { PaginatedDataCache } from "../services/paginated-cache";
|
|
3
|
+
|
|
4
|
+
export class PaginatedCacheRegistry {
|
|
5
|
+
protected _registry = new Map<string, IPaginatedDataCache>();
|
|
6
|
+
|
|
7
|
+
constructor(protected cacheService: CacheService) {}
|
|
8
|
+
|
|
9
|
+
public has(dataName: string): boolean {
|
|
10
|
+
return this._registry.has(dataName);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public getOrCreatePaginatedCache<T = any>(
|
|
14
|
+
dataName: string
|
|
15
|
+
): IPaginatedDataCache<T> {
|
|
16
|
+
if (this._registry.has(dataName)) {
|
|
17
|
+
return this._registry.get(dataName);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const service = new PaginatedDataCache(dataName, this.cacheService);
|
|
21
|
+
this._registry.set(dataName, service);
|
|
22
|
+
|
|
23
|
+
return service;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { FILTER_OPERATOR } from "../constants";
|
|
2
|
+
import { IFilter, ISort } from "../interfaces";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Parses a sort data such as "-price name date" into an array of generic sort data
|
|
6
|
+
* @param data the sort string
|
|
7
|
+
* @returns ISort[]
|
|
8
|
+
*/
|
|
9
|
+
export function parseSort(data: string): ISort[] {
|
|
10
|
+
if (!data) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const sortConditions: ISort[] = data.split(" ").map((item) => {
|
|
15
|
+
if (item[0] === "-") {
|
|
16
|
+
return {
|
|
17
|
+
columnName: item.slice(1, item.length - 1),
|
|
18
|
+
direction: "DESC",
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
columnName: item,
|
|
24
|
+
direction: "ASC",
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return sortConditions;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parses filter expression for a field name
|
|
33
|
+
* @param field The fieldname in query string ?email=
|
|
34
|
+
* @param filterData The filter expression. Ex: eq:123 lt:100 gt:400
|
|
35
|
+
* @returns IFilter
|
|
36
|
+
*/
|
|
37
|
+
export function parseFilter(field: string, filterData: string): IFilter {
|
|
38
|
+
if (!filterData) {
|
|
39
|
+
throw new Error("missing filter data");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const splittedData = filterData.split(":");
|
|
43
|
+
if (splittedData.length === 1) {
|
|
44
|
+
return {
|
|
45
|
+
field,
|
|
46
|
+
operator: FILTER_OPERATOR.EQUAL,
|
|
47
|
+
value: splittedData[0],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (splittedData.length === 2) {
|
|
52
|
+
const [operator, value] = splittedData;
|
|
53
|
+
|
|
54
|
+
if (!Object.keys(FILTER_OPERATOR).includes(operator)) {
|
|
55
|
+
throw new Error(`invalid filter operator ${operator}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (operator === FILTER_OPERATOR.OR || operator === FILTER_OPERATOR.AND) {
|
|
59
|
+
throw new Error("operator $or and $and are only for internal use");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (
|
|
63
|
+
operator === FILTER_OPERATOR.IN ||
|
|
64
|
+
operator === FILTER_OPERATOR.NOT_IN
|
|
65
|
+
) {
|
|
66
|
+
return {
|
|
67
|
+
field,
|
|
68
|
+
operator: operator as FILTER_OPERATOR,
|
|
69
|
+
value: value.split(","),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
field,
|
|
75
|
+
operator: operator as FILTER_OPERATOR,
|
|
76
|
+
value,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|