From 1c96fa6e75aac6f91f8c444f5f3bea20fa8a61fa Mon Sep 17 00:00:00 2001 From: RuiSiang <49300088+RuiSiang@users.noreply.github.com> Date: Thu, 8 Apr 2021 22:54:02 +0800 Subject: [PATCH] change database to redis --- .env.example | 4 + CONFIGURE.md | 5 + README.md | 2 +- USAGE.md | 22 +- app.ts | 12 +- docker-compose.example.yaml | 5 + package-lock.json | 669 ++++++++++----------------- package.json | 9 +- routes/test-router.ts | 6 +- service/controller-service.ts | 14 +- service/controllers/blacklist.ts | 33 +- service/controllers/rate-limiter.ts | 43 +- service/util/config-parser.ts | 17 +- service/util/database-service.ts | 92 ++-- tests/integration/controller.test.ts | 4 +- tests/unit/blacklist.test.ts | 4 +- tests/unit/config.spec.ts | 6 +- tests/unit/database.test.ts | 91 +--- tests/unit/rate-limit.test.ts | 2 +- 19 files changed, 408 insertions(+), 632 deletions(-) diff --git a/.env.example b/.env.example index 3c003fa5..3424a8c2 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,10 @@ NONCE_VALIDITY=60000 INITIAL_DIFFICULTY=13 BACKEND_URL="http://example.com" +DATABASE_HOST=127.0.0.1 +DATABASE_PORT=6379 +DATABASE_PASSWORD= + RATE_LIMIT=on RATE_LIMIT_SAMPLE_MINUTES=60 RATE_LIMIT_SESSION_THRESHOLD=100 diff --git a/CONFIGURE.md b/CONFIGURE.md index 3bcb2991..9201a1da 100644 --- a/CONFIGURE.md +++ b/CONFIGURE.md @@ -14,6 +14,11 @@ General Options - SESSION_KEY: secret key for cookie signatures, use a unique one for security reasons, or anyone can forge your signed cookies - BACKEND_URL: location to proxy authenticated traffic to, IP and URLs are both accepted(accepts protocol://url(:port) or protocol://ip(:port)) +Database Options (Redis) +- DATABASE_HOST: (default:127.0.0.1) redis service host +- DATABASE_PORT: (default:6379) redis service port +- DATABASE_PASSWORD: (default:null) redis service password + PoW Options - POW: (default:on) toggles PoW functionality on/off (if not temporary switched off, why use this project at all?) diff --git a/README.md b/README.md index 3a1d04b5..d07cc9a4 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,10 @@ environment variables - Ratelimiting - Unit Testing - WAF Implementation +- Multi-Instance Syncing (Redis) ## Todos - [ ] Dynamic Difficulty -- [ ] Multi-Instance Syncing - [ ] Monitoring - [ ] Captcha diff --git a/USAGE.md b/USAGE.md index b2c042c4..60605628 100644 --- a/USAGE.md +++ b/USAGE.md @@ -22,13 +22,25 @@ cp -n .env.example .env nano .env ``` -Build and run +Build ``` npm run build +``` + +Run with db (redis), recommended & faster +``` +# install redis first +# sudo apt-get install redis-server npm start ``` +Run without db (mock redis) + +``` +npm run start-standalone +``` + Test functionalities(optional) ``` @@ -37,12 +49,18 @@ npm test ## Docker ([repo](https://hub.docker.com/repository/docker/ruisiang/pow-shield)) -### docker run +### docker run with db (redis), recommended & faster ``` docker run -p 3000:3000 -e BACKEND_URL="http://example.com" -d ruisiang/pow-shield ``` +### docker run without db (mock redis) + +``` +docker run -p 3000:3000 -e BACKEND_URL="http://example.com" -e NODE_ENV="standalone" -d ruisiang/pow-shield +``` + ### docker-compose Configure docker-compose.example.yaml (more information in [configure section](CONFIGURE.md)) diff --git a/app.ts b/app.ts index 67be5e6f..30a2abcc 100644 --- a/app.ts +++ b/app.ts @@ -7,7 +7,7 @@ import logger from 'koa-logger' import { createProxyMiddleware } from 'http-proxy-middleware' import c2k from 'koa2-connect' import session from 'koa-session-minimal' - +import redisStore from 'koa-redis' import config from './service/util/config-parser' import powRouter from './routes/pow-router' import testRouter from './routes/test-router' @@ -24,6 +24,14 @@ app.use( signed: true, sameSite: 'strict', }, + store: + process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'standalone' + ? undefined + : redisStore({ + host: config.database_host, + port: config.database_port, + password: config.database_password, + }), }) ) @@ -58,7 +66,7 @@ app.use(async (ctx, next) => { }) // service and routes -if (process.env.NODE_ENV === 'test') { +if (process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'standalone') { app.use(testRouter.routes()) } app.use(controller) diff --git a/docker-compose.example.yaml b/docker-compose.example.yaml index 53185479..ac220aea 100644 --- a/docker-compose.example.yaml +++ b/docker-compose.example.yaml @@ -10,6 +10,9 @@ services: - NONCE_VALIDITY=60000 - INITIAL_DIFFICULTY=13 - BACKEND_URL="http://example.com" + - DATABASE_HOST=redis + - DATABASE_PORT=6379 + - DATABASE_PASSWORD= - RATE_LIMIT=on - RATE_LIMIT_SAMPLE_MINUTES=60 - RATE_LIMIT_SESSION_THRESHOLD=100 @@ -24,3 +27,5 @@ services: - '3000' ports: - '3000:3000' + redis: + image: redis:alpine diff --git a/package-lock.json b/package-lock.json index a162d6b3..0837f97f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -357,6 +357,14 @@ "@babel/helper-plugin-utils": "^7.12.13" } }, + "@babel/runtime": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.10.tgz", + "integrity": "sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "@babel/template": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", @@ -1208,6 +1216,15 @@ "@types/node": "*" } }, + "@types/ioredis": { + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.22.3.tgz", + "integrity": "sha512-V23g0XZUmkm0Hp/GsQYV5Wz12ynm6h6lyi5v/o63iyqFV7+8t3k5YxCnFWVWFEjO6mvRI7V6f7YxNVxIjPsSUw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -1372,15 +1389,6 @@ "@types/node": "*" } }, - "@types/sqlite3": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/sqlite3/-/sqlite3-3.1.7.tgz", - "integrity": "sha512-8FHV/8Uzd7IwdHm5mvmF2Aif4aC/gjrt4axWD9SmfaxITnOjtOhCbOSTuqv/VbH1uq0QrwlaTj9aTz3gmR6u4w==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/stack-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", @@ -1486,6 +1494,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1510,11 +1519,6 @@ } } }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1538,20 +1542,6 @@ "picomatch": "^2.0.4" } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1604,6 +1594,7 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, "requires": { "safer-buffer": "~2.1.0" } @@ -1663,7 +1654,8 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true }, "assign-symbols": { "version": "1.0.0", @@ -1674,7 +1666,8 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true }, "at-least-node": { "version": "1.0.0", @@ -1746,12 +1739,14 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true }, "aws4": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true }, "babel-jest": { "version": "26.6.3", @@ -1959,6 +1954,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, "requires": { "tweetnacl": "^0.14.3" } @@ -2003,15 +1999,6 @@ } } }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "optional": true, - "requires": { - "inherits": "~2.0.0" - } - }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -2382,7 +2369,8 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true }, "chalk": { "version": "2.4.2", @@ -2411,7 +2399,8 @@ "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true }, "ci-info": { "version": "2.0.0", @@ -2515,6 +2504,11 @@ "shallow-clone": "^0.1.2" } }, + "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==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -2531,10 +2525,13 @@ "type-is": "^1.6.16" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "co-wrap-all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/co-wrap-all/-/co-wrap-all-1.0.0.tgz", + "integrity": "sha1-Nwrj6DM1EKU/ay9/375FaKEbfs8=", + "requires": { + "co": "^4.0.0" + } }, "collect-v8-coverage": { "version": "1.0.1", @@ -2601,6 +2598,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -2658,11 +2656,6 @@ "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", "dev": true }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, "consolidate": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.16.0.tgz", @@ -2873,6 +2866,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -2919,11 +2913,6 @@ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -3006,13 +2995,19 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, + "denque": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -3045,11 +3040,6 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" - }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -3144,6 +3134,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -3553,7 +3544,8 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true }, "extend-shallow": { "version": "2.0.1", @@ -3651,17 +3643,20 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "fast-levenshtein": { "version": "2.0.6", @@ -3693,6 +3688,31 @@ "pend": "~1.2.0" } }, + "fengari": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/fengari/-/fengari-0.1.4.tgz", + "integrity": "sha512-6ujqUuiIYmcgkGz8MGAdERU57EIluGGPSUgGPTsco657EHa+srq0S3/YUl/r9kx1+D+d4rGfYObd+m8K22gB1g==", + "dev": true, + "requires": { + "readline-sync": "^1.4.9", + "sprintf-js": "^1.1.1", + "tmp": "^0.0.33" + }, + "dependencies": { + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + } + } + }, + "fengari-interop": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/fengari-interop/-/fengari-interop-0.1.2.tgz", + "integrity": "sha512-8iTvaByZVoi+lQJhHH9vC+c/Yaok9CwOqNQZN6JrVpjmWwW4dDkeblBXhnHC+BoI6eF4Cy5NKW3z6ICEjvgywQ==", + "dev": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -3830,12 +3850,14 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -3888,14 +3910,6 @@ } } }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3908,49 +3922,11 @@ "dev": true, "optional": true }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "optional": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "optional": true, - "requires": { - "minimist": "^1.2.5" - } - } - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -4012,6 +3988,7 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -4076,7 +4053,8 @@ "graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true }, "growly": { "version": "1.3.0", @@ -4088,12 +4066,14 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true }, "har-validator": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, "requires": { "ajv": "^6.12.3", "har-schema": "^2.0.0" @@ -4122,11 +4102,6 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -4367,6 +4342,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -4414,14 +4390,6 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "requires": { - "minimatch": "^3.0.4" - } - }, "import-local": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", @@ -4497,6 +4465,36 @@ "xtend": "^4.0.0" } }, + "ioredis": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-4.25.0.tgz", + "integrity": "sha512-UoeqXpZB05aerGD3gB9NiigMsAyph+N+GWH8+3lX1+26caVV03GkL6JoLxS2HCxyvqCWbNsVSZTAp5W12qe23A==", + "requires": { + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.1", + "denque": "^1.1.0", + "lodash.defaults": "^4.2.0", + "lodash.flatten": "^4.4.0", + "p-map": "^2.1.0", + "redis-commands": "1.7.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + } + }, + "ioredis-mock": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ioredis-mock/-/ioredis-mock-5.4.1.tgz", + "integrity": "sha512-H8yz/ndYvmnz/Vr2ngU01XI59NBq/3QNBmZ3GCyjOuMWVbPfUbhMKZON3Er5JRbU2BZZvAU4+9yW9tu7xLe7XA==", + "dev": true, + "requires": { + "fengari": "^0.1.4", + "fengari-interop": "^0.1.2", + "lodash": "^4.17.20", + "minimatch": "^3.0.4", + "standard-as-callback": "^2.0.1" + } + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -4618,14 +4616,6 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, "is-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", @@ -4742,7 +4732,8 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, "is-weakmap": { "version": "2.0.1", @@ -4782,7 +4773,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "isobject": { "version": "3.0.1", @@ -4793,7 +4785,8 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true }, "istanbul-lib-coverage": { "version": "3.0.0", @@ -6499,7 +6492,8 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true }, "jsdom": { "version": "16.5.1", @@ -6569,12 +6563,14 @@ "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "json-stringify-safe": { "version": "5.0.1", @@ -6618,6 +6614,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -6759,6 +6756,17 @@ "passthrough-counter": "^1.0.0" } }, + "koa-redis": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/koa-redis/-/koa-redis-4.0.1.tgz", + "integrity": "sha512-o2eTVNo1NBnloeUGhHed5Q2ZvJSLpUEj/+E1/7oH5EmH8WuQ+QLdl/VawkshxdFQ47W1p6V09lM3hCTu7D0YnQ==", + "requires": { + "@babel/runtime": "^7.8.3", + "co-wrap-all": "^1.0.0", + "debug": "^4.1.1", + "ioredis": "^4.14.1" + } + }, "koa-router": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/koa-router/-/koa-router-10.0.0.tgz", @@ -6911,6 +6919,16 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, "lodash.memoize": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", @@ -7159,31 +7177,8 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - }, - "dependencies": { - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - } + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true }, "mixin-deep": { "version": "1.3.2", @@ -7338,26 +7333,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "needle": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz", - "integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==", - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -7375,63 +7350,12 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "node-addon-api": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz", - "integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==" - }, "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, - "node-gyp": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", - "optional": true, - "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "optional": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "optional": true, - "requires": { - "abbrev": "1" - } - }, - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "optional": true - } - } - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -7505,61 +7429,6 @@ } } }, - "node-pre-gyp": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", - "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } - } - }, "node-releases": { "version": "1.1.71", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", @@ -7592,29 +7461,6 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -7624,22 +7470,6 @@ "path-key": "^2.0.0" } }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, "nwsapi": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", @@ -7649,7 +7479,8 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true }, "object-assign": { "version": "4.1.1", @@ -7790,21 +7621,14 @@ "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true }, "p-each-series": { "version": "2.2.0", @@ -7836,6 +7660,11 @@ "p-limit": "^2.2.0" } }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -7971,7 +7800,8 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true }, "picomatch": { "version": "2.2.2", @@ -8127,7 +7957,8 @@ "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true }, "public-encrypt": { "version": "4.0.3", @@ -8276,7 +8107,8 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true }, "puppeteer": { "version": "8.0.0", @@ -8388,17 +8220,6 @@ } } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, "react-is": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", @@ -8459,6 +8280,35 @@ "util-deprecate": "~1.0.1" } }, + "readline-sync": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", + "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", + "dev": true + }, + "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", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -8521,6 +8371,7 @@ "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -8547,7 +8398,8 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true } } }, @@ -8671,14 +8523,6 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, "ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", @@ -8757,11 +8601,6 @@ } } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, "saxes": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", @@ -8779,7 +8618,8 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true }, "set-value": { "version": "2.0.1", @@ -8891,7 +8731,8 @@ "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true }, "simple-concat": { "version": "1.0.1", @@ -9165,20 +9006,11 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "sqlite3": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.2.tgz", - "integrity": "sha512-1SdTNo+BVU211Xj1csWa8lV6KM0CtucDwRyA0VHl91wEH1Mgh7RxUpI4rVvG7OhHrzCSGaVyW5g8vKvlrk9DJA==", - "requires": { - "node-addon-api": "^3.0.0", - "node-gyp": "3.x", - "node-pre-gyp": "^0.11.0" - } - }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -9208,6 +9040,11 @@ } } }, + "standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -9344,16 +9181,6 @@ } } }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, "string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -9380,14 +9207,6 @@ "safe-buffer": "~5.1.0" } }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -9406,11 +9225,6 @@ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, "subarg": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", @@ -9476,17 +9290,6 @@ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true }, - "tar": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", - "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", - "optional": true, - "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" - } - }, "tar-fs": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", @@ -9593,6 +9396,15 @@ "process": "~0.11.0" } }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -9670,6 +9482,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, "requires": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -9755,6 +9568,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -9762,7 +9576,8 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true }, "type": { "version": "1.2.0", @@ -9968,6 +9783,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "requires": { "punycode": "^2.1.0" } @@ -10024,7 +9840,8 @@ "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true }, "v8-to-istanbul": { "version": "7.1.0", @@ -10073,6 +9890,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -10197,6 +10015,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -10244,14 +10063,6 @@ "is-typed-array": "^1.1.3" } }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, "with": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", diff --git a/package.json b/package.json index dfda815a..fcf76961 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "scripts": { "start": "node dist/bin/server.js", + "start-standalone": "set NODE_ENV=standalone&& node dist/bin/server.js", "test": "jest --runInBand --forceExit --detectOpenHandles", "build": "bash scripts/build.sh", "update-static": "bash scripts/update-static.sh" @@ -14,10 +15,12 @@ "debug": "^4.1.1", "dotenv": "^8.2.0", "http-proxy-middleware": "^1.1.0", + "ioredis": "^4.25.0", "koa": "^2.7.0", "koa-bodyparser": "^4.2.1", "koa-json": "^2.0.2", "koa-logger": "^3.2.0", + "koa-redis": "^4.0.1", "koa-router": "^10.0.0", "koa-session-minimal": "^4.0.1", "koa-static": "^5.0.0", @@ -26,13 +29,13 @@ "moment": "^2.29.1", "pug": "^3.0.1", "randomstring": "^1.1.5", - "source-map-loader": "^2.0.1", - "sqlite3": "^5.0.2" + "source-map-loader": "^2.0.1" }, "devDependencies": { "@types/create-hash": "^1.2.2", "@types/cron": "^1.7.2", "@types/debug": "^4.1.5", + "@types/ioredis": "^4.22.3", "@types/jest": "^26.0.22", "@types/koa": "^2.11.3", "@types/koa-bodyparser": "^4.3.0", @@ -42,9 +45,9 @@ "@types/koa-session-minimal": "^3.0.5", "@types/koa-views": "^2.0.4", "@types/randomstring": "^1.1.6", - "@types/sqlite3": "^3.1.7", "awesome-typescript-loader": "^5.2.1", "browserify": "^17.0.0", + "ioredis-mock": "^5.4.1", "jest": "^26.6.3", "jest-html-reporters": "^2.1.3", "jest-puppeteer": "^4.4.0", diff --git a/routes/test-router.ts b/routes/test-router.ts index d0971d63..ce60999d 100644 --- a/routes/test-router.ts +++ b/routes/test-router.ts @@ -7,11 +7,11 @@ const router = new Router() router.prefix('/test') router.get('/', async (ctx: Koa.ParameterizedContext, next: Koa.Next) => { - if (ctx.query.action == 'triggerRemoveExpired') { + if (ctx.query.action == 'triggerReset') { const rateLimiter = RateLimiter.getInstance() const blacklist = Blacklist.getInstance() - await rateLimiter.triggerRemoveExpired() - await blacklist.triggerRemoveExpired() + await rateLimiter.triggerReset() + await blacklist.triggerReset() } }) diff --git a/service/controller-service.ts b/service/controller-service.ts index de7cf9dd..d37e9557 100644 --- a/service/controller-service.ts +++ b/service/controller-service.ts @@ -22,28 +22,34 @@ export const controller: Koa.Middleware = async function ( ) } next() + return } else { if (ctx.request.url == '/') { ctx.redirect('/pow') + return } else if (ctx.request.url == '/pow') { await next() + return } else { ctx.redirect(`/pow?redirect=${ctx.request.url}`) + return } } } else { console.log( `Rule ${scanResult.id}: "${scanResult.cmt}" in category "${ scanResult.type - }" has been triggered by request ${scanResult.location} at ${new Date().toISOString()}` + }" has been triggered by request ${ + scanResult.location + } at ${new Date().toISOString()}` ) ctx.status = 403 await ctx.render('waf') + return } - } else { - ctx.status = 403 - await ctx.render('banned') } + ctx.status = 403 + await ctx.render('banned') } const inspect = async (ctx: Koa.ParameterizedContext) => { diff --git a/service/controllers/blacklist.ts b/service/controllers/blacklist.ts index 711aae10..a4e166a6 100644 --- a/service/controllers/blacklist.ts +++ b/service/controllers/blacklist.ts @@ -12,39 +12,22 @@ class Blacklist { } private db: Database = Database.getInstance() - constructor() { - this.job.start() - } public check = async (ip: string) => { - const dbQuery = await this.db.queryAsync({ - sql: 'select * from blacklist where ip=?', - values: [ip], - }) - if (!dbQuery.length) { + const blk = await this.db.get(`blk-${ip}`) + if (blk!='1') { return true } return false || !config.rate_limit } public ban = async (ip: string, minutes: number) => { - await this.db.queryAsync({ - sql: `insert into blacklist (ip, expiry) values(?, datetime(CURRENT_TIMESTAMP, "+${minutes} minutes"))`, - values: [ip], - }) - } - private removeExpired = async () => { - await this.db.queryAsync({ - sql: 'delete from blacklist where expiry<=datetime(CURRENT_TIMESTAMP)', - values: [], - }) + await this.db.set(`blk-${ip}`, '1', true, minutes * 60) } - private job = new CronJob('0 */5 * * * *', async () => { - if (process.env.NODE_ENV !== 'test') { - await this.removeExpired() - } - }) - public triggerRemoveExpired = async () => { + public triggerReset = async () => { if (process.env.NODE_ENV === 'test') { - await this.removeExpired() + const blkKeys = await this.db.keys('blk-*') + for (let i = 0; i < blkKeys.length; i++) { + await this.db.del(blkKeys[i]) + } } } } diff --git a/service/controllers/rate-limiter.ts b/service/controllers/rate-limiter.ts index 7ee05495..d4305982 100644 --- a/service/controllers/rate-limiter.ts +++ b/service/controllers/rate-limiter.ts @@ -1,5 +1,4 @@ import moment from 'moment' -import { CronJob } from 'cron' import Database from '../util/database-service' import Blacklist from './blacklist' import config from '../util/config-parser' @@ -15,9 +14,6 @@ class RateLimiter { private db: Database = Database.getInstance() private blacklist: Blacklist = Blacklist.getInstance() - constructor() { - this.job.start() - } public process = async ( ip: string, session: { @@ -60,37 +56,30 @@ class RateLimiter { } } private add = async (ip: string) => { - await this.db.queryAsync({ - sql: `insert into requests (ip, expiry, requests) values(?, datetime(CURRENT_TIMESTAMP, "+${config.rate_limit_sample_minutes} minutes"), 0) on conflict(ip) do update set requests=requests+1`, - values: [ip], - }) + await this.db.set( + `req-${ip}`, + '0', + true, + config.rate_limit_sample_minutes * 60 + ) + await this.db.incr(`req-${ip}`) } private get = async (ip: string) => { - const dbQuery = await this.db.queryAsync({ - sql: 'select requests from requests where ip=?', - values: [ip], - }) - if (!dbQuery.length) { + const reqCnt = await this.db.get(`req-${ip}`) + + if (reqCnt === null) { return 0 } else { - return dbQuery[0].requests + return parseInt(reqCnt) } } - private removeExpired = async () => { - await this.db.queryAsync({ - sql: 'delete from requests where expiry<=datetime(CURRENT_TIMESTAMP)', - values: [], - }) - } - private job = new CronJob('0 */5 * * * *', async () => { - if (process.env.NODE_ENV !== 'test') { - await this.removeExpired() - } - }) - public triggerRemoveExpired = async () => { + public triggerReset = async () => { if (process.env.NODE_ENV === 'test') { - await this.removeExpired() + const reqKeys = await this.db.keys('req-*') + for (let i = 0; i < reqKeys.length; i++) { + await this.db.del(reqKeys[i]) + } } } } diff --git a/service/util/config-parser.ts b/service/util/config-parser.ts index 27c6c9e8..5ea97028 100644 --- a/service/util/config-parser.ts +++ b/service/util/config-parser.ts @@ -8,6 +8,9 @@ interface Config { nonce_validity: number initial_difficulty: number backend_url: string + database_host?: string + database_port?: number + database_password?: string rate_limit: boolean rate_limit_sample_minutes: number rate_limit_session_threshold: number @@ -27,14 +30,14 @@ if (process.env.NODE_ENV === 'test') { session_key: 'abcdefghijklmnop', pow: true, nonce_validity: 60000, - initial_difficulty: 13, + initial_difficulty: 5, backend_url: 'http://example.org', rate_limit: true, - rate_limit_sample_minutes: 0, + rate_limit_sample_minutes: 10, rate_limit_session_threshold: 2, rate_limit_ban_ip: true, rate_limit_ip_threshold: 3, - rate_limit_ban_minutes: 0, + rate_limit_ban_minutes: 10, waf: true, waf_url_exclude_rules: '', waf_header_exclude_rules: '14,33,80,96,100', @@ -47,6 +50,11 @@ if (process.env.NODE_ENV === 'test') { nonce_validity: parseInt(process.env.NONCE_VALIDITY || '') || 60 * 1000, initial_difficulty: parseInt(process.env.INITIAL_DIFFICULTY || '') || 13, backend_url: process.env.BACKEND_URL || 'http://www.example.com', + database_host: process.env.DATABASE_HOST || '127.0.0.1', + database_port: process.env.DATABASE_PORT + ? parseInt(process.env.DATABASE_PORT) + : 6379, + database_password: process.env.DATABASE_PASSWORD || '', rate_limit: (process.env.RATE_LIMIT || 'on') == 'on', rate_limit_sample_minutes: parseInt( process.env.RATE_LIMIT_SAMPLE_MINUTES || '60' @@ -63,7 +71,8 @@ if (process.env.NODE_ENV === 'test') { ), waf: (process.env.WAF || 'on') == 'on', waf_url_exclude_rules: process.env.WAF_URL_EXCLUDE_RULES || '', - waf_header_exclude_rules: process.env.WAF_HEADER_EXCLUDE_RULES || '14,33,80,96,100', + waf_header_exclude_rules: + process.env.WAF_HEADER_EXCLUDE_RULES || '14,33,80,96,100', waf_body_exclude_rules: process.env.WAF_BODY_EXCLUDE_RULES || '', } } diff --git a/service/util/database-service.ts b/service/util/database-service.ts index b0eec4d5..440ce337 100644 --- a/service/util/database-service.ts +++ b/service/util/database-service.ts @@ -1,66 +1,58 @@ -import { verbose as sqlite3, Database as DatabaseType } from 'sqlite3' +import config from './config-parser' +import redis, { Redis } from 'ioredis' +import redisMock from 'ioredis-mock' class Database { private static instance: Database - public static getInstance(): Database { + public static getInstance() { if (!Database.instance) { - Database.instance = new Database() + try { + Database.instance = new Database() + } catch (err) { + console.log('Redis error, please check redis server status') + } } return Database.instance } - private sqliteDb: DatabaseType + private dbInstance: Redis constructor() { - this.sqliteDb = new (sqlite3().Database)(':memory:') + if ( + process.env.NODE_ENV === 'test' || + process.env.NODE_ENV === 'standalone' + ) { + this.dbInstance = new redisMock() + } else { + this.dbInstance = new redis({ + host: config.database_host, + port: config.database_port, + }) + } } - - private tableCheck = false - private createIfNotExist = async () => { - const table_cmds = [ - 'create table if not exists blacklist (ip text unique, expiry text)', - 'create table if not exists requests (ip text unique, expiry text, requests int)', - ] - let promises: Promise[] = [] - table_cmds.forEach((table_cmd) => { - promises.push( - new Promise((resolve, reject) => { - this.sqliteDb.run(table_cmd, (err) => { - if (err) { - reject(err) - } else { - this.tableCheck = true - resolve(true) - } - }) - }) - ) - }) - return Promise.all(promises) + public get = async (key: string) => { + return await this.dbInstance.get(key) } - - private sqliteQuery = async ( - query: string, - params: string[] | undefined - ): Promise => { - if (!params) { - params = [] - } - if (!this.tableCheck) { - await this.createIfNotExist() + public set = async ( + key: string, + value: string, + setexpr?: boolean, + exprTime?: number + ) => { + if (setexpr) { + return await this.dbInstance.set(key, value, 'EX', exprTime, 'NX') + } else { + return await this.dbInstance.set(key, value, 'NX') } - return new Promise((resolve, reject) => { - this.sqliteDb.all(query, params, (err, rows) => { - if (err) { - reject(err) - } else { - resolve(rows) - } - }) - }) } - - public queryAsync = async (data: { sql: string; values: string[] }) => { - return await this.sqliteQuery(data.sql, data.values) + public incr = async (key: string) => { + await this.dbInstance.incr(key) + } + public del = async (key: string) => { + await this.dbInstance.del(key) + } + public keys = async (pattern: string) => { + return await this.dbInstance.keys(pattern) } } + export default Database diff --git a/tests/integration/controller.test.ts b/tests/integration/controller.test.ts index 5290fe7f..ce6a1100 100644 --- a/tests/integration/controller.test.ts +++ b/tests/integration/controller.test.ts @@ -30,11 +30,11 @@ describe('IP banning', () => { await page.waitForFunction('window.nonceSent == true') await page.waitForNavigation() expect(await page.title()).toEqual('Example Domain') - for (let i = 0; i < 4; i++) { + for (let i = 0; i < 6; i++) { await page.goto('http://localhost:3000') } expect(await page.content()).toContain('banned') - await page.goto('http://localhost:3000/test?action=triggerRemoveExpired') + await page.goto('http://localhost:3000/test?action=triggerReset') const response = await page.goto('http://localhost:3000') expect(response.status()).toEqual(200) expect(await page.content()).not.toContain('banned') diff --git a/tests/unit/blacklist.test.ts b/tests/unit/blacklist.test.ts index 002d18e8..f3764e1d 100644 --- a/tests/unit/blacklist.test.ts +++ b/tests/unit/blacklist.test.ts @@ -11,11 +11,11 @@ describe(`Blacklist`, () => { expect(await blacklist.check('test_ip')).toBeTruthy() }) it('should not pass ban list checking after banned', async () => { - await blacklist.ban('test_ip', 0) + await blacklist.ban('test_ip', 10) expect(await blacklist.check('test_ip')).toBeFalsy() }) it('should pass ban list checking after ban expired', async () => { - await blacklist.triggerRemoveExpired() + await blacklist.triggerReset() expect(await blacklist.check('test_ip')).toBeTruthy() }) }) diff --git a/tests/unit/config.spec.ts b/tests/unit/config.spec.ts index 12a8ffae..943cef97 100644 --- a/tests/unit/config.spec.ts +++ b/tests/unit/config.spec.ts @@ -12,14 +12,14 @@ describe('Configuration', () => { session_key: 'abcdefghijklmnop', pow: true, nonce_validity: 60000, - initial_difficulty: 13, + initial_difficulty: 5, backend_url: 'http://example.org', rate_limit: true, - rate_limit_sample_minutes: 0, + rate_limit_sample_minutes: 10, rate_limit_session_threshold: 2, rate_limit_ban_ip: true, rate_limit_ip_threshold: 3, - rate_limit_ban_minutes: 0, + rate_limit_ban_minutes: 10, waf: true, waf_url_exclude_rules: '', waf_header_exclude_rules: '14,33,80,96,100', diff --git a/tests/unit/database.test.ts b/tests/unit/database.test.ts index 4318529e..3eff1189 100644 --- a/tests/unit/database.test.ts +++ b/tests/unit/database.test.ts @@ -1,85 +1,28 @@ import Database from '../../service/util/database-service' let db: Database -const tables = [ - { - name: 'blacklist', - columns: ['ip', 'expiry'], - insert_cmd: - 'insert into blacklist (ip, expiry) values("test_ip", "test_time")', - insert_result: [{ ip: 'test_ip', expiry: 'test_time' }], - update_cmd: 'update blacklist set expiry="test_time+1" where ip="test_ip"', - update_result: [{ ip: 'test_ip', expiry: 'test_time+1' }], - }, - { - name: 'requests', - columns: ['ip', 'expiry', 'requests'], - insert_cmd: - 'insert into requests (ip, expiry, requests) values("test_ip", "test_time", 0)', - insert_result: [{ ip: 'test_ip', expiry: 'test_time', requests: 0 }], - update_cmd: 'update requests set requests=requests+1 where ip="test_ip"', - update_result: [{ ip: 'test_ip', expiry: 'test_time', requests: 1 }], - }, -] beforeAll(() => { db = Database.getInstance() return }) -tables.forEach((table) => { - describe(`Database table "${table.name}"`, () => { - it('should have specified columns', async () => { - const pragma = await db.queryAsync({ - sql: `PRAGMA table_info(${table.name})`, - values: [], - }) - let sortedPragma: string[] = [] - pragma.forEach((element: { name: string }) => { - sortedPragma.push(element.name) - }) - table.columns.forEach((column) => { - expect(sortedPragma).toContain(column) - }) - }) - - it('should be insertable', async () => { - await db.queryAsync({ - sql: table.insert_cmd, - values: [], - }) - expect( - await db.queryAsync({ - sql: `select * from ${table.name}`, - values: [], - }) - ).toMatchObject(table.insert_result) - }) - - it('should be updatable', async () => { - await db.queryAsync({ - sql: table.update_cmd, - values: [], - }) - expect( - await db.queryAsync({ - sql: `select * from ${table.name}`, - values: [], - }) - ).toMatchObject(table.update_result) - }) - - it('should be deletable', async () => { - await db.queryAsync({ - sql: `delete from ${table.name}`, - values: [], - }) - expect( - await db.queryAsync({ - sql: `select * from ${table.name}`, - values: [], - }) - ).toMatchObject([]) - }) +describe(`Database`, () => { + it('should be settable', async () => { + expect(await db.set('test', '0')).toStrictEqual('OK') + }) + it('should be gettable', async () => { + expect(await db.get('test')).toStrictEqual('0') + }) + it('should be matchable', async () => { + expect(await db.keys('t*')).toMatchObject(['test']) + }) + it('should be incrementable', async () => { + await db.incr('test') + expect(await db.get('test')).toStrictEqual('1') + }) + it('should be deletable', async () => { + await db.del('test') + expect(await db.get('test')).toBeFalsy() }) }) diff --git a/tests/unit/rate-limit.test.ts b/tests/unit/rate-limit.test.ts index 1e2cd010..7a69b2b1 100644 --- a/tests/unit/rate-limit.test.ts +++ b/tests/unit/rate-limit.test.ts @@ -36,7 +36,7 @@ describe(`Ratelimiter`, () => { timestamp: 'Sun Jan 01 2000 00:00:00 GMT-0000', authorized: true, }) - await ratelimiter.triggerRemoveExpired() + await ratelimiter.triggerReset() expect(result.authorized).toEqual(true) }) })