diff --git a/oldCode/LINEWORKS_Pleasanter/package-lock.json b/oldCode/LINEWORKS_Pleasanter/package-lock.json new file mode 100644 index 00000000..e77faa4c --- /dev/null +++ b/oldCode/LINEWORKS_Pleasanter/package-lock.json @@ -0,0 +1,836 @@ +{ + "name": "LINEWORKS_Pleasanter", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "body-parser": "^2.2.0", + "cors": "^2.8.5", + "express": "^5.1.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "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==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.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==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "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==", + "license": "MIT", + "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.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "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==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "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==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "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" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.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==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + } + } +} diff --git a/oldCode/LINEWORKS_Pleasanter/package.json b/oldCode/LINEWORKS_Pleasanter/package.json new file mode 100644 index 00000000..392f6b6c --- /dev/null +++ b/oldCode/LINEWORKS_Pleasanter/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "body-parser": "^2.2.0", + "cors": "^2.8.5", + "express": "^5.1.0" + } +} diff --git a/oldCode/LINEWORKS_Pleasanter/server.js b/oldCode/LINEWORKS_Pleasanter/server.js new file mode 100644 index 00000000..475b235a --- /dev/null +++ b/oldCode/LINEWORKS_Pleasanter/server.js @@ -0,0 +1,45 @@ +const express = require('express'); +const cors = require('cors'); +const https = require('https'); +const fs = require('fs'); +const API_KEY = 'pgqhLFWbDFu4Byz#4afNYX2F6Fa1&$KPjved$8%sUdTQV52caip#EpIKxYUkdd4S'; // 任意のAPIキーを設定 + +const app = express(); +app.use(cors({ + origin: [ + 'https://neo999.next-hd.net', + 'https://nextoffice.next-hd.co.jp', + ], // 許可するオリジン + optionsSuccessStatus: 200 +})); + +// APIエンドポイント +app.post('/server', (req, res) => { + let rawBody = ''; + req.on('data', (chunk) => { + rawBody += chunk; // リクエストデータをそのまま取得 + }); + + req.on('end', () => { + // APIキーの検証 + const apiKey = req.headers['x-api-key']; + if (!apiKey || apiKey !== API_KEY) { + return res.status(401).json({ error: 'APIキーが不正です' }); + } + + console.log('Headers:', req.headers); // ヘッダーを出力 + console.log('Raw Body:', rawBody); // パースせずにそのまま出力 + res.json({ message: 'リクエストを受け付けました', rawBody }); + }); +}); + +// HTTPSサーバー起動 +const PORT = 30399; +const sslOptions = { + cert: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/fullchain.pem'), + key: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/privkey.pem') +}; + +https.createServer(sslOptions, app).listen(PORT, () => { + console.log(`APIサーバー起動: https://neo999.next-hd.net:${PORT}/server`); +}); diff --git a/oldCode/LINEWORKS_sendMessage/LINEEWORKSにメッセージを送るサンプルスクリプト.js.txt b/oldCode/LINEWORKS_sendMessage/LINEEWORKSにメッセージを送るサンプルスクリプト.js.txt new file mode 100644 index 00000000..fb342751 --- /dev/null +++ b/oldCode/LINEWORKS_sendMessage/LINEEWORKSにメッセージを送るサンプルスクリプト.js.txt @@ -0,0 +1,36 @@ +//lwSendMessage.jsにAPIリクエストを送信するクライアントサンプル +//node実行する場合は下記が必要 +const fetch = require('node-fetch'); + +//ブラウザスクリプトにする場合は下記から +//API-KEYは下記固定 +const API_KEY = 'pgqhLFWbDFu4Byz#4afNYX2F6Fa1&$KPjved$8%sUdTQV52caip#EpIKxYUkdd4S'; // 任意のAPIキーを設定 + +//CORSには本番用・開発用の両方のプリザンターアドレスを登録してあるので +//どちらの環境からでも実行可能 +async function sendMessageToLineworks() { + const apiUrl = 'https://neo999.next-hd.net:30309/lwSendMessage'; + const body = { + botId: '10130468', //新しいBotを作ったらここでIDを変更 + userId: 'k.nogi@works-nextgroup2', + messageText: 'テストメッセージ' + }; + + const response = await fetch(apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': API_KEY // ここにAPIキーを追加 + }, + body: JSON.stringify(body) + }); + + if (response.ok) { + const result = await response.json(); + console.log('APIレスポンス:', result); + } else { + console.error('APIリクエスト失敗:', await response.text()); + } +} + +sendMessageToLineworks(); \ No newline at end of file diff --git a/oldCode/LINEWORKS_sendMessage/lineworksCore.js b/oldCode/LINEWORKS_sendMessage/lineworksCore.js new file mode 100644 index 00000000..7bd73ec7 --- /dev/null +++ b/oldCode/LINEWORKS_sendMessage/lineworksCore.js @@ -0,0 +1,72 @@ +// +exports.sendLineworksMessage = async (botId, userId, messageText) => { + //アクセストークンの取得 + const accessToken = await getAccessToken(); + if (!accessToken) return; + + const apiUrl = `https://www.worksapis.com/v1.0/bots/${botId}/users/${userId}/messages`; + + const body = { + 'content': { + 'type': 'text', + 'text': messageText + } + }; + + const response = await fetch(apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json;charset=UTF-8', + 'Authorization': `Bearer ${accessToken}` + }, + body: JSON.stringify(body) + }); + + if (response.ok) { + console.log('送信成功'); + } else { + console.error('送信失敗', await response.text()); + } +}; + +// アクセストークン取得 +async function getAccessToken() { + const clientId = 'V58hNobcAqRcQPerySMz'; + const clientSecret = 'gJ84XQtigD'; + const serviceAccount = 'x94xh.serviceaccount@works-nextgroup2'; + const privateKey = fs.readFileSync('./private_20250530180606.key', 'utf8'); + const apiUrl = 'https://auth.worksmobile.com/oauth2/v2.0/token'; + const now = Math.floor(Date.now() / 1000); + + // JWT生成 + const payload = { + 'iss': clientId, + 'sub': serviceAccount, + 'iat': now, + 'exp': now + 60 * 5, + 'aud': apiUrl + }; + const assertion = jwt.sign(payload, privateKey, { 'algorithm': 'RS256' }); + + const body = new URLSearchParams({ + 'assertion': assertion, + 'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', + 'client_id': clientId, + 'client_secret': clientSecret, + 'scope': 'bot' + }); + + const response = await fetch(apiUrl, { + 'method': 'POST', + 'headers': { 'Content-Type': 'application/x-www-form-urlencoded' }, + 'body': body.toString() + }); + + if (response.ok) { + const data = await response.json(); + return data.access_token; + } else { + console.error('アクセストークン取得失敗:', await response.text()); + return null; + } +} \ No newline at end of file diff --git a/oldCode/LINEWORKS_sendMessage/lwSendMessageBot.js b/oldCode/LINEWORKS_sendMessage/lwSendMessageBot.js new file mode 100644 index 00000000..e412788c --- /dev/null +++ b/oldCode/LINEWORKS_sendMessage/lwSendMessageBot.js @@ -0,0 +1,142 @@ +// Node.jsでLINE WORKSのBot APIを使って特定ユーザーにメッセージを送信するサンプル(HTTPS対応) +const express = require('express'); +const bodyParser = require('body-parser'); +const fetch = require('node-fetch'); +const jwt = require('jsonwebtoken'); +const fs = require('fs'); +const https = require('https'); +const cors = require('cors'); // 追加 + +const app = express(); +app.use(cors({ + origin: [ + 'https://neo999.next-hd.net', + 'https://nextoffice.next-hd.co.jp', + ], // 許可するオリジン + optionsSuccessStatus: 200 +})); +//app.use(bodyParser.json()); + +const API_KEY = 'pgqhLFWbDFu4Byz#4afNYX2F6Fa1&$KPjved$8%sUdTQV52caip#EpIKxYUkdd4S'; // 任意のAPIキーを設定 + +// アクセストークン取得 +async function getAccessToken() { + const clientId = 'V58hNobcAqRcQPerySMz'; + const clientSecret = 'gJ84XQtigD'; + const serviceAccount = 'x94xh.serviceaccount@works-nextgroup2'; + const privateKey = fs.readFileSync('./private_20250530180606.key', 'utf8'); + const apiUrl = 'https://auth.worksmobile.com/oauth2/v2.0/token'; + const now = Math.floor(Date.now() / 1000); + + // JWT生成 + const payload = { + 'iss': clientId, + 'sub': serviceAccount, + 'iat': now, + 'exp': now + 60 * 5, + 'aud': apiUrl + }; + const assertion = jwt.sign(payload, privateKey, { 'algorithm': 'RS256' }); + + const body = new URLSearchParams({ + 'assertion': assertion, + 'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', + 'client_id': clientId, + 'client_secret': clientSecret, + 'scope': 'bot' + }); + + const response = await fetch(apiUrl, { + 'method': 'POST', + 'headers': { 'Content-Type': 'application/x-www-form-urlencoded' }, + 'body': body.toString() + }); + + if (response.ok) { + const data = await response.json(); + return data.access_token; + } else { + console.error('アクセストークン取得失敗:', await response.text()); + return null; + } +} + +// メッセージ送信 +async function sendLineworksMessage(botId, userId, messageText) { + const accessToken = await getAccessToken(); + if (!accessToken) return; + + const apiUrl = `https://www.worksapis.com/v1.0/bots/${botId}/users/${userId}/messages`; + + const body = { + 'content': { + 'type': 'text', + 'text': messageText + } + }; + + const response = await fetch(apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json;charset=UTF-8', + 'Authorization': `Bearer ${accessToken}` + }, + body: JSON.stringify(body) + }); + + if (response.ok) { + console.log('送信成功'); + } else { + console.error('送信失敗', await response.text()); + } +} + +// APIエンドポイント +app.post('/lwSendMessage', async (req, res) => { + let rawBody = ''; + req.on('data', (chunk) => { + rawBody += chunk; // リクエストデータをそのまま取得 + }); + + req.on('end', () => { + // APIキーの検証 + const apiKey = req.headers['x-api-key']; + if (!apiKey || apiKey !== API_KEY) { + return res.status(401).json({ error: 'APIキーが不正です' }); + } + + // botIdとuserIdをヘッダーから取得 + const botId = req.headers['x-bot-id']; + const userId = req.headers['x-user-id']; + if (!botId || !userId) { + return res.status(400).json({ error: 'x-bot-id, x-user-idは必須です' }); + } + + // リクエストボディ全体をmessageTextとして使用 + const messageText = rawBody; + console.log('messageText:', messageText); + + if (!messageText) { + return res.status(400).json({ error: 'messageTextは必須です' }); + } + + try { + sendLineworksMessage(botId, userId, messageText); + res.json({ result: 'ok' }); + } catch (e) { + res.status(500).json({ error: e.message }); + } + }); + +}); + +// HTTPSサーバー起動 +const PORT = 30309; +const sslOptions = { + cert: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/fullchain.pem'), + key: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/privkey.pem') +}; + +https.createServer(sslOptions, app).listen(PORT, () => { + console.log(`APIサーバー起動: https://neo999.next-hd.net:${PORT}/lwSendMessage`); +}); diff --git a/oldCode/LINEWORKS_sendMessage/lwSendMessageLogic.js.js b/oldCode/LINEWORKS_sendMessage/lwSendMessageLogic.js.js new file mode 100644 index 00000000..1d0d8788 --- /dev/null +++ b/oldCode/LINEWORKS_sendMessage/lwSendMessageLogic.js.js @@ -0,0 +1,19 @@ +const { sendLineworksMessage } = require('./lineworksCore'); +const API_KEY = 'pgqhLFWbDFu4Byz#4afNYX2F6Fa1&$KPjved$8%sUdTQV52caip#EpIKxYUkdd4S'; + +exports.lwSendMessageHandler = async (req, res) => { + const apiKey = req.headers['x-api-key']; + if (!apiKey || apiKey !== API_KEY) { + return res.status(401).json({ error: 'APIキーが不正です' }); + } + const { botId, userId, messageText } = req.body; + if (!botId || !userId || !messageText) { + return res.status(400).json({ error: 'botId, userId, messageTextは必須です' }); + } + try { + await sendLineworksMessage(botId, userId, messageText); + res.json({ result: 'ok' }); + } catch (e) { + res.status(500).json({ error: e.message }); + } +}; \ No newline at end of file diff --git a/oldCode/LINEWORKS_sendMessage/package-lock.json b/oldCode/LINEWORKS_sendMessage/package-lock.json new file mode 100644 index 00000000..a9d71e67 --- /dev/null +++ b/oldCode/LINEWORKS_sendMessage/package-lock.json @@ -0,0 +1,197 @@ +{ + "name": "lineworks_sendmessage", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lineworks_sendmessage", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "jsonwebtoken": "^9.0.2", + "node-fetch": "^2.7.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "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" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } +} diff --git a/oldCode/LINEWORKS_sendMessage/package.json b/oldCode/LINEWORKS_sendMessage/package.json new file mode 100644 index 00000000..fce37d13 --- /dev/null +++ b/oldCode/LINEWORKS_sendMessage/package.json @@ -0,0 +1,16 @@ +{ + "name": "lineworks_sendmessage", + "version": "1.0.0", + "main": "lwSendMessageBot.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "jsonwebtoken": "^9.0.2", + "node-fetch": "^2.7.0" + } +} \ No newline at end of file diff --git a/oldCode/LINEWORKS_sendMessage/sendMessage.js b/oldCode/LINEWORKS_sendMessage/sendMessage.js new file mode 100644 index 00000000..fb342751 --- /dev/null +++ b/oldCode/LINEWORKS_sendMessage/sendMessage.js @@ -0,0 +1,36 @@ +//lwSendMessage.jsにAPIリクエストを送信するクライアントサンプル +//node実行する場合は下記が必要 +const fetch = require('node-fetch'); + +//ブラウザスクリプトにする場合は下記から +//API-KEYは下記固定 +const API_KEY = 'pgqhLFWbDFu4Byz#4afNYX2F6Fa1&$KPjved$8%sUdTQV52caip#EpIKxYUkdd4S'; // 任意のAPIキーを設定 + +//CORSには本番用・開発用の両方のプリザンターアドレスを登録してあるので +//どちらの環境からでも実行可能 +async function sendMessageToLineworks() { + const apiUrl = 'https://neo999.next-hd.net:30309/lwSendMessage'; + const body = { + botId: '10130468', //新しいBotを作ったらここでIDを変更 + userId: 'k.nogi@works-nextgroup2', + messageText: 'テストメッセージ' + }; + + const response = await fetch(apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': API_KEY // ここにAPIキーを追加 + }, + body: JSON.stringify(body) + }); + + if (response.ok) { + const result = await response.json(); + console.log('APIレスポンス:', result); + } else { + console.error('APIリクエスト失敗:', await response.text()); + } +} + +sendMessageToLineworks(); \ No newline at end of file diff --git a/oldCode/LINEWORKS_sendMessage/server.js b/oldCode/LINEWORKS_sendMessage/server.js new file mode 100644 index 00000000..5d0cfa38 --- /dev/null +++ b/oldCode/LINEWORKS_sendMessage/server.js @@ -0,0 +1,27 @@ +const express = require('express'); +const bodyParser = require('body-parser'); +const cors = require('cors'); +const https = require('https'); +const fs = require('fs'); +const { lwSendMessageHandler } = require('./lwSendMessageLogic'); + +const app = express(); +app.use(cors({ + origin: [ + 'https://neo999.next-hd.net', + 'https://nextoffice.next-hd.co.jp', + ], + optionsSuccessStatus: 200 +})); +app.use(bodyParser.json()); + +app.post('/lwSendMessage', lwSendMessageHandler); + +const sslOptions = { + cert: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/fullchain.pem'), + key: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/privkey.pem') +}; + +https.createServer(sslOptions, app).listen(30309, () => { + console.log('APIサーバー起動: https://neo999.next-hd.net:30309/lwSendMessage'); +}); \ No newline at end of file diff --git a/oldCode/express/.vscode/settings.json b/oldCode/express/.vscode/settings.json new file mode 100644 index 00000000..3b664107 --- /dev/null +++ b/oldCode/express/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "git.ignoreLimitWarning": true +} \ No newline at end of file diff --git a/oldCode/express/Mail.json b/oldCode/express/Mail.json new file mode 100644 index 00000000..b0e9c1f9 --- /dev/null +++ b/oldCode/express/Mail.json @@ -0,0 +1,15 @@ +{ + "SmtpHost": "smtp.sendgrid.net", + "SmtpPort": "0", + "SmtpUserName": "apikey", + "SmtpPassword": "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", + "SmtpEnableSsl": false, + "ServerCertificateValidationCallback": false, + "SecureSocketOptions": null, + "FixedFrom": null, + "AllowedFrom": null, + "SupportFrom": "\"NEXTOFFICE\" ", + "InternalDomains": "", + "Encoding": null, + "ContentEncoding": null +} \ No newline at end of file diff --git a/oldCode/express/config.js b/oldCode/express/config.js new file mode 100644 index 00000000..2f66a3dc --- /dev/null +++ b/oldCode/express/config.js @@ -0,0 +1,20 @@ +const fs = require('fs'); + +module.exports = { + API_KEY: 'pgqhLFWbDFu4Byz#4afNYX2F6Fa1&$KPjved$8%sUdTQV52caip#EpIKxYUkdd4S', + PORT: 30309, + SSL_OPTIONS: { + cert: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/fullchain.pem'), + key: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/privkey.pem') + }, + CORS_ORIGIN: [ + 'https://neo999.next-hd.net', + 'https://nextoffice.next-hd.co.jp' + ], + PLEASANTER: { + // HOST: 'https://neo999.next-hd.net/pleasanter', + HOST: 'https://nextoffice.next-hd.co.jp/pleasanter', + API_KEY: '6504c8a807677a3a576e10327f3c19876c55736ee45d4a845796b9e7f5e087bfd4bd0d8184863da8cf1733ca635111432ab334aea59102b06a96ee6d2c05190d' + }, + LINEWORKS_BOT_SECRET: 'P/06DLKvwTMyn10QffjZv4rU+Mq/Pi' +}; \ No newline at end of file diff --git a/oldCode/express/config.js_old b/oldCode/express/config.js_old new file mode 100644 index 00000000..268d092a --- /dev/null +++ b/oldCode/express/config.js_old @@ -0,0 +1,14 @@ +const fs = require('fs'); + +module.exports = { + API_KEY: 'pgqhLFWbDFu4Byz#4afNYX2F6Fa1&$KPjved$8%sUdTQV52caip#EpIKxYUkdd4S', + PORT: 30309, + SSL_OPTIONS: { + cert: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/fullchain.pem'), + key: fs.readFileSync('/etc/letsencrypt/live/neo999.next-hd.net/privkey.pem') + }, + CORS_ORIGIN: [ + 'https://neo999.next-hd.net', + 'https://nextoffice.next-hd.co.jp' + ] +}; \ No newline at end of file diff --git a/oldCode/express/index copy.js b/oldCode/express/index copy.js new file mode 100644 index 00000000..98c365e7 --- /dev/null +++ b/oldCode/express/index copy.js @@ -0,0 +1,31 @@ +const express = require('express'); +const https = require('https'); +const cors = require('cors'); +const { API_KEY, PORT, SSL_OPTIONS, CORS_ORIGIN } = require('./config.js'); // config.jsから設定をインポート + +// expressとCORSの設定 +const app = express(); +app.use(cors({ + origin: CORS_ORIGIN, // config.jsから読み込み + optionsSuccessStatus: 200 +})); + +// lwSendMessageモジュールの読み込み +require('./modules/lwSendMessage.js')(app); + +// lineworksSurveyモジュールの読み込み +require('./modules/lineworksSurvey.js')(app); + +// pdfConvertモジュールの読み込み +require('./modules/pdfConvert.js')(app); + +// pdfMergeモジュールの読み込み +require('./modules/pdfMerge.js')(app); + +// pdfToTextモジュールの読み込み +require('./modules/pdfToText.js')(app); + +// HTTPSサーバーの起動 +https.createServer(SSL_OPTIONS, app).listen(PORT, () => { + console.log(`HTTPS server is running on https://localhost:${PORT}`); +}); diff --git a/oldCode/express/index.js b/oldCode/express/index.js new file mode 100644 index 00000000..299a1eb8 --- /dev/null +++ b/oldCode/express/index.js @@ -0,0 +1,32 @@ +const express = require('express'); +const http = require('http'); +const cors = require('cors'); +const { API_KEY, PORT, SSL_OPTIONS, CORS_ORIGIN } = require('./config.js'); // config.jsから設定をインポート + +// expressとCORSの設定 +const app = express(); +app.use(cors({ + origin: CORS_ORIGIN, // config.jsから読み込み + optionsSuccessStatus: 200 +})); + +// lwSendMessageモジュールの読み込み +require('./modules/lwSendMessage.js')(app); + +// lineworksSurveyモジュールの読み込み +require('./modules/lineworksSurvey.js')(app); + +// pdfConvertモジュールの読み込み +require('./modules/pdfConvert.js')(app); + +// pdfMergeモジュールの読み込み +require('./modules/pdfMerge.js')(app); + +// pdfToTextモジュールの読み込み +require('./modules/pdfToText.js')(app); + +// HTTPSサーバーの起動 +// const PORT = PORT; +http.createServer(app).listen(PORT, '127.0.0.1', () => { + console.log(`Server running on http://127.0.0.1:${PORT}`); +}); diff --git a/oldCode/express/lookupLineworksUser.js b/oldCode/express/lookupLineworksUser.js new file mode 100644 index 00000000..6e009bbe --- /dev/null +++ b/oldCode/express/lookupLineworksUser.js @@ -0,0 +1,77 @@ +const { getAccessToken } = require('./modules/lineworksAuth'); + +const USER_INFO_SCOPES = ['user.read', 'directory.read']; + +async function main() { + const [, , identifier, domainArg] = process.argv; + if (!identifier) { + console.error('使い方: node lookupLineworksUser.js [domainId]'); + process.exit(1); + } + + const domainId = domainArg || process.env.LINEWORKS_DOMAIN_ID || ''; + const encodedIdentifier = encodeURIComponent(identifier); + + const accessToken = await getAccessToken(USER_INFO_SCOPES); + const baseUrl = `https://www.worksapis.com/v1.0/users/${encodedIdentifier}`; + const apiUrl = domainId ? `${baseUrl}?domainId=${encodeURIComponent(domainId)}` : baseUrl; + + const response = await fetch(apiUrl, { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}` + } + }); + + const text = await response.text(); + let data; + try { + data = JSON.parse(text); + } catch { + console.error('LINE WORKS APIレスポンス解析に失敗しました:', text); + process.exit(1); + } + + if (!response.ok) { + console.error('LINE WORKS APIエラー:', JSON.stringify(data, null, 2)); + process.exit(1); + } + + printUserInfo(data); +} + +function printUserInfo(user) { + console.log('--- User Info ---'); + console.log(`domainId : ${user.domainId}`); + console.log(`userId : ${user.userId}`); + console.log(`userExternalKey : ${user.userExternalKey ?? 'N/A'}`); + console.log(`email : ${user.email}`); + console.log(`name : ${formatName(user.userName)}`); + console.log(`nickName : ${user.nickName ?? 'N/A'}`); + console.log(`employeeNumber : ${user.employeeNumber ?? 'N/A'}`); + console.log(`isAdministrator : ${user.isAdministrator}`); + console.log(`isSuspended : ${user.isSuspended}`); + console.log(`isDeleted : ${user.isDeleted}`); + + if (Array.isArray(user.organizations)) { + console.log('organizations:'); + for (const org of user.organizations) { + console.log(` - domainId : ${org.domainId}, primary: ${org.primary}, email: ${org.email ?? 'N/A'}`); + } + } + + if (Array.isArray(user.aliasEmails) && user.aliasEmails.length > 0) { + console.log(`aliasEmails : ${user.aliasEmails.join(', ')}`); + } +} + +function formatName(userName = {}) { + const last = userName.lastName ?? ''; + const first = userName.firstName ?? ''; + return `${last} ${first}`.trim() || 'N/A'; +} + +main().catch((error) => { + console.error('ユーザー照会でエラーが発生しました:', error.message); + process.exit(1); +}); diff --git a/oldCode/express/modules/lineworksAuth.js b/oldCode/express/modules/lineworksAuth.js new file mode 100644 index 00000000..f2251269 --- /dev/null +++ b/oldCode/express/modules/lineworksAuth.js @@ -0,0 +1,77 @@ +const fs = require('fs'); +const path = require('path'); +const jwt = require('jsonwebtoken'); + +let cachedToken = null; +let cachedExpiry = 0; +let cachedScopeKey = ''; + +const CLIENT_ID = 'tre8J_Tk8RblfsMSyZRh'; +const CLIENT_SECRET = 'O5_R5gcHxg'; +const SERVICE_ACCOUNT = 'wh4k8.serviceaccount@nexthd.jp'; +const TOKEN_ENDPOINT = 'https://auth.worksmobile.com/oauth2/v2.0/token'; +const PRIVATE_KEY_PATH = path.join(__dirname, 'private_20260307184804.key'); +const TOKEN_TTL_BUFFER = 30; // seconds +const DEFAULT_SCOPES = ['bot', 'contact']; + +function readPrivateKey() { + return fs.readFileSync(PRIVATE_KEY_PATH, 'utf8'); +} + +function makeScopeKey(scopes) { + return scopes.slice().sort().join(' '); +} + +function isTokenValid(scopeKey) { + return Boolean(cachedToken) + && cachedScopeKey === scopeKey + && cachedExpiry > Math.floor(Date.now() / 1000) + TOKEN_TTL_BUFFER; +} + +async function getAccessToken(scopes = DEFAULT_SCOPES) { + const normalizedScopes = Array.isArray(scopes) && scopes.length > 0 ? scopes : DEFAULT_SCOPES; + const scopeKey = makeScopeKey(normalizedScopes); + + if (isTokenValid(scopeKey)) { + return cachedToken; + } + + const now = Math.floor(Date.now() / 1000); + const payload = { + iss: CLIENT_ID, + sub: SERVICE_ACCOUNT, + iat: now, + exp: now + 5 * 60, + aud: TOKEN_ENDPOINT + }; + + const assertion = jwt.sign(payload, readPrivateKey(), { algorithm: 'RS256' }); + const body = new URLSearchParams({ + assertion, + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + client_id: CLIENT_ID, + client_secret: CLIENT_SECRET, + scope: normalizedScopes.join(' ') + }); + + const response = await fetch(TOKEN_ENDPOINT, { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: body.toString() + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`LINE WORKSアクセストークン取得失敗: ${errorText}`); + } + + const data = await response.json(); + cachedToken = data.access_token; + cachedExpiry = now + (data.expires_in || 300); + cachedScopeKey = scopeKey; + return cachedToken; +} + +module.exports = { + getAccessToken +}; diff --git a/oldCode/express/modules/lineworksAuth_old.js b/oldCode/express/modules/lineworksAuth_old.js new file mode 100644 index 00000000..d6fc9255 --- /dev/null +++ b/oldCode/express/modules/lineworksAuth_old.js @@ -0,0 +1,77 @@ +const fs = require('fs'); +const path = require('path'); +const jwt = require('jsonwebtoken'); + +let cachedToken = null; +let cachedExpiry = 0; +let cachedScopeKey = ''; + +const CLIENT_ID = 'V58hNobcAqRcQPerySMz'; +const CLIENT_SECRET = 'gJ84XQtigD'; +const SERVICE_ACCOUNT = 'x94xh.serviceaccount@works-nextgroup2'; +const TOKEN_ENDPOINT = 'https://auth.worksmobile.com/oauth2/v2.0/token'; +const PRIVATE_KEY_PATH = path.join(__dirname, 'private_20250530180606.key'); +const TOKEN_TTL_BUFFER = 30; // seconds +const DEFAULT_SCOPES = ['bot', 'contact']; + +function readPrivateKey() { + return fs.readFileSync(PRIVATE_KEY_PATH, 'utf8'); +} + +function makeScopeKey(scopes) { + return scopes.slice().sort().join(' '); +} + +function isTokenValid(scopeKey) { + return Boolean(cachedToken) + && cachedScopeKey === scopeKey + && cachedExpiry > Math.floor(Date.now() / 1000) + TOKEN_TTL_BUFFER; +} + +async function getAccessToken(scopes = DEFAULT_SCOPES) { + const normalizedScopes = Array.isArray(scopes) && scopes.length > 0 ? scopes : DEFAULT_SCOPES; + const scopeKey = makeScopeKey(normalizedScopes); + + if (isTokenValid(scopeKey)) { + return cachedToken; + } + + const now = Math.floor(Date.now() / 1000); + const payload = { + iss: CLIENT_ID, + sub: SERVICE_ACCOUNT, + iat: now, + exp: now + 5 * 60, + aud: TOKEN_ENDPOINT + }; + + const assertion = jwt.sign(payload, readPrivateKey(), { algorithm: 'RS256' }); + const body = new URLSearchParams({ + assertion, + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + client_id: CLIENT_ID, + client_secret: CLIENT_SECRET, + scope: normalizedScopes.join(' ') + }); + + const response = await fetch(TOKEN_ENDPOINT, { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: body.toString() + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`LINE WORKSアクセストークン取得失敗: ${errorText}`); + } + + const data = await response.json(); + cachedToken = data.access_token; + cachedExpiry = now + (data.expires_in || 300); + cachedScopeKey = scopeKey; + return cachedToken; +} + +module.exports = { + getAccessToken +}; diff --git a/oldCode/express/modules/lineworksSurvey.js b/oldCode/express/modules/lineworksSurvey.js new file mode 100644 index 00000000..eded39e1 --- /dev/null +++ b/oldCode/express/modules/lineworksSurvey.js @@ -0,0 +1,1515 @@ +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); +const { v4: uuidv4 } = require('uuid'); +const { API_KEY, PLEASANTER, LINEWORKS_BOT_SECRET } = require('../config.js'); +const { getAccessToken } = require('./lineworksAuth'); + +const USER_LOOKUP_SCOPES = ['user.read', 'directory.read']; + +let SURVEY_RESULT_TABLE_ID = null; +const SESSION_TIMEOUT_MS = 5 * 60 * 1000; +const SESSION_SWEEP_INTERVAL_MS = 60 * 1000; +const SESSION_TIMEOUT_MESSAGE = '一定時間回答がなかったため、アンケートを終了します'; +const SKIP_KEYWORD = 'スキップ'; +const CANCEL_KEYWORDS = ['取消', '取消し', 'やめる', 'キャンセル', '終了', 'おわり', '終わり', 'ストップ', '停止']; +const CANCEL_HINT_TEXT = '※回答をやめる場合は「取消」「キャンセル」「終了」など中止キーワードを送信してください。'; +const FIRST_RESPONSE_TIMEOUT_MS = 72 * 60 * 60 * 1000; +const FIRST_RESPONSE_TIMEOUT_MESSAGE = '72時間以内に回答がなかったため、アンケートを終了します。'; +const SURVEY_NOT_STARTED_MESSAGE = '※アンケートが開始されていません'; +const surveySessions = new Map(); +const sessionIdByTarget = new Map(); +const LOG_DIR = path.resolve(process.cwd(), 'logs'); +const LOG_FILE_PATH = path.join(LOG_DIR, 'lineworksSurvey.log'); + +if (!fs.existsSync(LOG_DIR)) { + fs.mkdirSync(LOG_DIR, { recursive: true }); +} + +function toLogText(payload) { + if (typeof payload === 'undefined') { + return ''; + } + try { + return ` ${JSON.stringify(payload)}`; + } catch { + return ' [payload stringify failed]'; + } +} + +function appendLog(level, message, payload = undefined) { + const line = `[lineworksSurvey] ${new Date().toISOString()} [${level}] ${message}${toLogText(payload)}\n`; + try { + fs.appendFileSync(LOG_FILE_PATH, line, 'utf8'); + } catch (error) { + console.error('[lineworksSurvey] log file write failed', { message: error.message }); + } +} + +function logInfo(message, payload = undefined) { + appendLog('INFO', message, payload); + if (typeof payload === 'undefined') { + console.log(`[lineworksSurvey] ${new Date().toISOString()} ${message}`); + return; + } + console.log(`[lineworksSurvey] ${new Date().toISOString()} ${message}`, payload); +} + +function logWarn(message, payload = undefined) { + appendLog('WARN', message, payload); + if (typeof payload === 'undefined') { + console.warn(`[lineworksSurvey] ${new Date().toISOString()} ${message}`); + return; + } + console.warn(`[lineworksSurvey] ${new Date().toISOString()} ${message}`, payload); +} + +function logError(message, payload = undefined) { + appendLog('ERROR', message, payload); + if (typeof payload === 'undefined') { + console.error(`[lineworksSurvey] ${new Date().toISOString()} ${message}`); + return; + } + console.error(`[lineworksSurvey] ${new Date().toISOString()} ${message}`, payload); +} + +setInterval(() => { + void sweepExpiredSessions(); +}, SESSION_SWEEP_INTERVAL_MS).unref(); +logInfo('session sweeper initialized', { intervalMs: SESSION_SWEEP_INTERVAL_MS, timeoutMs: SESSION_TIMEOUT_MS }); + +// アンケート開始・回答受付のAPIエンドポイントをExpressへ登録する。 +module.exports = (app) => { + app.use(require('express').json({ + verify: (req, _res, buf) => { + req.rawBody = Buffer.from(buf); + } + })); + logInfo('module initialized and json middleware attached'); + + app.post('/lineworksSurvey/webhook', async (req, res) => { + const body = req.body || {}; + const apiKey = req.headers['x-api-key']; + + logInfo('webhook request received', { + headers: req.headers, + body, + rawBody: req.rawBody ? req.rawBody.toString('utf8') : undefined + }); + + // 内部API(x-api-keyあり)以外は LINE WORKS署名検証 + if (!apiKey) { + const ok = verifyWebhookSignature(req); + if (!ok) { + logWarn('webhook rejected: signature verification failed', { + botId: req.headers['x-works-botid'] || req.headers['x-works-botno'] || null, + sourceIp: req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || null, + hasRawBody: Boolean(req.rawBody), + contentLength: req.headers['content-length'] || null + }); + return res.status(401).json({ error: '署名検証に失敗しました' }); + } + } + + try { + // 1) 内部API互換(x-api-keyあり): start / answer を同一入口で処理 + if (apiKey) { + if (apiKey !== API_KEY) { + logWarn('webhook rejected: invalid api key'); + return res.status(401).json({ error: 'APIキーが不正です' }); + } + + // start判定 + if (body.surveyId && body.targetId && body.botId && typeof body.answer === 'undefined') { + const surveyId = Number(body.surveyId); + if (!Number.isFinite(surveyId)) { + logWarn('webhook(start) rejected: invalid surveyId', { surveyId: body.surveyId }); + return res.status(400).json({ error: 'surveyIdが不正です' }); + } + + let resultId = null; + if (typeof body.resultId !== 'undefined') { + resultId = Number(body.resultId); + if (!Number.isFinite(resultId)) { + logWarn('webhook(start) rejected: invalid resultId', { resultId: body.resultId }); + return res.status(400).json({ error: 'resultIdが不正です' }); + } + } + + logInfo('webhook(start) received', { + targetId: body.targetId, + surveyId, + botId: body.botId + }); + + const survey = await getSurvey(surveyId); + const questions = normalizeQuestions(survey); + if (questions.length === 0) { + logWarn('webhook(start) rejected: no survey questions', { surveyId: body.surveyId }); + return res.status(400).json({ error: 'アンケート項目が存在しません' }); + } + + logInfo('resolving lineworks user identity', { + identifier: body.targetId, + domainId: body.domainId + }); + const userProfile = await fetchLineworksUserProfile(body.targetId, body.domainId); + if (!userProfile?.userId) { + logWarn('webhook(start) rejected: userId not found', { targetId: body.targetId, domainId: body.domainId }); + return res.status(404).json({ error: 'LINE WORKSユーザーが見つかりません' }); + } + logInfo('lineworks user resolved', { + identifier: body.targetId, + resolvedUserId: userProfile.userId, + domainId: body.domainId + }); + + if (!SURVEY_RESULT_TABLE_ID) { + SURVEY_RESULT_TABLE_ID = surveyId; + logInfo('survey result table id initialized', { surveyResultTableId: SURVEY_RESULT_TABLE_ID }); + } + + const sessionId = uuidv4(); + const session = { + sessionId, + surveyId, + botId: body.botId, + targetId: userProfile.userId, + deliveryTargetId: body.targetId, + deliveryTargetType: body.targetType || 'user', + targetDisplayName: buildUserDisplayName(userProfile), + userName: userProfile.userName || null, + title: survey.title || `Survey-${body.surveyId}`, + questions, + currentIndex: 0, + answers: [], + expiresAt: Date.now() + SESSION_TIMEOUT_MS, + targetType: 'user', + cancelHintSent: false, + startedAt: Date.now(), + firstAnswerReceived: false, + resultId + }; + + surveySessions.set(sessionId, session); + registerSessionIndex(session); + await sendSurvey(sessionId); + + return res.json({ + result: 'ok', + sessionId, + message: 'アンケートを開始しました' + }); + } + + // answer判定 + if (typeof body.answer !== 'undefined') { + logInfo('webhook(answer) received', { + sessionId: body.sessionId, + targetId: body.targetId, + botId: body.botId + }); + + const resolvedSessionId = resolveSessionId({ + sessionId: body.sessionId, + targetId: body.targetId + }); + + if (!resolvedSessionId) { + return res.status(404).json({ error: '有効なアンケートセッションが見つかりません' }); + } + + const result = await getAnswer(resolvedSessionId, body.answer); + return res.json(result); + } + + return res.status(400).json({ error: 'startまたはanswerに必要なパラメータが不足しています' }); + } + + // 2) LINE WORKS webhook(x-api-keyなし) + const { targetId, botId, answer } = parseLineworksWebhook(body, req.headers); + logInfo('webhook(lineworks) received', { + targetId, + botId, + hasAnswer: typeof answer !== 'undefined' + }); + + if (!targetId || typeof answer === 'undefined') { + logWarn('webhook rejected: targetId or answer missing'); + return res.status(400).json({ error: 'targetIdとanswerの解決に失敗しました' }); + } + + const resolvedSessionId = resolveSessionId({ targetId }); + if (!resolvedSessionId) { + logWarn('webhook: active session not found', { targetId, botId }); + await safeSendLineworksMessage( + botId, + targetId, + SURVEY_NOT_STARTED_MESSAGE, + 'user' + ); + return res.status(200).json({ + result: 'ok', + status: 'no_active_session', + message: SURVEY_NOT_STARTED_MESSAGE + }); + } + + const result = await getAnswer(resolvedSessionId, answer); + logInfo('webhook answer processed', { + resolvedSessionId, + status: result?.status, + nextQuestion: result?.nextQuestion + }); + return res.status(200).json(result); + } catch (error) { + const status = error.statusCode || 500; + logError('webhook processing failed', { status, message: error.message }); + return res.status(status).json({ error: error.message }); + } + }); +}; + +// セッション検索を高速化するため、targetId単位のインデックスを作成する。 +function registerSessionIndex(session) { + if (!session?.targetId) { + return; + } + sessionIdByTarget.set(session.targetId, session.sessionId); +} + +// セッション削除時にインデックスを掃除する。 +function unregisterSessionIndex(sessionId, session) { + if (!session?.targetId) { + return; + } + if (sessionIdByTarget.get(session.targetId) === sessionId) { + sessionIdByTarget.delete(session.targetId); + } +} + +// セッションIDの直接指定、またはtargetIdから有効なセッションを解決する。 +function resolveSessionId({ sessionId, targetId }) { + if (sessionId) { + if (surveySessions.has(sessionId)) { + return sessionId; + } + logWarn('resolveSessionId: sessionId not found', { sessionId }); + return null; + } + + if (!targetId) { + return null; + } + + const indexedSessionId = sessionIdByTarget.get(targetId); + if (!indexedSessionId) { + return null; + } + + if (surveySessions.has(indexedSessionId)) { + return indexedSessionId; + } + + sessionIdByTarget.delete(targetId); + return null; +} + +// Pleasanterからアンケート定義データを取得し、タイトルと設問情報を返す。 +async function getSurvey(surveyId) { + logInfo('fetching survey definition', { surveyId }); + const endpoint = `${PLEASANTER.HOST}/api/items/${Number(surveyId)}/getsite`; + + const json = { + 'ApiVersion': 1.1, + 'ApiKey': PLEASANTER.API_KEY, + }; + + const res = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(json) + }); + + const resData = await res.json(); + if (String(resData?.StatusCode) !== '200') { + logError('failed to fetch survey definition', { surveyId, statusCode: resData?.StatusCode, response: resData }); + throw new Error(`Pleasanterサイト取得失敗: ${JSON.stringify(resData)}`); + } + + const responseBody = resData?.Response || resData?.Repponse; + const data = responseBody?.Data || {}; + const siteSettings = data?.SiteSettings || {}; + const columns = siteSettings?.Columns || siteSettings?.Column || []; + + return { + title: data?.Title || '', + questions: columns + }; +} + +async function fetchLineworksUserProfile(identifier, domainId) { + if (!identifier) { + throw new Error('LINE WORKSユーザー識別子が指定されていません'); + } + + const accessToken = await getAccessToken(USER_LOOKUP_SCOPES); + let apiUrl = `https://www.worksapis.com/v1.0/users/${encodeURIComponent(identifier)}`; + if (domainId) { + apiUrl += `?domainId=${encodeURIComponent(domainId)}`; + } + + const response = await fetch(apiUrl, { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}` + } + }); + + const text = await response.text(); + let data; + try { + data = JSON.parse(text); + } catch (error) { + logError('failed to parse lineworks user response', { apiUrl, text }); + throw new Error('LINE WORKSユーザー情報の解析に失敗しました'); + } + + if (!response.ok) { + logError('lineworks user lookup failed', { apiUrl, response: data }); + throw new Error('LINE WORKSユーザー照会に失敗しました'); + } + + return data; +} + +function formatUserName(userName = {}) { + if (!userName) { + return ''; + } + const lastName = userName?.lastName || ''; + const firstName = userName?.firstName || ''; + return `${lastName} ${firstName}`.trim(); +} + +function buildUserDisplayName(userProfile = {}) { + const fullName = formatUserName(userProfile?.userName); + if (fullName) { + return fullName; + } + if (userProfile.email) { + return userProfile.email; + } + return userProfile.userId || ''; +} + +function parseLineworksWebhook(body, headers = {}) { + const source = body?.source || {}; + const content = body?.content || {}; + + const botId = headers['x-works-botid'] + || headers['x-works-botno'] + || headers['x-works-bot-id'] + || headers['x-works-bot-no'] + || body?.botId + || body?.botNo; + + return { + targetId: source.userId, + botId, + answer: content.text + }; +} + +const ERA_INFO = { + '令和': 2018, + '平成': 1988, + '昭和': 1925, + '大正': 1911 +}; + +const ERA_ALIASES = { + R: '令和', + H: '平成', + S: '昭和', + T: '大正' +}; + +function normalizeQuestionTypeValue(type) { + if (!type) { + return ''; + } + const raw = String(type).trim(); + switch (raw) { + case '選択式': + return 'select'; + case '短文テキスト': + return 'class_text'; + case '長文テキスト': + return 'description'; + case '数値': + return 'number'; + case '日付': + return 'date'; + default: + break; + } + + const lower = raw.toLowerCase(); + switch (lower) { + case 'select': + case 'button': + return 'select'; + case 'class_text': + case 'text': + return 'class_text'; + case 'description': + case 'long_text': + return 'description'; + case 'number': + return 'number'; + case 'date': + return 'date'; + default: + return lower; + } +} + +function isSelectQuestion(question) { + return normalizeQuestionTypeValue(question?.type) === 'select'; +} + +function isNumberQuestion(question) { + return normalizeQuestionTypeValue(question?.type) === 'number'; +} + +function isDateQuestion(question) { + return normalizeQuestionTypeValue(question?.type) === 'date'; +} + +// 現在の設問をLINE WORKSに送信する。 +async function sendSurvey(sessionId) { + logInfo('sending survey question', { sessionId }); + const session = surveySessions.get(sessionId); + if (!session) { + const error = new Error('セッションが見つかりません'); + error.statusCode = 404; + throw error; + } + + if (isSessionExpired(session)) { + await expireSessionWithNotice(sessionId, session); + const error = new Error('セッションの有効期限が切れました'); + error.statusCode = 410; + throw error; + } + + const question = session.questions[session.currentIndex]; + if (!question) { + logWarn('sendSurvey skipped: question not found', { sessionId, currentIndex: session.currentIndex }); + return; + } + + if (!session.cancelHintSent) { + await sendLineworksMessage( + session.botId, + session.deliveryTargetId, + CANCEL_HINT_TEXT, + session.deliveryTargetType || 'user' + ); + session.cancelHintSent = true; + logInfo('cancel hint sent', { sessionId }); + } + + const prefix = session.currentIndex === 0 ? `アンケート開始: ${session.title}\n\n` : ''; + const baseQuestionText = `${prefix}Q${session.currentIndex + 1}. ${question.text}`; + const questionText = question.required ? `${baseQuestionText}\n※必須` : baseQuestionText; + + if (isSelectQuestion(question) && question.options.length > 0) { + const maxOptions = question.required ? 10 : 9; + const actions = question.options.slice(0, maxOptions).map((option) => ({ + type: 'message', + label: String(option), + text: String(option) + })); + + if (!question.required) { + actions.push({ + type: 'message', + label: SKIP_KEYWORD, + text: SKIP_KEYWORD + }); + } + + const content = { + type: 'button_template', + contentText: questionText, + actions + }; + + const sendResult = await sendLineworksMessage( + session.botId, + session.deliveryTargetId, + content, + session.deliveryTargetType || 'user' + ); + if (session.currentIndex === 0) { + logInfo('initial survey question response logged', { + sessionId, + questionIndex: 1, + response: sendResult + }); + } + logInfo('select question sent', { sessionId, questionIndex: session.currentIndex + 1, optionCount: actions.length }); + return; + } + + const messageText = `${questionText}\n回答形式: ${question.type}`; + const sendResult = await sendLineworksMessage( + session.botId, + session.deliveryTargetId, + messageText, + session.deliveryTargetType || 'user' + ); + if (session.currentIndex === 0) { + logInfo('initial survey question response logged', { + sessionId, + questionIndex: 1, + response: sendResult + }); + } + logInfo('text question sent', { sessionId, questionIndex: session.currentIndex + 1, type: question.type }); + + if (isDateQuestion(question)) { + await sendDateShortcutButtons(session, questionText, !question.required); + } else if (!question.required) { + await sendSkipButton(session); + } +} + +async function sendDateShortcutButtons(session, questionText, allowSkip = false) { + const today = new Date(); + const formatDate = (offset) => { + const date = new Date(today); + date.setDate(date.getDate() + offset); + return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`; + }; + + const actions = [ + { label: `本日 (${formatDate(0)})`, text: formatDate(0) }, + { label: `昨日 (${formatDate(-1)})`, text: formatDate(-1) }, + { label: `明日 (${formatDate(1)})`, text: formatDate(1) } + ]; + + if (allowSkip) { + actions.push({ label: SKIP_KEYWORD, text: SKIP_KEYWORD }); + } + + const content = { + type: 'button_template', + contentText: `${questionText}\n入力しづらい場合は以下のボタンを利用できます。${allowSkip ? '\n※スキップ可能' : ''}`, + actions: actions.map((action) => ({ + type: 'message', + label: action.label, + text: action.text + })) + }; + + await sendLineworksMessage( + session.botId, + session.deliveryTargetId, + content, + session.deliveryTargetType || 'user' + ); + logInfo('date shortcut buttons sent', { + sessionId: session.sessionId, + questionIndex: session.currentIndex + 1, + actions: actions.map((action) => action.label) + }); +} + +async function sendSkipButton(session) { + const content = { + type: 'button_template', + contentText: '※スキップ可能', + actions: [ + { + type: 'message', + label: SKIP_KEYWORD, + text: SKIP_KEYWORD + } + ] + }; + + await sendLineworksMessage( + session.botId, + session.deliveryTargetId, + content, + session.deliveryTargetType || 'user' + ); + logInfo('optional question skip button sent', { + sessionId: session.sessionId, + questionIndex: session.currentIndex + 1 + }); +} + +// 受信した回答を検証して保持し、次設問送信または最終保存処理を実行する。 +async function getAnswer(sessionId, answerText) { + logInfo('processing answer', { sessionId }); + const session = surveySessions.get(sessionId); + if (!session) { + const error = new Error('セッションが見つかりません'); + error.statusCode = 404; + throw error; + } + + if (isSessionExpired(session)) { + await expireSessionWithNotice(sessionId, session); + const error = new Error('セッションの有効期限が切れました。再度アンケートを開始してください'); + error.statusCode = 410; + throw error; + } + + const question = session.questions[session.currentIndex]; + if (!question) { + const error = new Error('回答対象の設問が見つかりません'); + error.statusCode = 400; + throw error; + } + + const rawInput = typeof answerText === 'string' ? answerText : String(answerText ?? ''); + const trimmedInput = rawInput.trim(); + const skipRequested = trimmedInput === SKIP_KEYWORD; + const cancelRequested = CANCEL_KEYWORDS.includes(trimmedInput); + + if (cancelRequested) { + await cancelSurveySession(sessionId, session, trimmedInput); + return { + result: 'cancelled', + status: 'cancelled', + message: 'アンケートを取り消しました' + }; + } + + if (skipRequested && question.required) { + const message = 'この設問は必須です。回答を入力してください。'; + logWarn('skip requested on required question', { sessionId, questionIndex: session.currentIndex + 1 }); + await handleInvalidAnswer(sessionId, session, message); + return { + result: 'retry', + status: 'invalid_answer', + message + }; + } + + const shouldSkip = !question.required && (skipRequested || trimmedInput.length === 0); + + if (!shouldSkip) { + let normalizedAnswer; + try { + normalizedAnswer = validateAnswer(question, rawInput); + } catch (error) { + logWarn('answer validation failed', { sessionId, questionIndex: session.currentIndex + 1, message: error.message }); + await handleInvalidAnswer(sessionId, session, error.message); + return { + result: 'retry', + status: 'invalid_answer', + message: error.message + }; + } + + logInfo('answer validated', { sessionId, questionIndex: session.currentIndex + 1, type: question.type }); + session.answers.push({ + index: session.currentIndex + 1, + question: question.text, + type: question.type, + columnName: question.columnName, + answer: normalizedAnswer + }); + session.firstAnswerReceived = session.firstAnswerReceived || session.answers.length > 0; + } else { + logInfo('optional question skipped by user', { sessionId, questionIndex: session.currentIndex + 1 }); + } + + session.currentIndex += 1; + + if (session.currentIndex < session.questions.length) { + session.expiresAt = Date.now() + SESSION_TIMEOUT_MS; + logInfo('moving to next question', { sessionId, nextQuestion: session.currentIndex + 1 }); + await sendSurvey(sessionId); + return { + result: 'ok', + status: 'in_progress', + nextQuestion: session.currentIndex + 1 + }; + } + + let saveError = null; + let statusUpdateError = null; + try { + await sendAnswer(session); + } catch (error) { + saveError = error; + } + + if (!saveError && session.resultId) { + try { + await updatePleasanterStatus(session.resultId, 900); + } catch (error) { + statusUpdateError = error; + } + } + + const finalError = saveError || statusUpdateError; + + await notifySurveyCompletion(session, finalError); + surveySessions.delete(sessionId); + unregisterSessionIndex(sessionId, session); + logInfo('survey completed and session removed', { sessionId, answerCount: session.answers.length }); + + if (finalError) { + finalError.statusCode = finalError.statusCode || 500; + throw finalError; + } + + return { + result: 'ok', + status: 'completed', + message: '回答を保存しました' + }; +} + +// 回答完了後の全回答をPleasanterへ1レコードとして登録する。 +async function sendAnswer(session) { + logInfo('sending answers to pleasanter', { sessionId: session.sessionId, answerCount: session.answers.length }); + const targetSurveyId = Number(session?.surveyId ?? SURVEY_RESULT_TABLE_ID); + if (!Number.isFinite(targetSurveyId)) { + logError('sendAnswer aborted: surveyId unavailable', { sessionId: session.sessionId }); + throw new Error('アンケートIDが未設定のため回答登録に失敗しました'); + } + const endpoint = `${PLEASANTER.HOST}/api/items/${targetSurveyId}/create`; + const hashData = buildAnswerHashData(session.answers); + const ownerValue = formatUserName(session.userName) || session.targetDisplayName || session.targetId || ''; + + const bodyMessage = []; + bodyMessage.push(`回答者: ${ownerValue}`); + bodyMessage.push(`LINEWORKS ID: ${session.deliveryTargetId} (${session.targetId})`); + bodyMessage.push(`回答日時: ${new Date().toLocaleString()}`); + + const json = { + ApiVersion: 1.1, + ApiKey: PLEASANTER.API_KEY, + Body: bodyMessage.join('\n'), + ...hashData + }; + + //if (ownerValue) { + // json.Owner = ownerValue; + //} + + const requestPayload = JSON.stringify(json); + + const response = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: requestPayload + }); + + const responseText = await response.text(); + let responseJson; + try { + responseJson = JSON.parse(responseText); + } catch (error) { + logError('failed to parse pleasanter response', { + sessionId: session.sessionId, + responseText, + requestPayload + }); + throw new Error('Pleasanterのレスポンス解析に失敗しました'); + } + + const statusCode = Number(responseJson?.StatusCode); + if (statusCode !== 200) { + logError('failed to send answers to pleasanter', { + sessionId: session.sessionId, + statusCode, + message: responseJson?.Message, + response: responseJson, + requestPayload + }); + throw new Error(`Pleasanter回答登録失敗: ${responseJson?.Message || responseText}`); + } + + logInfo('answers saved to pleasanter', { + sessionId: session.sessionId, + statusCode, + message: responseJson?.Message + }); +} + +// PleasanterレコードのStatus項目を更新する。 +async function updatePleasanterStatus(resultId, status) { + const numericResultId = Number(resultId); + if (!Number.isFinite(numericResultId)) { + const error = new Error('resultIdが不正です'); + error.statusCode = 400; + throw error; + } + + const endpoint = `${PLEASANTER.HOST}/api/items/${numericResultId}/update`; + const payload = JSON.stringify({ + ApiVersion: 1.1, + ApiKey: PLEASANTER.API_KEY, + Status: status + }); + + logInfo('updating pleasanter status', { resultId: numericResultId, status }); + + const response = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: payload + }); + + const responseText = await response.text(); + const responseJson = parseJsonIfPossible(responseText); + const statusCode = Number(responseJson?.StatusCode); + + if (!response.ok || statusCode !== 200) { + logError('pleasanter status update failed', { + resultId: numericResultId, + status, + httpStatus: response.status, + statusCode, + response: responseJson + }); + throw new Error('Pleasanterステータス更新に失敗しました'); + } + + logInfo('pleasanter status updated', { + resultId: numericResultId, + status, + statusCode + }); +} + +// 列名プレフィックスに応じて回答をClass/Num/Date/Descriptionの各Hashへ振り分ける。 +function buildAnswerHashData(answers) { + const classHash = {}; + const numHash = {}; + const dateHash = {}; + const descriptionHash = {}; + + for (const item of answers) { + const columnName = String(item?.columnName || ''); + if (!columnName) { + continue; + } + + if (columnName.startsWith('Class')) { + classHash[columnName] = item.answer; + continue; + } + + if (columnName.startsWith('Num')) { + numHash[columnName] = item.answer; + continue; + } + + if (columnName.startsWith('Date')) { + dateHash[columnName] = item.answer; + continue; + } + + if (columnName.startsWith('Description')) { + descriptionHash[columnName] = item.answer; + } + } + + return { + ClassHash: classHash, + NumHash: numHash, + DateHash: dateHash, + DescriptionHash: descriptionHash + }; +} + +function parseExtendedControlCssTokens(value) { + if (typeof value === 'undefined' || value === null) { + return []; + } + if (Array.isArray(value)) { + return value + .flatMap((item) => parseExtendedControlCssTokens(item)) + .filter((item) => item); + } + + return String(value) + .split(/[\s\u3000,]+/u) + .map((item) => item.trim()) + .filter((item) => item); +} + +function isSurveyColumn(item) { + if (!item || typeof item !== 'object') { + return false; + } + + const extendedControlCss = item.ExtendedControlCss ?? item.extendedControlCss; + if (!extendedControlCss) { + return false; + } + + const tokens = parseExtendedControlCssTokens(extendedControlCss).map((token) => token.toLowerCase()); + return tokens.includes('survey'); +} + +// 取得したアンケート定義を内部で扱う設問配列形式へ正規化する。 +function normalizeQuestions(survey) { + const source = Array.isArray(survey.questions) ? survey.questions : parseJsonIfNeeded(survey.questions); + if (!Array.isArray(source)) { + return []; + } + + const filteredColumns = source.filter(isSurveyColumn); + if (filteredColumns.length !== source.length) { + logInfo('survey columns filtered by ExtendedControlCss', { + original: source.length, + filtered: filteredColumns.length + }); + } + + return filteredColumns + .map((item, idx) => { + const columnName = String(item.ColumnName || item.columnName || ''); + const label = item.LabelText || item.label || item.text || item.question || `質問${idx + 1}`; + const choicesText = item.ChoicesText || item.choicesText || ''; + const hasChoices = String(choicesText).trim().length > 0; + const required = toBooleanFlag(item?.ValidateRequired ?? item?.validateRequired); + + if (columnName.startsWith('Class')) { + if (hasChoices) { + return { + id: idx + 1, + columnName, + text: label, + type: '選択式', + options: splitChoices(choicesText), + required + }; + } + + return { + id: idx + 1, + columnName, + text: label, + type: '短文テキスト', + options: [], + required + }; + } + + if (columnName.startsWith('Num')) { + return { + id: idx + 1, + columnName, + text: label, + type: '数値', + options: [], + required + }; + } + + if (columnName.startsWith('Date')) { + return { + id: idx + 1, + columnName, + text: label, + type: '日付', + options: [], + required + }; + } + + if (columnName.startsWith('Description')) { + return { + id: idx + 1, + columnName, + text: label, + type: '長文テキスト', + options: [], + required + }; + } + + return null; + }) + .filter((question) => !!question); +} + +// 設問タイプに応じて回答値を検証し、保存可能な形式へ変換する。 +function validateAnswer(question, answerText) { + const rawValue = String(answerText ?? ''); + const value = rawValue.trim(); + if (!value) { + const error = new Error('回答が空です'); + error.statusCode = 400; + throw error; + } + const normalizedType = normalizeQuestionTypeValue(question?.type); + + if (normalizedType === 'number') { + const normalized = toHalfWidthNumber(value); + if (!/^[-+]?\d+(\.\d+)?$/.test(normalized)) { + const error = new Error('数値入力の設問です。数値で回答してください(例: 123 または -45.6)'); + error.statusCode = 400; + throw error; + } + return Number(normalized); + } + + if (normalizedType === 'date') { + try { + return parseDateInput(value); + } catch (error) { + const err = new Error(error.message || '日付入力の設問です。正しい形式で回答してください'); + err.statusCode = 400; + throw err; + } + } + + if (normalizedType === 'select') { + if (!Array.isArray(question.options) || question.options.length === 0) { + return value; + } + + const exists = question.options.some((option) => String(option) === value); + if (!exists) { + const error = new Error(`選択肢から選んでください: ${question.options.join(', ')}`); + error.statusCode = 400; + throw error; + } + return value; + } + + if (normalizedType === 'class_text') { + return value.replace(/[\r\n]+/g, ' '); + } + + return rawValue; +} + +// JSON文字列または配列を安全に配列へ変換する。 +function parseJsonIfNeeded(value) { + if (Array.isArray(value)) { + return value; + } + if (typeof value !== 'string') { + return []; + } + try { + return JSON.parse(value); + } catch { + return []; + } +} + +function parseJsonIfPossible(text) { + if (typeof text !== 'string' || text.length === 0) { + return null; + } + try { + return JSON.parse(text); + } catch { + return text; + } +} + +// 改行区切りの選択肢文字列を配列に変換する。 +function splitChoices(choicesText) { + return String(choicesText) + .split(/\r?\n/) + .map((option) => option.trim()) + .filter((option) => option.length > 0) + .slice(0, 10); +} + +function toBooleanFlag(value) { + if (typeof value === 'boolean') { + return value; + } + if (typeof value === 'number') { + return value === 1; + } + if (typeof value === 'string') { + const normalized = value.trim().toLowerCase(); + return normalized === 'true' || normalized === '1' || normalized === 'yes'; + } + return false; +} + +// 全角数字を半角数字へ変換し、数値判定しやすい形式に整える。 +function toHalfWidthNumber(value) { + return String(value) + .replace(/[0-9]/g, (s) => String.fromCharCode(s.charCodeAt(0) - 0xFEE0)) + .replace(/,/g, ',') + .replace(/./g, '.') + .replace(/-/g, '-') + .replace(/+/g, '+') + .replace(/,/g, ''); +} + +function parseDateInput(value) { + const trimmed = value.trim(); + if (!trimmed) { + throw new Error('日付が空です'); + } + const compact = trimmed.replace(/\s+/g, ''); + + try { + const eraResult = tryParseEraDate(compact); + if (eraResult) { + return eraResult; + } + } catch (error) { + throw error; + } + + const monthDayResult = tryParseMonthDay(compact); + if (monthDayResult) { + return monthDayResult; + } + + const normalized = compact + .replace(/年/g, '-') + .replace(/月/g, '-') + .replace(/日/g, '') + .replace(/[\.\/]/g, '-'); + const isoParts = normalized.split('-').filter((part) => part.length > 0); + if (isoParts.length === 3 && isoParts[0].length >= 4) { + return finalizeDateParts(Number(isoParts[0]), Number(isoParts[1]), Number(isoParts[2])); + } + + const parsed = new Date(trimmed); + if (Number.isNaN(parsed.getTime())) { + throw new Error('日付形式で回答してください(例: 2026-03-01 や 令和6年3月1日)'); + } + return finalizeDateParts(parsed.getFullYear(), parsed.getMonth() + 1, parsed.getDate()); +} + +function tryParseEraDate(compact) { + const match = compact.match(/^(令和|平成|昭和|大正|R|H|S|T)(.+)$/i); + if (!match) { + return null; + } + + let era = match[1]; + if (/^[RHST]$/i.test(era)) { + era = ERA_ALIASES[era.toUpperCase()] || era; + } + if (!ERA_INFO[era]) { + return null; + } + + const rest = match[2]; + const normalized = rest + .replace(/年/g, '-') + .replace(/月/g, '-') + .replace(/日/g, '') + .replace(/[\.\/]/g, '-'); + const parts = normalized.split('-').filter((part) => part.length > 0); + if (parts.length < 3) { + throw new Error('月と日まで入力してください(例: 令和6年3月1日)'); + } + + const eraYear = Number(parts[0]); + const month = Number(parts[1]); + const day = Number(parts[2]); + + if (!Number.isFinite(eraYear) || !Number.isFinite(month) || !Number.isFinite(day)) { + throw new Error('日付形式で回答してください(例: 令和6年3月1日)'); + } + + const year = ERA_INFO[era] + eraYear - 1; + return finalizeDateParts(year, month, day); +} + +function tryParseMonthDay(compact) { + const match = compact.match(/^(\d{1,2})(?:月|\/|\-|\.)(\d{1,2})(?:日)?$/); + if (!match) { + return null; + } + const currentYear = new Date().getFullYear(); + const month = Number(match[1]); + const day = Number(match[2]); + return finalizeDateParts(currentYear, month, day); +} + +function finalizeDateParts(year, month, day) { + if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day)) { + throw new Error('日付形式で回答してください(例: 2026-03-01)'); + } + + const date = new Date(year, month - 1, day); + if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) { + throw new Error('存在しない日付です'); + } + + return `${year}-${pad2(month)}-${pad2(day)}`; +} + +function pad2(value) { + return String(value).padStart(2, '0'); +} + +// LINE WORKS Bot APIを使ってユーザーまたはトークルームへメッセージを送信する。 +async function sendLineworksMessage(botId, targetId, messageText, targetType = 'user') { + logInfo('sending message to lineworks', { + botId, + targetId, + targetType, + contentType: typeof messageText === 'string' ? 'text' : messageText?.type + }); + const accessToken = await getAccessToken(); + if (!accessToken) { + logError('sendLineworksMessage failed: access token unavailable', { botId, targetId }); + throw new Error('アクセストークンの取得に失敗しました'); + } + + const encodedTarget = encodeURIComponent(targetId); + let apiUrl; + switch (targetType) { + case 'channel': + apiUrl = `https://www.worksapis.com/v1.0/bots/${botId}/channels/${encodedTarget}/messages`; + break; + case 'room': + apiUrl = `https://www.worksapis.com/v1.0/bots/${botId}/rooms/${encodedTarget}/messages`; + break; + default: + apiUrl = `https://www.worksapis.com/v1.0/bots/${botId}/users/${encodedTarget}/messages`; + break; + } + + const content = typeof messageText === 'string' + ? { + type: 'text', + text: messageText + } + : messageText; + + const body = { content }; + + const response = await fetch(apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json;charset=UTF-8', + Authorization: `Bearer ${accessToken}` + }, + body: JSON.stringify(body) + }); + + const responseText = await response.text(); + const responsePayload = parseJsonIfPossible(responseText); + + if (!response.ok) { + logError('lineworks send failed', { + botId, + targetId, + targetType, + apiUrl, + status: response.status, + response: responsePayload + }); + const message = typeof responsePayload === 'string' + ? responsePayload + : responsePayload === null + ? `status=${response.status}` + : JSON.stringify(responsePayload); + throw new Error(`LINE WORKS送信失敗: ${message}`); + } + + const logPayload = { + botId, + targetId, + targetType, + apiUrl, + status: response.status, + response: responsePayload + }; + logInfo('lineworks message sent', logPayload); + return logPayload; +} + +// セッションが期限切れかどうかを判定する。 +function isSessionExpired(session, now = Date.now()) { + return !session?.expiresAt || now > session.expiresAt; +} + +// 期限切れセッションを走査して終了通知後に削除する。 +async function sweepExpiredSessions() { + const now = Date.now(); + logInfo('sweeping expired sessions', { activeSessions: surveySessions.size }); + for (const [sessionId, session] of surveySessions.entries()) { + const startedAt = Number(session?.startedAt) || null; + const waitingTooLong = !session?.firstAnswerReceived + && Number.isFinite(startedAt) + && now - startedAt >= FIRST_RESPONSE_TIMEOUT_MS; + + if (waitingTooLong) { + logWarn('session expired due to no response within 72h', { sessionId }); + await expireSessionWithNotice(sessionId, session, FIRST_RESPONSE_TIMEOUT_MESSAGE); + continue; + } + + if (!isSessionExpired(session, now)) { + continue; + } + logWarn('expired session detected', { sessionId }); + await expireSessionWithNotice(sessionId, session); + } +} + +// 期限切れセッションに終了通知を送信してからセッションを削除する。 +async function expireSessionWithNotice(sessionId, session, message = SESSION_TIMEOUT_MESSAGE) { + const currentSession = session || surveySessions.get(sessionId); + if (!currentSession) { + return; + } + + try { + logInfo('sending session timeout notice', { sessionId, targetId: currentSession.targetId }); + await sendLineworksMessage( + currentSession.botId, + currentSession.deliveryTargetId, + message, + currentSession.deliveryTargetType || 'user' + ); + } catch (error) { + logError(`期限切れ通知送信失敗: ${sessionId}`, { message: error.message }); + } finally { + surveySessions.delete(sessionId); + unregisterSessionIndex(sessionId, currentSession); + logInfo('expired session removed', { sessionId }); + } +} + +function normalizeSignature(value) { + const raw = String(value || '').trim(); + return raw.replace(/^sha256=/i, ''); +} + +function safeEqual(a, b) { + const ab = Buffer.from(String(a), 'utf8'); + const bb = Buffer.from(String(b), 'utf8'); + if (ab.length !== bb.length) return false; + return crypto.timingSafeEqual(ab, bb); +} + +function verifyWebhookSignature(req) { + if (!LINEWORKS_BOT_SECRET) { + logWarn('LINEWORKS_BOT_SECRET is empty'); + return false; + } + + const headerSig = normalizeSignature(req.headers['x-works-signature']); + if (!headerSig) { + logWarn('webhook rejected: x-works-signature missing'); + return false; + } + + // 生ボディ優先(なければフォールバック) + const payload = req.rawBody + ? req.rawBody + : Buffer.from(JSON.stringify(req.body || {}), 'utf8'); + + const expected = crypto + .createHmac('sha256', LINEWORKS_BOT_SECRET) + .update(payload) + .digest('base64'); + + const matched = safeEqual(headerSig, expected); + if (!matched) { + logWarn('webhook signature mismatch', { + headerSignaturePreview: `${headerSig.slice(0, 8)}...(${headerSig.length})`, + expectedSignaturePreview: `${expected.slice(0, 8)}...(${expected.length})`, + payloadByteLength: payload.length, + hasRawBody: Boolean(req.rawBody), + botId: req.headers['x-works-botid'] || req.headers['x-works-botno'] || null + }); + } + + return matched; +} + +async function safeSendLineworksMessage(botId, targetId, messageText, targetType = 'user') { + if (!botId || !targetId) { + return; + } + try { + await sendLineworksMessage(botId, targetId, messageText, targetType); + } catch (error) { + logError('lineworks notification failed', { botId, targetId, message: error.message }); + } +} + +async function handleInvalidAnswer(sessionId, session, errorMessage) { + const notice = `入力内容に不備があります。\n理由: ${errorMessage}\n同じ設問にもう一度お答えください。`; + await safeSendLineworksMessage( + session.botId, + session.deliveryTargetId, + notice, + session.deliveryTargetType || 'user' + ); + session.expiresAt = Date.now() + SESSION_TIMEOUT_MS; + await sendSurvey(sessionId); +} + +async function notifySurveyCompletion(session, saveError) { + const message = saveError + ? 'アンケートは完了しましたが、内部処理でエラーが発生しました。担当者により確認を進めます。' + : 'アンケート回答を受け付けました。ありがとうございました。'; + await safeSendLineworksMessage( + session.botId, + session.deliveryTargetId, + message, + session.deliveryTargetType || 'user' + ); +} + +async function cancelSurveySession(sessionId, session, cancelInput = '') { + const currentSession = session || surveySessions.get(sessionId); + if (!currentSession) { + return; + } + + const trimmed = String(cancelInput || '').trim(); + const notice = trimmed + ? `「${trimmed}」を受け取り、アンケート回答を中止しました。` + : 'アンケート回答を中止しました。'; + + if (currentSession.resultId) { + try { + await updatePleasanterStatus(currentSession.resultId, 910); + } catch (error) { + logError('failed to update pleasanter status on cancel', { + sessionId: currentSession.sessionId, + resultId: currentSession.resultId, + message: error.message + }); + } + } + + await safeSendLineworksMessage( + currentSession.botId, + currentSession.deliveryTargetId, + notice, + currentSession.deliveryTargetType || 'user' + ); + + surveySessions.delete(currentSession.sessionId); + unregisterSessionIndex(currentSession.sessionId, currentSession); + logInfo('survey cancelled by user', { + sessionId: currentSession.sessionId, + cancelInput: trimmed || undefined + }); +} \ No newline at end of file diff --git a/oldCode/express/modules/lineworks_gptBot.js b/oldCode/express/modules/lineworks_gptBot.js new file mode 100644 index 00000000..51c68b0a --- /dev/null +++ b/oldCode/express/modules/lineworks_gptBot.js @@ -0,0 +1,58 @@ +require('dotenv').config(); +const express = require('express'); +const bodyParser = require('body-parser'); +const axios = require('axios'); + +app.use(bodyParser.json()); + +module.exports = (app) => { + // LINE WORKS Webhookエンドポイント + app.post('/nextgpt', async (req, res) => { + try { + const { source, content } = req.body; + + // LINE WORKSからのメッセージ + const userMessage = content.text; + const userId = source.accountId; + + console.log(`Received message: "${userMessage}" from User: "${userId}"`); + + // anythingLLMにメッセージを送信して応答を取得 + const llmResponse = await axios.post(process.env.ANYTHING_LLM_API, { + question: userMessage + }); + + const llmAnswer = llmResponse.data.answer || "申し訳ありませんが、応答を生成できませんでした。"; + + // LINE WORKSに応答を送信 + await axios.post( + `https://apis.worksmobile.com/r/${process.env.LINEWORKS_BOT_ID}/message/push`, + { + botNo: process.env.LINEWORKS_BOT_ID, + accountId: userId, + content: { + type: "text", + text: llmAnswer + } + }, + { + headers: { + "Content-Type": "application/json", + "consumerKey": process.env.LINEWORKS_API_KEY + } + } + ); + + console.log(`Reply sent to User: "${userId}"`); + res.status(200).send('OK'); + } catch (error) { + console.error('Error:', error.message); + res.status(500).send('Internal Server Error'); + } + }); + +}; + + + +$p.apiUpdate({ id: 186849, data: { ApiVersion: 1.1, ClassHash: { ClassU: ["遠藤 晃太", "(退職) 横井 英治"], ClassV: ["原田 朋保"], ClassW: ["宮倉 由紀"] } }, done: function (data) { console.log(data); }, }); diff --git a/oldCode/express/modules/lwSendMessage.js b/oldCode/express/modules/lwSendMessage.js new file mode 100644 index 00000000..04cba6f4 --- /dev/null +++ b/oldCode/express/modules/lwSendMessage.js @@ -0,0 +1,99 @@ +const { API_KEY } = require('../config.js'); // API_KEYをインポート +const { getAccessToken } = require('./lineworksAuth'); + +module.exports = (app) => { + app.post('/lineworks/sendMessage', async (req, res) => { + let rawBody = ''; + req.on('data', (chunk) => { + rawBody += chunk; // リクエストデータをそのまま取得 + }); + + req.on('end', async () => { + // reqのすべてのパラメータを表示 + // console.log('Request Headers:', req.headers); + // console.log('Request Body:', rawBody); + + const apiKey = req.headers['x-api-key']; + if (!apiKey || apiKey !== API_KEY) { + console.log('APIキーが不正です'); + return res.status(401).json({ error: 'APIキーが不正です' }); + } + + // botIdとuserIdをヘッダーから取得 + const botId = req.headers['x-bot-id']; + const userId = req.headers['x-user-id']; + if (!botId || !userId) { + console.log('x-bot-id, x-user-idは必須です'); + return res.status(400).json({ error: 'x-bot-id, x-user-idは必須です' }); + } + + const userIds = userId.split(';').map(id => id.trim()).filter(id => id); + + // リクエストボディからメッセージテキストを取得 + const messageText = rawBody; + if (!messageText) { + console.log('messageTextは必須です'); + return res.status(400).json({ error: 'messageTextは必須です' }); + } + + try { + for (const uid of userIds) { + const result = await sendLineworksMessage(botId, uid, messageText); + if (result.success) { + console.log(`送信成功: ${uid}:`, result.message); + } else { + console.log(`送信失敗: ${uid}:`, result.message); + } + } + res.json({ result: 'ok', message: '全ユーザーに送信処理完了' }); + return; + } catch (e) { + console.log('エラーが発生しました:', e.message); + res.status(500).json({ error: e.message }); + } + }); + }); +}; + +// メッセージ送信 +async function sendLineworksMessage(botId, userId, messageText) { + const accessToken = await getAccessToken(); + if (!accessToken) { + console.log('アクセストークンの取得に失敗しました'); + throw new Error('アクセストークンの取得に失敗しました'); + } + + // userIdに@が含まれない場合はchannelIdとして扱う + let apiUrl; + if (userId.includes('@')) { + apiUrl = `https://www.worksapis.com/v1.0/bots/${botId}/users/${userId}/messages`; + } else { + apiUrl = `https://www.worksapis.com/v1.0/bots/${botId}/channels/${userId}/messages`; + } + + const body = { + 'content': { + 'type': 'text', + 'text': messageText + } + }; + + const response = await fetch(apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json;charset=UTF-8', + 'Authorization': `Bearer ${accessToken}` + }, + body: JSON.stringify(body) + }); + + if (response.ok) { + console.log('メッセージ送信成功'); + return { success: true, message: '送信成功' }; + } else { + const errorText = await response.text(); + console.log('メッセージ送信失敗:', errorText); + return { success: false, message: `送信失敗: ${errorText}` }; + } +} + diff --git a/oldCode/express/modules/pdfConvert.js b/oldCode/express/modules/pdfConvert.js new file mode 100644 index 00000000..481e8a0a --- /dev/null +++ b/oldCode/express/modules/pdfConvert.js @@ -0,0 +1,100 @@ +const fs = require('fs'); +const path = require('path'); +const { execFile } = require('child_process'); +const { v4: uuidv4 } = require('uuid'); +const express = require('express'); // JSON解析用にexpressをインポート +const { API_KEY } = require('../config.js'); // API_KEYをインポート + +// LibreOffice AppImageのパス +const libreofficeAppImage = './modules/LibreOffice-still.standard-x86_64.AppImage'; + +// tmpディレクトリのパス +const tmpDir = path.join(__dirname, 'tmp'); + +// tmpディレクトリが存在しない場合は作成 +if (!fs.existsSync(tmpDir)) { + fs.mkdirSync(tmpDir); +} + +// POSTエンドポイント +module.exports = (app) => { + app.post('/pdfConvert', express.json({ limit: '30mb' }), async (req, res) => { + try { + // APIキーのチェック + const apiKey = req.headers['x-api-key']; + if (!apiKey || apiKey !== API_KEY) { + console.log('APIキーが不正です'); + return res.status(401).json({ error: 'APIキーが不正です' }); + } + + const { base64, fileName } = req.body; + if (!base64 || !fileName) { + return res.status(400).json({ error: 'base64とfileNameは必須です' }); + } + const pdfBuffer = await convertBase64ToPdf(base64, fileName); + const safeFileName = encodeURIComponent(path.parse(fileName).name); // ファイル名をエスケープ + res.set({ + 'Content-Type': 'application/pdf', + 'Content-Disposition': `attachment; filename="${safeFileName}.pdf"` + }); + res.send(pdfBuffer); + } catch (err) { + console.error('変換エラー:', err.message); + res.status(500).json({ error: err.message }); + } + }); +}; + +// PDF変換処理 +async function convertBase64ToPdf(base64Data, originalFileName) { + const tempId = uuidv4(); // 一意のIDを生成 + const ext = path.extname(originalFileName) || '.tmp'; // 拡張子を取得、なければ.tmpを使用 + const orgPath = path.join(tmpDir, `${tempId}${ext}`); // 一時的なファイルのパス + const pdfPath = path.join(tmpDir, `${tempId}.pdf`); + + // base64データをファイルに保存 + fs.writeFileSync(orgPath, Buffer.from(base64Data, 'base64')); + + // LibreOfficeでPDF変換(タイムアウト60秒) + await new Promise((resolve, reject) => { + const child = execFile( + libreofficeAppImage, + [ + '--headless', + '--convert-to', 'pdf', + '--outdir', tmpDir, + orgPath + ], + (error, stdout, stderr) => { + if (error) { + console.error('stderr:', stderr); + reject(new Error(`LibreOfficeエラー: ${stderr}`)); + } else { + resolve(); + } + } + ); + const timeout = setTimeout(() => { + child.kill('SIGTERM'); // SIGTERMを使用してプロセスを終了 + reject(new Error('PDF変換がタイムアウトしました(60秒経過)')); + }, 60000); + child.on('exit', () => clearTimeout(timeout)); + }); + + // PDFファイルを読み込む + if (!fs.existsSync(pdfPath)) { + throw new Error('PDF変換に失敗しました'); + } + const pdfBuffer = fs.readFileSync(pdfPath); + + // cleanup + try { + if (fs.existsSync(orgPath)) fs.unlinkSync(orgPath); + if (fs.existsSync(pdfPath)) fs.unlinkSync(pdfPath); + } catch (cleanupError) { + console.error('クリーンアップエラー:', cleanupError.message); + } + + return pdfBuffer; +} + diff --git a/oldCode/express/modules/pdfMerge.js b/oldCode/express/modules/pdfMerge.js new file mode 100644 index 00000000..676c003b --- /dev/null +++ b/oldCode/express/modules/pdfMerge.js @@ -0,0 +1,97 @@ +const fs = require('fs'); +const path = require('path'); +const { execFile } = require('child_process'); +const { v4: uuidv4 } = require('uuid'); +const express = require('express'); // JSON解析用にexpressをインポート +const { API_KEY } = require('../config.js'); // API_KEYをインポート + +// tmpディレクトリのパス +const tmpDir = path.join(__dirname, 'tmp'); + +// tmpディレクトリが存在しない場合は作成 +if (!fs.existsSync(tmpDir)) { + fs.mkdirSync(tmpDir); +} + +// POSTエンドポイント +module.exports = (app) => { + app.post('/pdfMerge', express.json({ limit: '30mb' }), async (req, res) => { + try { + // APIキーのチェック + const apiKey = req.headers['x-api-key']; + if (!apiKey || apiKey !== API_KEY) { + console.log('APIキーが不正です'); + return res.status(401).json({ error: 'APIキーが不正です' }); + } + + // base64_1, base64_2, fileName の3つをチェック + const { base64_1, base64_2, fileName } = req.body; + if (!base64_1 || !base64_2 || !fileName) { + return res.status(400).json({ error: 'base64_1, base64_2, fileNameは必須です' }); + } + + const pdfBuffer = await mergeBase64ToPdf(base64_1, base64_2, fileName); + const safeFileName = encodeURIComponent(path.parse(fileName).name); // ファイル名をエスケープ + res.set({ + 'Content-Type': 'application/pdf', + 'Content-Disposition': `attachment; filename="${safeFileName}.pdf"` + }); + res.send(pdfBuffer); + } catch (err) { + console.error('変換エラー:', err.message); + res.status(500).json({ error: err.message }); + } + }); +}; + +// PDF結合処理 +async function mergeBase64ToPdf(base64Data1, base64Data2) { + const tempId = uuidv4(); // 一意のIDを生成 + const orgPath1 = path.join(tmpDir, `${tempId}_1.pdf`); + const orgPath2 = path.join(tmpDir, `${tempId}_2.pdf`); + const mergedPath = path.join(tmpDir, `${tempId}_merged.pdf`); + + // base64データをファイルに保存 + fs.writeFileSync(orgPath1, Buffer.from(base64Data1, 'base64')); + fs.writeFileSync(orgPath2, Buffer.from(base64Data2, 'base64')); + + // pdfuniteでPDF結合 + await new Promise((resolve, reject) => { + const child = execFile( + 'pdfunite', + [orgPath1, orgPath2, mergedPath], + (error, stdout, stderr) => { + if (error) { + console.error('stderr:', stderr); + reject(new Error(`pdfuniteエラー: ${stderr}`)); + } else { + resolve(); + } + } + ); + const timeout = setTimeout(() => { + child.kill('SIGTERM'); // SIGTERMを使用してプロセスを終了 + reject(new Error('PDF変換がタイムアウトしました(60秒経過)')); + }, 60000); + child.on('exit', () => clearTimeout(timeout)); + }); + + // 結合されたPDFを読み込む + if (!fs.existsSync(mergedPath)) { + throw new Error('PDF結合に失敗しました'); + } + const pdfBuffer = fs.readFileSync(mergedPath); + + // cleanup + try { + if (fs.existsSync(orgPath1)) fs.unlinkSync(orgPath1); + if (fs.existsSync(orgPath2)) fs.unlinkSync(orgPath2); + if (fs.existsSync(mergedPath)) fs.unlinkSync(mergedPath); + } catch (cleanupError) { + console.error('クリーンアップエラー:', cleanupError.message); + } + + return pdfBuffer; +} + + diff --git a/oldCode/express/modules/pdfToText.js b/oldCode/express/modules/pdfToText.js new file mode 100644 index 00000000..0df810b6 --- /dev/null +++ b/oldCode/express/modules/pdfToText.js @@ -0,0 +1,98 @@ +const fs = require('fs'); +const path = require('path'); +const { execFile } = require('child_process'); +const { v4: uuidv4 } = require('uuid'); +const express = require('express'); // JSON解析用にexpressをインポート +const { API_KEY } = require('../config.js'); // API_KEYをインポート + +// tmpディレクトリのパス +const tmpDir = path.join(__dirname, 'tmp'); + +// tmpディレクトリが存在しない場合は作成 +if (!fs.existsSync(tmpDir)) { + fs.mkdirSync(tmpDir); +} + +// POSTエンドポイント +module.exports = (app) => { + app.post('/pdfToText', express.json({ limit: '30mb' }), async (req, res) => { + try { + // APIキーのチェック + const apiKey = req.headers['x-api-key']; + if (!apiKey || apiKey !== API_KEY) { + console.log('APIキーが不正です'); + return res.status(401).json({ error: 'APIキーが不正です' }); + } + + // base64, fileName の3つをチェック + const { base64, fileName } = req.body; + if (!base64 || !fileName) { + return res.status(400).json({ error: 'base64とfileNameは必須です' }); + } + + const txtBuffer = await pdfToTextConvert(base64, fileName); + const safeFileName = encodeURIComponent(path.parse(fileName).name); // ファイル名をエスケープ + res.set({ + 'Content-Type': 'text/plain', + 'Content-Disposition': `attachment; filename="${safeFileName}.txt"` + }); + res.send(txtBuffer); + } catch (err) { + console.error('変換エラー:', err.message); + res.status(500).json({ error: err.message }); + } + }); +}; + +// PDF テキスト変換処理 +async function pdfToTextConvert(base64) { + const tempId = uuidv4(); // 一意のIDを生成 + const pdfPath = path.join(tmpDir, `${tempId}.pdf`); + const outputPath = path.join(tmpDir, `${tempId}.txt`); + + // base64データをファイルに保存 + fs.writeFileSync(pdfPath, Buffer.from(base64, 'base64')); + + // pdfuniteでテキストに変換 + await new Promise((resolve, reject) => { + const child = execFile( + 'pdftotext', + [ + '-layout', + '-nopgbrk', + pdfPath, + outputPath + ], + (error, stdout, stderr) => { + if (error) { + console.error('stderr:', stderr); + reject(error); + } else { + resolve(); + } + } + ); + // 60秒タイムアウト + const timeout = setTimeout(() => { + child.kill('SIGTERM'); // SIGTERMを使用してプロセスを終了 + reject(new Error('テキストへの変換がタイムアウトしました(60秒経過)')); + }, 60000); + child.on('exit', () => clearTimeout(timeout)); + }); + + // 変換されたテキストファイルを読み込み + if (!fs.existsSync(outputPath)) { + // cleanup + fs.unlinkSync(pdfPath);; + throw new Error('テキストへの変換に失敗しました'); + } + const txtBuffer = fs.readFileSync(outputPath); + + // cleanup + fs.unlinkSync(pdfPath); + fs.unlinkSync(outputPath); + + return txtBuffer; +} + + diff --git a/oldCode/express/package-lock.json b/oldCode/express/package-lock.json new file mode 100644 index 00000000..34e337f7 --- /dev/null +++ b/oldCode/express/package-lock.json @@ -0,0 +1,1154 @@ +{ + "name": "express", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "body-parser": "^2.2.0", + "cors": "^2.8.5", + "express": "^5.1.0", + "fetch": "^1.1.0", + "fs": "^0.0.1-security", + "https": "^1.0.0", + "jsonwebtoken": "^9.0.2", + "node-fetch": "^3.3.2", + "url": "^0.11.4", + "uuid": "^11.1.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/biskviit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/biskviit/-/biskviit-1.0.1.tgz", + "integrity": "sha512-VGCXdHbdbpEkFgtjkeoBN8vRlbj1ZRX2/mxhE8asCCRalUx2nBzOomLJv8Aw/nRt5+ccDb+tPKidg4XxcfGW4w==", + "license": "MIT", + "dependencies": { + "psl": "^1.1.7" + }, + "engines": { + "node": ">=1.0.0" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha512-bl1LAgiQc4ZWr++pNYUdRe/alecaHFeHxIJ/pNciqGdKXghaTCOwKkbKp6ye7pKZGu/GcaSXFk8PBVhgs+dJdA==", + "license": "MIT", + "dependencies": { + "iconv-lite": "~0.4.13" + } + }, + "node_modules/encoding/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==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "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==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-5O8TwrGzoNblBG/jtK4NFuZwNCkZX6s5GfRNOaGtm+QGJEuNakSC/i2RW0R93KX6E0jVjNXm6O3CRN4Ql3K+yA==", + "license": "MIT", + "dependencies": { + "biskviit": "1.0.1", + "encoding": "0.1.12" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "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==", + "license": "MIT", + "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/https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", + "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==", + "license": "ISC" + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "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==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "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==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "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" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.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==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/url": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", + "license": "MIT", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + } + } +} diff --git a/oldCode/express/package.json b/oldCode/express/package.json new file mode 100644 index 00000000..ee22727b --- /dev/null +++ b/oldCode/express/package.json @@ -0,0 +1,14 @@ +{ + "dependencies": { + "body-parser": "^2.2.0", + "cors": "^2.8.5", + "express": "^5.1.0", + "fetch": "^1.1.0", + "fs": "^0.0.1-security", + "https": "^1.0.0", + "jsonwebtoken": "^9.0.2", + "node-fetch": "^3.3.2", + "url": "^0.11.4", + "uuid": "^11.1.0" + } +} diff --git a/oldCode/express/pleasanterScript copy.js b/oldCode/express/pleasanterScript copy.js new file mode 100644 index 00000000..ea618469 --- /dev/null +++ b/oldCode/express/pleasanterScript copy.js @@ -0,0 +1,170 @@ +//参考スクリプト startLineworksSurvey.js +// botId = $('#Results_Class001').val(); +// surveyId = $('#Results_Class002').val(); +// targetId = $('#Results_Class003').val(); + +const WEBHOOK_URL = 'https://neo999.next-hd.net/lineworksSurvey/webhook'; +const API_KEY = 'pgqhLFWbDFu4Byz#4afNYX2F6Fa1&$KPjved$8%sUdTQV52caip#EpIKxYUkdd4S'; + +$p.events.on_grid_load = function () { + // アンケート一括発行ボタンの作成 + createSurveyAllButton(); +} + +function createSurveyAllButton() { + if ($('#surveyAll').length > 0) { + return; + } + + //処理内容 + let button = document.createElement('button'); + button.type = 'button'; + button.id = 'surveyAll'; + button.name = 'surveyAll'; + button.innerText = 'アンケート一括発行'; + button.setAttribute('onclick', 'sendSurveyAll()'); + $("#MainCommands button:last-child").after(button); + + $('#surveyAll').button({ + icon: 'ui-icon-disk' + }); + +} + +async function sendStartRequest(botId, surveyId, targetId, resultId) { + // const botId = $('#Results_Class001').val(); + // const surveyId = $('#Results_Class002').val(); + // const targetId = $('#Results_Class003').val(); + // const resultId = $p.id(); + + if (!surveyId || !targetId || !botId) { + console.log('surveyId, targetId, botId をソース内で設定してください。'); + + return false; + } + + if (!API_KEY) { + console.log('API_KEY is empty. Check config.js / environment.'); + return false; + } + + const payload = { surveyId, targetId, botId, resultId }; + const res = await fetch(WEBHOOK_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': API_KEY + }, + body: JSON.stringify(payload) + }); + + const text = await res.text(); + let body; + try { + body = JSON.parse(text); + } catch { + body = { raw: text }; + } + + if (!res.ok) { + console.log('Start request failed:', res.status, body); + process.exit(1); + } + /* + Start request success: { + result: 'ok', + sessionId: 'eef7af18-065a-4486-b5a5-9f65bca61d5a', + message: 'アンケートを開始しました' + } + */ + console.log('Start request success:', body); + + + + return true; +} + +/* +アンケート発行処理 +*/ +async function sendSurveyAll() { + let siteId = $p.siteId(); + + /*#Grid .grid-check 一覧のチェックボックスがオンのものの、data-idの値を全て取得し、配列に格納 */ + let checkedIds = []; + $('#Grid .grid-check:checked').each(function () { + checkedIds.push($(this).data('id')); + } + ); + console.log('Checked IDs:', checkedIds); + + + if (checkedIds.length === 0) { + console.log('No records selected. Please check at least one record.'); + + $p.clearMessage(); + $p.setMessage('#Message', JSON.stringify({ + Css: 'alert-warning', + Text: 'アンケートを一括発行するレコードの左端のチェックボックスをオンにしてください。' + })); + + //return; + } else { + + $p.apiGet({ + 'id': siteId, + 'data': { + 'View': { + 'ColumnFilterHash': { + 'Status': "['100']" + } + } + }, + 'done': function (data) { + console.log('API data:', data); + if (data.Response.TotalCount > 0) { + let records = data.Response.Data.filter(record => checkedIds.includes(record.ResultId)); + $.each(records, async function (index, record) { + let resultId = record.ResultId;; + let botId = record.ClassHash.Class001; + let surveyId = record.ClassHash.Class002; + let targetId = record.ClassHash.Class003; + console.log(`Processing record ${index + 1}/${records.length}:`, { resultId, botId, surveyId, targetId }); + + // アンケート開始リクエストを送信 + if (await sendStartRequest(botId, surveyId, targetId, resultId)) { + console.log(`Survey started for record ${resultId}. Updating status...`); + await updateSurveyRecord(resultId); + } + + }); + + $p.clearMessage(); + $p.setMessage('#Message', JSON.stringify({ + Css: 'alert-success', + Text: 'アンケートを一括発行しました' + })); + + setTimeout(() => { + location.reload(); + }, 3000); + + } + } + }); + } +} + +// アンケート開始後、ステータスを150に更新する関数 +async function updateSurveyRecord(resultId) { + $p.apiUpdate({ + 'id': resultId, + 'data': { + 'ApiVersion': 1.1, + 'Status': '150' + }, + 'done': function (data) { + console.log(data); + } + }); +} \ No newline at end of file diff --git a/oldCode/express/pleasanterScript.js b/oldCode/express/pleasanterScript.js new file mode 100644 index 00000000..846c2990 --- /dev/null +++ b/oldCode/express/pleasanterScript.js @@ -0,0 +1,86 @@ +//参考スクリプト startLineworksSurvey.js +// botId = $('#Results_Class001').val(); +// surveyId = $('#Results_Class002').val(); +// targetId = $('#Results_Class003').val(); + +const WEBHOOK_URL = 'https://neo999.next-hd.net/lineworksSurvey/webhook'; +const API_KEY = 'pgqhLFWbDFu4Byz#4afNYX2F6Fa1&$KPjved$8%sUdTQV52caip#EpIKxYUkdd4S'; + +async function sendStartRequest() { + const botId = $('#Results_Class001').val(); + const surveyId = $('#Results_Class002').val(); + const targetId = $('#Results_Class003').val(); + const resultId = $p.id(); + + if (!surveyId || !targetId || !botId) { + console.error('surveyId, targetId, botId をソース内で設定してください。'); + return false; + } + + if (!API_KEY) { + console.error('API_KEY is empty. Check config.js / environment.'); + return false; + } + + const payload = { surveyId, targetId, botId, resultId }; + const res = await fetch(WEBHOOK_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': API_KEY + }, + body: JSON.stringify(payload) + }); + + const text = await res.text(); + let body; + try { + body = JSON.parse(text); + } catch { + body = { raw: text }; + } + + if (!res.ok) { + console.error('Start request failed:', res.status, body); + process.exit(1); + } + /* + Start request success: { + result: 'ok', + sessionId: 'eef7af18-065a-4486-b5a5-9f65bca61d5a', + message: 'アンケートを開始しました' + } + */ + console.log('Start request success:', body); + + return true; +} + +/* +アンケート発行処理 +*/ +async function sendSurvey(proccessButton) { + + let procNum = proccessButton[0].id.replace('Process_', ''); + console.log('Process number:', procNum); + + if (await sendStartRequest()) { + // プロセス実行 + $p.setMessage('#Message', JSON.stringify({ + Css: 'alert-success', + Text: 'アンケートを発行しました' + })); + //$p.set($('#Results_Status'), 150); + + //プロセスを継続 + $p.execProcess($('#Process_' + procNum)); + return true; + } else { + $p.setMessage('#Message', JSON.stringify({ + Css: 'alert-error', + Text: 'アンケートの発行に失敗しました' + })); + + return false; + } +} \ No newline at end of file diff --git a/oldCode/express/sample.html b/oldCode/express/sample.html new file mode 100644 index 00000000..22e35359 --- /dev/null +++ b/oldCode/express/sample.html @@ -0,0 +1 @@ +
ID
状況
bot
アンケートテーブル
対象者
更新者
更新日時
484321

