From 575efff1cb61bab5d64716eeb23362b40e6ae994 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Mon, 31 Mar 2025 17:00:28 +0200 Subject: [PATCH] feat: Added authentication system and configManager bases fix: Fixed some errors related to users --- config/config.json | 4 +- package.json | 3 + pnpm-lock.yaml | 429 ++++++++++++++++++++++++++++++++++++ server.js | 2 + src/controllers/auth.js | 12 + src/database/index.js | 31 ++- src/middleware/auth.js | 23 ++ src/models/user.js | 24 +- src/routes/login.js | 11 + src/routes/mods.js | 2 +- src/services/authService.js | 66 ++++++ src/utils/appError.js | 14 +- src/utils/configManager.js | 24 ++ src/utils/validate.js | 57 ++++- 14 files changed, 681 insertions(+), 21 deletions(-) create mode 100644 src/controllers/auth.js create mode 100644 src/middleware/auth.js create mode 100644 src/routes/login.js create mode 100644 src/services/authService.js diff --git a/config/config.json b/config/config.json index f798a38..0b7f9c1 100644 --- a/config/config.json +++ b/config/config.json @@ -10,5 +10,7 @@ "database": { "type": "sqlite" - } + }, + + "JWT_secret": "HGF7654EGBNKJNBJH6754356788GJHGY" } \ No newline at end of file diff --git a/package.json b/package.json index 07b3836..fd5b65d 100644 --- a/package.json +++ b/package.json @@ -13,15 +13,18 @@ "license": "ISC", "packageManager": "pnpm@10.5.2", "dependencies": { + "bcrypt": "^5.1.1", "better-sqlite3": "^11.9.1", "dompurify": "^3.2.4", "express": "^4.21.2", "jsdom": "^26.0.0", + "jsonwebtoken": "^9.0.2", "marked": "^15.0.7", "mysql": "^2.18.1" }, "pnpm": { "onlyBuiltDependencies": [ + "bcrypt", "better-sqlite3" ] }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 498707d..c3f90e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + bcrypt: + specifier: ^5.1.1 + version: 5.1.1 better-sqlite3: specifier: ^11.9.1 version: 11.9.1 @@ -20,6 +23,9 @@ importers: jsdom: specifier: ^26.0.0 version: 26.0.0 + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 marked: specifier: ^15.0.7 version: 15.0.7 @@ -64,21 +70,44 @@ packages: resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} engines: {node: '>=18'} + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} + hasBin: true + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + agent-base@7.1.3: resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + + are-we-there-yet@2.0.0: + resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} @@ -91,6 +120,10 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + bcrypt@5.1.1: + resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==} + engines: {node: '>= 10.0.0'} + better-sqlite3@11.9.1: resolution: {integrity: sha512-Ba0KR+Fzxh2jDRhdg6TSH0SJGzb8C0aBY4hR8w8madIdIzzC6Y1+kx5qR6eS1Z+Gy20h6ZU28aeyg0z1VIrShQ==} @@ -118,6 +151,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -140,6 +176,14 @@ packages: chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -147,6 +191,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -205,6 +252,9 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -224,9 +274,15 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} @@ -299,6 +355,13 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -307,6 +370,11 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + gauge@3.0.2: + resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} + engines: {node: '>=10'} + deprecated: This package is no longer supported. + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -322,6 +390,10 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -338,6 +410,9 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -354,6 +429,10 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -372,6 +451,10 @@ packages: ignore-by-default@1.0.1: resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -390,6 +473,10 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -413,9 +500,44 @@ packages: canvas: optional: true + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + + jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + marked@15.0.7: resolution: {integrity: sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==} engines: {node: '>= 18'} @@ -459,9 +581,26 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -483,18 +622,43 @@ packages: resolution: {integrity: sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==} engines: {node: '>=10'} + node-addon-api@5.1.0: + resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + nodemon@3.1.9: resolution: {integrity: sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==} engines: {node: '>=10'} hasBin: true + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + nwsapi@2.2.20: resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -513,6 +677,10 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-to-regexp@0.1.12: resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} @@ -569,6 +737,11 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} @@ -585,6 +758,10 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.7.1: resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} engines: {node: '>=10'} @@ -598,6 +775,9 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -617,6 +797,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} @@ -635,12 +818,20 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -659,6 +850,10 @@ packages: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + tldts-core@6.1.85: resolution: {integrity: sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==} @@ -682,6 +877,9 @@ packages: resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} engines: {node: '>=16'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@5.1.0: resolution: {integrity: sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==} engines: {node: '>=18'} @@ -715,6 +913,9 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -731,6 +932,12 @@ packages: resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} engines: {node: '>=18'} + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -753,6 +960,9 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + snapshots: '@asamuzakjp/css-color@3.1.1': @@ -783,21 +993,53 @@ snapshots: '@csstools/css-tokenizer@3.0.3': {} + '@mapbox/node-pre-gyp@1.0.11': + dependencies: + detect-libc: 2.0.3 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0 + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.7.1 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + '@types/trusted-types@2.0.7': optional: true + abbrev@1.1.1: {} + accepts@1.3.8: dependencies: mime-types: 2.1.35 negotiator: 0.6.3 + agent-base@6.0.2: + dependencies: + debug: 4.4.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + agent-base@7.1.3: {} + ansi-regex@5.0.1: {} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 + aproba@2.0.0: {} + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + array-flatten@1.1.1: {} asynckit@0.4.0: {} @@ -806,6 +1048,14 @@ snapshots: base64-js@1.5.1: {} + bcrypt@5.1.1: + dependencies: + '@mapbox/node-pre-gyp': 1.0.11 + node-addon-api: 5.1.0 + transitivePeerDependencies: + - encoding + - supports-color + better-sqlite3@11.9.1: dependencies: bindings: 1.5.0 @@ -851,6 +1101,8 @@ snapshots: dependencies: fill-range: 7.1.1 + buffer-equal-constant-time@1.0.1: {} + buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -882,12 +1134,18 @@ snapshots: chownr@1.1.4: {} + chownr@2.0.0: {} + + color-support@1.1.3: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 concat-map@0.0.1: {} + console-control-strings@1.1.0: {} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -930,6 +1188,8 @@ snapshots: delayed-stream@1.0.0: {} + delegates@1.0.0: {} + depd@2.0.0: {} destroy@1.2.0: {} @@ -946,8 +1206,14 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + ee-first@1.1.1: {} + emoji-regex@8.0.0: {} + encodeurl@1.0.2: {} encodeurl@2.0.0: {} @@ -1046,11 +1312,29 @@ snapshots: fs-constants@1.0.0: {} + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs.realpath@1.0.0: {} + fsevents@2.3.3: optional: true function-bind@1.1.2: {} + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -1075,6 +1359,15 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + gopd@1.2.0: {} has-flag@3.0.0: {} @@ -1085,6 +1378,8 @@ snapshots: dependencies: has-symbols: 1.1.0 + has-unicode@2.0.1: {} + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -1108,6 +1403,13 @@ snapshots: transitivePeerDependencies: - supports-color + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.0(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 @@ -1127,6 +1429,11 @@ snapshots: ignore-by-default@1.0.1: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + inherits@2.0.4: {} ini@1.3.8: {} @@ -1139,6 +1446,8 @@ snapshots: is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -1177,8 +1486,50 @@ snapshots: - supports-color - utf-8-validate + jsonwebtoken@9.0.2: + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.1 + + jwa@1.4.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@3.2.2: + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.once@4.1.1: {} + lru-cache@10.4.3: {} + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + marked@15.0.7: {} math-intrinsics@1.1.0: {} @@ -1205,8 +1556,21 @@ snapshots: minimist@1.2.8: {} + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + mkdirp-classic@0.5.3: {} + mkdirp@1.0.4: {} + ms@2.0.0: {} ms@2.1.3: {} @@ -1226,6 +1590,12 @@ snapshots: dependencies: semver: 7.7.1 + node-addon-api@5.1.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + nodemon@3.1.9: dependencies: chokidar: 3.6.0 @@ -1239,10 +1609,23 @@ snapshots: touch: 3.1.1 undefsafe: 2.0.5 + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 + normalize-path@3.0.0: {} + npmlog@5.0.1: + dependencies: + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 + nwsapi@2.2.20: {} + object-assign@4.1.1: {} + object-inspect@1.13.4: {} on-finished@2.4.1: @@ -1259,6 +1642,8 @@ snapshots: parseurl@1.3.3: {} + path-is-absolute@1.0.1: {} + path-to-regexp@0.1.12: {} picomatch@2.3.1: {} @@ -1334,6 +1719,10 @@ snapshots: dependencies: picomatch: 2.3.1 + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + rrweb-cssom@0.8.0: {} safe-buffer@5.1.2: {} @@ -1346,6 +1735,8 @@ snapshots: dependencies: xmlchars: 2.2.0 + semver@6.3.1: {} + semver@7.7.1: {} send@0.19.0: @@ -1375,6 +1766,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-blocking@2.0.0: {} + setprototypeof@1.2.0: {} side-channel-list@1.0.0: @@ -1405,6 +1798,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + signal-exit@3.0.7: {} + simple-concat@1.0.1: {} simple-get@4.0.1: @@ -1421,6 +1816,12 @@ snapshots: statuses@2.0.1: {} + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 @@ -1429,6 +1830,10 @@ snapshots: dependencies: safe-buffer: 5.2.1 + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + strip-json-comments@2.0.1: {} supports-color@5.5.0: @@ -1452,6 +1857,15 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + tldts-core@6.1.85: {} tldts@6.1.85: @@ -1470,6 +1884,8 @@ snapshots: dependencies: tldts: 6.1.85 + tr46@0.0.3: {} + tr46@5.1.0: dependencies: punycode: 2.3.1 @@ -1497,6 +1913,8 @@ snapshots: dependencies: xml-name-validator: 5.0.0 + webidl-conversions@3.0.1: {} + webidl-conversions@7.0.0: {} whatwg-encoding@3.1.1: @@ -1510,6 +1928,15 @@ snapshots: tr46: 5.1.0 webidl-conversions: 7.0.0 + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + wide-align@1.1.5: + dependencies: + string-width: 4.2.3 + wrappy@1.0.2: {} ws@8.18.1: {} @@ -1517,3 +1944,5 @@ snapshots: xml-name-validator@5.0.0: {} xmlchars@2.2.0: {} + + yallist@4.0.0: {} diff --git a/server.js b/server.js index 404c877..8f0804b 100644 --- a/server.js +++ b/server.js @@ -48,6 +48,8 @@ app.use(express.json()); // Request body handling app.use("/", require("./src/routes/index")); app.use("/mods", require("./src/routes/mods")); +app.use("/users", require("./src/routes/users")); +app.use("/login", require("./src/routes/login")); // --- Launch --- diff --git a/src/controllers/auth.js b/src/controllers/auth.js new file mode 100644 index 0000000..a96c813 --- /dev/null +++ b/src/controllers/auth.js @@ -0,0 +1,12 @@ +const handleError = require("../middleware/errors"); +const authService = require("../services/authService"); + +async function login(req, res) { + try { + const token = await authService.login(req.body.username, req.body.password); + res.json({ token }); + } catch (err) { + handleError(err, req, res, null); + } + +} \ No newline at end of file diff --git a/src/database/index.js b/src/database/index.js index 0049147..7576626 100644 --- a/src/database/index.js +++ b/src/database/index.js @@ -25,12 +25,12 @@ async function initDatabase(config) { // Create mods table db.exec("CREATE TABLE IF NOT EXISTS mods ( \ - Name tinytext PRIMARY KEY, \ - DisplayName tinytext, \ - Author tinytext,\ - Versions longtext,\ - OtherInfos longtext \ - );"); + Username tinytext PRIMARY KEY, \ + DisplayName tinytext, \ + Author tinytext,\ + Versions longtext,\ + OtherInfos longtext \ + );"); // Insert example mod if (!(await db.exists("mods", "Name", "example"))) { @@ -38,6 +38,25 @@ async function initDatabase(config) { db.exec(`INSERT INTO mods (Name, DisplayName, Author, Versions, OtherInfos) \ VALUES ('example', 'Example mod', '${config.users.admin.username}', '', '');`); } + + db.exec("DROP TABLE users"); + // Create users table + db.exec("CREATE TABLE IF NOT EXISTS users ( \ + Username tinytext PRIMARY KEY, \ + DisplayName tinytext, \ + Email tinytext,\ + Password tinytext,\ + ProfilePicture longtext,\ + Preferences longtext, \ + Favorites longtext \ + );"); + + // Insert default admin account + if (!(await db.exists("users", "Username", config.users.admin.username))) { + console.debug("Creating default admin user"); + db.exec(`INSERT INTO users (Username, DisplayName, Email, Password, ProfilePicture, Preferences, Favorites) \ + VALUES ('${config.users.admin.username}', 'Admin', '', '${config.users.admin.password}', '', '', '' );`); + } } diff --git a/src/middleware/auth.js b/src/middleware/auth.js new file mode 100644 index 0000000..7e5d098 --- /dev/null +++ b/src/middleware/auth.js @@ -0,0 +1,23 @@ +const authService = require("../services/authService"); +const AppError = require("../utils/appError"); + + +function authenticateToken(req, res, next) { + + const auth_header = req.headers["authorization"]; + const token = auth_header && auth_header.split(' ')[1]; + + if (token == null) { + throw new AppError(401, "Unauthorized: missing or bad authorization header"); + } + + try { + req.user = authService.verifyToken(token); + next(); + } catch (err) { + throw new AppError(403, "Forbidden: Error verifying the authorization token"); + } +} + + +module.exports = { authenticateToken } \ No newline at end of file diff --git a/src/models/user.js b/src/models/user.js index 4975f48..3f80528 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -9,7 +9,23 @@ async function getAllUsers() { async function getUserByName(name) { try { console.debug("Searching for", name); - const res = await db.query("SELECT * FROM users WHERE Name = ?;", [name]); + const res = await db.query("SELECT * FROM users WHERE Username = ?;", [name]); + if (res && res.length > 0) { + return res[0]; + } else { + return null; + } + } catch (err) { + console.error("Error in getUserByName:", err); + throw err; + } + +} + +async function getUserByEmail(email) { + try { + console.debug("Searching for", email); + const res = await db.query("SELECT * FROM users WHERE Email = ?;", [email]); if (res && res.length > 0) { return res[0]; } else { @@ -23,20 +39,20 @@ async function getUserByName(name) { } async function exists(name) { - return db.exists("users", "Name", name); + return db.exists("users", "Username", name); } async function createUser(user_data) { const { name, email, password, displayName, profilePicture, favorites, preferences } = user_data; - await db.prepare("INSERT INTO users (Name, Email, Password, DisplayName, ProfilePicture, Favorites, Preferences) \ + await db.prepare("INSERT INTO users (Username, Email, Password, DisplayName, ProfilePicture, Favorites, Preferences) \ VALUES (?, ?, ?, ?)", [name, email, password, displayName, profilePicture, favorites, preferences]); return; } async function deleteUser(name) { - db.prepare("DELETE FROM users WHERE Name = ?", [name]); + db.prepare("DELETE FROM users WHERE Username = ?", [name]); return; } diff --git a/src/routes/login.js b/src/routes/login.js new file mode 100644 index 0000000..14b8a87 --- /dev/null +++ b/src/routes/login.js @@ -0,0 +1,11 @@ +const express = require("express"); +const controller = require("../controllers/auth"); + +const router = express.Router(); + +// Login +router.post("/", async (req, res) => { + controller.login(req, res); +}); + +module.exports = router; \ No newline at end of file diff --git a/src/routes/mods.js b/src/routes/mods.js index 1b21887..3226f22 100644 --- a/src/routes/mods.js +++ b/src/routes/mods.js @@ -13,7 +13,7 @@ router.get("/", async (req,res) => { router.post("/", async (req, res) => { console.debug("Creating mod ", req.body.name); controller.createMod(req, res); -}) +}); // Get mod infos router.get("/:name", async (req,res) => { diff --git a/src/services/authService.js b/src/services/authService.js new file mode 100644 index 0000000..dc5bb90 --- /dev/null +++ b/src/services/authService.js @@ -0,0 +1,66 @@ +const bcrypt = require("bcrypt"); +const jwt = require("jsonwebtoken"); +const userModel = require("../models/user"); +const AppError = require("../utils/appError"); +const configManager = require("../utils/configManager"); +const validate = require("../utils/validate"); + +const JWT_Secret = configManager.getJWTSecret(); + +async function login(identifier, password) { + + // Check for null + if (!username || !password) { + throw new AppError(400, "Bad request", "missing credentials"); + } + + // Get user data + let user; + if (validate.isEmail(identifier)) { // If matches email + user = await userModel.getUserByEmail(username); + } else + if (validate.isID(identifier)) { // if matches username + user = await userModel.getUserByName(username); + } else { + throw new AppError(401, "Unauthorized", "Invalid credentials"); + } + + // Check if user exists + if (!user || user.length == 0) { + // throw new AppError(401, "Unauthorized: No user with this name"); + throw new AppError(401, "Unauthorized", "Invalid credentials"); + } + // Just in case + if (user.length > 1) { + throw new AppError(500, "Internal server error", "Found multiple users with this name or email, please contact administration"); + } + + // Check if passwords match + const passwords_match = await bcrypt.compare(password, user[0].password); + if (!passwords_match) { + // throw new AppError(401, "Unauthorized: Invalid password"); + throw new AppError(401, "Unauthorized", "Invalid credentials"); + } + + return jwt.sign({ username: user[0].username, role: user[0].role }, await JWT_Secret); +} + +function verifyToken(token) { + return new Promise( (resolve, reject) => { + jwt.verify( token, JWT_Secret, (err, user) => { + if (err) { + reject(err); + } else { + resolve(user); + } + }); + }); +} + +// function authorizeRole(user, roles) { +// if (!user || !roles.includes(user.role)) { +// throw new AppError(401, "Unauthorized: You don't have the necessary permissions to access this resource"); +// } +// } + +module.exports = { login, verifyToken }; \ No newline at end of file diff --git a/src/utils/appError.js b/src/utils/appError.js index bf37a85..490fcfb 100644 --- a/src/utils/appError.js +++ b/src/utils/appError.js @@ -1,12 +1,16 @@ class AppError extends Error { - constructor(statusCode, message) { + constructor(statusCode, status = "", message) { super(message); this.statusCode = statusCode; - - if (statusCode.toString().startsWith("4")) { - this.status = "Fail"; + // Get status + if (status === "") { + if (statusCode.toString().startsWith("4")) { + this.status = "Fail"; + } else { + this.status = "Error"; + } } else { - this.status = "Error"; + this.status = status; } } } diff --git a/src/utils/configManager.js b/src/utils/configManager.js index e69de29..8bb1d6d 100644 --- a/src/utils/configManager.js +++ b/src/utils/configManager.js @@ -0,0 +1,24 @@ +const fs = require("fs"); +const path = require("path"); + +// Var decalaration +let config; +const config_folder = "config"; +const config_file_name = "config.json" + +// Load config +config = JSON.parse(fs.readFileSync(path.join(config_folder, config_file_name))); + + +async function getConfig() { + return config; +} + +async function getJWTSecret() { + return config.JWT_Secret || process.env.JWT_Secret; +} + +// Default values + +// Exports +module.exports = { getJWTSecret }; \ No newline at end of file diff --git a/src/utils/validate.js b/src/utils/validate.js index dface71..63c605b 100644 --- a/src/utils/validate.js +++ b/src/utils/validate.js @@ -1,4 +1,5 @@ const mod_model = require("../models/mod"); +const user_model = require("../models/user"); const AppError = require("./appError"); async function validateModData(mod_data) { @@ -22,7 +23,7 @@ async function validateModData(mod_data) { if (!not_null) { console.debug("Item is missing expected fields:", mod_data); - throw new AppError(400, "Bad request: missing expected fields"); + throw new AppError(400, "Bad request", "Missing expected fields"); } // Check fields format (check if sanitized) @@ -34,16 +35,64 @@ async function validateModData(mod_data) { const is_valid = is_valid_name && is_valid_displayName; if (!is_valid) { console.debug("Fields are not following the expected formats"); - throw new AppError(400, "Bad request: The provided fields don't match the expected format"); + throw new AppError(400, "Bad request", "The provided fields don't match the expected format"); } // Check if mod already exists const exists = await mod_model.exists(mod_data.name); if (exists) { console.debug("Error: Item already exists"); - throw new AppError(403, "Forbidden: Content with this name already exists"); + throw new AppError(403, "Forbidden", "Content with this name already exists"); } } -module.exports = { validateModData } \ No newline at end of file +async function validateUserData(user_data) { + throw new AppError(501, "Not implemented"); + + //TODO + + // Check fields existence + // ... + + if (!not_null) { + console.debug("Missing expected fields:", mod_data); + throw new AppError(400, "Bad request: Missing expected fields"); + } + + // Check fields format (check if sanitized) + const is_valid_username = /^[a-zA-Z0-9_]+$/.test(user_data.username); + // const is_valid_email = ... + // ... + + const is_valid = is_valid_username && is_valid_email; + if (!is_valid) { + console.debug("Fields are not following the expected formats"); + throw new AppError(400, "Bad request: The provided fields don't match the expected format"); + } + + // Check if user already exists + const exists = await user_model.exists(user_data.username); + if (exists) { + console.debug("Error: User already exists"); + throw new AppError(403, "Forbidden: User with this name already exists"); + } +} + +async function validateCretendials(identifier, password) { + + throw new AppError(501, "Not implemented"); +} + +async function isEmail(text) { + const email_regex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/; + return email_regex.test(text); +} + +async function isID(text) { + const id_regex = /[a-zA-Z0-9_]+/; + return id_regex.test(text); +} + + +module.exports = { validateModData, validateUserData, isEmail, isID }; \ No newline at end of file