mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2024-11-10 09:07:47 +01:00
Merge branch 'master' of https://github.com/ajayyy/SponsorBlockServer
This commit is contained in:
commit
fb414ed6db
16 changed files with 312 additions and 158 deletions
6
ci.json
6
ci.json
|
@ -17,8 +17,10 @@
|
|||
"port": 5432
|
||||
},
|
||||
"redis": {
|
||||
"host": "localhost",
|
||||
"port": 6379
|
||||
"socket": {
|
||||
"host": "localhost",
|
||||
"port": 6379
|
||||
}
|
||||
},
|
||||
"createDatabaseIfNotExist": true,
|
||||
"schemaFolder": "./databases",
|
||||
|
|
|
@ -72,6 +72,9 @@ http {
|
|||
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.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:4442 max_fails=25 fail_timeout=20s;
|
||||
|
||||
|
|
234
package-lock.json
generated
234
package-lock.json
generated
|
@ -18,7 +18,8 @@
|
|||
"express-rate-limit": "^6.3.0",
|
||||
"lodash": "^4.17.21",
|
||||
"pg": "^8.7.1",
|
||||
"redis": "^3.1.2",
|
||||
"rate-limit-redis": "^3.0.1",
|
||||
"redis": "^4.0.6",
|
||||
"sync-mysql": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -29,7 +30,6 @@
|
|||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^16.11.11",
|
||||
"@types/pg": "^8.6.1",
|
||||
"@types/redis": "^2.8.32",
|
||||
"@typescript-eslint/eslint-plugin": "^5.5.0",
|
||||
"@typescript-eslint/parser": "^5.5.0",
|
||||
"eslint": "^8.3.0",
|
||||
|
@ -127,6 +127,65 @@
|
|||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"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": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
@ -356,15 +415,6 @@
|
|||
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
|
||||
"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": {
|
||||
"version": "1.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
|
||||
|
@ -1204,6 +1254,14 @@
|
|||
"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": {
|
||||
"version": "1.1.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
|
@ -2201,6 +2251,14 @@
|
|||
"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": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
|
@ -3872,6 +3930,17 @@
|
|||
"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": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
|
||||
|
@ -3927,28 +3996,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/redis": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz",
|
||||
"integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==",
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-4.0.6.tgz",
|
||||
"integrity": "sha512-IaPAxgF5dV0jx+A9l6yd6R9/PAChZIoAskDVRzUODeLDNhsMlq7OLLTmu0AwAr0xjrJ1bibW5xdpRwqIQ8Q0Xg==",
|
||||
"dependencies": {
|
||||
"denque": "^1.5.0",
|
||||
"redis-commands": "^1.7.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-redis"
|
||||
"@node-redis/bloom": "1.0.1",
|
||||
"@node-redis/client": "1.0.5",
|
||||
"@node-redis/graph": "1.0.0",
|
||||
"@node-redis/json": "1.0.2",
|
||||
"@node-redis/search": "1.0.5",
|
||||
"@node-redis/time-series": "1.0.2"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
|
@ -5165,6 +5224,54 @@
|
|||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"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": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
@ -5379,15 +5486,6 @@
|
|||
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
|
||||
"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": {
|
||||
"version": "1.13.10",
|
||||
"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": {
|
||||
"version": "1.1.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.1.2",
|
||||
"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": {
|
||||
"version": "2.0.5",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
|
||||
|
@ -8082,21 +8191,18 @@
|
|||
}
|
||||
},
|
||||
"redis": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz",
|
||||
"integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==",
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-4.0.6.tgz",
|
||||
"integrity": "sha512-IaPAxgF5dV0jx+A9l6yd6R9/PAChZIoAskDVRzUODeLDNhsMlq7OLLTmu0AwAr0xjrJ1bibW5xdpRwqIQ8Q0Xg==",
|
||||
"requires": {
|
||||
"denque": "^1.5.0",
|
||||
"redis-commands": "^1.7.0",
|
||||
"redis-errors": "^1.2.0",
|
||||
"redis-parser": "^3.0.0"
|
||||
"@node-redis/bloom": "1.0.1",
|
||||
"@node-redis/client": "1.0.5",
|
||||
"@node-redis/graph": "1.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": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
"express-rate-limit": "^6.3.0",
|
||||
"lodash": "^4.17.21",
|
||||
"pg": "^8.7.1",
|
||||
"redis": "^3.1.2",
|
||||
"rate-limit-redis": "^3.0.1",
|
||||
"redis": "^4.0.6",
|
||||
"sync-mysql": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -37,7 +38,6 @@
|
|||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^16.11.11",
|
||||
"@types/pg": "^8.6.1",
|
||||
"@types/redis": "^2.8.32",
|
||||
"@typescript-eslint/eslint-plugin": "^5.5.0",
|
||||
"@typescript-eslint/parser": "^5.5.0",
|
||||
"eslint": "^8.3.0",
|
||||
|
|
|
@ -8,6 +8,7 @@ const configFile = process.env.TEST_POSTGRES ? "ci.json"
|
|||
: "config.json";
|
||||
export const config: SBSConfig = JSON.parse(fs.readFileSync(configFile).toString("utf8"));
|
||||
|
||||
migrate(config);
|
||||
addDefaults(config, {
|
||||
port: 80,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,9 @@ import { RateLimitConfig } from "../types/config.model";
|
|||
import { Request } from "express";
|
||||
import { isUserVIP } from "../utils/isUserVIP";
|
||||
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 {
|
||||
return rateLimit({
|
||||
|
@ -24,6 +27,9 @@ export function rateLimitMiddleware(limitConfig: RateLimitConfig, getUserID?: (r
|
|||
} else {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
},
|
||||
store: config.redis ? new RedisStore({
|
||||
sendCommand: (...args: string[]) => redis.sendCommand(args),
|
||||
}) : null,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import { isUserVIP } from "../utils/isUserVIP";
|
|||
import { HashedUserID } from "../types/user.model";
|
||||
import redis from "../utils/redis";
|
||||
import { tempVIPKey } from "../utils/redisKeys";
|
||||
import { Logger } from "../utils/logger";
|
||||
|
||||
interface AddUserAsTempVIPRequest extends Request {
|
||||
query: {
|
||||
|
@ -65,12 +66,22 @@ export async function addUserAsTempVIP(req: AddUserAsTempVIPRequest, res: Respon
|
|||
if (!channelInfo?.id) {
|
||||
return res.status(404).send(`No channel found for videoID ${channelVideoID}`);
|
||||
}
|
||||
await redis.setAsyncEx(tempVIPKey(userID), channelInfo?.id, dayInSeconds);
|
||||
await privateDB.prepare("run", `INSERT INTO "tempVipLog" VALUES (?, ?, ?, ?)`, [adminUserID, userID, + enabled, startTime]);
|
||||
return res.status(200).send(`Temp VIP added on channel ${channelInfo?.name}`);
|
||||
}
|
||||
|
||||
await redis.delAsync(tempVIPKey(userID));
|
||||
await privateDB.prepare("run", `INSERT INTO "tempVipLog" VALUES (?, ?, ?, ?)`, [adminUserID, userID, + enabled, startTime]);
|
||||
return res.status(200).send(`Temp VIP removed`);
|
||||
try {
|
||||
await redis.setEx(tempVIPKey(userID), dayInSeconds, channelInfo?.id);
|
||||
await privateDB.prepare("run", `INSERT INTO "tempVipLog" VALUES (?, ?, ?, ?)`, [adminUserID, userID, + enabled, startTime]);
|
||||
return res.status(200).send(`Temp VIP added on channel ${channelInfo?.name}`);
|
||||
} catch (e) {
|
||||
Logger.error(e as string);
|
||||
return res.status(500).send();
|
||||
}
|
||||
}
|
||||
try {
|
||||
await redis.del(tempVIPKey(userID));
|
||||
await privateDB.prepare("run", `INSERT INTO "tempVipLog" VALUES (?, ?, ?, ?)`, [adminUserID, userID, + enabled, startTime]);
|
||||
return res.status(200).send(`Temp VIP removed`);
|
||||
} catch (e) {
|
||||
Logger.error(e as string);
|
||||
return res.status(500).send();
|
||||
}
|
||||
}
|
|
@ -10,8 +10,12 @@ export async function getStatus(req: Request, res: Response): Promise<Response>
|
|||
value = Array.isArray(value) ? value[0] : value;
|
||||
try {
|
||||
const dbVersion = (await db.prepare("get", "SELECT key, value FROM config where key = ?", ["version"])).value;
|
||||
const numberRequests = await redis.increment("statusRequest");
|
||||
const statusRequests = numberRequests?.replies?.[0];
|
||||
let statusRequests: unknown = 0;
|
||||
try {
|
||||
const numberRequests = await redis.increment("statusRequest");
|
||||
statusRequests = numberRequests?.[0];
|
||||
} catch (error) { } // eslint-disable-line no-empty
|
||||
|
||||
const statusValues: Record<string, any> = {
|
||||
uptime: process.uptime(),
|
||||
commit: (global as any).HEADCOMMIT || "unknown",
|
||||
|
|
|
@ -41,7 +41,7 @@ export interface SBSConfig {
|
|||
privateMysql?: any;
|
||||
minimumPrefix?: string;
|
||||
maximumPrefix?: string;
|
||||
redis?: redis.ClientOpts;
|
||||
redis?: redis.RedisClientOptions;
|
||||
maxRewardTimePerSegmentInSeconds?: number;
|
||||
postgres?: PoolConfig;
|
||||
dumpDatabase?: DumpDatabase;
|
||||
|
|
|
@ -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> {
|
||||
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}`);
|
||||
return reply as T & HashedValue;
|
||||
} catch (e) {
|
||||
// If all else, continue on hashing
|
||||
}
|
||||
}
|
||||
const data = getHash(key, cachedHashTimes);
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
||||
|
||||
// Otherwise, calculate it
|
||||
const data = getHash(key, cachedHashTimes);
|
||||
redis.set(key, data);
|
||||
|
||||
redis.setAsync(key, data);
|
||||
return data as T & HashedValue;
|
||||
}
|
|
@ -5,6 +5,7 @@ import { YouTubeAPI } from "../utils/youtubeApi";
|
|||
import { APIVideoInfo } from "../types/youtubeApi.model";
|
||||
import { VideoID } from "../types/segments.model";
|
||||
import { config } from "../config";
|
||||
import { Logger } from "./logger";
|
||||
|
||||
function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promise<APIVideoInfo> {
|
||||
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> => {
|
||||
const apiVideoInfo = await getYouTubeVideoInfo(videoID);
|
||||
const channelID = apiVideoInfo?.data?.authorId;
|
||||
const { err, reply } = await redis.getAsync(tempVIPKey(hashedUserID));
|
||||
|
||||
return err || !reply ? false : (reply == channelID);
|
||||
try {
|
||||
const reply = await redis.get(tempVIPKey(hashedUserID));
|
||||
return reply && reply == channelID;
|
||||
} catch (e) {
|
||||
Logger.error(e as string);
|
||||
return false;
|
||||
}
|
||||
};
|
|
@ -5,21 +5,18 @@ import { Service, VideoID, VideoIDHash } from "../types/segments.model";
|
|||
import { UserID } from "../types/user.model";
|
||||
|
||||
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}`);
|
||||
|
||||
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();
|
||||
|
||||
redis.setAsync(key, JSON.stringify(data));
|
||||
redis.set(key, JSON.stringify(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>> {
|
||||
const cachedValues = await Promise.all(values.map(async (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}`);
|
||||
|
||||
return {
|
||||
value,
|
||||
result: JSON.parse(reply)
|
||||
};
|
||||
} catch (e) { } //eslint-disable-line no-empty
|
||||
}
|
||||
}
|
||||
} catch (e) { } //eslint-disable-line no-empty
|
||||
|
||||
return {
|
||||
value,
|
||||
|
@ -71,7 +67,7 @@ async function getAndSplit<T, U extends string>(fetchFromDB: (values: U[]) => Pr
|
|||
}
|
||||
|
||||
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 {
|
||||
if (videoInfo) {
|
||||
redis.delAsync(skipSegmentsKey(videoInfo.videoID, videoInfo.service));
|
||||
redis.delAsync(skipSegmentGroupsKey(videoInfo.videoID, videoInfo.service));
|
||||
redis.delAsync(skipSegmentsHashKey(videoInfo.hashedVideoID, videoInfo.service));
|
||||
if (videoInfo.userID) redis.delAsync(reputationKey(videoInfo.userID));
|
||||
redis.del(skipSegmentsKey(videoInfo.videoID, videoInfo.service));
|
||||
redis.del(skipSegmentGroupsKey(videoInfo.videoID, videoInfo.service));
|
||||
redis.del(skipSegmentsHashKey(videoInfo.hashedVideoID, videoInfo.service));
|
||||
if (videoInfo.userID) redis.del(reputationKey(videoInfo.userID));
|
||||
}
|
||||
}
|
||||
|
||||
function clearRatingCache(videoInfo: { hashedVideoID: VideoIDHash; service: Service;}): void {
|
||||
if (videoInfo) {
|
||||
redis.delAsync(ratingHashKey(videoInfo.hashedVideoID, videoInfo.service));
|
||||
redis.del(ratingHashKey(videoInfo.hashedVideoID, videoInfo.service));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,59 +1,56 @@
|
|||
import { config } from "../config";
|
||||
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 {
|
||||
get(key: string, callback?: Callback<string | null>): void;
|
||||
getAsync?(key: string): Promise<{err: Error | null, reply: string | null}>;
|
||||
set(key: string, value: string, callback?: Callback<string | null>): void;
|
||||
setAsync?(key: string, value: string): Promise<{err: Error | null, reply: string | null}>;
|
||||
setAsyncEx?(key: string, value: string, seconds: number): Promise<{err: Error | null, reply: string | null}>;
|
||||
delAsync?(...keys: [string]): Promise<Error | null>;
|
||||
close?(flush?: boolean): void;
|
||||
increment?(key: string): Promise<{err: Error| null, replies: any[] | null}>;
|
||||
get(key: RedisCommandArgument): Promise<string>;
|
||||
set(key: RedisCommandArgument, value: RedisCommandArgument): Promise<string>;
|
||||
setEx(key: RedisCommandArgument, seconds: number, value: RedisCommandArgument): Promise<string>;
|
||||
del(...keys: [RedisCommandArgument]): Promise<number>;
|
||||
increment?(key: RedisCommandArgument): Promise<RedisCommandRawReply[]>;
|
||||
sendCommand(args: RedisCommandArguments, options?: ClientCommandOptions): Promise<RedisReply>;
|
||||
quit(): Promise<void>;
|
||||
}
|
||||
|
||||
let exportObject: RedisSB = {
|
||||
get: (key, callback?) => callback(null, undefined),
|
||||
getAsync: () =>
|
||||
new Promise((resolve) => resolve({ err: null, reply: undefined })),
|
||||
set: (key, value, callback) => callback(null, undefined),
|
||||
setAsync: () =>
|
||||
new Promise((resolve) => resolve({ err: null, reply: undefined })),
|
||||
setAsyncEx: () =>
|
||||
new Promise((resolve) => resolve({ err: null, reply: undefined })),
|
||||
delAsync: () =>
|
||||
new Promise((resolve) => resolve(null)),
|
||||
increment: () =>
|
||||
new Promise((resolve) => resolve({ err: null, replies: undefined })),
|
||||
let exportClient: RedisSB = {
|
||||
get: () => new Promise((resolve, reject) => reject()),
|
||||
set: () => new Promise((resolve, reject) => reject()),
|
||||
setEx: () => new Promise((resolve, reject) => reject()),
|
||||
del: () => new Promise((resolve, reject) => reject()),
|
||||
increment: () => new Promise((resolve, reject) => reject()),
|
||||
sendCommand: () => new Promise((resolve, reject) => reject()),
|
||||
quit: () => new Promise((resolve, reject) => reject()),
|
||||
};
|
||||
|
||||
if (config.redis) {
|
||||
Logger.info("Connected to redis");
|
||||
const client = redis.createClient(config.redis);
|
||||
exportObject = client;
|
||||
const client = createClient(config.redis);
|
||||
client.connect();
|
||||
exportClient = client;
|
||||
|
||||
const timeoutDuration = 200;
|
||||
exportObject.getAsync = (key) => new Promise((resolve) => {
|
||||
const timeout = setTimeout(() => resolve({ err: null, reply: undefined }), timeoutDuration);
|
||||
client.get(key, (err, reply) => {
|
||||
const get = client.get.bind(client);
|
||||
exportClient.get = (key) => new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => reject(), timeoutDuration);
|
||||
get(key).then((reply) => {
|
||||
clearTimeout(timeout);
|
||||
resolve({ err, reply });
|
||||
});
|
||||
resolve(reply);
|
||||
}).catch((err) => reject(err));
|
||||
});
|
||||
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) =>
|
||||
exportClient.increment = (key) => new Promise((resolve, reject) =>
|
||||
client.multi()
|
||||
.incr(key)
|
||||
.expire(key, 60)
|
||||
.exec((err, replies) => resolve({ err, replies }))
|
||||
.exec()
|
||||
.then((reply) => resolve(reply))
|
||||
.catch((err) => reject(err))
|
||||
);
|
||||
client.on("error", function(error) {
|
||||
Logger.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
export default exportObject;
|
||||
export default exportClient;
|
||||
|
|
|
@ -12,21 +12,20 @@ const randKey2 = genRandom(16);
|
|||
describe("redis test", function() {
|
||||
before(async function() {
|
||||
if (!config.redis) this.skip();
|
||||
await redis.setAsync(randKey1, randValue1);
|
||||
await redis.set(randKey1, randValue1);
|
||||
});
|
||||
it("Should get stored value", (done) => {
|
||||
redis.getAsync(randKey1)
|
||||
redis.get(randKey1)
|
||||
.then(res => {
|
||||
if (res.err) assert.fail(res.err);
|
||||
assert.strictEqual(res.reply, randValue1);
|
||||
assert.strictEqual(res, randValue1);
|
||||
done();
|
||||
});
|
||||
}).catch(err => assert.fail(err));
|
||||
});
|
||||
it("Should not be able to get not stored value", (done) => {
|
||||
redis.getAsync(randKey2)
|
||||
redis.get(randKey2)
|
||||
.then(res => {
|
||||
if (res.reply || res.err ) assert.fail("Value should not be found");
|
||||
if (res) assert.fail("Value should not be found");
|
||||
done();
|
||||
});
|
||||
}).catch(err => assert.fail(err));
|
||||
});
|
||||
});
|
|
@ -6,6 +6,7 @@ import { client } from "../utils/httpClient";
|
|||
import { db, privateDB } from "../../src/databases/databases";
|
||||
import redis from "../../src/utils/redis";
|
||||
import assert from "assert";
|
||||
import { Logger } from "../../src/utils/logger";
|
||||
|
||||
// helpers
|
||||
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 { reply } = await redis.getAsync(tempVIPKey(publicID));
|
||||
return reply;
|
||||
try {
|
||||
const reply = await redis.get(tempVIPKey(publicID));
|
||||
return reply;
|
||||
} catch (e) {
|
||||
Logger.error(e as string);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
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 (?)', [publicPermVIP2]);
|
||||
// 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", () => {
|
||||
|
|
|
@ -57,7 +57,7 @@ async function init() {
|
|||
mocha.run((failures) => {
|
||||
mockServer.close();
|
||||
server.close();
|
||||
redis.close(true);
|
||||
redis.quit();
|
||||
process.exitCode = failures ? 1 : 0; // exit with non-zero status if there were failures
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue