From 0cccd04c72e278300c603d746b13d0a07c444ef8 Mon Sep 17 00:00:00 2001 From: Kerwin Date: Tue, 14 Mar 2023 23:42:01 +0800 Subject: [PATCH 001/147] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E7=99=BB=E5=BD=95&=E5=90=8C=E6=AD=A5=E8=81=8A?= =?UTF-8?q?=E5=A4=A9=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + docker-compose/docker-compose.yml | 41 ++- package.json | 1 + pnpm-lock.yaml | 6 + service/.env.example | 39 ++- service/package.json | 4 + service/pnpm-lock.yaml | 345 ++++++++++++++------- service/src/index.ts | 214 ++++++++++++- service/src/middleware/auth.ts | 10 +- service/src/storage/model.ts | 81 +++++ service/src/storage/mongo.ts | 143 +++++++++ service/src/types.ts | 1 + service/src/utils/mail.ts | 30 ++ service/src/utils/security.ts | 32 ++ src/api/index.ts | 68 +++- src/components/common/UserAvatar/index.vue | 13 +- src/locales/en-US.ts | 5 +- src/locales/zh-CN.ts | 5 +- src/locales/zh-TW.ts | 5 +- src/store/modules/auth/index.ts | 16 +- src/store/modules/chat/index.ts | 44 ++- src/store/modules/user/helper.ts | 6 +- src/typings/chat.d.ts | 1 + src/views/chat/index.vue | 10 +- src/views/chat/layout/Permission.vue | 105 ++++++- src/views/chat/layout/sider/Footer.vue | 17 +- src/views/chat/layout/sider/List.vue | 15 +- 27 files changed, 1095 insertions(+), 163 deletions(-) create mode 100644 service/src/storage/model.ts create mode 100644 service/src/storage/mongo.ts create mode 100644 service/src/utils/mail.ts create mode 100644 service/src/utils/security.ts diff --git a/.gitignore b/.gitignore index 897b8d4033..0fedcce528 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ coverage # Environment variables files /service/.env +/docker-compose/nginx/html \ No newline at end of file diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 91dee3c0a8..48456817eb 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -5,6 +5,8 @@ services: image: chenzhaoyu94/chatgpt-web # 总是使用latest,更新时重新pull该tag镜像即可 ports: - 3002:3002 + depends_on: + - database environment: # 二选一 OPENAI_API_KEY: xxxx @@ -16,7 +18,7 @@ services: OPENAI_API_MODEL: xxxx # 反向代理,可选 API_REVERSE_PROXY: xxx - # 访问权限密钥,可选 + # 访问jwt加密参数,可选 不为空则允许登录 同时需要设置 MONGODB_URL AUTH_SECRET_KEY: xxx # 超时,单位毫秒,可选 TIMEOUT_MS: 60000 @@ -26,6 +28,40 @@ services: SOCKS_PROXY_PORT: xxxx # HTTPS_PROXY 代理,可选 HTTPS_PROXY: http://xxxx:7890 + # mongodb 的连接字符串 + MONGODB_URL: 'mongodb://chatgpt:xxxx@database:27017' + # 网站是否开启注册 + REGISTER_ENABLED: false + # 开启注册之后 网站注册允许的邮箱后缀 如果空 则允许任意后缀 + REGISTER_MAILS: '@qq.com,@sina.com,@163.com' + # 开启注册之后 密码加密的盐 + PASSWORD_MD5_SALT: anysalt + # 开启注册之后 超级管理邮箱 + ROOT_USER: xxx@qq.com + # 开启注册之后 网站域名 不含 / 注册的时候发送验证邮箱使用 + SITE_DOMAIN: http://127.0.0.1:1002 + # 开启注册之后 发送验证邮箱配置 + SMTP_HOST: smtp.exmail.qq.com + SMTP_PORT: 465 + SMTP_TSL: true + SMTP_USERNAME: ${SMTP_USERNAME} + SMTP_PASSWORD: ${SMTP_PASSWORD} + links: + - database + + database: + image: mongo + ports: + - '27017:27017' + expose: + - '27017' + volumes: + - mongodb:/data/db + environment: + MONGO_INITDB_ROOT_USERNAME: chatgpt + MONGO_INITDB_ROOT_PASSWORD: xxxx + MONGO_INITDB_DATABASE: chatgpt + nginx: image: nginx:alpine ports: @@ -37,3 +73,6 @@ services: - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf links: - app + +volumes: + mongodb: {} diff --git a/package.json b/package.json index 9dad0f4e71..58e3a696f2 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@vueuse/core": "^9.13.0", "highlight.js": "^11.7.0", "html2canvas": "^1.4.1", + "jwt-decode": "^3.1.2", "katex": "^0.16.4", "markdown-it": "^13.0.1", "naive-ui": "^2.34.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c2f4362465..c4c6532402 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,7 @@ specifiers: highlight.js: ^11.7.0 html2canvas: ^1.4.1 husky: ^8.0.3 + jwt-decode: ^3.1.2 katex: ^0.16.4 less: ^4.1.3 lint-staged: ^13.1.2 @@ -42,6 +43,7 @@ dependencies: '@vueuse/core': 9.13.0_vue@3.2.47 highlight.js: 11.7.0 html2canvas: 1.4.1 + jwt-decode: 3.1.2 katex: 0.16.4 markdown-it: 13.0.1 naive-ui: 2.34.3_vue@3.2.47 @@ -4593,6 +4595,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /jwt-decode/3.1.2: + resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==} + dev: false + /katex/0.16.4: resolution: {integrity: sha512-WudRKUj8yyBeVDI4aYMNxhx5Vhh2PjpzQw1GRu/LVGqL4m1AxwD1GcUp0IMbdJaf5zsjtj8ghP0DOQRYhroNkw==} hasBin: true diff --git a/service/.env.example b/service/.env.example index 7ca4a4db2d..ac2366f6e3 100644 --- a/service/.env.example +++ b/service/.env.example @@ -16,9 +16,6 @@ API_REVERSE_PROXY= # timeout TIMEOUT_MS=100000 -# Secret key -AUTH_SECRET_KEY= - # Socks Proxy Host SOCKS_PROXY_HOST= @@ -27,3 +24,39 @@ SOCKS_PROXY_PORT= # HTTPS PROXY HTTPS_PROXY= + +# Databse connection string +# MONGODB_URL=mongodb://chatgpt:xxxx@yourip:port +MONGODB_URL=mongodb://chatgpt:xxxx@database:27017 + +# Secret key for jwt +# If not empty, will need login +AUTH_SECRET_KEY= + +# ----- Only valid after setting AUTH_SECRET_KEY begin ---- + +# Allow anyone register +REGISTER_ENABLED=false + +# The site domain, Only for registration account verification +# without end / +SITE_DOMAIN=http://127.0.0.1:1002 + +# Allowed Email Providers, If it is empty, any mailbox is allowed +# REGISTER_MAILS=@qq.com,@sina.com,@163.com +REGISTER_MAILS=@qq.com,@sina.com,@163.com + +# The roon user only email +ROOT_USER= + +# Password salt +PASSWORD_MD5_SALT=anysalt + +# User register email verify +SMTP_HOST=smtp.exmail.qq.com +SMTP_PORT=465 +SMTP_TSL=true +SMTP_USERNAME=yourname@example.com +SMTP_PASSWORD=yourpassword + +# ----- Only valid after setting AUTH_SECRET_KEY end ---- \ No newline at end of file diff --git a/service/package.json b/service/package.json index 4b74e5a284..e9535cb1de 100644 --- a/service/package.json +++ b/service/package.json @@ -30,14 +30,18 @@ "express": "^4.18.2", "https-proxy-agent": "^5.0.1", "isomorphic-fetch": "^3.0.0", + "mongodb": "^5.1.0", "node-fetch": "^3.3.0", + "nodemailer": "^6.9.1", "socks-proxy-agent": "^7.0.0" }, "devDependencies": { "@antfu/eslint-config": "^0.35.3", "@types/express": "^4.17.17", + "@types/mongodb": "^4.0.7", "@types/node": "^18.14.6", "eslint": "^8.35.0", + "jsonwebtoken": "^9.0.0", "rimraf": "^4.3.0", "tsup": "^6.6.3", "typescript": "^4.9.5" diff --git a/service/pnpm-lock.yaml b/service/pnpm-lock.yaml index e2e251f56c..90636bb318 100644 --- a/service/pnpm-lock.yaml +++ b/service/pnpm-lock.yaml @@ -3,6 +3,7 @@ lockfileVersion: 5.4 specifiers: '@antfu/eslint-config': ^0.35.3 '@types/express': ^4.17.17 + '@types/mongodb': ^4.0.7 '@types/node': ^18.14.6 chatgpt: ^5.0.10 dotenv: ^16.0.3 @@ -11,7 +12,10 @@ specifiers: express: ^4.18.2 https-proxy-agent: ^5.0.1 isomorphic-fetch: ^3.0.0 + jsonwebtoken: ^9.0.0 + mongodb: ^5.1.0 node-fetch: ^3.3.0 + nodemailer: ^6.9.1 rimraf: ^4.3.0 socks-proxy-agent: ^7.0.0 tsup: ^6.6.3 @@ -24,15 +28,19 @@ dependencies: express: 4.18.2 https-proxy-agent: 5.0.1 isomorphic-fetch: 3.0.0 + mongodb: 5.1.0 node-fetch: 3.3.0 + nodemailer: 6.9.1 socks-proxy-agent: 7.0.0 devDependencies: '@antfu/eslint-config': 0.35.3_ycpbpc6yetojsgtrx3mwntkhsu '@types/express': 4.17.17 + '@types/mongodb': 4.0.7 '@types/node': 18.14.6 eslint: 8.35.0 - rimraf: 4.3.0 + jsonwebtoken: 9.0.0 + rimraf: 4.3.1 tsup: 6.6.3_typescript@4.9.5 typescript: 4.9.5 @@ -166,7 +174,7 @@ packages: /@esbuild-kit/core-utils/3.1.0: resolution: {integrity: sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==} dependencies: - esbuild: 0.17.11 + esbuild: 0.17.8 source-map-support: 0.5.21 dev: false @@ -177,187 +185,187 @@ packages: get-tsconfig: 4.4.0 dev: false - /@esbuild/android-arm/0.17.11: - resolution: {integrity: sha512-CdyX6sRVh1NzFCsf5vw3kULwlAhfy9wVt8SZlrhQ7eL2qBjGbFhRBWkkAzuZm9IIEOCKJw4DXA6R85g+qc8RDw==} + /@esbuild/android-arm/0.17.8: + resolution: {integrity: sha512-0/rb91GYKhrtbeglJXOhAv9RuYimgI8h623TplY2X+vA4EXnk3Zj1fXZreJ0J3OJJu1bwmb0W7g+2cT/d8/l/w==} engines: {node: '>=12'} cpu: [arm] os: [android] requiresBuild: true optional: true - /@esbuild/android-arm64/0.17.11: - resolution: {integrity: sha512-QnK4d/zhVTuV4/pRM4HUjcsbl43POALU2zvBynmrrqZt9LPcLA3x1fTZPBg2RRguBQnJcnU059yKr+bydkntjg==} + /@esbuild/android-arm64/0.17.8: + resolution: {integrity: sha512-oa/N5j6v1svZQs7EIRPqR8f+Bf8g6HBDjD/xHC02radE/NjKHK7oQmtmLxPs1iVwYyvE+Kolo6lbpfEQ9xnhxQ==} engines: {node: '>=12'} cpu: [arm64] os: [android] requiresBuild: true optional: true - /@esbuild/android-x64/0.17.11: - resolution: {integrity: sha512-3PL3HKtsDIXGQcSCKtWD/dy+mgc4p2Tvo2qKgKHj9Yf+eniwFnuoQ0OUhlSfAEpKAFzF9N21Nwgnap6zy3L3MQ==} + /@esbuild/android-x64/0.17.8: + resolution: {integrity: sha512-bTliMLqD7pTOoPg4zZkXqCDuzIUguEWLpeqkNfC41ODBHwoUgZ2w5JBeYimv4oP6TDVocoYmEhZrCLQTrH89bg==} engines: {node: '>=12'} cpu: [x64] os: [android] requiresBuild: true optional: true - /@esbuild/darwin-arm64/0.17.11: - resolution: {integrity: sha512-pJ950bNKgzhkGNO3Z9TeHzIFtEyC2GDQL3wxkMApDEghYx5Qers84UTNc1bAxWbRkuJOgmOha5V0WUeh8G+YGw==} + /@esbuild/darwin-arm64/0.17.8: + resolution: {integrity: sha512-ghAbV3ia2zybEefXRRm7+lx8J/rnupZT0gp9CaGy/3iolEXkJ6LYRq4IpQVI9zR97ID80KJVoUlo3LSeA/sMAg==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@esbuild/darwin-x64/0.17.11: - resolution: {integrity: sha512-iB0dQkIHXyczK3BZtzw1tqegf0F0Ab5texX2TvMQjiJIWXAfM4FQl7D909YfXWnB92OQz4ivBYQ2RlxBJrMJOw==} + /@esbuild/darwin-x64/0.17.8: + resolution: {integrity: sha512-n5WOpyvZ9TIdv2V1K3/iIkkJeKmUpKaCTdun9buhGRWfH//osmUjlv4Z5mmWdPWind/VGcVxTHtLfLCOohsOXw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] requiresBuild: true optional: true - /@esbuild/freebsd-arm64/0.17.11: - resolution: {integrity: sha512-7EFzUADmI1jCHeDRGKgbnF5sDIceZsQGapoO6dmw7r/ZBEKX7CCDnIz8m9yEclzr7mFsd+DyasHzpjfJnmBB1Q==} + /@esbuild/freebsd-arm64/0.17.8: + resolution: {integrity: sha512-a/SATTaOhPIPFWvHZDoZYgxaZRVHn0/LX1fHLGfZ6C13JqFUZ3K6SMD6/HCtwOQ8HnsNaEeokdiDSFLuizqv5A==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] requiresBuild: true optional: true - /@esbuild/freebsd-x64/0.17.11: - resolution: {integrity: sha512-iPgenptC8i8pdvkHQvXJFzc1eVMR7W2lBPrTE6GbhR54sLcF42mk3zBOjKPOodezzuAz/KSu8CPyFSjcBMkE9g==} + /@esbuild/freebsd-x64/0.17.8: + resolution: {integrity: sha512-xpFJb08dfXr5+rZc4E+ooZmayBW6R3q59daCpKZ/cDU96/kvDM+vkYzNeTJCGd8rtO6fHWMq5Rcv/1cY6p6/0Q==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] requiresBuild: true optional: true - /@esbuild/linux-arm/0.17.11: - resolution: {integrity: sha512-M9iK/d4lgZH0U5M1R2p2gqhPV/7JPJcRz+8O8GBKVgqndTzydQ7B2XGDbxtbvFkvIs53uXTobOhv+RyaqhUiMg==} + /@esbuild/linux-arm/0.17.8: + resolution: {integrity: sha512-6Ij8gfuGszcEwZpi5jQIJCVIACLS8Tz2chnEBfYjlmMzVsfqBP1iGmHQPp7JSnZg5xxK9tjCc+pJ2WtAmPRFVA==} engines: {node: '>=12'} cpu: [arm] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-arm64/0.17.11: - resolution: {integrity: sha512-Qxth3gsWWGKz2/qG2d5DsW/57SeA2AmpSMhdg9TSB5Svn2KDob3qxfQSkdnWjSd42kqoxIPy3EJFs+6w1+6Qjg==} + /@esbuild/linux-arm64/0.17.8: + resolution: {integrity: sha512-v3iwDQuDljLTxpsqQDl3fl/yihjPAyOguxuloON9kFHYwopeJEf1BkDXODzYyXEI19gisEsQlG1bM65YqKSIww==} engines: {node: '>=12'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-ia32/0.17.11: - resolution: {integrity: sha512-dB1nGaVWtUlb/rRDHmuDQhfqazWE0LMro/AIbT2lWM3CDMHJNpLckH+gCddQyhhcLac2OYw69ikUMO34JLt3wA==} + /@esbuild/linux-ia32/0.17.8: + resolution: {integrity: sha512-8svILYKhE5XetuFk/B6raFYIyIqydQi+GngEXJgdPdI7OMKUbSd7uzR02wSY4kb53xBrClLkhH4Xs8P61Q2BaA==} engines: {node: '>=12'} cpu: [ia32] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-loong64/0.17.11: - resolution: {integrity: sha512-aCWlq70Q7Nc9WDnormntGS1ar6ZFvUpqr8gXtO+HRejRYPweAFQN615PcgaSJkZjhHp61+MNLhzyVALSF2/Q0g==} + /@esbuild/linux-loong64/0.17.8: + resolution: {integrity: sha512-B6FyMeRJeV0NpyEOYlm5qtQfxbdlgmiGdD+QsipzKfFky0K5HW5Td6dyK3L3ypu1eY4kOmo7wW0o94SBqlqBSA==} engines: {node: '>=12'} cpu: [loong64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-mips64el/0.17.11: - resolution: {integrity: sha512-cGeGNdQxqY8qJwlYH1BP6rjIIiEcrM05H7k3tR7WxOLmD1ZxRMd6/QIOWMb8mD2s2YJFNRuNQ+wjMhgEL2oCEw==} + /@esbuild/linux-mips64el/0.17.8: + resolution: {integrity: sha512-CCb67RKahNobjm/eeEqeD/oJfJlrWyw29fgiyB6vcgyq97YAf3gCOuP6qMShYSPXgnlZe/i4a8WFHBw6N8bYAA==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-ppc64/0.17.11: - resolution: {integrity: sha512-BdlziJQPW/bNe0E8eYsHB40mYOluS+jULPCjlWiHzDgr+ZBRXPtgMV1nkLEGdpjrwgmtkZHEGEPaKdS/8faLDA==} + /@esbuild/linux-ppc64/0.17.8: + resolution: {integrity: sha512-bytLJOi55y55+mGSdgwZ5qBm0K9WOCh0rx+vavVPx+gqLLhxtSFU0XbeYy/dsAAD6xECGEv4IQeFILaSS2auXw==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-riscv64/0.17.11: - resolution: {integrity: sha512-MDLwQbtF+83oJCI1Cixn68Et/ME6gelmhssPebC40RdJaect+IM+l7o/CuG0ZlDs6tZTEIoxUe53H3GmMn8oMA==} + /@esbuild/linux-riscv64/0.17.8: + resolution: {integrity: sha512-2YpRyQJmKVBEHSBLa8kBAtbhucaclb6ex4wchfY0Tj3Kg39kpjeJ9vhRU7x4mUpq8ISLXRXH1L0dBYjAeqzZAw==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-s390x/0.17.11: - resolution: {integrity: sha512-4N5EMESvws0Ozr2J94VoUD8HIRi7X0uvUv4c0wpTHZyZY9qpaaN7THjosdiW56irQ4qnJ6Lsc+i+5zGWnyqWqQ==} + /@esbuild/linux-s390x/0.17.8: + resolution: {integrity: sha512-QgbNY/V3IFXvNf11SS6exkpVcX0LJcob+0RWCgV9OiDAmVElnxciHIisoSix9uzYzScPmS6dJFbZULdSAEkQVw==} engines: {node: '>=12'} cpu: [s390x] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-x64/0.17.11: - resolution: {integrity: sha512-rM/v8UlluxpytFSmVdbCe1yyKQd/e+FmIJE2oPJvbBo+D0XVWi1y/NQ4iTNx+436WmDHQBjVLrbnAQLQ6U7wlw==} + /@esbuild/linux-x64/0.17.8: + resolution: {integrity: sha512-mM/9S0SbAFDBc4OPoyP6SEOo5324LpUxdpeIUUSrSTOfhHU9hEfqRngmKgqILqwx/0DVJBzeNW7HmLEWp9vcOA==} engines: {node: '>=12'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@esbuild/netbsd-x64/0.17.11: - resolution: {integrity: sha512-4WaAhuz5f91h3/g43VBGdto1Q+X7VEZfpcWGtOFXnggEuLvjV+cP6DyLRU15IjiU9fKLLk41OoJfBFN5DhPvag==} + /@esbuild/netbsd-x64/0.17.8: + resolution: {integrity: sha512-eKUYcWaWTaYr9zbj8GertdVtlt1DTS1gNBWov+iQfWuWyuu59YN6gSEJvFzC5ESJ4kMcKR0uqWThKUn5o8We6Q==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] requiresBuild: true optional: true - /@esbuild/openbsd-x64/0.17.11: - resolution: {integrity: sha512-UBj135Nx4FpnvtE+C8TWGp98oUgBcmNmdYgl5ToKc0mBHxVVqVE7FUS5/ELMImOp205qDAittL6Ezhasc2Ev/w==} + /@esbuild/openbsd-x64/0.17.8: + resolution: {integrity: sha512-Vc9J4dXOboDyMXKD0eCeW0SIeEzr8K9oTHJU+Ci1mZc5njPfhKAqkRt3B/fUNU7dP+mRyralPu8QUkiaQn7iIg==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] requiresBuild: true optional: true - /@esbuild/sunos-x64/0.17.11: - resolution: {integrity: sha512-1/gxTifDC9aXbV2xOfCbOceh5AlIidUrPsMpivgzo8P8zUtczlq1ncFpeN1ZyQJ9lVs2hILy1PG5KPp+w8QPPg==} + /@esbuild/sunos-x64/0.17.8: + resolution: {integrity: sha512-0xvOTNuPXI7ft1LYUgiaXtpCEjp90RuBBYovdd2lqAFxje4sEucurg30M1WIm03+3jxByd3mfo+VUmPtRSVuOw==} engines: {node: '>=12'} cpu: [x64] os: [sunos] requiresBuild: true optional: true - /@esbuild/win32-arm64/0.17.11: - resolution: {integrity: sha512-vtSfyx5yRdpiOW9yp6Ax0zyNOv9HjOAw8WaZg3dF5djEHKKm3UnoohftVvIJtRh0Ec7Hso0RIdTqZvPXJ7FdvQ==} + /@esbuild/win32-arm64/0.17.8: + resolution: {integrity: sha512-G0JQwUI5WdEFEnYNKzklxtBheCPkuDdu1YrtRrjuQv30WsYbkkoixKxLLv8qhJmNI+ATEWquZe/N0d0rpr55Mg==} engines: {node: '>=12'} cpu: [arm64] os: [win32] requiresBuild: true optional: true - /@esbuild/win32-ia32/0.17.11: - resolution: {integrity: sha512-GFPSLEGQr4wHFTiIUJQrnJKZhZjjq4Sphf+mM76nQR6WkQn73vm7IsacmBRPkALfpOCHsopSvLgqdd4iUW2mYw==} + /@esbuild/win32-ia32/0.17.8: + resolution: {integrity: sha512-Fqy63515xl20OHGFykjJsMnoIWS+38fqfg88ClvPXyDbLtgXal2DTlhb1TfTX34qWi3u4I7Cq563QcHpqgLx8w==} engines: {node: '>=12'} cpu: [ia32] os: [win32] requiresBuild: true optional: true - /@esbuild/win32-x64/0.17.11: - resolution: {integrity: sha512-N9vXqLP3eRL8BqSy8yn4Y98cZI2pZ8fyuHx6lKjiG2WABpT2l01TXdzq5Ma2ZUBzfB7tx5dXVhge8X9u0S70ZQ==} + /@esbuild/win32-x64/0.17.8: + resolution: {integrity: sha512-1iuezdyDNngPnz8rLRDO2C/ZZ/emJLb72OsZeqQ6gL6Avko/XCXZw+NuxBSNhBAP13Hie418V7VMt9et1FMvpg==} engines: {node: '>=12'} cpu: [x64] os: [win32] requiresBuild: true optional: true - /@eslint-community/eslint-utils/4.2.0_eslint@8.35.0: - resolution: {integrity: sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==} + /@eslint-community/eslint-utils/4.1.2_eslint@8.35.0: + resolution: {integrity: sha512-7qELuQWWjVDdVsFQ5+beUl+KPczrEDA7S3zM4QUd/bJl7oXgsmpXaEVqrRTnOBqenOV4rWf2kVZk2Ot085zPWA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: eslint: 8.35.0 eslint-visitor-keys: 3.3.0 @@ -453,7 +461,7 @@ packages: '@types/body-parser': 1.19.2 '@types/express-serve-static-core': 4.17.33 '@types/qs': 6.9.7 - '@types/serve-static': 1.15.1 + '@types/serve-static': 1.15.0 dev: true /@types/json-schema/7.0.11: @@ -474,9 +482,19 @@ packages: resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} dev: true + /@types/mongodb/4.0.7: + resolution: {integrity: sha512-lPUYPpzA43baXqnd36cZ9xxorprybxXDzteVKCPAdp14ppHtFJHnXYvNpmBvtMUTb5fKXVv6sVbzo1LHkWhJlw==} + deprecated: mongodb provides its own types. @types/mongodb is no longer needed. + dependencies: + mongodb: 5.1.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - mongodb-client-encryption + - snappy + dev: true + /@types/node/18.14.6: resolution: {integrity: sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA==} - dev: true /@types/normalize-package-data/2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -493,8 +511,8 @@ packages: resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} dev: true - /@types/serve-static/1.15.1: - resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} + /@types/serve-static/1.15.0: + resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==} dependencies: '@types/mime': 3.0.1 '@types/node': 18.14.6 @@ -504,6 +522,15 @@ packages: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: true + /@types/webidl-conversions/7.0.0: + resolution: {integrity: sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==} + + /@types/whatwg-url/8.2.2: + resolution: {integrity: sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==} + dependencies: + '@types/node': 18.14.6 + '@types/webidl-conversions': 7.0.0 + /@typescript-eslint/eslint-plugin/5.54.0_6mj2wypvdnknez7kws2nfdgupi: resolution: {integrity: sha512-+hSN9BdSr629RF02d7mMtXhAJvDTyCbprNYJKrXETlul/Aml6YZwd90XioVbjejQeHbb3R8Dg0CkRgoJDxo8aw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -831,6 +858,14 @@ packages: fill-range: 7.0.1 dev: true + /bson/5.0.1: + resolution: {integrity: sha512-y09gBGusgHtinMon/GVbv1J6FrXhnr/+6hqLlSmEFzkz6PodqF6TxjyvfvY3AfO+oG1mgUtbC86xSbOlwvM62Q==} + engines: {node: '>=14.20.1'} + + /buffer-equal-constant-time/1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + dev: true + /buffer-from/1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: false @@ -846,13 +881,13 @@ packages: semver: 7.3.8 dev: true - /bundle-require/4.0.1_esbuild@0.17.11: + /bundle-require/4.0.1_esbuild@0.17.8: resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} peerDependencies: esbuild: '>=0.17' dependencies: - esbuild: 0.17.11 + esbuild: 0.17.8 load-tsconfig: 0.2.3 dev: true @@ -1152,6 +1187,12 @@ packages: engines: {node: '>=12'} dev: false + /ecdsa-sig-formatter/1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + /ee-first/1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false @@ -1195,7 +1236,7 @@ packages: has-proto: 1.0.1 has-symbols: 1.0.3 internal-slot: 1.0.5 - is-array-buffer: 3.0.2 + is-array-buffer: 3.0.1 is-callable: 1.2.7 is-negative-zero: 2.0.2 is-regex: 1.1.4 @@ -1239,34 +1280,34 @@ packages: is-symbol: 1.0.4 dev: true - /esbuild/0.17.11: - resolution: {integrity: sha512-pAMImyokbWDtnA/ufPxjQg0fYo2DDuzAlqwnDvbXqHLphe+m80eF++perYKVm8LeTuj2zUuFXC+xgSVxyoHUdg==} + /esbuild/0.17.8: + resolution: {integrity: sha512-g24ybC3fWhZddZK6R3uD2iF/RIPnRpwJAqLov6ouX3hMbY4+tKolP0VMF3zuIYCaXun+yHwS5IPQ91N2BT191g==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.17.11 - '@esbuild/android-arm64': 0.17.11 - '@esbuild/android-x64': 0.17.11 - '@esbuild/darwin-arm64': 0.17.11 - '@esbuild/darwin-x64': 0.17.11 - '@esbuild/freebsd-arm64': 0.17.11 - '@esbuild/freebsd-x64': 0.17.11 - '@esbuild/linux-arm': 0.17.11 - '@esbuild/linux-arm64': 0.17.11 - '@esbuild/linux-ia32': 0.17.11 - '@esbuild/linux-loong64': 0.17.11 - '@esbuild/linux-mips64el': 0.17.11 - '@esbuild/linux-ppc64': 0.17.11 - '@esbuild/linux-riscv64': 0.17.11 - '@esbuild/linux-s390x': 0.17.11 - '@esbuild/linux-x64': 0.17.11 - '@esbuild/netbsd-x64': 0.17.11 - '@esbuild/openbsd-x64': 0.17.11 - '@esbuild/sunos-x64': 0.17.11 - '@esbuild/win32-arm64': 0.17.11 - '@esbuild/win32-ia32': 0.17.11 - '@esbuild/win32-x64': 0.17.11 + '@esbuild/android-arm': 0.17.8 + '@esbuild/android-arm64': 0.17.8 + '@esbuild/android-x64': 0.17.8 + '@esbuild/darwin-arm64': 0.17.8 + '@esbuild/darwin-x64': 0.17.8 + '@esbuild/freebsd-arm64': 0.17.8 + '@esbuild/freebsd-x64': 0.17.8 + '@esbuild/linux-arm': 0.17.8 + '@esbuild/linux-arm64': 0.17.8 + '@esbuild/linux-ia32': 0.17.8 + '@esbuild/linux-loong64': 0.17.8 + '@esbuild/linux-mips64el': 0.17.8 + '@esbuild/linux-ppc64': 0.17.8 + '@esbuild/linux-riscv64': 0.17.8 + '@esbuild/linux-s390x': 0.17.8 + '@esbuild/linux-x64': 0.17.8 + '@esbuild/netbsd-x64': 0.17.8 + '@esbuild/openbsd-x64': 0.17.8 + '@esbuild/sunos-x64': 0.17.8 + '@esbuild/win32-arm64': 0.17.8 + '@esbuild/win32-ia32': 0.17.8 + '@esbuild/win32-x64': 0.17.8 /escape-html/1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} @@ -1384,7 +1425,7 @@ packages: object.values: 1.1.6 resolve: 1.22.1 semver: 6.3.0 - tsconfig-paths: 3.14.2 + tsconfig-paths: 3.14.1 transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -1474,11 +1515,11 @@ packages: eslint: '>=8.28.0' dependencies: '@babel/helper-validator-identifier': 7.19.1 - '@eslint-community/eslint-utils': 4.2.0_eslint@8.35.0 + '@eslint-community/eslint-utils': 4.1.2_eslint@8.35.0 ci-info: 3.8.0 clean-regexp: 1.0.0 eslint: 8.35.0 - esquery: 1.5.0 + esquery: 1.4.2 indent-string: 4.0.0 is-builtin-module: 3.2.1 jsesc: 3.0.2 @@ -1613,7 +1654,7 @@ packages: eslint-utils: 3.0.0_eslint@8.35.0 eslint-visitor-keys: 3.3.0 espree: 9.4.1 - esquery: 1.5.0 + esquery: 1.4.2 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 @@ -1658,8 +1699,8 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /esquery/1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + /esquery/1.4.2: + resolution: {integrity: sha512-JVSoLdTlTDkmjFmab7H/9SL9qGSyjElT3myyKp7krqjVFQCDLmj1QFaCLRFBszBKI0XVZaiiXvuPIX3ZwHe1Ng==} engines: {node: '>=0.10'} dependencies: estraverse: 5.3.0 @@ -2147,7 +2188,6 @@ packages: /ip/2.0.0: resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} - dev: false /ipaddr.js/1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} @@ -2165,8 +2205,8 @@ packages: is-decimal: 1.0.4 dev: true - /is-array-buffer/3.0.2: - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + /is-array-buffer/3.0.1: + resolution: {integrity: sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==} dependencies: call-bind: 1.0.2 get-intrinsic: 1.2.0 @@ -2396,6 +2436,31 @@ packages: semver: 7.3.8 dev: true + /jsonwebtoken/9.0.0: + resolution: {integrity: sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==} + engines: {node: '>=12', npm: '>=6'} + dependencies: + jws: 3.2.2 + lodash: 4.17.21 + ms: 2.1.3 + semver: 7.3.8 + dev: true + + /jwa/1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: true + + /jws/3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + dev: true + /keyv/4.5.2: resolution: {integrity: sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==} dependencies: @@ -2410,8 +2475,8 @@ packages: type-check: 0.4.0 dev: true - /lilconfig/2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + /lilconfig/2.0.6: + resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} engines: {node: '>=10'} dev: true @@ -2467,8 +2532,8 @@ packages: dependencies: yallist: 4.0.0 - /lru-cache/7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + /lru-cache/7.18.1: + resolution: {integrity: sha512-8/HcIENyQnfUTCDizRu9rrDyG6XG/21M4X7/YEGZeD76ZJilFPAUVb/2zysFf7VVO1LEjCDFyHp8pMMvozIrvg==} engines: {node: '>=12'} dev: true @@ -2493,6 +2558,10 @@ packages: engines: {node: '>= 0.6'} dev: false + /memory-pager/1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + optional: true + /merge-descriptors/1.0.1: resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} dev: false @@ -2583,6 +2652,33 @@ packages: engines: {node: '>=8'} dev: true + /mongodb-connection-string-url/2.6.0: + resolution: {integrity: sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==} + dependencies: + '@types/whatwg-url': 8.2.2 + whatwg-url: 11.0.0 + + /mongodb/5.1.0: + resolution: {integrity: sha512-qgKb7y+EI90y4weY3z5+lIgm8wmexbonz0GalHkSElQXVKtRuwqXuhXKccyvIjXCJVy9qPV82zsinY0W1FBnJw==} + engines: {node: '>=14.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.201.0 + mongodb-client-encryption: ^2.3.0 + snappy: ^7.2.2 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + dependencies: + bson: 5.0.1 + mongodb-connection-string-url: 2.6.0 + socks: 2.7.1 + optionalDependencies: + saslprep: 1.0.3 + /ms/2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: false @@ -2640,6 +2736,11 @@ packages: formdata-polyfill: 4.0.10 dev: false + /nodemailer/6.9.1: + resolution: {integrity: sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA==} + engines: {node: '>=6.0.0'} + dev: false + /normalize-package-data/2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -2853,7 +2954,7 @@ packages: resolution: {integrity: sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA==} engines: {node: '>=14'} dependencies: - lru-cache: 7.18.3 + lru-cache: 7.18.1 minipass: 4.2.4 dev: true @@ -2893,7 +2994,7 @@ packages: ts-node: optional: true dependencies: - lilconfig: 2.1.0 + lilconfig: 2.0.6 yaml: 1.10.2 dev: true @@ -3060,16 +3161,16 @@ packages: glob: 7.2.3 dev: true - /rimraf/4.3.0: - resolution: {integrity: sha512-5qVDXPbByA1qSJEWMv1qAwKsoS22vVpsL2QyxCKBw4gf6XiFo1K3uRLY6uSOOBFDwnqAZtnbILqWKKlzh8bkGg==} + /rimraf/4.3.1: + resolution: {integrity: sha512-GfHJHBzFQra23IxDzIdBqhOWfbtdgS1/dCHrDy+yvhpoJY5TdwdT28oWaHWfRpKFDLd3GZnGTx6Mlt4+anbsxQ==} engines: {node: '>=14'} hasBin: true dependencies: glob: 9.2.1 dev: true - /rollup/3.18.0: - resolution: {integrity: sha512-J8C6VfEBjkvYPESMQYxKHxNOh4A5a3FlP+0BETGo34HEcE4eTlgCrO2+eWzlu2a/sHs2QUkZco+wscH7jhhgWg==} + /rollup/3.15.0: + resolution: {integrity: sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -3084,7 +3185,6 @@ packages: /safe-buffer/5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: false /safe-regex-test/1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} @@ -3104,6 +3204,14 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: false + /saslprep/1.0.3: + resolution: {integrity: sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + sparse-bitfield: 3.0.3 + optional: true + /semver/5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} hasBin: true @@ -3189,7 +3297,6 @@ packages: /smart-buffer/4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - dev: false /socks-proxy-agent/7.0.0: resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} @@ -3208,7 +3315,6 @@ packages: dependencies: ip: 2.0.0 smart-buffer: 4.2.0 - dev: false /source-map-support/0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -3229,6 +3335,12 @@ packages: whatwg-url: 7.1.0 dev: true + /sparse-bitfield/3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + dependencies: + memory-pager: 1.5.0 + optional: true + /spdx-correct/3.1.1: resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} dependencies: @@ -3371,6 +3483,12 @@ packages: punycode: 2.3.0 dev: true + /tr46/3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + dependencies: + punycode: 2.3.0 + /tree-kill/1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -3380,8 +3498,8 @@ packages: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true - /tsconfig-paths/3.14.2: - resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} + /tsconfig-paths/3.14.1: + resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} dependencies: '@types/json5': 0.0.29 json5: 1.0.2 @@ -3409,17 +3527,17 @@ packages: typescript: optional: true dependencies: - bundle-require: 4.0.1_esbuild@0.17.11 + bundle-require: 4.0.1_esbuild@0.17.8 cac: 6.7.14 chokidar: 3.5.3 debug: 4.3.4 - esbuild: 0.17.11 + esbuild: 0.17.8 execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 postcss-load-config: 3.1.4 resolve-from: 5.0.0 - rollup: 3.18.0 + rollup: 3.15.0 source-map: 0.8.0-beta.0 sucrase: 3.29.0 tree-kill: 1.2.2 @@ -3560,7 +3678,7 @@ packages: eslint-scope: 7.1.1 eslint-visitor-keys: 3.3.0 espree: 9.4.1 - esquery: 1.5.0 + esquery: 1.4.2 lodash: 4.17.21 semver: 7.3.8 transitivePeerDependencies: @@ -3580,10 +3698,21 @@ packages: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} dev: true + /webidl-conversions/7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + /whatwg-fetch/3.6.2: resolution: {integrity: sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==} dev: false + /whatwg-url/11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + /whatwg-url/5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: diff --git a/service/src/index.ts b/service/src/index.ts index 402deaa424..b3dbe81e01 100644 --- a/service/src/index.ts +++ b/service/src/index.ts @@ -1,7 +1,13 @@ import express from 'express' +import jwt from 'jsonwebtoken' import type { ChatContext, ChatMessage } from './chatgpt' import { chatConfig, chatReplyProcess } from './chatgpt' import { auth } from './middleware/auth' +import type { ChatOptions } from './storage/model' +import { Status } from './storage/model' +import { clearChat, createChatRoom, createUser, deleteChat, deleteChatRoom, existsChatRoom, getChat, getChatRooms, getChats, getUser, insertChat, renameChatRoom, updateChat, verifyUser } from './storage/mongo' +import { sendMail } from './utils/mail' +import { checkUserVerify, getUserVerifyUrl, md5 } from './utils/security' const app = express() const router = express.Router() @@ -16,16 +22,147 @@ app.all('*', (_, res, next) => { next() }) +router.get('/chatrooms', auth, async (req, res) => { + const userId = req.headers.userId + const rooms = await getChatRooms(userId) + const result = [] + rooms.forEach((r) => { + result.push({ + uuid: r.roomId, + title: r.title, + isEdit: false, + }) + }) + res.send({ status: 'Success', message: null, data: result }) +}) + +router.post('/room-create', auth, async (req, res) => { + const userId = req.headers.userId + const { title, roomId } = req.body as { title: string; roomId: number } + const room = await createChatRoom(userId, title, roomId) + res.send({ status: 'Success', message: null, data: room }) +}) + +router.post('/room-rename', auth, async (req, res) => { + const userId = req.headers.userId + const { title, roomId } = req.body as { title: string; roomId: number } + const room = await renameChatRoom(userId, title, roomId) + res.send({ status: 'Success', message: null, data: room }) +}) + +router.post('/room-delete', auth, async (req, res) => { + const userId = req.headers.userId + const { roomId } = req.body as { roomId: number } + if (!roomId || !await existsChatRoom(userId, roomId)) { + res.send({ status: 'Fail', message: 'Unknow room', data: null }) + return + } + await deleteChatRoom(userId, roomId) + res.send({ status: 'Success', message: null }) +}) + +router.get('/chat-hisroty', auth, async (req, res) => { + const userId = req.headers.userId + const roomId = +req.query.roomid + const lastTime = req.query.lasttime + if (!roomId || !await existsChatRoom(userId, roomId)) { + res.send({ status: 'Success', message: null, data: [] }) + // res.send({ status: 'Fail', message: 'Unknow room', data: null }) + return + } + const chats = await getChats(roomId, !lastTime ? null : parseInt(lastTime)) + + const result = [] + chats.forEach((c) => { + if (c.status !== Status.InversionDeleted) { + result.push({ + dateTime: new Date(c.dateTime).toLocaleString(), + text: c.prompt, + inversion: true, + error: false, + conversationOptions: null, + requestOptions: { + prompt: c.prompt, + options: null, + }, + }) + } + if (c.status !== Status.ResponseDeleted) { + result.push({ + dateTime: new Date(c.dateTime).toLocaleString(), + text: c.response, + inversion: false, + error: false, + loading: false, + conversationOptions: { + parentMessageId: c.options.messageId, + }, + requestOptions: { + prompt: c.prompt, + parentMessageId: c.options.parentMessageId, + }, + }) + } + }) + + res.send({ status: 'Success', message: null, data: result }) +}) + +router.post('/chat-delete', auth, async (req, res) => { + const userId = req.headers.userId + const { roomId, uuid, inversion } = req.body as { roomId: number; uuid: number; inversion: boolean } + if (!roomId || !await existsChatRoom(userId, roomId)) { + res.send({ status: 'Fail', message: 'Unknow room', data: null }) + return + } + await deleteChat(roomId, uuid, inversion) + res.send({ status: 'Success', message: null, data: null }) +}) + +router.post('/chat-clear', auth, async (req, res) => { + const userId = req.headers.userId + const { roomId } = req.body as { roomId: number } + if (!roomId || !await existsChatRoom(userId, roomId)) { + res.send({ status: 'Fail', message: 'Unknow room', data: null }) + return + } + await clearChat(roomId) + res.send({ status: 'Success', message: null, data: null }) +}) + +router.post('/chat', auth, async (req, res) => { + try { + const { roomId, uuid, regenerate, prompt, options = {} } = req.body as + { roomId: number; uuid: number; regenerate: boolean; prompt: string; options?: ChatContext } + const message = regenerate + ? await getChat(roomId, uuid) + : await insertChat(uuid, prompt, roomId, options as ChatOptions) + const response = await chatReply(prompt, options) + if (response.status === 'Success') + await updateChat(message._id, response.data.text, response.data.id) + res.send(response) + } + catch (error) { + res.send(error) + } +}) + router.post('/chat-process', auth, async (req, res) => { res.setHeader('Content-type', 'application/octet-stream') try { - const { prompt, options = {} } = req.body as { prompt: string; options?: ChatContext } + const { roomId, uuid, regenerate, prompt, options = {} } = req.body as + { roomId: number; uuid: number; regenerate: boolean; prompt: string; options?: ChatContext } + const message = regenerate + ? await getChat(roomId, uuid) + : await insertChat(uuid, prompt, roomId, options as ChatOptions) let firstChunk = true - await chatReplyProcess(prompt, options, (chat: ChatMessage) => { + const result = await chatReplyProcess(prompt, options, (chat: ChatMessage) => { res.write(firstChunk ? JSON.stringify(chat) : `\n${JSON.stringify(chat)}`) firstChunk = false }) + if (result.status === 'Success') + await updateChat(message._id, result.data.text, result.data.id) } catch (error) { res.write(JSON.stringify(error)) @@ -35,6 +172,45 @@ router.post('/chat-process', auth, async (req, res) => { } }) +router.post('/user-register', async (req, res) => { + const { username, password } = req.body as { username: string; password: string } + + if (process.env.REGISTER_ENABLED !== 'true') { + res.send({ status: 'Fail', message: '注册账号功能未启用 | Register account is disabled!', data: null }) + return + } + if (typeof process.env.REGISTER_MAILS === 'string' && process.env.REGISTER_MAILS.length > 0) { + let allowSuffix = false + const emailSuffixs = process.env.REGISTER_MAILS.split(',') + for (let index = 0; index < emailSuffixs.length; index++) { + const element = emailSuffixs[index] + allowSuffix = username.toLowerCase().endsWith(element) + if (allowSuffix) + break + } + if (!allowSuffix) { + res.send({ status: 'Fail', message: '该邮箱后缀不支持 | The email service provider is not allowed', data: null }) + return + } + } + + const user = await getUser(username) + if (user != null) { + res.send({ status: 'Fail', message: '邮箱已存在 | The email exists', data: null }) + return + } + const newPassword = md5(password) + await createUser(username, newPassword) + + if (username.toLowerCase() === process.env.ROOT_USER) { + res.send({ status: 'Success', message: '注册成功 | Register success', data: null }) + } + else { + sendMail(username, getUserVerifyUrl(username)) + res.send({ status: 'Success', message: '注册成功, 去邮箱中验证吧 | Registration is successful, you need to go to email verification', data: null }) + } +}) + router.post('/config', async (req, res) => { try { const response = await chatConfig() @@ -49,7 +225,31 @@ router.post('/session', async (req, res) => { try { const AUTH_SECRET_KEY = process.env.AUTH_SECRET_KEY const hasAuth = typeof AUTH_SECRET_KEY === 'string' && AUTH_SECRET_KEY.length > 0 - res.send({ status: 'Success', message: '', data: { auth: hasAuth } }) + const allowRegister = process.env.REGISTER_ENABLED === 'true' + res.send({ status: 'Success', message: '', data: { auth: hasAuth, allowRegister } }) + } + catch (error) { + res.send({ status: 'Fail', message: error.message, data: null }) + } +}) + +router.post('/user-login', async (req, res) => { + try { + const { username, password } = req.body as { username: string; password: string } + if (!username || !password) + throw new Error('用户名或密码为空 | Username or password is empty') + + const user = await getUser(username) + if (user == null + || user.status !== Status.Normal + || user.password !== md5(password)) { + if (user != null && user.status === Status.PreVerify) + throw new Error('请去邮箱中验证 | Please verify in the mailbox') + throw new Error('用户不存在或密码错误 | User does not exist or incorrect password.') + } + + const token = jwt.sign({ email: username, userId: user._id }, process.env.AUTH_SECRET_KEY) + res.send({ status: 'Success', message: '登录成功 | Login successfully', data: { token } }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) @@ -61,11 +261,9 @@ router.post('/verify', async (req, res) => { const { token } = req.body as { token: string } if (!token) throw new Error('Secret key is empty') - - if (process.env.AUTH_SECRET_KEY !== token) - throw new Error('密钥无效 | Secret key is invalid') - - res.send({ status: 'Success', message: 'Verify successfully', data: null }) + const username = await checkUserVerify(token) + await verifyUser(username) + res.send({ status: 'Success', message: '验证成功 | Verify successfully', data: null }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) diff --git a/service/src/middleware/auth.ts b/service/src/middleware/auth.ts index 50bf02a257..6f1dba0a3f 100644 --- a/service/src/middleware/auth.ts +++ b/service/src/middleware/auth.ts @@ -1,10 +1,12 @@ +import jwt from 'jsonwebtoken' + const auth = async (req, res, next) => { const AUTH_SECRET_KEY = process.env.AUTH_SECRET_KEY if (typeof AUTH_SECRET_KEY === 'string' && AUTH_SECRET_KEY.length > 0) { try { - const Authorization = req.header('Authorization') - if (!Authorization || Authorization.replace('Bearer ', '').trim() !== AUTH_SECRET_KEY.trim()) - throw new Error('Error: 无访问权限 | No access rights') + const token = req.header('Authorization').replace('Bearer ', '') + const info = jwt.verify(token, AUTH_SECRET_KEY.trim()) + req.headers.userId = info.userId next() } catch (error) { @@ -12,6 +14,8 @@ const auth = async (req, res, next) => { } } else { + // fake userid + req.headers.userId = '6406d8c50aedd633885fa16f' next() } } diff --git a/service/src/storage/model.ts b/service/src/storage/model.ts new file mode 100644 index 0000000000..14786fa4b5 --- /dev/null +++ b/service/src/storage/model.ts @@ -0,0 +1,81 @@ +import type { ObjectId } from 'mongodb' + +enum Status { + Normal = 0, + Deleted = 1, + InversionDeleted = 2, + ResponseDeleted = 3, + PreVerify = 4, +} + +class UserInfo { + _id: ObjectId + name: string + email: string + password: string + status: Status + createTime: string + verifyTime?: string + constructor(email: string, password: string) { + this.name = email + this.email = email + this.password = password + this.status = Status.PreVerify + this.createTime = new Date().toLocaleString() + this.verifyTime = null + } +} + +class UserOauth { + userId: number + oauthType: OauthType + oauthId: string + + constructor(userId: number, oauthType: OauthType, oauthId: string) { + this.userId = userId + this.oauthType = oauthType + this.oauthId = oauthId + } +} + +class ChatRoom { + _id: ObjectId + roomId: number + userId: number + title: string + status: Status = Status.Normal + constructor(userId: number, title: string, roomId: number) { + this.userId = userId + this.title = title + this.roomId = roomId + } +} + +class ChatOptions { + parentMessageId?: string + messageId?: string + constructor(parentMessageId?: string, messageId?: string) { + this.parentMessageId = parentMessageId + this.messageId = messageId + } +} + +class ChatInfo { + _id: ObjectId + roomId: number + uuid: number + dateTime: number + prompt: string + response?: string + status: Status = Status.Normal + options: ChatOptions + constructor(roomId: number, uuid: number, prompt: string, options: ChatOptions) { + this.roomId = roomId + this.uuid = uuid + this.prompt = prompt + this.options = options + this.dateTime = new Date().getTime() + } +} + +export { UserInfo, UserOauth, ChatRoom, ChatInfo, ChatOptions, Status } diff --git a/service/src/storage/mongo.ts b/service/src/storage/mongo.ts new file mode 100644 index 0000000000..fa5762383f --- /dev/null +++ b/service/src/storage/mongo.ts @@ -0,0 +1,143 @@ +import { MongoClient, ObjectId } from 'mongodb' +import { ChatInfo, ChatRoom, Status, UserInfo } from './model' +import type { ChatOptions } from './model' + +const url = process.env.MONGODB_URL +const client = new MongoClient(url) +const chatCol = client.db('chatgpt').collection('chat') +const roomCol = client.db('chatgpt').collection('chat_room') +const userCol = client.db('chatgpt').collection('user') + +/** + * 插入聊天信息 + * @param text 内容 prompt or response + * @param roomId + * @param options + * @returns model + */ +export async function insertChat(uuid: number, text: string, roomId: number, options?: ChatOptions) { + const chatInfo = new ChatInfo(roomId, uuid, text, options) + await chatCol.insertOne(chatInfo) + return chatInfo +} + +export async function getChat(roomId: number, uuid: number) { + return await chatCol.findOne({ roomId, uuid }) +} + +export async function updateChat(chatId: string, response: string, messageId: string) { + const query = { _id: new ObjectId(chatId) } + const update = { + $set: { 'response': response, 'options.messageId': messageId }, + } + await chatCol.updateOne(query, update) +} + +export async function createChatRoom(userId: ObjectId, title: string, roomId: number) { + const room = new ChatRoom(userId, title, roomId) + await roomCol.insertOne(room) + return room +} +export async function renameChatRoom(userId: ObjectId, title: string, roomId: number) { + const query = { userId, roomId } + const update = { + $set: { + title, + }, + } + const result = await roomCol.updateOne(query, update) + return result +} + +export async function deleteChatRoom(userId: ObjectId, roomId: number) { + const result = await roomCol.updateOne({ roomId, userId }, { $set: { status: Status.Deleted } }) + await clearChat(roomId) + return result +} + +export async function getChatRooms(userId: ObjectId) { + const cursor = await roomCol.find({ userId, status: { $ne: Status.Deleted } }) + const rooms = [] + await cursor.forEach(doc => rooms.push(doc)) + return rooms +} + +export async function existsChatRoom(userId: ObjectId, roomId: number) { + const room = await roomCol.findOne({ roomId, userId }) + return !!room +} + +export async function getChats(roomId: number, lastTime?: number) { + if (!lastTime) + lastTime = new Date().getTime() + const query = { roomId, dateTime: { $lt: lastTime }, status: { $ne: Status.Deleted } } + const sort = { dateTime: -1 } + const limit = 200 + const cursor = await chatCol.find(query).sort(sort).limit(limit) + const chats = [] + await cursor.forEach(doc => chats.push(doc)) + chats.reverse() + return chats +} + +export async function clearChat(roomId: number) { + const query = { roomId } + const update = { + $set: { + status: Status.Deleted, + }, + } + await chatCol.updateMany(query, update) +} + +export async function deleteChat(roomId: number, uuid: number, inversion: boolean) { + const query = { roomId, uuid } + let update = { + $set: { + status: Status.Deleted, + }, + } + const chat = await chatCol.findOne(query) + if (chat.status === Status.InversionDeleted && !inversion) { /* empty */ } + else if (chat.status === Status.ResponseDeleted && inversion) { /* empty */ } + else if (inversion) { + update = { + $set: { + status: Status.InversionDeleted, + }, + } + } + else { + update = { + $set: { + status: Status.ResponseDeleted, + }, + } + } + chatCol.updateOne(query, update) +} + +export async function createUser(email: string, password: string) { + email = email.toLowerCase() + const userInfo = new UserInfo(email, password) + if (email === process.env.ROOT_USER) + userInfo.status = Status.Normal + + await userCol.insertOne(userInfo) + return userInfo +} + +export async function updateUserName(userId: ObjectId, name: string) { + const result = userCol.updateOne({ _id: userId }, { name: { $set: name } }) + return result +} + +export async function getUser(email: string) { + email = email.toLowerCase() + return await userCol.findOne({ email }) +} + +export async function verifyUser(email: string) { + email = email.toLowerCase() + return await userCol.updateOne({ email }, { $set: { status: Status.Normal, verifyTime: new Date().toLocaleString() } }) +} diff --git a/service/src/types.ts b/service/src/types.ts index 995894cd4c..5eb450729c 100644 --- a/service/src/types.ts +++ b/service/src/types.ts @@ -20,6 +20,7 @@ export interface ModelConfig { timeoutMs?: number socksProxy?: string httpsProxy?: string + allowRegister?: boolean } export type ApiModel = 'ChatGPTAPI' | 'ChatGPTUnofficialProxyAPI' | undefined diff --git a/service/src/utils/mail.ts b/service/src/utils/mail.ts new file mode 100644 index 0000000000..1256fd4fec --- /dev/null +++ b/service/src/utils/mail.ts @@ -0,0 +1,30 @@ +import nodemailer from 'nodemailer' + +// create reusable transporter object using SMTP transport +const transporter = nodemailer.createTransport({ + host: process.env.SMTP_HOST, + port: process.env.SMTP_PORT, + secure: process.env.SMTP_TSL, + auth: { + user: process.env.SMTP_USERNAME, + pass: process.env.SMTP_PASSWORD, + }, +}) + +export function sendMail(toMail: string, verifyUrl: string) { + const mailOptions = { + from: process.env.SMTP_USERNAME, // sender address + to: toMail, // list of receivers + subject: '账号验证', // Subject line + // text: 'Hello world?', // plain text body + html: `

