From a0356e630ee09f8c37154800d9265953bfe20464 Mon Sep 17 00:00:00 2001 From: CyberRex <26585194+CyberRex0@users.noreply.github.com> Date: Mon, 25 May 2026 16:29:38 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=BB=E3=83=97=E3=83=83=E3=82=B7=E3=83=A5?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E3=81=AE=E4=BF=AE=E6=AD=A3=20=E3=83=BB?= =?UTF-8?q?=E3=83=A1=E3=83=8B=E3=83=A5=E3=83=BC=E3=82=92=E3=82=B9=E3=83=9E?= =?UTF-8?q?=E3=83=9B=E3=81=AB=E6=9C=80=E9=81=A9=E5=8C=96=20=E3=83=BB?= =?UTF-8?q?=E3=82=A2=E3=83=A9=E3=83=BC=E3=83=88=E9=80=81=E4=BF=A1=E6=B8=88?= =?UTF-8?q?=E3=81=BF=E3=81=AE=E6=9D=A1=E4=BB=B6=E3=81=8C=E5=86=8D=E5=BA=A6?= =?UTF-8?q?=E7=99=BA=E5=8B=95=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/schema.sql | 8 + development_status.md | 15 +- docker-compose.yml | 2 + src/client/components/Sidebar.jsx | 41 ++- src/client/routes/NotificationMethodsView.jsx | 177 ++++++++--- src/client/styles/app.css | 98 +++++-- src/server/modules/monitoring/monitor.js | 53 +++- .../modules/notificationMethods/routes.js | 85 ++++-- tests/apiSecurity.test.js | 177 +++++++++++ tests/monitoring.test.js | 276 +++++++++++++++++- 10 files changed, 844 insertions(+), 88 deletions(-) diff --git a/db/schema.sql b/db/schema.sql index 840c5be..6c8884d 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -106,11 +106,19 @@ CREATE TABLE IF NOT EXISTS site_alert_conditions ( threshold_hours integer NOT NULL CHECK (threshold_hours > 0 AND threshold_hours <= 17520), webhook_method_ids uuid[] NOT NULL DEFAULT '{}', push_enabled boolean NOT NULL DEFAULT false, + last_notified_certificate_expires_at timestamptz, + last_notified_at timestamptz, + last_notification_skipped_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), UNIQUE (site_id, condition_type, threshold_hours) ); +ALTER TABLE site_alert_conditions + ADD COLUMN IF NOT EXISTS last_notified_certificate_expires_at timestamptz, + ADD COLUMN IF NOT EXISTS last_notified_at timestamptz, + ADD COLUMN IF NOT EXISTS last_notification_skipped_at timestamptz; + CREATE INDEX IF NOT EXISTS site_alert_conditions_site_id_idx ON site_alert_conditions(site_id); DROP TRIGGER IF EXISTS site_alert_conditions_set_updated_at ON site_alert_conditions; diff --git a/development_status.md b/development_status.md index e4eb791..9eb3270 100644 --- a/development_status.md +++ b/development_status.md @@ -42,11 +42,14 @@ - Webhook 登録 API - Webhook 更新 API - Webhook 削除 API +- 現在ブラウザの Push 購読状態確認 API - Push 購読情報登録 API +- Push 購読情報解除 API - OpenSSL による証明書発行元・発行日時・期限取得処理 - 監視ジョブで取得した最新の証明書発行元・発行日時・期限・確認日時・取得失敗状態をサイトに保存 - サイトごとの通知条件評価 - 条件一致時のアラート履歴作成 +- 通知条件ごとの証明書期限単位の送信済み管理 - 証明書取得失敗時のアラート履歴作成 - Webhook 通知送信処理 - Push 通知送信処理 @@ -151,6 +154,7 @@ - `updated_at` 更新用トリガーを定義。 - ユーザー関連データは `ON DELETE CASCADE` を中心に設計。 - サイト削除時は通知条件も削除される。 +- 通知条件は、対象の証明書期限ごとに送信済み状態を保持する。 - アラート履歴のサイト参照は `ON DELETE SET NULL`。 ## API @@ -183,7 +187,9 @@ GET /api/notification-methods POST /api/notification-methods/webhooks PATCH /api/notification-methods/webhooks/:methodId DELETE /api/notification-methods/webhooks/:methodId +POST /api/notification-methods/push-subscription-status POST /api/notification-methods/push-subscriptions +DELETE /api/notification-methods/push-subscriptions GET /api/account PATCH /api/account/profile @@ -238,7 +244,9 @@ pnpm monitor:worker - Webhook 編集 - 確認ダイアログ付き Webhook 削除 - ブラウザ Push 通知の許可状態表示 + - 現在のブラウザの Push 登録状態表示 - VAPID public key がある場合の Push 購読登録 + - 現在のブラウザが登録済みの場合の Push 購読解除 - アカウント設定画面 - 表示名更新 - ダイアログでのパスワード更新 @@ -248,7 +256,7 @@ pnpm monitor:worker - 確認ダイアログ付きアカウント削除 - 認証後画面共通の左サイドメニュー - PC 幅では展開表示 - - スマートフォンなど狭い幅ではアイコンのみの畳み表示 + - スマートフォンなど狭い幅では上部の横メニューバーとハンバーガーメニューで表示 - サイト一覧、アラート履歴、通知方法、アカウント、ログアウトに対応 - 認証後画面の URL ルーティング - サイト一覧、サイト設定、アラート履歴、通知方法、アカウントに個別 URL を付与 @@ -283,6 +291,8 @@ pnpm monitor:worker - Webhook URL は `normalizeHttpsUrl` を通し、localhost / private IPv4 / loopback IPv4 を拒否。 - Webhook 更新・削除はログインユーザーの通知方法のみ対象。 - Push endpoint は HTTPS のみ許可。 +- Push 設定画面では登録済みデバイス一覧を返さず、現在ブラウザの登録状態のみ確認。 +- Push 購読解除はログインユーザーの現在ブラウザ endpoint のみ対象。 - OpenSSL 呼び出しはタイムアウトを設定。 - 監視ジョブはサイト単位の失敗で全体を止めない。 - 外部通信を伴う監視処理は並列数を制限。 @@ -336,9 +346,12 @@ API 動作確認: - API 経由で Webhook 登録成功。 - API 経由で Webhook 更新成功。 - API 経由で Webhook 削除成功。 +- API 経由で現在ブラウザの Push 登録状態確認成功。 - API 経由で Push 購読情報登録成功。 +- API 経由で Push 購読情報解除成功。 - `pnpm monitor:once` 成功。 - API セキュリティ境界と証明書監視処理のテスト成功。 +- 通知条件ごとの重複送信防止、1 時間以上経過時の送信済み扱い、証明書更新時の再通知をテストで確認。 - `/push-sw.js` が SPA fallback ではなく Service Worker JavaScript として返ることをテストで確認。 - サイト登録時の証明書期限初期取得と取得失敗時の登録拒否テスト成功。 - 権限付き実行で OpenSSL による実サイトの証明書期限取得成功。 diff --git a/docker-compose.yml b/docker-compose.yml index c54ad1c..0658c54 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,8 @@ services: interval: 10s timeout: 5s retries: 5 + ports: + - '127.0.0.1:54320:5432' app: build: . diff --git a/src/client/components/Sidebar.jsx b/src/client/components/Sidebar.jsx index a658057..ebaf0a1 100644 --- a/src/client/components/Sidebar.jsx +++ b/src/client/components/Sidebar.jsx @@ -1,4 +1,5 @@ -import { Bell, Globe2, Link, LogOut, ShieldCheck, UserRound } from 'lucide-react'; +import { useState } from 'react'; +import { Bell, Globe2, Link, LogOut, Menu, ShieldCheck, UserRound, X } from 'lucide-react'; const navItems = [ { view: 'sites', label: 'サイト一覧', icon: Globe2 }, @@ -8,14 +9,44 @@ const navItems = [ ]; export function Sidebar({ activeView, user, onNavigate, onLogout }) { + const [menuOpen, setMenuOpen] = useState(false); + const MenuIcon = menuOpen ? X : Menu; + + function handleNavigate(view) { + onNavigate(view); + setMenuOpen(false); + } + + function handleLogout() { + setMenuOpen(false); + onLogout(); + } + return ( -