This commit is contained in:
yaoyanwei
2025-08-04 16:25:38 +08:00
parent 8d542ea201
commit 4b2bb35c20
46 changed files with 5128 additions and 0 deletions

65
tools/date.tool.js Normal file
View File

@@ -0,0 +1,65 @@
var DateTool = {};
// -------- Date & timestamp -------
DateTool.isDate = function(date)
{
if (Object.prototype.toString.call(date) === "[object Date]") {
return !isNaN(date.getTime());
}
return false;
};
DateTool.tagToDate = function(tag)
{
if(typeof tag !== "string")
return null;
[year, month, day] = tag.split("-");
var d = new Date(year, month - 1, day, 0, 0, 0, 0);
return DateTool.isDate(d) ? d : null;
};
DateTool.dateToTag = function(d)
{
if(!DateTool.isDate(d))
return "";
var year = '' + d.getFullYear();
var month = '' + (d.getMonth() + 1);
var day = '' + d.getDate();
if (day.length < 2) day = '0' + day;
return [year, month, day].join('-');
};
DateTool.getStartOfDay = function(date){
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
}
DateTool.addDays = function(date, days) {
return new Date(date.getTime() + days*24*60*60*1000);
}
DateTool.addHours = function(date, hours) {
return new Date(date.getTime() + hours*60*60*1000);
}
DateTool.addMinutes = function(date, minutes) {
return new Date(date.getTime() + minutes*60000);
}
DateTool.minDate = function()
{
return new Date(-8640000000000000);
}
DateTool.maxDate = function()
{
return new Date(8640000000000000);
}
DateTool.countDays = function(from, to) {
const ms_per_day = 1000 * 60 * 60 * 24;
return Math.round((to - from) / ms_per_day);
}
module.exports = DateTool;

121
tools/email.tool.js Normal file
View File

@@ -0,0 +1,121 @@
const nodeMailer = require('nodemailer');
const config = require('../config');
const fs = require('fs');
const path = require('path');
//Send One Mail
exports.SendEmail = function(email_to, subject, text, callback){
if(!config.smtp_enabled)
return;
console.log("Sending email to: " + email_to);
let transporter = nodeMailer.createTransport({
host: config.smtp_server,
port: config.smtp_port, //Port must be 465 (encrypted) or 587 (STARTTSL, first pre-request is unencrypted to know the encryption method supported, followed by encrypted request)
secure: (config.smtp_port == "465"), //On port 587 secure must be false since it will first send unsecured pre-request to know which encryption to use
requireTLS: true, //Force using encryption on port 587 on the second request
auth: {
user: config.smtp_user,
pass: config.smtp_password,
}
});
let mailOptions = {
from: '"' + config.smtp_name + '" <' + config.smtp_email + '>', // sender address
to: email_to, // list of receivers
subject: subject, // Subject line
//text: text, // plain text body
html: text, // html body
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
if(callback)
callback(false, error);
console.log(error);
return;
}
if(callback)
callback(true);
});
};
//Send same mail to multiple recipients (emails array)
exports.SendEmailList = function(emails, subject, text, callback){
if(!config.smtp_enabled)
return;
if(!Array.isArray(emails))
return;
if(emails.length == 0)
return;
let transporter = nodeMailer.createTransport({
pool: true,
host: config.smtp_server,
port: config.smtp_port,
secure: (config.smtp_port == "465"),
requireTLS: true,
auth: {
user: config.smtp_user,
pass: config.smtp_password,
}
});
var email_list = emails;
var email_from = '"' + config.smtp_name + '" <' + config.smtp_email + '>';
var total = emails.length;
var sent_success = 0;
var sent_count = 0;
var ended = false;
transporter.on("idle", function () {
while (transporter.isIdle() && email_list.length > 0)
{
var email_to = email_list.shift();
let mailOptions = {
from: email_from,
to: email_to,
subject: subject,
html: text,
};
transporter.sendMail(mailOptions, (error, info) => {
sent_count++;
if (!error) {
sent_success++;
}
if(email_list.length == 0 && sent_count == total && !ended)
{
ended = true;
if(callback)
callback(sent_success);
}
});
}
});
};
exports.ReadTemplate = function(template)
{
const rootDir = path.dirname(require.main.filename);
const fullpath = rootDir + "/emails/" + template;
try{
const html = fs.readFileSync(fullpath, "utf8");
return html;
}
catch
{
return null;
}
}

16
tools/file.tool.js Normal file
View File

@@ -0,0 +1,16 @@
var fs = require('fs');
exports.readFileArraySync = function(filename){
var data = fs.readFileSync(filename, {encoding: "utf8"});
var adata = data.split('\r\n');
return adata;
};
exports.readFileArray = function(filename, callback){
fs.readFile(filename, {encoding: "utf8"}, function(data){
var adata = data.split('\r\n');
callback(adata);
});
};

