Pārlūkot izejas kodu

添加文件上传,读取,本机启动配置文件与指令

lrf 10 mēneši atpakaļ
vecāks
revīzija
0dfd852b8d

+ 1 - 0
.gitignore

@@ -11,3 +11,4 @@ run/
 *.un~
 .tsbuildinfo
 .tsbuildinfo.*
+upload/

+ 3 - 2
.prettierrc.js

@@ -1,3 +1,4 @@
 module.exports = {
-  ...require('mwts/.prettierrc.json')
-}
+  ...require('mwts/.prettierrc.json'),
+  printWidth: 250,
+};

+ 8 - 4
README.md

@@ -4,15 +4,19 @@
   * [X]	2.单点登录迁移至代理
   * [x]	3.接口鉴权
   * [x] 4.前端路由处理: 未加载到路由,但是跳转了,所以又重刷新页面了
-  * [ ] 5.重新整理异常:
-  * [ ] 6.需要将条件转换为sql,sql转换器!,先试试通过pg的方式连接,不行再说,有nodejs驱动,不行就全都自己写
+  * [x] 5.重新整理异常:
+  * [x] 6.需要将条件转换为sql,sql转换器!,先试试通过pg的方式连接,不行再说,有nodejs驱动,不行就全都自己写
+  * [x] 7.上传于读取基础接口,之后再判断是否有静态文件访问权限的问题
+  * [ ] 8.数据库监听
+  * [ ] 9.敏感词过滤
+  * [ ] 10.文本内容屏蔽
+  * [ ] 11.用户行为采集
   * [x] x.对数据进行解密
   * [x] x2.服务接口只接收本地请求
 
 ## 置后处理:
   * [ ] 1.检查白名单
-  * [ ] 2.配置文件外置共用
-  * [ ] 3.数据分级(需要与业务绑定)
+  * [ ] 2.数据分级(需要与业务绑定)
 
 # 异常使用
 ## 1.只设ServiceError在每个项目中,并将自己项目使用的Code做成enum存放在各自项目中使用.

+ 20 - 0
ecosystem.config.js

@@ -0,0 +1,20 @@
+'use strict';
+// 开发服务设置
+const app = 'proxy';
+module.exports = {
+  apps: [
+    {
+      name: app, // 应用名称
+      script: './bootstrap.js', // 实际启动脚本
+      out: `./logs/${app}.log`,
+      error: `./logs/${app}.err`,
+      watch: [
+        // 监控变化的目录,一旦变化,自动重启
+        'dist',
+      ],
+      env: {
+        NODE_ENV: 'production', // 环境参数,当前指定为生产环境
+      },
+    },
+  ],
+};

+ 9 - 0
nginx.conf

@@ -0,0 +1,9 @@
+# 接口代理
+location /cxyy/api {
+  proxy_pass http://127.0.0.1:19700;
+}
+# 静态文件上传与读取
+location /files/ {
+  rewrite ^/files/(.*)$ /$1 break;
+  proxy_pass http://127.0.0.1:19700;
+}

+ 303 - 18
package-lock.json

@@ -18,11 +18,13 @@
         "@midwayjs/koa": "^3.12.0",
         "@midwayjs/logger": "^3.1.0",
         "@midwayjs/redis": "^3.16.0",
+        "@midwayjs/upload": "^3.16.2",
         "@midwayjs/validate": "^3.12.0",
         "@types/crypto-js": "^4.2.2",
         "@types/node-rsa": "^1.1.4",
         "crypto-js": "^4.2.0",
         "lodash": "^4.17.21",
+        "mime-types": "^2.1.35",
         "node-rsa": "^1.1.1"
       },
       "devDependencies": {
@@ -31,6 +33,7 @@
         "@types/lodash": "^4.17.1",
         "@types/node": "14",
         "cross-env": "^6.0.0",
+        "fs-extra": "^11.1.1",
         "jest": "^29.2.2",
         "mwts": "^1.3.0",
         "mwtsc": "^1.4.0",
@@ -1301,6 +1304,18 @@
         "node": ">=12"
       }
     },
