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>
This commit is contained in:
egg
2025-12-17 10:06:29 +08:00
parent b1633fdcff
commit 58f379bc0c
11 changed files with 1003 additions and 17 deletions

112
backend/build.py Normal file
View File

@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""
Build script for creating standalone backend executable using PyInstaller.
Uses --onedir mode for faster startup compared to --onefile.
"""
import subprocess
import sys
import os
import shutil
def build():
"""Build the backend executable."""
script_dir = os.path.dirname(os.path.abspath(__file__))
# PyInstaller command with --onedir for faster startup
cmd = [
sys.executable, "-m", "PyInstaller",
"--onedir",
"--name", "backend",
"--distpath", "dist",
"--workpath", "build",
"--specpath", "build",
# FastAPI and web framework
"--hidden-import", "uvicorn",
"--hidden-import", "uvicorn.logging",
"--hidden-import", "uvicorn.loops",
"--hidden-import", "uvicorn.loops.auto",
"--hidden-import", "uvicorn.protocols",
"--hidden-import", "uvicorn.protocols.http",
"--hidden-import", "uvicorn.protocols.http.auto",
"--hidden-import", "uvicorn.protocols.websockets",
"--hidden-import", "uvicorn.protocols.websockets.auto",
"--hidden-import", "uvicorn.lifespan",
"--hidden-import", "uvicorn.lifespan.on",
"--hidden-import", "uvicorn.lifespan.off",
"--hidden-import", "fastapi",
"--hidden-import", "starlette",
"--hidden-import", "pydantic",
"--hidden-import", "pydantic_core",
# Database
"--hidden-import", "mysql.connector",
"--hidden-import", "mysql.connector.pooling",
# HTTP client
"--hidden-import", "httpx",
"--hidden-import", "httpcore",
# Authentication
"--hidden-import", "jose",
"--hidden-import", "jose.jwt",
"--hidden-import", "cryptography",
# Excel export
"--hidden-import", "openpyxl",
# Multipart form handling
"--hidden-import", "multipart",
"--hidden-import", "python_multipart",
# Environment loading
"--hidden-import", "dotenv",
# Application modules
"--hidden-import", "app",
"--hidden-import", "app.main",
"--hidden-import", "app.config",
"--hidden-import", "app.database",
"--hidden-import", "app.auth",
"--hidden-import", "app.routers",
"--hidden-import", "app.routers.auth",
"--hidden-import", "app.routers.meetings",
"--hidden-import", "app.routers.dify",
"--hidden-import", "app.routers.health",
"--hidden-import", "app.routers.excel",
# Collect package data
"--collect-data", "pydantic",
"--collect-data", "uvicorn",
"run_server.py"
]
print("Building backend executable...")
print(f"Command: {' '.join(cmd)}")
result = subprocess.run(cmd, cwd=script_dir)
if result.returncode != 0:
print("\nBuild failed!")
sys.exit(1)
# Copy template directory to dist
template_src = os.path.join(script_dir, "template")
template_dst = os.path.join(script_dir, "dist", "backend", "template")
if os.path.exists(template_src):
print(f"\nCopying template directory to {template_dst}...")
if os.path.exists(template_dst):
shutil.rmtree(template_dst)
shutil.copytree(template_src, template_dst)
print("Template directory copied successfully.")
else:
print(f"\nWarning: Template directory not found at {template_src}")
# Create empty record directory
record_dst = os.path.join(script_dir, "dist", "backend", "record")
os.makedirs(record_dst, exist_ok=True)
print(f"Created record directory at {record_dst}")
print("\nBuild successful!")
print(f"Executable created at: dist/backend/backend.exe (Windows) or dist/backend/backend (Linux)")
print("\nTo run:")
print(" 1. Copy config.json to dist/backend/")
print(" 2. Run: dist/backend/backend.exe (Windows) or ./dist/backend/backend (Linux)")
if __name__ == "__main__":
build()