refactor: move functions

fix: locked accounts was not implemented lol
This commit is contained in:
Jonny_Bro (Nikita) 2025-01-25 19:47:47 +05:00
parent eabc1bfba9
commit 0036d4448c
8 changed files with 350 additions and 299 deletions

125
index.js
View file

@ -6,8 +6,7 @@ const express = require("express"),
passport = require("passport"),
SteamStrategy = require("passport-steam").Strategy;
const { JsonDB, Config } = require("node-json-db"),
{ generateRandomString, log } = require("./utils/functions");
const { JsonDB, Config } = require("node-json-db");
const config = require("./config");
const db = new JsonDB(new Config(`data/${config.production ? "main" : "test"}_db`, true, true, "/"));
@ -110,130 +109,8 @@ db.getData("/admins").then(data => {
app.locals = {
config: config,
db: db,
getKey: getKey,
isRatelimited: isRatelimited,
isMultiAccount: isMultiAccount,
};
/**
* Gets a user's key from the database.
*
* Checks if the user already has a key, and returns it if so.
* Otherwise generates a new one.
*
* @param {Object | string} user The user object or SteamID64.
* @returns {Promise<String>} The user's key.
*/
async function getKey(user) {
user = typeof user === "string" ? user : user.steamid;
const keys = await db.getData("/keys");
const key = keys[user];
if (key) {
await log(
`[KEY] User logged in (SteamID: ${user}, Key ${key}).`,
`[KEY] User logged in (SteamID: \`${user}\`, Key \`${key}\`).`,
);
return key;
} else return await _createKey(user);
}
/**
* Creates a new unique key for the given user and saves it to the database.
*
* @param {Object | string} user The OpenID Steam user object or SteamID64.
* @returns {Promise<String>} The new unique key generated for the user.
*/
async function _createKey(user) {
user = typeof user === "string" ? user : user.steamid;
const keys = await db.getData("/keys");
const key = generateRandomString();
const isFound = keys[key];
if (!isFound) {
keys[user] = key;
const now = Date.now();
await log(
`[KEY] New user (SteamID: ${user}, Key: ${key}, TimeCreated: ${new Date(now).toLocaleString("ru-RU")}).`,
`[KEY] New user (SteamID: \`${user}\`, Key: \`${key}\`, TimeCreated: <t:${Math.floor(now / 1000)}:f>).`,
);
await db.push("/keys", keys);
return key;
} else return await _createKey(user);
}
/**
* Checks if an IP address is currently rate limited.
*
* Gets the current rate limits from the database.
* If the IP already has a recent rate limit, returns true.
* Otherwise, saves a new rate limit for the IP and returns false.
*
* @param {string} ip The IP address to check
* @returns {Promise<boolean>} Whether the IP is currently rate limited
*/
async function isRatelimited(ip) {
const rateLimits = await db.getData("/ratelimits");
if (rateLimits[ip] && Date.now() - rateLimits[ip] <= config.rateLimitTime) return true;
rateLimits[ip] = Date.now();
await db.push("/ratelimits", rateLimits);
return false;
}
/**
* Checks if a user is using multiple accounts based on their IP address and Steam ID.
*
* Retrieves the locked accounts and user records from the database. If the user's account is locked, returns true.
* Otherwise, checks if the user has changed their IP address more than the configured `ipChangeTime`. If so, clears the
* user's IP address history and locks the account if the user has changed their IP more than 3 times. Updates the
* database with the new account status and returns the result.
*
* @param {string} ip The IP address of the user.
* @param {string} steamid The Steam ID of the user.
* @returns {Promise<boolean>} Whether the user is using multiple accounts.
*/
async function isMultiAccount(ip, steamid) {
const locked = await db.getData("/locked");
const records = await db.getData("/records");
if (locked[steamid]) return true;
if (!records[steamid])
records[steamid] = {
ips: {
[ip]: true,
},
lastchanged: Date.now(),
};
// Clear IPs if the user has changed their ip more than ipChangeTime
if (Date.now() - records[steamid]["lastchanged"] > config.ipChangeTime) {
records[steamid]["ips"] = [];
records[steamid]["lastchanged"] = Date.now();
}
// Lock account if the user changed their IP more than 3 time in ipChangeTime
if (Object.keys(records[steamid]["ips"]).length > 2) {
locked[steamid] = true;
await db.push("/locked", locked);
return true;
}
await db.push("/records", records);
return false;
}
// HTTP server
const http = require("http");

View file

@ -7,16 +7,16 @@
"start": "node ."
},
"dependencies": {
"daisyui": "^4.11.1",
"daisyui": "^4.12.23",
"ejs": "^2.6.1",
"express": "^4.16.1",
"express-session": "^1.18.0",
"express": "^4.21.2",
"express-session": "^1.18.1",
"formidable": "^2.1.2",
"lzma": "^2.3.2",
"mongoose": "^8.9.0",
"morgan": "^1.9.1",
"mongoose": "^8.9.5",
"morgan": "^1.10.0",
"node-fetch": "^2.7.0",
"node-json-db": "^2.3.0",
"node-json-db": "^2.3.1",
"open-graph-scraper": "^6.5.0",
"passport": "^0.7.0",
"passport-steam": "^1.0.18"

View file

@ -9,17 +9,17 @@ importers:
.:
dependencies:
daisyui:
specifier: ^4.11.1
version: 4.11.1(postcss@8.4.36)
specifier: ^4.12.23
version: 4.12.23(postcss@8.4.36)
ejs:
specifier: ^2.6.1
version: 2.6.2
express:
specifier: ^4.16.1
version: 4.16.4
specifier: ^4.21.2
version: 4.21.2
express-session:
specifier: ^1.18.0
version: 1.18.0
specifier: ^1.18.1
version: 1.18.1
formidable:
specifier: ^2.1.2
version: 2.1.2
@ -27,17 +27,17 @@ importers:
specifier: ^2.3.2
version: 2.3.2
mongoose:
specifier: ^8.9.0
version: 8.9.0
specifier: ^8.9.5
version: 8.9.5
morgan:
specifier: ^1.9.1
version: 1.9.1
specifier: ^1.10.0
version: 1.10.0
node-fetch:
specifier: ^2.7.0
version: 2.7.0
node-json-db:
specifier: ^2.3.0
version: 2.3.0
specifier: ^2.3.1
version: 2.3.1
open-graph-scraper:
specifier: ^6.5.0
version: 6.5.0
@ -224,9 +224,9 @@ packages:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
body-parser@1.18.3:
resolution: {integrity: sha512-YQyoqQG3sO8iCmf8+hyVpgHHOv0/hCEFiS4zTGUwTA1HjAFX66wRcNQrVCeJq9pgESMRvUAOvSil5MJlmccuKQ==}
engines: {node: '>= 0.8'}
body-parser@1.20.3:
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
@ -245,8 +245,8 @@ packages:
resolution: {integrity: sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==}
engines: {node: '>=16.20.1'}
bytes@3.0.0:
resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==}
bytes@3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
call-bind@1.0.7:
@ -297,8 +297,8 @@ packages:
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
content-disposition@0.5.2:
resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==}
content-disposition@0.5.4:
resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
engines: {node: '>= 0.6'}
content-type@1.0.5:
@ -311,12 +311,12 @@ packages:
cookie-signature@1.0.7:
resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==}
cookie@0.3.1:
resolution: {integrity: sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==}
cookie@0.7.1:
resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
engines: {node: '>= 0.6'}
cookie@0.6.0:
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
cookie@0.7.2:
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
engines: {node: '>= 0.6'}
cross-spawn@7.0.3:
@ -342,8 +342,8 @@ packages:
resolution: {integrity: sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
daisyui@4.11.1:
resolution: {integrity: sha512-obT9CUbQdW6eoHwSeT5VwaRrWlwrM4OT5qlfdJ0oQlSIEYhwnEl2+L2fwu5PioLbitwuMdYC2X8I1cyy8Pf6LQ==}
daisyui@4.12.23:
resolution: {integrity: sha512-EM38duvxutJ5PD65lO/AFMpcw+9qEy6XAZrTpzp7WyaPeO/l+F/Qiq0ECHHmFNcFXh5aVoALY4MGrrxtCiaQCQ==}
engines: {node: '>=16.9.0'}
debug@2.6.9:
@ -374,16 +374,13 @@ packages:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
depd@1.1.2:
resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==}
engines: {node: '>= 0.6'}
depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
destroy@1.0.4:
resolution: {integrity: sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==}
destroy@1.2.0:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
dezalgo@1.0.4:
resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
@ -431,6 +428,10 @@ packages:
resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
engines: {node: '>= 0.8'}
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
@ -487,12 +488,12 @@ packages:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
express-session@1.18.0:
resolution: {integrity: sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==}
express-session@1.18.1:
resolution: {integrity: sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==}
engines: {node: '>= 0.8.0'}
express@4.16.4:
resolution: {integrity: sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==}
express@4.21.2:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
engines: {node: '>= 0.10.0'}
fast-deep-equal@3.1.3:
@ -522,8 +523,8 @@ packages:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
finalhandler@1.1.1:
resolution: {integrity: sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==}
finalhandler@1.3.1:
resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
engines: {node: '>= 0.8'}
find-up@5.0.0:
@ -632,12 +633,12 @@ packages:
htmlparser2@8.0.2:
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
http-errors@1.6.3:
resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==}
engines: {node: '>= 0.6'}
http-errors@2.0.0:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
iconv-lite@0.4.23:
resolution: {integrity: sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==}
iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
iconv-lite@0.6.3:
@ -662,6 +663,9 @@ packages:
inherits@2.0.3:
resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
@ -761,8 +765,8 @@ packages:
memory-pager@1.5.0:
resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==}
merge-descriptors@1.0.1:
resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
merge-descriptors@1.0.3:
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
@ -784,8 +788,9 @@ packages:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
mime@1.4.1:
resolution: {integrity: sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==}
mime@1.6.0:
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
engines: {node: '>=4'}
hasBin: true
minimatch@3.1.2:
@ -829,12 +834,12 @@ packages:
socks:
optional: true
mongoose@8.9.0:
resolution: {integrity: sha512-b58zY3PLNBcoz6ZXFckr0leJcVVBMAOBvD+7Bj2ZjghAwntXmNnqwlDixTKQU3UYoQIGTv+AQx/0ThsvaeVrCA==}
mongoose@8.9.5:
resolution: {integrity: sha512-SPhOrgBm0nKV3b+IIHGqpUTOmgVL5Z3OO9AwkFEmvOZznXTvplbomstCnPOGAyungtRXE5pJTgKpKcZTdjeESg==}
engines: {node: '>=16.20.1'}
morgan@1.9.1:
resolution: {integrity: sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==}
morgan@1.10.0:
resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==}
engines: {node: '>= 0.8.0'}
mpath@0.9.0:
@ -878,8 +883,8 @@ packages:
encoding:
optional: true
node-json-db@2.3.0:
resolution: {integrity: sha512-B8T+w4q6zXZ20YcfQINLSjMGgImRKzkvR0ShYYoNRdLxtMhVvbzaMBzNdEaRcCjilW/lKS+g9CwVXNoK5uTncw==}
node-json-db@2.3.1:
resolution: {integrity: sha512-cZ0HGQuMUhMg9iGLgS7eOGK5gGYfAyEsPXMD16mcHvUNhMMtOxgUAMuiCED7qh+c2IOeN/6l9FXUCgnGtXvLIA==}
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
@ -903,6 +908,10 @@ packages:
resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==}
engines: {node: '>= 0.8'}
on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
on-headers@1.0.2:
resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==}
engines: {node: '>= 0.8'}
@ -975,8 +984,8 @@ packages:
resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
engines: {node: '>=16 || 14 >=14.17'}
path-to-regexp@0.1.7:
resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
path-to-regexp@0.1.12:
resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==}
pause@0.0.1:
resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==}
@ -1061,6 +1070,10 @@ packages:
resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==}
engines: {node: '>=0.6'}
qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
engines: {node: '>=0.6'}
qs@6.5.2:
resolution: {integrity: sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==}
engines: {node: '>=0.6'}
@ -1076,8 +1089,8 @@ packages:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
raw-body@2.3.3:
resolution: {integrity: sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==}
raw-body@2.5.2:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
engines: {node: '>= 0.8'}
read-cache@1.0.0:
@ -1118,20 +1131,20 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
send@0.16.2:
resolution: {integrity: sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==}
send@0.19.0:
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
engines: {node: '>= 0.8.0'}
serve-static@1.13.2:
resolution: {integrity: sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==}
serve-static@1.16.2:
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
engines: {node: '>= 0.8.0'}
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
setprototypeof@1.1.0:
resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==}
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
@ -1159,13 +1172,9 @@ packages:
sparse-bitfield@3.0.3:
resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==}
statuses@1.4.0:
resolution: {integrity: sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==}
engines: {node: '>= 0.6'}
statuses@1.5.0:
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
engines: {node: '>= 0.6'}
statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
steam-web@0.4.0:
resolution: {integrity: sha512-FgSYhL7GaP4Va5JKT09yZ+WrTZttFtLwenIPuZd7GUA1z3W7vu7qqPT/qTC76Pd9+sf85txMgIPr/y0+3gNlUA==}
@ -1223,6 +1232,10 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
@ -1478,18 +1491,20 @@ snapshots:
binary-extensions@2.3.0: {}
body-parser@1.18.3:
body-parser@1.20.3:
dependencies:
bytes: 3.0.0
bytes: 3.1.2
content-type: 1.0.5
debug: 2.6.9
depd: 1.1.2
http-errors: 1.6.3
iconv-lite: 0.4.23
on-finished: 2.3.0
qs: 6.5.2
raw-body: 2.3.3
depd: 2.0.0
destroy: 1.2.0
http-errors: 2.0.0
iconv-lite: 0.4.24
on-finished: 2.4.1
qs: 6.13.0
raw-body: 2.5.2
type-is: 1.6.18
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
@ -1510,7 +1525,7 @@ snapshots:
bson@6.10.1: {}
bytes@3.0.0: {}
bytes@3.1.2: {}
call-bind@1.0.7:
dependencies:
@ -1576,7 +1591,9 @@ snapshots:
concat-map@0.0.1: {}
content-disposition@0.5.2: {}
content-disposition@0.5.4:
dependencies:
safe-buffer: 5.2.1
content-type@1.0.5: {}
@ -1584,9 +1601,9 @@ snapshots:
cookie-signature@1.0.7: {}
cookie@0.3.1: {}
cookie@0.7.1: {}
cookie@0.6.0: {}
cookie@0.7.2: {}
cross-spawn@7.0.3:
dependencies:
@ -1613,7 +1630,7 @@ snapshots:
culori@3.3.0: {}
daisyui@4.11.1(postcss@8.4.36):
daisyui@4.12.23(postcss@8.4.36):
dependencies:
css-selector-tokenizer: 0.8.0
culori: 3.3.0
@ -1640,11 +1657,9 @@ snapshots:
delayed-stream@1.0.0: {}
depd@1.1.2: {}
depd@2.0.0: {}
destroy@1.0.4: {}
destroy@1.2.0: {}
dezalgo@1.0.4:
dependencies:
@ -1689,6 +1704,8 @@ snapshots:
encodeurl@1.0.2: {}
encodeurl@2.0.0: {}
entities@4.5.0: {}
es-define-property@1.0.0:
@ -1771,9 +1788,9 @@ snapshots:
etag@1.8.1: {}
express-session@1.18.0:
express-session@1.18.1:
dependencies:
cookie: 0.6.0
cookie: 0.7.2
cookie-signature: 1.0.7
debug: 2.6.9
depd: 2.0.0
@ -1784,35 +1801,36 @@ snapshots:
transitivePeerDependencies:
- supports-color
express@4.16.4:
express@4.21.2:
dependencies:
accepts: 1.3.8
array-flatten: 1.1.1
body-parser: 1.18.3
content-disposition: 0.5.2
body-parser: 1.20.3
content-disposition: 0.5.4
content-type: 1.0.5
cookie: 0.3.1
cookie: 0.7.1
cookie-signature: 1.0.6
debug: 2.6.9
depd: 1.1.2
encodeurl: 1.0.2
depd: 2.0.0
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
finalhandler: 1.1.1
finalhandler: 1.3.1
fresh: 0.5.2
merge-descriptors: 1.0.1
http-errors: 2.0.0
merge-descriptors: 1.0.3
methods: 1.1.2
on-finished: 2.3.0
on-finished: 2.4.1
parseurl: 1.3.3
path-to-regexp: 0.1.7
path-to-regexp: 0.1.12
proxy-addr: 2.0.7
qs: 6.5.2
qs: 6.13.0
range-parser: 1.2.1
safe-buffer: 5.1.2
send: 0.16.2
serve-static: 1.13.2
setprototypeof: 1.1.0
statuses: 1.4.0
safe-buffer: 5.2.1
send: 0.19.0
serve-static: 1.16.2
setprototypeof: 1.2.0
statuses: 2.0.1
type-is: 1.6.18
utils-merge: 1.0.1
vary: 1.1.2
@ -1847,14 +1865,14 @@ snapshots:
dependencies:
to-regex-range: 5.0.1
finalhandler@1.1.1:
finalhandler@1.3.1:
dependencies:
debug: 2.6.9
encodeurl: 1.0.2
encodeurl: 2.0.0
escape-html: 1.0.3
on-finished: 2.3.0
on-finished: 2.4.1
parseurl: 1.3.3
statuses: 1.4.0
statuses: 2.0.1
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
@ -1969,14 +1987,15 @@ snapshots:
domutils: 3.1.0
entities: 4.5.0
http-errors@1.6.3:
http-errors@2.0.0:
dependencies:
depd: 1.1.2
inherits: 2.0.3
setprototypeof: 1.1.0
statuses: 1.5.0
depd: 2.0.0
inherits: 2.0.4
setprototypeof: 1.2.0
statuses: 2.0.1
toidentifier: 1.0.1
iconv-lite@0.4.23:
iconv-lite@0.4.24:
dependencies:
safer-buffer: 2.1.2
@ -2000,6 +2019,8 @@ snapshots:
inherits@2.0.3: {}
inherits@2.0.4: {}
ipaddr.js@1.9.1: {}
is-binary-path@2.1.0:
@ -2073,7 +2094,7 @@ snapshots:
memory-pager@1.5.0: {}
merge-descriptors@1.0.1: {}
merge-descriptors@1.0.3: {}
merge2@1.4.1: {}
@ -2090,7 +2111,7 @@ snapshots:
dependencies:
mime-db: 1.52.0
mime@1.4.1: {}
mime@1.6.0: {}
minimatch@3.1.2:
dependencies:
@ -2113,7 +2134,7 @@ snapshots:
bson: 6.10.1
mongodb-connection-string-url: 3.0.1
mongoose@8.9.0:
mongoose@8.9.5:
dependencies:
bson: 6.10.1
kareem: 2.6.3
@ -2132,11 +2153,11 @@ snapshots:
- socks
- supports-color
morgan@1.9.1:
morgan@1.10.0:
dependencies:
basic-auth: 2.0.1
debug: 2.6.9
depd: 1.1.2
depd: 2.0.0
on-finished: 2.3.0
on-headers: 1.0.2
transitivePeerDependencies:
@ -2172,7 +2193,7 @@ snapshots:
dependencies:
whatwg-url: 5.0.0
node-json-db@2.3.0:
node-json-db@2.3.1:
dependencies:
rwlock: 5.0.0
@ -2192,6 +2213,10 @@ snapshots:
dependencies:
ee-first: 1.1.1
on-finished@2.4.1:
dependencies:
ee-first: 1.1.1
on-headers@1.0.2: {}
once@1.4.0:
@ -2273,7 +2298,7 @@ snapshots:
lru-cache: 10.2.0
minipass: 7.0.4
path-to-regexp@0.1.7: {}
path-to-regexp@0.1.12: {}
pause@0.0.1: {}
@ -2339,6 +2364,10 @@ snapshots:
dependencies:
side-channel: 1.0.6
qs@6.13.0:
dependencies:
side-channel: 1.0.6
qs@6.5.2: {}
queue-microtask@1.2.3: {}
@ -2347,11 +2376,11 @@ snapshots:
range-parser@1.2.1: {}
raw-body@2.3.3:
raw-body@2.5.2:
dependencies:
bytes: 3.0.0
http-errors: 1.6.3
iconv-lite: 0.4.23
bytes: 3.1.2
http-errors: 2.0.0
iconv-lite: 0.4.24
unpipe: 1.0.0
read-cache@1.0.0:
@ -2388,30 +2417,30 @@ snapshots:
safer-buffer@2.1.2: {}
send@0.16.2:
send@0.19.0:
dependencies:
debug: 2.6.9
depd: 1.1.2
destroy: 1.0.4
depd: 2.0.0
destroy: 1.2.0
encodeurl: 1.0.2
escape-html: 1.0.3
etag: 1.8.1
fresh: 0.5.2
http-errors: 1.6.3
mime: 1.4.1
ms: 2.0.0
on-finished: 2.3.0
http-errors: 2.0.0
mime: 1.6.0
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 1.4.0
statuses: 2.0.1
transitivePeerDependencies:
- supports-color
serve-static@1.13.2:
serve-static@1.16.2:
dependencies:
encodeurl: 1.0.2
encodeurl: 2.0.0
escape-html: 1.0.3
parseurl: 1.3.3
send: 0.16.2
send: 0.19.0
transitivePeerDependencies:
- supports-color
@ -2424,7 +2453,7 @@ snapshots:
gopd: 1.0.1
has-property-descriptors: 1.0.2
setprototypeof@1.1.0: {}
setprototypeof@1.2.0: {}
shebang-command@2.0.0:
dependencies:
@ -2449,9 +2478,7 @@ snapshots:
dependencies:
memory-pager: 1.5.0
statuses@1.4.0: {}
statuses@1.5.0: {}
statuses@2.0.1: {}
steam-web@0.4.0:
dependencies:
@ -2536,6 +2563,8 @@ snapshots:
dependencies:
is-number: 7.0.0
toidentifier@1.0.1: {}
tr46@0.0.3: {}
tr46@4.1.1:

