dymo-api 1.2.36 → 1.2.38
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/cjs/dymo-api.cjs +954 -786
- package/dist/esm/dymo-api.js +954 -786
- package/dist/types/branches/private/functions/extractWithTextly/index.d.ts +8 -11
- package/dist/types/branches/private/functions/extractWithTextly/index.d.ts.map +1 -1
- package/dist/types/branches/private/functions/getRandom/index.d.ts +8 -11
- package/dist/types/branches/private/functions/getRandom/index.d.ts.map +1 -1
- package/dist/types/branches/private/functions/isValidDataRaw/index.d.ts +8 -10
- package/dist/types/branches/private/functions/isValidDataRaw/index.d.ts.map +1 -1
- package/dist/types/branches/private/functions/isValidEmail/index.d.ts +9 -15
- package/dist/types/branches/private/functions/isValidEmail/index.d.ts.map +1 -1
- package/dist/types/branches/private/functions/isValidIP/index.d.ts +10 -15
- package/dist/types/branches/private/functions/isValidIP/index.d.ts.map +1 -1
- package/dist/types/branches/private/functions/isValidPhone/index.d.ts +10 -15
- package/dist/types/branches/private/functions/isValidPhone/index.d.ts.map +1 -1
- package/dist/types/branches/private/functions/protectReq/index.d.ts +12 -1
- package/dist/types/branches/private/functions/protectReq/index.d.ts.map +1 -1
- package/dist/types/branches/private/functions/sendEmail/index.d.ts +10 -13
- package/dist/types/branches/private/functions/sendEmail/index.d.ts.map +1 -1
- package/dist/types/branches/public/functions/getPrayerTimes/index.d.ts +8 -12
- package/dist/types/branches/public/functions/getPrayerTimes/index.d.ts.map +1 -1
- package/dist/types/branches/public/functions/isValidPwd/index.d.ts +9 -24
- package/dist/types/branches/public/functions/isValidPwd/index.d.ts.map +1 -1
- package/dist/types/branches/public/functions/satinize/index.d.ts +9 -8
- package/dist/types/branches/public/functions/satinize/index.d.ts.map +1 -1
- package/dist/types/dymo-api.d.ts +22 -24
- package/dist/types/dymo-api.d.ts.map +1 -1
- package/dist/types/lib/types/data-verifier.d.ts +7 -1
- package/dist/types/lib/types/data-verifier.d.ts.map +1 -1
- package/package.json +7 -4
package/dist/cjs/dymo-api.cjs
CHANGED
|
@@ -16,807 +16,1011 @@ const validBaseURL = (baseUrl) => {
|
|
|
16
16
|
if (/^(https:\/\/api\.tpeoficial\.com$|http:\/\/(localhost:\d+|dymoapi:\d+))$/.test(baseUrl)) return baseUrl;
|
|
17
17
|
else throw new Error("[Dymo API] Invalid URL. It must be https://api.tpeoficial.com or start with http://localhost or http://dymoapi followed by a port.");
|
|
18
18
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
class FallbackDataGenerator {
|
|
20
|
+
static generateFallbackData(method, inputData) {
|
|
21
|
+
switch (method) {
|
|
22
|
+
case "isValidData":
|
|
23
|
+
case "isValidDataRaw":
|
|
24
|
+
return this.generateDataValidationAnalysis(inputData);
|
|
25
|
+
case "isValidEmail":
|
|
26
|
+
return this.generateEmailValidatorResponse(inputData);
|
|
27
|
+
case "isValidIP":
|
|
28
|
+
return this.generateIPValidatorResponse(inputData);
|
|
29
|
+
case "isValidPhone":
|
|
30
|
+
return this.generatePhoneValidatorResponse(inputData);
|
|
31
|
+
case "protectReq":
|
|
32
|
+
return this.generateHTTPRequest(inputData);
|
|
33
|
+
case "sendEmail":
|
|
34
|
+
return this.generateEmailStatus();
|
|
35
|
+
case "getRandom":
|
|
36
|
+
return this.generateSRNSummary(inputData);
|
|
37
|
+
case "extractWithTextly":
|
|
38
|
+
return this.generateExtractWithTextly(inputData);
|
|
39
|
+
case "getPrayerTimes":
|
|
40
|
+
return this.generatePrayerTimes(inputData);
|
|
41
|
+
case "satinize":
|
|
42
|
+
case "satinizer":
|
|
43
|
+
return this.generateSatinizedInputAnalysis(inputData);
|
|
44
|
+
case "isValidPwd":
|
|
45
|
+
return this.generatePasswordValidationResult(inputData);
|
|
46
|
+
default:
|
|
47
|
+
throw new Error(`Unknown method for fallback: ${method}`);
|
|
48
|
+
}
|
|
27
49
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const params = { password: encodeURIComponent(password) };
|
|
33
|
-
if (email) {
|
|
34
|
-
if (!/^[a-zA-Z0-9._\-+]+@?[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/.test(email)) throw customError(1500, "If you provide an email address it must be valid.");
|
|
35
|
-
params.email = encodeURIComponent(email);
|
|
50
|
+
static validateURL(url) {
|
|
51
|
+
if (!url) return false;
|
|
52
|
+
const urlRegex = /^https?:\/\/(?:[-\w.])+(?:\:[0-9]+)?(?:\/(?:[\w\/_.])*(?:\?(?:[\w&=%.])*)?(?:\#(?:[\w.])*)?)?$/;
|
|
53
|
+
return urlRegex.test(url);
|
|
36
54
|
}
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
params.bannedWords = bannedWords;
|
|
55
|
+
static validateEmail(email) {
|
|
56
|
+
if (!email) return false;
|
|
57
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
58
|
+
return emailRegex.test(email);
|
|
42
59
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return response.data;
|
|
50
|
-
} catch (error) {
|
|
51
|
-
throw customError(5e3, error.response?.data?.message || error.message);
|
|
60
|
+
static validateDomain(domain) {
|
|
61
|
+
if (!domain) return false;
|
|
62
|
+
const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](?:\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])*$/;
|
|
63
|
+
if (!domainRegex.test(domain)) return false;
|
|
64
|
+
const parts = domain.split(".");
|
|
65
|
+
return parts.length >= 2 && parts[parts.length - 1].length > 0;
|
|
52
66
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
return
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
static validateCreditCard(creditCard) {
|
|
68
|
+
if (!creditCard) return false;
|
|
69
|
+
const cardNumber = typeof creditCard === "string" ? creditCard : creditCard?.pan || "";
|
|
70
|
+
if (!cardNumber) return false;
|
|
71
|
+
const cardRegex = /^\d{13,19}$/;
|
|
72
|
+
if (!cardRegex.test(cardNumber.replace(/\s/g, ""))) return false;
|
|
73
|
+
const digits = cardNumber.replace(/\s/g, "").split("").map(Number);
|
|
74
|
+
let sum = 0;
|
|
75
|
+
let isEven = false;
|
|
76
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
77
|
+
let digit = digits[i];
|
|
78
|
+
if (isEven) {
|
|
79
|
+
digit *= 2;
|
|
80
|
+
if (digit > 9) digit -= 9;
|
|
81
|
+
}
|
|
82
|
+
sum += digit;
|
|
83
|
+
isEven = !isEven;
|
|
84
|
+
}
|
|
85
|
+
return sum % 10 === 0;
|
|
61
86
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
const response = await axiosClient.post("/private/textly/extract", data, { headers: { "Content-Type": "application/json" } });
|
|
69
|
-
return response.data;
|
|
70
|
-
} catch (error) {
|
|
71
|
-
throw customError(5e3, error.response?.data?.message || error.message);
|
|
87
|
+
static validateIP(ip) {
|
|
88
|
+
if (!ip) return false;
|
|
89
|
+
const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
|
90
|
+
const ipv6Regex = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
|
|
91
|
+
return ipv4Regex.test(ip) || ipv6Regex.test(ip);
|
|
72
92
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (data.min >= data.max) throw customError(1500, "'min' must be less than 'max'.");
|
|
78
|
-
if (data.min < -1e9 || data.min > 1e9) throw customError(1500, "'min' must be an integer in the interval [-1000000000}, 1000000000].");
|
|
79
|
-
if (data.max < -1e9 || data.max > 1e9) throw customError(1500, "'max' must be an integer in the interval [-1000000000}, 1000000000].");
|
|
80
|
-
try {
|
|
81
|
-
const response = await axiosClient.post("/private/srng", data, { headers: { "Content-Type": "application/json" } });
|
|
82
|
-
return response.data;
|
|
83
|
-
} catch (error) {
|
|
84
|
-
throw customError(5e3, error.response?.data?.message || error.message);
|
|
93
|
+
static validatePhone(phone) {
|
|
94
|
+
if (!phone) return false;
|
|
95
|
+
const phoneRegex = /^\+?[1-9]\d{1,14}$/;
|
|
96
|
+
return phoneRegex.test(phone.replace(/[^\d+]/g, ""));
|
|
85
97
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
console.warn("[Dymo API] DRY_RUN mode is enabled. No requests with real data will be processed until you switch to LIVE mode.");
|
|
92
|
-
return {
|
|
93
|
-
ip,
|
|
94
|
-
allow: true,
|
|
95
|
-
reasons: [],
|
|
96
|
-
response: "CHANGE TO LIVE MODE"
|
|
97
|
-
};
|
|
98
|
+
static validateWallet(wallet) {
|
|
99
|
+
if (!wallet) return false;
|
|
100
|
+
const bitcoinRegex = /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/;
|
|
101
|
+
const ethereumRegex = /^0x[a-fA-F0-9]{40}$/;
|
|
102
|
+
return bitcoinRegex.test(wallet) || ethereumRegex.test(wallet);
|
|
98
103
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return
|
|
110
|
-
ip: responseIP.ip,
|
|
111
|
-
allow: false,
|
|
112
|
-
reasons: ["INVALID"],
|
|
113
|
-
response: responseIP
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
if (rules.deny.includes("FRAUD") && responseIP.fraud) reasons.push("FRAUD");
|
|
117
|
-
if (rules.deny.includes("TOR_NETWORK") && responseIP.plugins.torNetwork) reasons.push("TOR_NETWORK");
|
|
118
|
-
if (rules.deny.includes("HIGH_RISK_SCORE") && responseIP.plugins.riskScore >= 80) reasons.push("HIGH_RISK_SCORE");
|
|
119
|
-
for (const rule of rules.deny) {
|
|
120
|
-
if (rule.startsWith("COUNTRY:")) {
|
|
121
|
-
const block = rule.split(":")[1];
|
|
122
|
-
if (responseIP.countryCode === block) reasons.push(`COUNTRY:${block}`);
|
|
123
|
-
}
|
|
104
|
+
static validateIBAN(iban) {
|
|
105
|
+
if (!iban) return false;
|
|
106
|
+
const ibanRegex = /^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$/;
|
|
107
|
+
return ibanRegex.test(iban.replace(/\s/g, "").toUpperCase());
|
|
108
|
+
}
|
|
109
|
+
static extractDomain(url) {
|
|
110
|
+
if (!url) return "";
|
|
111
|
+
try {
|
|
112
|
+
return new URL(url).hostname;
|
|
113
|
+
} catch {
|
|
114
|
+
return "";
|
|
124
115
|
}
|
|
125
|
-
return {
|
|
126
|
-
ip: responseIP.ip,
|
|
127
|
-
allow: reasons.length === 0,
|
|
128
|
-
reasons,
|
|
129
|
-
response: responseIP
|
|
130
|
-
};
|
|
131
|
-
} catch (error) {
|
|
132
|
-
const statusCode = error.response?.status || 500;
|
|
133
|
-
const errorMessage = error.response?.data?.message || error.message;
|
|
134
|
-
const errorDetails = JSON.stringify(error.response?.data || {});
|
|
135
|
-
throw customError(5e3, `Error ${statusCode}: ${errorMessage}. Details: ${errorDetails}`);
|
|
136
116
|
}
|
|
137
|
-
|
|
138
|
-
const isValidPhone = async (axiosClient, phone, rules) => {
|
|
139
|
-
if (!axiosClient.defaults.headers?.Authorization) throw customError(3e3, "Invalid private token.");
|
|
140
|
-
if (rules.deny.length === 0) throw customError(1500, "You must provide at least one deny rule.");
|
|
141
|
-
if (rules.mode === "DRY_RUN") {
|
|
142
|
-
console.warn("[Dymo API] DRY_RUN mode is enabled. No requests with real data will be processed until you switch to LIVE mode.");
|
|
117
|
+
static generateDataValidationAnalysis(inputData) {
|
|
143
118
|
return {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
119
|
+
url: {
|
|
120
|
+
valid: this.validateURL(inputData?.url),
|
|
121
|
+
fraud: false,
|
|
122
|
+
freeSubdomain: false,
|
|
123
|
+
customTLD: false,
|
|
124
|
+
url: inputData?.url || "",
|
|
125
|
+
domain: this.extractDomain(inputData?.url),
|
|
126
|
+
plugins: {
|
|
127
|
+
blocklist: false,
|
|
128
|
+
compromiseDetector: false,
|
|
129
|
+
mxRecords: [],
|
|
130
|
+
nsfw: false,
|
|
131
|
+
reputation: "unknown",
|
|
132
|
+
riskScore: 0,
|
|
133
|
+
torNetwork: false,
|
|
134
|
+
typosquatting: 0,
|
|
135
|
+
urlShortener: false
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
email: this.generateEmailDataAnalysis(inputData?.email),
|
|
139
|
+
phone: this.generatePhoneDataAnalysis(inputData?.phone),
|
|
140
|
+
domain: {
|
|
141
|
+
valid: this.validateDomain(inputData?.domain),
|
|
142
|
+
fraud: false,
|
|
143
|
+
freeSubdomain: false,
|
|
144
|
+
customTLD: false,
|
|
145
|
+
domain: inputData?.domain || "",
|
|
146
|
+
plugins: {
|
|
147
|
+
blocklist: false,
|
|
148
|
+
compromiseDetector: false,
|
|
149
|
+
mxRecords: [],
|
|
150
|
+
nsfw: false,
|
|
151
|
+
reputation: "unknown",
|
|
152
|
+
riskScore: 0,
|
|
153
|
+
torNetwork: false,
|
|
154
|
+
typosquatting: 0,
|
|
155
|
+
urlShortener: false
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
creditCard: {
|
|
159
|
+
valid: this.validateCreditCard(inputData?.creditCard),
|
|
160
|
+
fraud: false,
|
|
161
|
+
test: false,
|
|
162
|
+
type: "unknown",
|
|
163
|
+
creditCard: typeof inputData?.creditCard === "string" ? inputData.creditCard : inputData?.creditCard?.pan || "",
|
|
164
|
+
plugins: {
|
|
165
|
+
blocklist: false,
|
|
166
|
+
riskScore: 0
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
ip: this.generateIPDataAnalysis(inputData?.ip),
|
|
170
|
+
wallet: {
|
|
171
|
+
valid: this.validateWallet(inputData?.wallet),
|
|
172
|
+
fraud: false,
|
|
173
|
+
wallet: inputData?.wallet || "",
|
|
174
|
+
type: "unknown",
|
|
175
|
+
plugins: {
|
|
176
|
+
blocklist: false,
|
|
177
|
+
riskScore: 0,
|
|
178
|
+
torNetwork: false
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
userAgent: {
|
|
182
|
+
valid: false,
|
|
183
|
+
fraud: false,
|
|
184
|
+
userAgent: inputData?.userAgent || "",
|
|
185
|
+
bot: true,
|
|
186
|
+
device: { type: "unknown", brand: "unknown" },
|
|
187
|
+
plugins: {
|
|
188
|
+
blocklist: false,
|
|
189
|
+
riskScore: 0
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
iban: {
|
|
193
|
+
valid: this.validateIBAN(inputData?.iban),
|
|
194
|
+
fraud: false,
|
|
195
|
+
iban: inputData?.iban || "",
|
|
196
|
+
plugins: {
|
|
197
|
+
blocklist: false,
|
|
198
|
+
riskScore: 0
|
|
199
|
+
}
|
|
200
|
+
}
|
|
148
201
|
};
|
|
149
202
|
}
|
|
150
|
-
|
|
151
|
-
const responsePhone = (await axiosClient.post("/private/secure/verify", {
|
|
152
|
-
phone,
|
|
153
|
-
plugins: [
|
|
154
|
-
rules.deny.includes("HIGH_RISK_SCORE") ? "riskScore" : void 0
|
|
155
|
-
].filter(Boolean)
|
|
156
|
-
}, { headers: { "Content-Type": "application/json" } })).data.phone;
|
|
157
|
-
let reasons = [];
|
|
158
|
-
if (rules.deny.includes("INVALID") && !responsePhone.valid) {
|
|
159
|
-
return {
|
|
160
|
-
phone: responsePhone.phone,
|
|
161
|
-
allow: false,
|
|
162
|
-
reasons: ["INVALID"],
|
|
163
|
-
response: responsePhone
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
if (rules.deny.includes("FRAUD") && responsePhone.fraud) reasons.push("FRAUD");
|
|
167
|
-
if (rules.deny.includes("HIGH_RISK_SCORE") && responsePhone.plugins.riskScore >= 80) reasons.push("HIGH_RISK_SCORE");
|
|
168
|
-
for (const rule of rules.deny) {
|
|
169
|
-
if (rule.startsWith("COUNTRY:")) {
|
|
170
|
-
const block = rule.split(":")[1];
|
|
171
|
-
if (responsePhone.countryCode === block) reasons.push(`COUNTRY:${block}`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
203
|
+
static generateEmailValidatorResponse(inputData) {
|
|
174
204
|
return {
|
|
175
|
-
|
|
176
|
-
allow:
|
|
177
|
-
reasons,
|
|
178
|
-
response:
|
|
205
|
+
email: inputData?.email || "",
|
|
206
|
+
allow: this.validateEmail(inputData?.email),
|
|
207
|
+
reasons: this.validateEmail(inputData?.email) ? [] : ["INVALID"],
|
|
208
|
+
response: this.generateEmailDataAnalysis(inputData?.email)
|
|
179
209
|
};
|
|
180
|
-
} catch (error) {
|
|
181
|
-
const statusCode = error.response?.status || 500;
|
|
182
|
-
const errorMessage = error.response?.data?.message || error.message;
|
|
183
|
-
const errorDetails = JSON.stringify(error.response?.data || {});
|
|
184
|
-
throw customError(5e3, `Error ${statusCode}: ${errorMessage}. Details: ${errorDetails}`);
|
|
185
210
|
}
|
|
186
|
-
|
|
187
|
-
const getUserAgent = (req) => {
|
|
188
|
-
return req.headers?.["user-agent"] || req.headers?.["User-Agent"];
|
|
189
|
-
};
|
|
190
|
-
const getIp = (req) => {
|
|
191
|
-
return req.ip || req.headers?.["x-forwarded-for"] || req.connection?.remoteAddress || req.socket?.remoteAddress || req.req?.socket?.remoteAddress;
|
|
192
|
-
};
|
|
193
|
-
const handleRequest = (req) => {
|
|
194
|
-
return {
|
|
195
|
-
body: req.body,
|
|
196
|
-
userAgent: getUserAgent(req),
|
|
197
|
-
ip: getIp(req)
|
|
198
|
-
};
|
|
199
|
-
};
|
|
200
|
-
const protectReq = async (axiosClient, req, rules) => {
|
|
201
|
-
if (!axiosClient.defaults.headers?.Authorization) throw customError(3e3, "Invalid private token.");
|
|
202
|
-
const reqData = handleRequest(req);
|
|
203
|
-
if (!reqData.userAgent || !reqData.ip) throw customError(1500, "You must provide user agent and ip.");
|
|
204
|
-
if (rules.mode === "DRY_RUN") {
|
|
205
|
-
console.warn("[Dymo API] DRY_RUN mode is enabled. No requests with real data will be processed until you switch to LIVE mode.");
|
|
211
|
+
static generateEmailDataAnalysis(email) {
|
|
206
212
|
return {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
213
|
+
valid: this.validateEmail(email),
|
|
214
|
+
fraud: false,
|
|
215
|
+
proxiedEmail: false,
|
|
216
|
+
freeSubdomain: false,
|
|
217
|
+
corporate: false,
|
|
218
|
+
email: email || "",
|
|
219
|
+
realUser: "",
|
|
220
|
+
didYouMean: null,
|
|
221
|
+
noReply: false,
|
|
222
|
+
customTLD: false,
|
|
223
|
+
domain: "",
|
|
224
|
+
roleAccount: false,
|
|
225
|
+
plugins: {
|
|
226
|
+
mxRecords: [],
|
|
227
|
+
blocklist: false,
|
|
228
|
+
compromiseDetector: false,
|
|
229
|
+
nsfw: false,
|
|
230
|
+
reputation: "unknown",
|
|
231
|
+
riskScore: 0,
|
|
232
|
+
torNetwork: false,
|
|
233
|
+
typosquatting: 0,
|
|
234
|
+
urlShortener: false
|
|
235
|
+
}
|
|
211
236
|
};
|
|
212
237
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
ip:
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
return response.data;
|
|
221
|
-
} catch (error) {
|
|
222
|
-
const statusCode = error.response?.status || 500;
|
|
223
|
-
const errorMessage = error.response?.data?.message || error.message;
|
|
224
|
-
const errorDetails = JSON.stringify(error.response?.data || {});
|
|
225
|
-
throw customError(5e3, `Error ${statusCode}: ${errorMessage}. Details: ${errorDetails}`);
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
const fs = {};
|
|
229
|
-
const convertTailwindToInlineCss = (htmlContent) => {
|
|
230
|
-
return htmlContent.replace(/class="([^"]+)"( style="([^"]+)")?/g, (match, classList, _, existingStyle) => {
|
|
231
|
-
const compiledStyles = twToCss.twi(classList, { minify: true, merge: true });
|
|
232
|
-
return match.replace(/class="[^"]+"/, "").replace(/ style="[^"]+"/, "").concat(` style="${existingStyle ? `${existingStyle.trim().slice(0, -1)}; ${compiledStyles}` : compiledStyles}"`);
|
|
233
|
-
});
|
|
234
|
-
};
|
|
235
|
-
const sendEmail = async (axiosClient, data) => {
|
|
236
|
-
if (!axiosClient.defaults.headers?.Authorization) throw customError(3e3, "Invalid private token.");
|
|
237
|
-
if (!data.from) throw customError(1500, "You must provide an email address from which the following will be sent.");
|
|
238
|
-
if (!data.to) throw customError(1500, "You must provide an email to be sent to.");
|
|
239
|
-
if (!data.subject) throw customError(1500, "You must provide a subject for the email to be sent.");
|
|
240
|
-
if (!data.html && !data.react && !React.isValidElement(data.react)) throw customError(1500, "You must provide HTML or a React component.");
|
|
241
|
-
if (data.html && data.react) throw customError(1500, "You must provide only HTML or a React component, not both.");
|
|
242
|
-
try {
|
|
243
|
-
if (data.react) {
|
|
244
|
-
data.html = await render.render(data.react);
|
|
245
|
-
delete data.react;
|
|
246
|
-
}
|
|
247
|
-
if (data.options && data.options.composeTailwindClasses) {
|
|
248
|
-
data.html = convertTailwindToInlineCss(data.html);
|
|
249
|
-
delete data.options.composeTailwindClasses;
|
|
250
|
-
}
|
|
251
|
-
} catch (error) {
|
|
252
|
-
throw customError(1500, `An error occurred while rendering your React component. Details: ${error}`);
|
|
253
|
-
}
|
|
254
|
-
try {
|
|
255
|
-
let totalSize = 0;
|
|
256
|
-
if (data.attachments && Array.isArray(data.attachments)) {
|
|
257
|
-
const processedAttachments = await Promise.all(
|
|
258
|
-
data.attachments.map(async (attachment) => {
|
|
259
|
-
if (attachment.path && attachment.content || !attachment.path && !attachment.content) throw customError(1500, "You must provide either 'path' or 'content', not both.");
|
|
260
|
-
let contentBuffer;
|
|
261
|
-
if (attachment.path) contentBuffer = await fs.readFile(path.resolve(attachment.path));
|
|
262
|
-
else if (attachment.content) contentBuffer = attachment.content instanceof Buffer ? attachment.content : Buffer.from(attachment.content);
|
|
263
|
-
totalSize += Buffer.byteLength(contentBuffer);
|
|
264
|
-
if (totalSize > 40 * 1024 * 1024) throw customError(1500, "Attachments exceed the maximum allowed size of 40 MB.");
|
|
265
|
-
return {
|
|
266
|
-
filename: attachment.filename || path.basename(attachment.path || ""),
|
|
267
|
-
content: contentBuffer,
|
|
268
|
-
cid: attachment.cid || attachment.filename
|
|
269
|
-
};
|
|
270
|
-
})
|
|
271
|
-
);
|
|
272
|
-
data.attachments = processedAttachments;
|
|
273
|
-
}
|
|
274
|
-
const response = await axiosClient.post("/private/sender/sendEmail", data);
|
|
275
|
-
return response.data;
|
|
276
|
-
} catch (error) {
|
|
277
|
-
throw customError(5e3, error.response?.data?.message || error.message);
|
|
238
|
+
static generateIPValidatorResponse(inputData) {
|
|
239
|
+
return {
|
|
240
|
+
ip: inputData?.ip || "",
|
|
241
|
+
allow: this.validateIP(inputData?.ip),
|
|
242
|
+
reasons: this.validateIP(inputData?.ip) ? [] : ["INVALID"],
|
|
243
|
+
response: this.generateIPDataAnalysis(inputData?.ip)
|
|
244
|
+
};
|
|
278
245
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
246
|
+
static generateIPDataAnalysis(ip) {
|
|
247
|
+
const isValid = this.validateIP(ip);
|
|
248
|
+
return {
|
|
249
|
+
valid: isValid,
|
|
250
|
+
type: isValid ? "IPv4" : "Invalid",
|
|
251
|
+
class: isValid ? "A" : "Unknown",
|
|
252
|
+
fraud: false,
|
|
253
|
+
ip: ip || "",
|
|
254
|
+
continent: "",
|
|
255
|
+
continentCode: "",
|
|
256
|
+
country: "",
|
|
257
|
+
countryCode: "",
|
|
258
|
+
region: "",
|
|
259
|
+
regionName: "",
|
|
260
|
+
city: "",
|
|
261
|
+
district: "",
|
|
262
|
+
zipCode: "",
|
|
263
|
+
lat: 0,
|
|
264
|
+
lon: 0,
|
|
265
|
+
timezone: "",
|
|
266
|
+
offset: 0,
|
|
267
|
+
currency: "",
|
|
268
|
+
isp: "",
|
|
269
|
+
org: "",
|
|
270
|
+
as: "",
|
|
271
|
+
asname: "",
|
|
272
|
+
mobile: false,
|
|
273
|
+
proxy: true,
|
|
274
|
+
hosting: false,
|
|
275
|
+
plugins: {
|
|
276
|
+
blocklist: false,
|
|
277
|
+
riskScore: 0
|
|
278
|
+
}
|
|
279
|
+
};
|
|
283
280
|
}
|
|
284
|
-
static
|
|
285
|
-
|
|
286
|
-
|
|
281
|
+
static generatePhoneValidatorResponse(inputData) {
|
|
282
|
+
return {
|
|
283
|
+
phone: inputData?.phone || "",
|
|
284
|
+
allow: this.validatePhone(inputData?.phone),
|
|
285
|
+
reasons: this.validatePhone(inputData?.phone) ? [] : ["INVALID"],
|
|
286
|
+
response: this.generatePhoneDataAnalysis(inputData?.phone)
|
|
287
|
+
};
|
|
287
288
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
289
|
+
static generatePhoneDataAnalysis(phone) {
|
|
290
|
+
const phoneNumber = phone?.phone || phone;
|
|
291
|
+
const isValid = this.validatePhone(phoneNumber);
|
|
292
|
+
return {
|
|
293
|
+
valid: isValid,
|
|
294
|
+
fraud: false,
|
|
295
|
+
phone: phone?.phone || "",
|
|
296
|
+
prefix: "",
|
|
297
|
+
number: "",
|
|
298
|
+
lineType: "Unknown",
|
|
299
|
+
carrierInfo: {
|
|
300
|
+
carrierName: "",
|
|
301
|
+
accuracy: 0,
|
|
302
|
+
carrierCountry: "",
|
|
303
|
+
carrierCountryCode: ""
|
|
304
|
+
},
|
|
305
|
+
country: "",
|
|
306
|
+
countryCode: "",
|
|
307
|
+
plugins: {
|
|
308
|
+
blocklist: false,
|
|
309
|
+
riskScore: 0
|
|
310
|
+
}
|
|
311
|
+
};
|
|
299
312
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (parsedLimit !== void 0) limitInfo.limit = parsedLimit;
|
|
311
|
-
if (parsedRemaining !== void 0) limitInfo.remaining = parsedRemaining;
|
|
312
|
-
if (typeof remainingRequests === "string" && remainingRequests.trim().toLowerCase() === "unlimited") limitInfo.isUnlimited = true;
|
|
313
|
-
if (resetRequests && typeof resetRequests === "string") limitInfo.resetTime = resetRequests;
|
|
314
|
-
if (parsedRetryAfter !== void 0) limitInfo.retryAfter = parsedRetryAfter;
|
|
315
|
-
limitInfo.lastUpdated = Date.now();
|
|
313
|
+
static generateHTTPRequest(inputData) {
|
|
314
|
+
return {
|
|
315
|
+
method: inputData?.method || "GET",
|
|
316
|
+
url: inputData?.url || "",
|
|
317
|
+
headers: inputData?.headers || {},
|
|
318
|
+
body: inputData?.body || null,
|
|
319
|
+
allow: false,
|
|
320
|
+
reasons: ["FRAUD"],
|
|
321
|
+
protected: true
|
|
322
|
+
};
|
|
316
323
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
324
|
+
static generateEmailStatus() {
|
|
325
|
+
return {
|
|
326
|
+
status: false,
|
|
327
|
+
error: "API unavailable - using fallback response"
|
|
328
|
+
};
|
|
322
329
|
}
|
|
323
|
-
|
|
324
|
-
|
|
330
|
+
static generateSRNSummary(inputData) {
|
|
331
|
+
const quantity = inputData?.quantity || 1;
|
|
332
|
+
const values = Array.from({ length: quantity }, () => ({
|
|
333
|
+
integer: 0,
|
|
334
|
+
float: 0
|
|
335
|
+
}));
|
|
336
|
+
return {
|
|
337
|
+
values,
|
|
338
|
+
executionTime: 0
|
|
339
|
+
};
|
|
325
340
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
341
|
+
static generateExtractWithTextly(inputData) {
|
|
342
|
+
return {
|
|
343
|
+
data: inputData?.data || "",
|
|
344
|
+
extracted: {},
|
|
345
|
+
error: "API unavailable - using fallback response"
|
|
346
|
+
};
|
|
332
347
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
this.config = {
|
|
337
|
-
fallbackEnabled: config2.fallbackEnabled ?? false,
|
|
338
|
-
retryAttempts: Math.max(0, config2.retryAttempts ?? 2),
|
|
339
|
-
// Number of additional retries
|
|
340
|
-
retryDelay: Math.max(0, config2.retryDelay ?? 1e3)
|
|
348
|
+
static generatePrayerTimes(inputData) {
|
|
349
|
+
return {
|
|
350
|
+
error: "API unavailable - using fallback response"
|
|
341
351
|
};
|
|
342
|
-
this.clientId = clientId;
|
|
343
|
-
this.rateLimitManager = RateLimitManager.getInstance();
|
|
344
352
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
353
|
+
static generateSatinizedInputAnalysis(inputData) {
|
|
354
|
+
return {
|
|
355
|
+
input: inputData?.input || "",
|
|
356
|
+
formats: {
|
|
357
|
+
ascii: false,
|
|
358
|
+
bitcoinAddress: false,
|
|
359
|
+
cLikeIdentifier: false,
|
|
360
|
+
coordinates: false,
|
|
361
|
+
crediCard: false,
|
|
362
|
+
date: false,
|
|
363
|
+
discordUsername: false,
|
|
364
|
+
doi: false,
|
|
365
|
+
domain: false,
|
|
366
|
+
e164Phone: false,
|
|
367
|
+
email: false,
|
|
368
|
+
emoji: false,
|
|
369
|
+
hanUnification: false,
|
|
370
|
+
hashtag: false,
|
|
371
|
+
hyphenWordBreak: false,
|
|
372
|
+
ipv6: false,
|
|
373
|
+
ip: false,
|
|
374
|
+
jiraTicket: false,
|
|
375
|
+
macAddress: false,
|
|
376
|
+
name: false,
|
|
377
|
+
number: false,
|
|
378
|
+
panFromGstin: false,
|
|
379
|
+
password: false,
|
|
380
|
+
port: false,
|
|
381
|
+
tel: false,
|
|
382
|
+
text: false,
|
|
383
|
+
semver: false,
|
|
384
|
+
ssn: false,
|
|
385
|
+
uuid: false,
|
|
386
|
+
url: false,
|
|
387
|
+
urlSlug: false,
|
|
388
|
+
username: false
|
|
389
|
+
},
|
|
390
|
+
includes: {
|
|
391
|
+
spaces: false,
|
|
392
|
+
hasSql: false,
|
|
393
|
+
hasNoSql: false,
|
|
394
|
+
letters: false,
|
|
395
|
+
uppercase: false,
|
|
396
|
+
lowercase: false,
|
|
397
|
+
symbols: false,
|
|
398
|
+
digits: false
|
|
384
399
|
}
|
|
385
|
-
}
|
|
386
|
-
throw lastError;
|
|
387
|
-
}
|
|
388
|
-
shouldRetry(error) {
|
|
389
|
-
const statusCode = error.response?.status;
|
|
390
|
-
const isNetworkError = !error.response && error.code !== "ECONNABORTED";
|
|
391
|
-
const isServerError = statusCode && statusCode >= 500;
|
|
392
|
-
return isNetworkError || isServerError;
|
|
393
|
-
}
|
|
394
|
-
sleep(ms) {
|
|
395
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
396
|
-
}
|
|
397
|
-
getConfig() {
|
|
398
|
-
return { ...this.config };
|
|
400
|
+
};
|
|
399
401
|
}
|
|
400
|
-
|
|
401
|
-
return
|
|
402
|
+
static generatePasswordValidationResult(inputData) {
|
|
403
|
+
return {
|
|
404
|
+
valid: false,
|
|
405
|
+
password: inputData?.password || "",
|
|
406
|
+
details: [
|
|
407
|
+
{
|
|
408
|
+
validation: "length",
|
|
409
|
+
message: "API unavailable - using fallback response"
|
|
410
|
+
}
|
|
411
|
+
]
|
|
412
|
+
};
|
|
402
413
|
}
|
|
403
414
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
return this.generatePrayerTimes(inputData);
|
|
426
|
-
case "satinize":
|
|
427
|
-
case "satinizer":
|
|
428
|
-
return this.generateSatinizedInputAnalysis(inputData);
|
|
429
|
-
case "isValidPwd":
|
|
430
|
-
return this.generatePasswordValidationResult(inputData);
|
|
431
|
-
default:
|
|
432
|
-
throw new Error(`Unknown method for fallback: ${method}`);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
static validateURL(url) {
|
|
436
|
-
if (!url) return false;
|
|
437
|
-
const urlRegex = /^https?:\/\/(?:[-\w.])+(?:\:[0-9]+)?(?:\/(?:[\w\/_.])*(?:\?(?:[\w&=%.])*)?(?:\#(?:[\w.])*)?)?$/;
|
|
438
|
-
return urlRegex.test(url);
|
|
439
|
-
}
|
|
440
|
-
static validateEmail(email) {
|
|
441
|
-
if (!email) return false;
|
|
442
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
443
|
-
return emailRegex.test(email);
|
|
444
|
-
}
|
|
445
|
-
static validateDomain(domain) {
|
|
446
|
-
if (!domain) return false;
|
|
447
|
-
const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](?:\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])*$/;
|
|
448
|
-
if (!domainRegex.test(domain)) return false;
|
|
449
|
-
const parts = domain.split(".");
|
|
450
|
-
return parts.length >= 2 && parts[parts.length - 1].length > 0;
|
|
451
|
-
}
|
|
452
|
-
static validateCreditCard(creditCard) {
|
|
453
|
-
if (!creditCard) return false;
|
|
454
|
-
const cardNumber = typeof creditCard === "string" ? creditCard : creditCard?.pan || "";
|
|
455
|
-
if (!cardNumber) return false;
|
|
456
|
-
const cardRegex = /^\d{13,19}$/;
|
|
457
|
-
if (!cardRegex.test(cardNumber.replace(/\s/g, ""))) return false;
|
|
458
|
-
const digits = cardNumber.replace(/\s/g, "").split("").map(Number);
|
|
459
|
-
let sum = 0;
|
|
460
|
-
let isEven = false;
|
|
461
|
-
for (let i = digits.length - 1; i >= 0; i--) {
|
|
462
|
-
let digit = digits[i];
|
|
463
|
-
if (isEven) {
|
|
464
|
-
digit *= 2;
|
|
465
|
-
if (digit > 9) digit -= 9;
|
|
466
|
-
}
|
|
467
|
-
sum += digit;
|
|
468
|
-
isEven = !isEven;
|
|
469
|
-
}
|
|
470
|
-
return sum % 10 === 0;
|
|
471
|
-
}
|
|
472
|
-
static validateIP(ip) {
|
|
473
|
-
if (!ip) return false;
|
|
474
|
-
const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
|
475
|
-
const ipv6Regex = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
|
|
476
|
-
return ipv4Regex.test(ip) || ipv6Regex.test(ip);
|
|
477
|
-
}
|
|
478
|
-
static validatePhone(phone) {
|
|
479
|
-
if (!phone) return false;
|
|
480
|
-
const phoneRegex = /^\+?[1-9]\d{1,14}$/;
|
|
481
|
-
return phoneRegex.test(phone.replace(/[^\d+]/g, ""));
|
|
482
|
-
}
|
|
483
|
-
static validateWallet(wallet) {
|
|
484
|
-
if (!wallet) return false;
|
|
485
|
-
const bitcoinRegex = /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/;
|
|
486
|
-
const ethereumRegex = /^0x[a-fA-F0-9]{40}$/;
|
|
487
|
-
return bitcoinRegex.test(wallet) || ethereumRegex.test(wallet);
|
|
415
|
+
const getPrayerTimes = async ({
|
|
416
|
+
axiosClient,
|
|
417
|
+
resilienceManager,
|
|
418
|
+
data
|
|
419
|
+
}) => {
|
|
420
|
+
const { lat, lon } = data;
|
|
421
|
+
if (lat === void 0 || lon === void 0) throw customError(1e3, "You must provide a latitude and longitude.");
|
|
422
|
+
if (resilienceManager) {
|
|
423
|
+
const fallbackData = FallbackDataGenerator.generateFallbackData("getPrayerTimes", data);
|
|
424
|
+
return await resilienceManager.executeWithResilience(
|
|
425
|
+
axiosClient,
|
|
426
|
+
{
|
|
427
|
+
method: "GET",
|
|
428
|
+
url: "/public/islam/prayertimes",
|
|
429
|
+
params: data
|
|
430
|
+
},
|
|
431
|
+
resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
432
|
+
);
|
|
433
|
+
} else {
|
|
434
|
+
const response = await axiosClient.get("/public/islam/prayertimes", { params: data });
|
|
435
|
+
return response.data;
|
|
488
436
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
437
|
+
};
|
|
438
|
+
const isValidPwd = async ({
|
|
439
|
+
axiosClient,
|
|
440
|
+
resilienceManager,
|
|
441
|
+
data
|
|
442
|
+
}) => {
|
|
443
|
+
let { email, password, bannedWords, min, max } = data;
|
|
444
|
+
if (password === void 0) throw customError(1e3, "You must specify at least the password.");
|
|
445
|
+
const params = { password: encodeURIComponent(password) };
|
|
446
|
+
if (email) {
|
|
447
|
+
if (!/^[a-zA-Z0-9._\-+]+@?[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/.test(email)) throw customError(1500, "If you provide an email address it must be valid.");
|
|
448
|
+
params.email = encodeURIComponent(email);
|
|
493
449
|
}
|
|
494
|
-
|
|
495
|
-
if (
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
return "";
|
|
500
|
-
}
|
|
450
|
+
if (bannedWords) {
|
|
451
|
+
if (typeof bannedWords === "string") bannedWords = bannedWords.slice(1, -1).trim().split(",").map((item) => item.trim());
|
|
452
|
+
if (!Array.isArray(bannedWords) || bannedWords.length > 10) throw customError(1500, "If you provide a list of banned words; the list may not exceed 10 words and must be of array type.");
|
|
453
|
+
if (!bannedWords.every((word) => typeof word === "string") || new Set(bannedWords).size !== bannedWords.length) throw customError(1500, "If you provide a list of banned words; all elements must be non-repeated strings.");
|
|
454
|
+
params.bannedWords = bannedWords;
|
|
501
455
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
mxRecords: [],
|
|
515
|
-
nsfw: false,
|
|
516
|
-
reputation: "unknown",
|
|
517
|
-
riskScore: 0,
|
|
518
|
-
torNetwork: false,
|
|
519
|
-
typosquatting: 0,
|
|
520
|
-
urlShortener: false
|
|
521
|
-
}
|
|
456
|
+
if (min !== void 0 && (!Number.isInteger(min) || min < 8 || min > 32)) throw customError(1500, "If you provide a minimum it must be valid.");
|
|
457
|
+
if (max !== void 0 && (!Number.isInteger(max) || max < 32 || max > 100)) throw customError(1500, "If you provide a maximum it must be valid.");
|
|
458
|
+
if (min !== void 0) params.min = min;
|
|
459
|
+
if (max !== void 0) params.max = max;
|
|
460
|
+
if (resilienceManager) {
|
|
461
|
+
const fallbackData = FallbackDataGenerator.generateFallbackData("isValidPwd", data);
|
|
462
|
+
return await resilienceManager.executeWithResilience(
|
|
463
|
+
axiosClient,
|
|
464
|
+
{
|
|
465
|
+
method: "GET",
|
|
466
|
+
url: "/public/validPwd",
|
|
467
|
+
params
|
|
522
468
|
},
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
469
|
+
resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
470
|
+
);
|
|
471
|
+
} else {
|
|
472
|
+
const response = await axiosClient.get("/public/validPwd", { params });
|
|
473
|
+
return response.data;
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
const satinize = async ({
|
|
477
|
+
axiosClient,
|
|
478
|
+
resilienceManager,
|
|
479
|
+
input
|
|
480
|
+
}) => {
|
|
481
|
+
if (input === void 0) throw customError(1e3, "You must specify at least the input.");
|
|
482
|
+
if (resilienceManager) {
|
|
483
|
+
const fallbackData = FallbackDataGenerator.generateFallbackData("satinize", input);
|
|
484
|
+
return await resilienceManager.executeWithResilience(
|
|
485
|
+
axiosClient,
|
|
486
|
+
{
|
|
487
|
+
method: "GET",
|
|
488
|
+
url: "/public/inputSatinizer",
|
|
489
|
+
params: { input: encodeURIComponent(input) }
|
|
542
490
|
},
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
491
|
+
resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
492
|
+
);
|
|
493
|
+
} else {
|
|
494
|
+
const response = await axiosClient.get("/public/inputSatinizer", { params: { input: encodeURIComponent(input) } });
|
|
495
|
+
return response.data;
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
const extractWithTextly = async ({
|
|
499
|
+
axiosClient,
|
|
500
|
+
resilienceManager,
|
|
501
|
+
data
|
|
502
|
+
}) => {
|
|
503
|
+
if (!axiosClient.defaults.headers?.Authorization) throw customError(3e3, "Invalid private token.");
|
|
504
|
+
if (!data.data) throw customError(1500, "No data provided.");
|
|
505
|
+
if (!data.format) throw customError(1500, "No format provided.");
|
|
506
|
+
if (resilienceManager) {
|
|
507
|
+
const fallbackData = FallbackDataGenerator.generateFallbackData("extractWithTextly", data);
|
|
508
|
+
return await resilienceManager.executeWithResilience(
|
|
509
|
+
axiosClient,
|
|
510
|
+
{
|
|
511
|
+
method: "POST",
|
|
512
|
+
url: "/private/textly/extract",
|
|
513
|
+
data
|
|
553
514
|
},
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
515
|
+
resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
516
|
+
);
|
|
517
|
+
} else {
|
|
518
|
+
const response = await axiosClient.post("/private/textly/extract", data, { headers: { "Content-Type": "application/json" } });
|
|
519
|
+
return response.data;
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
const getRandom = async ({
|
|
523
|
+
axiosClient,
|
|
524
|
+
resilienceManager,
|
|
525
|
+
data
|
|
526
|
+
}) => {
|
|
527
|
+
if (!axiosClient.defaults.headers?.Authorization) throw customError(3e3, "Invalid private token.");
|
|
528
|
+
if (data.min === void 0 || data.max === void 0) throw customError(1500, "Both 'min' and 'max' parameters must be defined.");
|
|
529
|
+
if (data.min >= data.max) throw customError(1500, "'min' must be less than 'max'.");
|
|
530
|
+
if (data.min < -1e9 || data.min > 1e9) throw customError(1500, "'min' must be an integer in the interval [-1000000000, 1000000000].");
|
|
531
|
+
if (data.max < -1e9 || data.max > 1e9) throw customError(1500, "'max' must be an integer in the interval [-1000000000, 1000000000].");
|
|
532
|
+
if (resilienceManager) {
|
|
533
|
+
const fallbackData = FallbackDataGenerator.generateFallbackData("getRandom", data);
|
|
534
|
+
return await resilienceManager.executeWithResilience(
|
|
535
|
+
axiosClient,
|
|
536
|
+
{
|
|
537
|
+
method: "POST",
|
|
538
|
+
url: "/private/srng",
|
|
539
|
+
data
|
|
565
540
|
},
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
541
|
+
resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
542
|
+
);
|
|
543
|
+
} else {
|
|
544
|
+
const response = await axiosClient.post("/private/srng", data, { headers: { "Content-Type": "application/json" } });
|
|
545
|
+
return response.data;
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
const isValidDataRaw = async ({
|
|
549
|
+
axiosClient,
|
|
550
|
+
resilienceManager,
|
|
551
|
+
data
|
|
552
|
+
}) => {
|
|
553
|
+
if (!axiosClient.defaults.headers?.Authorization) throw customError(3e3, "Invalid private token.");
|
|
554
|
+
if (!Object.keys(data).some((key) => ["url", "email", "phone", "domain", "creditCard", "ip", "wallet", "userAgent", "iban"].includes(key) && data.hasOwnProperty(key))) throw customError(1500, "You must provide at least one parameter.");
|
|
555
|
+
if (resilienceManager) {
|
|
556
|
+
const fallbackData = FallbackDataGenerator.generateFallbackData("isValidDataRaw", data);
|
|
557
|
+
return await resilienceManager.executeWithResilience(
|
|
558
|
+
axiosClient,
|
|
559
|
+
{
|
|
560
|
+
method: "POST",
|
|
561
|
+
url: "/private/secure/verify",
|
|
562
|
+
data
|
|
576
563
|
},
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
564
|
+
resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
565
|
+
);
|
|
566
|
+
} else {
|
|
567
|
+
const response = await axiosClient.post("/private/secure/verify", data, { headers: { "Content-Type": "application/json" } });
|
|
568
|
+
return response.data;
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
const isValidEmail = async ({
|
|
572
|
+
axiosClient,
|
|
573
|
+
resilienceManager,
|
|
574
|
+
email,
|
|
575
|
+
rules
|
|
576
|
+
}) => {
|
|
577
|
+
if (!axiosClient.defaults.headers?.Authorization) throw customError(3e3, "Invalid private token.");
|
|
578
|
+
if (rules.deny.length === 0) throw customError(1500, "You must provide at least one deny rule.");
|
|
579
|
+
if (rules.mode === "DRY_RUN") {
|
|
580
|
+
console.warn("[Dymo API] DRY_RUN mode is enabled. No requests with real data will be processed until you switch to LIVE mode.");
|
|
581
|
+
return {
|
|
582
|
+
email: typeof email === "string" ? email : "",
|
|
583
|
+
allow: true,
|
|
584
|
+
reasons: [],
|
|
585
|
+
response: "CHANGE TO LIVE MODE"
|
|
586
586
|
};
|
|
587
587
|
}
|
|
588
|
-
|
|
588
|
+
const plugins = [
|
|
589
|
+
rules.deny.includes("NO_MX_RECORDS") ? "mxRecords" : void 0,
|
|
590
|
+
rules.deny.includes("NO_REACHABLE") ? "reachable" : void 0,
|
|
591
|
+
rules.deny.includes("HIGH_RISK_SCORE") ? "riskScore" : void 0,
|
|
592
|
+
rules.deny.includes("NO_GRAVATAR") ? "gravatar" : void 0
|
|
593
|
+
].filter(Boolean);
|
|
594
|
+
let responseEmail;
|
|
595
|
+
if (resilienceManager) {
|
|
596
|
+
const fallbackData = FallbackDataGenerator.generateFallbackData("isValidEmail", email);
|
|
597
|
+
const response = await resilienceManager.executeWithResilience(
|
|
598
|
+
axiosClient,
|
|
599
|
+
{
|
|
600
|
+
method: "POST",
|
|
601
|
+
url: "/private/secure/verify",
|
|
602
|
+
data: { email, plugins }
|
|
603
|
+
},
|
|
604
|
+
resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
605
|
+
);
|
|
606
|
+
responseEmail = response.email;
|
|
607
|
+
} else {
|
|
608
|
+
const response = await axiosClient.post("/private/secure/verify", { email, plugins }, { headers: { "Content-Type": "application/json" } });
|
|
609
|
+
responseEmail = response.data.email;
|
|
610
|
+
}
|
|
611
|
+
if (!responseEmail || !responseEmail.valid) {
|
|
589
612
|
return {
|
|
590
|
-
email:
|
|
591
|
-
allow:
|
|
592
|
-
reasons:
|
|
593
|
-
response:
|
|
613
|
+
email: responseEmail?.email || (typeof email === "string" ? email : ""),
|
|
614
|
+
allow: false,
|
|
615
|
+
reasons: ["INVALID"],
|
|
616
|
+
response: responseEmail
|
|
594
617
|
};
|
|
595
618
|
}
|
|
596
|
-
|
|
619
|
+
const reasons = [];
|
|
620
|
+
if (rules.deny.includes("FRAUD") && responseEmail.fraud) reasons.push("FRAUD");
|
|
621
|
+
if (rules.deny.includes("PROXIED_EMAIL") && responseEmail.proxiedEmail) reasons.push("PROXIED_EMAIL");
|
|
622
|
+
if (rules.deny.includes("FREE_SUBDOMAIN") && responseEmail.freeSubdomain) reasons.push("FREE_SUBDOMAIN");
|
|
623
|
+
if (rules.deny.includes("PERSONAL_EMAIL") && !responseEmail.corporate) reasons.push("PERSONAL_EMAIL");
|
|
624
|
+
if (rules.deny.includes("CORPORATE_EMAIL") && responseEmail.corporate) reasons.push("CORPORATE_EMAIL");
|
|
625
|
+
if (rules.deny.includes("NO_MX_RECORDS") && responseEmail.plugins?.mxRecords?.length === 0) reasons.push("NO_MX_RECORDS");
|
|
626
|
+
if (rules.deny.includes("NO_REPLY_EMAIL") && responseEmail.noReply) reasons.push("NO_REPLY_EMAIL");
|
|
627
|
+
if (rules.deny.includes("ROLE_ACCOUNT") && responseEmail.roleAccount) reasons.push("ROLE_ACCOUNT");
|
|
628
|
+
if (rules.deny.includes("NO_REACHABLE") && responseEmail.plugins?.reachable === false) reasons.push("NO_REACHABLE");
|
|
629
|
+
if (rules.deny.includes("HIGH_RISK_SCORE") && (responseEmail.plugins?.riskScore ?? 0) >= 80) reasons.push("HIGH_RISK_SCORE");
|
|
630
|
+
if (rules.deny.includes("NO_GRAVATAR") && !responseEmail.plugins?.gravatar) reasons.push("NO_GRAVATAR");
|
|
631
|
+
return {
|
|
632
|
+
email: responseEmail.email,
|
|
633
|
+
allow: reasons.length === 0,
|
|
634
|
+
reasons,
|
|
635
|
+
response: responseEmail
|
|
636
|
+
};
|
|
637
|
+
};
|
|
638
|
+
const isValidIP = async ({
|
|
639
|
+
axiosClient,
|
|
640
|
+
resilienceManager,
|
|
641
|
+
ip,
|
|
642
|
+
rules
|
|
643
|
+
}) => {
|
|
644
|
+
if (!axiosClient.defaults.headers?.Authorization) throw customError(3e3, "Invalid private token.");
|
|
645
|
+
if (rules.deny.length === 0) throw customError(1500, "You must provide at least one deny rule.");
|
|
646
|
+
if (rules.mode === "DRY_RUN") {
|
|
647
|
+
console.warn("[Dymo API] DRY_RUN mode is enabled. No requests with real data will be processed until you switch to LIVE mode.");
|
|
597
648
|
return {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
corporate: false,
|
|
603
|
-
email: email || "",
|
|
604
|
-
realUser: "",
|
|
605
|
-
didYouMean: null,
|
|
606
|
-
noReply: false,
|
|
607
|
-
customTLD: false,
|
|
608
|
-
domain: "",
|
|
609
|
-
roleAccount: false,
|
|
610
|
-
plugins: {
|
|
611
|
-
mxRecords: [],
|
|
612
|
-
blocklist: false,
|
|
613
|
-
compromiseDetector: false,
|
|
614
|
-
nsfw: false,
|
|
615
|
-
reputation: "unknown",
|
|
616
|
-
riskScore: 0,
|
|
617
|
-
torNetwork: false,
|
|
618
|
-
typosquatting: 0,
|
|
619
|
-
urlShortener: false
|
|
620
|
-
}
|
|
649
|
+
ip: typeof ip === "string" ? ip : "",
|
|
650
|
+
allow: true,
|
|
651
|
+
reasons: [],
|
|
652
|
+
response: "CHANGE TO LIVE MODE"
|
|
621
653
|
};
|
|
622
654
|
}
|
|
623
|
-
|
|
655
|
+
const plugins = [
|
|
656
|
+
rules.deny.includes("TOR_NETWORK") ? "torNetwork" : void 0,
|
|
657
|
+
rules.deny.includes("HIGH_RISK_SCORE") ? "riskScore" : void 0
|
|
658
|
+
].filter(Boolean);
|
|
659
|
+
let responseIP;
|
|
660
|
+
if (resilienceManager) {
|
|
661
|
+
const fallbackData = FallbackDataGenerator.generateFallbackData("isValidIP", ip);
|
|
662
|
+
const response = await resilienceManager.executeWithResilience(
|
|
663
|
+
axiosClient,
|
|
664
|
+
{
|
|
665
|
+
method: "POST",
|
|
666
|
+
url: "/private/secure/verify",
|
|
667
|
+
data: { ip, plugins }
|
|
668
|
+
},
|
|
669
|
+
resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
670
|
+
);
|
|
671
|
+
responseIP = response.ip;
|
|
672
|
+
} else {
|
|
673
|
+
const response = await axiosClient.post("/private/secure/verify", { ip, plugins }, { headers: { "Content-Type": "application/json" } });
|
|
674
|
+
responseIP = response.data.ip;
|
|
675
|
+
}
|
|
676
|
+
if (!responseIP || !responseIP.valid) {
|
|
624
677
|
return {
|
|
625
|
-
ip:
|
|
626
|
-
allow:
|
|
627
|
-
reasons:
|
|
628
|
-
response:
|
|
678
|
+
ip: responseIP?.ip || (typeof ip === "string" ? ip : ""),
|
|
679
|
+
allow: false,
|
|
680
|
+
reasons: ["INVALID"],
|
|
681
|
+
response: responseIP
|
|
629
682
|
};
|
|
630
683
|
}
|
|
631
|
-
|
|
632
|
-
|
|
684
|
+
const reasons = [];
|
|
685
|
+
if (rules.deny.includes("FRAUD") && responseIP.fraud) reasons.push("FRAUD");
|
|
686
|
+
if (rules.deny.includes("TOR_NETWORK") && responseIP.plugins?.torNetwork) reasons.push("TOR_NETWORK");
|
|
687
|
+
if (rules.deny.includes("HIGH_RISK_SCORE") && (responseIP.plugins?.riskScore ?? 0) >= 80) reasons.push("HIGH_RISK_SCORE");
|
|
688
|
+
for (const rule of rules.deny) {
|
|
689
|
+
if (rule.startsWith("COUNTRY:")) {
|
|
690
|
+
const block = rule.split(":")[1];
|
|
691
|
+
if (responseIP.countryCode === block) reasons.push(`COUNTRY:${block}`);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return {
|
|
695
|
+
ip: responseIP.ip,
|
|
696
|
+
allow: reasons.length === 0,
|
|
697
|
+
reasons,
|
|
698
|
+
response: responseIP
|
|
699
|
+
};
|
|
700
|
+
};
|
|
701
|
+
const isValidPhone = async ({
|
|
702
|
+
axiosClient,
|
|
703
|
+
resilienceManager,
|
|
704
|
+
phone,
|
|
705
|
+
rules
|
|
706
|
+
}) => {
|
|
707
|
+
if (!axiosClient.defaults.headers?.Authorization) throw customError(3e3, "Invalid private token.");
|
|
708
|
+
if (rules.deny.length === 0) throw customError(1500, "You must provide at least one deny rule.");
|
|
709
|
+
if (rules.mode === "DRY_RUN") {
|
|
710
|
+
console.warn("[Dymo API] DRY_RUN mode is enabled. No requests with real data will be processed until you switch to LIVE mode.");
|
|
633
711
|
return {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
ip: ip || "",
|
|
639
|
-
continent: "",
|
|
640
|
-
continentCode: "",
|
|
641
|
-
country: "",
|
|
642
|
-
countryCode: "",
|
|
643
|
-
region: "",
|
|
644
|
-
regionName: "",
|
|
645
|
-
city: "",
|
|
646
|
-
district: "",
|
|
647
|
-
zipCode: "",
|
|
648
|
-
lat: 0,
|
|
649
|
-
lon: 0,
|
|
650
|
-
timezone: "",
|
|
651
|
-
offset: 0,
|
|
652
|
-
currency: "",
|
|
653
|
-
isp: "",
|
|
654
|
-
org: "",
|
|
655
|
-
as: "",
|
|
656
|
-
asname: "",
|
|
657
|
-
mobile: false,
|
|
658
|
-
proxy: true,
|
|
659
|
-
hosting: false,
|
|
660
|
-
plugins: {
|
|
661
|
-
blocklist: false,
|
|
662
|
-
riskScore: 0
|
|
663
|
-
}
|
|
712
|
+
phone: typeof phone === "string" ? phone : "",
|
|
713
|
+
allow: true,
|
|
714
|
+
reasons: [],
|
|
715
|
+
response: "CHANGE TO LIVE MODE"
|
|
664
716
|
};
|
|
665
717
|
}
|
|
666
|
-
|
|
718
|
+
const plugins = [
|
|
719
|
+
rules.deny.includes("HIGH_RISK_SCORE") ? "riskScore" : void 0
|
|
720
|
+
].filter(Boolean);
|
|
721
|
+
let responsePhone;
|
|
722
|
+
if (resilienceManager) {
|
|
723
|
+
const fallbackData = FallbackDataGenerator.generateFallbackData("isValidPhone", phone);
|
|
724
|
+
const response = await resilienceManager.executeWithResilience(
|
|
725
|
+
axiosClient,
|
|
726
|
+
{
|
|
727
|
+
method: "POST",
|
|
728
|
+
url: "/private/secure/verify",
|
|
729
|
+
data: { phone, plugins }
|
|
730
|
+
},
|
|
731
|
+
resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
732
|
+
);
|
|
733
|
+
responsePhone = response.phone;
|
|
734
|
+
} else {
|
|
735
|
+
const response = await axiosClient.post("/private/secure/verify", { phone, plugins }, { headers: { "Content-Type": "application/json" } });
|
|
736
|
+
responsePhone = response.data.phone;
|
|
737
|
+
}
|
|
738
|
+
if (!responsePhone || !responsePhone.valid) {
|
|
667
739
|
return {
|
|
668
|
-
phone:
|
|
669
|
-
allow:
|
|
670
|
-
reasons:
|
|
671
|
-
response:
|
|
740
|
+
phone: responsePhone?.phone || (typeof phone === "string" ? phone : ""),
|
|
741
|
+
allow: false,
|
|
742
|
+
reasons: ["INVALID"],
|
|
743
|
+
response: responsePhone
|
|
672
744
|
};
|
|
673
745
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
746
|
+
const reasons = [];
|
|
747
|
+
if (rules.deny.includes("FRAUD") && responsePhone.fraud) reasons.push("FRAUD");
|
|
748
|
+
if (rules.deny.includes("HIGH_RISK_SCORE") && (responsePhone.plugins?.riskScore ?? 0) >= 80) reasons.push("HIGH_RISK_SCORE");
|
|
749
|
+
for (const rule of rules.deny) {
|
|
750
|
+
if (rule.startsWith("COUNTRY:")) {
|
|
751
|
+
const block = rule.split(":")[1];
|
|
752
|
+
if (responsePhone.countryCode === block) reasons.push(`COUNTRY:${block}`);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return {
|
|
756
|
+
phone: responsePhone.phone,
|
|
757
|
+
allow: reasons.length === 0,
|
|
758
|
+
reasons,
|
|
759
|
+
response: responsePhone
|
|
760
|
+
};
|
|
761
|
+
};
|
|
762
|
+
const getUserAgent = (req) => {
|
|
763
|
+
return req.headers?.["user-agent"] || req.headers?.["User-Agent"];
|
|
764
|
+
};
|
|
765
|
+
const getIp = (req) => {
|
|
766
|
+
return req.ip || req.headers?.["x-forwarded-for"] || req.connection?.remoteAddress || req.socket?.remoteAddress || req.req?.socket?.remoteAddress;
|
|
767
|
+
};
|
|
768
|
+
const handleRequest = (req) => {
|
|
769
|
+
return {
|
|
770
|
+
body: req.body,
|
|
771
|
+
userAgent: getUserAgent(req),
|
|
772
|
+
ip: getIp(req)
|
|
773
|
+
};
|
|
774
|
+
};
|
|
775
|
+
const protectReq = async ({
|
|
776
|
+
axiosClient,
|
|
777
|
+
resilienceManager,
|
|
778
|
+
req,
|
|
779
|
+
rules
|
|
780
|
+
}) => {
|
|
781
|
+
if (!axiosClient.defaults.headers?.Authorization) throw customError(3e3, "Invalid private token.");
|
|
782
|
+
const reqData = handleRequest(req);
|
|
783
|
+
if (!reqData.userAgent || !reqData.ip) throw customError(1500, "You must provide user agent and ip.");
|
|
784
|
+
if (rules.mode === "DRY_RUN") {
|
|
785
|
+
console.warn("[Dymo API] DRY_RUN mode is enabled. No requests with real data will be processed until you switch to LIVE mode.");
|
|
677
786
|
return {
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
787
|
+
ip: reqData.ip,
|
|
788
|
+
userAgent: reqData.userAgent,
|
|
789
|
+
allow: true,
|
|
790
|
+
reasons: []
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
const requestData = {
|
|
794
|
+
ip: reqData.ip,
|
|
795
|
+
userAgent: reqData.userAgent,
|
|
796
|
+
allowBots: rules.allowBots,
|
|
797
|
+
deny: rules.deny
|
|
798
|
+
};
|
|
799
|
+
if (resilienceManager) {
|
|
800
|
+
const fallbackData = FallbackDataGenerator.generateFallbackData("protectReq", req);
|
|
801
|
+
return await resilienceManager.executeWithResilience(
|
|
802
|
+
axiosClient,
|
|
803
|
+
{
|
|
804
|
+
method: "POST",
|
|
805
|
+
url: "/private/waf/verifyRequest",
|
|
806
|
+
data: requestData
|
|
807
|
+
},
|
|
808
|
+
resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
809
|
+
);
|
|
810
|
+
} else {
|
|
811
|
+
const response = await axiosClient.post("/private/waf/verifyRequest", requestData, { headers: { "Content-Type": "application/json" } });
|
|
812
|
+
return response.data;
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
const fs = {};
|
|
816
|
+
const convertTailwindToInlineCss = (htmlContent) => {
|
|
817
|
+
return htmlContent.replace(/class="([^"]+)"( style="([^"]+)")?/g, (match, classList, _, existingStyle) => {
|
|
818
|
+
const compiledStyles = twToCss.twi(classList, { minify: true, merge: true });
|
|
819
|
+
return match.replace(/class="[^"]+"/, "").replace(/ style="[^"]+"/, "").concat(` style="${existingStyle ? `${existingStyle.trim().slice(0, -1)}; ${compiledStyles}` : compiledStyles}"`);
|
|
820
|
+
});
|
|
821
|
+
};
|
|
822
|
+
const sendEmail = async ({
|
|
823
|
+
axiosClient,
|
|
824
|
+
resilienceManager,
|
|
825
|
+
data
|
|
826
|
+
}) => {
|
|
827
|
+
if (!axiosClient.defaults.headers?.Authorization) throw customError(3e3, "Invalid private token.");
|
|
828
|
+
if (!data.from) throw customError(1500, "You must provide an email address from which the following will be sent.");
|
|
829
|
+
if (!data.to) throw customError(1500, "You must provide an email to be sent to.");
|
|
830
|
+
if (!data.subject) throw customError(1500, "You must provide a subject for the email to be sent.");
|
|
831
|
+
if (!data.html && !data.react && !React.isValidElement(data.react)) throw customError(1500, "You must provide HTML or a React component.");
|
|
832
|
+
if (data.html && data.react) throw customError(1500, "You must provide only HTML or a React component, not both.");
|
|
833
|
+
try {
|
|
834
|
+
if (data.react) {
|
|
835
|
+
data.html = await render.render(data.react);
|
|
836
|
+
delete data.react;
|
|
837
|
+
}
|
|
838
|
+
if (data.options && data.options.composeTailwindClasses) {
|
|
839
|
+
data.html = convertTailwindToInlineCss(data.html);
|
|
840
|
+
delete data.options.composeTailwindClasses;
|
|
841
|
+
}
|
|
842
|
+
} catch (error) {
|
|
843
|
+
throw customError(1500, `An error occurred while rendering your React component. Details: ${error}`);
|
|
844
|
+
}
|
|
845
|
+
let totalSize = 0;
|
|
846
|
+
if (data.attachments && Array.isArray(data.attachments)) {
|
|
847
|
+
const processedAttachments = await Promise.all(
|
|
848
|
+
data.attachments.map(async (attachment) => {
|
|
849
|
+
if (attachment.path && attachment.content || !attachment.path && !attachment.content) throw customError(1500, "You must provide either 'path' or 'content', not both.");
|
|
850
|
+
let contentBuffer;
|
|
851
|
+
if (attachment.path) contentBuffer = await fs.readFile(path.resolve(attachment.path));
|
|
852
|
+
else if (attachment.content) contentBuffer = attachment.content instanceof Buffer ? attachment.content : Buffer.from(attachment.content);
|
|
853
|
+
totalSize += Buffer.byteLength(contentBuffer);
|
|
854
|
+
if (totalSize > 40 * 1024 * 1024) throw customError(1500, "Attachments exceed the maximum allowed size of 40 MB.");
|
|
855
|
+
return {
|
|
856
|
+
filename: attachment.filename || path.basename(attachment.path || ""),
|
|
857
|
+
content: contentBuffer,
|
|
858
|
+
cid: attachment.cid || attachment.filename
|
|
859
|
+
};
|
|
860
|
+
})
|
|
861
|
+
);
|
|
862
|
+
data.attachments = processedAttachments;
|
|
863
|
+
}
|
|
864
|
+
if (resilienceManager) {
|
|
865
|
+
const fallbackData = FallbackDataGenerator.generateFallbackData("sendEmail", data);
|
|
866
|
+
return await resilienceManager.executeWithResilience(
|
|
867
|
+
axiosClient,
|
|
868
|
+
{
|
|
869
|
+
method: "POST",
|
|
870
|
+
url: "/private/sender/sendEmail",
|
|
871
|
+
data
|
|
689
872
|
},
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
}
|
|
696
|
-
};
|
|
873
|
+
resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
874
|
+
);
|
|
875
|
+
} else {
|
|
876
|
+
const response = await axiosClient.post("/private/sender/sendEmail", data);
|
|
877
|
+
return response.data;
|
|
697
878
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
headers: inputData?.headers || {},
|
|
703
|
-
body: inputData?.body || null,
|
|
704
|
-
allow: false,
|
|
705
|
-
reasons: ["FRAUD"],
|
|
706
|
-
protected: true
|
|
707
|
-
};
|
|
879
|
+
};
|
|
880
|
+
class RateLimitManager {
|
|
881
|
+
constructor() {
|
|
882
|
+
this.tracker = {};
|
|
708
883
|
}
|
|
709
|
-
static
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
error: "API unavailable - using fallback response"
|
|
713
|
-
};
|
|
884
|
+
static getInstance() {
|
|
885
|
+
if (!RateLimitManager.instance) RateLimitManager.instance = new RateLimitManager();
|
|
886
|
+
return RateLimitManager.instance;
|
|
714
887
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
return
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
888
|
+
/**
|
|
889
|
+
* Parses a header value that could be a number or "unlimited".
|
|
890
|
+
* Returns undefined if the value is "unlimited", null, undefined, or invalid.
|
|
891
|
+
*/
|
|
892
|
+
parseHeaderValue(value) {
|
|
893
|
+
if (value === null || value === void 0) return void 0;
|
|
894
|
+
if (typeof value !== "string" && typeof value !== "number") return void 0;
|
|
895
|
+
const strValue = String(value).trim().toLowerCase();
|
|
896
|
+
if (!strValue || strValue === "unlimited") return void 0;
|
|
897
|
+
const parsed = parseInt(strValue, 10);
|
|
898
|
+
return isNaN(parsed) || parsed < 0 ? void 0 : parsed;
|
|
725
899
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
900
|
+
updateRateLimit(clientId, headers) {
|
|
901
|
+
if (!this.tracker[clientId]) this.tracker[clientId] = {};
|
|
902
|
+
const limitInfo = this.tracker[clientId];
|
|
903
|
+
const limitRequests = headers["x-ratelimit-limit-requests"];
|
|
904
|
+
const remainingRequests = headers["x-ratelimit-remaining-requests"];
|
|
905
|
+
const resetRequests = headers["x-ratelimit-reset-requests"];
|
|
906
|
+
const retryAfter = headers["retry-after"];
|
|
907
|
+
const parsedLimit = this.parseHeaderValue(limitRequests);
|
|
908
|
+
const parsedRemaining = this.parseHeaderValue(remainingRequests);
|
|
909
|
+
const parsedRetryAfter = this.parseHeaderValue(retryAfter);
|
|
910
|
+
if (parsedLimit !== void 0) limitInfo.limit = parsedLimit;
|
|
911
|
+
if (parsedRemaining !== void 0) limitInfo.remaining = parsedRemaining;
|
|
912
|
+
if (typeof remainingRequests === "string" && remainingRequests.trim().toLowerCase() === "unlimited") limitInfo.isUnlimited = true;
|
|
913
|
+
if (resetRequests && typeof resetRequests === "string") limitInfo.resetTime = resetRequests;
|
|
914
|
+
if (parsedRetryAfter !== void 0) limitInfo.retryAfter = parsedRetryAfter;
|
|
915
|
+
limitInfo.lastUpdated = Date.now();
|
|
732
916
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
917
|
+
isRateLimited(clientId) {
|
|
918
|
+
const limitInfo = this.tracker[clientId];
|
|
919
|
+
if (!limitInfo) return false;
|
|
920
|
+
if (limitInfo.isUnlimited) return false;
|
|
921
|
+
return limitInfo.remaining !== void 0 && limitInfo.remaining <= 0;
|
|
737
922
|
}
|
|
738
|
-
|
|
739
|
-
return
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
hyphenWordBreak: false,
|
|
757
|
-
ipv6: false,
|
|
758
|
-
ip: false,
|
|
759
|
-
jiraTicket: false,
|
|
760
|
-
macAddress: false,
|
|
761
|
-
name: false,
|
|
762
|
-
number: false,
|
|
763
|
-
panFromGstin: false,
|
|
764
|
-
password: false,
|
|
765
|
-
port: false,
|
|
766
|
-
tel: false,
|
|
767
|
-
text: false,
|
|
768
|
-
semver: false,
|
|
769
|
-
ssn: false,
|
|
770
|
-
uuid: false,
|
|
771
|
-
url: false,
|
|
772
|
-
urlSlug: false,
|
|
773
|
-
username: false
|
|
774
|
-
},
|
|
775
|
-
includes: {
|
|
776
|
-
spaces: false,
|
|
777
|
-
hasSql: false,
|
|
778
|
-
hasNoSql: false,
|
|
779
|
-
letters: false,
|
|
780
|
-
uppercase: false,
|
|
781
|
-
lowercase: false,
|
|
782
|
-
symbols: false,
|
|
783
|
-
digits: false
|
|
784
|
-
}
|
|
923
|
+
getRetryAfter(clientId) {
|
|
924
|
+
return this.tracker[clientId]?.retryAfter;
|
|
925
|
+
}
|
|
926
|
+
clearExpiredLimits() {
|
|
927
|
+
const now = Date.now();
|
|
928
|
+
Object.keys(this.tracker).forEach((clientId) => {
|
|
929
|
+
const limitInfo = this.tracker[clientId];
|
|
930
|
+
if (limitInfo.lastUpdated && now - limitInfo.lastUpdated > 3e5) delete this.tracker[clientId];
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
class ResilienceManager {
|
|
935
|
+
constructor(config2 = {}, clientId = "default") {
|
|
936
|
+
this.config = {
|
|
937
|
+
fallbackEnabled: config2.fallbackEnabled ?? false,
|
|
938
|
+
retryAttempts: Math.max(0, config2.retryAttempts ?? 2),
|
|
939
|
+
// Number of additional retries
|
|
940
|
+
retryDelay: Math.max(0, config2.retryDelay ?? 1e3)
|
|
785
941
|
};
|
|
942
|
+
this.clientId = clientId;
|
|
943
|
+
this.rateLimitManager = RateLimitManager.getInstance();
|
|
786
944
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
945
|
+
async executeWithResilience(axiosClient, requestConfig, fallbackData) {
|
|
946
|
+
let lastError;
|
|
947
|
+
const totalAttempts = 1 + this.config.retryAttempts;
|
|
948
|
+
this.rateLimitManager.clearExpiredLimits();
|
|
949
|
+
if (this.rateLimitManager.isRateLimited(this.clientId)) {
|
|
950
|
+
const retryAfter = this.rateLimitManager.getRetryAfter(this.clientId);
|
|
951
|
+
if (retryAfter) {
|
|
952
|
+
console.warn(`[Dymo API] Client ${this.clientId} is rate limited. Waiting ${retryAfter} seconds...`);
|
|
953
|
+
await this.sleep(retryAfter * 1e3);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
for (let attempt = 1; attempt <= totalAttempts; attempt++) {
|
|
957
|
+
try {
|
|
958
|
+
const response = await axiosClient.request(requestConfig);
|
|
959
|
+
this.rateLimitManager.updateRateLimit(this.clientId, response.headers);
|
|
960
|
+
if (response.status === 429) {
|
|
961
|
+
const retryAfter = this.rateLimitManager.getRetryAfter(this.clientId);
|
|
962
|
+
if (retryAfter) {
|
|
963
|
+
console.warn(`[Dymo API] Rate limited. Waiting ${retryAfter} seconds (no retries)`);
|
|
964
|
+
await this.sleep(retryAfter * 1e3);
|
|
965
|
+
}
|
|
966
|
+
throw new Error(`Rate limited (429) - not retrying`);
|
|
795
967
|
}
|
|
796
|
-
|
|
797
|
-
|
|
968
|
+
return response.data;
|
|
969
|
+
} catch (error) {
|
|
970
|
+
lastError = error;
|
|
971
|
+
let shouldRetry = this.shouldRetry(error);
|
|
972
|
+
const isLastAttempt = attempt === totalAttempts;
|
|
973
|
+
if (error.response?.status === 429) shouldRetry = false;
|
|
974
|
+
if (!shouldRetry || isLastAttempt) {
|
|
975
|
+
if (this.config.fallbackEnabled && fallbackData) {
|
|
976
|
+
console.warn(`[Dymo API] Request failed after ${attempt} attempts. Using fallback data.`);
|
|
977
|
+
return fallbackData;
|
|
978
|
+
}
|
|
979
|
+
throw error;
|
|
980
|
+
}
|
|
981
|
+
const delay = this.config.retryDelay * Math.pow(2, attempt - 1);
|
|
982
|
+
console.warn(`[Dymo API] Attempt ${attempt} failed. Retrying in ${delay}ms...`);
|
|
983
|
+
await this.sleep(delay);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
throw lastError;
|
|
987
|
+
}
|
|
988
|
+
shouldRetry(error) {
|
|
989
|
+
const statusCode = error.response?.status;
|
|
990
|
+
const isNetworkError = !error.response && error.code !== "ECONNABORTED";
|
|
991
|
+
const isServerError = statusCode && statusCode >= 500;
|
|
992
|
+
return isNetworkError || isServerError;
|
|
993
|
+
}
|
|
994
|
+
sleep(ms) {
|
|
995
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
996
|
+
}
|
|
997
|
+
getConfig() {
|
|
998
|
+
return { ...this.config };
|
|
999
|
+
}
|
|
1000
|
+
getClientId() {
|
|
1001
|
+
return this.clientId;
|
|
798
1002
|
}
|
|
799
1003
|
}
|
|
800
1004
|
class DymoAPI {
|
|
801
1005
|
/**
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1006
|
+
* @param {Object} options - Options to create the DymoAPI instance.
|
|
1007
|
+
* @param {string} [options.rootApiKey] - The root API key.
|
|
1008
|
+
* @param {string} [options.apiKey] - The API key.
|
|
1009
|
+
* @param {string} [options.baseUrl] - Whether to use a local server instead of the cloud server.
|
|
1010
|
+
* @param {Object} [options.serverEmailConfig] - The server email config.
|
|
1011
|
+
* @param {Object} [options.rules] - The rules.
|
|
1012
|
+
* @param {Object} [options.resilience] - The resilience config.
|
|
1013
|
+
* @description
|
|
1014
|
+
* This is the main class to interact with the Dymo API. It should be
|
|
1015
|
+
* instantiated with the root API key and the API key. The root API key is
|
|
1016
|
+
* used to fetch the tokens and the API key is used to authenticate the
|
|
1017
|
+
* requests. Requests are retried once by default with exponential backoff.
|
|
1018
|
+
* @example
|
|
1019
|
+
* const dymoApi = new DymoAPI({
|
|
1020
|
+
* rootApiKey: "6bfb7675-6b69-4f8d-9f43-5a6f7f02c6c5",
|
|
1021
|
+
* apiKey: "dm_4c8b7675-6b69-4f8d-9f43-5a6f7f02c6c5"
|
|
1022
|
+
* });
|
|
1023
|
+
*/
|
|
820
1024
|
constructor({
|
|
821
1025
|
rootApiKey = null,
|
|
822
1026
|
apiKey = null,
|
|
@@ -844,19 +1048,11 @@ class DymoAPI {
|
|
|
844
1048
|
headers: {
|
|
845
1049
|
"User-Agent": "DymoAPISDK/1.0.0",
|
|
846
1050
|
"X-Dymo-SDK-Env": "Node",
|
|
847
|
-
"X-Dymo-SDK-Version": "1.2.
|
|
1051
|
+
"X-Dymo-SDK-Version": "1.2.38"
|
|
848
1052
|
}
|
|
849
1053
|
});
|
|
850
1054
|
if (this.rootApiKey || this.apiKey) this.axiosClient.defaults.headers.Authorization = `Bearer ${this.rootApiKey || this.apiKey}`;
|
|
851
1055
|
}
|
|
852
|
-
getEmailPlugins(rules) {
|
|
853
|
-
return [
|
|
854
|
-
rules.deny.includes("NO_MX_RECORDS") ? "mxRecords" : void 0,
|
|
855
|
-
rules.deny.includes("NO_REACHABLE") ? "reachable" : void 0,
|
|
856
|
-
rules.deny.includes("HIGH_RISK_SCORE") ? "riskScore" : void 0,
|
|
857
|
-
rules.deny.includes("NO_GRAVATAR") ? "gravatar" : void 0
|
|
858
|
-
].filter(Boolean);
|
|
859
|
-
}
|
|
860
1056
|
// FUNCTIONS / Private.
|
|
861
1057
|
/**
|
|
862
1058
|
* Validates the given data against the configured validation settings.
|
|
@@ -882,16 +1078,7 @@ class DymoAPI {
|
|
|
882
1078
|
* [Documentation](https://docs.tpeoficial.com/docs/dymo-api/private/data-verifier)
|
|
883
1079
|
*/
|
|
884
1080
|
async isValidData(data) {
|
|
885
|
-
|
|
886
|
-
return await this.resilienceManager.executeWithResilience(
|
|
887
|
-
this.axiosClient,
|
|
888
|
-
{
|
|
889
|
-
method: "POST",
|
|
890
|
-
url: "/private/secure/verify",
|
|
891
|
-
data
|
|
892
|
-
},
|
|
893
|
-
this.resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
894
|
-
);
|
|
1081
|
+
return await isValidDataRaw({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, data });
|
|
895
1082
|
}
|
|
896
1083
|
/**
|
|
897
1084
|
* Validates the given data against the configured validation settings.
|
|
@@ -916,16 +1103,7 @@ class DymoAPI {
|
|
|
916
1103
|
* [Documentation](https://docs.tpeoficial.com/docs/dymo-api/private/data-verifier)
|
|
917
1104
|
*/
|
|
918
1105
|
async isValidDataRaw(data) {
|
|
919
|
-
|
|
920
|
-
return await this.resilienceManager.executeWithResilience(
|
|
921
|
-
this.axiosClient,
|
|
922
|
-
{
|
|
923
|
-
method: "POST",
|
|
924
|
-
url: "/private/secure/verify",
|
|
925
|
-
data
|
|
926
|
-
},
|
|
927
|
-
this.resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
928
|
-
);
|
|
1106
|
+
return await isValidDataRaw({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, data });
|
|
929
1107
|
}
|
|
930
1108
|
/**
|
|
931
1109
|
* Validates the given email against the configured rules.
|
|
@@ -942,20 +1120,11 @@ class DymoAPI {
|
|
|
942
1120
|
*
|
|
943
1121
|
* @example
|
|
944
1122
|
* const valid = await dymoClient.isValidEmail("user@example.com", { deny: ["FRAUD", "NO_MX_RECORDS"] });
|
|
945
|
-
*
|
|
1123
|
+
*
|
|
946
1124
|
* @see [Documentation](https://docs.tpeoficial.com/docs/dymo-api/private/email-validation)
|
|
947
1125
|
*/
|
|
948
1126
|
async isValidEmail(email, rules = this.rules.email) {
|
|
949
|
-
|
|
950
|
-
return await this.resilienceManager.executeWithResilience(
|
|
951
|
-
this.axiosClient,
|
|
952
|
-
{
|
|
953
|
-
method: "POST",
|
|
954
|
-
url: "/private/secure/verify",
|
|
955
|
-
data: { email, plugins: this.getEmailPlugins(rules) }
|
|
956
|
-
},
|
|
957
|
-
this.resilienceManager.getConfig().fallbackEnabled ? fallbackData : void 0
|
|
958
|
-
);
|
|
1127
|
+
return await isValidEmail({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, email, rules });
|
|
959
1128
|
}
|
|
960
1129
|
/**
|
|
961
1130
|
* Validates the given IP against the configured rules.
|
|
@@ -964,7 +1133,7 @@ class DymoAPI {
|
|
|
964
1133
|
* If neither is set, it will throw an error.
|
|
965
1134
|
*
|
|
966
1135
|
* @param {string} [ip] - IP address to validate.
|
|
967
|
-
* @param {
|
|
1136
|
+
* @param {NegativeIPRules[]} [rules] - Optional rules for validation. Some rules are premium features.
|
|
968
1137
|
* @important
|
|
969
1138
|
* **⚠️ TOR_NETWORK and HIGH_RISK_SCORE are [PREMIUM](https://docs.tpeoficial.com/docs/dymo-api/private/data-verifier) features.**
|
|
970
1139
|
* @returns {Promise<Interfaces.IPValidatorResponse>} Resolves with the validation response.
|
|
@@ -972,14 +1141,14 @@ class DymoAPI {
|
|
|
972
1141
|
*
|
|
973
1142
|
* @example
|
|
974
1143
|
* const valid = await isValidIP("52.94.236.248", { deny: ["FRAUD", "TOR_NETWORK", "COUNTRY:RU"] });
|
|
975
|
-
*
|
|
1144
|
+
*
|
|
976
1145
|
* @see [Documentation](https://docs.tpeoficial.com/docs/dymo-api/private/ip-validation)
|
|
977
1146
|
*/
|
|
978
1147
|
async isValidIP(ip, rules = this.rules.ip) {
|
|
979
|
-
return await isValidIP(this.axiosClient, ip, rules);
|
|
1148
|
+
return await isValidIP({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, ip, rules });
|
|
980
1149
|
}
|
|
981
1150
|
/**
|
|
982
|
-
* Validates the given
|
|
1151
|
+
* Validates the given phone against the configured rules.
|
|
983
1152
|
*
|
|
984
1153
|
* This method requires either the root API key or the API key to be set.
|
|
985
1154
|
* If neither is set, it will throw an error.
|
|
@@ -988,16 +1157,16 @@ class DymoAPI {
|
|
|
988
1157
|
* @param {NegativePhoneRules[]} [rules] - Optional rules for validation. Some rules are premium features.
|
|
989
1158
|
* @important
|
|
990
1159
|
* **⚠️ HIGH_RISK_SCORE is a [PREMIUM](https://docs.tpeoficial.com/docs/dymo-api/private/data-verifier) feature.**
|
|
991
|
-
* @returns {Promise<Interfaces.
|
|
1160
|
+
* @returns {Promise<Interfaces.PhoneValidatorResponse>} Resolves with the validation response.
|
|
992
1161
|
* @throws Will throw an error if validation cannot be performed.
|
|
993
1162
|
*
|
|
994
1163
|
* @example
|
|
995
1164
|
* const valid = await dymoClient.isValidPhone("+34617509462", { deny: ["FRAUD", "INVALID"] });
|
|
996
|
-
*
|
|
1165
|
+
*
|
|
997
1166
|
* @see [Documentation](https://docs.tpeoficial.com/docs/dymo-api/private/phone-validation)
|
|
998
1167
|
*/
|
|
999
1168
|
async isValidPhone(phone, rules = this.rules.phone) {
|
|
1000
|
-
return await isValidPhone(this.axiosClient, phone, rules);
|
|
1169
|
+
return await isValidPhone({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, phone, rules });
|
|
1001
1170
|
}
|
|
1002
1171
|
/**
|
|
1003
1172
|
* Protects the given request against the configured rules.
|
|
@@ -1014,11 +1183,11 @@ class DymoAPI {
|
|
|
1014
1183
|
*
|
|
1015
1184
|
* @example
|
|
1016
1185
|
* const protectedReq = await dymoClient.protectReq(req);
|
|
1017
|
-
*
|
|
1186
|
+
*
|
|
1018
1187
|
* @see [Documentation](https://docs.tpeoficial.com/docs/dymo-api/private/data-verifier)
|
|
1019
1188
|
*/
|
|
1020
1189
|
async protectReq(req, rules = this.rules.waf) {
|
|
1021
|
-
return await protectReq(this.axiosClient, req, rules);
|
|
1190
|
+
return await protectReq({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, req, rules });
|
|
1022
1191
|
}
|
|
1023
1192
|
/**
|
|
1024
1193
|
* Sends an email using the configured email client settings.
|
|
@@ -1048,7 +1217,7 @@ class DymoAPI {
|
|
|
1048
1217
|
*/
|
|
1049
1218
|
async sendEmail(data) {
|
|
1050
1219
|
if (!this.serverEmailConfig && !this.rootApiKey) console.error(`[${config.lib.name}] You must configure the email client settings.`);
|
|
1051
|
-
return await sendEmail(this.axiosClient, { serverEmailConfig: this.serverEmailConfig, ...data });
|
|
1220
|
+
return await sendEmail({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, data: { serverEmailConfig: this.serverEmailConfig, ...data } });
|
|
1052
1221
|
}
|
|
1053
1222
|
/**
|
|
1054
1223
|
* Generates a random number between the provided min and max values.
|
|
@@ -1066,7 +1235,7 @@ class DymoAPI {
|
|
|
1066
1235
|
* [Documentation](https://docs.tpeoficial.com/docs/dymo-api/private/secure-random-number-generator)
|
|
1067
1236
|
*/
|
|
1068
1237
|
async getRandom(data) {
|
|
1069
|
-
return await getRandom(this.axiosClient, data);
|
|
1238
|
+
return await getRandom({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, data });
|
|
1070
1239
|
}
|
|
1071
1240
|
/**
|
|
1072
1241
|
* Extracts structured data from a given string using Textly.
|
|
@@ -1081,7 +1250,7 @@ class DymoAPI {
|
|
|
1081
1250
|
* [Documentation](https://docs.tpeoficial.com/docs/dymo-api/private/textly/text-extraction)
|
|
1082
1251
|
*/
|
|
1083
1252
|
async extractWithTextly(data) {
|
|
1084
|
-
return await extractWithTextly(this.axiosClient, data);
|
|
1253
|
+
return await extractWithTextly({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, data });
|
|
1085
1254
|
}
|
|
1086
1255
|
// FUNCTIONS / Public.
|
|
1087
1256
|
/**
|
|
@@ -1099,7 +1268,7 @@ class DymoAPI {
|
|
|
1099
1268
|
* [Documentation](https://docs.tpeoficial.com/docs/dymo-api/public/prayertimes)
|
|
1100
1269
|
*/
|
|
1101
1270
|
async getPrayerTimes(data) {
|
|
1102
|
-
return await getPrayerTimes(this.axiosClient, data);
|
|
1271
|
+
return await getPrayerTimes({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, data });
|
|
1103
1272
|
}
|
|
1104
1273
|
/**
|
|
1105
1274
|
* Satinizes the input, replacing any special characters with their HTML
|
|
@@ -1114,21 +1283,20 @@ class DymoAPI {
|
|
|
1114
1283
|
* [Documentation](https://docs.tpeoficial.com/docs/dymo-api/public/input-satinizer)
|
|
1115
1284
|
*/
|
|
1116
1285
|
async satinizer(data) {
|
|
1117
|
-
return await satinize(
|
|
1286
|
+
return await this.satinize(data.input);
|
|
1118
1287
|
}
|
|
1119
1288
|
/**
|
|
1120
1289
|
* Satinizes the input, replacing any special characters with their HTML
|
|
1121
1290
|
* entities.
|
|
1122
1291
|
*
|
|
1123
|
-
* @param {
|
|
1124
|
-
* @param {string} data.input - The input to be satinized.
|
|
1292
|
+
* @param {string} input - The input to be satinized.
|
|
1125
1293
|
* @returns {Promise<Interfaces.SatinizedInputAnalysis>} A promise that resolves to the response from the server.
|
|
1126
1294
|
* @throws Will throw an error if there is an issue with the satinization process.
|
|
1127
1295
|
*
|
|
1128
1296
|
* [Documentation](https://docs.tpeoficial.com/docs/dymo-api/public/input-satinizer)
|
|
1129
1297
|
*/
|
|
1130
1298
|
async satinize(input) {
|
|
1131
|
-
return await satinize(this.axiosClient, input);
|
|
1299
|
+
return await satinize({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, input });
|
|
1132
1300
|
}
|
|
1133
1301
|
/**
|
|
1134
1302
|
* Validates a password based on the given parameters.
|
|
@@ -1156,7 +1324,7 @@ class DymoAPI {
|
|
|
1156
1324
|
* [Documentation](https://docs.tpeoficial.com/docs/dymo-api/public/password-validator)
|
|
1157
1325
|
*/
|
|
1158
1326
|
async isValidPwd(data) {
|
|
1159
|
-
return await isValidPwd(this.axiosClient, data);
|
|
1327
|
+
return await isValidPwd({ axiosClient: this.axiosClient, resilienceManager: this.resilienceManager, data });
|
|
1160
1328
|
}
|
|
1161
1329
|
}
|
|
1162
1330
|
module.exports = DymoAPI;
|