46
tools/limiter.tool.js Normal file
View File

@@ -0,0 +1,46 @@
const RateLimit = require('express-rate-limit');
//const Slowdown = require('express-slow-down');
const config = require('../config.js');
exports.limit = function(app)
{
//Restrict to access from domain only
app.use(function(req, res, next)
{
//Ip address
req.ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
if(config.ip_blacklist.includes(req.ip))
return res.status(401).send("Forbidden");
//Check server host
var host = req.hostname;
if(config.api_url && host != config.api_url)
return res.status(401).send("Forbidden");
next();
});
//Rate limiter
if(config.limiter_proxy)
app.enable('trust proxy'); // only if your server is behind a reverse proxy
app.use(RateLimit({
windowMs: config.limiter_window,
max: config.limiter_max,
skip: function(req) { return config.ip_whitelist.includes(req.ip); },
}));
app.auth_limiter = RateLimit({
windowMs: config.limiter_window,
max: config.limiter_auth_max,
skip: function(req) { return config.ip_whitelist.includes(req.ip); },
handler: function (req, res) {
res.status(429).send({error: "Too many requests!"});
},
});
app.post_limiter = RateLimit({
windowMs: config.limiter_window,
max: config.limiter_post_max,
skip: function(req) { return config.ip_whitelist.includes(req.ip); },
});
}

92
tools/validator.tool.js Normal file
View File

@@ -0,0 +1,92 @@
const FileTool = require('../tools/file.tool');
var Validator = {};
Validator.isInteger = function(value){
return Number.isInteger(value);
}
Validator.isNumber = function(value){
return !isNaN(parseFloat(value)) && isFinite(value);
};
Validator.validateUsername = function(username){
if(typeof username != "string")
return false;
if(username.length < 3 || username.length > 50)
return false;
//Cant have some special characters, must be letters or digits and start with a letter
var regex = /^[a-zA-Z][a-zA-Z\d]+$/;
if(!regex.test(username))
return false;
return true;
}
Validator.validatePhone = function(phone){
if(typeof phone != "string")
return false;
if(phone.length < 7)
return false;
if(!/^[0-9]+$/.test(phone))
return false;
return true;
}
Validator.validateEmail = function(email){
if(typeof email != "string")
return false;
if(email.length < 7 || email.length > 320)
return false;
var regex_email = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if(!regex_email.test(email))
return false;
return true;
}
Validator.validatePassword = function(pass){
if(typeof pass != "string")
return false;
if(pass.length < 4 || pass.length > 50)
return false;
//Password validations could be improved here
return true;
}
Validator.countQuantity = function(array){
if (!array || !Array.isArray(array))
return 0;
var total = 0;
for (const elem of array) {
var q = elem.quantity || 1;
total += q;
}
return total;
}
//Returns true or false checking if array has the expected quantity
Validator.validateArray = function(array, quantity){
var nb = Validator.countQuantity(array);
return quantity == nb;
}
module.exports = Validator;

100
tools/web.tool.js Normal file
View File

@@ -0,0 +1,100 @@
var http = require('http');
var url = require('url');
var WebTool = {};
// -------- Http -----------------
WebTool.get = function(path, callback) {
var hostname = url.parse(path).hostname;
var pathname = url.parse(path).pathname;
var post_options = {
host: hostname,
port: '80',
path: pathname,
method: 'GET'
};
var request = http.request(post_options, function(res) {
res.setEncoding('utf8');
var oData = "";
res.on('data', function (chunk) {
oData += chunk;
});
res.on('end', function(){
callback(oData, res.statusCode);
});
});
request.end();
};
WebTool.post = function(path, data, callback) {
var post_data = JSON.stringify(data);
var hostname = url.parse(path).hostname;
var pathname = url.parse(path).pathname;
var post_options = {
host: hostname,
port: '80',
path: pathname,
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': post_data.length
}
};
var request = http.request(post_options, function(res) {
res.setEncoding('utf8');
var oData = "";
res.on('data', function (chunk) {
oData += chunk;
});
res.on('end', function(){
callback(oData, res.statusCode);
});
});
request.write(post_data);
request.end();
};
WebTool.toObject = function(json)
{
try{
var data = JSON.parse(json);
return data;
}
catch{
return {};
}
}
WebTool.toJson = function(data)
{
try{
var data = JSON.stringify(json);
return data;
}
catch{
return "";
}
}
WebTool.GenerateUID = function(length, numberOnly)
{
var result = '';
var characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
if(numberOnly)
characters = '0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
module.exports = WebTool;