ken_nogi/NodeSrv/docs/superpowers/specs/2026-07-25-app-portal-design.md
Kenichiro NOGI ce58cb4be4 初回コミット: dev配下(NodeSrv/Pleasanter等)をGitea管理下に統合
GitHub(nextgroup2706/ken_nogi)は今後使わず自社Gitea運用に切替え。
NodeSrvは旧リポジトリの履歴を破棄しファイルのみ統合(Dokploy用サービスアカウントは
別途mygit-admin/NodeSrv.gitに履歴あり)。notepmエクスポート(12GB)とPleasanter
インストーラzip(208MB)はサイズが大きいため.gitignoreで除外。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 15:37:06 +09:00

129 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# アプリポータル 設計書
作成日: 2026-07-25
## 背景・目的
NodeSrv配下に複数のNode.jsアプリ`authgw-poc`、`lineworks-board-sync`、`lineworks-cxtalk-sync`、`mlmanager`等をDokploy上へデプロイしていく運用が確立した。しかしDokployは稼働インフラの管理はできても、アプリを横断した実行管理起動・停止・手動実行・ログ確認をまとめて行う場所は提供しない。個別アプリのDokploy管理画面を都度行き来する運用は、アプリ数が増えるほど煩雑になる。
これを解消するため、登録済みアプリを一覧表示し、種別に応じた操作常駐WEBサービスの起動/停止/再起動、バッチジョブの手動実行トリガーとログ確認を1画面で行える集中管理ポータルを新設する。
## スコープ
- 対象: `webapps` project配下にDokploy Composeとして登録された全アプリ
- 非対象: mail-infra・auth-infra・app-gateway・Gitea等のインフラ系サービスアプリ運用者向けの管理画面であり、インフラ管理はDokploy管理画面で行う想定は変えない
## アーキテクチャ概要
```
[ブラウザ] → Traefik(oauth-auth) → [ポータル(Express+SSR)]
┌─────────────────────┼─────────────────────┐
│ │ │
Dokploy tRPC API 各アプリの管理API Dokploy tRPC API
(start/stop/deploy) (/admin/trigger等、 (ログ取得)
X-Portal-Secret検証)
```
ポータルは `NodeSrv/apps/app-portal`(仮称)として、他アプリと同じ `_template` パターンExpress、Dockerfile、Gitea経由デプロイで追加する。`webapps` project配下に独立したComposeとしてデプロイし、他アプリのデプロイ・再起動に影響しない既存の「1アプリ1フォルダ1Compose」独立性方針を踏襲
Dokploy APIトークン・アプリ間共有シークレット`PORTAL_SECRET`)はポータルの本番環境変数としてのみ保持し、`AUTH_SECRETS.md`と同様の機密情報として扱う。レスポンス・ログには一切出力しない。
## 前提: Dokploy CLI GET系400エラーの回避
調査の結果、`@dokploy/cli`v0.29.4)の`apiGet`実装がtRPCのGETクエリを`{json: params}`でラップせず生の`params`をJSON化しているだけの実装バグと判明した。DokployのtRPCサーバーは`{"json": {...}}`形式のラップを要求するため、CLIのGET系コマンド`compose one`等は軒並み400エラーになる。
Dokploy API自体は正常で、`x-api-key`ヘッダー付きで`GET {DOKPLOY_URL}/api/trpc/<endpoint>?input=<encodeURIComponent('{"json":{...}}')>`の形式で直接叩けば200が返ることを実証済み`compose.one`で確認済み)。
**方針**: CLI自体へのパッチは行わないグローバルnpmパッケージの一時修正は`npm update`等で消える。ポータルはCLIを介さず、この正しいtRPC形式で自前のDokployクライアントモジュールを実装する。
## 前提: 認証ゲートウェイの404問題先行タスク
ポータルはKeycloak/oauth2-proxy認証ゲートウェイ経由でのアクセスを前提とする。しかし前回のセッションで、Traefikルーターに`oauth-errors@file,oauth-auth@file`ミドルウェアを付けると404になる問題が発生し、原因は未特定のまま認証ミドルウェアを外して回避した経緯がある`README.md`参照)。
**ポータルの実装に着手する前に、この404の原因を特定し認証ゲートウェイを実際に機能させる必要がある。** これは本設計のスコープ外の先行タスクとして、実装計画の最初のステップに位置づける。
## 機能要件
### ダッシュボード1ページ
ロード時にサーバー側で `webapps` project配下のCompose一覧をDokploy tRPC APIから取得し、各Composeの`env`から以下のポータル用メタ情報をパースしてカード表示する。
| 環境変数 | 内容 | 必須 |
|---|---|---|
| `PORTAL_APP_TYPE` | `web` または `batch` | 必須 |
| `PORTAL_APP_LABEL` | カードの表示名(省略時は`appName` | 任意 |
| `PORTAL_APP_URL` | 「開く」リンク先web型のみ | web型は必須 |
| `PORTAL_TRIGGER_PATH` | 手動実行トリガーのパスbatch型のみ | batch型は必須 |
このメタ情報の規約は`apps/_template`・`README.md`・`SKILL.md`に追記し、新規アプリ作成時の標準手順に組み込む。
### カードの表示・操作
| 項目 | web型 | batch型 |
|---|---|---|
| ステータス表示 | 稼働中/停止中/デプロイ中(`composeStatus` | 同左 |
| 操作ボタン | Start / Stop / Restart | 今すぐ実行 |
| 追加リンク | 「開く」→ `PORTAL_APP_URL` を新規タブで開く | なし |
| 共通 | 「ログ表示」→ モーダルでログ取得・表示 | 同左 |
### 操作フロー
- **Start/Stop/Restart**: ポータル → Dokploy tRPC API`compose.start`/`compose.stop`等、要実装時に正確なエンドポイント名を確認)
- **今すぐ実行**: ポータル → 対象アプリの`PORTAL_TRIGGER_PATH`へPOST`X-Portal-Secret`ヘッダー付与。トリガー先は202を返して即応答し、実処理は非同期で行い結果は標準出力ログに書く
- **ログ表示**: ポータル → Dokploy tRPC APIでデプロイ/コンテナログ取得 → モーダル表示
**未検証事項(実装時に確認)**: Compose一覧取得・ログ取得の具体的なtRPCエンドポイント名とレスポンス形式。`compose.one`は動作確認済みだが、一覧系・ログ系は別エンドポイントで未検証。
## アプリ側の規約(`_template`拡張)
batch型アプリは、`_template`に追加する以下のパターンでトリガーエンドポイントを実装する。
```js
app.post(process.env.PORTAL_TRIGGER_PATH, (req, res) => {
if (req.headers['x-portal-secret'] !== process.env.PORTAL_SECRET) {
return res.sendStatus(403);
}
// 実処理を非同期でキューイングし即座に202を返す
res.sendStatus(202);
});
```
`PORTAL_SECRET`はポータルと各batch型アプリで共有する値。Dokploy Environment画面で個別に設定し、`AUTH_SECRETS.md`同様に機密情報として扱う。
## 認証・権限
- ポータルへのアクセス自体はTraefik層でoauth2-proxy認証必須`websecure`ルーターに`oauth-errors@file,oauth-auth@file`を付与。上記「先行タスク」解決後に適用)
- ログイン後、Express側のミドルウェアで`X-Auth-Request-Email`ヘッダーを`PORTAL_ALLOWED_EMAILS`カンマ区切り環境変数と照合。リスト外なら403Dokploy情報自体も見せない
- 将来的に利用者が増えKeycloakのGroup/Role連携が必要になった場合は、oauth2-proxy設定変更を伴う拡張として別途検討する現時点ではYAGNIによりallowlist方式のみ実装
## エラーハンドリング
- Dokploy API呼び出し失敗トークン切れ・400/500等: 該当カードに「状態取得失敗」を表示。`Promise.allSettled`等で個別処理し、1アプリの取得失敗が他アプリの表示を巻き込まないようにする
- 実行トリガー失敗応答なし・403: 「シークレット不一致」「応答なし」を区別してポータル側にエラー表示
- allowlist外ユーザーのアクセス: 403ページ表示操作ボタン・Dokploy情報を一切見せない
## テスト方針
- **ユニットテスト**: DokployクライアントモジュールtRPCリクエスト形成・レスポンスパース、allowlist照合ロジック、`.env`メタ情報パース処理
- **手動E2E確認**実機、Dokployへの実デプロイ後:
1. ダッシュボードにアプリ一覧が表示される
2. web型アプリのStart/Stop/Restartが機能する
3. batch型アプリの実行トリガーが機能する
4. ログ表示が機能する
5. allowlist外ユーザーでアクセスすると403になる
## 対象ファイル(実装時)
- `apps/app-portal/`新規、Express+SSR
- `apps/_template/docker-compose.yml`batch型メタ情報・トリガーエンドポイントパターンの追記
- `apps/_template/README.md`
- `README.md`(アプリポータルの章を追加)
- `.claude/skills/dokploy-webapp/SKILL.md``PORTAL_APP_TYPE`等の規約を追記)
## 未解決・後続タスク
- 認証ゲートウェイ404問題の原因特定本設計の先行タスク
- Dokploy tRPC APIのCompose一覧取得・ログ取得エンドポイントの実地検証
- `lineworks-board-sync`/`lineworks-cxtalk-sync`/`mlmanager`は現状`_template`のスケルトンのまま。ポータル動作確認用に、最低1つのbatch型アプリを実装しておく必要がある