From a55a2e5592130eb53cd69cca2f984b26c182a87f Mon Sep 17 00:00:00 2001 From: ChrisChrome Date: Wed, 14 Feb 2024 12:14:41 -0700 Subject: [PATCH] Did some goofy stuff, you can filter things now --- docs.html | 25 ++++++++++++ index.js | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 docs.html diff --git a/docs.html b/docs.html new file mode 100644 index 0000000..60bd62b --- /dev/null +++ b/docs.html @@ -0,0 +1,25 @@ + + + + + + + Docs + + + +

This is a simple server list API for Stormworks. The following endpoints are available:
+ /check?address=IP:PORT
+ /serverList
+ /docs
+ /robots.txt
+
+ /serverList takes the following optional parameters:
+ ?uptodate - Filters out servers that are not running the latest version
+ ?outdated - Filters out servers that are running the latest version
+ ?version=v1.10.0 - Filters servers by version
+ ?dlc=1|2|3 - Filters servers by DLC
+

+ + + \ No newline at end of file diff --git a/index.js b/index.js index 7a30cfe..a1c9edd 100755 --- a/index.js +++ b/index.js @@ -60,6 +60,38 @@ var removeDuplicates = function (nums) { }; servers = []; +// Make DLC bitfield from dlc array, same outputs as splitKeyword in reverse, inputs in an array are 1 = Weapons, 2 = Arid, 3 = Space, figure out the output based on an array of inputs, so an input if [3] would be "6" and so on +function calculateDLCNumber(array) { + array = array.map(Number); // Ensure all are numbers + array.sort(); // Sort array to have lowest values first + + if (array.length === 1) { + if (array[0] === 1) { // Weapons + return "1"; + } else if (array[0] === 2) { // Arid + return "2"; + } else if (array[0] === 3) { // Space + return "6"; + } + } else if (array.length === 2) { + if (array.includes(1) && array.includes(2)) { // Weapons + Arid + return "3"; + } else if (array.includes(1) && array.includes(3)) { // Weapons + Space + return "5"; + } else if (array.includes(2) && array.includes(3)) { // Arid + Space + return "4"; + } + } else if (array.length === 3) { + if (array.includes(1) && array.includes(2) && array.includes(3)) { // Weapons + Arid + Space + return "7"; + } + } else { + return 0; // Not a valid combination + } +} + + + // Keyword split to version, dlcs, tps function splitKeyword(keyword) { data = keyword.split("-") @@ -400,7 +432,7 @@ if (config.rateLimiterEnabled) { return config.behindProxy ? req.headers['x-real-ip'] : req.ip; }, skipFailedRequests: true, - handler: function (req, res /*, next*/ ) { + handler: function (req, res /*, next*/) { const ip = config.behindProxy ? req.headers['x-real-ip'] : req.ip; const remainingTime = Math.round((req.rateLimit.resetTime - Date.now()) / 1000); res.status(429).json({ @@ -486,15 +518,94 @@ app.get('/check', (req, res) => { // }); app.get('/serverList', (req, res) => { - res.setHeader("Content-Type", "application/json").send(JSON.stringify(serverList)); + // check if ?filter is present, if so filter servers with other variables like ?uptodate=true or ?version=v1.10.0 + // make the output variable a copy of serverList, not serverList itself, as to not modify the original object + output = JSON.parse(JSON.stringify(serverList)); + + filters = req.query; + console.log(filters) + // valid filters, uptodate, outdated, version, dlc + // valid values for version, v1.10.0, v1.9.9, etc + + // valid values for dlc, 0, 1, 2, 3, 1|2, 1|3, 2|3, 1|2|3 + // uptodate and outdated are just there, but cannot be used together + // version and dlc can be used together + + // Do the filters + if (filters.uptodate && filters.outdated) { + res.status(400).json({ + "error": "Cannot use uptodate and outdated filters together" + }) + return; + } + // If version is present, split by = and check if it's a valid version + if (filters.version) { + versionFilter = filters.version; + if (!semver.valid(versionFilter)) { + res.status(400).json({ + "error": "Invalid version" + }) + return; + } + for (const key in output.servers) { + if (output.servers[key].version != versionFilter) { + delete output.servers[key]; + } + } + } + // If dlc is present, split by = and check if it's a valid dlc + if (filters.dlc) { + dlcFilter = filters.dlc; + if (!dlcFilter.match(/^(0|1|2|3|1\|2|1\|3|2\|3|1\|2\|3)$/)) { + res.status(400).json({ + "error": "Invalid dlc" + }) + return; + } + console.log(calculateDLCNumber(dlcFilter.split("|"))) + for (const key in output.servers) { + if (output.servers[key].dlc != calculateDLCNumber(dlcFilter.split("|"))) { + delete output.servers[key]; + } + } + } + // For all filters, remember that output.servers is an object, so .filter wont work + if (filters.uptodate) { + console.log("Deleting outdated servers") + for (const key in output.servers) { + if (output.servers[key].outdated) { + console.log(`Deleting ${key}`) + delete output.servers[key]; + } + } + } + // If outdated is present, filter out uptodate servers + if (filters.outdated) { + for (const key in output.servers) { + if (!output.servers[key].outdated) { + delete output.servers[key]; + } + } + } + + // Return filtered servers + output.filteredCount = objectLength(output.servers); + if (output.filteredCount == output.serverCount) delete output.filteredCount; + res.setHeader("Content-Type", "application/json").send(JSON.stringify(output)); }); + + +app.get('/docs', (req, res) => { + res.sendFile(__dirname + '/docs.html'); +}); app.get('/', (req, res) => { // Send list of all endpoints res.setHeader("Content-Type", "application/json").send(JSON.stringify({ "endpoints": [ "/check?address=IP:PORT", - "/serverList" + "/serverList", + "/docs" ], "about": { "author": "Chris Chrome",