feat: Add build scripts and runtime config support

Backend:
- Add setup-backend.sh/bat for one-click backend setup
- Fix test_auth.py mock settings (JWT_EXPIRE_HOURS)
- Fix test_excel_export.py TEMPLATE_DIR reference

Frontend:
- Add config.json for runtime API URL configuration
- Add init.js and settings.js for config loading
- Update main.js to load config from external file
- Update api.js to use dynamic API_BASE_URL
- Update all pages to initialize config before API calls
- Update package.json with extraResources for config

Build:
- Add build-client.sh/bat for packaging Electron + Sidecar
- Add build-all.ps1 PowerShell script with -ApiUrl parameter
- Add GitHub Actions workflow for Windows builds
- Add scripts/README.md documentation

This allows IT to configure backend URL without rebuilding.

🤖 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-16 20:03:16 +08:00
parent 01aee1fd0d
commit 7d4fc69071
20 changed files with 2454 additions and 32 deletions

199
scripts/README.md Normal file
View File

@@ -0,0 +1,199 @@
# Meeting Assistant 建置腳本
本目錄包含後端設置與前端打包的自動化腳本。
## 腳本清單
| 腳本 | 平台 | 說明 |
|------|------|------|
| `setup-backend.sh` | Linux/macOS/WSL | 後端一鍵設置與啟動 |
| `setup-backend.bat` | Windows | 後端一鍵設置與啟動 |
| `build-client.sh` | Linux/macOS/WSL | 前端打包腳本 |
| `build-client.bat` | Windows | 前端打包腳本 |
| `deploy-backend.sh` | Linux | 生產環境部署腳本 |
---
## 後端設置腳本
### 功能
- 自動檢查 Python 環境 (需要 3.10+)
- 自動建立虛擬環境
- 自動安裝依賴
- 自動設置環境變數檔案
- 啟動後端服務
### 使用方式
**Linux/macOS/WSL:**
```bash
# 一鍵設置並啟動
./scripts/setup-backend.sh start
# 僅設置環境
./scripts/setup-backend.sh setup
# 背景執行
./scripts/setup-backend.sh start-bg
# 停止背景服務
./scripts/setup-backend.sh stop
# 使用自訂端口
./scripts/setup-backend.sh start --port 8080
# 不安裝 Sidecar 依賴
./scripts/setup-backend.sh setup --no-sidecar
```
**Windows:**
```batch
REM 一鍵設置並啟動
scripts\setup-backend.bat start
REM 僅設置環境
scripts\setup-backend.bat setup
REM 使用自訂端口
scripts\setup-backend.bat start --port 8080
```
### 環境變數
啟動後請編輯 `backend/.env` 設定:
- 資料庫連線
- API 密鑰
- 服務配置
---
## 前端打包腳本
### 功能
- 將 Python Sidecar 打包成獨立執行檔 (PyInstaller)
- 將 Electron 應用打包成免安裝 exe
- 整合 Sidecar 到最終輸出
### 系統需求
- Node.js 18+
- Python 3.10+
- 磁碟空間 5GB+ (Whisper 模型)
### 使用方式
**Linux/macOS/WSL:**
```bash
# 完整建置 (Sidecar + Electron)
./scripts/build-client.sh build
# 僅打包 Sidecar
./scripts/build-client.sh sidecar
# 僅打包 Electron (需先打包 Sidecar)
./scripts/build-client.sh electron
# 建置前清理
./scripts/build-client.sh build --clean
# 指定目標平台
./scripts/build-client.sh build --platform linux
./scripts/build-client.sh build --platform mac
./scripts/build-client.sh build --platform win
```
**Windows:**
```batch
REM 完整建置
scripts\build-client.bat build
REM 僅打包 Sidecar
scripts\build-client.bat sidecar
REM 僅打包 Electron
scripts\build-client.bat electron
REM 建置前清理
scripts\build-client.bat build --clean
```
### 輸出目錄
- Sidecar: `sidecar/dist/transcriber/`
- Electron: `client/dist/`
- 最終輸出: `build/`
### 注意事項
1. **跨平台打包限制**
- Windows exe 必須在 Windows 環境打包
- macOS dmg 必須在 macOS 環境打包
- Linux AppImage 可在 Linux 或 WSL 打包
2. **首次打包時間**
- Sidecar 首次打包需下載 Whisper 模型
- 根據網路速度可能需要 10-30 分鐘
3. **磁碟空間**
- 完整打包需要約 2-3GB 空間
- 建議預留 5GB 以上
---
## 生產環境部署
使用 `deploy-backend.sh` 在 Linux 伺服器上部署:
```bash
# 安裝後端服務
sudo ./scripts/deploy-backend.sh install
# 更新後端服務
sudo ./scripts/deploy-backend.sh update
# 查看狀態
./scripts/deploy-backend.sh status
# 查看日誌
./scripts/deploy-backend.sh logs
# 移除服務
sudo ./scripts/deploy-backend.sh uninstall
```
### 自訂配置
```bash
# 指定安裝目錄和端口
sudo ./scripts/deploy-backend.sh install --dir /opt/my-meeting --port 8080 --user meeting
```
部署後會建立 systemd 服務 `meeting-assistant-backend`,可使用標準 systemctl 命令管理。
---
## 疑難排解
### Python 版本問題
確保使用 Python 3.10 或更高版本:
```bash
python3 --version
```
### 虛擬環境問題
如果虛擬環境損壞,可刪除後重建:
```bash
rm -rf backend/venv sidecar/venv
./scripts/setup-backend.sh setup
```
### Electron 打包失敗
確保已安裝 node_modules
```bash
cd client
npm install
```
### Sidecar 打包失敗
確保 PyInstaller 已安裝:
```bash
cd sidecar
source venv/bin/activate # Linux/macOS
pip install pyinstaller
```

304
scripts/build-all.ps1 Normal file
View File

