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

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 "$@"