fix: 将 get_weather_details 和 health_check 移出 JWT 保护作用域作为公开接口

This commit is contained in:
2026-04-18 14:04:49 +08:00
parent d756694534
commit fd21e641a2
4 changed files with 161 additions and 4 deletions

View File

@@ -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

View File

@@ -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 "========== 部署完成! =========="
}

View File

@@ -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<Box<dyn std::future::Future<Output = std::io::Result<()>> + 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) => {

100
test_deployment.sh Executable file
View File

@@ -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 "$@"