From ceaf45ef03ae7f777c384b9dadf18a3095309c1d Mon Sep 17 00:00:00 2001 From: jacy-dukang Date: Fri, 3 Jul 2026 00:58:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E9=83=A8=E7=BD=B2=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + deploy/deploy.env.example | 9 ++ deploy/deploy.sh | 124 ++++++++++++++++++++ deploy/ecosystem.config.cjs | 9 ++ deploy/enable-api-lingshi-ssl.sh | 65 +++++++++++ deploy/nginx-lingshivip-http.conf | 105 +++++++++++++++++ deploy/nginx-lingshivip.conf | 135 ++++++++++++++++++++++ deploy/remote-release.sh | 88 ++++++++++++++ deploy/sync-api-env.sh | 48 ++++++++ server/dukang-api/.env.example | 7 +- server/dukang-api/.env.production.example | 40 +++++++ 11 files changed, 633 insertions(+), 1 deletion(-) create mode 100644 deploy/deploy.env.example create mode 100644 deploy/deploy.sh create mode 100644 deploy/enable-api-lingshi-ssl.sh create mode 100644 deploy/nginx-lingshivip-http.conf create mode 100644 deploy/nginx-lingshivip.conf create mode 100644 deploy/remote-release.sh create mode 100644 deploy/sync-api-env.sh create mode 100644 server/dukang-api/.env.production.example diff --git a/.gitignore b/.gitignore index 27bb47a..7bcbf3e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,10 @@ dist/ *.log .env .env.local +server/dukang-api/.env +server/dukang-api/.env.development +server/dukang-api/.env.production +deploy/deploy.env .DS_Store coverage/ *.tsbuildinfo diff --git a/deploy/deploy.env.example b/deploy/deploy.env.example new file mode 100644 index 0000000..cc0c8fe --- /dev/null +++ b/deploy/deploy.env.example @@ -0,0 +1,9 @@ +# 复制为 deploy.env 后填写(deploy.env 勿提交) +DEPLOY_HOST=your.server.ip.or.domain +DEPLOY_USER=root +DEPLOY_PORT=22 +# DEPLOY_SSH_KEY=~/.ssh/id_rsa + +APP_ROOT=/opt/dukang-haoke +GIT_REMOTE=origin +GIT_BRANCH=main diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100644 index 0000000..f65eab4 --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +# 本地执行:SSH 到服务器拉代码并发布 +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ENV_FILE="$SCRIPT_DIR/deploy.env" + +DEPLOY_HOST="" +DEPLOY_USER="root" +DEPLOY_PORT="22" +DEPLOY_SSH_KEY="" +APP_ROOT="/opt/dukang-haoke" +GIT_REMOTE="origin" +GIT_BRANCH="main" + +CHECK_ONLY=false +RELEASE_ARGS=() + +usage() { + cat <<'EOF' +用法: deploy/deploy.sh [选项] [-- 远程 release 参数] + +环境配置: deploy/deploy.env(可复制 deploy.env.example) + +选项: + --host HOST 服务器地址(覆盖 deploy.env) + --user USER SSH 用户,默认 root + --port PORT SSH 端口,默认 22 + --key PATH SSH 私钥路径 + --branch BRANCH 发布分支,默认 main + --check 仅 SSH 连接并查看 PM2 / 端口状态 + -h, --help 显示帮助 + +示例: + ./deploy/deploy.sh + ./deploy/deploy.sh --branch main + ./deploy/deploy.sh -- --skip-db + ./deploy/deploy.sh -- --seed --accept-data-loss +EOF +} + +load_env() { + if [[ -f "$ENV_FILE" ]]; then + # shellcheck disable=SC1090 + source "$ENV_FILE" + fi +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --host) DEPLOY_HOST="$2"; shift 2 ;; + --user) DEPLOY_USER="$2"; shift 2 ;; + --port) DEPLOY_PORT="$2"; shift 2 ;; + --key) DEPLOY_SSH_KEY="$2"; shift 2 ;; + --branch) GIT_BRANCH="$2"; shift 2 ;; + --check) CHECK_ONLY=true; shift ;; + -h|--help) usage; exit 0 ;; + --) + shift + RELEASE_ARGS=("$@") + break + ;; + *) RELEASE_ARGS+=("$1"); shift ;; + esac +done + +load_env + +if [[ -z "$DEPLOY_HOST" ]]; then + echo "错误: 未配置 DEPLOY_HOST。请创建 deploy/deploy.env 或使用 --host" >&2 + exit 1 +fi + +SSH_OPTS=(-o "StrictHostKeyChecking=accept-new" -p "$DEPLOY_PORT") +if [[ -n "$DEPLOY_SSH_KEY" ]]; then + SSH_OPTS+=(-i "$DEPLOY_SSH_KEY") +fi +TARGET="${DEPLOY_USER}@${DEPLOY_HOST}" + +run_remote() { + ssh "${SSH_OPTS[@]}" "$TARGET" "$@" +} + +if [[ "$CHECK_ONLY" == true ]]; then + echo "==> 检查 $TARGET" + run_remote "pm2 list; ss -tlnp | grep -E '809[0-3]' || true" + exit 0 +fi + +REMOTE_RELEASE_ARGS="" +if [[ ${#RELEASE_ARGS[@]} -gt 0 ]]; then + REMOTE_RELEASE_ARGS="${RELEASE_ARGS[*]}" +fi + +echo "==> 发布到 $TARGET ($GIT_BRANCH)" + +run_remote bash -s <&2 + exit 1 +fi + +echo "==> 拉取代码" +git fetch "\$GIT_REMOTE" +git checkout "\$GIT_BRANCH" +git pull --ff-only "\$GIT_REMOTE" "\$GIT_BRANCH" + +if [[ ! -f deploy/remote-release.sh ]]; then + echo "错误: deploy/remote-release.sh 不存在,请先推送部署脚本到仓库" >&2 + exit 1 +fi + +bash deploy/remote-release.sh \$RELEASE_ARGS +EOF + +echo "==> 本地发布命令已完成" diff --git a/deploy/ecosystem.config.cjs b/deploy/ecosystem.config.cjs index 8f8dce5..22f5798 100644 --- a/deploy/ecosystem.config.cjs +++ b/deploy/ecosystem.config.cjs @@ -41,5 +41,14 @@ module.exports = { instances: 1, exec_mode: 'fork', }, + { + name: 'dukang-admin-web', + cwd: `${APP_ROOT}/apps/admin-web`, + script: 'npx', + args: 'serve -s dist -l 8094', + interpreter: 'none', + instances: 1, + exec_mode: 'fork', + }, ], }; diff --git a/deploy/enable-api-lingshi-ssl.sh b/deploy/enable-api-lingshi-ssl.sh new file mode 100644 index 0000000..e028a91 --- /dev/null +++ b/deploy/enable-api-lingshi-ssl.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# 为 api.lingshivip.cn 申请 DNS 证书并启用 HTTPS +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SSL_DIR="/etc/nginx/ssl/api.lingshivip.cn" +NGINX_CONF="/etc/nginx/conf.d/dukang-lingshivip.conf" + +mkdir -p "$SSL_DIR" /var/www/certbot/.well-known/acme-challenge + +echo "==> 1. 发起 DNS 验证(需先在 DNSPod 添加 TXT 记录)" +~/.acme.sh/acme.sh --issue -d api.lingshivip.cn --dns \ + --yes-I-know-dns-manual-mode-enough-go-ahead-please \ + --server letsencrypt || true + +echo "" +echo "请在 DNSPod 添加上述 TXT 记录后,执行:" +echo " $0 --renew" +echo "" + +if [[ "${1:-}" != "--renew" ]]; then + exit 0 +fi + +echo "==> 2. 继续 DNS 验证并签发证书" +~/.acme.sh/acme.sh --renew -d api.lingshivip.cn --yes-I-know-dns-manual-mode-enough-go-ahead-please + +echo "==> 3. 安装证书到 nginx 目录" +install -m 644 ~/.acme.sh/api.lingshivip.cn_ecc/fullchain.cer "$SSL_DIR/fullchain.cer" +install -m 600 ~/.acme.sh/api.lingshivip.cn_ecc/api.lingshivip.cn.key "$SSL_DIR/api.lingshivip.cn.key" + +echo "==> 4. 更新 nginx 并重载" +cp "$SCRIPT_DIR/nginx-lingshivip.conf" "$NGINX_CONF" +python3 - <<'PY' +from pathlib import Path +p = Path("/etc/nginx/conf.d/dukang-lingshivip.conf") +text = p.read_text() +old = """ location / { + proxy_pass http://127.0.0.1:8090; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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 Connection ""; + } +} + +# API 独立域名 HTTPS""" +new = """ location / { + return 301 https://$host$request_uri; + } +} + +# API 独立域名 HTTPS""" +if old not in text: + raise SystemExit("nginx api http redirect patch failed") +p.write_text(text.replace(old, new, 1)) +PY + +nginx -t +systemctl reload nginx + +echo "==> 5. 健康检查" +curl -sf -o /dev/null -w "api-https:%{http_code}\n" https://api.lingshivip.cn/api/v1/health +echo "==> api.lingshivip.cn HTTPS 已启用" diff --git a/deploy/nginx-lingshivip-http.conf b/deploy/nginx-lingshivip-http.conf new file mode 100644 index 0000000..4da8d6a --- /dev/null +++ b/deploy/nginx-lingshivip-http.conf @@ -0,0 +1,105 @@ +# 证书申请期间 — lingshivip.cn HTTP(api HTTPS 证书就绪后换 nginx-lingshivip.conf) + +map $host $dukang_lingshi_port { + user.lingshivip.cn 8091; + shop.lingshivip.cn 8092; + partner.lingshivip.cn 8093; + admin.lingshivip.cn 8094; +} + +server { + listen 80; + server_name user.lingshivip.cn shop.lingshivip.cn partner.lingshivip.cn; + + location ^~ /.well-known/acme-challenge/ { + root /var/www/certbot; + default_type "text/plain"; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 80; + server_name api.lingshivip.cn; + + location ^~ /.well-known/acme-challenge/ { + root /var/www/certbot; + default_type "text/plain"; + } + + location / { + proxy_pass http://127.0.0.1:8090; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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 Connection ""; + } +} + +server { + listen 80; + server_name admin.lingshivip.cn; + + location ^~ /.well-known/acme-challenge/ { + root /var/www/certbot; + default_type "text/plain"; + } + + location /api/ { + proxy_pass http://127.0.0.1:8090; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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 Connection ""; + } + + location / { + proxy_pass http://127.0.0.1:8094; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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; + } +} + +server { + listen 443 ssl; + server_name user.lingshivip.cn shop.lingshivip.cn partner.lingshivip.cn; + + access_log /var/log/nginx/dukang/lingshi.access.log main; + error_log /var/log/nginx/dukang/lingshi.error.log warn; + + ssl_certificate /etc/letsencrypt/live/user.lingshivip.cn/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/user.lingshivip.cn/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + + client_max_body_size 20m; + + location /api/ { + proxy_pass http://127.0.0.1:8090; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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 Connection ""; + } + + location / { + proxy_pass http://127.0.0.1:$dukang_lingshi_port; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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; + } +} diff --git a/deploy/nginx-lingshivip.conf b/deploy/nginx-lingshivip.conf new file mode 100644 index 0000000..1efb64b --- /dev/null +++ b/deploy/nginx-lingshivip.conf @@ -0,0 +1,135 @@ +# 杜康好客 — lingshivip.cn 五端 + API +# API 8090;H5 8091/8092/8093;admin-web 8094 + +map $host $dukang_lingshi_port { + user.lingshivip.cn 8091; + shop.lingshivip.cn 8092; + partner.lingshivip.cn 8093; + admin.lingshivip.cn 8094; +} + +# HTTP → HTTPS(H5 三端) +server { + listen 80; + server_name user.lingshivip.cn shop.lingshivip.cn partner.lingshivip.cn; + + location ^~ /.well-known/acme-challenge/ { + root /var/www/certbot; + default_type "text/plain"; + } + + location / { + return 301 https://$host$request_uri; + } +} + +# API:证书就绪前 HTTP 直连;就绪后改走 HTTPS server +server { + listen 80; + server_name api.lingshivip.cn; + + location ^~ /.well-known/acme-challenge/ { + root /var/www/certbot; + default_type "text/plain"; + } + + location / { + proxy_pass http://127.0.0.1:8090; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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 Connection ""; + } +} + +# admin 待 DNS + 证书就绪前先走 HTTP +server { + listen 80; + server_name admin.lingshivip.cn; + + location ^~ /.well-known/acme-challenge/ { + root /var/www/certbot; + default_type "text/plain"; + } + + location /api/ { + proxy_pass http://127.0.0.1:8090; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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 Connection ""; + } + + location / { + proxy_pass http://127.0.0.1:8094; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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; + } +} + +# API 独立域名 HTTPS(证书:/etc/nginx/ssl/api.lingshivip.cn/) +server { + listen 443 ssl; + server_name api.lingshivip.cn; + + access_log /var/log/nginx/dukang/lingshi-api.access.log main; + error_log /var/log/nginx/dukang/lingshi-api.error.log warn; + + ssl_certificate /etc/nginx/ssl/api.lingshivip.cn/fullchain.cer; + ssl_certificate_key /etc/nginx/ssl/api.lingshivip.cn/api.lingshivip.cn.key; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + + client_max_body_size 20m; + + location / { + proxy_pass http://127.0.0.1:8090; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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 Connection ""; + } +} + +server { + listen 443 ssl; + server_name user.lingshivip.cn shop.lingshivip.cn partner.lingshivip.cn; + + access_log /var/log/nginx/dukang/lingshi.access.log main; + error_log /var/log/nginx/dukang/lingshi.error.log warn; + + ssl_certificate /etc/letsencrypt/live/user.lingshivip.cn/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/user.lingshivip.cn/privkey.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + + client_max_body_size 20m; + + location /api/ { + proxy_pass http://127.0.0.1:8090; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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 Connection ""; + } + + location / { + proxy_pass http://127.0.0.1:$dukang_lingshi_port; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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; + } +} diff --git a/deploy/remote-release.sh b/deploy/remote-release.sh new file mode 100644 index 0000000..b574373 --- /dev/null +++ b/deploy/remote-release.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# 在服务器上执行:拉取后的常规发版(不覆盖 .env、默认不 seed) +set -euo pipefail + +APP_ROOT="${APP_ROOT:-/opt/dukang-haoke}" +DEPLOY_DIR="$APP_ROOT/deploy" + +SKIP_BUILD=false +SKIP_DB=false +RUN_SEED=false +DB_PUSH_ACCEPT_DATA_LOSS=false + +usage() { + cat <<'EOF' +用法: remote-release.sh [选项] + + --skip-build 跳过 pnpm build(仅重启 PM2) + --skip-db 跳过 Prisma generate / db push + --seed 执行 prisma:seed(默认不执行) + --accept-data-loss prisma db push 时允许数据丢失(慎用) + -h, --help 显示帮助 +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --skip-build) SKIP_BUILD=true ;; + --skip-db) SKIP_DB=true ;; + --seed) RUN_SEED=true ;; + --accept-data-loss) DB_PUSH_ACCEPT_DATA_LOSS=true ;; + -h|--help) usage; exit 0 ;; + *) echo "未知参数: $1" >&2; usage; exit 1 ;; + esac + shift +done + +cd "$APP_ROOT" + +echo "==> 1. 启用 pnpm" +corepack enable 2>/dev/null || true +corepack prepare pnpm@11.2.2 --activate 2>/dev/null || true + +echo "==> 2. 安装依赖" +export NODE_OPTIONS="${NODE_OPTIONS:---max-old-space-size=2048}" +pnpm install --frozen-lockfile 2>/dev/null || pnpm install + +if [[ "$SKIP_BUILD" == false ]]; then + echo "==> 3. 构建" + pnpm build +else + echo "==> 3. 跳过构建" +fi + +if [[ "$SKIP_DB" == false ]]; then + echo "==> 4. 数据库 schema 同步" + cd "$APP_ROOT/server/dukang-api" + pnpm prisma:generate + if [[ "$DB_PUSH_ACCEPT_DATA_LOSS" == true ]]; then + npx prisma db push --accept-data-loss + else + npx prisma db push + fi + if [[ "$RUN_SEED" == true ]]; then + echo "==> 执行 seed" + pnpm prisma:seed + fi +else + echo "==> 4. 跳过数据库" +fi + +echo "==> 5. 重启 PM2" +if pm2 describe dukang-api &>/dev/null; then + pm2 restart dukang-api dukang-h5-user dukang-h5-shop dukang-h5-partner +else + pm2 start "$DEPLOY_DIR/ecosystem.config.cjs" +fi +pm2 save + +echo "==> 6. 健康检查" +sleep 2 +pm2 list +ss -tlnp | grep -E '809[0-3]' || true +curl -sf -o /dev/null -w "h5-user: %{http_code}\n" http://127.0.0.1:8091/ || echo "h5-user: FAIL" +curl -sf -o /dev/null -w "api: %{http_code}\n" http://127.0.0.1:8090/api/v1/health 2>/dev/null \ + || curl -sf -o /dev/null -w "api: %{http_code}\n" http://127.0.0.1:8090/ \ + || echo "api: FAIL" + +echo "==> 发版完成" diff --git a/deploy/sync-api-env.sh b/deploy/sync-api-env.sh new file mode 100644 index 0000000..6f1ee62 --- /dev/null +++ b/deploy/sync-api-env.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# 同步 API 环境变量到服务器 +# 用法: deploy/sync-api-env.sh [development|production] +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +ENV_FILE="$REPO_ROOT/deploy/deploy.env" + +DEPLOY_HOST="" +DEPLOY_USER="root" +DEPLOY_PORT="22" +DEPLOY_SSH_KEY="" +APP_ROOT="/opt/dukang-haoke" + +TARGET="${1:-development}" +case "$TARGET" in + development|dev) SRC_ENV="$REPO_ROOT/server/dukang-api/.env.development" ;; + production|prod) SRC_ENV="$REPO_ROOT/server/dukang-api/.env.production" ;; + *) + echo "用法: $0 [development|production]" >&2 + exit 1 + ;; +esac + +if [[ -f "$ENV_FILE" ]]; then + # shellcheck disable=SC1090 + source "$ENV_FILE" +fi + +if [[ ! -f "$SRC_ENV" ]]; then + echo "错误: 未找到 $SRC_ENV" >&2 + exit 1 +fi + +if [[ -z "$DEPLOY_HOST" ]]; then + echo "错误: 未配置 DEPLOY_HOST(deploy/deploy.env)" >&2 + exit 1 +fi + +SSH_OPTS=(-o "StrictHostKeyChecking=accept-new" -p "$DEPLOY_PORT") +[[ -n "$DEPLOY_SSH_KEY" ]] && SSH_OPTS+=(-i "$DEPLOY_SSH_KEY") +REMOTE="${DEPLOY_USER}@${DEPLOY_HOST}" + +echo "==> 同步 $TARGET 环境到 $REMOTE:$APP_ROOT/server/dukang-api/.env" +scp "${SSH_OPTS[@]}" "$SRC_ENV" "$REMOTE:$APP_ROOT/server/dukang-api/.env" +ssh "${SSH_OPTS[@]}" "$REMOTE" "pm2 restart dukang-api && sleep 2 && curl -sf -o /dev/null -w 'api-health:%{http_code}\n' http://127.0.0.1:8090/api/v1/health" +echo "==> 完成" diff --git a/server/dukang-api/.env.example b/server/dukang-api/.env.example index cb14b88..4884396 100644 --- a/server/dukang-api/.env.example +++ b/server/dukang-api/.env.example @@ -1,3 +1,8 @@ +# 杜康 API 环境变量模板 +# 开发:cp .env.development.example .env.development → 本地再 cp 为 .env(改 PORT/DATABASE_URL) +# 生产:cp .env.production.example .env.production +# 服务器同步:bash deploy/sync-api-env.sh development|production + DATABASE_URL="mysql://root:root@localhost:3306/dukang_haoke" REDIS_URL="redis://localhost:6379" JWT_SECRET="dukang-prev1-dev-secret-change-in-prod" @@ -46,4 +51,4 @@ OSS_UPLOAD_EXPIRE_SECONDS=900 # 单文件大小上限(字节,默认 10MB) OSS_MAX_UPLOAD_BYTES=10485760 # 浏览器直传 OSS 时需配置 Bucket CORS;运行 pnpm oss:cors 或控制台手动添加 -# OSS_CORS_ORIGINS=http://localhost:5173,http://localhost:5174,http://localhost:5175,https://your-domain.com \ No newline at end of file +# OSS_CORS_ORIGINS=http://localhost:5173,http://localhost:5174,http://localhost:5175,https://your-domain.com diff --git a/server/dukang-api/.env.production.example b/server/dukang-api/.env.production.example new file mode 100644 index 0000000..ecba97a --- /dev/null +++ b/server/dukang-api/.env.production.example @@ -0,0 +1,40 @@ +# 生产环境(真实第三方,关闭 Mock) +# 用法:cp .env.production.example .env.production 后填写密钥 +# 服务器:bash deploy/sync-api-env.sh production + +NODE_ENV=production + +DATABASE_URL="mysql://dukang:CHANGE_ME@localhost:3306/dukang_haoke" +REDIS_URL="redis://localhost:6379" +JWT_SECRET="CHANGE_ME-strong-random-secret" +JWT_EXPIRES_IN="7d" +PORT=8090 + +MOCK_SMS=false +MOCK_SMS_CODE= +MOCK_PAY=false +MOCK_DELIVERY_AUTO=false +AUTO_APPROVE_STORE=false + +TRUST_PROXY=true + +WECHAT_AUTH_ENABLED=true +WECHAT_PAY_ENABLED=true +WX_APP_ID= +WX_APP_SECRET= +WX_MCH_ID= +WX_MCH_SERIAL_NO= +WX_MCH_PRIVATE_KEY= +WX_API_V3_KEY= +WX_PLATFORM_CERT= +WX_PAY_NOTIFY_URL=https://api.lingshivip.cn/api/v1/callbacks/wechat/pay + +OSS_ENABLED=true +OSS_ACCESS_KEY_ID= +OSS_ACCESS_KEY_SECRET= +OSS_BUCKET=dukang-prod +OSS_REGION=oss-cn-beijing +OSS_CDN_BASE=https://lingshivip.cn +OSS_UPLOAD_PREFIX=uploads +OSS_UPLOAD_EXPIRE_SECONDS=900 +OSS_MAX_UPLOAD_BYTES=10485760