From bad54a2129e5e919afb1a9b7f71f5a712a759bab Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 7 Sep 2021 14:33:41 +0800 Subject: [PATCH] feat: backup module --- global.d.ts | 3 + package.json | 8 +- paw.paw | Bin 40438 -> 54612 bytes pnpm-lock.yaml | 522 ++++++++++++++++-- src/app.module.ts | 2 + src/common/adapt/fastify.ts | 10 +- src/common/decorator/http.decorator.ts | 37 +- src/common/guard/spider.guard.ts | 12 +- src/common/interceptors/cache.interceptor.ts | 1 + .../interceptors/response.interceptors.ts | 16 +- src/common/middlewares/analyze.middleware.ts | 4 +- src/constants/path.constant.ts | 6 +- src/constants/system.constant.ts | 2 + src/main.ts | 6 +- src/modules/backup/backup.controller.ts | 106 ++++ src/modules/backup/backup.module.ts | 10 + src/modules/backup/backup.service.ts | 151 +++++ src/processors/helper/helper.cron.service.ts | 103 +++- src/processors/helper/helper.module.ts | 2 + .../helper/helper.upload.service.ts | 20 + src/shared/dto/file.dto.ts | 6 + src/utils/global.util.ts | 5 + src/utils/ip.util.ts | 11 +- src/utils/system.util.ts | 3 + src/zx.global-fix.ts | 12 + tsconfig.json | 4 + 26 files changed, 993 insertions(+), 69 deletions(-) create mode 100644 src/modules/backup/backup.controller.ts create mode 100644 src/modules/backup/backup.module.ts create mode 100644 src/modules/backup/backup.service.ts create mode 100644 src/processors/helper/helper.upload.service.ts create mode 100644 src/shared/dto/file.dto.ts create mode 100644 src/utils/global.util.ts create mode 100644 src/utils/system.util.ts create mode 100644 src/zx.global-fix.ts diff --git a/global.d.ts b/global.d.ts index a59b0203..ac4e281d 100644 --- a/global.d.ts +++ b/global.d.ts @@ -1,10 +1,13 @@ import { ModelType } from '@typegoose/typegoose/lib/types' import { Document, PaginateModel } from 'mongoose' +import 'zx/globals' declare global { export type KV = Record // @ts-ignore export type MongooseModel = ModelType & PaginateModel + + export const isDev: boolean } export {} diff --git a/package.json b/package.json index 32d579a5..cd97aca7 100644 --- a/package.json +++ b/package.json @@ -60,13 +60,16 @@ "chalk": "*", "class-transformer": "^0.4.0", "class-validator": "^0.13.1", + "cos-nodejs-sdk-v5": "2.10.0", "dayjs": "^1.10.6", "dotenv": "*", "ejs": "^3.1.6", + "fastify-multipart": "^4.0.7", "fastify-swagger": "^4.9.0", "image-size": "^1.0.0", "inquirer": "*", "lodash": "*", + "mkdirp": "*", "mongoose": "*", "mongoose-lean-id": "^0.2.0", "mongoose-lean-virtuals": "^0.8.0", @@ -82,9 +85,11 @@ "reflect-metadata": "^0.1.13", "rxjs": "^7.3.0", "snakecase-keys": "^4.0.2", - "ua-parser-js": "^0.7.28" + "ua-parser-js": "^0.7.28", + "zx": "^4.1.1" }, "devDependencies": { + "rimraf": "^3.0.2", "@innei-util/eslint-config-ts": "^0.2.3", "@innei-util/prettier": "^0.1.3", "@nestjs/cli": "^8.1.1", @@ -111,7 +116,6 @@ "jest": "27.1.0", "lint-staged": "^11.1.2", "prettier": "^2.3.2", - "rimraf": "^3.0.2", "run-script-webpack-plugin": "^0.0.11", "socket.io": "*", "supertest": "^6.1.6", diff --git a/paw.paw b/paw.paw index 87dc68d6cc0f7be7d643b9732b8d4d2f70cc5a9a..d6d893f34910e9689886bffaa00ec12be1bfa8e7 100644 GIT binary patch delta 27822 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 delta 17034 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 HzMA}hQ5y3e diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4a50011b..3abdb788 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,12 +40,14 @@ specifiers: chalk: '*' class-transformer: ^0.4.0 class-validator: ^0.13.1 + cos-nodejs-sdk-v5: 2.10.0 cross-env: ^7.0.3 dayjs: ^1.10.6 dotenv: '*' ejs: ^3.1.6 eslint: ^7.32.0 fastify: '*' + fastify-multipart: ^4.0.7 fastify-swagger: ^4.9.0 husky: ^7.0.1 image-size: ^1.0.0 @@ -54,6 +56,7 @@ specifiers: jest: 27.1.0 lint-staged: ^11.1.2 lodash: '*' + mkdirp: '*' mongoose: '*' mongoose-lean-id: ^0.2.0 mongoose-lean-virtuals: ^0.8.0 @@ -82,6 +85,7 @@ specifiers: ua-parser-js: ^0.7.28 webpack: ^5.51.1 webpack-node-externals: ^3.0.0 + zx: ^4.1.1 dependencies: '@nestjs/common': 8.0.6_4d0c20d2c2a765e9ff99ebac79ad2484 @@ -105,13 +109,16 @@ dependencies: chalk: 4.1.2 class-transformer: 0.4.0 class-validator: 0.13.1 + cos-nodejs-sdk-v5: 2.10.0 dayjs: 1.10.6 dotenv: 10.0.0 ejs: 3.1.6 + fastify-multipart: 4.0.7 fastify-swagger: 4.9.1 image-size: 1.0.0 inquirer: 8.1.1 lodash: 4.17.21 + mkdirp: 1.0.4 mongoose: 5.13.8 mongoose-lean-id: 0.2.0_mongoose@5.13.8 mongoose-lean-virtuals: 0.8.0_mongoose@5.13.8 @@ -128,6 +135,7 @@ dependencies: rxjs: 7.3.0 snakecase-keys: 4.0.2 ua-parser-js: 0.7.28 + zx: 4.1.1 devDependencies: '@innei-util/eslint-config-ts': 0.2.3_typescript@4.4.2 @@ -1280,12 +1288,10 @@ packages: dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - dev: true /@nodelib/fs.stat/2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - dev: true /@nodelib/fs.walk/1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} @@ -1293,7 +1299,6 @@ packages: dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.12.0 - dev: true /@nuxtjs/opencollective/0.3.2: resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} @@ -1468,6 +1473,12 @@ packages: '@types/serve-static': 1.13.10 dev: true + /@types/fs-extra/9.0.12: + resolution: {integrity: sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw==} + dependencies: + '@types/node': 16.7.8 + dev: false + /@types/graceful-fs/4.1.5: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: @@ -1531,6 +1542,10 @@ packages: resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==} dev: true + /@types/minimist/1.2.2: + resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} + dev: false + /@types/mongodb/3.6.20: resolution: {integrity: sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==} dependencies: @@ -1550,10 +1565,21 @@ packages: '@types/node': 16.7.8 dev: true + /@types/node-fetch/2.5.12: + resolution: {integrity: sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==} + dependencies: + '@types/node': 16.7.8 + form-data: 3.0.1 + dev: false + /@types/node/10.17.60: resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} dev: false + /@types/node/14.17.14: + resolution: {integrity: sha512-rsAj2u8Xkqfc332iXV12SqIsjVi07H479bOP4q94NAcjzmAvapumEhuVIt53koEf7JFrpjgNKjBga5Pnn/GL8A==} + dev: false + /@types/node/16.7.8: resolution: {integrity: sha512-8upnoQU0OPzbIkm+ZMM0zCeFCkw2s3mS0IWdx0+AAaWqm4fkBb0UJp8Edl7FVKRamYbpJC/aVsHpKWBIbiC7Zg==} @@ -2031,6 +2057,15 @@ packages: indent-string: 4.0.0 dev: true + /ajv-formats/1.6.1: + resolution: {integrity: sha512-4CjkH20If1lhR5CGtqkrVg3bbOtFEG80X9v6jDOIUhbzzbB+UzPBGy8GQhUNVZ0yvMHdMpawCOcy5ydGMsagGQ==} + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 7.2.4 + dev: false + /ajv-formats/2.0.2: resolution: {integrity: sha512-Brah4Uo5/U8v76c6euTwtjVFFaVishwnJrQBYpev1JRh4vjA1F4HY3UzQez41YUCszUCXKagG8v6eVRBHV1gkw==} peerDependenciesMeta: @@ -2065,6 +2100,15 @@ packages: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + /ajv/7.2.4: + resolution: {integrity: sha512-nBeQgg/ZZA3u3SYxyaDvpvDtgZ/EZPF547ARgZBrG9Bhu1vKDwAIjtIf+sDtJUKa2zOcEbmRLBRSyMraS/Oy1A==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: false + /ajv/8.2.0: resolution: {integrity: sha512-WSNGFuyWd//XO8n/m/EaOlNLtO0yL8EXT/74LqT4khdhpZjP7lkj/kT5uwRmGitKEVp/Oj7ZUHeGfPtgHhQ5CA==} dependencies: @@ -2184,6 +2228,11 @@ packages: engines: {node: '>=8'} dev: true + /array-union/3.0.1: + resolution: {integrity: sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==} + engines: {node: '>=12'} + dev: false + /array.prototype.flat/1.2.3: resolution: {integrity: sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==} engines: {node: '>= 0.4'} @@ -2192,6 +2241,17 @@ packages: es-abstract: 1.18.5 dev: false + /asn1/0.2.4: + resolution: {integrity: sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==} + dependencies: + safer-buffer: 2.1.2 + dev: false + + /assert-plus/1.0.0: + resolution: {integrity: sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=} + engines: {node: '>=0.8'} + dev: false + /astral-regex/2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -2207,7 +2267,6 @@ packages: /asynckit/0.4.0: resolution: {integrity: sha1-x57Zf380y48robyXkLzDZkdLS3k=} - dev: true /at-least-node/1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} @@ -2218,6 +2277,11 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} + /atomically/1.7.0: + resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==} + engines: {node: '>=10.12.0'} + dev: false + /author-regex/1.0.0: resolution: {integrity: sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA=} engines: {node: '>=0.8'} @@ -2233,6 +2297,14 @@ packages: transitivePeerDependencies: - supports-color + /aws-sign2/0.7.0: + resolution: {integrity: sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=} + dev: false + + /aws4/1.11.0: + resolution: {integrity: sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==} + dev: false + /axios/0.21.1: resolution: {integrity: sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==} dependencies: @@ -2328,6 +2400,12 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} + /bcrypt-pbkdf/1.0.2: + resolution: {integrity: sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=} + dependencies: + tweetnacl: 0.14.5 + dev: false + /bcrypt/5.0.1: resolution: {integrity: sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==} engines: {node: '>= 10.0.0'} @@ -2377,7 +2455,6 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.0.1 - dev: true /browser-process-hrtime/1.0.0: resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} @@ -2432,6 +2509,13 @@ packages: base64-js: 1.5.1 ieee754: 1.2.1 + /busboy/0.3.1: + resolution: {integrity: sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==} + engines: {node: '>=4.5.0'} + dependencies: + dicer: 0.3.0 + dev: false + /cache-manager-ioredis/2.1.0: resolution: {integrity: sha512-TCxbp9ceuFveTKWuNaCX8QjoC41rAlHen4s63u9Yd+iXlw3efYmimc/u935PKPxSdhkXpnMes4mxtK3/yb0L4g==} engines: {node: '>=6.0.0'} @@ -2474,6 +2558,10 @@ packages: resolution: {integrity: sha512-I56jhWDGMtdILQORdusxBOH+Nl/KgQSdDmpJezYddnAkVOmnoU8zwjTV9xAjMIYxr0iPreEAVylCGcmHCjfaOw==} dev: true + /caseless/0.12.0: + resolution: {integrity: sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=} + dev: false + /chalk/2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -2649,7 +2737,6 @@ packages: engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 - dev: true /commander/2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2671,6 +2758,23 @@ packages: /concat-map/0.0.1: resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + /conf/9.0.2: + resolution: {integrity: sha512-rLSiilO85qHgaTBIIHQpsv8z+NnVfZq3cKuYNCXN1AOqPzced0GWZEe/A517VldRLyQYXUMyV+vszavE2jSAqw==} + engines: {node: '>=10'} + dependencies: + ajv: 7.2.4 + ajv-formats: 1.6.1 + atomically: 1.7.0 + debounce-fn: 4.0.0 + dot-prop: 6.0.1 + env-paths: 2.2.1 + json-schema-typed: 7.0.3 + make-dir: 3.1.0 + onetime: 5.1.2 + pkg-up: 3.1.0 + semver: 7.3.5 + dev: false + /consola/2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} dev: false @@ -2711,6 +2815,17 @@ packages: object-assign: 4.1.1 vary: 1.1.2 + /cos-nodejs-sdk-v5/2.10.0: + resolution: {integrity: sha512-FA/lrydYCQSXzo8An4vITOsADxO995Km0YY+2lcKxH161cP1ztPRXp9uTwP3y0Bv85rzmif6By1rlTduu318TQ==} + engines: {node: '>= 6'} + dependencies: + '@types/node': 14.17.14 + conf: 9.0.2 + mime-types: 2.1.32 + request: 2.88.2 + xml2js: 0.4.23 + dev: false + /cosmiconfig/6.0.0: resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} engines: {node: '>=8'} @@ -2775,6 +2890,13 @@ packages: cssom: 0.3.8 dev: true + /dashdash/1.14.1: + resolution: {integrity: sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=} + engines: {node: '>=0.10'} + dependencies: + assert-plus: 1.0.0 + dev: false + /data-urls/2.0.0: resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==} engines: {node: '>=10'} @@ -2788,6 +2910,13 @@ packages: resolution: {integrity: sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw==} dev: false + /debounce-fn/4.0.0: + resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==} + engines: {node: '>=10'} + dependencies: + mimic-fn: 3.1.0 + dev: false + /debug/2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} dependencies: @@ -2842,7 +2971,6 @@ packages: /delayed-stream/1.0.0: resolution: {integrity: sha1-3zrhmayt+31ECqrgsp4icrJOxhk=} engines: {node: '>=0.4.0'} - dev: true /delegates/1.0.0: resolution: {integrity: sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=} @@ -2872,6 +3000,13 @@ packages: engines: {node: '>=8'} dev: true + /dicer/0.3.0: + resolution: {integrity: sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==} + engines: {node: '>=4.5.0'} + dependencies: + streamsearch: 0.1.2 + dev: false + /diff-sequences/27.0.6: resolution: {integrity: sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -2887,7 +3022,6 @@ packages: engines: {node: '>=8'} dependencies: path-type: 4.0.0 - dev: true /doctrine/3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} @@ -2914,6 +3048,13 @@ packages: tslib: 2.3.1 dev: false + /dot-prop/6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + dependencies: + is-obj: 2.0.0 + dev: false + /dotenv-expand/5.1.0: resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} dev: false @@ -2923,6 +3064,17 @@ packages: engines: {node: '>=10'} dev: false + /duplexer/0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + dev: false + + /ecc-jsbn/0.1.2: + resolution: {integrity: sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=} + dependencies: + jsbn: 0.1.1 + safer-buffer: 2.1.2 + dev: false + /ecdsa-sig-formatter/1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} dependencies: @@ -2967,7 +3119,6 @@ packages: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: once: 1.4.0 - dev: true /engine.io-parser/4.0.3: resolution: {integrity: sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA==} @@ -3006,6 +3157,11 @@ packages: ansi-colors: 4.1.1 dev: true + /env-paths/2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + dev: false + /error-ex/1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -3226,6 +3382,18 @@ packages: engines: {node: '>= 0.6'} dev: false + /event-stream/3.3.4: + resolution: {integrity: sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=} + dependencies: + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.1.0 + pause-stream: 0.0.11 + split: 0.3.3 + stream-combiner: 0.0.4 + through: 2.3.8 + dev: false + /events/3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -3282,6 +3450,10 @@ packages: jest-regex-util: 27.0.6 dev: true + /extend/3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + dev: false + /external-editor/3.1.0: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} @@ -3290,6 +3462,11 @@ packages: iconv-lite: 0.4.24 tmp: 0.0.33 + /extsprintf/1.3.0: + resolution: {integrity: sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=} + engines: {'0': node >=0.6.0} + dev: false + /fast-decode-uri-component/1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} @@ -3305,7 +3482,6 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.4 - dev: true /fast-json-stable-stringify/2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -3346,6 +3522,18 @@ packages: fastify-plugin: 3.0.0 dev: false + /fastify-multipart/4.0.7: + resolution: {integrity: sha512-bV6QB3mmDYdrYwIrUrkGplutQLUUSzXFXtkCL2HRw5eLXyv9+DD5Gb5GJNDtKYCiCtyaGLbJmt2rLsF6A3WCUw==} + dependencies: + busboy: 0.3.1 + deepmerge: 4.2.2 + end-of-stream: 1.4.4 + fastify-error: 0.3.1 + fastify-plugin: 3.0.0 + hexoid: 1.0.0 + stream-wormhole: 1.1.0 + dev: false + /fastify-plugin/3.0.0: resolution: {integrity: sha512-ZdCvKEEd92DNLps5n0v231Bha8bkz1DjnPP/aEz37rz/q42Z5JVLmgnqR4DYuNn3NXAO3IDCPyRvgvxtJ4Ym4w==} dev: false @@ -3438,7 +3626,6 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: true /find-my-way/4.3.3: resolution: {integrity: sha512-5E4bRdaATB1MewjOCBjx4xvD205a4t2ripCnXB+YFhYEJ0NABtrcC7XLXLq0TPoFe/WYGUFqys3Qk3HCOGeNcw==} @@ -3449,6 +3636,13 @@ packages: safe-regex2: 2.0.0 semver-store: 0.3.0 + /find-up/3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + dependencies: + locate-path: 3.0.0 + dev: false + /find-up/4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -3482,6 +3676,10 @@ packages: optional: true dev: false + /forever-agent/0.6.1: + resolution: {integrity: sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=} + dev: false + /fork-ts-checker-webpack-plugin/6.3.1: resolution: {integrity: sha512-uxqlKTEeSJ5/JRr0zaCiw2U+kOV8F4/MhCnnRf6vbxj4ZU3Or0DLl/0CNtXro7uLWDssnuR7wUN7fU9w1I0REA==} engines: {node: '>=10', yarn: '>=1.0.0'} @@ -3501,6 +3699,15 @@ packages: tapable: 1.1.3 dev: true + /form-data/2.3.3: + resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} + engines: {node: '>= 0.12'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.32 + dev: false + /form-data/3.0.1: resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} engines: {node: '>= 6'} @@ -3508,7 +3715,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.32 - dev: true /formidable/1.2.2: resolution: {integrity: sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==} @@ -3523,6 +3729,10 @@ packages: engines: {node: '>= 0.6'} dev: false + /from/0.1.7: + resolution: {integrity: sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=} + dev: false + /fs-extra/10.0.0: resolution: {integrity: sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==} engines: {node: '>=12'} @@ -3530,7 +3740,6 @@ packages: graceful-fs: 4.2.8 jsonfile: 6.1.0 universalify: 2.0.0 - dev: true /fs-extra/9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} @@ -3621,6 +3830,12 @@ packages: engines: {node: '>=10'} dev: true + /getpass/0.1.7: + resolution: {integrity: sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=} + dependencies: + assert-plus: 1.0.0 + dev: false + /gifwrap/0.9.2: resolution: {integrity: sha512-fcIswrPaiCDAyO8xnWvHSZdWChjKXUanKKpAiWWJ/UTkEi/aYKn5+90e7DE820zbEaVR9CE2y4z9bzhQijZ0BA==} dependencies: @@ -3633,7 +3848,6 @@ packages: engines: {node: '>= 6'} dependencies: is-glob: 4.0.1 - dev: true /glob-to-regexp/0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} @@ -3680,9 +3894,34 @@ packages: slash: 3.0.0 dev: true + /globby/12.0.2: + resolution: {integrity: sha512-lAsmb/5Lww4r7MM9nCCliDZVIKbZTavrsunAsHLr9oHthrZP1qi7/gAnHOsUs9bLvEt2vKVJhHmxuL7QbDuPdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + array-union: 3.0.1 + dir-glob: 3.0.1 + fast-glob: 3.2.7 + ignore: 5.1.8 + merge2: 1.4.1 + slash: 4.0.0 + dev: false + /graceful-fs/4.2.8: resolution: {integrity: sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==} - dev: true + + /har-schema/2.0.0: + resolution: {integrity: sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=} + engines: {node: '>=4'} + dev: false + + /har-validator/5.1.5: + resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} + engines: {node: '>=6'} + deprecated: this library is no longer supported + dependencies: + ajv: 6.12.6 + har-schema: 2.0.0 + dev: false /has-bigints/1.0.1: resolution: {integrity: sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==} @@ -3717,6 +3956,11 @@ packages: dependencies: function-bind: 1.1.1 + /hexoid/1.0.0: + resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==} + engines: {node: '>=8'} + dev: false + /html-encoding-sniffer/2.0.1: resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==} engines: {node: '>=10'} @@ -3750,6 +3994,15 @@ packages: - supports-color dev: true + /http-signature/1.2.0: + resolution: {integrity: sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=} + engines: {node: '>=0.8', npm: '>=1.3.7'} + dependencies: + assert-plus: 1.0.0 + jsprim: 1.4.1 + sshpk: 1.16.1 + dev: false + /https-proxy-agent/5.0.0: resolution: {integrity: sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==} engines: {node: '>= 6'} @@ -3792,7 +4045,6 @@ packages: /ignore/5.1.8: resolution: {integrity: sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==} engines: {node: '>= 4'} - dev: true /image-q/1.1.1: resolution: {integrity: sha1-/IQJlmRGC5DKhi2TALa/u7+/gFY=} @@ -3974,7 +4226,6 @@ packages: /is-extglob/2.1.1: resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} engines: {node: '>=0.10.0'} - dev: true /is-fullwidth-code-point/1.0.0: resolution: {integrity: sha1-754xOG8DGn8NZDr4L95QxFfvAMs=} @@ -4006,7 +4257,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - dev: true /is-interactive/1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} @@ -4027,13 +4277,17 @@ packages: /is-number/7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true /is-obj/1.0.1: resolution: {integrity: sha1-PkcprB9f3gJc19g6iW2rn09n2w8=} engines: {node: '>=0.10.0'} dev: true + /is-obj/2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + dev: false + /is-potential-custom-element-name/1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} dev: true @@ -4072,7 +4326,6 @@ packages: /is-typedarray/1.0.0: resolution: {integrity: sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=} - dev: true /is-unicode-supported/0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} @@ -4084,7 +4337,10 @@ packages: /isexe/2.0.0: resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=} - dev: true + + /isstream/0.1.2: + resolution: {integrity: sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=} + dev: false /istanbul-lib-coverage/3.0.0: resolution: {integrity: sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==} @@ -4642,6 +4898,10 @@ packages: argparse: 2.0.1 dev: false + /jsbn/0.1.1: + resolution: {integrity: sha1-peZUwuWi3rXyAdls77yoDA7y9RM=} + dev: false + /jsdom/16.7.0: resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==} engines: {node: '>=10'} @@ -4714,12 +4974,23 @@ packages: /json-schema-traverse/1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - dev: true + + /json-schema-typed/7.0.3: + resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==} + dev: false + + /json-schema/0.2.3: + resolution: {integrity: sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=} + dev: false /json-stable-stringify-without-jsonify/1.0.1: resolution: {integrity: sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=} dev: true + /json-stringify-safe/5.0.1: + resolution: {integrity: sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=} + dev: false + /json5/1.0.1: resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} hasBin: true @@ -4745,7 +5016,6 @@ packages: universalify: 2.0.0 optionalDependencies: graceful-fs: 4.2.8 - dev: true /jsonwebtoken/8.5.1: resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} @@ -4763,6 +5033,16 @@ packages: semver: 5.7.1 dev: false + /jsprim/1.4.1: + resolution: {integrity: sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=} + engines: {'0': node >=0.6.0} + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.2.3 + verror: 1.10.0 + dev: false + /jwa/1.4.1: resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} dependencies: @@ -4881,6 +5161,14 @@ packages: engines: {node: '>=6.11.5'} dev: true + /locate-path/3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + dev: false + /locate-path/5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -5022,6 +5310,10 @@ packages: engines: {node: '>=8'} dev: false + /map-stream/0.1.0: + resolution: {integrity: sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=} + dev: false + /memfs/3.2.2: resolution: {integrity: sha512-RE0CwmIM3CEvpcdK3rZ19BC4E6hv9kADkMN5rPduRak58cNArWLi/9jFLsa4rhsjfVxMP3v0jO7FHXq7SvFY5Q==} engines: {node: '>= 4.0.0'} @@ -5041,7 +5333,6 @@ packages: /merge2/1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - dev: true /methods/1.1.2: resolution: {integrity: sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=} @@ -5054,7 +5345,6 @@ packages: dependencies: braces: 3.0.2 picomatch: 2.3.0 - dev: true /middie/5.3.0: resolution: {integrity: sha512-uq6Ob4dmmHeT6rJpBDWVwSxBzxzKlBvnrZdLSRJeuhHzljvZ6ccgLP/HaShgfiYrQvekRH0KUe/G1WTu/IrXsQ==} @@ -5091,6 +5381,11 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + /mimic-fn/3.1.0: + resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} + engines: {node: '>=8'} + dev: false + /min-document/2.19.0: resolution: {integrity: sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=} dependencies: @@ -5397,6 +5692,10 @@ packages: resolution: {integrity: sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==} dev: true + /oauth-sign/0.9.0: + resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} + dev: false + /object-assign/4.1.1: resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=} engines: {node: '>=0.10.0'} @@ -5534,7 +5833,6 @@ packages: engines: {node: '>=6'} dependencies: p-try: 2.2.0 - dev: true /p-limit/3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} @@ -5543,6 +5841,13 @@ packages: yocto-queue: 0.1.0 dev: true + /p-locate/3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + dependencies: + p-limit: 2.3.0 + dev: false + /p-locate/4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -5564,7 +5869,6 @@ packages: /p-try/2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - dev: true /pako/1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -5637,6 +5941,11 @@ packages: pause: 0.0.1 dev: false + /path-exists/3.0.0: + resolution: {integrity: sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=} + engines: {node: '>=4'} + dev: false + /path-exists/4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -5666,12 +5975,21 @@ packages: /path-type/4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - dev: true + + /pause-stream/0.0.11: + resolution: {integrity: sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=} + dependencies: + through: 2.3.8 + dev: false /pause/0.0.1: resolution: {integrity: sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=} dev: false + /performance-now/2.1.0: + resolution: {integrity: sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=} + dev: false + /phin/2.9.3: resolution: {integrity: sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==} dev: false @@ -5679,7 +5997,6 @@ packages: /picomatch/2.3.0: resolution: {integrity: sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==} engines: {node: '>=8.6'} - dev: true /pino-std-serializers/3.2.0: resolution: {integrity: sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==} @@ -5717,6 +6034,13 @@ packages: find-up: 4.1.0 dev: true + /pkg-up/3.1.0: + resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} + engines: {node: '>=8'} + dependencies: + find-up: 3.0.0 + dev: false + /please-upgrade-node/3.2.0: resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} dependencies: @@ -5811,9 +6135,16 @@ packages: forwarded: 0.2.0 ipaddr.js: 1.9.1 + /ps-tree/1.2.0: + resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} + engines: {node: '>= 0.10'} + hasBin: true + dependencies: + event-stream: 3.3.4 + dev: false + /psl/1.8.0: resolution: {integrity: sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==} - dev: true /pump/3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} @@ -5837,6 +6168,11 @@ packages: side-channel: 1.0.4 dev: true + /qs/6.5.2: + resolution: {integrity: sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==} + engines: {node: '>=0.6'} + dev: false + /querystring/0.2.0: resolution: {integrity: sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=} engines: {node: '>=0.4.x'} @@ -5944,6 +6280,33 @@ packages: engines: {node: '>=8'} dev: true + /request/2.88.2: + resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} + engines: {node: '>= 6'} + deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 + dependencies: + aws-sign2: 0.7.0 + aws4: 1.11.0 + caseless: 0.12.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 2.3.3 + har-validator: 5.1.5 + http-signature: 1.2.0 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.32 + oauth-sign: 0.9.0 + performance-now: 2.1.0 + qs: 6.5.2 + safe-buffer: 5.2.1 + tough-cookie: 2.5.0 + tunnel-agent: 0.6.0 + uuid: 3.4.0 + dev: false + /require-directory/2.1.1: resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} engines: {node: '>=0.10.0'} @@ -5952,7 +6315,6 @@ packages: /require-from-string/2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - dev: true /resolve-cwd/3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} @@ -6010,7 +6372,6 @@ packages: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 - dev: true /run-script-webpack-plugin/0.0.11: resolution: {integrity: sha512-QmuBhiqBPmhQLpO5vMBHVTAGyoPBnrCM5gQ3IzgieiImBXiBbXcIv4kysCT1gilFNFxQk22oKQfiIhWbT/zXCw==} @@ -6187,6 +6548,11 @@ packages: engines: {node: '>=8'} dev: true + /slash/4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + dev: false + /slice-ansi/3.0.0: resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} engines: {node: '>=8'} @@ -6306,10 +6672,32 @@ packages: dev: false optional: true + /split/0.3.3: + resolution: {integrity: sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=} + dependencies: + through: 2.3.8 + dev: false + /sprintf-js/1.0.3: resolution: {integrity: sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=} dev: true + /sshpk/1.16.1: + resolution: {integrity: sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + asn1: 0.2.4 + assert-plus: 1.0.0 + bcrypt-pbkdf: 1.0.2 + dashdash: 1.14.1 + ecc-jsbn: 0.1.2 + getpass: 0.1.7 + jsbn: 0.1.1 + safer-buffer: 2.1.2 + tweetnacl: 0.14.5 + dev: false + /stack-utils/2.0.3: resolution: {integrity: sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==} engines: {node: '>=10'} @@ -6325,6 +6713,22 @@ packages: engines: {node: '>= 0.6'} dev: false + /stream-combiner/0.0.4: + resolution: {integrity: sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=} + dependencies: + duplexer: 0.1.2 + dev: false + + /stream-wormhole/1.1.0: + resolution: {integrity: sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==} + engines: {node: '>=4.0.0'} + dev: false + + /streamsearch/0.1.2: + resolution: {integrity: sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=} + engines: {node: '>=0.8.0'} + dev: false + /string-argv/0.3.1: resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} engines: {node: '>=0.6.19'} @@ -6639,13 +7043,20 @@ packages: engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 - dev: true /toidentifier/1.0.0: resolution: {integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==} engines: {node: '>=0.6'} dev: false + /tough-cookie/2.5.0: + resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} + engines: {node: '>=0.8'} + dependencies: + psl: 1.8.0 + punycode: 2.1.1 + dev: false + /tough-cookie/4.0.0: resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==} engines: {node: '>=6'} @@ -6793,6 +7204,16 @@ packages: typescript: 4.4.2 dev: true + /tunnel-agent/0.6.0: + resolution: {integrity: sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /tweetnacl/0.14.5: + resolution: {integrity: sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=} + dev: false + /type-check/0.3.2: resolution: {integrity: sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=} engines: {node: '>= 0.8.0'} @@ -6860,7 +7281,6 @@ packages: /universalify/2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} - dev: true /uri-js/4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -6883,6 +7303,12 @@ packages: /util-deprecate/1.0.2: resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} + /uuid/3.4.0: + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} + deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + hasBin: true + dev: false + /uuid/8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -6910,6 +7336,15 @@ packages: resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=} engines: {node: '>= 0.8'} + /verror/1.10.0: + resolution: {integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=} + engines: {'0': node >=0.6.0} + dependencies: + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.3.0 + dev: false + /w3c-hr-time/1.0.2: resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} dependencies: @@ -7076,7 +7511,6 @@ packages: hasBin: true dependencies: isexe: 2.0.0 - dev: true /wide-align/1.1.3: resolution: {integrity: sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==} @@ -7230,3 +7664,21 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true + + /zx/4.1.1: + resolution: {integrity: sha512-KMC3s+Dk2IpAxvpgb0dKzLNE3NuFRz9LIVrJamLTpLtN1S3waiEsaRMYrc3SOJN/tT3V6elkwXg2xozHTeO83g==} + engines: {node: '>= 14.8.0'} + hasBin: true + dependencies: + '@types/fs-extra': 9.0.12 + '@types/minimist': 1.2.2 + '@types/node': 16.7.8 + '@types/node-fetch': 2.5.12 + chalk: 4.1.2 + fs-extra: 10.0.0 + globby: 12.0.2 + minimist: 1.2.5 + node-fetch: 2.6.1 + ps-tree: 1.2.0 + which: 2.0.2 + dev: false diff --git a/src/app.module.ts b/src/app.module.ts index 8467b823..ad24d9fb 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -20,6 +20,7 @@ import { SecurityMiddleware } from './common/middlewares/security.middleware' import { AnalyzeModule } from './modules/analyze/analyze.module' import { AuthModule } from './modules/auth/auth.module' import { RolesGuard } from './modules/auth/roles.guard' +import { BackupModule } from './modules/backup/backup.module' import { CategoryModule } from './modules/category/category.module' import { CommentModule } from './modules/comment/comment.module' import { ConfigsModule } from './modules/configs/configs.module' @@ -55,6 +56,7 @@ import { HelperModule } from './processors/helper/helper.module' AnalyzeModule, AuthModule, + BackupModule, CategoryModule, CommentModule, ConfigsModule, diff --git a/src/common/adapt/fastify.ts b/src/common/adapt/fastify.ts index cd20ae96..227b88f6 100644 --- a/src/common/adapt/fastify.ts +++ b/src/common/adapt/fastify.ts @@ -1,9 +1,17 @@ import { FastifyAdapter } from '@nestjs/platform-fastify' - +import FastifyMultipart from 'fastify-multipart' export const fastifyApp: FastifyAdapter = new FastifyAdapter({ trustProxy: true, }) +fastifyApp.register(FastifyMultipart, { + limits: { + fields: 10, // Max number of non-file fields + fileSize: 1024 * 1024 * 6, // limit size 6M + files: 5, // Max number of file fields + }, +}) + fastifyApp.getInstance().addHook('onRequest', (request, reply, done) => { const origin = request.headers.origin if (!origin) { diff --git a/src/common/decorator/http.decorator.ts b/src/common/decorator/http.decorator.ts index 695d9703..9c80854f 100644 --- a/src/common/decorator/http.decorator.ts +++ b/src/common/decorator/http.decorator.ts @@ -1,6 +1,8 @@ -import { SetMetadata } from '@nestjs/common' +import { applyDecorators, SetMetadata } from '@nestjs/common' +import { ApiBody, ApiConsumes } from '@nestjs/swagger' import { HTTP_RES_TRANSFORM_PAGINATE } from '~/constants/meta.constant' - +import * as SYSTEM from '~/constants/system.constant' +import { FileUploadDto } from '~/shared/dto/file.dto' export const Paginator: MethodDecorator = ( target, key, @@ -8,3 +10,34 @@ export const Paginator: MethodDecorator = ( ) => { SetMetadata(HTTP_RES_TRANSFORM_PAGINATE, true)(descriptor.value) } + +/** + * @description 跳过响应体处理 + */ +export const Bypass: MethodDecorator = ( + target, + key, + descriptor: PropertyDescriptor, +) => { + SetMetadata(SYSTEM.RESPONSE_PASSTHROUGH_METADATA, true)(descriptor.value) +} + +export declare interface FileDecoratorProps { + description: string +} + +export function FileUpload({ description }: FileDecoratorProps) { + return applyDecorators( + ApiConsumes('multipart/form-data'), + ApiBody({ + description, + type: FileUploadDto, + }), + ) +} + +export const HTTPDecorators = { + Paginator, + Bypass, + FileUpload, +} diff --git a/src/common/guard/spider.guard.ts b/src/common/guard/spider.guard.ts index d05b99e7..803ef29b 100644 --- a/src/common/guard/spider.guard.ts +++ b/src/common/guard/spider.guard.ts @@ -1,12 +1,8 @@ -/* - * @Author: Innei - * @Date: 2020-04-30 19:09:37 - * @LastEditTime: 2020-07-08 21:35:06 - * @LastEditors: Innei - * @FilePath: /mx-server/src/core/guards/spider.guard.ts - * @Coding with Love +/** + * @module common/guard/spider.guard + * @description 禁止爬虫的守卫 + * @author Innei */ - import { CanActivate, ExecutionContext, diff --git a/src/common/interceptors/cache.interceptor.ts b/src/common/interceptors/cache.interceptor.ts index 2a1ee31c..766ae6e5 100644 --- a/src/common/interceptors/cache.interceptor.ts +++ b/src/common/interceptors/cache.interceptor.ts @@ -3,6 +3,7 @@ * @file 缓存拦截器 * @module interceptor/cache * @author Surmon + * @author Innei */ import { diff --git a/src/common/interceptors/response.interceptors.ts b/src/common/interceptors/response.interceptors.ts index 85882159..2045a69b 100644 --- a/src/common/interceptors/response.interceptors.ts +++ b/src/common/interceptors/response.interceptors.ts @@ -16,8 +16,8 @@ import { Observable } from 'rxjs' import { map } from 'rxjs/operators' import snakecaseKeys from 'snakecase-keys' import { HTTP_RES_TRANSFORM_PAGINATE } from '~/constants/meta.constant' +import * as SYSTEM from '~/constants/system.constant' import { Paginator } from '~/shared/model/base.model' - export interface Response { data: T } @@ -29,6 +29,17 @@ export class ResponseInterceptor implements NestInterceptor> { context: ExecutionContext, next: CallHandler, ): Observable> { + const handler = context.getHandler() + + // 跳过 bypass 装饰的请求 + const bypass = this.reflector.get( + SYSTEM.RESPONSE_PASSTHROUGH_METADATA, + handler, + ) + if (bypass) { + return next.handle() + } + const reorganize = (data) => { if (!data) { throw new UnprocessableEntityException('数据丢失了(。 ́︿ ̀。)') @@ -37,13 +48,12 @@ export class ResponseInterceptor implements NestInterceptor> { ? { ...data } : { data } } - const handler = context.getHandler() return next.handle().pipe( map((data) => { if (typeof data === 'undefined') { context.switchToHttp().getResponse().status(204) - return + return data } // 分页转换 if (this.reflector.get(HTTP_RES_TRANSFORM_PAGINATE, handler)) { diff --git a/src/common/middlewares/analyze.middleware.ts b/src/common/middlewares/analyze.middleware.ts index 2dca143d..3d65e3bb 100644 --- a/src/common/middlewares/analyze.middleware.ts +++ b/src/common/middlewares/analyze.middleware.ts @@ -5,7 +5,7 @@ import { IncomingMessage, ServerResponse } from 'http' import { InjectModel } from 'nestjs-typegoose' import { UAParser } from 'ua-parser-js' import { RedisKeys } from '~/constants/cache.constant' -import { localBotListDataFilePath } from '~/constants/path.constant' +import { LOCAL_BOT_LIST_DATA_FILE_PATH } from '~/constants/path.constant' import { AnalyzeModel } from '~/modules/analyze/analyze.model' import { OptionModel } from '~/modules/configs/configs.model' import { CacheService } from '~/processors/cache/cache.service' @@ -40,7 +40,7 @@ export class AnalyzeMiddleware implements NestMiddleware { try { return this.pickPattern2Regexp( JSON.parse( - readFileSync(localBotListDataFilePath, { + readFileSync(LOCAL_BOT_LIST_DATA_FILE_PATH, { encoding: 'utf-8', }), ), diff --git a/src/constants/path.constant.ts b/src/constants/path.constant.ts index 470935e6..2232d9bc 100644 --- a/src/constants/path.constant.ts +++ b/src/constants/path.constant.ts @@ -12,4 +12,8 @@ export const DATA_DIR = isDev export const LOGGER_DIR = join(DATA_DIR, 'log') -export const localBotListDataFilePath = join(DATA_DIR, 'bot_list.json') +export const LOCAL_BOT_LIST_DATA_FILE_PATH = join(DATA_DIR, 'bot_list.json') + +export const BACKUP_DIR = !isDev + ? join(DATA_DIR, 'backup') + : join(TEMP_DIR, 'backup') diff --git a/src/constants/system.constant.ts b/src/constants/system.constant.ts index 7db35dd0..ad5b58d3 100644 --- a/src/constants/system.constant.ts +++ b/src/constants/system.constant.ts @@ -1,2 +1,4 @@ export const HTTP_ADAPTER_HOST = 'HttpAdapterHost' export const REFLECTOR = 'Reflector' + +export const RESPONSE_PASSTHROUGH_METADATA = '__responsePassthrough__' diff --git a/src/main.ts b/src/main.ts index 55760364..7c183653 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,11 +7,13 @@ import { AppModule } from './app.module' import { fastifyApp } from './common/adapt/fastify' import { SpiderGuard } from './common/guard/spider.guard' import { LoggingInterceptor } from './common/interceptors/logging.interceptor' -import { isDev } from './utils/index.util' -// const PORT = parseInt(process.env.PORT) || 2333 +import './utils/global.util' +import './zx.global-fix' + const PORT = 2333 const APIVersion = 1 const Origin = CROSS_DOMAIN.allowedOrigins + declare const module: any async function bootstrap() { diff --git a/src/modules/backup/backup.controller.ts b/src/modules/backup/backup.controller.ts new file mode 100644 index 00000000..568b0871 --- /dev/null +++ b/src/modules/backup/backup.controller.ts @@ -0,0 +1,106 @@ +import { + Controller, + Delete, + Get, + Param, + Patch, + Post, + Query, + Req, + Res, + Scope, + UnprocessableEntityException, +} from '@nestjs/common' +import { ApiProperty, ApiResponseProperty } from '@nestjs/swagger' +import { FastifyReply, FastifyRequest } from 'fastify' +import { Readable } from 'stream' +import { Auth } from '~/common/decorator/auth.decorator' +import { HTTPDecorators } from '~/common/decorator/http.decorator' +import { ApiName } from '~/common/decorator/openapi.decorator' +import { CronService } from '~/processors/helper/helper.cron.service' +import { UploadService } from '~/processors/helper/helper.upload.service' +import { BackupService } from './backup.service' + +@Controller({ path: 'backups', scope: Scope.REQUEST }) +@ApiName +@Auth() +export class BackupController { + constructor( + private readonly backupService: BackupService, + private readonly uploadService: UploadService, + private readonly cronService: CronService, + ) {} + + @Get('/new') + @ApiResponseProperty({ type: 'string', format: 'binary' }) + @HTTPDecorators.Bypass + async createNewBackup(@Res() res: FastifyReply) { + const buffer = await this.cronService.backupDB({ uploadCOS: false }) + const stream = new Readable() + + stream.push(buffer) + stream.push(null) + res + .header( + 'Content-Disposition', + `attachment; filename="backup-${new Date().toISOString()}.zip"`, + ) + .type('application/zip') + .send(stream) + } + + @Get('/') + async get() { + return this.backupService.list() + } + + @HTTPDecorators.Bypass + @Get('/:dirname') + async download(@Param('dirname') dirname: string, @Res() res: FastifyReply) { + res.send(this.backupService.getFileStream(dirname)) + } + + @Post(['/rollback/', '/']) + @ApiProperty({ description: '上传备份恢复' }) + @HTTPDecorators.FileUpload({ description: 'Upload backup and restore' }) + async uploadAndRestore(@Req() req: FastifyRequest) { + const data = await this.uploadService.validMultipartField(req) + const { mimetype } = data + if (mimetype !== 'application/zip') { + throw new UnprocessableEntityException('备份格式必须为 application/zip') + } + + await this.backupService.saveTempBackupByUpload(await data.toBuffer()) + + return + } + @Patch(['/rollback/:dirname', '/:dirname']) + async rollback(@Param('dirname') dirname: string) { + if (!dirname) { + throw new UnprocessableEntityException('参数有误') + } + + return await this.backupService.rollbackTo(dirname) + } + + @Delete('/') + async deleteBackups(@Query('files') files: string) { + if (!files) { + return + } + const _files = files.split(',') + for await (const f of _files) { + await this.backupService.deleteBackup(f) + } + return + } + + @Delete('/:filename') + async delete(@Param('filename') filename: string) { + if (!filename) { + return + } + await this.backupService.deleteBackup(filename) + return + } +} diff --git a/src/modules/backup/backup.module.ts b/src/modules/backup/backup.module.ts new file mode 100644 index 00000000..b380c390 --- /dev/null +++ b/src/modules/backup/backup.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common' +import { BackupController } from './backup.controller' +import { BackupService } from './backup.service' + +@Module({ + controllers: [BackupController], + providers: [BackupService], + exports: [BackupService], +}) +export class BackupModule {} diff --git a/src/modules/backup/backup.service.ts b/src/modules/backup/backup.service.ts new file mode 100644 index 00000000..37589489 --- /dev/null +++ b/src/modules/backup/backup.service.ts @@ -0,0 +1,151 @@ +import { + BadRequestException, + Injectable, + InternalServerErrorException, + Logger, +} from '@nestjs/common' +import { + existsSync, + readdirSync, + readFileSync, + rmSync, + statSync, + writeFileSync, +} from 'fs' +import mkdirp from 'mkdirp' +import { resolve } from 'path' +import { join } from 'path/posix' +import { Readable } from 'stream' +import { BACKUP_DIR } from '~/constants/path.constant' +import { AdminEventsGateway } from '~/processors/gateway/admin/events.gateway' +import { EventTypes } from '~/processors/gateway/events.types' +import { getFolderSize } from '~/utils/system.util' + +@Injectable() +export class BackupService { + private logger: Logger + + constructor(private readonly adminGateway: AdminEventsGateway) { + this.logger = new Logger(BackupService.name) + } + async list() { + const backupPath = BACKUP_DIR + if (!existsSync(backupPath)) { + return [] + } + const backupFilenames = readdirSync(backupPath) + const backups = [] + + for (const filename of backupFilenames) { + const path = resolve(backupPath, filename) + if (!statSync(path).isDirectory()) { + continue + } + backups.push({ + filename, + path, + }) + } + return Promise.all( + backups.map(async (item) => { + const { path } = item + const { stdout } = await getFolderSize(path) + delete item.path + return { ...item, size: stdout } + }), + ) + } + + getFileStream(dirname: string) { + const path = this.checkBackupExist(dirname) + const stream = new Readable() + + stream.push(readFileSync(path)) + stream.push(null) + + return stream + } + + checkBackupExist(dirname: string) { + const path = join(BACKUP_DIR, dirname, 'backup-' + dirname + '.zip') + if (!existsSync(path)) { + throw new BadRequestException('文件不存在') + } + return path + } + + async saveTempBackupByUpload(buffer: Buffer) { + const tempDirPath = '/tmp/mx-space/backup' + const tempBackupPath = join(tempDirPath, 'backup.zip') + mkdirp.sync(tempDirPath) + writeFileSync(tempBackupPath, buffer) + + try { + cd(tempDirPath) + await $`unzip backup.zip` + await $`mongorestore -h ${ + process.env.DB_URL || '127.0.0.1' + } -d mx-space ./mx-space --drop >/dev/null 2>&1` + + this.logger.debug('恢复成功') + await this.adminGateway.broadcast( + EventTypes.CONTENT_REFRESH, + 'restore_done', + ) + } catch (e) { + const logDir = '/tmp/mx-space/log' + mkdirp.sync(logDir) + writeFileSync(logDir, e.message, { encoding: 'utf-8', flag: 'a+' }) + throw new InternalServerErrorException(e.message) + } finally { + rmSync(tempDirPath, { recursive: true }) + } + } + + async rollbackTo(dirname: string) { + const bakFilePath = this.checkBackupExist(dirname) // zip file path + const dirPath = join(BACKUP_DIR, dirname) + try { + if (existsSync(join(join(dirPath, 'mx-space')))) { + rmSync(join(dirPath, 'mx-space'), { recursive: true }) + } + + cd(dirPath) + await $`unzip ${bakFilePath}` + } catch { + throw new InternalServerErrorException('服务端 unzip 命令未找到') + } + try { + if (!existsSync(join(dirPath, 'mx-space'))) { + throw new InternalServerErrorException('备份文件错误, 目录不存在') + } + + cd(dirPath) + await $`mongorestore -h ${ + process.env.DB_URL || '127.0.0.1' + } -d mx-space ./mx-space --drop >/dev/null 2>&1` + } catch (e) { + this.logger.error(e) + throw e + } finally { + try { + rmSync(join(dirPath, 'mx-space'), { recursive: true }) + } catch {} + } + + await this.adminGateway.broadcast( + EventTypes.CONTENT_REFRESH, + 'restore_done', + ) + } + + async deleteBackup(filename) { + const path = join(BACKUP_DIR, filename) + if (!existsSync(path)) { + throw new BadRequestException('文件不存在') + } + + rmSync(path, { recursive: true }) + return true + } +} diff --git a/src/processors/helper/helper.cron.service.ts b/src/processors/helper/helper.cron.service.ts index 602f1315..8819ff04 100644 --- a/src/processors/helper/helper.cron.service.ts +++ b/src/processors/helper/helper.cron.service.ts @@ -1,15 +1,31 @@ import { Injectable, Logger } from '@nestjs/common' import { Cron, CronExpression } from '@nestjs/schedule' -import { writeFileSync } from 'fs' -import { localBotListDataFilePath } from '~/constants/path.constant' +import COS from 'cos-nodejs-sdk-v5' +import dayjs from 'dayjs' +import { existsSync, readFileSync, writeFileSync } from 'fs' +import mkdirp from 'mkdirp' +import { join } from 'path' +import { $, cd } from 'zx' +import { + BACKUP_DIR, + LOCAL_BOT_LIST_DATA_FILE_PATH, +} from '~/constants/path.constant' +import { ConfigsService } from '~/modules/configs/configs.service' +import { isDev } from '~/utils/index.util' import { HttpService } from './helper.http.service' @Injectable() export class CronService { private logger: Logger - constructor(private readonly http: HttpService) { + constructor( + private readonly http: HttpService, + private readonly configs: ConfigsService, + ) { this.logger = new Logger(CronService.name) } - + /** + * + * @description 每天凌晨更新 Bot 列表 + */ @Cron(CronExpression.EVERY_WEEK) async updateBotList() { try { @@ -17,7 +33,7 @@ export class CronService { 'https://cdn.jsdelivr.net/gh/atmire/COUNTER-Robots@master/COUNTER_Robots_list.json', ) - writeFileSync(localBotListDataFilePath, JSON.stringify(json), { + writeFileSync(LOCAL_BOT_LIST_DATA_FILE_PATH, JSON.stringify(json), { encoding: 'utf-8', flag: 'w+', }) @@ -27,4 +43,81 @@ export class CronService { this.logger.warn('更新 Bot 列表错误') } } + + @Cron(CronExpression.EVERY_DAY_AT_1AM, { name: 'backup' }) + async backupDB({ uploadCOS = true }: { uploadCOS?: boolean } = {}) { + if (!this.configs.get('backupOptions').enable) { + return + } + this.logger.log('--> 备份数据库中') + + const dateDir = this.nowStr + + const backupDirPath = join(BACKUP_DIR, dateDir) + mkdirp.sync(backupDirPath) + try { + await $`mongodump -h 127.0.0.1 -d mx-space -o ${backupDirPath} >/dev/null 2>&1` + cd(backupDirPath) + await $`zip -r backup-${dateDir} mx-space/* && rm -r mx-space` + + this.logger.log('--> 备份成功') + } catch (e) { + if (isDev) { + console.log(e) + } + this.logger.error( + '--> 备份失败, 请确保已安装 zip 或 mongo-tools, mongo-tools 的版本需要与 mongod 版本一致', + ) + return + } + + // 开始上传 COS + process.nextTick(() => { + if (!uploadCOS) { + return + } + const backupOptions = this.configs.get('backupOptions') + if ( + !backupOptions.Bucket || + !backupOptions.Region || + !backupOptions.SecretId || + !backupOptions.SecretKey + ) { + return + } + const backupFilePath = join(backupDirPath, 'backup-' + dateDir + '.zip') + + if (!existsSync(backupFilePath)) { + this.logger.warn('文件不存在, 无法上传到 COS') + return + } + this.logger.log('--> 开始上传到 COS') + const cos = new COS({ + SecretId: backupOptions.SecretId, + SecretKey: backupOptions.SecretKey, + }) + // 分片上传 + cos.sliceUploadFile( + { + Bucket: backupOptions.Bucket, + Region: backupOptions.Region, + Key: `backup-${dateDir}.zip`, + FilePath: backupFilePath, + }, + (err) => { + if (!err) { + this.logger.log('--> 上传成功') + } else { + this.logger.error('--> 上传失败了' + err) + } + }, + ) + }) + + return readFileSync(join(backupDirPath, 'backup-' + dateDir + '.zip')) + } + + private get nowStr() { + return dayjs().format('YYYY-MM-DD-HH:mm:ss') + } } diff --git a/src/processors/helper/helper.module.ts b/src/processors/helper/helper.module.ts index e981b554..7c776fef 100644 --- a/src/processors/helper/helper.module.ts +++ b/src/processors/helper/helper.module.ts @@ -5,6 +5,7 @@ import { CronService } from './helper.cron.service' import { EmailService } from './helper.email.service' import { HttpService } from './helper.http.service' import { ImageService } from './helper.image.service' +import { UploadService } from './helper.upload.service' const providers: Provider[] = [ EmailService, @@ -12,6 +13,7 @@ const providers: Provider[] = [ ImageService, CronService, CountingService, + UploadService, ] @Module({ diff --git a/src/processors/helper/helper.upload.service.ts b/src/processors/helper/helper.upload.service.ts new file mode 100644 index 00000000..b2c975bb --- /dev/null +++ b/src/processors/helper/helper.upload.service.ts @@ -0,0 +1,20 @@ +import { BadRequestException, Injectable } from '@nestjs/common' +import { FastifyRequest } from 'fastify' +import { MultipartFile } from 'fastify-multipart' +@Injectable() +export class UploadService { + public async validMultipartField( + req: FastifyRequest, + ): Promise { + const data = await req.file() + + if (!data) { + throw new BadRequestException('仅供上传文件!') + } + if (data.fieldname != 'file') { + throw new BadRequestException('字段必须为 file') + } + + return data + } +} diff --git a/src/shared/dto/file.dto.ts b/src/shared/dto/file.dto.ts new file mode 100644 index 00000000..525e4b0f --- /dev/null +++ b/src/shared/dto/file.dto.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from '@nestjs/swagger' + +export class FileUploadDto { + @ApiProperty({ type: 'string', format: 'binary' }) + file: any +} diff --git a/src/utils/global.util.ts b/src/utils/global.util.ts new file mode 100644 index 00000000..f7037ca8 --- /dev/null +++ b/src/utils/global.util.ts @@ -0,0 +1,5 @@ +import { isDev } from './index.util' + +Object.assign(globalThis, { + isDev: isDev, +}) diff --git a/src/utils/ip.util.ts b/src/utils/ip.util.ts index 8a5534ce..8e792723 100644 --- a/src/utils/ip.util.ts +++ b/src/utils/ip.util.ts @@ -1,12 +1,7 @@ -/* - * @Author: Innei - * @Date: 2020-05-10 15:31:44 - * @LastEditTime: 2020-07-08 21:42:06 - * @LastEditors: Innei - * @FilePath: /mx-server/src/utils/ip.ts - * @Coding with Love +/** + * @module utils/ip + * @description IP utility functions */ - import type { FastifyRequest } from 'fastify' import { IncomingMessage } from 'http' export const getIp = (request: FastifyRequest | IncomingMessage) => { diff --git a/src/utils/system.util.ts b/src/utils/system.util.ts new file mode 100644 index 00000000..e537e042 --- /dev/null +++ b/src/utils/system.util.ts @@ -0,0 +1,3 @@ +export function getFolderSize(folderPath: string) { + return $`du -shc ${folderPath} | head -n 1 | cut -f1` +} diff --git a/src/zx.global-fix.ts b/src/zx.global-fix.ts new file mode 100644 index 00000000..113d7073 --- /dev/null +++ b/src/zx.global-fix.ts @@ -0,0 +1,12 @@ +// @ts-nocheck +import { registerGlobals } from 'zx' +import { isDev } from './utils/index.util' + +// FIX: zx 4.1.1 import 'zx/globals' error +// ERROR: Package subpath './globals.mjs' is not defined by "exports" in /Users/xiaoxun/github/innei-repo/mx-space/server-next/node_modules/zx/package.json +// FIXME: registerGlobals manally +registerGlobals() + +/// config for zx + +$.verbose = isDev diff --git a/tsconfig.json b/tsconfig.json index 34aa8064..53c06266 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,4 +24,8 @@ ] }, }, + "exclude": [ + "dist", + "tmp" + ] } \ No newline at end of file