├── src ├── typings.d.ts ├── interface │ ├── errorCodes.ts │ └── common.ts ├── index.ts ├── app.ts ├── routers.ts ├── controllers │ └── storage.ts └── class │ └── storage.ts ├── .dockerignore ├── .gitignore ├── Dockerfile ├── package.json ├── LICENSE ├── tsconfig.json ├── .github └── workflows │ └── docker-publish.yml ├── README.md └── pnpm-lock.yaml /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.json" { 2 | const value: any; 3 | export default value; 4 | } 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | .gitignore 4 | *.md 5 | dist 6 | storage 7 | .vscode 8 | .github -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | 23 | # 发布的二进制文件 24 | /releases 25 | /database 26 | /config 27 | /storage -------------------------------------------------------------------------------- /src/interface/errorCodes.ts: -------------------------------------------------------------------------------- 1 | const ERROR = { 2 | PermissionDenied: { 3 | code: 10001, 4 | msg: "Permission Denied" 5 | }, 6 | 7 | MaxResourceLimit: { 8 | code: 10002, 9 | msg: "Maximum resource limit" 10 | }, 11 | 12 | InvalidRequest: { 13 | code: 10003, 14 | msg: "Invalid request" 15 | }, 16 | 17 | InvalidResourceId: { 18 | code: 10004, 19 | msg: "Invalid request id" 20 | }, 21 | 22 | InvalidResourceName: { 23 | code: 10005, 24 | msg: "Invalid request name" 25 | }, 26 | 27 | InvalidResourcePath: { 28 | code: 10006, 29 | msg: "Invalid resource path" 30 | }, 31 | 32 | InvalidResourceData: { 33 | code: 10007, 34 | msg: "Invalid resource data" 35 | } 36 | }; 37 | 38 | export default ERROR; 39 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # ⚙️ 基础构建镜像:含 pnpm + 源码 2 | FROM node:22-alpine AS base 3 | ENV PNPM_HOME="/pnpm" 4 | ENV PATH="$PNPM_HOME:$PATH" 5 | RUN corepack enable pnpm 6 | WORKDIR /app 7 | COPY package.json pnpm-lock.yaml ./ 8 | 9 | # 📦 安装 prod 依赖 10 | FROM base AS prod-deps 11 | RUN --mount=type=cache,id=pnpm,target=/pnpm/store \ 12 | pnpm install --prod --frozen-lockfile 13 | 14 | # 🛠️ 构建阶段 15 | FROM base AS build 16 | RUN --mount=type=cache,id=pnpm,target=/pnpm/store \ 17 | pnpm install --frozen-lockfile 18 | COPY . . 19 | RUN pnpm run build 20 | 21 | # 🚀 最终运行镜像 22 | FROM node:22-alpine 23 | WORKDIR /app 24 | COPY --from=prod-deps /app/node_modules ./node_modules 25 | COPY --from=build /app/dist ./dist 26 | COPY --from=build /app/package.json ./package.json 27 | VOLUME ["/app/storage", "/app/config"] 28 | EXPOSE 8088 29 | CMD [ "node", "./dist/index.js" ] 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-web-simple-storage", 3 | "version": "1.0.0", 4 | "description": "Open Web Simple Storage", 5 | "main": "index.js", 6 | "author": "culturist", 7 | "license": "MIT", 8 | "scripts": { 9 | "test": "tsc && node ./dist/index.js", 10 | "watch": "tsc -w", 11 | "build": "tsc", 12 | "dev": "tsc && node ./dist/index.js", 13 | "start": "node ./dist/index.js", 14 | "init": "pnpm install && tsc && node ./dist/index.js", 15 | "clean": "rm -rf ./node_modules", 16 | "install:prod": "pnpm install --production" 17 | }, 18 | "dependencies": { 19 | "body-parser": "^2.2.0", 20 | "crypto-js": "^4.2.0", 21 | "extend": "^3.0.2", 22 | "request-ip": "^3.3.0", 23 | "restify": "^11.1.0" 24 | }, 25 | "devDependencies": { 26 | "@types/crypto-js": "^4.2.2", 27 | "@types/extend": "^3.0.4", 28 | "@types/node": "^22.14.0", 29 | "@types/request-ip": "^0.0.41", 30 | "@types/restify": "^8.5.12", 31 | "ts-node": "^10.9.2", 32 | "typescript": "^5.8.3" 33 | } 34 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 栽培者 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // tsconfig 所在的根目录, 则是一个project 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": [ 7 | "src/*" 8 | ] 9 | }, 10 | "module": "commonjs", // 模块系统 11 | "target": "es2015", // 生成目标, 一般选择ES6,因为不是客户端环境,没必要还编译成 ES5 12 | "outDir": "dist", 13 | // 一组严苛的编译选项 14 | "noImplicitAny": false, 15 | "strictNullChecks": true, 16 | "strict": true, 17 | "alwaysStrict": true, 18 | "sourceMap": false, 19 | "noImplicitReturns": true, 20 | "noImplicitThis": true, 21 | "pretty": true, 22 | "listFiles": true, // 包含了哪些库,这个必要的时候还是很有用的 23 | "listEmittedFiles": true, 24 | "lib": [ // 要那些 lib,按需选择即可 25 | "es2016" 26 | ], 27 | // "noUnusedLocals": true, 28 | // "noUnusedParameters": true, 29 | // "noFallthroughCasesInSwitch": true, 30 | // 指定库的搜索路径,这个比较有用,一般会指定 @types,还可以按需添加 31 | "typeRoots": [ 32 | "./node_modules/@types" 33 | ] 34 | // 库搜索路径下, 仅使用哪些库, 一般没啥用 35 | // "types": [ 36 | // ] 37 | }, 38 | // file include会算出一个交集, 指明哪些是项目的 ts 文件 39 | "include": [ 40 | "./**/*" 41 | ], 42 | // 排除项目下面不符合要求的文件,这个按需设定即可,可以放心排除乱七八糟的文件 43 | "exclude": [ 44 | "node_modules", 45 | "**/*.spec.ts", 46 | "*.js" 47 | ] 48 | } -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push Docker image 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | inputs: 9 | tag: 10 | description: 'Docker image tag (e.g. v1.0.0)' 11 | required: false 12 | default: 'latest' 13 | 14 | jobs: 15 | build-and-push: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v3 21 | 22 | - name: Set Docker image tag 23 | id: vars 24 | run: | 25 | # 如果是手动输入 tag 就用它,否则默认用 latest 26 | echo "tag=${{ github.event.inputs.tag || 'latest' }}" >> $GITHUB_OUTPUT 27 | 28 | - name: Log in to Docker Hub 29 | uses: docker/login-action@v2 30 | with: 31 | username: ${{ secrets.DOCKER_USERNAME }} 32 | password: ${{ secrets.DOCKER_PASSWORD }} 33 | 34 | - name: Set up Docker Buildx 35 | uses: docker/setup-buildx-action@v3 36 | 37 | - name: Build and push Docker image 38 | uses: docker/build-push-action@v5 39 | with: 40 | context: . 41 | push: true 42 | tags: ronggang/owss:${{ steps.vars.outputs.tag }} 43 | -------------------------------------------------------------------------------- /src/interface/common.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 服务器部署类型 3 | */ 4 | export enum EDeployType { 5 | Private = "Private", 6 | Public = "Public" 7 | } 8 | 9 | /** 10 | * 用户验证类型 11 | */ 12 | export enum EAuthType { 13 | OAuth2 = "OAuth2", 14 | None = "None" 15 | } 16 | 17 | export interface IAppConfig { 18 | server: { 19 | version?: string; 20 | name: string; 21 | port: number; 22 | // 启用访问白名单 23 | enableAccessWhitelist?: boolean; 24 | // 访问白名单 25 | accessWhitelist?: string[]; 26 | // 部署类型,设置为私有时,默认仅在内网可申请创建资源 27 | deployType?: EDeployType | string; 28 | }; 29 | 30 | isFirstTime?: boolean; 31 | storage: IStorageServiceConfig; 32 | } 33 | 34 | export interface IResourceOptions { 35 | authType?: EAuthType; 36 | resourceCount?: number; 37 | } 38 | 39 | export interface IStorageServiceConfig { 40 | dataPath: string; 41 | configPath: string; 42 | rootPath: string; 43 | maxResource?: number; 44 | tmpPath?: string; 45 | sharePath?: string; 46 | resourceBlacklist?: string[]; 47 | autoCleanOldResource?: boolean; 48 | } 49 | 50 | export enum EResourceOrderBy { 51 | time = "time", 52 | name = "name", 53 | size = "size" 54 | } 55 | 56 | export enum EResourceOrderMode { 57 | desc = "desc", 58 | asc = "asc" 59 | } 60 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as PATH from "path"; 2 | import * as FS from "fs"; 3 | import { IAppConfig, EDeployType } from "./interface/common"; 4 | import App from "./app"; 5 | 6 | function main() { 7 | // 由环境变量指定部署类型 8 | const deployType = process.env.DEPLOY_TYPE; 9 | const confPath = PATH.join(__dirname, "../config"); 10 | const conf = PATH.join(confPath, "config.json"); 11 | const packageFile = FS.readFileSync( 12 | PATH.join(__dirname, "../package.json"), 13 | "utf-8" 14 | ); 15 | 16 | if (!FS.existsSync(conf)) { 17 | if (!FS.existsSync(confPath)) { 18 | FS.mkdirSync(confPath, { 19 | recursive: true 20 | }); 21 | } 22 | 23 | let defaultConf: IAppConfig = { 24 | server: { 25 | port: 8088, 26 | name: "OWSS", 27 | version: "0.0.1", 28 | enableAccessWhitelist: false, 29 | deployType: deployType || EDeployType.Private 30 | }, 31 | storage: { 32 | rootPath: "storage", 33 | configPath: "conf", 34 | dataPath: "data", 35 | autoCleanOldResource: false, 36 | maxResource: 100 37 | } 38 | }; 39 | FS.writeFileSync(conf, JSON.stringify(defaultConf)); 40 | defaultConf.isFirstTime = true; 41 | new App(defaultConf); 42 | return; 43 | } 44 | 45 | try { 46 | let appConf: IAppConfig = JSON.parse(FS.readFileSync(conf, "utf-8")); 47 | if (deployType) { 48 | appConf.server.deployType = deployType; 49 | } 50 | appConf.server.version = JSON.parse(packageFile).version; 51 | new App(appConf); 52 | } catch (error) { 53 | console.log(error); 54 | } 55 | } 56 | 57 | main(); 58 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | // 导入基础库 2 | import * as restify from "restify"; 3 | import { IAppConfig } from "./interface/common"; 4 | import * as PATH from "path"; 5 | import * as FS from "fs"; 6 | import { Routers } from "./routers"; 7 | import * as extend from "extend"; 8 | import * as requestIP from "request-ip"; 9 | 10 | /** 11 | * 默认APP 12 | */ 13 | class App { 14 | public routers: Routers = new Routers(this); 15 | public config: IAppConfig = { 16 | server: { 17 | port: 8088, 18 | name: "OWSS" 19 | }, 20 | storage: { 21 | rootPath: "storage", 22 | configPath: "conf", 23 | dataPath: "data" 24 | } 25 | }; 26 | public rootPath: string = ""; 27 | 28 | constructor(config?: IAppConfig) { 29 | this.rootPath = PATH.join(__dirname, "../"); 30 | this.config.storage.tmpPath = PATH.join( 31 | this.rootPath, 32 | this.config.storage.rootPath, 33 | "tmp" 34 | ); 35 | 36 | if (!FS.existsSync(this.config.storage.tmpPath)) { 37 | FS.mkdirSync(this.config.storage.tmpPath, { recursive: true }); 38 | } 39 | 40 | this.config.storage.sharePath = PATH.join( 41 | this.rootPath, 42 | this.config.storage.rootPath, 43 | "share" 44 | ); 45 | 46 | if (!FS.existsSync(this.config.storage.sharePath)) { 47 | FS.mkdirSync(this.config.storage.sharePath, { recursive: true }); 48 | } 49 | 50 | this.config = extend(true, this.config, config); 51 | this.routers.start(); 52 | } 53 | 54 | /** 55 | * 验证ip白名单 56 | * @param req 57 | */ 58 | public isWhitelist(req: restify.Request, whitelist?: string[]): boolean { 59 | let ip = requestIP.getClientIp(req); 60 | 61 | if (!this.config.server.enableAccessWhitelist && !whitelist) { 62 | return true; 63 | } 64 | 65 | if (!ip) { 66 | return false; 67 | } 68 | 69 | if (!whitelist) { 70 | const config = this.config.server; 71 | whitelist = config.accessWhitelist; 72 | } 73 | 74 | let result = false; 75 | if (whitelist && whitelist.length > 0) { 76 | whitelist.some(item => { 77 | let rule = item.replace(/\*/g, "\\d+"); 78 | rule = rule.replace(/\./g, "\\."); 79 | let match = new RegExp(`^(${rule})$`, ""); 80 | if (match.test(ip)) { 81 | result = true; 82 | return true; 83 | } 84 | return false; 85 | }); 86 | } 87 | return result; 88 | } 89 | } 90 | 91 | export default App; 92 | -------------------------------------------------------------------------------- /src/routers.ts: -------------------------------------------------------------------------------- 1 | // 导入基础库 2 | import * as restify from "restify"; 3 | import { StorageController } from "./controllers/storage"; 4 | import App from "./app"; 5 | import ERROR from "./interface/errorCodes"; 6 | 7 | export class Routers { 8 | public server: restify.Server; 9 | constructor(public app: App) { 10 | this.server = {} as any; 11 | } 12 | 13 | /** 14 | * 挂载路由 15 | */ 16 | private mountRoutes(): void { 17 | this.server.get( 18 | "/storage", 19 | (req: restify.Request, res: restify.Response, next: restify.Next) => { 20 | res.send(200, "Welcome to use OWSS."); 21 | next(); 22 | } 23 | ); 24 | 25 | let storage = new StorageController(this.app); 26 | 27 | // 创建 28 | this.server.get( 29 | "/storage/create", 30 | (req: restify.Request, res: restify.Response, next: restify.Next) => { 31 | storage.create(req, res, next); 32 | } 33 | ); 34 | 35 | // 获取列表 36 | this.server.get( 37 | "/storage/:resourceId/list", 38 | (req: restify.Request, res: restify.Response, next: restify.Next) => { 39 | storage.list(req, res, next); 40 | } 41 | ); 42 | 43 | this.server.post( 44 | "/storage/:resourceId/add", 45 | (req: restify.Request, res: restify.Response, next: restify.Next) => { 46 | storage.add(req, res, next); 47 | } 48 | ); 49 | 50 | this.server.get( 51 | "/storage/:resourceId/get/:path", 52 | (req: restify.Request, res: restify.Response, next: restify.Next) => { 53 | storage.get(req, res, next); 54 | } 55 | ); 56 | 57 | this.server.post( 58 | "/storage/:resourceId/delete/:path", 59 | (req: restify.Request, res: restify.Response, next: restify.Next) => { 60 | storage.delete(req, res, next); 61 | } 62 | ); 63 | 64 | this.server.get( 65 | "/share/:id", 66 | (req: restify.Request, res: restify.Response, next: restify.Next) => { 67 | storage.getShare(req, res, next); 68 | } 69 | ); 70 | } 71 | 72 | /** 73 | * 初始化插件 74 | */ 75 | private initPlugins(server: restify.Server) { 76 | // 使用数据解析器 77 | server.use( 78 | restify.plugins.bodyParser({ 79 | uploadDir: this.app.config.storage.tmpPath 80 | }), 81 | restify.plugins.queryParser() 82 | ); 83 | 84 | // 设置允许跨域头信息 85 | server.use((req, res, next) => { 86 | res.header("Access-Control-Allow-Origin", "*"); 87 | res.header("Access-Control-Allow-Headers", "X-Requested-With"); 88 | next(); 89 | }); 90 | 91 | // 验证白名单 92 | server.use((req, res, next) => { 93 | if (req.socket.remoteAddress && this.app.isWhitelist(req)) { 94 | next(); 95 | } else { 96 | res.send(401, ERROR.PermissionDenied); 97 | } 98 | }); 99 | } 100 | 101 | /** 102 | * 启动服务 103 | */ 104 | public start() { 105 | let server = restify.createServer({ 106 | name: this.app.config.server.name 107 | }); 108 | 109 | this.initPlugins(server); 110 | 111 | this.server = server; 112 | this.mountRoutes(); 113 | 114 | const port = this.app.config.server.port; 115 | 116 | server.listen(port, () => { 117 | const storageConfg = this.app.config.storage; 118 | console.log( 119 | "\n%s 服务已启动 \n当前版本:%s \n本地端口:%s \n部署类型:%s \n自动清理:%s \n服务地址:%s \n%s \n使用说明:%s", 120 | this.server.name, 121 | this.app.config.server.version, 122 | port, 123 | this.app.config.server.deployType, 124 | storageConfg.autoCleanOldResource 125 | ? `已启用,每个授权码最多可存储 ${storageConfg.maxResource} 个资源` 126 | : "未启用", 127 | `http://127.0.0.1:${port}/storage`, 128 | "-----------------------------------------", 129 | "https://github.com/ronggang/OWSS" 130 | ); 131 | }); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | OWSS
3 | 4 | 5 | 6 | 7 | 8 |

