epii-1 commited on
Commit
f0953a4
·
1 Parent(s): b43a46c
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .eslintignore +4 -0
  2. .eslintrc.js +57 -0
  3. .gitattributes +2 -0
  4. .gitignore +24 -0
  5. .prettierignore +30 -0
  6. .prettierrc.js +14 -0
  7. Dockerfile +78 -13
  8. LICENSE +21 -0
  9. backend/.env.example +9 -0
  10. backend/package-lock.json +1694 -0
  11. backend/package.json +39 -0
  12. backend/src/app.ts +57 -0
  13. backend/src/config/database.ts +9 -0
  14. backend/src/config/index.ts +88 -0
  15. backend/src/controllers/BaseCloudController.ts +32 -0
  16. backend/src/controllers/BaseController.ts +24 -0
  17. backend/src/controllers/cloud115.ts +11 -0
  18. backend/src/controllers/douban.ts +25 -0
  19. backend/src/controllers/quark.ts +12 -0
  20. backend/src/controllers/resource.ts +23 -0
  21. backend/src/controllers/setting.ts +28 -0
  22. backend/src/controllers/sponsors.ts +18 -0
  23. backend/src/controllers/teleImages.ts +30 -0
  24. backend/src/controllers/user.ts +26 -0
  25. backend/src/core/ApiResponse.ts +21 -0
  26. backend/src/core/types.ts +20 -0
  27. backend/src/inversify.config.ts +45 -0
  28. backend/src/middleware/auth.ts +43 -0
  29. backend/src/middleware/cors.ts +15 -0
  30. backend/src/middleware/errorHandler.ts +12 -0
  31. backend/src/middleware/index.ts +14 -0
  32. backend/src/middleware/rateLimiter.ts +27 -0
  33. backend/src/middleware/requestLogger.ts +23 -0
  34. backend/src/middleware/validateRequest.ts +16 -0
  35. backend/src/models/GlobalSetting.ts +67 -0
  36. backend/src/models/User.ts +62 -0
  37. backend/src/models/UserSetting.ts +72 -0
  38. backend/src/routes/api.ts +55 -0
  39. backend/src/services/Cloud115Service.ts +147 -0
  40. backend/src/services/DatabaseService.ts +62 -0
  41. backend/src/services/DoubanService.ts +64 -0
  42. backend/src/services/ImageService.ts +68 -0
  43. backend/src/services/QuarkService.ts +187 -0
  44. backend/src/services/Searcher.ts +222 -0
  45. backend/src/services/SettingService.ts +59 -0
  46. backend/src/services/SponsorsService.ts +25 -0
  47. backend/src/services/UserService.ts +63 -0
  48. backend/src/sponsors/sponsors.json +54 -0
  49. backend/src/types/cloud.ts +83 -0
  50. backend/src/types/cloud115.ts +10 -0
.eslintignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ node_modules
2
+ dist
3
+ build
4
+ coverage
.eslintrc.js ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ root: true,
3
+ ignorePatterns: ["node_modules", "dist", "build", "coverage"],
4
+ env: {
5
+ node: true,
6
+ es6: true,
7
+ },
8
+ parser: "@typescript-eslint/parser",
9
+ plugins: ["@typescript-eslint"],
10
+ extends: [
11
+ "eslint:recommended",
12
+ "plugin:@typescript-eslint/recommended",
13
+ "plugin:prettier/recommended",
14
+ ],
15
+ rules: {
16
+ "prettier/prettier": "error",
17
+ "@typescript-eslint/no-explicit-any": "warn",
18
+ "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
19
+ "@typescript-eslint/explicit-function-return-type": 0,
20
+ },
21
+ overrides: [
22
+ {
23
+ files: ["frontend/**/*.{js,ts,vue}"],
24
+ env: {
25
+ browser: true,
26
+ },
27
+ parser: "vue-eslint-parser",
28
+ parserOptions: {
29
+ parser: "@typescript-eslint/parser",
30
+ ecmaVersion: 2020,
31
+ sourceType: "module",
32
+ },
33
+ extends: [
34
+ "eslint:recommended",
35
+ "plugin:@typescript-eslint/recommended",
36
+ "plugin:vue/vue3-recommended",
37
+ "plugin:prettier/recommended",
38
+ ],
39
+ plugins: ["@typescript-eslint", "vue"],
40
+ rules: {
41
+ "vue/multi-word-component-names": "off",
42
+ "vue/require-default-prop": "off",
43
+ "vue/no-v-html": "off",
44
+ },
45
+ },
46
+ {
47
+ files: ["backend/**/*.{js,ts}"],
48
+ env: {
49
+ node: true,
50
+ },
51
+ rules: {
52
+ "@typescript-eslint/explicit-function-return-type": 0,
53
+ "@typescript-eslint/no-non-null-assertion": "warn",
54
+ },
55
+ },
56
+ ],
57
+ };
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.jpg filter=lfs diff=lfs merge=lfs -text
37
+ *.png filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ node_modules/
2
+ logs/
3
+ dist/
4
+ .env
5
+ .env.local
6
+ .env.*.local
7
+
8
+ *.tar
9
+
10
+ # 数据库数据
11
+ *.sqlite
12
+
13
+ # 保留模板
14
+ !.env.example
15
+
16
+ !frontend/.env
17
+
18
+ # 其他敏感文件
19
+ config.private.ts
20
+ *.pem
21
+ *.key
22
+
23
+ .DS_Store
24
+ *.log
.prettierignore ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 构建产物
2
+ dist
3
+ build
4
+ coverage
5
+
6
+ # 依赖目录
7
+ node_modules
8
+
9
+ # 日志文件
10
+ *.log
11
+
12
+ # 环境配置
13
+ .env*
14
+ !.env.example
15
+
16
+ # 编辑器配置
17
+ .idea
18
+ .vscode
19
+ *.suo
20
+ *.ntvs*
21
+ *.njsproj
22
+ *.sln
23
+ *.sw?
24
+
25
+ # 系统文件
26
+ .DS_Store
27
+ Thumbs.db
28
+
29
+ # 版本控制
30
+ .git
.prettierrc.js ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ semi: true,
3
+ trailingComma: "es5",
4
+ singleQuote: false,
5
+ printWidth: 100,
6
+ tabWidth: 2,
7
+ useTabs: false,
8
+ endOfLine: "auto",
9
+ arrowParens: "always",
10
+ bracketSpacing: true,
11
+ embeddedLanguageFormatting: "auto",
12
+ htmlWhitespaceSensitivity: "css",
13
+ vueIndentScriptAndStyle: false,
14
+ };
Dockerfile CHANGED
@@ -1,18 +1,83 @@
1
- # 使用官方的 Nginx 镜像
2
- FROM nginxinc/nginx-unprivileged:stable-alpine
3
 
4
- # 创建必要的目录并设置权限
5
- RUN mkdir -p /var/cache/nginx/client_temp && \
6
- chown -R nginx:nginx /var/cache/nginx
7
 
8
- # 复制自定义的 Nginx 配置文件
9
- COPY nginx.conf /etc/nginx/nginx.conf
10
- # 复制自定义的 index.html 页面
11
- COPY index.html /usr/share/nginx/html/index.html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  # 设置工作目录
14
- # 设置工作目录
15
- WORKDIR /usr/share/nginx/html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
- # 设置默认命令
18
- CMD ["nginx", "-g", "daemon off;"]
 
1
+ # 构建前端项目
2
+ FROM nikolaik/python-nodejs:python3.10-nodejs18
3
 
4
+ # Install nginx and give permissions to 'pn'
5
+ # See https://www.rockyourcode.com/run-docker-nginx-as-non-root-user/
6
+ USER root
7
 
8
+ RUN apt-get -y update && apt-get -y install nginx
9
+
10
+ RUN mkdir -p /var/cache/nginx \
11
+ /var/log/nginx \
12
+ /var/lib/nginx
13
+ RUN touch /var/run/nginx.pid
14
+
15
+ RUN chown -R pn:pn /var/cache/nginx \
16
+ /var/log/nginx \
17
+ /var/lib/nginx \
18
+ /var/run/nginx.pid
19
+
20
+ # Install dependencies and build app as non-root
21
+ USER pn
22
+ ENV HOME=/home/pn \
23
+ PATH=/home/pn/.local/bin:$PATH
24
+
25
+ RUN mkdir $HOME/app
26
+
27
+ WORKDIR $HOME/app
28
+
29
+ # Copy nginx configuration
30
+ COPY --chown=pn nginx.conf /etc/nginx/sites-available/default
31
+ COPY --chown=pn . .
32
+ WORKDIR /app
33
+ COPY frontend/package*.json ./
34
+ RUN npm install -g pnpm
35
+ RUN pnpm install
36
+ COPY frontend/ ./
37
+ RUN npm run build
38
+
39
+ # 构建后端项目
40
+
41
+ WORKDIR /app
42
+ COPY backend/package*.json ./
43
+ RUN npm install -g pnpm
44
+ RUN pnpm install
45
+ COPY backend/ ./
46
+ RUN rm -f database.sqlite
47
+ RUN npm run build
48
+
49
+ # 生产环境镜像
50
+ FROM node:18-alpine
51
+
52
+ # 安装 Nginx
53
+ RUN apk add --no-cache nginx
54
 
55
  # 设置工作目录
56
+ WORKDIR /app
57
+
58
+ # 创建配置和数据目录
59
+ RUN mkdir -p /app/config /app/data
60
+
61
+ # 复制前端构建产物到 Nginx
62
+ COPY --from=frontend-build /app/dist /usr/share/nginx/html
63
+
64
+ # 复制 Nginx 配置文件
65
+ COPY nginx.conf /etc/nginx/nginx.conf
66
+
67
+ # 复制后端构建产物到生产环境镜像
68
+ COPY --from=backend-build /app /app
69
+
70
+ # 安装生产环境依赖
71
+ RUN npm install --production
72
+
73
+ # 设置数据卷
74
+
75
+ # 暴露端口
76
+ EXPOSE 8008
77
+
78
+ # 启动脚本
79
+ COPY docker-entrypoint.sh /app/
80
+ RUN chmod +x /app/docker-entrypoint.sh
81
 