View file

@ -4,7 +4,7 @@ const express = require("express"),
openGraphScraper = require("open-graph-scraper"),
lzma = require("lzma");
const { isAdmin, isUser, isUserGame, generateCode, isCourseFileValid, log } = require("../utils/functions"),
const { isAdmin, isUser, isUserGame, isRatelimited, isMultiAccount, isLocked, getKey, generateCode, isCourseFileValid, log } = require("../utils/functions"),
{ formidable } = require("formidable");
router.post("/", isUser, async (req, res) => {
@ -21,19 +21,21 @@ router.get("/download", isUserGame, async (req, res) => {
if (!headers.map) return res.status(401).json({ res: res.statusCode, message: "No map provided. Please provide a valid map." });
const ip = headers["cf-connecting-ip"] || "Unknown";
const db = req.app.locals.db;
const key = headers.authorization;
const keys = await req.app.locals.db.getData("/keys");
const keys = await db.getData("/keys");
const steamIds = Object.fromEntries(Object.entries(keys).map(([k, v]) => [v, k]));
const steamid = steamIds[key];
if (ip !== "Unknown" && (await req.app.locals.isRatelimited(ip))) return res.status(401).json({ res: res.statusCode, message: "Too many requests. Please try again later." });
if (ip !== "Unknown" && (await req.app.locals.isMultiAccount(ip, steamid))) return res.status(401).json({ res: res.statusCode, message: "Your account was detected as multiaccount. Please open a ticket on our Discord server." });
if (ip !== "Unknown" && (await isRatelimited(db, ip))) return res.status(401).json({ res: res.statusCode, message: "Too many requests. Please try again later." });
if (ip !== "Unknown" && (await isMultiAccount(db, ip, steamid))) return res.status(401).json({ res: res.statusCode, message: "Your account was detected as multiaccount. Please open a ticket on our Discord server." });
if (await isLocked(db, steamid)) return res.status(401).json({ res: res.statusCode, message: "Your account is locked. Please open a ticket on our Discord server." });
let courseData;
try {
courseData = await req.app.locals.db.getData(`/courses/${headers.code.toUpperCase()}`);
} catch (e) {
courseData = await db.getData(`/courses/${headers.code.toUpperCase()}`);
} catch {
return res.status(401).json({ res: res.statusCode, message: "Invalid course code provided." });
}
@ -49,7 +51,7 @@ router.get("/download", isUserGame, async (req, res) => {
if (!courseData.plays) courseData.plays = 1;
else courseData.plays++;
await req.app.locals.db.push(`/courses/${headers.code.toUpperCase()}`, courseData);
await db.push(`/courses/${headers.code.toUpperCase()}`, courseData);
res.send({ res: res.statusCode, file: file });
});
@ -61,18 +63,20 @@ router.post("/upload", isUserGame, async (req, res) => {
if (headers.mapid === null || headers.mapid === undefined) return res.status(401).json({ res: res.statusCode, message: "No map id provided. Please provide a valid map id." });
const ip = headers["cf-connecting-ip"] || "Unknown";
const db = req.app.locals.db;
const key = headers.authorization;
const keys = await req.app.locals.db.getData("/keys");
const keys = await db.getData("/keys");
const steamIds = Object.fromEntries(Object.entries(keys).map(([k, v]) => [v, k]));
const steamid = steamIds[key];
if (ip !== "Unknown" && (await req.app.locals.isRatelimited(ip))) return res.status(401).json({ res: res.statusCode, message: "Too many requests. Please try again later." });
if (ip !== "Unknown" && (await req.app.locals.isMultiAccount(ip, steamid))) return res.status(401).json({ res: res.statusCode, message: "Your account was detected as multiaccount. Please open a ticket on our Discord server." });
if (ip !== "Unknown" && (await isRatelimited(db, ip))) return res.status(401).json({ res: res.statusCode, message: "Too many requests. Please try again later." });
if (ip !== "Unknown" && (await isMultiAccount(db, ip, steamid))) return res.status(401).json({ res: res.statusCode, message: "Your account was detected as multiaccount. Please open a ticket on our Discord server." });
if (await isLocked(db, steamid)) return res.status(401).json({ res: res.statusCode, message: "Your account is locked. Please open a ticket on our Discord server." });
let course = "";
try {
course = lzma.decompress(Buffer.from(headers.course, "base64"));
} catch (e) {
} catch {
course = Buffer.from(headers.course, "base64").toString("utf-8");
}
@ -90,7 +94,7 @@ router.post("/upload", isUserGame, async (req, res) => {
const mapImage = headers.mapid === "0" || headers.mapid === "no_map_id" ? "" : await openGraphScraper({ url: `https://steamcommunity.com/sharedfiles/filedetails/?id=${headers.mapid}` }).then(data => data.result.ogImage[0].url);
await req.app.locals.db.push("/courses", {
await db.push("/courses", {
[code]: {
map: headers.map,
uploader: {
@ -115,9 +119,11 @@ router.post("/upload", isUserGame, async (req, res) => {
router.post("/upload_site", isUser, async (req, res) => {
const { headers, user } = req;
const ip = headers["cf-connecting-ip"] || "Unknown";
const db = req.app.locals.db;
if (ip !== "Unknown" && (await req.app.locals.isRatelimited(ip))) return res.status(401).json({ res: res.statusCode, message: "Too many requests. Please try again later." });
if (ip !== "Unknown" && (await req.app.locals.isMultiAccount(ip, user.steamid))) return res.status(401).json({ res: res.statusCode, message: "Your account was detected as multiaccount. Please open a ticket on our Discord server." });
if (ip !== "Unknown" && (await isRatelimited(db, ip))) return res.status(401).json({ res: res.statusCode, message: "Too many requests. Please try again later." });
if (ip !== "Unknown" && (await isMultiAccount(db, ip, user.steamid))) return res.status(401).json({ res: res.statusCode, message: "Your account was detected as multiaccount. Please open a ticket on our Discord server." });
if (await isLocked(db, user.steamid)) return res.status(401).json({ res: res.statusCode, message: "Your account is locked. Please open a ticket on our Discord server." });
const form = formidable({ maxFileSize: 10 * 1024 * 1024 });
@ -129,7 +135,7 @@ router.post("/upload_site", isUser, async (req, res) => {
let course = "";
try {
course = lzma.decompress(uploaded);
} catch (e) {
} catch {
course = uploaded;
}
@ -148,7 +154,7 @@ router.post("/upload_site", isUser, async (req, res) => {
const mapid = fields.link.match(/id=(\d+)/)[1];
const mapImage = await openGraphScraper({ url: `https://steamcommunity.com/sharedfiles/filedetails/?id=${mapid}` }).then(data => data.result.ogImage[0].url);
await req.app.locals.db.push("/courses", {
await db.push("/courses", {
[code]: {
map: fields.map,
uploader: {
@ -178,15 +184,17 @@ router.post("/update", isUserGame, async (req, res) => {
if (!headers.code) return res.status(401).json({ res: res.statusCode, message: "No code provided. Please provide a valid course code." });
const ip = headers["cf-connecting-ip"] || "Unknown";
const db = req.app.locals.db;
const key = headers.authorization;
const keys = await req.app.locals.db.getData("/keys");
const keys = await db.getData("/keys");
const steamIds = Object.fromEntries(Object.entries(keys).map(([k, v]) => [v, k]));
const steamid = steamIds[key];
if (ip !== "Unknown" && (await req.app.locals.isRatelimited(ip))) return res.status(401).json({ message: "Too many requests. Please try again later." });
if (ip !== "Unknown" && (await req.app.locals.isMultiAccount(ip, steamid))) return res.status(401).json({ message: "Your account was detected as multiaccount. Please open a ticket on our Discord server." });
if (ip !== "Unknown" && (await isRatelimited(db, ip))) return res.status(401).json({ message: "Too many requests. Please try again later." });
if (ip !== "Unknown" && (await isMultiAccount(db, ip, steamid))) return res.status(401).json({ message: "Your account was detected as multiaccount. Please open a ticket on our Discord server." });
if (await isLocked(db, steamid)) return res.status(401).json({ res: res.statusCode, message: "Your account is locked. Please open a ticket on our Discord server." });
const courseData = await req.app.locals.db.getData(`/courses/${headers.code.toUpperCase()}`);
const courseData = await db.getData(`/courses/${headers.code.toUpperCase()}`);
if (courseData.map !== headers.map) return res.status(401).json({ res: res.statusCode, message: "Invalid map. You should provide the same map as before." });
if (courseData.uploader.userid !== steamIds[key]) return res.status(401).json({ res: res.statusCode, message: "Invalid key. You are not the uploader of this course. Only the uploader can update their course." });
@ -194,7 +202,7 @@ router.post("/update", isUserGame, async (req, res) => {
let course = "";
try {
course = lzma.decompress(Buffer.from(headers.course, "base64"));
} catch (e) {
} catch {
course = Buffer.from(headers.course, "base64").toString("utf-8");
}
@ -204,7 +212,7 @@ router.post("/update", isUserGame, async (req, res) => {
courseData.time = Date.now();
await req.app.locals.db.push(`/courses/${headers.code.toUpperCase()}`, courseData);
await db.push(`/courses/${headers.code.toUpperCase()}`, courseData);
await log(
`[UPDATE] User updated a course (Course: ${headers.code.toUpperCase()}, SteamID: ${steamIds[key]}, Key ${key}).`,
@ -242,7 +250,7 @@ router.post("/admin", isAdmin, async (req, res) => {
if (action === "addKey") {
if (!target) return res.send({ success: false, message: "Target not provided. Please provide a target." });
const key = await req.app.locals.getKey(target);
const key = await getKey(req.app.locals.db, target);
if (!key) return res.send({ success: false, message: "Internal error. Contact the developer." });
@ -364,7 +372,7 @@ router.get("/info/:code", async (req, res) => {
try {
course = await req.app.locals.db.getData(`/courses/${req.params.code.toUpperCase()}`);
} catch (e) {
} catch {
return res.status(401).json({ res: res.statusCode, message: "Invalid course code provided." });
}

View file

@ -60,7 +60,7 @@ router.get("/", async (req, res) => {
let codeFile = "";
try {
codeFile = fs.readFileSync(`public/${codeData.path}`, "utf-8");
} catch (e) {
} catch {
return console.log(`[WARNING] Not found file for: ${code}`);
}

View file

@ -2,7 +2,7 @@ const express = require("express"),
router = express.Router(),
fetch = require("node-fetch");
const { sanitize } = require("../utils/functions");
const { getKey, sanitize } = require("../utils/functions");
router.get("/", async (req, res) => {
if (req.user) {
@ -55,7 +55,7 @@ async function registerUser(locals, user) {
if (!hasGame(locals, user)) return "Account doesn't have Garry's mod. Make sure your game details are public.";
const usernames = await locals.db.getData("/usernames");
const key = await locals.getKey(user);
const key = await getKey(locals.db, user);
const username = sanitize(user.personaname, false, true);
usernames[user.steamid] = username || "Unknown";

View file

@ -7,7 +7,7 @@ router.get("/:code", async (req, res) => {
try {
course = await req.app.locals.db.getData(`/courses/${req.params.code.toUpperCase()}`);
} catch (e) {
} catch {
return res.status(401).json({ res: res.statusCode, message: "Invalid course code provided." });
}

View file

@ -2,6 +2,35 @@ const fs = require("fs"),
config = require("../config"),
fetch = require("node-fetch");
/**
* Creates a new unique key for the given user and saves it to the database.
*
* @param {Object} db The database
* @param {Object | string} user The OpenID Steam user object or SteamID64.
* @returns {Promise<String>} The new unique key generated for the user.
*/
async function createKey(db, user) {
user = typeof user === "string" ? user : user.steamid;
const keys = await db.getData("/keys");
const key = generateRandomString();
const isFound = keys[key];
if (!isFound) {
keys[user] = key;
const now = Date.now();
await log(
`[KEY] New user (SteamID: ${user}, Key: ${key}, TimeCreated: ${new Date(now).toLocaleString("ru-RU")}).`,
`[KEY] New user (SteamID: \`${user}\`, Key: \`${key}\`, TimeCreated: <t:${Math.floor(now / 1000)}:f>).`,
);
await db.push("/keys", keys);
return key;
} else return await createKey(db, user);
}
/**
* Middleware function that checks if the current user is an admin.
* If the user is not an admin, it redirects them to the "/key" route.
@ -65,6 +94,115 @@ async function isUserGame(req, res, next) {
return next();
}
/**
* Checks if an IP address is currently rate limited.
*
* Gets the current rate limits from the database.
* If the IP already has a recent rate limit, returns true.
* Otherwise, saves a new rate limit for the IP and returns false.
*
* @param {Object} db The database
* @param {string} ip The IP address to check
* @returns {Promise<boolean>} Whether the IP is currently rate limited
*/
async function isRatelimited(db, ip) {
const rateLimits = await db.getData("/ratelimits");
if (rateLimits[ip] && Date.now() - rateLimits[ip] <= config.rateLimitTime) return true;
rateLimits[ip] = Date.now();
await db.push("/ratelimits", rateLimits);
return false;
}
/**
* Checks if a user is using multiple accounts based on their IP address and Steam ID.
*
* Retrieves the locked accounts and user records from the database. If the user's account is locked, returns true.
* Otherwise, checks if the user has changed their IP address more than the configured `ipChangeTime`. If so, clears the
* user's IP address history and locks the account if the user has changed their IP more than 3 times. Updates the
* database with the new account status and returns the result.
*
* @param {Object} db The database
* @param {string} ip The IP address of the user.
* @param {string} steamid The Steam ID of the user.
* @returns {Promise<boolean>} Whether the user is using multiple accounts.
*/
async function isMultiAccount(db, ip, steamid) {
const locked = await db.getData("/locked");
const records = await db.getData("/records");
if (!records[steamid])
records[steamid] = {
ips: {
[ip]: true,
},
lastchanged: Date.now(),
};
// Clear IPs if the user has changed their ip more than ipChangeTime
if (Date.now() - records[steamid]["lastchanged"] > config.ipChangeTime) {
records[steamid] = {
ips: {},
lastchanged: Date.now(),
};
}
// Lock account if the user changed their IP more than 3 time in ipChangeTime
if (Object.keys(records[steamid]["ips"]).length > 2) {
locked[steamid] = true;
await db.push("/locked", locked);
return true;
}
await db.push("/records", records);
return false;
}
/**
* Checks if user is locked from using the app.
*
* @param {Object} db The database
* @param {string} steamid The Steam ID of the user.
* @returns {Promise<boolean>} Whether the user is locked.
*/
async function isLocked(db, steamid) {
const locks = await db.getData("/locked");
if (locks[steamid]) return true;
return false;
}
/**
* Gets a user's key from the database.
*
* Checks if the user already has a key, and returns it if so.
* Otherwise generates a new one.
*
* @param {Object} db The database
* @param {Object | string} user The user object or SteamID64.
* @returns {Promise<String>} The user's key.
*/
async function getKey(db, user) {
user = typeof user === "string" ? user : user.steamid;
const keys = await db.getData("/keys");
const key = keys[user];
if (key) {
await log(
`[KEY] User logged in (SteamID: ${user}, Key ${key}).`,
`[KEY] User logged in (SteamID: \`${user}\`, Key \`${key}\`).`,
);
return key;
} else return await createKey(db, user);
}
/**
* Generates a random string of the given length.
*
@ -134,7 +272,6 @@ function isCourseFileValid(content) {
return true;
}
/**
* Logs a message to a log file and optionally sends it to a Discord webhook.
*
@ -162,4 +299,4 @@ async function log(logs_message, discord_message) {
});
}
module.exports = { isAdmin, isUser, isUserGame, generateRandomString, generateCode, sanitize, isCourseFileValid, log };
module.exports = { isAdmin, isUser, isUserGame, isRatelimited, isMultiAccount, isLocked, getKey, generateRandomString, generateCode, sanitize, isCourseFileValid, log };