新增分享码日志管理,美化界面,优化代码上传脚本

This commit is contained in:
eafonyang
2026-06-16 19:15:02 +08:00
parent 1ad4cd034a
commit 481261b334
14 changed files with 1437 additions and 145 deletions
+145 -88
View File
@@ -1,28 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail
# ============================================================
# 部署上传脚本
# 将前端/后端/docker-compose 文件上传至正式服务器
#
# 用法:
# ./scripts/upload.sh # 上传所有
# ./scripts/upload.sh frontend # 仅上传前端
# ./scripts/upload.sh backend # 仅上传后端
# ./scripts/upload.sh compose # 仅上传 docker-compose
# ./scripts/upload.sh ./frontend/dist/assets # 上传指定路径
# ============================================================
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)
# SSH / SCP 通用选项
SSH_OPTS="-i $SSH_KEY -o StrictHostKeyChecking=no"
SCP_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'
@@ -32,102 +36,155 @@ info() { echo -e "${CYAN}[INFO]${NC} $1"; }
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
# ---- 上传函数 ----
show_help() {
cat <<EOF
用法: $(basename "$0") [选项] [目标|路径...]
upload_frontend() {
info "上传前端 dist/ 和 nginx.conf ..."
ssh $SSH_OPTS "$SERVER" "mkdir -p ${REMOTE_ROOT}/frontend"
scp $SCP_OPTS -r "${PROJECT_ROOT}/frontend/dist" "${SERVER}:${REMOTE_ROOT}/frontend/"
scp $SCP_OPTS "${PROJECT_ROOT}/frontend/nginx.conf" "${SERVER}:${REMOTE_ROOT}/frontend/"
ok "前端上传完成"
将本地代码同步到远程服务器(rsync)。
远程: ${SERVER}:${REMOTE_ROOT}/
选项:
-n 虚拟执行,不实际传输或删除远程文件
-h, --help 显示此帮助信息
目标:
frontend, f 同步前端 dist/(不含 nginx.conf
backend, b 同步后端 src/、package.json、pnpm-lock.yaml(不含 Dockerfile
compose, c 同步 docker-compose.yml
all, a 同步以上全部(默认)
(路径) 同步指定文件或目录,可传多个
默认不同步 nginx.conf 与 Dockerfile,需要时请显式指定,例如:
./frontend/nginx.conf
./backend/Dockerfile
指定路径时,会映射到服务器对应位置,例如:
./frontend/dist/assets -> ${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
}
upload_backend() {
info "上传后端 src/、Dockerfile、package.json、pnpm-lock.yaml ..."
ssh $SSH_OPTS "$SERVER" "mkdir -p ${REMOTE_ROOT}/backend"
scp $SCP_OPTS -r "${PROJECT_ROOT}/backend/src" "${SERVER}:${REMOTE_ROOT}/backend/"
scp $SCP_OPTS \
"${PROJECT_ROOT}/backend/Dockerfile" \
"${PROJECT_ROOT}/backend/package.json" \
"${PROJECT_ROOT}/backend/pnpm-lock.yaml" \
"${SERVER}:${REMOTE_ROOT}/backend/"
ok "后端上传完成"
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
}
upload_compose() {
info "上传 docker-compose.yml ..."
scp $SCP_OPTS "${PROJECT_ROOT}/docker-compose.yml" "${SERVER}:${REMOTE_ROOT}/"
ok "docker-compose.yml 上传完成"
}
rsync_item() {
local item="$1"
local rel remote_path local_full
upload_all() {
upload_frontend
upload_backend
upload_compose
}
# ---- 上传指定路径 ----
# 根据传入的相对/绝对路径,映射到服务器对应位置
# 例如: ./frontend/dist/assets → ${REMOTE_ROOT}/frontend/dist/assets
upload_path() {
local input_path="$1"
# 转为绝对路径
local abs_path
if [[ "$input_path" == /* ]]; then
abs_path="$input_path"
if [[ "$item" == /* ]]; then
if [[ "$item" == "$PROJECT_ROOT"/* ]]; then
rel="${item#"$PROJECT_ROOT"/}"
local_full="$item"
else
warn "路径不在项目内,跳过: $item"
return 1
fi
else
abs_path="$(cd "$(dirname "$input_path")" 2>/dev/null && pwd)/$(basename "$input_path")"
rel="${item#./}"
rel="${rel%/}"
local_full="${PROJECT_ROOT}/${rel}"
fi
# 去掉项目根目录前缀,得到相对路径
local rel_path="${abs_path#"$PROJECT_ROOT"/}"
# 如果去掉前缀失败(路径不在项目内),回退到去掉 ./ 前缀
if [[ "$rel_path" == "$abs_path" ]]; then
rel_path="${input_path#./}"
fi
local local_full="${PROJECT_ROOT}/${rel_path}"
local remote_path="${REMOTE_ROOT}/${rel_path}"
remote_path="${REMOTE_ROOT}/${rel}"
if [[ ! -e "$local_full" ]]; then
warn "路径不存在: ${local_full}"
return 1
fi
# 确保远程父目录存在
ssh $SSH_OPTS "$SERVER" "mkdir -p $(dirname "$remote_path")"
local rsync_opts=(-avz --progress)
if [[ "$DRY_RUN" = true ]]; then
rsync_opts+=(-n)
fi
if [[ -d "$local_full" ]]; then
info "上传目录: ${rel_path}/ → ${SERVER}:${remote_path}/"
# 先删除远程同名目录,避免 scp 将目录嵌套到已有目录内
ssh $SSH_OPTS "$SERVER" "rm -rf '${remote_path}'"
scp $SCP_OPTS -r "$local_full" "${SERVER}:${remote_path}"
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
info "上传文件: ${rel_path}${SERVER}:${remote_path}"
scp $SCP_OPTS "$local_full" "${SERVER}:${remote_path}"
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
ok "上传完成: ${rel_path}"
}
# ---- 主逻辑 ----
sync_paths() {
local paths=("$@")
local item
if [[ $# -eq 0 ]]; then
info "上传全部文件至 ${SERVER}:${REMOTE_ROOT}/"
upload_all
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 "$@"; do
case "$arg" in
frontend|f) upload_frontend ;;
backend|b) upload_backend ;;
compose|c) upload_compose ;;
all|a) upload_all ;;
*) upload_path "$arg" ;;
esac
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 ""
info "完成!"
ok "完成!"