fix: 将 get_weather_details 和 health_check 移出 JWT 保护作用域作为公开接口
This commit is contained in:
35
AGENTS.md
35
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
|
||||
|
||||
18
deploy.sh
18
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 "========== 部署完成! =========="
|
||||
}
|
||||
|
||||
|
||||
12
src/main.rs
12
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<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
100
test_deployment.sh
Executable 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 "$@"
|
||||
Reference in New Issue
Block a user