merge(dev_jacy): server uses .env.production only
This commit is contained in:
@@ -7,6 +7,7 @@ dist/
|
|||||||
server/dukang-api/.env
|
server/dukang-api/.env
|
||||||
server/dukang-api/.env.development
|
server/dukang-api/.env.development
|
||||||
server/dukang-api/.env.production
|
server/dukang-api/.env.production
|
||||||
|
server/dukang-api/.env.production.local
|
||||||
deploy/deploy.env
|
deploy/deploy.env
|
||||||
.DS_Store
|
.DS_Store
|
||||||
coverage/
|
coverage/
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function formatMoney(n: number) {
|
|||||||
function formatScanError(e: unknown): string {
|
function formatScanError(e: unknown): string {
|
||||||
const msg = e instanceof Error ? e.message : '扫码失败,请重试';
|
const msg = e instanceof Error ? e.message : '扫码失败,请重试';
|
||||||
if (/invalid signature/i.test(msg)) {
|
if (/invalid signature/i.test(msg)) {
|
||||||
return '微信 JSSDK 签名校验失败:请确认公众号已配置 JS 接口安全域名为 m.runxian.top,并刷新页面后重试';
|
return '微信 JSSDK 签名校验失败:请确认公众号已配置 JS 接口安全域名为 user.runxian.top,并刷新页面后重试';
|
||||||
}
|
}
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,24 +50,18 @@ fi
|
|||||||
|
|
||||||
log "PULL $LOCAL_SHA -> $REMOTE_SHA"
|
log "PULL $LOCAL_SHA -> $REMOTE_SHA"
|
||||||
|
|
||||||
# 保留生产密钥,避免 reset 覆盖
|
# 保留服务器生产密钥,避免 reset 覆盖
|
||||||
ENV_BACKUP="$(mktemp)"
|
ENV_BACKUP="$(mktemp)"
|
||||||
API_ENV_BACKUP="$(mktemp)"
|
|
||||||
API_ENV_DEV_BACKUP="$(mktemp)"
|
|
||||||
API_ENV_PROD_BACKUP="$(mktemp)"
|
API_ENV_PROD_BACKUP="$(mktemp)"
|
||||||
cp "$ENV_FILE" "$ENV_BACKUP" 2>/dev/null || true
|
cp "$ENV_FILE" "$ENV_BACKUP" 2>/dev/null || true
|
||||||
cp "$APP_ROOT/server/dukang-api/.env" "$API_ENV_BACKUP" 2>/dev/null || true
|
|
||||||
cp "$APP_ROOT/server/dukang-api/.env.development" "$API_ENV_DEV_BACKUP" 2>/dev/null || true
|
|
||||||
cp "$APP_ROOT/server/dukang-api/.env.production" "$API_ENV_PROD_BACKUP" 2>/dev/null || true
|
cp "$APP_ROOT/server/dukang-api/.env.production" "$API_ENV_PROD_BACKUP" 2>/dev/null || true
|
||||||
|
|
||||||
git checkout "$GIT_BRANCH"
|
git checkout "$GIT_BRANCH"
|
||||||
git reset --hard "$GIT_REMOTE/$GIT_BRANCH"
|
git reset --hard "$GIT_REMOTE/$GIT_BRANCH"
|
||||||
|
|
||||||
cp "$ENV_BACKUP" "$ENV_FILE" 2>/dev/null || true
|
cp "$ENV_BACKUP" "$ENV_FILE" 2>/dev/null || true
|
||||||
cp "$API_ENV_BACKUP" "$APP_ROOT/server/dukang-api/.env" 2>/dev/null || true
|
|
||||||
cp "$API_ENV_DEV_BACKUP" "$APP_ROOT/server/dukang-api/.env.development" 2>/dev/null || true
|
|
||||||
cp "$API_ENV_PROD_BACKUP" "$APP_ROOT/server/dukang-api/.env.production" 2>/dev/null || true
|
cp "$API_ENV_PROD_BACKUP" "$APP_ROOT/server/dukang-api/.env.production" 2>/dev/null || true
|
||||||
rm -f "$ENV_BACKUP" "$API_ENV_BACKUP" "$API_ENV_DEV_BACKUP" "$API_ENV_PROD_BACKUP"
|
rm -f "$ENV_BACKUP" "$API_ENV_PROD_BACKUP"
|
||||||
sed -i 's/\r$//' "$ENV_FILE" 2>/dev/null || true
|
sed -i 's/\r$//' "$ENV_FILE" 2>/dev/null || true
|
||||||
|
|
||||||
# 修复 Windows 换行
|
# 修复 Windows 换行
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ module.exports = {
|
|||||||
instances: 1,
|
instances: 1,
|
||||||
exec_mode: 'fork',
|
exec_mode: 'fork',
|
||||||
env: {
|
env: {
|
||||||
// 预发/联调阶段用 development;正式上线后改回 production
|
NODE_ENV: 'production',
|
||||||
NODE_ENV: 'development',
|
|
||||||
PORT: 8090,
|
PORT: 8090,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 为杜康 runxian.top 申请 Let's Encrypt 并切换 HTTPS 配置
|
# 为杜康 runxian.top 申请 Let's Encrypt 并切换 HTTPS 配置
|
||||||
# 三端 H5 统一入口 m.runxian.top(微信网页授权单域名)
|
# 三端 H5 统一入口 user.runxian.top/{user,shop,partner}/
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
DOMAINS=(
|
DOMAINS=(
|
||||||
m.runxian.top
|
|
||||||
user.runxian.top
|
user.runxian.top
|
||||||
shop.runxian.top
|
shop.runxian.top
|
||||||
partner.runxian.top
|
partner.runxian.top
|
||||||
@@ -27,8 +26,7 @@ mkdir -p /var/www/certbot /var/log/nginx/dukang
|
|||||||
|
|
||||||
echo "==> 申请证书..."
|
echo "==> 申请证书..."
|
||||||
certbot certonly --webroot -w /var/www/certbot \
|
certbot certonly --webroot -w /var/www/certbot \
|
||||||
--cert-name m.runxian.top \
|
--cert-name user.runxian.top \
|
||||||
-d m.runxian.top \
|
|
||||||
-d user.runxian.top \
|
-d user.runxian.top \
|
||||||
-d shop.runxian.top \
|
-d shop.runxian.top \
|
||||||
-d partner.runxian.top \
|
-d partner.runxian.top \
|
||||||
@@ -46,14 +44,14 @@ systemctl reload nginx
|
|||||||
|
|
||||||
echo "==> 验证..."
|
echo "==> 验证..."
|
||||||
for url in \
|
for url in \
|
||||||
https://m.runxian.top/user/ \
|
https://user.runxian.top/user/ \
|
||||||
https://m.runxian.top/shop/ \
|
https://user.runxian.top/shop/ \
|
||||||
https://m.runxian.top/partner/ \
|
https://user.runxian.top/partner/ \
|
||||||
https://user.runxian.top/ \
|
https://shop.runxian.top/ \
|
||||||
https://webadmin.runxian.top/ \
|
https://webadmin.runxian.top/ \
|
||||||
https://dkapi.runxian.top/api/v1/health; do
|
https://dkapi.runxian.top/api/v1/health; do
|
||||||
code="$(curl -sf -o /dev/null -w '%{http_code}' "$url" || echo fail)"
|
code="$(curl -sf -o /dev/null -w '%{http_code}' "$url" || echo fail)"
|
||||||
echo " $url -> $code"
|
echo " $url -> $code"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "==> runxian.top 杜康域名 HTTPS 已启用(H5 统一入口 m.runxian.top)"
|
echo "==> runxian.top 杜康域名 HTTPS 已启用(H5 统一入口 user.runxian.top)"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# 杜康好客 — runxian.top HTTPS
|
# 杜康好客 — runxian.top HTTPS
|
||||||
# 执行: deploy/enable-runxian-dukang-ssl.sh
|
# 执行: deploy/enable-runxian-dukang-ssl.sh
|
||||||
# 三端 H5 统一入口 m.runxian.top/{user,shop,partner}/
|
# 三端 H5 统一入口 user.runxian.top/{user,shop,partner}/
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name m.runxian.top user.runxian.top shop.runxian.top partner.runxian.top;
|
server_name user.runxian.top shop.runxian.top partner.runxian.top m.runxian.top;
|
||||||
|
|
||||||
location ^~ /.well-known/acme-challenge/ {
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
root /var/www/certbot;
|
root /var/www/certbot;
|
||||||
@@ -12,7 +12,7 @@ server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
return 301 https://$host$request_uri;
|
return 301 https://user.runxian.top$request_uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,11 +20,22 @@ server {
|
|||||||
listen 443 ssl;
|
listen 443 ssl;
|
||||||
server_name m.runxian.top;
|
server_name m.runxian.top;
|
||||||
|
|
||||||
access_log /var/log/nginx/dukang/runxian-m.access.log main;
|
ssl_certificate /etc/letsencrypt/live/user.runxian.top/fullchain.pem;
|
||||||
error_log /var/log/nginx/dukang/runxian-m.error.log warn;
|
ssl_certificate_key /etc/letsencrypt/live/user.runxian.top/privkey.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/m.runxian.top/fullchain.pem;
|
return 301 https://user.runxian.top$request_uri;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/m.runxian.top/privkey.pem;
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name user.runxian.top;
|
||||||
|
|
||||||
|
access_log /var/log/nginx/dukang/runxian-h5.access.log main;
|
||||||
|
error_log /var/log/nginx/dukang/runxian-h5.error.log warn;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/user.runxian.top/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/user.runxian.top/privkey.pem;
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
ssl_prefer_server_ciphers on;
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
@@ -51,52 +62,61 @@ server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
location /user/ {
|
location /user/ {
|
||||||
alias /opt/dukang-haoke/apps/h5-user/dist/;
|
rewrite ^/user/(.*)$ /$1 break;
|
||||||
try_files $uri $uri/ index.html;
|
proxy_pass http://127.0.0.1:8091;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /shop/ {
|
location /shop/ {
|
||||||
alias /opt/dukang-haoke/apps/h5-shop/dist/;
|
rewrite ^/shop/(.*)$ /$1 break;
|
||||||
try_files $uri $uri/ index.html;
|
proxy_pass http://127.0.0.1:8092;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /partner/ {
|
location /partner/ {
|
||||||
alias /opt/dukang-haoke/apps/h5-partner/dist/;
|
rewrite ^/partner/(.*)$ /$1 break;
|
||||||
try_files $uri $uri/ index.html;
|
proxy_pass http://127.0.0.1:8093;
|
||||||
|
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 {
|
# 旧 C 端根路径书签(无 /user 前缀)→ /user/...
|
||||||
listen 443 ssl;
|
location / {
|
||||||
server_name user.runxian.top;
|
return 302 /user$request_uri;
|
||||||
|
}
|
||||||
ssl_certificate /etc/letsencrypt/live/m.runxian.top/fullchain.pem;
|
|
||||||
ssl_certificate_key /etc/letsencrypt/live/m.runxian.top/privkey.pem;
|
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
|
||||||
|
|
||||||
return 301 https://m.runxian.top/user$request_uri;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 443 ssl;
|
listen 443 ssl;
|
||||||
server_name shop.runxian.top;
|
server_name shop.runxian.top;
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/m.runxian.top/fullchain.pem;
|
ssl_certificate /etc/letsencrypt/live/user.runxian.top/fullchain.pem;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/m.runxian.top/privkey.pem;
|
ssl_certificate_key /etc/letsencrypt/live/user.runxian.top/privkey.pem;
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
return 301 https://m.runxian.top/shop$request_uri;
|
return 301 https://user.runxian.top/shop$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 443 ssl;
|
listen 443 ssl;
|
||||||
server_name partner.runxian.top;
|
server_name partner.runxian.top;
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/m.runxian.top/fullchain.pem;
|
ssl_certificate /etc/letsencrypt/live/user.runxian.top/fullchain.pem;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/m.runxian.top/privkey.pem;
|
ssl_certificate_key /etc/letsencrypt/live/user.runxian.top/privkey.pem;
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
return 301 https://m.runxian.top/partner$request_uri;
|
return 301 https://user.runxian.top/partner$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
@@ -108,6 +128,8 @@ server {
|
|||||||
default_type "text/plain";
|
default_type "text/plain";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
include /opt/dukang-haoke/deploy/nginx-deploy-webhook.conf;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
return 301 https://$host$request_uri;
|
return 301 https://$host$request_uri;
|
||||||
}
|
}
|
||||||
@@ -120,13 +142,15 @@ server {
|
|||||||
access_log /var/log/nginx/dukang/runxian-api.access.log main;
|
access_log /var/log/nginx/dukang/runxian-api.access.log main;
|
||||||
error_log /var/log/nginx/dukang/runxian-api.error.log warn;
|
error_log /var/log/nginx/dukang/runxian-api.error.log warn;
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/m.runxian.top/fullchain.pem;
|
ssl_certificate /etc/letsencrypt/live/user.runxian.top/fullchain.pem;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/m.runxian.top/privkey.pem;
|
ssl_certificate_key /etc/letsencrypt/live/user.runxian.top/privkey.pem;
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
ssl_prefer_server_ciphers on;
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
client_max_body_size 20m;
|
client_max_body_size 20m;
|
||||||
|
|
||||||
|
include /opt/dukang-haoke/deploy/nginx-deploy-webhook.conf;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://127.0.0.1:8090;
|
proxy_pass http://127.0.0.1:8090;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
@@ -159,8 +183,8 @@ server {
|
|||||||
access_log /var/log/nginx/dukang/runxian-admin.access.log main;
|
access_log /var/log/nginx/dukang/runxian-admin.access.log main;
|
||||||
error_log /var/log/nginx/dukang/runxian-admin.error.log warn;
|
error_log /var/log/nginx/dukang/runxian-admin.error.log warn;
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/m.runxian.top/fullchain.pem;
|
ssl_certificate /etc/letsencrypt/live/user.runxian.top/fullchain.pem;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/m.runxian.top/privkey.pem;
|
ssl_certificate_key /etc/letsencrypt/live/user.runxian.top/privkey.pem;
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
ssl_prefer_server_ciphers on;
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
# 杜康好客 — runxian.top(HTTP)
|
# 杜康好客 — runxian.top(HTTP)
|
||||||
# 三端 H5 统一入口 m.runxian.top/{user,shop,partner}/(微信网页授权单域名)
|
# 三端 H5 统一入口 user.runxian.top/{user,shop,partner}/(微信网页授权单域名)
|
||||||
# 旧子域名 user/shop/partner 301 到统一入口;webadmin → 8094;dkapi → 8090
|
# shop/partner 子域名 301 到 user.runxian.top 路径;webadmin → 8094;dkapi → 8090
|
||||||
|
|
||||||
# --- 统一 H5 入口(证书就绪前 HTTP;HTTPS 见 dukang-runxian-ssl.conf)---
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name m.runxian.top;
|
server_name user.runxian.top;
|
||||||
|
|
||||||
access_log /var/log/nginx/dukang/runxian-m.access.log main;
|
access_log /var/log/nginx/dukang/runxian-h5.access.log main;
|
||||||
error_log /var/log/nginx/dukang/runxian-m.error.log warn;
|
error_log /var/log/nginx/dukang/runxian-h5.error.log warn;
|
||||||
|
|
||||||
client_max_body_size 20m;
|
client_max_body_size 20m;
|
||||||
|
|
||||||
@@ -38,41 +37,58 @@ server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
location /user/ {
|
location /user/ {
|
||||||
alias /opt/dukang-haoke/apps/h5-user/dist/;
|
rewrite ^/user/(.*)$ /$1 break;
|
||||||
try_files $uri $uri/ index.html;
|
proxy_pass http://127.0.0.1:8091;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /shop/ {
|
location /shop/ {
|
||||||
alias /opt/dukang-haoke/apps/h5-shop/dist/;
|
rewrite ^/shop/(.*)$ /$1 break;
|
||||||
try_files $uri $uri/ index.html;
|
proxy_pass http://127.0.0.1:8092;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /partner/ {
|
location /partner/ {
|
||||||
alias /opt/dukang-haoke/apps/h5-partner/dist/;
|
rewrite ^/partner/(.*)$ /$1 break;
|
||||||
try_files $uri $uri/ index.html;
|
proxy_pass http://127.0.0.1:8093;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# --- 旧 H5 子域名 → 统一入口(保留书签/推广链接)---
|
location / {
|
||||||
server {
|
return 302 /user$request_uri;
|
||||||
listen 80;
|
}
|
||||||
server_name user.runxian.top;
|
|
||||||
return 301 http://m.runxian.top/user$request_uri;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name shop.runxian.top;
|
server_name shop.runxian.top;
|
||||||
return 301 http://m.runxian.top/shop$request_uri;
|
return 301 http://user.runxian.top/shop$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name partner.runxian.top;
|
server_name partner.runxian.top;
|
||||||
return 301 http://m.runxian.top/partner$request_uri;
|
return 301 http://user.runxian.top/partner$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name m.runxian.top;
|
||||||
|
return 301 http://user.runxian.top$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- HTTP: API ---
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name dkapi.runxian.top;
|
server_name dkapi.runxian.top;
|
||||||
@@ -98,7 +114,6 @@ server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- HTTP: admin-web ---
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name webadmin.runxian.top;
|
server_name webadmin.runxian.top;
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 续部署:m.runxian.top 统一入口 + SSL 扩展 + nginx 切换
|
# 续部署:user.runxian.top 统一路径入口 + nginx 切换
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
APP_ROOT="${APP_ROOT:-/opt/dukang-haoke}"
|
APP_ROOT="${APP_ROOT:-/opt/dukang-haoke}"
|
||||||
CERT_NAME="${CERT_CERT_NAME:-user.runxian.top}"
|
H5_BASE="https://user.runxian.top"
|
||||||
CERT_DIR="/etc/letsencrypt/live/${CERT_NAME}"
|
|
||||||
|
|
||||||
cd "$APP_ROOT"
|
cd "$APP_ROOT"
|
||||||
|
|
||||||
@@ -12,63 +11,29 @@ echo "==> 1. PM2 重启(H5 dist 已含 /user|shop|partner/ 路径)"
|
|||||||
pm2 restart dukang-api dukang-h5-user dukang-h5-shop dukang-h5-partner dukang-admin-web
|
pm2 restart dukang-api dukang-h5-user dukang-h5-shop dukang-h5-partner dukang-admin-web
|
||||||
pm2 save
|
pm2 save
|
||||||
|
|
||||||
echo "==> 2. 临时 ACME:m.runxian.top:80"
|
echo "==> 2. 安装 nginx HTTPS 配置"
|
||||||
cat > /etc/nginx/conf.d/dukang-m-acme-temp.conf <<'EOF'
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name m.runxian.top;
|
|
||||||
location ^~ /.well-known/acme-challenge/ {
|
|
||||||
root /var/www/certbot;
|
|
||||||
default_type "text/plain";
|
|
||||||
}
|
|
||||||
location / { return 404; }
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
nginx -t
|
|
||||||
systemctl reload nginx
|
|
||||||
|
|
||||||
echo "==> 3. 扩展证书 SAN:加入 m.runxian.top"
|
|
||||||
certbot certonly --webroot -w /var/www/certbot \
|
|
||||||
--expand --cert-name "$CERT_NAME" \
|
|
||||||
-d user.runxian.top \
|
|
||||||
-d shop.runxian.top \
|
|
||||||
-d partner.runxian.top \
|
|
||||||
-d webadmin.runxian.top \
|
|
||||||
-d dkapi.runxian.top \
|
|
||||||
-d m.runxian.top \
|
|
||||||
--non-interactive --agree-tos -m admin@runxian.top
|
|
||||||
|
|
||||||
echo "==> 4. 安装 nginx HTTPS 配置"
|
|
||||||
install -m 644 "$APP_ROOT/deploy/nginx-runxian-dukang-ssl.conf" /etc/nginx/conf.d/dukang-runxian.conf
|
install -m 644 "$APP_ROOT/deploy/nginx-runxian-dukang-ssl.conf" /etc/nginx/conf.d/dukang-runxian.conf
|
||||||
sed -i "s|/etc/letsencrypt/live/m.runxian.top/|${CERT_DIR}/|g" /etc/nginx/conf.d/dukang-runxian.conf
|
|
||||||
|
|
||||||
if ! grep -q nginx-deploy-webhook /etc/nginx/conf.d/dukang-runxian.conf; then
|
|
||||||
sed -i '/server_name dkapi.runxian.top;/a\ include /opt/dukang-haoke/deploy/nginx-deploy-webhook.conf;' \
|
|
||||||
/etc/nginx/conf.d/dukang-runxian.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -f /etc/nginx/conf.d/dukang-m-acme-temp.conf
|
|
||||||
nginx -t
|
nginx -t
|
||||||
systemctl reload nginx
|
systemctl reload nginx
|
||||||
|
|
||||||
echo "==> 5. 更新 USER_H5_URL"
|
echo "==> 3. 更新 USER_H5_URL"
|
||||||
ENV_FILE="$APP_ROOT/server/dukang-api/.env"
|
ENV_FILE="$APP_ROOT/server/dukang-api/.env.production"
|
||||||
if grep -q '^USER_H5_URL=' "$ENV_FILE"; then
|
if grep -q '^USER_H5_URL=' "$ENV_FILE"; then
|
||||||
sed -i 's|^USER_H5_URL=.*|USER_H5_URL=https://m.runxian.top/user|' "$ENV_FILE"
|
sed -i "s|^USER_H5_URL=.*|USER_H5_URL=${H5_BASE}/user|" "$ENV_FILE"
|
||||||
else
|
else
|
||||||
echo 'USER_H5_URL=https://m.runxian.top/user' >> "$ENV_FILE"
|
echo "USER_H5_URL=${H5_BASE}/user" >> "$ENV_FILE"
|
||||||
fi
|
fi
|
||||||
pm2 restart dukang-api
|
pm2 restart dukang-api
|
||||||
|
|
||||||
echo "==> 6. 验证"
|
echo "==> 4. 验证"
|
||||||
for url in \
|
for url in \
|
||||||
"https://m.runxian.top/user/" \
|
"${H5_BASE}/user/" \
|
||||||
"https://m.runxian.top/shop/" \
|
"${H5_BASE}/shop/" \
|
||||||
"https://m.runxian.top/partner/" \
|
"${H5_BASE}/partner/" \
|
||||||
"https://user.runxian.top/" \
|
"https://shop.runxian.top/" \
|
||||||
"https://dkapi.runxian.top/api/v1/health"; do
|
"https://dkapi.runxian.top/api/v1/health"; do
|
||||||
code="$(curl -sk -o /dev/null -w '%{http_code}' "$url" || echo fail)"
|
code="$(curl -sk -o /dev/null -w '%{http_code}' "$url" || echo fail)"
|
||||||
echo " $url -> $code"
|
echo " $url -> $code"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "==> m.runxian.top 部署完成"
|
echo "==> user.runxian.top 统一部署完成"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export NODE_OPTIONS="--max-old-space-size=1024"
|
|||||||
pnpm install --filter @dukang/api... --filter @dukang/domain --filter @dukang/shared-types
|
pnpm install --filter @dukang/api... --filter @dukang/domain --filter @dukang/shared-types
|
||||||
|
|
||||||
echo "==> env"
|
echo "==> env"
|
||||||
cat > "$APP_ROOT/server/dukang-api/.env" <<'ENV'
|
cat > "$APP_ROOT/server/dukang-api/.env.production" <<'ENV'
|
||||||
DATABASE_URL="mysql://dukang:Dukang2026!@localhost:3306/dukang_haoke"
|
DATABASE_URL="mysql://dukang:Dukang2026!@localhost:3306/dukang_haoke"
|
||||||
REDIS_URL="redis://localhost:6379"
|
REDIS_URL="redis://localhost:6379"
|
||||||
JWT_SECRET="dukang-prod-jwt-secret-2026"
|
JWT_SECRET="dukang-prod-jwt-secret-2026"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 在服务器上执行:拉取后的常规发版(不覆盖 .env、默认不 seed)
|
# 在服务器上执行:拉取后的常规发版(不覆盖 .env.production、默认不 seed)
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
APP_ROOT="${APP_ROOT:-/opt/dukang-haoke}"
|
APP_ROOT="${APP_ROOT:-/opt/dukang-haoke}"
|
||||||
@@ -46,17 +46,21 @@ pnpm approve-builds --all 2>/dev/null || true
|
|||||||
pnpm install --frozen-lockfile 2>/dev/null || pnpm install
|
pnpm install --frozen-lockfile 2>/dev/null || pnpm install
|
||||||
|
|
||||||
if [[ "$SKIP_DB" == false ]]; then
|
if [[ "$SKIP_DB" == false ]]; then
|
||||||
echo "==> 3. 数据库 schema 同步(构建前)"
|
echo "==> 3. 数据库 schema 同步(构建前,读取 .env.production)"
|
||||||
cd "$APP_ROOT/server/dukang-api"
|
cd "$APP_ROOT/server/dukang-api"
|
||||||
pnpm prisma:generate
|
if [[ ! -f .env.production ]]; then
|
||||||
|
echo "错误: 未找到 server/dukang-api/.env.production" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
node scripts/with-api-env.cjs pnpm prisma:generate
|
||||||
if [[ "$DB_PUSH_ACCEPT_DATA_LOSS" == true ]]; then
|
if [[ "$DB_PUSH_ACCEPT_DATA_LOSS" == true ]]; then
|
||||||
npx prisma db push --accept-data-loss
|
node scripts/with-api-env.cjs npx prisma db push --accept-data-loss
|
||||||
else
|
else
|
||||||
npx prisma db push
|
node scripts/with-api-env.cjs npx prisma db push
|
||||||
fi
|
fi
|
||||||
if [[ "$RUN_SEED" == true ]]; then
|
if [[ "$RUN_SEED" == true ]]; then
|
||||||
echo "==> 执行 seed"
|
echo "==> 执行 seed"
|
||||||
pnpm prisma:seed
|
node scripts/with-api-env.cjs pnpm prisma:seed
|
||||||
fi
|
fi
|
||||||
cd "$APP_ROOT"
|
cd "$APP_ROOT"
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ pnpm install --frozen-lockfile 2>/dev/null || pnpm install
|
|||||||
pnpm build
|
pnpm build
|
||||||
|
|
||||||
echo "==> 5. 写入 API 生产环境变量"
|
echo "==> 5. 写入 API 生产环境变量"
|
||||||
cat > "$APP_ROOT/server/dukang-api/.env" <<'ENV'
|
cat > "$APP_ROOT/server/dukang-api/.env.production" <<'ENV'
|
||||||
DATABASE_URL="mysql://dukang:Dukang2026!@localhost:3306/dukang_haoke"
|
DATABASE_URL="mysql://dukang:Dukang2026!@localhost:3306/dukang_haoke"
|
||||||
REDIS_URL="redis://localhost:6379"
|
REDIS_URL="redis://localhost:6379"
|
||||||
JWT_SECRET="dukang-prod-jwt-secret-2026"
|
JWT_SECRET="dukang-prod-jwt-secret-2026"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# 服务器机器相关(覆盖 .env.development 中的同名项)
|
# 服务器机器相关(可合并到 .env.production 的参考项)
|
||||||
PORT=8090
|
PORT=8090
|
||||||
DATABASE_URL="mysql://dukang:Dukang2026!@localhost:3306/dukang_haoke"
|
DATABASE_URL="mysql://dukang:Dukang2026!@localhost:3306/dukang_haoke"
|
||||||
REDIS_URL="redis://localhost:6379"
|
REDIS_URL="redis://localhost:6379"
|
||||||
|
|||||||
+11
-8
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 同步 API 环境变量到服务器
|
# 同步 API 生产环境变量到服务器 .env.production
|
||||||
# 用法: deploy/sync-api-env.sh [development|production]
|
# 用法: deploy/sync-api-env.sh [production]
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
@@ -13,12 +13,15 @@ DEPLOY_PORT="22"
|
|||||||
DEPLOY_SSH_KEY=""
|
DEPLOY_SSH_KEY=""
|
||||||
APP_ROOT="/opt/dukang-haoke"
|
APP_ROOT="/opt/dukang-haoke"
|
||||||
|
|
||||||
TARGET="${1:-development}"
|
TARGET="${1:-production}"
|
||||||
case "$TARGET" in
|
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" ;;
|
production|prod) SRC_ENV="$REPO_ROOT/server/dukang-api/.env.production" ;;
|
||||||
|
development|dev)
|
||||||
|
echo "错误: development 仅用于本地开发,请使用 .env / .env.development,不同步到服务器" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "用法: $0 [development|production]" >&2
|
echo "用法: $0 [production]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -29,7 +32,7 @@ if [[ -f "$ENV_FILE" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ! -f "$SRC_ENV" ]]; then
|
if [[ ! -f "$SRC_ENV" ]]; then
|
||||||
echo "错误: 未找到 $SRC_ENV" >&2
|
echo "错误: 未找到 $SRC_ENV(可从 .env.production.example 复制)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -42,7 +45,7 @@ SSH_OPTS=(-o "StrictHostKeyChecking=accept-new" -p "$DEPLOY_PORT")
|
|||||||
[[ -n "$DEPLOY_SSH_KEY" ]] && SSH_OPTS+=(-i "$DEPLOY_SSH_KEY")
|
[[ -n "$DEPLOY_SSH_KEY" ]] && SSH_OPTS+=(-i "$DEPLOY_SSH_KEY")
|
||||||
REMOTE="${DEPLOY_USER}@${DEPLOY_HOST}"
|
REMOTE="${DEPLOY_USER}@${DEPLOY_HOST}"
|
||||||
|
|
||||||
echo "==> 同步 $TARGET 环境到 $REMOTE:$APP_ROOT/server/dukang-api/.env"
|
echo "==> 同步 production 环境到 $REMOTE:$APP_ROOT/server/dukang-api/.env.production"
|
||||||
scp "${SSH_OPTS[@]}" "$SRC_ENV" "$REMOTE:$APP_ROOT/server/dukang-api/.env"
|
scp "${SSH_OPTS[@]}" "$SRC_ENV" "$REMOTE:$APP_ROOT/server/dukang-api/.env.production"
|
||||||
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"
|
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 "==> 完成"
|
echo "==> 完成"
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const defaultOrigins = [
|
|||||||
'http://localhost:5173/user',
|
'http://localhost:5173/user',
|
||||||
'http://localhost:5174/shop',
|
'http://localhost:5174/shop',
|
||||||
'http://localhost:5175/partner',
|
'http://localhost:5175/partner',
|
||||||
'https://m.runxian.top',
|
'https://user.runxian.top',
|
||||||
];
|
];
|
||||||
|
|
||||||
const allowedOrigin = (OSS_CORS_ORIGINS ?? defaultOrigins.join(','))
|
const allowedOrigin = (OSS_CORS_ORIGINS ?? defaultOrigins.join(','))
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# 杜康 API 环境变量模板
|
# 杜康 API 环境变量模板
|
||||||
# 本地开发:复制为 .env 后按需修改(pnpm dev:api 仅读取 .env / .env.local)
|
# 本地开发:复制为 .env 后按需修改(pnpm dev:api 仅读取 .env / .env.local)
|
||||||
# 可选 .env.local — 本机覆盖(gitignore)
|
# 可选 .env.local — 本机覆盖(gitignore)
|
||||||
# 服务器部署:bash deploy/sync-api-env.sh development|production(同步到远端 .env)
|
# 服务器:仅使用 .env.production(bash deploy/sync-api-env.sh production)
|
||||||
# .env.development / .env.production — 部署源模板,本地 dev 不自动加载
|
# .env.development — 本地参考模板,不部署到服务器
|
||||||
|
|
||||||
DATABASE_URL="mysql://root:root@localhost:3306/dukang_haoke"
|
DATABASE_URL="mysql://root:root@localhost:3306/dukang_haoke"
|
||||||
REDIS_URL="redis://localhost:6379"
|
REDIS_URL="redis://localhost:6379"
|
||||||
@@ -26,15 +26,15 @@ MOCK_WECHAT=true
|
|||||||
WX_AUTHORIZE=false
|
WX_AUTHORIZE=false
|
||||||
|
|
||||||
# C 端 H5 落地页(推广码二维码链接前缀)
|
# C 端 H5 落地页(推广码二维码链接前缀)
|
||||||
# 本地开发:http://localhost:5173/user 生产统一入口:https://m.runxian.top/user
|
# 本地开发:http://localhost:5173/user 生产统一入口:https://user.runxian.top/user
|
||||||
USER_H5_URL=http://localhost:5173/user
|
USER_H5_URL=http://localhost:5173/user
|
||||||
|
|
||||||
# 反向代理后提取真实客户端 IP(下单 IP 定位)
|
# 反向代理后提取真实客户端 IP(下单 IP 定位)
|
||||||
TRUST_PROXY=true
|
TRUST_PROXY=true
|
||||||
|
|
||||||
# 微信 SDK(生产:WECHAT_AUTH_ENABLED=true,配置 WX_APP_ID / WX_APP_SECRET)
|
# 微信 SDK(生产:WECHAT_AUTH_ENABLED=true,配置 WX_APP_ID / WX_APP_SECRET)
|
||||||
# OAuth 授权页由 /common/wechat/oauth-url 生成;三端统一入口 m.runxian.top/{user,shop,partner}。
|
# OAuth 授权页由 /common/wechat/oauth-url 生成;三端统一入口 user.runxian.top/{user,shop,partner}。
|
||||||
# 微信服务号「网页授权域名」「JS 接口安全域名」均配置 m.runxian.top(仅 1 个名额)。
|
# 微信服务号「网页授权域名」「JS 接口安全域名」均配置 user.runxian.top(仅 1 个名额)。
|
||||||
WX_APP_ID=
|
WX_APP_ID=
|
||||||
WX_APP_SECRET=
|
WX_APP_SECRET=
|
||||||
WECHAT_AUTH_ENABLED=false
|
WECHAT_AUTH_ENABLED=false
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 生产环境(真实第三方,关闭 Mock)
|
# 生产环境(真实第三方,关闭 Mock)
|
||||||
# 用途:deploy/sync-api-env.sh production 的源文件,同步到服务器 .env
|
# 用途:服务器 runtime 配置;bash deploy/sync-api-env.sh production 同步到远端 .env.production
|
||||||
# 本地开发请使用 .env(从 .env.example 复制)
|
# 本地开发请使用 .env(从 .env.example 复制),勿与生产混用
|
||||||
|
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ AUTO_APPROVE_STORE=false
|
|||||||
TRUST_PROXY=true
|
TRUST_PROXY=true
|
||||||
|
|
||||||
# C 端 H5 落地页(推广码二维码;生产统一入口)
|
# C 端 H5 落地页(推广码二维码;生产统一入口)
|
||||||
USER_H5_URL=https://m.runxian.top/user
|
USER_H5_URL=https://user.runxian.top/user
|
||||||
|
|
||||||
WECHAT_AUTH_ENABLED=true
|
WECHAT_AUTH_ENABLED=true
|
||||||
WECHAT_PAY_ENABLED=true
|
WECHAT_PAY_ENABLED=true
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { config } from 'dotenv';
|
||||||
|
import { spawnSync } from 'child_process';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
|
const apiRoot = resolve(__dirname, '..');
|
||||||
|
const envFile = process.env.DUKANG_ENV_FILE ?? '.env.production';
|
||||||
|
const envPath = resolve(apiRoot, envFile);
|
||||||
|
|
||||||
|
if (!existsSync(envPath)) {
|
||||||
|
console.error(`[with-api-env] 未找到 ${envPath}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
config({ path: envPath, override: true });
|
||||||
|
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
if (args.length === 0) {
|
||||||
|
console.error('用法: node scripts/with-api-env.cjs <command> [args...]');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = spawnSync(args[0], args.slice(1), {
|
||||||
|
cwd: apiRoot,
|
||||||
|
stdio: 'inherit',
|
||||||
|
env: process.env,
|
||||||
|
shell: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
process.exit(result.status ?? 1);
|
||||||
@@ -5,22 +5,18 @@ import { resolve } from 'path';
|
|||||||
/**
|
/**
|
||||||
* 环境变量加载策略(后加载的文件覆盖先前的同名键):
|
* 环境变量加载策略(后加载的文件覆盖先前的同名键):
|
||||||
*
|
*
|
||||||
* - 本地开发(NODE_ENV !== production):仅 `.env` + `.env.local`
|
* - 本地开发:仅 `.env` + `.env.local`
|
||||||
* `.env.development` 仅作 deploy/sync-api-env.sh 的源模板,不在本地 dev 自动加载
|
* `.env.development` 仅作参考模板,不自动加载
|
||||||
*
|
*
|
||||||
* - 线上(NODE_ENV=production):`.env.production` → `.env.production.local` → `.env`
|
* - 服务器(NODE_ENV=production):仅 `.env.production` + `.env.production.local`
|
||||||
* 服务器实际运行时以 sync 脚本写入的 `.env` 为准
|
* 不使用 `.env`,避免与本地开发配置混淆
|
||||||
*/
|
*/
|
||||||
const apiRoot = resolve(__dirname, '..');
|
const apiRoot = resolve(__dirname, '..');
|
||||||
const nodeEnv = process.env.NODE_ENV ?? 'development';
|
const nodeEnv = process.env.NODE_ENV ?? 'development';
|
||||||
const isProduction = nodeEnv === 'production';
|
const isProduction = nodeEnv === 'production';
|
||||||
|
|
||||||
const layers = isProduction
|
const layers = isProduction
|
||||||
? [
|
? [resolve(apiRoot, '.env.production'), resolve(apiRoot, '.env.production.local')]
|
||||||
resolve(apiRoot, '.env.production'),
|
|
||||||
resolve(apiRoot, '.env.production.local'),
|
|
||||||
resolve(apiRoot, '.env'),
|
|
||||||
]
|
|
||||||
: [resolve(apiRoot, '.env'), resolve(apiRoot, '.env.local')];
|
: [resolve(apiRoot, '.env'), resolve(apiRoot, '.env.local')];
|
||||||
|
|
||||||
for (const file of layers) {
|
for (const file of layers) {
|
||||||
|
|||||||
Reference in New Issue
Block a user