@@ -0,0 +1,304 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Meeting Assistant - Windows 一鍵打包腳本
.DESCRIPTION
在 Windows 環境下一鍵打包 Sidecar + Electron 成免安裝 exe
.EXAMPLE
.\scripts\build-all.ps1
.\scripts\build-all.ps1 -ApiUrl "http://192.168.1.100:8000/api"
.\scripts\build-all.ps1 -SkipSidecar
.\scripts\build-all.ps1 -Clean
#>
param(
[string]$ApiUrl,
[switch]$SkipSidecar,
[switch]$Clean,
[switch]$Help
)
$ErrorActionPreference = "Stop"
# 顏色輸出函數
function Write-Step { param($msg) Write-Host "[STEP] $msg" -ForegroundColor Cyan }
function Write-OK { param($msg) Write-Host "[OK] $msg" -ForegroundColor Green }
function Write-Warn { param($msg) Write-Host "[WARN] $msg" -ForegroundColor Yellow }
function Write-Err { param($msg) Write-Host "[ERROR] $msg" -ForegroundColor Red }
# 路徑設定
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$ProjectDir = Split-Path -Parent $ScriptDir
$SidecarDir = Join-Path $ProjectDir "sidecar"
$ClientDir = Join-Path $ProjectDir "client"
$BuildDir = Join-Path $ProjectDir "build"
function Show-Banner {
Write-Host ""
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host " Meeting Assistant - Windows Builder" -ForegroundColor Cyan
Write-Host " 一鍵打包 Electron + Sidecar" -ForegroundColor Cyan
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host ""
}
function Show-Help {
Write-Host @"
Meeting Assistant - Windows
: .\build-all.ps1 []
:
-ApiUrl URL API URL (: http://localhost:8000/api)
-SkipSidecar Sidecar
-Clean
-Help
:
.\build-all.ps1 # (使 localhost)
.\build-all.ps1 -ApiUrl "http://192.168.1.100:8000/api" # URL
.\build-all.ps1 -ApiUrl "https://api.example.com/api" # 使
.\build-all.ps1 -Clean #
.\build-all.ps1 -SkipSidecar # Electron
"@
}
function Update-Config {
param([string]$NewApiUrl)
Write-Step "更新設定檔..."
$configPath = Join-Path $ClientDir "config.json"
if (Test-Path $configPath) {
$config = Get-Content $configPath -Raw | ConvertFrom-Json
if ($NewApiUrl) {
$config.apiBaseUrl = $NewApiUrl
Write-Host " API URL: $NewApiUrl" -ForegroundColor Gray
}
$config | ConvertTo-Json -Depth 10 | Set-Content $configPath -Encoding UTF8
Write-OK "設定檔已更新"
} else {
Write-Warn "找不到 config.json使用預設設定"
}
}
function Test-Prerequisites {
Write-Step "檢查建置環境..."
# 檢查 Python
try {
$pyVersion = python --version 2>&1
Write-OK "Python: $pyVersion"
} catch {
Write-Err "Python 未安裝,請安裝 Python 3.10+"
exit 1
}
# 檢查 Node.js
try {
$nodeVersion = node --version 2>&1
Write-OK "Node.js: $nodeVersion"
} catch {
Write-Err "Node.js 未安裝,請安裝 Node.js 18+"
exit 1
}
# 檢查 npm
try {
$npmVersion = npm --version 2>&1
Write-OK "npm: $npmVersion"
} catch {
Write-Err "npm 未安裝"
exit 1
}
}
function Clear-BuildDirs {
Write-Step "清理建置目錄..."
$dirsToClean = @(
$BuildDir,
(Join-Path $ClientDir "dist"),
(Join-Path $SidecarDir "dist"),
(Join-Path $SidecarDir "build")
)
foreach ($dir in $dirsToClean) {
if (Test-Path $dir) {
Remove-Item -Recurse -Force $dir
Write-Host " 已刪除: $dir" -ForegroundColor Gray
}
}
# 刪除 spec 檔案
Get-ChildItem -Path $SidecarDir -Filter "*.spec" | Remove-Item -Force
Write-OK "清理完成"
}
function Build-Sidecar {
Write-Step "打包 Sidecar (Python → exe)..."
Write-Host " 這可能需要 5-10 分鐘..." -ForegroundColor Gray
Push-Location $SidecarDir
try {
# 建立/啟動虛擬環境
if (-not (Test-Path "venv")) {
Write-Host " 建立虛擬環境..." -ForegroundColor Gray
python -m venv venv
}
# 啟動虛擬環境並安裝依賴
& ".\venv\Scripts\Activate.ps1"
Write-Host " 安裝依賴..." -ForegroundColor Gray
pip install --upgrade pip -q
pip install -r requirements.txt -q
pip install pyinstaller -q
# 建立 dist 目錄
if (-not (Test-Path "dist")) {
New-Item -ItemType Directory -Path "dist" | Out-Null
}
Write-Host " 執行 PyInstaller..." -ForegroundColor Gray
# PyInstaller 打包
pyinstaller `
--onedir `
--name transcriber `
--distpath dist `
--workpath build `
--noconfirm `
--clean `
--console `
--hidden-import=faster_whisper `
--hidden-import=ctranslate2 `
--hidden-import=huggingface_hub `
--hidden-import=tokenizers `
--hidden-import=onnxruntime `
--hidden-import=opencc `
--hidden-import=pydub `
--hidden-import=numpy `
--hidden-import=av `
--collect-data=onnxruntime `
--collect-data=faster_whisper `
transcriber.py
if (Test-Path "dist\transcriber\transcriber.exe") {
Write-OK "Sidecar 打包完成"
} else {
Write-Err "Sidecar 打包失敗"
exit 1
}
}
finally {
Pop-Location
}
}
function Build-Electron {
Write-Step "打包 Electron 應用..."
Push-Location $ClientDir
try {
# 檢查 Sidecar 是否存在
$sidecarExe = Join-Path $SidecarDir "dist\transcriber\transcriber.exe"
if (-not (Test-Path $sidecarExe)) {
Write-Err "找不到 Sidecar: $sidecarExe"
Write-Warn "請先執行完整打包或移除 -SkipSidecar 參數"
exit 1
}
# 安裝依賴
if (-not (Test-Path "node_modules")) {
Write-Host " 安裝 npm 依賴..." -ForegroundColor Gray
npm install
}
# 建立 .env 如果不存在
if (-not (Test-Path ".env") -and (Test-Path ".env.example")) {
Copy-Item ".env.example" ".env"
Write-Warn "已建立 .env請確認設定"
}
Write-Host " 執行 electron-builder..." -ForegroundColor Gray
npm run build -- --win
if (Test-Path "dist\*.exe") {
Write-OK "Electron 打包完成"
} else {
Write-Err "Electron 打包失敗"
exit 1
}
}
finally {
Pop-Location
}
}
function Copy-Output {
Write-Step "整合輸出..."
if (-not (Test-Path $BuildDir)) {
New-Item -ItemType Directory -Path $BuildDir | Out-Null
}
# 複製 exe 檔案
$exeFiles = Get-ChildItem -Path (Join-Path $ClientDir "dist") -Filter "*.exe"
foreach ($file in $exeFiles) {
Copy-Item $file.FullName -Destination $BuildDir
Write-Host " 複製: $($file.Name)" -ForegroundColor Gray
}
Write-Host ""
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host " 打包完成!" -ForegroundColor Green
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host " 輸出目錄: $BuildDir" -ForegroundColor White
Write-Host ""
# 列出輸出檔案
Get-ChildItem -Path $BuildDir | ForEach-Object {
$size = [math]::Round($_.Length / 1MB, 2)
Write-Host " $($_.Name) ($size MB)" -ForegroundColor Gray
}
Write-Host ""
Write-Host " 使用說明:" -ForegroundColor Yellow
Write-Host " 直接執行 .exe 檔案即可,無需安裝" -ForegroundColor Gray
Write-Host ""
}
# 主程式
if ($Help) {
Show-Help
exit 0
}
Show-Banner
Test-Prerequisites
if ($Clean) {
Clear-BuildDirs
}
# Update config.json if API URL is specified
if ($ApiUrl) {
Update-Config -NewApiUrl $ApiUrl
}
if (-not $SkipSidecar) {
Build-Sidecar
}
Build-Electron
Copy-Output

324
scripts/build-client.bat Normal file
View File

@@ -0,0 +1,324 @@
@echo off
REM Meeting Assistant Client - Windows 打包腳本
REM 將 Electron 應用與 Python Sidecar 打包成免安裝 exe
setlocal enabledelayedexpansion
REM 顏色設定 (Windows 10+)
set "GREEN=[92m"
set "YELLOW=[93m"
set "RED=[91m"
set "BLUE=[94m"
set "CYAN=[96m"
set "NC=[0m"
REM 專案路徑
set "SCRIPT_DIR=%~dp0"
set "PROJECT_DIR=%SCRIPT_DIR%.."
set "CLIENT_DIR=%PROJECT_DIR%\client"
set "SIDECAR_DIR=%PROJECT_DIR%\sidecar"
set "BUILD_DIR=%PROJECT_DIR%\build"
REM 預設配置
set "SKIP_SIDECAR=false"
set "CLEAN_BUILD=false"
REM 解析參數
set "COMMAND=help"
:parse_args
if "%~1"=="" goto :main
if /i "%~1"=="build" (set "COMMAND=build" & shift & goto :parse_args)
if /i "%~1"=="sidecar" (set "COMMAND=sidecar" & shift & goto :parse_args)
if /i "%~1"=="electron" (set "COMMAND=electron" & shift & goto :parse_args)
if /i "%~1"=="clean" (set "COMMAND=clean" & shift & goto :parse_args)
if /i "%~1"=="help" (set "COMMAND=help" & shift & goto :parse_args)
if /i "%~1"=="--skip-sidecar" (set "SKIP_SIDECAR=true" & shift & goto :parse_args)
if /i "%~1"=="--clean" (set "CLEAN_BUILD=true" & shift & goto :parse_args)
echo %RED%[ERROR]%NC% 未知參數: %~1
goto :show_help
:main
if "%COMMAND%"=="help" goto :show_help
if "%COMMAND%"=="build" goto :do_build
if "%COMMAND%"=="sidecar" goto :do_sidecar
if "%COMMAND%"=="electron" goto :do_electron
if "%COMMAND%"=="clean" goto :do_clean
goto :show_help
:show_banner
echo.
echo %CYAN%==========================================
echo Meeting Assistant Client Builder
echo 打包 Electron + Sidecar 為免安裝執行檔
echo ==========================================%NC%
echo.
goto :eof
:check_environment
echo %BLUE%[STEP]%NC% 檢查建置環境...
REM 檢查 Node.js
where node >nul 2>&1
if %errorlevel% equ 0 (
for /f "tokens=*" %%i in ('node --version') do echo %GREEN%[OK]%NC% Node.js: %%i
) else (
echo %RED%[ERROR]%NC% Node.js 未安裝
exit /b 1
)
REM 檢查 npm
where npm >nul 2>&1
if %errorlevel% equ 0 (
for /f "tokens=*" %%i in ('npm --version') do echo %GREEN%[OK]%NC% npm: %%i
) else (
echo %RED%[ERROR]%NC% npm 未安裝
exit /b 1
)
REM 檢查 Python
where python >nul 2>&1
if %errorlevel% equ 0 (
for /f "tokens=*" %%i in ('python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"') do set "PY_VERSION=%%i"
for /f "tokens=*" %%i in ('python -c "import sys; print(sys.version_info.major)"') do set "PY_MAJOR=%%i"
for /f "tokens=*" %%i in ('python -c "import sys; print(sys.version_info.minor)"') do set "PY_MINOR=%%i"
if !PY_MAJOR! geq 3 if !PY_MINOR! geq 10 (
set "PYTHON_CMD=python"
echo %GREEN%[OK]%NC% Python !PY_VERSION!
goto :eof
)
)
echo %RED%[ERROR]%NC% 需要 Python 3.10 或更高版本
exit /b 1
:do_clean
echo %BLUE%[STEP]%NC% 清理建置目錄...
if exist "%BUILD_DIR%" rmdir /s /q "%BUILD_DIR%"
if exist "%CLIENT_DIR%\dist" rmdir /s /q "%CLIENT_DIR%\dist"
if exist "%SIDECAR_DIR%\dist" rmdir /s /q "%SIDECAR_DIR%\dist"
if exist "%SIDECAR_DIR%\build" rmdir /s /q "%SIDECAR_DIR%\build"
if exist "%SIDECAR_DIR%\*.spec" del /q "%SIDECAR_DIR%\*.spec"
echo %GREEN%[OK]%NC% 建置目錄已清理
goto :eof
:setup_sidecar_venv
echo %BLUE%[STEP]%NC% 設置 Sidecar 建置環境...
cd /d "%SIDECAR_DIR%"
if not exist "venv" (
echo %BLUE%[INFO]%NC% 創建虛擬環境...
%PYTHON_CMD% -m venv venv
)
echo %BLUE%[INFO]%NC% 安裝 Sidecar 依賴...
call venv\Scripts\activate.bat
pip install --upgrade pip -q
pip install -r requirements.txt -q
echo %BLUE%[INFO]%NC% 安裝 PyInstaller...
pip install pyinstaller -q
echo %GREEN%[OK]%NC% Sidecar 建置環境就緒
goto :eof
:build_sidecar
echo %BLUE%[STEP]%NC% 打包 Sidecar (Python → 獨立執行檔)...
cd /d "%SIDECAR_DIR%"
call venv\Scripts\activate.bat
if not exist "dist" mkdir dist
echo %BLUE%[INFO]%NC% 執行 PyInstaller...
echo %BLUE%[INFO]%NC% 這可能需要幾分鐘...
pyinstaller ^
--onedir ^
--name transcriber ^
--distpath dist ^
--workpath build ^
--specpath . ^
--noconfirm ^
--clean ^
--log-level WARN ^
--console ^
--hidden-import=faster_whisper ^
--hidden-import=ctranslate2 ^
--hidden-import=huggingface_hub ^
--hidden-import=tokenizers ^
--hidden-import=onnxruntime ^
--hidden-import=opencc ^
--hidden-import=pydub ^
--hidden-import=numpy ^
--hidden-import=av ^
--collect-data=onnxruntime ^
--collect-data=faster_whisper ^
transcriber.py
if exist "dist\transcriber" (
echo %GREEN%[OK]%NC% Sidecar 打包完成: %SIDECAR_DIR%\dist\transcriber
) else (
echo %RED%[ERROR]%NC% Sidecar 打包失敗
exit /b 1
)
goto :eof
:setup_client
echo %BLUE%[STEP]%NC% 設置前端建置環境...
cd /d "%CLIENT_DIR%"
if not exist "node_modules" (
echo %BLUE%[INFO]%NC% 安裝前端依賴...
call npm install
) else (
echo %BLUE%[INFO]%NC% 前端依賴已安裝
)
if not exist ".env" (
if exist ".env.example" (
copy .env.example .env >nul
echo %YELLOW%[WARN]%NC% 已創建 .env 檔案,請確認設定
)
)
echo %GREEN%[OK]%NC% 前端建置環境就緒
goto :eof
:build_electron
echo %BLUE%[STEP]%NC% 打包 Electron 應用...
cd /d "%CLIENT_DIR%"
echo %BLUE%[INFO]%NC% 目標平台: Windows (Portable)
echo %BLUE%[INFO]%NC% 執行 electron-builder...
call npm run build -- --win
if exist "dist" (
echo %GREEN%[OK]%NC% Electron 打包完成
echo %BLUE%[INFO]%NC% 輸出目錄: %CLIENT_DIR%\dist
) else (
echo %RED%[ERROR]%NC% Electron 打包失敗
exit /b 1
)
goto :eof
:finalize_build
echo %BLUE%[STEP]%NC% 整合建置輸出...
if not exist "%BUILD_DIR%" mkdir "%BUILD_DIR%"
REM 複製 Electron 輸出
if exist "%CLIENT_DIR%\dist" (
xcopy /s /e /y "%CLIENT_DIR%\dist\*" "%BUILD_DIR%\" >nul 2>&1
)
echo.
echo %CYAN%==========================================
echo 建置完成
echo ==========================================%NC%
echo.
echo 輸出目錄: %BUILD_DIR%
echo.
dir /b "%BUILD_DIR%"
echo.
echo %GREEN%[OK]%NC% 打包完成!
echo.
echo Windows 使用說明:
echo 1. 找到 build\ 中的 .exe 檔案
echo 2. 直接執行即可,無需安裝
echo.
goto :eof
:do_build
call :show_banner
call :check_environment
if errorlevel 1 exit /b 1
if "%CLEAN_BUILD%"=="true" call :do_clean
if "%SKIP_SIDECAR%"=="false" (
call :setup_sidecar_venv
call :build_sidecar
)
call :setup_client
call :build_electron
call :finalize_build
goto :eof
:do_sidecar
call :show_banner
call :check_environment
if errorlevel 1 exit /b 1
if "%CLEAN_BUILD%"=="true" (
if exist "%SIDECAR_DIR%\dist" rmdir /s /q "%SIDECAR_DIR%\dist"
if exist "%SIDECAR_DIR%\build" rmdir /s /q "%SIDECAR_DIR%\build"
)
call :setup_sidecar_venv
call :build_sidecar
goto :eof
:do_electron
call :show_banner
call :check_environment
if errorlevel 1 exit /b 1
if not exist "%SIDECAR_DIR%\dist\transcriber" (
if "%SKIP_SIDECAR%"=="false" (
echo %YELLOW%[WARN]%NC% Sidecar 尚未打包
echo %BLUE%[INFO]%NC% 請先執行: %~nx0 sidecar
echo %BLUE%[INFO]%NC% 或使用 --skip-sidecar 跳過
exit /b 1
)
)
if "%CLEAN_BUILD%"=="true" (
if exist "%CLIENT_DIR%\dist" rmdir /s /q "%CLIENT_DIR%\dist"
)
call :setup_client
call :build_electron
call :finalize_build
goto :eof
:show_help
echo.
echo Meeting Assistant Client - Windows 打包腳本
echo.
echo 用法: %~nx0 [命令] [選項]
echo.
echo 命令:
echo build 完整建置 (Sidecar + Electron)
echo sidecar 僅打包 Sidecar
echo electron 僅打包 Electron (需先打包 Sidecar)
echo clean 清理建置目錄
echo help 顯示此幫助訊息
echo.
echo 選項:
echo --skip-sidecar 跳過 Sidecar 打包
echo --clean 建置前先清理
echo.
echo 範例:
echo %~nx0 build 完整建置
echo %~nx0 sidecar 僅打包 Sidecar
echo %~nx0 electron --skip-sidecar 僅打包 Electron
echo.
echo 注意:
echo - 首次打包 Sidecar 需下載 Whisper 模型,可能需要較長時間
echo - 確保有足夠的磁碟空間 (建議 5GB+)
echo.
goto :eof
:end
endlocal

473
scripts/build-client.sh Executable file
View File

@@ -0,0 +1,473 @@
#!/bin/bash
#
# Meeting Assistant Client - 打包腳本
# 將 Electron 應用與 Python Sidecar 打包成免安裝 exe
#
# 依賴:
# - Node.js 18+
# - Python 3.10+
# - PyInstaller (會自動安裝)
#
set -e
# 顏色定義
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# 專案路徑
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
CLIENT_DIR="$PROJECT_DIR/client"
SIDECAR_DIR="$PROJECT_DIR/sidecar"
BUILD_DIR="$PROJECT_DIR/build"
# 預設配置
TARGET_PLATFORM="win" # win, mac, linux
SKIP_SIDECAR=false
CLEAN_BUILD=false
# 函數:印出訊息
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[OK]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${CYAN}[STEP]${NC} $1"
}
# 函數:顯示標題
show_banner() {
echo ""
echo -e "${CYAN}=========================================="
echo " Meeting Assistant Client Builder"
echo " 打包 Electron + Sidecar 為免安裝執行檔"
echo -e "==========================================${NC}"
echo ""
}
# 函數:檢查環境
check_environment() {
log_step "檢查建置環境..."
local all_ok=true
# 檢查 Node.js
if command -v node &> /dev/null; then
local node_version=$(node --version)
log_success "Node.js: $node_version"
else
log_error "Node.js 未安裝"
all_ok=false
fi
# 檢查 npm
if command -v npm &> /dev/null; then
local npm_version=$(npm --version)
log_success "npm: $npm_version"
else
log_error "npm 未安裝"
all_ok=false
fi
# 檢查 Python
local python_cmd=""
for cmd in python3 python; do
if command -v $cmd &> /dev/null; then
local version=$($cmd -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
local major=$($cmd -c 'import sys; print(sys.version_info.major)')
local minor=$($cmd -c 'import sys; print(sys.version_info.minor)')
if [ "$major" -ge 3 ] && [ "$minor" -ge 10 ]; then
python_cmd=$cmd
log_success "Python: $version ($cmd)"
break
fi
fi
done
if [ -z "$python_cmd" ]; then
log_error "需要 Python 3.10+"
all_ok=false
fi
if [ "$all_ok" = false ]; then
log_error "環境檢查失敗,請安裝缺少的依賴"
exit 1
fi
echo "$python_cmd"
}
# 函數:清理建置目錄
clean_build() {
log_step "清理建置目錄..."
rm -rf "$BUILD_DIR"
rm -rf "$CLIENT_DIR/dist"
rm -rf "$SIDECAR_DIR/dist"
rm -rf "$SIDECAR_DIR/build"
rm -rf "$SIDECAR_DIR/*.spec"
log_success "建置目錄已清理"
}
# 函數:設置 Sidecar 虛擬環境
setup_sidecar_venv() {
local python_cmd=$1
log_step "設置 Sidecar 建置環境..."
cd "$SIDECAR_DIR"
# 創建或使用現有虛擬環境
if [ ! -d "venv" ]; then
log_info "創建虛擬環境..."
$python_cmd -m venv venv
fi
# 啟動虛擬環境
if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
source venv/Scripts/activate
else
source venv/bin/activate
fi
# 安裝依賴
log_info "安裝 Sidecar 依賴..."
pip install --upgrade pip -q
pip install -r requirements.txt -q
# 安裝 PyInstaller
log_info "安裝 PyInstaller..."
pip install pyinstaller -q
log_success "Sidecar 建置環境就緒"
}
# 函數:打包 Sidecar
build_sidecar() {
log_step "打包 Sidecar (Python → 獨立執行檔)..."
cd "$SIDECAR_DIR"
# 啟動虛擬環境
if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
source venv/Scripts/activate
else
source venv/bin/activate
fi
# 確保 dist 目錄存在
mkdir -p dist
# PyInstaller 打包參數
local pyinstaller_args=(
--onedir # 打包成目錄 (比 onefile 啟動快)
--name transcriber # 輸出名稱
--distpath dist # 輸出目錄
--workpath build # 工作目錄
--specpath . # spec 檔案位置
--noconfirm # 覆蓋現有檔案
--clean # 清理暫存檔
--log-level WARN # 日誌等級
)
# 根據平台添加特定參數
if [[ "$TARGET_PLATFORM" == "win" ]]; then
pyinstaller_args+=(--console) # Windows 保留控制台 (用於 stdio 通訊)
fi
# 收集 faster-whisper 的隱藏導入
pyinstaller_args+=(
--hidden-import=faster_whisper
--hidden-import=ctranslate2
--hidden-import=huggingface_hub
--hidden-import=tokenizers
--hidden-import=onnxruntime
--hidden-import=opencc
--hidden-import=pydub
--hidden-import=numpy
--hidden-import=av
)
# 收集 onnxruntime 資料
pyinstaller_args+=(
--collect-data=onnxruntime
--collect-data=faster_whisper
)
log_info "執行 PyInstaller..."
log_info "這可能需要幾分鐘..."
pyinstaller "${pyinstaller_args[@]}" transcriber.py
if [ -d "dist/transcriber" ]; then
log_success "Sidecar 打包完成: $SIDECAR_DIR/dist/transcriber"
else
log_error "Sidecar 打包失敗"
exit 1
fi
}
# 函數:安裝前端依賴
setup_client() {
log_step "設置前端建置環境..."
cd "$CLIENT_DIR"
if [ ! -d "node_modules" ]; then
log_info "安裝前端依賴..."
npm install
else
log_info "前端依賴已安裝"
fi
# 確保 .env 存在
if [ ! -f ".env" ]; then
if [ -f ".env.example" ]; then
cp .env.example .env
log_warn "已創建 .env 檔案,請確認設定"
fi
fi
log_success "前端建置環境就緒"
}
# 函數:打包 Electron
build_electron() {
log_step "打包 Electron 應用..."
cd "$CLIENT_DIR"
# 根據目標平台設置建置參數
local build_args=()
case $TARGET_PLATFORM in
win)
build_args+=(--win)
log_info "目標平台: Windows (Portable)"
;;
mac)
build_args+=(--mac)
log_info "目標平台: macOS (DMG)"
;;
linux)
build_args+=(--linux)
log_info "目標平台: Linux (AppImage)"
;;
*)
log_error "不支援的平台: $TARGET_PLATFORM"
exit 1
;;
esac
log_info "執行 electron-builder..."
npm run build -- "${build_args[@]}"
if [ -d "dist" ]; then
log_success "Electron 打包完成"
log_info "輸出目錄: $CLIENT_DIR/dist"
else
log_error "Electron 打包失敗"
exit 1
fi
}
# 函數:整合輸出
finalize_build() {
log_step "整合建置輸出..."
mkdir -p "$BUILD_DIR"
# 複製 Electron 輸出
if [ -d "$CLIENT_DIR/dist" ]; then
cp -r "$CLIENT_DIR/dist/"* "$BUILD_DIR/" 2>/dev/null || true
fi
# 顯示結果
echo ""
echo -e "${CYAN}=========================================="
echo " 建置完成"
echo -e "==========================================${NC}"
echo ""
echo " 輸出目錄: $BUILD_DIR"
echo ""
# 列出輸出檔案
if command -v tree &> /dev/null; then
tree -L 2 "$BUILD_DIR" 2>/dev/null || ls -la "$BUILD_DIR"
else
ls -la "$BUILD_DIR"
fi
echo ""
log_success "打包完成!"
echo ""
# 平台特定說明
case $TARGET_PLATFORM in
win)
echo " Windows 使用說明:"
echo " 1. 找到 dist/ 中的 .exe 檔案"
echo " 2. 直接執行即可,無需安裝"
echo ""
;;
mac)
echo " macOS 使用說明:"
echo " 1. 打開 dist/ 中的 .dmg 檔案"
echo " 2. 將應用拖到 Applications 資料夾"
echo ""
;;
linux)
echo " Linux 使用說明:"
echo " 1. 賦予 .AppImage 執行權限: chmod +x *.AppImage"
echo " 2. 直接執行 AppImage 檔案"
echo ""
;;
esac
}
# 函數:顯示幫助
show_help() {
echo ""
echo "Meeting Assistant Client - 打包腳本"
echo ""
echo "用法: $0 [命令] [選項]"
echo ""
echo "命令:"
echo " build 完整建置 (Sidecar + Electron)"
echo " sidecar 僅打包 Sidecar"
echo " electron 僅打包 Electron (需先打包 Sidecar)"
echo " clean 清理建置目錄"
echo " help 顯示此幫助訊息"
echo ""
echo "選項:"
echo " --platform PLATFORM 目標平台: win, mac, linux (預設: win)"
echo " --skip-sidecar 跳過 Sidecar 打包"
echo " --clean 建置前先清理"
echo ""
echo "範例:"
echo " $0 build # 完整建置 Windows 版本"
echo " $0 build --platform linux # 建置 Linux 版本"
echo " $0 sidecar # 僅打包 Sidecar"
echo " $0 electron --skip-sidecar # 僅打包 Electron"
echo ""
echo "注意:"
echo " - Windows 打包需在 Windows 環境執行"
echo " - macOS 打包需在 macOS 環境執行"
echo " - Linux 打包可在 Linux 或 WSL 環境執行"
echo ""
}
# 主程式
main() {
local command=${1:-"help"}
# 解析參數
shift || true
while [[ $# -gt 0 ]]; do
case $1 in
--platform)
TARGET_PLATFORM="$2"
shift 2
;;
--skip-sidecar)
SKIP_SIDECAR=true
shift
;;
--clean)
CLEAN_BUILD=true
shift
;;
*)
log_error "未知參數: $1"
show_help
exit 1
;;
esac
done
case $command in
build)
show_banner
local python_cmd=$(check_environment)
if [ "$CLEAN_BUILD" = true ]; then
clean_build
fi
if [ "$SKIP_SIDECAR" = false ]; then
setup_sidecar_venv "$python_cmd"
build_sidecar
fi
setup_client
build_electron
finalize_build
;;
sidecar)
show_banner
local python_cmd=$(check_environment)
if [ "$CLEAN_BUILD" = true ]; then
rm -rf "$SIDECAR_DIR/dist"
rm -rf "$SIDECAR_DIR/build"
fi
setup_sidecar_venv "$python_cmd"
build_sidecar
;;
electron)
show_banner
check_environment > /dev/null
# 檢查 Sidecar 是否已打包
if [ ! -d "$SIDECAR_DIR/dist/transcriber" ] && [ "$SKIP_SIDECAR" = false ]; then
log_warn "Sidecar 尚未打包"
log_info "請先執行: $0 sidecar"
log_info "或使用 --skip-sidecar 跳過"
exit 1
fi
if [ "$CLEAN_BUILD" = true ]; then
rm -rf "$CLIENT_DIR/dist"
fi
setup_client
build_electron
finalize_build
;;
clean)
clean_build
;;
help|--help|-h)
show_help
;;
*)
log_error "未知命令: $command"
show_help
exit 1
;;
esac
}
# 執行主程式
main "$@"

237
scripts/setup-backend.bat Normal file
View File

@@ -0,0 +1,237 @@
@echo off
REM Meeting Assistant Backend - Windows 一鍵設置與啟動腳本
REM 自動安裝依賴、設置環境並啟動後端服務
setlocal enabledelayedexpansion
REM 顏色設定 (Windows 10+)
set "GREEN=[92m"
set "YELLOW=[93m"
set "RED=[91m"
set "BLUE=[94m"
set "CYAN=[96m"
set "NC=[0m"
REM 專案路徑
set "SCRIPT_DIR=%~dp0"
set "PROJECT_DIR=%SCRIPT_DIR%.."
set "BACKEND_DIR=%PROJECT_DIR%\backend"
set "SIDECAR_DIR=%PROJECT_DIR%\sidecar"
REM 預設配置
set "DEFAULT_PORT=8000"
set "DEFAULT_HOST=0.0.0.0"
REM 解析參數
set "COMMAND=start"
set "PORT=%DEFAULT_PORT%"
set "HOST=%DEFAULT_HOST%"
set "NO_SIDECAR=false"
:parse_args
if "%~1"=="" goto :main
if /i "%~1"=="setup" (set "COMMAND=setup" & shift & goto :parse_args)
if /i "%~1"=="start" (set "COMMAND=start" & shift & goto :parse_args)
if /i "%~1"=="stop" (set "COMMAND=stop" & shift & goto :parse_args)
if /i "%~1"=="help" (set "COMMAND=help" & shift & goto :parse_args)
if /i "%~1"=="--port" (set "PORT=%~2" & shift & shift & goto :parse_args)
if /i "%~1"=="--host" (set "HOST=%~2" & shift & shift & goto :parse_args)
if /i "%~1"=="--no-sidecar" (set "NO_SIDECAR=true" & shift & goto :parse_args)
echo %RED%[ERROR]%NC% 未知參數: %~1
goto :show_help
:main
if "%COMMAND%"=="help" goto :show_help
if "%COMMAND%"=="setup" goto :do_setup
if "%COMMAND%"=="start" goto :do_start
if "%COMMAND%"=="stop" goto :do_stop
goto :show_help
:show_banner
echo.
echo %CYAN%==========================================
echo Meeting Assistant Backend Setup
echo Windows 一鍵設置與啟動腳本
echo ==========================================%NC%
echo.
goto :eof
:check_python
echo %BLUE%[STEP]%NC% 檢查 Python 環境...
REM 嘗試找到 Python
where python >nul 2>&1
if %errorlevel% equ 0 (
for /f "tokens=*" %%i in ('python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"') do set "PY_VERSION=%%i"
for /f "tokens=*" %%i in ('python -c "import sys; print(sys.version_info.major)"') do set "PY_MAJOR=%%i"
for /f "tokens=*" %%i in ('python -c "import sys; print(sys.version_info.minor)"') do set "PY_MINOR=%%i"
if !PY_MAJOR! geq 3 if !PY_MINOR! geq 10 (
set "PYTHON_CMD=python"
echo %GREEN%[OK]%NC% Python !PY_VERSION!
goto :eof
)
)
echo %RED%[ERROR]%NC% 需要 Python 3.10 或更高版本
echo %BLUE%[INFO]%NC% 請安裝 Python: https://www.python.org/downloads/
exit /b 1
:setup_backend_venv
echo %BLUE%[STEP]%NC% 設置後端虛擬環境...
cd /d "%BACKEND_DIR%"
if not exist "venv" (
echo %BLUE%[INFO]%NC% 創建虛擬環境...
%PYTHON_CMD% -m venv venv
echo %GREEN%[OK]%NC% 虛擬環境已創建
) else (
echo %BLUE%[INFO]%NC% 虛擬環境已存在
)
echo %BLUE%[INFO]%NC% 安裝後端依賴...
call venv\Scripts\activate.bat
pip install --upgrade pip -q
pip install -r requirements.txt -q
echo %GREEN%[OK]%NC% 後端依賴安裝完成
goto :eof
:setup_sidecar_venv
echo %BLUE%[STEP]%NC% 設置 Sidecar 虛擬環境...
cd /d "%SIDECAR_DIR%"
if not exist "venv" (
echo %BLUE%[INFO]%NC% 創建 Sidecar 虛擬環境...
%PYTHON_CMD% -m venv venv
echo %GREEN%[OK]%NC% Sidecar 虛擬環境已創建
) else (
echo %BLUE%[INFO]%NC% Sidecar 虛擬環境已存在
)
echo %BLUE%[INFO]%NC% 安裝 Sidecar 依賴 (這可能需要幾分鐘)...
call venv\Scripts\activate.bat
pip install --upgrade pip -q
pip install -r requirements.txt -q
echo %GREEN%[OK]%NC% Sidecar 依賴安裝完成
goto :eof
:setup_env_file
echo %BLUE%[STEP]%NC% 檢查環境變數配置...
cd /d "%BACKEND_DIR%"
if not exist ".env" (
if exist ".env.example" (
copy .env.example .env >nul
echo %YELLOW%[WARN]%NC% 已從 .env.example 創建 .env 檔案
echo %YELLOW%[WARN]%NC% 請編輯 %BACKEND_DIR%\.env 設置資料庫和 API 密鑰
) else (
echo %RED%[ERROR]%NC% 找不到 .env.example 檔案
exit /b 1
)
) else (
echo %GREEN%[OK]%NC% 環境變數檔案已存在
)
goto :eof
:start_backend
echo %BLUE%[STEP]%NC% 啟動後端服務...
cd /d "%BACKEND_DIR%"
REM 載入環境變數
if exist ".env" (
for /f "usebackq tokens=1,* delims==" %%a in (".env") do (
set "%%a=%%b"
)
)
REM 使用 .env 中的配置或預設值
if defined BACKEND_HOST set "HOST=%BACKEND_HOST%"
if defined BACKEND_PORT set "PORT=%BACKEND_PORT%"
REM 啟動虛擬環境
call venv\Scripts\activate.bat
echo.
echo %GREEN%[OK]%NC% 後端服務準備就緒!
echo.
echo %CYAN%==========================================
echo 服務資訊
echo ==========================================%NC%
echo.
echo API 地址: http://localhost:%PORT%
echo API 文件: http://localhost:%PORT%/docs
echo 健康檢查: http://localhost:%PORT%/api/health
echo.
echo 按 Ctrl+C 停止服務
echo.
uvicorn app.main:app --host %HOST% --port %PORT% --reload
goto :eof
:do_setup
call :show_banner
call :check_python
if errorlevel 1 exit /b 1
call :setup_backend_venv
if "%NO_SIDECAR%"=="false" call :setup_sidecar_venv
call :setup_env_file
echo.
echo %GREEN%[OK]%NC% 環境設置完成!
echo.
echo %BLUE%[INFO]%NC% 啟動服務: %~nx0 start
goto :eof
:do_start
call :show_banner
call :check_python
if errorlevel 1 exit /b 1
call :setup_backend_venv
call :setup_env_file
call :start_backend
goto :eof
:do_stop
echo %BLUE%[STEP]%NC% 停止後端服務...
REM 查找並終止 uvicorn 程序
for /f "tokens=2" %%i in ('tasklist /fi "imagename eq python.exe" /fo list ^| findstr "PID:"') do (
wmic process where "processid=%%i" get commandline 2>nul | findstr /i "uvicorn" >nul
if not errorlevel 1 (
taskkill /pid %%i /f >nul 2>&1
echo %GREEN%[OK]%NC% 已停止 PID: %%i
)
)
echo %GREEN%[OK]%NC% 後端服務已停止
goto :eof
:show_help
echo.
echo Meeting Assistant Backend - Windows 一鍵設置與啟動腳本
echo.
echo 用法: %~nx0 [命令] [選項]
echo.
echo 命令:
echo setup 僅設置環境 (安裝依賴)
echo start 設置並啟動後端服務 (預設)
echo stop 停止後端服務
echo help 顯示此幫助訊息
echo.
echo 選項:
echo --port PORT 服務端口 (預設: %DEFAULT_PORT%)
echo --host HOST 綁定地址 (預設: %DEFAULT_HOST%)
echo --no-sidecar 不安裝 Sidecar 依賴
echo.
echo 範例:
echo %~nx0 start 設置並啟動服務
echo %~nx0 start --port 8080 使用自訂端口
echo %~nx0 setup 僅安裝依賴
echo.
goto :eof
:end
endlocal

393
scripts/setup-backend.sh Executable file
View File

@@ -0,0 +1,393 @@
#!/bin/bash
#
# Meeting Assistant Backend - 一鍵設置與啟動腳本
# 自動安裝依賴、設置環境並啟動後端服務
#
set -e
# 顏色定義
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 專案路徑
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
BACKEND_DIR="$PROJECT_DIR/backend"
SIDECAR_DIR="$PROJECT_DIR/sidecar"
# 預設配置
DEFAULT_PORT=8000
DEFAULT_HOST="0.0.0.0"
# 函數:印出訊息
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[OK]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${CYAN}[STEP]${NC} $1"
}
# 函數:顯示標題
show_banner() {
echo ""
echo -e "${CYAN}=========================================="
echo " Meeting Assistant Backend Setup"
echo " 一鍵設置與啟動腳本"
echo -e "==========================================${NC}"
echo ""
}
# 函數:檢查 Python 版本
check_python() {
log_step "檢查 Python 環境..."
# 嘗試不同的 Python 命令
local python_cmd=""
for cmd in python3 python; do
if command -v $cmd &> /dev/null; then
local version=$($cmd -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
local major=$($cmd -c 'import sys; print(sys.version_info.major)')
local minor=$($cmd -c 'import sys; print(sys.version_info.minor)')
if [ "$major" -ge 3 ] && [ "$minor" -ge 10 ]; then
python_cmd=$cmd
log_success "Python $version ($cmd)"
break
fi
fi
done
if [ -z "$python_cmd" ]; then
log_error "需要 Python 3.10 或更高版本"
log_info "請安裝 Python: https://www.python.org/downloads/"
exit 1
fi
echo "$python_cmd"
}
# 函數:設置後端虛擬環境
setup_backend_venv() {
local python_cmd=$1
log_step "設置後端虛擬環境..."
cd "$BACKEND_DIR"
if [ ! -d "venv" ]; then
log_info "創建虛擬環境..."
$python_cmd -m venv venv
log_success "虛擬環境已創建"
else
log_info "虛擬環境已存在"
fi
# 啟動虛擬環境並安裝依賴
log_info "安裝後端依賴..."
if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
# Windows
source venv/Scripts/activate
else
# Linux/Mac
source venv/bin/activate
fi
pip install --upgrade pip -q
pip install -r requirements.txt -q
log_success "後端依賴安裝完成"
}
# 函數:設置 Sidecar 虛擬環境
setup_sidecar_venv() {
local python_cmd=$1
log_step "設置 Sidecar 虛擬環境..."
cd "$SIDECAR_DIR"
if [ ! -d "venv" ]; then
log_info "創建 Sidecar 虛擬環境..."
$python_cmd -m venv venv
log_success "Sidecar 虛擬環境已創建"
else
log_info "Sidecar 虛擬環境已存在"
fi
# 啟動虛擬環境並安裝依賴
log_info "安裝 Sidecar 依賴 (這可能需要幾分鐘)..."
if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
source venv/Scripts/activate
else
source venv/bin/activate
fi
pip install --upgrade pip -q
pip install -r requirements.txt -q
log_success "Sidecar 依賴安裝完成"
}
# 函數:設置環境變數檔案
setup_env_file() {
log_step "檢查環境變數配置..."
cd "$BACKEND_DIR"
if [ ! -f ".env" ]; then
if [ -f ".env.example" ]; then
cp .env.example .env
log_warn "已從 .env.example 創建 .env 檔案"
log_warn "請編輯 $BACKEND_DIR/.env 設置資料庫和 API 密鑰"
else
log_error "找不到 .env.example 檔案"
exit 1
fi
else
log_success "環境變數檔案已存在"
fi
}
# 函數:檢查 port 是否被佔用
check_port() {
local port=$1
if command -v lsof &> /dev/null; then
if lsof -i :$port > /dev/null 2>&1; then
return 0 # port 被佔用
fi
elif command -v netstat &> /dev/null; then
if netstat -tuln | grep -q ":$port "; then
return 0
fi
fi
return 1 # port 可用
}
# 函數:釋放 port
release_port() {
local port=$1
if check_port $port; then
log_warn "Port $port 被佔用,嘗試釋放..."
if command -v lsof &> /dev/null; then
local pid=$(lsof -t -i :$port 2>/dev/null)
if [ -n "$pid" ]; then
kill -9 $pid 2>/dev/null || true
sleep 1
log_success "Port $port 已釋放"
fi
else
log_error "無法釋放 port請手動關閉佔用 $port 的程序"
exit 1
fi
fi
}
# 函數:啟動後端服務
start_backend() {
local host=${1:-$DEFAULT_HOST}
local port=${2:-$DEFAULT_PORT}
local mode=${3:-"foreground"} # foreground 或 background
log_step "啟動後端服務..."
cd "$BACKEND_DIR"
# 載入環境變數
if [ -f ".env" ]; then
export $(grep -v '^#' .env | grep -v '^$' | xargs)
fi
# 使用 .env 中的配置或預設值
host=${BACKEND_HOST:-$host}
port=${BACKEND_PORT:-$port}
# 釋放 port
release_port $port
# 啟動虛擬環境
if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]] || [[ "$OSTYPE" == "cygwin" ]]; then
source venv/Scripts/activate
else
source venv/bin/activate
fi
echo ""
log_success "後端服務準備就緒!"
echo ""
echo -e "${CYAN}=========================================="
echo " 服務資訊"
echo -e "==========================================${NC}"
echo ""
echo " API 地址: http://localhost:$port"
echo " API 文件: http://localhost:$port/docs"
echo " 健康檢查: http://localhost:$port/api/health"
echo ""
echo " 按 Ctrl+C 停止服務"
echo ""
if [ "$mode" = "background" ]; then
nohup uvicorn app.main:app --host $host --port $port --reload > "$PROJECT_DIR/backend.log" 2>&1 &
local pid=$!
echo $pid > "$PROJECT_DIR/.backend_pid"
log_success "後端服務已在背景啟動 (PID: $pid)"
log_info "查看日誌: tail -f $PROJECT_DIR/backend.log"
else
# 前景執行
uvicorn app.main:app --host $host --port $port --reload
fi
}
# 函數:顯示幫助
show_help() {
echo ""
echo "Meeting Assistant Backend - 一鍵設置與啟動腳本"
echo ""
echo "用法: $0 [命令] [選項]"
echo ""
echo "命令:"
echo " setup 僅設置環境 (安裝依賴)"
echo " start 設置並啟動後端服務 (前景執行)"
echo " start-bg 設置並啟動後端服務 (背景執行)"
echo " stop 停止背景執行的後端服務"
echo " help 顯示此幫助訊息"
echo ""
echo "選項:"
echo " --port PORT 服務端口 (預設: $DEFAULT_PORT)"
echo " --host HOST 綁定地址 (預設: $DEFAULT_HOST)"
echo " --no-sidecar 不安裝 Sidecar 依賴"
echo ""
echo "範例:"
echo " $0 start # 設置並啟動服務"
echo " $0 start --port 8080 # 使用自訂端口"
echo " $0 setup # 僅安裝依賴"
echo " $0 start-bg # 背景執行"
echo ""
}
# 函數:停止背景服務
stop_backend() {
log_step "停止後端服務..."
if [ -f "$PROJECT_DIR/.backend_pid" ]; then
local pid=$(cat "$PROJECT_DIR/.backend_pid")
if kill -0 $pid 2>/dev/null; then
kill $pid
rm -f "$PROJECT_DIR/.backend_pid"
log_success "後端服務已停止 (PID: $pid)"
else
rm -f "$PROJECT_DIR/.backend_pid"
log_warn "服務已經停止"
fi
else
# 嘗試通過 port 查找
if command -v lsof &> /dev/null; then
local port=${BACKEND_PORT:-$DEFAULT_PORT}
local pid=$(lsof -t -i :$port 2>/dev/null)
if [ -n "$pid" ]; then
kill $pid 2>/dev/null || true
log_success "已停止 port $port 上的服務"
else
log_warn "沒有找到運行中的後端服務"
fi
fi
fi
}
# 主程式
main() {
local command=${1:-"start"}
local port=$DEFAULT_PORT
local host=$DEFAULT_HOST
local setup_sidecar=true
# 解析參數
shift || true
while [[ $# -gt 0 ]]; do
case $1 in
--port)
port="$2"
shift 2
;;
--host)
host="$2"
shift 2
;;
--no-sidecar)
setup_sidecar=false
shift
;;
*)
log_error "未知參數: $1"
show_help
exit 1
;;
esac
done
case $command in
setup)
show_banner
local python_cmd=$(check_python)
setup_backend_venv "$python_cmd"
if [ "$setup_sidecar" = true ]; then
setup_sidecar_venv "$python_cmd"
fi
setup_env_file
echo ""
log_success "環境設置完成!"
echo ""
log_info "啟動服務: $0 start"
;;
start)
show_banner
local python_cmd=$(check_python)
setup_backend_venv "$python_cmd"
setup_env_file
start_backend "$host" "$port" "foreground"
;;
start-bg)
show_banner
local python_cmd=$(check_python)
setup_backend_venv "$python_cmd"
setup_env_file
start_backend "$host" "$port" "background"
;;
stop)
stop_backend
;;
help|--help|-h)
show_help
;;
*)
log_error "未知命令: $command"
show_help
exit 1
;;
esac
}
# 捕捉中斷信號
trap 'echo ""; log_info "收到中斷信號"; exit 0' INT TERM
# 執行主程式
main "$@"