forked from svrjs/svrjs
Cleaned up the code
This commit is contained in:
parent
4179e4020c
commit
1123f40961
1 changed files with 331 additions and 328 deletions
659
svr.js
659
svr.js
|
@ -805,8 +805,8 @@ function generateErrorStack(errorObject) {
|
||||||
|
|
||||||
// If the error stack starts with the error name, return the original stack (it is V8-style then).
|
// If the error stack starts with the error name, return the original stack (it is V8-style then).
|
||||||
if (errorStack.some(function (errorStackLine) {
|
if (errorStack.some(function (errorStackLine) {
|
||||||
return (errorStackLine.indexOf(errorObject.name) == 0);
|
return (errorStackLine.indexOf(errorObject.name) == 0);
|
||||||
})) {
|
})) {
|
||||||
return errorObject.stack;
|
return errorObject.stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1506,7 +1506,7 @@ if (!disableMods) {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// If it's not a ".tar.gz" file, throw an error about `svrmodpack` support being dropped
|
// If it's not a ".tar.gz" file, throw an error about `svrmodpack` support being dropped
|
||||||
throw new Error("This version of SVR.JS no longer supports \"svrmodpack\" library for SVR.JS mods. Please consider using newer mods with .tar.gz format.")
|
throw new Error("This version of SVR.JS no longer supports \"svrmodpack\" library for SVR.JS mods. Please consider using newer mods with .tar.gz format.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize variables for mod loading
|
// Initialize variables for mod loading
|
||||||
|
@ -2108,7 +2108,9 @@ if (!cluster.isPrimary) {
|
||||||
var snMatches = sniCredentialsSingle.name.match(/^([^:[]*|\[[^]]*\]?)((?::.*)?)$/);
|
var snMatches = sniCredentialsSingle.name.match(/^([^:[]*|\[[^]]*\]?)((?::.*)?)$/);
|
||||||
if(!snMatches[1][0].match(/^\.+$/)) snMatches[1][0] = snMatches[1][0].replace(/\.+$/,"");
|
if(!snMatches[1][0].match(/^\.+$/)) snMatches[1][0] = snMatches[1][0].replace(/\.+$/,"");
|
||||||
server._contexts[server._contexts.length-1][0] = new RegExp("^" + snMatches[1].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.:]*") + ((snMatches[1][0] == "[" || snMatches[1].match(/^(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/)) ? "" : "\.?") + snMatches[2].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.]*") + "$", "i");
|
server._contexts[server._contexts.length-1][0] = new RegExp("^" + snMatches[1].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.:]*") + ((snMatches[1][0] == "[" || snMatches[1].match(/^(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/)) ? "" : "\.?") + snMatches[2].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.]*") + "$", "i");
|
||||||
} catch(ex) {}
|
} catch(ex) {
|
||||||
|
// Can't replace regex, ignoring...
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
server.on("request", reqhandler);
|
server.on("request", reqhandler);
|
||||||
|
@ -3494,23 +3496,23 @@ if (!cluster.isPrimary) {
|
||||||
var customDirListingHeader = fs.existsSync(("." + decodeURIComponent(href) + "/.dirhead").replace(/\/+/g, "/")) ?
|
var customDirListingHeader = fs.existsSync(("." + decodeURIComponent(href) + "/.dirhead").replace(/\/+/g, "/")) ?
|
||||||
fs.readFileSync(("." + decodeURIComponent(href) + "/.dirhead").replace(/\/+/g, "/")).toString() :
|
fs.readFileSync(("." + decodeURIComponent(href) + "/.dirhead").replace(/\/+/g, "/")).toString() :
|
||||||
(fs.existsSync(("." + decodeURIComponent(href) + "/HEAD.html").replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/")) ?
|
(fs.existsSync(("." + decodeURIComponent(href) + "/HEAD.html").replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/")) ?
|
||||||
fs.readFileSync(("." + decodeURIComponent(href) + "/HEAD.html").replace(/\/+/g, "/")).toString() :
|
fs.readFileSync(("." + decodeURIComponent(href) + "/HEAD.html").replace(/\/+/g, "/")).toString() :
|
||||||
"";
|
"";
|
||||||
var customDirListingFooter = fs.existsSync(("." + decodeURIComponent(href) + "/.dirfoot").replace(/\/+/g, "/")) ?
|
var customDirListingFooter = fs.existsSync(("." + decodeURIComponent(href) + "/.dirfoot").replace(/\/+/g, "/")) ?
|
||||||
fs.readFileSync(("." + decodeURIComponent(href) + "/.dirfoot").replace(/\/+/g, "/")).toString() :
|
fs.readFileSync(("." + decodeURIComponent(href) + "/.dirfoot").replace(/\/+/g, "/")).toString() :
|
||||||
(fs.existsSync(("." + decodeURIComponent(href) + "/FOOT.html").replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/")) ?
|
(fs.existsSync(("." + decodeURIComponent(href) + "/FOOT.html").replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/")) ?
|
||||||
fs.readFileSync(("." + decodeURIComponent(href) + "/FOOT.html").replace(/\/+/g, "/")).toString() :
|
fs.readFileSync(("." + decodeURIComponent(href) + "/FOOT.html").replace(/\/+/g, "/")).toString() :
|
||||||
"";
|
"";
|
||||||
|
|
||||||
// Check if custom header has HTML tag
|
// Check if custom header has HTML tag
|
||||||
var headerHasHTMLTag = customDirListingHeader.replace(/<!--(?:(?:(?!--\>)[\s\S])*|)(?:-->|$)/g, "").match(/<html(?![a-zA-Z0-9])(?:"(?:\\(?:[\s\S]|$)|[^\\"])*(?:"|$)|'(?:\\(?:[\s\S]|$)|[^\\'])*(?:'|$)|[^'">])*(?:>|$)/i);
|
var headerHasHTMLTag = customDirListingHeader.replace(/<!--(?:(?:(?!--\>)[\s\S])*|)(?:-->|$)/g, "").match(/<html(?![a-zA-Z0-9])(?:"(?:\\(?:[\s\S]|$)|[^\\"])*(?:"|$)|'(?:\\(?:[\s\S]|$)|[^\\'])*(?:'|$)|[^'">])*(?:>|$)/i);
|
||||||
|
|
||||||
// Generate HTML head and footer based on configuration and custom content
|
// Generate HTML head and footer based on configuration and custom content
|
||||||
var htmlHead = (!configJSON.enableDirectoryListingWithDefaultHead || head == "" ?
|
var htmlHead = (!configJSON.enableDirectoryListingWithDefaultHead || head == "" ?
|
||||||
(!headerHasHTMLTag ?
|
(!headerHasHTMLTag ?
|
||||||
"<!DOCTYPE html><html><head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "</title><meta charset=\"UTF-8\" /><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body>" :
|
"<!DOCTYPE html><html><head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "</title><meta charset=\"UTF-8\" /><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body>" :
|
||||||
customDirListingHeader.replace(/<head>/i, "<head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "</title>")) :
|
customDirListingHeader.replace(/<head>/i, "<head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "</title>")) :
|
||||||
head.replace(/<head>/i, "<head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "</title>")) +
|
head.replace(/<head>/i, "<head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "</title>")) +
|
||||||
(!headerHasHTMLTag ? customDirListingHeader : "") +
|
(!headerHasHTMLTag ? customDirListingHeader : "") +
|
||||||
"<h1>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "</h1><table id=\"directoryListing\"> <tr> <th></th> <th>Filename</th> <th>Size</th> <th>Date</th> </tr>" + (checkPathLevel(decodeURIComponent(origHref)) < 1 ? "" : "<tr><td style=\"width: 24px;\"><img src=\"/.dirimages/return.png\" width=\"24px\" height=\"24px\" alt=\"[RET]\" /></td><td style=\"word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;\"><a href=\"" + (origHref).replace(/\/+/g, "/").replace(/\/[^\/]*\/?$/, "/") + "\">Return</a></td><td></td><td></td></tr>");
|
"<h1>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "</h1><table id=\"directoryListing\"> <tr> <th></th> <th>Filename</th> <th>Size</th> <th>Date</th> </tr>" + (checkPathLevel(decodeURIComponent(origHref)) < 1 ? "" : "<tr><td style=\"width: 24px;\"><img src=\"/.dirimages/return.png\" width=\"24px\" height=\"24px\" alt=\"[RET]\" /></td><td style=\"word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;\"><a href=\"" + (origHref).replace(/\/+/g, "/").replace(/\/[^\/]*\/?$/, "/") + "\">Return</a></td><td></td><td></td></tr>");
|
||||||
|
|
||||||
|
@ -4170,37 +4172,37 @@ if (!cluster.isPrimary) {
|
||||||
|
|
||||||
var rewrittenURL = address;
|
var rewrittenURL = address;
|
||||||
if (!isProxy) {
|
if (!isProxy) {
|
||||||
var doCallback = true;
|
var doCallback = true;
|
||||||
for(var i=(_mapBegIndex ? _mapBegIndex : 0);i<map.length;i++) {
|
for(var i=(_mapBegIndex ? _mapBegIndex : 0);i<map.length;i++) {
|
||||||
var mapEntry = map[i];
|
var mapEntry = map[i];
|
||||||
if(href != "/" && (mapEntry.isNotDirectory || mapEntry.isNotFile) && !_fileState) {
|
if(href != "/" && (mapEntry.isNotDirectory || mapEntry.isNotFile) && !_fileState) {
|
||||||
fs.stat("." + decodeURIComponent(href), function(err, stats) {
|
fs.stat("." + decodeURIComponent(href), function(err, stats) {
|
||||||
var _fileState = 3;
|
var _fileState = 3;
|
||||||
if(err) {
|
if(err) {
|
||||||
_fileState = 3;
|
_fileState = 3;
|
||||||
} else if(stats.isDirectory()) {
|
} else if(stats.isDirectory()) {
|
||||||
_fileState = 2;
|
_fileState = 2;
|
||||||
} else if(stats.isFile()) {
|
} else if(stats.isFile()) {
|
||||||
_fileState = 1;
|
_fileState = 1;
|
||||||
} else {
|
} else {
|
||||||
_fileState = 3;
|
_fileState = 3;
|
||||||
}
|
}
|
||||||
rewriteURL(address, map, callback, _fileState, i);
|
rewriteURL(address, map, callback, _fileState, i);
|
||||||
});
|
});
|
||||||
doCallback = false;
|
doCallback = false;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
if (matchHostname(mapEntry.host) && createRegex(mapEntry.definingRegex).test(address) && !(mapEntry.isNotDirectory && _fileState == 2) && !(mapEntry.isNotFile && _fileState == 1)) {
|
||||||
|
mapEntry.replacements.forEach(function (replacement) {
|
||||||
|
rewrittenURL = rewrittenURL.replace(createRegex(replacement.regex), replacement.replacement);
|
||||||
|
});
|
||||||
|
if (mapEntry.append) rewrittenURL += mapEntry.append;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (matchHostname(mapEntry.host) && createRegex(mapEntry.definingRegex).test(address) && !(mapEntry.isNotDirectory && _fileState == 2) && !(mapEntry.isNotFile && _fileState == 1)) {
|
|
||||||
mapEntry.replacements.forEach(function (replacement) {
|
|
||||||
rewrittenURL = rewrittenURL.replace(createRegex(replacement.regex), replacement.replacement);
|
|
||||||
});
|
|
||||||
if (mapEntry.append) rewrittenURL += mapEntry.append;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if(doCallback) callback(rewrittenURL);
|
if(doCallback) callback(rewrittenURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trailing slash redirection
|
// Trailing slash redirection
|
||||||
|
@ -4236,7 +4238,7 @@ if (!cluster.isPrimary) {
|
||||||
|
|
||||||
// Rewrite URLs
|
// Rewrite URLs
|
||||||
rewriteURL(req.url, rewriteMap, function(rewrittenURL) {
|
rewriteURL(req.url, rewriteMap, function(rewrittenURL) {
|
||||||
if (rewrittenURL != req.url) {
|
if (rewrittenURL != req.url) {
|
||||||
serverconsole.resmessage("URL rewritten: " + req.url + " => " + rewrittenURL);
|
serverconsole.resmessage("URL rewritten: " + req.url + " => " + rewrittenURL);
|
||||||
req.url = rewrittenURL;
|
req.url = rewrittenURL;
|
||||||
uobject = parseURL(req.url);
|
uobject = parseURL(req.url);
|
||||||
|
@ -4287,311 +4289,312 @@ if (!cluster.isPrimary) {
|
||||||
serverconsole.errmessage("Bad request!");
|
serverconsole.errmessage("Bad request!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
// Set response headers
|
|
||||||
if (!isProxy) {
|
|
||||||
var hkh = getCustomHeaders();
|
|
||||||
Object.keys(hkh).forEach(function (hkS) {
|
|
||||||
try {
|
|
||||||
res.setHeader(hkS, hkh[hkS]);
|
|
||||||
} catch (err) {
|
|
||||||
// Headers will not be set.
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if path is forbidden
|
|
||||||
if ((isForbiddenPath(decodedHref, "config") || isForbiddenPath(decodedHref, "certificates")) && !isProxy) {
|
|
||||||
callServerError(403);
|
|
||||||
serverconsole.errmessage("Access to configuration file/certificates is denied.");
|
|
||||||
return;
|
|
||||||
} else if (isIndexOfForbiddenPath(decodedHref, "temp") && !isProxy) {
|
|
||||||
callServerError(403);
|
|
||||||
serverconsole.errmessage("Access to temporary folder is denied.");
|
|
||||||
return;
|
|
||||||
} else if (isIndexOfForbiddenPath(decodedHref, "log") && !isProxy && (configJSON.enableLogging || configJSON.enableLogging == undefined) && !configJSON.enableRemoteLogBrowsing) {
|
|
||||||
callServerError(403);
|
|
||||||
serverconsole.errmessage("Access to log files is denied.");
|
|
||||||
return;
|
|
||||||
} else if (isForbiddenPath(decodedHref, "svrjs") && !isProxy && !exposeServerVersion) {
|
|
||||||
callServerError(403);
|
|
||||||
serverconsole.errmessage("Access to SVR.JS script is denied.");
|
|
||||||
return;
|
|
||||||
} else if ((isForbiddenPath(decodedHref, "svrjs") || isForbiddenPath(decodedHref, "serverSideScripts") || isIndexOfForbiddenPath(decodedHref, "serverSideScriptDirectories")) && !isProxy && (configJSON.disableServerSideScriptExpose || configJSON.disableServerSideScriptExpose === undefined)) {
|
|
||||||
callServerError(403);
|
|
||||||
serverconsole.errmessage("Access to sources is denied.");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
var nonscodeIndex = -1;
|
|
||||||
var authIndex = -1;
|
|
||||||
var regexI = [];
|
|
||||||
|
|
||||||
// Scan for non-standard codes
|
|
||||||
if (!isProxy && nonStandardCodes != undefined) {
|
|
||||||
for (var i = 0; i < nonStandardCodes.length; i++) {
|
|
||||||
if (matchHostname(nonStandardCodes[i].host)) {
|
|
||||||
var isMatch = false;
|
|
||||||
if (nonStandardCodes[i].regex) {
|
|
||||||
// Regex match
|
|
||||||
var createdRegex = createRegex(nonStandardCodes[i].regex, true);
|
|
||||||
isMatch = req.url.match(createdRegex) || href.match(createdRegex);
|
|
||||||
regexI[i] = createdRegex;
|
|
||||||
} else {
|
|
||||||
// Non-regex match
|
|
||||||
isMatch = nonStandardCodes[i].url == href || (os.platform() == "win32" && nonStandardCodes[i].url.toLowerCase() == href.toLowerCase());
|
|
||||||
}
|
|
||||||
if (isMatch) {
|
|
||||||
if (nonStandardCodes[i].scode == 401) {
|
|
||||||
// HTTP authentication
|
|
||||||
if (authIndex == -1) {
|
|
||||||
authIndex = i;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (nonscodeIndex == -1) {
|
|
||||||
if ((nonStandardCodes[i].scode == 403 || nonStandardCodes[i].scode == 451) && nonStandardCodes[i].users !== undefined) {
|
|
||||||
if (nonStandardCodes[i].users.check(reqip)) nonscodeIndex = i;
|
|
||||||
} else {
|
|
||||||
nonscodeIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Set response headers
|
||||||
// Handle non-standard codes
|
if (!isProxy) {
|
||||||
if (nonscodeIndex > -1) {
|
var hkh = getCustomHeaders();
|
||||||
var nonscode = nonStandardCodes[nonscodeIndex];
|
Object.keys(hkh).forEach(function (hkS) {
|
||||||
if (nonscode.scode == 301 || nonscode.scode == 302) {
|
|
||||||
var location = "";
|
|
||||||
if (regexI[nonscodeIndex]) {
|
|
||||||
location = req.url.replace(regexI[nonscodeIndex], nonscode.location);
|
|
||||||
} else if (req.url.split("?")[1] == undefined || req.url.split("?")[1] == null || req.url.split("?")[1] == "" || req.url.split("?")[1] == " ") {
|
|
||||||
location = nonscode.location;
|
|
||||||
} else {
|
|
||||||
location = nonscode.location + "?" + req.url.split("?")[1];
|
|
||||||
}
|
|
||||||
redirect(location, nonscode.scode == 302);
|
|
||||||
return;
|
|
||||||
} else if (nonscode.scode == 403) {
|
|
||||||
callServerError(403);
|
|
||||||
serverconsole.errmessage("Content blocked.");
|
|
||||||
return;
|
|
||||||
} else if (nonscode.scode == 410) {
|
|
||||||
callServerError(410);
|
|
||||||
serverconsole.errmessage("Content is gone.");
|
|
||||||
return;
|
|
||||||
} else if (nonscode.scode == 418) {
|
|
||||||
callServerError(418);
|
|
||||||
serverconsole.errmessage("SVR.JS is always a teapot ;)");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
callServerError(nonscode.scode);
|
|
||||||
serverconsole.errmessage("Client fails receiving content.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle HTTP authentication
|
|
||||||
if (authIndex > -1) {
|
|
||||||
var authcode = nonStandardCodes[authIndex];
|
|
||||||
|
|
||||||
// Function to check if passwords match
|
|
||||||
function checkIfPasswordMatches(list, password, callback, _i) {
|
|
||||||
if (!_i) _i = 0;
|
|
||||||
var cb = function (hash) {
|
|
||||||
var matches = (hash == list[_i].pass);
|
|
||||||
if (matches) {
|
|
||||||
callback(true);
|
|
||||||
} else if (_i >= list.length - 1) {
|
|
||||||
callback(false);
|
|
||||||
} else {
|
|
||||||
checkIfPasswordMatches(list, password, callback, _i + 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var hashedPassword = sha256(password + list[_i].salt);
|
|
||||||
if (list[_i].scrypt) {
|
|
||||||
if (!crypto.scrypt) {
|
|
||||||
callServerError(500, undefined, new Error("SVR.JS doesn't support scrypt-hashed passwords on Node.JS versions without scrypt hash support."));
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
var cacheEntry = scryptCache.find(function (entry) {
|
|
||||||
return (entry.password == hashedPassword && entry.salt == list[_i].salt);
|
|
||||||
});
|
|
||||||
if (cacheEntry) {
|
|
||||||
cb(cacheEntry.hash);
|
|
||||||
} else {
|
|
||||||
crypto.scrypt(password, list[_i].salt, 64, function (err, derivedKey) {
|
|
||||||
if (err) {
|
|
||||||
callServerError(500, undefined, err);
|
|
||||||
} else {
|
|
||||||
var key = derivedKey.toString("hex");
|
|
||||||
scryptCache.push({
|
|
||||||
hash: key,
|
|
||||||
password: hashedPassword,
|
|
||||||
salt: list[_i].salt,
|
|
||||||
addDate: new Date()
|
|
||||||
});
|
|
||||||
cb(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (list[_i].pbkdf2) {
|
|
||||||
if (crypto.__disabled__ !== undefined) {
|
|
||||||
callServerError(500, undefined, new Error("SVR.JS doesn't support PBKDF2-hashed passwords on Node.JS versions without crypto support."));
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
var cacheEntry = pbkdf2Cache.find(function (entry) {
|
|
||||||
return (entry.password == hashedPassword && entry.salt == list[_i].salt);
|
|
||||||
});
|
|
||||||
if (cacheEntry) {
|
|
||||||
cb(cacheEntry.hash);
|
|
||||||
} else {
|
|
||||||
crypto.pbkdf2(password, list[_i].salt, 36250, 64, "sha512", function (err, derivedKey) {
|
|
||||||
if (err) {
|
|
||||||
callServerError(500, undefined, err);
|
|
||||||
} else {
|
|
||||||
var key = derivedKey.toString("hex");
|
|
||||||
pbkdf2Cache.push({
|
|
||||||
hash: key,
|
|
||||||
password: hashedPassword,
|
|
||||||
salt: list[_i].salt,
|
|
||||||
addDate: new Date()
|
|
||||||
});
|
|
||||||
cb(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cb(hashedPassword);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function authorizedCallback(bruteProtection) {
|
|
||||||
try {
|
try {
|
||||||
var ha = getCustomHeaders();
|
res.setHeader(hkS, hkh[hkS]);
|
||||||
ha["WWW-Authenticate"] = "Basic realm=\"" + (authcode.realm ? authcode.realm.replace(/(\\|")/g, "\\$1") : "SVR.JS HTTP Basic Authorization") + "\", charset=\"UTF-8\"";
|
|
||||||
var credentials = req.headers["authorization"];
|
|
||||||
if (!credentials) {
|
|
||||||
callServerError(401, undefined, undefined, ha);
|
|
||||||
serverconsole.errmessage("Content needs authorization.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var credentialsMatch = credentials.match(/^Basic (.+)$/);
|
|
||||||
if (!credentialsMatch) {
|
|
||||||
callServerError(401, undefined, undefined, ha);
|
|
||||||
serverconsole.errmessage("Malformed credentials.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var decodedCredentials = Buffer.from(credentialsMatch[1], "base64").toString("utf8");
|
|
||||||
var decodedCredentialsMatch = decodedCredentials.match(/^([^:]*):(.*)$/);
|
|
||||||
if (!decodedCredentialsMatch) {
|
|
||||||
callServerError(401, undefined, undefined, ha);
|
|
||||||
serverconsole.errmessage("Malformed credentials.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var username = decodedCredentialsMatch[1];
|
|
||||||
var password = decodedCredentialsMatch[2];
|
|
||||||
var usernameMatch = [];
|
|
||||||
if (!authcode.userList || authcode.userList.indexOf(username) > -1) {
|
|
||||||
usernameMatch = users.filter(function (entry) {
|
|
||||||
return entry.name == username;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (usernameMatch.length == 0) {
|
|
||||||
usernameMatch.push({
|
|
||||||
name: username,
|
|
||||||
pass: "FAKEPASS",
|
|
||||||
salt: "FAKESALT"
|
|
||||||
}); // Fake credentials
|
|
||||||
}
|
|
||||||
checkIfPasswordMatches(usernameMatch, password, function (authorized) {
|
|
||||||
try {
|
|
||||||
if (!authorized) {
|
|
||||||
if (bruteProtection) {
|
|
||||||
if (process.send) {
|
|
||||||
process.send("\x12AUTHW" + reqip);
|
|
||||||
} else {
|
|
||||||
if (!bruteForceDb[reqip]) bruteForceDb[reqip] = {
|
|
||||||
invalidAttempts: 0
|
|
||||||
};
|
|
||||||
bruteForceDb[reqip].invalidAttempts++;
|
|
||||||
if (bruteForceDb[reqip].invalidAttempts >= 10) {
|
|
||||||
bruteForceDb[reqip].lastAttemptDate = new Date();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callServerError(401, undefined, undefined, ha);
|
|
||||||
serverconsole.errmessage("User \"" + username + "\" failed to log in.");
|
|
||||||
} else {
|
|
||||||
if (bruteProtection) {
|
|
||||||
if (process.send) {
|
|
||||||
process.send("\x12AUTHR" + reqip);
|
|
||||||
} else {
|
|
||||||
if (bruteForceDb[reqip]) bruteForceDb[reqip] = {
|
|
||||||
invalidAttempts: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
serverconsole.reqmessage("Client is logged in as \"" + username + "\"");
|
|
||||||
redirectTrailingSlashes(function () {
|
|
||||||
modExecute(mods, vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, "", callServerError, getCustomHeaders, origHref, redirect, parsePostData));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
callServerError(500, undefined, generateErrorStack(err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
callServerError(500, undefined, generateErrorStack(err));
|
// Headers will not be set.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if path is forbidden
|
||||||
|
if ((isForbiddenPath(decodedHref, "config") || isForbiddenPath(decodedHref, "certificates")) && !isProxy) {
|
||||||
|
callServerError(403);
|
||||||
|
serverconsole.errmessage("Access to configuration file/certificates is denied.");
|
||||||
|
return;
|
||||||
|
} else if (isIndexOfForbiddenPath(decodedHref, "temp") && !isProxy) {
|
||||||
|
callServerError(403);
|
||||||
|
serverconsole.errmessage("Access to temporary folder is denied.");
|
||||||
|
return;
|
||||||
|
} else if (isIndexOfForbiddenPath(decodedHref, "log") && !isProxy && (configJSON.enableLogging || configJSON.enableLogging == undefined) && !configJSON.enableRemoteLogBrowsing) {
|
||||||
|
callServerError(403);
|
||||||
|
serverconsole.errmessage("Access to log files is denied.");
|
||||||
|
return;
|
||||||
|
} else if (isForbiddenPath(decodedHref, "svrjs") && !isProxy && !exposeServerVersion) {
|
||||||
|
callServerError(403);
|
||||||
|
serverconsole.errmessage("Access to SVR.JS script is denied.");
|
||||||
|
return;
|
||||||
|
} else if ((isForbiddenPath(decodedHref, "svrjs") || isForbiddenPath(decodedHref, "serverSideScripts") || isIndexOfForbiddenPath(decodedHref, "serverSideScriptDirectories")) && !isProxy && (configJSON.disableServerSideScriptExpose || configJSON.disableServerSideScriptExpose === undefined)) {
|
||||||
|
callServerError(403);
|
||||||
|
serverconsole.errmessage("Access to sources is denied.");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
var nonscodeIndex = -1;
|
||||||
|
var authIndex = -1;
|
||||||
|
var regexI = [];
|
||||||
|
|
||||||
|
// Scan for non-standard codes
|
||||||
|
if (!isProxy && nonStandardCodes != undefined) {
|
||||||
|
for (var i = 0; i < nonStandardCodes.length; i++) {
|
||||||
|
if (matchHostname(nonStandardCodes[i].host)) {
|
||||||
|
var isMatch = false;
|
||||||
|
if (nonStandardCodes[i].regex) {
|
||||||
|
// Regex match
|
||||||
|
var createdRegex = createRegex(nonStandardCodes[i].regex, true);
|
||||||
|
isMatch = req.url.match(createdRegex) || href.match(createdRegex);
|
||||||
|
regexI[i] = createdRegex;
|
||||||
|
} else {
|
||||||
|
// Non-regex match
|
||||||
|
isMatch = nonStandardCodes[i].url == href || (os.platform() == "win32" && nonStandardCodes[i].url.toLowerCase() == href.toLowerCase());
|
||||||
|
}
|
||||||
|
if (isMatch) {
|
||||||
|
if (nonStandardCodes[i].scode == 401) {
|
||||||
|
// HTTP authentication
|
||||||
|
if (authIndex == -1) {
|
||||||
|
authIndex = i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (nonscodeIndex == -1) {
|
||||||
|
if ((nonStandardCodes[i].scode == 403 || nonStandardCodes[i].scode == 451) && nonStandardCodes[i].users !== undefined) {
|
||||||
|
if (nonStandardCodes[i].users.check(reqip)) nonscodeIndex = i;
|
||||||
|
} else {
|
||||||
|
nonscodeIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle non-standard codes
|
||||||
|
if (nonscodeIndex > -1) {
|
||||||
|
var nonscode = nonStandardCodes[nonscodeIndex];
|
||||||
|
if (nonscode.scode == 301 || nonscode.scode == 302) {
|
||||||
|
var location = "";
|
||||||
|
if (regexI[nonscodeIndex]) {
|
||||||
|
location = req.url.replace(regexI[nonscodeIndex], nonscode.location);
|
||||||
|
} else if (req.url.split("?")[1] == undefined || req.url.split("?")[1] == null || req.url.split("?")[1] == "" || req.url.split("?")[1] == " ") {
|
||||||
|
location = nonscode.location;
|
||||||
|
} else {
|
||||||
|
location = nonscode.location + "?" + req.url.split("?")[1];
|
||||||
|
}
|
||||||
|
redirect(location, nonscode.scode == 302);
|
||||||
|
return;
|
||||||
|
} else if (nonscode.scode == 403) {
|
||||||
|
callServerError(403);
|
||||||
|
serverconsole.errmessage("Content blocked.");
|
||||||
|
return;
|
||||||
|
} else if (nonscode.scode == 410) {
|
||||||
|
callServerError(410);
|
||||||
|
serverconsole.errmessage("Content is gone.");
|
||||||
|
return;
|
||||||
|
} else if (nonscode.scode == 418) {
|
||||||
|
callServerError(418);
|
||||||
|
serverconsole.errmessage("SVR.JS is always a teapot ;)");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
callServerError(nonscode.scode);
|
||||||
|
serverconsole.errmessage("Client fails receiving content.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (authcode.disableBruteProtection) {
|
|
||||||
// Don't brute-force protect it, just do HTTP authentication
|
|
||||||
authorizedCallback(false);
|
|
||||||
} else if (!process.send) {
|
|
||||||
// Query data from JS object database
|
|
||||||
if (!bruteForceDb[reqip] || !bruteForceDb[reqip].lastAttemptDate || (new Date() - 300000 >= bruteForceDb[reqip].lastAttemptDate)) {
|
|
||||||
if (bruteForceDb[reqip] && bruteForceDb[reqip].invalidAttempts >= 10) bruteForceDb[reqip] = {
|
|
||||||
invalidAttempts: 5
|
|
||||||
};
|
|
||||||
authorizedCallback(true);
|
|
||||||
} else {
|
|
||||||
callServerError(429);
|
|
||||||
serverconsole.errmessage("Brute force limit reached!");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var listenerEmitted = false;
|
|
||||||
|
|
||||||
// Listen for brute-force protection response
|
// Handle HTTP authentication
|
||||||
function authMessageListener(message) {
|
if (authIndex > -1) {
|
||||||
if (listenerEmitted) return;
|
var authcode = nonStandardCodes[authIndex];
|
||||||
if (message == "\x14AUTHA" + reqip || message == "\x14AUTHD" + reqip) {
|
|
||||||
process.removeListener("message", authMessageListener);
|
// Function to check if passwords match
|
||||||
listenerEmitted = true;
|
function checkIfPasswordMatches(list, password, callback, _i) {
|
||||||
|
if (!_i) _i = 0;
|
||||||
|
var cb = function (hash) {
|
||||||
|
var matches = (hash == list[_i].pass);
|
||||||
|
if (matches) {
|
||||||
|
callback(true);
|
||||||
|
} else if (_i >= list.length - 1) {
|
||||||
|
callback(false);
|
||||||
|
} else {
|
||||||
|
checkIfPasswordMatches(list, password, callback, _i + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var hashedPassword = sha256(password + list[_i].salt);
|
||||||
|
var cacheEntry = null;
|
||||||
|
if (list[_i].scrypt) {
|
||||||
|
if (!crypto.scrypt) {
|
||||||
|
callServerError(500, undefined, new Error("SVR.JS doesn't support scrypt-hashed passwords on Node.JS versions without scrypt hash support."));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
cacheEntry = scryptCache.find(function (entry) {
|
||||||
|
return (entry.password == hashedPassword && entry.salt == list[_i].salt);
|
||||||
|
});
|
||||||
|
if (cacheEntry) {
|
||||||
|
cb(cacheEntry.hash);
|
||||||
|
} else {
|
||||||
|
crypto.scrypt(password, list[_i].salt, 64, function (err, derivedKey) {
|
||||||
|
if (err) {
|
||||||
|
callServerError(500, undefined, err);
|
||||||
|
} else {
|
||||||
|
var key = derivedKey.toString("hex");
|
||||||
|
scryptCache.push({
|
||||||
|
hash: key,
|
||||||
|
password: hashedPassword,
|
||||||
|
salt: list[_i].salt,
|
||||||
|
addDate: new Date()
|
||||||
|
});
|
||||||
|
cb(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (list[_i].pbkdf2) {
|
||||||
|
if (crypto.__disabled__ !== undefined) {
|
||||||
|
callServerError(500, undefined, new Error("SVR.JS doesn't support PBKDF2-hashed passwords on Node.JS versions without crypto support."));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
cacheEntry = pbkdf2Cache.find(function (entry) {
|
||||||
|
return (entry.password == hashedPassword && entry.salt == list[_i].salt);
|
||||||
|
});
|
||||||
|
if (cacheEntry) {
|
||||||
|
cb(cacheEntry.hash);
|
||||||
|
} else {
|
||||||
|
crypto.pbkdf2(password, list[_i].salt, 36250, 64, "sha512", function (err, derivedKey) {
|
||||||
|
if (err) {
|
||||||
|
callServerError(500, undefined, err);
|
||||||
|
} else {
|
||||||
|
var key = derivedKey.toString("hex");
|
||||||
|
pbkdf2Cache.push({
|
||||||
|
hash: key,
|
||||||
|
password: hashedPassword,
|
||||||
|
salt: list[_i].salt,
|
||||||
|
addDate: new Date()
|
||||||
|
});
|
||||||
|
cb(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb(hashedPassword);
|
||||||
}
|
}
|
||||||
if (message == "\x14AUTHD" + reqip) {
|
}
|
||||||
|
|
||||||
|
function authorizedCallback(bruteProtection) {
|
||||||
|
try {
|
||||||
|
var ha = getCustomHeaders();
|
||||||
|
ha["WWW-Authenticate"] = "Basic realm=\"" + (authcode.realm ? authcode.realm.replace(/(\\|")/g, "\\$1") : "SVR.JS HTTP Basic Authorization") + "\", charset=\"UTF-8\"";
|
||||||
|
var credentials = req.headers["authorization"];
|
||||||
|
if (!credentials) {
|
||||||
|
callServerError(401, undefined, undefined, ha);
|
||||||
|
serverconsole.errmessage("Content needs authorization.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var credentialsMatch = credentials.match(/^Basic (.+)$/);
|
||||||
|
if (!credentialsMatch) {
|
||||||
|
callServerError(401, undefined, undefined, ha);
|
||||||
|
serverconsole.errmessage("Malformed credentials.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var decodedCredentials = Buffer.from(credentialsMatch[1], "base64").toString("utf8");
|
||||||
|
var decodedCredentialsMatch = decodedCredentials.match(/^([^:]*):(.*)$/);
|
||||||
|
if (!decodedCredentialsMatch) {
|
||||||
|
callServerError(401, undefined, undefined, ha);
|
||||||
|
serverconsole.errmessage("Malformed credentials.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var username = decodedCredentialsMatch[1];
|
||||||
|
var password = decodedCredentialsMatch[2];
|
||||||
|
var usernameMatch = [];
|
||||||
|
if (!authcode.userList || authcode.userList.indexOf(username) > -1) {
|
||||||
|
usernameMatch = users.filter(function (entry) {
|
||||||
|
return entry.name == username;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (usernameMatch.length == 0) {
|
||||||
|
usernameMatch.push({
|
||||||
|
name: username,
|
||||||
|
pass: "FAKEPASS",
|
||||||
|
salt: "FAKESALT"
|
||||||
|
}); // Fake credentials
|
||||||
|
}
|
||||||
|
checkIfPasswordMatches(usernameMatch, password, function (authorized) {
|
||||||
|
try {
|
||||||
|
if (!authorized) {
|
||||||
|
if (bruteProtection) {
|
||||||
|
if (process.send) {
|
||||||
|
process.send("\x12AUTHW" + reqip);
|
||||||
|
} else {
|
||||||
|
if (!bruteForceDb[reqip]) bruteForceDb[reqip] = {
|
||||||
|
invalidAttempts: 0
|
||||||
|
};
|
||||||
|
bruteForceDb[reqip].invalidAttempts++;
|
||||||
|
if (bruteForceDb[reqip].invalidAttempts >= 10) {
|
||||||
|
bruteForceDb[reqip].lastAttemptDate = new Date();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callServerError(401, undefined, undefined, ha);
|
||||||
|
serverconsole.errmessage("User \"" + username + "\" failed to log in.");
|
||||||
|
} else {
|
||||||
|
if (bruteProtection) {
|
||||||
|
if (process.send) {
|
||||||
|
process.send("\x12AUTHR" + reqip);
|
||||||
|
} else {
|
||||||
|
if (bruteForceDb[reqip]) bruteForceDb[reqip] = {
|
||||||
|
invalidAttempts: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serverconsole.reqmessage("Client is logged in as \"" + username + "\"");
|
||||||
|
redirectTrailingSlashes(function () {
|
||||||
|
modExecute(mods, vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, "", callServerError, getCustomHeaders, origHref, redirect, parsePostData));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
callServerError(500, undefined, generateErrorStack(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
callServerError(500, undefined, generateErrorStack(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (authcode.disableBruteProtection) {
|
||||||
|
// Don't brute-force protect it, just do HTTP authentication
|
||||||
|
authorizedCallback(false);
|
||||||
|
} else if (!process.send) {
|
||||||
|
// Query data from JS object database
|
||||||
|
if (!bruteForceDb[reqip] || !bruteForceDb[reqip].lastAttemptDate || (new Date() - 300000 >= bruteForceDb[reqip].lastAttemptDate)) {
|
||||||
|
if (bruteForceDb[reqip] && bruteForceDb[reqip].invalidAttempts >= 10) bruteForceDb[reqip] = {
|
||||||
|
invalidAttempts: 5
|
||||||
|
};
|
||||||
|
authorizedCallback(true);
|
||||||
|
} else {
|
||||||
callServerError(429);
|
callServerError(429);
|
||||||
serverconsole.errmessage("Brute force limit reached!");
|
serverconsole.errmessage("Brute force limit reached!");
|
||||||
} else if (message == "\x14AUTHA" + reqip) {
|
|
||||||
authorizedCallback(true);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
var listenerEmitted = false;
|
||||||
|
|
||||||
|
// Listen for brute-force protection response
|
||||||
|
function authMessageListener(message) {
|
||||||
|
if (listenerEmitted) return;
|
||||||
|
if (message == "\x14AUTHA" + reqip || message == "\x14AUTHD" + reqip) {
|
||||||
|
process.removeListener("message", authMessageListener);
|
||||||
|
listenerEmitted = true;
|
||||||
|
}
|
||||||
|
if (message == "\x14AUTHD" + reqip) {
|
||||||
|
callServerError(429);
|
||||||
|
serverconsole.errmessage("Brute force limit reached!");
|
||||||
|
} else if (message == "\x14AUTHA" + reqip) {
|
||||||
|
authorizedCallback(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.on("message", authMessageListener);
|
||||||
|
process.send("\x12AUTHQ" + reqip);
|
||||||
}
|
}
|
||||||
process.on("message", authMessageListener);
|
} else {
|
||||||
process.send("\x12AUTHQ" + reqip);
|
redirectTrailingSlashes(function () {
|
||||||
|
modExecute(mods, vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, "", callServerError, getCustomHeaders, origHref, redirect, parsePostData));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
redirectTrailingSlashes(function () {
|
|
||||||
modExecute(mods, vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, "", callServerError, getCustomHeaders, origHref, redirect, parsePostData));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -4860,11 +4863,11 @@ function start(init) {
|
||||||
if (process.isBun) {
|
if (process.isBun) {
|
||||||
serverconsole.locwarnmessage("Bun support is experimental. Some features of SVR.JS, SVR.JS mods and SVR.JS server-side JavaScript may not work as expected.");
|
serverconsole.locwarnmessage("Bun support is experimental. Some features of SVR.JS, SVR.JS mods and SVR.JS server-side JavaScript may not work as expected.");
|
||||||
if (users.some(function (entry) {
|
if (users.some(function (entry) {
|
||||||
return entry.pbkdf2;
|
return entry.pbkdf2;
|
||||||
})) serverconsole.locwarnmessage("PBKDF2 password hashing function in Bun blocks the event loop, which may result in denial of service.");
|
})) serverconsole.locwarnmessage("PBKDF2 password hashing function in Bun blocks the event loop, which may result in denial of service.");
|
||||||
if (users.some(function (entry) {
|
if (users.some(function (entry) {
|
||||||
return entry.scrypt;
|
return entry.scrypt;
|
||||||
})) serverconsole.locwarnmessage("scrypt password hashing function in Bun blocks the event loop, which may result in denial of service.");
|
})) serverconsole.locwarnmessage("scrypt password hashing function in Bun blocks the event loop, which may result in denial of service.");
|
||||||
}
|
}
|
||||||
if (cluster.isPrimary === undefined) serverconsole.locwarnmessage("You're running SVR.JS on single thread. Reliability may suffer, as the server is stopped after crash.");
|
if (cluster.isPrimary === undefined) serverconsole.locwarnmessage("You're running SVR.JS on single thread. Reliability may suffer, as the server is stopped after crash.");
|
||||||
if (crypto.__disabled__ !== undefined) serverconsole.locwarnmessage("Your Node.JS version doesn't have crypto support! The 'crypto' module is essential for providing cryptographic functionality in Node.JS. Without crypto support, certain security features may be unavailable, and some functionality may not work as expected. It's recommended to use a Node.JS version that includes crypto support to ensure the security and proper functioning of your server.");
|
if (crypto.__disabled__ !== undefined) serverconsole.locwarnmessage("Your Node.JS version doesn't have crypto support! The 'crypto' module is essential for providing cryptographic functionality in Node.JS. Without crypto support, certain security features may be unavailable, and some functionality may not work as expected. It's recommended to use a Node.JS version that includes crypto support to ensure the security and proper functioning of your server.");
|
||||||
|
|
Reference in a new issue