Sho-Room 仕様書(詳細)
1. システム概要
1.1 目的
本システムは、会津大学学園祭(通称: 蒼翔祭)実行委員会 における統合管理ツールである。
来場者管理、イベント運営、備品管理、外部連携など、学祭運営に必要な機能を一元的に提供する。
1.2 システム構成
- フロントエンド: Vite + React(TypeScript)/ Cloudflare Pages
- BFF: Cloudflare Pages Functions
- バックエンド: Hono / Cloudflare Workers
- データベース: Cloudflare D1 (
soshosai-system-db) - UI: Shadcn UI (Radix UI base) + Tailwind CSS
1.3 対象ユーザー
- 学祭委員全員: Sho-Room Webアプリを使用
- 管理者: スタッフ管理、BAN操作、システム設定
2. アーキテクチャ構成
2.1 技術スタック
| レイヤー | 技術 |
|---|---|
| Frontend | Vite + React (TypeScript) |
| Hosting | Cloudflare Pages |
| BFF | Cloudflare Pages Functions |
| API | Hono / Cloudflare Workers |
| Database | Cloudflare D1 |
| ORM | Drizzle ORM |
| Validation | Zod / drizzle-orm/zod |
| Test | Vitest |
2.2 ドメイン構成
本システムは複数のドメイン構成で運用される:
- login-api:
loginapi.soshosai.com- 認証フロントエンド - auth-workers: 認証バックエンドサービス群(Service Binding経由)
discord-auth-worker: Discord OAuth2認証line-auth-worker: LINE OAuth2認証unified-auth-worker: JWT検証・BAN管理
- Sho-Room:
room.soshosai.com- 学祭委員向け管理画面(本システム) - API:
v3.api.soshosai.com- バックエンドAPI(共通)
2.3 認証アーキテクチャ
認証は login-api と auth-workers の2層構成で実装する。
認証システムの詳細は認証システム仕様書を参照
概要:
| コンポーネント | 役割 |
|---|---|
login-api | 認証フロントエンド。OAuthリダイレクト、Cookie設定 |
discord-auth-worker | Discord OAuth2認証・ロール検証 |
line-auth-worker | LINE OAuth2認証 |
unified-auth-worker | JWT検証・BAN管理・レート制限 |
BFFパターン:
Sho-RoomではCloudflare Pages Functionsを使用したBFFパターンを採用。
CookieからJWTを取得し、Authorization: Bearer xxx ヘッダーに変換してAPIに転送する。
Browser → Pages (React) → Pages Functions (BFF) → API → unified-auth-worker
Cookie Header変換
2.4 ID戦略
- システム内部ID: 原則 UUIDv7 を使用(時系列ソート可能)
- Discord関連: Discord User ID(文字列)をそのまま使用
- 学籍番号:
s1234567形式
2.5 システム構成図
2.6 BFF Service Binding設定
Sho-RoomのPages FunctionsはService Bindingで直接Workerを呼び出す。
wrangler.jsonc(room.soshosai.com):
{
"name": "sho-room",
"pages_build_output_dir": "./dist",
"compatibility_date": "2025-02-01",
"services": [
{ "binding": "AUTH_SERVICE", "service": "unified-auth-worker" },
{ "binding": "GATEWAY", "service": "system-gateway-worker" },
],
}
Pages Functions実装例(functions/api/[[path]].ts):
interface Env {
AUTH_SERVICE: Fetcher;
GATEWAY: Fetcher;
}
export const onRequest: PagesFunction<Env> = async (context) => {
const { request, env, params } = context;
// CookieからJWTを取得
const cookies = request.headers.get('Cookie') || '';
const staffJwt = cookies.match(/soshosai_staff_jwt=([^;]+)/)?.[1];
if (!staffJwt) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
// Service Binding経由でGateway Workerを呼び出し
const path = Array.isArray(params.path)
? params.path.join('/')
: params.path;
return env.GATEWAY.fetch(`https://gateway/${path}`, {
method: request.method,
headers: {
Authorization: `Bearer ${staffJwt}`,
'Content-Type':
request.headers.get('Content-Type') || 'application/json',
},
body: request.body,
});
};
3. データベース設計
詳細はDB設計を参照
4. ユースケース図
権限ビット(permission bits)に基づくアクセス制御を表す。上位権限は下位権限を包含する。
5. 機能要件詳細
F-001: 認証基盤
概要: Discord OAuth2による認証。BFFパターンによるセキュアな通信。
認証フロー:
- ユーザーが
room.soshosai.comにアクセス - Pages FunctionsでCookieの有無を確認
- 分岐処理:
- 未ログイン:
loginapi.soshosai.comへリダイレクト - BANユーザー: 403 Forbidden画面を表示
- 正常: アクセス許可
- 未ログイン:
Cookieの設定(login-apiが設定):
Set-Cookie: soshosai_staff_jwt=eyJhbGc...;
Domain=.soshosai.com;
Path=/;
HttpOnly;
Secure;
SameSite=Lax;
Max-Age=259200
権限更新:
- ログイン時、Discordのロール情報を再取得
Departmentsテーブルでロールに対応する権限を確認- 最高権限を
Staffs.authorityに設定 - JWTを再発行
F-002: 入場者データ表示
概要: 全来場者データの閲覧。
UI仕様:
- ページネーション: 1ページあたり50〜100件
- 検索: ID、氏名、属性等で絞り込み(実行ボタン必須)
- レスポンシブ: PC=テーブル、Mobile=カードリスト
F-003: 入場者グループ管理
概要: グループ情報の閲覧・修正。
UI仕様:
- グループID検索機能(実行ボタン必須)
- グループ詳細画面で紐づくメンバー情報を展開表示
F-004: 入場者数管理(ダッシュボード)
概要: リアルタイム集計データの可視化。
UI仕様:
- 自動更新: 60秒ポーリングまたは手動リロードボタン
- グラフ描画: Recharts使用
F-005: 入場者確認
概要: 予約・入場資格の照会(Read-only)。
API: v3.api.soshosai.com(BFF経由)
UI仕様:
- 検索バー固定配置
- 検索結果にステータスバッジ(入場済み/未入場)
F-006: Trackable Links
概要: 短縮URLプロジェクトの一覧・作成・QR生成。
API: fwd.soshosai.com
UI仕様:
- プロジェクト一覧にページネーション適用
F-007: 管理者システム
概要: スタッフ管理・BAN操作。
画面構成:
-
スタッフ検索・一覧画面:
- 氏名、ID、所属部署での検索(実行ボタン必須)
- ページネーション
- BAN済みユーザーは行をグレーアウト
-
スタッフ詳細・操作モーダル:
- 基本情報表示(名前、Discord名、部署、権限)
- BAN操作エリア(理由入力、確認ダイアログ)
ロジック:
- 操作者と対象者の権限レベルを比較
- 同等以上の権限を持つ対象者の操作は不可(super_adminは例外)
F-008: イベント管理
概要: 学内イベントデータの操作。
UI仕様:
- 参加者一覧はページネーション適用
F-009: LINE連携
概要: LINE公式アカウントとの連携。
機能:
- 受信メッセージ一覧表示
- 返信管理
UI仕様:
- メッセージ履歴はページネーション取得
F-010: 備品管理
概要: 備品一覧、検索、編集、CSV出力。
詳細は備品管理システム仕様書を参照
6. 非機能要件
6.1 パフォーマンス
- 一覧画面: 大量データでも高速表示
- 想定同時接続数: 最大30人
6.2 セキュリティ
- 認証: Discord OAuth2 + JWT(HttpOnly Cookie)
- BFF: Pages FunctionsによるCookie→Header変換
- 認可: 権限レベルによるアクセス制御
- 権限レベル:
staff: 一般スタッフdept_admin: 部門管理者system_admin: システム管理者super_admin: 最高管理者
6.3 可用性
- Cloudflare Pages/Workersを使用し、基本的に常時稼働
7. API仕様
7.1 共通仕様
ベースURL: https://v3.api.soshosai.com(BFF経由でアクセス)
認証: BFFがCookieからJWTを取得し、Authorization: Bearer <JWT> ヘッダーを付与
レスポンス形式:
// 成功時
{
"status": 200,
"data": { ... }
}
// エラー時
{
"status": 400,
"message": "エラーメッセージ"
}
ステータスコード:
200 OK: 成功201 Created: 作成成功204 No Content: 削除成功400 Bad Request: リクエスト不正401 Unauthorized: 認証失敗403 Forbidden: 権限不足 / BAN404 Not Found: リソース不在500 Internal Server Error: サーバーエラー
クエリパラメータ:
limit: 取得件数(Default: 50)offset/cursor: ページネーション用q: 検索キーワード
8. 開発環境
8.1 プロジェクト構造
sho-room/
├── functions/ # Pages Functions (BFF)
│ └── api/
│ └── [[path]].ts # APIプロキシ
├── src/
│ ├── components/ # Reactコンポーネント
│ ├── hooks/ # カスタムフック
│ ├── utils/ # ユーティリティ関数
│ ├── services/ # API通信ロジック
│ ├── types/ # TypeScript型定義
│ ├── App.tsx
│ └── main.tsx
├── tests/ # テストファイル
│ ├── unit/ # 単体テスト
│ └── integration/ # 統合テスト
├── public/
├── package.json
├── vite.config.ts
├── vitest.config.ts
└── tsconfig.json
8.2 BFF実装
functions/api/[[path]].ts:
interface Env {
AUTH_SERVICE: Fetcher;
GATEWAY: Fetcher;
}
export const onRequest: PagesFunction<Env> = async (context) => {
const { request, env, params } = context;
// CookieからJWTを取得
const cookies = request.headers.get('Cookie') || '';
const staffJwt = cookies.match(/soshosai_staff_jwt=([^;]+)/)?.[1];
if (!staffJwt) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
// Service Binding経由でGateway Workerを呼び出し
const path = Array.isArray(params.path)
? params.path.join('/')
: params.path;
return env.GATEWAY.fetch(`https://gateway/${path}`, {
method: request.method,
headers: {
Authorization: `Bearer ${staffJwt}`,
'Content-Type':
request.headers.get('Content-Type') || 'application/json',
},
body: request.body,
});
};
8.3 開発コマンド
# 開発サーバー起動
npm run dev
# または(Pages Functions含む)
wrangler pages dev -- npm run dev
# テスト実行
npm run test
# カバレッジ
npm run test:coverage
# ビルド&デプロイ
npm run build
wrangler pages deploy dist
9. UI/UXルール
9.1 デザインシステム
- Component Library: Shadcn UI (Radix UI base)
- Styling: Tailwind CSS
- Color Palette:
- Primary:
#008578(ブランドカラー) - Base: Slate / Zinc
- Primary:
9.2 データ操作ルール
- 動的フィルタ禁止: リアルタイム検索は行わない
- 実行トリガー: 検索条件入力後、「検索/適用」ボタンでクエリ発行
10. 用語集
| 用語 | 意味 |
|---|---|
| JWT | JSON Web Token。改ざん防止署名付きの認証トークン |
| BFF | Backend for Frontend。フロント専用のプロキシ層 |
| UUID | 一意識別子。UUIDv7は時系列ソート可能 |
| PK | Primary Key(主キー) |
| FK | Foreign Key(外部キー) |
| BAN | アカウント停止措置 |
| OAuth2 | 認証プロトコル。Discord認証で使用 |