+    "node_modules/@midwayjs/upload": {
+      "version": "3.16.2",
+      "resolved": "https://registry.npmmirror.com/@midwayjs/upload/-/upload-3.16.2.tgz",
+      "integrity": "sha512-LVA06rmp8RkUwsT4c7Pi2vgDOXO9O2JVnlY4BZcrvHblcIhU/RcL/uWxavU99Ih1pdOGL9Oj0FlTtcospeOE1g==",
+      "dependencies": {
+        "file-type": "16.5.4",
+        "raw-body": "2.5.2"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/@midwayjs/validate": {
       "version": "3.16.1",
       "resolved": "https://registry.npmmirror.com/@midwayjs/validate/-/validate-3.16.1.tgz",
@@ -1411,6 +1426,11 @@
         "node": ">=6"
       }
     },
+    "node_modules/@tokenizer/token": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/@tokenizer/token/-/token-0.3.0.tgz",
+      "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
+    },
     "node_modules/@types/accepts": {
       "version": "1.3.7",
       "resolved": "https://registry.npmmirror.com/@types/accepts/-/accepts-1.3.7.tgz",
@@ -3671,6 +3691,22 @@
         "node": "^10.12.0 || >=12.0.0"
       }
     },
+    "node_modules/file-type": {
+      "version": "16.5.4",
+      "resolved": "https://registry.npmmirror.com/file-type/-/file-type-16.5.4.tgz",
+      "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
+      "dependencies": {
+        "readable-web-to-node-stream": "^3.0.0",
+        "strtok3": "^6.2.4",
+        "token-types": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/file-type?sponsor=1"
+      }
+    },
     "node_modules/fill-range": {
       "version": "7.0.1",
       "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
@@ -3762,6 +3798,20 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/fs-extra": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.2.0.tgz",
+      "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+      "dev": true,
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
     "node_modules/fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -4167,6 +4217,25 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
     "node_modules/ignore": {
       "version": "5.3.1",
       "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.1.tgz",
@@ -5213,6 +5282,18 @@
         "node": ">=6"
       }
     },
+    "node_modules/jsonfile": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
+      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+      "dev": true,
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
     "node_modules/jsonwebtoken": {
       "version": "9.0.2",
       "resolved": "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
@@ -5658,18 +5739,6 @@
         "node": ">=8.6"
       }
     },
-    "node_modules/mime": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz",
-      "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
-      "dev": true,
-      "bin": {
-        "mime": "cli.js"
-      },
-      "engines": {
-        "node": ">=4.0.0"
-      }
-    },
     "node_modules/mime-db": {
       "version": "1.52.0",
       "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
@@ -6166,6 +6235,18 @@
         "node": ">=8"
       }
     },
+    "node_modules/peek-readable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/peek-readable/-/peek-readable-4.1.0.tgz",
+      "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz",
@@ -6501,6 +6582,34 @@
         "node": ">=8"
       }
     },
+    "node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/readable-web-to-node-stream": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
+      "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
+      "dependencies": {
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/readdirp": {
       "version": "3.6.0",
       "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
@@ -6950,6 +7059,14 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
     "node_modules/string-length": {
       "version": "4.0.2",
       "resolved": "https://registry.npmmirror.com/string-length/-/string-length-4.0.2.tgz",
@@ -7028,6 +7145,22 @@
         "node": ">=8"
       }
     },
+    "node_modules/strtok3": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmmirror.com/strtok3/-/strtok3-6.3.0.tgz",
+      "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
+      "dependencies": {
+        "@tokenizer/token": "^0.3.0",
+        "peek-readable": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/superagent": {
       "version": "8.1.2",
       "resolved": "https://registry.npmmirror.com/superagent/-/superagent-8.1.2.tgz",
@@ -7050,6 +7183,18 @@
         "node": ">=6.4.0 <13 || >=14"
       }
     },
+    "node_modules/superagent/node_modules/mime": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz",
+      "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+      "dev": true,
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
     "node_modules/superagent/node_modules/semver": {
       "version": "7.6.2",
       "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.2.tgz",
@@ -7212,6 +7357,22 @@
         "node": ">=0.6"
       }
     },
