ken_nogi/NodeSrv/README.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

334 lines
27 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.

# NodeSrv
Node.js WEBアプリを個別に開発し、既存の [Dokploy](https://dokploy.com/) 環境AWS Lightsail上でセルフホスト稼働中へデプロイ・運用するためのプロジェクト。Synology NASは開発機のローカルストレージ用途であり、アプリの実行基盤ではない。
## 構成
```
NodeSrv/
apps/
_template/ # 新規アプリのひな形(コピーして使う)
<app-name>/ # 実際の各アプリ
deploy/
dokploy.env.example # Dokploy接続情報のテンプレート実値はdokploy.envに。gitignore済み
lightsail.env.example # AWS Lightsail接続情報のテンプレート実値はlightsail.envに。gitignore済み
AUTH_SECRETS.md # 認証基盤の機密情報Client Secret等。gitignore済み、リポジトリには含まれない
```
1アプリ = 1フォルダ = 1 Dokploy Application/Compose リソース。アプリ同士は完全に独立しており、片方の開発・再デプロイがもう片方に影響しない。
## 運用方針
- デプロイ先: DokployAWS Lightsail上のセルフホストPaaS
- Gitソース: 自前ホストのGitea`https://git73.next-hd.net/mygit-admin/NodeSrv.git`)経由でビルド
- 公開ドメイン: `*.apps.next-hd.net` 配下ワイルドカードAレコード登録済み。新規アプリは個別のDNS登録不要、`<app-name>.apps.next-hd.net`で命名しデプロイするだけでよい)
- TLS証明書: Let's EncryptTraefikのcertresolverで自動発行、HTTP-01方式。新規サブドメインも初回アクセス時に自動発行される
- 認証: **デフォルトは認証なしで公開。** 認証が必要なアプリのみ、下記「認証ゲートウェイ」章の手順でオプトインする(全アプリ共通の必須事項ではない)
新規アプリはまず認証なしの最小構成でデプロイし、稼働・疎通を確認してから、必要な場合のみ認証を追加する段階的な進め方を基本とする。
`dokploy45`/`auth91`/`gate66`等の既存インフラは `next-hd.net` 直下(ランダム数字命名)のまま。個別アプリ用の `apps.next-hd.net` サブゾーンとは名前空間を分離している。
## 新規アプリの作り方
1. `apps/_template``apps/<新アプリ名>` としてコピー
2. `package.json``name` を変更、必要な依存関係を追加
3. ローカルで開発(詳細は `apps/_template/README.md` および各アプリの README を参照)
4. `docker-compose.yml` 内の以下3つを実際の値に置換テンプレートのままで認証なし公開ができる。下記「ポートを`ports:`で直接ホスト公開しない」ルールのみ厳守)
- `your-app-name`: Dockerサービス名・Traefikルーター識別子内部識別子。外部には露出しない
- `yourapp.apps.next-hd.net`: 公開ドメイン(先頭ラベルは`your-app-name`と一致させる必要はなく、自由に決めてよい)
- ポート番号
5. Giteaへpush → Dokployに登録しデプロイ[.claude/skills/dokploy-webapp/SKILL.md](.claude/skills/dokploy-webapp/SKILL.md) 参照)
6. 認証が必要な場合のみ、下記「認証ゲートウェイ」章の手順を追加で適用
### 共通ルール
- **Compose の Provider は必ず「Raw」を選ぶ**Dokploy標準の「Domains」タブは使わない。Gitea自体もこの方式で稼働実績があり、後から認証ミドルウェアを追加する場合もシームレスに拡張できるため、無認証アプリでも統一して使う
- **ポートを `ports:` で直接ホスト公開しない。** `ports: - "3000:3000"` のようにホストへポートを直接公開すると、Traefikを経由しない直アクセス経路が生まれる将来認証を追加した場合の迂回経路にもなる。**必ず `expose` を使い、`dokploy-network` 経由でのみ到達可能にすること。**
## 認証ゲートウェイoauth2-proxy + Keycloak── 任意オプション
**デフォルトでは適用しない。** 特定のアプリに認証を必須化したい場合のみ、以下の手順で追加する。
アプリ側でログイン画面やOIDC処理を自前実装する必要は**ない**。認証はTraefik層oauth2-proxy経由で強制される。
### 前提となる構成
- IdP: Keycloak`https://auth91.next-hd.net`、realm: `nexthd`
- 認証ゲートウェイ: oauth2-proxy`https://gate66.next-hd.net`、Dokploy上の`app-gateway`プロジェクトで稼働)
- Cookieドメイン: `.next-hd.net`(この配下のサブドメイン間でログインセッションが共有される=一度ログインすれば他アプリも再ログイン不要)
- Dokploy管理画面: `https://dokploy45.next-hd.net`
### 追加ルール
#### 1. ドメインは必ず `next-hd.net` のサブドメインにする
Cookie共有の前提が `.next-hd.net` のため、別ドメイン・別TLD例: `next-hd.co.jp`側や外部ドメインで公開するとSSOが機能しない。
#### 2. `_template` の compose に認証ミドルウェアを1行追加する
`apps/_template/docker-compose.yml`共通ルール通りRaw provider・手書きラベルに、`...-websecure` ルーターへ以下の1行を追加するだけでよい。
```yaml
- traefik.http.routers.your-app-name-websecure.middlewares=oauth-errors@file,oauth-auth@file
```
`web`HTTPリダイレクト用ルーターには追加しないことHTTP→HTTPSへの転送のみを担当させる
> 補足: 複数アプリで1つのHostを共有し `/<app名>` パスプレフィックスで振り分ける方式DNS登録の手間を減らす狙いを検証中。確定後、この節に追記する。
### ログインユーザー情報の取得方法
認証済みリクエストには、oauth2-proxyが以下のヘッダーを付与してアプリに転送する。
| ヘッダー名 | 内容 |
|---|---|
| `X-Auth-Request-User` | Keycloak上のユーザー名 |
| `X-Auth-Request-Email` | メールアドレス |
**Node.js例:**
```js
const userEmail = req.headers['x-auth-request-email'];
const userName = req.headers['x-auth-request-user'];
```
**Python (Flask) 例:**
```python
user_email = request.headers.get('X-Auth-Request-Email')
user_name = request.headers.get('X-Auth-Request-User')
```
これらのヘッダーは、Traefik/oauth2-proxyを正しく通過したリクエストにのみ付与される。アプリ側で改めてJWT検証等を行う必要はないただし本番運用でセキュリティ要件が上がった場合は、アプリ側でもヘッダーの信頼境界を見直すこと。ポート直公開さえしていなければTraefik経由以外の到達手段がないため、現状はこの前提で問題ない
### ローカル開発時の注意
`localhost` でのローカル起動時はTraefik/oauth2-proxyを経由しないため、上記ヘッダーは存在しない。ローカル開発では以下のようなフォールバックを入れておくと開発しやすい。
```js
const userEmail = req.headers['x-auth-request-email'] || 'dev-local@next-hd.co.jp';
```
### 機密情報の扱い
- Keycloakのクライアントシークレットやoauth2-proxyのcookie secret、SendGrid APIキー等は、このリポジトリやアプリのコード・`.env`には**書かない**。値は `AUTH_SECRETS.md`gitignore済み・リポジトリ非追跡でのみ管理する。
- アプリ自体は認証ゲートウェイの設定を一切知る必要がない(ヘッダーを読むだけ)。
### トラブルシューティング
| 事象 | 確認ポイント |
|---|---|
| ずっと401が返る画面が真っ白 | `oauth-errors@file` がミドルウェアの1番目に指定されているか`oauth-errors@file,oauth-auth@file` の順序を保つ) |
| ログインループになる | Cookieドメインが `.next-hd.net` になっているか、アプリのドメインが `next-hd.net` 配下か |
| 別アプリで再ログインを求められる | 初回ログインからのCookie有効期限切れ、またはドメインが`next-hd.net`配下でない可能性 |
| X-Auth-Request-* ヘッダーが空 | `ports:` でポートを直接ホスト公開し、Traefikを迂回してアクセスしていないか確認 |
## インフラ環境詳細
対象サーバー: Dokploy (`dokploy45.next-hd.net`) / Keycloak (`auth91.next-hd.net`)
### インフラ全体サマリー
| 項目 | 値 |
|---|---|
| クラウド | AWS Lightsail(東京リージョン ap-northeast-1) |
| インスタンス名 | `nexthd-node-a-keycloak` |
| インスタンススペック | 4GB RAM/2vCPU/80GB SSD、Ubuntu 24.04 LTS |
| 静的IP | `52.193.142.134` |
| デプロイ管理基盤 | Dokploy(Docker Composeベース、Traefikリバースプロキシ内蔵) |
サブドメインには推測されにくいランダムな数字を付与する方針(`dokploy45`、`auth91`、`gate66`、`git73`等)。個別アプリ用の`apps.next-hd.net`サブゾーンとは名前空間を分離している。
| ドメイン | 用途 |
|---|---|
| `dokploy45.next-hd.net` | Dokploy管理画面 |
| `auth91.next-hd.net` | Keycloak(realm: `nexthd`) |
| `gate66.next-hd.net` | 個別アプリ用認証ゲートウェイ(oauth2-proxy) |
| `git73.next-hd.net` | Gitea(Dokployのビルドソース) |
| `*.apps.next-hd.net` | 個別開発アプリの公開先(ワイルドカードDNS) |
| Dokployプロジェクト | environment | サービス |
|---|---|---|
| `auth-infra` | production | `keycloak`(内部名 `auth-keycloak-yyjuz6`) |
| `mail-infra` | production | `postfix-relay` |
| `app-gateway` | production | `oauth2-proxy`(内部名 `appgateway-oauth2proxy-y7xl6r`) |
| `git-infra` | production | `gitea`(projectId `VGqtH-ONBrAObGLDANJb1` / environmentId `qdsi2v5W39qWHqYpmT21c`、composeId `jMFGNMUjbwzqanyo58rkD`、Dokploy-Gitea連携giteaId `O5-CqLQwVdlzXw3KfmN-8`) |
| `webapps` | production | 個別開発アプリ一式(projectId `3T-drY1Q377JcLeTiNTDK` / environmentId `Cm0HjMIFyl11UdIcIGRy8`) |
サーバーOS初期設定(スワップ追加、ufw、fail2ban、自動セキュリティアップデート等)とDokployインストール自体は初回構築時に完了済み。再構築が必要な場合の手順は過去の作業ログ(Cowork上の対話記録)を参照すること。
### Keycloak
- Dokployプロジェクト: `auth-infra` / environment: `production`
- 管理コンソール: `https://auth91.next-hd.net/admin/master/console/`
- realm構成: `master`(Keycloak自体の管理者用)、`nexthd`(業務ユーザー・アプリ連携用。LINE WORKS SSO・app-gatewayのOIDCクライアントはすべてこちら)
- DB: PostgreSQL 16(同一Compose内、ボリューム永続化)
- 構築時のトラブルと対応: `command: start --optimized`は未ビルドイメージ非対応のため`start`に変更、`KC_DB_PASSWORD`と`POSTGRES_PASSWORD`不一致でクラッシュループ、管理コンソールのmixed content問題は`KC_PROXY: edge`(非推奨)を`KC_PROXY_HEADERS: xforwarded` + `KC_HTTP_ENABLED: true`に変更して解消
### mail-infra (postfix-relay)
Dokployプロジェクト「mail-infra」→サービス「postfix-relay」(Docker Compose、production環境)
- コンテナ名: `mailinfra-postfixrelay-jodrpy`
- 内部ホスト名: `postfix-relay`(dokploy-networkの他コンテナから到達可能)
- リッスンポート: 587(expose、外部公開なし)
- ネットワーク: `dokploy-network`(external)
- Compose環境変数: `ALLOWED_SENDER_DOMAINS=next-hd.co.jp`、`RELAYHOST="[smtp.sendgrid.net]:587"`、`RELAYHOST_USERNAME=apikey`、`RELAYHOST_PASSWORD=<AUTH_SECRETS.md参照>`、`POSTFIX_smtp_tls_security_level=encrypt`、`POSTFIX_smtpd_tls_security_level=none`
- `POSTFIX_smtpd_tls_security_level=none`は内部ネットワーク(`dokploy-network`)限定の通信が前提の意図的な設定(自己署名証明書によるTLSエラー回避のため)。外部(さくらVPS等)から使う場合はポート587の外部公開・SASL認証追加・TLS再設計が別途必要(未実施)
### Dokploy通知設定(Settings > Notifications)
- Name: `postfix-relay`
- SMTP Server: `postfix-relay` / Port: `587`
- Username: `noreply`(ダミー値、認証は使用されません)
- Password: ダミー値(画面上マスク済み、実際の認証には使われません)
- From Address / To Address: 設定値が `dokploy-admin@next-hd.co.jp` にリセットされる挙動あり編集ダイアログを開くたびに表示がリセットされるUI上の癖の可能性。正しい値(From: `no-reply@next-hd.co.jp`、To: `kenichiro.nogi@next-hd.co.jp`)を維持したい場合は保存前に必ず確認。
### Keycloak メール送信設定(nexthd レルム)
Realm settings > Email タブ
- Host: `postfix-relay` / Port: `587`
- SSL/StartTLS: OFF、Authentication: OFF
- From: `keycloak-admin@next-hd.co.jp`
### Keycloak クライアント「app-gateway」(nexthd レルム)
| 項目 | 値 |
|---|---|
| Client ID | `app-gateway` |
| Name | 個別アプリ認証ゲートウェイ |
| Client type | OpenID Connect / Confidential(Client authentication ON) |
| Authentication flow | Standard flow のみ |
| Valid redirect URIs | `https://gate66.next-hd.net/oauth2/callback` |
| Valid post logout redirect URIs | `https://gate66.next-hd.net/*` |
| Web origins | `https://gate66.next-hd.net` |
| Client Secret | `AUTH_SECRETS.md`参照 |
| Issuer URL | `https://auth91.next-hd.net/realms/nexthd` |
### Dokploy「app-gateway」プロジェクト / oauth2-proxy サービス
- プロジェクト: `app-gateway`(production環境)
- サービス: `oauth2-proxy`(コンテナ名: `appgateway-oauth2proxy-y7xl6r`)
- 公開ドメイン: `https://gate66.next-hd.net` (Container Port: 4180、HTTPS/Let's Encrypt)
- Compose環境変数: `OAUTH2_PROXY_PROVIDER=keycloak-oidc`、`OAUTH2_PROXY_OIDC_ISSUER_URL=https://auth91.next-hd.net/realms/nexthd`、`OAUTH2_PROXY_CLIENT_ID=app-gateway`、`OAUTH2_PROXY_CLIENT_SECRET=<AUTH_SECRETS.md参照>`、`OAUTH2_PROXY_COOKIE_SECRET=<AUTH_SECRETS.md参照>`、`OAUTH2_PROXY_COOKIE_DOMAINS=.next-hd.net`、`OAUTH2_PROXY_WHITELIST_DOMAINS=.next-hd.net`、`OAUTH2_PROXY_REDIRECT_URL=https://gate66.next-hd.net/oauth2/callback`、`OAUTH2_PROXY_EMAIL_DOMAINS="*"`、`OAUTH2_PROXY_HTTP_ADDRESS=0.0.0.0:4180`、`OAUTH2_PROXY_UPSTREAMS=static://202`、`OAUTH2_PROXY_REVERSE_PROXY="true"`、`OAUTH2_PROXY_SET_XAUTHREQUEST="true"`、`OAUTH2_PROXY_SKIP_PROVIDER_BUTTON="true"`、`OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL="true"`(下記LINE WORKS SSO連携の理由により必須)
- このComposeは`sourceType: Raw`(GeneralタブのCompose Fileに直接記述)で、`env_file`指定はない。**Dokploy Environment画面(.envファイル)に変数を設定しても一切反映されない**ため、環境変数を変更する場合は必ずCompose本体の`environment:`ブロックを直接編集すること(VSCode拡張`devpanda0.dokploy`等で編集可能)
### LINE WORKS SSO連携(Keycloak Identity Provider)
「全社システム統合・AI活用基盤構想」LINE WORKSをIdP、KeycloakをID連携ハブとする構想の第一段階。LINE WORKS ⇔ Keycloak間の連携は完了・稼働中。
**LINE WORKS側(Developer Console > SSO > WORKS as IdP)**: SAML App「Keycloak SSO」を登録・有効化済み。
| 項目 | 値 |
|---|---|
| ACS URL | `https://auth91.next-hd.net/realms/nexthd/broker/lineworks/endpoint` |
| SP Issuer(Entity Id) | `https://auth91.next-hd.net/realms/nexthd` |
| Name ID | EMAIL固定 |
| SSO URL(LINE WORKS発行) | `https://auth.worksmobile.com/saml2/idp/next-hd.co.jp` |
| Response Issuer(LINE WORKS発行) | `https://auth.worksmobile.com/saml2/next-hd.co.jp` |
**Keycloak側(nexthdレルム、Identity Providers > SAML v2.0)**: Alias `lineworks`。証明書は base64本文のみ貼り付け(`BEGIN/END CERTIFICATE`のヘッダー行を含めると改行崩れでパースエラーになるため含めないこと)。
**既知の注意点**:
- LINE WORKSはSP-initiated SSOのみ対応(IdP-initiatedは非対応)。必ずKeycloakのログイン画面(「LINE WORKSでログイン」ボタン)からログインを開始する必要がある
- LINE WORKS側SAML Apps一覧の「TEST」ボタンは有効化後も404になるが、実際のSP-initiatedログインフローには影響しない(LINE WORKS側のテスト機能自体の不具合と推測)
- LINE WORKS SAMLフェデレーション経由のKeycloakユーザーは`email_verified`クレームがfalse/未設定になる。oauth2-proxyはデフォルトでこれを拒否し`Error redeeming code during OAuth2 callback: email in id_token isn't verified`で500になるため、上記の`OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL=true`が必須(2026-07-26判明)
- LINE WORKSのSAML応答にはメールアドレス以外の属性(氏名等)が含まれないため、新規ユーザー初回ログイン時に毎回Keycloakの「Update Account Information」画面でプロフィール手動入力が発生する。運用開始前にRealm settings > User profileでEmail/First name/Last nameを必須項目から外すか、LINE WORKS側属性マッピングの可否を確認すること(**未対応**)
### Traefikミドルウェア(`/etc/dokploy/traefik/dynamic/middlewares.yml`)
既存の `redirect-to-https` に加え、以下を追記済み(他アプリのルーターに `oauth-auth` ミドルウェアを適用することで認証を強制できる)。
```yaml
http:
middlewares:
redirect-to-https:
redirectScheme:
scheme: https
permanent: true
oauth-auth:
forwardAuth:
address: https://gate66.next-hd.net/oauth2/auth
trustForwardHeader: true
authResponseHeaders:
- X-Auth-Request-User
- X-Auth-Request-Email
oauth-errors:
errors:
status:
- "401"
service: oauth-backend-svc
query: /?rd={url}
services:
oauth-backend-svc:
loadBalancer:
passHostHeader: false
servers:
- url: https://auth-redirect.apps.next-hd.net
```
**`statusRewrites`は使っていない**2026-07-25判明。Traefikの`errors`ミドルウェアは、リダイレクト先のHTMLボディは反映するが、レスポンスのステータスコード自体は元のエラーコード401のまま保持する既知の制約がある[Traefik issue #10720](https://github.com/traefik/traefik/issues/10720)。ブラウザはステータスコードに関わらずHTMLボディをレンダリングするため、代わりに`apps/auth-redirect``meta refresh`で`gate66.next-hd.net/oauth2/sign_in`へ自動遷移する薄いページ)を用意し、`oauth-errors`の転送先をここに向けることで自動リダイレクトを実現している。`auth-redirect`自体は`webapps`project配下の通常のComposecomposeId: `2OUXZISkrQ2Wk5uG86hcz`)としてデプロイ済み。デバッグ用に`auth-redirect.apps.next-hd.net`でも単体公開している(認証ミドルウェアなし)。
**`status`は`401`のみを対象にする(`401-403`にしない)**2026-07-26判明。アプリ側が独自に返す`403`(例: app-portalのallowlist拒否まで`oauth-errors`が「未ログイン」として拾ってしまうと、ログイン画面へ誘導→セッション有効なので即座に302で戻る→また403→また誘導…という無限リダイレクトループになる。oauth2-proxy自体は`401`か`202`しか返さないため、`403`を対象から外してもoauth2-proxy側の挙動には影響しない。403はアプリ側の権限拒否として、Traefikを素通りしてブラウザにそのまま表示させること。
### 認証ゲートウェイPoC関連の識別子
`authgw-poc`PoC用アプリと`auth-redirect`のDokploy上の識別子。トラブル調査時の参照用。
| 項目 | 値 |
|---|---|
| authgw-poc composeId | `VVUKrFGLIWN5NxS-h3Gax` |
| authgw-poc公開ドメイン | `poc-demo.apps.next-hd.net`(認証ゲートウェイ経由) |
| auth-redirect composeId | `2OUXZISkrQ2Wk5uG86hcz` |
| auth-redirect公開ドメイン | `auth-redirect.apps.next-hd.net`(デバッグ用、認証なし) |
### Dokploy CLI(claude code for vscode からのデプロイ用)
- インストール: `npm install -g @dokploy/cli`
- 認証: `dokploy auth -u https://dokploy45.next-hd.net -t <APIトークン>`(トークン値は`AUTH_SECRETS.md`または発行時にコピーしたクリップボードを参照。チャットには貼らない)
- Compose作成・デプロイの具体的なコマンド手順は [.claude/skills/dokploy-webapp/SKILL.md](.claude/skills/dokploy-webapp/SKILL.md) 参照新規Compose作成含め実地検証済み
- **CLIのGET系コマンドは実装バグにより全滅する**`@dokploy/cli` 0.29.4、`compose one`・`load-services`・`get-converted-compose`・`deployment all-by-compose`等、パラメータを取るGETコマンド全般。CLI内部の`apiGet`がtRPC入力を`{json: ...}`でラップせず渡しているため、サーバー側で400エラーになる。パラメータなしのコマンド`project all`等は影響を受けず、POST系コマンド`save-environment`等も正しく動作する。回避策として、CLIのconfig.json認証情報を読み取り、正しい形式でtRPCエンドポイントを直接fetchする一時スクリプトを使う。取得結果に機密情報gitea clientSecret・APIキー等が含まれることがあるため、内容は画面に出さずファイル経由で必要な値だけ抽出すること。
## アプリポータル(apps/app-portal)
`webapps` project配下の複数アプリを横断して起動/停止/再起動/バッチ手動実行/ログ確認を行う集中管理ポータル。設計の経緯・アーキテクチャは `docs/superpowers/specs/2026-07-25-app-portal-design.md`、実装計画は `docs/superpowers/plans/2026-07-25-app-portal.md` 参照。
- 公開ドメイン: `https://portal.apps.next-hd.net`(認証ゲートウェイ必須 + アプリ層allowlist二段防御)
- 各アプリをポータルに登録するには、`.env.example`のPORTAL_*変数(`apps/_template/README.md`参照)をDokploy Environment画面で設定する
- Dokploy tRPC APIを自前クライアント(`apps/app-portal/src/dokployClient.js`)で直接呼び出している。CLIのGET系コマンドが実装バグで全滅する問題(上記「Dokploy CLI」章参照)の回避策の実例でもある
- AWS Lightsailインスタンス操作用に`apps/app-portal/src/lightsailClient.js`と開発者マシン用CLI`apps/app-portal/scripts/lightsail-cli.js`を用意している(list/status/create/delete/start/stop/reboot/snapshot)。認証情報は`deploy/lightsail.env`(gitignore済み)に記入し、`apps/app-portal/`から`npm run lightsail -- <command>`、または`node --env-file=../../deploy/lightsail.env scripts/lightsail-cli.js <command>`で実行する。本番サーバーからは呼び出されない開発用ツールのため、SDK(`@aws-sdk/client-lightsail`)はdevDependencies扱い
### allowlist管理画面(`/admin`)
`https://portal.apps.next-hd.net/admin` から、ポータル本体へのアクセスを許可するメールアドレス一覧(allowlist)をブラウザ上で追加・削除できる。設計・実装計画は `docs/superpowers/specs/2026-07-26-allowlist-admin-design.md` / `docs/superpowers/plans/2026-07-26-allowlist-admin.md` 参照。
- **認証方式が異なる**: `/admin`配下はKeycloak/LINE WORKS SSOを経由せず、Master Key(`PORTAL_MASTER_KEY`環境変数)のみで保護される。Traefikで`/admin`用の専用ルーター(priority高め)を用意し、oauth2-proxy認証をバイパスしている
- ログイン後はセッションcookie(署名鍵は`PORTAL_MASTER_KEY`を流用、有効期限24時間)で認証状態を保持する
- allowlistの実データは`apps/app-portal/data/allowlist.json`にファイル永続化され、Dockerボリューム(`app_portal_data`)でコンテナ再作成をまたいで保持される。環境変数`PORTAL_ALLOWED_EMAILS`は、このファイルが存在しない初回起動時のみ使われる初期値
- `PORTAL_MASTER_KEY`は`AUTH_SECRETS.md`同様の機密情報として扱い、Dokploy Environment画面でのみ設定する(値をチャットやコードに出さない)
## 未着手タスク
- **LINE WORKS SSO ⇔ プリザンター連携**「全社システム統合・AI活用基盤構想」の本題: Keycloak(`nexthd`レルム)にプリザンター用SAMLクライアントを作成 → さくらVPS上の既存プリザンター(.NET Core版)の`Implem.Pleasanter/App_Data/Parameters/Authentication.json`にSAML SP設定を追加 → LINE WORKS → Keycloak → プリザンターの一連のSSOフロー動作確認、の3ステップが未着手。SP Entity ID・ACS URL等はプリザンターの実ドメイン確定後に設定すること(現時点で実ドメイン未確定)
- LINE WORKS新規ユーザー初回ログイン時のプロフィール入力画面の扱い上記LINE WORKS SSO連携の章参照を運用開始前に確定させること
- Keycloakの`nexthd`レルムにパスワードリセット等のメール通知設定(`master`レルムと同様のSMTP設定が必要かどうか未検証。LINE WORKSフェデレーションユーザーはLINE WORKS側で認証が完結するため優先度は低い
- 複数アプリで1つのHostを共有し`/<app名>`パスプレフィックスで振り分ける方式DNS登録の手間を減らす狙いを検証中。確定したら「認証ゲートウェイ」章に追記する
- `auth-redirect`のデバッグ用ドメイン公開(`auth-redirect.apps.next-hd.net`、認証なし)を残したままにしている。運用上不要になったら`docker-compose.yml`のラベル削除を検討
## 参考情報
- 認証ゲートウェイ: `https://gate66.next-hd.net`Dokploy `app-gateway` プロジェクト、oauth2-proxyサービス
- Keycloak管理コンソール: `https://auth91.next-hd.net/admin/master/console/`realm: `nexthd`
- Keycloakクライアント: `app-gateway`Client Credentials方式、Standard flowのみ有効
- Dokploy管理画面: `https://dokploy45.next-hd.net`
### 外部ドキュメント
- [Dokploy公式ドキュメント](https://docs.dokploy.com/)
- [LINE WORKS Developers - LINE WORKS を IdP とした SSO](https://developers.worksmobile.com/jp/docs/sso-idp)
- [LINE WORKS Developers - Service Provider 情報の登録](https://developers.worksmobile.com/jp/docs/sso-idp-register)
- [Keycloak公式ドキュメントIdentity Brokering](https://www.keycloak.org/docs/latest/server_admin/)
- [oauth2-proxy公式ドキュメントTraefik連携](https://oauth2-proxy.github.io/oauth2-proxy/configuration/integrations/traefik/)
- [Pleasanterユーザーマニュアル](https://pleasanter.org/en/manual)