#!/bin/bash # 一键生成生产环境服务文件脚本(静默安装) # 包括:Caddy 安装、后端服务 systemd 文件、Caddy 服务 systemd 文件 # # 用法: # ./setup_production.sh # 静默安装所有内容(默认) # ./setup_production.sh --no-install # 只生成服务文件,不安装到系统 set -e # 获取脚本所在目录(项目根目录) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" || exit 1 # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # 打印函数 info() { echo -e "${BLUE}ℹ${NC} $1"; } success() { echo -e "${GREEN}✓${NC} $1"; } warn() { echo -e "${YELLOW}⚠${NC} $1"; } error() { echo -e "${RED}✗${NC} $1"; } # 配置变量 CADDY_VERSION="2.10.2" CADDY_ARCH="linux_amd64" CADDY_URL="https://github.com/caddyserver/caddy/releases/download/v${CADDY_VERSION}/caddy_${CADDY_VERSION}_${CADDY_ARCH}.tar.gz" CADDY_BINARY="caddy" CADDY_INSTALL_DIR="/usr/local/bin" CADDY_CONFIG_DIR="/etc/caddy" CADDY_DATA_DIR="/var/lib/caddy" PROJECT_ROOT="$SCRIPT_DIR" JINGROW_APP_DIR="$PROJECT_ROOT/apps/jingrow" SERVICE_USER="${SERVICE_USER:-$(whoami)}" SERVICE_GROUP="${SERVICE_GROUP:-$(id -gn)}" TMP_DIR="$PROJECT_ROOT/tmp" # 检测 uv 路径 detect_uv_path() { if command -v uv &> /dev/null; then UV_PATH=$(command -v uv) info "检测到 uv 路径: $UV_PATH" else # 尝试常见路径 if [ -f "$HOME/.local/bin/uv" ]; then UV_PATH="$HOME/.local/bin/uv" elif [ -f "/usr/local/bin/uv" ]; then UV_PATH="/usr/local/bin/uv" else error "未找到 uv,请先安装" exit 1 fi warn "使用 uv 路径: $UV_PATH" fi } # 检测系统架构 detect_arch() { local arch=$(uname -m) case "$arch" in x86_64|amd64) CADDY_ARCH="linux_amd64" ;; aarch64|arm64) CADDY_ARCH="linux_arm64" ;; armv7l|armv6l) CADDY_ARCH="linux_armv7" ;; *) error "不支持的架构: $arch" exit 1 ;; esac CADDY_URL="https://github.com/caddyserver/caddy/releases/download/v${CADDY_VERSION}/caddy_${CADDY_VERSION}_${CADDY_ARCH}.tar.gz" info "检测到架构: $arch,使用 $CADDY_ARCH" } # 创建 Caddyfile 符号链接 setup_caddyfile_link() { # 创建必要的目录 if [ "$EUID" -eq 0 ]; then mkdir -p "$CADDY_CONFIG_DIR" "$CADDY_DATA_DIR" chown -R "$SERVICE_USER:$SERVICE_GROUP" "$CADDY_CONFIG_DIR" "$CADDY_DATA_DIR" 2>/dev/null || true else sudo mkdir -p "$CADDY_CONFIG_DIR" "$CADDY_DATA_DIR" sudo chown -R "$SERVICE_USER:$SERVICE_GROUP" "$CADDY_CONFIG_DIR" "$CADDY_DATA_DIR" 2>/dev/null || true fi # 创建 Caddyfile 符号链接 if [ -f "$PROJECT_ROOT/Caddyfile" ]; then info "创建 Caddyfile 符号链接..." # 如果已存在文件或链接,先删除 if [ "$EUID" -eq 0 ]; then [ -e "$CADDY_CONFIG_DIR/Caddyfile" ] && rm -f "$CADDY_CONFIG_DIR/Caddyfile" ln -s "$PROJECT_ROOT/Caddyfile" "$CADDY_CONFIG_DIR/Caddyfile" else [ -e "$CADDY_CONFIG_DIR/Caddyfile" ] && sudo rm -f "$CADDY_CONFIG_DIR/Caddyfile" sudo ln -s "$PROJECT_ROOT/Caddyfile" "$CADDY_CONFIG_DIR/Caddyfile" fi success "Caddyfile 符号链接已创建: $CADDY_CONFIG_DIR/Caddyfile -> $PROJECT_ROOT/Caddyfile" else warn "未找到 Caddyfile,请手动创建符号链接: ln -s $PROJECT_ROOT/Caddyfile $CADDY_CONFIG_DIR/Caddyfile" fi } # 安装 Caddy install_caddy() { info "开始安装 Caddy ${CADDY_VERSION}..." # 检查是否已安装 if command -v caddy &> /dev/null; then local installed_version=$(caddy version 2>/dev/null | head -n1 | grep -oP 'v\d+\.\d+\.\d+' || echo "unknown") info "Caddy 已安装,版本: $installed_version,跳过二进制安装" else # 创建临时目录 local tmp_dir=$(mktemp -d) trap "rm -rf $tmp_dir" EXIT info "下载 Caddy..." local caddy_tar="$tmp_dir/caddy.tar.gz" if ! curl -L -o "$caddy_tar" "$CADDY_URL"; then error "下载 Caddy 失败" exit 1 fi info "解压 Caddy..." cd "$tmp_dir" tar -xzf "$caddy_tar" # 检查二进制文件是否存在 if [ ! -f "$CADDY_BINARY" ]; then error "未找到 Caddy 二进制文件" exit 1 fi # 安装到系统目录(需要 root 权限) info "安装 Caddy 到 $CADDY_INSTALL_DIR..." if [ "$EUID" -eq 0 ]; then cp "$CADDY_BINARY" "$CADDY_INSTALL_DIR/caddy" chmod +x "$CADDY_INSTALL_DIR/caddy" else warn "需要 root 权限安装 Caddy,尝试使用 sudo..." sudo cp "$CADDY_BINARY" "$CADDY_INSTALL_DIR/caddy" sudo chmod +x "$CADDY_INSTALL_DIR/caddy" fi fi # 无论是否已安装,都设置 Caddyfile 符号链接 setup_caddyfile_link success "Caddy 安装完成" # 验证安装 if command -v caddy &> /dev/null; then local version=$(caddy version 2>/dev/null | head -n1 || echo "unknown") info "Caddy 版本: $version" fi } # 生成后端服务 systemd 文件 generate_backend_service() { info "生成后端服务 systemd 文件..." # 确保 tmp 目录存在 mkdir -p "$TMP_DIR" local service_file="$TMP_DIR/jingrow-backend.service" local uv_dir=$(dirname "$UV_PATH") local env_file_line="" # 如果 .env 文件存在,使用 EnvironmentFile 引用 if [ -f "$PROJECT_ROOT/.env" ]; then env_file_line="EnvironmentFile=${PROJECT_ROOT}/.env" fi cat > "$service_file" < "$service_file" < "$service_file" < /dev/null; then error "需要 curl,请先安装" exit 1 fi if ! command -v tar &> /dev/null; then error "需要 tar,请先安装" exit 1 fi # 检测 uv 路径 detect_uv_path # 检查项目目录 if [ ! -d "$JINGROW_APP_DIR" ]; then error "未找到后端目录: $JINGROW_APP_DIR" exit 1 fi # 安装 Caddy install_caddy # 生成服务文件 generate_backend_service generate_worker_service generate_caddy_service # 安装服务(静默安装,除非指定 --no-install) install_services "$@" success "完成!" info "生成的文件(位于 $TMP_DIR):" echo " - $TMP_DIR/jingrow-backend.service" echo " - $TMP_DIR/jingrow-worker.service" echo " - $TMP_DIR/caddy.service" echo "" info "注意事项:" echo " 1. 确保已构建前端: npm run build:frontend" echo " 2. 确保后端依赖已安装: npm run install:backend" echo " 3. 确保 Redis 已安装并运行" echo " 4. 检查并配置 .env 文件" echo " 5. 修改服务文件中的用户和组(如需要)" } main "$@"