9 | 10 | --- 11 | Open Web Simple Storage(OWSS),一个基于 `nodejs` 简单的 Web 存储微服务,可用于私人配置文件集中存储。 12 | 13 | ## 如何使用 14 | - 部署服务器程序; 15 | - 向已部署的服务器申请一个资源ID(授权码); 16 | - 开始使用 17 | - 创建文件; 18 | - 下载文件; 19 | - 删除文件; 20 | 21 | ## 服务器部署 22 | ### 方式一:Docker 部署 23 | - 安装 [Docker](https://docker.com) 24 | - 拉取镜像 25 | ``` shell 26 | docker pull ronggang/owss 27 | ``` 28 | - 部署示例 29 | ``` shell 30 | # Linux 31 | docker run -d -v /OWSS/storage:/app/storage -v /OWSS/config:/app/config -p 8088:8088 ronggang/owss 32 | 33 | # Windows 34 | docker run -d -v "D:/OWSS/storage":/app/storage -v "D:/OWSS/config":/app/config -p 8088:8088 ronggang/owss 35 | ``` 36 | - 数据目录 37 | - `/app/storage` : 数据存储目录; 38 | - `/app/config` : 服务运行配置目录; 39 | 40 | - 如果您需要部署到 `群晖 NAS` 里,请参考 [Synology 部署说明](https://github.com/ronggang/OWSS/wiki/Install-Synology); 41 | 42 | 43 | ### 方式二:从源码部署 44 | - 安装环境依赖: 45 | - [nodejs](https://nodejs.org) ; 46 | - [pnpm](https://pnpm.io/zh/) ; 47 | - 下载最新的发布版本,[点我前往下载](https://github.com/ronggang/OWSS/releases) ; 48 | - 解压到你想要保存的目录(如:`/OWSS/` ); 49 | - 如果您已安装 [git](https://git-scm.com) ,也可直接 `clone` 本项目: 50 | ``` 51 | git clone https://github.com/ronggang/OWSS.git 52 | ``` 53 | - 运行程序; 54 | ``` 55 | pnpm init 56 | ``` 57 | 58 | ## 首次运行说明 59 | - 首次运行时,会自动创建一个 `./config/config.json` 文件,同时生成一个授权码,请留意 `stdout` 输出内容; 60 | - 程序默认运行在 `8088` 端口下,可通过 `./config/config.json` 进行参数调整;调整后,重新运行程序即可; 61 | 62 | ## config 文件说明 63 | ``` jsonc 64 | { 65 | // 服务器配置 66 | "server": { 67 | // 服务运行的端口号 68 | "port": 8118, 69 | // 是否启用访问白名单 70 | "enableAccessWhitelist": false, 71 | // 访问白名单列表 72 | "accessWhitelist": [ 73 | "127.0.0.1", 74 | "localhost", 75 | "::1" 76 | ], 77 | // 部署类型(Private, Public) 78 | // 设置为私有时,默认仅在本机可申请创建资源,其他接口(如上传、下载)可正常使用 79 | "deployType": "Private" 80 | }, 81 | // 存储配置 82 | "storage": { 83 | // 最大资源数量 84 | "maxResource": 10, 85 | // 是否启用自动清理,如果启用该参数,每个授权码最多允许存储 `maxResource` 个文件,超过时会自动删除最老的文件 86 | "autoCleanOldResource": false 87 | } 88 | } 89 | ``` 90 | 示例: 91 | ``` json 92 | { 93 | "server": { 94 | "port": 8118, 95 | "deployType": "Private" 96 | } 97 | } 98 | ``` 99 | 100 | ## 客户端使用 101 | - 请求根路径默认为: `http(s)://ip_or_host:port/storage` ,该地址为服务地址,例: 102 | - `http://192.168.10.10:8088/storage` 103 | 104 | - 为行文方便,服务地址以下简称:`service_url/` 105 | 106 | 107 | ### GET `service_url/create` 108 | - 申请一个资源ID(`resourceId`),该资源ID用于后续所有操作; 109 | - 如果服务器部署配置为 `Private` ,那么默认情况下,仅允许 `本机` 访问创建资源; 110 | 111 | ### GET `service_url/:resourceId/list` 112 | - 列出当前资源下的符合条件的文件; 113 | - 可用查询参数: 114 | - `search`: 要过滤的关键字,默认为所有; 115 | - `orderBy`: 排序方式,可用:`time`(默认), `name`, `size` ; 116 | - `orderMode`: 排序模式,可用:`desc`(默认), `asc` ; 117 | 118 | ### POST `service_url/:resourceId/add` 119 | - 添加一个文件,需提供以下字段: 120 | - `name`: 文件名称; 121 | - `data`: 文件内容字段; 122 | - `share`: 是否共享文件,默认为 `false` 123 | - 当启用共享文件时,创建成功后会同时返回一个 `shareId` ,可根据这个 `shareId` 来访问资源,而不必暴露 `resourceId` 124 | - 访问方法:`http(s)://ip_or_host:port/share/shareId` ,如: 125 | - `http://192.168.10.10:8088/share/abc1234567890` 126 | 127 | ### GET `service_url/:resourceId/get/:path` 128 | - 获取(下载)一个文件 129 | 130 | ### POST `service_url/:resourceId/delete/:path` 131 | - 删除一个文件 132 | 133 | ### GET `service_url/share/:id` 134 | - 获取(下载)一个已共享的文件 -------------------------------------------------------------------------------- /src/controllers/storage.ts: -------------------------------------------------------------------------------- 1 | import * as restify from "restify"; 2 | import App from "../app"; 3 | import { Storage } from "../class/storage"; 4 | import { EDeployType } from "../interface/common"; 5 | import ERROR from "../interface/errorCodes"; 6 | 7 | export class StorageController { 8 | public service: Storage; 9 | constructor(public app: App) { 10 | this.service = new Storage(app); 11 | if (app.config.isFirstTime) { 12 | this.service.create().then(result => { 13 | console.log( 14 | `\n疑似首次部署,已为您自动生成一个授权码: ${result} ,请妥善保管。` 15 | ); 16 | }); 17 | } 18 | } 19 | 20 | /** 21 | * 获取指定的资源 22 | * @param req 23 | * @param res 24 | * @param next 25 | */ 26 | public get(req: restify.Request, res: restify.Response, next: restify.Next) { 27 | this.service 28 | .get(req.params.resourceId, req.params.path) 29 | .then(result => { 30 | res.send(200, result, { 31 | "Content-Type": "application/octet-stream", 32 | "Content-Disposition": 33 | "attachment; filename=" + (req.params.name || req.params.path) 34 | }); 35 | }) 36 | .catch(err => { 37 | res.json({ 38 | error: err 39 | }); 40 | }).finally(()=>{ 41 | next(); 42 | }); 43 | } 44 | 45 | /** 46 | * 获取共享资源 47 | * @param req 48 | * @param res 49 | * @param next 50 | */ 51 | public getShare(req: restify.Request, res: restify.Response, next: restify.Next) { 52 | this.service 53 | .getShare(req.params.id) 54 | .then(result => { 55 | res.send(200, result, { 56 | "Content-Type": "application/octet-stream", 57 | "Content-Disposition": 58 | "attachment; filename=" + (req.params.name || req.params.id) 59 | }); 60 | }) 61 | .catch(err => { 62 | res.json({ 63 | error: err 64 | }); 65 | }).finally(()=>{ 66 | next(); 67 | }); 68 | } 69 | 70 | /** 71 | * 获取资源列表 72 | * @param req 73 | * @param res 74 | * @param next 75 | */ 76 | public list(req: restify.Request, res: restify.Response, next: restify.Next) { 77 | this.service 78 | .list( 79 | req.params.resourceId, 80 | req.query.search, 81 | req.query.page, 82 | req.query.pageSize, 83 | req.query.orderBy, 84 | req.query.orderMode 85 | ) 86 | .then(result => { 87 | res.json({ 88 | data: result 89 | }); 90 | }) 91 | .catch(err => { 92 | res.json({ 93 | error: err 94 | }); 95 | }).finally(()=>{ 96 | next(); 97 | }); 98 | } 99 | 100 | /** 101 | * 创建资源 102 | * @param req 103 | * @param res 104 | * @param next 105 | */ 106 | public create( 107 | req: restify.Request, 108 | res: restify.Response, 109 | next: restify.Next 110 | ) { 111 | console.log("create", req.socket.remoteAddress); 112 | if (this.app.config.server.deployType === EDeployType.Private) { 113 | const whiteList = ["::1", "127.0.0.1", "localhost"]; 114 | 115 | // 如果有定义白名单,不管是否已启用,都允许创建资源 116 | if ( 117 | this.app.config.server.accessWhitelist && 118 | this.app.config.server.accessWhitelist.length > 0 119 | ) { 120 | whiteList.push(...this.app.config.server.accessWhitelist); 121 | } 122 | if (!this.app.isWhitelist(req, whiteList)) { 123 | res.send(401, ERROR.PermissionDenied); 124 | return next(); 125 | } 126 | } 127 | 128 | this.service 129 | .create() 130 | .then(result => { 131 | // 输出一下已创建的资源ID 132 | console.log("new id: %s", result); 133 | res.json({ 134 | data: result 135 | }); 136 | }) 137 | .catch(err => { 138 | res.json({ 139 | error: err 140 | }); 141 | }).finally(()=>{ 142 | next(); 143 | }); 144 | } 145 | 146 | /** 147 | * 增加资源 148 | * @param req 149 | * @param res 150 | * @param next 151 | */ 152 | public add(req: restify.Request, res: restify.Response, next: restify.Next) { 153 | if (req.files && req.files.data) { 154 | this.service 155 | .add(req.params.resourceId, req.params.name, req.files.data.path, req.params.share) 156 | .then(result => { 157 | res.json({ 158 | data: result 159 | }); 160 | }) 161 | .catch(err => { 162 | res.json({ 163 | error: err 164 | }); 165 | }).finally(()=>{ 166 | next(); 167 | }); 168 | } else if (req.params.type == "dir") { 169 | this.service 170 | .addFolder(req.params.resourceId, req.params.name) 171 | .then(result => { 172 | res.json({ 173 | data: result 174 | }); 175 | }) 176 | .catch(err => { 177 | res.json({ 178 | error: err 179 | }); 180 | }).finally(()=>{ 181 | next(); 182 | }); 183 | } else { 184 | res.send(400, ERROR.InvalidRequest); 185 | next(); 186 | } 187 | } 188 | 189 | /** 190 | * 删除指定的文件或目录 191 | * @param req 192 | * @param res 193 | * @param next 194 | */ 195 | public delete( 196 | req: restify.Request, 197 | res: restify.Response, 198 | next: restify.Next 199 | ) { 200 | this.service 201 | .delete(req.params.resourceId, req.params.path) 202 | .then(result => { 203 | res.json({ 204 | data: result 205 | }); 206 | }) 207 | .catch(err => { 208 | res.json({ 209 | error: err 210 | }); 211 | }).finally(()=>{ 212 | next(); 213 | }); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/class/storage.ts: -------------------------------------------------------------------------------- 1 | import * as PATH from "path"; 2 | import * as FS from "fs"; 3 | import * as CryptoJS from "crypto-js"; 4 | import App from "../app"; 5 | import { 6 | IStorageServiceConfig, 7 | IResourceOptions, 8 | EResourceOrderBy, 9 | EResourceOrderMode 10 | } from "../interface/common"; 11 | import ERROR from "../interface/errorCodes"; 12 | 13 | export class Storage { 14 | private rootPath: string = ""; 15 | public config: IStorageServiceConfig = { 16 | dataPath: "data", 17 | configPath: "conf", 18 | rootPath: "storage", 19 | maxResource: 100, 20 | autoCleanOldResource: false 21 | }; 22 | 23 | constructor(app: App) { 24 | this.config = Object.assign(this.config, app.config.storage); 25 | this.rootPath = PATH.resolve(app.rootPath, this.config.rootPath); 26 | } 27 | 28 | /** 29 | * 创建一个资源目录 30 | */ 31 | public create(options?: IResourceOptions): Promise { 32 | return new Promise((resolve?: any, reject?: any) => { 33 | const id = this.getNewId(); 34 | this.createConfig(id, options); 35 | 36 | resolve(id); 37 | }); 38 | } 39 | 40 | /** 41 | * 创建资源配置信息 42 | * @param resourceId 43 | * @param options 44 | */ 45 | private createConfig(resourceId: string, options?: IResourceOptions) { 46 | const configFile = this.getConfigFile(resourceId); 47 | const configPath = PATH.parse(configFile).dir; 48 | const dataPath = this.getDataDirectory(resourceId); 49 | FS.mkdirSync(configPath, { recursive: true }); 50 | FS.mkdirSync(dataPath, { recursive: true }); 51 | FS.writeFileSync( 52 | configFile, 53 | JSON.stringify( 54 | Object.assign({ authType: "none", resourceCount: 0 }, options) 55 | ) 56 | ); 57 | } 58 | 59 | private getConfigFile(resourceId: string): string { 60 | return PATH.join( 61 | this.rootPath, 62 | this.config.configPath, 63 | resourceId.substr(0, 1), 64 | `${resourceId}.json` 65 | ); 66 | } 67 | 68 | private getConfig(resourceId: string): IResourceOptions { 69 | const path = this.getConfigFile(resourceId); 70 | 71 | const result = FS.readFileSync(path, "utf-8"); 72 | return JSON.parse(result); 73 | } 74 | 75 | /** 76 | * 获取当前资源目录 77 | * @param resourceId 78 | */ 79 | private getDataDirectory(resourceId: string): string { 80 | return PATH.join( 81 | this.rootPath, 82 | this.config.dataPath, 83 | resourceId.substr(0, 1), 84 | resourceId 85 | ); 86 | } 87 | 88 | public get(resourceId: string, path: string): Promise { 89 | return new Promise((resolve?: any, reject?: any) => { 90 | if (!this.checkResourceId(resourceId)) { 91 | reject(ERROR.InvalidResourceId); 92 | return; 93 | } 94 | 95 | const currentDirectory = this.getDataDirectory(resourceId); 96 | const fullPath = PATH.join(currentDirectory, path); 97 | 98 | if (FS.existsSync(fullPath)) { 99 | FS.readFile(fullPath, (err, data) => { 100 | resolve(data); 101 | }); 102 | } else { 103 | reject(ERROR.InvalidResourceName); 104 | } 105 | }); 106 | } 107 | 108 | public getShare(shareId: string): Promise { 109 | return new Promise((resolve?: any, reject?: any) => { 110 | if (!this.config.sharePath) { 111 | reject(null); 112 | return; 113 | } 114 | 115 | const sharePath = PATH.join(this.config.sharePath, shareId); 116 | 117 | if (FS.existsSync(sharePath)) { 118 | const filePath = FS.readFileSync(sharePath); 119 | FS.readFile(filePath, (err, data) => { 120 | resolve(data); 121 | }); 122 | } else { 123 | reject(ERROR.InvalidResourceId); 124 | } 125 | }); 126 | } 127 | 128 | /** 129 | * 获取指定路径下的资源列表 130 | * @param path 路径 131 | * @param search 搜索关键字 132 | * @param page 页码 133 | * @param pageSize 需要显示的数量 134 | * @param orderBy 排序字段 135 | * @param orderMode 排序模式(asc, desc) 136 | */ 137 | private getResourceList( 138 | path: string, 139 | search: string = "", 140 | page: number = 1, 141 | pageSize: number = 10, 142 | orderBy: EResourceOrderBy = EResourceOrderBy.time, 143 | orderMode: EResourceOrderMode = EResourceOrderMode.desc 144 | ) { 145 | const startIndex = (page - 1) * pageSize; 146 | return FS.readdirSync(path) 147 | .filter(name => { 148 | return name.indexOf(search) !== -1; 149 | }) 150 | .map(name => { 151 | const stat = FS.statSync(PATH.join(path, name)); 152 | 153 | return { 154 | name, 155 | time: stat.mtime.getTime(), 156 | type: stat.isDirectory() ? "directory" : "file", 157 | size: stat.size 158 | }; 159 | }) 160 | .sort((a, b) => { 161 | let v1, v2; 162 | switch (orderBy) { 163 | case EResourceOrderBy.name: 164 | v1 = a.name; 165 | v2 = b.name; 166 | break; 167 | 168 | case EResourceOrderBy.size: 169 | v1 = a.size; 170 | v2 = b.size; 171 | break; 172 | 173 | default: 174 | v1 = a.time; 175 | v2 = b.time; 176 | break; 177 | } 178 | 179 | if (orderMode === EResourceOrderMode.desc) { 180 | return v1.toString().localeCompare(v2) == 1 ? -1 : 1; 181 | } else { 182 | return v1.toString().localeCompare(v2); 183 | } 184 | }) 185 | .slice(startIndex, pageSize === -1 ? undefined : startIndex + pageSize); 186 | } 187 | 188 | public list( 189 | resourceId: string, 190 | search: string = "", 191 | page: number = 1, 192 | pageSize: number = 10, 193 | orderBy: EResourceOrderBy = EResourceOrderBy.time, 194 | orderMode: EResourceOrderMode = EResourceOrderMode.desc 195 | ): Promise { 196 | return new Promise((resolve?: any, reject?: any) => { 197 | if (!this.checkResourceId(resourceId)) { 198 | reject(ERROR.InvalidResourceId); 199 | return; 200 | } 201 | const currentDirectory = this.getDataDirectory(resourceId); 202 | const result = this.getResourceList( 203 | currentDirectory, 204 | search, 205 | page, 206 | pageSize, 207 | orderBy, 208 | orderMode 209 | ); 210 | 211 | resolve(result); 212 | }); 213 | } 214 | 215 | /** 216 | * 删除指定的文件或目录 217 | */ 218 | public delete(resourceId: string, path: string): Promise { 219 | return new Promise((resolve?: any, reject?: any) => { 220 | if (!this.checkResourceId(resourceId)) { 221 | reject(ERROR.InvalidResourceId); 222 | return; 223 | } 224 | 225 | if (!path) { 226 | reject(ERROR.InvalidResourcePath); 227 | return; 228 | } 229 | 230 | const currentDirectory = this.getDataDirectory(resourceId); 231 | const fullPath = PATH.join(currentDirectory, path); 232 | const stat = FS.statSync(fullPath); 233 | 234 | if (stat.isDirectory()) { 235 | FS.rmdir(fullPath, error => { 236 | if (!error) { 237 | this.resourceCountDecrement(resourceId); 238 | resolve(true); 239 | } else { 240 | reject(error); 241 | } 242 | }); 243 | } else { 244 | FS.unlink(fullPath, error => { 245 | if (!error) { 246 | this.resourceCountDecrement(resourceId); 247 | resolve(true); 248 | } else { 249 | reject(error); 250 | } 251 | }); 252 | } 253 | }); 254 | } 255 | 256 | /** 257 | * 添加文件 258 | * @param resourceId 259 | * @param name 260 | * @param data 261 | */ 262 | public add(resourceId: string, name: string, data: any, share: boolean=false): Promise { 263 | return new Promise((resolve?: any, reject?: any) => { 264 | if (!this.checkResourceId(resourceId)) { 265 | reject(ERROR.InvalidResourceId); 266 | return; 267 | } 268 | 269 | if (!name) { 270 | reject(ERROR.InvalidResourceName); 271 | return; 272 | } 273 | 274 | if (!data) { 275 | reject(ERROR.InvalidResourceData); 276 | return; 277 | } 278 | 279 | const config = this.getConfig(resourceId); 280 | if (config.resourceCount === undefined) { 281 | this.resetResourceCount(resourceId); 282 | } 283 | 284 | this.cleanOldResource(resourceId); 285 | 286 | // 替换非法文件名称 287 | name = name.replace(/(<|>|\/|\||\\|\:|\"|\*|\?)/g, "_"); 288 | 289 | const currentDirectory = this.getDataDirectory(resourceId); 290 | const path = PATH.join(currentDirectory, name); 291 | 292 | try { 293 | if (PATH.isAbsolute(data)) { 294 | FS.renameSync(data, path) 295 | } else { 296 | FS.writeFileSync(path, data); 297 | } 298 | 299 | this.resourceCountIncrement(resourceId); 300 | 301 | // 是否共享 302 | if (share && this.config.sharePath) { 303 | const shareId = this.getNewId(); 304 | const sharePath = PATH.join(this.config.sharePath, shareId); 305 | FS.writeFileSync(sharePath, path); 306 | resolve({ 307 | shareId 308 | }) 309 | return; 310 | } 311 | 312 | resolve(true); 313 | } catch (error) { 314 | reject(error) 315 | } 316 | }); 317 | } 318 | 319 | public addFolder(resourceId: string, path: string): Promise { 320 | return new Promise((resolve?: any, reject?: any) => { 321 | if (!this.checkResourceId(resourceId)) { 322 | reject(ERROR.InvalidResourceId); 323 | return; 324 | } 325 | 326 | if (!path) { 327 | reject(ERROR.InvalidResourcePath); 328 | return; 329 | } 330 | 331 | const currentDirectory = this.getDataDirectory(resourceId); 332 | const fullPath = PATH.join(currentDirectory, path); 333 | 334 | FS.mkdirSync(fullPath, { recursive: true }); 335 | 336 | resolve(true); 337 | }); 338 | } 339 | 340 | public checkResourceId(resourceId: string): boolean { 341 | if (/^[a-z0-9]{32}$/.test(resourceId)) { 342 | return FS.existsSync(this.getDataDirectory(resourceId)); 343 | } 344 | return false; 345 | } 346 | 347 | private getNewId() { 348 | const chars = 349 | "abcdefghijkmnopqrstuvwxyz0123456789ABCDEFGHIJKMNOPQRSTUVWXYZ"; 350 | const maxLength = chars.length; 351 | const result: string[] = []; 352 | for (let i = 0; i < 32; i++) { 353 | result.push(chars.charAt(Math.floor(Math.random() * maxLength))); 354 | } 355 | 356 | return CryptoJS.MD5( 357 | new Date().getTime().toString() + result.join("") 358 | ).toString(); 359 | } 360 | 361 | /** 362 | * 重置指定资源的文件数量(不包含子目录) 363 | * @param resourceId 364 | */ 365 | public resetResourceCount(resourceId: string) { 366 | const config = this.getConfig(resourceId); 367 | if (config.resourceCount === undefined) { 368 | config.resourceCount = 0; 369 | } 370 | 371 | const configFile = this.getConfigFile(resourceId); 372 | 373 | const currentDirectory = this.getDataDirectory(resourceId); 374 | 375 | config.resourceCount = FS.readdirSync(currentDirectory, { 376 | withFileTypes: true 377 | }).filter(item => { 378 | return item.isFile(); 379 | }).length; 380 | 381 | FS.writeFileSync(configFile, JSON.stringify(config)); 382 | } 383 | 384 | public resourceCountIncrement(resourceId: string): number { 385 | const config = this.getConfig(resourceId); 386 | if (config.resourceCount === undefined) { 387 | config.resourceCount = 0; 388 | } 389 | 390 | const configFile = this.getConfigFile(resourceId); 391 | 392 | config.resourceCount++; 393 | 394 | FS.writeFileSync(configFile, JSON.stringify(config)); 395 | 396 | return config.resourceCount; 397 | } 398 | 399 | public resourceCountDecrement(resourceId: string, count: number = 1): number { 400 | const config = this.getConfig(resourceId); 401 | if (config.resourceCount === undefined) { 402 | config.resourceCount = 0; 403 | } 404 | 405 | const configFile = this.getConfigFile(resourceId); 406 | 407 | config.resourceCount = config.resourceCount - count; 408 | if (config.resourceCount < 0) { 409 | config.resourceCount = 0; 410 | } 411 | 412 | FS.writeFileSync(configFile, JSON.stringify(config)); 413 | 414 | return config.resourceCount; 415 | } 416 | 417 | /** 418 | * 清理老文件 419 | * @param resourceId 420 | */ 421 | private cleanOldResource(resourceId: string) { 422 | if (!this.config.maxResource || !this.config.autoCleanOldResource) { 423 | return; 424 | } 425 | 426 | const config = this.getConfig(resourceId); 427 | 428 | if (!config.resourceCount) { 429 | return; 430 | } 431 | 432 | // 如果超出最大数量,则删除最老的文件 433 | if (config.resourceCount >= this.config.maxResource) { 434 | const cleanCount = config.resourceCount - this.config.maxResource + 1; 435 | const currentDirectory = this.getDataDirectory(resourceId); 436 | const list = this.getResourceList( 437 | currentDirectory, 438 | "", 439 | 1, 440 | cleanCount, 441 | EResourceOrderBy.time, 442 | EResourceOrderMode.asc 443 | ); 444 | 445 | if (list.length > 0) { 446 | list.forEach(item => { 447 | const fullPath = PATH.join(currentDirectory, item.name); 448 | FS.unlinkSync(fullPath); 449 | }); 450 | 451 | this.resourceCountDecrement(resourceId, cleanCount); 452 | } 453 | } 454 | } 455 | } 456 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | body-parser: 12 | specifier: ^2.2.0 13 | version: 2.2.0 14 | crypto-js: 15 | specifier: ^4.2.0 16 | version: 4.2.0 17 | extend: 18 | specifier: ^3.0.2 19 | version: 3.0.2 20 | request-ip: 21 | specifier: ^3.3.0 22 | version: 3.3.0 23 | restify: 24 | specifier: ^11.1.0 25 | version: 11.1.0 26 | devDependencies: 27 | '@types/crypto-js': 28 | specifier: ^4.2.2 29 | version: 4.2.2 30 | '@types/extend': 31 | specifier: ^3.0.4 32 | version: 3.0.4 33 | '@types/node': 34 | specifier: ^22.14.0 35 | version: 22.14.0 36 | '@types/request-ip': 37 | specifier: ^0.0.41 38 | version: 0.0.41 39 | '@types/restify': 40 | specifier: ^8.5.12 41 | version: 8.5.12 42 | ts-node: 43 | specifier: ^10.9.2 44 | version: 10.9.2(@types/node@22.14.0)(typescript@5.8.3) 45 | typescript: 46 | specifier: ^5.8.3 47 | version: 5.8.3 48 | 49 | packages: 50 | 51 | '@cspotcode/source-map-support@0.8.1': 52 | resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} 53 | engines: {node: '>=12'} 54 | 55 | '@jridgewell/resolve-uri@3.1.2': 56 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 57 | engines: {node: '>=6.0.0'} 58 | 59 | '@jridgewell/sourcemap-codec@1.5.0': 60 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 61 | 62 | '@jridgewell/trace-mapping@0.3.9': 63 | resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} 64 | 65 | '@netflix/nerror@1.1.3': 66 | resolution: {integrity: sha512-b+MGNyP9/LXkapreJzNUzcvuzZslj/RGgdVVJ16P2wSlYatfLycPObImqVJSmNAdyeShvNeM/pl3sVZsObFueg==} 67 | 68 | '@tsconfig/node10@1.0.11': 69 | resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} 70 | 71 | '@tsconfig/node12@1.0.11': 72 | resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} 73 | 74 | '@tsconfig/node14@1.0.3': 75 | resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} 76 | 77 | '@tsconfig/node16@1.0.4': 78 | resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} 79 | 80 | '@types/bunyan@1.8.11': 81 | resolution: {integrity: sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==} 82 | 83 | '@types/crypto-js@4.2.2': 84 | resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} 85 | 86 | '@types/extend@3.0.4': 87 | resolution: {integrity: sha512-ArMouDUTJEz1SQRpFsT2rIw7DeqICFv5aaVzLSIYMYQSLcwcGOfT3VyglQs/p7K3F7fT4zxr0NWxYZIdifD6dA==} 88 | 89 | '@types/formidable@1.2.8': 90 | resolution: {integrity: sha512-6psvrUy5VDYb+yaPJReF1WrRsz+FBwyJutK9Twz1Efa27tm07bARNIkK2B8ZPWq80dXqpKfrxTO96xrtPp+AuA==} 91 | 92 | '@types/node@22.14.0': 93 | resolution: {integrity: sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==} 94 | 95 | '@types/request-ip@0.0.41': 96 | resolution: {integrity: sha512-Qzz0PM2nSZej4lsLzzNfADIORZhhxO7PED0fXpg4FjXiHuJ/lMyUg+YFF5q8x9HPZH3Gl6N+NOM8QZjItNgGKg==} 97 | 98 | '@types/restify@8.5.12': 99 | resolution: {integrity: sha512-tW9y4O/B0gNIhin/88PzLTPgolkzOOrFK0IfgdiiYjGgjXI+PDTwp7QThhQy0p0l5FuNy59NW2PXU3Qx3r5cOQ==} 100 | 101 | '@types/spdy@3.4.9': 102 | resolution: {integrity: sha512-ZYGzQrZX6j4GEy9x9+e4bjWOPOiZqFPm/3GnZ9xS0SMJqs+FbNiF6hBzr8EjSF2Z6TFY/PzUWOswUiRcZxW3FQ==} 103 | 104 | abort-controller@3.0.0: 105 | resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} 106 | engines: {node: '>=6.5'} 107 | 108 | acorn-walk@8.3.4: 109 | resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} 110 | engines: {node: '>=0.4.0'} 111 | 112 | acorn@8.14.1: 113 | resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} 114 | engines: {node: '>=0.4.0'} 115 | hasBin: true 116 | 117 | arg@4.1.3: 118 | resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} 119 | 120 | asn1@0.2.6: 121 | resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} 122 | 123 | assert-plus@1.0.0: 124 | resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} 125 | engines: {node: '>=0.8'} 126 | 127 | atomic-sleep@1.0.0: 128 | resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} 129 | engines: {node: '>=8.0.0'} 130 | 131 | base64-js@1.5.1: 132 | resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} 133 | 134 | bcrypt-pbkdf@1.0.2: 135 | resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} 136 | 137 | body-parser@2.2.0: 138 | resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} 139 | engines: {node: '>=18'} 140 | 141 | buffer@6.0.3: 142 | resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} 143 | 144 | bytes@3.1.2: 145 | resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} 146 | engines: {node: '>= 0.8'} 147 | 148 | call-bind-apply-helpers@1.0.2: 149 | resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} 150 | engines: {node: '>= 0.4'} 151 | 152 | call-bound@1.0.4: 153 | resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} 154 | engines: {node: '>= 0.4'} 155 | 156 | content-type@1.0.5: 157 | resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} 158 | engines: {node: '>= 0.6'} 159 | 160 | core-util-is@1.0.2: 161 | resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} 162 | 163 | core-util-is@1.0.3: 164 | resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} 165 | 166 | create-require@1.1.1: 167 | resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} 168 | 169 | crypto-js@4.2.0: 170 | resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} 171 | 172 | csv-generate@4.4.2: 173 | resolution: {integrity: sha512-W6nVsf+rz0J3yo9FOjeer7tmzBJKaTTxf7K0uw6GZgRocZYPVpuSWWa5/aoWWrjQZj4/oNIKTYapOM7hiNjVMA==} 174 | 175 | csv-parse@5.6.0: 176 | resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==} 177 | 178 | csv-stringify@6.5.2: 179 | resolution: {integrity: sha512-RFPahj0sXcmUyjrObAK+DOWtMvMIFV328n4qZJhgX3x2RqkQgOTU2mCUmiFR0CzM6AzChlRSUErjiJeEt8BaQA==} 180 | 181 | csv@6.3.11: 182 | resolution: {integrity: sha512-a8bhT76Q546jOElHcTrkzWY7Py925mfLO/jqquseH61ThOebYwOjLbWHBqdRB4K1VpU36sTyIei6Jwj7QdEZ7g==} 183 | engines: {node: '>= 0.1.90'} 184 | 185 | dashdash@1.14.1: 186 | resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} 187 | engines: {node: '>=0.10'} 188 | 189 | debug@2.6.9: 190 | resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} 191 | peerDependencies: 192 | supports-color: '*' 193 | peerDependenciesMeta: 194 | supports-color: 195 | optional: true 196 | 197 | debug@4.4.0: 198 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 199 | engines: {node: '>=6.0'} 200 | peerDependencies: 201 | supports-color: '*' 202 | peerDependenciesMeta: 203 | supports-color: 204 | optional: true 205 | 206 | depd@2.0.0: 207 | resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} 208 | engines: {node: '>= 0.8'} 209 | 210 | destroy@1.2.0: 211 | resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} 212 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 213 | 214 | detect-node@2.1.0: 215 | resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} 216 | 217 | diff@4.0.2: 218 | resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} 219 | engines: {node: '>=0.3.1'} 220 | 221 | dtrace-provider@0.8.8: 222 | resolution: {integrity: sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==} 223 | engines: {node: '>=0.10'} 224 | 225 | dunder-proto@1.0.1: 226 | resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} 227 | engines: {node: '>= 0.4'} 228 | 229 | ecc-jsbn@0.1.2: 230 | resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} 231 | 232 | ee-first@1.1.1: 233 | resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} 234 | 235 | encodeurl@1.0.2: 236 | resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} 237 | engines: {node: '>= 0.8'} 238 | 239 | es-define-property@1.0.1: 240 | resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} 241 | engines: {node: '>= 0.4'} 242 | 243 | es-errors@1.3.0: 244 | resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 245 | engines: {node: '>= 0.4'} 246 | 247 | es-object-atoms@1.1.1: 248 | resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} 249 | engines: {node: '>= 0.4'} 250 | 251 | escape-html@1.0.3: 252 | resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} 253 | 254 | escape-regexp-component@1.0.2: 255 | resolution: {integrity: sha512-B0yxafj1D1ZTNEHkFoQxz4iboZSfaZHhaNhIug7GcUCL4ZUrVSJZTmWUAkPOFaYDfi3RNT9XM082TuGE6jpmiQ==} 256 | 257 | etag@1.8.1: 258 | resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} 259 | engines: {node: '>= 0.6'} 260 | 261 | event-target-shim@5.0.1: 262 | resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} 263 | engines: {node: '>=6'} 264 | 265 | events@3.3.0: 266 | resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} 267 | engines: {node: '>=0.8.x'} 268 | 269 | ewma@2.0.1: 270 | resolution: {integrity: sha512-MYYK17A76cuuyvkR7MnqLW4iFYPEi5Isl2qb8rXiWpLiwFS9dxW/rncuNnjjgSENuVqZQkIuR4+DChVL4g1lnw==} 271 | 272 | extend@3.0.2: 273 | resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} 274 | 275 | extsprintf@1.3.0: 276 | resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} 277 | engines: {'0': node >=0.6.0} 278 | 279 | extsprintf@1.4.1: 280 | resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==} 281 | engines: {'0': node >=0.6.0} 282 | 283 | fast-decode-uri-component@1.0.1: 284 | resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} 285 | 286 | fast-deep-equal@3.1.3: 287 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 288 | 289 | fast-querystring@1.1.2: 290 | resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} 291 | 292 | fast-redact@3.5.0: 293 | resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} 294 | engines: {node: '>=6'} 295 | 296 | find-my-way@7.7.0: 297 | resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} 298 | engines: {node: '>=14'} 299 | 300 | formidable@1.2.6: 301 | resolution: {integrity: sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==} 302 | deprecated: 'Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau' 303 | 304 | fresh@0.5.2: 305 | resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} 306 | engines: {node: '>= 0.6'} 307 | 308 | function-bind@1.1.2: 309 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 310 | 311 | get-intrinsic@1.3.0: 312 | resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} 313 | engines: {node: '>= 0.4'} 314 | 315 | get-proto@1.0.1: 316 | resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} 317 | engines: {node: '>= 0.4'} 318 | 319 | getpass@0.1.7: 320 | resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} 321 | 322 | gopd@1.2.0: 323 | resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} 324 | engines: {node: '>= 0.4'} 325 | 326 | handle-thing@2.0.1: 327 | resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} 328 | 329 | has-symbols@1.1.0: 330 | resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} 331 | engines: {node: '>= 0.4'} 332 | 333 | hasown@2.0.2: 334 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 335 | engines: {node: '>= 0.4'} 336 | 337 | hpack.js@2.1.6: 338 | resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} 339 | 340 | http-deceiver@1.2.7: 341 | resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} 342 | 343 | http-errors@2.0.0: 344 | resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} 345 | engines: {node: '>= 0.8'} 346 | 347 | http-signature@1.4.0: 348 | resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==} 349 | engines: {node: '>=0.10'} 350 | 351 | iconv-lite@0.6.3: 352 | resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} 353 | engines: {node: '>=0.10.0'} 354 | 355 | ieee754@1.2.1: 356 | resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} 357 | 358 | inherits@2.0.4: 359 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 360 | 361 | isarray@1.0.0: 362 | resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} 363 | 364 | jsbn@0.1.1: 365 | resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} 366 | 367 | json-schema@0.4.0: 368 | resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} 369 | 370 | jsprim@2.0.2: 371 | resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} 372 | engines: {'0': node >=0.6.0} 373 | 374 | lodash@4.17.21: 375 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} 376 | 377 | lru-cache@7.18.3: 378 | resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} 379 | engines: {node: '>=12'} 380 | 381 | make-error@1.3.6: 382 | resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} 383 | 384 | math-intrinsics@1.1.0: 385 | resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} 386 | engines: {node: '>= 0.4'} 387 | 388 | media-typer@1.1.0: 389 | resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} 390 | engines: {node: '>= 0.8'} 391 | 392 | mime-db@1.54.0: 393 | resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} 394 | engines: {node: '>= 0.6'} 395 | 396 | mime-types@3.0.1: 397 | resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} 398 | engines: {node: '>= 0.6'} 399 | 400 | mime@1.6.0: 401 | resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} 402 | engines: {node: '>=4'} 403 | hasBin: true 404 | 405 | mime@3.0.0: 406 | resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} 407 | engines: {node: '>=10.0.0'} 408 | hasBin: true 409 | 410 | minimalistic-assert@1.0.1: 411 | resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} 412 | 413 | ms@2.0.0: 414 | resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} 415 | 416 | ms@2.1.3: 417 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 418 | 419 | nan@2.22.2: 420 | resolution: {integrity: sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==} 421 | 422 | negotiator@0.6.4: 423 | resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} 424 | engines: {node: '>= 0.6'} 425 | 426 | object-inspect@1.13.4: 427 | resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} 428 | engines: {node: '>= 0.4'} 429 | 430 | obuf@1.1.2: 431 | resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} 432 | 433 | on-exit-leak-free@2.1.2: 434 | resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} 435 | engines: {node: '>=14.0.0'} 436 | 437 | on-finished@2.4.1: 438 | resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} 439 | engines: {node: '>= 0.8'} 440 | 441 | once@1.4.0: 442 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 443 | 444 | pidusage@3.0.2: 445 | resolution: {integrity: sha512-g0VU+y08pKw5M8EZ2rIGiEBaB8wrQMjYGFfW2QVIfyT8V+fq8YFLkvlz4bz5ljvFDJYNFCWT3PWqcRr2FKO81w==} 446 | engines: {node: '>=10'} 447 | 448 | pino-abstract-transport@1.2.0: 449 | resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} 450 | 451 | pino-std-serializers@6.2.2: 452 | resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} 453 | 454 | pino@8.21.0: 455 | resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} 456 | hasBin: true 457 | 458 | process-nextick-args@2.0.1: 459 | resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} 460 | 461 | process-warning@3.0.0: 462 | resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} 463 | 464 | process@0.11.10: 465 | resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} 466 | engines: {node: '>= 0.6.0'} 467 | 468 | qs@6.14.0: 469 | resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} 470 | engines: {node: '>=0.6'} 471 | 472 | quick-format-unescaped@4.0.4: 473 | resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} 474 | 475 | range-parser@1.2.1: 476 | resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} 477 | engines: {node: '>= 0.6'} 478 | 479 | raw-body@3.0.0: 480 | resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} 481 | engines: {node: '>= 0.8'} 482 | 483 | readable-stream@2.3.8: 484 | resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} 485 | 486 | readable-stream@3.6.2: 487 | resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} 488 | engines: {node: '>= 6'} 489 | 490 | readable-stream@4.7.0: 491 | resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} 492 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 493 | 494 | real-require@0.2.0: 495 | resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} 496 | engines: {node: '>= 12.13.0'} 497 | 498 | request-ip@3.3.0: 499 | resolution: {integrity: sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==} 500 | 501 | restify-errors@8.0.2: 502 | resolution: {integrity: sha512-UsXUVQo7M26xoQzeUcZQ0+H8L2t9DGzrXcAgR3WB/1vnbl+UdI4tZ1PqYsN+sS5WnqHKZ0Xy9w0CKf83bbrwYA==} 503 | 504 | restify@11.1.0: 505 | resolution: {integrity: sha512-ng7uBlj4wpIpshhAjNNSd6JG5Eg32+zgync2gG8OlF4e2xzIflZo54GJ/qLs765OtQaVU+uJPcNOL5Atm2F/dg==} 506 | engines: {node: '>=10.0.0'} 507 | hasBin: true 508 | 509 | ret@0.2.2: 510 | resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} 511 | engines: {node: '>=4'} 512 | 513 | safe-buffer@5.1.2: 514 | resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} 515 | 516 | safe-buffer@5.2.1: 517 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 518 | 519 | safe-json-stringify@1.2.0: 520 | resolution: {integrity: sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==} 521 | 522 | safe-regex2@2.0.0: 523 | resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} 524 | 525 | safe-stable-stringify@2.5.0: 526 | resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} 527 | engines: {node: '>=10'} 528 | 529 | safer-buffer@2.1.2: 530 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 531 | 532 | select-hose@2.0.0: 533 | resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} 534 | 535 | semver@7.7.1: 536 | resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} 537 | engines: {node: '>=10'} 538 | hasBin: true 539 | 540 | send@0.18.0: 541 | resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} 542 | engines: {node: '>= 0.8.0'} 543 | 544 | setprototypeof@1.2.0: 545 | resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} 546 | 547 | side-channel-list@1.0.0: 548 | resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} 549 | engines: {node: '>= 0.4'} 550 | 551 | side-channel-map@1.0.1: 552 | resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} 553 | engines: {node: '>= 0.4'} 554 | 555 | side-channel-weakmap@1.0.2: 556 | resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} 557 | engines: {node: '>= 0.4'} 558 | 559 | side-channel@1.1.0: 560 | resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} 561 | engines: {node: '>= 0.4'} 562 | 563 | sonic-boom@3.8.1: 564 | resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} 565 | 566 | spdy-transport@3.0.0: 567 | resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} 568 | 569 | spdy@4.0.2: 570 | resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} 571 | engines: {node: '>=6.0.0'} 572 | 573 | split2@4.2.0: 574 | resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} 575 | engines: {node: '>= 10.x'} 576 | 577 | sshpk@1.18.0: 578 | resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} 579 | engines: {node: '>=0.10.0'} 580 | hasBin: true 581 | 582 | statuses@2.0.1: 583 | resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} 584 | engines: {node: '>= 0.8'} 585 | 586 | stream-transform@3.3.3: 587 | resolution: {integrity: sha512-dALXrXe+uq4aO5oStdHKlfCM/b3NBdouigvxVPxCdrMRAU6oHh3KNss20VbTPQNQmjAHzZGKGe66vgwegFEIog==} 588 | 589 | string_decoder@1.1.1: 590 | resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} 591 | 592 | string_decoder@1.3.0: 593 | resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} 594 | 595 | thread-stream@2.7.0: 596 | resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} 597 | 598 | toidentifier@1.0.1: 599 | resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} 600 | engines: {node: '>=0.6'} 601 | 602 | ts-node@10.9.2: 603 | resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} 604 | hasBin: true 605 | peerDependencies: 606 | '@swc/core': '>=1.2.50' 607 | '@swc/wasm': '>=1.2.50' 608 | '@types/node': '*' 609 | typescript: '>=2.7' 610 | peerDependenciesMeta: 611 | '@swc/core': 612 | optional: true 613 | '@swc/wasm': 614 | optional: true 615 | 616 | tweetnacl@0.14.5: 617 | resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} 618 | 619 | type-is@2.0.1: 620 | resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} 621 | engines: {node: '>= 0.6'} 622 | 623 | typescript@5.8.3: 624 | resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} 625 | engines: {node: '>=14.17'} 626 | hasBin: true 627 | 628 | undici-types@6.21.0: 629 | resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 630 | 631 | unpipe@1.0.0: 632 | resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} 633 | engines: {node: '>= 0.8'} 634 | 635 | util-deprecate@1.0.2: 636 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 637 | 638 | uuid@9.0.1: 639 | resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} 640 | hasBin: true 641 | 642 | v8-compile-cache-lib@3.0.1: 643 | resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} 644 | 645 | vasync@2.2.1: 646 | resolution: {integrity: sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==} 647 | engines: {'0': node >=0.6.0} 648 | 649 | verror@1.10.0: 650 | resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} 651 | engines: {'0': node >=0.6.0} 652 | 653 | wbuf@1.7.3: 654 | resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} 655 | 656 | wrappy@1.0.2: 657 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 658 | 659 | yn@3.1.1: 660 | resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} 661 | engines: {node: '>=6'} 662 | 663 | snapshots: 664 | 665 | '@cspotcode/source-map-support@0.8.1': 666 | dependencies: 667 | '@jridgewell/trace-mapping': 0.3.9 668 | 669 | '@jridgewell/resolve-uri@3.1.2': {} 670 | 671 | '@jridgewell/sourcemap-codec@1.5.0': {} 672 | 673 | '@jridgewell/trace-mapping@0.3.9': 674 | dependencies: 675 | '@jridgewell/resolve-uri': 3.1.2 676 | '@jridgewell/sourcemap-codec': 1.5.0 677 | 678 | '@netflix/nerror@1.1.3': 679 | dependencies: 680 | assert-plus: 1.0.0 681 | extsprintf: 1.4.1 682 | lodash: 4.17.21 683 | 684 | '@tsconfig/node10@1.0.11': {} 685 | 686 | '@tsconfig/node12@1.0.11': {} 687 | 688 | '@tsconfig/node14@1.0.3': {} 689 | 690 | '@tsconfig/node16@1.0.4': {} 691 | 692 | '@types/bunyan@1.8.11': 693 | dependencies: 694 | '@types/node': 22.14.0 695 | 696 | '@types/crypto-js@4.2.2': {} 697 | 698 | '@types/extend@3.0.4': {} 699 | 700 | '@types/formidable@1.2.8': 701 | dependencies: 702 | '@types/node': 22.14.0 703 | 704 | '@types/node@22.14.0': 705 | dependencies: 706 | undici-types: 6.21.0 707 | 708 | '@types/request-ip@0.0.41': 709 | dependencies: 710 | '@types/node': 22.14.0 711 | 712 | '@types/restify@8.5.12': 713 | dependencies: 714 | '@types/bunyan': 1.8.11 715 | '@types/formidable': 1.2.8 716 | '@types/node': 22.14.0 717 | '@types/spdy': 3.4.9 718 | 719 | '@types/spdy@3.4.9': 720 | dependencies: 721 | '@types/node': 22.14.0 722 | 723 | abort-controller@3.0.0: 724 | dependencies: 725 | event-target-shim: 5.0.1 726 | 727 | acorn-walk@8.3.4: 728 | dependencies: 729 | acorn: 8.14.1 730 | 731 | acorn@8.14.1: {} 732 | 733 | arg@4.1.3: {} 734 | 735 | asn1@0.2.6: 736 | dependencies: 737 | safer-buffer: 2.1.2 738 | 739 | assert-plus@1.0.0: {} 740 | 741 | atomic-sleep@1.0.0: {} 742 | 743 | base64-js@1.5.1: {} 744 | 745 | bcrypt-pbkdf@1.0.2: 746 | dependencies: 747 | tweetnacl: 0.14.5 748 | 749 | body-parser@2.2.0: 750 | dependencies: 751 | bytes: 3.1.2 752 | content-type: 1.0.5 753 | debug: 4.4.0 754 | http-errors: 2.0.0 755 | iconv-lite: 0.6.3 756 | on-finished: 2.4.1 757 | qs: 6.14.0 758 | raw-body: 3.0.0 759 | type-is: 2.0.1 760 | transitivePeerDependencies: 761 | - supports-color 762 | 763 | buffer@6.0.3: 764 | dependencies: 765 | base64-js: 1.5.1 766 | ieee754: 1.2.1 767 | 768 | bytes@3.1.2: {} 769 | 770 | call-bind-apply-helpers@1.0.2: 771 | dependencies: 772 | es-errors: 1.3.0 773 | function-bind: 1.1.2 774 | 775 | call-bound@1.0.4: 776 | dependencies: 777 | call-bind-apply-helpers: 1.0.2 778 | get-intrinsic: 1.3.0 779 | 780 | content-type@1.0.5: {} 781 | 782 | core-util-is@1.0.2: {} 783 | 784 | core-util-is@1.0.3: {} 785 | 786 | create-require@1.1.1: {} 787 | 788 | crypto-js@4.2.0: {} 789 | 790 | csv-generate@4.4.2: {} 791 | 792 | csv-parse@5.6.0: {} 793 | 794 | csv-stringify@6.5.2: {} 795 | 796 | csv@6.3.11: 797 | dependencies: 798 | csv-generate: 4.4.2 799 | csv-parse: 5.6.0 800 | csv-stringify: 6.5.2 801 | stream-transform: 3.3.3 802 | 803 | dashdash@1.14.1: 804 | dependencies: 805 | assert-plus: 1.0.0 806 | 807 | debug@2.6.9: 808 | dependencies: 809 | ms: 2.0.0 810 | 811 | debug@4.4.0: 812 | dependencies: 813 | ms: 2.1.3 814 | 815 | depd@2.0.0: {} 816 | 817 | destroy@1.2.0: {} 818 | 819 | detect-node@2.1.0: {} 820 | 821 | diff@4.0.2: {} 822 | 823 | dtrace-provider@0.8.8: 824 | dependencies: 825 | nan: 2.22.2 826 | optional: true 827 | 828 | dunder-proto@1.0.1: 829 | dependencies: 830 | call-bind-apply-helpers: 1.0.2 831 | es-errors: 1.3.0 832 | gopd: 1.2.0 833 | 834 | ecc-jsbn@0.1.2: 835 | dependencies: 836 | jsbn: 0.1.1 837 | safer-buffer: 2.1.2 838 | 839 | ee-first@1.1.1: {} 840 | 841 | encodeurl@1.0.2: {} 842 | 843 | es-define-property@1.0.1: {} 844 | 845 | es-errors@1.3.0: {} 846 | 847 | es-object-atoms@1.1.1: 848 | dependencies: 849 | es-errors: 1.3.0 850 | 851 | escape-html@1.0.3: {} 852 | 853 | escape-regexp-component@1.0.2: {} 854 | 855 | etag@1.8.1: {} 856 | 857 | event-target-shim@5.0.1: {} 858 | 859 | events@3.3.0: {} 860 | 861 | ewma@2.0.1: 862 | dependencies: 863 | assert-plus: 1.0.0 864 | 865 | extend@3.0.2: {} 866 | 867 | extsprintf@1.3.0: {} 868 | 869 | extsprintf@1.4.1: {} 870 | 871 | fast-decode-uri-component@1.0.1: {} 872 | 873 | fast-deep-equal@3.1.3: {} 874 | 875 | fast-querystring@1.1.2: 876 | dependencies: 877 | fast-decode-uri-component: 1.0.1 878 | 879 | fast-redact@3.5.0: {} 880 | 881 | find-my-way@7.7.0: 882 | dependencies: 883 | fast-deep-equal: 3.1.3 884 | fast-querystring: 1.1.2 885 | safe-regex2: 2.0.0 886 | 887 | formidable@1.2.6: {} 888 | 889 | fresh@0.5.2: {} 890 | 891 | function-bind@1.1.2: {} 892 | 893 | get-intrinsic@1.3.0: 894 | dependencies: 895 | call-bind-apply-helpers: 1.0.2 896 | es-define-property: 1.0.1 897 | es-errors: 1.3.0 898 | es-object-atoms: 1.1.1 899 | function-bind: 1.1.2 900 | get-proto: 1.0.1 901 | gopd: 1.2.0 902 | has-symbols: 1.1.0 903 | hasown: 2.0.2 904 | math-intrinsics: 1.1.0 905 | 906 | get-proto@1.0.1: 907 | dependencies: 908 | dunder-proto: 1.0.1 909 | es-object-atoms: 1.1.1 910 | 911 | getpass@0.1.7: 912 | dependencies: 913 | assert-plus: 1.0.0 914 | 915 | gopd@1.2.0: {} 916 | 917 | handle-thing@2.0.1: {} 918 | 919 | has-symbols@1.1.0: {} 920 | 921 | hasown@2.0.2: 922 | dependencies: 923 | function-bind: 1.1.2 924 | 925 | hpack.js@2.1.6: 926 | dependencies: 927 | inherits: 2.0.4 928 | obuf: 1.1.2 929 | readable-stream: 2.3.8 930 | wbuf: 1.7.3 931 | 932 | http-deceiver@1.2.7: {} 933 | 934 | http-errors@2.0.0: 935 | dependencies: 936 | depd: 2.0.0 937 | inherits: 2.0.4 938 | setprototypeof: 1.2.0 939 | statuses: 2.0.1 940 | toidentifier: 1.0.1 941 | 942 | http-signature@1.4.0: 943 | dependencies: 944 | assert-plus: 1.0.0 945 | jsprim: 2.0.2 946 | sshpk: 1.18.0 947 | 948 | iconv-lite@0.6.3: 949 | dependencies: 950 | safer-buffer: 2.1.2 951 | 952 | ieee754@1.2.1: {} 953 | 954 | inherits@2.0.4: {} 955 | 956 | isarray@1.0.0: {} 957 | 958 | jsbn@0.1.1: {} 959 | 960 | json-schema@0.4.0: {} 961 | 962 | jsprim@2.0.2: 963 | dependencies: 964 | assert-plus: 1.0.0 965 | extsprintf: 1.3.0 966 | json-schema: 0.4.0 967 | verror: 1.10.0 968 | 969 | lodash@4.17.21: {} 970 | 971 | lru-cache@7.18.3: {} 972 | 973 | make-error@1.3.6: {} 974 | 975 | math-intrinsics@1.1.0: {} 976 | 977 | media-typer@1.1.0: {} 978 | 979 | mime-db@1.54.0: {} 980 | 981 | mime-types@3.0.1: 982 | dependencies: 983 | mime-db: 1.54.0 984 | 985 | mime@1.6.0: {} 986 | 987 | mime@3.0.0: {} 988 | 989 | minimalistic-assert@1.0.1: {} 990 | 991 | ms@2.0.0: {} 992 | 993 | ms@2.1.3: {} 994 | 995 | nan@2.22.2: 996 | optional: true 997 | 998 | negotiator@0.6.4: {} 999 | 1000 | object-inspect@1.13.4: {} 1001 | 1002 | obuf@1.1.2: {} 1003 | 1004 | on-exit-leak-free@2.1.2: {} 1005 | 1006 | on-finished@2.4.1: 1007 | dependencies: 1008 | ee-first: 1.1.1 1009 | 1010 | once@1.4.0: 1011 | dependencies: 1012 | wrappy: 1.0.2 1013 | 1014 | pidusage@3.0.2: 1015 | dependencies: 1016 | safe-buffer: 5.2.1 1017 | 1018 | pino-abstract-transport@1.2.0: 1019 | dependencies: 1020 | readable-stream: 4.7.0 1021 | split2: 4.2.0 1022 | 1023 | pino-std-serializers@6.2.2: {} 1024 | 1025 | pino@8.21.0: 1026 | dependencies: 1027 | atomic-sleep: 1.0.0 1028 | fast-redact: 3.5.0 1029 | on-exit-leak-free: 2.1.2 1030 | pino-abstract-transport: 1.2.0 1031 | pino-std-serializers: 6.2.2 1032 | process-warning: 3.0.0 1033 | quick-format-unescaped: 4.0.4 1034 | real-require: 0.2.0 1035 | safe-stable-stringify: 2.5.0 1036 | sonic-boom: 3.8.1 1037 | thread-stream: 2.7.0 1038 | 1039 | process-nextick-args@2.0.1: {} 1040 | 1041 | process-warning@3.0.0: {} 1042 | 1043 | process@0.11.10: {} 1044 | 1045 | qs@6.14.0: 1046 | dependencies: 1047 | side-channel: 1.1.0 1048 | 1049 | quick-format-unescaped@4.0.4: {} 1050 | 1051 | range-parser@1.2.1: {} 1052 | 1053 | raw-body@3.0.0: 1054 | dependencies: 1055 | bytes: 3.1.2 1056 | http-errors: 2.0.0 1057 | iconv-lite: 0.6.3 1058 | unpipe: 1.0.0 1059 | 1060 | readable-stream@2.3.8: 1061 | dependencies: 1062 | core-util-is: 1.0.3 1063 | inherits: 2.0.4 1064 | isarray: 1.0.0 1065 | process-nextick-args: 2.0.1 1066 | safe-buffer: 5.1.2 1067 | string_decoder: 1.1.1 1068 | util-deprecate: 1.0.2 1069 | 1070 | readable-stream@3.6.2: 1071 | dependencies: 1072 | inherits: 2.0.4 1073 | string_decoder: 1.3.0 1074 | util-deprecate: 1.0.2 1075 | 1076 | readable-stream@4.7.0: 1077 | dependencies: 1078 | abort-controller: 3.0.0 1079 | buffer: 6.0.3 1080 | events: 3.3.0 1081 | process: 0.11.10 1082 | string_decoder: 1.3.0 1083 | 1084 | real-require@0.2.0: {} 1085 | 1086 | request-ip@3.3.0: {} 1087 | 1088 | restify-errors@8.0.2: 1089 | dependencies: 1090 | '@netflix/nerror': 1.1.3 1091 | assert-plus: 1.0.0 1092 | lodash: 4.17.21 1093 | optionalDependencies: 1094 | safe-json-stringify: 1.2.0 1095 | 1096 | restify@11.1.0: 1097 | dependencies: 1098 | assert-plus: 1.0.0 1099 | csv: 6.3.11 1100 | escape-regexp-component: 1.0.2 1101 | ewma: 2.0.1 1102 | find-my-way: 7.7.0 1103 | formidable: 1.2.6 1104 | http-signature: 1.4.0 1105 | lodash: 4.17.21 1106 | lru-cache: 7.18.3 1107 | mime: 3.0.0 1108 | negotiator: 0.6.4 1109 | once: 1.4.0 1110 | pidusage: 3.0.2 1111 | pino: 8.21.0 1112 | qs: 6.14.0 1113 | restify-errors: 8.0.2 1114 | semver: 7.7.1 1115 | send: 0.18.0 1116 | spdy: 4.0.2 1117 | uuid: 9.0.1 1118 | vasync: 2.2.1 1119 | optionalDependencies: 1120 | dtrace-provider: 0.8.8 1121 | transitivePeerDependencies: 1122 | - supports-color 1123 | 1124 | ret@0.2.2: {} 1125 | 1126 | safe-buffer@5.1.2: {} 1127 | 1128 | safe-buffer@5.2.1: {} 1129 | 1130 | safe-json-stringify@1.2.0: 1131 | optional: true 1132 | 1133 | safe-regex2@2.0.0: 1134 | dependencies: 1135 | ret: 0.2.2 1136 | 1137 | safe-stable-stringify@2.5.0: {} 1138 | 1139 | safer-buffer@2.1.2: {} 1140 | 1141 | select-hose@2.0.0: {} 1142 | 1143 | semver@7.7.1: {} 1144 | 1145 | send@0.18.0: 1146 | dependencies: 1147 | debug: 2.6.9 1148 | depd: 2.0.0 1149 | destroy: 1.2.0 1150 | encodeurl: 1.0.2 1151 | escape-html: 1.0.3 1152 | etag: 1.8.1 1153 | fresh: 0.5.2 1154 | http-errors: 2.0.0 1155 | mime: 1.6.0 1156 | ms: 2.1.3 1157 | on-finished: 2.4.1 1158 | range-parser: 1.2.1 1159 | statuses: 2.0.1 1160 | transitivePeerDependencies: 1161 | - supports-color 1162 | 1163 | setprototypeof@1.2.0: {} 1164 | 1165 | side-channel-list@1.0.0: 1166 | dependencies: 1167 | es-errors: 1.3.0 1168 | object-inspect: 1.13.4 1169 | 1170 | side-channel-map@1.0.1: 1171 | dependencies: 1172 | call-bound: 1.0.4 1173 | es-errors: 1.3.0 1174 | get-intrinsic: 1.3.0 1175 | object-inspect: 1.13.4 1176 | 1177 | side-channel-weakmap@1.0.2: 1178 | dependencies: 1179 | call-bound: 1.0.4 1180 | es-errors: 1.3.0 1181 | get-intrinsic: 1.3.0 1182 | object-inspect: 1.13.4 1183 | side-channel-map: 1.0.1 1184 | 1185 | side-channel@1.1.0: 1186 | dependencies: 1187 | es-errors: 1.3.0 1188 | object-inspect: 1.13.4 1189 | side-channel-list: 1.0.0 1190 | side-channel-map: 1.0.1 1191 | side-channel-weakmap: 1.0.2 1192 | 1193 | sonic-boom@3.8.1: 1194 | dependencies: 1195 | atomic-sleep: 1.0.0 1196 | 1197 | spdy-transport@3.0.0: 1198 | dependencies: 1199 | debug: 4.4.0 1200 | detect-node: 2.1.0 1201 | hpack.js: 2.1.6 1202 | obuf: 1.1.2 1203 | readable-stream: 3.6.2 1204 | wbuf: 1.7.3 1205 | transitivePeerDependencies: 1206 | - supports-color 1207 | 1208 | spdy@4.0.2: 1209 | dependencies: 1210 | debug: 4.4.0 1211 | handle-thing: 2.0.1 1212 | http-deceiver: 1.2.7 1213 | select-hose: 2.0.0 1214 | spdy-transport: 3.0.0 1215 | transitivePeerDependencies: 1216 | - supports-color 1217 | 1218 | split2@4.2.0: {} 1219 | 1220 | sshpk@1.18.0: 1221 | dependencies: 1222 | asn1: 0.2.6 1223 | assert-plus: 1.0.0 1224 | bcrypt-pbkdf: 1.0.2 1225 | dashdash: 1.14.1 1226 | ecc-jsbn: 0.1.2 1227 | getpass: 0.1.7 1228 | jsbn: 0.1.1 1229 | safer-buffer: 2.1.2 1230 | tweetnacl: 0.14.5 1231 | 1232 | statuses@2.0.1: {} 1233 | 1234 | stream-transform@3.3.3: {} 1235 | 1236 | string_decoder@1.1.1: 1237 | dependencies: 1238 | safe-buffer: 5.1.2 1239 | 1240 | string_decoder@1.3.0: 1241 | dependencies: 1242 | safe-buffer: 5.2.1 1243 | 1244 | thread-stream@2.7.0: 1245 | dependencies: 1246 | real-require: 0.2.0 1247 | 1248 | toidentifier@1.0.1: {} 1249 | 1250 | ts-node@10.9.2(@types/node@22.14.0)(typescript@5.8.3): 1251 | dependencies: 1252 | '@cspotcode/source-map-support': 0.8.1 1253 | '@tsconfig/node10': 1.0.11 1254 | '@tsconfig/node12': 1.0.11 1255 | '@tsconfig/node14': 1.0.3 1256 | '@tsconfig/node16': 1.0.4 1257 | '@types/node': 22.14.0 1258 | acorn: 8.14.1 1259 | acorn-walk: 8.3.4 1260 | arg: 4.1.3 1261 | create-require: 1.1.1 1262 | diff: 4.0.2 1263 | make-error: 1.3.6 1264 | typescript: 5.8.3 1265 | v8-compile-cache-lib: 3.0.1 1266 | yn: 3.1.1 1267 | 1268 | tweetnacl@0.14.5: {} 1269 | 1270 | type-is@2.0.1: 1271 | dependencies: 1272 | content-type: 1.0.5 1273 | media-typer: 1.1.0 1274 | mime-types: 3.0.1 1275 | 1276 | typescript@5.8.3: {} 1277 | 1278 | undici-types@6.21.0: {} 1279 | 1280 | unpipe@1.0.0: {} 1281 | 1282 | util-deprecate@1.0.2: {} 1283 | 1284 | uuid@9.0.1: {} 1285 | 1286 | v8-compile-cache-lib@3.0.1: {} 1287 | 1288 | vasync@2.2.1: 1289 | dependencies: 1290 | verror: 1.10.0 1291 | 1292 | verror@1.10.0: 1293 | dependencies: 1294 | assert-plus: 1.0.0 1295 | core-util-is: 1.0.2 1296 | extsprintf: 1.4.1 1297 | 1298 | wbuf@1.7.3: 1299 | dependencies: 1300 | minimalistic-assert: 1.0.1 1301 | 1302 | wrappy@1.0.2: {} 1303 | 1304 | yn@3.1.1: {} 1305 | --------------------------------------------------------------------------------