塾向けのシフト管理 Web アプリ。Google OR-Tools の制約充足ソルバーで講師と生徒を最適にマッチングし、AWS にフルマネージドでデプロイされます。
塾の講師シフト作成は「講師の出勤可否・担当科目・生徒の希望コマ・1対1指定」など多数の制約が絡み、手作業では数時間かかる組合せ最適化問題です。本アプリは Google OR-Tools (CP-SAT) で数秒で解き、ドラッグ&ドロップ編集・公開フォーム受付・監査ログまでを フルサーバーレス (AWS Amplify Gen 2) で提供します。
- 🧩 最適化エンジン — 希望コマ充足を最大化しつつ講師の負荷を平準化
- ☁️ フルマネージド — AppSync + DynamoDB + Docker Lambda、スケールゼロで運用レス
- 🔐 セキュリティ重視 — Cognito 認証 / 公開APIキー非露出 / append-only / SSRF・CSP 対策
- ✅ CI/CD — セキュリティ監査と認証テストを通過したコードのみ自動デプロイ
- 講師・生徒の登録/編集
- OR-Tools CP-SAT による自動シフト生成(最大化目標:希望コマ充足、最小化目標:講師偏り)
- ドラッグ&ドロップによるシフト編集
- 公開フォームから受信した申請の承認/却下
- 操作監査ログ
- Excel エクスポート
- ID 入力でシフト確認(ログイン不要)
- スマホからのシフト希望提出フォーム
- 二重送信防止(DynamoDB の Conditional Write)
- 月別カレンダー表示
- 管理画面:Cognito User Pool(管理者作成のみ/自己登録不可)
- 公開エンドポイント:Lambda 経由でのみ AppSync にアクセス、API キーはフロントに含まれない
- CSP / X-Frame-Options / HSTS ヘッダー
- 監査ログによる操作履歴記録
- フォーム送信は append-only(漏洩しても削除・列挙不可)
┌──────────────────────────────────────────────────────────────────┐
│ React フロントエンド (Vite + TypeScript) │
│ ・ /admin 管理画面 (Cognito 認証) │
│ ・ /view シフト確認 (ID ベース) │
│ ・ /form 公開フォーム │
└──────────┬─────────────────────┬────────────────────────────────┘
│ Cognito JWT │ Lambda Function URL
▼ ▼
AppSync GraphQL FastAPI Lambda (Docker, OR-Tools)
│ │ x-api-key
▼ ▼
DynamoDB AppSync GraphQL → DynamoDB
| レイヤー | 技術 |
|---|---|
| フロントエンド | React 19, TypeScript, Vite, TailwindCSS, Framer Motion |
| 状態管理 | Zustand, React hooks |
| 認証 | Amazon Cognito (User Pool), Amplify UI Authenticator |
| API | AWS AppSync (GraphQL), AWS Lambda Function URL |
| データベース | Amazon DynamoDB |
| 計算エンジン | Python 3.12, FastAPI, Google OR-Tools (CP-SAT) |
| パッケージング | Docker (linux/amd64), Amazon ECR |
| CI/CD | GitHub Actions, AWS Amplify Hosting |
| インフラ as Code | AWS CDK (Amplify Gen 2) |
1. 組合せ最適化を実用時間で 講師×生徒×コマの割当は制約が多く、手作業では非効率。OR-Tools の CP-SAT ソルバーで「希望コマ充足の最大化」と「講師偏りの最小化」を同時に扱う多目的最適化として定式化し、数秒で解いています。
2. 「公開フォーム」を安全に晒す設計 生徒は認証なしで希望を提出できる必要がある一方、データ流出は許されません。公開エンドポイントは Lambda 経由でのみ AppSync にアクセスし、API キーをフロントへ一切出しません。送信は append-only 設計で、万一漏洩しても既存データの削除・列挙は不可能です。
3. キーレスな CI/CD
GitHub Actions は OIDC で AWS STS と連携し、長期アクセスキーを保持しません。pip-audit / npm audit(本番依存の高深刻度は必ず失敗)と認証テストスイート (pytest) をデプロイ前ゲートにしています。
4. 実運用で踏んだ罠を記録
AWS Lambda は docker buildx が生成する OCI manifest を拒否するため、あえて通常の docker build(Manifest v2 Schema 2)でビルドしています。こうした知見はコード中のコメントに残しています。
- Node.js 20+
- Python 3.12+
- AWS アカウント(Amplify Gen 2 のサンドボックス用)
- AWS CLI 認証済み
# 依存インストール
npm install
pip install -r python/requirements.txt
# Amplify サンドボックス起動(バックエンドが立ち上がる)
npx ampx sandbox
# 別ターミナルでフロント + Python API を同時起動
npm run devブラウザで http://localhost:5173 にアクセス。
.env.example を .env.local にコピーし、必要に応じて設定。
APPSYNC_API_URL=... # Lambda 経由テスト時
APPSYNC_API_KEY=... # 同上
通常の管理画面開発ではこれらは不要です(管理画面は Cognito 経由)。
本番環境(main ブランチ)へのデプロイは GitHub への push で自動実行されます。
main へ push
├─→ GitHub Actions: Docker ビルド → ECR push(python/** 変更時)
└─→ Amplify Hosting: CDK 適用 → フロント配信
初回セットアップ(ECR / IAM OIDC ロール / Amplify サービスロール)が必要です。
GitHub Actions の Variables に AWS_ACCOUNT_ID を設定してください(.github/workflows/deploy.yml がロール ARN の組み立てに使用します)。
test2/
├── src/ # React フロントエンド
│ ├── components/ # UI コンポーネント
│ ├── pages/ # ルーティング先のページ
│ ├── services/ # AppSync / Lambda クライアント
│ ├── store/ # Zustand ストア
│ ├── utils/ # マッチングロジック等
│ └── main.tsx # ルーター
├── python/ # Lambda (FastAPI + OR-Tools)
│ ├── api.py # エンドポイント定義
│ ├── shift_generator.py # CP-SAT ソルバー
│ ├── auth.py # Cognito JWT 検証
│ └── Dockerfile
├── amplify/ # AWS CDK バックエンド定義
│ ├── auth/ # Cognito 設定
│ ├── data/ # AppSync + DynamoDB スキーマ
│ └── backend.ts # Lambda Function URL 等
├── scripts/ # デプロイ用シェルスクリプト
└── .github/workflows/ # GitHub Actions
MIT License で公開しています。
技術相談・コードレビュー:Claude (Anthropic) との協同開発