Skip to content

プロダクション化設計書

概要

MileTrek を「モックで動くプロトタイプ」から「実運用可能なサービス」へ昇格させるために必要な全設計を記述する。


1. 管理画面(Admin Panel)

結論: 必要。 以下の運用業務を非エンジニアでも行えるようにする。

1.1 管理画面の機能一覧

機能優先度説明
キャンペーン管理ANA/JALキャンペーンの CRUD。公開/非公開切替、優先度設定
マイルチャート管理ANA/JALが年1-2回更新するマイルチャートの編集。CSV一括更新
シーズン定義管理L/R/H シーズン期間の年度更新
レストランデータ管理追加/編集/削除。食べログ/Google URL の手動キュレーション
ホテルデータ管理追加/編集/削除。料金・画像の更新
ユーザー管理ユーザー一覧、凍結/削除、パスワードリセット
空港マスタ管理新規空港追加(国際線拡充時)
広告管理広告枠の有効/無効、掲載位置設定
アナリティクスDAU/MAU、検索数、予約インテント数、人気目的地
コンテンツモデレーションユーザー生成コンテンツ(レビュー等)が発生した場合

1.2 管理画面の技術方針

項目方針
フレームワークReact + Vite(フロント共通化)or 軽量に AdminJS / React Admin
認証JWT + admin ロール。users.role カラム追加(user/admin)
URL/admin/* をExpress で配信。本番は Basic認証 + IP制限
デザインシンプルなテーブル+フォーム。DESIGN.md のトークン流用

2. 認証・セキュリティ

2.1 現在の問題

問題リスク対応
JWT secret がハードコードトークン偽造可能✅ 対応済 (#71): JWT_SECRET を必須環境変数化。本番で未設定の場合は起動時にエラーで fail-fast。開発環境では ephemeral なランダム値を使用し警告を出力。
パスワードリセットなしユーザーがロックアウトされるメール送信 + リセットトークン
Rate limiting なしブルートフォース攻撃express-rate-limit 導入
CORS が *CSRF 攻撃本番ドメインのみ許可
入力バリデーション最小限インジェクションexpress-validator 導入
HTTPS 未強制中間者攻撃Cloudflare で自動HTTPS
XSS 対策スクリプト注入helmet.js 導入
SQLite WAL ロック同時書き込み制限読み取り専用レプリカ or PostgreSQL 移行検討

2.2 ユーザーロール

users テーブル拡張:
  role TEXT DEFAULT 'user' CHECK (role IN ('user', 'admin'))
ロール権限
user通常のアプリ利用(検索、予約、グルメ、イベント管理)
admin上記 + 管理画面アクセス + データ CRUD

2.3 JWT シークレット管理 (#77)

原則:秘密値はソースコード・リポジトリに一切含めない。

環境ソース備考
ローカル開発ephemeral random (メモリのみ)プロセス再起動で無効化
本番 (macOS/Linux VPS)Keychain / pass / systemd EnvironmentFileデプロイスクリプトで env 化
CI/CDGitHub SecretsActions run 時に inject

トークン運用:

  • アクセストークン: 15〜30分の短命
  • リフレッシュトークン: 7日、DB管理
  • 失効機能: Redis or revoked_tokens テーブルで jti blacklist

3. 実データ統合

3.1 データソース別の統合方針

データ現状本番方針API/手段コスト
マイルチャートシードデータ手動更新(年1-2回) + 管理画面CSVインポートANA/JAL公式PDFから転記無料
シーズン定義 (#78)2026分シード年次自動バッチ + 管理画面編集公式PDF/ページパース(不可時はCSV手動)無料
キャンペーン (#79)モック10件定期取得バッチ + 管理画面CRUDANA/JAL公式ページ スクレイプ/RSS無料
空席情報モック生成フェーズ判断要 ※下記参照--
ホテルUnsplash画像+固定価格楽天トラベルAPI楽天Web Service無料(レート制限あり)
レストランモック24件Google Places API + 手動キュレーションGoogle Maps Platform$17/1000リクエスト
レビュー(食べログ)モック評価食べログAPIは非公開 → 手動転記 or 検索リンクのみ手動無料
レビュー(Google)モック評価Google Places API (rating, user_ratings_total)Google Maps Platform$17/1000リクエスト
目的地画像UnsplashUnsplash API or 固定画像CDNUnsplash API (無料)無料

3.1.1 自動更新バッチ (#78, #79)

対象実行頻度手段失敗時
シーズン定義年1回(1月1日)GitHub Actions cron → scripts/update-seasons.mjsSlack通知 + 管理画面で手動補正
キャンペーン毎日GitHub Actions cron → scripts/fetch-campaigns.mjsSentry記録 + 前回データ維持

バッチは必ず「追加/更新」操作のみを行い、管理者が手動で編集した値は上書きしないsource='manual' 列で区別)。

3.1.2 キャンペーン考慮のマイル最適化 (#80)

検索・プラン作成時に DB の campaigns を参照し、最も少ないマイル消費となる出発日・路線・キャンペーン組み合わせを提案する。

検索結果.miles_required = mile_charts.miles_required
                       - applicable_campaigns.maxBy(discount_amount)
                       - (season 割引)

UIでは「キャンペーン適用 -50% 🎉」バッジを表示し、元マイル数とキャンペーン名をホバー/タップで確認可能。

3.2 空席情報のフェーズ判断

ANA/JAL は空席情報の公開APIを提供していない。選択肢:

方式メリットデメリット
A. モック維持(現状)開発コストゼロユーザーの信頼性に欠ける
B. 公式サイトスクレイピングリアルデータ利用規約違反リスク、構造変更で壊れる
C. 手動更新確実運用コスト大
D. 空席非表示 + インテント起動のみリスクゼロUXが若干劣る
E. お得月のみ表示(推奨)シーズン定義は公開情報具体的な座席数は非表示

推奨: 方式 E — シーズン定義(L/R/H)に基づいて「お得な時期」を表示し、具体的な空席数は表示しない。予約ボタンから公式サイトへインテント起動。


4. インフラ・デプロイ

4.1 本番構成

[Cloudflare CDN/DNS]


[Cloudflare Workers / Pages] ← 静的フロントエンド配信

        ▼ /api/*
[VPS / Docker Container]
    ├── Express Server (Node.js)
    ├── SQLite (data/miletrek.db)
    └── Cron Jobs (キャンペーン更新チェック等)

4.2 デプロイ方針

項目方針
コンテナ化Docker + docker-compose
フロントエンドCloudflare Pages(自動ビルド+CDN配信)
バックエンドVPS (ConoHa / さくら / Vultr) or Fly.io
データベースSQLite(小〜中規模)→ 大規模時 PostgreSQL 移行
CI/CDGitHub Actions(lint + test + build + deploy)
監視UptimeRobot(死活)+ Sentry(エラー)
ログwinston + ログローテーション
バックアップSQLite DB を毎日 S3/R2 にバックアップ
ドメインmiletrek.jp or miletrek.app

4.3 環境変数

env
NODE_ENV=production
JWT_SECRET=<Keychain管理>
PORT=3000
DB_PATH=/data/miletrek.db
RAKUTEN_API_KEY=<楽天Web Service>
GOOGLE_PLACES_API_KEY=<Google Maps Platform>
SENTRY_DSN=<Sentry>

5. 収益化・広告

5.1 広告戦略

収益源方式想定収益
Google AdSenseバナー広告(ダッシュボード下部、検索結果間)CPM ¥100〜300
アフィリエイト楽天トラベル/agoda/booking.com リンク経由の予約手数料予約額の3-8%
ネイティブ広告グルメタブのスポンサードカード月額固定 or CPC
プレミアム機能広告非表示、優先空席通知月額 ¥300〜500

5.2 アフィリエイト連携

サービスプログラム報酬率
楽天トラベル楽天アフィリエイト予約額の1%
agodaagoda パートナー予約額の5-7%
booking.comBooking.com アフィリエイト予約額の25-40%(コミッション)
じゃらんリクルートアフィリエイト予約額の1%

ホテル予約リンクをアフィリエイトリンクに差し替えるだけで収益化可能。

5.3 広告枠の設計(DESIGN.md 準拠)

ダッシュボード
  └── マイル残高の下: バナー広告 (320x100)
検索結果
  └── 3件目と6件目の後: ネイティブ広告カード
グルメタブ
  └── 先頭: スポンサードレストラン
イベント予算タブ
  └── 合計の下: 旅行保険広告
旅のしおり(SNSシェア画面)
  └── 広告なし(UX優先)

6. UX 品質向上

6.1 必須対応

項目現状対応
ローディング状態一部のみ全API呼び出しにスケルトンUI
エラーハンドリング最小限ネットワークエラー、API失敗、再試行ボタン
オフライン対応なしService Worker でキャッシュ(PWA)
プッシュ通知なしマイル有効期限アラート、お得なキャンペーン通知
ダークモード設定UIあり/未実装DESIGN.md にダークパレット追加 + 実装
アクセシビリティ未対応ARIA属性、キーボード操作、色コントラスト
多言語対応日本語のみi18n 基盤(react-intl)は Phase 4
画像最適化Unsplash直リンクCloudflare Image Resizing or imgproxy
コード分割なし(855KB単一チャンク)React.lazy + dynamic import

6.2 PWA 対応

json
// manifest.json
{
  "name": "MileTrek",
  "short_name": "MileTrek",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#FFFFFF",
  "theme_color": "#0071E3",
  "icons": [...]
}

Service Worker キャッシュ戦略:

  • マイルチャート → Cache First(月1更新)
  • 空港マスタ → Cache First(年1更新)
  • グルメ/ホテル → Network First + Stale While Revalidate
  • 検索結果 → Network Only

7. テスト戦略

7.1 テスト種別

種別ツールカバレッジ目標
ユニットテストVitestAPI ルート: 80%
コンポーネントテストTesting Library主要コンポーネント: 70%
E2E テストPlaywright主要フロー: 100%
APIテストsupertest全エンドポイント: 100%
リンクヘルスチェックlink-health-check.mjs全DB URL: 100%

7.2 CI パイプライン

yaml
# .github/workflows/ci.yml
on: [push, pull_request]
jobs:
  test:
    - npm install
    - npm run lint
    - npm run test
    - npm run build
    - node scripts/link-health-check.mjs
  deploy:
    needs: test
    if: github.ref == 'refs/heads/main'
    - docker build & push
    - deploy to production

8. 法務・コンプライアンス

項目必要性対応
利用規約必須/terms ページ作成
プライバシーポリシー必須/privacy ページ。個人情報保護法対応
特定商取引法表記有料機能提供時/legal ページ
Cookie同意GDPR/改正電通法Cookie バナー
免責事項必須マイル情報・空席情報はANA/JAL公式を確認するよう明記
航空券仲介業不要直接販売しない(インテント起動のみ)
旅行業法不要手配行為を行わない(情報提供のみ)

9. SEO・マーケティング

項目対応
SSR/プリレンダリング主要ランディングページのみ静的生成
Meta タグtitle, description, OGP画像
サイトマップ/sitemap.xml 自動生成
構造化データJSON-LD(FAQPage, HowTo)
SNSシェア旅のしおり画面の OGP画像自動生成
ASO(アプリストア)PWA としてホーム画面追加を促進

10. フェーズ計画

Phase 3: プロダクション化(推奨順序)

3-1. セキュリティ強化 ─────── JWT Keychain連携+短命化+失効 (#77), helmet, rate limiting
3-2. 管理画面 MVP ──────── キャンペーン/マイルチャート/シーズン/ユーザー管理 (#55)
3-3. 自動更新バッチ ─────── シーズン年次バッチ (#78) + キャンペーン日次バッチ (#79)
3-4. マイル最適化エンジン ── キャンペーン考慮の最適プラン提案 (#80)
3-5. 楽天トラベルAPI統合 ─── ホテル実データ取得 + アフィリエイト (#56)
3-6. Docker化 + CI/CD ──── GitHub Actions + 本番デプロイ (#57)
3-7. 法務ページ ──────── 利用規約/プライバシーポリシー (#58)
3-8. エラーハンドリング ──── 全画面のローディング/エラー/リトライ (#59)
3-9. Google Places API ─── レストラン実データ + レビュー (#60)
3-10. PWA化 ─────────── manifest + Service Worker + オフライン (#61)
3-11. テスト整備 ────── Vitest + Playwright E2E (#62)
3-12. 広告・アフィリエイト ── AdSense + 楽天/agoda/booking アフィリエイト (#63)

Phase 4: グロース

4-1. プッシュ通知 ────── マイル有効期限 + お得キャンペーン
4-2. ダークモード ────── DESIGN.md ダークパレット
4-3. 国際線対応 ─────── 海外空港/路線データ追加
4-4. コード分割 ─────── React.lazy + dynamic import (バンドル最適化)
4-5. 多言語対応 ─────── react-intl (英語/中国語)
4-6. プレミアム機能 ──── 広告非表示、優先通知、高度な検索フィルタ