Files
Meeting_Assistant/backend/app/main.py
egg 771655e03e fix: Browser mode 404 error for meeting-detail page
- Preserve query parameters (e.g., ?id=123) when opening in browser
- Add packaged mode detection for CLIENT_DIR path resolution
- Include client files in extraResources for backend to serve
- Add debug logging for client directory detection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 17:50:14 +08:00

119 lines
4.0 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
# In development: backend/../client/src
# In packaged mode: backend/backend/_internal/../../client (relative to backend executable)
BACKEND_DIR = Path(__file__).parent.parent
PROJECT_DIR = BACKEND_DIR.parent
CLIENT_DIR = PROJECT_DIR / "client" / "src"
# Check for packaged mode (PyInstaller sets _MEIPASS)
import sys
if getattr(sys, 'frozen', False):
# Packaged mode: look for client folder relative to executable
# Backend runs from resources/backend/, client files at resources/backend/client/
EXEC_DIR = Path(sys.executable).parent.parent # up from backend/backend.exe
CLIENT_DIR = EXEC_DIR / "client"
print(f"[Backend] Packaged mode: CLIENT_DIR={CLIENT_DIR}")
else:
print(f"[Backend] Development mode: CLIENT_DIR={CLIENT_DIR}")
@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
print(f"[Backend] CLIENT_DIR exists: {CLIENT_DIR.exists()}")
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")