patches v1.3.2 #60
18 changed files with 245 additions and 31 deletions
|
@ -35,4 +35,11 @@ CLUB_WEBSITE = https://my-club-website-url #optional, muss aber mit http:// oder
|
||||||
|
|
||||||
BACKUP_INTERVAL = number of days (min 1) # default 1
|
BACKUP_INTERVAL = number of days (min 1) # default 1
|
||||||
BACKUP_COPIES = number of parallel copies # default 7
|
BACKUP_COPIES = number of parallel copies # default 7
|
||||||
BACKUP_AUTO_RESTORE = (true|false) # default false
|
BACKUP_AUTO_RESTORE = (true|false) # default ist true
|
||||||
|
|
||||||
|
USE_SECURITY_STRICT_LIMIT = (true|false) # default ist true
|
||||||
|
SECURITY_STRICT_LIMIT_WINDOW = [0-9]*(y|d|h|m|s) # default ist 15
|
||||||
|
SECURITY_STRICT_LIMIT_REQUEST_COUNT = strict_request_count # default ist 15
|
||||||
|
USE_SECURITY_LIMIT = (true|false) # default ist true
|
||||||
|
SECURITY_LIMIT_WINDOW = [0-9]*(y|d|h|m|s) # default ist 1m
|
||||||
|
SECURITY_LIMIT_REQUEST_COUNT = request_count # default ist 500
|
122
package-lock.json
generated
122
package-lock.json
generated
|
@ -12,11 +12,15 @@
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^5.0.0-beta.3",
|
"express": "^5.0.0-beta.3",
|
||||||
|
"express-rate-limit": "^7.5.0",
|
||||||
|
"express-validator": "^7.2.1",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
|
"helmet": "^8.0.0",
|
||||||
"ics": "^3.8.1",
|
"ics": "^3.8.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lodash.uniqby": "^4.7.0",
|
"lodash.uniqby": "^4.7.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
|
"morgan": "^1.10.0",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
|
@ -39,6 +43,7 @@
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
"@types/jsonwebtoken": "^9.0.6",
|
"@types/jsonwebtoken": "^9.0.6",
|
||||||
"@types/lodash.uniqby": "^4.7.9",
|
"@types/lodash.uniqby": "^4.7.9",
|
||||||
|
"@types/morgan": "^1.9.9",
|
||||||
"@types/ms": "^0.7.34",
|
"@types/ms": "^0.7.34",
|
||||||
"@types/multer": "^1.4.12",
|
"@types/multer": "^1.4.12",
|
||||||
"@types/mysql": "^2.15.21",
|
"@types/mysql": "^2.15.21",
|
||||||
|
@ -563,6 +568,16 @@
|
||||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/morgan": {
|
||||||
|
"version": "1.9.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.9.tgz",
|
||||||
|
"integrity": "sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/ms": {
|
"node_modules/@types/ms": {
|
||||||
"version": "0.7.34",
|
"version": "0.7.34",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
|
||||||
|
@ -956,6 +971,24 @@
|
||||||
"node": "^4.5.0 || >= 5.9"
|
"node": "^4.5.0 || >= 5.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/basic-auth": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "5.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/basic-auth/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/basic-ftp": {
|
"node_modules/basic-ftp": {
|
||||||
"version": "5.0.5",
|
"version": "5.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
|
||||||
|
@ -2045,6 +2078,34 @@
|
||||||
"node": ">= 4"
|
"node": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/express-rate-limit": {
|
||||||
|
"version": "7.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
|
||||||
|
"integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/express-rate-limit"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/express-validator": {
|
||||||
|
"version": "7.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.2.1.tgz",
|
||||||
|
"integrity": "sha512-CjNE6aakfpuwGaHQZ3m8ltCG2Qvivd7RHtVMS/6nVxOM7xVGqr4bhflsm4+N5FP5zI7Zxp+Hae+9RE+o8e3ZOQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"validator": "~13.12.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/express/node_modules/debug": {
|
"node_modules/express/node_modules/debug": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||||
|
@ -2405,6 +2466,15 @@
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/helmet": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/helmet/-/helmet-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-VyusHLEIIO5mjQPUI1wpOAEu+wl6Q0998jzTxqUYGE45xCIcAxy3MsbEK/yyJUJ3ADeMoB6MornPH6GMWAf+Pw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/highlight.js": {
|
"node_modules/highlight.js": {
|
||||||
"version": "10.7.3",
|
"version": "10.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
||||||
|
@ -2772,6 +2842,12 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/lodash.includes": {
|
"node_modules/lodash.includes": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||||
|
@ -3248,6 +3324,34 @@
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/morgan": {
|
||||||
|
"version": "1.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
|
||||||
|
"integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"basic-auth": "~2.0.1",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "~2.0.0",
|
||||||
|
"on-finished": "~2.3.0",
|
||||||
|
"on-headers": "~1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/morgan/node_modules/on-finished": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ee-first": "1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
@ -3531,6 +3635,15 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/on-headers": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/once": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
@ -5482,6 +5595,15 @@
|
||||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/validator": {
|
||||||
|
"version": "13.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz",
|
||||||
|
"integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vary": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
|
|
@ -27,11 +27,15 @@
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^5.0.0-beta.3",
|
"express": "^5.0.0-beta.3",
|
||||||
|
"express-rate-limit": "^7.5.0",
|
||||||
|
"express-validator": "^7.2.1",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
|
"helmet": "^8.0.0",
|
||||||
"ics": "^3.8.1",
|
"ics": "^3.8.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lodash.uniqby": "^4.7.0",
|
"lodash.uniqby": "^4.7.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
|
"morgan": "^1.10.0",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
|
@ -54,6 +58,7 @@
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
"@types/jsonwebtoken": "^9.0.6",
|
"@types/jsonwebtoken": "^9.0.6",
|
||||||
"@types/lodash.uniqby": "^4.7.9",
|
"@types/lodash.uniqby": "^4.7.9",
|
||||||
|
"@types/morgan": "^1.9.9",
|
||||||
"@types/ms": "^0.7.34",
|
"@types/ms": "^0.7.34",
|
||||||
"@types/multer": "^1.4.12",
|
"@types/multer": "^1.4.12",
|
||||||
"@types/mysql": "^2.15.21",
|
"@types/mysql": "^2.15.21",
|
||||||
|
|
|
@ -28,6 +28,13 @@ export const BACKUP_INTERVAL = Number(process.env.BACKUP_INTERVAL ?? "1");
|
||||||
export const BACKUP_COPIES = Number(process.env.BACKUP_COPIES ?? "7");
|
export const BACKUP_COPIES = Number(process.env.BACKUP_COPIES ?? "7");
|
||||||
export const BACKUP_AUTO_RESTORE = process.env.BACKUP_AUTO_RESTORE ?? "true";
|
export const BACKUP_AUTO_RESTORE = process.env.BACKUP_AUTO_RESTORE ?? "true";
|
||||||
|
|
||||||
|
export const USE_SECURITY_STRICT_LIMIT = process.env.USE_SECURITY_STRICT_LIMIT ?? "true";
|
||||||
|
export const SECURITY_STRICT_LIMIT_WINDOW = process.env.SECURITY_STRICT_LIMIT_WINDOW ?? "15m";
|
||||||
|
export const SECURITY_STRICT_LIMIT_REQUEST_COUNT = Number(process.env.SECURITY_STRICT_LIMIT_REQUEST_COUNT ?? "15");
|
||||||
|
export const USE_SECURITY_LIMIT = process.env.USE_SECURITY_LIMIT ?? "true";
|
||||||
|
export const SECURITY_LIMIT_WINDOW = process.env.SECURITY_LIMIT_WINDOW ?? "1m";
|
||||||
|
export const SECURITY_LIMIT_REQUEST_COUNT = Number(process.env.SECURITY_LIMIT_REQUEST_COUNT ?? "500");
|
||||||
|
|
||||||
export function configCheck() {
|
export function configCheck() {
|
||||||
if (DB_TYPE != "mysql" && DB_TYPE != "sqlite" && DB_TYPE != "postgres")
|
if (DB_TYPE != "mysql" && DB_TYPE != "sqlite" && DB_TYPE != "postgres")
|
||||||
throw new Error("set valid value to DB_TYPE (mysql|sqlite|postgres)");
|
throw new Error("set valid value to DB_TYPE (mysql|sqlite|postgres)");
|
||||||
|
@ -62,15 +69,26 @@ export function configCheck() {
|
||||||
throw new Error("set 'true' or 'false' to BACKUP_AUTO_RESTORE");
|
throw new Error("set 'true' or 'false' to BACKUP_AUTO_RESTORE");
|
||||||
if (BACKUP_INTERVAL < 1) throw new Error("BACKUP_INTERVAL has to be at least 1");
|
if (BACKUP_INTERVAL < 1) throw new Error("BACKUP_INTERVAL has to be at least 1");
|
||||||
if (BACKUP_COPIES < 1) throw new Error("BACKUP_COPIES has to be at least 1");
|
if (BACKUP_COPIES < 1) throw new Error("BACKUP_COPIES has to be at least 1");
|
||||||
|
|
||||||
|
if (USE_SECURITY_STRICT_LIMIT != "true" && USE_SECURITY_STRICT_LIMIT != "false")
|
||||||
|
throw new Error("set 'true' or 'false' to USE_SECURITY_STRICT_LIMIT");
|
||||||
|
checkMS(SECURITY_STRICT_LIMIT_WINDOW, "SECURITY_STRICT_LIMIT_WINDOW");
|
||||||
|
if (typeof SECURITY_STRICT_LIMIT_REQUEST_COUNT != "number")
|
||||||
|
throw new Error("set valid numeric value to SECURITY_STRICT_LIMIT_REQUEST_COUNT");
|
||||||
|
if (USE_SECURITY_LIMIT != "true" && USE_SECURITY_LIMIT != "false")
|
||||||
|
throw new Error("set 'true' or 'false' to USE_SECURITY_LIMIT");
|
||||||
|
checkMS(SECURITY_LIMIT_WINDOW, "SECURITY_LIMIT_WINDOW");
|
||||||
|
if (typeof SECURITY_LIMIT_REQUEST_COUNT != "number")
|
||||||
|
throw new Error("set valid numeric value to SECURITY_LIMIT_REQUEST_COUNT");
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkMS(input: string, origin: string) {
|
function checkMS(input: string, origin: string) {
|
||||||
try {
|
try {
|
||||||
const result = ms(input);
|
const result = ms(input);
|
||||||
if (result === undefined) {
|
if (result === undefined) {
|
||||||
throw new Error(`set valid ms value to ${origin}`);
|
throw new Error(`set valid ms value to ${origin} -> [0-9]*(y|d|h|m|s)`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`set valid ms value to ${origin}`);
|
throw new Error(`set valid ms value to ${origin} -> [0-9]*(y|d|h|m|s)`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
src/factory/admin/club/member/dateMappingHelper.ts
Normal file
14
src/factory/admin/club/member/dateMappingHelper.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { DB_TYPE } from "../../../../env.defaults";
|
||||||
|
|
||||||
|
export default abstract class DateMappingHelper {
|
||||||
|
static mapDate(entry: any) {
|
||||||
|
switch (DB_TYPE) {
|
||||||
|
case "postgres":
|
||||||
|
return `${entry.years} years ${entry.months} months ${entry.days} days`;
|
||||||
|
case "mysql":
|
||||||
|
return entry.toString();
|
||||||
|
case "sqlite":
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import { MemberStatisticsViewModel, MemberViewModel } from "../../../../viewmode
|
||||||
import { memberView } from "../../../../views/memberView";
|
import { memberView } from "../../../../views/memberView";
|
||||||
import SalutationFactory from "../../settings/salutation";
|
import SalutationFactory from "../../settings/salutation";
|
||||||
import CommunicationFactory from "./communication";
|
import CommunicationFactory from "./communication";
|
||||||
|
import DateMappingHelper from "./dateMappingHelper";
|
||||||
import MembershipFactory from "./membership";
|
import MembershipFactory from "./membership";
|
||||||
|
|
||||||
export default abstract class MemberFactory {
|
export default abstract class MemberFactory {
|
||||||
|
@ -59,7 +60,7 @@ export default abstract class MemberFactory {
|
||||||
birthdate: record.birthdate,
|
birthdate: record.birthdate,
|
||||||
todayAge: record.todayAge,
|
todayAge: record.todayAge,
|
||||||
ageThisYear: record.ageThisYear,
|
ageThisYear: record.ageThisYear,
|
||||||
exactAge: record.exactAge,
|
exactAge: DateMappingHelper.mapDate(record.exactAge),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
MembershipViewModel,
|
MembershipViewModel,
|
||||||
} from "../../../../viewmodel/admin/club/member/membership.models";
|
} from "../../../../viewmodel/admin/club/member/membership.models";
|
||||||
import { membershipView } from "../../../../views/membershipsView";
|
import { membershipView } from "../../../../views/membershipsView";
|
||||||
|
import DateMappingHelper from "./dateMappingHelper";
|
||||||
|
|
||||||
export default abstract class MembershipFactory {
|
export default abstract class MembershipFactory {
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +41,7 @@ export default abstract class MembershipFactory {
|
||||||
return {
|
return {
|
||||||
durationInDays: record.durationInDays,
|
durationInDays: record.durationInDays,
|
||||||
durationInYears: record.durationInYears,
|
durationInYears: record.durationInYears,
|
||||||
exactDuration: record.exactDuration.toString(),
|
exactDuration: DateMappingHelper.mapDate(record.exactDuration),
|
||||||
status: record.status,
|
status: record.status,
|
||||||
statusId: record.statusId,
|
statusId: record.statusId,
|
||||||
memberId: record.memberId,
|
memberId: record.memberId,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import UserService from "../service/user/userService";
|
import UserService from "../service/user/userService";
|
||||||
import CustomRequestException from "../exceptions/customRequestException";
|
import CustomRequestException from "../exceptions/customRequestException";
|
||||||
|
|
||||||
export default async function allowSetup(req: Request, res: Response, next: Function) {
|
export default async function allowSetup(req: Request, res: Response, next: NextFunction) {
|
||||||
let count = await UserService.count();
|
let count = await UserService.count();
|
||||||
if (count != 0) {
|
if (count != 0) {
|
||||||
throw new CustomRequestException(405, "service is already set up");
|
throw new CustomRequestException(405, "service is already set up");
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import BadRequestException from "../exceptions/badRequestException";
|
import BadRequestException from "../exceptions/badRequestException";
|
||||||
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
||||||
import InternalException from "../exceptions/internalException";
|
import InternalException from "../exceptions/internalException";
|
||||||
import { JWTHelper } from "../helpers/jwtHelper";
|
import { JWTHelper } from "../helpers/jwtHelper";
|
||||||
|
|
||||||
export default async function authenticate(req: Request, res: Response, next: Function) {
|
export default async function authenticate(req: Request, res: Response, next: NextFunction) {
|
||||||
const bearer = req.headers.authorization?.split(" ")?.[1] ?? undefined;
|
const bearer = req.headers.authorization?.split(" ")?.[1] ?? undefined;
|
||||||
|
|
||||||
if (!bearer) {
|
if (!bearer) {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import BadRequestException from "../exceptions/badRequestException";
|
import BadRequestException from "../exceptions/badRequestException";
|
||||||
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
||||||
import InternalException from "../exceptions/internalException";
|
import InternalException from "../exceptions/internalException";
|
||||||
import { JWTHelper } from "../helpers/jwtHelper";
|
import { JWTHelper } from "../helpers/jwtHelper";
|
||||||
|
|
||||||
export default async function authenticateAPI(req: Request, res: Response, next: Function) {
|
export default async function authenticateAPI(req: Request, res: Response, next: NextFunction) {
|
||||||
const bearer = req.headers.authorization?.split(" ")?.[1] ?? undefined;
|
const bearer = req.headers.authorization?.split(" ")?.[1] ?? undefined;
|
||||||
|
|
||||||
if (!bearer) {
|
if (!bearer) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
|
|
||||||
export default async function detectPWA(req: Request, res: Response, next: Function) {
|
export default async function detectPWA(req: Request, res: Response, next: NextFunction) {
|
||||||
const userAgent = req.headers["user-agent"] || "";
|
const userAgent = req.headers["user-agent"] || "";
|
||||||
if ((userAgent.includes("Mobile") && userAgent.includes("Standalone")) || req.headers["x-pwa-client"] === "true") {
|
if ((userAgent.includes("Mobile") && userAgent.includes("Standalone")) || req.headers["x-pwa-client"] === "true") {
|
||||||
req.isPWA = true;
|
req.isPWA = true;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import { ExceptionBase } from "../exceptions/exceptionsBaseType";
|
import { ExceptionBase } from "../exceptions/exceptionsBaseType";
|
||||||
import CustomRequestException from "../exceptions/customRequestException";
|
import CustomRequestException from "../exceptions/customRequestException";
|
||||||
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
||||||
|
|
||||||
export default function errorHandler(err: ExceptionBase | Error, req: Request, res: Response, next: Function) {
|
export default function errorHandler(err: ExceptionBase | Error, req: Request, res: Response, next: NextFunction) {
|
||||||
let status = 500;
|
let status = 500;
|
||||||
let msg = "Internal Server Error";
|
let msg = "Internal Server Error";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import ForbiddenRequestException from "../exceptions/forbiddenRequestException";
|
import ForbiddenRequestException from "../exceptions/forbiddenRequestException";
|
||||||
|
|
||||||
export default async function preventWebapiAccess(req: Request, res: Response, next: Function) {
|
export default async function preventWebapiAccess(req: Request, res: Response, next: NextFunction) {
|
||||||
if (req.isWebApiRequest) {
|
if (req.isWebApiRequest) {
|
||||||
throw new ForbiddenRequestException("This route cannot be accessed via webapi");
|
throw new ForbiddenRequestException("This route cannot be accessed via webapi");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -336,7 +336,7 @@ export const member_executive_positions_view_postgres = new View({
|
||||||
"member"."birthdate" AS "memberBirthdate",
|
"member"."birthdate" AS "memberBirthdate",
|
||||||
"salutation"."salutation" AS "memberSalutation",
|
"salutation"."salutation" AS "memberSalutation",
|
||||||
SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start") AS "durationInDays",
|
SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start") AS "durationInDays",
|
||||||
SUM(EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate)) AS "durationInYears",
|
SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start"))) AS "durationInYears",
|
||||||
SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")) AS "exactDuration"
|
SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")) AS "exactDuration"
|
||||||
FROM "member_executive_positions" "memberExecutivePositions"
|
FROM "member_executive_positions" "memberExecutivePositions"
|
||||||
LEFT JOIN "executive_position" "executivePosition" ON "executivePosition"."id"="memberExecutivePositions"."executivePositionId"
|
LEFT JOIN "executive_position" "executivePosition" ON "executivePosition"."id"="memberExecutivePositions"."executivePositionId"
|
||||||
|
@ -415,7 +415,8 @@ export const member_qualifications_view_postgres = new View({
|
||||||
"member"."birthdate" AS "memberBirthdate",
|
"member"."birthdate" AS "memberBirthdate",
|
||||||
"salutation"."salutation" AS "memberSalutation",
|
"salutation"."salutation" AS "memberSalutation",
|
||||||
SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start") AS "durationInDays",
|
SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start") AS "durationInDays",
|
||||||
SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start")) AS "durationInYears"
|
SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start"))) AS "durationInYears",
|
||||||
|
SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start")) AS "exactDuration"
|
||||||
FROM "member_qualifications" "memberQualifications"
|
FROM "member_qualifications" "memberQualifications"
|
||||||
LEFT JOIN "qualification" "qualification" ON "qualification"."id"="memberQualifications"."qualificationId"
|
LEFT JOIN "qualification" "qualification" ON "qualification"."id"="memberQualifications"."qualificationId"
|
||||||
LEFT JOIN "member" "member" ON "member"."id"="memberQualifications"."memberId"
|
LEFT JOIN "member" "member" ON "member"."id"="memberQualifications"."memberId"
|
||||||
|
@ -493,7 +494,8 @@ export const membership_view_postgres = new View({
|
||||||
"member"."birthdate" AS "memberBirthdate",
|
"member"."birthdate" AS "memberBirthdate",
|
||||||
"salutation"."salutation" AS "memberSalutation",
|
"salutation"."salutation" AS "memberSalutation",
|
||||||
SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") AS "durationInDays",
|
SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") AS "durationInDays",
|
||||||
SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")) AS "durationInYears"
|
SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))) AS "durationInYears",
|
||||||
|
SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")) AS "exactDuration"
|
||||||
FROM "membership" "membership"
|
FROM "membership" "membership"
|
||||||
LEFT JOIN "membership_status" "status" ON "status"."id"="membership"."statusId"
|
LEFT JOIN "membership_status" "status" ON "status"."id"="membership"."statusId"
|
||||||
LEFT JOIN "member" "member" ON "member"."id"="membership"."memberId"
|
LEFT JOIN "member" "member" ON "member"."id"="membership"."memberId"
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import type { Express } from "express";
|
import type { Express, NextFunction, Request, RequestHandler, Response } from "express";
|
||||||
import cors from "cors";
|
import cors from "cors";
|
||||||
|
import helmet from "helmet";
|
||||||
|
import morgan from "morgan";
|
||||||
|
import rateLimit from "express-rate-limit";
|
||||||
|
|
||||||
import allowSetup from "../middleware/allowSetup";
|
import allowSetup from "../middleware/allowSetup";
|
||||||
import authenticate from "../middleware/authenticate";
|
import authenticate from "../middleware/authenticate";
|
||||||
|
@ -19,26 +22,67 @@ import authenticateAPI from "../middleware/authenticateAPI";
|
||||||
import server from "./server";
|
import server from "./server";
|
||||||
import PermissionHelper from "../helpers/permissionHelper";
|
import PermissionHelper from "../helpers/permissionHelper";
|
||||||
import preventWebapiAccess from "../middleware/preventWebApiAccess";
|
import preventWebapiAccess from "../middleware/preventWebApiAccess";
|
||||||
|
import ms from "ms";
|
||||||
|
import {
|
||||||
|
SECURITY_LIMIT_REQUEST_COUNT,
|
||||||
|
SECURITY_LIMIT_WINDOW,
|
||||||
|
SECURITY_STRICT_LIMIT_REQUEST_COUNT,
|
||||||
|
SECURITY_STRICT_LIMIT_WINDOW,
|
||||||
|
USE_SECURITY_LIMIT,
|
||||||
|
USE_SECURITY_STRICT_LIMIT,
|
||||||
|
} from "../env.defaults";
|
||||||
|
|
||||||
|
const strictLimiter = rateLimit({
|
||||||
|
windowMs: ms(SECURITY_STRICT_LIMIT_WINDOW),
|
||||||
|
max: SECURITY_STRICT_LIMIT_REQUEST_COUNT,
|
||||||
|
message: `Zu viele Anmeldeversuche innerhalb von ${SECURITY_STRICT_LIMIT_WINDOW}. Bitte warten.`,
|
||||||
|
skipSuccessfulRequests: true,
|
||||||
|
skip: () => {
|
||||||
|
return USE_SECURITY_STRICT_LIMIT == "false";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const generalLimiter = rateLimit({
|
||||||
|
windowMs: ms(SECURITY_LIMIT_WINDOW),
|
||||||
|
max: SECURITY_LIMIT_REQUEST_COUNT,
|
||||||
|
message: `Zu viele Anfragen innerhalb von ${SECURITY_LIMIT_WINDOW}. Bitte warten.`,
|
||||||
|
skipSuccessfulRequests: true,
|
||||||
|
skip: () => {
|
||||||
|
return USE_SECURITY_LIMIT == "false";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function excludePaths(middleware: RequestHandler, excludedPaths: Array<string>) {
|
||||||
|
return (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
if (excludedPaths.includes(req.path)) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
return middleware(req, res, next);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default (app: Express) => {
|
export default (app: Express) => {
|
||||||
app.set("query parser", "extended");
|
app.set("query parser", "extended");
|
||||||
|
app.use(cors());
|
||||||
|
app.options("*", cors());
|
||||||
|
app.use(helmet());
|
||||||
|
app.use(morgan("short"));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(
|
app.use(
|
||||||
express.urlencoded({
|
express.urlencoded({
|
||||||
extended: true,
|
extended: true,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
app.use(cors());
|
|
||||||
app.options("*", cors());
|
|
||||||
|
|
||||||
app.use(detectPWA);
|
app.use(detectPWA);
|
||||||
app.use("/api/public", publicAvailable);
|
app.use("/api/public", publicAvailable);
|
||||||
app.use("/api/setup", preventWebapiAccess, allowSetup, setup);
|
app.use("/api/setup", strictLimiter, preventWebapiAccess, allowSetup, setup);
|
||||||
app.use("/api/reset", preventWebapiAccess, reset);
|
app.use("/api/reset", strictLimiter, preventWebapiAccess, reset);
|
||||||
app.use("/api/invite", preventWebapiAccess, invite);
|
app.use("/api/invite", strictLimiter, preventWebapiAccess, invite);
|
||||||
app.use("/api/auth", preventWebapiAccess, auth);
|
app.use("/api/auth", strictLimiter, preventWebapiAccess, auth);
|
||||||
app.use("/api/webapi", authenticateAPI, webapi);
|
app.use("/api/webapi", authenticateAPI, webapi);
|
||||||
app.use(authenticate);
|
app.use(authenticate);
|
||||||
|
app.use(excludePaths(generalLimiter, ["/synchronize"]));
|
||||||
app.use("/api/admin", admin);
|
app.use("/api/admin", admin);
|
||||||
app.use("/api/user", preventWebapiAccess, user);
|
app.use("/api/user", preventWebapiAccess, user);
|
||||||
app.use("/api/server", preventWebapiAccess, PermissionHelper.isAdminMiddleware(), server);
|
app.use("/api/server", preventWebapiAccess, PermissionHelper.isAdminMiddleware(), server);
|
||||||
|
|
|
@ -7,7 +7,7 @@ let durationInYears: string;
|
||||||
let exactDuration: string;
|
let exactDuration: string;
|
||||||
if (DB_TYPE == "postgres") {
|
if (DB_TYPE == "postgres") {
|
||||||
durationInDays = `SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start")`;
|
durationInDays = `SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start")`;
|
||||||
durationInYears = `SUM(EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate))`;
|
durationInYears = `SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")))`;
|
||||||
exactDuration = `SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start"))`;
|
exactDuration = `SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start"))`;
|
||||||
} else if (DB_TYPE == "mysql") {
|
} else if (DB_TYPE == "mysql") {
|
||||||
durationInDays = `SUM(DATEDIFF(COALESCE(memberExecutivePositions.end, CURDATE()), memberExecutivePositions.start))`;
|
durationInDays = `SUM(DATEDIFF(COALESCE(memberExecutivePositions.end, CURDATE()), memberExecutivePositions.start))`;
|
||||||
|
|
|
@ -7,7 +7,7 @@ let durationInYears: string;
|
||||||
let exactDuration: string;
|
let exactDuration: string;
|
||||||
if (DB_TYPE == "postgres") {
|
if (DB_TYPE == "postgres") {
|
||||||
durationInDays = `SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start") `;
|
durationInDays = `SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start") `;
|
||||||
durationInYears = `SUM(EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate))`;
|
durationInYears = `SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start")))`;
|
||||||
exactDuration = `SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start"))`;
|
exactDuration = `SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start"))`;
|
||||||
} else if (DB_TYPE == "mysql") {
|
} else if (DB_TYPE == "mysql") {
|
||||||
durationInDays = `SUM(DATEDIFF(COALESCE(memberQualifications.end, CURDATE()), memberQualifications.start))`;
|
durationInDays = `SUM(DATEDIFF(COALESCE(memberQualifications.end, CURDATE()), memberQualifications.start))`;
|
||||||
|
|
|
@ -7,7 +7,7 @@ let durationInYears: string;
|
||||||
let exactDuration: string;
|
let exactDuration: string;
|
||||||
if (DB_TYPE == "postgres") {
|
if (DB_TYPE == "postgres") {
|
||||||
durationInDays = `SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") `;
|
durationInDays = `SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") `;
|
||||||
durationInYears = `SUM(EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate))`;
|
durationInYears = `SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")))`;
|
||||||
exactDuration = `SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))`;
|
exactDuration = `SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))`;
|
||||||
} else if (DB_TYPE == "mysql") {
|
} else if (DB_TYPE == "mysql") {
|
||||||
durationInDays = `SUM(DATEDIFF(COALESCE(membership.end, CURDATE()), membership.start))`;
|
durationInDays = `SUM(DATEDIFF(COALESCE(membership.end, CURDATE()), membership.start))`;
|
||||||
|
|
Loading…
Add table
Reference in a new issue