Compare commits
3 Commits
ce58cb4be4
...
06c28e8b5a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06c28e8b5a | ||
|
|
5abf932fa6 | ||
|
|
8e8bbcde6e |
@ -0,0 +1,123 @@
|
|||||||
|
# org-master-sync n8n production移行 設計メモ
|
||||||
|
|
||||||
|
2026-09-04〜進行中。org-syncのn8nワークフロー群をproduction(本番プリザンター `nextoffice.next-hd.co.jp`)向けに構築する作業の設計意図・進捗記録。適宜追記していく。
|
||||||
|
|
||||||
|
## 1. 背景: 「模倣(Mock)テーブル」の設計意図
|
||||||
|
|
||||||
|
n8nの`org-sync`系ワークフロー(①③④④-1④-2④-3)は、Pleasanter標準のUsers/Depts/Groups API(`/api/users/get`等)ではなく、staging環境に用意した「模倣テーブル」(SiteId 504737=ユーザー, 504736=組織, 504738=グループ、いずれも`ReferenceType: Results`の通常テーブル)を参照して構築されていた。
|
||||||
|
|
||||||
|
**意図(本人確認、2026-09-05)**: 開発当時は本番データを直接いじれなかったため、模倣テーブルでフロー構築を先に進め、「宛先(SiteId/BASE_URL)を変更するだけで本番化できる」ことを狙った設計だった。
|
||||||
|
|
||||||
|
**判明した実態(2026-09-05調査)**: この狙いは実現できていなかった。
|
||||||
|
- 模倣テーブルはPleasanterのitems/export形式(表示名キーのJSON/CSV、例: `ログインID`, `組織`)を返すが、本物の標準API(`/api/users/get`, `/api/depts/get`, `/api/groups/get`)はシステムフィールド名(`UserId`/`LoginId`/`DeptId`/`GroupId`等)+Offset/TotalCountページング必須という全く別の形式。「宛先を変えるだけ」では動かず、レスポンス変換層の追加が必要だった
|
||||||
|
- LWグループマッピングテーブル(SiteId 504411、66件: 組織48・役職8・職級6・利用権限タイプ4)の「グループID(」列は模倣Groupsテーブル内の独自連番(1〜135+)で、本物のPleasanter Groups(GroupId実在範囲5〜69、全59件)とは無関係。名前での突合でも一致0件=本物のGroupsには1件も対応物が無い
|
||||||
|
|
||||||
|
## 2. n8nワークフロー構成(2026-09-04時点で判明した正式版一覧)
|
||||||
|
|
||||||
|
Proto系(`org-master-sync prototype`フォルダに格納済み、検証用として維持、変更しない)とは別に、正式版として以下が存在:
|
||||||
|
|
||||||
|
| ワークフロー | ID |
|
||||||
|
|---|---|
|
||||||
|
| org-sync: ① LINEWORKSマスタ→プリザンターマスタ情報更新 | pJBj07V5p4Av8f6E |
|
||||||
|
| org-sync: ② バックアップ | HuKpZQEQeU4iUCUf |
|
||||||
|
| org-sync: ③ マージマスターメイン処理 | ScoUpIshCsdO2KdI |
|
||||||
|
| org-sync: ④ プリザンターマスタ更新(④-0〜④-3の統合版) | 82y3S515PbEBLSDv |
|
||||||
|
| org-sync: ④-0 状態判定 | UQ7vtaajy9O93jhW |
|
||||||
|
| org-sync: ④-1 新規ユーザー作成 | 6NwZXgXY2eK1Wt4g |
|
||||||
|
| org-sync: ④-2 ユーザー更新・退職処理 | I9NimeonT8rM51KW |
|
||||||
|
| org-sync: ④-3 グループ更新 | QPt4JRHOgSuN0nnS |
|
||||||
|
|
||||||
|
## 3. staging/production分離(2026-09-04実施)
|
||||||
|
|
||||||
|
上記8本を複製し、`org-sync: staging: ...`(既存の8本をリネーム)と`org-sync: production: ...`(新規複製8本)に分離。
|
||||||
|
|
||||||
|
- production版はdataTable取得ノードのconfigKeyを`_TEST`→`_PROD`へ全置換(④-0〜④-3内の「設定値統合」Codeノードにハードコードされてた`_TEST`参照も含む)
|
||||||
|
- production版のWebhookパスは全部`-prod`サフィックスで一意化(例: `org-sync-01-lw-master-update-prod`)
|
||||||
|
- production版8本は全部`active: false`(未有効化)で作成。**稼働テストは今回のスコープ外、明示許可があるまで有効化しない**
|
||||||
|
- フォルダ構成: `org-master-sync`(production版8本)/ `org-master-sync staging`(staging版8本)/ `org-master-sync prototype`(Proto系旧版15本)/ `LINEWORKS`(通知系2本)
|
||||||
|
|
||||||
|
## 4. `workflow_config_values`(n8n Data Table)のPROD値修正
|
||||||
|
|
||||||
|
旧`_PROD`値(`PLEASANTER_MASTER_SITE_ID_PROD=502552`等)はいずれも404で存在しない廃止済みSiteIdだった。TESTが既に`504406〜504412`系列を使っていたのに対応させ、以下へ更新(2026-09-04、UI手動編集で完了):
|
||||||
|
|
||||||
|
| キー | 更新後の値 |
|
||||||
|
|---|---|
|
||||||
|
| PLEASANTER_MASTER_SITE_ID_PROD | 504412(社員・組織マスタ管理テーブル) |
|
||||||
|
| PLEASANTER_POSITION_MASTER_SITE_ID_PROD | 504408(LINEWORKS:役職マスタ) |
|
||||||
|
| PLEASANTER_LEVEL_MASTER_SITE_ID_PROD | 504410(LINEWORKS:職級マスタ) |
|
||||||
|
| PLEASANTER_USERTYPE_MASTER_SITE_ID_PROD | 504409(LINEWORKS:利用権限マスタ) |
|
||||||
|
| PLEASANTER_BACKUP_SITE_ID_PROD | 504407(社員・組織マスタ管理(副・バックアップ用)) |
|
||||||
|
| PLEASANTER_LW_GROUP_MAP_SITE_ID_PROD | 504411(LWグループマッピング、新規追加) |
|
||||||
|
|
||||||
|
n8n Data Table Public APIは行のupdate/delete専用エンドポイントが存在しない(PATCH/PUT/DELETEいずれも404か405、POSTのみ実装)。UI側の値セルはダブルクリックで編集・削除可能と判明(削除もUIから実施できた)。
|
||||||
|
|
||||||
|
## 5. 模倣テーブル→本物API移行の対応状況
|
||||||
|
|
||||||
|
| ワークフロー | 参照する模倣テーブル | 対応状況 |
|
||||||
|
|---|---|---|
|
||||||
|
| ③ マージマスターメイン処理(production版のみ) | Users(504737), Depts(504736) | **完了(2026-09-05)**。下記5-1参照 |
|
||||||
|
| ① LINEWORKSマスタ→プリザンターマスタ情報更新(production版のみ) | Groups(504738) | **完了(2026-09-05)**。下記5-2参照 |
|
||||||
|
| ④ プリザンターマスタ更新(統合版) | Users, Depts, Groups全部 | **完了(2026-09-05)**。下記5-3参照 |
|
||||||
|
| ④-1 新規ユーザー作成 | Depts, Groups, Users(create先も504737) | **完了(2026-09-05)**。下記5-4参照 |
|
||||||
|
| ④-2 ユーザー更新・退職処理 | Depts, Users, Groups | **完了(2026-09-05)**。下記5-4参照 |
|
||||||
|
| ④-3 グループ更新 | Groups | **完了(2026-09-05)**。下記5-4参照 |
|
||||||
|
|
||||||
|
これで①②③④④-0④-1④-2④-3、production版8本すべての模倣テーブル参照が解消された。
|
||||||
|
|
||||||
|
### 5-1. ③の対応内容(production版のみ、staging版は変更していない)
|
||||||
|
|
||||||
|
- `プリザンターUsers取得`ノード: `POST items/504737/export` → `POST api/users/get`(`View:{ApiGetMailAddresses:true}`付き、Offset+TotalCountページング設定済み)
|
||||||
|
- `プリザンターDepts取得`ノード: `POST items/504736/export` → `POST api/depts/get`(同上ページング設定)
|
||||||
|
- 「マージ計算」Codeノード: 標準APIの生データ(`UserId`/`LoginId`/`DeptId`等)を、旧・模倣テーブルと同じキー名(`ユーザID`/`ログインID`/`組織`等)の中間形式へ変換する処理を先頭に追加。`組織`は`DeptId`から`プリザンターDepts取得`の結果で逆引き(`deptIdToName`マップ)。これにより後続のマージ判定ロジック(`pleasanterUserToFields`等)は無改造で流用できている
|
||||||
|
- `SecretKey`・`Manager`は本番データ上使用者ゼロを確認済み(該当者が出ても空文字扱いになるだけで実害小)
|
||||||
|
|
||||||
|
### 5-2. ①の対応内容(production版のみ、2026-09-05完了)
|
||||||
|
|
||||||
|
当初は「LWグループマッピング66件が本物のGroupsに1件も対応物を持たないため本物化には書込み移行が前提になり、構築のみでは完結しない」と判断していたが、①のワークフロー自体が元々「本物のGroupsに存在しないグループを自動で新規作成する」設計(差分計算→groupsToCreate→グループ作成実行)だったため、**ワークフロー定義の書き換え自体は構築の範囲で完結できる**と整理し直した(実際の新規作成・無効化という書込みは、このワークフローを実際に実行したときに初めて発生する=稼働テスト禁止と同じ制約の範囲内)。
|
||||||
|
|
||||||
|
- `模倣Groups取得`ノード → `Groups取得`へ改名、`items/504738/export` → `api/groups/get`(Offset+TotalCountページング設定込み)
|
||||||
|
- `グループ作成実行`ノード: `items/504738/create`(`ClassA`=独自連番, `ClassB`=グループ名) → `api/groups/create`(`GroupName`のみ送信。Pleasanterが自動採番するGroupIdを使う設計に変更、独自連番の`nextGroupId`計算は廃止)
|
||||||
|
- `グループ無効化更新実行`ノード: `items/{itemId}/update`(`CheckA`) → `api/groups/{groupId}/update`(body`{Disabled}`のみ、GroupIdはURLパス側)。**実機検証済み(2026-09-05、本人許可の下stagingのGroupId=40「テスト用グループ」で確認)**。当初`api/groups/update`(bodyにGroupId)で試したところ404(nginxレベルで存在しないパス)、`items`系と同じ「IDをURLパスに含める」パターンが正解だった
|
||||||
|
- `作成結果ペアリング`ノード: 独自連番をそのまま通していた処理から、`グループ作成実行`のレスポンス(`Id`)を実際に取得して使うよう変更
|
||||||
|
- `差分計算`ノード: 標準APIから取得した本物のGroups一覧(`GroupName`→`GroupId`のマップ)を使い、LWグループマッピング66件の各行について「グループ名が本物のGroupsに実在するか」を追加でチェック。実在しなければ`groupsToCreate`に回して作り直し、実在するがグループID列がズレていれば`groupsToFixId`として修正対象に追加。これにより66件の移行問題もワークフロー実行時に自動的に解消される設計にした
|
||||||
|
- `新規マッピングCSV組立`ノード: `groupsToFixId`も統合。ここでCSVの列を共通ヘッダーにする際、fixId行が持たない列(分類・sourceKey等)を空文字で送ると既存値を上書き消去してしまう危険があった([[org-master-sync-n8n-mock-tables]]、8月の実データ事故と同種のパターン)ため、`groupsToFixId`側にも`category`/`sourceKey`/`groupName`の元の値を保持させて対応
|
||||||
|
|
||||||
|
**未検証・要注意点**: `グループ作成実行`のレスポンス形式(`Id`をトップレベルで返す前提、`createGroup`関数の実装から類推)、SplitInBatches(`グループ作成Loop`)内での入出力アイテムの対応順序、いずれも稼働テストなしでは確証が持てない。①のproduction版を初めて実行する際は、必ず少数件(1件)に絞ってから全体を回すこと。
|
||||||
|
|
||||||
|
(2026-09-05追記)`グループ更新`のURL(`/api/groups/{groupId}/update`)は本人許可の下stagingのGroupId=40「テスト用グループ」で実機検証済み。詳細は8-2節参照。
|
||||||
|
|
||||||
|
### 5-3. ④(統合版)の対応内容(production版のみ、2026-09-05完了)
|
||||||
|
|
||||||
|
`NodeSrv/apps/org-master-sync/sample/sampleアカウント作成スクリプト.js`(本番プリザンターのフィールドスクリプトとして稼働実績あり)から、Users/Groups標準APIの正確な使用例が得られたため、これをベースに実装した。
|
||||||
|
|
||||||
|
- `プリザンターUsers取得`/`プリザンターGroups取得`/`プリザンターDepts取得`: ①③と同様に標準API化(`/api/users/get`, `/api/groups/get`, `/api/depts/get`、Pagination込み)
|
||||||
|
- `users_create実行`: `items/504737/create` → `/api/users/create`。サンプルスクリプトの`createPleasanterUser`関数のbody構造をそのまま採用(`LoginId`/`Name`/`Password`/`DeptId`/`DeptCode`/`MailAddresses`等、フラットなフィールド名でHashネスト不要)
|
||||||
|
- `users_update実行`: `items/{itemId}/update` → `/api/users/{userId}/update`。**このURL自体はサンプルスクリプトに例が無く実機未検証**。Groups update(`/api/groups/{groupId}/update`)と同じ「IDをURLパスに含める」パターンからの類推
|
||||||
|
- `グループ取得`(メンバー判定用): `items/504738/export`(全件取得して対象1件をfind) → `/api/groups/{groupId}/get`(単体取得、対象1件だけ取得するよう効率化)
|
||||||
|
- `グループ更新実行`: `items/{itemId}/update` → `/api/groups/{groupId}/update`。サンプルスクリプトの`addUserGroup`関数を参考に、`GroupName`/`Body`/`GroupMembers`をまとめて送る形に変更(`Disabled`には触れない)
|
||||||
|
- **重要な発見**: 模倣Groupsの「グループメンバー(種別,キー,管理者フラグ 1行1メンバー)」というテキスト行形式は、本物の標準API`GroupMembers`配列の各要素(`'User,{userId},False'`という文字列)と**完全に同じフォーマット**だった(サンプルスクリプトで確認)。改行区切りテキスト⇔配列という違いだけで、行内容自体の変換は不要だった
|
||||||
|
- `対象判定`ロジックの変更点:
|
||||||
|
- 新規ユーザー作成時のメールアドレスを、旧実装の`社員マスタのメールアドレスのローカル部 + '@nexthd.jp'`固定ドメインから、**LINEWORKS側のメールアドレスをそのまま使う**方式に変更(本人指示、2026-09-05。ドメイン統一済みのため固定ドメイン付け替えは不要と判断)
|
||||||
|
- `DeptId`はUsers create時に必須のため、Depts標準APIの結果から`DeptName→DeptId`マップを新設(旧実装は`DeptCode`しか計算していなかった)
|
||||||
|
- 退職処理・組織変更の`userUpdateTasks.fields`を、模倣テーブルの内部列名(`無効`/`ロック`/`組織コード`等)から標準APIのフィールド名(`Disabled`/`Lockout`/`DeptCode`/`DeptId`/`MailAddresses`)へ変更
|
||||||
|
|
||||||
|
**未検証点**: `/api/users/{userId}/update`の実在(ダミーユーザー作成テストは本人許可が得られていないため確認できていない)。④のproduction版を実行する前に、退職処理か組織変更いずれかの経路で必ず1件だけ実行して確認すること。
|
||||||
|
|
||||||
|
### 5-4. ④-1・④-2・④-3の対応内容(production版のみ、2026-09-05完了)
|
||||||
|
|
||||||
|
④(統合版)を機能別に分割した部分抜粋版のため、同じ修正パターンを適用した。
|
||||||
|
|
||||||
|
- ④-1新規ユーザー作成: ④と同じくUsers/Depts/Groups取得を標準API化、`users_create実行`を`/api/users/create`化。新規ユーザーの初期グループ所属反映(`書き戻しグループ反映組立`)もGroupMembers配列ベースに変更
|
||||||
|
- ④-2ユーザー更新・退職処理: 同様にDepts/Users/Groups取得を標準API化。`対象展開`(退職処理・組織変更)を`/api/users/{userId}/update`化。模倣Usersテーブル固有の「itemId」概念(`userIdToItemId`によるUserId→模倣テーブル内部ID変換)は不要になった(標準APIはUserIdそのものが更新のキーになるため)
|
||||||
|
- ④-3グループ更新: Groups取得を標準API化、グループメンバー反映を`/api/groups/{groupId}/update`化
|
||||||
|
|
||||||
|
**重要な発見(バグ)**: ④(統合版)の対象判定コードに`const NEXT_GROUP_ALL_ID = '4'`という定義があったが、模倣Groupsテーブルにも本物のGroupsにも`GroupId=4`は存在しない誤った値だった(未使用の変数だったため実害はなかった)。④-1/④-2/④-3では同じ定数が正しく`'27'`(実在する「ネクストグループ全社員」グループ)になっていたため、そちらを正として全ワークフローで統一した。
|
||||||
|
|
||||||
|
**検証結果(2026-09-05追記)**: `/api/users/{userId}/update`は本人許可の下stagingのUserId=705「野木テストユーザ」で実機検証済み(`Disabled:false`送信、200 OK「"野木テストユーザ"を更新しました。」)。Groups/Depts系と同じ「IDをURLパスに含める」パターンで、④④④-1④-2の実装通りで問題なかった。①③④④-1④-2④-3、production版8本すべて未検証エンドポイントなしの状態になった。
|
||||||
|
|
||||||
|
## 6. 今回の制約(2026-09-05確認)
|
||||||
|
|
||||||
|
- n8nワークフロー定義の編集(PUT/POST /workflows、Data Table操作含む)は確認不要で全自動
|
||||||
|
- 本番プリザンター・LINEWORKSへの**読み取り**(GET系API、`items/{id}/export`、`items/{id}/get`含む)は許可
|
||||||
|
- 本番プリザンターへの**書き込み**(create/update/import/bulkupsert等)は不可。特に①のグループ作成、③のマージ結果書き込みは明示的に禁止
|
||||||
|
- ワークフローの稼働テスト(Webhookを実際に叩く、Manual Trigger実行)は不可
|
||||||
@ -0,0 +1,120 @@
|
|||||||
|
# 健康診断管理×LINEWORKSアンケート n8n化 設計書
|
||||||
|
|
||||||
|
- 作成日: 2026-09-05
|
||||||
|
- 対象: プリザンター「健康診断管理」プロジェクト(SiteId 508971)へLINEWORKS Bot経由アンケート機能を実装
|
||||||
|
- 位置付け: 既存Express実装(`OldCode/express/modules/lineworksSurvey.js`、仕様書`Pleasanter/LINEWORKSアンケート管理/docs/lineworks-survey-scheme.md`)と同等機能をn8nで新規構築。Expressの流用・移植ではなく作り直し
|
||||||
|
|
||||||
|
## 1. 背景・既存Express実装との違い
|
||||||
|
|
||||||
|
Express版(ケアセブンプロジェクト向け)は以下の構成だった。
|
||||||
|
|
||||||
|
- 「アンケート実行管理」を独立サイト(484186)として持ち、対象テーブル(例: 車両点検報告484182)へ回答を新規レコードcreateする
|
||||||
|
- Bot・対象者マスタも専用サイト(484184/484181)を新設
|
||||||
|
- セッション状態はExpressプロセスのメモリ(`Map`)で保持
|
||||||
|
|
||||||
|
今回の健康診断管理版は要件が異なるため、以下の方針で作り直す。
|
||||||
|
|
||||||
|
- **アンケート実行管理を独立サイトにせず、508971自体に統合**(Process+列追加)。回答は新規レコードcreateではなく、発行元の508971レコード自体へupdateで書き戻す
|
||||||
|
- **Botマスタは新設せず、既存の「LINEWORKSアンケート管理」プロジェクトのSiteId 484184をそのまま参照**
|
||||||
|
- **対象者マスタは新設せず、プリザンター標準Usersのメールアドレスをそのまま LINEWORKS の宛先ID(userId)として使う**(LINEWORKS ID=メールアドレスという前提。484181のような変換用マスタは不要)
|
||||||
|
- **サーバー実装はExpressでなくn8n**。セッション状態はn8nプロセスのメモリではなく、n8n Data Table(新規テーブル `survey_sessions`)で保持する
|
||||||
|
|
||||||
|
n8n環境自体の詳細(URL・API・既存ワークフロー・過去の罠)は`NodeSrv/apps/n8n/docs/n8n-guide.md`参照。以下の設計はこのガイドの制約(コンテナメモリ768MB、Data Table操作の罠、Schedule Trigger運用方針等)を踏まえている。
|
||||||
|
|
||||||
|
## 2. 全体アーキテクチャ
|
||||||
|
|
||||||
|
```
|
||||||
|
プリザンター(508971 健康診断管理データ本体)
|
||||||
|
└ Process「アンケート発行」ボタン(flowIdごとに1つ)
|
||||||
|
│ クライアントスクリプト fetch
|
||||||
|
▼
|
||||||
|
n8n: W1「健診アンケート開始」(Webhook)
|
||||||
|
└ resultId取得 → ClassC(Users参照列)からPleasanterUserId解決
|
||||||
|
└ POST api/users/get(View:{ApiGetMailAddresses:true})でメールアドレス取得
|
||||||
|
→ LINEWORKS userIdとしてそのまま使用
|
||||||
|
└ 508971自身のColumnsからflowId該当設問を抽出
|
||||||
|
└ n8n Data Table「survey_sessions」へセッション行Insert
|
||||||
|
└ 484184(LINEWORKS Bot)から該当BotId取得 → LINEWORKS Bot APIで第1問送信
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
LINEWORKS トーク(Bot⇔対象者)
|
||||||
|
│ 回答
|
||||||
|
▼
|
||||||
|
n8n: W2「健診アンケート回答受信」(Webhook, 署名検証)
|
||||||
|
└ targetId(=メール)でsurvey_sessions検索 → 型検証 → 回答蓄積
|
||||||
|
└ 未完了 → 次設問送信 / セッション更新
|
||||||
|
└ 完了 → 508971へupdate(Hashネスト)→ アンケート進捗列更新 → セッション削除
|
||||||
|
|
||||||
|
n8n: W3「健診アンケートタイムアウト監視」(Schedule Trigger、数分毎)
|
||||||
|
└ survey_sessions走査 → 期限切れセッションに終了通知 → 削除
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. プリザンター側(508971)変更点
|
||||||
|
|
||||||
|
| 追加項目 | 内容 |
|
||||||
|
|---|---|
|
||||||
|
| 設問対象列 | アンケートで使う列の`ExtendedControlCss`に`survey survey-<flowId>`トークンを付与。1列が複数flowに属してもよい(スペース区切りで複数トークン) |
|
||||||
|
| アンケート進捗列(新規、列名は実装時に採番) | アンケートの発行・回答状況を管理する専用列。既存業務Status(100申込中〜910対象外)とは別軸で持つ |
|
||||||
|
| Process「アンケート発行」 | flowIdごとに1つずつ追加。`OnClick`でn8n WebhookのW1へPOST(`resultId`・`flowId`を送信) |
|
||||||
|
| ClassC(Users参照、既存) | そのまま流用。ここからPleasanterUserIdを取得し、n8n側でメールアドレスを解決する |
|
||||||
|
|
||||||
|
新規アンケート(flowId)を追加する際の運用手順:
|
||||||
|
1. 該当列の`ExtendedControlCss`に`survey-<新flowId>`トークンを追記(既存列流用 or 新規列追加はflow内容次第で都度判断)
|
||||||
|
2. 508971にProcess「◯◯アンケート発行」を追加、`flowId`を固定値でn8n Webhookへ渡すよう設定
|
||||||
|
3. n8n側の設問抽出ロジックは`ExtendedControlCss`のトークンでflowIdを判定するため、n8nワークフロー自体の改修は不要(列追加・Process追加のみで新flowを増やせる設計)
|
||||||
|
|
||||||
|
アンケート進捗列の具体的な値・遷移(業務Statusとの整合を取るか等)は、実際のアンケートフロー内容を設計する際に個別に確定する。本設計書では「既存業務Statusとは独立した列で管理する」という方針のみを定める。
|
||||||
|
|
||||||
|
## 4. n8n側詳細
|
||||||
|
|
||||||
|
### Data Table「survey_sessions」(新規作成)
|
||||||
|
|
||||||
|
| 列 | 内容 |
|
||||||
|
|---|---|
|
||||||
|
| sessionId | UUID |
|
||||||
|
| resultId | 508971のResultId |
|
||||||
|
| flowId | アンケート種別識別子 |
|
||||||
|
| botId | 484184から取得したBotId |
|
||||||
|
| targetEmail | 対象者メールアドレス(LINEWORKS userId) |
|
||||||
|
| currentIndex | 現在の設問番号 |
|
||||||
|
| answersJson | ここまでの回答(JSON文字列) |
|
||||||
|
| expiresAt | 現在設問のタイムアウト時刻 |
|
||||||
|
| startedAt | 開始日時(無応答タイムアウト判定用) |
|
||||||
|
| firstAnswerAt | 初回応答有無判定用 |
|
||||||
|
|
||||||
|
n8n-guide.md 7-1(Clear出力の握り潰し)・7-2(複数行が後続へそのまま渡ると行数分繰り返し実行される)の罠を踏まえ、1セッション=1行の読み書きに限定し、複数セッションを一括で展開・集約する処理は入れない。
|
||||||
|
|
||||||
|
### ワークフロー構成
|
||||||
|
|
||||||
|
- **W1 開始**: Webhookトリガー(`X-API-Key`ヘッダー認証、508971のクライアントスクリプト専用)
|
||||||
|
- **W2 回答受信**: Webhookトリガー(LINEWORKS本体からの直接コールバック、`x-works-signature`をHMAC-SHA256検証。Express版`verifyWebhookSignature`と同等ロジック)
|
||||||
|
- **W3 タイムアウト監視**: Schedule Trigger(数分間隔)。n8n-guide.md 9章では「Schedule Trigger等の自動化は今回のスコープ外」としているが、**本機能ではセッションタイムアウト監視のために自動実行を解禁する**(今回の設計で追加する例外)
|
||||||
|
|
||||||
|
### 認証まわり
|
||||||
|
|
||||||
|
- LINEWORKS Bot APIメッセージ送信: 既存Credential「LINEWORKS Bot Private Key (v4)」(`Hw0qlEaGfLPnQWp1`)が流用できるか、実装時にBotIdとの対応関係を確認する
|
||||||
|
- 対象者メール解決: `POST api/users/get`(`View:{ApiGetMailAddresses:true}`、Offset+TotalCountページング)。org-master-sync③で実績のある実装パターンをそのまま使う
|
||||||
|
|
||||||
|
## 5. 設問の型・検証ルール
|
||||||
|
|
||||||
|
Express版(選択式/数値/自由記述の3種、日付は和暦・月日省略に対応)と同等の検証ロジックをn8n上(Codeノード)に再実装する。列名プレフィックスによる型判定(`Class*`→選択式 or 短文、`Num*`→数値、`Date*`→日付、`Description*`→長文)もExpress版を踏襲する。
|
||||||
|
|
||||||
|
## 6. エラーハンドリング・異常系
|
||||||
|
|
||||||
|
- キャンセルキーワード(取消/キャンセル/終了等)受信 → セッション削除+508971のアンケート進捗列を「キャンセル」相当へ
|
||||||
|
- 必須設問での無回答・スキップ要求 → 再送(同じ設問を再送信)
|
||||||
|
- 型検証失敗(数値・日付フォーマット不正)→ エラーメッセージ+再送
|
||||||
|
- セッション未検出(期限切れ後の回答等)→ 「アンケートが開始されていません」を返信
|
||||||
|
- LINEWORKS送信失敗・508971 update失敗 → n8n Execution History(Postgres保存)に残す。追加のログ実装はしない
|
||||||
|
|
||||||
|
## 7. 導入・検証方針
|
||||||
|
|
||||||
|
- n8n運用ルール(n8n-guide.md 9章)に従い、ワークフローの構築・編集自体は確認不要。**Webhook実行によるプリザンター書き込み・LINEWORKS送信を伴うテストは都度事前確認**
|
||||||
|
- 初回検証は1flow・1件のみで実施し、疎通確認後に複数flow展開へ進める
|
||||||
|
- 508971は本番の健診データそのものなので、検証は既存レコードを壊さない捨てレコードを用意して行う
|
||||||
|
|
||||||
|
## 8. 未確定事項(今後のflow設計時に個別確定)
|
||||||
|
|
||||||
|
- アンケートflowの具体的な設問内容・対象列・flowId命名は本設計書のスコープ外。flow追加のたびに3章の運用手順に従って個別設計する
|
||||||
|
- アンケート進捗列の具体的な値・遷移
|
||||||
|
- LINEWORKS Bot Private Key CredentialとBotIdの対応関係の実機確認
|
||||||
836
oldCode/LINEWORKS_Pleasanter/package-lock.json
generated
Normal file
836
oldCode/LINEWORKS_Pleasanter/package-lock.json
generated
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
oldCode/LINEWORKS_Pleasanter/package.json
Normal file
7
oldCode/LINEWORKS_Pleasanter/package.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"body-parser": "^2.2.0",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^5.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
45
oldCode/LINEWORKS_Pleasanter/server.js
Normal file
45
oldCode/LINEWORKS_Pleasanter/server.js
Normal file
@ -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`);
|
||||||
|
});
|
||||||
@ -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();
|
||||||
72
oldCode/LINEWORKS_sendMessage/lineworksCore.js
Normal file
72
oldCode/LINEWORKS_sendMessage/lineworksCore.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
142
oldCode/LINEWORKS_sendMessage/lwSendMessageBot.js
Normal file
142
oldCode/LINEWORKS_sendMessage/lwSendMessageBot.js
Normal file
@ -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`);
|
||||||
|
});
|
||||||
19
oldCode/LINEWORKS_sendMessage/lwSendMessageLogic.js.js
Normal file
19
oldCode/LINEWORKS_sendMessage/lwSendMessageLogic.js.js
Normal file
@ -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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
197
oldCode/LINEWORKS_sendMessage/package-lock.json
generated
Normal file
197
oldCode/LINEWORKS_sendMessage/package-lock.json
generated
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
oldCode/LINEWORKS_sendMessage/package.json
Normal file
16
oldCode/LINEWORKS_sendMessage/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
36
oldCode/LINEWORKS_sendMessage/sendMessage.js
Normal file
36
oldCode/LINEWORKS_sendMessage/sendMessage.js
Normal file
@ -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();
|
||||||
27
oldCode/LINEWORKS_sendMessage/server.js
Normal file
27
oldCode/LINEWORKS_sendMessage/server.js
Normal file
@ -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');
|
||||||
|
});
|
||||||
3
oldCode/express/.vscode/settings.json
vendored
Normal file
3
oldCode/express/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"git.ignoreLimitWarning": true
|
||||||
|
}
|
||||||
15
oldCode/express/Mail.json
Normal file
15
oldCode/express/Mail.json
Normal file
@ -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\" <systemadmin@next-hd.co.jp>",
|
||||||
|
"InternalDomains": "",
|
||||||
|
"Encoding": null,
|
||||||
|
"ContentEncoding": null
|
||||||
|
}
|
||||||
20
oldCode/express/config.js
Normal file
20
oldCode/express/config.js
Normal file
@ -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'
|
||||||
|
};
|
||||||
14
oldCode/express/config.js_old
Normal file
14
oldCode/express/config.js_old
Normal file
@ -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'
|
||||||
|
]
|
||||||
|
};
|
||||||
31
oldCode/express/index copy.js
Normal file
31
oldCode/express/index copy.js
Normal file
@ -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}`);
|
||||||
|
});
|
||||||
32
oldCode/express/index.js
Normal file
32
oldCode/express/index.js
Normal file
@ -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}`);
|
||||||
|
});
|
||||||
77
oldCode/express/lookupLineworksUser.js
Normal file
77
oldCode/express/lookupLineworksUser.js
Normal file
@ -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 <userId|email|externalKey:xxx> [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);
|
||||||
|
});
|
||||||
77
oldCode/express/modules/lineworksAuth.js
Normal file
77
oldCode/express/modules/lineworksAuth.js
Normal file
@ -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
|
||||||
|
};
|
||||||
77
oldCode/express/modules/lineworksAuth_old.js
Normal file
77
oldCode/express/modules/lineworksAuth_old.js
Normal file
@ -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
|
||||||
|
};
|
||||||
1515
oldCode/express/modules/lineworksSurvey.js
Normal file
1515
oldCode/express/modules/lineworksSurvey.js
Normal file
File diff suppressed because it is too large
Load Diff
58
oldCode/express/modules/lineworks_gptBot.js
Normal file
58
oldCode/express/modules/lineworks_gptBot.js
Normal file
@ -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); }, });
|
||||||
99
oldCode/express/modules/lwSendMessage.js
Normal file
99
oldCode/express/modules/lwSendMessage.js
Normal file
@ -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}` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
100
oldCode/express/modules/pdfConvert.js
Normal file
100
oldCode/express/modules/pdfConvert.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
97
oldCode/express/modules/pdfMerge.js
Normal file
97
oldCode/express/modules/pdfMerge.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
98
oldCode/express/modules/pdfToText.js
Normal file
98
oldCode/express/modules/pdfToText.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
1154
oldCode/express/package-lock.json
generated
Normal file
1154
oldCode/express/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
oldCode/express/package.json
Normal file
14
oldCode/express/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
170
oldCode/express/pleasanterScript copy.js
Normal file
170
oldCode/express/pleasanterScript copy.js
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
86
oldCode/express/pleasanterScript.js
Normal file
86
oldCode/express/pleasanterScript.js
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
oldCode/express/sample.html
Normal file
1
oldCode/express/sample.html
Normal file
File diff suppressed because one or more lines are too long
55
oldCode/express/startLineworksSurvey.js
Normal file
55
oldCode/express/startLineworksSurvey.js
Normal file
@ -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);
|
||||||
|
});
|
||||||
25
oldCode/express/style.css
Normal file
25
oldCode/express/style.css
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
34
oldCode/express/webhooktest.js
Normal file
34
oldCode/express/webhooktest.js
Normal file
@ -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}`);
|
||||||
|
});
|
||||||
25
oldCode/express/仕様書.txt
Normal file
25
oldCode/express/仕様書.txt
Normal file
@ -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はスクリプト内で定数で指定する
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user