docs: 添加域名部署实施计划
This commit is contained in:
@@ -0,0 +1,937 @@
|
||||
# 域名部署实施计划
|
||||
|
||||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 将情绪博物馆所有服务迁移到域名 lifescript.happylifeos.com,配置 HTTPS SSL 证书,并实现自动续期。
|
||||
|
||||
**Architecture:**
|
||||
- 前端应用通过 Nginx 托管,使用相对路径 API 调用
|
||||
- 后端 API 通过 Nginx 反向代理暴露
|
||||
- WebSocket 通过 Nginx 升级协议代理
|
||||
- SSL 证书使用 Let's Encrypt 通过 certbot 申请,配置 systemd timer 自动续期
|
||||
|
||||
**Tech Stack:** Spring Boot, Vue 3, React, Nginx, Let's Encrypt certbot, Python 3
|
||||
|
||||
---
|
||||
|
||||
## Chunk 1: SSL 证书申请脚本
|
||||
|
||||
### Task 1: 创建 SSL 证书申请 Python 脚本
|
||||
|
||||
**Files:**
|
||||
- Create: `tools/deploy-ssl-cert.py`
|
||||
|
||||
- [ ] **Step 1: 创建 Python 脚本文件**
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
SSL 证书申请与自动续期脚本
|
||||
使用 certbot 工具申请 Let's Encrypt 证书并配置 nginx
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
DOMAIN = "lifescript.happylifeos.com"
|
||||
CERT_PATH = f"/etc/letsencrypt/live/{DOMAIN}/fullchain.pem"
|
||||
KEY_PATH = f"/etc/letsencrypt/live/{DOMAIN}/privkey.pem"
|
||||
|
||||
|
||||
def log_info(msg):
|
||||
print(f"\033[32m[INFO]\033[0m {msg}")
|
||||
|
||||
|
||||
def log_warn(msg):
|
||||
print(f"\033[33m[WARN]\033[0m {msg}")
|
||||
|
||||
|
||||
def log_error(msg):
|
||||
print(f"\033[31m[ERROR]\033[0m {msg}")
|
||||
|
||||
|
||||
def run_command(cmd, check=True):
|
||||
"""执行 shell 命令"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd, shell=True, check=check, capture_output=False, text=True
|
||||
)
|
||||
return result.returncode == 0
|
||||
except subprocess.CalledProcessError as e:
|
||||
log_error(f"命令执行失败:{e}")
|
||||
return False
|
||||
|
||||
|
||||
def check_certbot():
|
||||
"""检查 certbot 是否安装"""
|
||||
log_info("检查 certbot 是否安装...")
|
||||
if not run_command("which certbot", check=False):
|
||||
log_error("certbot 未安装,请先安装:apt install certbot python3-certbot-nginx")
|
||||
return False
|
||||
log_info("certbot 已安装")
|
||||
return True
|
||||
|
||||
|
||||
def check_nginx():
|
||||
"""检查 nginx 配置是否正确"""
|
||||
log_info("检查 nginx 配置...")
|
||||
if not run_command("nginx -t", check=False):
|
||||
log_error("nginx 配置有误,请先修复")
|
||||
return False
|
||||
log_info("nginx 配置正确")
|
||||
return True
|
||||
|
||||
|
||||
def apply_certificate(dry_run=False):
|
||||
"""申请 SSL 证书"""
|
||||
log_info(f"开始为 {DOMAIN} 申请 SSL 证书...")
|
||||
|
||||
cmd = f"certbot --nginx -d {DOMAIN} --non-interactive --agree-tos --email admin@happylifeos.com"
|
||||
if dry_run:
|
||||
cmd += " --dry-run"
|
||||
log_info(f"[DRY RUN] 执行:{cmd}")
|
||||
|
||||
if run_command(cmd):
|
||||
log_info("证书申请成功")
|
||||
return True
|
||||
else:
|
||||
log_error("证书申请失败")
|
||||
return False
|
||||
|
||||
|
||||
def setup_auto_renewal():
|
||||
"""配置自动续期"""
|
||||
log_info("配置证书自动续期...")
|
||||
|
||||
# 检查是否已存在 systemd timer
|
||||
if run_command("systemctl list-timers | grep certbot", check=False):
|
||||
log_info("certbot 自动续期 timer 已存在")
|
||||
return True
|
||||
|
||||
# 启用 systemd timer
|
||||
if run_command("systemctl enable certbot.timer && systemctl start certbot.timer"):
|
||||
log_info("已启用 certbot 自动续期 timer")
|
||||
return True
|
||||
else:
|
||||
log_warn("systemd timer 配置失败,尝试配置 cron 任务")
|
||||
return setup_cron_renewal()
|
||||
|
||||
|
||||
def setup_cron_renewal():
|
||||
"""配置 cron 自动续期"""
|
||||
cron_job = "0 3 1 * * certbot renew --quiet --deploy-hook 'systemctl reload nginx'"
|
||||
log_info(f"添加 cron 任务:{cron_job}")
|
||||
|
||||
# 添加到 crontab
|
||||
result = subprocess.run(
|
||||
f"(crontab -l 2>/dev/null | grep -v '{DOMAIN}'; echo '{cron_job}') | crontab -",
|
||||
shell=True, capture_output=True, text=True
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
log_info("cron 任务添加成功")
|
||||
return True
|
||||
else:
|
||||
log_error("cron 任务添加失败")
|
||||
return False
|
||||
|
||||
|
||||
def verify_certificate():
|
||||
"""验证证书是否有效"""
|
||||
log_info("验证 SSL 证书...")
|
||||
cmd = f'echo | openssl s_client -connect {DOMAIN}:443 -servername {DOMAIN} 2>/dev/null | openssl x509 -noout -dates'
|
||||
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
log_info(f"证书信息:\n{result.stdout}")
|
||||
return True
|
||||
else:
|
||||
log_warn("无法验证证书(可能域名 DNS 尚未生效)")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="SSL 证书申请与自动续期脚本")
|
||||
parser.add_argument("--dry-run", action="store_true", help="模拟运行,不实际申请证书")
|
||||
parser.add_argument("--renew", action="store_true", help="手动续期证书")
|
||||
parser.add_argument("--verify", action="store_true", help="验证证书状态")
|
||||
parser.add_argument("--setup-renewal", action="store_true", help="仅配置自动续期")
|
||||
args = parser.parse_args()
|
||||
|
||||
# 验证模式
|
||||
if args.verify:
|
||||
verify_certificate()
|
||||
return 0
|
||||
|
||||
# 仅配置自动续期
|
||||
if args.setup_renewal:
|
||||
setup_auto_renewal()
|
||||
return 0
|
||||
|
||||
# 手动续期
|
||||
if args.renew:
|
||||
run_command("certbot renew --force-renewal")
|
||||
run_command("systemctl reload nginx")
|
||||
return 0
|
||||
|
||||
# 完整流程
|
||||
log_info("=== SSL 证书申请流程 ===")
|
||||
|
||||
if not check_certbot():
|
||||
return 1
|
||||
|
||||
if not check_nginx():
|
||||
return 1
|
||||
|
||||
if not apply_certificate(dry_run=args.dry_run):
|
||||
return 1
|
||||
|
||||
if not args.dry_run:
|
||||
setup_auto_renewal()
|
||||
verify_certificate()
|
||||
|
||||
log_info("=== SSL 证书申请完成 ===")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 添加文件执行权限**
|
||||
|
||||
```bash
|
||||
chmod +x tools/deploy-ssl-cert.py
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 添加 Python 脚本元信息**
|
||||
|
||||
在文件顶部注释中添加:
|
||||
```python
|
||||
"""
|
||||
Author: [创建人姓名]
|
||||
Created: 2026-03-17
|
||||
Purpose: SSL 证书申请与自动续期 - 使用 certbot 配置 Let's Encrypt 证书
|
||||
"""
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 提交**
|
||||
|
||||
```bash
|
||||
git add tools/deploy-ssl-cert.py
|
||||
git commit -m "feat: 添加 SSL 证书申请与自动续期脚本"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Chunk 2: 前端环境配置文件修改
|
||||
|
||||
### Task 2: 修改用户前端 (web) 生产环境配置
|
||||
|
||||
**Files:**
|
||||
- Modify: `web/.env.production`
|
||||
|
||||
- [ ] **Step 1: 读取当前文件内容**
|
||||
|
||||
- [ ] **Step 2: 修改为域名配置**
|
||||
|
||||
```env
|
||||
# 生产环境配置
|
||||
VITE_APP_ENV=prod
|
||||
VITE_APP_TITLE=情绪博物馆
|
||||
VITE_APP_VERSION=1.0.0
|
||||
|
||||
# API 配置 - 使用相对路径,通过 nginx 代理
|
||||
VITE_API_BASE_URL=/api
|
||||
VITE_WS_BASE_URL=/ws
|
||||
VITE_UPLOAD_URL=/api/upload
|
||||
|
||||
# 调试配置
|
||||
VITE_DEBUG=false
|
||||
VITE_MOCK=false
|
||||
|
||||
# 其他配置
|
||||
VITE_APP_DESCRIPTION=情绪博物馆 Web 系统
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add web/.env.production
|
||||
git commit -m "config: 更新 web 前端生产环境配置为域名访问"
|
||||
```
|
||||
|
||||
### Task 3: 修改管理后台 (web-admin) 生产环境配置
|
||||
|
||||
**Files:**
|
||||
- Modify: `web-admin/.env.production`
|
||||
|
||||
- [ ] **Step 1: 读取当前文件内容**
|
||||
|
||||
- [ ] **Step 2: 修改为域名配置**
|
||||
|
||||
```env
|
||||
# 生产环境配置
|
||||
VITE_APP_TITLE=情绪博物馆管理后台
|
||||
# 生产环境使用相对路径,通过 nginx 代理到后端服务
|
||||
VITE_APP_BASE_API=/api
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add web-admin/.env.production
|
||||
git commit -m "config: 更新管理后台生产环境配置为域名访问"
|
||||
```
|
||||
|
||||
### Task 4: 修改 Life-Script 生产环境配置
|
||||
|
||||
**Files:**
|
||||
- Modify: `life-script/.env.production`
|
||||
|
||||
- [ ] **Step 1: 读取当前文件内容**
|
||||
|
||||
- [ ] **Step 2: 修改为域名配置**
|
||||
|
||||
```env
|
||||
# 生产环境配置
|
||||
VITE_API_BASE_URL=/api
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add life-script/.env.production
|
||||
git commit -m "config: 更新 Life-Script 生产环境配置为域名访问"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Chunk 3: Nginx 配置文件修改
|
||||
|
||||
### Task 5: 创建新的 Nginx 配置文件
|
||||
|
||||
**Files:**
|
||||
- Modify: `conf/emotion-museum.conf`
|
||||
|
||||
- [ ] **Step 1: 备份当前配置文件**
|
||||
|
||||
```bash
|
||||
cp conf/emotion-museum.conf conf/emotion-museum.conf.backup
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 修改 Nginx 配置为域名版本**
|
||||
|
||||
```nginx
|
||||
# Emotion Museum - 域名部署配置
|
||||
# 域名:lifescript.happylifeos.com
|
||||
# 配置路径:/etc/nginx/sites-available/lifescript.happylifeos.com.conf
|
||||
|
||||
# HTTP 服务器 - 强制跳转 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name lifescript.happylifeos.com;
|
||||
|
||||
# 强制跳转 HTTPS
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
# HTTPS 服务器
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name lifescript.happylifeos.com;
|
||||
|
||||
# SSL 证书配置
|
||||
ssl_certificate /etc/letsencrypt/live/lifescript.happylifeos.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/lifescript.happylifeos.com/privkey.pem;
|
||||
|
||||
# SSL 优化配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# HSTS (可选,生产环境建议开启)
|
||||
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# 根路径 - 用户前端应用
|
||||
location / {
|
||||
alias /data/www/emotion-museum/;
|
||||
autoindex off;
|
||||
|
||||
# 处理 Vue Router 的 history 模式
|
||||
try_files $uri $uri/ /index.html;
|
||||
|
||||
# HTML 文件不缓存
|
||||
location ~ \.html?$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
# 静态资源缓存 1 年
|
||||
location ~ \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
expires 1y;
|
||||
}
|
||||
}
|
||||
|
||||
# 管理后台应用路径
|
||||
location /emotion-museum-admin/ {
|
||||
alias /data/www/emotion-museum-admin/;
|
||||
autoindex off;
|
||||
|
||||
# 处理 Vue Router 的 history 模式
|
||||
try_files $uri $uri/ /emotion-museum-admin/index.html;
|
||||
|
||||
# HTML 文件不缓存
|
||||
location ~ \.html?$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
# 静态资源缓存 1 年
|
||||
location ~ \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
expires 1y;
|
||||
}
|
||||
}
|
||||
|
||||
# 处理不带末尾斜杠的 /emotion-museum-admin 请求
|
||||
location = /emotion-museum-admin {
|
||||
rewrite ^(.*)$ $1/ permanent;
|
||||
}
|
||||
|
||||
# Life-Script 应用路径
|
||||
location /life-script/ {
|
||||
alias /data/www/life-script/;
|
||||
autoindex off;
|
||||
|
||||
# 处理 React Router 的 history 模式
|
||||
try_files $uri $uri/ /life-script/index.html;
|
||||
|
||||
# HTML 文件不缓存
|
||||
location ~ \.html?$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
# 静态资源缓存 1 年
|
||||
location ~ \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
expires 1y;
|
||||
}
|
||||
}
|
||||
|
||||
# 处理不带末尾斜杠的 /life-script 请求
|
||||
location = /life-script {
|
||||
rewrite ^ /life-script/ last;
|
||||
}
|
||||
|
||||
# 后端 API 代理
|
||||
location /api {
|
||||
proxy_pass http://127.0.0.1:19089;
|
||||
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_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# WebSocket 代理
|
||||
location /ws {
|
||||
proxy_pass http://127.0.0.1:19089;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
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;
|
||||
|
||||
# WebSocket 超时设置
|
||||
proxy_connect_timeout 7d;
|
||||
proxy_send_timeout 7d;
|
||||
proxy_read_timeout 7d;
|
||||
}
|
||||
|
||||
# 健康检查端点
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
# 禁止访问敏感文件
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
access_log /var/log/nginx/lifescript_access.log;
|
||||
error_log /var/log/nginx/lifescript_error.log;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add conf/emotion-museum.conf
|
||||
git commit -m "config: 更新 nginx 配置为域名访问,添加 HTTPS SSL 支持"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Chunk 4: 部署脚本修改
|
||||
|
||||
### Task 6: 创建新域名下的一键部署脚本
|
||||
|
||||
**Files:**
|
||||
- Create: `deploy-to-prod.sh`
|
||||
|
||||
- [ ] **Step 1: 创建部署脚本**
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# 情绪博物馆 - 域名部署脚本
|
||||
# 域名:lifescript.happylifeos.com
|
||||
# 使用方法:bash deploy-to-prod.sh [ssl|frontend|admin|life-script|backend|all]
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_section() {
|
||||
echo ""
|
||||
echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║${NC} $1"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 服务器配置
|
||||
SERVER_IP="101.200.208.45"
|
||||
USERNAME="root"
|
||||
DOMAIN="lifescript.happylifeos.com"
|
||||
|
||||
# SSL 证书申请
|
||||
deploy_ssl() {
|
||||
log_section "申请 SSL 证书"
|
||||
|
||||
if [ ! -f "tools/deploy-ssl-cert.py" ]; then
|
||||
log_error "SSL 证书脚本不存在:tools/deploy-ssl-cert.py"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "在服务器上执行 SSL 证书申请..."
|
||||
scp tools/deploy-ssl-cert.py ${USERNAME}@${SERVER_IP}:/tmp/deploy-ssl-cert.py
|
||||
ssh ${USERNAME}@${SERVER_IP} "python3 /tmp/deploy-ssl-cert.py"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_info "✅ SSL 证书申请完成"
|
||||
return 0
|
||||
else
|
||||
log_error "❌ SSL 证书申请失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 部署后端
|
||||
deploy_backend() {
|
||||
log_section "部署后端服务"
|
||||
|
||||
if [ ! -f "backend-single/deploy.sh" ]; then
|
||||
log_error "后端部署脚本不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "执行后端部署..."
|
||||
cd backend-single
|
||||
bash deploy.sh remote
|
||||
local result=$?
|
||||
cd ..
|
||||
|
||||
if [ $result -eq 0 ]; then
|
||||
log_info "✅ 后端部署完成"
|
||||
return 0
|
||||
else
|
||||
log_error "❌ 后端部署失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 部署前端
|
||||
deploy_frontend() {
|
||||
log_section "部署用户前端"
|
||||
|
||||
if [ ! -f "web/deploy.sh" ]; then
|
||||
log_error "前端部署脚本不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "执行前端部署..."
|
||||
cd web
|
||||
bash deploy.sh
|
||||
local result=$?
|
||||
cd ..
|
||||
|
||||
if [ $result -eq 0 ]; then
|
||||
log_info "✅ 前端部署完成"
|
||||
return 0
|
||||
else
|
||||
log_error "❌ 前端部署失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 部署管理后台
|
||||
deploy_admin() {
|
||||
log_section "部署管理后台"
|
||||
|
||||
if [ ! -f "web-admin/deploy.sh" ]; then
|
||||
log_error "管理后台部署脚本不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "执行管理后台部署..."
|
||||
cd web-admin
|
||||
bash deploy.sh
|
||||
local result=$?
|
||||
cd ..
|
||||
|
||||
if [ $result -eq 0 ]; then
|
||||
log_info "✅ 管理后台部署完成"
|
||||
return 0
|
||||
else
|
||||
log_error "❌ 管理后台部署失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 部署 Life-Script
|
||||
deploy_life_script() {
|
||||
log_section "部署 Life-Script"
|
||||
|
||||
if [ ! -f "life-script/deploy.sh" ]; then
|
||||
log_error "Life-Script 部署脚本不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "执行 Life-Script 部署..."
|
||||
cd life-script
|
||||
bash deploy.sh
|
||||
local result=$?
|
||||
cd ..
|
||||
|
||||
if [ $result -eq 0 ]; then
|
||||
log_info "✅ Life-Script 部署完成"
|
||||
return 0
|
||||
else
|
||||
log_error "❌ Life-Script 部署失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 部署 Nginx 配置
|
||||
deploy_nginx() {
|
||||
log_section "部署 Nginx 配置"
|
||||
|
||||
if [ ! -f "conf/emotion-museum.conf" ]; then
|
||||
log_error "Nginx 配置文件不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "上传 Nginx 配置文件..."
|
||||
scp conf/emotion-museum.conf ${USERNAME}@${SERVER_IP}:/etc/nginx/sites-available/${DOMAIN}.conf
|
||||
|
||||
log_info "启用站点配置..."
|
||||
ssh ${USERNAME}@${SERVER_IP} "ln -snf /etc/nginx/sites-available/${DOMAIN}.conf /etc/nginx/sites-enabled/${DOMAIN}.conf"
|
||||
|
||||
log_info "移除默认配置(如果存在冲突)..."
|
||||
ssh ${USERNAME}@${SERVER_IP} "rm -f /etc/nginx/sites-enabled/default"
|
||||
|
||||
log_info "验证 Nginx 配置..."
|
||||
if ssh ${USERNAME}@${SERVER_IP} "nginx -t"; then
|
||||
log_info "Nginx 配置验证通过"
|
||||
ssh ${USERNAME}@${SERVER_IP} "systemctl reload nginx"
|
||||
log_info "✅ Nginx 配置重载完成"
|
||||
return 0
|
||||
else
|
||||
log_error "❌ Nginx 配置验证失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主程序
|
||||
main() {
|
||||
DEPLOY_TYPE="${1:-all}"
|
||||
|
||||
log_section "情绪博物馆 - 域名部署"
|
||||
log_info "域名:https://${DOMAIN}"
|
||||
log_info "部署类型:${DEPLOY_TYPE}"
|
||||
log_info "部署时间:$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
|
||||
case "$DEPLOY_TYPE" in
|
||||
ssl)
|
||||
deploy_ssl
|
||||
;;
|
||||
backend)
|
||||
deploy_backend
|
||||
;;
|
||||
frontend)
|
||||
deploy_frontend
|
||||
;;
|
||||
admin)
|
||||
deploy_admin
|
||||
;;
|
||||
life-script)
|
||||
deploy_life_script
|
||||
;;
|
||||
nginx)
|
||||
deploy_nginx
|
||||
;;
|
||||
all)
|
||||
deploy_ssl
|
||||
deploy_nginx
|
||||
deploy_backend
|
||||
deploy_frontend
|
||||
deploy_admin
|
||||
deploy_life_script
|
||||
;;
|
||||
*)
|
||||
log_error "无效的部署类型:$DEPLOY_TYPE"
|
||||
echo "使用方法:bash deploy-to-prod.sh [ssl|backend|frontend|admin|life-script|nginx|all]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
log_section "部署完成"
|
||||
|
||||
if [ "$DEPLOY_TYPE" = "all" ] || [ "$DEPLOY_TYPE" = "ssl" ]; then
|
||||
log_info "📋 验证 SSL 证书:运行 python3 tools/deploy-ssl-cert.py --verify"
|
||||
fi
|
||||
|
||||
if [ "$DEPLOY_TYPE" = "all" ] || [ "$DEPLOY_TYPE" = "nginx" ]; then
|
||||
log_info "🌐 访问地址:"
|
||||
log_info " 用户前端:https://${DOMAIN}/"
|
||||
log_info " 管理后台:https://${DOMAIN}/emotion-museum-admin/"
|
||||
log_info " Life-Script: https://${DOMAIN}/life-script/"
|
||||
log_info " API 地址:https://${DOMAIN}/api"
|
||||
log_info " WebSocket: wss://${DOMAIN}/ws"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 添加执行权限**
|
||||
|
||||
```bash
|
||||
chmod +x deploy-to-prod.sh
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 添加元信息**
|
||||
|
||||
在文件顶部添加:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Author: [创建人姓名]
|
||||
# Created: 2026-03-17
|
||||
# Purpose: 域名部署入口脚本 - 支持 SSL 证书申请、前端、后端、nginx 配置部署
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 提交**
|
||||
|
||||
```bash
|
||||
git add deploy-to-prod.sh
|
||||
git commit -m "feat: 添加域名一键部署脚本"
|
||||
```
|
||||
|
||||
### Task 7: 修改用户前端部署脚本
|
||||
|
||||
**Files:**
|
||||
- Modify: `web/deploy.sh`
|
||||
|
||||
- [ ] **Step 1: 读取当前文件**
|
||||
|
||||
- [ ] **Step 2: 修改远程路径和访问地址提示**
|
||||
|
||||
将访问地址提示修改为:
|
||||
```bash
|
||||
echo "✅ 部署完成!"
|
||||
echo "📱 访问地址:https://lifescript.happylifeos.com/"
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add web/deploy.sh
|
||||
git commit -m "config: 更新前端部署脚本访问地址提示"
|
||||
```
|
||||
|
||||
### Task 8: 修改管理后台部署脚本
|
||||
|
||||
**Files:**
|
||||
- Modify: `web-admin/deploy.sh`
|
||||
|
||||
- [ ] **Step 1: 读取当前文件**
|
||||
|
||||
- [ ] **Step 2: 修改访问地址提示**
|
||||
|
||||
将访问地址提示修改为:
|
||||
```bash
|
||||
echo "✅ 管理后台部署完成!"
|
||||
echo "🔧 访问地址:https://lifescript.happylifeos.com/emotion-museum-admin/"
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add web-admin/deploy.sh
|
||||
git commit -m "config: 更新管理后台部署脚本访问地址提示"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Chunk 5: 后端配置与验证
|
||||
|
||||
### Task 9: 检查并修改后端 CORS 配置
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend-single/src/main/resources/application.yml` (或相应配置文件)
|
||||
|
||||
- [ ] **Step 1: 读取当前后端配置文件**
|
||||
|
||||
检查 CORS 配置是否允许新域名访问
|
||||
|
||||
- [ ] **Step 2: 如需修改,添加域名白名单**
|
||||
|
||||
确保 CORS 配置包含 `https://lifescript.happylifeos.com`
|
||||
|
||||
- [ ] **Step 3: 提交**
|
||||
|
||||
```bash
|
||||
git add backend-single/src/main/resources/application.yml
|
||||
git commit -m "config: 更新 CORS 配置允许新域名访问"
|
||||
```
|
||||
|
||||
### Task 10: 执行部署验证
|
||||
|
||||
**Files:**
|
||||
- 无需修改文件
|
||||
|
||||
- [ ] **Step 1: 本地类型检查**
|
||||
|
||||
```bash
|
||||
cd web && npm run type-check
|
||||
cd ../web-admin && npm run type-check
|
||||
cd ../life-script && npm run type-check
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 推送代码到服务器**
|
||||
|
||||
```bash
|
||||
git push origin master
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 执行 SSL 证书申请**
|
||||
|
||||
```bash
|
||||
bash deploy-to-prod.sh ssl
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 执行完整部署**
|
||||
|
||||
```bash
|
||||
bash deploy-to-prod.sh all
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 浏览器验证**
|
||||
|
||||
使用浏览器访问以下地址验证:
|
||||
- https://lifescript.happylifeos.com/
|
||||
- https://lifescript.happylifeos.com/emotion-museum-admin/
|
||||
- https://lifescript.happylifeos.com/life-script/
|
||||
- https://lifescript.happylifeos.com/api/health
|
||||
|
||||
- [ ] **Step 6: 验证浏览器 Console 无错误**
|
||||
|
||||
确保所有页面:
|
||||
- ✅ 正常加载
|
||||
- ✅ 无 JavaScript 错误
|
||||
- ✅ API 请求成功
|
||||
- ✅ WebSocket 连接成功
|
||||
|
||||
---
|
||||
|
||||
## 部署总结
|
||||
|
||||
### 文件清单
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
|------|------|------|
|
||||
| `tools/deploy-ssl-cert.py` | 创建 | SSL 证书申请与自动续期脚本 |
|
||||
| `web/.env.production` | 修改 | 前端 API 配置改为相对路径 |
|
||||
| `web-admin/.env.production` | 修改 | 管理后台 API 配置改为相对路径 |
|
||||
| `life-script/.env.production` | 修改 | Life-Script API 配置改为相对路径 |
|
||||
| `conf/emotion-museum.conf` | 修改 | Nginx 域名配置 |
|
||||
| `deploy-to-prod.sh` | 创建 | 域名部署入口脚本 |
|
||||
| `web/deploy.sh` | 修改 | 更新访问地址提示 |
|
||||
| `web-admin/deploy.sh` | 修改 | 更新访问地址提示 |
|
||||
|
||||
### 部署命令
|
||||
|
||||
```bash
|
||||
# 1. 提交所有配置变更
|
||||
git add .
|
||||
git commit -m "chore: 域名部署配置更新"
|
||||
git push
|
||||
|
||||
# 2. 申请 SSL 证书
|
||||
bash deploy-to-prod.sh ssl
|
||||
|
||||
# 3. 部署所有服务
|
||||
bash deploy-to-prod.sh all
|
||||
```
|
||||
|
||||
### 验证检查清单
|
||||
|
||||
- [ ] SSL 证书有效且自动续期已配置
|
||||
- [ ] HTTPS 强制跳转生效
|
||||
- [ ] 用户前端正常访问
|
||||
- [ ] 管理后台正常访问
|
||||
- [ ] Life-Script 正常访问
|
||||
- [ ] API 代理正常
|
||||
- [ ] WebSocket 连接正常
|
||||
- [ ] 浏览器 Console 无错误
|
||||
- [ ] 所有静态资源正确加载
|
||||
Reference in New Issue
Block a user