+    "node_modules/token-types": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/token-types/-/token-types-4.2.1.tgz",
+      "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
+      "dependencies": {
+        "@tokenizer/token": "^0.3.0",
+        "ieee754": "^1.2.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
     "node_modules/trim-newlines": {
       "version": "3.0.1",
       "resolved": "https://registry.npmmirror.com/trim-newlines/-/trim-newlines-3.0.1.tgz",
@@ -7407,6 +7568,15 @@
         "node": ">=8"
       }
     },
+    "node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
     "node_modules/unpipe": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz",
@@ -7489,6 +7659,11 @@
         "node": ">=4"
       }
     },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+    },
     "node_modules/v8-compile-cache": {
       "version": "2.4.0",
       "resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz",
@@ -8661,6 +8836,15 @@
         "@midwayjs/cookies": "^1.0.2"
       }
     },
+    "@midwayjs/upload": {
+      "version": "3.16.2",
+      "resolved": "https://registry.npmmirror.com/@midwayjs/upload/-/upload-3.16.2.tgz",
+      "integrity": "sha512-LVA06rmp8RkUwsT4c7Pi2vgDOXO9O2JVnlY4BZcrvHblcIhU/RcL/uWxavU99Ih1pdOGL9Oj0FlTtcospeOE1g==",
+      "requires": {
+        "file-type": "16.5.4",
+        "raw-body": "2.5.2"
+      }
+    },
     "@midwayjs/validate": {
       "version": "3.16.1",
       "resolved": "https://registry.npmmirror.com/@midwayjs/validate/-/validate-3.16.1.tgz",
@@ -8753,6 +8937,11 @@
         "defer-to-connect": "^1.0.1"
       }
     },
+    "@tokenizer/token": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/@tokenizer/token/-/token-0.3.0.tgz",
+      "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
+    },
     "@types/accepts": {
       "version": "1.3.7",
       "resolved": "https://registry.npmmirror.com/@types/accepts/-/accepts-1.3.7.tgz",
@@ -10569,6 +10758,16 @@
         "flat-cache": "^3.0.4"
       }
     },
+    "file-type": {
+      "version": "16.5.4",
+      "resolved": "https://registry.npmmirror.com/file-type/-/file-type-16.5.4.tgz",
+      "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
+      "requires": {
+        "readable-web-to-node-stream": "^3.0.0",
+        "strtok3": "^6.2.4",
+        "token-types": "^4.1.1"
+      }
+    },
     "fill-range": {
       "version": "7.0.1",
       "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
@@ -10637,6 +10836,17 @@
       "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz",
       "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
     },
+    "fs-extra": {
+      "version": "11.2.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.2.0.tgz",
+      "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      }
+    },
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -10954,6 +11164,11 @@
         "safer-buffer": ">= 2.1.2 < 3"
       }
     },
+    "ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
+    },
     "ignore": {
       "version": "5.3.1",
       "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.1.tgz",
@@ -11765,6 +11980,16 @@
       "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
       "dev": true
     },
+    "jsonfile": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
+      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.6",
+        "universalify": "^2.0.0"
+      }
+    },
     "jsonwebtoken": {
       "version": "9.0.2",
       "resolved": "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
@@ -12138,12 +12363,6 @@
         "picomatch": "^2.3.1"
       }
     },
-    "mime": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz",
-      "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
-      "dev": true
-    },
     "mime-db": {
       "version": "1.52.0",
       "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
@@ -12534,6 +12753,11 @@
       "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
       "dev": true
     },
+    "peek-readable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/peek-readable/-/peek-readable-4.1.0.tgz",
+      "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="
+    },
     "picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz",
@@ -12799,6 +13023,24 @@
         }
       }
     },
+    "readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "requires": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      }
+    },
+    "readable-web-to-node-stream": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz",
+      "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==",
+      "requires": {
+        "readable-stream": "^3.6.0"
+      }
+    },
     "readdirp": {
       "version": "3.6.0",
       "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
@@ -13156,6 +13398,14 @@
       "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz",
       "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
     },