82
+ # 启动服务
83
+ ENTRYPOINT ["/app/docker-entrypoint.sh"]
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2024 CloudSaver
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.
backend/.env.example ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # JWT配置
2
+ JWT_SECRET=your_jwt_secret_here
3
+
4
+ # Telegram配置
5
+ TELEGRAM_BASE_URL=https://t.me/s
6
+
7
+ # Telegram频道配置
8
+ TELE_CHANNELS=[]
9
+
backend/package-lock.json ADDED
@@ -0,0 +1,1694 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "cloud-disk-server",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "cloud-disk-server",
9
+ "version": "1.0.0",
10
+ "dependencies": {
11
+ "axios": "^1.6.7",
12
+ "cookie-parser": "^1.4.6",
13
+ "cors": "^2.8.5",
14
+ "dotenv": "^16.4.5",
15
+ "express": "^4.18.3",
16
+ "rss-parser": "^3.13.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/cookie-parser": "^1.4.7",
20
+ "@types/cors": "^2.8.17",
21
+ "@types/express": "^4.17.21",
22
+ "@types/node": "^20.11.25",
23
+ "nodemon": "^3.1.0",
24
+ "ts-node": "^10.9.2",
25
+ "typescript": "^5.4.2"
26
+ }
27
+ },
28
+ "node_modules/@cspotcode/source-map-support": {
29
+ "version": "0.8.1",
30
+ "resolved": "https://registry.npmmirror.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
31
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
32
+ "dev": true,
33
+ "license": "MIT",
34
+ "dependencies": {
35
+ "@jridgewell/trace-mapping": "0.3.9"
36
+ },
37
+ "engines": {
38
+ "node": ">=12"
39
+ }
40
+ },
41
+ "node_modules/@jridgewell/resolve-uri": {
42
+ "version": "3.1.2",
43
+ "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
44
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
45
+ "dev": true,
46
+ "license": "MIT",
47
+ "engines": {
48
+ "node": ">=6.0.0"
49
+ }
50
+ },
51
+ "node_modules/@jridgewell/sourcemap-codec": {
52
+ "version": "1.5.0",
53
+ "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
54
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
55
+ "dev": true,
56
+ "license": "MIT"
57
+ },
58
+ "node_modules/@jridgewell/trace-mapping": {
59
+ "version": "0.3.9",
60
+ "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
61
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
62
+ "dev": true,
63
+ "license": "MIT",
64
+ "dependencies": {
65
+ "@jridgewell/resolve-uri": "^3.0.3",
66
+ "@jridgewell/sourcemap-codec": "^1.4.10"
67
+ }
68
+ },
69
+ "node_modules/@tsconfig/node10": {
70
+ "version": "1.0.11",
71
+ "resolved": "https://registry.npmmirror.com/@tsconfig/node10/-/node10-1.0.11.tgz",
72
+ "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
73
+ "dev": true,
74
+ "license": "MIT"
75
+ },
76
+ "node_modules/@tsconfig/node12": {
77
+ "version": "1.0.11",
78
+ "resolved": "https://registry.npmmirror.com/@tsconfig/node12/-/node12-1.0.11.tgz",
79
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
80
+ "dev": true,
81
+ "license": "MIT"
82
+ },
83
+ "node_modules/@tsconfig/node14": {
84
+ "version": "1.0.3",
85
+ "resolved": "https://registry.npmmirror.com/@tsconfig/node14/-/node14-1.0.3.tgz",
86
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
87
+ "dev": true,
88
+ "license": "MIT"
89
+ },
90
+ "node_modules/@tsconfig/node16": {
91
+ "version": "1.0.4",
92
+ "resolved": "https://registry.npmmirror.com/@tsconfig/node16/-/node16-1.0.4.tgz",
93
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
94
+ "dev": true,
95
+ "license": "MIT"
96
+ },
97
+ "node_modules/@types/body-parser": {
98
+ "version": "1.19.5",
99
+ "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.5.tgz",
100
+ "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
101
+ "dev": true,
102
+ "license": "MIT",
103
+ "dependencies": {
104
+ "@types/connect": "*",
105
+ "@types/node": "*"
106
+ }
107
+ },
108
+ "node_modules/@types/connect": {
109
+ "version": "3.4.38",
110
+ "resolved": "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz",
111
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
112
+ "dev": true,
113
+ "license": "MIT",
114
+ "dependencies": {
115
+ "@types/node": "*"
116
+ }
117
+ },
118
+ "node_modules/@types/cookie-parser": {
119
+ "version": "1.4.8",
120
+ "resolved": "https://registry.npmmirror.com/@types/cookie-parser/-/cookie-parser-1.4.8.tgz",
121
+ "integrity": "sha512-l37JqFrOJ9yQfRQkljb41l0xVphc7kg5JTjjr+pLRZ0IyZ49V4BQ8vbF4Ut2C2e+WH4al3xD3ZwYwIUfnbT4NQ==",
122
+ "dev": true,
123
+ "license": "MIT",
124
+ "peerDependencies": {
125
+ "@types/express": "*"
126
+ }
127
+ },
128
+ "node_modules/@types/cors": {
129
+ "version": "2.8.17",
130
+ "resolved": "https://registry.npmmirror.com/@types/cors/-/cors-2.8.17.tgz",
131
+ "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
132
+ "dev": true,
133
+ "license": "MIT",
134
+ "dependencies": {
135
+ "@types/node": "*"
136
+ }
137
+ },
138
+ "node_modules/@types/express": {
139
+ "version": "4.17.21",
140
+ "resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.21.tgz",
141
+ "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
142
+ "dev": true,
143
+ "license": "MIT",
144
+ "dependencies": {
145
+ "@types/body-parser": "*",
146
+ "@types/express-serve-static-core": "^4.17.33",
147
+ "@types/qs": "*",
148
+ "@types/serve-static": "*"
149
+ }
150
+ },
151
+ "node_modules/@types/express-serve-static-core": {
152
+ "version": "4.19.6",
153
+ "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz",
154
+ "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==",
155
+ "dev": true,
156
+ "license": "MIT",
157
+ "dependencies": {
158
+ "@types/node": "*",
159
+ "@types/qs": "*",
160
+ "@types/range-parser": "*",
161
+ "@types/send": "*"
162
+ }
163
+ },
164
+ "node_modules/@types/http-errors": {
165
+ "version": "2.0.4",
166
+ "resolved": "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.4.tgz",
167
+ "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
168
+ "dev": true,
169
+ "license": "MIT"
170
+ },
171
+ "node_modules/@types/mime": {
172
+ "version": "1.3.5",
173
+ "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz",
174
+ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
175
+ "dev": true,
176
+ "license": "MIT"
177
+ },
178
+ "node_modules/@types/node": {
179
+ "version": "20.17.9",
180
+ "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.17.9.tgz",
181
+ "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==",
182
+ "dev": true,
183
+ "license": "MIT",
184
+ "dependencies": {
185
+ "undici-types": "~6.19.2"
186
+ }
187
+ },
188
+ "node_modules/@types/qs": {
189
+ "version": "6.9.17",
190
+ "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.9.17.tgz",
191
+ "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==",
192
+ "dev": true,
193
+ "license": "MIT"
194
+ },
195
+ "node_modules/@types/range-parser": {
196
+ "version": "1.2.7",
197
+ "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz",
198
+ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
199
+ "dev": true,
200
+ "license": "MIT"
201
+ },
202
+ "node_modules/@types/send": {
203
+ "version": "0.17.4",
204
+ "resolved": "https://registry.npmmirror.com/@types/send/-/send-0.17.4.tgz",
205
+ "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
206
+ "dev": true,
207
+ "license": "MIT",
208
+ "dependencies": {
209
+ "@types/mime": "^1",
210
+ "@types/node": "*"
211
+ }
212
+ },
213
+ "node_modules/@types/serve-static": {
214
+ "version": "1.15.7",
215
+ "resolved": "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.7.tgz",
216
+ "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
217
+ "dev": true,
218
+ "license": "MIT",
219
+ "dependencies": {
220
+ "@types/http-errors": "*",
221
+ "@types/node": "*",
222
+ "@types/send": "*"
223
+ }
224
+ },
225
+ "node_modules/accepts": {
226
+ "version": "1.3.8",
227
+ "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz",
228
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
229
+ "license": "MIT",
230
+ "dependencies": {
231
+ "mime-types": "~2.1.34",
232
+ "negotiator": "0.6.3"
233
+ },
234
+ "engines": {
235
+ "node": ">= 0.6"
236
+ }
237
+ },
238
+ "node_modules/acorn": {
239
+ "version": "8.14.0",
240
+ "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.0.tgz",
241
+ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
242
+ "dev": true,
243
+ "license": "MIT",
244
+ "bin": {
245
+ "acorn": "bin/acorn"
246
+ },
247
+ "engines": {
248
+ "node": ">=0.4.0"
249
+ }
250
+ },
251
+ "node_modules/acorn-walk": {
252
+ "version": "8.3.4",
253
+ "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.4.tgz",
254
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
255
+ "dev": true,
256
+ "license": "MIT",
257
+ "dependencies": {
258
+ "acorn": "^8.11.0"
259
+ },
260
+ "engines": {
261
+ "node": ">=0.4.0"
262
+ }
263
+ },
264
+ "node_modules/anymatch": {
265
+ "version": "3.1.3",
266
+ "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
267
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
268
+ "dev": true,
269
+ "license": "ISC",
270
+ "dependencies": {
271
+ "normalize-path": "^3.0.0",
272
+ "picomatch": "^2.0.4"
273
+ },
274
+ "engines": {
275
+ "node": ">= 8"
276
+ }
277
+ },
278
+ "node_modules/arg": {
279
+ "version": "4.1.3",
280
+ "resolved": "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz",
281
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
282
+ "dev": true,
283
+ "license": "MIT"
284
+ },
285
+ "node_modules/array-flatten": {
286
+ "version": "1.1.1",
287
+ "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz",
288
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
289
+ "license": "MIT"
290
+ },
291
+ "node_modules/asynckit": {
292
+ "version": "0.4.0",
293
+ "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
294
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
295
+ "license": "MIT"
296
+ },
297
+ "node_modules/axios": {
298
+ "version": "1.7.9",
299
+ "resolved": "https://registry.npmmirror.com/axios/-/axios-1.7.9.tgz",
300
+ "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
301
+ "license": "MIT",
302
+ "dependencies": {
303
+ "follow-redirects": "^1.15.6",
304
+ "form-data": "^4.0.0",
305
+ "proxy-from-env": "^1.1.0"
306
+ }
307
+ },
308
+ "node_modules/balanced-match": {
309
+ "version": "1.0.2",
310
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
311
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
312
+ "dev": true,
313
+ "license": "MIT"
314
+ },
315
+ "node_modules/binary-extensions": {
316
+ "version": "2.3.0",
317
+ "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
318
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
319
+ "dev": true,
320
+ "license": "MIT",
321
+ "engines": {
322
+ "node": ">=8"
323
+ },
324
+ "funding": {
325
+ "url": "https://github.com/sponsors/sindresorhus"
326
+ }
327
+ },
328
+ "node_modules/body-parser": {
329
+ "version": "1.20.3",
330
+ "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.3.tgz",
331
+ "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
332
+ "license": "MIT",
333
+ "dependencies": {
334
+ "bytes": "3.1.2",
335
+ "content-type": "~1.0.5",
336
+ "debug": "2.6.9",
337
+ "depd": "2.0.0",
338
+ "destroy": "1.2.0",
339
+ "http-errors": "2.0.0",
340
+ "iconv-lite": "0.4.24",
341
+ "on-finished": "2.4.1",
342
+ "qs": "6.13.0",
343
+ "raw-body": "2.5.2",
344
+ "type-is": "~1.6.18",
345
+ "unpipe": "1.0.0"
346
+ },
347
+ "engines": {
348
+ "node": ">= 0.8",
349
+ "npm": "1.2.8000 || >= 1.4.16"
350
+ }
351
+ },
352
+ "node_modules/brace-expansion": {
353
+ "version": "1.1.11",
354
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
355
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
356
+ "dev": true,
357
+ "license": "MIT",
358
+ "dependencies": {
359
+ "balanced-match": "^1.0.0",
360
+ "concat-map": "0.0.1"
361
+ }
362
+ },
363
+ "node_modules/braces": {
364
+ "version": "3.0.3",
365
+ "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
366
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
367
+ "dev": true,
368
+ "license": "MIT",
369
+ "dependencies": {
370
+ "fill-range": "^7.1.1"
371
+ },
372
+ "engines": {
373
+ "node": ">=8"
374
+ }
375
+ },
376
+ "node_modules/bytes": {
377
+ "version": "3.1.2",
378
+ "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz",
379
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
380
+ "license": "MIT",
381
+ "engines": {
382
+ "node": ">= 0.8"
383
+ }
384
+ },
385
+ "node_modules/call-bind": {
386
+ "version": "1.0.8",
387
+ "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz",
388
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
389
+ "license": "MIT",
390
+ "dependencies": {
391
+ "call-bind-apply-helpers": "^1.0.0",
392
+ "es-define-property": "^1.0.0",
393
+ "get-intrinsic": "^1.2.4",
394
+ "set-function-length": "^1.2.2"
395
+ },
396
+ "engines": {
397
+ "node": ">= 0.4"
398
+ },
399
+ "funding": {
400
+ "url": "https://github.com/sponsors/ljharb"
401
+ }
402
+ },
403
+ "node_modules/call-bind-apply-helpers": {
404
+ "version": "1.0.1",
405
+ "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
406
+ "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
407
+ "license": "MIT",
408
+ "dependencies": {
409
+ "es-errors": "^1.3.0",
410
+ "function-bind": "^1.1.2"
411
+ },
412
+ "engines": {
413
+ "node": ">= 0.4"
414
+ }
415
+ },
416
+ "node_modules/chokidar": {
417
+ "version": "3.6.0",
418
+ "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
419
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
420
+ "dev": true,
421
+ "license": "MIT",
422
+ "dependencies": {
423
+ "anymatch": "~3.1.2",
424
+ "braces": "~3.0.2",
425
+ "glob-parent": "~5.1.2",
426
+ "is-binary-path": "~2.1.0",
427
+ "is-glob": "~4.0.1",
428
+ "normalize-path": "~3.0.0",
429
+ "readdirp": "~3.6.0"
430
+ },
431
+ "engines": {
432
+ "node": ">= 8.10.0"
433
+ },
434
+ "funding": {
435
+ "url": "https://paulmillr.com/funding/"
436
+ },
437
+ "optionalDependencies": {
438
+ "fsevents": "~2.3.2"
439
+ }
440
+ },
441
+ "node_modules/combined-stream": {
442
+ "version": "1.0.8",
443
+ "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
444
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
445
+ "license": "MIT",
446
+ "dependencies": {
447
+ "delayed-stream": "~1.0.0"
448
+ },
449
+ "engines": {
450
+ "node": ">= 0.8"
451
+ }
452
+ },
453
+ "node_modules/concat-map": {
454
+ "version": "0.0.1",
455
+ "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
456
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
457
+ "dev": true,
458
+ "license": "MIT"
459
+ },
460
+ "node_modules/content-disposition": {
461
+ "version": "0.5.4",
462
+ "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz",
463
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
464
+ "license": "MIT",
465
+ "dependencies": {
466
+ "safe-buffer": "5.2.1"
467
+ },
468
+ "engines": {
469
+ "node": ">= 0.6"
470
+ }
471
+ },
472
+ "node_modules/content-type": {
473
+ "version": "1.0.5",
474
+ "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz",
475
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
476
+ "license": "MIT",
477
+ "engines": {
478
+ "node": ">= 0.6"
479
+ }
480
+ },
481
+ "node_modules/cookie": {
482
+ "version": "0.7.2",
483
+ "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz",
484
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
485
+ "license": "MIT",
486
+ "engines": {
487
+ "node": ">= 0.6"
488
+ }
489
+ },
490
+ "node_modules/cookie-parser": {
491
+ "version": "1.4.7",
492
+ "resolved": "https://registry.npmmirror.com/cookie-parser/-/cookie-parser-1.4.7.tgz",
493
+ "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
494
+ "license": "MIT",
495
+ "dependencies": {
496
+ "cookie": "0.7.2",
497
+ "cookie-signature": "1.0.6"
498
+ },
499
+ "engines": {
500
+ "node": ">= 0.8.0"
501
+ }
502
+ },
503
+ "node_modules/cookie-signature": {
504
+ "version": "1.0.6",
505
+ "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz",
506
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
507
+ "license": "MIT"
508
+ },
509
+ "node_modules/cors": {
510
+ "version": "2.8.5",
511
+ "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz",
512
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
513
+ "license": "MIT",
514
+ "dependencies": {
515
+ "object-assign": "^4",
516
+ "vary": "^1"
517
+ },
518
+ "engines": {
519
+ "node": ">= 0.10"
520
+ }
521
+ },
522
+ "node_modules/create-require": {
523
+ "version": "1.1.1",
524
+ "resolved": "https://registry.npmmirror.com/create-require/-/create-require-1.1.1.tgz",
525
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
526
+ "dev": true,
527
+ "license": "MIT"
528
+ },
529
+ "node_modules/debug": {
530
+ "version": "2.6.9",
531
+ "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz",
532
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
533
+ "license": "MIT",
534
+ "dependencies": {
535
+ "ms": "2.0.0"
536
+ }
537
+ },
538
+ "node_modules/define-data-property": {
539
+ "version": "1.1.4",
540
+ "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz",
541
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
542
+ "license": "MIT",
543
+ "dependencies": {
544
+ "es-define-property": "^1.0.0",
545
+ "es-errors": "^1.3.0",
546
+ "gopd": "^1.0.1"
547
+ },
548
+ "engines": {
549
+ "node": ">= 0.4"
550
+ },
551
+ "funding": {
552
+ "url": "https://github.com/sponsors/ljharb"
553
+ }
554
+ },
555
+ "node_modules/delayed-stream": {
556
+ "version": "1.0.0",
557
+ "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
558
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
559
+ "license": "MIT",
560
+ "engines": {
561
+ "node": ">=0.4.0"
562
+ }
563
+ },
564
+ "node_modules/depd": {
565
+ "version": "2.0.0",
566
+ "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
567
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
568
+ "license": "MIT",
569
+ "engines": {
570
+ "node": ">= 0.8"
571
+ }
572
+ },
573
+ "node_modules/destroy": {
574
+ "version": "1.2.0",
575
+ "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz",
576
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
577
+ "license": "MIT",
578
+ "engines": {
579
+ "node": ">= 0.8",
580
+ "npm": "1.2.8000 || >= 1.4.16"
581
+ }
582
+ },
583
+ "node_modules/diff": {
584
+ "version": "4.0.2",
585
+ "resolved": "https://registry.npmmirror.com/diff/-/diff-4.0.2.tgz",
586
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
587
+ "dev": true,
588
+ "license": "BSD-3-Clause",
589
+ "engines": {
590
+ "node": ">=0.3.1"
591
+ }
592
+ },
593
+ "node_modules/dotenv": {
594
+ "version": "16.4.7",
595
+ "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.4.7.tgz",
596
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
597
+ "license": "BSD-2-Clause",
598
+ "engines": {
599
+ "node": ">=12"
600
+ },
601
+ "funding": {
602
+ "url": "https://dotenvx.com"
603
+ }
604
+ },
605
+ "node_modules/dunder-proto": {
606
+ "version": "1.0.0",
607
+ "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.0.tgz",
608
+ "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==",
609
+ "license": "MIT",
610
+ "dependencies": {
611
+ "call-bind-apply-helpers": "^1.0.0",
612
+ "es-errors": "^1.3.0",
613
+ "gopd": "^1.2.0"
614
+ },
615
+ "engines": {
616
+ "node": ">= 0.4"
617
+ }
618
+ },
619
+ "node_modules/ee-first": {
620
+ "version": "1.1.1",
621
+ "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
622
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
623
+ "license": "MIT"
624
+ },
625
+ "node_modules/encodeurl": {
626
+ "version": "2.0.0",
627
+ "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz",
628
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
629
+ "license": "MIT",
630
+ "engines": {
631
+ "node": ">= 0.8"
632
+ }
633
+ },
634
+ "node_modules/entities": {
635
+ "version": "2.2.0",
636
+ "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz",
637
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
638
+ "license": "BSD-2-Clause",
639
+ "funding": {
640
+ "url": "https://github.com/fb55/entities?sponsor=1"
641
+ }
642
+ },
643
+ "node_modules/es-define-property": {
644
+ "version": "1.0.1",
645
+ "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
646
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
647
+ "license": "MIT",
648
+ "engines": {
649
+ "node": ">= 0.4"
650
+ }
651
+ },
652
+ "node_modules/es-errors": {
653
+ "version": "1.3.0",
654
+ "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
655
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
656
+ "license": "MIT",
657
+ "engines": {
658
+ "node": ">= 0.4"
659
+ }
660
+ },
661
+ "node_modules/escape-html": {
662
+ "version": "1.0.3",
663
+ "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
664
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
665
+ "license": "MIT"
666
+ },
667
+ "node_modules/etag": {
668
+ "version": "1.8.1",
669
+ "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
670
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
671
+ "license": "MIT",
672
+ "engines": {
673
+ "node": ">= 0.6"
674
+ }
675
+ },
676
+ "node_modules/express": {
677
+ "version": "4.21.2",
678
+ "resolved": "https://registry.npmmirror.com/express/-/express-4.21.2.tgz",
679
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
680
+ "license": "MIT",
681
+ "dependencies": {
682
+ "accepts": "~1.3.8",
683
+ "array-flatten": "1.1.1",
684
+ "body-parser": "1.20.3",
685
+ "content-disposition": "0.5.4",
686
+ "content-type": "~1.0.4",
687
+ "cookie": "0.7.1",
688
+ "cookie-signature": "1.0.6",
689
+ "debug": "2.6.9",
690
+ "depd": "2.0.0",
691
+ "encodeurl": "~2.0.0",
692
+ "escape-html": "~1.0.3",
693
+ "etag": "~1.8.1",
694
+ "finalhandler": "1.3.1",
695
+ "fresh": "0.5.2",
696
+ "http-errors": "2.0.0",
697
+ "merge-descriptors": "1.0.3",
698
+ "methods": "~1.1.2",
699
+ "on-finished": "2.4.1",
700
+ "parseurl": "~1.3.3",
701
+ "path-to-regexp": "0.1.12",
702
+ "proxy-addr": "~2.0.7",
703
+ "qs": "6.13.0",
704
+ "range-parser": "~1.2.1",
705
+ "safe-buffer": "5.2.1",
706
+ "send": "0.19.0",
707
+ "serve-static": "1.16.2",
708
+ "setprototypeof": "1.2.0",
709
+ "statuses": "2.0.1",
710
+ "type-is": "~1.6.18",
711
+ "utils-merge": "1.0.1",
712
+ "vary": "~1.1.2"
713
+ },
714
+ "engines": {
715
+ "node": ">= 0.10.0"
716
+ },
717
+ "funding": {
718
+ "type": "opencollective",
719
+ "url": "https://opencollective.com/express"
720
+ }
721
+ },
722
+ "node_modules/express/node_modules/cookie": {
723
+ "version": "0.7.1",
724
+ "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.1.tgz",
725
+ "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
726
+ "license": "MIT",
727
+ "engines": {
728
+ "node": ">= 0.6"
729
+ }
730
+ },
731
+ "node_modules/fill-range": {
732
+ "version": "7.1.1",
733
+ "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
734
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
735
+ "dev": true,
736
+ "license": "MIT",
737
+ "dependencies": {
738
+ "to-regex-range": "^5.0.1"
739
+ },
740
+ "engines": {
741
+ "node": ">=8"
742
+ }
743
+ },
744
+ "node_modules/finalhandler": {
745
+ "version": "1.3.1",
746
+ "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.1.tgz",
747
+ "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
748
+ "license": "MIT",
749
+ "dependencies": {
750
+ "debug": "2.6.9",
751
+ "encodeurl": "~2.0.0",
752
+ "escape-html": "~1.0.3",
753
+ "on-finished": "2.4.1",
754
+ "parseurl": "~1.3.3",
755
+ "statuses": "2.0.1",
756
+ "unpipe": "~1.0.0"
757
+ },
758
+ "engines": {
759
+ "node": ">= 0.8"
760
+ }
761
+ },
762
+ "node_modules/follow-redirects": {
763
+ "version": "1.15.9",
764
+ "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
765
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
766
+ "funding": [
767
+ {
768
+ "type": "individual",
769
+ "url": "https://github.com/sponsors/RubenVerborgh"
770
+ }
771
+ ],
772
+ "license": "MIT",
773
+ "engines": {
774
+ "node": ">=4.0"
775
+ },
776
+ "peerDependenciesMeta": {
777
+ "debug": {
778
+ "optional": true
779
+ }
780
+ }
781
+ },
782
+ "node_modules/form-data": {
783
+ "version": "4.0.1",
784
+ "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.1.tgz",
785
+ "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
786
+ "license": "MIT",
787
+ "dependencies": {
788
+ "asynckit": "^0.4.0",
789
+ "combined-stream": "^1.0.8",
790
+ "mime-types": "^2.1.12"
791
+ },
792
+ "engines": {
793
+ "node": ">= 6"
794
+ }
795
+ },
796
+ "node_modules/forwarded": {
797
+ "version": "0.2.0",
798
+ "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
799
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
800
+ "license": "MIT",
801
+ "engines": {
802
+ "node": ">= 0.6"
803
+ }
804
+ },
805
+ "node_modules/fresh": {
806
+ "version": "0.5.2",
807
+ "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz",
808
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
809
+ "license": "MIT",
810
+ "engines": {
811
+ "node": ">= 0.6"
812
+ }
813
+ },
814
+ "node_modules/fsevents": {
815
+ "version": "2.3.3",
816
+ "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
817
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
818
+ "dev": true,
819
+ "hasInstallScript": true,
820
+ "license": "MIT",
821
+ "optional": true,
822
+ "os": [
823
+ "darwin"
824
+ ],
825
+ "engines": {
826
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
827
+ }
828
+ },
829
+ "node_modules/function-bind": {
830
+ "version": "1.1.2",
831
+ "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
832
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
833
+ "license": "MIT",
834
+ "funding": {
835
+ "url": "https://github.com/sponsors/ljharb"
836
+ }
837
+ },
838
+ "node_modules/get-intrinsic": {
839
+ "version": "1.2.5",
840
+ "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.5.tgz",
841
+ "integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==",
842
+ "license": "MIT",
843
+ "dependencies": {
844
+ "call-bind-apply-helpers": "^1.0.0",
845
+ "dunder-proto": "^1.0.0",
846
+ "es-define-property": "^1.0.1",
847
+ "es-errors": "^1.3.0",
848
+ "function-bind": "^1.1.2",
849
+ "gopd": "^1.2.0",
850
+ "has-symbols": "^1.1.0",
851
+ "hasown": "^2.0.2"
852
+ },
853
+ "engines": {
854
+ "node": ">= 0.4"
855
+ },
856
+ "funding": {
857
+ "url": "https://github.com/sponsors/ljharb"
858
+ }
859
+ },
860
+ "node_modules/glob-parent": {
861
+ "version": "5.1.2",
862
+ "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
863
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
864
+ "dev": true,
865
+ "license": "ISC",
866
+ "dependencies": {
867
+ "is-glob": "^4.0.1"
868
+ },
869
+ "engines": {
870
+ "node": ">= 6"
871
+ }
872
+ },
873
+ "node_modules/gopd": {
874
+ "version": "1.2.0",
875
+ "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
876
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
877
+ "license": "MIT",
878
+ "engines": {
879
+ "node": ">= 0.4"
880
+ },
881
+ "funding": {
882
+ "url": "https://github.com/sponsors/ljharb"
883
+ }
884
+ },
885
+ "node_modules/has-flag": {
886
+ "version": "3.0.0",
887
+ "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz",
888
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
889
+ "dev": true,
890
+ "license": "MIT",
891
+ "engines": {
892
+ "node": ">=4"
893
+ }
894
+ },
895
+ "node_modules/has-property-descriptors": {
896
+ "version": "1.0.2",
897
+ "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
898
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
899
+ "license": "MIT",
900
+ "dependencies": {
901
+ "es-define-property": "^1.0.0"
902
+ },
903
+ "funding": {
904
+ "url": "https://github.com/sponsors/ljharb"
905
+ }
906
+ },
907
+ "node_modules/has-symbols": {
908
+ "version": "1.1.0",
909
+ "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
910
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
911
+ "license": "MIT",
912
+ "engines": {
913
+ "node": ">= 0.4"
914
+ },
915
+ "funding": {
916
+ "url": "https://github.com/sponsors/ljharb"
917
+ }
918
+ },
919
+ "node_modules/hasown": {
920
+ "version": "2.0.2",
921
+ "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
922
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
923
+ "license": "MIT",
924
+ "dependencies": {
925
+ "function-bind": "^1.1.2"
926
+ },
927
+ "engines": {
928
+ "node": ">= 0.4"
929
+ }
930
+ },
931
+ "node_modules/http-errors": {
932
+ "version": "2.0.0",
933
+ "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz",
934
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
935
+ "license": "MIT",
936
+ "dependencies": {
937
+ "depd": "2.0.0",
938
+ "inherits": "2.0.4",
939
+ "setprototypeof": "1.2.0",
940
+ "statuses": "2.0.1",
941
+ "toidentifier": "1.0.1"
942
+ },
943
+ "engines": {
944
+ "node": ">= 0.8"
945
+ }
946
+ },
947
+ "node_modules/iconv-lite": {
948
+ "version": "0.4.24",
949
+ "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz",
950
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
951
+ "license": "MIT",
952
+ "dependencies": {
953
+ "safer-buffer": ">= 2.1.2 < 3"
954
+ },
955
+ "engines": {
956
+ "node": ">=0.10.0"
957
+ }
958
+ },
959
+ "node_modules/ignore-by-default": {
960
+ "version": "1.0.1",
961
+ "resolved": "https://registry.npmmirror.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
962
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
963
+ "dev": true,
964
+ "license": "ISC"
965
+ },
966
+ "node_modules/inherits": {
967
+ "version": "2.0.4",
968
+ "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
969
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
970
+ "license": "ISC"
971
+ },
972
+ "node_modules/ipaddr.js": {
973
+ "version": "1.9.1",
974
+ "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
975
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
976
+ "license": "MIT",
977
+ "engines": {
978
+ "node": ">= 0.10"
979
+ }
980
+ },
981
+ "node_modules/is-binary-path": {
982
+ "version": "2.1.0",
983
+ "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
984
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
985
+ "dev": true,
986
+ "license": "MIT",
987
+ "dependencies": {
988
+ "binary-extensions": "^2.0.0"
989
+ },
990
+ "engines": {
991
+ "node": ">=8"
992
+ }
993
+ },
994
+ "node_modules/is-extglob": {
995
+ "version": "2.1.1",
996
+ "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
997
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
998
+ "dev": true,
999
+ "license": "MIT",
1000
+ "engines": {
1001
+ "node": ">=0.10.0"
1002
+ }
1003
+ },
1004
+ "node_modules/is-glob": {
1005
+ "version": "4.0.3",
1006
+ "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
1007
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
1008
+ "dev": true,
1009
+ "license": "MIT",
1010
+ "dependencies": {
1011
+ "is-extglob": "^2.1.1"
1012
+ },
1013
+ "engines": {
1014
+ "node": ">=0.10.0"
1015
+ }
1016
+ },
1017
+ "node_modules/is-number": {
1018
+ "version": "7.0.0",
1019
+ "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
1020
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
1021
+ "dev": true,
1022
+ "license": "MIT",
1023
+ "engines": {
1024
+ "node": ">=0.12.0"
1025
+ }
1026
+ },
1027
+ "node_modules/make-error": {
1028
+ "version": "1.3.6",
1029
+ "resolved": "https://registry.npmmirror.com/make-error/-/make-error-1.3.6.tgz",
1030
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
1031
+ "dev": true,
1032
+ "license": "ISC"
1033
+ },
1034
+ "node_modules/media-typer": {
1035
+ "version": "0.3.0",
1036
+ "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz",
1037
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
1038
+ "license": "MIT",
1039
+ "engines": {
1040
+ "node": ">= 0.6"
1041
+ }
1042
+ },
1043
+ "node_modules/merge-descriptors": {
1044
+ "version": "1.0.3",
1045
+ "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
1046
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
1047
+ "license": "MIT",
1048
+ "funding": {
1049
+ "url": "https://github.com/sponsors/sindresorhus"
1050
+ }
1051
+ },
1052
+ "node_modules/methods": {
1053
+ "version": "1.1.2",
1054
+ "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz",
1055
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
1056
+ "license": "MIT",
1057
+ "engines": {
1058
+ "node": ">= 0.6"
1059
+ }
1060
+ },
1061
+ "node_modules/mime": {
1062
+ "version": "1.6.0",
1063
+ "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz",
1064
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
1065
+ "license": "MIT",
1066
+ "bin": {
1067
+ "mime": "cli.js"
1068
+ },
1069
+ "engines": {
1070
+ "node": ">=4"
1071
+ }
1072
+ },
1073
+ "node_modules/mime-db": {
1074
+ "version": "1.52.0",
1075
+ "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
1076
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
1077
+ "license": "MIT",
1078
+ "engines": {
1079
+ "node": ">= 0.6"
1080
+ }
1081
+ },
1082
+ "node_modules/mime-types": {
1083
+ "version": "2.1.35",
1084
+ "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
1085
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1086
+ "license": "MIT",
1087
+ "dependencies": {
1088
+ "mime-db": "1.52.0"
1089
+ },
1090
+ "engines": {
1091
+ "node": ">= 0.6"
1092
+ }
1093
+ },
1094
+ "node_modules/minimatch": {
1095
+ "version": "3.1.2",
1096
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
1097
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
1098
+ "dev": true,
1099
+ "license": "ISC",
1100
+ "dependencies": {
1101
+ "brace-expansion": "^1.1.7"
1102
+ },
1103
+ "engines": {
1104
+ "node": "*"
1105
+ }
1106
+ },
1107
+ "node_modules/ms": {
1108
+ "version": "2.0.0",
1109
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
1110
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
1111
+ "license": "MIT"
1112
+ },
1113
+ "node_modules/negotiator": {
1114
+ "version": "0.6.3",
1115
+ "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz",
1116
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
1117
+ "license": "MIT",
1118
+ "engines": {
1119
+ "node": ">= 0.6"
1120
+ }
1121
+ },
1122
+ "node_modules/nodemon": {
1123
+ "version": "3.1.7",
1124
+ "resolved": "https://registry.npmmirror.com/nodemon/-/nodemon-3.1.7.tgz",
1125
+ "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==",
1126
+ "dev": true,
1127
+ "license": "MIT",
1128
+ "dependencies": {
1129
+ "chokidar": "^3.5.2",
1130
+ "debug": "^4",
1131
+ "ignore-by-default": "^1.0.1",
1132
+ "minimatch": "^3.1.2",
1133
+ "pstree.remy": "^1.1.8",
1134
+ "semver": "^7.5.3",
1135
+ "simple-update-notifier": "^2.0.0",
1136
+ "supports-color": "^5.5.0",
1137
+ "touch": "^3.1.0",
1138
+ "undefsafe": "^2.0.5"
1139
+ },
1140
+ "bin": {
1141
+ "nodemon": "bin/nodemon.js"
1142
+ },
1143
+ "engines": {
1144
+ "node": ">=10"
1145
+ },
1146
+ "funding": {
1147
+ "type": "opencollective",
1148
+ "url": "https://opencollective.com/nodemon"
1149
+ }
1150
+ },
1151
+ "node_modules/nodemon/node_modules/debug": {
1152
+ "version": "4.4.0",
1153
+ "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.0.tgz",
1154
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
1155
+ "dev": true,
1156
+ "license": "MIT",
1157
+ "dependencies": {
1158
+ "ms": "^2.1.3"
1159
+ },
1160
+ "engines": {
1161
+ "node": ">=6.0"
1162
+ },
1163
+ "peerDependenciesMeta": {
1164
+ "supports-color": {
1165
+ "optional": true
1166
+ }
1167
+ }
1168
+ },
1169
+ "node_modules/nodemon/node_modules/ms": {
1170
+ "version": "2.1.3",
1171
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
1172
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1173
+ "dev": true,
1174
+ "license": "MIT"
1175
+ },
1176
+ "node_modules/normalize-path": {
1177
+ "version": "3.0.0",
1178
+ "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
1179
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1180
+ "dev": true,
1181
+ "license": "MIT",
1182
+ "engines": {
1183
+ "node": ">=0.10.0"
1184
+ }
1185
+ },
1186
+ "node_modules/object-assign": {
1187
+ "version": "4.1.1",
1188
+ "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
1189
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1190
+ "license": "MIT",
1191
+ "engines": {
1192
+ "node": ">=0.10.0"
1193
+ }
1194
+ },
1195
+ "node_modules/object-inspect": {
1196
+ "version": "1.13.3",
1197
+ "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.3.tgz",
1198
+ "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
1199
+ "license": "MIT",
1200
+ "engines": {
1201
+ "node": ">= 0.4"
1202
+ },
1203
+ "funding": {
1204
+ "url": "https://github.com/sponsors/ljharb"
1205
+ }
1206
+ },
1207
+ "node_modules/on-finished": {
1208
+ "version": "2.4.1",
1209
+ "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz",
1210
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1211
+ "license": "MIT",
1212
+ "dependencies": {
1213
+ "ee-first": "1.1.1"
1214
+ },
1215
+ "engines": {
1216
+ "node": ">= 0.8"
1217
+ }
1218
+ },
1219
+ "node_modules/parseurl": {
1220
+ "version": "1.3.3",
1221
+ "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz",
1222
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1223
+ "license": "MIT",
1224
+ "engines": {
1225
+ "node": ">= 0.8"
1226
+ }
1227
+ },
1228
+ "node_modules/path-to-regexp": {
1229
+ "version": "0.1.12",
1230
+ "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
1231
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
1232
+ "license": "MIT"
1233
+ },
1234
+ "node_modules/picomatch": {
1235
+ "version": "2.3.1",
1236
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
1237
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1238
+ "dev": true,
1239
+ "license": "MIT",
1240
+ "engines": {
1241
+ "node": ">=8.6"
1242
+ },
1243
+ "funding": {
1244
+ "url": "https://github.com/sponsors/jonschlinkert"
1245
+ }
1246
+ },
1247
+ "node_modules/proxy-addr": {
1248
+ "version": "2.0.7",
1249
+ "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz",
1250
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1251
+ "license": "MIT",
1252
+ "dependencies": {
1253
+ "forwarded": "0.2.0",
1254
+ "ipaddr.js": "1.9.1"
1255
+ },
1256
+ "engines": {
1257
+ "node": ">= 0.10"
1258
+ }
1259
+ },
1260
+ "node_modules/proxy-from-env": {
1261
+ "version": "1.1.0",
1262
+ "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
1263
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
1264
+ "license": "MIT"
1265
+ },
1266
+ "node_modules/pstree.remy": {
1267
+ "version": "1.1.8",
1268
+ "resolved": "https://registry.npmmirror.com/pstree.remy/-/pstree.remy-1.1.8.tgz",
1269
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
1270
+ "dev": true,
1271
+ "license": "MIT"
1272
+ },
1273
+ "node_modules/qs": {
1274
+ "version": "6.13.0",
1275
+ "resolved": "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz",
1276
+ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
1277
+ "license": "BSD-3-Clause",
1278
+ "dependencies": {
1279
+ "side-channel": "^1.0.6"
1280
+ },
1281
+ "engines": {
1282
+ "node": ">=0.6"
1283
+ },
1284
+ "funding": {
1285
+ "url": "https://github.com/sponsors/ljharb"
1286
+ }
1287
+ },
1288
+ "node_modules/range-parser": {
1289
+ "version": "1.2.1",
1290
+ "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz",
1291
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1292
+ "license": "MIT",
1293
+ "engines": {
1294
+ "node": ">= 0.6"
1295
+ }
1296
+ },
1297
+ "node_modules/raw-body": {
1298
+ "version": "2.5.2",
1299
+ "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz",
1300
+ "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
1301
+ "license": "MIT",
1302
+ "dependencies": {
1303
+ "bytes": "3.1.2",
1304
+ "http-errors": "2.0.0",
1305
+ "iconv-lite": "0.4.24",
1306
+ "unpipe": "1.0.0"
1307
+ },
1308
+ "engines": {
1309
+ "node": ">= 0.8"
1310
+ }
1311
+ },
1312
+ "node_modules/readdirp": {
1313
+ "version": "3.6.0",
1314
+ "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
1315
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1316
+ "dev": true,
1317
+ "license": "MIT",
1318
+ "dependencies": {
1319
+ "picomatch": "^2.2.1"
1320
+ },
1321
+ "engines": {
1322
+ "node": ">=8.10.0"
1323
+ }
1324
+ },
1325
+ "node_modules/rss-parser": {
1326
+ "version": "3.13.0",
1327
+ "resolved": "https://registry.npmmirror.com/rss-parser/-/rss-parser-3.13.0.tgz",
1328
+ "integrity": "sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==",
1329
+ "license": "MIT",
1330
+ "dependencies": {
1331
+ "entities": "^2.0.3",
1332
+ "xml2js": "^0.5.0"
1333
+ }
1334
+ },
1335
+ "node_modules/safe-buffer": {
1336
+ "version": "5.2.1",
1337
+ "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
1338
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1339
+ "funding": [
1340
+ {
1341
+ "type": "github",
1342
+ "url": "https://github.com/sponsors/feross"
1343
+ },
1344
+ {
1345
+ "type": "patreon",
1346
+ "url": "https://www.patreon.com/feross"
1347
+ },
1348
+ {
1349
+ "type": "consulting",
1350
+ "url": "https://feross.org/support"
1351
+ }
1352
+ ],
1353
+ "license": "MIT"
1354
+ },
1355
+ "node_modules/safer-buffer": {
1356
+ "version": "2.1.2",
1357
+ "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
1358
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
1359
+ "license": "MIT"
1360
+ },
1361
+ "node_modules/sax": {
1362
+ "version": "1.4.1",
1363
+ "resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
1364
+ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
1365
+ "license": "ISC"
1366
+ },
1367
+ "node_modules/semver": {
1368
+ "version": "7.6.3",
1369
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz",
1370
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
1371
+ "dev": true,
1372
+ "license": "ISC",
1373
+ "bin": {
1374
+ "semver": "bin/semver.js"
1375
+ },
1376
+ "engines": {
1377
+ "node": ">=10"
1378
+ }
1379
+ },
1380
+ "node_modules/send": {
1381
+ "version": "0.19.0",
1382
+ "resolved": "https://registry.npmmirror.com/send/-/send-0.19.0.tgz",
1383
+ "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
1384
+ "license": "MIT",
1385
+ "dependencies": {
1386
+ "debug": "2.6.9",
1387
+ "depd": "2.0.0",
1388
+ "destroy": "1.2.0",
1389
+ "encodeurl": "~1.0.2",
1390
+ "escape-html": "~1.0.3",
1391
+ "etag": "~1.8.1",
1392
+ "fresh": "0.5.2",
1393
+ "http-errors": "2.0.0",
1394
+ "mime": "1.6.0",
1395
+ "ms": "2.1.3",
1396
+ "on-finished": "2.4.1",
1397
+ "range-parser": "~1.2.1",
1398
+ "statuses": "2.0.1"
1399
+ },
1400
+ "engines": {
1401
+ "node": ">= 0.8.0"
1402
+ }
1403
+ },
1404
+ "node_modules/send/node_modules/encodeurl": {
1405
+ "version": "1.0.2",
1406
+ "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz",
1407
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
1408
+ "license": "MIT",
1409
+ "engines": {
1410
+ "node": ">= 0.8"
1411
+ }
1412
+ },
1413
+ "node_modules/send/node_modules/ms": {
1414
+ "version": "2.1.3",
1415
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
1416
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1417
+ "license": "MIT"
1418
+ },
1419
+ "node_modules/serve-static": {
1420
+ "version": "1.16.2",
1421
+ "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.2.tgz",
1422
+ "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
1423
+ "license": "MIT",
1424
+ "dependencies": {
1425
+ "encodeurl": "~2.0.0",
1426
+ "escape-html": "~1.0.3",
1427
+ "parseurl": "~1.3.3",
1428
+ "send": "0.19.0"
1429
+ },
1430
+ "engines": {
1431
+ "node": ">= 0.8.0"
1432
+ }
1433
+ },
1434
+ "node_modules/set-function-length": {
1435
+ "version": "1.2.2",
1436
+ "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
1437
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
1438
+ "license": "MIT",
1439
+ "dependencies": {
1440
+ "define-data-property": "^1.1.4",
1441
+ "es-errors": "^1.3.0",
1442
+ "function-bind": "^1.1.2",
1443
+ "get-intrinsic": "^1.2.4",
1444
+ "gopd": "^1.0.1",
1445
+ "has-property-descriptors": "^1.0.2"
1446
+ },
1447
+ "engines": {
1448
+ "node": ">= 0.4"
1449
+ }
1450
+ },
1451
+ "node_modules/setprototypeof": {
1452
+ "version": "1.2.0",
1453
+ "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz",
1454
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
1455
+ "license": "ISC"
1456
+ },
1457
+ "node_modules/side-channel": {
1458
+ "version": "1.0.6",
1459
+ "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.6.tgz",
1460
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
1461
+ "license": "MIT",
1462
+ "dependencies": {
1463
+ "call-bind": "^1.0.7",
1464
+ "es-errors": "^1.3.0",
1465
+ "get-intrinsic": "^1.2.4",
1466
+ "object-inspect": "^1.13.1"
1467
+ },
1468
+ "engines": {
1469
+ "node": ">= 0.4"
1470
+ },
1471
+ "funding": {
1472
+ "url": "https://github.com/sponsors/ljharb"
1473
+ }
1474
+ },
1475
+ "node_modules/simple-update-notifier": {
1476
+ "version": "2.0.0",
1477
+ "resolved": "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
1478
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
1479
+ "dev": true,
1480
+ "license": "MIT",
1481
+ "dependencies": {
1482
+ "semver": "^7.5.3"
1483
+ },
1484
+ "engines": {
1485
+ "node": ">=10"
1486
+ }
1487
+ },
1488
+ "node_modules/statuses": {
1489
+ "version": "2.0.1",
1490
+ "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz",
1491
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1492
+ "license": "MIT",
1493
+ "engines": {
1494
+ "node": ">= 0.8"
1495
+ }
1496
+ },
1497
+ "node_modules/supports-color": {
1498
+ "version": "5.5.0",
1499
+ "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz",
1500
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1501
+ "dev": true,
1502
+ "license": "MIT",
1503
+ "dependencies": {
1504
+ "has-flag": "^3.0.0"
1505
+ },
1506
+ "engines": {
1507
+ "node": ">=4"
1508
+ }
1509
+ },
1510
+ "node_modules/to-regex-range": {
1511
+ "version": "5.0.1",
1512
+ "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
1513
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1514
+ "dev": true,
1515
+ "license": "MIT",
1516
+ "dependencies": {
1517
+ "is-number": "^7.0.0"
1518
+ },
1519
+ "engines": {
1520
+ "node": ">=8.0"
1521
+ }
1522
+ },
1523
+ "node_modules/toidentifier": {
1524
+ "version": "1.0.1",
1525
+ "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz",
1526
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1527
+ "license": "MIT",
1528
+ "engines": {
1529
+ "node": ">=0.6"
1530
+ }
1531
+ },
1532
+ "node_modules/touch": {
1533
+ "version": "3.1.1",
1534
+ "resolved": "https://registry.npmmirror.com/touch/-/touch-3.1.1.tgz",
1535
+ "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
1536
+ "dev": true,
1537
+ "license": "ISC",
1538
+ "bin": {
1539
+ "nodetouch": "bin/nodetouch.js"
1540
+ }
1541
+ },
1542
+ "node_modules/ts-node": {
1543
+ "version": "10.9.2",
1544
+ "resolved": "https://registry.npmmirror.com/ts-node/-/ts-node-10.9.2.tgz",
1545
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
1546
+ "dev": true,
1547
+ "license": "MIT",
1548
+ "dependencies": {
1549
+ "@cspotcode/source-map-support": "^0.8.0",
1550
+ "@tsconfig/node10": "^1.0.7",
1551
+ "@tsconfig/node12": "^1.0.7",
1552
+ "@tsconfig/node14": "^1.0.0",
1553
+ "@tsconfig/node16": "^1.0.2",
1554
+ "acorn": "^8.4.1",
1555
+ "acorn-walk": "^8.1.1",
1556
+ "arg": "^4.1.0",
1557
+ "create-require": "^1.1.0",
1558
+ "diff": "^4.0.1",
1559
+ "make-error": "^1.1.1",
1560
+ "v8-compile-cache-lib": "^3.0.1",
1561
+ "yn": "3.1.1"
1562
+ },
1563
+ "bin": {
1564
+ "ts-node": "dist/bin.js",
1565
+ "ts-node-cwd": "dist/bin-cwd.js",
1566
+ "ts-node-esm": "dist/bin-esm.js",
1567
+ "ts-node-script": "dist/bin-script.js",
1568
+ "ts-node-transpile-only": "dist/bin-transpile.js",
1569
+ "ts-script": "dist/bin-script-deprecated.js"
1570
+ },
1571
+ "peerDependencies": {
1572
+ "@swc/core": ">=1.2.50",
1573
+ "@swc/wasm": ">=1.2.50",
1574
+ "@types/node": "*",
1575
+ "typescript": ">=2.7"
1576
+ },
1577
+ "peerDependenciesMeta": {
1578
+ "@swc/core": {
1579
+ "optional": true
1580
+ },
1581
+ "@swc/wasm": {
1582
+ "optional": true
1583
+ }
1584
+ }
1585
+ },
1586
+ "node_modules/type-is": {
1587
+ "version": "1.6.18",
1588
+ "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz",
1589
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1590
+ "license": "MIT",
1591
+ "dependencies": {
1592
+ "media-typer": "0.3.0",
1593
+ "mime-types": "~2.1.24"
1594
+ },
1595
+ "engines": {
1596
+ "node": ">= 0.6"
1597
+ }
1598
+ },
1599
+ "node_modules/typescript": {
1600
+ "version": "5.7.2",
1601
+ "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.7.2.tgz",
1602
+ "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
1603
+ "dev": true,
1604
+ "license": "Apache-2.0",
1605
+ "bin": {
1606
+ "tsc": "bin/tsc",
1607
+ "tsserver": "bin/tsserver"
1608
+ },
1609
+ "engines": {
1610
+ "node": ">=14.17"
1611
+ }
1612
+ },
1613
+ "node_modules/undefsafe": {
1614
+ "version": "2.0.5",
1615
+ "resolved": "https://registry.npmmirror.com/undefsafe/-/undefsafe-2.0.5.tgz",
1616
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
1617
+ "dev": true,
1618
+ "license": "MIT"
1619
+ },
1620
+ "node_modules/undici-types": {
1621
+ "version": "6.19.8",
1622
+ "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.19.8.tgz",
1623
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
1624
+ "dev": true,
1625
+ "license": "MIT"
1626
+ },
1627
+ "node_modules/unpipe": {
1628
+ "version": "1.0.0",
1629
+ "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz",
1630
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1631
+ "license": "MIT",
1632
+ "engines": {
1633
+ "node": ">= 0.8"
1634
+ }
1635
+ },
1636
+ "node_modules/utils-merge": {
1637
+ "version": "1.0.1",
1638
+ "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz",
1639
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
1640
+ "license": "MIT",
1641
+ "engines": {
1642
+ "node": ">= 0.4.0"
1643
+ }
1644
+ },
1645
+ "node_modules/v8-compile-cache-lib": {
1646
+ "version": "3.0.1",
1647
+ "resolved": "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
1648
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
1649
+ "dev": true,
1650
+ "license": "MIT"
1651
+ },
1652
+ "node_modules/vary": {
1653
+ "version": "1.1.2",
1654
+ "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz",
1655
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1656
+ "license": "MIT",
1657
+ "engines": {
1658
+ "node": ">= 0.8"
1659
+ }
1660
+ },
1661
+ "node_modules/xml2js": {
1662
+ "version": "0.5.0",
1663
+ "resolved": "https://registry.npmmirror.com/xml2js/-/xml2js-0.5.0.tgz",
1664
+ "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
1665
+ "license": "MIT",
1666
+ "dependencies": {
1667
+ "sax": ">=0.6.0",
1668
+ "xmlbuilder": "~11.0.0"
1669
+ },
1670
+ "engines": {
1671
+ "node": ">=4.0.0"
1672
+ }
1673
+ },
1674
+ "node_modules/xmlbuilder": {
1675
+ "version": "11.0.1",
1676
+ "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
1677
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
1678
+ "license": "MIT",
1679
+ "engines": {
1680
+ "node": ">=4.0"
1681
+ }
1682
+ },
1683
+ "node_modules/yn": {
1684
+ "version": "3.1.1",
1685
+ "resolved": "https://registry.npmmirror.com/yn/-/yn-3.1.1.tgz",
1686
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
1687
+ "dev": true,
1688
+ "license": "MIT",
1689
+ "engines": {
1690
+ "node": ">=6"
1691
+ }
1692
+ }
1693
+ }
1694
+ }
backend/package.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "cloud-saver-server",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "nodemon --exec ts-node src/app.ts",
7
+ "build": "tsc",
8
+ "start": "node dist/app.js"
9
+ },
10
+ "dependencies": {
11
+ "axios": "^1.6.7",
12
+ "bcrypt": "^5.1.1",
13
+ "cheerio": "^1.0.0",
14
+ "cookie-parser": "^1.4.6",
15
+ "cors": "^2.8.5",
16
+ "dotenv": "^16.4.5",
17
+ "express": "^4.18.3",
18
+ "inversify": "^7.1.0",
19
+ "jsonwebtoken": "^9.0.2",
20
+ "rss-parser": "^3.13.0",
21
+ "sequelize": "^6.37.5",
22
+ "socket.io": "^4.8.1",
23
+ "sqlite3": "^5.1.7",
24
+ "tunnel": "^0.0.6",
25
+ "winston": "^3.17.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/bcrypt": "^5.0.2",
29
+ "@types/cookie-parser": "^1.4.7",
30
+ "@types/cors": "^2.8.17",
31
+ "@types/express": "^4.17.21",
32
+ "@types/jsonwebtoken": "^9.0.7",
33
+ "@types/node": "^20.11.25",
34
+ "@types/tunnel": "^0.0.7",
35
+ "nodemon": "^3.1.0",
36
+ "ts-node": "^10.9.2",
37
+ "typescript": "^5.4.2"
38
+ }
39
+ }
backend/src/app.ts ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // filepath: /d:/code/CloudDiskDown/backend/src/app.ts
2
+ import "./types/express";
3
+ import express from "express";
4
+ import { container } from "./inversify.config";
5
+ import { TYPES } from "./core/types";
6
+ import { DatabaseService } from "./services/DatabaseService";
7
+ import { setupMiddlewares } from "./middleware";
8
+ import routes from "./routes/api";
9
+ import { logger } from "./utils/logger";
10
+ import { errorHandler } from "./middleware/errorHandler";
11
+ class App {
12
+ private app = express();
13
+ private databaseService = container.get<DatabaseService>(TYPES.DatabaseService);
14
+
15
+ constructor() {
16
+ this.setupExpress();
17
+ }
18
+
19
+ private setupExpress(): void {
20
+ // 设置中间件
21
+ setupMiddlewares(this.app);
22
+
23
+ // 设置路由
24
+ this.app.use("/", routes);
25
+ this.app.use(errorHandler);
26
+ }
27
+
28
+ public async start(): Promise<void> {
29
+ try {
30
+ // 初始化数据库
31
+ await this.databaseService.initialize();
32
+ logger.info("数据库初始化成功");
33
+
34
+ // 启动服务器
35
+ const port = process.env.PORT || 8009;
36
+ this.app.listen(port, () => {
37
+ logger.info(`
38
+ 🚀 服务器启动成功
39
+ 🌍 监听端口: ${port}
40
+ 🔧 运行环境: ${process.env.NODE_ENV || "development"}
41
+ `);
42
+ });
43
+ } catch (error) {
44
+ logger.error("服务器启动失败:", error);
45
+ process.exit(1);
46
+ }
47
+ }
48
+ }
49
+
50
+ // 创建并启动应用
51
+ const application = new App();
52
+ application.start().catch((error) => {
53
+ logger.error("应用程序启动失败:", error);
54
+ process.exit(1);
55
+ });
56
+
57
+ export default application;
backend/src/config/database.ts ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ // backend/src/config/database.ts
2
+ import { Sequelize } from "sequelize";
3
+
4
+ const sequelize = new Sequelize({
5
+ dialect: "sqlite",
6
+ storage: "./data/database.sqlite",
7
+ });
8
+
9
+ export default sequelize;
backend/src/config/index.ts ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import dotenv from "dotenv";
2
+
3
+ // 加载.env文件
4
+ dotenv.config();
5
+
6
+ interface Channel {
7
+ id: string;
8
+ name: string;
9
+ }
10
+
11
+ interface CloudPatterns {
12
+ baiduPan: RegExp;
13
+ tianyi: RegExp;
14
+ aliyun: RegExp;
15
+ pan115: RegExp;
16
+ pan123: RegExp;
17
+ quark: RegExp;
18
+ yidong: RegExp;
19
+ }
20
+
21
+ interface Config {
22
+ jwtSecret: string;
23
+ telegram: {
24
+ baseUrl: string;
25
+ channels: Channel[];
26
+ };
27
+ cloudPatterns: CloudPatterns;
28
+ app: {
29
+ port: number;
30
+ env: string;
31
+ };
32
+ database: {
33
+ type: string;
34
+ path: string;
35
+ };
36
+ jwt: {
37
+ secret: string;
38
+ expiresIn: string;
39
+ };
40
+ }
41
+
42
+ // 从环境变量读取频道配置
43
+ const getTeleChannels = (): Channel[] => {
44
+ try {
45
+ const channelsStr = process.env.TELE_CHANNELS;
46
+ if (channelsStr) {
47
+ return JSON.parse(channelsStr);
48
+ }
49
+ } catch (error) {
50
+ console.warn("无法解析 TELE_CHANNELS 环境变量,使用默认配置");
51
+ }
52
+
53
+ // 默认配置
54
+ return [];
55
+ };
56
+
57
+ export const config: Config = {
58
+ app: {
59
+ port: parseInt(process.env.PORT || "8009"),
60
+ env: process.env.NODE_ENV || "development",
61
+ },
62
+ database: {
63
+ type: "sqlite",
64
+ path: "./data/database.sqlite",
65
+ },
66
+ jwt: {
67
+ secret: process.env.JWT_SECRET || "your-secret-key",
68
+ expiresIn: "6h",
69
+ },
70
+ jwtSecret: process.env.JWT_SECRET || "uV7Y$k92#LkF^q1b!",
71
+
72
+ telegram: {
73
+ baseUrl: process.env.TELEGRAM_BASE_URL || "https://t.me/s",
74
+ channels: getTeleChannels(),
75
+ },
76
+ cloudPatterns: {
77
+ baiduPan: /https?:\/\/(?:pan|yun)\.baidu\.com\/[^\s<>"]+/g,
78
+ tianyi: /https?:\/\/cloud\.189\.cn\/[^\s<>"]+/g,
79
+ aliyun: /https?:\/\/\w+\.(?:alipan|aliyundrive)\.com\/[^\s<>"]+/g,
80
+ // pan115有两个域名 115.com 和 anxia.com 和 115cdn.com
81
+ pan115: /https?:\/\/(?:115|anxia|115cdn)\.com\/s\/[^\s<>"]+/g,
82
+ // 修改为匹配所有以123开头的域名
83
+ // eslint-disable-next-line no-useless-escape
84
+ pan123: /https?:\/\/(?:www\.)?123[^\/\s<>"]+\.com\/s\/[^\s<>"]+/g,
85
+ quark: /https?:\/\/pan\.quark\.cn\/[^\s<>"]+/g,
86
+ yidong: /https?:\/\/caiyun\.139\.com\/[^\s<>"]+/g,
87
+ },
88
+ };
backend/src/controllers/BaseCloudController.ts ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import { BaseController } from "./BaseController";
3
+ import { ICloudStorageService } from "@/types/services";
4
+
5
+ export abstract class BaseCloudController extends BaseController {
6
+ constructor(protected cloudService: ICloudStorageService) {
7
+ super();
8
+ }
9
+
10
+ async getShareInfo(req: Request, res: Response): Promise<void> {
11
+ await this.handleRequest(req, res, async () => {
12
+ const { shareCode, receiveCode } = req.query;
13
+ // await this.cloudService.setCookie(req);
14
+ return await this.cloudService.getShareInfo(shareCode as string, receiveCode as string);
15
+ });
16
+ }
17
+
18
+ async getFolderList(req: Request, res: Response): Promise<void> {
19
+ await this.handleRequest(req, res, async () => {
20
+ const { parentCid } = req.query;
21
+ await this.cloudService.setCookie(req);
22
+ return await this.cloudService.getFolderList(parentCid as string);
23
+ });
24
+ }
25
+
26
+ async saveFile(req: Request, res: Response): Promise<void> {
27
+ await this.handleRequest(req, res, async () => {
28
+ await this.cloudService.setCookie(req);
29
+ return await this.cloudService.saveSharedFile(req.body);
30
+ });
31
+ }
32
+ }
backend/src/controllers/BaseController.ts ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import { ApiResponse } from "../core/ApiResponse";
3
+ interface ApiResponseData<T> {
4
+ data?: T;
5
+ message?: string;
6
+ }
7
+
8
+ export abstract class BaseController {
9
+ protected async handleRequest<T>(
10
+ req: Request,
11
+ res: Response,
12
+ action: () => Promise<ApiResponseData<T> | void>
13
+ ): Promise<void> {
14
+ try {
15
+ const result = await action();
16
+ if (result) {
17
+ res.json(ApiResponse.success(result.data, result.message));
18
+ }
19
+ } catch (error: unknown) {
20
+ const errorMessage = error instanceof Error ? error.message : "未知错误";
21
+ res.status(200).json(ApiResponse.error(errorMessage));
22
+ }
23
+ }
24
+ }
backend/src/controllers/cloud115.ts ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Cloud115Service } from "../services/Cloud115Service";
2
+ import { injectable, inject } from "inversify";
3
+ import { TYPES } from "../core/types";
4
+ import { BaseCloudController } from "./BaseCloudController";
5
+
6
+ @injectable()
7
+ export class Cloud115Controller extends BaseCloudController {
8
+ constructor(@inject(TYPES.Cloud115Service) cloud115Service: Cloud115Service) {
9
+ super(cloud115Service);
10
+ }
11
+ }
backend/src/controllers/douban.ts ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import { injectable, inject } from "inversify";
3
+ import { TYPES } from "../core/types";
4
+ import { DoubanService } from "../services/DoubanService";
5
+ import { BaseController } from "./BaseController";
6
+
7
+ @injectable()
8
+ export class DoubanController extends BaseController {
9
+ constructor(@inject(TYPES.DoubanService) private doubanService: DoubanService) {
10
+ super();
11
+ }
12
+
13
+ async getDoubanHotList(req: Request, res: Response): Promise<void> {
14
+ await this.handleRequest(req, res, async () => {
15
+ const { type = "movie", tag = "热门", page_limit = "50", page_start = "0" } = req.query;
16
+ const result = await this.doubanService.getHotList({
17
+ type: type as string,
18
+ tag: tag as string,
19
+ page_limit: page_limit as string,
20
+ page_start: page_start as string,
21
+ });
22
+ return result;
23
+ });
24
+ }
25
+ }
backend/src/controllers/quark.ts ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import { injectable, inject } from "inversify";
3
+ import { TYPES } from "../core/types";
4
+ import { QuarkService } from "../services/QuarkService";
5
+ import { BaseCloudController } from "./BaseCloudController";
6
+
7
+ @injectable()
8
+ export class QuarkController extends BaseCloudController {
9
+ constructor(@inject(TYPES.QuarkService) quarkService: QuarkService) {
10
+ super(quarkService);
11
+ }
12
+ }
backend/src/controllers/resource.ts ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import { injectable, inject } from "inversify";
3
+ import { TYPES } from "../core/types";
4
+ import { Searcher } from "../services/Searcher";
5
+ import { BaseController } from "./BaseController";
6
+
7
+ @injectable()
8
+ export class ResourceController extends BaseController {
9
+ constructor(@inject(TYPES.Searcher) private searcher: Searcher) {
10
+ super();
11
+ }
12
+
13
+ async search(req: Request, res: Response): Promise<void> {
14
+ await this.handleRequest(req, res, async () => {
15
+ const { keyword, channelId = "", lastMessageId = "" } = req.query;
16
+ return await this.searcher.searchAll(
17
+ keyword as string,
18
+ channelId as string,
19
+ lastMessageId as string
20
+ );
21
+ });
22
+ }
23
+ }
backend/src/controllers/setting.ts ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import { injectable, inject } from "inversify";
3
+ import { TYPES } from "../core/types";
4
+ import { SettingService } from "../services/SettingService";
5
+ import { BaseController } from "./BaseController";
6
+
7
+ @injectable()
8
+ export class SettingController extends BaseController {
9
+ constructor(@inject(TYPES.SettingService) private settingService: SettingService) {
10
+ super();
11
+ }
12
+
13
+ async get(req: Request, res: Response): Promise<void> {
14
+ await this.handleRequest(req, res, async () => {
15
+ const userId = req.user?.userId;
16
+ const role = Number(req.user?.role);
17
+ return await this.settingService.getSettings(userId, role);
18
+ });
19
+ }
20
+
21
+ async save(req: Request, res: Response): Promise<void> {
22
+ await this.handleRequest(req, res, async () => {
23
+ const userId = req.user?.userId;
24
+ const role = Number(req.user?.role);
25
+ return await this.settingService.saveSettings(userId, role, req.body);
26
+ });
27
+ }
28
+ }
backend/src/controllers/sponsors.ts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import { injectable, inject } from "inversify";
3
+ import { TYPES } from "../core/types";
4
+ import { SponsorsService } from "../services/SponsorsService";
5
+ import { BaseController } from "./BaseController";
6
+
7
+ @injectable()
8
+ export class SponsorsController extends BaseController {
9
+ constructor(@inject(TYPES.SponsorsService) private sponsorsService: SponsorsService) {
10
+ super();
11
+ }
12
+
13
+ async get(req: Request, res: Response): Promise<void> {
14
+ await this.handleRequest(req, res, async () => {
15
+ return await this.sponsorsService.getSponsors();
16
+ });
17
+ }
18
+ }
backend/src/controllers/teleImages.ts ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import { injectable, inject } from "inversify";
3
+ import { TYPES } from "../core/types";
4
+ import { ImageService } from "../services/ImageService";
5
+ import { BaseController } from "./BaseController";
6
+
7
+ @injectable()
8
+ export class ImageController extends BaseController {
9
+ constructor(@inject(TYPES.ImageService) private imageService: ImageService) {
10
+ super();
11
+ }
12
+
13
+ async getImages(req: Request, res: Response): Promise<void> {
14
+ await this.handleRequest(req, res, async () => {
15
+ const url = decodeURIComponent((req.query.url as string) || "");
16
+ const response = await this.imageService.getImages(url);
17
+
18
+ // 设置正确的响应头
19
+ res.setHeader("Content-Type", response.headers["content-type"]);
20
+ res.setHeader("Cache-Control", "no-cache");
21
+
22
+ // 确保清除任何可能导致304响应的头信息
23
+ res.removeHeader("etag");
24
+ res.removeHeader("last-modified");
25
+
26
+ // 直接传输图片数据
27
+ response.data.pipe(res);
28
+ });
29
+ }
30
+ }
backend/src/controllers/user.ts ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+ import { injectable, inject } from "inversify";
3
+ import { TYPES } from "../core/types";
4
+ import { UserService } from "../services/UserService";
5
+ import { BaseController } from "./BaseController";
6
+
7
+ @injectable()
8
+ export class UserController extends BaseController {
9
+ constructor(@inject(TYPES.UserService) private userService: UserService) {
10
+ super();
11
+ }
12
+
13
+ async register(req: Request, res: Response): Promise<void> {
14
+ await this.handleRequest(req, res, async () => {
15
+ const { username, password, registerCode } = req.body;
16
+ return await this.userService.register(username, password, registerCode);
17
+ });
18
+ }
19
+
20
+ async login(req: Request, res: Response): Promise<void> {
21
+ await this.handleRequest(req, res, async () => {
22
+ const { username, password } = req.body;
23
+ return await this.userService.login(username, password);
24
+ });
25
+ }
26
+ }
backend/src/core/ApiResponse.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export class ApiResponse<T> {
2
+ success: boolean;
3
+ data?: T;
4
+ message?: string;
5
+ code: number;
6
+
7
+ private constructor(success: boolean, code: number, data?: T, message?: string) {
8
+ this.success = success;
9
+ this.code = code;
10
+ this.data = data;
11
+ this.message = message;
12
+ }
13
+
14
+ static success<T>(data?: T, message = "操作成功"): ApiResponse<T> {
15
+ return new ApiResponse(true, 0, data, message);
16
+ }
17
+
18
+ static error(message: string, code = 10000): ApiResponse<null> {
19
+ return new ApiResponse(false, code, null, message);
20
+ }
21
+ }
backend/src/core/types.ts ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const TYPES = {
2
+ DatabaseService: Symbol.for("DatabaseService"),
3
+ Cloud115Service: Symbol.for("Cloud115Service"),
4
+ QuarkService: Symbol.for("QuarkService"),
5
+ Searcher: Symbol.for("Searcher"),
6
+ DoubanService: Symbol.for("DoubanService"),
7
+ ImageService: Symbol.for("ImageService"),
8
+ SettingService: Symbol.for("SettingService"),
9
+ UserService: Symbol.for("UserService"),
10
+ SponsorsService: Symbol.for("SponsorsService"),
11
+
12
+ Cloud115Controller: Symbol.for("Cloud115Controller"),
13
+ QuarkController: Symbol.for("QuarkController"),
14
+ ResourceController: Symbol.for("ResourceController"),
15
+ DoubanController: Symbol.for("DoubanController"),
16
+ ImageController: Symbol.for("ImageController"),
17
+ SettingController: Symbol.for("SettingController"),
18
+ UserController: Symbol.for("UserController"),
19
+ SponsorsController: Symbol.for("SponsorsController"),
20
+ };
backend/src/inversify.config.ts ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Container } from "inversify";
2
+ import { TYPES } from "./core/types";
3
+
4
+ // Services
5
+ import { DatabaseService } from "./services/DatabaseService";
6
+ import { Cloud115Service } from "./services/Cloud115Service";
7
+ import { QuarkService } from "./services/QuarkService";
8
+ import { Searcher } from "./services/Searcher";
9
+ import { DoubanService } from "./services/DoubanService";
10
+ import { UserService } from "./services/UserService";
11
+ import { ImageService } from "./services/ImageService";
12
+ import { SettingService } from "./services/SettingService";
13
+ import { SponsorsService } from "./services/SponsorsService";
14
+ // Controllers
15
+ import { Cloud115Controller } from "./controllers/cloud115";
16
+ import { QuarkController } from "./controllers/quark";
17
+ import { ResourceController } from "./controllers/resource";
18
+ import { DoubanController } from "./controllers/douban";
19
+ import { ImageController } from "./controllers/teleImages";
20
+ import { SettingController } from "./controllers/setting";
21
+ import { UserController } from "./controllers/user";
22
+ import { SponsorsController } from "./controllers/sponsors";
23
+ const container = new Container();
24
+
25
+ // Services
26
+ container.bind<DatabaseService>(TYPES.DatabaseService).to(DatabaseService).inSingletonScope();
27
+ container.bind<Cloud115Service>(TYPES.Cloud115Service).to(Cloud115Service).inSingletonScope();
28
+ container.bind<QuarkService>(TYPES.QuarkService).to(QuarkService).inSingletonScope();
29
+ container.bind<Searcher>(TYPES.Searcher).to(Searcher).inSingletonScope();
30
+ container.bind<ImageService>(TYPES.ImageService).to(ImageService).inSingletonScope();
31
+ container.bind<SettingService>(TYPES.SettingService).to(SettingService).inSingletonScope();
32
+ container.bind<DoubanService>(TYPES.DoubanService).to(DoubanService).inSingletonScope();
33
+ container.bind<UserService>(TYPES.UserService).to(UserService).inSingletonScope();
34
+ container.bind<SponsorsService>(TYPES.SponsorsService).to(SponsorsService).inSingletonScope();
35
+ // Controllers
36
+ container.bind<Cloud115Controller>(TYPES.Cloud115Controller).to(Cloud115Controller);
37
+ container.bind<QuarkController>(TYPES.QuarkController).to(QuarkController);
38
+ container.bind<ResourceController>(TYPES.ResourceController).to(ResourceController);
39
+ container.bind<DoubanController>(TYPES.DoubanController).to(DoubanController);
40
+ container.bind<ImageController>(TYPES.ImageController).to(ImageController);
41
+ container.bind<SettingController>(TYPES.SettingController).to(SettingController);
42
+ container.bind<UserController>(TYPES.UserController).to(UserController);
43
+ container.bind<SponsorsController>(TYPES.SponsorsController).to(SponsorsController);
44
+
45
+ export { container };
backend/src/middleware/auth.ts ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // filepath: /D:/code/CloudDiskDown/backend/src/middleware/auth.ts
2
+ import { Request, Response, NextFunction } from "express";
3
+ import jwt, { JwtPayload } from "jsonwebtoken";
4
+ import User from "../models/User";
5
+ import { config } from "../config";
6
+
7
+ interface AuthenticatedRequest extends Request {
8
+ user?: {
9
+ userId: string;
10
+ role: number;
11
+ };
12
+ }
13
+
14
+ export const authMiddleware = async (
15
+ req: AuthenticatedRequest,
16
+ res: Response,
17
+ next: NextFunction
18
+ ): Promise<void | Response> => {
19
+ if (req.path === "/user/login" || req.path === "/user/register" || req.path === "/tele-images/") {
20
+ return next();
21
+ }
22
+
23
+ const token = req.headers.authorization?.split(" ")[1];
24
+ if (!token) {
25
+ return res.status(401).json({ message: "未提供 token" });
26
+ }
27
+
28
+ try {
29
+ const decoded = jwt.verify(token, config.jwtSecret) as JwtPayload;
30
+
31
+ req.user = {
32
+ userId: decoded.userId,
33
+ role: decoded.role,
34
+ };
35
+ const user = await User.findOne({ where: { userId: decoded.userId } });
36
+ if (!user) {
37
+ return res.status(401).json({ message: "无效的 token" });
38
+ }
39
+ next();
40
+ } catch (error) {
41
+ res.status(401).json({ message: "无效的 token" });
42
+ }
43
+ };
backend/src/middleware/cors.ts ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response, NextFunction } from "express";
2
+
3
+ export const cors = () => {
4
+ return (req: Request, res: Response, next: NextFunction) => {
5
+ res.header("Access-Control-Allow-Origin", "*");
6
+ res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
7
+ res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, Cookie");
8
+ res.header("Access-Control-Allow-Credentials", "true");
9
+
10
+ if (req.method === "OPTIONS") {
11
+ return res.sendStatus(200);
12
+ }
13
+ next();
14
+ };
15
+ };
backend/src/middleware/errorHandler.ts ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response } from "express";
2
+
3
+ interface CustomError extends Error {
4
+ status?: number;
5
+ }
6
+
7
+ export const errorHandler = (err: CustomError, req: Request, res: Response): void => {
8
+ res.status(err.status || 500).json({
9
+ success: false,
10
+ error: err.message || "服务器内部错误",
11
+ });
12
+ };
backend/src/middleware/index.ts ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Application } from "express";
2
+ import express from "express";
3
+ import { authMiddleware } from "./auth";
4
+ import { requestLogger } from "./requestLogger";
5
+ import { rateLimiter } from "./rateLimiter";
6
+ import { cors } from "./cors";
7
+
8
+ export const setupMiddlewares = (app: Application) => {
9
+ app.use(express.json());
10
+ app.use(cors());
11
+ app.use(requestLogger());
12
+ app.use(rateLimiter());
13
+ app.use(authMiddleware);
14
+ };
backend/src/middleware/rateLimiter.ts ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response, NextFunction } from "express";
2
+
3
+ const requestCounts = new Map<string, { count: number; timestamp: number }>();
4
+ const WINDOW_MS = 60 * 1000; // 1分钟窗口
5
+ const MAX_REQUESTS = 600; // 每个IP每分钟最多60个请求
6
+
7
+ export const rateLimiter = () => {
8
+ return (req: Request, res: Response, next: NextFunction) => {
9
+ const ip = req.ip || req.socket.remoteAddress || "unknown";
10
+ const now = Date.now();
11
+ const record = requestCounts.get(ip) || { count: 0, timestamp: now };
12
+
13
+ if (now - record.timestamp > WINDOW_MS) {
14
+ record.count = 0;
15
+ record.timestamp = now;
16
+ }
17
+
18
+ record.count++;
19
+ requestCounts.set(ip, record);
20
+
21
+ if (record.count > MAX_REQUESTS) {
22
+ return res.status(429).json({ message: "请求过于频繁,请稍后再试" });
23
+ }
24
+
25
+ next();
26
+ };
27
+ };
backend/src/middleware/requestLogger.ts ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response, NextFunction } from "express";
2
+ import { logger } from "../utils/logger";
3
+
4
+ const excludePaths = ["/tele-images/"];
5
+
6
+ export const requestLogger = () => {
7
+ return (req: Request, res: Response, next: NextFunction) => {
8
+ const start = Date.now();
9
+ res.on("finish", () => {
10
+ if (excludePaths.includes(req.path)) {
11
+ return;
12
+ }
13
+ const duration = Date.now() - start;
14
+ logger.info({
15
+ method: req.method,
16
+ path: req.path,
17
+ status: res.statusCode,
18
+ duration: `${duration}ms`,
19
+ });
20
+ });
21
+ next();
22
+ };
23
+ };
backend/src/middleware/validateRequest.ts ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Request, Response, NextFunction } from "express";
2
+
3
+ export const validateRequest = (
4
+ requiredParams: string[]
5
+ ): ((req: Request, res: Response, next: NextFunction) => Response | void) => {
6
+ return (req: Request, res: Response, next: NextFunction) => {
7
+ const missingParams = requiredParams.filter((param) => !req.query[param] && !req.body[param]);
8
+ if (missingParams.length > 0) {
9
+ return res.status(400).json({
10
+ success: false,
11
+ error: `缺少必要的参数: ${missingParams.join(", ")}`,
12
+ });
13
+ }
14
+ next();
15
+ };
16
+ };
backend/src/models/GlobalSetting.ts ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { DataTypes, Model, Optional } from "sequelize";
2
+ import sequelize from "../config/database";
3
+
4
+ export interface GlobalSettingAttributes {
5
+ id: number;
6
+ httpProxyHost: string;
7
+ httpProxyPort: number;
8
+ isProxyEnabled: boolean;
9
+ CommonUserCode: number;
10
+ AdminUserCode: number;
11
+ }
12
+
13
+ interface GlobalSettingCreationAttributes extends Optional<GlobalSettingAttributes, "id"> {}
14
+
15
+ class GlobalSetting
16
+ extends Model<GlobalSettingAttributes, GlobalSettingCreationAttributes>
17
+ implements GlobalSettingAttributes
18
+ {
19
+ public id!: number;
20
+ public httpProxyHost!: string;
21
+ public httpProxyPort!: number;
22
+ public isProxyEnabled!: boolean;
23
+ public CommonUserCode!: number;
24
+ public AdminUserCode!: number;
25
+ }
26
+
27
+ GlobalSetting.init(
28
+ {
29
+ id: {
30
+ type: DataTypes.INTEGER,
31
+ autoIncrement: true,
32
+ primaryKey: true,
33
+ },
34
+ httpProxyHost: {
35
+ type: DataTypes.STRING,
36
+ allowNull: false,
37
+ defaultValue: "127.0.0.1",
38
+ },
39
+ httpProxyPort: {
40
+ type: DataTypes.INTEGER,
41
+ allowNull: false,
42
+ defaultValue: 7890,
43
+ },
44
+ isProxyEnabled: {
45
+ type: DataTypes.BOOLEAN,
46
+ allowNull: false,
47
+ defaultValue: true,
48
+ },
49
+ CommonUserCode: {
50
+ type: DataTypes.INTEGER,
51
+ allowNull: true,
52
+ defaultValue: 9527,
53
+ },
54
+ AdminUserCode: {
55
+ type: DataTypes.INTEGER,
56
+ allowNull: false,
57
+ defaultValue: 230713,
58
+ },
59
+ },
60
+ {
61
+ sequelize,
62
+ modelName: "GlobalSetting",
63
+ tableName: "global_settings",
64
+ }
65
+ );
66
+
67
+ export default GlobalSetting;
backend/src/models/User.ts ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { DataTypes, Model, Optional } from "sequelize";
2
+ import sequelize from "../config/database";
3
+
4
+ interface UserAttributes {
5
+ id: number;
6
+ userId?: number;
7
+ username: string;
8
+ password: string;
9
+ role: number; // 修改为数字类型
10
+ }
11
+
12
+ interface UserCreationAttributes extends Optional<UserAttributes, "id"> {}
13
+
14
+ class User extends Model<UserAttributes, UserCreationAttributes> implements UserAttributes {
15
+ public id!: number;
16
+ public userId!: number;
17
+ public username!: string;
18
+ public password!: string;
19
+ public role!: number; // 实现数字类型的角色属性
20
+ }
21
+
22
+ User.init(
23
+ {
24
+ id: {
25
+ type: DataTypes.INTEGER,
26
+ autoIncrement: true,
27
+ primaryKey: true,
28
+ allowNull: false, // 显式设置为不可为空
29
+ },
30
+ userId: {
31
+ type: DataTypes.UUID, // 对外暴露的不可预测ID
32
+ defaultValue: DataTypes.UUIDV4,
33
+ unique: true,
34
+ allowNull: false, // 显式设置为不可为空
35
+ },
36
+ username: {
37
+ type: DataTypes.STRING,
38
+ allowNull: false,
39
+ unique: true,
40
+ },
41
+ password: {
42
+ type: DataTypes.STRING,
43
+ allowNull: false,
44
+ },
45
+ role: {
46
+ type: DataTypes.INTEGER, // 修改为数字类型
47
+ allowNull: false,
48
+ defaultValue: 0, // 默认值为普通用户
49
+ },
50
+ },
51
+ {
52
+ sequelize,
53
+ modelName: "User",
54
+ tableName: "users",
55
+ }
56
+ );
57
+
58
+ // 角色映射
59
+ // 0: 普通用户
60
+ // 1: 管理员
61
+
62
+ export default User;
backend/src/models/UserSetting.ts ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { DataTypes, Model, Optional } from "sequelize";
2
+ import sequelize from "../config/database";
3
+ import User from "./User";
4
+
5
+ interface UserSettingAttributes {
6
+ id: number;
7
+ userId: string;
8
+ cloud115UserId?: string;
9
+ cloud115Cookie: string;
10
+ quarkCookie: string;
11
+ }
12
+
13
+ interface UserSettingCreationAttributes extends Optional<UserSettingAttributes, "id"> {}
14
+
15
+ class UserSetting
16
+ extends Model<UserSettingAttributes, UserSettingCreationAttributes>
17
+ implements UserSettingAttributes
18
+ {
19
+ public id!: number;
20
+ public userId!: string;
21
+ public cloud115UserId?: string;
22
+ public cloud115Cookie!: string;
23
+ public quarkCookie!: string;
24
+ }
25
+
26
+ UserSetting.init(
27
+ {
28
+ id: {
29
+ type: DataTypes.INTEGER,
30
+ autoIncrement: true,
31
+ primaryKey: true,
32
+ },
33
+ userId: {
34
+ type: DataTypes.UUID,
35
+ allowNull: false,
36
+ unique: true,
37
+ references: {
38
+ model: User,
39
+ key: "userId",
40
+ },
41
+ onDelete: "CASCADE",
42
+ },
43
+ cloud115UserId: {
44
+ type: DataTypes.STRING,
45
+ allowNull: true,
46
+ },
47
+ cloud115Cookie: {
48
+ type: DataTypes.STRING,
49
+ allowNull: true,
50
+ },
51
+ quarkCookie: {
52
+ type: DataTypes.STRING,
53
+ allowNull: true,
54
+ },
55
+ },
56
+ {
57
+ sequelize,
58
+ modelName: "UserSetting",
59
+ tableName: "user_settings",
60
+ }
61
+ );
62
+
63
+ User.hasOne(UserSetting, {
64
+ foreignKey: "userId",
65
+ as: "settings",
66
+ });
67
+ UserSetting.belongsTo(User, {
68
+ foreignKey: "userId",
69
+ as: "user",
70
+ });
71
+
72
+ export default UserSetting;
backend/src/routes/api.ts ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Router } from "express";
2
+ import { container } from "../inversify.config";
3
+ import { TYPES } from "../core/types";
4
+ import { Cloud115Controller } from "../controllers/cloud115";
5
+ import { QuarkController } from "../controllers/quark";
6
+ import { ResourceController } from "../controllers/resource";
7
+ import { DoubanController } from "../controllers/douban";
8
+ import { ImageController } from "../controllers/teleImages";
9
+ import { SettingController } from "../controllers/setting";
10
+ import { UserController } from "../controllers/user";
11
+ import { SponsorsController } from "../controllers/sponsors";
12
+
13
+ const router = Router();
14
+
15
+ // 获取控制器实例
16
+ const cloud115Controller = container.get<Cloud115Controller>(TYPES.Cloud115Controller);
17
+ const quarkController = container.get<QuarkController>(TYPES.QuarkController);
18
+ const resourceController = container.get<ResourceController>(TYPES.ResourceController);
19
+ const doubanController = container.get<DoubanController>(TYPES.DoubanController);
20
+ const imageController = container.get<ImageController>(TYPES.ImageController);
21
+ const settingController = container.get<SettingController>(TYPES.SettingController);
22
+ const userController = container.get<UserController>(TYPES.UserController);
23
+ const sponsorsController = container.get<SponsorsController>(TYPES.SponsorsController);
24
+
25
+ // 用户相关路由
26
+ router.post("/user/login", (req, res) => userController.login(req, res));
27
+ router.post("/user/register", (req, res) => userController.register(req, res));
28
+
29
+ // 图片相关路由
30
+ router.get("/tele-images", (req, res) => imageController.getImages(req, res));
31
+
32
+ // 设置相关路由
33
+ router.get("/setting/get", (req, res) => settingController.get(req, res));
34
+ router.post("/setting/save", (req, res) => settingController.save(req, res));
35
+
36
+ // 资源搜索
37
+ router.get("/search", (req, res) => resourceController.search(req, res));
38
+
39
+ // 获取赞助者列表
40
+ router.get("/sponsors", (req, res) => sponsorsController.get(req, res));
41
+
42
+ // 115网盘相关
43
+ router.get("/cloud115/share-info", (req, res) => cloud115Controller.getShareInfo(req, res));
44
+ router.get("/cloud115/folders", (req, res) => cloud115Controller.getFolderList(req, res));
45
+ router.post("/cloud115/save", (req, res) => cloud115Controller.saveFile(req, res));
46
+
47
+ // 夸克网盘相关
48
+ router.get("/quark/share-info", (req, res) => quarkController.getShareInfo(req, res));
49
+ router.get("/quark/folders", (req, res) => quarkController.getFolderList(req, res));
50
+ router.post("/quark/save", (req, res) => quarkController.saveFile(req, res));
51
+
52
+ // 获取豆瓣热门列表
53
+ router.get("/douban/hot", (req, res) => doubanController.getDoubanHotList(req, res));
54
+
55
+ export default router;
backend/src/services/Cloud115Service.ts ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { AxiosHeaders, AxiosInstance } from "axios"; // 导入 AxiosHeaders
2
+ import { createAxiosInstance } from "../utils/axiosInstance";
3
+ import { ShareInfoResponse, FolderListResponse, SaveFileParams } from "../types/cloud";
4
+ import { injectable } from "inversify";
5
+ import { Request } from "express";
6
+ import UserSetting from "../models/UserSetting";
7
+ import { ICloudStorageService } from "@/types/services";
8
+ import { logger } from "../utils/logger";
9
+
10
+ interface Cloud115ListItem {
11
+ cid: string;
12
+ n: string;
13
+ s: number;
14
+ }
15
+
16
+ interface Cloud115FolderItem {
17
+ cid: string;
18
+ n: string;
19
+ ns: number;
20
+ }
21
+
22
+ @injectable()
23
+ export class Cloud115Service implements ICloudStorageService {
24
+ private api: AxiosInstance;
25
+ private cookie: string = "";
26
+
27
+ constructor() {
28
+ this.api = createAxiosInstance(
29
+ "https://webapi.115.com",
30
+ AxiosHeaders.from({
31
+ Host: "webapi.115.com",
32
+ Connection: "keep-alive",
33
+ xweb_xhr: "1",
34
+ Origin: "",
35
+ "Content-Type": "application/x-www-form-urlencoded",
36
+ "User-Agent":
37
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 MicroMessenger/6.8.0(0x16080000) NetType/WIFI MiniProgramEnv/Mac MacWechat/WMPF MacWechat/3.8.9(0x13080910) XWEB/1227",
38
+ Accept: "*/*",
39
+ "Sec-Fetch-Site": "cross-site",
40
+ "Sec-Fetch-Mode": "cors",
41
+ "Sec-Fetch-Dest": "empty",
42
+ Referer: "https://servicewechat.com/wx2c744c010a61b0fa/94/page-frame.html",
43
+ "Accept-Encoding": "gzip, deflate, br",
44
+ "Accept-Language": "zh-CN,zh;q=0.9",
45
+ })
46
+ );
47
+
48
+ this.api.interceptors.request.use((config) => {
49
+ config.headers.cookie = this.cookie;
50
+ return config;
51
+ });
52
+ }
53
+
54
+ async setCookie(req: Request): Promise<void> {
55
+ const userId = req.user?.userId;
56
+ const userSetting = await UserSetting.findOne({
57
+ where: { userId },
58
+ });
59
+ if (userSetting && userSetting.dataValues.cloud115Cookie) {
60
+ this.cookie = userSetting.dataValues.cloud115Cookie;
61
+ } else {
62
+ throw new Error("请先设置115网盘cookie");
63
+ }
64
+ }
65
+
66
+ async getShareInfo(shareCode: string, receiveCode = ""): Promise<ShareInfoResponse> {
67
+ const response = await this.api.get("/share/snap", {
68
+ params: {
69
+ share_code: shareCode,
70
+ receive_code: receiveCode,
71
+ offset: 0,
72
+ limit: 20,
73
+ cid: "",
74
+ },
75
+ });
76
+ if (response.data?.state && response.data.data?.list?.length > 0) {
77
+ return {
78
+ data: {
79
+ list: response.data.data.list.map((item: Cloud115ListItem) => ({
80
+ fileId: item.cid,
81
+ fileName: item.n,
82
+ fileSize: item.s,
83
+ })),
84
+ },
85
+ };
86
+ } else {
87
+ logger.error("未找到文件信息:", response.data);
88
+ throw new Error("未找到文件信息");
89
+ }
90
+ }
91
+
92
+ async getFolderList(parentCid = "0"): Promise<FolderListResponse> {
93
+ const response = await this.api.get("/files", {
94
+ params: {
95
+ aid: 1,
96
+ cid: parentCid,
97
+ o: "user_ptime",
98
+ asc: 1,
99
+ offset: 0,
100
+ show_dir: 1,
101
+ limit: 50,
102
+ type: 0,
103
+ format: "json",
104
+ star: 0,
105
+ suffix: "",
106
+ natsort: 0,
107
+ snap: 0,
108
+ record_open_time: 1,
109
+ fc_mix: 0,
110
+ },
111
+ });
112
+ if (response.data?.state) {
113
+ return {
114
+ data: response.data.data
115
+ .filter((item: Cloud115FolderItem) => item.cid && !!item.ns)
116
+ .map((folder: Cloud115FolderItem) => ({
117
+ cid: folder.cid,
118
+ name: folder.n,
119
+ path: response.data.path,
120
+ })),
121
+ };
122
+ } else {
123
+ logger.error("获取目录列表失败:", response.data.error);
124
+ throw new Error("获取115pan目录列表失败:" + response.data.error);
125
+ }
126
+ }
127
+
128
+ async saveSharedFile(params: SaveFileParams): Promise<{ message: string; data: unknown }> {
129
+ const param = new URLSearchParams({
130
+ cid: params.folderId || "",
131
+ share_code: params.shareCode || "",
132
+ receive_code: params.receiveCode || "",
133
+ file_id: params.fids?.[0] || "",
134
+ });
135
+ const response = await this.api.post("/share/receive", param.toString());
136
+ logger.info("保存文件:", response.data);
137
+ if (response.data.state) {
138
+ return {
139
+ message: response.data.error,
140
+ data: response.data.data,
141
+ };
142
+ } else {
143
+ logger.error("保存文件失败:", response.data.error);
144
+ throw new Error("保存115pan文件失败:" + response.data.error);
145
+ }
146
+ }
147
+ }
backend/src/services/DatabaseService.ts ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Sequelize, QueryTypes } from "sequelize";
2
+ import GlobalSetting from "../models/GlobalSetting";
3
+ import { Searcher } from "./Searcher";
4
+ import sequelize from "../config/database";
5
+
6
+ // 全局设置默认值
7
+ const DEFAULT_GLOBAL_SETTINGS = {
8
+ httpProxyHost: "127.0.0.1",
9
+ httpProxyPort: 7890,
10
+ isProxyEnabled: false,
11
+ CommonUserCode: 9527,
12
+ AdminUserCode: 230713,
13
+ };
14
+
15
+ export class DatabaseService {
16
+ private sequelize: Sequelize;
17
+
18
+ constructor() {
19
+ this.sequelize = sequelize;
20
+ }
21
+
22
+ async initialize(): Promise<void> {
23
+ try {
24
+ await this.sequelize.query("PRAGMA foreign_keys = OFF");
25
+ await this.cleanupBackupTables();
26
+ await this.sequelize.sync({ alter: true });
27
+ await this.sequelize.query("PRAGMA foreign_keys = ON");
28
+ await this.initializeGlobalSettings();
29
+ } catch (error) {
30
+ throw new Error(`数据库初始化失败: ${(error as Error).message}`);
31
+ }
32
+ }
33
+
34
+ private async initializeGlobalSettings(): Promise<void> {
35
+ try {
36
+ const settings = await GlobalSetting.findOne();
37
+ if (!settings) {
38
+ await GlobalSetting.create(DEFAULT_GLOBAL_SETTINGS);
39
+ console.log("✅ Global settings initialized with default values.");
40
+ }
41
+ await Searcher.updateAxiosInstance();
42
+ } catch (error) {
43
+ console.error("❌ Failed to initialize global settings:", error);
44
+ throw error;
45
+ }
46
+ }
47
+
48
+ private async cleanupBackupTables(): Promise<void> {
49
+ const backupTables = await this.sequelize.query<{ name: string }>(
50
+ "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%\\_backup%' ESCAPE '\\'",
51
+ { type: QueryTypes.SELECT }
52
+ );
53
+
54
+ for (const table of backupTables) {
55
+ if (table?.name) {
56
+ await this.sequelize.query(`DROP TABLE IF EXISTS ${table.name}`);
57
+ }
58
+ }
59
+ }
60
+
61
+ // ... 其他数据库相关方法
62
+ }
backend/src/services/DoubanService.ts ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { AxiosHeaders, AxiosInstance } from "axios";
2
+ import { createAxiosInstance } from "../utils/axiosInstance";
3
+
4
+ interface DoubanSubject {
5
+ id: string;
6
+ title: string;
7
+ rate: string;
8
+ cover: string;
9
+ url: string;
10
+ is_new: boolean;
11
+ }
12
+
13
+ export class DoubanService {
14
+ private baseUrl: string;
15
+ private api: AxiosInstance;
16
+
17
+ constructor() {
18
+ this.baseUrl = "https://movie.douban.com/j";
19
+ this.api = createAxiosInstance(
20
+ this.baseUrl,
21
+ AxiosHeaders.from({
22
+ accept: "*/*",
23
+ "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
24
+ priority: "u=1, i",
25
+ "sec-ch-ua": '"Not A(Brand";v="8", "Chromium";v="132", "Microsoft Edge";v="132"',
26
+ "sec-ch-ua-mobile": "?0",
27
+ "sec-ch-ua-platform": '"Windows"',
28
+ "sec-fetch-dest": "empty",
29
+ "sec-fetch-mode": "cors",
30
+ "sec-fetch-site": "same-origin",
31
+ "x-requested-with": "XMLHttpRequest",
32
+ cookie:
33
+ 'll="118282"; bid=StA6AQFsAWQ; _pk_id.100001.4cf6=6448be57b1b5ca7e.1723172321.; _vwo_uuid_v2=DC15B8183560FF1E538FFE1D480723310|c08e2d213ecb5510005f90a6ff332121; __utmv=30149280.6282; _vwo_uuid_v2=DC15B8183560FF1E538FFE1D480723310|c08e2d213ecb5510005f90a6ff332121; __utmz=30149280.1731915179.21.6.utmcsr=search.douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/movie/subject_search; __utmz=223695111.1731915179.21.6.utmcsr=search.douban.com|utmccn=(referral)|utmcmd=referral|utmcct=/movie/subject_search; douban-fav-remind=1; __utmc=30149280; __utmc=223695111; _pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1739176523%2C%22https%3A%2F%2Fsearch.douban.com%2Fmovie%2Fsubject_search%3Fsearch_text%3D%E8%84%91%E6%B4%9E%E5%A4%A7%E5%BC%80%26cat%3D1002%22%5D; _pk_ses.100001.4cf6=1; ap_v=0,6.0; __utma=30149280.859303574.1723448979.1739167503.1739176523.42; __utmb=30149280.0.10.1739176523; __utma=223695111.1882744177.1723448979.1739167503.1739176523.42; __utmb=223695111.0.10.1739176523',
34
+ Referer: "https://movie.douban.com/",
35
+ "Referrer-Policy": "unsafe-url",
36
+ })
37
+ );
38
+ }
39
+
40
+ async getHotList(params: {
41
+ type: string;
42
+ tag: string;
43
+ page_limit: string;
44
+ page_start: string;
45
+ }): Promise<{ data: DoubanSubject[] }> {
46
+ try {
47
+ const response = await this.api.get("/search_subjects", {
48
+ params: params,
49
+ });
50
+ if (response.data && response.data.subjects) {
51
+ return {
52
+ data: response.data.subjects,
53
+ };
54
+ } else {
55
+ return {
56
+ data: [],
57
+ };
58
+ }
59
+ } catch (error) {
60
+ console.error("Error fetching hot list:", error);
61
+ throw error;
62
+ }
63
+ }
64
+ }
backend/src/services/ImageService.ts ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { injectable } from "inversify";
2
+ import axios, { AxiosInstance } from "axios";
3
+ import tunnel from "tunnel";
4
+ import GlobalSetting from "../models/GlobalSetting";
5
+ import { GlobalSettingAttributes } from "../models/GlobalSetting";
6
+
7
+ @injectable()
8
+ export class ImageService {
9
+ private axiosInstance: AxiosInstance | null = null;
10
+
11
+ constructor() {
12
+ // 移除构造函数中的初始化,改为懒加载
13
+ }
14
+
15
+ private async ensureAxiosInstance(): Promise<AxiosInstance> {
16
+ if (!this.axiosInstance) {
17
+ const settings = await GlobalSetting.findOne();
18
+ const globalSetting = settings?.dataValues || ({} as GlobalSettingAttributes);
19
+
20
+ this.axiosInstance = axios.create({
21
+ timeout: 30000,
22
+ headers: {
23
+ Accept: "image/*, */*",
24
+ "User-Agent":
25
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
26
+ },
27
+ withCredentials: false,
28
+ maxRedirects: 5,
29
+ httpsAgent: globalSetting.isProxyEnabled
30
+ ? tunnel.httpsOverHttp({
31
+ proxy: {
32
+ host: globalSetting.httpProxyHost,
33
+ port: globalSetting.httpProxyPort,
34
+ headers: {
35
+ "Proxy-Authorization": "",
36
+ },
37
+ },
38
+ })
39
+ : undefined,
40
+ });
41
+
42
+ this.axiosInstance.interceptors.response.use(
43
+ (response) => response,
44
+ (error) => {
45
+ throw error;
46
+ }
47
+ );
48
+ }
49
+ return this.axiosInstance;
50
+ }
51
+
52
+ async updateAxiosInstance(): Promise<void> {
53
+ this.axiosInstance = null;
54
+ await this.ensureAxiosInstance();
55
+ }
56
+
57
+ async getImages(url: string): Promise<any> {
58
+ const axiosInstance = await this.ensureAxiosInstance();
59
+
60
+ return await axiosInstance.get(url, {
61
+ responseType: "stream",
62
+ validateStatus: (status) => status >= 200 && status < 300,
63
+ headers: {
64
+ Referer: new URL(url).origin,
65
+ },
66
+ });
67
+ }
68
+ }
backend/src/services/QuarkService.ts ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { AxiosInstance, AxiosHeaders } from "axios";
2
+ import { logger } from "../utils/logger";
3
+ import { createAxiosInstance } from "../utils/axiosInstance";
4
+ import { injectable } from "inversify";
5
+ import { Request } from "express";
6
+ import UserSetting from "../models/UserSetting";
7
+ import {
8
+ ShareInfoResponse,
9
+ FolderListResponse,
10
+ QuarkFolderItem,
11
+ SaveFileParams,
12
+ } from "../types/cloud";
13
+ import { ICloudStorageService } from "@/types/services";
14
+
15
+ interface QuarkShareInfo {
16
+ stoken?: string;
17
+ pwdId?: string;
18
+ fileSize?: number;
19
+ list: {
20
+ fid: string;
21
+ file_name: string;
22
+ file_type: number;
23
+ share_fid_token: string;
24
+ }[];
25
+ }
26
+
27
+ @injectable()
28
+ export class QuarkService implements ICloudStorageService {
29
+ private api: AxiosInstance;
30
+ private cookie: string = "";
31
+
32
+ constructor() {
33
+ this.api = createAxiosInstance(
34
+ "https://drive-h.quark.cn",
35
+ AxiosHeaders.from({
36
+ cookie: this.cookie,
37
+ accept: "application/json, text/plain, */*",
38
+ "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
39
+ "content-type": "application/json",
40
+ priority: "u=1, i",
41
+ "sec-ch-ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
42
+ "sec-ch-ua-mobile": "?0",
43
+ "sec-ch-ua-platform": '"Windows"',
44
+ "sec-fetch-dest": "empty",
45
+ "sec-fetch-mode": "cors",
46
+ "sec-fetch-site": "same-site",
47
+ })
48
+ );
49
+
50
+ this.api.interceptors.request.use((config) => {
51
+ config.headers.cookie = this.cookie;
52
+ return config;
53
+ });
54
+ }
55
+
56
+ async setCookie(req: Request): Promise<void> {
57
+ const userId = req.user?.userId;
58
+ const userSetting = await UserSetting.findOne({
59
+ where: { userId },
60
+ });
61
+ if (userSetting && userSetting.dataValues.quarkCookie) {
62
+ this.cookie = userSetting.dataValues.quarkCookie;
63
+ } else {
64
+ throw new Error("请先设置夸克网盘cookie");
65
+ }
66
+ }
67
+
68
+ async getShareInfo(pwdId: string, passcode = ""): Promise<ShareInfoResponse> {
69
+ const response = await this.api.post(
70
+ `/1/clouddrive/share/sharepage/token?pr=ucpro&fr=pc&uc_param_str=&__dt=994&__t=${Date.now()}`,
71
+ {
72
+ pwd_id: pwdId,
73
+ passcode,
74
+ }
75
+ );
76
+ if (response.data?.status === 200 && response.data.data) {
77
+ const fileInfo = response.data.data;
78
+ if (fileInfo.stoken) {
79
+ const res = await this.getShareList(pwdId, fileInfo.stoken);
80
+ return {
81
+ data: res,
82
+ };
83
+ }
84
+ }
85
+ throw new Error("获取夸克分享信息失败");
86
+ }
87
+
88
+ async getShareList(pwdId: string, stoken: string): Promise<ShareInfoResponse["data"]> {
89
+ const response = await this.api.get("/1/clouddrive/share/sharepage/detail", {
90
+ params: {
91
+ pr: "ucpro",
92
+ fr: "pc",
93
+ uc_param_str: "",
94
+ pwd_id: pwdId,
95
+ stoken: stoken,
96
+ pdir_fid: "0",
97
+ force: "0",
98
+ _page: "1",
99
+ _size: "50",
100
+ _fetch_banner: "1",
101
+ _fetch_share: "1",
102
+ _fetch_total: "1",
103
+ _sort: "file_type:asc,updated_at:desc",
104
+ __dt: "1589",
105
+ __t: Date.now(),
106
+ },
107
+ });
108
+ if (response.data?.data) {
109
+ const list = response.data.data.list
110
+ .filter((item: QuarkShareInfo["list"][0]) => item.fid)
111
+ .map((folder: QuarkShareInfo["list"][0]) => ({
112
+ fileId: folder.fid,
113
+ fileName: folder.file_name,
114
+ fileIdToken: folder.share_fid_token,
115
+ }));
116
+ return {
117
+ list,
118
+ pwdId,
119
+ stoken,
120
+ fileSize: response.data.data.share?.size || 0,
121
+ };
122
+ } else {
123
+ return {
124
+ list: [],
125
+ };
126
+ }
127
+ }
128
+
129
+ async getFolderList(parentCid = "0"): Promise<FolderListResponse> {
130
+ const response = await this.api.get("/1/clouddrive/file/sort", {
131
+ params: {
132
+ pr: "ucpro",
133
+ fr: "pc",
134
+ uc_param_str: "",
135
+ pdir_fid: parentCid,
136
+ _page: "1",
137
+ _size: "100",
138
+ _fetch_total: "false",
139
+ _fetch_sub_dirs: "1",
140
+ _sort: "",
141
+ __dt: "2093126",
142
+ __t: Date.now(),
143
+ },
144
+ });
145
+ if (response.data?.data && response.data.data.list) {
146
+ const data = response.data.data.list
147
+ .filter((item: QuarkFolderItem) => item.fid && item.file_type === 0)
148
+ .map((folder: QuarkFolderItem) => ({
149
+ cid: folder.fid,
150
+ name: folder.file_name,
151
+ path: [],
152
+ }));
153
+ return {
154
+ data,
155
+ };
156
+ } else {
157
+ const message = "获取夸克目录列表失败:" + response.data.error;
158
+ logger.error(message);
159
+ throw new Error(message);
160
+ }
161
+ }
162
+
163
+ async saveSharedFile(params: SaveFileParams): Promise<{ message: string; data: unknown }> {
164
+ const quarkParams = {
165
+ fid_list: params.fids,
166
+ fid_token_list: params.fidTokens,
167
+ to_pdir_fid: params.folderId,
168
+ pwd_id: params.shareCode,
169
+ stoken: params.receiveCode,
170
+ pdir_fid: "0",
171
+ scene: "link",
172
+ };
173
+ try {
174
+ const response = await this.api.post(
175
+ `/1/clouddrive/share/sharepage/save?pr=ucpro&fr=pc&uc_param_str=&__dt=208097&__t=${Date.now()}`,
176
+ quarkParams
177
+ );
178
+
179
+ return {
180
+ message: response.data.message,
181
+ data: response.data.data,
182
+ };
183
+ } catch (error) {
184
+ throw new Error(error instanceof Error ? error.message : "未知错误");
185
+ }
186
+ }
187
+ }
backend/src/services/Searcher.ts ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { AxiosInstance, AxiosHeaders } from "axios";
2
+ import { createAxiosInstance } from "../utils/axiosInstance";
3
+ import GlobalSetting from "../models/GlobalSetting";
4
+ import { GlobalSettingAttributes } from "../models/GlobalSetting";
5
+ import * as cheerio from "cheerio";
6
+ import { config } from "../config";
7
+ import { logger } from "../utils/logger";
8
+ import { injectable } from "inversify";
9
+
10
+ interface sourceItem {
11
+ messageId?: string;
12
+ title?: string;
13
+ completeTitle?: string;
14
+ link?: string;
15
+ pubDate?: string;
16
+ content?: string;
17
+ description?: string;
18
+ image?: string;
19
+ cloudLinks?: string[];
20
+ tags?: string[];
21
+ cloudType?: string;
22
+ }
23
+
24
+ @injectable()
25
+ export class Searcher {
26
+ private static instance: Searcher;
27
+ private api: AxiosInstance | null = null;
28
+
29
+ constructor() {
30
+ this.initAxiosInstance();
31
+ Searcher.instance = this;
32
+ }
33
+
34
+ private async initAxiosInstance(isUpdate: boolean = false) {
35
+ let globalSetting = {} as GlobalSettingAttributes;
36
+ if (isUpdate) {
37
+ const settings = await GlobalSetting.findOne();
38
+ globalSetting = settings?.dataValues || ({} as GlobalSettingAttributes);
39
+ }
40
+ this.api = createAxiosInstance(
41
+ config.telegram.baseUrl,
42
+ AxiosHeaders.from({
43
+ accept:
44
+ "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
45
+ "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
46
+ "cache-control": "max-age=0",
47
+ priority: "u=0, i",
48
+ "sec-ch-ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
49
+ "sec-ch-ua-mobile": "?0",
50
+ "sec-ch-ua-platform": '"macOS"',
51
+ "sec-fetch-dest": "document",
52
+ "sec-fetch-mode": "navigate",
53
+ "sec-fetch-site": "none",
54
+ "sec-fetch-user": "?1",
55
+ "upgrade-insecure-requests": "1",
56
+ }),
57
+ globalSetting?.isProxyEnabled,
58
+ globalSetting?.isProxyEnabled
59
+ ? { host: globalSetting?.httpProxyHost, port: globalSetting?.httpProxyPort }
60
+ : undefined
61
+ );
62
+ }
63
+
64
+ public static async updateAxiosInstance(): Promise<void> {
65
+ await Searcher.instance.initAxiosInstance(true);
66
+ }
67
+
68
+ private extractCloudLinks(text: string): { links: string[]; cloudType: string } {
69
+ const links: string[] = [];
70
+ let cloudType = "";
71
+ Object.values(config.cloudPatterns).forEach((pattern, index) => {
72
+ const matches = text.match(pattern);
73
+ if (matches) {
74
+ links.push(...matches);
75
+ if (!cloudType) cloudType = Object.keys(config.cloudPatterns)[index];
76
+ }
77
+ });
78
+ return {
79
+ links: [...new Set(links)],
80
+ cloudType,
81
+ };
82
+ }
83
+
84
+ async searchAll(keyword: string, channelId?: string, messageId?: string) {
85
+ const allResults: any[] = [];
86
+
87
+ const channelList: any[] = channelId
88
+ ? config.telegram.channels.filter((channel: any) => channel.id === channelId)
89
+ : config.telegram.channels;
90
+
91
+ // 使用Promise.all进行并行请求
92
+ const searchPromises = channelList.map(async (channel) => {
93
+ try {
94
+ const messageIdparams = messageId ? `before=${messageId}` : "";
95
+ const url = `/${channel.id}${keyword ? `?q=${encodeURIComponent(keyword)}&${messageIdparams}` : `?${messageIdparams}`}`;
96
+ console.log(`Searching in channel ${channel.name} with URL: ${url}`);
97
+ return this.searchInWeb(url).then((results) => {
98
+ console.log(`Found ${results.items.length} items in channel ${channel.name}`);
99
+ if (results.items.length > 0) {
100
+ const channelResults = results.items
101
+ .filter((item: sourceItem) => item.cloudLinks && item.cloudLinks.length > 0)
102
+ .map((item: sourceItem) => ({
103
+ ...item,
104
+ channel: channel.name,
105
+ channelId: channel.id,
106
+ }));
107
+
108
+ allResults.push({
109
+ list: channelResults,
110
+ channelInfo: {
111
+ ...channel,
112
+ channelLogo: results.channelLogo,
113
+ },
114
+ id: channel.id,
115
+ });
116
+ }
117
+ });
118
+ } catch (error) {
119
+ logger.error(`搜索频道 ${channel.name} 失败:`, error);
120
+ }
121
+ });
122
+
123
+ // 等待所有请求完成
124
+ await Promise.all(searchPromises);
125
+
126
+ return {
127
+ data: allResults,
128
+ };
129
+ }
130
+
131
+ async searchInWeb(url: string) {
132
+ try {
133
+ if (!this.api) {
134
+ throw new Error("Axios instance is not initialized");
135
+ }
136
+ const response = await this.api.get(url);
137
+ const html = response.data;
138
+ const $ = cheerio.load(html);
139
+ const items: sourceItem[] = [];
140
+ let channelLogo = "";
141
+ $(".tgme_header_link").each((_, element) => {
142
+ channelLogo = $(element).find("img").attr("src") || "";
143
+ });
144
+ // 遍历每个消息容器
145
+ $(".tgme_widget_message_wrap").each((_, element) => {
146
+ const messageEl = $(element);
147
+
148
+ // 通过 data-post 属性来获取消息的链接 去除channelId 获得消息id
149
+ const messageId = messageEl
150
+ .find(".tgme_widget_message")
151
+ .data("post")
152
+ ?.toString()
153
+ .split("/")[1];
154
+
155
+ // 提取标题 (第一个<br>标签前的内容)
156
+ const title =
157
+ messageEl
158
+ .find(".js-message_text")
159
+ .html()
160
+ ?.split("<br>")[0]
161
+ .replace(/<[^>]+>/g, "")
162
+ .replace(/\n/g, "") || "";
163
+
164
+ // 提取描述 (第一个<a>标签前面的内容,不包含标题)
165
+ const content =
166
+ messageEl
167
+ .find(".js-message_text")
168
+ .html()
169
+ ?.replace(title, "")
170
+ .split("<a")[0]
171
+ .replace(/<br>/g, "")
172
+ .trim() || "";
173
+
174
+ // 提取链接 (消息中的链接)
175
+ // const link = messageEl.find('.tgme_widget_message').data('post');
176
+
177
+ // 提取发布时间
178
+ const pubDate = messageEl.find("time").attr("datetime");
179
+
180
+ // 提取图片
181
+ const image = messageEl
182
+ .find(".tgme_widget_message_photo_wrap")
183
+ .attr("style")
184
+ ?.match(/url\('(.+?)'\)/)?.[1];
185
+
186
+ const tags: string[] = [];
187
+ // 提取云盘链接
188
+ const links = messageEl
189
+ .find(".tgme_widget_message_text a")
190
+ .map((_, el) => $(el).attr("href"))
191
+ .get();
192
+ messageEl.find(".tgme_widget_message_text a").each((index, element) => {
193
+ const tagText = $(element).text();
194
+ if (tagText && tagText.startsWith("#")) {
195
+ tags.push(tagText);
196
+ }
197
+ });
198
+ const cloudInfo = this.extractCloudLinks(links.join(" "));
199
+ // 添加到数组第一位
200
+ items.unshift({
201
+ messageId,
202
+ title,
203
+ pubDate,
204
+ content,
205
+ image,
206
+ cloudLinks: cloudInfo.links,
207
+ cloudType: cloudInfo.cloudType,
208
+ tags,
209
+ });
210
+ });
211
+ return { items: items, channelLogo };
212
+ } catch (error) {
213
+ logger.error(`搜索错误: ${url}`, error);
214
+ return {
215
+ items: [],
216
+ channelLogo: "",
217
+ };
218
+ }
219
+ }
220
+ }
221
+
222
+ export default new Searcher();
backend/src/services/SettingService.ts ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { injectable, inject } from "inversify";
2
+ import { TYPES } from "../core/types";
3
+ import UserSetting from "../models/UserSetting";
4
+ import GlobalSetting from "../models/GlobalSetting";
5
+ import { Searcher } from "./Searcher";
6
+ import { ImageService } from "./ImageService";
7
+
8
+ @injectable()
9
+ export class SettingService {
10
+ constructor(@inject(TYPES.ImageService) private imageService: ImageService) {}
11
+
12
+ async getSettings(userId: string | undefined, role: number | undefined) {
13
+ if (!userId) {
14
+ throw new Error("用户ID无效");
15
+ }
16
+
17
+ let userSettings = await UserSetting.findOne({ where: { userId: userId.toString() } });
18
+ if (!userSettings) {
19
+ userSettings = await UserSetting.create({
20
+ userId: userId.toString(),
21
+ cloud115Cookie: "",
22
+ quarkCookie: "",
23
+ });
24
+ }
25
+
26
+ const globalSetting = await GlobalSetting.findOne();
27
+ return {
28
+ data: {
29
+ userSettings,
30
+ globalSetting: role === 1 ? globalSetting : null,
31
+ },
32
+ };
33
+ }
34
+
35
+ async saveSettings(userId: string | undefined, role: number | undefined, settings: any) {
36
+ if (!userId) {
37
+ throw new Error("用户ID无效");
38
+ }
39
+
40
+ const { userSettings, globalSetting } = settings;
41
+ await UserSetting.update(userSettings, { where: { userId: userId.toString() } });
42
+
43
+ if (role === 1 && globalSetting) {
44
+ await GlobalSetting.update(globalSetting, { where: {} });
45
+ }
46
+ await this.updateSettings();
47
+ return { message: "保存成功" };
48
+ }
49
+
50
+ async updateSettings(/* 参数 */): Promise<void> {
51
+ // ... 其他代码 ...
52
+
53
+ // 修改这一行,使用注入的实例方法而不是静态方法
54
+ await this.imageService.updateAxiosInstance();
55
+ await Searcher.updateAxiosInstance();
56
+
57
+ // ... 其他代码 ...
58
+ }
59
+ }
backend/src/services/SponsorsService.ts ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { injectable } from "inversify";
2
+ import { createAxiosInstance } from "../utils/axiosInstance";
3
+ import { AxiosInstance } from "axios";
4
+ import sponsors from "../sponsors/sponsors.json";
5
+
6
+ @injectable()
7
+ export class SponsorsService {
8
+ private axiosInstance: AxiosInstance;
9
+
10
+ constructor() {
11
+ this.axiosInstance = createAxiosInstance("http://oss.jiangmuxin.cn/cloudsaver/");
12
+ }
13
+ async getSponsors() {
14
+ try {
15
+ const response = await this.axiosInstance.get("sponsors.json");
16
+ return {
17
+ data: response.data.sponsors,
18
+ };
19
+ } catch (error) {
20
+ return {
21
+ data: sponsors.sponsors,
22
+ };
23
+ }
24
+ }
25
+ }
backend/src/services/UserService.ts ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { injectable } from "inversify";
2
+ import bcrypt from "bcrypt";
3
+ import jwt from "jsonwebtoken";
4
+ import { config } from "../config";
5
+ import User from "../models/User";
6
+ import GlobalSetting from "../models/GlobalSetting";
7
+
8
+ @injectable()
9
+ export class UserService {
10
+ private isValidInput(input: string): boolean {
11
+ // 检查是否包含空格或汉字
12
+ const regex = /^[^\s\u4e00-\u9fa5]+$/;
13
+ return regex.test(input);
14
+ }
15
+
16
+ async register(username: string, password: string, registerCode: string) {
17
+ const globalSetting = await GlobalSetting.findOne();
18
+ const registerCodeList = [
19
+ globalSetting?.dataValues.CommonUserCode,
20
+ globalSetting?.dataValues.AdminUserCode,
21
+ ];
22
+ if (!registerCode || !registerCodeList.includes(Number(registerCode))) {
23
+ throw new Error("注册码错误");
24
+ }
25
+
26
+ // 验证输入
27
+ if (!this.isValidInput(username) || !this.isValidInput(password)) {
28
+ throw new Error("用户名、密码或注册码不能包含空格或汉字");
29
+ }
30
+
31
+ // 检查用户名是否已存在
32
+ const existingUser = await User.findOne({ where: { username } });
33
+ if (existingUser) {
34
+ throw new Error("用户名已存在");
35
+ }
36
+
37
+ const hashedPassword = await bcrypt.hash(password, 10);
38
+ const role = registerCodeList.findIndex((x) => x === Number(registerCode));
39
+ const user = await User.create({ username, password: hashedPassword, role });
40
+
41
+ return {
42
+ data: user,
43
+ message: "用户注册成功",
44
+ };
45
+ }
46
+
47
+ async login(username: string, password: string) {
48
+ const user = await User.findOne({ where: { username } });
49
+ if (!user || !(await bcrypt.compare(password, user.password))) {
50
+ throw new Error("用户名或密码错误");
51
+ }
52
+
53
+ const token = jwt.sign({ userId: user.userId, role: user.role }, config.jwtSecret, {
54
+ expiresIn: "6h",
55
+ });
56
+
57
+ return {
58
+ data: {
59
+ token,
60
+ },
61
+ };
62
+ }
63
+ }
backend/src/sponsors/sponsors.json ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "sponsors": [
3
+ {
4
+ "name": "立本狗头",
5
+ "avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks1.jpg",
6
+ "message": "怒搓楼上狗头! "
7
+ },
8
+ {
9
+ "name": "帝国鼻屎",
10
+ "avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks2.jpg",
11
+ "message": "芜湖起飞! "
12
+ },
13
+ {
14
+ "name": "雷霆222",
15
+ "avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks3.jpg",
16
+ "message": "把我弄帅点 "
17
+ },
18
+ {
19
+ "name": "黑田奈奈子",
20
+ "avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks4.jpg",
21
+ "message": "流年笑掷 未来可期 ",
22
+ "link": "https://github.com/htnanako"
23
+ },
24
+ {
25
+ "name": "原野🐇",
26
+ "avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks5.jpg"
27
+ },
28
+ {
29
+ "name": "我摆烂!",
30
+ "avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks6.jpg",
31
+ "message": "人生苦短,及时行乐,卷什么卷,随缘摆烂 "
32
+ },
33
+ {
34
+ "name": "田培",
35
+ "avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks7.jpg"
36
+ },
37
+ {
38
+ "name": "River",
39
+ "avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks8.jpg"
40
+ },
41
+ {
42
+ "name": "午夜学徒",
43
+ "avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks9.jpg"
44
+ },
45
+ {
46
+ "name": "阿潘",
47
+ "avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks10.jpg"
48
+ },
49
+ {
50
+ "name": "闹闹黑",
51
+ "avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks11.jpg"
52
+ }
53
+ ]
54
+ }
backend/src/types/cloud.ts ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface ShareInfoResponse {
2
+ data: {
3
+ list: ShareInfoItem[];
4
+ fileSize?: number;
5
+ pwdId?: string;
6
+ stoken?: string;
7
+ };
8
+ }
9
+
10
+ export interface GetShareInfoParams {
11
+ shareCode: string;
12
+ receiveCode?: string;
13
+ }
14
+
15
+ export interface ShareInfoItem {
16
+ fileId: string;
17
+ fileName: string;
18
+ fileSize?: number;
19
+ fileIdToken?: string;
20
+ }
21
+ export interface FolderListResponse {
22
+ data: {
23
+ cid: string;
24
+ name: string;
25
+ path: { cid: string; name: string }[];
26
+ }[];
27
+ }
28
+
29
+ export interface SaveFileParams {
30
+ shareCode: string; // 分享code
31
+ receiveCode?: string; // 分享文件的密码
32
+ folderId?: string; // 文件夹id
33
+ fids?: string[]; // 存储文件id
34
+ fidTokens?: string[]; // 存储文件token
35
+ }
36
+
37
+ export interface SaveFileResponse {
38
+ message: string;
39
+ data: unknown;
40
+ }
41
+
42
+ export interface ShareFileInfo {
43
+ shareCode: string;
44
+ receiveCode?: string;
45
+ fileId: string;
46
+ cid?: string;
47
+ fid_list?: string[];
48
+ fid_token_list?: string[];
49
+ to_pdir_fid?: string;
50
+ pwd_id?: string;
51
+ stoken?: string;
52
+ pdir_fid?: string;
53
+ scene?: string;
54
+ [key: string]: any;
55
+ }
56
+
57
+ export interface QuarkShareFileInfo {
58
+ fid_list: string[];
59
+ fid_token_list: string[];
60
+ to_pdir_fid: string;
61
+ pwd_id: string;
62
+ stoken: string;
63
+ pdir_fid: string;
64
+ scene: string;
65
+ }
66
+
67
+ export interface QuarkShareInfo {
68
+ stoken?: string;
69
+ pwdId?: string;
70
+ fileSize?: number;
71
+ list: {
72
+ fid: string;
73
+ file_name: string;
74
+ file_type: number;
75
+ share_fid_token: string;
76
+ }[];
77
+ }
78
+
79
+ export interface QuarkFolderItem {
80
+ fid: string;
81
+ file_name: string;
82
+ file_type: number;
83
+ }
backend/src/types/cloud115.ts ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface ShareInfo {
2
+ fileId: string;
3
+ fileName: string;
4
+ fileSize: number;
5
+ }
6
+
7
+ export interface ShareInfoResponse {
8
+ data?: ShareInfo[];
9
+ message?: string;
10
+ }