This commit is contained in:
Ajay 2022-04-14 01:54:28 -04:00
commit fb414ed6db
16 changed files with 312 additions and 158 deletions

View file

@ -17,8 +17,10 @@
"port": 5432 "port": 5432
}, },
"redis": { "redis": {
"socket": {
"host": "localhost", "host": "localhost",
"port": 6379 "port": 6379
}
}, },
"createDatabaseIfNotExist": true, "createDatabaseIfNotExist": true,
"schemaFolder": "./databases", "schemaFolder": "./databases",

View file

@ -72,6 +72,9 @@ http {
server 10.0.0.13:4441 max_fails=25 fail_timeout=20s; server 10.0.0.13:4441 max_fails=25 fail_timeout=20s;
server 10.0.0.13:4442 max_fails=25 fail_timeout=20s; server 10.0.0.13:4442 max_fails=25 fail_timeout=20s;
server 10.0.0.14:4441 max_fails=25 fail_timeout=20s;
server 10.0.0.14:4442 max_fails=25 fail_timeout=20s;
server 10.0.0.11:4441 max_fails=25 fail_timeout=20s; server 10.0.0.11:4441 max_fails=25 fail_timeout=20s;
server 10.0.0.11:4442 max_fails=25 fail_timeout=20s; server 10.0.0.11:4442 max_fails=25 fail_timeout=20s;

234
package-lock.json generated
View file

@ -18,7 +18,8 @@
"express-rate-limit": "^6.3.0", "express-rate-limit": "^6.3.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"pg": "^8.7.1", "pg": "^8.7.1",
"redis": "^3.1.2", "rate-limit-redis": "^3.0.1",
"redis": "^4.0.6",
"sync-mysql": "^3.0.1" "sync-mysql": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {
@ -29,7 +30,6 @@
"@types/mocha": "^9.0.0", "@types/mocha": "^9.0.0",
"@types/node": "^16.11.11", "@types/node": "^16.11.11",
"@types/pg": "^8.6.1", "@types/pg": "^8.6.1",
"@types/redis": "^2.8.32",
"@typescript-eslint/eslint-plugin": "^5.5.0", "@typescript-eslint/eslint-plugin": "^5.5.0",
"@typescript-eslint/parser": "^5.5.0", "@typescript-eslint/parser": "^5.5.0",
"eslint": "^8.3.0", "eslint": "^8.3.0",
@ -127,6 +127,65 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true "dev": true
}, },
"node_modules/@node-redis/bloom": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@node-redis/bloom/-/bloom-1.0.1.tgz",
"integrity": "sha512-mXEBvEIgF4tUzdIN89LiYsbi6//EdpFA7L8M+DHCvePXg+bfHWi+ct5VI6nHUFQE5+ohm/9wmgihCH3HSkeKsw==",
"peerDependencies": {
"@node-redis/client": "^1.0.0"
}
},
"node_modules/@node-redis/client": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@node-redis/client/-/client-1.0.5.tgz",
"integrity": "sha512-ESZ3bd1f+od62h4MaBLKum+klVJfA4wAeLHcVQBkoXa1l0viFesOWnakLQqKg+UyrlJhZmXJWtu0Y9v7iTMrig==",
"dependencies": {
"cluster-key-slot": "1.1.0",
"generic-pool": "3.8.2",
"redis-parser": "3.0.0",
"yallist": "4.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@node-redis/client/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/@node-redis/graph": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@node-redis/graph/-/graph-1.0.0.tgz",
"integrity": "sha512-mRSo8jEGC0cf+Rm7q8mWMKKKqkn6EAnA9IA2S3JvUv/gaWW/73vil7GLNwion2ihTptAm05I9LkepzfIXUKX5g==",
"peerDependencies": {
"@node-redis/client": "^1.0.0"
}
},
"node_modules/@node-redis/json": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@node-redis/json/-/json-1.0.2.tgz",
"integrity": "sha512-qVRgn8WfG46QQ08CghSbY4VhHFgaTY71WjpwRBGEuqGPfWwfRcIf3OqSpR7Q/45X+v3xd8mvYjywqh0wqJ8T+g==",
"peerDependencies": {
"@node-redis/client": "^1.0.0"
}
},
"node_modules/@node-redis/search": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@node-redis/search/-/search-1.0.5.tgz",
"integrity": "sha512-MCOL8iCKq4v+3HgEQv8zGlSkZyXSXtERgrAJ4TSryIG/eLFy84b57KmNNa/V7M1Q2Wd2hgn2nPCGNcQtk1R1OQ==",
"peerDependencies": {
"@node-redis/client": "^1.0.0"
}
},
"node_modules/@node-redis/time-series": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@node-redis/time-series/-/time-series-1.0.2.tgz",
"integrity": "sha512-HGQ8YooJ8Mx7l28tD7XjtB3ImLEjlUxG1wC1PAjxu6hPJqjPshUZxAICzDqDjtIbhDTf48WXXUcx8TQJB1XTKA==",
"peerDependencies": {
"@node-redis/client": "^1.0.0"
}
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -356,15 +415,6 @@
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
"devOptional": true "devOptional": true
}, },
"node_modules/@types/redis": {
"version": "2.8.32",
"resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.32.tgz",
"integrity": "sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/serve-static": { "node_modules/@types/serve-static": {
"version": "1.13.10", "version": "1.13.10",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
@ -1204,6 +1254,14 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/cluster-key-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz",
"integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/code-point-at": { "node_modules/code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@ -1434,14 +1492,6 @@
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
}, },
"node_modules/denque": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
"integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": { "node_modules/depd": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
@ -2201,6 +2251,14 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/generic-pool": {
"version": "3.8.2",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz",
"integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==",
"engines": {
"node": ">= 4"
}
},
"node_modules/get-caller-file": { "node_modules/get-caller-file": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@ -3872,6 +3930,17 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/rate-limit-redis": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-3.0.1.tgz",
"integrity": "sha512-L6yhOUBrAZ8VEMX9DwlM3X6hfm8yq+gBO4LoOW7+JgmNq59zE7QmLz4v5VnwYPvLeSh/e7PDcrzUI3UumJw1iw==",
"engines": {
"node": ">= 14.5.0"
},
"peerDependencies": {
"express-rate-limit": "^6"
}
},
"node_modules/raw-body": { "node_modules/raw-body": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
@ -3927,28 +3996,18 @@
} }
}, },
"node_modules/redis": { "node_modules/redis": {
"version": "3.1.2", "version": "4.0.6",
"resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.6.tgz",
"integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", "integrity": "sha512-IaPAxgF5dV0jx+A9l6yd6R9/PAChZIoAskDVRzUODeLDNhsMlq7OLLTmu0AwAr0xjrJ1bibW5xdpRwqIQ8Q0Xg==",
"dependencies": { "dependencies": {
"denque": "^1.5.0", "@node-redis/bloom": "1.0.1",
"redis-commands": "^1.7.0", "@node-redis/client": "1.0.5",
"redis-errors": "^1.2.0", "@node-redis/graph": "1.0.0",
"redis-parser": "^3.0.0" "@node-redis/json": "1.0.2",
}, "@node-redis/search": "1.0.5",
"engines": { "@node-redis/time-series": "1.0.2"
"node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-redis"
} }
}, },
"node_modules/redis-commands": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
},
"node_modules/redis-errors": { "node_modules/redis-errors": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
@ -5165,6 +5224,54 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true "dev": true
}, },
"@node-redis/bloom": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@node-redis/bloom/-/bloom-1.0.1.tgz",
"integrity": "sha512-mXEBvEIgF4tUzdIN89LiYsbi6//EdpFA7L8M+DHCvePXg+bfHWi+ct5VI6nHUFQE5+ohm/9wmgihCH3HSkeKsw==",
"requires": {}
},
"@node-redis/client": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@node-redis/client/-/client-1.0.5.tgz",
"integrity": "sha512-ESZ3bd1f+od62h4MaBLKum+klVJfA4wAeLHcVQBkoXa1l0viFesOWnakLQqKg+UyrlJhZmXJWtu0Y9v7iTMrig==",
"requires": {
"cluster-key-slot": "1.1.0",
"generic-pool": "3.8.2",
"redis-parser": "3.0.0",
"yallist": "4.0.0"
},
"dependencies": {
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
},
"@node-redis/graph": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@node-redis/graph/-/graph-1.0.0.tgz",
"integrity": "sha512-mRSo8jEGC0cf+Rm7q8mWMKKKqkn6EAnA9IA2S3JvUv/gaWW/73vil7GLNwion2ihTptAm05I9LkepzfIXUKX5g==",
"requires": {}
},
"@node-redis/json": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@node-redis/json/-/json-1.0.2.tgz",
"integrity": "sha512-qVRgn8WfG46QQ08CghSbY4VhHFgaTY71WjpwRBGEuqGPfWwfRcIf3OqSpR7Q/45X+v3xd8mvYjywqh0wqJ8T+g==",
"requires": {}
},
"@node-redis/search": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@node-redis/search/-/search-1.0.5.tgz",
"integrity": "sha512-MCOL8iCKq4v+3HgEQv8zGlSkZyXSXtERgrAJ4TSryIG/eLFy84b57KmNNa/V7M1Q2Wd2hgn2nPCGNcQtk1R1OQ==",
"requires": {}
},
"@node-redis/time-series": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@node-redis/time-series/-/time-series-1.0.2.tgz",
"integrity": "sha512-HGQ8YooJ8Mx7l28tD7XjtB3ImLEjlUxG1wC1PAjxu6hPJqjPshUZxAICzDqDjtIbhDTf48WXXUcx8TQJB1XTKA==",
"requires": {}
},
"@nodelib/fs.scandir": { "@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -5379,15 +5486,6 @@
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
"devOptional": true "devOptional": true
}, },
"@types/redis": {
"version": "2.8.32",
"resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.32.tgz",
"integrity": "sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/serve-static": { "@types/serve-static": {
"version": "1.13.10", "version": "1.13.10",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
@ -6016,6 +6114,11 @@
} }
} }
}, },
"cluster-key-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz",
"integrity": "sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw=="
},
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
@ -6196,11 +6299,6 @@
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
}, },
"denque": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
"integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw=="
},
"depd": { "depd": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
@ -6790,6 +6888,11 @@
} }
} }
}, },
"generic-pool": {
"version": "3.8.2",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz",
"integrity": "sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg=="
},
"get-caller-file": { "get-caller-file": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@ -8039,6 +8142,12 @@
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
}, },
"rate-limit-redis": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/rate-limit-redis/-/rate-limit-redis-3.0.1.tgz",
"integrity": "sha512-L6yhOUBrAZ8VEMX9DwlM3X6hfm8yq+gBO4LoOW7+JgmNq59zE7QmLz4v5VnwYPvLeSh/e7PDcrzUI3UumJw1iw==",
"requires": {}
},
"raw-body": { "raw-body": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
@ -8082,21 +8191,18 @@
} }
}, },
"redis": { "redis": {
"version": "3.1.2", "version": "4.0.6",
"resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.6.tgz",
"integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==", "integrity": "sha512-IaPAxgF5dV0jx+A9l6yd6R9/PAChZIoAskDVRzUODeLDNhsMlq7OLLTmu0AwAr0xjrJ1bibW5xdpRwqIQ8Q0Xg==",
"requires": { "requires": {
"denque": "^1.5.0", "@node-redis/bloom": "1.0.1",
"redis-commands": "^1.7.0", "@node-redis/client": "1.0.5",
"redis-errors": "^1.2.0", "@node-redis/graph": "1.0.0",
"redis-parser": "^3.0.0" "@node-redis/json": "1.0.2",
"@node-redis/search": "1.0.5",
"@node-redis/time-series": "1.0.2"
} }
}, },
"redis-commands": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
},
"redis-errors": { "redis-errors": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",

View file

@ -26,7 +26,8 @@
"express-rate-limit": "^6.3.0", "express-rate-limit": "^6.3.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"pg": "^8.7.1", "pg": "^8.7.1",
"redis": "^3.1.2", "rate-limit-redis": "^3.0.1",
"redis": "^4.0.6",
"sync-mysql": "^3.0.1" "sync-mysql": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {
@ -37,7 +38,6 @@
"@types/mocha": "^9.0.0", "@types/mocha": "^9.0.0",
"@types/node": "^16.11.11", "@types/node": "^16.11.11",
"@types/pg": "^8.6.1", "@types/pg": "^8.6.1",
"@types/redis": "^2.8.32",
"@typescript-eslint/eslint-plugin": "^5.5.0", "@typescript-eslint/eslint-plugin": "^5.5.0",
"@typescript-eslint/parser": "^5.5.0", "@typescript-eslint/parser": "^5.5.0",
"eslint": "^8.3.0", "eslint": "^8.3.0",

View file

@ -8,6 +8,7 @@ const configFile = process.env.TEST_POSTGRES ? "ci.json"
: "config.json"; : "config.json";
export const config: SBSConfig = JSON.parse(fs.readFileSync(configFile).toString("utf8")); export const config: SBSConfig = JSON.parse(fs.readFileSync(configFile).toString("utf8"));
migrate(config);
addDefaults(config, { addDefaults(config, {
port: 80, port: 80,
behindProxy: "X-Forwarded-For", behindProxy: "X-Forwarded-For",
@ -109,3 +110,20 @@ function addDefaults(config: SBSConfig, defaults: SBSConfig) {
} }
} }
} }
function migrate(config: SBSConfig) {
// Redis change
if (config.redis) {
const redisConfig = config.redis as any;
if (redisConfig.host || redisConfig.port) {
config.redis.socket = {
host: redisConfig.host,
port: redisConfig.port
};
}
if (redisConfig.enable_offline_queue !== undefined) {
config.disableOfflineQueue = !redisConfig.enable_offline_queue;
}
}
}

View file

@ -6,6 +6,9 @@ import { RateLimitConfig } from "../types/config.model";
import { Request } from "express"; import { Request } from "express";
import { isUserVIP } from "../utils/isUserVIP"; import { isUserVIP } from "../utils/isUserVIP";
import { UserID } from "../types/user.model"; import { UserID } from "../types/user.model";
import RedisStore from "rate-limit-redis";
import redis from "../utils/redis";
import { config } from "../config";
export function rateLimitMiddleware(limitConfig: RateLimitConfig, getUserID?: (req: Request) => UserID): RateLimitRequestHandler { export function rateLimitMiddleware(limitConfig: RateLimitConfig, getUserID?: (req: Request) => UserID): RateLimitRequestHandler {
return rateLimit({ return rateLimit({
@ -24,6 +27,9 @@ export function rateLimitMiddleware(limitConfig: RateLimitConfig, getUserID?: (r
} else { } else {
return next(); return next();
} }
} },
store: config.redis ? new RedisStore({
sendCommand: (...args: string[]) => redis.sendCommand(args),
}) : null,
}); });
} }

