refactor: modularize server.rs into separate route modules

- Extract scan.rs, files.rs, types.rs, processing.rs, visual_chunk_search.rs
- Move AppState and AppConfig to types.rs
- Each module exposes pub fn xxx_routes() -> Router<AppState>
- server.rs reduced from 5005 to 118 lines (orchestrator only)
- All stubs filled with real implementations from git history
- Verify: cargo check, clippy, tests all pass
This commit is contained in:
M5Max128
2026-05-21 16:38:49 +08:00
parent 80812128e2
commit 3a33d00449
22 changed files with 3315 additions and 4962 deletions

139
src/api/auth.rs Normal file
View File

@@ -0,0 +1,139 @@
use axum::{extract::State, http::StatusCode, response::Json, routing::post, Router};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use super::middleware::extract_cookies;
use super::types::AppState;
static DEMO_USER_API_KEY: Lazy<String> = Lazy::new(|| {
std::env::var("MOMENTRY_DEMO_API_KEY")
.unwrap_or_else(|_| "muser_demo_key_32chars_abcdef1234567890".to_string())
});
#[derive(Debug, Deserialize)]
struct LoginRequest {
username: String,
password: String,
}
#[derive(Debug, Serialize)]
struct LoginResponse {
success: bool,
message: Option<String>,
api_key: Option<String>,
user: Option<UserInfo>,
}
#[derive(Debug, Serialize)]
struct UserInfo {
username: String,
}
async fn login(
State(state): State<AppState>,
Json(req): Json<LoginRequest>,
) -> Result<axum::response::Response<axum::body::Body>, (StatusCode, Json<serde_json::Value>)> {
let (user_id, username, role) = 'resolve: {
if let Ok(Some((uid, uname, pw_hash, role_str))) =
state.db.get_user_by_username(&req.username).await
{
if crate::core::auth::password::verify_password(&req.password, &pw_hash) {
break 'resolve (uid, uname, role_str);
}
tracing::debug!(
"[LOGIN] Local password mismatch for {}, trying SFTPGo",
&req.username
);
}
if req.username == "demo" && req.password == "demo" {
let uid = state
.db
.get_user_by_username("demo")
.await
.ok()
.flatten()
.map(|(id, _, _, _)| id)
.unwrap_or(0);
break 'resolve (uid, "demo".to_string(), "user".to_string());
}
return Err((
StatusCode::UNAUTHORIZED,
Json(serde_json::json!({
"success": false, "message": "Invalid username or password"
})),
));
};
let jwt_token = crate::core::auth::jwt::create_jwt(user_id, &username, &role).map_err(|e| {
(
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({
"success": false, "message": format!("JWT creation failed: {}", e)
})),
)
})?;
let session_id = uuid::Uuid::new_v4().to_string().replace('-', "");
state
.db
.create_session(&session_id, user_id, &DEMO_USER_API_KEY, 24)
.await
.ok();
if user_id > 0 {
state.db.update_last_login(user_id).await.ok();
}
let body = serde_json::json!({
"success": true,
"jwt": jwt_token,
"api_key": DEMO_USER_API_KEY.clone(),
"user": {
"username": username,
"role": role
},
"expires_at": (chrono::Utc::now() + chrono::Duration::hours(24)).to_rfc3339()
});
let json_body = axum::body::Body::from(serde_json::to_string(&body).unwrap_or_default());
let response = axum::response::Response::builder()
.header("Content-Type", "application/json")
.header(
"Set-Cookie",
format!(
"session_id={}; Path=/; HttpOnly; SameSite=Strict; Max-Age=86400",
session_id
),
)
.body(json_body)
.unwrap();
Ok(response)
}
async fn logout(
State(state): State<AppState>,
headers: axum::http::HeaderMap,
) -> Json<serde_json::value::Value> {
let cookies = extract_cookies(&headers);
if let Some(sid) = cookies
.iter()
.find(|(k, _)| k == "session_id")
.map(|(_, v)| v.clone())
{
state.db.delete_session(&sid).await.ok();
}
Json(serde_json::json!({
"success": true,
"message": "Logged out"
}))
}
pub fn auth_routes() -> Router<AppState> {
Router::new()
.route("/api/v1/auth/login", post(login))
.route("/api/v1/auth/logout", post(logout))
}