Files
Meeting_Assistant/backend/run_server.py
egg 58f379bc0c feat: Add embedded backend packaging for all-in-one deployment
- Add backend/run_server.py entry point for embedded deployment
- Add backend/build.py PyInstaller script for backend packaging
- Modify config.py to support frozen executable paths
- Extend client/config.json with backend configuration section
- Add backend sidecar management in Electron main process
- Add Whisper model download progress reporting
- Update build-client.bat with --embedded-backend flag
- Update DEPLOYMENT.md with all-in-one deployment documentation

This enables packaging frontend and backend into a single executable
for simplified enterprise deployment. Backward compatible with
existing separate deployment mode (backend.embedded: false).

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 10:06:29 +08:00

146 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""
Backend entry point for embedded deployment.
Loads configuration from config.json and starts uvicorn server.
"""
import json
import os
import sys
def get_base_dir() -> str:
"""Get base directory, supporting PyInstaller frozen executables."""
if getattr(sys, "frozen", False):
# Running as PyInstaller bundle
return os.path.dirname(sys.executable)
else:
# Running as script
return os.path.dirname(os.path.abspath(__file__))
def load_config(config_path: str | None = None) -> dict:
"""Load configuration from config.json file."""
if config_path is None:
base_dir = get_base_dir()
config_path = os.path.join(base_dir, "config.json")
if os.path.exists(config_path):
with open(config_path, "r", encoding="utf-8") as f:
return json.load(f)
return {}
def apply_config_to_env(config: dict) -> None:
"""
Apply config.json values to environment variables.
Environment variables take precedence (already set values are not overwritten).
"""
backend_config = config.get("backend", {})
# Server configuration
if "host" in backend_config:
os.environ.setdefault("BACKEND_HOST", backend_config["host"])
if "port" in backend_config:
os.environ.setdefault("BACKEND_PORT", str(backend_config["port"]))
# Database configuration
db_config = backend_config.get("database", {})
if "host" in db_config:
os.environ.setdefault("DB_HOST", db_config["host"])
if "port" in db_config:
os.environ.setdefault("DB_PORT", str(db_config["port"]))
if "user" in db_config:
os.environ.setdefault("DB_USER", db_config["user"])
if "password" in db_config:
os.environ.setdefault("DB_PASS", db_config["password"])
if "database" in db_config:
os.environ.setdefault("DB_NAME", db_config["database"])
if "poolSize" in db_config:
os.environ.setdefault("DB_POOL_SIZE", str(db_config["poolSize"]))
# External API configuration
api_config = backend_config.get("externalApis", {})
if "authApiUrl" in api_config:
os.environ.setdefault("AUTH_API_URL", api_config["authApiUrl"])
if "difyApiUrl" in api_config:
os.environ.setdefault("DIFY_API_URL", api_config["difyApiUrl"])
if "difyApiKey" in api_config:
os.environ.setdefault("DIFY_API_KEY", api_config["difyApiKey"])
if "difySttApiKey" in api_config:
os.environ.setdefault("DIFY_STT_API_KEY", api_config["difySttApiKey"])
# Authentication configuration
auth_config = backend_config.get("auth", {})
if "adminEmail" in auth_config:
os.environ.setdefault("ADMIN_EMAIL", auth_config["adminEmail"])
if "jwtSecret" in auth_config:
os.environ.setdefault("JWT_SECRET", auth_config["jwtSecret"])
if "jwtExpireHours" in auth_config:
os.environ.setdefault("JWT_EXPIRE_HOURS", str(auth_config["jwtExpireHours"]))
# File configuration - set TEMPLATE_DIR and RECORD_DIR relative to base
base_dir = get_base_dir()
if not os.environ.get("TEMPLATE_DIR"):
template_dir = os.path.join(base_dir, "template")
if os.path.exists(template_dir):
os.environ["TEMPLATE_DIR"] = template_dir
if not os.environ.get("RECORD_DIR"):
record_dir = os.path.join(base_dir, "record")
os.makedirs(record_dir, exist_ok=True)
os.environ["RECORD_DIR"] = record_dir
def main():
"""Main entry point."""
import argparse
parser = argparse.ArgumentParser(description="Meeting Assistant Backend Server")
parser.add_argument(
"--config",
type=str,
help="Path to config.json file",
)
parser.add_argument(
"--host",
type=str,
help="Host to bind to (overrides config)",
)
parser.add_argument(
"--port",
type=int,
help="Port to bind to (overrides config)",
)
args = parser.parse_args()
# Load and apply configuration
config = load_config(args.config)
apply_config_to_env(config)
# Command line arguments override everything
if args.host:
os.environ["BACKEND_HOST"] = args.host
if args.port:
os.environ["BACKEND_PORT"] = str(args.port)
# Get final host/port values
host = os.environ.get("BACKEND_HOST", "127.0.0.1")
port = int(os.environ.get("BACKEND_PORT", "8000"))
print(f"Starting backend server on {host}:{port}", flush=True)
# Import and run uvicorn
import uvicorn
uvicorn.run(
"app.main:app",
host=host,
port=port,
log_level="info",
)
if __name__ == "__main__":
main()