機能 #10519
未完了[VEND-V2-06] 競合3社診断ジョブ・レート制限・コスト制御
0%
説明
親: #10516
依存: #10518 検索API資格情報
目的¶
対象1社+競合3社の診断を同期HTTPで長時間実行せず、版付きジョブとして安全に処理する。
要件¶
- 競合候補検索とAIO診断を非同期ジョブ化
- 1ジョブ最大3競合、同一domain重複排除
- IP/session単位レート制限、同一URL再利用、結果キャッシュ
- LLM呼出数・検索API呼出数・実行時間の上限
- status: queued/running/completed/partial/failed
- 個別競合失敗でもpartial結果を返す
- SSRF、redirect再検証、prompt injection対策を全URLへ適用
- worker concurrencyを制限
- API key、raw HTML、検索provider内部情報を公開しない
- 比較時は同じrubric versionを固定
- 旧rubric結果を混在させない
- 検索日時、検索語、provider、候補順位、除外理由を保存
表示¶
-
検索条件に基づく比較対象と明記 - 市場順位・シェア・業界トップとは表現しない
- 会社名/公式URL/総合点/6軸差分
- 競合共通対策、自社のみ未対応、自社優位
完了条件¶
- 負荷・コスト上限試験
- ジョブ再開/失敗/partial試験
- E2Eとセキュリティレビュー
- 運用監視・停止スイッチ
Redmine Admin さんが12日前に更新
- ステータス を 新規 から 進行中 に変更
着手。検索API資格情報なしで実装可能な基盤を先行する。競合比較ジョブ、最大3社、同一rubric固定、レート制限、URL重複排除、結果キャッシュ、partial failure、停止スイッチを実装する。検索候補取得はprovider interfaceとして分離し、資格情報接続後に統合する。
Redmine Admin さんが12日前に更新
フェーズ1を本番反映。
実装:
- disabled-by-default /v2/competitor-jobs POST/GET基盤
- competitor_jobs / competitor_job_itemsテーブル・indexを冪等作成
- target診断はschema2.4/rubric2026.5、利用者入力の業種+エリア必須
- 手動競合URL 1〜3件、URL正規化、対象/競合同士の同一registrable domainを拒否
- 生IP非保存。HMAC-SHA256 requester hashのみ保存
- DBベース rate limit: requester 3件/時、target 2件/時
- target/rubric/sorted URL集合のcompleted/partialを24時間cache
- public responseからrequester hash/cache key/internal情報を除外
- feature flag falseまたは32文字未満secretで503
検証:
- Node20 backend全36件合格
- DB integrity ok、V1/V2保持
- 公開POSTはfeature_unavailable 503(機能停止状態)
- API healthy
- source commit 1deaa41
- image be0180cfff5b
- backup /root/backup/website-vending-machine/20260727-1315-10519-phase1
未完:
- FIFO runner、診断再利用、partial/all-fail、再起動回復
- 検索API接続
機能は有効化していない。
Redmine Admin さんが12日前に更新
進捗: 2026-07-27 JST #10519 phase1本番反映。disabled-by-default競合ジョブAPI、competitor_jobs/items、IP HMAC、1h rate limit requester3/target2、24h cache、manual competitor URLs max3、registrable domain重複排除、schema2.4/rubric2026.5固定対象検証。Node20 backend36/36、DB integrity、公開503 disabled、API healthy。source 1deaa41、image be0180cfff5b、backup 20260727-1315-10519-phase1。次はFIFO runner、診断再利用、partial/all-fail、restart recovery。全体進捗55%。
Redmine Admin さんが11日前に更新
フェーズ2を本番反映。
実装:
- process-wide concurrency 1
- FIFO処理。同一秒作成時はDB rowidで挿入順を保証
- 競合URLを逐次診断
- target site_contextを完全一致で再利用
- 同一URL/rubric/site_contextの正常診断を24時間再利用
- completed / partial / failed集約
- 個別エラーをinvalid_url/not_crawlable/robots_blocked/timeout/diagnosis_failedへ限定
- 内部例外文を公開・保存しない
- rubric不一致時は診断を実行せずfailed
- 起動時runningをqueuedへ戻す
- feature disabled時は再キューのみで実行しない
- target欠損時もGET public shapeを維持
検証:
- Node20 backend全45件合格
- independent review approve
- site_context完全一致引き継ぎ試験
- FIFO競合試験で同時刻順序バグを検出・修正
- DB integrity ok、running job 0
- 公開APIはfeature_unavailable 503のまま
- API healthy、ログ異常なし
- source 0607b97
- image 91d3eb2f42eb
- backup /root/backup/website-vending-machine/20260728-0718-10519-phase2
残作業:
Redmine Admin さんが11日前に更新
進捗: 2026-07-28 JST #10519 phase2本番反映。競合ジョブFIFO単一並列、逐次診断、target site_context完全継承、同一URL/rubric/context正常診断24h再利用、completed/partial/failed、generic error only、rubric mismatch fail-fast、running->queued再起動回復、disabled時非実行。FIFO同時刻バグをrowid tie-breakで修正。Node20 backend45/45、review approve、DB integrity、public503 disabled、API healthy。source 0607b97、image 91d3eb2f42eb、backup 20260728-0718-10519-phase2。全体進捗60%。
Redmine Admin さんが10日前に更新
Node 22移行とAPIセキュリティ強化を本番反映しました。
実施内容:
- Node 22.22.0 / npm 10へ移行
- node:22.22.0-bookworm-slimへ変更
- better-sqlite3 11.3.0互換性確認
- CSP / nosniff / frame-ancestors / Referrer-Policy / Permissions-Policy
- CORS完全一致、Origin/Referer検証、application/json強制によるCSRF/XSRF対策
- XSS、CRLF、NUL、ディレクトリトラバーサル、二重エンコード、危険スキーム対策
- SSRF、DNS解決後検証、リダイレクト再検証
- 競合検索結果・検索語のサニタイズ
- プロンプトインジェクション境界と16KB制限
- 外部API/内部例外の固定エラーコード化
- インメモリレート制限
検証:
- Backend 59/59 PASS
- Frontend 31/31 PASS
- 合計90/90 PASS
- 本番DBコピー integrity_check=ok、既存テーブル8
- 本番コンテナ healthy、Node v22.22.0
- 公開health HTTP 200
- evil Origin 403
- text/plain POST 415
- 正規OPTIONS 204
- 競合検索は機能フラグOFFのため503を維持
コミット: 87d4759, a6522c5, d5d403a, cd90a63, 1cf1340
master: 73cef94
Redmine Admin さんが10日前に更新
進捗: 2026-07-29 Node 22移行とAPIセキュリティ強化を本番反映。Backend 59/59、Frontend 31/31、合計90/90 PASS。本番DBコピー integrity_check=ok、既存テーブル8。vend01-apiはNode v22.22.0でhealthy。公開経路でhealth 200、evil Origin 403、非JSON POST 415、正規OPTIONS 204、競合検索は機能フラグOFFで503維持。master 73cef94。バックアップ: /root/backups/vend01-node22-security-20260729-065459。
Redmine Admin さんが10日前に更新
基盤強化を本番反映。Node 22.22.0へ移行し、競合ジョブ/検索を含むAPIにsame-origin+allowlist Origin検証、JSON強制、レート制限、CRLF/NUL/traversal/XSS入力検証、private/link-local/metadata URL拒否、プロンプトインジェクション境界、固定エラーコードを適用。backend 61/61、frontend 31/31 PASS。公開health 200、DB integrity ok。関連チケット #10556。競合検索機能フラグは引き続き既定OFFで、SearXNG本接続は別工程。
Redmine Admin さんが10日前に更新
SearXNG provider本接続を本番反映(commit 12c99ab)。正式env: COMPETITOR_SEARCH_PROVIDER / SEARXNG_BASE_URL、旧SEARCH_PROVIDER / SEARXNG_ENDPOINTは後方互換。VPS・Node22コンテナからTailnet SearXNG http://100.64.0.2:8888 へJSON検索HTTP 200確認。Content-Type、1MiB、15秒timeout、429/5xx/invalid JSON固定エラー、検索語/結果サニタイズ、private URL拒否、NFKC正規化、一覧・ランキング・比較記事および既知ポータル除外を実装。検索監査metadata(provider/searched_at/query/source_rank/採否理由)をnullable列search_metadata_jsonへ保存可能にし、既存DB migration integrity=ok。テスト backend 77/77、frontend 31/31、計108/108 PASS。公開health 200、コンテナhealthy、Node v22.22.0。COMPETITOR_SEARCH_ENABLED=falseを維持し、公開候補APIは503 feature_unavailable。endpoint/API keyはレスポンス・監査情報へ非公開。バックアップ: /root/backups/vend01-searxng-release-20260729-075956。候補品質は横浜制作で誤候補を返さず0件、大阪工務店で公式候補1件を確認。次工程はフロント候補選択UI/E2Eと限定的な機能ON判定。
Redmine Admin さんが10日前に更新
競合候補選択UIと検索→選択→競合診断ジョブE2Eを本番反映(commit 9dab02f)。UIは診断結果内で候補検索、1〜3件選択、ジョブ作成、queued/running/completed/partial/failed表示、最大2分poll、対象/競合スコア表示に対応。search_metadataはJSメモリ内のみ保持。安全DOM、外部リンクnoopener、strict response normalize、canonical lowercase UUIDv4検証を実装。バックエンドのGET /competitor-jobs/:idがUUIDを拒否していた不具合も修正。各poll GETは15秒Abort、全体120秒上限。古いタイマー/レスポンスが新規処理へ干渉しないrunId+controller identity対策を実装。テスト backend 79/79、frontend 36/36、計115/115 PASS。隔離E2E: SearXNG候補200→job 202→UUID GET 200→frontend normalize成功。Codex最終レビュー APPROVE。本番はCOMPETITOR_SEARCH_ENABLED=falseを維持し、公開APIは503 feature_unavailable。公開HTML/JS更新、health 200、DB integrity ok。バックアップ: /root/backups/vend01-competitor-ui-release-20260729-093317。