- Add sidecar management to backend (sidecar_manager.py) - Add sidecar API router for browser mode (/api/sidecar/*) - Add browser-api.js polyfill for running in Chrome/Edge - Add "Open in Browser" button when audio access fails - Update build scripts with new sidecar modules - Add start-browser.sh for development browser mode Browser mode allows users to open the app in their system browser when Electron's audio access is blocked by security software. The backend manages the sidecar process in browser mode (BROWSER_MODE=true). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
105 lines
3.3 KiB
Python
105 lines
3.3 KiB
Python
import os
|
|
from pathlib import Path
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.responses import FileResponse
|
|
from contextlib import asynccontextmanager
|
|
|
|
from .database import init_db_pool, init_tables
|
|
from .routers import auth, meetings, ai, export, sidecar
|
|
from .sidecar_manager import get_sidecar_manager
|
|
|
|
# Determine client directory path
|
|
BACKEND_DIR = Path(__file__).parent.parent
|
|
PROJECT_DIR = BACKEND_DIR.parent
|
|
CLIENT_DIR = PROJECT_DIR / "client" / "src"
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
# Startup
|
|
init_db_pool()
|
|
init_tables()
|
|
|
|
# Only start sidecar in browser mode (not when Electron manages it)
|
|
# Set BROWSER_MODE=true in start-browser.sh to enable
|
|
browser_mode = os.environ.get("BROWSER_MODE", "").lower() == "true"
|
|
sidecar_mgr = get_sidecar_manager()
|
|
|
|
if browser_mode and sidecar_mgr.is_available():
|
|
print("[Backend] Browser mode: Starting sidecar...")
|
|
await sidecar_mgr.start()
|
|
elif browser_mode:
|
|
print("[Backend] Browser mode: Sidecar not available (transcription disabled)")
|
|
else:
|
|
print("[Backend] Electron mode: Sidecar managed by Electron")
|
|
|
|
yield
|
|
|
|
# Shutdown - only stop if we started it
|
|
if browser_mode:
|
|
sidecar_mgr.stop()
|
|
|
|
|
|
app = FastAPI(
|
|
title="Meeting Assistant API",
|
|
description="Enterprise meeting knowledge management API",
|
|
version="1.0.0",
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
# CORS configuration for Electron client
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# Include routers
|
|
app.include_router(auth.router, prefix="/api", tags=["Authentication"])
|
|
app.include_router(meetings.router, prefix="/api", tags=["Meetings"])
|
|
app.include_router(ai.router, prefix="/api", tags=["AI"])
|
|
app.include_router(export.router, prefix="/api", tags=["Export"])
|
|
app.include_router(sidecar.router, prefix="/api", tags=["Sidecar"])
|
|
|
|
|
|
@app.get("/api/health")
|
|
async def health_check():
|
|
"""Health check endpoint."""
|
|
return {"status": "healthy", "service": "meeting-assistant"}
|
|
|
|
|
|
# ========================================
|
|
# Browser Mode: Serve static files
|
|
# ========================================
|
|
|
|
# Check if client directory exists for browser mode
|
|
if CLIENT_DIR.exists():
|
|
# Serve static assets (CSS, JS, etc.)
|
|
app.mount("/styles", StaticFiles(directory=CLIENT_DIR / "styles"), name="styles")
|
|
app.mount("/services", StaticFiles(directory=CLIENT_DIR / "services"), name="services")
|
|
app.mount("/config", StaticFiles(directory=CLIENT_DIR / "config"), name="config")
|
|
|
|
@app.get("/")
|
|
async def serve_login():
|
|
"""Serve login page."""
|
|
return FileResponse(CLIENT_DIR / "pages" / "login.html")
|
|
|
|
@app.get("/login")
|
|
async def serve_login_page():
|
|
"""Serve login page."""
|
|
return FileResponse(CLIENT_DIR / "pages" / "login.html")
|
|
|
|
@app.get("/meetings")
|
|
async def serve_meetings_page():
|
|
"""Serve meetings list page."""
|
|
return FileResponse(CLIENT_DIR / "pages" / "meetings.html")
|
|
|
|
@app.get("/meeting-detail")
|
|
async def serve_meeting_detail_page():
|
|
"""Serve meeting detail page."""
|
|
return FileResponse(CLIENT_DIR / "pages" / "meeting-detail.html")
|