ケアセブンBot_12298662

車両点検報告_484182

松本 雅美 : masami.matsumoto@nexthd.jp

久保 恵

2026/05/22 金 15:32

484320

ケアセブンBot_12298662

車両点検報告_484182

山口 賢人 : kento.yamaguchi@nexthd.jp

久保 恵

2026/05/22 金 15:31

484319

ケアセブンBot_12298662

車両点検報告_484182

田部井 優果 : yuka.tabei@nexthd.jp

久保 恵

2026/05/22 金 15:31

484318

ケアセブンBot_12298662

車両点検報告_484182

園田 由茉 : yuma.sonoda@nexthd.jp

久保 恵

2026/05/22 金 15:31

484317

ケアセブンBot_12298662

車両点検報告_484182

芦田 友絵 : tomoe.ashida@nexthd.jp

久保 恵

2026/05/22 金 15:31

484316

ケアセブンBot_12298662

車両点検報告_484182

岸上 明子 : akiko.kishigami@nexthd.jp

久保 恵

2026/05/22 金 15:30

484315

ケアセブンBot_12298662

車両点検報告_484182

井尾 絢香 : ayaka.io@nexthd.jp

久保 恵

2026/05/22 金 15:30

484311

ケアセブンBot_12298662

車両点検報告_484182