你正在注册网站,你的邮箱验证链接为(12小时内有效)



点我验证`, // html body + } + + // send mail with defined transport object + transporter.sendMail(mailOptions, (error, info) => { + if (error) + throw error + else + return info.messageId + }) +} diff --git a/service/src/utils/security.ts b/service/src/utils/security.ts new file mode 100644 index 0000000000..965d97f6cf --- /dev/null +++ b/service/src/utils/security.ts @@ -0,0 +1,32 @@ +import { createHash } from 'crypto' + +export function md5(input: string) { + input = input + process.env.PASSWORD_MD5_SALT + const md5 = createHash('md5') + md5.update(input) + return md5.digest('hex') +} + +// 可以换 aes 等方式 +export function getUserVerifyUrl(username: string) { + const sign = getUserVerify(username) + return `${process.env.SITE_DOMAIN}/#/chat/?verifytoken=${sign}` +} + +function getUserVerify(username: string) { + const expired = new Date().getTime() + (12 * 60 * 60 * 1000) + const sign = `${username}-${expired}` + return `${sign}-${md5(sign)}` +} + +export function checkUserVerify(verify: string) { + const vs = verify.split('-') + const sign = vs[vs.length - 1] + const expired = vs[vs.length - 2] + vs.splice(vs.length - 2, 2) + const username = vs.join('-') + // 简单点没校验有效期 + if (sign === md5(`${username}-${expired}`)) + return username + throw new Error('Verify failed') +} diff --git a/src/api/index.ts b/src/api/index.ts index e576ab1319..5f7e324c6d 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,5 +1,5 @@ import type { AxiosProgressEvent, GenericAbortSignal } from 'axios' -import { post } from '@/utils/request' +import { get, post } from '@/utils/request' export function fetchChatAPI( prompt: string, @@ -21,6 +21,9 @@ export function fetchChatConfig() { export function fetchChatAPIProcess( params: { + roomId: number + uuid: number + regenerate?: boolean prompt: string options?: { conversationId?: string; parentMessageId?: string } signal?: GenericAbortSignal @@ -28,7 +31,7 @@ export function fetchChatAPIProcess( ) { return post({ url: '/chat-process', - data: { prompt: params.prompt, options: params.options }, + data: { roomId: params.roomId, uuid: params.uuid, regenerate: params.regenerate || false, prompt: params.prompt, options: params.options }, signal: params.signal, onDownloadProgress: params.onDownloadProgress, }) @@ -46,3 +49,64 @@ export function fetchVerify(token: string) { data: { token }, }) } + +export function fetchLogin(username: string, password: string) { + return post({ + url: '/user-login', + data: { username, password }, + }) +} + +export function fetchRegister(username: string, password: string) { + return post({ + url: '/user-register', + data: { username, password }, + }) +} + +export function fetchGetChatRooms() { + return get({ + url: '/chatrooms', + }) +} + +export function fetchCreateChatRoom(title: string, roomId: number) { + return post({ + url: '/room-create', + data: { title, roomId }, + }) +} + +export function fetchRenameChatRoom(title: string, roomId: number) { + return post({ + url: '/room-rename', + data: { title, roomId }, + }) +} + +export function fetchDeleteChatRoom(roomId: number) { + return post({ + url: '/room-delete', + data: { roomId }, + }) +} + +export function fetchGetChatHistory(roomId: number) { + return get({ + url: `/chat-hisroty?roomid=${roomId}`, + }) +} + +export function fetchClearChat(roomId: number) { + return post({ + url: '/chat-clear', + data: { roomId }, + }) +} + +export function fetchDeleteChat(roomId: number, uuid: number, inversion?: boolean) { + return post({ + url: '/chat-delete', + data: { roomId, uuid, inversion }, + }) +} diff --git a/src/components/common/UserAvatar/index.vue b/src/components/common/UserAvatar/index.vue index 19b18b4ba0..a6973fa026 100644 --- a/src/components/common/UserAvatar/index.vue +++ b/src/components/common/UserAvatar/index.vue @@ -26,15 +26,12 @@ const userInfo = computed(() => userStore.userInfo)
-

