このリポジトリは、deciscope のWebアプリケーションです。
- フロントエンドFW: React Router 7 + Vite + React
- ルーティング:
app/routes.ts - 開発起動:
npm run dev - ビルド:
npm run build - 開発サーバー:
http://localhost:5193
.env.example を参考に .env.local を作成してください。
API_PROXY_TARGET=http://127.0.0.1:9090
WS_PROXY_TARGET=ws://127.0.0.1:9090
VITE_API_BASE_URL=/api
VITE_WS_BASE_URL=/ws
VITE_FIREBASE_API_KEY=...
VITE_FIREBASE_AUTH_DOMAIN=deciscope-app.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=deciscope-app
VITE_FIREBASE_APP_ID=...
# 任意
VITE_FIREBASE_STORAGE_BUCKET=deciscope-app.firebasestorage.app
VITE_FIREBASE_MESSAGING_SENDER_ID=...
# 任意。falseにすると診断イベントのAPI送信を停止します
VITE_DECISCOPE_CLIENT_DIAGNOSTICS=true
# 任意。診断イベントに載せるcommit SHA等
VITE_FRONTEND_BUILD_VERSION=
# 本番buildではCIが設定するartifact追跡情報
VITE_COMMIT_SHA=
VITE_BUILD_TIMESTAMP=
VITE_DIRTY_BUILD=false開発時、ブラウザは同一オリジンの /api と /ws に接続し、Vite が上記のプロキシ先へ転送します。
会議画面のworkspace-scoped APIは VITE_API_BASE_URL、文字起こしWebSocketは
VITE_WS_BASE_URL を使います。本番環境でブラウザから別オリジンの API に直接接続する場合のみ、
この2つを公開ホストに合わせて設定してください。
会議画面のクライアント診断はブラウザコンソールではなく /internal/client-diagnostics へ送信し、
core-api の標準ログおよび設定されたJSONL診断ログへ記録します。
各環境変数の公開範囲と用途は .env.example のコメントを参照してください。
Firebase Console の Authentication で Microsoft プロバイダーを有効化し、承認済みドメインに localhost が含まれていることを確認してください。
workspace配下の「Teams 会議に入室」画面でTeams会議URLを貼って 会議に入室 を押すと、フロントエンドは認証済みのworkspace APIへ次のリクエストを送ります。
POST /v1/workspaces/{workspaceId}/meeting-sessions
{
"joinUrl": "https://teams.microsoft.com/l/meetup-join/...",
"userProvidedTitle": "週次定例",
"candidateUserPrincipalNames": ["user@example.com"],
"createdByEmail": "user@example.com",
"purpose": "今週の進捗と課題を共有する",
"context": "プロジェクトAの週次定例",
"agenda": "進捗、課題、次のアクション",
"customInstruction": "決定事項と担当者を明確にする"
}userProvidedTitle 以降は画面で入力された場合だけ送信されます。
レスポンスの sessionId を受け取ったら、フロントエンドは sessionId をパスに含めて会議画面へ遷移します。
/w/<workspaceId>/meetings/<sessionId>
会議URL送信時の流れは、workspace-scoped APIでのsession作成、pending navigationの保存、sessionId パスへの遷移の順です。sessionId をルートパラメータに持たせることで、認証状態の再確認、ページ再読み込み、React stateの破棄を挟んでも会議セッションを復元できます。送信中はボタンをdisabledにし、同じsubmitの二重実行を防ぎます。
ホームの進行中一覧では、Go APIの GET /v1/workspaces/{workspaceId}/meeting-sessions と GET /v1/workspaces/{workspaceId}/meeting-sessions/{sessionId} のstatusを使ってTeams会議を表示します。ended、failed、stale、timeout のTeams sessionは進行中一覧から外れ、最近の会議側に表示されます。進行中の会議は直近3件、最近の会議は直近7件を表示し、全件は「すべて」から会議履歴ページ (/w/<workspaceId>/meetings/history) で確認できます。Teams会議の「開く」「記録を見る」は sessionId を含む会議画面パスへ遷移するため、既存meetingレコードだけを開いて空の会議画面になることを避けます。
sessionId はURLパスを正とします。localStorage の deciscope:meetingSessions:v1 は作成直後の復帰や旧導線の補助情報、deciscope:pendingMeetingNavigation:v1 は作成直後の遷移復旧に使います。最後に選択したworkspaceは deciscope:lastWorkspaceId に保存します。
認証状態が loading の間は未認証扱いでredirectせず、「認証状態を確認しています...」を表示します。WebSocketの一時切断や再接続中も会議画面は維持し、画面内の接続状態として表示します。
フロントエンドが直接VM Botを叩くことはありません。正しい流れは Frontend -> Go API -> VM Bot です。DECISCOPE_BOT_CONTROL_URL、DECISCOPE_BOT_CONTROL_TOKEN、DECISCOPE_INGEST_API_KEY はフロントエンド環境変数に設定しないでください。
開発時の推奨構成は、ブラウザからフロントエンドと同一オリジンへ接続し、Vite が Go API へプロキシする方式です。ブラウザ上のJavaScriptから ws://api:9090/... へ直接接続しないでください。api はDocker Compose内部のサービス名であり、ホストPC上のブラウザからは通常解決できません。
Vite はworkspace-scoped APIとWebSocketを Go API へ転送します。
/api/v1/meeting-sessions
/v1/workspaces/.../meeting-sessions
/v1/workspaces/.../meeting-sessions/.../transcript-stream
/ws/v1/workspaces/.../meeting-sessions/.../transcript-stream
/api/v1/meeting-sessions はVM Bot連携や手動確認に残しているAPI-key系の互換ルートです。通常のブラウザUIは /v1/workspaces/.../meeting-sessions を使います。
npm run dev では、環境変数を省略した場合も .env.example と同じ http://127.0.0.1:9090 をAPIのproxy先として使います。
Docker Compose内では、proxy先は既定で次になります。
http://host.docker.internal:9090
Go APIとfrontendを同じDocker networkに入れて api:9090 で解決できる場合だけ、API_PROXY_TARGET=http://api:9090 と WS_PROXY_TARGET=ws://api:9090 に上書きしてください。
ローカルでGo APIをホスト公開ポートから使う場合は、.env.local で次を使います。
API_PROXY_TARGET=http://127.0.0.1:9090
WS_PROXY_TARGET=ws://127.0.0.1:9090workspace-scoped WebSocketはセッションCookieとworkspace所属検査で認証されます。共有トークンをフロントエンド環境変数へ設定しないでください。Go API側の許可Originには http://localhost:5193 と http://127.0.0.1:5193 も含めます。
フロントエンド単体の開発コンテナを起動できます。
docker compose up --build frontendこのcomposeは、Go APIがホスト公開ポート localhost:9090 で動いている前提で、frontendコンテナから host.docker.internal:9090 へproxyします。VM上のTeams Botはこのcomposeには含めず、Go APIからTailscale経由で接続します。
Go APIコンテナが同じDocker network上で api:9090 として解決できる場合は、次のようにproxy先を上書きしてください。
API_PROXY_TARGET=http://api:9090 WS_PROXY_TARGET=ws://api:9090 docker compose up --build frontendGo APIが別リポジトリのComposeで動き、api サービス名で接続したい場合は、両方のComposeを同じネットワークへ参加させてください。
docker network create deciscopeGo API側Composeにも同じ外部ネットワークを追加し、APIサービスに api エイリアスを付けます。このリポジトリ側は既定で deciscope ネットワークを使います。別名を使う場合は DECISCOPE_DOCKER_NETWORK を設定してください。
Go APIをDockerではなくホスト上の localhost:9090 で起動している場合も、既定設定のままで host.docker.internal:9090 へ接続します。明示する場合は次のように指定できます。
API_PROXY_TARGET=http://host.docker.internal:9090 WS_PROXY_TARGET=ws://host.docker.internal:9090 docker compose up --build frontend- Go APIを起動します。
- フロントエンドを
npm run devまたはdocker compose up --build frontendで起動します。 - workspace配下の「Teams 会議に入室」画面でTeams会議URLを入力して
会議に入室を押します。 - 既存の会議画面へ遷移することを確認します。
- VM Bot、またはバックエンド手順に沿った手動POSTで
POST /api/v1/transcript-segmentsへ文字起こしを投入します。VM BotはTeamsの音声をAzure Speechで文字起こしし、raw audioではなくtranscript segmentをGo APIへ送ります。 - 認証済みの会議画面に最新の文字起こしが表示されることを確認します。
speaker情報つきの手動POST例:
{
"sessionId": "manual-speaker-session",
"eventId": "manual-speaker-test-12345",
"callId": "manual-speaker-call",
"sequenceNo": 12345,
"recognizedAtUtc": "2026-06-27T07:00:00Z",
"offsetTicks": 0,
"durationTicks": 10000000,
"text": "話者情報つきの手動テストです",
"speakerId": "manual-speaker-001",
"speakerName": "手動テスト太郎"
}このpayloadを送信すると、該当workspaceの会議中画面のタイムラインに 手動テスト太郎 が表示されます。
ビルド手順は BUILD.md を参照してください。