須藤 悠輔 : yusuke.sudo@nexthd.jp

久保 恵

2026/05/22 金 15:30

484310

ケアセブンBot_12298662

車両点検報告_484182

山之内 香織 : kaori.yamanouchi@nexthd.jp

久保 恵

2026/05/22 金 15:29

484309

ケアセブンBot_12298662

車両点検報告_484182

山田 怜子 : reiko.yamada@nexthd.jp

久保 恵

2026/05/22 金 15:29

\ No newline at end of file diff --git a/oldCode/express/startLineworksSurvey.js b/oldCode/express/startLineworksSurvey.js new file mode 100644 index 00000000..ed87b1f1 --- /dev/null +++ b/oldCode/express/startLineworksSurvey.js @@ -0,0 +1,55 @@ +'use strict'; + +const { API_KEY } = require('./config.js'); + +const WEBHOOK_URL = + process.env.LINEWORKS_SURVEY_WEBHOOK_URL || + 'https://neo999.next-hd.net/lineworksSurvey/webhook'; + +// ここで固定値を指定 +const surveyId = '335338'; +const targetId = 'kenichiro.nogi@works-nextgroup2'; +const botId = '11747661'; + +if (!surveyId || !targetId || !botId) { + console.error('surveyId, targetId, botId をソース内で設定してください。'); + process.exit(1); +} + +if (!API_KEY) { + console.error('API_KEY is empty. Check config.js / environment.'); + process.exit(1); +} + +async function main() { + const payload = { surveyId, targetId, botId }; + + const res = await fetch(WEBHOOK_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': API_KEY + }, + body: JSON.stringify(payload) + }); + + const text = await res.text(); + let body; + try { + body = JSON.parse(text); + } catch { + body = { raw: text }; + } + + if (!res.ok) { + console.error('Start request failed:', res.status, body); + process.exit(1); + } + + console.log('Start request success:', body); +} + +main().catch((err) => { + console.error('Unexpected error:', err); + process.exit(1); +}); diff --git a/oldCode/express/style.css b/oldCode/express/style.css new file mode 100644 index 00000000..5764f8f6 --- /dev/null +++ b/oldCode/express/style.css @@ -0,0 +1,25 @@ +#Results_ClassAField, +#Results_NumAField, +#Results_NumBField, +#Results_Class001Field, +#Results_Class002Field, +#Results_Class003Field, +#Results_Class004Field, +#Results_Class005Field, +#Results_Class006Field, +#Results_Class007Field, +#Results_Class008Field, +#Results_Class009Field, +#Results_Class010Field, +#Results_Class011Field { + width: 500px; + + .field-label { + width: 300px; + margin-right: -300px; + } + + .field-control>.container-normal { + margin-left: 300px; + } +} \ No newline at end of file diff --git a/oldCode/express/webhooktest.js b/oldCode/express/webhooktest.js new file mode 100644 index 00000000..b2f77781 --- /dev/null +++ b/oldCode/express/webhooktest.js @@ -0,0 +1,34 @@ +const express = require('express'); +const https = require('https'); +const { SSL_OPTIONS } = require('./config.js'); + +const app = express(); + +app.use((req, res, next) => { + console.log(`[${new Date().toISOString()}] ${req.method} ${req.originalUrl}`); + next(); +}); + +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +app.get('/webhook', (req, res) => { + console.log('GET /webhook headers:', req.headers); + res.status(200).send('ok'); +}); + +app.head('/webhook', (req, res) => { + console.log('HEAD /webhook headers:', req.headers); + res.status(200).end(); +}); + +app.post('/webhook', (req, res) => { + console.log('POST /webhook headers:', req.headers); + console.log('POST /webhook body:', req.body); + res.status(200).json({ message: 'Webhook received' }); +}); + +const PORT = 30399; +https.createServer(SSL_OPTIONS, app).listen(PORT, '0.0.0.0', () => { + console.log(`Webhook HTTPS server running on https://neo999.next-hd.net:${PORT}`); +}); \ No newline at end of file diff --git a/oldCode/express/仕様書.txt b/oldCode/express/仕様書.txt new file mode 100644 index 00000000..906c67dc --- /dev/null +++ b/oldCode/express/仕様書.txt @@ -0,0 +1,25 @@ +lineworksでトーク画面でアンケートを取りたい。アンケートの内容は事前にプリザンターで定義を登録できるようにしたい。 +アンケートは選択式、数字入力、自由入力の3種類を選べるようにする。 +アンケート結果は事前に指定したプリザンターのテーブルにレコードを登録したい。 +スクリプトはNode.jsを使用。Expressでサーバーを利用。 +sendMessage.jsでLineworksにトークを送る関数とAccessTyokenの取得関数は準備済み。 + +アンケートIDと宛先IDを指定して、最初にアンケートの開始を受け付ける処理を作成。 +ユーザーからの回答をすべて受信したら、まとめてプリザンターのレコードに保存する。 + +スクリプト名 +lineworksSurvey.js modules内に配置 +関数名 +getSurvey : プリザンターからアンケートの元となるデータを取得する処理 +sendSurvey : ユーザーにアンケートを送る処理 +getAnswer : 回答を受け取る処理 +sendAnswer : プリザンターに回答を保存する処理 + +lwSendMessage.js sendLineworksMessage を参考にアンケートを宛先のトークに送信する処理を作成する。 + +index.js に追加したモジュールを追加してexpressサーバーで稼働させる + +プリザンター(Pleasanter)からデータを取得するときはFetchを利用 +APIとURLはConfig.jsから取得 +テーブルIDはスクリプト内で定数で指定する +