- {{ userInfo.name ?? 'ChenZhaoYu' }} +

+ {{ userInfo.name }} +

+

+ {{ $t('common.notLoggedIn') }}

-

- -

diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index b1b88ba052..3c19d18f9a 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -24,7 +24,10 @@ export default { wrong: 'Something went wrong, please try again later.', success: 'Success', failed: 'Failed', - verify: 'Verify', + register: 'Register', + login: 'Login', + notLoggedIn: 'Login / Register', + logOut: 'Login Out', unauthorizedTips: 'Unauthorized, please verify first.', }, chat: { diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 813b637e6f..5a91770039 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -24,7 +24,10 @@ export default { wrong: '好像出错了,请稍后再试。', success: '操作成功', failed: '操作失败', - verify: '验证', + register: '注册', + login: '登录', + notLoggedIn: '登录 / 注册', + logOut: '退出登录', unauthorizedTips: '未经授权,请先进行验证。', }, chat: { diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 3ed65df9b1..1073821841 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -24,7 +24,10 @@ export default { wrong: '發生錯誤,請稍後再試。', success: '操作成功', failed: '操作失敗', - verify: '驗證', + register: '註冊', + login: '登錄', + notLoggedIn: '登錄 / 註冊', + logOut: '退出登錄', unauthorizedTips: '未經授權,請先進行驗證。', }, chat: { diff --git a/src/store/modules/auth/index.ts b/src/store/modules/auth/index.ts index 5b8c8d83e9..904a4313f9 100644 --- a/src/store/modules/auth/index.ts +++ b/src/store/modules/auth/index.ts @@ -1,11 +1,12 @@ import { defineStore } from 'pinia' +import jwt_decode from 'jwt-decode' import { getToken, removeToken, setToken } from './helper' -import { store } from '@/store' +import { store, useUserStore } from '@/store' import { fetchSession } from '@/api' export interface AuthState { token: string | undefined - session: { auth: boolean } | null + session: { auth: boolean; allowRegister: boolean } | null } export const useAuthStore = defineStore('auth-store', { @@ -17,7 +18,7 @@ export const useAuthStore = defineStore('auth-store', { actions: { async getSession() { try { - const { data } = await fetchSession<{ auth: boolean }>() + const { data } = await fetchSession<{ auth: boolean; allowRegister: boolean }>() this.session = { ...data } return Promise.resolve(data) } @@ -28,11 +29,20 @@ export const useAuthStore = defineStore('auth-store', { setToken(token: string) { this.token = token + const decoded = jwt_decode(token) as { email: string } + const userStore = useUserStore() + userStore.updateUserInfo({ + avatar: '', + name: decoded.email, + description: '', + }) setToken(token) }, removeToken() { this.token = undefined + const userStore = useUserStore() + userStore.resetUserInfo() removeToken() }, }, diff --git a/src/store/modules/chat/index.ts b/src/store/modules/chat/index.ts index 540954de15..9ac4a4175b 100644 --- a/src/store/modules/chat/index.ts +++ b/src/store/modules/chat/index.ts @@ -1,6 +1,7 @@ import { defineStore } from 'pinia' import { getLocalState, setLocalState } from './helper' import { router } from '@/router' +import { fetchClearChat, fetchCreateChatRoom, fetchDeleteChat, fetchDeleteChatRoom, fetchGetChatHistory, fetchGetChatRooms, fetchRenameChatRoom } from '@/api' export const useChatStore = defineStore('chat-store', { state: (): Chat.ChatState => getLocalState(), @@ -23,7 +24,31 @@ export const useChatStore = defineStore('chat-store', { }, actions: { + async syncHistory() { + const rooms = (await fetchGetChatRooms()).data + // if (rooms.length <= 0) + // return + + let uuid = null + this.history = [] + this.chat = [] + await rooms.forEach(async (r: Chat.History) => { + this.history.unshift(r) + uuid = r.uuid + const chatData = (await fetchGetChatHistory(r.uuid)).data + this.chat.unshift({ uuid: r.uuid, data: chatData }) + }) + if (uuid == null) { + uuid = Date.now() + this.addHistory({ title: 'New Chat', uuid: Date.now(), isEdit: false }) + } + + this.active = uuid + this.reloadRoute(uuid) + }, + addHistory(history: Chat.History, chatData: Chat.Chat[] = []) { + fetchCreateChatRoom(history.title, history.uuid) this.history.unshift(history) this.chat.unshift({ uuid: history.uuid, data: chatData }) this.active = history.uuid @@ -35,10 +60,12 @@ export const useChatStore = defineStore('chat-store', { if (index !== -1) { this.history[index] = { ...this.history[index], ...edit } this.recordState() + fetchRenameChatRoom(this.history[index].title, this.history[index].uuid) } }, async deleteHistory(index: number) { + fetchDeleteChatRoom(this.history[index].uuid) this.history.splice(index, 1) this.chat.splice(index, 1) @@ -91,6 +118,7 @@ export const useChatStore = defineStore('chat-store', { if (!uuid || uuid === 0) { if (this.history.length === 0) { const uuid = Date.now() + fetchCreateChatRoom(chat.text, uuid) this.history.push({ uuid, title: chat.text, isEdit: false }) this.chat.push({ uuid, data: [chat] }) this.active = uuid @@ -98,8 +126,10 @@ export const useChatStore = defineStore('chat-store', { } else { this.chat[0].data.push(chat) - if (this.history[0].title === 'New Chat') + if (this.history[0].title === 'New Chat') { this.history[0].title = chat.text + fetchRenameChatRoom(chat.text, this.history[0].uuid) + } this.recordState() } } @@ -107,8 +137,10 @@ export const useChatStore = defineStore('chat-store', { const index = this.chat.findIndex(item => item.uuid === uuid) if (index !== -1) { this.chat[index].data.push(chat) - if (this.history[index].title === 'New Chat') + if (this.history[index].title === 'New Chat') { this.history[index].title = chat.text + fetchRenameChatRoom(chat.text, this.history[index].uuid) + } this.recordState() } }, @@ -116,6 +148,7 @@ export const useChatStore = defineStore('chat-store', { updateChatByUuid(uuid: number, index: number, chat: Chat.Chat) { if (!uuid || uuid === 0) { if (this.chat.length) { + chat.uuid = this.chat[0].data[index].uuid this.chat[0].data[index] = chat this.recordState() } @@ -124,6 +157,7 @@ export const useChatStore = defineStore('chat-store', { const chatIndex = this.chat.findIndex(item => item.uuid === uuid) if (chatIndex !== -1) { + chat.uuid = this.chat[chatIndex].data[index].uuid this.chat[chatIndex].data[index] = chat this.recordState() } @@ -132,6 +166,7 @@ export const useChatStore = defineStore('chat-store', { updateChatSomeByUuid(uuid: number, index: number, chat: Partial) { if (!uuid || uuid === 0) { if (this.chat.length) { + chat.uuid = this.chat[0].data[index].uuid this.chat[0].data[index] = { ...this.chat[0].data[index], ...chat } this.recordState() } @@ -140,6 +175,7 @@ export const useChatStore = defineStore('chat-store', { const chatIndex = this.chat.findIndex(item => item.uuid === uuid) if (chatIndex !== -1) { + chat.uuid = this.chat[chatIndex].data[index].uuid this.chat[chatIndex].data[index] = { ...this.chat[chatIndex].data[index], ...chat } this.recordState() } @@ -148,6 +184,7 @@ export const useChatStore = defineStore('chat-store', { deleteChatByUuid(uuid: number, index: number) { if (!uuid || uuid === 0) { if (this.chat.length) { + fetchDeleteChat(uuid, this.chat[0].data[index].uuid || 0, this.chat[0].data[index].inversion) this.chat[0].data.splice(index, 1) this.recordState() } @@ -156,6 +193,7 @@ export const useChatStore = defineStore('chat-store', { const chatIndex = this.chat.findIndex(item => item.uuid === uuid) if (chatIndex !== -1) { + fetchDeleteChat(uuid, this.chat[chatIndex].data[index].uuid || 0, this.chat[0].data[index].inversion) this.chat[chatIndex].data.splice(index, 1) this.recordState() } @@ -164,6 +202,7 @@ export const useChatStore = defineStore('chat-store', { clearChatByUuid(uuid: number) { if (!uuid || uuid === 0) { if (this.chat.length) { + fetchClearChat(this.chat[0].uuid) this.chat[0].data = [] this.recordState() } @@ -172,6 +211,7 @@ export const useChatStore = defineStore('chat-store', { const index = this.chat.findIndex(item => item.uuid === uuid) if (index !== -1) { + fetchClearChat(uuid) this.chat[index].data = [] this.recordState() } diff --git a/src/store/modules/user/helper.ts b/src/store/modules/user/helper.ts index cf0f04f1dd..8791fd74ed 100644 --- a/src/store/modules/user/helper.ts +++ b/src/store/modules/user/helper.ts @@ -15,9 +15,9 @@ export interface UserState { export function defaultSetting(): UserState { return { userInfo: { - avatar: 'https://raw.githubusercontent.com/Chanzhaoyu/chatgpt-web/main/src/assets/avatar.jpg', - name: 'ChenZhaoYu', - description: 'Star on Github', + avatar: '', + name: '', + description: '', }, } } diff --git a/src/typings/chat.d.ts b/src/typings/chat.d.ts index c0b80c9632..8b2e32f941 100644 --- a/src/typings/chat.d.ts +++ b/src/typings/chat.d.ts @@ -1,6 +1,7 @@ declare namespace Chat { interface Chat { + uuid?: number dateTime: string text: string inversion?: boolean diff --git a/src/views/chat/index.vue b/src/views/chat/index.vue index 9d1e1c691a..c24c0bf104 100644 --- a/src/views/chat/index.vue +++ b/src/views/chat/index.vue @@ -61,9 +61,11 @@ async function onConversation() { controller = new AbortController() + const chatUuid = Date.now() addChat( +uuid, { + uuid: chatUuid, dateTime: new Date().toLocaleString(), text: message, inversion: true, @@ -86,6 +88,7 @@ async function onConversation() { addChat( +uuid, { + uuid: chatUuid, dateTime: new Date().toLocaleString(), text: '', loading: true, @@ -101,6 +104,8 @@ async function onConversation() { let lastText = '' const fetchChatAPIOnce = async () => { await fetchChatAPIProcess({ + roomId: +uuid, + uuid: chatUuid, prompt: message, options, signal: controller.signal, @@ -212,7 +217,7 @@ async function onRegenerate(index: number) { options = { ...requestOptions.options } loading.value = true - + const chatUuid = dataSources.value[index].uuid updateChat( +uuid, index, @@ -231,6 +236,9 @@ async function onRegenerate(index: number) { let lastText = '' const fetchChatAPIOnce = async () => { await fetchChatAPIProcess({ + roomId: +uuid, + uuid: chatUuid || Date.now(), + regenerate: true, prompt: message, options, signal: controller.signal, diff --git a/src/views/chat/layout/Permission.vue b/src/views/chat/layout/Permission.vue index 9e9b26e74e..3fcaa22154 100644 --- a/src/views/chat/layout/Permission.vue +++ b/src/views/chat/layout/Permission.vue @@ -1,7 +1,8 @@ @@ -64,15 +114,38 @@ function handlePress(event: KeyboardEvent) {

- + + + + + + {{ $t('common.register') }} + + + {{ $t('common.login') }} + + - {{ $t('common.verify') }} + {{ $t('common.login') }} diff --git a/src/views/chat/layout/sider/Footer.vue b/src/views/chat/layout/sider/Footer.vue index 583754775c..70a6df1950 100644 --- a/src/views/chat/layout/sider/Footer.vue +++ b/src/views/chat/layout/sider/Footer.vue @@ -1,10 +1,19 @@ + + + + + + + + diff --git a/src/components/common/Setting/model.ts b/src/components/common/Setting/model.ts new file mode 100644 index 0000000000..444d73bb6a --- /dev/null +++ b/src/components/common/Setting/model.ts @@ -0,0 +1,28 @@ +export class ConfigState { + timeoutMs?: number + apiKey?: string + accessToken?: string + apiBaseUrl?: string + apiModel?: string + reverseProxy?: string + socksProxy?: string + httpsProxy?: string + balance?: number + siteConfig?: SiteConfig + mailConfig?: MailConfig +} + +export class SiteConfig { + siteTitle?: string + registerEnabled?: boolean + registerMails?: string + siteDomain?: string +} + +export class MailConfig { + smtpHost?: string + smtpPort?: number + smtpTsl?: boolean + smtpUserName?: string + smtpPassword?: string +} diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 7444804ada..bff87980cc 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -52,7 +52,9 @@ export default { setting: { setting: 'Setting', general: 'General', - config: 'Config', + config: 'Base Config', + siteConfig: 'Site Config', + mailConfig: 'Mail Config', avatarLink: 'Avatar Link', name: 'Name', description: 'Description', @@ -62,10 +64,22 @@ export default { language: 'Language', api: 'API', reverseProxy: 'Reverse Proxy', - timeout: 'Timeout', + timeout: 'Timeout(ms)', socks: 'Socks', httpsProxy: 'HTTPS Proxy', balance: 'API Balance', + smtpHost: 'Host', + smtpPort: 'Port', + smtpTsl: 'Tsl', + smtpUserName: 'UserName', + smtpPassword: 'Password', + siteTitle: 'Title', + siteDomain: 'Domain', + registerEnabled: 'Register Enabled', + registerMails: 'Register Mails', + apiBaseUrl: 'Api Base Url', + apiModel: 'Api Model', + accessToken: 'Access Token', }, store: { local: 'Local', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index ee51bbb361..9825ea02b7 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -52,7 +52,9 @@ export default { setting: { setting: '设置', general: '总览', - config: '配置', + config: '基本配置', + siteConfig: '网站配置', + mailConfig: '邮箱配置', avatarLink: '头像链接', name: '名称', description: '描述', @@ -62,10 +64,22 @@ export default { language: '语言', api: 'API', reverseProxy: '反向代理', - timeout: '超时', + timeout: '超时(ms)', socks: 'Socks', httpsProxy: 'HTTPS Proxy', balance: 'API余额', + smtpHost: 'Host', + smtpPort: 'Port', + smtpTsl: 'Tsl', + smtpUserName: '账号', + smtpPassword: '密码/专用密码', + siteTitle: '网站标题', + siteDomain: '域名 不含/', + registerEnabled: '新用户', + registerMails: '邮箱后缀', + apiBaseUrl: '接口地址', + apiModel: 'Api 模型', + accessToken: 'Access Token', }, store: { local: '本地', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index ef4ca8d4aa..4c293cdff2 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -52,7 +52,9 @@ export default { setting: { setting: '設定', general: '總覽', - config: '設定', + config: '基本設定', + siteConfig: '网站配置', + mailConfig: '邮箱配置', avatarLink: '頭貼連結', name: '名稱', description: '描述', @@ -62,10 +64,22 @@ export default { language: '語言', api: 'API', reverseProxy: '反向代理', - timeout: '逾時', + timeout: '逾時(ms)', socks: 'Socks', httpsProxy: 'HTTPS Proxy', balance: 'API余額', + smtpHost: 'Host', + smtpPort: 'Port', + smtpTsl: 'Tsl', + smtpUserName: '账号', + smtpPassword: '密码/专用密码', + siteTitle: '网站标题', + siteDomain: '域名 不含/', + registerEnabled: '新用户', + registerMails: '邮箱后缀', + apiBaseUrl: '接口地址', + apiModel: 'Api 模型', + accessToken: 'Access Token', }, store: { local: '本機', diff --git a/src/store/modules/chat/index.ts b/src/store/modules/chat/index.ts index db62635ac5..4e7ac2798e 100644 --- a/src/store/modules/chat/index.ts +++ b/src/store/modules/chat/index.ts @@ -29,6 +29,9 @@ export const useChatStore = defineStore('chat-store', { let uuid = this.active this.history = [] this.chat = [] + if (rooms.findIndex((item: { uuid: number | null }) => item.uuid === uuid) <= -1 && rooms.length > 0) + uuid = null + for (const r of rooms) { this.history.unshift(r) if (uuid == null) From 4670df4614d8f134c94d733ffdcb145111ac79e9 Mon Sep 17 00:00:00 2001 From: Kerwin Date: Tue, 21 Mar 2023 23:15:09 +0800 Subject: [PATCH 011/147] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.en.md | 2 ++ README.md | 2 ++ docs/basesettings.jpg | Bin 0 -> 199594 bytes docs/login.jpg | Bin 0 -> 150583 bytes docs/mailsettings.jpg | Bin 0 -> 194224 bytes docs/sitesettings.jpg | Bin 0 -> 208663 bytes 6 files changed, 4 insertions(+) create mode 100644 docs/basesettings.jpg create mode 100644 docs/login.jpg create mode 100644 docs/mailsettings.jpg create mode 100644 docs/sitesettings.jpg diff --git a/README.en.md b/README.en.md index e3f72fe5d3..0dc1b0aeea 100644 --- a/README.en.md +++ b/README.en.md @@ -13,8 +13,10 @@ ## Screenshots > Disclaimer: This project is only released on GitHub, under the MIT License, free and for open-source learning purposes. There will be no account selling, paid services, discussion groups, or forums. Beware of fraud. +![cover3](./docs/login.jpg) ![cover](./docs/c1.png) ![cover2](./docs/c2.png) +![cover3](./docs/basesettings.jpg) - [ChatGPT Web](#chatgpt-web) - [Introduction](#introduction) diff --git a/README.md b/README.md index 37ca6e8ba9..a36c514b92 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,10 @@ ## 截图 > 声明:此项目只发布于 Github,基于 MIT 协议,免费且作为开源学习使用。并且不会有任何形式的卖号、付费服务、讨论群、讨论组等行为。谨防受骗。 +![cover3](./docs/login.jpg) ![cover](./docs/c1.png) ![cover2](./docs/c2.png) +![cover3](./docs/basesettings.jpg) - [ChatGPT Web](#chatgpt-web) - [介绍](#介绍) diff --git a/docs/basesettings.jpg b/docs/basesettings.jpg new file mode 100644 index 0000000000000000000000000000000000000000..75a9a2f79d47d744fff61cca40809f4dc717ccba GIT binary patch literal 199594 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