Browse Source

代码提交

hl 11 months ago
parent
commit
83cafa55f0
16 changed files with 1817 additions and 47 deletions
  1. 18 3
      README.md
  2. 8 17
      client.js
  3. 2 0
      dockerfile
  4. 1 1
      old_client/server.py
  5. 841 20
      package-lock.json
  6. 2 0
      package.json
  7. 64 0
      src/Interval.js
  8. 62 0
      src/config.js
  9. 409 0
      src/robot.js
  10. 14 0
      src/utils/config2.json
  11. 101 0
      src/utils/file.js
  12. 149 0
      src/utils/http.js
  13. 50 0
      src/utils/logger.js
  14. 15 0
      src/utils/node.json
  15. 81 0
      src/web.js
  16. 0 6
      utils/logger.js

+ 18 - 3
README.md

@@ -10,9 +10,24 @@ message
 已停止的:STOPPED
 正在停止:STOP_PENDING
 正在运行:RUNNING
-启动中:START_PENDING
-重启中:RESTART_PENDING
+启动中:START_PENDING 
+重启中:RESTART_PENDING 
 下载中:DOWNLOADING
 下载失败:DOWNLOAD_FAILED
-等待下载:DOWNLOAD_PENDING
+等待下载:DOWNLOADING
 错误:ERROR
