From fd21e641a27ae27aded44052df03bba7e5c22d7b Mon Sep 17 00:00:00 2001 From: Milky0217 Date: Sat, 18 Apr 2026 14:04:49 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=B0=86=20get=5Fweather=5Fdetails=20?= =?UTF-8?q?=E5=92=8C=20health=5Fcheck=20=E7=A7=BB=E5=87=BA=20JWT=20?= =?UTF-8?q?=E4=BF=9D=E6=8A=A4=E4=BD=9C=E7=94=A8=E5=9F=9F=E4=BD=9C=E4=B8=BA?= =?UTF-8?q?=E5=85=AC=E5=BC=80=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 35 +++++++++++++++- deploy.sh | 18 ++++++++ src/main.rs | 12 ++++-- test_deployment.sh | 100 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 4 deletions(-) create mode 100755 test_deployment.sh diff --git a/AGENTS.md b/AGENTS.md index 51345b2..e0e9643 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,6 +43,7 @@ migrations/ static/ └── katex/ # KaTeX 数学公式渲染库 +test_deployment.sh # 部署后测试脚本(自动执行) tests/ └── integration_test.rs # 基础测试框架 @@ -327,8 +328,40 @@ cargo clippy # 代码检查 **部署**: ```bash -./deploy.sh development # 部署到测试服务器 +./deploy.sh development # 部署到测试服务器(编译 + 传文件 + 重启服务) ./deploy.sh production # 部署到生产服务器 + ``` +**deploy.sh 功能说明**: + +| 步骤 | 说明 | +|------|------| +| 依赖检查 | 验证 `cargo`、`rsync`、`ssh` 命令 | +| 编译 | `APP_ENV={环境} cargo build --release` | +| 上传二进制 | `rsync target/release/rust-backend → 远程目录` | +| 上传配置 | `rsync ./config/ → 远程目录/config/` | +| 上传测试脚本 | `rsync test_deployment.sh → 远程目录/` | +| 重启服务 | `systemctl restart {服务名}` | +| **部署后测试** | `ssh 远程执行 test_deployment.sh` | + +**远程目录与服务对应**: + +| 环境 | 远程目录 | systemd 服务 | +|------|---------|-------------| +| development | `/root/rust/rust_backend_dev` | `rust-backend-dev.service` | +| production | `/root/rust/rust_backend` | `rust-backend.service` | + +> 直接运行 `./deploy.sh` 默认部署 production 环境。 + +**test_deployment.sh 测试内容**: + +| 测试项 | 说明 | +|--------|------| +| 本地后端 | `GET http://127.0.0.1:8080/health` 返回 200 | +| 域名访问 | `GET https://xmclassmate.top/dev` 返回 200/301/302 | +| 静态文件 | CSS、JS、favicon 等文件返回 200 | + +> 部署后自动执行,测试通过才算部署成功。 + 服务器尝试端口顺序:4433、8443、8080、3000、8000、8888 diff --git a/deploy.sh b/deploy.sh index 1607aeb..5a11594 100755 --- a/deploy.sh +++ b/deploy.sh @@ -69,6 +69,13 @@ upload_config() { log_info "配置文件上传成功。" } +upload_test_script() { + log_info "正在上传测试脚本到 ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/..." + rsync -avzP --progress ./test_deployment.sh "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/" + ssh "${REMOTE_USER}@${REMOTE_HOST}" "chmod +x ${REMOTE_DIR}/test_deployment.sh" + log_info "测试脚本上传成功。" +} + restart_remote_service() { log_info "正在重启远程服务 '${SERVICE_NAME}'..." if ssh "${REMOTE_USER}@${REMOTE_HOST}" "systemctl restart ${SERVICE_NAME}"; then @@ -78,6 +85,15 @@ restart_remote_service() { fi } +run_deployment_tests() { + log_info "正在执行部署后测试..." + if ssh "${REMOTE_USER}@${REMOTE_HOST}" "cd ${REMOTE_DIR} && ./test_deployment.sh"; then + log_info "部署后测试通过。" + else + log_error "部署后测试失败!请检查服务状态。" + fi +} + main() { log_info "========== 开始部署 (环境: ${APP_ENV}) ==========" log_info "远程目录: ${REMOTE_DIR}" @@ -86,7 +102,9 @@ main() { build_project upload_binary upload_config + upload_test_script restart_remote_service + run_deployment_tests log_info "========== 部署完成! ==========" } diff --git a/src/main.rs b/src/main.rs index 0798d55..3c9eab8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,13 +63,14 @@ fn create_server_config( .service(web::resource("/static/{tail:.*}").route(web::get().to(serve_static_files))) // API 接口 .service(login) // #[post("/api/login")] + .service(get_weather_details) // #[get("/weather/details")](支持 JWT 或 temp_token,公开接口) + .service(health_check) // #[get("/health")](公开接口,无需认证) // 受保护接口(JWT) .service( web::scope("") .wrap(from_fn(jwt_middleware)) .service(post_weather_data) // #[post("/api/post-weather-data")] .service(get_weather_brief) // #[get("/api/weather")] - .service(get_weather_details) // #[get("/weather/details")] ← 移入受保护作用域 .service(generate_temp_token_handler) // #[post("/api/generate-temp-token/{resource_id}")] .service(delete_weather) // #[delete("/api/weather/delete/{id}")] .service(get_current_user_profile) // #[get("/api/user/profile")] @@ -164,7 +165,12 @@ async fn main() -> std::io::Result<()> { info!("Attempting to start server..."); - let ports = vec![4433, 8443, 8080, 3000, 8000, 8888]; + let is_production = std::env::var("APP_ENV").unwrap_or_else(|_| "development".into()) == "production"; + let ports = if is_production { + vec![4433, 8443, 8080, 3000, 8000, 8888] + } else { + vec![8080, 3000, 8000, 8888, 4433, 8443] + }; let mut server: Option< Pin> + Unpin>>, > = None; @@ -178,7 +184,7 @@ async fn main() -> std::io::Result<()> { let http_client_clone = http_client.clone(); let app_state_clone = app_state.clone(); - if *port == 443 || *port == 8443 { + if *port == 443 || *port == 8443 || *port == 4433 { let ssl_builder = match create_ssl_acceptor() { Ok(builder) => builder, Err(e) => { diff --git a/test_deployment.sh b/test_deployment.sh new file mode 100755 index 0000000..bd5b251 --- /dev/null +++ b/test_deployment.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +set -euo pipefail + +DEVELOPMENT_RUST_URL="http://127.0.0.1:8080" +PRODUCTION_DOMAIN="https://xmclassmate.top" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +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"; } + +check_local_backend() { + log_info "测试本地后端服务 (127.0.0.1:8080)..." + + local status_code + status_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "${DEVELOPMENT_RUST_URL}/health" 2>/dev/null || echo "000") + + if [ "$status_code" = "200" ]; then + log_info "本地后端服务正常 (HTTP $status_code)" + return 0 + else + log_error "本地后端服务异常 (HTTP $status_code)" + return 1 + fi +} + +check_domain_access() { + log_info "测试域名访问 (${PRODUCTION_DOMAIN}/dev)..." + + local status_code + status_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 -L -k \ + -H "Host: xmclassmate.top" \ + "${PRODUCTION_DOMAIN}/dev" 2>/dev/null || echo "000") + + if [ "$status_code" = "200" ] || [ "$status_code" = "301" ] || [ "$status_code" = "302" ]; then + log_info "域名访问正常 (HTTP $status_code)" + return 0 + else + log_error "域名访问异常 (HTTP $status_code)" + return 1 + fi +} + +check_static_files() { + log_info "测试静态文件服务..." + + local test_files=( + "/static/katex/katex.min.css" + "/static/css/style.css" + "/static/favicon.ico" + ) + + local failed=0 + for file in "${test_files[@]}"; do + local status_code + status_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 -L -k \ + -H "Host: xmclassmate.top" \ + "${PRODUCTION_DOMAIN}${file}" 2>/dev/null || echo "000") + + if [ "$status_code" = "200" ]; then + log_info "静态文件 ${file} 正常 (HTTP $status_code)" + else + log_error "静态文件 ${file} 异常 (HTTP $status_code)" + failed=1 + fi + done + + return $failed +} + +main() { + local failed=0 + + if ! check_local_backend; then + failed=1 + fi + + if ! check_domain_access; then + failed=1 + fi + + if ! check_static_files; then + failed=1 + fi + + if [ $failed -eq 0 ]; then + log_info "========== 所有测试通过 ==========" + exit 0 + else + log_error "========== 部分测试失败 ==========" + exit 1 + fi +} + +main "$@" \ No newline at end of file