+    "string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "requires": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
     "string-length": {
       "version": "4.0.2",
       "resolved": "https://registry.npmmirror.com/string-length/-/string-length-4.0.2.tgz",
@@ -13213,6 +13463,15 @@
       "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
       "dev": true
     },
+    "strtok3": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmmirror.com/strtok3/-/strtok3-6.3.0.tgz",
+      "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
+      "requires": {
+        "@tokenizer/token": "^0.3.0",
+        "peek-readable": "^4.1.0"
+      }
+    },
     "superagent": {
       "version": "8.1.2",
       "resolved": "https://registry.npmmirror.com/superagent/-/superagent-8.1.2.tgz",
@@ -13231,6 +13490,12 @@
         "semver": "^7.3.8"
       },
       "dependencies": {
+        "mime": {
+          "version": "2.6.0",
+          "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz",
+          "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+          "dev": true
+        },
         "semver": {
           "version": "7.6.2",
           "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.2.tgz",
@@ -13361,6 +13626,15 @@
       "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz",
       "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
     },
+    "token-types": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/token-types/-/token-types-4.2.1.tgz",
+      "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
+      "requires": {
+        "@tokenizer/token": "^0.3.0",
+        "ieee754": "^1.2.1"
+      }
+    },
     "trim-newlines": {
       "version": "3.0.1",
       "resolved": "https://registry.npmmirror.com/trim-newlines/-/trim-newlines-3.0.1.tgz",
@@ -13485,6 +13759,12 @@
         "crypto-random-string": "^2.0.0"
       }
     },
+    "universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true
+    },
     "unpipe": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz",
