Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ac7c8d04ed | |||
| fd44712e37 | |||
| b3c7f7cb5e | |||
| db03373892 | |||
| 267ecaa705 | |||
| 3a8cfd39ed | |||
| b157ef18ff | |||
| 500035284f | |||
| c5360a70d6 | |||
| cf4c8a0f99 | |||
| 21527d8931 | |||
| a54e802942 | |||
| f8abd9110d | |||
| 406345ada1 | |||
| d79a70d66f | |||
| cec30620ec | |||
| 763efb75bf |
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
FROM node:18
|
|
||||||
|
FROM node:18-bullseye-slim
|
||||||
WORKDIR "/app"
|
WORKDIR "/app"
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm install --production
|
RUN npm install --production
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2021 Kayne Ruse, KR Game Studios
|
Copyright (c) 2021-2023 Kayne Ruse, KR Game Studios
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -133,7 +133,7 @@ networks:
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const dockerfile = `
|
const dockerfile = `
|
||||||
FROM node:18
|
FROM node:18-bullseye-slim
|
||||||
WORKDIR "/app"
|
WORKDIR "/app"
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm install --production
|
RUN npm install --production
|
||||||
|
|||||||
Generated
+97
-138
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "auth-server",
|
"name": "auth-server",
|
||||||
"version": "1.6.4",
|
"version": "1.7.8",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "auth-server",
|
"name": "auth-server",
|
||||||
"version": "1.6.4",
|
"version": "1.7.8",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
@@ -14,15 +14,15 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"mariadb": "^3.0.2",
|
"mariadb": "^3.1.1",
|
||||||
"node-cron": "^3.0.2",
|
"node-cron": "^3.0.2",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.9",
|
||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.9.1",
|
||||||
"sequelize": "^6.25.5"
|
"sequelize": "^6.31.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^2.0.20"
|
"nodemon": "^2.0.22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/debug": {
|
"node_modules/@types/debug": {
|
||||||
@@ -49,9 +49,9 @@
|
|||||||
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
|
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/validator": {
|
"node_modules/@types/validator": {
|
||||||
"version": "13.7.10",
|
"version": "13.7.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.15.tgz",
|
||||||
"integrity": "sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ=="
|
"integrity": "sha512-yeinDVQunb03AEP8luErFcyf/7Lf7AzKCD0NXfgVoGCCQDNpZET8Jgq74oBgqKld3hafLbfzt/3inUdQvaFeXQ=="
|
||||||
},
|
},
|
||||||
"node_modules/abbrev": {
|
"node_modules/abbrev": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
@@ -72,9 +72,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/anymatch": {
|
"node_modules/anymatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
@@ -224,9 +224,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/content-type": {
|
"node_modules/content-type": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
|
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -310,9 +310,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dottie": {
|
"node_modules/dottie": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.3.tgz",
|
||||||
"integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg=="
|
"integrity": "sha512-4liA0PuRkZWQFQjwBypdxPfZaRWiv5tkhMXY2hzsa2pNf5s7U3m9cwUchfNKe8wZQxdGPQQzO6Rm2uGe0rvohQ=="
|
||||||
},
|
},
|
||||||
"node_modules/ecdsa-sig-formatter": {
|
"node_modules/ecdsa-sig-formatter": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
@@ -462,9 +462,9 @@
|
|||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||||
},
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.1.3",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||||
"integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
|
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.1",
|
"function-bind": "^1.1.1",
|
||||||
"has": "^1.0.3",
|
"has": "^1.0.3",
|
||||||
@@ -613,24 +613,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsonwebtoken": {
|
"node_modules/jsonwebtoken": {
|
||||||
"version": "8.5.1",
|
"version": "9.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
|
||||||
"integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
|
"integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jws": "^3.2.2",
|
"jws": "^3.2.2",
|
||||||
"lodash.includes": "^4.3.0",
|
"lodash": "^4.17.21",
|
||||||
"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.0.0",
|
|
||||||
"ms": "^2.1.1",
|
"ms": "^2.1.1",
|
||||||
"semver": "^5.6.0"
|
"semver": "^7.3.8"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4",
|
"node": ">=12",
|
||||||
"npm": ">=1.4.28"
|
"npm": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsonwebtoken/node_modules/ms": {
|
"node_modules/jsonwebtoken/node_modules/ms": {
|
||||||
@@ -662,60 +656,24 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
},
|
},
|
||||||
"node_modules/lodash.includes": {
|
|
||||||
"version": "4.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
|
||||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
|
|
||||||
},
|
|
||||||
"node_modules/lodash.isboolean": {
|
|
||||||
"version": "3.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
|
||||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
|
||||||
},
|
|
||||||
"node_modules/lodash.isinteger": {
|
|
||||||
"version": "4.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
|
||||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
|
|
||||||
},
|
|
||||||
"node_modules/lodash.isnumber": {
|
|
||||||
"version": "3.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
|
||||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
|
|
||||||
},
|
|
||||||
"node_modules/lodash.isplainobject": {
|
|
||||||
"version": "4.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
|
||||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
|
|
||||||
},
|
|
||||||
"node_modules/lodash.isstring": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
|
|
||||||
},
|
|
||||||
"node_modules/lodash.once": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
|
|
||||||
},
|
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "7.14.1",
|
"version": "7.18.3",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
|
||||||
"integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==",
|
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mariadb": {
|
"node_modules/mariadb": {
|
||||||
"version": "3.0.2",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/mariadb/-/mariadb-3.1.1.tgz",
|
||||||
"integrity": "sha512-dVjiQZ6RW0IXFnX+T/ZEmnqs724DgkQsXqfCyInXn0XxVfO2Px6KbS4M3Ny6UiBg0zJ93SHHvfVBgYO4ZnFvvw==",
|
"integrity": "sha512-Y5tu9pQr8uZs63FATr7ldODXn8N2aIFlAg/rp6kRTohgQiJfdl9DNGu9PXRTYdY4JgF5mT2ASD81Jdo5kfGYzg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/geojson": "^7946.0.10",
|
"@types/geojson": "^7946.0.10",
|
||||||
"@types/node": "^17.0.45",
|
"@types/node": "^17.0.45",
|
||||||
"denque": "^2.1.0",
|
"denque": "^2.1.0",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"lru-cache": "^7.14.0",
|
"lru-cache": "^7.14.0"
|
||||||
"moment-timezone": "^0.5.38"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 12"
|
"node": ">= 12"
|
||||||
@@ -804,11 +762,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/moment-timezone": {
|
"node_modules/moment-timezone": {
|
||||||
"version": "0.5.38",
|
"version": "0.5.43",
|
||||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.38.tgz",
|
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz",
|
||||||
"integrity": "sha512-nMIrzGah4+oYZPflDvLZUgoVUO4fvAqHstvG3xAUnMolWncuAiLDWNnJZj6EwJGMGfb1ZcuTFE6GI3hNOVWI/Q==",
|
"integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"moment": ">= 2.9.0"
|
"moment": "^2.29.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "*"
|
"node": "*"
|
||||||
@@ -839,9 +797,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.6.7",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
||||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
"integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"whatwg-url": "^5.0.0"
|
"whatwg-url": "^5.0.0"
|
||||||
},
|
},
|
||||||
@@ -858,17 +816,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nodemailer": {
|
"node_modules/nodemailer": {
|
||||||
"version": "6.8.0",
|
"version": "6.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.1.tgz",
|
||||||
"integrity": "sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ==",
|
"integrity": "sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nodemon": {
|
"node_modules/nodemon": {
|
||||||
"version": "2.0.20",
|
"version": "2.0.22",
|
||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz",
|
||||||
"integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==",
|
"integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^3.5.2",
|
"chokidar": "^3.5.2",
|
||||||
@@ -908,6 +866,15 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/nodemon/node_modules/semver": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nopt": {
|
"node_modules/nopt": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
|
||||||
@@ -941,9 +908,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/object-inspect": {
|
"node_modules/object-inspect": {
|
||||||
"version": "1.12.2",
|
"version": "1.12.3",
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||||
"integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
|
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
@@ -1056,9 +1023,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/retry-as-promised": {
|
"node_modules/retry-as-promised": {
|
||||||
"version": "6.1.0",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz",
|
||||||
"integrity": "sha512-Hj/jY+wFC+SB9SDlIIFWiGOHnNG0swYbGYsOj2BJ8u2HKUaobNKab0OIC0zOLYzDy0mb7A4xA5BMo4LMz5YtEA=="
|
"integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA=="
|
||||||
},
|
},
|
||||||
"node_modules/safe-buffer": {
|
"node_modules/safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
@@ -1085,11 +1052,28 @@
|
|||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "5.7.1",
|
"version": "7.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz",
|
||||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
"integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==",
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": "^6.0.0"
|
||||||
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver"
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/semver/node_modules/lru-cache": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/send": {
|
"node_modules/send": {
|
||||||
@@ -1121,9 +1105,9 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
},
|
},
|
||||||
"node_modules/sequelize": {
|
"node_modules/sequelize": {
|
||||||
"version": "6.25.5",
|
"version": "6.31.1",
|
||||||
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.25.5.tgz",
|
"resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.31.1.tgz",
|
||||||
"integrity": "sha512-QwA2kL4lSNy4mYTv1Zm4DjvloZLaYajuYF5QMOv0O7FameMUEl8Q5zOWRD+FNKGCLzyOJ28gQCo3HX0N9mcssA==",
|
"integrity": "sha512-cahWtRrYLjqoZP/aurGBoaxn29qQCF4bxkAUPEQ/ozjJjt6mtL4Q113S3N39mQRmX5fgxRbli+bzZARP/N51eg==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -1138,9 +1122,9 @@
|
|||||||
"inflection": "^1.13.2",
|
"inflection": "^1.13.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"moment-timezone": "^0.5.34",
|
"moment-timezone": "^0.5.35",
|
||||||
"pg-connection-string": "^2.5.0",
|
"pg-connection-string": "^2.5.0",
|
||||||
"retry-as-promised": "^6.1.0",
|
"retry-as-promised": "^7.0.3",
|
||||||
"semver": "^7.3.5",
|
"semver": "^7.3.5",
|
||||||
"sequelize-pool": "^7.1.0",
|
"sequelize-pool": "^7.1.0",
|
||||||
"toposort-class": "^1.0.1",
|
"toposort-class": "^1.0.1",
|
||||||
@@ -1205,36 +1189,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sequelize/node_modules/lru-cache": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
|
||||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
|
||||||
"dependencies": {
|
|
||||||
"yallist": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sequelize/node_modules/ms": {
|
"node_modules/sequelize/node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
"node_modules/sequelize/node_modules/semver": {
|
|
||||||
"version": "7.3.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
|
||||||
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
|
|
||||||
"dependencies": {
|
|
||||||
"lru-cache": "^6.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"semver": "bin/semver.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/serve-static": {
|
"node_modules/serve-static": {
|
||||||
"version": "1.15.0",
|
"version": "1.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
||||||
@@ -1268,9 +1227,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/simple-update-notifier": {
|
"node_modules/simple-update-notifier": {
|
||||||
"version": "1.0.7",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
|
||||||
"integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==",
|
"integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"semver": "~7.0.0"
|
"semver": "~7.0.0"
|
||||||
@@ -1393,9 +1352,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/validator": {
|
"node_modules/validator": {
|
||||||
"version": "13.7.0",
|
"version": "13.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz",
|
||||||
"integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==",
|
"integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "auth-server",
|
"name": "auth-server",
|
||||||
"version": "1.6.4",
|
"version": "1.7.8",
|
||||||
"description": "An API centric auth server. Uses Sequelize and mariaDB by default.",
|
"description": "An API centric auth server. Uses Sequelize and mariaDB by default.",
|
||||||
"main": "server/server.js",
|
"main": "server/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -24,14 +24,14 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"mariadb": "^3.0.2",
|
"mariadb": "^3.1.1",
|
||||||
"node-cron": "^3.0.2",
|
"node-cron": "^3.0.2",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.9",
|
||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.9.1",
|
||||||
"sequelize": "^6.25.5"
|
"sequelize": "^6.31.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^2.0.20"
|
"nodemon": "^2.0.22"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-1
@@ -19,6 +19,13 @@ const route = async (req, res) => {
|
|||||||
return res.status(401).send(validateErr);
|
return res.status(401).send(validateErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//script throttle
|
||||||
|
const throttle = await checkThrottle(req.body.email);
|
||||||
|
if (throttle) {
|
||||||
|
console.warn(`Spam Throttled\t${req.body.email} (${req.body.username})`);
|
||||||
|
return res.status(401).send(throttle);
|
||||||
|
}
|
||||||
|
|
||||||
//generate the password hash
|
//generate the password hash
|
||||||
const hash = await bcrypt.hash(req.body.password, await bcrypt.genSalt(11));
|
const hash = await bcrypt.hash(req.body.password, await bcrypt.genSalt(11));
|
||||||
|
|
||||||
@@ -83,6 +90,10 @@ const validateDetails = async (body) => {
|
|||||||
return 'Missing password';
|
return 'Missing password';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof body.password != "string") {
|
||||||
|
return 'Invalid password';
|
||||||
|
}
|
||||||
|
|
||||||
if (body.password.length < 8) {
|
if (body.password.length < 8) {
|
||||||
return 'Password too short';
|
return 'Password too short';
|
||||||
}
|
}
|
||||||
@@ -90,8 +101,41 @@ const validateDetails = async (body) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkThrottle = async (email) => {
|
||||||
|
//check email delay
|
||||||
|
const prev = await pendingSignups.findOne({
|
||||||
|
where: {
|
||||||
|
email: email,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const DateOffset = ( offset ) => { //Thanks, SO!
|
||||||
|
return new Date( +new Date + offset );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!prev && prev.updatedAt > DateOffset( -5000 )) {
|
||||||
|
return "An unknown error occurred";
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const registerPendingSignup = async (body, hash, token) => {
|
const registerPendingSignup = async (body, hash, token) => {
|
||||||
const record = await pendingSignups.upsert({
|
//BUGFIX: delete existing pending signups that clash
|
||||||
|
await pendingSignups.destroy({
|
||||||
|
where: {
|
||||||
|
email: body.email
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await pendingSignups.destroy({
|
||||||
|
where: {
|
||||||
|
username: body.username
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//record it
|
||||||
|
const record = await pendingSignups.create({
|
||||||
email: body.email,
|
email: body.email,
|
||||||
username: body.username,
|
username: body.username,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const sequelize = require('..');
|
||||||
|
|
||||||
|
module.exports = sequelize.define('bannedIPAddresses', {
|
||||||
|
content: {
|
||||||
|
type: 'varchar(320)',
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
|
||||||
|
expiry: {
|
||||||
|
type: 'DATETIME',
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: null
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -2,5 +2,6 @@ module.exports = {
|
|||||||
tokens: require('./tokens'),
|
tokens: require('./tokens'),
|
||||||
accounts: require('./accounts'),
|
accounts: require('./accounts'),
|
||||||
pendingSignups: require('./pending-signups'),
|
pendingSignups: require('./pending-signups'),
|
||||||
recovery: require('./recovery')
|
recovery: require('./recovery'),
|
||||||
|
bannedIPAddresses: require("./banned-ip-addresses"),
|
||||||
};
|
};
|
||||||
@@ -23,6 +23,9 @@ app.use(cookieParser());
|
|||||||
//database connection
|
//database connection
|
||||||
const database = require('./database');
|
const database = require('./database');
|
||||||
|
|
||||||
|
//ip-based management
|
||||||
|
app.use(require('./utilities/banned-up-addresses-middleware'));
|
||||||
|
|
||||||
//access the admin
|
//access the admin
|
||||||
app.use('/admin', require('./admin'));
|
app.use('/admin', require('./admin'));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
const { Op } = require("sequelize");
|
||||||
|
const { bannedIPAddresses } = require('../database/models');
|
||||||
|
|
||||||
|
//middleware to manage banned IP addresses
|
||||||
|
module.exports = async (req, res, next) => {
|
||||||
|
const address = req.header('x-forwarded-for') || req.socket.remoteAddress;
|
||||||
|
|
||||||
|
const record = await bannedIPAddresses.findOne({
|
||||||
|
where: {
|
||||||
|
content: address,
|
||||||
|
|
||||||
|
expiry: {
|
||||||
|
[Op.or]: {
|
||||||
|
//future or forever
|
||||||
|
[Op.gt]: Date.now(),
|
||||||
|
[Op.eq]: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//log the access timestamp
|
||||||
|
const date = new Date();
|
||||||
|
|
||||||
|
if (!!record) {
|
||||||
|
console.log(`IP blocked\t${address}\t\t\t${date.toTimeString()}`);
|
||||||
|
return res.status(403).send("IP address banned");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`IP allowed\t${address}\t\t\t${date.toTimeString()}`);
|
||||||
|
|
||||||
|
return next();
|
||||||
|
};
|
||||||
@@ -9,6 +9,12 @@ const TokenProvider = props => {
|
|||||||
//state to be used
|
//state to be used
|
||||||
const [accessToken, setAccessToken] = useState('');
|
const [accessToken, setAccessToken] = useState('');
|
||||||
|
|
||||||
|
//force a logout under certain conditions
|
||||||
|
const forceLogout = () => {
|
||||||
|
localStorage.removeItem("accessToken");
|
||||||
|
setAccessToken("");
|
||||||
|
};
|
||||||
|
|
||||||
//make the access token persist between reloads
|
//make the access token persist between reloads
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAccessToken(localStorage.getItem("accessToken") || '');
|
setAccessToken(localStorage.getItem("accessToken") || '');
|
||||||
@@ -19,6 +25,11 @@ const TokenProvider = props => {
|
|||||||
localStorage.setItem("accessToken", accessToken);
|
localStorage.setItem("accessToken", accessToken);
|
||||||
}, [accessToken]);
|
}, [accessToken]);
|
||||||
|
|
||||||
|
//force a logout if refresh token is too old
|
||||||
|
if (accessToken && (new Date(Date.now() - 60 * 60 * 24 * 30 * 1000).getTime() > decode(accessToken).exp * 1000)) {
|
||||||
|
forceLogout();
|
||||||
|
}
|
||||||
|
|
||||||
//wrap the default fetch function
|
//wrap the default fetch function
|
||||||
const tokenFetch = async (url, options) => {
|
const tokenFetch = async (url, options) => {
|
||||||
//use this?
|
//use this?
|
||||||
@@ -47,6 +58,9 @@ const TokenProvider = props => {
|
|||||||
|
|
||||||
//any errors, throw them
|
//any errors, throw them
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
if (response.status == 403) {
|
||||||
|
forceLogout();
|
||||||
|
}
|
||||||
throw `${response.status}: ${await response.text()}`;
|
throw `${response.status}: ${await response.text()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +96,9 @@ const TokenProvider = props => {
|
|||||||
|
|
||||||
//any errors, throw them
|
//any errors, throw them
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
if (response.status == 403) {
|
||||||
|
forceLogout();
|
||||||
|
}
|
||||||
throw `${response.status}: ${await response.text()}`;
|
throw `${response.status}: ${await response.text()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user