#!/bin/bash # # Meeting Assistant Backend - Deployment Script # 用於在 1Panel 或其他 Linux 服務器上部署後端服務 # set -e # 顏色定義 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 預設配置 DEFAULT_INSTALL_DIR="/opt/meeting-assistant" DEFAULT_USER="meeting" DEFAULT_PORT="8000" # 函數:印出訊息 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" } # 函數:顯示幫助 show_help() { echo "" echo "Meeting Assistant Backend 部署腳本" echo "" echo "用法: $0 [命令] [選項]" echo "" echo "命令:" echo " install 安裝後端服務" echo " update 更新後端服務" echo " uninstall 移除後端服務" echo " status 顯示服務狀態" echo " logs 顯示服務日誌" echo " help 顯示此幫助訊息" echo "" echo "選項:" echo " --dir DIR 安裝目錄 (預設: $DEFAULT_INSTALL_DIR)" echo " --user USER 運行服務的用戶 (預設: $DEFAULT_USER)" echo " --port PORT 服務端口 (預設: $DEFAULT_PORT)" echo "" echo "範例:" echo " $0 install --port 8080" echo " $0 update" echo " $0 status" echo "" } # 函數:檢查是否為 root check_root() { if [ "$EUID" -ne 0 ]; then log_error "請使用 root 權限執行此腳本" log_info "使用: sudo $0 $@" exit 1 fi } # 函數:檢查依賴 check_dependencies() { log_info "檢查系統依賴..." local missing=() # 檢查 Python 3.10+ if command -v python3 &> /dev/null; then local py_version=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') log_success "Python: $py_version" else missing+=("python3") fi # 檢查 pip if ! command -v pip3 &> /dev/null; then missing+=("python3-pip") fi # 檢查 venv if ! python3 -c "import venv" &> /dev/null 2>&1; then missing+=("python3-venv") fi if [ ${#missing[@]} -gt 0 ]; then log_error "缺少依賴: ${missing[*]}" log_info "請先安裝: apt install ${missing[*]}" exit 1 fi log_success "所有依賴已滿足" } # 函數:創建系統用戶 create_user() { local username=$1 if id "$username" &>/dev/null; then log_info "用戶 $username 已存在" else log_info "創建系統用戶: $username" useradd --system --no-create-home --shell /bin/false "$username" log_success "用戶 $username 已創建" fi } # 函數:安裝後端 install_backend() { local install_dir=$1 local username=$2 local port=$3 log_info "開始安裝 Meeting Assistant Backend..." log_info "安裝目錄: $install_dir" log_info "服務用戶: $username" log_info "服務端口: $port" echo "" # 檢查依賴 check_dependencies # 創建用戶 create_user "$username" # 創建目錄 log_info "創建安裝目錄..." mkdir -p "$install_dir" mkdir -p "$install_dir/logs" mkdir -p "$install_dir/templates" mkdir -p "$install_dir/records" # 複製後端代碼 log_info "複製後端代碼..." local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" local source_dir="$(dirname "$script_dir")/backend" if [ ! -d "$source_dir" ]; then log_error "找不到後端源碼目錄: $source_dir" exit 1 fi cp -r "$source_dir/app" "$install_dir/" cp "$source_dir/requirements.txt" "$install_dir/" # 複製 .env.example 如果 .env 不存在 if [ ! -f "$install_dir/.env" ]; then if [ -f "$source_dir/.env.example" ]; then cp "$source_dir/.env.example" "$install_dir/.env" log_warn "已創建 .env 檔案,請編輯配置: $install_dir/.env" fi fi # 複製模板檔案 if [ -d "$source_dir/templates" ]; then cp -r "$source_dir/templates/"* "$install_dir/templates/" 2>/dev/null || true fi # 創建虛擬環境 log_info "創建 Python 虛擬環境..." python3 -m venv "$install_dir/venv" # 安裝依賴 log_info "安裝 Python 依賴..." "$install_dir/venv/bin/pip" install --upgrade pip "$install_dir/venv/bin/pip" install -r "$install_dir/requirements.txt" # 更新 .env 中的端口配置 if [ -f "$install_dir/.env" ]; then sed -i "s/^BACKEND_PORT=.*/BACKEND_PORT=$port/" "$install_dir/.env" fi # 設置權限 log_info "設置檔案權限..." chown -R "$username:$username" "$install_dir" chmod 750 "$install_dir" chmod 640 "$install_dir/.env" 2>/dev/null || true # 創建 systemd 服務 create_systemd_service "$install_dir" "$username" "$port" # 啟動服務 log_info "啟動服務..." systemctl daemon-reload systemctl enable meeting-assistant-backend systemctl start meeting-assistant-backend # 等待啟動 sleep 3 # 檢查狀態 if systemctl is-active --quiet meeting-assistant-backend; then log_success "Meeting Assistant Backend 安裝成功!" echo "" echo "==========================================" echo " 安裝完成" echo "==========================================" echo "" echo " 服務狀態: systemctl status meeting-assistant-backend" echo " 查看日誌: journalctl -u meeting-assistant-backend -f" echo " API 地址: http://localhost:$port" echo " API 文件: http://localhost:$port/docs" echo "" echo " 配置檔案: $install_dir/.env" echo " 請確保已正確配置數據庫和 API 密鑰" echo "" else log_error "服務啟動失敗,請檢查日誌" journalctl -u meeting-assistant-backend --no-pager -n 20 exit 1 fi } # 函數:創建 systemd 服務檔案 create_systemd_service() { local install_dir=$1 local username=$2 local port=$3 log_info "創建 systemd 服務..." cat > /etc/systemd/system/meeting-assistant-backend.service << EOF [Unit] Description=Meeting Assistant Backend API After=network.target [Service] Type=simple User=$username Group=$username WorkingDirectory=$install_dir Environment="PATH=$install_dir/venv/bin" EnvironmentFile=$install_dir/.env ExecStart=$install_dir/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port $port Restart=always RestartSec=5 StandardOutput=journal StandardError=journal # Security hardening NoNewPrivileges=true ProtectSystem=strict ProtectHome=true ReadWritePaths=$install_dir/logs $install_dir/records $install_dir/templates PrivateTmp=true [Install] WantedBy=multi-user.target EOF log_success "systemd 服務已創建" } # 函數:更新後端 update_backend() { local install_dir=$1 log_info "更新 Meeting Assistant Backend..." if [ ! -d "$install_dir" ]; then log_error "安裝目錄不存在: $install_dir" exit 1 fi # 停止服務 log_info "停止服務..." systemctl stop meeting-assistant-backend || true # 備份配置 log_info "備份配置..." cp "$install_dir/.env" "$install_dir/.env.backup" 2>/dev/null || true # 複製新代碼 local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" local source_dir="$(dirname "$script_dir")/backend" log_info "更新代碼..." rm -rf "$install_dir/app" cp -r "$source_dir/app" "$install_dir/" cp "$source_dir/requirements.txt" "$install_dir/" # 更新依賴 log_info "更新依賴..." "$install_dir/venv/bin/pip" install -r "$install_dir/requirements.txt" # 恢復配置 if [ -f "$install_dir/.env.backup" ]; then mv "$install_dir/.env.backup" "$install_dir/.env" fi # 重新設置權限 local username=$(stat -c '%U' "$install_dir") chown -R "$username:$username" "$install_dir" # 重啟服務 log_info "重啟服務..." systemctl daemon-reload systemctl start meeting-assistant-backend sleep 2 if systemctl is-active --quiet meeting-assistant-backend; then log_success "更新完成!" else log_error "服務重啟失敗" exit 1 fi } # 函數:移除後端 uninstall_backend() { local install_dir=$1 log_warn "即將移除 Meeting Assistant Backend" read -p "確定要繼續嗎?(y/N) " confirm if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then log_info "取消移除" exit 0 fi # 停止並禁用服務 log_info "停止服務..." systemctl stop meeting-assistant-backend 2>/dev/null || true systemctl disable meeting-assistant-backend 2>/dev/null || true # 刪除 systemd 服務 log_info "移除 systemd 服務..." rm -f /etc/systemd/system/meeting-assistant-backend.service systemctl daemon-reload # 詢問是否刪除數據 read -p "是否刪除安裝目錄 $install_dir?(y/N) " delete_dir if [ "$delete_dir" = "y" ] || [ "$delete_dir" = "Y" ]; then log_info "刪除安裝目錄..." rm -rf "$install_dir" else log_info "保留安裝目錄: $install_dir" fi log_success "Meeting Assistant Backend 已移除" } # 函數:顯示狀態 show_status() { echo "" echo "==========================================" echo " Meeting Assistant Backend 狀態" echo "==========================================" echo "" if systemctl is-active --quiet meeting-assistant-backend; then log_success "服務狀態: 運行中" systemctl status meeting-assistant-backend --no-pager | grep -E "(Active|Main PID|Memory|CPU)" else log_warn "服務狀態: 未運行" fi echo "" } # 函數:顯示日誌 show_logs() { journalctl -u meeting-assistant-backend -f } # 解析參數 INSTALL_DIR=$DEFAULT_INSTALL_DIR SERVICE_USER=$DEFAULT_USER SERVICE_PORT=$DEFAULT_PORT COMMAND="" while [[ $# -gt 0 ]]; do case $1 in install|update|uninstall|status|logs|help) COMMAND=$1 shift ;; --dir) INSTALL_DIR="$2" shift 2 ;; --user) SERVICE_USER="$2" shift 2 ;; --port) SERVICE_PORT="$2" shift 2 ;; *) log_error "未知參數: $1" show_help exit 1 ;; esac done # 執行命令 case $COMMAND in install) check_root install_backend "$INSTALL_DIR" "$SERVICE_USER" "$SERVICE_PORT" ;; update) check_root update_backend "$INSTALL_DIR" ;; uninstall) check_root uninstall_backend "$INSTALL_DIR" ;; status) show_status ;; logs) show_logs ;; help|"") show_help ;; *) log_error "未知命令: $COMMAND" show_help exit 1 ;; esac