From d4918762d2b089925dc1ac7f07ac42b82b2a5500 Mon Sep 17 00:00:00 2001 From: CyberRex <26585194+CyberRex0@users.noreply.github.com> Date: Mon, 25 May 2026 10:01:34 +0900 Subject: [PATCH] =?UTF-8?q?=E6=9C=AC=E7=95=AA=E7=94=A8Docker=20Compose?= =?UTF-8?q?=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 6 ++++++ .env.example | 1 + AGENTS.md | 29 +++++++++++++++++++++++++++++ Dockerfile | 22 ++++++++++++++++++++++ README.md | 37 +++++++++++++++++++++++++++++++++++++ development_status.md | 17 ++++++++++++++--- docker-compose.dev.yml | 31 +++++++++++++++++++++++++++++++ docker-compose.yml | 40 ++++++++++++++++++++++++++++++++++++++-- package.json | 2 +- src/server/config/env.js | 1 + src/server/index.js | 2 +- vite.config.js | 2 ++ 12 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.dev.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b991eab --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +node_modules +.pnpm-store +dist +.env +*.log +.git diff --git a/.env.example b/.env.example index 659e221..b1286f3 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ NODE_ENV=development +HOST=127.0.0.1 PORT=3000 DATABASE_URL=postgres://certremind:certremind@localhost:5432/certremind COOKIE_SECRET=replace-with-a-long-random-string diff --git a/AGENTS.md b/AGENTS.md index de28e69..69594ad 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,6 +29,8 @@ ```text pnpm install docker compose up -d postgres +docker compose up -d --build +docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build pnpm dev pnpm monitor:once pnpm monitor:worker @@ -45,10 +47,29 @@ Frontend: http://127.0.0.1:5173/ API: http://127.0.0.1:3000 ``` +Docker Compose: + +```text +本番相当: docker compose up -d --build +開発用: docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build +本番 URL: http://127.0.0.1:3000/ +開発 URL: http://127.0.0.1:5173/ +``` + +- `docker-compose.yml` は本番相当の標準 Compose として、PostgreSQL、Web/API アプリ、`monitor-worker` を起動する。 +- `docker-compose.dev.yml` は開発用 override として、アプリを bind mount し `pnpm dev` で Vite と API を起動する。 +- Docker CLI が Compose v2 サブコマンドに対応していない環境では、同じ引数で `docker-compose` を使う。 +- Compose のアプリ公開ポートは `127.0.0.1` に限定し、外部 NIC へ公開しない。 +- Compose の PostgreSQL はホストに公開せず、`app` / `monitor-worker` から内部ネットワーク経由でのみアクセスする。 +- コンテナ内では `HOST=0.0.0.0` を使い、ホスト側への公開範囲は Compose の `ports` で制御する。 + ## ディレクトリ構成 ```text db/schema.sql +Dockerfile +docker-compose.yml +docker-compose.dev.yml README.md public/push-sw.js src/client @@ -79,6 +100,7 @@ tests/urlPolicy.test.js 主な項目: - `NODE_ENV` +- `HOST` - `PORT` - `DATABASE_URL` - `COOKIE_SECRET` @@ -89,6 +111,8 @@ tests/urlPolicy.test.js 補足: +- `HOST` 未設定時は `127.0.0.1` で待ち受ける。Docker Compose ではコンテナ内到達性のため `0.0.0.0` を指定する。 +- ホスト実行時の `DATABASE_URL` は通常 `localhost:5432`、Compose 内のサービスでは `postgres:5432` を使う。ただし Compose 起動中の PostgreSQL はホストに公開しない。 - `OPENSSL_PATH` 未設定時は `openssl` を使う。 - Windows では Git 付属の `openssl.exe` を自動検出する実装がある。 - VAPID private key が未設定の場合、Push 実送信は失敗として `delivery_result` に記録される。 @@ -121,6 +145,9 @@ DDL は `db/schema.sql` に保管している。 注意: - `docker-compose.yml` は初回 DB 起動時に `db/schema.sql` を読み込む。既存 volume がある場合、schema の変更は自動再適用されない。 +- `docker-compose.yml` はアプリ本体と監視 worker も起動するため、DB だけを起動したい場合は `docker compose up -d postgres` を使う。 +- 開発用 Compose は `docker-compose.dev.yml` を重ねて使い、Vite の公開ポートも `127.0.0.1:5173` に限定する。 +- Compose の PostgreSQL へホストから直接接続する前提の作業は行わない。DB 操作が必要な場合はコンテナ内実行や一時的な明示設定を検討する。 ## 実装済み API @@ -317,6 +344,7 @@ pnpm monitor:worker - Webhook / Push の送信失敗は `delivery_result` に記録する。 - 監視ジョブの開始、終了、失敗は構造化ログで出力する。 - `pnpm monitor:worker` は 1 時間ごとに監視ジョブを実行する長時間起動プロセス。 +- 本番相当の Docker Compose では `monitor-worker` サービスとして `pnpm monitor:worker` を常駐起動する。 注意: @@ -363,6 +391,7 @@ pnpm monitor:worker - UI の削除操作には確認ダイアログを追加する。 - 既存の左サイドメニューとレスポンシブ挙動を崩さない。 - フロントエンドの入力チェックを追加しても、サーバー側検証を必ず維持する。 +- Docker Compose のアプリポートを変更する場合も、ホスト側 bind は `127.0.0.1` に限定する。 - 変更後は少なくとも以下を実行する。 ```text diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d80ceaa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM node:22-bookworm-slim + +WORKDIR /app + +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates openssl \ + && rm -rf /var/lib/apt/lists/* \ + && npm install -g pnpm@11.1.3 + +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +RUN pnpm install --frozen-lockfile + +COPY . . +RUN pnpm exec vite build + +ENV NODE_ENV=production \ + HOST=0.0.0.0 \ + PORT=3000 + +EXPOSE 3000 + +CMD ["pnpm", "start"] diff --git a/README.md b/README.md index 11e34f2..9c21cd3 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,39 @@ Frontend: http://127.0.0.1:5173/ API: http://127.0.0.1:3000 ``` +## Docker Compose + +Production-like startup: + +```text +docker compose up -d --build +``` + +If your Docker CLI does not provide the Compose v2 subcommand, use `docker-compose` with the same arguments. + +Production URL: + +```text +App: http://127.0.0.1:3000/ +``` + +This starts PostgreSQL, the Web/API application, and the hourly certificate monitor worker. The application container serves the built React assets from `dist` through the Hono server. + +Docker-based development startup: + +```text +docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build +``` + +Development URLs: + +```text +Frontend: http://127.0.0.1:5173/ +API: http://127.0.0.1:3000 +``` + +The Compose application port mappings bind to `127.0.0.1` only, so the application ports are not published on external network interfaces. PostgreSQL is not published to the host; only the `app` and `monitor-worker` services can reach it through the internal Compose network. Inside containers, the application binds to `0.0.0.0` through `HOST=0.0.0.0`; host exposure is controlled by Compose. + Run the one-shot certificate monitor: ```text @@ -58,6 +91,7 @@ Copy `.env.example` to `.env` for local development. | Name | Required | Default | Purpose | | --- | --- | --- | --- | | `NODE_ENV` | No | `development` | Runtime mode. `production` enables secure cookies. | +| `HOST` | No | `127.0.0.1` | Server listen host. Compose sets this to `0.0.0.0` inside containers. | | `PORT` | No | `3000` | API server port. | | `DATABASE_URL` | No | `postgres://certremind:certremind@localhost:5432/certremind` | PostgreSQL connection string. | | `COOKIE_SECRET` | Reserved | none | Reserved for future signed-cookie support. | @@ -66,9 +100,12 @@ Copy `.env.example` to `.env` for local development. | `VAPID_SUBJECT` | For Push | `mailto:admin@example.com` | VAPID contact subject. | | `OPENSSL_PATH` | No | `openssl` | OpenSSL executable path. On Windows, the app can also detect Git's bundled `openssl.exe`. | +For local host execution, `DATABASE_URL` normally points to `localhost:5432`. For Docker Compose services, it points to the internal service name: `postgres://certremind:certremind@postgres:5432/certremind`. + ## Operational Notes - Run `pnpm monitor:worker` as a long-lived Node process for hourly certificate checks. +- `docker compose up -d --build` runs the monitor worker as the `monitor-worker` service. - `pnpm monitor:once` remains available for manual checks or external schedulers. - The monitor limits concurrent external certificate checks and records per-site failures without stopping the whole run. - Webhook URLs and monitored site URLs must be HTTPS and reject localhost/private IPv4 targets. diff --git a/development_status.md b/development_status.md index 2173245..720da2f 100644 --- a/development_status.md +++ b/development_status.md @@ -1,6 +1,6 @@ # CertRemind 開発進捗 -最終更新: 2026-05-22 +最終更新: 2026-05-25 ## 現在の実装状況 @@ -10,7 +10,8 @@ - Hono.js ベースの API サーバー構成 - React / Vite ベースのフロントエンド構成 -- PostgreSQL 開発環境用の `docker-compose.yml` +- 本番相当 / 開発用に分離した Docker Compose 構成 +- Docker Compose での Web/API アプリと監視 worker 起動 - 環境変数サンプル `.env.example` - DB 論理定義に基づく `db/schema.sql` - DB 接続モジュール @@ -72,7 +73,8 @@ - API セキュリティ境界の統合テスト - 証明書監視処理の単体テスト - API 未捕捉エラーと監視ジョブの構造化ログ -- README に開発環境起動手順と環境変数一覧を追加 +- README に開発環境起動手順、Docker Compose 手順、環境変数一覧を追加 +- AGENTS に Docker Compose 構成と loopback 公開方針を追加 - `.gitignore` の整備 ## 作成・更新した主なファイル @@ -80,7 +82,10 @@ - `package.json` - `pnpm-lock.yaml` - `pnpm-workspace.yaml` +- `Dockerfile` +- `.dockerignore` - `docker-compose.yml` +- `docker-compose.dev.yml` - `.env.example` - `.gitignore` - `README.md` @@ -348,6 +353,8 @@ API 動作確認: ```text docker compose up -d postgres pnpm dev +docker compose up -d --build +docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build ``` 確認 URL: @@ -355,6 +362,7 @@ pnpm dev ```text Frontend: http://127.0.0.1:5173/ API: http://127.0.0.1:3000 +Docker App: http://127.0.0.1:3000/ ``` ## 既知の注意点 @@ -362,6 +370,9 @@ API: http://127.0.0.1:3000 - `pnpm` の build script 承認で `esbuild` を承認済み。 - サンドボックス環境では Vite / Vitest の設定解決時に権限付き実行が必要だった。 - `docker-compose.yml` は初回 DB 起動時に `db/schema.sql` を読み込む。既存 volume がある場合、schema の変更は自動再適用されない。 +- Compose のアプリ公開ポートは `127.0.0.1` に限定し、外部 NIC へ公開しない。 +- Compose の PostgreSQL はホストに公開せず、`app` / `monitor-worker` から内部ネットワーク経由でのみアクセスする。 +- コンテナ内では `HOST=0.0.0.0` で待ち受け、ホスト側への公開範囲は Compose の `ports` で制御する。 - 実装確認用に API 経由でテストユーザーと `https://example.com/` / `https://example.org/` / `https://example.net/` のサイトを登録済み。 - 実装確認用にアラート履歴を手動挿入済み。 - 実装確認用に Webhook と Push 購読情報を API 経由で登録済み。 diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..01b32d8 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,31 @@ +services: + app: + command: ['pnpm', 'dev'] + environment: + NODE_ENV: development + HOST: 0.0.0.0 + VITE_HOST: 0.0.0.0 + PORT: 3000 + DATABASE_URL: postgres://certremind:certremind@postgres:5432/certremind + ports: + - '127.0.0.1:5173:5173' + volumes: + - .:/app + - app-node-modules:/app/node_modules + depends_on: + postgres: + condition: service_healthy + + monitor-worker: + profiles: + - worker + command: ['pnpm', 'monitor:worker'] + environment: + NODE_ENV: development + DATABASE_URL: postgres://certremind:certremind@postgres:5432/certremind + volumes: + - .:/app + - app-node-modules:/app/node_modules + +volumes: + app-node-modules: diff --git a/docker-compose.yml b/docker-compose.yml index 5ac63cf..c272e61 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,8 +6,6 @@ services: POSTGRES_DB: certremind POSTGRES_USER: certremind POSTGRES_PASSWORD: certremind - ports: - - '5432:5432' volumes: - postgres-data:/var/lib/postgresql/data - ./db/schema.sql:/docker-entrypoint-initdb.d/001-schema.sql:ro @@ -17,5 +15,43 @@ services: timeout: 5s retries: 5 + app: + build: . + image: certremind-app:local + container_name: certremind-app + environment: + NODE_ENV: production + HOST: 0.0.0.0 + PORT: 3000 + DATABASE_URL: postgres://certremind:certremind@postgres:5432/certremind + COOKIE_SECRET: ${COOKIE_SECRET:-replace-with-a-long-random-string} + VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-} + VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-} + VAPID_SUBJECT: ${VAPID_SUBJECT:-mailto:admin@example.com} + OPENSSL_PATH: ${OPENSSL_PATH:-openssl} + ports: + - '127.0.0.1:3000:3000' + depends_on: + postgres: + condition: service_healthy + + monitor-worker: + image: certremind-app:local + container_name: certremind-monitor-worker + command: ['pnpm', 'monitor:worker'] + environment: + NODE_ENV: production + DATABASE_URL: postgres://certremind:certremind@postgres:5432/certremind + COOKIE_SECRET: ${COOKIE_SECRET:-replace-with-a-long-random-string} + VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-} + VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-} + VAPID_SUBJECT: ${VAPID_SUBJECT:-mailto:admin@example.com} + OPENSSL_PATH: ${OPENSSL_PATH:-openssl} + depends_on: + postgres: + condition: service_healthy + app: + condition: service_started + volumes: postgres-data: diff --git a/package.json b/package.json index 15fe459..eb0a8fb 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "concurrently \"pnpm dev:server\" \"pnpm dev:client\"", "dev:server": "node --watch src/server/index.js", - "dev:client": "vite --host 127.0.0.1", + "dev:client": "vite", "start": "node src/server/index.js", "monitor:once": "node src/server/jobs/monitorCertificates.js", "monitor:worker": "node src/server/jobs/monitorWorker.js", diff --git a/src/server/config/env.js b/src/server/config/env.js index a0cf054..7a2b660 100644 --- a/src/server/config/env.js +++ b/src/server/config/env.js @@ -1,5 +1,6 @@ export const env = { nodeEnv: process.env.NODE_ENV ?? 'development', + host: process.env.HOST ?? '127.0.0.1', port: Number.parseInt(process.env.PORT ?? '3000', 10), databaseUrl: process.env.DATABASE_URL ?? 'postgres://certremind:certremind@localhost:5432/certremind', diff --git a/src/server/index.js b/src/server/index.js index b75775a..d793eac 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -8,7 +8,7 @@ serve( { fetch: app.fetch, port: env.port, - hostname: '127.0.0.1', + hostname: env.host, }, (info) => { console.log(`CertRemind API listening on http://${info.address}:${info.port}`); diff --git a/vite.config.js b/vite.config.js index fd4489b..159e289 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,9 +1,11 @@ import react from '@vitejs/plugin-react'; +import { env } from 'node:process'; import { defineConfig } from 'vite'; export default defineConfig({ plugins: [react()], server: { + host: env.VITE_HOST ?? '127.0.0.1', port: 5173, proxy: { '/api': 'http://127.0.0.1:3000',