chore: harden build and deployment workflow
This commit is contained in:
278
install.sh
278
install.sh
@@ -35,6 +35,7 @@ PROJECT_NAME="wanwanyun"
|
||||
PROJECT_DIR="/var/www/${PROJECT_NAME}"
|
||||
REPO_URL="https://git.workyai.cn/237899745/vue-driven-cloud-storage.git"
|
||||
NODE_VERSION="20"
|
||||
NODE_MIN_VERSION="20.19.0"
|
||||
ADMIN_USERNAME=""
|
||||
ADMIN_PASSWORD=""
|
||||
DOMAIN=""
|
||||
@@ -529,13 +530,17 @@ install_dependencies() {
|
||||
echo ""
|
||||
}
|
||||
|
||||
node_version_supported() {
|
||||
node -e "const [major, minor] = process.versions.node.split('.').map(Number); process.exit((major > 20 && major < 25) || (major === 20 && minor >= 19) ? 0 : 1)" 2>/dev/null
|
||||
}
|
||||
|
||||
install_nodejs_apt() {
|
||||
if command -v node &> /dev/null; then
|
||||
NODE_VER=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
||||
if [[ $NODE_VER -ge $NODE_VERSION ]]; then
|
||||
if node_version_supported; then
|
||||
print_success "Node.js 已安装: $(node -v)"
|
||||
return
|
||||
fi
|
||||
print_warning "当前 Node.js $(node -v) 不满足 ${NODE_MIN_VERSION} 至 24.x,准备安装 20.x"
|
||||
fi
|
||||
|
||||
print_info "正在安装 Node.js ${NODE_VERSION}.x..."
|
||||
@@ -546,11 +551,11 @@ install_nodejs_apt() {
|
||||
|
||||
install_nodejs_yum() {
|
||||
if command -v node &> /dev/null; then
|
||||
NODE_VER=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
||||
if [[ $NODE_VER -ge $NODE_VERSION ]]; then
|
||||
if node_version_supported; then
|
||||
print_success "Node.js 已安装: $(node -v)"
|
||||
return
|
||||
fi
|
||||
print_warning "当前 Node.js $(node -v) 不满足 ${NODE_MIN_VERSION} 至 24.x,准备安装 20.x"
|
||||
fi
|
||||
|
||||
print_info "正在安装 Node.js ${NODE_VERSION}.x..."
|
||||
@@ -585,11 +590,11 @@ install_nginx_yum() {
|
||||
|
||||
install_nodejs_dnf() {
|
||||
if command -v node &> /dev/null; then
|
||||
NODE_VER=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
||||
if [[ $NODE_VER -ge $NODE_VERSION ]]; then
|
||||
if node_version_supported; then
|
||||
print_success "Node.js 已安装: $(node -v)"
|
||||
return
|
||||
fi
|
||||
print_warning "当前 Node.js $(node -v) 不满足 ${NODE_MIN_VERSION} 至 24.x,准备安装 20.x"
|
||||
fi
|
||||
|
||||
print_info "正在安装 Node.js ${NODE_VERSION}.x..."
|
||||
@@ -612,11 +617,11 @@ install_nginx_dnf() {
|
||||
|
||||
install_nodejs_zypper() {
|
||||
if command -v node &> /dev/null; then
|
||||
NODE_VER=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
||||
if [[ $NODE_VER -ge $NODE_VERSION ]]; then
|
||||
if node_version_supported; then
|
||||
print_success "Node.js 已安装: $(node -v)"
|
||||
return
|
||||
fi
|
||||
print_warning "当前 Node.js $(node -v) 不满足 ${NODE_MIN_VERSION} 至 24.x,准备安装 20.x"
|
||||
fi
|
||||
|
||||
print_info "正在安装 Node.js ${NODE_VERSION}.x..."
|
||||
@@ -1390,7 +1395,7 @@ deploy_certbot() {
|
||||
echo ""
|
||||
print_info "正在申请 Let's Encrypt 证书..."
|
||||
|
||||
if certbot certonly --webroot -w "${PROJECT_DIR}/frontend" -d "$DOMAIN" --non-interactive --agree-tos --email "admin@${DOMAIN}"; then
|
||||
if certbot certonly --webroot -w "${PROJECT_DIR}/frontend/dist" -d "$DOMAIN" --non-interactive --agree-tos --email "admin@${DOMAIN}"; then
|
||||
# 将证书复制到Nginx SSL目录
|
||||
mkdir -p /etc/nginx/ssl
|
||||
ln -sf "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" "/etc/nginx/ssl/${DOMAIN}.crt"
|
||||
@@ -1541,7 +1546,7 @@ deploy_acme_letsencrypt() {
|
||||
|
||||
# 使用webroot模式申请证书(更可靠)
|
||||
# 先尝试正常申请,如果证书已存在则使用--force强制更新
|
||||
if ~/.acme.sh/acme.sh --issue -d "$DOMAIN" --webroot "${PROJECT_DIR}/frontend"; then
|
||||
if ~/.acme.sh/acme.sh --issue -d "$DOMAIN" --webroot "${PROJECT_DIR}/frontend/dist"; then
|
||||
print_success "证书申请成功"
|
||||
else
|
||||
# 检查是否是因为证书已存在
|
||||
@@ -1707,7 +1712,7 @@ deploy_acme_zerossl() {
|
||||
fi
|
||||
|
||||
# 使用webroot模式申请证书(更可靠)
|
||||
if ~/.acme.sh/acme.sh --server zerossl --issue -d "$DOMAIN" --webroot "${PROJECT_DIR}/frontend"; then
|
||||
if ~/.acme.sh/acme.sh --server zerossl --issue -d "$DOMAIN" --webroot "${PROJECT_DIR}/frontend/dist"; then
|
||||
print_success "证书申请成功"
|
||||
else
|
||||
# 检查是否是因为证书已存在
|
||||
@@ -1866,7 +1871,7 @@ deploy_acme_buypass() {
|
||||
fi
|
||||
|
||||
# 使用webroot模式申请证书(更可靠)
|
||||
if ~/.acme.sh/acme.sh --server buypass --issue -d "$DOMAIN" --webroot "${PROJECT_DIR}/frontend"; then
|
||||
if ~/.acme.sh/acme.sh --server buypass --issue -d "$DOMAIN" --webroot "${PROJECT_DIR}/frontend/dist"; then
|
||||
print_success "证书申请成功"
|
||||
else
|
||||
# 检查是否是因为证书已存在
|
||||
@@ -2113,6 +2118,26 @@ install_backend_dependencies() {
|
||||
echo ""
|
||||
}
|
||||
|
||||
install_frontend_dependencies() {
|
||||
print_step "安装前端依赖并构建生产资源..."
|
||||
|
||||
cd "${PROJECT_DIR}/frontend"
|
||||
if [[ ! -f "package-lock.json" ]]; then
|
||||
print_error "前端 package-lock.json 不存在,无法执行可重复构建"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if npm ci --include=dev && npm run build; then
|
||||
rm -rf node_modules
|
||||
print_success "前端生产资源构建完成"
|
||||
else
|
||||
print_error "前端构建失败"
|
||||
print_info "可手动重试: cd ${PROJECT_DIR}/frontend && npm ci --include=dev && npm run build"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
create_env_file() {
|
||||
print_step "创建配置文件..."
|
||||
|
||||
@@ -2212,6 +2237,15 @@ PUBLIC_PORT=${HTTP_PORT}
|
||||
# CSRF 保护(生产环境强烈建议开启)
|
||||
# 使用 Double Submit Cookie 模式防止跨站请求伪造攻击
|
||||
ENABLE_CSRF=true
|
||||
|
||||
# 全局 API 限流与健康检查阈值
|
||||
API_RATE_LIMIT_WINDOW_MS=60000
|
||||
API_RATE_LIMIT_MAX=600
|
||||
HEALTH_MIN_DISK_FREE_BYTES=268435456
|
||||
|
||||
# 结构化日志
|
||||
LOG_LEVEL=info
|
||||
LOG_FORMAT=json
|
||||
EOF
|
||||
|
||||
print_success "配置文件创建完成"
|
||||
@@ -2369,7 +2403,9 @@ server {
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; media-src 'self' blob: https:; connect-src 'self' https: ws: wss:; worker-src 'self' blob:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self';" always;
|
||||
|
||||
# 隐藏Nginx版本号
|
||||
server_tokens off;
|
||||
@@ -2388,11 +2424,21 @@ server {
|
||||
|
||||
# 前端静态文件
|
||||
location / {
|
||||
root ${PROJECT_DIR}/frontend;
|
||||
root ${PROJECT_DIR}/frontend/dist;
|
||||
index index.html;
|
||||
try_files \$uri \$uri/ =404;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
alias ${PROJECT_DIR}/frontend/dist/assets/;
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
location /downloads/ {
|
||||
alias ${PROJECT_DIR}/frontend/downloads/;
|
||||
expires 1h;
|
||||
}
|
||||
|
||||
# 后端API
|
||||
location /api {
|
||||
proxy_pass http://localhost:${BACKEND_PORT};
|
||||
@@ -2404,6 +2450,7 @@ server {
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_set_header X-Request-ID \$request_id;
|
||||
|
||||
# Cookie传递配置(验证码session需要)
|
||||
proxy_set_header Cookie \$http_cookie;
|
||||
@@ -2422,15 +2469,15 @@ server {
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_set_header X-Request-ID \$request_id;
|
||||
}
|
||||
|
||||
# 静态资源
|
||||
location /libs {
|
||||
alias ${PROJECT_DIR}/frontend/libs;
|
||||
location /libs/ {
|
||||
alias ${PROJECT_DIR}/frontend/dist/libs/;
|
||||
expires 30d;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
@@ -2656,7 +2703,9 @@ server {
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; media-src 'self' blob: https:; connect-src 'self' https: ws: wss:; worker-src 'self' blob:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self';" always;
|
||||
|
||||
# 隐藏Nginx版本号
|
||||
server_tokens off;
|
||||
@@ -2675,11 +2724,21 @@ server {
|
||||
|
||||
# 前端静态文件
|
||||
location / {
|
||||
root ${PROJECT_DIR}/frontend;
|
||||
root ${PROJECT_DIR}/frontend/dist;
|
||||
index index.html;
|
||||
try_files \$uri \$uri/ =404;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
alias ${PROJECT_DIR}/frontend/dist/assets/;
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
location /downloads/ {
|
||||
alias ${PROJECT_DIR}/frontend/downloads/;
|
||||
expires 1h;
|
||||
}
|
||||
|
||||
# 后端API
|
||||
location /api {
|
||||
proxy_pass http://localhost:${BACKEND_PORT};
|
||||
@@ -2691,6 +2750,7 @@ server {
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_set_header X-Request-ID \$request_id;
|
||||
|
||||
# Cookie传递配置(验证码session需要)
|
||||
proxy_set_header Cookie \$http_cookie;
|
||||
@@ -2709,15 +2769,15 @@ server {
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_set_header X-Request-ID \$request_id;
|
||||
}
|
||||
|
||||
# 静态资源
|
||||
location /libs {
|
||||
alias ${PROJECT_DIR}/frontend/libs;
|
||||
location /libs/ {
|
||||
alias ${PROJECT_DIR}/frontend/dist/libs/;
|
||||
expires 30d;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
@@ -2792,7 +2852,9 @@ server {
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; media-src 'self' blob: https:; connect-src 'self' https: ws: wss:; worker-src 'self' blob:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self';" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# 隐藏Nginx版本号
|
||||
@@ -2812,11 +2874,21 @@ server {
|
||||
|
||||
# 前端静态文件
|
||||
location / {
|
||||
root ${PROJECT_DIR}/frontend;
|
||||
root ${PROJECT_DIR}/frontend/dist;
|
||||
index index.html;
|
||||
try_files \$uri \$uri/ =404;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
alias ${PROJECT_DIR}/frontend/dist/assets/;
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
location /downloads/ {
|
||||
alias ${PROJECT_DIR}/frontend/downloads/;
|
||||
expires 1h;
|
||||
}
|
||||
|
||||
# 后端API
|
||||
location /api {
|
||||
proxy_pass http://localhost:${BACKEND_PORT};
|
||||
@@ -2828,6 +2900,7 @@ server {
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_set_header X-Request-ID \$request_id;
|
||||
|
||||
# Cookie传递配置(验证码session需要)
|
||||
proxy_set_header Cookie \$http_cookie;
|
||||
@@ -2846,15 +2919,15 @@ server {
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_set_header X-Request-ID \$request_id;
|
||||
}
|
||||
|
||||
# 静态资源
|
||||
location /libs {
|
||||
alias ${PROJECT_DIR}/frontend/libs;
|
||||
location /libs/ {
|
||||
alias ${PROJECT_DIR}/frontend/dist/libs/;
|
||||
expires 30d;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
@@ -3398,6 +3471,12 @@ update_backup_important_files() {
|
||||
print_success "配置文件已备份"
|
||||
fi
|
||||
|
||||
# 桌面安装包可能由管理端动态上传,不可随前端源码更新丢失
|
||||
if [[ -d "${PROJECT_DIR}/frontend/downloads" ]]; then
|
||||
cp -a "${PROJECT_DIR}/frontend/downloads" "$TEMP_BACKUP/frontend-downloads"
|
||||
print_success "桌面安装包已备份"
|
||||
fi
|
||||
|
||||
print_success "备份完成: $TEMP_BACKUP"
|
||||
echo ""
|
||||
}
|
||||
@@ -3424,6 +3503,23 @@ update_pull_latest_code() {
|
||||
# 克隆最新代码
|
||||
git clone "$REPO_URL" "${PROJECT_NAME}-update"
|
||||
|
||||
# 在替换线上文件前完成前端构建,失败时旧版本仍保持完整。
|
||||
if [[ -d "$TEMP_BACKUP/frontend-downloads" ]]; then
|
||||
mkdir -p "/tmp/${PROJECT_NAME}-update/frontend/downloads"
|
||||
cp -a "$TEMP_BACKUP/frontend-downloads/." "/tmp/${PROJECT_NAME}-update/frontend/downloads/"
|
||||
fi
|
||||
|
||||
print_info "构建前端生产资源..."
|
||||
cd "/tmp/${PROJECT_NAME}-update/frontend"
|
||||
if ! npm ci --include=dev || ! npm run build; then
|
||||
print_error "前端构建失败,线上文件未替换"
|
||||
if command -v pm2 &> /dev/null; then
|
||||
pm2 restart ${PROJECT_NAME}-backend || true
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
rm -rf node_modules
|
||||
|
||||
# 更新前端文件
|
||||
print_info "更新前端文件..."
|
||||
if [[ -d "/tmp/${PROJECT_NAME}-update/frontend" ]]; then
|
||||
@@ -3470,14 +3566,109 @@ update_pull_latest_code() {
|
||||
print_success "配置文件已恢复"
|
||||
fi
|
||||
|
||||
# 清理临时文件
|
||||
# 临时源码可删除,数据备份保留到健康检查通过后再清理
|
||||
rm -rf "/tmp/${PROJECT_NAME}-update"
|
||||
rm -rf "$TEMP_BACKUP"
|
||||
|
||||
print_success "代码更新完成"
|
||||
echo ""
|
||||
}
|
||||
|
||||
update_migrate_nginx_config() {
|
||||
print_step "迁移 Nginx 前端构建目录..."
|
||||
|
||||
local candidates=(
|
||||
"/www/server/panel/vhost/nginx/${PROJECT_NAME}.conf"
|
||||
"/etc/nginx/sites-available/${PROJECT_NAME}.conf"
|
||||
"/etc/nginx/conf.d/${PROJECT_NAME}.conf"
|
||||
)
|
||||
local migrated=false
|
||||
local backup_dir="$TEMP_BACKUP/nginx"
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
for config_file in "${candidates[@]}"; do
|
||||
[[ -f "$config_file" ]] || continue
|
||||
migrated=true
|
||||
|
||||
local backup_name
|
||||
backup_name=$(echo "$config_file" | tr '/ ' '__')
|
||||
cp -a "$config_file" "$backup_dir/$backup_name"
|
||||
|
||||
sed -i "s|root ${PROJECT_DIR}/frontend;|root ${PROJECT_DIR}/frontend/dist;|g" "$config_file"
|
||||
sed -i "s|location /libs {|location /libs/ {|g" "$config_file"
|
||||
sed -i "s|alias ${PROJECT_DIR}/frontend/libs;|alias ${PROJECT_DIR}/frontend/dist/libs/;|g" "$config_file"
|
||||
sed -i 's|Referrer-Policy "no-referrer-when-downgrade"|Referrer-Policy "strict-origin-when-cross-origin"|g' "$config_file"
|
||||
|
||||
if ! grep -q "add_header Permissions-Policy" "$config_file" || ! grep -q "add_header Content-Security-Policy" "$config_file"; then
|
||||
local secured_file="${config_file}.security.$$"
|
||||
local need_permissions=0
|
||||
local need_csp=0
|
||||
grep -q "add_header Permissions-Policy" "$config_file" || need_permissions=1
|
||||
grep -q "add_header Content-Security-Policy" "$config_file" || need_csp=1
|
||||
awk -v need_permissions="$need_permissions" -v need_csp="$need_csp" '
|
||||
/add_header Referrer-Policy/ && !inserted_security {
|
||||
print
|
||||
if (need_permissions == 1) {
|
||||
print " add_header Permissions-Policy \"camera=(), geolocation=(), microphone=()\" always;"
|
||||
}
|
||||
if (need_csp == 1) {
|
||||
print " add_header Content-Security-Policy \"default-src \047self\047; script-src \047self\047 \047unsafe-inline\047 \047unsafe-eval\047; style-src \047self\047 \047unsafe-inline\047; img-src \047self\047 data: blob: https:; font-src \047self\047 data:; media-src \047self\047 blob: https:; connect-src \047self\047 https: ws: wss:; worker-src \047self\047 blob:; object-src \047none\047; base-uri \047self\047; frame-ancestors \047self\047; form-action \047self\047;\" always;"
|
||||
}
|
||||
inserted_security = 1
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$config_file" > "$secured_file"
|
||||
mv "$secured_file" "$config_file"
|
||||
fi
|
||||
|
||||
if ! grep -q "location /downloads/" "$config_file"; then
|
||||
local migrated_file="${config_file}.migrate.$$"
|
||||
awk -v project_dir="$PROJECT_DIR" '
|
||||
/^[[:space:]]*# 静态资源/ && !inserted {
|
||||
print " # 桌面安装包由管理端动态发布,不属于 Vite dist"
|
||||
print " location /downloads/ {"
|
||||
print " alias " project_dir "/frontend/downloads/;"
|
||||
print " expires 1h;"
|
||||
print " }"
|
||||
print ""
|
||||
inserted = 1
|
||||
}
|
||||
{ print }
|
||||
' "$config_file" > "$migrated_file"
|
||||
mv "$migrated_file" "$config_file"
|
||||
fi
|
||||
|
||||
if ! grep -q "root ${PROJECT_DIR}/frontend/dist;" "$config_file" || ! grep -q "location /downloads/" "$config_file"; then
|
||||
print_error "无法安全迁移 Nginx 配置: $config_file"
|
||||
cp -a "$backup_dir/$backup_name" "$config_file"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$migrated" != "true" ]]; then
|
||||
print_warning "未找到 install.sh 管理的 Nginx 配置,请手动确认 root 指向 ${PROJECT_DIR}/frontend/dist"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! nginx -t; then
|
||||
print_error "迁移后的 Nginx 配置校验失败,正在恢复"
|
||||
for config_file in "${candidates[@]}"; do
|
||||
[[ -f "$config_file" ]] || continue
|
||||
local backup_name
|
||||
backup_name=$(echo "$config_file" | tr '/ ' '__')
|
||||
if [[ -f "$backup_dir/$backup_name" ]]; then
|
||||
cp -a "$backup_dir/$backup_name" "$config_file"
|
||||
fi
|
||||
done
|
||||
nginx -t || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
restart_nginx_safe
|
||||
print_success "Nginx 已切换到 Vite 构建目录"
|
||||
echo ""
|
||||
}
|
||||
|
||||
update_install_dependencies() {
|
||||
print_step "更新后端依赖..."
|
||||
|
||||
@@ -3511,7 +3702,7 @@ update_install_dependencies() {
|
||||
check_cpp_compiler
|
||||
if [[ -d "node_modules" ]]; then
|
||||
print_info "清理旧依赖..."
|
||||
rm -rf node_modules package-lock.json
|
||||
rm -rf node_modules
|
||||
fi
|
||||
|
||||
print_info "正在重新安装依赖(可能需要几分钟)..."
|
||||
@@ -3521,6 +3712,7 @@ update_install_dependencies() {
|
||||
else
|
||||
print_error "依赖更新失败"
|
||||
print_warning "请检查错误日志: ~/.npm/_logs/"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
@@ -3750,6 +3942,23 @@ update_patch_env() {
|
||||
else
|
||||
print_info ".env 已包含 ENABLE_CSRF,保持不变"
|
||||
fi
|
||||
|
||||
if ! grep -q "^API_RATE_LIMIT_MAX=" "${PROJECT_DIR}/backend/.env"; then
|
||||
echo "API_RATE_LIMIT_WINDOW_MS=60000" >> "${PROJECT_DIR}/backend/.env"
|
||||
echo "API_RATE_LIMIT_MAX=600" >> "${PROJECT_DIR}/backend/.env"
|
||||
print_info "已补充全局 API 限流默认值"
|
||||
fi
|
||||
|
||||
if ! grep -q "^HEALTH_MIN_DISK_FREE_BYTES=" "${PROJECT_DIR}/backend/.env"; then
|
||||
echo "HEALTH_MIN_DISK_FREE_BYTES=268435456" >> "${PROJECT_DIR}/backend/.env"
|
||||
print_info "已补充健康检查磁盘阈值"
|
||||
fi
|
||||
|
||||
if ! grep -q "^LOG_FORMAT=" "${PROJECT_DIR}/backend/.env"; then
|
||||
echo "LOG_LEVEL=info" >> "${PROJECT_DIR}/backend/.env"
|
||||
echo "LOG_FORMAT=json" >> "${PROJECT_DIR}/backend/.env"
|
||||
print_info "已启用 JSON 结构化日志"
|
||||
fi
|
||||
else
|
||||
print_warning "未找到 ${PROJECT_DIR}/backend/.env,请手动确认配置"
|
||||
fi
|
||||
@@ -3815,6 +4024,9 @@ update_main() {
|
||||
# 拉取最新代码
|
||||
update_pull_latest_code
|
||||
|
||||
# 旧版本 Nginx 指向 frontend 源码,需迁移到 dist
|
||||
update_migrate_nginx_config
|
||||
|
||||
# 更新依赖
|
||||
|
||||
update_install_dependencies
|
||||
@@ -3831,9 +4043,12 @@ update_main() {
|
||||
if ! health_check; then
|
||||
print_error "健康检查未通过,请检查日志"
|
||||
print_info "查看日志: pm2 logs ${PROJECT_NAME}-backend"
|
||||
print_info "更新备份保留在: $TEMP_BACKUP"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf "$TEMP_BACKUP"
|
||||
|
||||
# 检查版本
|
||||
update_check_version
|
||||
|
||||
@@ -3951,6 +4166,9 @@ main() {
|
||||
# 安装后端依赖
|
||||
install_backend_dependencies
|
||||
|
||||
# 安装前端依赖并生成 dist
|
||||
install_frontend_dependencies
|
||||
|
||||
# 创建配置文件
|
||||
create_env_file
|
||||
|
||||
|
||||
Reference in New Issue
Block a user