2021-07-27 20:47:13 +03:00
|
|
|
const tcpp = require("tcp-ping");
|
2021-07-01 12:00:23 +03:00
|
|
|
const Ping = require("./ping-lite");
|
2021-07-27 20:47:13 +03:00
|
|
|
const { R } = require("redbean-node");
|
2021-08-02 19:08:46 +03:00
|
|
|
const { debug } = require("../src/util");
|
2021-08-09 08:34:44 +03:00
|
|
|
const passwordHash = require("./password-hash");
|
|
|
|
const dayjs = require("dayjs");
|
2021-08-23 17:30:11 +03:00
|
|
|
const { Resolver } = require("dns");
|
2021-10-05 14:13:57 +03:00
|
|
|
const child_process = require("child_process");
|
2021-08-09 08:34:44 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Init or reset JWT secret
|
|
|
|
* @returns {Promise<Bean>}
|
|
|
|
*/
|
|
|
|
exports.initJWTSecret = async () => {
|
|
|
|
let jwtSecretBean = await R.findOne("setting", " `key` = ? ", [
|
|
|
|
"jwtSecret",
|
|
|
|
]);
|
|
|
|
|
|
|
|
if (! jwtSecretBean) {
|
|
|
|
jwtSecretBean = R.dispense("setting");
|
|
|
|
jwtSecretBean.key = "jwtSecret";
|
|
|
|
}
|
|
|
|
|
|
|
|
jwtSecretBean.value = passwordHash.generate(dayjs() + "");
|
|
|
|
await R.store(jwtSecretBean);
|
|
|
|
return jwtSecretBean;
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-07-01 09:03:06 +03:00
|
|
|
|
|
|
|
exports.tcping = function (hostname, port) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
tcpp.ping({
|
|
|
|
address: hostname,
|
|
|
|
port: port,
|
|
|
|
attempts: 1,
|
2021-08-05 14:04:38 +03:00
|
|
|
}, function (err, data) {
|
2021-07-01 09:03:06 +03:00
|
|
|
|
|
|
|
if (err) {
|
|
|
|
reject(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data.results.length >= 1 && data.results[0].err) {
|
|
|
|
reject(data.results[0].err);
|
|
|
|
}
|
|
|
|
|
|
|
|
resolve(Math.round(data.max));
|
|
|
|
});
|
|
|
|
});
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-07-01 12:00:23 +03:00
|
|
|
|
2021-08-10 16:03:14 +03:00
|
|
|
exports.ping = async (hostname) => {
|
|
|
|
try {
|
2021-08-10 17:00:29 +03:00
|
|
|
return await exports.pingAsync(hostname);
|
2021-08-10 16:03:14 +03:00
|
|
|
} catch (e) {
|
|
|
|
// If the host cannot be resolved, try again with ipv6
|
|
|
|
if (e.message.includes("service not known")) {
|
2021-08-10 17:00:29 +03:00
|
|
|
return await exports.pingAsync(hostname, true);
|
2021-08-10 16:03:14 +03:00
|
|
|
} else {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-08-10 16:03:14 +03:00
|
|
|
|
|
|
|
exports.pingAsync = function (hostname, ipv6 = false) {
|
2021-07-01 12:00:23 +03:00
|
|
|
return new Promise((resolve, reject) => {
|
2021-08-10 16:03:14 +03:00
|
|
|
const ping = new Ping(hostname, {
|
|
|
|
ipv6
|
|
|
|
});
|
2021-07-01 12:00:23 +03:00
|
|
|
|
2021-08-10 16:03:14 +03:00
|
|
|
ping.send(function (err, ms, stdout) {
|
2021-07-01 12:00:23 +03:00
|
|
|
if (err) {
|
2021-08-10 16:03:14 +03:00
|
|
|
reject(err);
|
2021-07-01 12:00:23 +03:00
|
|
|
} else if (ms === null) {
|
2021-09-20 11:22:18 +03:00
|
|
|
reject(new Error(stdout));
|
2021-07-01 12:00:23 +03:00
|
|
|
} else {
|
2021-09-20 11:22:18 +03:00
|
|
|
resolve(Math.round(ms));
|
2021-07-01 12:00:23 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-07-09 09:14:03 +03:00
|
|
|
|
2021-08-23 01:05:48 +03:00
|
|
|
exports.dnsResolve = function (hostname, resolver_server, rrtype) {
|
|
|
|
const resolver = new Resolver();
|
|
|
|
resolver.setServers([resolver_server]);
|
|
|
|
return new Promise((resolve, reject) => {
|
2021-08-23 17:30:11 +03:00
|
|
|
if (rrtype == "PTR") {
|
2021-08-23 01:05:48 +03:00
|
|
|
resolver.reverse(hostname, (err, records) => {
|
|
|
|
if (err) {
|
|
|
|
reject(err);
|
|
|
|
} else {
|
|
|
|
resolve(records);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
resolver.resolve(hostname, rrtype, (err, records) => {
|
|
|
|
if (err) {
|
|
|
|
reject(err);
|
|
|
|
} else {
|
|
|
|
resolve(records);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2021-09-20 11:22:18 +03:00
|
|
|
});
|
|
|
|
};
|
2021-08-23 01:05:48 +03:00
|
|
|
|
2021-07-09 09:14:03 +03:00
|
|
|
exports.setting = async function (key) {
|
2021-07-31 17:02:30 +03:00
|
|
|
let value = await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [
|
2021-07-27 20:47:13 +03:00
|
|
|
key,
|
2021-07-31 17:02:30 +03:00
|
|
|
]);
|
|
|
|
|
|
|
|
try {
|
2021-08-02 19:08:46 +03:00
|
|
|
const v = JSON.parse(value);
|
2021-09-20 11:22:18 +03:00
|
|
|
debug(`Get Setting: ${key}: ${v}`);
|
2021-08-02 19:08:46 +03:00
|
|
|
return v;
|
2021-07-31 17:02:30 +03:00
|
|
|
} catch (e) {
|
|
|
|
return value;
|
|
|
|
}
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-07-09 09:14:03 +03:00
|
|
|
|
2021-07-21 21:02:35 +03:00
|
|
|
exports.setSetting = async function (key, value) {
|
|
|
|
let bean = await R.findOne("setting", " `key` = ? ", [
|
2021-07-27 20:47:13 +03:00
|
|
|
key,
|
2021-09-20 11:22:18 +03:00
|
|
|
]);
|
2021-08-05 14:04:38 +03:00
|
|
|
if (!bean) {
|
2021-09-20 11:22:18 +03:00
|
|
|
bean = R.dispense("setting");
|
2021-07-21 21:02:35 +03:00
|
|
|
bean.key = key;
|
|
|
|
}
|
2021-07-31 17:02:30 +03:00
|
|
|
bean.value = JSON.stringify(value);
|
2021-09-20 11:22:18 +03:00
|
|
|
await R.store(bean);
|
|
|
|
};
|
2021-07-21 21:02:35 +03:00
|
|
|
|
2021-07-09 09:14:03 +03:00
|
|
|
exports.getSettings = async function (type) {
|
2021-07-31 16:57:58 +03:00
|
|
|
let list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [
|
2021-07-27 20:47:13 +03:00
|
|
|
type,
|
2021-09-20 11:22:18 +03:00
|
|
|
]);
|
2021-07-09 09:14:03 +03:00
|
|
|
|
|
|
|
let result = {};
|
|
|
|
|
|
|
|
for (let row of list) {
|
2021-07-31 17:02:30 +03:00
|
|
|
try {
|
|
|
|
result[row.key] = JSON.parse(row.value);
|
|
|
|
} catch (e) {
|
|
|
|
result[row.key] = row.value;
|
|
|
|
}
|
2021-07-09 09:14:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-07-21 07:09:09 +03:00
|
|
|
|
2021-07-31 16:57:58 +03:00
|
|
|
exports.setSettings = async function (type, data) {
|
|
|
|
let keyList = Object.keys(data);
|
|
|
|
|
|
|
|
let promiseList = [];
|
|
|
|
|
|
|
|
for (let key of keyList) {
|
|
|
|
let bean = await R.findOne("setting", " `key` = ? ", [
|
|
|
|
key
|
|
|
|
]);
|
|
|
|
|
|
|
|
if (bean == null) {
|
|
|
|
bean = R.dispense("setting");
|
|
|
|
bean.type = type;
|
|
|
|
bean.key = key;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bean.type === type) {
|
|
|
|
bean.value = JSON.stringify(data[key]);
|
2021-09-20 11:22:18 +03:00
|
|
|
promiseList.push(R.store(bean));
|
2021-07-31 16:57:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
await Promise.all(promiseList);
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-07-31 16:57:58 +03:00
|
|
|
|
2021-07-21 07:09:09 +03:00
|
|
|
// ssl-checker by @dyaa
|
|
|
|
// param: res - response object from axios
|
|
|
|
// return an object containing the certificate information
|
|
|
|
|
|
|
|
const getDaysBetween = (validFrom, validTo) =>
|
|
|
|
Math.round(Math.abs(+validFrom - +validTo) / 8.64e7);
|
|
|
|
|
|
|
|
const getDaysRemaining = (validFrom, validTo) => {
|
|
|
|
const daysRemaining = getDaysBetween(validFrom, validTo);
|
|
|
|
if (new Date(validTo).getTime() < new Date().getTime()) {
|
|
|
|
return -daysRemaining;
|
|
|
|
}
|
|
|
|
return daysRemaining;
|
|
|
|
};
|
|
|
|
|
2021-10-01 13:44:32 +03:00
|
|
|
// Fix certificate Info for display
|
|
|
|
// param: info - the chain obtained from getPeerCertificate()
|
|
|
|
const parseCertificateInfo = function (info) {
|
|
|
|
let link = info;
|
|
|
|
|
|
|
|
while (link) {
|
|
|
|
if (!link.valid_from || !link.valid_to) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
link.validTo = new Date(link.valid_to);
|
|
|
|
link.validFor = link.subjectaltname?.replace(/DNS:|IP Address:/g, "").split(", ");
|
|
|
|
link.daysRemaining = getDaysRemaining(new Date(), link.validTo);
|
|
|
|
|
|
|
|
// Move up the chain until loop is encountered
|
|
|
|
if (link.issuerCertificate == null) {
|
|
|
|
break;
|
|
|
|
} else if (link.fingerprint == link.issuerCertificate.fingerprint) {
|
|
|
|
link.issuerCertificate = null;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
link = link.issuerCertificate;
|
|
|
|
}
|
2021-07-21 07:09:09 +03:00
|
|
|
}
|
|
|
|
|
2021-10-01 13:44:32 +03:00
|
|
|
return info;
|
|
|
|
};
|
2021-07-21 07:09:09 +03:00
|
|
|
|
2021-10-01 13:44:32 +03:00
|
|
|
exports.checkCertificate = function (res) {
|
|
|
|
const info = res.request.res.socket.getPeerCertificate(true);
|
|
|
|
const valid = res.request.res.socket.authorized || false;
|
2021-07-21 07:09:09 +03:00
|
|
|
|
2021-10-01 13:44:32 +03:00
|
|
|
const parsedInfo = parseCertificateInfo(info);
|
2021-07-21 07:09:09 +03:00
|
|
|
|
|
|
|
return {
|
2021-10-01 13:44:32 +03:00
|
|
|
valid: valid,
|
|
|
|
certInfo: parsedInfo
|
2021-07-21 07:09:09 +03:00
|
|
|
};
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-08-05 14:04:38 +03:00
|
|
|
|
|
|
|
// Check if the provided status code is within the accepted ranges
|
|
|
|
// Param: status - the status code to check
|
|
|
|
// Param: accepted_codes - an array of accepted status codes
|
|
|
|
// Return: true if the status code is within the accepted ranges, false otherwise
|
|
|
|
// Will throw an error if the provided status code is not a valid range string or code string
|
|
|
|
|
|
|
|
exports.checkStatusCode = function (status, accepted_codes) {
|
|
|
|
if (accepted_codes == null || accepted_codes.length === 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const code_range of accepted_codes) {
|
|
|
|
const code_range_split = code_range.split("-").map(string => parseInt(string));
|
|
|
|
if (code_range_split.length === 1) {
|
|
|
|
if (status === code_range_split[0]) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else if (code_range_split.length === 2) {
|
|
|
|
if (status >= code_range_split[0] && status <= code_range_split[1]) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw new Error("Invalid status code range");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-08-30 09:55:33 +03:00
|
|
|
|
|
|
|
exports.getTotalClientInRoom = (io, roomName) => {
|
|
|
|
|
|
|
|
const sockets = io.sockets;
|
|
|
|
|
|
|
|
if (! sockets) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const adapter = sockets.adapter;
|
|
|
|
|
|
|
|
if (! adapter) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const room = adapter.rooms.get(roomName);
|
|
|
|
|
|
|
|
if (room) {
|
|
|
|
return room.size;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-09-11 14:40:03 +03:00
|
|
|
|
|
|
|
exports.allowDevAllOrigin = (res) => {
|
|
|
|
if (process.env.NODE_ENV === "development") {
|
|
|
|
exports.allowAllOrigin(res);
|
|
|
|
}
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-09-11 14:40:03 +03:00
|
|
|
|
|
|
|
exports.allowAllOrigin = (res) => {
|
|
|
|
res.header("Access-Control-Allow-Origin", "*");
|
|
|
|
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-09-16 17:48:28 +03:00
|
|
|
|
|
|
|
exports.checkLogin = (socket) => {
|
|
|
|
if (! socket.userID) {
|
|
|
|
throw new Error("You are not logged in.");
|
|
|
|
}
|
2021-09-20 11:22:18 +03:00
|
|
|
};
|
2021-10-05 14:13:57 +03:00
|
|
|
|
|
|
|
exports.startUnitTest = async () => {
|
|
|
|
console.log("Starting unit test...");
|
|
|
|
const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm";
|
|
|
|
const child = child_process.spawn(npm, ["run", "jest"]);
|
|
|
|
|
|
|
|
child.stdout.on("data", (data) => {
|
|
|
|
console.log(data.toString());
|
|
|
|
});
|
|
|
|
|
|
|
|
child.stderr.on("data", (data) => {
|
|
|
|
console.log(data.toString());
|
|
|
|
});
|
|
|
|
|
|
|
|
child.on("close", function (code) {
|
|
|
|
console.log("Jest exit code: " + code);
|
2021-10-05 15:40:40 +03:00
|
|
|
process.exit(code);
|
2021-10-05 14:13:57 +03:00
|
|
|
});
|
|
|
|
};
|