@@ -13548,6 +13828,11 @@
         "prepend-http": "^2.0.0"
       }
     },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+    },
     "v8-compile-cache": {
       "version": "2.4.0",
       "resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz",

+ 4 - 0
package.json

@@ -13,11 +13,13 @@
     "@midwayjs/koa": "^3.12.0",
     "@midwayjs/logger": "^3.1.0",
     "@midwayjs/redis": "^3.16.0",
+    "@midwayjs/upload": "^3.16.2",
     "@midwayjs/validate": "^3.12.0",
     "@types/crypto-js": "^4.2.2",
     "@types/node-rsa": "^1.1.4",
     "crypto-js": "^4.2.0",
     "lodash": "^4.17.21",
+    "mime-types": "^2.1.35",
     "node-rsa": "^1.1.1"
   },
   "devDependencies": {
@@ -26,6 +28,7 @@
     "@types/lodash": "^4.17.1",
     "@types/node": "14",
     "cross-env": "^6.0.0",
+    "fs-extra": "^11.1.1",
     "jest": "^29.2.2",
     "mwts": "^1.3.0",
     "mwtsc": "^1.4.0",
@@ -38,6 +41,7 @@
   "scripts": {
     "start": "NODE_ENV=production node ./bootstrap.js",
     "dev": "cross-env NODE_ENV=local mwtsc --watch --run @midwayjs/mock/app.js",
+    "self": "cross-env NODE_ENV=self mwtsc --watch --run @midwayjs/mock/app.js",
     "test": "cross-env NODE_ENV=unittest jest",
     "cov": "jest --coverage",
     "lint": "mwts check",

+ 4 - 1
src/config/config.default.ts

@@ -1,5 +1,5 @@
 import { MidwayConfig } from '@midwayjs/core';
-import path = require('path');
+import * as path from 'path';
 export default {
   // use for cookie sign key, should change to your own and keep security
   keys: '1715577385578_8567',
@@ -8,6 +8,9 @@ export default {
   },
   //是否启用数据加密,需要与前端配合一致,否则会一端加密,一端不解密
   useCrypto: false,
+  upload: {
+    realdir: 'upload',
+  },
   midwayLogger: {
     default: {
       level: 'warn',

+ 1 - 1
src/config/config.prod.ts

@@ -1,5 +1,5 @@
 import { MidwayConfig } from '@midwayjs/core';
-const redisHost = '10.120.114.6';
+const redisHost = 'host.docker.internal'; // 10.120.114.6
 const redisPwd = '1234qwer!@#$';
 const redisDB = 0;
 const coreApiPrefix = '/cxyy/api';

+ 45 - 0
src/config/config.self.ts

@@ -0,0 +1,45 @@
+import { MidwayConfig } from '@midwayjs/core';
+const redisHost = 'localhost';
+const redisPwd = '1234qwer!@#$';
+const redisDB = 0;
+const coreApiPrefix = '/cxyy/api';
+const loginSign = 'cxyy';
+export default {
+  // use for cookie sign key, should change to your own and keep security
+  keys: '1715577385578_8567',
+  koa: {
+    port: 19700,
+  },
+  // 项目前缀
+  projectPrefix: {
+    core: coreApiPrefix,
+  },
+  loginSign,
+  // 项目前缀指向
+  axios: {
+    clients: {
+      // key则为路由前缀
+      [coreApiPrefix]: {
+        baseURL: 'http://127.0.0.1:9700',
+      },
+      '/test/api': {
+        baseURL: 'http://127.0.0.1:9701',
+      },
+    },
+  },
+  // 权限验证的uri
+  authUri: {
+    // 获取路由编码地址
+    getRouteCode: '/cxyy/api/token/gerRouterInfo',
+    // 获取角色权限编码地址
+    getUserApiCode: '/cxyy/api/token/getUserApiCodes',
+  },
+  redis: {
+    client: {
+      port: 6379, // Redis port
+      host: redisHost, // Redis host
+      password: redisPwd,
+      db: redisDB,
+    },
+  },
+} as MidwayConfig;

+ 2 - 0
src/configuration.ts

@@ -10,6 +10,7 @@ import * as jwt from '@midwayjs/jwt';
 import * as i18n from '@midwayjs/i18n';
 import { CustomErrorFilter } from './filter/customError.filter';
 import { SetLocaleToCtxMiddleware } from './middleware/setLocaleToCtx.middleware';
+import * as upload from '@midwayjs/upload';
 @Configuration({
   imports: [
     koa,
@@ -18,6 +19,7 @@ import { SetLocaleToCtxMiddleware } from './middleware/setLocaleToCtx.middleware
     redis,
     jwt,
     i18n,
+    upload,
     {
       component: info,
       enabledEnvironment: ['local'],

+ 75 - 0
src/controller/File.controller.ts

@@ -0,0 +1,75 @@
+import { Context } from '@midwayjs/koa';
+import { join, sep } from 'path';
+import { FileService } from '../service/File.service';
+import { createReadStream } from 'fs';
+import { Controller, Inject, Config, Post, Files, Fields, Get } from '@midwayjs/core';
+import * as mime from 'mime-types';
+/**文件上传默认类 */
+@Controller('/', { ignoreGlobalPrefix: true })
+export class FileController {
+  @Inject()
+  ctx: Context;
+  @Inject()
+  fileService: FileService;
+  @Config('upload.realdir')
+  uploadDir;
+
+  /**
+   * 文件上传
+   * @param {Array} files 文件数组
+   * @param {object} fields 其他字段
+   * @param {string} project 项目名
+   * @param {string} catalog 文件层级名 用'_'连接上下层级
+   * @param {string} item 文件名,没有默认用时间戳
+   */
+  @Post('/:project/upload')
+  @Post('/:project/:catalog/upload')
+  @Post('/:project/:catalog/:item/upload')
+  async upload(@Files() files, @Fields() fields) {
+    const { project, catalog, item } = this.ctx.params;
+    const dirs = [project];
+    if (catalog && catalog !== '_') {
+      const subs = catalog.split('_');
+      dirs.push(...subs);
+    }
+    let path = this.uploadDir;
+    // 检查分级目录是否存在,不存在则创建
+    for (let i = 0; i < dirs.length; i++) {
+      const p = `${path}${sep}${dirs.slice(0, i + 1).join(sep)}`;
+      this.fileService.mkdir(p);
+    }
+    path = `${join(path, dirs.join(sep))}${sep}`;
+    const file = files[0];
+    const ext = this.fileService.getExt(file.filename);
+    let filename = `${this.fileService.getNowDateTime()}${ext}`;
+    if (item) filename = item;
+    const uri = `/files/${dirs.join('/')}/${filename}`;
+    this.fileService.moveFile(file.data, `${path}${filename}`);
+    return { id: filename, name: filename, uri };
+  }
+
+  /**
+   * 读取文件
+   */
+  @Get('/*')
+  async readFile() {
+    const shortRealPath = this.fileService.getFileShortRealPath();
+    const realPath = join(this.uploadDir, shortRealPath);
+    this.ctx.body = createReadStream(realPath);
+    const type = mime.lookup(realPath);
+    this.ctx.response.set('content-type', type);
+  }
+  @Post('/clear/files')
+  async clearFiles() {
+    try {
+      // 先重写文件地址表
+      const modelResults = this.fileService.scanModelHaveFile();
+      await this.fileService.setFileUriFromDefaultDataBase(modelResults);
+      // 再清除未使用的上传文件
+      await this.fileService.deleteNoUseFile();
+    } catch (error) {
+      console.error(error);
+    }
+    return 'ok';
+  }
+}

+ 219 - 0
src/service/File.service.ts

@@ -0,0 +1,219 @@
+import { existsSync, lstatSync, mkdirSync, readdirSync } from 'fs';
+import { Context } from '@midwayjs/koa';
+import { dirname, extname, join, sep } from 'path';
+import { pick, flattenDeep } from 'lodash';
+import { moveSync, remveSync } from 'fs-extra';
+import { Provide, Inject, Config } from '@midwayjs/core';
+interface ScanModelFileValue {
+  model: any;
+  projection: object;
+}
+interface ScanModelFileResult {
+  [key: string]: ScanModelFileValue;
+}
+const limit = 50;
+
+/**
+ * 文件上传相关服务
+ */
+@Provide()
+export class FileService {
+  @Inject()
+  ctx: Context;
+  @Config('upload')
+  uploadConfig;
+
+  // #region 递归清理未被使用的上传文件
+  /**
+   * 删除不在文件使用表登记的文件
+   */
+  async deleteNoUseFile() {
+    const realDir = this.uploadConfig.realdir;
+    this.recursionFindFile(realDir);
+  }
+  /**
+   *
+   * @param basePath 基础路径
+   * @param list 基础路径下的所有内容
+   */
+  async recursionFindFile(basePath) {
+    const dirExists = existsSync(basePath);
+    if (!dirExists) return;
+    const list = readdirSync(basePath);
+    for (const f of list) {
+      const thisPath = join(basePath, f);
+      // 文件夹就继续递归找
+      if (this.isDir(thisPath)) this.recursionFindFile(thisPath);
+      else if (this.isFile(thisPath)) {
+        // 文件,需要到表里找是否存在,存在保留,不存在就删除
+        const shortPath = this.realPathTurnToShortPath(thisPath);
+        // const count = await this.UseFileModel.count({ uri: shortPath });
+        // if (count <= 0) this.toUnlink(thisPath);
+      }
+    }
+  }
+  /**
+   * 真实路径=>短地址
+   * @param realPath 文件真实路径
+   * @returns string 短地址
+   */
+  realPathTurnToShortPath(realPath: string) {
+    const realDir = this.uploadConfig.realdir;
+    let shortPath = realPath.replace(realDir, '/files');
+    while (shortPath.includes('\\')) {
+      shortPath = shortPath.replace('\\', '/');
+    }
+    return shortPath;
+  }
+
+  /**
+   * 删除文件
+   * @param path 文件路径
+   */
+  toUnlink(path) {
+    remveSync(path);
+  }
+
+  /**
+   * 判断路径是否存在
+   * @param path 路径
+   * @returns boolean: true-存在/false-不存在
+   */
+  isExists(path) {
+    return existsSync(path);
+  }
+  /**
+   * 判断是否是文件夹
+   * @param path 路径
+   * @returns boolean: true-是文件夹
+   */
+  isDir(path) {
+    const f = lstatSync(path);
+    return f.isDirectory();
+  }
+  /**
+   * 判断是否是文件
+   * @param path 路径
+   * @returns boolean: true-是文件
+   */
+  isFile(path) {
+    const f = lstatSync(path);
+    return f.isFile();
+  }
+  // #endregion
+
+  // #region 写入文件统计地址表
+  /**文件统计地址表重写, 由 scanModelHaveFile()提供参数*/
+  async setFileUriFromDefaultDataBase(scanResult: ScanModelFileResult) {
+    // 清空表内容,重写
+    // await this.UseFileModel.deleteMany({});
+    for (const mn in scanResult) {
+      const obj = scanResult[mn];
+      const { model, projection } = obj;
+      // 获取表总数量
+      const total = await model.count();
+      // 计算循环次数
+      const skip = 0;
+      const times = Math.ceil(total / limit);
+      for (let i = 0; i < times; i++) {
+        // 查数据
+        const r = await model.find({}, projection).skip(skip).limit(limit).lean();
+        // 整理批量添加数据格式
+        let filesUri = r.map(i => {
+          const arr = [];
+          for (const prop in projection) {
+            const targetVal = i[prop];
+            const uris = targetVal.map(ti => pick(ti, 'uri'));
+            arr.push(...uris);
+          }
+          return arr;
+        });
+        filesUri = flattenDeep(filesUri);
+        //批量添加
+        // await this.UseFileModel.insertMany(filesUri);
+      }
+    }
+  }
+
+  /**扫描注册的model中是否有File类型字段,以Object的形式提取出来*/
+  scanModelHaveFile(): ScanModelFileResult {
+    throw new Error('cant use');
+    // const modelNames = this.uploadConfig.modelNames || [];
+    // const models = modelNames.map(i => GetModel(i));
+    // const result: ScanModelFileResult = {};
+    // for (const model of models) {
+    //   const s = model['schema'];
+    //   const fields: object = s['tree'];
+    //   const projection = {};
+    //   for (const f in fields) {
+    //     const field = fields[f];
+    //     if (isObject(field)) {
+    //       const type = get(field, 'type');
+    //       if (type && type === mongoose.Schema.Types['File']) {
+    //         projection[f] = 1;
+    //       }
+    //     }
+    //   }
+    //   if (Object.keys(projection).length > 0) {
+    //     result[model.modelName] = { model, projection };
+    //   }
+    // }
+    // return result;
+  }
+  // #endregion
+
+  // #region 上传部分
+  /**文件真实短地址 */
+  getFileShortRealPath() {
+    let originalUrl = this.ctx.request.originalUrl;
+    originalUrl = decodeURI(originalUrl);
+    //先去掉请求前缀
+    // 再去掉files
+    originalUrl = originalUrl.replace('/files', '');
+    const arr = originalUrl.split('/');
+    // 首行为空去掉
+    arr.splice(0, 1);
+    // 最后一个数据为文件,其余的为路径拼成一起就行
+    return arr.join(sep);
+  }
+  /**
+   * 移动文件
+   * @param tempPath 临时上传文件位置
+   * @param path 实际文件应处位置
+   */
+  moveFile(tempPath: string, path: string) {
+    moveSync(tempPath, path);
+  }
+
+  // 创建文件夹
+  mkdir(dn: string) {
+    if (existsSync(dn)) {
+      return true;
+    }
+    if (this.mkdir(dirname(dn))) {
+      mkdirSync(dn);
+      return true;
+    }
+  }
+  /**获取文件名后缀 */
+  getExt(name: string) {
+    return extname(name);
+  }
+  /**获取年月日时分秒, 格式: 年月日时分秒 */
+  getNowDateTime() {
+    const date = new Date();
+    const y = date.getFullYear();
+    const m = date.getMonth() + 1;
+    const mstr = m < 10 ? '0' + m : m;
+    const d = date.getDate();
+    const dstr = d < 10 ? '0' + d : d;
+    const h = date.getHours();
+    const hstr = h < 10 ? '0' + h : h;
+    const minute = date.getMinutes();
+    const minutestr = minute < 10 ? '0' + minute : minute;
+    const second = date.getSeconds();
+    const secondstr = second < 10 ? '0' + second : second;
+    return `${y}${mstr}${dstr}${hstr}${minutestr}${secondstr}`;
+  }
+  // #endregion
+}