You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

579 lines
19 KiB

const fs = require('fs');
const { v4: uuidv4 } = require('uuid');
const _ = require("lodash");
const crypto = require("crypto");
const bcrypt = require("bcrypt");
const sleep = (ms) => {
return new Promise(resolve => {
console.log(`${(ms / 1000).toFixed(2)}s SHOULD WAIT`);
setTimeout(resolve, ms);
})
}
function convertStringToJson(string) {
try {
return JSON.parse(string);
} catch (error) {
console.log(error);
return {};
}
}
const checkCondition = ({ data, condition }, conditionArray = new Set()) => {
let ands = true;
for (const key in condition) {
const conditionValue = condition[key];
if (key == 'or') {
let ors = false;
for (const cond of conditionValue) {
// const orConditionChecking = checkCondition({ data, condition: cond }, conditionArray);
// ors = ors || orConditionChecking.ok;
ors = ors || checkCondition({ data, condition: cond });
// conditionArray = new Set([...conditionArray, ...orConditionChecking.conditionArray])
if (ors) break;
}
ands = ands && ors;
} else {
if (data[key] == 'true') data[key] = true;
if (data[key] == 'false') data[key] = false;
switch (key) {
case [Op.gt]:
ands = ands && data[key] > conditionValue;
// if (!conditionArray.has(key)) conditionArray.add(key);
break;
default:
ands = ands && data[key] == conditionValue;
// if (!conditionArray.has(key)) conditionArray.add(key);
}
}
if (!ands) {
// return { ok: false, conditionArray };
return false;
}
}
// return { ok: true, conditionArray };
return true;
};
function checkingOptions({ question, answer }) {
let options = convertStringToJson(question?.options) || [],
inputType = +question?.inputType || -1;
// console.log("options", options)
if (!Array.isArray(options)) options = [options];
const OptionsValidValue = options.map((option) => {
return parseValue(option?.value);
})
const questionAnswer = answer[question.id];
if (questionAnswer === null || questionAnswer === undefined) return {
ok: false
}
let min = question?.min,
max = question?.max,
length = question?.length,
validation = question?.validation;
switch (inputType) {
case 1: //TEXT
case 3:
if (typeof questionAnswer !== "string") {
return {
statusCode: 400,
message: ErrorMessages.InvalidData
}
}
if (length && isNumber(length) && questionAnswer.length > +length) {
return {
statusCode: 400,
message: ErrorMessages.InvalidTextLength(question.id, length)
}
}
if (validation && isValidRegex(validation)) {
let validationRegex = new RegExp(validation);
if (!validationRegex.test(questionAnswer)) return {
statusCode: 400,
message: ErrorMessages.InvalidTextPattern
}
}
break;
case 2: //NUMBER
if (!isNumber(questionAnswer)) {
return {
statusCode: 400,
message: ErrorMessages.InvalidNumber(question.id)
}
}
if (min && isNumber(min) && +min > +questionAnswer) return {
statusCode: 400,
message: ErrorMessages.InvalidMinNumber(question.id, min)
}
if (max && isNumber(max) && +max < +questionAnswer) return {
statusCode: 400,
message: ErrorMessages.InvalidMaxNumber(question.id, max)
}
break;
case 4: // tak entekhab
case 6:
if (!OptionsValidValue.includes(questionAnswer)) {
return {
statusCode: 400,
message: ErrorMessages.InvalidSelectedOption(question.id)
}
}
break;
case 5: // chand entekhab
case 7:
if (Array.isArray(questionAnswer)) {
for (let ans of questionAnswer) {
if (!OptionsValidValue.includes(ans)) return { statusCode: 400, message: ErrorMessages.InvalidAdditionalData(question.id) };
}
} else if (typeof questionAnswer === "string") {
for (let ans of questionAnswer.split(",")) {
if (!OptionsValidValue.includes(ans)) return { statusCode: 400, message: ErrorMessages.InvalidAdditionalData(question.id) };
}
} else {
return { statusCode: 400, message: ErrorMessages.InvalidData(question.id) };
}
break;
case 8:
if (!isValidDateStrict(questionAnswer)) return { statusCode: 400, message: ErrorMessages.InvalidDate(question.id) };
const questionDate = new Date(questionAnswer);
if (min) {
if (isValidDateStrict(min)) {
if (new Date(min) > questionDate) return {
statusCode: 400,
message: ErrorMessages.InvalidMinDate(question.id)
}
} else if (min === "now()") {
if (new Date() > questionDate) return {
statusCode: 400,
message: ErrorMessages.InvalidMinDate(question.id)
}
} else if (min.includes("day")) {
let days = min.match(/day(s)?\(([0-9]{1,2})\)/)[2];
if (
!isNaN(+days)
// &&
// Date.now() < questionDate
&&
Math.abs(Date.now() - questionDate.getTime()) > convertDaysToSeconds(days)
) {
return {
statusCode: 400,
message: ErrorMessages.InvalidDayMinDate(question.id, days)
}
}
}
}
if (max) {
if (isValidDateStrict(max)) {
if (new Date(max) < questionDate) return {
statusCode: 400,
message: ErrorMessages.InvalidMaxDate(question.id)
}
} else if (max === "now()") {
if (new Date() < questionDate) return {
statusCode: 400,
message: ErrorMessages.InvalidMaxDate(question.id)
}
} else if (max.includes("day")) {
let days = max.match(/day(s)?\(([0-9]{1,2})\)/)[2];
if (
!isNaN(+days)
// &&
// Date.now() < questionDate
&&
Math.abs(Date.now() - questionDate.getTime()) < convertDaysToSeconds(days)
) {
return {
statusCode: 400,
message: ErrorMessages.InvalidDayMaxDate(question.id, days)
}
}
}
}
break;
case 10:
if (!isBoolean(questionAnswer)) return { statusCode: 400, message: ErrorMessages.InvalidBooleanData(question.id) }
break;
default:
return {
statusCode: 400,
message: ErrorMessages.InvalidInputType(question.id)
}
}
return true;
}
async function checkingConditionTypes({ fQ, question, mojazData = [], answer }) {
let check = false, checkOption = false, inputType = 10;
let condition = {}, isRequired = null;
let requiredCondition = {}, validation = null;
requiredCondition = question?.requiredCondition || {};
validation = question?.validation || null;
condition = convertStringToJson(fQ?.condition) || convertStringToJson(question?.condition) || null;
isRequired = returnBooleanOfArguments([fQ?.isRequired, question?.required]);
inputType = question?.inputType || 10;
console.log("question id:", question.id)
console.log("requiredCondition", requiredCondition);
// console.log("validation",validation);
console.log("condition", condition);
console.log("isRequired", isRequired);
// console.log("options",options);
console.log("inputType", inputType);
//1.check requiredConditions
//2.check Conditions and check they required or not
//3.check Options and check they required or not
//4.check validation
//5.remove extra data
//1.check requiredConditions
check = checkCondition({ data: answer, condition: requiredCondition })
if (check.ok === false) throw `${defects[question.id]}`;
mojazData = [...new Set([...mojazData, ...check.conditionArray])];
console.log("DONE SOLVE requiredConditions")
//2.check Conditions and check they required or not
check = checkCondition({ data: answer, condition: condition })
checkOption = checkingOptions({ question, answer });
if ((check.ok === false || checkOption.ok === false) && isRequired === true) throw `${defects[question.id]}`;
if (checkOption.ok === true) mojazData.push(`${question.id}`);
mojazData = [...new Set([...mojazData, ...check.conditionArray])];
console.log("DONE SOLVE condition")
//3.check Validation (ro bayad beporsam az aghaye sadeghi)
console.log("mojazDataAfter-requiredCondition-condition", mojazData);
return {
mojazData
}
}
function isBoolean(value) {
if(typeof value==="string"){
if(value?.trim()?.toLowerCase()=="true") value=true;
else if(value?.trim()?.toLowerCase()=="false") value=false;
}
return typeof value === 'boolean';
}
function safeRegexFromString(regexString) {
try {
const parts = regexString.split('/');
if (parts.length < 3) {
// اگر / وجود نداشت، کل رشته به عنوان pattern در نظر گرفته میشود
return new RegExp(regexString);
}
const pattern = parts.slice(1, -1).join('/'); // برای الگوهایی که شامل / هستند
const flags = parts[parts.length - 1];
return new RegExp(pattern, flags);
} catch (error) {
console.error("Invalid regex string:", error);
// return null;
throw `invalidRegexFrom:${regexString}`;
}
}
function returnBooleanOfArguments(array){
if(!Array.isArray(array)) return false;
for(let i of array){
if(isBoolean(i)) return !!i;
}
return false;
}
function isNumber(value) {
// return typeof value === 'number' && !isNaN(value) && isFinite(value);
// return !isNaN(parseInt(value)) && isFinite(value);
return !isNaN(+value) && isFinite(+value);
}
function parseValue(value) {
// اگر ورودی از قبل رشته نباشد، همان مقدار را برگردانیم.
if (typeof value !== 'string') {
return value;
}
const trimmed = value.trim();
// // بررسی مقادیر بولی
// if (trimmed.toLowerCase() === 'true') return true;
// if (trimmed.toLowerCase() === 'false') return false;
// بررسی null و undefined
if (trimmed.toLowerCase() === 'null') return null;
if (trimmed.toLowerCase() === 'undefined') return undefined;
// بررسی عدد
// اگر رشته به صورت عددی تفسیر شود (توجه کنید که isNaN('') === false، پس باید رشته خالی هم چک شود)
if (trimmed !== '' && isNumber(trimmed)) {
return Number(trimmed);
}
// بررسی تاریخ
// Date.parse در صورت عدم توانایی در تبدیل، مقدار NaN برمیگرداند.
const timestamp = Date.parse(trimmed);
if (!isNaN(timestamp)) {
return new Date(trimmed);
}
// بررسی آرایه یا شیء (در صورتی که رشته با { یا [ شروع و به ترتیب با } یا ] پایان یابد)
if (
(trimmed.startsWith('{') && trimmed.endsWith('}')) ||
(trimmed.startsWith('[') && trimmed.endsWith(']'))
) {
try {
return JSON.parse(trimmed);
} catch (e) {
// در صورت بروز خطا، به ادامه کد میرویم
}
}
// در نهایت اگر هیچ یک از شرایط بالا صدق نکرد،
// رشته اصلی را برمیگردانیم.
return value;
}
function isValidDateStrict(date) {
if (date instanceof Date && !isNaN(date.getTime())) {
return true;
}
if (typeof date === "string") {
const parsed = new Date(date);
return parsed instanceof Date && !isNaN(parsed.getTime());
}
return false;
}
function convertDaysToSeconds(days){
return days * 24 * 3600 * 1000;
}
function isValidRegex(str) {
try {
if (str.startsWith('/')) {
let endSlashIndex = -1;
let backslashCount = 0;
// شروع جستجو از ایندکس ۱ (بعد از اسلش اول)
for (let i = 1; i < str.length; i++) {
if (str[i] === '\\') {
backslashCount++;
} else {
if (str[i] === '/' && backslashCount % 2 === 0) {
endSlashIndex = i;
break;
}
backslashCount = 0;
}
}
// اگر اسلش پایانی پیدا نشد
if (endSlashIndex === -1) {
return false;
}
const pattern = str.slice(1, endSlashIndex);
const modifiers = str.slice(endSlashIndex + 1);
new RegExp(pattern, modifiers);
} else {
new RegExp(str);
}
return true;
} catch (e) {
return false;
}
}
async function saveLargeObject(obj, filename) {
const ws = fs.createWriteStream(filename, { encoding: 'utf8' });
ws.write('{\n');
const entries = Object.entries(obj);
for (let i = 0; i < entries.length; i++) {
const [key, value] = entries[i];
// stringify مقدار
const chunk = JSON.stringify(value, null, 2)
.split('\n')
.map((line, idx) => idx === 0
? ` "${key}": ${line}`
: ` ${line}`)
.join('\n');
// اگر اولین نیست، قبلش کاما بگذار
ws.write((i > 0 ? ',\n' : '') + chunk);
}
ws.write('\n}\n');
ws.end();
await new Promise((res, rej) => {
ws.on('finish', res);
ws.on('error', rej);
});
console.log(`فایل ${filename} ذخیره شد.`);
}
async function realPayloadGenerator(preferedPayload={},user,{restricted=false,workingTimeLimit=true,canRefreshSession=true,passwordLogin=true,otpLogin=true}={}) {
user = user || await createUser();
const roleId = preferedPayload?.roleId || 23;
const networkId = preferedPayload?.networkId || 10001;
let network = await db.network.findByPk(networkId,{
raw:true,
paranoid:false
});
if (!network) {
network = await db.network.create({
id: networkId,
name: `GodNetwork${networkId}`,
level: preferedPayload?.level || 1,
[`level${preferedPayload?.level || 1}Id`]:networkId,
siamId: uuidv4()
});
}
let role = await db.role.findByPk(roleId,{
raw:true,
paranoid:false
});
if (!role) {
role = await db.role.create({
id: roleId,
name: `GodRole ${roleId}`,
description: `Test Role ${roleId} Description`,
workingTimeLimit,
canRefreshSession,
passwordLogin,
otpLogin,
restricted
});
}
let position = await db.position.create({
name: "Test Position 1",
description: "Test Position 1 With 1 Max User",
maxUsers: preferedPayload?.maxUsers || 1,
});
let userRole = await db.userRole.create({
id: -getRandomNumber(1e7, 1e8-1),
userId: user.id,
roleId: role.id,
networkId: network.id,
positionId: position.id,
description: `Test UserRole ${user.id} Description`,
expirationTime:preferedPayload?.expirationTime || null,
});
let networkPosition = await db.networkPosition.create({
networkId: +await network.id,
positionId: +await position.id,
level: +await network.level
});
let positionRole = await db.positionRole.create({
roleId: role.id,
positionId: position.id,
level: +await network.level
})
return {
payload:{
username: user.username,
userId: user.id.toString(),
userRoleId: userRole.id.toString(),
networkId: network.id.toString(),
level:+network.level,
roleId:role.id.toString(),
include:[],
exclude:[],
positionId: position.id.toString(),
timestamp: Date.now(),
jwtId: getRandomNumber(1, 1e3),
ip:getRandomIp(),
agent:null,
...preferedPayload
},
network,
role,
position,
userRole,
user,
networkPosition,
positionRole
}
}
const hash = (data) =>
crypto.createHash("sha256").update(JSON.stringify(data)).digest("hex");
async function createUser(data){
const newUser = await db.user.create({
firstName: `createUserTestName${getRandomNumber(1, 1e3)}`,
lastName: `createUserTestLastName${getRandomNumber(1, 1e3)}`,
nationalId: getRandomNumber(1e9, 9e9).toString(),
username: `baseuser${getRandomNumber(1e6, 1e9-1)}`,
cellphone: `0965${getRandomNumber(1e6, 9e6)}`,
password: await bcrypt.hash("Password@123",10),
hash:hash("Password@123"),
status: 0,
consecutiveLoginFailures:0,
birthDate:`${getRandomNumber(1900, 1950)}/${getRandomNumber(1, 12)}/${getRandomNumber(1, 31)}`,
...data
});
return newUser;
}
function getRandomIp(){
return `${getRandomNumber(1, 254)}.${getRandomNumber(1, 254)}.${getRandomNumber(1, 254)}.${getRandomNumber(1, 254)}`;
}
function getRandomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function areArraysEqualUnordered(arr1, arr2) {
if (arr1?.length !== arr2?.length) {
return false;
}
return arr1.every(item => arr2.includes(item)) &&
arr2.every(item => arr1.includes(item));
}
function getRandomElement(arr) {
if (!Array.isArray(arr) || arr.length === 0) {
throw new Error("آرایه معتبر نیست");
}
const randomIndex = Math.floor(Math.random() * arr.length);
return arr[randomIndex];
}
module.exports = {
sleep,
convertStringToJson,
isValidRegex,
convertDaysToSeconds,
isValidDateStrict,
parseValue,
isNumber,
returnBooleanOfArguments,
safeRegexFromString,
isBoolean,
checkingConditionTypes,
checkingOptions,
checkCondition,
saveLargeObject,
realPayloadGenerator,
hash,
createUser,
getRandomIp,
getRandomNumber,
areArraysEqualUnordered,
getRandomElement
}