#!/usr/bin/env bash set -euo pipefail SERVER="ubuntu@ec2-3-71-153-45.eu-central-1.compute.amazonaws.com" REMOTE_ROOT="/data/project/dashboard" PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" SSH_KEY="$HOME/.ssh/aws_eafon.pem" SSH_OPTS=(-i "$SSH_KEY" -o StrictHostKeyChecking=no) FRONTEND_PATHS=( ./frontend/dist/ ) BACKEND_PATHS=( ./backend/src/ ./backend/package.json ./backend/pnpm-lock.yaml ) COMPOSE_PATHS=( ./docker-compose.yml ) DEFAULT_PATHS=( "${FRONTEND_PATHS[@]}" "${BACKEND_PATHS[@]}" "${COMPOSE_PATHS[@]}" ) GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' info() { echo -e "${CYAN}[INFO]${NC} $1"; } ok() { echo -e "${GREEN}[OK]${NC} $1"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } show_help() { cat < ${REMOTE_ROOT}/frontend/dist/assets 示例: $(basename "$0") 同步默认内容到服务器 $(basename "$0") frontend 仅同步前端 $(basename "$0") backend compose 同步后端与 docker-compose $(basename "$0") ./frontend/nginx.conf 单独同步 nginx.conf $(basename "$0") ./backend/Dockerfile 单独同步 Dockerfile $(basename "$0") ./frontend/dist/assets 同步指定目录 $(basename "$0") -n 虚拟执行,预览全部同步 $(basename "$0") -n frontend 虚拟执行,预览前端同步 EOF } resolve_preset_paths() { local preset="$1" case "$preset" in frontend|f) printf '%s\n' "${FRONTEND_PATHS[@]}" ;; backend|b) printf '%s\n' "${BACKEND_PATHS[@]}" ;; compose|c) printf '%s\n' "${COMPOSE_PATHS[@]}" ;; all|a) printf '%s\n' "${DEFAULT_PATHS[@]}" ;; *) echo "$preset" ;; esac } rsync_item() { local item="$1" local rel remote_path local_full if [[ "$item" == /* ]]; then if [[ "$item" == "$PROJECT_ROOT"/* ]]; then rel="${item#"$PROJECT_ROOT"/}" local_full="$item" else warn "路径不在项目内,跳过: $item" return 1 fi else rel="${item#./}" rel="${rel%/}" local_full="${PROJECT_ROOT}/${rel}" fi remote_path="${REMOTE_ROOT}/${rel}" if [[ ! -e "$local_full" ]]; then warn "路径不存在: ${local_full}" return 1 fi local rsync_opts=(-avz --progress) if [[ "$DRY_RUN" = true ]]; then rsync_opts+=(-n) fi if [[ -d "$local_full" ]]; then if [[ "$DRY_RUN" = true ]]; then info "预览目录: ${rel}/ -> ${SERVER}:${remote_path}/" else info "同步目录: ${rel}/ -> ${SERVER}:${remote_path}/" fi rsync "${rsync_opts[@]}" --delete \ -e "ssh ${SSH_OPTS[*]}" \ "${local_full}/" "${SERVER}:${remote_path}/" else if [[ "$DRY_RUN" = true ]]; then info "预览文件: ${rel} -> ${SERVER}:${remote_path}" else info "同步文件: ${rel} -> ${SERVER}:${remote_path}" ssh "${SSH_OPTS[@]}" "$SERVER" "mkdir -p $(dirname "$remote_path")" fi rsync "${rsync_opts[@]}" \ -e "ssh ${SSH_OPTS[*]}" \ "$local_full" "${SERVER}:${remote_path}" fi } sync_paths() { local paths=("$@") local item cd "$PROJECT_ROOT" for item in "${paths[@]}"; do rsync_item "$item" done } DRY_RUN=false ARGS=() for arg in "$@"; do case "$arg" in -n) DRY_RUN=true ;; -h|--help) show_help exit 0 ;; *) ARGS+=("$arg") ;; esac done PATHS=() if [[ ${#ARGS[@]} -eq 0 ]]; then PATHS=("${DEFAULT_PATHS[@]}") else for arg in "${ARGS[@]}"; do while IFS= read -r path; do PATHS+=("$path") done < <(resolve_preset_paths "$arg") done fi if [[ "$DRY_RUN" = true ]]; then info "虚拟同步至 ${SERVER}:${REMOTE_ROOT}/" else info "同步至 ${SERVER}:${REMOTE_ROOT}/" fi sync_paths "${PATHS[@]}" echo "" ok "完成!"