+
+
+[//]: # (STOPPED: 已停止, )
+
+[//]: # (STOP_PENDING: 停止中,*)
+
+[//]: # (RUNNING:运行中,)
+
+[//]: # (START_PENDING:启动中,*)
+
+[//]: # (RESTART PENDING:重启中 *)
+
+[//]: # (DOWNLOADING:下载中, )
+
+[//]: # (ERROR:错误())

+ 8 - 17
client.js

@@ -1,19 +1,10 @@
-const { spawn } = require('child_process')
-const logger = require('./utils/logger')
+const web = require('./src/web')
+const config = require('./src/config')
+const interval = require('./src/Interval')
 
-const command = './as_test/as-rust.exe'
-const args = ['--config=./as_test/config.toml']
 
-const childProcess = spawn(command, args)
-
-childProcess.stdout.on('data', (msg) => {
-    logger.info(msg)
-})
-
-childProcess.stderr.on('data', (err_msg) => {
-    logger.error(err_msg);
-});
-
-childProcess.on('close', (code) => {
-    logger.info(`子进程退出,退出码 ${code}`);
-});
+config.init()
+// 启动web
+web.init()
+// 定时任务
+interval.init()

+ 2 - 0
dockerfile

@@ -13,5 +13,7 @@ RUN npm install
 # 将项目的所有文件复制到容器内
 COPY . .
 
+EXPOSE 3000
+
 # 容器启动时执行的命令
 CMD ["node", "client.js"]

+ 1 - 1
old_client/server.py

@@ -63,7 +63,7 @@ class api():
 
             print(url,post)
 
-            try:
+            try:else
                 print(r.text)
             except:
                 traceback.print_exc()

+ 841 - 20
package-lock.json

@@ -1,86 +1,907 @@
 {
   "name": "4l_capital_central_control_client",
   "version": "1.0.0",
-  "lockfileVersion": 1,
+  "lockfileVersion": 3,
   "requires": true,
-  "dependencies": {
-    "date-format": {
+  "packages": {
+    "": {
+      "name": "4l_capital_central_control_client",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "axios": "^1.6.7",
+        "express": "^4.18.3",
+        "log4js": "^6.9.1"
+      }
+    },
+    "node_modules/accepts": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+      "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+      "dependencies": {
+        "mime-types": "~2.1.34",
+        "negotiator": "0.6.3"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/axios": {
+      "version": "1.6.7",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
+      "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
+      "dependencies": {
+        "follow-redirects": "^1.15.4",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/body-parser": {
+      "version": "1.20.2",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
+      "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "content-type": "~1.0.5",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "on-finished": "2.4.1",
+        "qs": "6.11.0",
+        "raw-body": "2.5.2",
+        "type-is": "~1.6.18",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/body-parser/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/body-parser/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/call-bind": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+      "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+      "dependencies": {
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2",
+        "get-intrinsic": "^1.2.4",
+        "set-function-length": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/content-disposition": {
+      "version": "0.5.4",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+      "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+      "dependencies": {
+        "safe-buffer": "5.2.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+      "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
+    },
+    "node_modules/date-format": {
       "version": "4.0.14",
       "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
-      "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg=="
+      "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==",
+      "engines": {
+        "node": ">=4.0"
+      }
     },
-    "debug": {
+    "node_modules/debug": {
       "version": "4.3.4",
       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
       "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
-      "requires": {
+      "dependencies": {
         "ms": "2.1.2"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/define-data-property": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+      "dependencies": {
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/destroy": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+      "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+      "engines": {
+        "node": ">= 0.8",
+        "npm": "1.2.8000 || >= 1.4.16"
+      }
+    },
+    "node_modules/ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
+    },
+    "node_modules/encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+      "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+      "dependencies": {
+        "get-intrinsic": "^1.2.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/express": {
+      "version": "4.18.3",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz",
+      "integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==",
+      "dependencies": {
+        "accepts": "~1.3.8",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.20.2",
+        "content-disposition": "0.5.4",
+        "content-type": "~1.0.4",
+        "cookie": "0.5.0",
+        "cookie-signature": "1.0.6",
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "1.2.0",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.7",
+        "qs": "6.11.0",
+        "range-parser": "~1.2.1",
+        "safe-buffer": "5.2.1",
+        "send": "0.18.0",
+        "serve-static": "1.15.0",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "type-is": "~1.6.18",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      }
+    },
+    "node_modules/express/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/express/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/finalhandler": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+      "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+      "dependencies": {
+        "debug": "2.6.9",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "2.4.1",
+        "parseurl": "~1.3.3",
+        "statuses": "2.0.1",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/finalhandler/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
       }
     },
-    "flatted": {
+    "node_modules/finalhandler/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/flatted": {
       "version": "3.3.1",
       "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
       "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="
     },
-    "fs-extra": {
+    "node_modules/follow-redirects": {
+      "version": "1.15.6",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+      "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fs-extra": {
       "version": "8.1.0",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
       "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
-      "requires": {
+      "dependencies": {
         "graceful-fs": "^4.2.0",
         "jsonfile": "^4.0.0",
         "universalify": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=6 <7 || >=8"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+      "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2",
+        "has-proto": "^1.0.1",
+        "has-symbols": "^1.0.3",
+        "hasown": "^2.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "graceful-fs": {
+    "node_modules/gopd": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+      "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+      "dependencies": {
+        "get-intrinsic": "^1.1.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/graceful-fs": {
       "version": "4.2.11",
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
     },
-    "jsonfile": {
+    "node_modules/has-property-descriptors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+      "dependencies": {
+        "es-define-property": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-proto": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+      "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
+      "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+      "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+      "dependencies": {
+        "depd": "2.0.0",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.2.0",
+        "statuses": "2.0.1",
+        "toidentifier": "1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/jsonfile": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
       "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
-      "requires": {
+      "optionalDependencies": {
         "graceful-fs": "^4.1.6"
       }
     },
-    "log4js": {
+    "node_modules/log4js": {
       "version": "6.9.1",
       "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz",
       "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==",
-      "requires": {
+      "dependencies": {
         "date-format": "^4.0.14",
         "debug": "^4.3.4",
         "flatted": "^3.2.7",
         "rfdc": "^1.3.0",
         "streamroller": "^3.1.5"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
+    },
+    "node_modules/methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+      "engines": {
+        "node": ">= 0.6"
       }
     },
-    "ms": {
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
-    "rfdc": {
+    "node_modules/negotiator": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+      "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+      "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "node_modules/qs": {
+      "version": "6.11.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+      "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+      "dependencies": {
+        "side-channel": "^1.0.4"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+      "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+      "dependencies": {
+        "bytes": "3.1.2",
+        "http-errors": "2.0.0",
+        "iconv-lite": "0.4.24",
+        "unpipe": "1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/rfdc": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz",
       "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg=="
     },
-    "streamroller": {
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "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/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "node_modules/send": {
+      "version": "0.18.0",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+      "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+      "dependencies": {
+        "debug": "2.6.9",
+        "depd": "2.0.0",
+        "destroy": "1.2.0",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "2.0.0",
+        "mime": "1.6.0",
+        "ms": "2.1.3",
+        "on-finished": "2.4.1",
+        "range-parser": "~1.2.1",
+        "statuses": "2.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/send/node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/send/node_modules/debug/node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+    },
+    "node_modules/send/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+    },
+    "node_modules/serve-static": {
+      "version": "1.15.0",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+      "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+      "dependencies": {
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.3",
+        "send": "0.18.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/set-function-length": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
+      "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==",
+      "dependencies": {
+        "define-data-property": "^1.1.2",
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2",
+        "get-intrinsic": "^1.2.3",
+        "gopd": "^1.0.1",
+        "has-property-descriptors": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+    },
+    "node_modules/side-channel": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+      "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+      "dependencies": {
+        "call-bind": "^1.0.7",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.4",
+        "object-inspect": "^1.13.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/statuses": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+      "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/streamroller": {
       "version": "3.1.5",
       "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz",
       "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==",
-      "requires": {
+      "dependencies": {
         "date-format": "^4.0.14",
         "debug": "^4.3.4",
         "fs-extra": "^8.1.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "engines": {
+        "node": ">=0.6"
       }
     },
-    "universalify": {
+    "node_modules/type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "dependencies": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/universalify": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
-      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
+      "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+      "engines": {
+        "node": ">= 0.8"
+      }
     }
   }
 }

+ 2 - 0
package.json

@@ -10,6 +10,8 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
+    "axios": "^1.6.7",
+    "express": "^4.18.3",
     "log4js": "^6.9.1"
   }
 }

+ 64 - 0
src/Interval.js

@@ -0,0 +1,64 @@
+const robot = require('./robot')
+ const { logger, fileLogger }  = require("./utils/logger");
+const http = require("./utils/http");
+const {getRustConfig} = require('./config')
+const constants = require("constants");
+
+
+/*******
+ * 定时任务
+ * **** */
+function init() {
+    reportDel()
+    reportStatus()
+    reportAmount()
+    logger.info('--定时任务 启动');
+}
+
+//定时清理机器人
+function reportDel() {
+    const intervalInMilliseconds = 2000;
+    setInterval(() => {
+        // logger.info('当前1',robot.appMap);
+        robot.appMap.forEach((value, key) => {
+            if (value.id !== -1) {
+                // logger.info(`清理没用机器人开始111!`,value);
+                robot.delRobot(value)
+            }
+        });
+    }, intervalInMilliseconds);
+}
+
+
+// 5秒扫描本机所有机器人状态,进行上报
+function reportStatus() {
+    const intervalInMilliseconds = 5000;
+    setInterval(() => {
+        robot.appMap.forEach((value, key) => {
+            // logger.info('当前',robot.appMap);
+            if (value.id !== -1) {
+                robot.robotStatus(value)
+            }
+        });
+    }, intervalInMilliseconds);
+}
+
+
+// 2秒一次对正在运行的,服务器进行余额比对,发生变化则上报
+function reportAmount() {
+    // 设置定时器,每隔一定时间(比如每隔5秒)发送一次GET请求
+    const intervalInMilliseconds = 2000;
+    setInterval(() => {
+        // logger.info('当前2',robot.appMap);
+        robot.appMap.forEach((value, key) => {
+            if (value.threadStatus === robot.RobotStatus.RUNNING) {
+                robot.robotAmount(value)
+            }
+        });
+    }, intervalInMilliseconds);
+}
+
+
+module.exports = {
+    init
+};

+ 62 - 0
src/config.js

@@ -0,0 +1,62 @@
+const file = require('./utils/file')
+const { logger, fileLogger }  = require("./utils/logger");
+const fs = require('fs');
+const path = require('path');
+/**
+ * 配置
+ * */
+
+let rustConfig = {}
+
+
+function init() {
+    /**
+     * 1. 检查目录
+     * */
+    getConfig()
+    logger.info('--配置 检查完成');
+}
+
+function getConfig() {
+    const configFilePath = path.join(__dirname, './utils/node.json');
+    // 使用 fs 模块同步地读取 JSON 文件的内容
+    try {
+        // 读取文件内容
+        const rawContent = fs.readFileSync(configFilePath, 'utf8');
+
+        // 解析 JSON 内容为 JavaScript 对象
+        const config = JSON.parse(rawContent);
+        rustConfig = config.rustConfig
+
+        // 现在可以访问 config 对象中的数据了
+        logger.info('配置文件内容:', rustConfig);
+    } catch (error) {
+        // 如果有错误发生,比如文件不存在或者 JSON 格式不正确,打印错误信息
+        logger.error('读取配置文件时出错:', error);
+    }
+}
+function getRustConfig(){
+    return rustConfig
+}
+
+// 检查目录,与文件
+function checkConfig(id) {
+    var appPath = path + "/" + id
+    //1. 检查目录
+    file.checkPath(appPath)
+    //2. 检查文件
+    for (var i in fileArray) {
+        if (file.checkFilePath(appPath + "/" + fileArray[i])) {
+
+        } else {
+            //不存在,需要下载,比对版本号等
+        }
+    }
+}
+
+
+module.exports = {
+    init,
+    checkConfig,
+    getRustConfig
+}

+ 409 - 0
src/robot.js

@@ -0,0 +1,409 @@
+const file = require("./utils/file");
+const {getRustConfig} = require("./config");
+const {spawn, exec, fork, execSync} = require("child_process");
+const {logger} = require("./utils/logger");
+const http = require("./utils/http");
+const e = require("express");
+/*******
+ * web
+ * **** */
+
+
+const RobotStatus = Object.freeze({
+    // 已停止的
+    STOPPED: "STOPPED",
+    // 正在停止
+    STOP_PENDING: "STOP_PENDING",
+    //正在运行
+    RUNNING: "RUNNING",
+    //启动中
+    START_PENDING: "START_PENDING",
+    //下载中
+    DOWNLOADING: "DOWNLOADING",
+    //错误
+    ERROR: "ERROR"
+});
+
+let appMap = new Map();
+
+
+function getApp(key) {
+    let app = {
+        id: -1,
+        childProcess: undefined,
+        nowBalance: 0,
+        messlist: [],
+        threadStatus: RobotStatus.STOPPED,
+        errorMessage: "成功",
+        threadStartTime: -1,
+    }
+    // logger.info(appMap, appMap.has(key));
+    if (appMap.has(key)) {
+        app = appMap.get(key)
+    } else {
+        appMap.set(key, app)
+    }
+    // logger.info(app);
+    return app
+}
+
+function delApp(key) {
+    appMap.delete(key)
+}
+
+async function run(param) {
+    var key = param.id
+    var appName = param.path
+    var app = getApp(key)
+    var config = getRustConfig()
+
+
+    /****
+     *** 第一步:防止 反复启动,先要kill一次
+     ***/
+    var waitForCondition = function (intervalDuration, conditionFunction) {
+        return new Promise((resolve, reject) => {
+            const intervalId = setInterval(() => {
+                if (conditionFunction()) {
+                    clearInterval(intervalId);
+                    resolve();
+                }
+            }, intervalDuration);
+        });
+    }
+
+    logger.info('当前app', app);
+    if (app.childProcess !== undefined && app.threadStatus !== RobotStatus.STOPPED
+        && app.threadStatus !== RobotStatus.ERROR && app.threadStatus !== RobotStatus.STOP_PENDING) {
+        logger.info('防止重复启动,需要先杀死');
+        app.threadStatus = RobotStatus.STOP_PENDING
+        robotStatus(app)
+        app.childProcess.kill()
+
+        // 当这个条件变为true时,会结束等待
+        await waitForCondition(1000, () => {
+            if (app.threadStatus === RobotStatus.STOPPED) {
+                logger.info('成功杀死可以重新开启');
+                return true
+            } else {
+                logger.info('等待杀死');
+                return false
+            }
+        });
+    }
+    /********************新杀进程*************************************/
+    // await waitForCondition(1000, () => {
+    //     if (app.threadStatus === RobotStatus.STOPPED) {
+    //         logger.info('成功杀死可以重新开启');
+    //         return true
+    //     } else {
+    //         logger.info('等待杀死');
+    //         return false
+    //     }
+    // });
+    // return;
+    /********************新杀进程*************************************/
+
+    // 初始化机器人状态
+    app.threadStatus = RobotStatus.START_PENDING
+    app.id = key
+    robotStatus(app)
+
+    /****
+     *** 第二步:路径经组装
+     *   注意:可能存在一台服务器多个机器人,通过机器人ID创建文件夹区分,需要组装好路径
+     ***/
+        //系统不同 做不同的路径处理
+    const platform = process.platform;
+    let exeName = appName;           //可执行程序
+    // let configName = "config.toml";        //配置文件
+    let configName = "config.json";        //配置文件
+    let appPath = "";
+    if (platform === 'win32') {//直接运行相对路径
+        appPath = config.winPath + "/" + app.id
+    } else {//打包为docker 镜像
+        appPath = config.linuxPath + "/" + app.id
+    }
+
+    /****
+     *** 第三步:rust 启动程序检查(下载更新)
+     ***/
+
+    var isDow = false
+    var scheduleDow = 0
+    var scheduleConfig = 0
+    //1. 检查目录
+    file.checkPathSync(appPath);
+    //2、 检查执行程序
+    var destination = appPath + "/" + exeName
+    if (!file.checkFilePath(destination)) {
+        app.threadStatus = RobotStatus.DOWNLOADING
+        robotStatus(app)
+        isDow = true
+        var dowHeaders = {...config.headers};
+        if (platform === 'win32') {
+            downloadFileWithPowerShell(
+                config.baseUrl + config.dowAppURL + '/?path=' + appName,
+                appPath + "/" + appName,
+                dowHeaders, (err, b) => {
+                    if (err === null) {
+                        scheduleDow = 1
+                    } else {
+                        app.threadStatus = RobotStatus.ERROR
+                        app.errorMessage = '下载失败!'
+                        scheduleDow = -1
+                        robotStatus(app)
+                    }
+                });
+        } else {
+            downloadFileWithLinux(
+                config.baseUrl + config.dowAppURL + '/?path=' + appName,
+                appPath + "/" + appName,
+                dowHeaders, (err, b) => {
+                    if (err === null) {
+                        scheduleDow = 1
+                    } else {
+                        app.threadStatus = RobotStatus.ERROR
+                        app.errorMessage = '下载失败!'
+                        scheduleDow = -1
+                        robotStatus(app)
+                    }
+                });
+        }
+    } else {
+        scheduleDow = 1
+    }
+
+    /****
+     *** 第四步:rust 启动配置检查(下载更新)
+     ***/
+        //2 为防止启动指令不同,每次重新写入
+    var destination2 = appPath + "/" + configName
+    var urrrl = config.baseUrl + config.dowConfigURL + '/?robotId=' + app.id
+    var configHeaders = {...config.headers};
+    http.request_get(urrrl, {...config.headers})
+        .then((data) => {
+            logger.info('配置参数:', data);
+            const json = JSON.parse(data)
+            //处理成配置文件
+            const map = json.data
+            var json_obj = JSON.parse("{}")
+            for (k in map) {
+                //未完成 参数解析有问题
+                if (k === "account") {
+                    var account = map["account"];
+                    delete map.account
+                    json_obj["account_name"] = account["name"]
+                    json_obj["access_key"] = account["accessKey"]
+                    json_obj["secret_key"] = account["secretKey"]
+                    json_obj["pass_key"] = account["pass"]
+                } else if (k === "ref_pair") {
+                    json_obj["ref_pair"] = [map["ref_pair"]]
+                } else {
+                    json_obj[k] = map[k]
+                }
+                logger.info(map[k] + "\t")
+            }
+            logger.info("参数组装完成!")
+            te = JSON.stringify(json_obj)
+            file.writeFile(destination2, te, (errer, b) => {
+                if (errer === null && b === true) {
+                    scheduleConfig = 1
+                } else {
+                    logger.info("配置参数写入配置失败!", errer)
+                    app.threadStatus = RobotStatus.ERROR
+                    app.errorMessage = '配置参数写入配置失败!'
+                    scheduleConfig = -1
+                    robotStatus(app)
+                }
+            })
+        })
+        .catch((e) => {
+            logger.info("配置参数获取失败", e)
+            app.threadStatus = RobotStatus.ERROR
+            app.errorMessage = '配置参数获取失败!'
+            scheduleConfig = -1
+            robotStatus(app)
+        })
+
+    //监听下载只有下载完成了才能继续
+    while (true) {
+        await delay(5000);
+        let info_t = ""
+        let info_t2 = ""
+        //是否开启下载,如果是新下载,下载完成需要授权
+        if (isDow) {
+            if (scheduleDow === 1) {
+                info_t = "启动文件:下载完成!"
+                //文件授权
+                execSync(`chmod +x ${appPath + "/" + appName}`, (error, stdout, stderr) => {
+                    if (error) {
+                        logger.error(`启动文件:授权失败: ${error}`);
+                    }
+                    logger.info(`启动文件:授权完成!`);
+                });
+            } else if (scheduleDow === -1) {
+                info_t = "启动文件:下载失败!"
+            } else {
+                info_t = "启动文件:还在下载..."
+            }
+        } else {
+            info_t = "启动文件:完整无需下载"
+        }
+
+        if (scheduleConfig === 1) {
+            info_t2 = "配置文件:读取成功!"
+        } else if (scheduleConfig === -1) {
+            info_t = "配置文件:读取失败!"
+        } else {
+            info_t2 = "配置文件:还在读取..."
+        }
+        logger.info(info_t, info_t2);
+        if (scheduleDow === 1 && scheduleConfig === 1) {
+            break
+        } else if (scheduleConfig === -1 || scheduleDow === -1) {
+            return;
+        }
+    }
+
+    app.threadStatus = RobotStatus.START_PENDING
+    robotStatus(app)
+    logger.info("开始启动程序!");
+    //3. spawn启动
+    const exePath = appPath + "/" + exeName
+    const configPath = appPath + "/" + configName
+    logger.info(`文件地址:${exePath}-----${configPath}`);
+    const command = exePath
+    const args = ['--config=' + configPath]
+    app.childProcess = spawn(command, args)
+
+
+    app.threadStartTime = new Date().getTime()
+    /**********监听*********/
+    app.childProcess.stdout.on('data', (msg) => {
+        // logger.info('stdout:' + msg.toString())
+    })
+
+    app.childProcess.on('message', (msg) => {
+        // logger.info(`message: ${msg}`);
+    });
+    app.childProcess.on('exit', (code, signal) => {
+        logger.info(`子进程退出-exit,退出码: ${code}, 信号: ${signal}`);
+    });
+
+    app.childProcess.on('close', (code) => {
+        logger.info(`子进程退出-close,退出码 ${code}`);
+        app.threadStatus = RobotStatus.STOPPED
+        robotStatus(app)
+    });
+    app.threadStatus = RobotStatus.RUNNING
+}
+
+function delay(ms) {
+    return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+async function closeApp(param) {
+    var key = param.id
+    var app = getApp(key)
+    logger.info(` 信号:  `, app.threadStatus);
+    if (app.threadStatus === RobotStatus.RUNNING) {
+        app.childProcess.kill('SIGTERM');
+        app.threadStatus = RobotStatus.STOP_PENDING
+        robotStatus(app)
+    }
+}
+
+
+// 下载执行程序,使用的实行服务器指令的方式进行下载
+function downloadFileWithPowerShell(url, destination, headers, funBreak) {
+    const headersString = Object.entries(headers)
+        .map(([key, value]) => `'${key}'='${value}'`) // 使用单引号来确保特殊字符不被解析
+        .join('; '); // 使用分号和空格分隔每个键值对
+    const command = `powershell -command "Invoke-WebRequest -Uri '${url}' -OutFile '${destination}' -Headers @{${headersString}}"`;
+    exec(command, (error, stdout, stderr) => {
+        if (error) {
+            logger.error(`下载出错: ${error}`);
+            return funBreak(error);
+        }
+        logger.info(`下载完成!`);
+        funBreak(null, true); // 通知下载成功,修改了B为true
+    });
+}
+
+function downloadFileWithLinux(url, destination, headers, funBreak) {
+    const headersString = Object.entries(headers)
+        .map(([key, value]) => `-H '${key}: ${value}'`) // 设置curl的HTTP头参数
+        .join(' '); // 使用空格分隔每个头参数
+    const command = `curl ${headersString} '${url}' -o '${destination}'`;
+    exec(command, (error, stdout, stderr) => {
+        if (error) {
+            logger.error(`下载出错: ${error}`);
+            return funBreak(error);
+        }
+        logger.info(`下载完成!`);
+        funBreak(null, true); // 通知下载成功,修改了B为true
+    });
+}
+
+
+// 上报-状态
+function robotStatus(app) {
+    var config = getRustConfig()
+
+    var msg = (app.threadStatus !== RobotStatus.ERROR ? "完成" : app.errorMessage)
+    http.request_post(`${config.baseUrl}/report/statusReport`, {
+        "robotId": app.id,
+        "status": app.threadStatus,
+        "msg": msg
+    }, {...config.headers}).then((data) => {
+        // logger.info('??', data);
+        logger.info('汇报--状态:', '机器人id:', app.id, '状态:', app.threadStatus);
+    }).catch((error) => {
+        logger.error(`请求遇到问题1: ${error.message}`); // 处理可能发生的错误
+    });
+}
+
+// 上报-余额
+function robotAmount(app) {
+    //拿到策略余额
+    try {
+        var config = getRustConfig()
+        http.request_get(`${config.rustUrl}/account`, {...config.headers})
+            .then((data) => {
+                var d = JSON.parse(data)
+                //余额有变动上报一次
+                if ((app.nowBalance + "") !== (d.now_balance + "")) {
+                    http.request_post(`${config.baseUrl}/report/amountReport`, {
+                        "robotId": app.id,
+                        "amount": d.now_balance
+                    }, {...config.headers})
+                        .then((data2) => {
+                            // logger.info('上报响应', data2);
+                            logger.info('汇报--余额:pid:', app.childProcess.pid, '机器人id:', app.id, '机器人本地余额:', app.nowBalance, '实际余额:', d.now_balance);
+                        }).catch((error) => {
+                        logger.error(`请求遇到问题:2 ${error.message}`); // 处理可能发生的错误
+                    });
+                    app.nowBalance = d.now_balance
+                }
+            }).catch((error) => {
+            logger.error(`请求遇到问题2: ${error.message}`); // 处理可能发生的错误
+        });
+    } catch (e) {
+        logger.error('请求失败!:', e)
+    }
+}
+
+// 定时清理停止状态的 机器人
+function delRobot(app) {
+    const platform = process.platform;
+    if (platform !== 'win32' && app.threadStatus === RobotStatus.STOPPED) {
+        var pid = app.childProcess.pid
+        delApp(app.id)
+    }
+}
+
+
+module.exports = {
+    run, closeApp, delRobot, robotStatus, robotAmount, appMap, RobotStatus
+};

+ 14 - 0
src/utils/config2.json

@@ -0,0 +1,14 @@
+{
+  "rustConfig": {
+    "reportUrl": "http://192.168.1.5:81",
+    "rustUrl": "http://127.0.0.1:5555",
+    "dowAppURL": "http://cc.skyfffire.com/api/report/downloadFile",
+    "dowConfigURL": "http://192.168.1.5:81/report/getRobotConfig",
+    "winPath": "C:/Users/hl/Desktop/node_config",
+    "linuxPath": "/4l/rust",
+    "headers": {
+      "auth": "4L",
+      "token": "r7T$8gBV!f&L@E2+"
+    }
+  }
+}

+ 101 - 0
src/utils/file.js

@@ -0,0 +1,101 @@
+const fs = require('fs');
+const path = require('path');
+const {logger} = require("./logger");
+const {rustConfig} = require("../config");
+const http = require("./http");
+
+
+// 检查目录是否存在,不存在新建
+function checkPathSync(filePath) {
+    // 将相对路径转换为绝对路径
+    const directoryPath = path.resolve(filePath);
+    // 同步检查目录是否存在
+    if (!fs.existsSync(directoryPath)) {
+        // 目录不存在,需要创建它
+        logger.info('目录不存在,正在创建...');
+        try {
+            // 同步创建目录
+            fs.mkdirSync(directoryPath, {recursive: true});
+            logger.info('目录已成功创建');
+        } catch (error) {
+            // 创建目录失败
+            logger.error('创建目录失败:', error);
+            return false
+        }
+    } else {
+        // 目录已存在
+        logger.info('目录已存在');
+    }
+    return true
+}
+
+// 检查文件是否存在
+function checkFilePath(filePath) {
+    // 同步检查文件是否存在
+    const directoryPath = path.resolve(filePath);
+    if (fs.existsSync(directoryPath)) {
+        logger.info('文件存在:' + filePath);
+        return true
+    } else {
+        logger.error('文件不存在:' + filePath);
+        return false
+    }
+}
+
+// 创建文件
+function writeFile(filePath, text, breakFun) {
+    fs.writeFile(filePath, text, (err) => {
+        if (err) {
+            console.error('写入文件时发生错误:', err);
+            breakFun(err)
+        } else {
+            console.log('文件已成功写入!');
+            breakFun(null, true)
+        }
+    });
+}
+
+// 同步复制
+function copyFileSync(source, destination) {
+    try {
+        // 同步复制文件
+        fs.copyFileSync(source, destination);
+        // logger.info("成功");
+        return true;
+    } catch (err) {
+        return false;
+        // logger.info("失败");
+    }
+}
+
+// 读取日志(倒叙)指定行数
+function readLastNLines(filePath, n) {
+    const content = fs.readFileSync(filePath, 'utf8');
+    const lines = content.trim().split('\n');
+    const lastNLines = lines.slice(-n).join('\n');
+
+    // 此处处理或输出最后n行的内容
+    // console.log(lastNLines);
+    return lastNLines.split("\n").reverse();
+}
+
+
+// 下载文件
+function dowFile(url, fileName, dowPath, headers) {
+    return http.dowFile(url, fileName, dowPath, headers)
+}
+
+// 删除文件
+function delFile() {
+
+}
+
+
+module.exports = {
+    dowFile,
+    checkFilePath,
+    checkPathSync,
+    writeFile,
+    copyFileSync,
+    readLastNLines
+}

+ 149 - 0
src/utils/http.js

@@ -0,0 +1,149 @@
+// 发送GET请求
+const {logger} = require("./logger");
+const http = require('http');
+const https = require('https');
+const fs = require("fs");
+
+
+//公共的请求头
+function getHeaders(headers, body) {
+    if (body.length !== 0) {
+        headers['Content-Length'] = Buffer.byteLength(body)
+    }
+    headers['Content-Type'] = 'application/json'
+    return headers
+}
+
+
+function request_get(url, headers) {
+    return new Promise((resolve, reject) => {
+        var callback = function (res) {
+            let data = '';
+
+            // 监听数据事件来收集响应数据
+            res.on('data', (chunk) => {
+                data += chunk;
+            });
+
+            // 监听结束事件来处理响应结束
+            res.on('end', () => {
+                // logger.info(`响应体: ${data}`);
+                resolve(data);
+            });
+        }
+        var errorback = function errorback(e) {
+            // logger.error(`请求遇到问题: ${e.message}`);
+            reject(e);
+        }
+        // POST 请求的选项
+        const options = {
+            method: 'GET',
+            headers: getHeaders(headers, "")
+        };
+
+        let httpsReq = null
+        if (!url.includes("https")) {
+            httpsReq = http.request(url, options, callback).on('error', errorback);
+        } else {
+            httpsReq = https.request(url, options, callback).on('error', errorback);
+        }
+        // 不要忘了关闭请求,以防止内存泄漏
+        httpsReq.end();
+    });
+}
+
+function request_post(url, param, headers) {
+    return new Promise((resolve, reject) => {
+        var callback = function (res) {
+            let data = '';
+
+            // 监听数据事件来收集响应数据
+            res.on('data', (chunk) => {
+                data += chunk;
+            });
+
+            // 监听结束事件来处理响应结束
+            res.on('end', () => {
+                // logger.info(`响应体: ${data}`);
+                resolve(data);
+            });
+        }
+        var errorback = function errorback(e) {
+            // logger.error(`请求遇到问题: ${e.message}`);
+            reject(e);
+        }
+
+        // POST 请求的数据
+        const postData = JSON.stringify(param);
+        // logger.info('请求参数:', postData, headers);
+        // POST 请求的选项
+        // logger.info('请求头:', headers);
+        const options = {
+            method: 'POST',
+            headers: getHeaders(headers, postData)
+        };
+
+        var postReq = null;
+        if (!url.includes("https")) {
+            postReq = http.request(url, options, callback).on('error', errorback);
+        } else {
+            postReq = https.request(url, options, callback).on('error', errorback);
+        }
+        // 写入数据到请求体
+        postReq.write(postData);
+        // 结束请求
+        postReq.end();
+    })
+}
+
+
+function dowFile(url, fileName, dowPath, headers) {
+
+    return new Promise((resolve, reject) => {
+        // 设定您想要下载的宝藏(文件)的位置
+        const fileUrl = url + "/" + fileName;
+
+        // 指定要将下载的宝藏存放在何处(本地路径)
+        const filePath = dowPath + "/" + fileName;
+
+        // 创建一个能写入宝藏的空箱子(写入流)
+        const fileStream = fs.createWriteStream(filePath);
+        headers = getHeaders(headers, "")
+        logger.info('headers:!', headers, fileUrl);
+        headers["User-Agent"] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
+        const options = {
+            headers: headers
+        };
+
+        http.get(fileUrl, options, (response) => {
+            // 将收到的宝藏(响应流)流入箱子(文件流)中
+            response.pipe(fileStream);
+
+            // 等待宝藏完全转移完毕
+            fileStream.on('finish', () => {
+                // 现在宝藏已经安全地放在箱子里(文件写入完成)
+                fileStream.close();
+                logger.info('文件下载完成,船长!');
+                resolve(true)
+            });
+        }).on('error', (err) => {
+            // 如果在转移宝藏过程中遇到海怪(错误)...
+            logger.error('在下载文件时遇到了问题:', err);
+            // 别忘了关闭箱子(文件流)
+            fileStream.close();
+            // 如果宝藏已经损坏(文件已经部分写入),丢弃它(删除文件)
+            if (fileStream.bytesWritten === 0) {
+                fs.unlink(filePath, (unlinkErr) => {
+                    if (unlinkErr) logger.error('无法删除不完整的文件:', unlinkErr);
+                });
+            }
+            reject(err)
+        });
+    });
+}
+
+module.exports = {
+    request_get,
+    request_post,
+    dowFile
+};

+ 50 - 0
src/utils/logger.js

@@ -0,0 +1,50 @@
+const log4js = require('log4js')
+// 配置 log4js
+log4js.configure({
+    //输出格式
+    appenders: {
+        nodeFile: {
+            type: 'dateFile',
+            filename: 'logs/node/application.log', // 指定日志文件的路径和基本名称
+            pattern: '.yyyy-MM-dd', // 文件名后缀的日期格式
+            compress: true, // 当日志文件滚动时,压缩(归档)旧文件
+            numBackups: 30, // 保留30个备份文件
+            keepFileExt: true, // 保持文件扩展名
+            layout: {
+                type: 'pattern',
+                pattern: '[%d] [%p] %c - %m', // 自定义日志格式
+            }
+        },
+        console: {
+            type: 'console',
+            layout: {
+                type: 'pattern',
+                pattern: '[%d] [%p] %c - %m', // 自定义日志格式
+            }
+        }
+    },
+    //TRACE>DEBUG>INFO >WARN>ERROR>FATAL\
+    //default 代表默认的日志对象 : log4js.getLogger();
+    //日志对象
+    categories: {
+        //控制台输出对象
+        default: {
+            appenders: ['console','nodeFile'],
+            level: 'TRACE'
+        },
+        // //文件输出对象
+        // infoFile: {
+        //     appenders: ['fileAppender'],
+        //     level: 'DEBUG'
+        // }
+    }
+});
+
+
+// 获取默认logger(控制台)
+const logger = log4js.getLogger();
+// 获取记录info级别日志到文件的logger
+// const fileLogger = log4js.getLogger('infoFile');
+
+
+module.exports = {logger};

+ 15 - 0
src/utils/node.json

@@ -0,0 +1,15 @@
+{
+  "rustConfig": {
+    "baseUrl": "http://cc.skyfffire.com/api",
+    "rustUrl": "http://127.0.0.1:5555",
+    "dowAppURL": "/report/downloadFile",
+    "dowConfigURL": "/report/getRobotConfig",
+    "winPath": "C:/Users/hl/Desktop/node_config",
+    "linuxPath": "/4l/rust",
+    "headers": {
+      "auth": "4L",
+      "token": "r7T$8gBV!f&L@E2+"
+    },
+    "logPath": "./logs5555"
+  }
+}

+ 81 - 0
src/web.js

@@ -0,0 +1,81 @@
+const express = require('express');
+const robot = require('./robot')
+const config = require('./config')
+const file = require('./utils/file')
+const { logger, fileLogger }  = require("./utils/logger");
+const {spawn, exec, fork, execSync} = require("child_process");
+const path = require("path");
+
+/*******
+ * web
+ * **** */
+
+function init() {
+    const app = express();
+    const port = 3000;
+    // 中间件示例,用于处理JSON请求体
+    app.use(express.json());
+
+    // GET请求的路由示例
+    app.get('/', (req, res) => {
+        res.send('is OK!');
+    });
+
+
+    //简单三个接口,为了方便使用get请求
+    app.get('/isOK', isOK);
+    app.get('/logs', (req, res) => {
+        // 获取发送过来的信息(请求体的数据)
+        const param = req.query;
+        const n = param.n
+        const id = param.id
+
+        //时间
+        const today = new Date();
+        const year = today.getFullYear(); // 获取年份
+        const month = (today.getMonth() + 1).toString().padStart(2, '0');
+        const day = today.getDate().toString().padStart(2, '0');
+        // 拼接字符串得到 YYYY-MM-DD 格式
+        var logName = `${year}-${month}-${day}`;
+
+        var f = config.getRustConfig().logPath + "/"+logName+".log"
+        const directoryPath = path.resolve(f);
+
+        var array = file.readLastNLines(directoryPath, n)
+        res.send({'code': 200, 'data': array, "message": "SUCCESS"});
+    });
+    app.post('/execute', (req, res) => {
+        // 获取发送过来的信息(请求体的数据)
+        const param = req.body;
+        (async (param) => {
+            console.log('请求体:', param);
+            var executeType = param.executeType
+            if (executeType === "RUN") {
+                await robot.run(param)
+            } else if (executeType === "STOP") {
+                await robot.closeApp(param)
+            } else if (executeType === "RESTART") {
+                await robot.run(param)
+            }
+        })(param);
+        // 回应信号(响应请求)
+        res.send({'code': 200, 'data': "null", "message": "SUCCESS"});
+    })
+
+    // 使服务器监听特定端口
+    app.listen(port, () => {
+        // console.log(`服务器正在监听 http://localhost:${port}`);
+    });
+
+    logger.info('--web 启动');
+}
+
+
+function isOK(req, res) {
+    res.send({'code': 200, 'data': "null", "message": "SUCCESS"});
+}
+
+// 日志读取操作
+module.exports = {
+    init
+};

+ 0 - 6
utils/logger.js

@@ -1,6 +0,0 @@
-const log4js = require('log4js')
-const logger = log4js.getLogger()
-
-logger.level = "debug";
-
-module.exports = logger