View file

@ -9,6 +9,7 @@ import { isUserVIP } from "../utils/isUserVIP";
import { HashedUserID } from "../types/user.model"; import { HashedUserID } from "../types/user.model";
import redis from "../utils/redis"; import redis from "../utils/redis";
import { tempVIPKey } from "../utils/redisKeys"; import { tempVIPKey } from "../utils/redisKeys";
import { Logger } from "../utils/logger";
interface AddUserAsTempVIPRequest extends Request { interface AddUserAsTempVIPRequest extends Request {
query: { query: {
@ -65,12 +66,22 @@ export async function addUserAsTempVIP(req: AddUserAsTempVIPRequest, res: Respon
if (!channelInfo?.id) { if (!channelInfo?.id) {
return res.status(404).send(`No channel found for videoID ${channelVideoID}`); return res.status(404).send(`No channel found for videoID ${channelVideoID}`);
} }
await redis.setAsyncEx(tempVIPKey(userID), channelInfo?.id, dayInSeconds);
try {
await redis.setEx(tempVIPKey(userID), dayInSeconds, channelInfo?.id);
await privateDB.prepare("run", `INSERT INTO "tempVipLog" VALUES (?, ?, ?, ?)`, [adminUserID, userID, + enabled, startTime]); await privateDB.prepare("run", `INSERT INTO "tempVipLog" VALUES (?, ?, ?, ?)`, [adminUserID, userID, + enabled, startTime]);
return res.status(200).send(`Temp VIP added on channel ${channelInfo?.name}`); return res.status(200).send(`Temp VIP added on channel ${channelInfo?.name}`);
} catch (e) {
Logger.error(e as string);
return res.status(500).send();
} }
}
await redis.delAsync(tempVIPKey(userID)); try {
await redis.del(tempVIPKey(userID));
await privateDB.prepare("run", `INSERT INTO "tempVipLog" VALUES (?, ?, ?, ?)`, [adminUserID, userID, + enabled, startTime]); await privateDB.prepare("run", `INSERT INTO "tempVipLog" VALUES (?, ?, ?, ?)`, [adminUserID, userID, + enabled, startTime]);
return res.status(200).send(`Temp VIP removed`); return res.status(200).send(`Temp VIP removed`);
} catch (e) {
Logger.error(e as string);
return res.status(500).send();
}
} }

View file

@ -10,8 +10,12 @@ export async function getStatus(req: Request, res: Response): Promise<Response>
value = Array.isArray(value) ? value[0] : value; value = Array.isArray(value) ? value[0] : value;
try { try {
const dbVersion = (await db.prepare("get", "SELECT key, value FROM config where key = ?", ["version"])).value; const dbVersion = (await db.prepare("get", "SELECT key, value FROM config where key = ?", ["version"])).value;
let statusRequests: unknown = 0;
try {
const numberRequests = await redis.increment("statusRequest"); const numberRequests = await redis.increment("statusRequest");
const statusRequests = numberRequests?.replies?.[0]; statusRequests = numberRequests?.[0];
} catch (error) { } // eslint-disable-line no-empty
const statusValues: Record<string, any> = { const statusValues: Record<string, any> = {
uptime: process.uptime(), uptime: process.uptime(),
commit: (global as any).HEADCOMMIT || "unknown", commit: (global as any).HEADCOMMIT || "unknown",

View file

@ -41,7 +41,7 @@ export interface SBSConfig {
privateMysql?: any; privateMysql?: any;
minimumPrefix?: string; minimumPrefix?: string;
maximumPrefix?: string; maximumPrefix?: string;
redis?: redis.ClientOpts; redis?: redis.RedisClientOptions;
maxRewardTimePerSegmentInSeconds?: number; maxRewardTimePerSegmentInSeconds?: number;
postgres?: PoolConfig; postgres?: PoolConfig;
dumpDatabase?: DumpDatabase; dumpDatabase?: DumpDatabase;

View file

@ -18,18 +18,19 @@ export async function getHashCache<T extends string>(value: T, times = defaulted
async function getFromRedis<T extends string>(key: HashedValue): Promise<T & HashedValue> { async function getFromRedis<T extends string>(key: HashedValue): Promise<T & HashedValue> {
const redisKey = shaHashKey(key); const redisKey = shaHashKey(key);
const { err, reply } = await redis.getAsync(redisKey);
if (!err && reply) {
try { try {
const reply = await redis.get(redisKey);
if (reply) {
Logger.debug(`Got data from redis: ${reply}`); Logger.debug(`Got data from redis: ${reply}`);
return reply as T & HashedValue; return reply as T & HashedValue;
} catch (e) {
// If all else, continue on hashing
} }
} } catch (e) {} // eslint-disable-line no-empty
const data = getHash(key, cachedHashTimes);
// Otherwise, calculate it
const data = getHash(key, cachedHashTimes);
redis.set(key, data);
redis.setAsync(key, data);
return data as T & HashedValue; return data as T & HashedValue;
} }

View file

@ -5,6 +5,7 @@ import { YouTubeAPI } from "../utils/youtubeApi";
import { APIVideoInfo } from "../types/youtubeApi.model"; import { APIVideoInfo } from "../types/youtubeApi.model";
import { VideoID } from "../types/segments.model"; import { VideoID } from "../types/segments.model";
import { config } from "../config"; import { config } from "../config";
import { Logger } from "./logger";
function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promise<APIVideoInfo> { function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promise<APIVideoInfo> {
return config.newLeafURLs ? YouTubeAPI.listVideos(videoID, ignoreCache) : null; return config.newLeafURLs ? YouTubeAPI.listVideos(videoID, ignoreCache) : null;
@ -13,7 +14,11 @@ function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promise<API
export const isUserTempVIP = async (hashedUserID: HashedUserID, videoID: VideoID): Promise<boolean> => { export const isUserTempVIP = async (hashedUserID: HashedUserID, videoID: VideoID): Promise<boolean> => {
const apiVideoInfo = await getYouTubeVideoInfo(videoID); const apiVideoInfo = await getYouTubeVideoInfo(videoID);
const channelID = apiVideoInfo?.data?.authorId; const channelID = apiVideoInfo?.data?.authorId;
const { err, reply } = await redis.getAsync(tempVIPKey(hashedUserID)); try {
const reply = await redis.get(tempVIPKey(hashedUserID));
return err || !reply ? false : (reply == channelID); return reply && reply == channelID;
} catch (e) {
Logger.error(e as string);
return false;
}
}; };

View file

@ -5,21 +5,18 @@ import { Service, VideoID, VideoIDHash } from "../types/segments.model";
import { UserID } from "../types/user.model"; import { UserID } from "../types/user.model";
async function get<T>(fetchFromDB: () => Promise<T>, key: string): Promise<T> { async function get<T>(fetchFromDB: () => Promise<T>, key: string): Promise<T> {
const { err, reply } = await redis.getAsync(key);
if (!err && reply) {
try { try {
const reply = await redis.get(key);
if (reply) {
Logger.debug(`Got data from redis: ${reply}`); Logger.debug(`Got data from redis: ${reply}`);
return JSON.parse(reply); return JSON.parse(reply);
} catch (e) {
// If all else, continue on to fetching from the database
}
} }
} catch (e) { } //eslint-disable-line no-empty
const data = await fetchFromDB(); const data = await fetchFromDB();
redis.setAsync(key, JSON.stringify(data)); redis.set(key, JSON.stringify(data));
return data; return data;
} }
@ -30,18 +27,17 @@ async function get<T>(fetchFromDB: () => Promise<T>, key: string): Promise<T> {
async function getAndSplit<T, U extends string>(fetchFromDB: (values: U[]) => Promise<Array<T>>, keyGenerator: (value: U) => string, splitKey: string, values: U[]): Promise<Array<T>> { async function getAndSplit<T, U extends string>(fetchFromDB: (values: U[]) => Promise<Array<T>>, keyGenerator: (value: U) => string, splitKey: string, values: U[]): Promise<Array<T>> {
const cachedValues = await Promise.all(values.map(async (value) => { const cachedValues = await Promise.all(values.map(async (value) => {
const key = keyGenerator(value); const key = keyGenerator(value);
const { err, reply } = await redis.getAsync(key);
if (!err && reply) {
try { try {
const reply = await redis.get(key);
if (reply) {
Logger.debug(`Got data from redis: ${reply}`); Logger.debug(`Got data from redis: ${reply}`);
return { return {
value, value,
result: JSON.parse(reply) result: JSON.parse(reply)
}; };
} catch (e) { } //eslint-disable-line no-empty
} }
} catch (e) { } //eslint-disable-line no-empty
return { return {
value, value,
@ -71,7 +67,7 @@ async function getAndSplit<T, U extends string>(fetchFromDB: (values: U[]) => Pr
} }
for (const key in newResults) { for (const key in newResults) {
redis.setAsync(key, JSON.stringify(newResults[key])); redis.set(key, JSON.stringify(newResults[key]));
} }
}); });
} }
@ -81,16 +77,16 @@ async function getAndSplit<T, U extends string>(fetchFromDB: (values: U[]) => Pr
function clearSegmentCache(videoInfo: { videoID: VideoID; hashedVideoID: VideoIDHash; service: Service; userID?: UserID; }): void { function clearSegmentCache(videoInfo: { videoID: VideoID; hashedVideoID: VideoIDHash; service: Service; userID?: UserID; }): void {
if (videoInfo) { if (videoInfo) {
redis.delAsync(skipSegmentsKey(videoInfo.videoID, videoInfo.service)); redis.del(skipSegmentsKey(videoInfo.videoID, videoInfo.service));
redis.delAsync(skipSegmentGroupsKey(videoInfo.videoID, videoInfo.service)); redis.del(skipSegmentGroupsKey(videoInfo.videoID, videoInfo.service));
redis.delAsync(skipSegmentsHashKey(videoInfo.hashedVideoID, videoInfo.service)); redis.del(skipSegmentsHashKey(videoInfo.hashedVideoID, videoInfo.service));
if (videoInfo.userID) redis.delAsync(reputationKey(videoInfo.userID)); if (videoInfo.userID) redis.del(reputationKey(videoInfo.userID));
} }
} }
function clearRatingCache(videoInfo: { hashedVideoID: VideoIDHash; service: Service;}): void { function clearRatingCache(videoInfo: { hashedVideoID: VideoIDHash; service: Service;}): void {
if (videoInfo) { if (videoInfo) {
redis.delAsync(ratingHashKey(videoInfo.hashedVideoID, videoInfo.service)); redis.del(ratingHashKey(videoInfo.hashedVideoID, videoInfo.service));
} }
} }

View file

@ -1,59 +1,56 @@
import { config } from "../config"; import { config } from "../config";
import { Logger } from "./logger"; import { Logger } from "./logger";
import redis, { Callback } from "redis"; import { createClient } from "redis";
import { RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply } from "@node-redis/client/dist/lib/commands";
import { ClientCommandOptions } from "@node-redis/client/dist/lib/client";
import { RedisReply } from "rate-limit-redis";
interface RedisSB { interface RedisSB {
get(key: string, callback?: Callback<string | null>): void; get(key: RedisCommandArgument): Promise<string>;
getAsync?(key: string): Promise<{err: Error | null, reply: string | null}>; set(key: RedisCommandArgument, value: RedisCommandArgument): Promise<string>;
set(key: string, value: string, callback?: Callback<string | null>): void; setEx(key: RedisCommandArgument, seconds: number, value: RedisCommandArgument): Promise<string>;
setAsync?(key: string, value: string): Promise<{err: Error | null, reply: string | null}>; del(...keys: [RedisCommandArgument]): Promise<number>;
setAsyncEx?(key: string, value: string, seconds: number): Promise<{err: Error | null, reply: string | null}>; increment?(key: RedisCommandArgument): Promise<RedisCommandRawReply[]>;
delAsync?(...keys: [string]): Promise<Error | null>; sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions): Promise<RedisReply>;
close?(flush?: boolean): void; quit(): Promise<void>;
increment?(key: string): Promise<{err: Error| null, replies: any[] | null}>;
} }
let exportObject: RedisSB = { let exportClient: RedisSB = {
get: (key, callback?) => callback(null, undefined), get: () => new Promise((resolve, reject) => reject()),
getAsync: () => set: () => new Promise((resolve, reject) => reject()),
new Promise((resolve) => resolve({ err: null, reply: undefined })), setEx: () => new Promise((resolve, reject) => reject()),
set: (key, value, callback) => callback(null, undefined), del: () => new Promise((resolve, reject) => reject()),
setAsync: () => increment: () => new Promise((resolve, reject) => reject()),
new Promise((resolve) => resolve({ err: null, reply: undefined })), sendCommand: () => new Promise((resolve, reject) => reject()),
setAsyncEx: () => quit: () => new Promise((resolve, reject) => reject()),
new Promise((resolve) => resolve({ err: null, reply: undefined })),
delAsync: () =>
new Promise((resolve) => resolve(null)),
increment: () =>
new Promise((resolve) => resolve({ err: null, replies: undefined })),
}; };
if (config.redis) { if (config.redis) {
Logger.info("Connected to redis"); Logger.info("Connected to redis");
const client = redis.createClient(config.redis); const client = createClient(config.redis);
exportObject = client; client.connect();
exportClient = client;
const timeoutDuration = 200; const timeoutDuration = 200;
exportObject.getAsync = (key) => new Promise((resolve) => { const get = client.get.bind(client);
const timeout = setTimeout(() => resolve({ err: null, reply: undefined }), timeoutDuration); exportClient.get = (key) => new Promise((resolve, reject) => {
client.get(key, (err, reply) => { const timeout = setTimeout(() => reject(), timeoutDuration);
get(key).then((reply) => {
clearTimeout(timeout); clearTimeout(timeout);
resolve({ err, reply }); resolve(reply);
}).catch((err) => reject(err));
}); });
}); exportClient.increment = (key) => new Promise((resolve, reject) =>
exportObject.setAsync = (key, value) => new Promise((resolve) => client.set(key, value, (err, reply) => resolve({ err, reply })));
exportObject.setAsyncEx = (key, value, seconds) => new Promise((resolve) => client.setex(key, seconds, value, (err, reply) => resolve({ err, reply })));
exportObject.delAsync = (...keys) => new Promise((resolve) => client.del(keys, (err) => resolve(err)));
exportObject.close = (flush) => client.end(flush);
exportObject.increment = (key) => new Promise((resolve) =>
client.multi() client.multi()
.incr(key) .incr(key)
.expire(key, 60) .expire(key, 60)
.exec((err, replies) => resolve({ err, replies })) .exec()
.then((reply) => resolve(reply))
.catch((err) => reject(err))
); );
client.on("error", function(error) { client.on("error", function(error) {
Logger.error(error); Logger.error(error);
}); });
} }
export default exportObject; export default exportClient;

View file

@ -12,21 +12,20 @@ const randKey2 = genRandom(16);
describe("redis test", function() { describe("redis test", function() {
before(async function() { before(async function() {
if (!config.redis) this.skip(); if (!config.redis) this.skip();
await redis.setAsync(randKey1, randValue1); await redis.set(randKey1, randValue1);
}); });
it("Should get stored value", (done) => { it("Should get stored value", (done) => {
redis.getAsync(randKey1) redis.get(randKey1)
.then(res => { .then(res => {
if (res.err) assert.fail(res.err); assert.strictEqual(res, randValue1);
assert.strictEqual(res.reply, randValue1);
done(); done();
}); }).catch(err => assert.fail(err));
}); });
it("Should not be able to get not stored value", (done) => { it("Should not be able to get not stored value", (done) => {
redis.getAsync(randKey2) redis.get(randKey2)
.then(res => { .then(res => {
if (res.reply || res.err ) assert.fail("Value should not be found"); if (res) assert.fail("Value should not be found");
done(); done();
}); }).catch(err => assert.fail(err));
}); });
}); });

View file

@ -6,6 +6,7 @@ import { client } from "../utils/httpClient";
import { db, privateDB } from "../../src/databases/databases"; import { db, privateDB } from "../../src/databases/databases";
import redis from "../../src/utils/redis"; import redis from "../../src/utils/redis";
import assert from "assert"; import assert from "assert";
import { Logger } from "../../src/utils/logger";
// helpers // helpers
const getSegment = (UUID: string) => db.prepare("get", `SELECT "votes", "locked", "category" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]); const getSegment = (UUID: string) => db.prepare("get", `SELECT "votes", "locked", "category" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID]);
@ -51,8 +52,13 @@ const postVoteCategory = (userID: string, UUID: string, category: string) => cli
} }
}); });
const checkUserVIP = async (publicID: HashedUserID) => { const checkUserVIP = async (publicID: HashedUserID) => {
const { reply } = await redis.getAsync(tempVIPKey(publicID)); try {
const reply = await redis.get(tempVIPKey(publicID));
return reply; return reply;
} catch (e) {
Logger.error(e as string);
return false;
}
}; };
describe("tempVIP test", function() { describe("tempVIP test", function() {
@ -67,7 +73,7 @@ describe("tempVIP test", function() {
await db.prepare("run", 'INSERT INTO "vipUsers" ("userID") VALUES (?)', [publicPermVIP1]); await db.prepare("run", 'INSERT INTO "vipUsers" ("userID") VALUES (?)', [publicPermVIP1]);
await db.prepare("run", 'INSERT INTO "vipUsers" ("userID") VALUES (?)', [publicPermVIP2]); await db.prepare("run", 'INSERT INTO "vipUsers" ("userID") VALUES (?)', [publicPermVIP2]);
// clear redis if running consecutive tests // clear redis if running consecutive tests
await redis.delAsync(tempVIPKey(publicTempVIPOne)); await redis.del(tempVIPKey(publicTempVIPOne));
}); });
it("Should update db version when starting the application", () => { it("Should update db version when starting the application", () => {

View file

@ -57,7 +57,7 @@ async function init() {
mocha.run((failures) => { mocha.run((failures) => {
mockServer.close(); mockServer.close();
server.close(); server.close();
redis.close(true); redis.quit();
process.exitCode = failures ? 1 : 0; // exit with non-zero status if there were failures process.exitCode = failures ? 1 : 0; // exit with non-zero status if there were failures
}); });
}); });