mirror of
https://gitee.com/ShopeX/ECShopX
synced 2026-08-04 11:16:23 +08:00
部署脚本
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
{
|
||||
"name": "laravel/lumen",
|
||||
"version": "4.9.0",
|
||||
|
||||
497
dev-setup.sh
497
dev-setup.sh
@@ -20,6 +20,9 @@ CONTAINER_NAME="ecshopx-dev"
|
||||
WEB_CONTAINER_NAME="ecshopx-web-frontend"
|
||||
DOCKER_COMPOSE_FILE="$PROJECT_ROOT/docker-compose.dev.yml"
|
||||
|
||||
# 公开前端仓库地址;默认使用 Gitee 方便国内用户,如需 GitHub 可在运行脚本前导出 PUBLIC_REPO_BASE_URL 覆盖
|
||||
PUBLIC_REPO_BASE_URL="${PUBLIC_REPO_BASE_URL:-https://gitee.com/ShopeX}"
|
||||
|
||||
# 数据库配置
|
||||
MYSQL_ROOT_PWD="rootpassword"
|
||||
MYSQL_DATABASE="ecshopx"
|
||||
@@ -48,6 +51,10 @@ PC_URL="http://localhost:8082"
|
||||
QIANKUN_ENTRY_URL="http://localhost:8080/newpc/"
|
||||
MOBILE_API_URL="http://localhost:8080/api/h5app/wxapp"
|
||||
PC_API_URL="http://localhost:8080/api/h5app"
|
||||
ADMIN_HOST_PORT="8080"
|
||||
H5_HOST_PORT="8081"
|
||||
PC_HOST_PORT="8082"
|
||||
NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS="http://localhost:8080"
|
||||
|
||||
# 业务模式(全局变量,在 build_admin 中设置,build_vshop 中复用)
|
||||
SELECTED_PLATFORM=""
|
||||
@@ -55,6 +62,9 @@ SELECTED_PLATFORM=""
|
||||
# 默认语言(全局变量,在第一个编译的项目中设置,后续项目复用)
|
||||
SELECTED_LANG=""
|
||||
|
||||
# 最近一次前端 .env 配置是否发生变化(build_* 中用于决定已有产物能否跳过编译)
|
||||
FRONTEND_ENV_CHANGED=false
|
||||
|
||||
# 语言/业务模式等交互必须从 /dev/tty 读取:若仅从 stdin 读,在「管道、重定向、部分 IDE 集成终端」下会立即 EOF,
|
||||
# 空变量会触发 ${VAR:-1},表现为未确认就选用默认项 1。
|
||||
|
||||
@@ -130,6 +140,53 @@ validate_public_url() {
|
||||
esac
|
||||
}
|
||||
|
||||
extract_host_port_from_url() {
|
||||
local url=$1
|
||||
local default_port=$2
|
||||
local without_scheme=""
|
||||
local authority=""
|
||||
local port=""
|
||||
|
||||
without_scheme=${url#*://}
|
||||
authority=${without_scheme%%/*}
|
||||
|
||||
case "$authority" in
|
||||
*:*)
|
||||
port=${authority##*:}
|
||||
;;
|
||||
*)
|
||||
port=$default_port
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$port" in
|
||||
''|*[!0-9]*)
|
||||
log_error "访问地址端口无效: $url"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then
|
||||
log_error "访问地址端口超出范围: $url"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$port"
|
||||
}
|
||||
|
||||
configure_docker_host_ports() {
|
||||
ADMIN_HOST_PORT=$(extract_host_port_from_url "$ADMIN_URL" "8080")
|
||||
H5_HOST_PORT=$(extract_host_port_from_url "$H5_URL" "8081")
|
||||
PC_HOST_PORT=$(extract_host_port_from_url "$PC_URL" "8082")
|
||||
|
||||
export ADMIN_HOST_PORT H5_HOST_PORT PC_HOST_PORT
|
||||
|
||||
log_info "Docker 宿主机端口映射:"
|
||||
log_info " 管理后台/API: ${ADMIN_HOST_PORT}->8080"
|
||||
log_info " H5前端: ${H5_HOST_PORT}->8081"
|
||||
log_info " PC前端: ${PC_HOST_PORT}->8082"
|
||||
}
|
||||
|
||||
set_url_defaults_from_site_url() {
|
||||
local normalized_site_url
|
||||
normalized_site_url=$(trim_trailing_slashes "$SITE_URL")
|
||||
@@ -148,6 +205,36 @@ derive_api_base_from_admin_url() {
|
||||
PC_API_URL="$normalized_api_url/h5app"
|
||||
}
|
||||
|
||||
origin_from_url() {
|
||||
local url=$1
|
||||
local scheme=""
|
||||
local without_scheme=""
|
||||
local authority=""
|
||||
|
||||
scheme=${url%%://*}
|
||||
without_scheme=${url#*://}
|
||||
authority=${without_scheme%%/*}
|
||||
|
||||
echo "${scheme}://${authority}"
|
||||
}
|
||||
|
||||
read_env_value() {
|
||||
local env_file=$1
|
||||
local key=$2
|
||||
local value=""
|
||||
|
||||
if [ ! -f "$env_file" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
value=$(grep -m 1 "^${key}=" "$env_file" 2>/dev/null | cut -d'=' -f2- || true)
|
||||
value=${value%\"}
|
||||
value=${value#\"}
|
||||
value=${value%\'}
|
||||
value=${value#\'}
|
||||
echo "$value"
|
||||
}
|
||||
|
||||
configure_public_urls() {
|
||||
if [ -z "$SITE_URL" ] && [ -z "$ADMIN_URL_OVERRIDE" ] && [ -z "$H5_URL_OVERRIDE" ] && [ -z "$PC_URL_OVERRIDE" ]; then
|
||||
echo ""
|
||||
@@ -187,6 +274,10 @@ configure_public_urls() {
|
||||
fi
|
||||
|
||||
derive_api_base_from_admin_url
|
||||
export NUXT_PUBLIC_API_BASE="$PC_API_URL"
|
||||
NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=$(origin_from_url "$ADMIN_URL")
|
||||
export NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS="$NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS"
|
||||
configure_docker_host_ports
|
||||
|
||||
log_info "访问地址配置完成:"
|
||||
log_info " 管理后台: $ADMIN_URL"
|
||||
@@ -504,7 +595,7 @@ check_and_clone_frontend() {
|
||||
|
||||
# 检查 ECShopX_web-frontend
|
||||
PC_DIR="$PARENT_DIR/ECShopX_web-frontend"
|
||||
PC_REPO="https://gitee.com/ShopeX/ECShopX_web-frontend.git"
|
||||
PC_REPO="$PUBLIC_REPO_BASE_URL/ECShopX_web-frontend.git"
|
||||
|
||||
if [ ! -d "$PC_DIR" ] || [ ! -f "$PC_DIR/package.json" ]; then
|
||||
if [ ! -d "$PC_DIR" ]; then
|
||||
@@ -560,7 +651,7 @@ check_and_clone_frontend() {
|
||||
|
||||
# 检查 ECShopX_admin-frontend
|
||||
ADMIN_DIR="$PARENT_DIR/ECShopX_admin-frontend"
|
||||
ADMIN_REPO="https://gitee.com/ShopeX/ECShopX_admin-frontend.git"
|
||||
ADMIN_REPO="$PUBLIC_REPO_BASE_URL/ECShopX_admin-frontend.git"
|
||||
|
||||
if [ ! -d "$ADMIN_DIR" ] || [ ! -f "$ADMIN_DIR/package.json" ]; then
|
||||
if [ ! -d "$ADMIN_DIR" ]; then
|
||||
@@ -620,7 +711,7 @@ check_and_clone_frontend() {
|
||||
|
||||
# 检查 ECShopX_mobile-frontend
|
||||
VSHOP_DIR="$PARENT_DIR/ECShopX_mobile-frontend"
|
||||
VSHOP_REPO="https://gitee.com/ShopeX/ECShopX_mobile-frontend.git"
|
||||
VSHOP_REPO="$PUBLIC_REPO_BASE_URL/ECShopX_mobile-frontend.git"
|
||||
|
||||
if [ ! -d "$VSHOP_DIR" ] || [ ! -f "$VSHOP_DIR/package.json" ]; then
|
||||
if [ ! -d "$VSHOP_DIR" ]; then
|
||||
@@ -915,6 +1006,104 @@ read_product_model_from_env() {
|
||||
fi
|
||||
}
|
||||
|
||||
preserve_existing_product_model() {
|
||||
local product_model=""
|
||||
|
||||
product_model=$(read_env_value "$PROJECT_ROOT/.env" "PRODUCT_MODEL")
|
||||
|
||||
if [ -z "$product_model" ] && docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$" 2>/dev/null; then
|
||||
product_model=$(docker exec "$CONTAINER_NAME" sh -c \
|
||||
"cd /data/httpd/ECShopX && grep '^PRODUCT_MODEL=' .env 2>/dev/null | cut -d'=' -f2" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
if [ "$product_model" = "standard" ]; then
|
||||
SELECTED_PLATFORM="standard"
|
||||
log_info "检测到现有 PRODUCT_MODEL=standard,保留 B2C 业务模式"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$product_model" = "platform" ]; then
|
||||
SELECTED_PLATFORM="platform"
|
||||
log_info "检测到现有 PRODUCT_MODEL=platform,保留 BBC 业务模式"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
load_existing_frontend_lang() {
|
||||
if [ -n "$SELECTED_LANG" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local lang=""
|
||||
lang=$(read_env_value "$PARENT_DIR/ECShopX_admin-frontend/.env" "VUE_APP_DEFAULT_LANG")
|
||||
if [ -z "$lang" ]; then
|
||||
lang=$(read_env_value "$PARENT_DIR/ECShopX_web-frontend/.env" "NUXT_PUBLIC_DEFAULT_COUNTRY_CODE")
|
||||
fi
|
||||
if [ -z "$lang" ]; then
|
||||
lang=$(read_env_value "$PARENT_DIR/ECShopX_mobile-frontend/.env" "APP_I18N_ORIGIN_LANG")
|
||||
fi
|
||||
|
||||
if [ "$lang" = "zh-CN" ]; then
|
||||
SELECTED_LANG="zhcn"
|
||||
log_info "从现有前端 .env 读取默认语言: $SELECTED_LANG"
|
||||
elif [ "$lang" = "en-CN" ]; then
|
||||
SELECTED_LANG="en"
|
||||
log_info "从现有前端 .env 读取默认语言: $SELECTED_LANG"
|
||||
elif [ "$lang" = "zhcn" ] || [ "$lang" = "en" ]; then
|
||||
SELECTED_LANG="$lang"
|
||||
log_info "从现有前端 .env 读取默认语言: $SELECTED_LANG"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_selected_lang() {
|
||||
load_existing_frontend_lang
|
||||
|
||||
if [ -n "$SELECTED_LANG" ]; then
|
||||
log_info "复用默认语言: $SELECTED_LANG"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "请选择默认语言:"
|
||||
log_info " 1) 中文 (zhcn)"
|
||||
log_info " 2) 英文 (en)"
|
||||
echo ""
|
||||
while true; do
|
||||
read -r -p "请输入选项 (1 或 2,默认: 1): " LANG_CHOICE < /dev/tty
|
||||
LANG_CHOICE=${LANG_CHOICE:-1}
|
||||
if [ "$LANG_CHOICE" = "1" ] || [ "$LANG_CHOICE" = "zhcn" ] || [ "$LANG_CHOICE" = "zh" ]; then
|
||||
SELECTED_LANG="zhcn"
|
||||
break
|
||||
elif [ "$LANG_CHOICE" = "2" ] || [ "$LANG_CHOICE" = "en" ] || [ "$LANG_CHOICE" = "english" ]; then
|
||||
SELECTED_LANG="en"
|
||||
break
|
||||
else
|
||||
log_error "无效的选项,请输入 1 或 2"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
log_info "已选择默认语言: $SELECTED_LANG"
|
||||
echo ""
|
||||
}
|
||||
|
||||
pc_api_country_code_for_lang() {
|
||||
local lang=$1
|
||||
|
||||
case "$lang" in
|
||||
en|en-CN|en_US|en-US)
|
||||
echo "en-CN"
|
||||
;;
|
||||
zhcn|zh|zh-CN|zh_CN|"")
|
||||
echo "zh-CN"
|
||||
;;
|
||||
*)
|
||||
echo "$lang"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ===========================================
|
||||
# 配置 PHP 应用
|
||||
# ===========================================
|
||||
@@ -952,28 +1141,30 @@ configure_application() {
|
||||
(grep -q '^SHOP_ADMIN_URL=' .env && sed -i 's|^SHOP_ADMIN_URL=.*|SHOP_ADMIN_URL=$ADMIN_URL/|' .env || echo 'SHOP_ADMIN_URL=$ADMIN_URL/' >> .env) && \
|
||||
(grep -q '^API_BASE_URL=' .env && sed -i 's|^API_BASE_URL=.*|API_BASE_URL=$API_BASE_URL|' .env || echo 'API_BASE_URL=$API_BASE_URL' >> .env)" 2>/dev/null || true
|
||||
|
||||
# 选择业务模式并写入 PRODUCT_MODEL
|
||||
echo ""
|
||||
log_info "请选择业务模式:"
|
||||
log_info " 1) BBC (多商户入驻电商平台模式)"
|
||||
log_info " 2) B2C (线上商城、O2O云店、内购商城、供应链线上商城等模式)"
|
||||
echo ""
|
||||
while true; do
|
||||
read -r -p "请输入选项 (1 或 2,默认: 1): " BIZ_MODE_CHOICE < /dev/tty
|
||||
BIZ_MODE_CHOICE=${BIZ_MODE_CHOICE:-1}
|
||||
if [ "$BIZ_MODE_CHOICE" = "1" ] || [ "$BIZ_MODE_CHOICE" = "bbc" ]; then
|
||||
SELECTED_PLATFORM="platform"
|
||||
log_info "已选择业务模式: BBC (B2B2C)"
|
||||
break
|
||||
elif [ "$BIZ_MODE_CHOICE" = "2" ] || [ "$BIZ_MODE_CHOICE" = "b2c" ]; then
|
||||
SELECTED_PLATFORM="standard"
|
||||
log_info "已选择业务模式: B2C"
|
||||
break
|
||||
else
|
||||
log_error "无效的选项,请输入 1 或 2"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
# 选择业务模式并写入 PRODUCT_MODEL;重跑脚本时优先保留已有安装模式
|
||||
if ! preserve_existing_product_model; then
|
||||
echo ""
|
||||
log_info "请选择业务模式:"
|
||||
log_info " 1) BBC (多商户入驻电商平台模式)"
|
||||
log_info " 2) B2C (线上商城、O2O云店、内购商城、供应链线上商城等模式)"
|
||||
echo ""
|
||||
while true; do
|
||||
read -r -p "请输入选项 (1 或 2,默认: 1): " BIZ_MODE_CHOICE < /dev/tty
|
||||
BIZ_MODE_CHOICE=${BIZ_MODE_CHOICE:-1}
|
||||
if [ "$BIZ_MODE_CHOICE" = "1" ] || [ "$BIZ_MODE_CHOICE" = "bbc" ]; then
|
||||
SELECTED_PLATFORM="platform"
|
||||
log_info "已选择业务模式: BBC (B2B2C)"
|
||||
break
|
||||
elif [ "$BIZ_MODE_CHOICE" = "2" ] || [ "$BIZ_MODE_CHOICE" = "b2c" ]; then
|
||||
SELECTED_PLATFORM="standard"
|
||||
log_info "已选择业务模式: B2C"
|
||||
break
|
||||
else
|
||||
log_error "无效的选项,请输入 1 或 2"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 将 PRODUCT_MODEL 写入 ECShopX 的 .env
|
||||
docker exec "$CONTAINER_NAME" sh -c "cd /data/httpd/ECShopX && \
|
||||
@@ -1337,6 +1528,7 @@ configure_frontend_env() {
|
||||
local pc_url=${6:-"$PC_URL"}
|
||||
local qiankun_entry_url=${7:-"$QIANKUN_ENTRY_URL"}
|
||||
local target_container="$CONTAINER_NAME"
|
||||
FRONTEND_ENV_CHANGED=false
|
||||
|
||||
# 检查主机目录是否存在
|
||||
if [ ! -d "$project_dir" ]; then
|
||||
@@ -1362,6 +1554,9 @@ configure_frontend_env() {
|
||||
if ! docker ps --format '{{.Names}}' | grep -q "^${target_container}$" 2>/dev/null; then
|
||||
log_info "容器 $target_container 未运行,配置主机目录的 .env 文件..."
|
||||
local env_file="$project_dir/.env"
|
||||
local env_before=""
|
||||
local env_after=""
|
||||
env_before=$(cat "$env_file" 2>/dev/null || true)
|
||||
|
||||
# 如果 .env 不存在,尝试从 .env.example 复制
|
||||
if [ ! -f "$env_file" ]; then
|
||||
@@ -1481,6 +1676,8 @@ configure_frontend_env() {
|
||||
|
||||
# 配置 ECShopX_web-frontend
|
||||
if [ "$project_name" = "ECShopX_web-frontend" ]; then
|
||||
local pc_default_country_code=""
|
||||
|
||||
# 使用 sed 修改或添加 NUXT_PUBLIC_API_BASE
|
||||
if grep -q "^NUXT_PUBLIC_API_BASE=" "$env_file" 2>/dev/null; then
|
||||
# macOS 和 Linux 兼容的 sed 命令
|
||||
@@ -1493,35 +1690,49 @@ configure_frontend_env() {
|
||||
echo "NUXT_PUBLIC_API_BASE=$api_base_url" >> "$env_file"
|
||||
fi
|
||||
log_success "已配置 NUXT_PUBLIC_API_BASE=$api_base_url"
|
||||
|
||||
# 配置 NUXT_PUBLIC_COMPANY_ID(默认值为1)
|
||||
if grep -q "^NUXT_PUBLIC_COMPANY_ID=" "$env_file" 2>/dev/null; then
|
||||
# macOS 和 Linux 兼容的 sed 命令
|
||||
|
||||
# 配置 PC 装修预览允许接收的后台 origin
|
||||
if grep -q "^NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=" "$env_file" 2>/dev/null; then
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "s|^NUXT_PUBLIC_COMPANY_ID=.*|NUXT_PUBLIC_COMPANY_ID=1|" "$env_file"
|
||||
sed -i '' "s|^NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=.*|NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=$NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS|" "$env_file"
|
||||
else
|
||||
sed -i "s|^NUXT_PUBLIC_COMPANY_ID=.*|NUXT_PUBLIC_COMPANY_ID=1|" "$env_file"
|
||||
sed -i "s|^NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=.*|NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=$NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS|" "$env_file"
|
||||
fi
|
||||
else
|
||||
echo "NUXT_PUBLIC_COMPANY_ID=1" >> "$env_file"
|
||||
echo "NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=$NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS" >> "$env_file"
|
||||
fi
|
||||
log_success "已配置 NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=$NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS"
|
||||
|
||||
# 配置 NUXT_PUBLIC_COMPANY_ID(默认值为1):已有值可能对应已安装店铺,重跑脚本时不覆盖
|
||||
if grep -q "^NUXT_PUBLIC_COMPANY_ID=" "$env_file" 2>/dev/null; then
|
||||
log_info "NUXT_PUBLIC_COMPANY_ID 已存在,保留当前值"
|
||||
else
|
||||
echo "NUXT_PUBLIC_COMPANY_ID=1" >> "$env_file"
|
||||
log_success "NUXT_PUBLIC_COMPANY_ID 不存在,已配置默认值 1"
|
||||
fi
|
||||
log_success "已配置 NUXT_PUBLIC_COMPANY_ID=1"
|
||||
|
||||
# 配置 NUXT_PUBLIC_DEFAULT_COUNTRY_CODE(如果提供)
|
||||
if [ -n "$default_lang" ]; then
|
||||
pc_default_country_code=$(pc_api_country_code_for_lang "$default_lang")
|
||||
if grep -q "^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=" "$env_file" 2>/dev/null; then
|
||||
# macOS 和 Linux 兼容的 sed 命令
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
sed -i '' "s|^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=.*|NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang|" "$env_file"
|
||||
sed -i '' "s|^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=.*|NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$pc_default_country_code|" "$env_file"
|
||||
else
|
||||
sed -i "s|^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=.*|NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang|" "$env_file"
|
||||
sed -i "s|^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=.*|NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$pc_default_country_code|" "$env_file"
|
||||
fi
|
||||
else
|
||||
echo "NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang" >> "$env_file"
|
||||
echo "NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$pc_default_country_code" >> "$env_file"
|
||||
fi
|
||||
log_success "已配置 NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang"
|
||||
log_success "已配置 NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$pc_default_country_code"
|
||||
fi
|
||||
fi
|
||||
|
||||
env_after=$(cat "$env_file" 2>/dev/null || true)
|
||||
if [ "$env_before" != "$env_after" ]; then
|
||||
FRONTEND_ENV_CHANGED=true
|
||||
log_info "$project_name .env 已更新"
|
||||
fi
|
||||
else
|
||||
# 容器运行中,配置容器内的 .env 文件
|
||||
# 检查容器内目录是否存在
|
||||
@@ -1529,6 +1740,10 @@ configure_frontend_env() {
|
||||
log_warning "容器 $target_container 内 $container_path 目录不存在,跳过配置"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local env_before=""
|
||||
local env_after=""
|
||||
env_before=$(docker exec "$target_container" sh -c "cd $container_path && [ -f .env ] && cat .env" 2>/dev/null || true)
|
||||
|
||||
# 在容器内配置 .env 文件
|
||||
docker exec "$target_container" sh -c "
|
||||
@@ -1633,6 +1848,8 @@ configure_frontend_env() {
|
||||
|
||||
# 配置 ECShopX_web-frontend
|
||||
if [ "$project_name" = "ECShopX_web-frontend" ]; then
|
||||
local pc_default_country_code=""
|
||||
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^NUXT_PUBLIC_API_BASE=' .env 2>/dev/null; then
|
||||
@@ -1642,31 +1859,52 @@ configure_frontend_env() {
|
||||
fi
|
||||
" 2>/dev/null || true
|
||||
log_success "已配置容器内 NUXT_PUBLIC_API_BASE=$api_base_url"
|
||||
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=' .env 2>/dev/null; then
|
||||
sed -i 's|^NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=.*|NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=$NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS|' .env
|
||||
else
|
||||
echo 'NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=$NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS' >> .env
|
||||
fi
|
||||
" 2>/dev/null || true
|
||||
log_success "已配置容器内 NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=$NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS"
|
||||
|
||||
# 配置 NUXT_PUBLIC_COMPANY_ID(默认值为1)
|
||||
# 配置 NUXT_PUBLIC_COMPANY_ID(默认值为1):已有值可能对应已安装店铺,重跑脚本时不覆盖
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^NUXT_PUBLIC_COMPANY_ID=' .env 2>/dev/null; then
|
||||
sed -i 's|^NUXT_PUBLIC_COMPANY_ID=.*|NUXT_PUBLIC_COMPANY_ID=1|' .env
|
||||
true
|
||||
else
|
||||
echo 'NUXT_PUBLIC_COMPANY_ID=1' >> .env
|
||||
fi
|
||||
" 2>/dev/null || true
|
||||
log_success "已配置容器内 NUXT_PUBLIC_COMPANY_ID=1"
|
||||
if docker exec "$target_container" sh -c "cd $container_path && grep -q '^NUXT_PUBLIC_COMPANY_ID=' .env" 2>/dev/null; then
|
||||
log_info "容器内 NUXT_PUBLIC_COMPANY_ID 已存在或已配置默认值"
|
||||
else
|
||||
log_warning "容器内 NUXT_PUBLIC_COMPANY_ID 配置失败"
|
||||
fi
|
||||
|
||||
# 配置 NUXT_PUBLIC_DEFAULT_COUNTRY_CODE(如果提供)
|
||||
if [ -n "$default_lang" ]; then
|
||||
pc_default_country_code=$(pc_api_country_code_for_lang "$default_lang")
|
||||
docker exec "$target_container" sh -c "
|
||||
cd $container_path && \
|
||||
if grep -q '^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=' .env 2>/dev/null; then
|
||||
sed -i 's|^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=.*|NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang|' .env
|
||||
sed -i 's|^NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=.*|NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$pc_default_country_code|' .env
|
||||
else
|
||||
echo 'NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang' >> .env
|
||||
echo 'NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$pc_default_country_code' >> .env
|
||||
fi
|
||||
" 2>/dev/null || true
|
||||
log_success "已配置容器内 NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$default_lang"
|
||||
log_success "已配置容器内 NUXT_PUBLIC_DEFAULT_COUNTRY_CODE=$pc_default_country_code"
|
||||
fi
|
||||
fi
|
||||
|
||||
env_after=$(docker exec "$target_container" sh -c "cd $container_path && [ -f .env ] && cat .env" 2>/dev/null || true)
|
||||
if [ "$env_before" != "$env_after" ]; then
|
||||
FRONTEND_ENV_CHANGED=true
|
||||
log_info "$project_name .env 已更新"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1695,48 +1933,29 @@ build_admin() {
|
||||
log_error "ECShopX_admin-frontend/package.json 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查是否已有编译产物
|
||||
if [ -d "$ADMIN_DIR/dist" ] && [ -f "$ADMIN_DIR/dist/index.html" ]; then
|
||||
log_info "检测到已有编译产物,跳过编译"
|
||||
log_info "如需重新编译,请删除 ECShopX_admin-frontend/dist 目录"
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
# 检查容器内目录是否存在并确保正确挂载
|
||||
log_info "检查容器内目录挂载状态..."
|
||||
if ! check_container_directory "/data/httpd/ECShopX_admin-frontend" "$ADMIN_DIR" "ECShopX_admin-frontend"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 确认默认语言(如果还未选择)
|
||||
if [ -z "$SELECTED_LANG" ]; then
|
||||
echo ""
|
||||
log_info "请选择默认语言:"
|
||||
log_info " 1) 中文 (zhcn)"
|
||||
log_info " 2) 英文 (en)"
|
||||
echo ""
|
||||
while true; do
|
||||
read -r -p "请输入选项 (1 或 2,默认: 1): " LANG_CHOICE < /dev/tty
|
||||
LANG_CHOICE=${LANG_CHOICE:-1}
|
||||
if [ "$LANG_CHOICE" = "1" ] || [ "$LANG_CHOICE" = "zhcn" ] || [ "$LANG_CHOICE" = "zh" ]; then
|
||||
SELECTED_LANG="zhcn"
|
||||
break
|
||||
elif [ "$LANG_CHOICE" = "2" ] || [ "$LANG_CHOICE" = "en" ] || [ "$LANG_CHOICE" = "english" ]; then
|
||||
SELECTED_LANG="en"
|
||||
break
|
||||
else
|
||||
log_error "无效的选项,请输入 1 或 2"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
log_info "已选择默认语言: $SELECTED_LANG"
|
||||
echo ""
|
||||
else
|
||||
log_info "复用已选择的默认语言: $SELECTED_LANG"
|
||||
|
||||
load_existing_frontend_lang
|
||||
log_info "配置 ECShopX_admin-frontend 的 .env 文件..."
|
||||
configure_frontend_env "$ADMIN_DIR" "ECShopX_admin-frontend" "$API_BASE_URL" "" "$SELECTED_LANG" "$PC_URL" "$QIANKUN_ENTRY_URL"
|
||||
|
||||
# 检查是否已有编译产物;前端 .env 变化时必须重新编译
|
||||
if [ -d "$ADMIN_DIR/dist" ] && [ -f "$ADMIN_DIR/dist/index.html" ]; then
|
||||
if [ "$FRONTEND_ENV_CHANGED" = true ]; then
|
||||
log_info "检测到已有编译产物,但前端 .env 已变化,需要重新编译"
|
||||
else
|
||||
log_info "检测到已有编译产物,跳过编译"
|
||||
log_info "如需重新编译,请删除 ECShopX_admin-frontend/dist 目录"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# 配置前端项目的 .env 文件
|
||||
|
||||
ensure_selected_lang
|
||||
log_info "配置 ECShopX_admin-frontend 的 .env 文件..."
|
||||
configure_frontend_env "$ADMIN_DIR" "ECShopX_admin-frontend" "$API_BASE_URL" "" "$SELECTED_LANG" "$PC_URL" "$QIANKUN_ENTRY_URL"
|
||||
|
||||
@@ -1859,23 +2078,32 @@ build_pc() {
|
||||
if ! ensure_web_container_running; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查是否已有可启动的 Nuxt 生产产物(.nuxt 可能由 pnpm install/postinstall 生成,不能代表已编译完成)
|
||||
if docker exec "$WEB_CONTAINER_NAME" sh -c "[ -f /data/httpd/ECShopX_web-frontend/.output/server/index.mjs ]" 2>/dev/null || \
|
||||
[ -f "$PC_DIR/.output/server/index.mjs" ]; then
|
||||
log_info "检测到已有 Nuxt 生产编译产物,跳过编译"
|
||||
log_info "如需重新编译,请删除 ECShopX_web-frontend/.output 目录"
|
||||
# 即使跳过编译,也需要启动Nuxt服务
|
||||
need_build=false
|
||||
else
|
||||
need_build=true
|
||||
fi
|
||||
|
||||
|
||||
# 检查容器内目录是否存在并确保正确挂载
|
||||
log_info "检查容器内目录挂载状态..."
|
||||
if ! check_container_directory "/data/httpd/ECShopX_web-frontend" "$PC_DIR" "ECShopX_web-frontend"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
load_existing_frontend_lang
|
||||
log_info "配置 ECShopX_web-frontend 的 .env 文件..."
|
||||
configure_frontend_env "$PC_DIR" "ECShopX_web-frontend" "$PC_API_URL" "" "$SELECTED_LANG"
|
||||
|
||||
# 检查是否已有可启动的 Nuxt 生产产物(.nuxt 可能由 pnpm install/postinstall 生成,不能代表已编译完成)
|
||||
if docker exec "$WEB_CONTAINER_NAME" sh -c "[ -f /data/httpd/ECShopX_web-frontend/.output/server/index.mjs ]" 2>/dev/null || \
|
||||
[ -f "$PC_DIR/.output/server/index.mjs" ]; then
|
||||
if [ "$FRONTEND_ENV_CHANGED" = true ]; then
|
||||
log_info "检测到已有 Nuxt 生产编译产物,但前端 .env 已变化,需要重新编译"
|
||||
need_build=true
|
||||
else
|
||||
log_info "检测到已有 Nuxt 生产编译产物,跳过编译"
|
||||
log_info "如需重新编译,请删除 ECShopX_web-frontend/.output 目录"
|
||||
# 即使跳过编译,也需要启动Nuxt服务
|
||||
need_build=false
|
||||
fi
|
||||
else
|
||||
need_build=true
|
||||
fi
|
||||
|
||||
# 如果跳过编译,直接启动Nuxt服务,不需要确认语言
|
||||
if [ "$need_build" = false ]; then
|
||||
@@ -1903,34 +2131,7 @@ build_pc() {
|
||||
|
||||
# 只有在需要编译时才确认默认语言和配置.env
|
||||
if [ "$need_build" = true ]; then
|
||||
# 确认默认语言(如果还未选择)
|
||||
if [ -z "$SELECTED_LANG" ]; then
|
||||
echo ""
|
||||
log_info "请选择默认语言:"
|
||||
log_info " 1) 中文 (zhcn)"
|
||||
log_info " 2) 英文 (en)"
|
||||
echo ""
|
||||
while true; do
|
||||
read -r -p "请输入选项 (1 或 2,默认: 1): " LANG_CHOICE < /dev/tty
|
||||
LANG_CHOICE=${LANG_CHOICE:-1}
|
||||
if [ "$LANG_CHOICE" = "1" ] || [ "$LANG_CHOICE" = "zhcn" ] || [ "$LANG_CHOICE" = "zh" ]; then
|
||||
SELECTED_LANG="zhcn"
|
||||
break
|
||||
elif [ "$LANG_CHOICE" = "2" ] || [ "$LANG_CHOICE" = "en" ] || [ "$LANG_CHOICE" = "english" ]; then
|
||||
SELECTED_LANG="en"
|
||||
break
|
||||
else
|
||||
log_error "无效的选项,请输入 1 或 2"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
log_info "已选择默认语言: $SELECTED_LANG"
|
||||
echo ""
|
||||
else
|
||||
log_info "复用已选择的默认语言: $SELECTED_LANG"
|
||||
fi
|
||||
|
||||
# 配置前端项目的 .env 文件
|
||||
ensure_selected_lang
|
||||
log_info "配置 ECShopX_web-frontend 的 .env 文件..."
|
||||
configure_frontend_env "$PC_DIR" "ECShopX_web-frontend" "$PC_API_URL" "" "$SELECTED_LANG"
|
||||
|
||||
@@ -2102,14 +2303,7 @@ build_vshop() {
|
||||
log_error "ECShopX_mobile-frontend/package.json 不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查是否已有编译产物
|
||||
if [ -d "$VSHOP_DIR/dist" ]; then
|
||||
log_info "检测到已有编译产物,跳过编译"
|
||||
log_info "如需重新编译,请删除 ECShopX_mobile-frontend/dist 目录"
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
# 检查容器内目录是否存在并确保正确挂载
|
||||
log_info "检查容器内目录挂载状态..."
|
||||
if ! check_container_directory "/data/httpd/ECShopX_mobile-frontend" "$VSHOP_DIR" "ECShopX_mobile-frontend"; then
|
||||
@@ -2125,37 +2319,28 @@ build_vshop() {
|
||||
BUILD_MODE_NAME="BBC (B2B2C)"
|
||||
fi
|
||||
log_info "使用业务模式: $BUILD_MODE_NAME"
|
||||
|
||||
# 确认默认语言(如果还未选择)
|
||||
if [ -z "$SELECTED_LANG" ]; then
|
||||
echo ""
|
||||
log_info "请选择默认语言:"
|
||||
log_info " 1) 中文 (zhcn)"
|
||||
log_info " 2) 英文 (en)"
|
||||
echo ""
|
||||
while true; do
|
||||
read -r -p "请输入选项 (1 或 2,默认: 1): " LANG_CHOICE < /dev/tty
|
||||
LANG_CHOICE=${LANG_CHOICE:-1}
|
||||
if [ "$LANG_CHOICE" = "1" ] || [ "$LANG_CHOICE" = "zhcn" ] || [ "$LANG_CHOICE" = "zh" ]; then
|
||||
SELECTED_LANG="zhcn"
|
||||
break
|
||||
elif [ "$LANG_CHOICE" = "2" ] || [ "$LANG_CHOICE" = "en" ] || [ "$LANG_CHOICE" = "english" ]; then
|
||||
SELECTED_LANG="en"
|
||||
break
|
||||
else
|
||||
log_error "无效的选项,请输入 1 或 2"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
log_info "已选择默认语言: $SELECTED_LANG"
|
||||
echo ""
|
||||
else
|
||||
log_info "复用已选择的默认语言: $SELECTED_LANG"
|
||||
fi
|
||||
|
||||
# 配置前端项目的 .env 文件
|
||||
|
||||
ensure_selected_lang
|
||||
log_info "配置 ECShopX_mobile-frontend 的 .env 文件..."
|
||||
configure_frontend_env "$VSHOP_DIR" "ECShopX_mobile-frontend" "$MOBILE_API_URL" "$SELECTED_PLATFORM" "$SELECTED_LANG"
|
||||
|
||||
# 检查是否已有编译产物;前端 .env 变化时必须重新编译
|
||||
if [ -d "$VSHOP_DIR/dist" ]; then
|
||||
if [ "$FRONTEND_ENV_CHANGED" = true ]; then
|
||||
log_info "检测到已有编译产物,但前端 .env 已变化,需要重新编译 H5"
|
||||
else
|
||||
log_info "检测到已有编译产物,跳过编译"
|
||||
log_info "如需重新编译,请删除 ECShopX_mobile-frontend/dist 目录"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$FRONTEND_ENV_CHANGED" = true ]; then
|
||||
log_info "复用已更新的 ECShopX_mobile-frontend .env 配置"
|
||||
else
|
||||
log_info "配置 ECShopX_mobile-frontend 的 .env 文件..."
|
||||
configure_frontend_env "$VSHOP_DIR" "ECShopX_mobile-frontend" "$MOBILE_API_URL" "$SELECTED_PLATFORM" "$SELECTED_LANG"
|
||||
fi
|
||||
|
||||
log_info "安装 npm 依赖..."
|
||||
# 使用绝对路径并先验证目录存在
|
||||
|
||||
@@ -12,9 +12,9 @@ services:
|
||||
container_name: ecshopx-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080" # Nginx (容器8080端口映射到主机8080端口)
|
||||
- "8081:8081" # Nginx H5前端服务 (容器8081端口映射到主机8081端口)
|
||||
- "8082:8082" # Nginx PC前端代理 (容器8082端口映射到主机8082端口)
|
||||
- "${ADMIN_HOST_PORT:-8080}:8080" # Nginx 管理后台/API
|
||||
- "${H5_HOST_PORT:-8081}:8081" # Nginx H5 前端服务
|
||||
- "${PC_HOST_PORT:-8082}:8082" # Nginx PC 前端代理
|
||||
volumes:
|
||||
# 挂载目录
|
||||
- ..:/data/httpd
|
||||
@@ -63,6 +63,11 @@ services:
|
||||
- NUXT_WAIT_INTERVAL=10
|
||||
# SSR 走容器内网访问 API,不影响管理后台
|
||||
- NUXT_API_BASE=http://ecshopx-dev:8080/api/h5app
|
||||
- NUXT_INTERNAL_API_BASE=http://ecshopx-dev:8080/api/h5app
|
||||
# 浏览器侧请求使用外部可访问地址,dev-setup.sh 会按安装配置传入
|
||||
- NUXT_PUBLIC_API_BASE=${NUXT_PUBLIC_API_BASE:-http://localhost:8080/api/h5app}
|
||||
# PC 装修预览允许接收的后台 origin,dev-setup.sh 会按管理后台地址传入
|
||||
- NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS=${NUXT_PUBLIC_DECORATION_ADMIN_ORIGINS:-http://localhost:8080}
|
||||
networks:
|
||||
- ecshopx-dev-network
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ http {
|
||||
fastcgi_temp_path /var/lib/nginx/tmp/fastcgi;
|
||||
uwsgi_temp_path /var/lib/nginx/tmp/uwsgi;
|
||||
scgi_temp_path /var/lib/nginx/tmp/scgi;
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
@@ -172,7 +173,8 @@ http {
|
||||
|
||||
# 代理到独立的 Web 前端 Nuxt 服务(监听3000端口)
|
||||
location / {
|
||||
proxy_pass http://ecshopx-web-frontend:3000;
|
||||
set $web_frontend http://ecshopx-web-frontend:3000;
|
||||
proxy_pass $web_frontend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
|
||||
@@ -6,6 +6,8 @@ APP_DIR="/data/httpd/ECShopX_web-frontend"
|
||||
BUILD_FILE="$APP_DIR/.output/server/index.mjs"
|
||||
LOG_FILE="/var/log/nuxt.log"
|
||||
WAIT_INTERVAL="${NUXT_WAIT_INTERVAL:-10}"
|
||||
PUBLIC_API_BASE="${NUXT_PUBLIC_API_BASE:-http://localhost:8080/api/h5app}"
|
||||
INTERNAL_API_BASE="${NUXT_INTERNAL_API_BASE:-${NUXT_API_BASE:-http://ecshopx-dev:8080/api/h5app}}"
|
||||
|
||||
export TZ="${TZ:-Asia/Shanghai}"
|
||||
export NITRO_HOST=0.0.0.0
|
||||
@@ -18,6 +20,93 @@ cd "$APP_DIR" || {
|
||||
exit 1
|
||||
}
|
||||
|
||||
start_local_api_proxy() {
|
||||
node <<'NODE_PROXY' &
|
||||
const http = require('http')
|
||||
const https = require('https')
|
||||
|
||||
const publicBase = process.env.NUXT_PUBLIC_API_BASE || 'http://localhost:8080/api/h5app'
|
||||
const internalBase =
|
||||
process.env.NUXT_INTERNAL_API_BASE ||
|
||||
process.env.NUXT_API_BASE ||
|
||||
'http://ecshopx-dev:8080/api/h5app'
|
||||
|
||||
let publicUrl
|
||||
let internalUrl
|
||||
try {
|
||||
publicUrl = new URL(publicBase)
|
||||
internalUrl = new URL(internalBase)
|
||||
} catch (error) {
|
||||
console.error(`[SSR API Proxy] API URL 解析失败: ${error.message}`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const localHosts = new Set(['localhost', '127.0.0.1', '::1'])
|
||||
if (!localHosts.has(publicUrl.hostname)) {
|
||||
console.log(`[SSR API Proxy] NUXT_PUBLIC_API_BASE=${publicBase} 不是本机地址,跳过本地转发`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const listenHost = publicUrl.hostname === '::1' ? '::1' : '127.0.0.1'
|
||||
const listenPort = Number(publicUrl.port || (publicUrl.protocol === 'https:' ? 443 : 80))
|
||||
const publicPath = publicUrl.pathname.replace(/\/+$/, '') || '/'
|
||||
const internalPath = internalUrl.pathname.replace(/\/+$/, '') || '/'
|
||||
const transport = internalUrl.protocol === 'https:' ? https : http
|
||||
|
||||
function joinTargetPath(requestPath) {
|
||||
const suffix = requestPath.startsWith(publicPath) ? requestPath.slice(publicPath.length) : requestPath
|
||||
const normalizedSuffix = suffix.startsWith('/') ? suffix : `/${suffix}`
|
||||
return `${internalPath}${normalizedSuffix}`.replace(/\/{2,}/g, '/')
|
||||
}
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
const incomingUrl = new URL(req.url || '/', publicUrl)
|
||||
const headers = { ...req.headers, host: internalUrl.host }
|
||||
const targetPath = `${joinTargetPath(incomingUrl.pathname)}${incomingUrl.search}`
|
||||
const proxyReq = transport.request(
|
||||
{
|
||||
protocol: internalUrl.protocol,
|
||||
hostname: internalUrl.hostname,
|
||||
port: internalUrl.port || (internalUrl.protocol === 'https:' ? 443 : 80),
|
||||
method: req.method,
|
||||
path: targetPath,
|
||||
headers,
|
||||
},
|
||||
(proxyRes) => {
|
||||
res.writeHead(proxyRes.statusCode || 502, proxyRes.headers)
|
||||
proxyRes.pipe(res)
|
||||
}
|
||||
)
|
||||
|
||||
proxyReq.on('error', (error) => {
|
||||
console.error(`[SSR API Proxy] ${req.method} ${targetPath} -> ${error.message}`)
|
||||
if (!res.headersSent) {
|
||||
res.writeHead(502, { 'content-type': 'text/plain; charset=utf-8' })
|
||||
}
|
||||
res.end('Bad Gateway')
|
||||
})
|
||||
|
||||
req.pipe(proxyReq)
|
||||
})
|
||||
|
||||
server.on('error', (error) => {
|
||||
if (error.code === 'EADDRINUSE') {
|
||||
console.log(`[SSR API Proxy] ${listenHost}:${listenPort} 已被占用,跳过本地转发`)
|
||||
return
|
||||
}
|
||||
console.error(`[SSR API Proxy] 启动失败: ${error.message}`)
|
||||
})
|
||||
|
||||
server.listen(listenPort, listenHost, () => {
|
||||
console.log(`[SSR API Proxy] http://${listenHost}:${listenPort}${publicPath} -> ${internalBase}`)
|
||||
})
|
||||
NODE_PROXY
|
||||
}
|
||||
|
||||
export NUXT_PUBLIC_API_BASE="$PUBLIC_API_BASE"
|
||||
export NUXT_INTERNAL_API_BASE="$INTERNAL_API_BASE"
|
||||
start_local_api_proxy
|
||||
|
||||
echo "等待 Nuxt 生产构建产物: $BUILD_FILE"
|
||||
while [ ! -f "$BUILD_FILE" ]; do
|
||||
echo "未找到构建产物,${WAIT_INTERVAL} 秒后重试(可先运行 dev-setup.sh 完成编译)..."
|
||||
|
||||
@@ -157,13 +157,112 @@ class ThemePcTemplateContentServices
|
||||
return $this->emptyDecorationContent();
|
||||
}
|
||||
|
||||
$config = $row['params'] ?? $row['config'] ?? '';
|
||||
|
||||
return [
|
||||
'id' => $row['theme_pc_template_content_id'] ?? $row['id'] ?? 0,
|
||||
'name' => $row['name'] ?? '',
|
||||
'config' => $row['params'] ?? $row['config'] ?? '',
|
||||
'config' => self::normalizeStorageUrlsForCurrentAppUrl($config),
|
||||
];
|
||||
}
|
||||
|
||||
public static function normalizeStorageUrlsForCurrentAppUrl($config, $currentAppUrl = null)
|
||||
{
|
||||
if (!is_string($config) || $config === '') {
|
||||
return $config;
|
||||
}
|
||||
|
||||
$currentOrigin = self::normalizeAppOrigin($currentAppUrl ?: config('app.url'));
|
||||
if ($currentOrigin === '') {
|
||||
return $config;
|
||||
}
|
||||
|
||||
$decoded = json_decode($config, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
||||
$decoded = self::normalizeStorageUrlsInValue($decoded, $currentOrigin);
|
||||
return json_encode($decoded, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
return self::normalizeStorageUrlsInString($config, $currentOrigin);
|
||||
}
|
||||
|
||||
private static function normalizeStorageUrlsInValue($value, $currentOrigin)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $key => $item) {
|
||||
$value[$key] = self::normalizeStorageUrlsInValue($item, $currentOrigin);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
return self::normalizeStorageUrlsInString($value, $currentOrigin);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private static function normalizeStorageUrlsInString($value, $currentOrigin)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'#https?://[^\s"\'<>()]+/storage/[^\s"\'<>()]+#',
|
||||
function ($matches) use ($currentOrigin) {
|
||||
return self::normalizeOneStorageUrl($matches[0], $currentOrigin);
|
||||
},
|
||||
$value
|
||||
);
|
||||
}
|
||||
|
||||
private static function normalizeOneStorageUrl($url, $currentOrigin)
|
||||
{
|
||||
$oldParts = parse_url($url);
|
||||
$currentParts = parse_url($currentOrigin);
|
||||
if (empty($oldParts['host']) || empty($oldParts['path']) || empty($currentParts['host'])) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
if (strpos($oldParts['path'], '/storage/') !== 0) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$oldHost = strtolower($oldParts['host']);
|
||||
$currentHost = strtolower($currentParts['host']);
|
||||
$localHosts = ['localhost', '127.0.0.1', '::1'];
|
||||
if (!in_array($oldHost, $localHosts, true) && $oldHost !== $currentHost) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$next = rtrim($currentOrigin, '/').$oldParts['path'];
|
||||
if (isset($oldParts['query'])) {
|
||||
$next .= '?'.$oldParts['query'];
|
||||
}
|
||||
if (isset($oldParts['fragment'])) {
|
||||
$next .= '#'.$oldParts['fragment'];
|
||||
}
|
||||
|
||||
return $next;
|
||||
}
|
||||
|
||||
private static function normalizeAppOrigin($url)
|
||||
{
|
||||
$url = trim((string)$url);
|
||||
if ($url === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$parts = parse_url($url);
|
||||
if (empty($parts['scheme']) || empty($parts['host'])) {
|
||||
return rtrim($url, '/');
|
||||
}
|
||||
|
||||
$origin = $parts['scheme'].'://'.$parts['host'];
|
||||
if (isset($parts['port'])) {
|
||||
$origin .= ':'.$parts['port'];
|
||||
}
|
||||
|
||||
return $origin;
|
||||
}
|
||||
|
||||
private function emptyDecorationContent()
|
||||
{
|
||||
return [
|
||||
|
||||
Reference in New Issue
Block a user