PersonalWebApplication/Server/src/routes/web.rs

70 lines
2.1 KiB
Rust
Raw Normal View History

//! 前端React专用接口。挂 CORS仅放行前端开发源。
use axum::{
Json, Router,
extract::State,
http::{Method, StatusCode, header},
routing::{get, post},
};
use serde::{Deserialize, Serialize};
use tower_http::cors::{AllowOrigin, CorsLayer};
use crate::{monitor::Stats, state::AppState};
pub fn router(state: AppState) -> Router {
// CORS只有浏览器会校验。放行前端开发源。
// 登录是 POST + JSON所以要额外放行 POST 方法与 Content-Type 请求头,
// 否则浏览器的预检OPTIONS会被拦下。
let cors = CorsLayer::new()
.allow_origin(AllowOrigin::list([
"http://localhost:5173".parse().unwrap(),
"http://127.0.0.1:5173".parse().unwrap(),
]))
.allow_methods([Method::GET, Method::POST])
.allow_headers([header::CONTENT_TYPE]);
Router::new()
.route("/api/web/stats", get(stats))
.route("/api/web/login", post(login))
.layer(cors)
.with_state(state)
}
/// 返回最新系统状态快照。
async fn stats(State(state): State<AppState>) -> Json<Stats> {
let snapshot = state.snapshot.read().await.clone();
Json(snapshot)
}
/// 登录请求体:用户名 + 明文密码(开发期走 HTTP生产应套 HTTPS
#[derive(Deserialize)]
struct LoginRequest {
username: String,
password: String,
}
/// 登录成功响应。`token` 目前只是给前端持久化「登录态」用的标识,
/// 暂无受保护接口去校验它;将来加鉴权中间件时再赋予真实含义。
#[derive(Serialize)]
struct LoginResponse {
ok: bool,
username: String,
token: String,
}
/// 校验用户名/密码。成功 200 + token失败 401。
async fn login(
State(state): State<AppState>,
Json(req): Json<LoginRequest>,
) -> Result<Json<LoginResponse>, StatusCode> {
if state.users.verify(&req.username, &req.password) {
Ok(Json(LoginResponse {
ok: true,
username: req.username,
token: crate::auth::issue_token(),
}))
} else {
Err(StatusCode::UNAUTHORIZED)
}
}