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,165 @@
|
|
|
1
|
+
import { faker } from "@faker-js/faker";
|
|
2
|
+
|
|
3
|
+
import { stringUtils } from ".";
|
|
4
|
+
|
|
5
|
+
interface UserConfig {
|
|
6
|
+
rootRefCode?: string;
|
|
7
|
+
password: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface User {
|
|
11
|
+
parentRefCode: string;
|
|
12
|
+
refCode: string;
|
|
13
|
+
password: string;
|
|
14
|
+
email: string;
|
|
15
|
+
children: User[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface NormalizedUser {
|
|
19
|
+
parentRefCode: string;
|
|
20
|
+
refCode: string;
|
|
21
|
+
password: string;
|
|
22
|
+
email: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ReferralTreeConfig {
|
|
26
|
+
noOfLevels: number;
|
|
27
|
+
minChildPerNode: number;
|
|
28
|
+
maxChildPerNode: number;
|
|
29
|
+
mustHaveChildPerNode: number;
|
|
30
|
+
userConfig: UserConfig;
|
|
31
|
+
passwordHasher?: (password: string) => Promise<string>;
|
|
32
|
+
refCodeGenerator?: () => string;
|
|
33
|
+
addAdditionalData?: () => Promise<Record<string, any>>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const defaultRefcodeGenerator = () =>
|
|
37
|
+
stringUtils.generateRandomId().slice(10).toUpperCase();
|
|
38
|
+
|
|
39
|
+
export const generateReferralTree = async (data: ReferralTreeConfig) => {
|
|
40
|
+
const {
|
|
41
|
+
maxChildPerNode,
|
|
42
|
+
minChildPerNode,
|
|
43
|
+
mustHaveChildPerNode,
|
|
44
|
+
noOfLevels,
|
|
45
|
+
userConfig,
|
|
46
|
+
passwordHasher,
|
|
47
|
+
refCodeGenerator,
|
|
48
|
+
addAdditionalData = () => {},
|
|
49
|
+
} = data;
|
|
50
|
+
|
|
51
|
+
const hashedPassword = await passwordHasher(userConfig.password);
|
|
52
|
+
|
|
53
|
+
const rootUser = await genNewUser(null, {
|
|
54
|
+
hashedPassword,
|
|
55
|
+
additionalDataGenerator: addAdditionalData,
|
|
56
|
+
refCodeGenerator: refCodeGenerator || defaultRefcodeGenerator,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const totalUsersToInsert: NormalizedUser[] = [normalizeUser(rootUser)];
|
|
60
|
+
|
|
61
|
+
let currentLayerUsers: User[] = [rootUser];
|
|
62
|
+
let totalUsersCount = currentLayerUsers.length;
|
|
63
|
+
|
|
64
|
+
for (let currentLevel = 0; currentLevel < noOfLevels; currentLevel++) {
|
|
65
|
+
const allChildUserCreated: User[] = [];
|
|
66
|
+
|
|
67
|
+
// Handle the node where the number of children is required
|
|
68
|
+
const nodeSelectedAsRequired = currentLayerUsers[0];
|
|
69
|
+
for (let i = 0; i < mustHaveChildPerNode; i++) {
|
|
70
|
+
const newUser = await genNewUser(nodeSelectedAsRequired.refCode, {
|
|
71
|
+
hashedPassword,
|
|
72
|
+
refCodeGenerator: refCodeGenerator || defaultRefcodeGenerator,
|
|
73
|
+
additionalDataGenerator: addAdditionalData,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
allChildUserCreated.push(newUser);
|
|
77
|
+
nodeSelectedAsRequired.children.push(newUser);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Handle the nodes where the number of children is within range.
|
|
81
|
+
// Exclude the first user as it's already processed
|
|
82
|
+
await Promise.all(
|
|
83
|
+
currentLayerUsers.slice(1, currentLayerUsers.length).map(async (user) => {
|
|
84
|
+
const noOfChildNodes = faker.helpers.rangeToNumber({
|
|
85
|
+
min: minChildPerNode,
|
|
86
|
+
max: maxChildPerNode,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
for (let i = 0; i < noOfChildNodes; i++) {
|
|
90
|
+
const newUser = await genNewUser(user.refCode, {
|
|
91
|
+
hashedPassword,
|
|
92
|
+
refCodeGenerator: refCodeGenerator || defaultRefcodeGenerator,
|
|
93
|
+
additionalDataGenerator: addAdditionalData,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
allChildUserCreated.push(newUser);
|
|
97
|
+
user.children.push(newUser);
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Move on to next layer
|
|
103
|
+
const normalizedUsers: Omit<User, "children">[] =
|
|
104
|
+
allChildUserCreated.map(normalizeUser);
|
|
105
|
+
|
|
106
|
+
totalUsersToInsert.push(...normalizedUsers);
|
|
107
|
+
currentLayerUsers = allChildUserCreated;
|
|
108
|
+
totalUsersCount += currentLayerUsers.length;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { rootUser, totalUsersCount, totalUsersToInsert };
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const normalizeUser = (user: User) => {
|
|
115
|
+
const cloned = JSON.parse(JSON.stringify(user));
|
|
116
|
+
delete cloned.children;
|
|
117
|
+
|
|
118
|
+
return cloned;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const genNewUser = async (
|
|
122
|
+
parentRefCode: string,
|
|
123
|
+
{ hashedPassword, refCodeGenerator, additionalDataGenerator }
|
|
124
|
+
): Promise<User> => {
|
|
125
|
+
const additionalData = await additionalDataGenerator();
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
parentRefCode,
|
|
129
|
+
password: hashedPassword,
|
|
130
|
+
email: faker.internet.email().toLowerCase(),
|
|
131
|
+
children: [],
|
|
132
|
+
refCode: refCodeGenerator(),
|
|
133
|
+
...additionalData,
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// generateReferralTree({
|
|
138
|
+
// maxChildPerNode: 2,
|
|
139
|
+
// minChildPerNode: 1,
|
|
140
|
+
// mustHaveChildPerNode: 5,
|
|
141
|
+
// noOfLevels: 5,
|
|
142
|
+
// passwordHasher: async (data) => data,
|
|
143
|
+
// addAdditionalData: async () => {
|
|
144
|
+
// return {
|
|
145
|
+
// fullName: faker.person.fullName(),
|
|
146
|
+
// };
|
|
147
|
+
// },
|
|
148
|
+
// userConfig: {
|
|
149
|
+
// password: "12345",
|
|
150
|
+
// rootRefCode: "ABCD1234",
|
|
151
|
+
// },
|
|
152
|
+
// })
|
|
153
|
+
// .then((data) => {
|
|
154
|
+
// console.log(data.totalUsersCount);
|
|
155
|
+
// fs.writeFileSync(
|
|
156
|
+
// path.resolve(__dirname, "referral_tree.json"),
|
|
157
|
+
// Buffer.from(JSON.stringify(data.rootUser))
|
|
158
|
+
// );
|
|
159
|
+
|
|
160
|
+
// fs.writeFileSync(
|
|
161
|
+
// path.resolve(__dirname, "referral_users.json"),
|
|
162
|
+
// Buffer.from(JSON.stringify(data.totalUsersToInsert))
|
|
163
|
+
// );
|
|
164
|
+
// })
|
|
165
|
+
// .catch((e) => console.error(e));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * as luaScripts from "./lua";
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export const IncrByAndSetTTLIfNotExists = `
|
|
2
|
+
if redis.call("EXISTS", KEYS[1]) == 0 then
|
|
3
|
+
redis.call("INCRBY", KEYS[1], ARGV[1])
|
|
4
|
+
redis.call("EXPIRE", KEYS[1], ARGV[2])
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
return redis.call("GET", KEYS[1])
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
export const IncrByAndEnsureTTLIsSet = `
|
|
11
|
+
local inc = tonumber(ARGV[1])
|
|
12
|
+
local ttl = tonumber(ARGV[2])
|
|
13
|
+
|
|
14
|
+
redis.call("INCRBY", KEYS[1], inc)
|
|
15
|
+
|
|
16
|
+
if redis.call("TTL", KEYS[1]) == -1 then
|
|
17
|
+
redis.call("EXPIRE", KEYS[1], ttl)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
return tonumber(redis.call("GET", KEYS[1]))
|
|
21
|
+
`;
|
|
22
|
+
export const DecrByAndEnsureTTLIsSet = `
|
|
23
|
+
local inc = tonumber(ARGV[1])
|
|
24
|
+
local ttl = tonumber(ARGV[2])
|
|
25
|
+
|
|
26
|
+
redis.call("DECRBY", KEYS[1], inc)
|
|
27
|
+
|
|
28
|
+
if redis.call("TTL", KEYS[1]) == -1 then
|
|
29
|
+
redis.call("EXPIRE", KEYS[1], ttl)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
return tonumber(redis.call("GET", KEYS[1]))
|
|
33
|
+
`;
|
|
34
|
+
export const IncrByFloatAndEnsureTTLIsSet = `
|
|
35
|
+
local inc = tonumber(ARGV[1])
|
|
36
|
+
local ttl = tonumber(ARGV[2])
|
|
37
|
+
|
|
38
|
+
redis.call("INCRBYFLOAT", KEYS[1], inc)
|
|
39
|
+
|
|
40
|
+
if redis.call("TTL", KEYS[1]) == -1 then
|
|
41
|
+
redis.call("EXPIRE", KEYS[1], ttl)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
return tonumber(redis.call("GET", KEYS[1]))
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
export const IncrByIfExists = `
|
|
48
|
+
if redis.call("EXISTS", KEYS[1]) == 1 then
|
|
49
|
+
redis.call("INCRBY", KEYS[1], ARGV[1])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
return redis.call("GET", KEYS[1])
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
export const DecrbyAndSetTTLIfNotExists = `
|
|
56
|
+
if redis.call("EXISTS", KEYS[1]) == 0 then
|
|
57
|
+
redis.call("DECRBY", KEYS[1], ARGV[1])
|
|
58
|
+
redis.call("EXPIRE", KEYS[1], ARGV[2])
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
return redis.call("GET", KEYS[1])
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
export const DecrbyIfExists = `
|
|
65
|
+
if redis.call("EXISTS", KEYS[1]) == 1 then
|
|
66
|
+
redis.call("DECRBY", KEYS[1], ARGV[1])
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
return redis.call("GET", KEYS[1])
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
export const IncrByFloatAndSetTTLIfNotExists = `
|
|
73
|
+
if redis.call("EXISTS", KEYS[1]) == 0 then
|
|
74
|
+
redis.call("INCRBYFLOAT", KEYS[1], ARGV[1])
|
|
75
|
+
redis.call("EXPIRE", KEYS[1], ARGV[2])
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
return redis.call("GET", KEYS[1])
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
export const IncrByFloatIfExists = `
|
|
82
|
+
if redis.call("EXISTS", KEYS[1]) == 1 then
|
|
83
|
+
redis.call("INCRBYFLOAT", KEYS[1], ARGV[1])
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
return redis.call("GET", KEYS[1])
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
export const RefreshTTLIfBelowThreshold = `
|
|
90
|
+
-- KEYS[1] : key to inspect
|
|
91
|
+
-- ARGV[1] : threshold in seconds (e.g. 10)
|
|
92
|
+
-- ARGV[2] : new TTL in seconds to set (e.g. 60)
|
|
93
|
+
|
|
94
|
+
local ttl = redis.call("TTL", KEYS[1])
|
|
95
|
+
|
|
96
|
+
-- ttl == -2 → key doesn’t exist
|
|
97
|
+
-- ttl == -1 → key has no expiration
|
|
98
|
+
if ttl == -2 then
|
|
99
|
+
return -2 -- key absent
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
local threshold = tonumber(ARGV[1])
|
|
103
|
+
local newTTL = tonumber(ARGV[2])
|
|
104
|
+
|
|
105
|
+
-- If TTL is missing (-1) or below threshold, refresh it
|
|
106
|
+
if ttl == -1 or ttl < threshold then
|
|
107
|
+
redis.call("EXPIRE", KEYS[1], newTTL)
|
|
108
|
+
return newTTL -- return the TTL we just set
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
return ttl -- unchanged TTL
|
|
112
|
+
`;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ISort, SORT_DIRECTION } from "../interfaces";
|
|
2
|
+
|
|
3
|
+
export const toMongooseSort = (sorts: ISort[]): Record<string, number> => {
|
|
4
|
+
return sorts.reduce((agg, current) => {
|
|
5
|
+
agg[current.columnName] = current.direction === "ASC" ? 1 : -1;
|
|
6
|
+
|
|
7
|
+
return agg;
|
|
8
|
+
}, {});
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const toTypeOrmSort = (
|
|
12
|
+
sorts: ISort[]
|
|
13
|
+
): Record<string, SORT_DIRECTION> => {
|
|
14
|
+
return sorts.reduce((agg, current) => {
|
|
15
|
+
agg[current.columnName] = current.direction;
|
|
16
|
+
|
|
17
|
+
return agg;
|
|
18
|
+
}, {});
|
|
19
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { createId } from "@paralleldrive/cuid2";
|
|
2
|
+
|
|
3
|
+
export function generateRandomId(): string {
|
|
4
|
+
return createId();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function generatePassword(length = 16): string {
|
|
8
|
+
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
9
|
+
const numbers = "0123456789";
|
|
10
|
+
const symbols = "!@#$%^&*(){}|+-";
|
|
11
|
+
const all = chars + numbers + symbols;
|
|
12
|
+
|
|
13
|
+
let retVal = "";
|
|
14
|
+
retVal += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
15
|
+
retVal += numbers.charAt(Math.floor(Math.random() * numbers.length));
|
|
16
|
+
retVal += symbols.charAt(Math.floor(Math.random() * symbols.length));
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < length - 3; ++i) {
|
|
19
|
+
retVal += all.charAt(Math.floor(Math.random() * all.length));
|
|
20
|
+
}
|
|
21
|
+
return retVal;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function escapeRegexCharacters(str: string): string {
|
|
25
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function validatePasswordStrengthWithMessage(password: string): string {
|
|
29
|
+
const escapedPassword = escapeRegexCharacters(password);
|
|
30
|
+
|
|
31
|
+
if (escapedPassword.length < 8) {
|
|
32
|
+
return "Password must be at least 8 characters long";
|
|
33
|
+
}
|
|
34
|
+
if (!escapedPassword.match(/[a-z]/)) {
|
|
35
|
+
return "Password must contain at least one lowercase letter";
|
|
36
|
+
}
|
|
37
|
+
if (!escapedPassword.match(/[A-Z]/)) {
|
|
38
|
+
return "Password must contain at least one uppercase letter";
|
|
39
|
+
}
|
|
40
|
+
if (!escapedPassword.match(/[0-9]/)) {
|
|
41
|
+
return "Password must contain at least one number";
|
|
42
|
+
}
|
|
43
|
+
if (!escapedPassword.match(/[^a-zA-Z0-9]/)) {
|
|
44
|
+
return "Password must contain at least one special character";
|
|
45
|
+
}
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const standardlize = (f1: string) =>
|
|
50
|
+
f1.replace(new RegExp("_", "g"), "").toLowerCase();
|
|
51
|
+
|
|
52
|
+
type MaskRule = {
|
|
53
|
+
keys: string[];
|
|
54
|
+
pattern: RegExp;
|
|
55
|
+
replacer: (...groups: string[]) => string;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const maskRules: MaskRule[] = [
|
|
59
|
+
{
|
|
60
|
+
keys: [
|
|
61
|
+
"password",
|
|
62
|
+
"Authorization",
|
|
63
|
+
"access_token",
|
|
64
|
+
"refresh_token",
|
|
65
|
+
"signature",
|
|
66
|
+
],
|
|
67
|
+
pattern: /^(.*)$/,
|
|
68
|
+
replacer: () => "***masked***",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
keys: ["txid", "txnid"],
|
|
72
|
+
pattern: /^(.{10})(.*)(.{36})$/,
|
|
73
|
+
replacer: (_match, prefix, middle, suffix) =>
|
|
74
|
+
`${prefix}${"*".repeat(middle.length)}${suffix}`,
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
export const maskFn = (
|
|
79
|
+
key: string,
|
|
80
|
+
value: unknown,
|
|
81
|
+
additionalMaskRules: MaskRule[] = []
|
|
82
|
+
): unknown => {
|
|
83
|
+
// Only process string values
|
|
84
|
+
if (typeof value !== "string") {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const normalizedKey = standardlize(key);
|
|
89
|
+
// Merge default rules with any additional ones
|
|
90
|
+
const allRules = [...maskRules, ...additionalMaskRules];
|
|
91
|
+
|
|
92
|
+
// Find a rule whose keys match the normalized key
|
|
93
|
+
const rule = allRules.find(({ keys }) =>
|
|
94
|
+
keys.some((k) => standardlize(k) === normalizedKey)
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// No matching rule? Return the original value
|
|
98
|
+
if (!rule) {
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Apply pattern and replacer
|
|
103
|
+
return value.replace(rule.pattern, rule.replacer);
|
|
104
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { TaskFn } from "../interfaces";
|
|
2
|
+
|
|
3
|
+
export async function runWithTimeout<T = any>(
|
|
4
|
+
handler: TaskFn,
|
|
5
|
+
timeoutInMs: number
|
|
6
|
+
): Promise<T> {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const timeout = setTimeout(
|
|
9
|
+
() => reject(new Error("task timed out")),
|
|
10
|
+
timeoutInMs
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
handler()
|
|
14
|
+
.then((result: T) => {
|
|
15
|
+
clearTimeout(timeout);
|
|
16
|
+
|
|
17
|
+
resolve(result);
|
|
18
|
+
})
|
|
19
|
+
.catch((error: Error) => {
|
|
20
|
+
clearTimeout(timeout);
|
|
21
|
+
|
|
22
|
+
reject(error);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// test/normalizePairName.spec.ts
|
|
2
|
+
import { describe, it } from "mocha";
|
|
3
|
+
import { expect } from "chai";
|
|
4
|
+
import { normalizePairName } from "./trading-pair-helper";
|
|
5
|
+
import { CRYPTO_TOKEN, STABLE_COIN } from "../constants";
|
|
6
|
+
|
|
7
|
+
describe("normalizePairName (functional factory)", () => {
|
|
8
|
+
const normalize = normalizePairName(STABLE_COIN, CRYPTO_TOKEN);
|
|
9
|
+
|
|
10
|
+
it("places stable coin last when stableCoinRule = 'last' (order-agnostic input)", () => {
|
|
11
|
+
const a = normalize("USDTWVPC", {
|
|
12
|
+
stableCoinRule: "last",
|
|
13
|
+
sortOrder: "asc",
|
|
14
|
+
separator: "/",
|
|
15
|
+
outputFormater: undefined as any,
|
|
16
|
+
});
|
|
17
|
+
const b = normalize("WVPCUSDT", {
|
|
18
|
+
stableCoinRule: "last",
|
|
19
|
+
sortOrder: "asc",
|
|
20
|
+
separator: "/",
|
|
21
|
+
outputFormater: undefined as any,
|
|
22
|
+
});
|
|
23
|
+
expect(a).to.equal("wvpc/usdt");
|
|
24
|
+
expect(b).to.equal("wvpc/usdt");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("places stable coin first when stableCoinRule = 'first'", () => {
|
|
28
|
+
const out = normalize("WVPCUSDC", {
|
|
29
|
+
stableCoinRule: "first",
|
|
30
|
+
sortOrder: "asc",
|
|
31
|
+
separator: "/",
|
|
32
|
+
outputFormater: undefined as any,
|
|
33
|
+
});
|
|
34
|
+
expect(out).to.equal("usdc/wvpc");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("with stableCoinRule = 'default', uses general sort (no special placement)", () => {
|
|
38
|
+
const outAsc = normalize("WVPCUSDT", {
|
|
39
|
+
stableCoinRule: "default",
|
|
40
|
+
sortOrder: "asc",
|
|
41
|
+
separator: "/",
|
|
42
|
+
outputFormater: undefined as any,
|
|
43
|
+
});
|
|
44
|
+
const outDesc = normalize("WVPCUSDT", {
|
|
45
|
+
stableCoinRule: "default",
|
|
46
|
+
sortOrder: "desc",
|
|
47
|
+
separator: "/",
|
|
48
|
+
outputFormater: undefined as any,
|
|
49
|
+
});
|
|
50
|
+
// asc: 'usdt' < 'wvpc' lexicographically
|
|
51
|
+
expect(outAsc).to.equal("usdt/wvpc");
|
|
52
|
+
// desc: 'wvpc' > 'usdt'
|
|
53
|
+
expect(outDesc).to.equal("wvpc/usdt");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("strips separators and is case-insensitive", () => {
|
|
57
|
+
const a = normalize("ETH/USDT", {
|
|
58
|
+
stableCoinRule: "last",
|
|
59
|
+
sortOrder: "asc",
|
|
60
|
+
separator: "/",
|
|
61
|
+
outputFormater: undefined as any,
|
|
62
|
+
});
|
|
63
|
+
const b = normalize("ETH|USDT", {
|
|
64
|
+
stableCoinRule: "last",
|
|
65
|
+
sortOrder: "asc",
|
|
66
|
+
separator: "/",
|
|
67
|
+
outputFormater: undefined as any,
|
|
68
|
+
});
|
|
69
|
+
const c = normalize("wVpCusdT", {
|
|
70
|
+
stableCoinRule: "last",
|
|
71
|
+
sortOrder: "asc",
|
|
72
|
+
separator: "/",
|
|
73
|
+
outputFormater: undefined as any,
|
|
74
|
+
});
|
|
75
|
+
expect(a).to.equal("eth/usdt");
|
|
76
|
+
expect(b).to.equal("eth/usdt");
|
|
77
|
+
expect(c).to.equal("wvpc/usdt");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("handles overlapping tokens by preferring longest match (e.g., WETH vs ETH)", () => {
|
|
81
|
+
// No stable coin here, so general path + sorting
|
|
82
|
+
const outAsc = normalize("WETHETH", {
|
|
83
|
+
stableCoinRule: "default",
|
|
84
|
+
sortOrder: "asc",
|
|
85
|
+
separator: "/",
|
|
86
|
+
outputFormater: undefined as any,
|
|
87
|
+
});
|
|
88
|
+
const outDesc = normalize("WETHETH", {
|
|
89
|
+
stableCoinRule: "default",
|
|
90
|
+
sortOrder: "desc",
|
|
91
|
+
separator: "/",
|
|
92
|
+
outputFormater: undefined as any,
|
|
93
|
+
});
|
|
94
|
+
// After longest-first split → parts = ['weth','eth']; then sorted:
|
|
95
|
+
expect(outAsc).to.equal("eth/weth");
|
|
96
|
+
expect(outDesc).to.equal("weth/eth");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("sorts non-stable pairs lexicographically based on sortOrder", () => {
|
|
100
|
+
const outAsc = normalize("BTCETH", {
|
|
101
|
+
stableCoinRule: "default",
|
|
102
|
+
sortOrder: "asc",
|
|
103
|
+
separator: "/",
|
|
104
|
+
outputFormater: undefined as any,
|
|
105
|
+
});
|
|
106
|
+
const outDesc = normalize("BTCETH", {
|
|
107
|
+
stableCoinRule: "default",
|
|
108
|
+
sortOrder: "desc",
|
|
109
|
+
separator: "/",
|
|
110
|
+
outputFormater: undefined as any,
|
|
111
|
+
});
|
|
112
|
+
expect(outAsc).to.equal("btc/eth");
|
|
113
|
+
expect(outDesc).to.equal("eth/btc");
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("passes the separator into the output formatter", () => {
|
|
117
|
+
const out = normalize("WVPCUSDT", {
|
|
118
|
+
stableCoinRule: "last",
|
|
119
|
+
sortOrder: "asc",
|
|
120
|
+
separator: "-",
|
|
121
|
+
outputFormater: (parts, sep) => parts.join(sep),
|
|
122
|
+
});
|
|
123
|
+
expect(out).to.equal("wvpc-usdt");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("supports custom formatter (e.g., uppercase concatenation)", () => {
|
|
127
|
+
const out = normalize("WVPCUSDT", {
|
|
128
|
+
stableCoinRule: "last",
|
|
129
|
+
sortOrder: "asc",
|
|
130
|
+
separator: "/",
|
|
131
|
+
outputFormater: (parts) => parts.map((p) => p.toUpperCase()).join(""),
|
|
132
|
+
});
|
|
133
|
+
expect(out).to.equal("WVPCUSDT");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("throws when no known tokens found", () => {
|
|
137
|
+
expect(() =>
|
|
138
|
+
normalize("FOOBAR", {
|
|
139
|
+
stableCoinRule: "default",
|
|
140
|
+
sortOrder: "asc",
|
|
141
|
+
separator: "/",
|
|
142
|
+
outputFormater: undefined as any,
|
|
143
|
+
})
|
|
144
|
+
).to.throw(/does not contain crypto from list/i);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { CRYPTO_TOKEN, STABLE_COIN } from "../constants";
|
|
2
|
+
|
|
3
|
+
const esc = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4
|
+
|
|
5
|
+
export type PairNameFormater = (data: string[], separator: string) => string;
|
|
6
|
+
|
|
7
|
+
const defaultFormatter: PairNameFormater = (data, separator) =>
|
|
8
|
+
data.join(separator);
|
|
9
|
+
|
|
10
|
+
export type NormalizePairNameOptions = {
|
|
11
|
+
sortOrder?: "asc" | "desc";
|
|
12
|
+
stableCoinRule?: "first" | "last" | "default";
|
|
13
|
+
separator?: string;
|
|
14
|
+
outputFormater?: PairNameFormater;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const normalizePairName = (
|
|
18
|
+
stableCoinList = STABLE_COIN,
|
|
19
|
+
cryptoList = CRYPTO_TOKEN
|
|
20
|
+
) => {
|
|
21
|
+
const stableTokensLower = Object.values(stableCoinList).map((i) =>
|
|
22
|
+
i.toLowerCase()
|
|
23
|
+
);
|
|
24
|
+
const stableRe = new RegExp(`(${stableTokensLower.map(esc).join("|")})`, "i");
|
|
25
|
+
|
|
26
|
+
// Keep both raw-lower (for includes) and escaped (for regex)
|
|
27
|
+
const cryptoTokensLower = Object.values(cryptoList)
|
|
28
|
+
.map((i) => i.toLowerCase())
|
|
29
|
+
.sort((a, b) => b.length - a.length); // longest-first to avoid overlaps
|
|
30
|
+
|
|
31
|
+
const cryptoTokensEscaped = cryptoTokensLower.map(esc);
|
|
32
|
+
|
|
33
|
+
return (pairName: string, options?: NormalizePairNameOptions): string => {
|
|
34
|
+
const {
|
|
35
|
+
outputFormater = defaultFormatter,
|
|
36
|
+
sortOrder = "asc",
|
|
37
|
+
stableCoinRule = "last",
|
|
38
|
+
separator = "/",
|
|
39
|
+
} = options || {};
|
|
40
|
+
|
|
41
|
+
// strip separators and normalize case once
|
|
42
|
+
const normalized = pairName.replace(/[|/]/g, "").toLowerCase();
|
|
43
|
+
const parts: string[] = [];
|
|
44
|
+
|
|
45
|
+
// Stable-coin rule branch
|
|
46
|
+
const hasStable = stableTokensLower.some((t) => normalized.includes(t));
|
|
47
|
+
if (stableCoinRule !== "default" && hasStable) {
|
|
48
|
+
const other = normalized.replace(stableRe, ""); // remove first stable
|
|
49
|
+
const stable = normalized.replace(other, ""); // remainder is the stable
|
|
50
|
+
const ordered =
|
|
51
|
+
stableCoinRule === "first" ? [stable, other] : [other, stable];
|
|
52
|
+
|
|
53
|
+
return outputFormater(ordered, separator);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// General branch
|
|
57
|
+
for (let i = 0; i < cryptoTokensLower.length; i++) {
|
|
58
|
+
const raw = cryptoTokensLower[i];
|
|
59
|
+
if (normalized.includes(raw)) {
|
|
60
|
+
const pat = new RegExp(cryptoTokensEscaped[i], "i");
|
|
61
|
+
const remainder = normalized.replace(pat, "");
|
|
62
|
+
parts.push(raw, remainder);
|
|
63
|
+
break; // stop at first/best match
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (parts.length === 0) {
|
|
68
|
+
throw new Error("pairname does not contain crypto from list");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const ordered =
|
|
72
|
+
sortOrder === "asc"
|
|
73
|
+
? parts.sort((a, b) => a.localeCompare(b))
|
|
74
|
+
: parts.sort((a, b) => b.localeCompare(a));
|
|
75
|
+
|
|
76
|
+
return outputFormater(ordered, separator);
|
|
77
|
+
};
|
|
78
|
+
};
|