diff --git a/Dockerfile b/Dockerfile index c2bebc1..3bfc742 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM node:16 WORKDIR /ffs/ COPY package.json ./ -RUN npm install +RUN npm install --only=prod COPY /src/build ./ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 1f1d65e..87c052f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.8" services: - auth_server: + server: build: context: . dockerfile: Dockerfile @@ -9,54 +9,55 @@ services: volumes: - ./src/build:/ffs/ - /ffs/node_modules + - ./FileShare:/ffs/FileShare/ ports: - ${PORT}:${PORT} command: "node server.js" - task_runner_gantry: - build: - context: . - dockerfile: Dockerfile - env_file: ./.env - volumes: - - ./src/build:/ffs/ - - /ffs/node_modules - - ${LOCAL_DIR_GANTRY}:/ffs/FileShare/Gantry - network_mode: host - command: "node taskRunner.js gantry" + # task_runner_gantry: + # build: + # context: . + # dockerfile: Dockerfile + # env_file: ./.env + # volumes: + # - ./src/build:/ffs/ + # - /ffs/node_modules + # - ${LOCAL_DIR_GANTRY}:/ffs/FileShare/Gantry + # network_mode: host + # command: "node taskRunner.js gantry" + # + # task_runner_lathe: + # build: + # context: . + # dockerfile: Dockerfile + # env_file: ./.env + # volumes: + # - ./src/build:/ffs/ + # - /ffs/node_modules + # - ${LOCAL_DIR_LATHE}:/ffs/FileShare/Lathe + # network_mode: host + # command: "node taskRunner.js lathe" - task_runner_lathe: - build: - context: . - dockerfile: Dockerfile - env_file: ./.env - volumes: - - ./src/build:/ffs/ - - /ffs/node_modules - - ${LOCAL_DIR_LATHE}:/ffs/FileShare/Lathe - network_mode: host - command: "node taskRunner.js lathe" + # task_runner_mill: + # build: + # context: . + # dockerfile: Dockerfile + # env_file: ./.env + # volumes: + # - ./src/build:/ffs/ + # - /ffs/node_modules + # - ${LOCAL_DIR_MILL}:/ffs/FileShare/Mill + # network_mode: host + # command: "node taskRunner.js mill" - task_runner_mill: - build: - context: . - dockerfile: Dockerfile - env_file: ./.env - volumes: - - ./src/build:/ffs/ - - /ffs/node_modules - - ${LOCAL_DIR_MILL}:/ffs/FileShare/Mill - network_mode: host - command: "node taskRunner.js mill" - - task_runner_waterjet: - build: - context: . - dockerfile: Dockerfile - env_file: ./.env - volumes: - - ./src/build:/ffs/ - - /ffs/node_modules - - ${LOCAL_DIR_WATERJET}:/ffs/FileShare/Waterjet - network_mode: host - command: "node taskRunner.js waterjet" \ No newline at end of file + # task_runner_waterjet: + # build: + # context: . + # dockerfile: Dockerfile + # env_file: ./.env + # volumes: + # - ./src/build:/ffs/ + # - /ffs/node_modules + # - ${LOCAL_DIR_WATERJET}:/ffs/FileShare/Waterjet + # network_mode: host + # command: "node taskRunner.js waterjet" \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3f1ddbb..e0c5115 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,9 @@ "license": "ISC", "dependencies": { "axios": "^0.24.0", + "body-parser": "^1.19.1", "cookie-session": "^1.4.0", + "crypto": "^1.0.1", "dotenv": "^10.0.0", "express": "^4.17.1", "forge-apis": "^0.8.6" @@ -25,6 +27,7 @@ "eslint-plugin-import": "^2.25.3", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.1.1", + "opener": "^1.5.2", "typescript": "^4.5.2" } }, @@ -515,20 +518,20 @@ } }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", "dependencies": { - "bytes": "3.1.0", + "bytes": "3.1.1", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", - "http-errors": "1.7.2", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" }, "engines": { "node": ">= 0.8" @@ -542,6 +545,50 @@ "node": ">= 0.6" } }, + "node_modules/body-parser/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser/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/body-parser/node_modules/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/body-parser/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/body-parser/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" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -553,9 +600,9 @@ } }, "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", "engines": { "node": ">= 0.8" } @@ -711,6 +758,12 @@ "node": ">= 8" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -1355,6 +1408,34 @@ "node": ">= 0.10.0" } }, + "node_modules/express/node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/express/node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1363,6 +1444,20 @@ "node": ">= 0.6" } }, + "node_modules/express/node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -1453,9 +1548,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "funding": [ { "type": "individual", @@ -2335,6 +2430,15 @@ "wrappy": "1" } }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -2520,12 +2624,12 @@ } }, "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.1", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -2533,6 +2637,47 @@ "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body/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/raw-body/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/raw-body/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" + } + }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -3564,26 +3709,58 @@ } }, "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", "requires": { - "bytes": "3.1.0", + "bytes": "3.1.1", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", - "http-errors": "1.7.2", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" }, "dependencies": { "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" } } }, @@ -3598,9 +3775,9 @@ } }, "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" }, "call-bind": { "version": "1.0.2", @@ -3720,6 +3897,11 @@ "which": "^2.0.1" } }, + "crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -4214,10 +4396,43 @@ "vary": "~1.1.2" }, "dependencies": { + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } } } }, @@ -4296,9 +4511,9 @@ "dev": true }, "follow-redirects": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", - "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==" }, "forever-agent": { "version": "0.6.1", @@ -4927,6 +5142,12 @@ "wrappy": "1" } }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -5064,14 +5285,48 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.1", + "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + } } }, "regexpp": { diff --git a/package.json b/package.json index cc46461..fb712d2 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,12 @@ "version": "1.0.0", "main": "/src/server.js", "scripts": { - "start": "node /src/server.js", + "start:docker": "docker-compose up", + "start:ngrok": "ngrok http 3000", "test": "echo \"Error: no test specified\" && exit 1", - "tsc": "tsc" + "build": "npm run open:page && npm run build:docker", + "build:docker": "node src/ngrok_script.js && tsc --build && docker-compose up --build --remove-orphans", + "open:page": "opener http://localhost:3000/auth" }, "repository": { "type": "git", @@ -16,7 +19,9 @@ "license": "ISC", "dependencies": { "axios": "^0.24.0", + "body-parser": "^1.19.1", "cookie-session": "^1.4.0", + "crypto": "^1.0.1", "dotenv": "^10.0.0", "express": "^4.17.1", "forge-apis": "^0.8.6" @@ -31,6 +36,7 @@ "eslint-plugin-import": "^2.25.3", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.1.1", + "opener": "^1.5.2", "typescript": "^4.5.2" } } diff --git a/src/config.ts b/src/config.ts index 51e3beb..19eb40f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -45,7 +45,17 @@ export const folderMap: any = { } }; +// dictionary for looking up local directory path based on folder ID +export const folderIDtoLocal: any = { + "urn:adsk.wipprod:fs.folder:co.IxiedfeBTOGg6NSIGQhXxQ" : "/Gantry", + "urn:adsk.wipprod:fs.folder:co.FDWCeyBGQX2rv8xSNWo5lg": "/Lathe", + "urn:adsk.wipprod:fs.folder:co.nEihcpHUSW-ZsVzU-__1iw": "/Mill", + "urn:adsk.wipprod:fs.folder:co.vJLXAKGbQQayeljr-nwztQ": "/Waterjet" +} + export const port = process.env.PORT; export const forgeClientId = process.env.FORGE_CLIENT_ID; export const forgeClientSecret = process.env.FORGE_CLIENT_SECRET; -export const forgeCallbackURL = process.env.FORGE_CALLBACK_URL; \ No newline at end of file +export const forgeCallbackURL = process.env.FORGE_CALLBACK_URL; +export const hookCallbackURL = `${process.env.HOOK_CALLBACK_HOSTNAME}/hook`; +export const webhookToken: any = process.env.WEBHOOK_TOKEN; \ No newline at end of file diff --git a/src/downloader.ts b/src/downloader.ts index 144a167..45e80fb 100644 --- a/src/downloader.ts +++ b/src/downloader.ts @@ -1,4 +1,5 @@ const fs = require('fs').promises; +const axios = require('axios'); const ForgeSDK = require('forge-apis'); const config = require('./config'); @@ -14,11 +15,14 @@ const ObjectsApi = new ForgeSDK.ObjectsApi(); * @param {string} destination * @param {Object} credentials */ -exports.download = (storageLocation: string, fileName: string, destination: string, credentials: any) => { +exports.download = async (projectID: string, itemID: string, fileName: string, destination: string, credentials: any) => { + const storageLocation = await getStorageLocation(projectID, itemID, credentials); + + console.log("Downloading file "+ fileName); ObjectsApi.getObject('wip.dm.prod', storageLocation, {}, config.authClient, credentials) .then((res: any) => { - console.log(res) + console.log("Downloaded file " + fileName + " to " + destination); fs.writeFile(`${destination}/${fileName}`, res.body) }) .catch((err: any) => { @@ -27,3 +31,21 @@ exports.download = (storageLocation: string, fileName: string, destination: stri }); } +const getStorageLocation = (projectID: string, itemID: string, credentials: any) => { + return axios({ + method: 'GET', + url: `https://developer.api.autodesk.com/data/v1/projects/${projectID}/items/${itemID}`, + headers: { + Authorization: `Bearer ${credentials.access_token}` + }, + }).then((res: any) => { + return res.data.included[0]; + }).then((data: any) => { + var storageID = data.relationships.storage.data.id; + storageID = storageID.substring(storageID.indexOf('/') + 1); + return storageID; + }).catch((err: any) => { + console.log("unable to get storage location for " + itemID + ": " + err.response.status); + }) +} + diff --git a/src/ngrok_script.js b/src/ngrok_script.js new file mode 100644 index 0000000..2650b50 --- /dev/null +++ b/src/ngrok_script.js @@ -0,0 +1,25 @@ +const axios = require('axios'); +const fs = require('fs'); + +axios({ + method: 'GET', + url: 'http://127.0.0.1:4040/api/tunnels' +}).then(res => { + return res.data.tunnels; +}).then(data => { + data.forEach((entry) => { + if (entry.name === 'command_line (http)') { + const data = fs.readFileSync(`${__dirname}/../.env`).toString(); + if (data.includes(entry.public_url)) { + console.log("ngrok callback url not changed"); + return; + } + const newData = data.replace(new RegExp(/http:\/\/.+\.ngrok\.io/g), entry.public_url); + fs.writeFileSync(`${__dirname}/../.env`, newData); + console.log("updated ngrok callback url"); + } + }) +}).catch(err => { + console.log(); + throw("ngrok server not started"); +}); \ No newline at end of file diff --git a/src/taskRunner.ts b/src/old/taskRunner.ts similarity index 95% rename from src/taskRunner.ts rename to src/old/taskRunner.ts index 1e4b9ec..0a84cb5 100644 --- a/src/taskRunner.ts +++ b/src/old/taskRunner.ts @@ -2,7 +2,7 @@ const axios = require('axios'); const watcher = require('./watcher'); const downloader = require('./downloader'); const fs = require('fs'); -import * as config from './config'; +import * as config from '../config'; /** @@ -68,6 +68,8 @@ const fetchToken = () => { '\x1b[92mtaskRunner.js::fetchToken:', '\x1b[0mCredentials received' ); + // watcher2.createHook(credentials); + // watcher2.setup(credentials); } else { console.log( '\x1b[96mtaskRunner.js::fetchToken:', @@ -100,6 +102,11 @@ const getCredentials = () => { updateInterval = setInterval(fetchToken, 3300 * 1000); }; +getCredentials(); + + +// Below is the previous watcher implementation + /** * Function to dispatch the watcher */ @@ -124,8 +131,6 @@ const dispatchWatcher = async (interval: any) => { } }; -getCredentials(); - // There should be a buffer between how far back the watcher checks // and how often it runs. We may need to test this out. setInterval(dispatchWatcher, 5000, 7000); diff --git a/src/watcher.ts b/src/old/watcher.ts similarity index 98% rename from src/watcher.ts rename to src/old/watcher.ts index 4824607..1e9ed3a 100644 --- a/src/watcher.ts +++ b/src/old/watcher.ts @@ -1,4 +1,4 @@ -const axios = require('axios'); +// const axios = require('axios'); /** * Kevin Pan | pan261@purdue.edu | Last Modified: 12/2/2021 diff --git a/src/server.ts b/src/server.ts index 2aa3001..3d726fb 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,9 +1,14 @@ import express = require('express'); +import bodyParser = require('body-parser'); +import crypto = require('crypto'); import * as config from './config'; +import fs = require('fs'); +const webhooks = require('./webhooks'); +const downloader = require('./downloader'); /** - * Kevin Pan | pan261@purdue.edu | Last Modified: 12/2/2021 + * Kevin Pan | pan261@purdue.edu | Last Modified: 1/24/2022 * * Authentication server used to perform 3-legged auth through browser. * @@ -22,6 +27,25 @@ let refreshTime: any = null; let intervalID: NodeJS.Timeout; const app = express(); +const verifySignature = (req: any, res: any, buf: any, encoding: any) => { + console.log("Verifying webhook callback signature"); + const signature = req.header('x-adsk-signature'); + if(!signature) { return; } + + // use utf-8 encoding by default + const body = buf.toString(encoding); + const hmac = crypto.createHmac('sha1', config.webhookToken); + const calcSignature = 'sha1hash=' + hmac.update(body).digest('hex'); + req.signature_match = (calcSignature === signature); +} + +app.use(bodyParser.json({ + inflate: true, + limit: '1024kb', + type: 'application/json', + verify: verifySignature +})); + /** * Creates server with three endpoints: * 1. /auth @@ -63,6 +87,12 @@ const createServer = () => { ); }) .then(() => { + // run the setup function for webhooks + webhooks.setupHooks(credentials); + + // register secret token + webhooks.setupToken(credentials); + // sets refresh() function to run on an interval every 55 minutes intervalID = setInterval(() => refresh(), refreshTime * 1000); // 55 seconds to ms @@ -99,6 +129,26 @@ const createServer = () => { } }); + app.post('/hook', function (req: any, res: any) { + if(!req.signature_match) { + console.log('Request received from outside webhooks service') + return res.status(403).send('Not called from webhooks service'); + } + + res.status(204).send(); + + let body = req.body; + + if (credentials && body.hook.event === "dm.version.added") { + const itemID = body.payload.lineageUrn; + //TODO: check if extension is in the name + const fileName = body.payload.name; + const destination = __dirname + '/FileShare' + config.folderIDtoLocal[body.payload.parentFolderUrn]; + + downloader.download(config.projectID, itemID, fileName, destination, credentials); + } + }); + // Default endpoint app.use((err: any, req: any, res: any, next: any) => { console.error(err); @@ -122,6 +172,9 @@ const refresh = () => { '\x1b[0mnew token generated from refresh token:' ); console.log(credentials); + + // check on webhooks + webhooks.setupHooks(credentials); }) .catch(err => { console.log(err); diff --git a/src/webhooks.ts b/src/webhooks.ts new file mode 100644 index 0000000..b847c82 --- /dev/null +++ b/src/webhooks.ts @@ -0,0 +1,164 @@ +const axios = require('axios'); +import * as config from './config'; + + +/** + * Kevin Pan | pan261@purdue.edu | Last Modified: 1/24/2022 + * + * Functions to manage hook lifecycle + * setup() checks if hooks are valid and active, if not it will reactivated + * and/or recreate the specified hook. There is one hook for each of the + * following events: + * + * - 'dm.version.added' (file added) + * - 'dm.version.modified' (file modified) + * - dm.version.deleted (file deleted) + * + */ + +const FileShareID = "urn:adsk.wipprod:fs.folder:co.T0n0mYQeS16K0lq1VuuYVQ" + +/** + * Function to manage hook lifecycle + */ +exports.setupHooks = (credentials: any) => { + console.log("checking hooks"); + axios({ + method: 'GET', + url: 'https://developer.api.autodesk.com/webhooks/v1/hooks', + headers: { + Authorization: `Bearer ${credentials.access_token}` + } + }).then((res: any) => { + return res.data.data; + }).then((data: any) => { + var hookEvents = []; + for (var index in data) { + + const hook = data[index]; + + if (hook.callbackUrl != config.hookCallbackURL) { // hooks with invalid callbacks will be deleted, not added to current list of hooks + deleteHook(hook.event, hook.hookId, credentials); + } else { + hookEvents.push(hook.event); + if (hook.status === 'inactive') { + console.log("reactivating " + hook.event + " hook"); + reactivateHook(hook.event, hook.hookId, credentials); + } + } + } + + // these are the events we want, if not in the list, then register the hook + if (!hookEvents.includes('dm.version.added')) { + createHook('dm.version.added', credentials); + } + + if (!hookEvents.includes('dm.version.modified')) { + createHook('dm.version.modified', credentials); + } + + if (!hookEvents.includes('dm.version.deleted')) { + createHook('dm.version.deleted', credentials); + } + }).catch((err: any) => { + console.log(err); + }) +} + + +const createHook = (event: string, credentials: any) => { + axios({ + method: 'POST', + url: `https://developer.api.autodesk.com/webhooks/v1/systems/data/events/${event}/hooks`, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${credentials.access_token}` + }, + data: { + autoReactivateHook: "true", + callbackUrl: config.hookCallbackURL, + scope: { + folder: FileShareID + } + } + }).then((res: any) => { + console.log("sucessfully created hook for " + event); + }).catch((err: any) => { + console.log("error creating hook for " + event + ": " + err.response.status); + }); +} + +const reactivateHook = (event: string, hookID: string, credentials: any) => { + axios({ + method: 'PATCH', + url: `https://developer.api.autodesk.com/webhooks/v1/systems/data/events/${event}/hooks/${hookID}`, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${credentials.access_token}` + }, + data: { + status: 'active', + autoReactivateHook: true, + } + }).then((res: any) => { + console.log("successfully reactivated " + event + " hook"); + }).catch((err: any) => { + console.log("error reactivating hook for " + event + ": " + err.response.status); + }); +} + +const deleteHook = (event:string, hookID: string, credentials: any) => { + axios({ + method: 'DELETE', + url: `https://developer.api.autodesk.com/webhooks/v1/systems/data/events/${event}/hooks/${hookID}`, + headers: { + Authorization: `Bearer ${credentials.access_token}` + } + }).then((res: any) => { + console.log("deleted " + event + " hook with bad callback url"); + }).catch((err: any) => { + console.log("error deleting hook for " + event + ": " + err.response.status); + }); +} + +exports.setupToken = (credentials: any) => { + console.log("Checking webhook token") + axios({ + method: 'POST', + url: 'https://developer.api.autodesk.com/webhooks/v1/tokens', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${credentials.access_token}` + }, + data: { + token: config.webhookToken + } + }).then((res: any) => { + console.log(res.data.detail); + }).catch((err: any) => { + if (err.response.status == 400) { + console.log("Webhook token already exists") + } else { + console.log("Error creating a new webhook token"); + } + }); +} + +// not sure how to incorporate this into webhook token workflow +const updateToken = (credentials: any) => { + axios({ + method: 'PUT', + url: 'https://developer.api.autodesk.com/webhooks/v1/tokens/@me', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${credentials.access_token}` + }, + data: { + token: config.webhookToken + } + }).then((res: any) => { + console.log("Successfully updated webhook token"); + }).catch((err: any) => { + console.log("Error updating webhook token"); + }); +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 0b315b7..7f1a7ff 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,7 +25,7 @@ /* Modules */ "module": "commonjs", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ + "rootDir": "./src", /* Specify the root folder within your source files. */ // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ @@ -99,5 +99,8 @@ "skipLibCheck": true, /* Skip type checking all .d.ts files. */ "outDir": "./src/build", "sourceMap": true - } + }, + "exclude": [ + "./src/old" + ] }