fix: 将 get_weather_details 和 health_check 移出 JWT 保护作用域作为公开接口
This commit is contained in:
35
AGENTS.md
35
AGENTS.md
@@ -43,6 +43,7 @@ migrations/
|
|||||||
static/
|
static/
|
||||||
└── katex/ # KaTeX 数学公式渲染库
|
└── katex/ # KaTeX 数学公式渲染库
|
||||||
|
|
||||||
|
test_deployment.sh # 部署后测试脚本(自动执行)
|
||||||
tests/
|
tests/
|
||||||
└── integration_test.rs # 基础测试框架
|
└── integration_test.rs # 基础测试框架
|
||||||
|
|
||||||
@@ -327,8 +328,40 @@ cargo clippy # 代码检查
|
|||||||
|
|
||||||
**部署**:
|
**部署**:
|
||||||
```bash
|
```bash
|
||||||
./deploy.sh development # 部署到测试服务器
|
./deploy.sh development # 部署到测试服务器(编译 + 传文件 + 重启服务)
|
||||||
./deploy.sh production # 部署到生产服务器
|
./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
|
服务器尝试端口顺序:4433、8443、8080、3000、8000、8888
|
||||||
|
|||||||
18
deploy.sh
18
deploy.sh
@@ -69,6 +69,13 @@ upload_config() {
|
|||||||
log_info "配置文件上传成功。"
|
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() {
|
restart_remote_service() {
|
||||||
log_info "正在重启远程服务 '${SERVICE_NAME}'..."
|
log_info "正在重启远程服务 '${SERVICE_NAME}'..."
|
||||||
if ssh "${REMOTE_USER}@${REMOTE_HOST}" "systemctl restart ${SERVICE_NAME}"; then
|
if ssh "${REMOTE_USER}@${REMOTE_HOST}" "systemctl restart ${SERVICE_NAME}"; then
|
||||||
@@ -78,6 +85,15 @@ restart_remote_service() {
|
|||||||
fi
|
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() {
|
main() {
|
||||||
log_info "========== 开始部署 (环境: ${APP_ENV}) =========="
|
log_info "========== 开始部署 (环境: ${APP_ENV}) =========="
|
||||||
log_info "远程目录: ${REMOTE_DIR}"
|
log_info "远程目录: ${REMOTE_DIR}"
|
||||||
@@ -86,7 +102,9 @@ main() {
|
|||||||
build_project
|
build_project
|
||||||
upload_binary
|
upload_binary
|
||||||
upload_config
|
upload_config
|
||||||
|
upload_test_script
|
||||||
restart_remote_service
|
restart_remote_service
|
||||||
|
run_deployment_tests
|
||||||
log_info "========== 部署完成! =========="
|
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)))
|
.service(web::resource("/static/{tail:.*}").route(web::get().to(serve_static_files)))
|
||||||
// API 接口
|
// API 接口
|
||||||
.service(login) // #[post("/api/login")]
|
.service(login) // #[post("/api/login")]
|
||||||
|
.service(get_weather_details) // #[get("/weather/details")](支持 JWT 或 temp_token,公开接口)
|
||||||
|
.service(health_check) // #[get("/health")](公开接口,无需认证)
|
||||||
// 受保护接口(JWT)
|
// 受保护接口(JWT)
|
||||||
.service(
|
.service(
|
||||||
web::scope("")
|
web::scope("")
|
||||||
.wrap(from_fn(jwt_middleware))
|
.wrap(from_fn(jwt_middleware))
|
||||||
.service(post_weather_data) // #[post("/api/post-weather-data")]
|
.service(post_weather_data) // #[post("/api/post-weather-data")]
|
||||||
.service(get_weather_brief) // #[get("/api/weather")]
|
.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(generate_temp_token_handler) // #[post("/api/generate-temp-token/{resource_id}")]
|
||||||
.service(delete_weather) // #[delete("/api/weather/delete/{id}")]
|
.service(delete_weather) // #[delete("/api/weather/delete/{id}")]
|
||||||
.service(get_current_user_profile) // #[get("/api/user/profile")]
|
.service(get_current_user_profile) // #[get("/api/user/profile")]
|
||||||
@@ -164,7 +165,12 @@ async fn main() -> std::io::Result<()> {
|
|||||||
|
|
||||||
info!("Attempting to start server...");
|
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<
|
let mut server: Option<
|
||||||
Pin<Box<dyn std::future::Future<Output = std::io::Result<()>> + Unpin>>,
|
Pin<Box<dyn std::future::Future<Output = std::io::Result<()>> + Unpin>>,
|
||||||
> = None;
|
> = None;
|
||||||
@@ -178,7 +184,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
let http_client_clone = http_client.clone();
|
let http_client_clone = http_client.clone();
|
||||||
let app_state_clone = app_state.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() {
|
let ssl_builder = match create_ssl_acceptor() {
|
||||||
Ok(builder) => builder,
|
Ok(builder) => builder,
|
||||||
Err(e) => {
|
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