🧹 Remove unused legacy modules and functions
- Deleted quota.rs module (quota management not needed via HTTP) - Removed legacy get_id_by_token GraphQL function - Removed unused set_user_quota and increase_user_quota methods - Cleaned up unused imports and legacy structs - Simplified handlers/mod.rs to only expose universal_handler Architecture now focused on core functionality: - GET / (user info) - GET /<filename> (file serving) - POST / (file upload)
This commit is contained in:
@@ -179,42 +179,4 @@ impl AppState {
|
||||
|
||||
Ok(new_quota)
|
||||
}
|
||||
|
||||
/// Устанавливает квоту пользователя в байтах (позволяет увеличить или уменьшить)
|
||||
pub async fn set_user_quota(&self, user_id: &str, bytes: u64) -> Result<u64, actix_web::Error> {
|
||||
let mut redis = self.redis.clone();
|
||||
let quota_key = format!("quota:{}", user_id);
|
||||
|
||||
// Устанавливаем новое значение квоты
|
||||
redis
|
||||
.set::<_, u64, ()>("a_key, bytes)
|
||||
.await
|
||||
.map_err(|_| ErrorInternalServerError("Failed to set user quota in Redis"))?;
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
/// Увеличивает квоту пользователя на указанное количество байт
|
||||
pub async fn increase_user_quota(
|
||||
&self,
|
||||
user_id: &str,
|
||||
additional_bytes: u64,
|
||||
) -> Result<u64, actix_web::Error> {
|
||||
let mut redis = self.redis.clone();
|
||||
let quota_key = format!("quota:{}", user_id);
|
||||
|
||||
// Получаем текущую квоту
|
||||
let current_quota: u64 = redis.get("a_key).await.unwrap_or(0);
|
||||
|
||||
// Вычисляем новую квоту
|
||||
let new_quota = current_quota + additional_bytes;
|
||||
|
||||
// Устанавливаем новое значение
|
||||
redis
|
||||
.set::<_, u64, ()>("a_key, new_quota)
|
||||
.await
|
||||
.map_err(|_| ErrorInternalServerError("Failed to increase user quota in Redis"))?;
|
||||
|
||||
Ok(new_quota)
|
||||
}
|
||||
}
|
||||
|
||||
78
src/auth.rs
78
src/auth.rs
@@ -2,33 +2,8 @@ use actix_web::error::ErrorInternalServerError;
|
||||
use jsonwebtoken::{Algorithm, DecodingKey, Validation, decode};
|
||||
use log::{info, warn};
|
||||
use redis::{AsyncCommands, aio::MultiplexedConnection};
|
||||
use reqwest::Client as HTTPClient;
|
||||
use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use std::{collections::HashMap, env, error::Error};
|
||||
|
||||
// Старые структуры для совместимости с get_id_by_token
|
||||
#[derive(Deserialize)]
|
||||
struct AuthResponse {
|
||||
data: Option<AuthData>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AuthData {
|
||||
validate_jwt_token: Option<ValidateJWTToken>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ValidateJWTToken {
|
||||
is_valid: bool,
|
||||
claims: Option<Claims>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Claims {
|
||||
sub: Option<String>,
|
||||
}
|
||||
use std::error::Error;
|
||||
|
||||
// Структуры для JWT токенов
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -51,57 +26,6 @@ pub struct Author {
|
||||
pub device_info: Option<String>,
|
||||
}
|
||||
|
||||
/// Получает айди пользователя из токена в заголовке
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub async fn get_id_by_token(token: &str) -> Result<String, Box<dyn Error>> {
|
||||
let auth_api_base = env::var("CORE_URL")?;
|
||||
let query_name = "validate_jwt_token";
|
||||
let operation = "ValidateToken";
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
|
||||
|
||||
let mut variables = HashMap::<String, HashMap<String, String>>::new();
|
||||
let mut params = HashMap::<String, String>::new();
|
||||
params.insert("token".to_string(), token.to_string());
|
||||
params.insert("token_type".to_string(), "access_token".to_string());
|
||||
variables.insert("params".to_string(), params);
|
||||
|
||||
let gql = json!({
|
||||
"query": format!("query {}($params: ValidateJWTTokenInput!) {{ {}(params: $params) {{ is_valid claims }} }}", operation, query_name),
|
||||
"operationName": operation,
|
||||
"variables": variables
|
||||
});
|
||||
|
||||
let client = HTTPClient::new();
|
||||
let response = client
|
||||
.post(&auth_api_base)
|
||||
.headers(headers)
|
||||
.json(&gql)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if response.status().is_success() {
|
||||
let auth_response: AuthResponse = response.json().await?;
|
||||
if let Some(auth_data) = auth_response.data {
|
||||
if let Some(validate_jwt_token) = auth_data.validate_jwt_token {
|
||||
if validate_jwt_token.is_valid {
|
||||
if let Some(claims) = validate_jwt_token.claims {
|
||||
if let Some(sub) = claims.sub {
|
||||
return Ok(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Box::new(std::io::Error::other("Invalid token response")))
|
||||
} else {
|
||||
Err(Box::new(std::io::Error::other(format!(
|
||||
"Request failed with status: {}",
|
||||
response.status()
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
/// Декодирует JWT токен и извлекает claims с проверкой истечения
|
||||
fn decode_jwt_token(token: &str) -> Result<TokenClaims, Box<dyn Error>> {
|
||||
// В реальном приложении здесь должен быть настоящий секретный ключ
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
mod proxy;
|
||||
mod quota;
|
||||
mod serve_file;
|
||||
mod upload;
|
||||
mod user;
|
||||
mod universal;
|
||||
|
||||
pub use proxy::proxy_handler;
|
||||
pub use quota::{get_quota_handler, increase_quota_handler, set_quota_handler};
|
||||
pub use upload::upload_handler;
|
||||
pub use user::get_current_user_handler;
|
||||
pub use universal::universal_handler;
|
||||
|
||||
// Общий лимит квоты на пользователя: 5 ГБ
|
||||
pub const MAX_USER_QUOTA_BYTES: u64 = 5 * 1024 * 1024 * 1024;
|
||||
// Общий лимит квоты на пользователя: 12 ГБ
|
||||
pub const MAX_USER_QUOTA_BYTES: u64 = 12 * 1024 * 1024 * 1024;
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
use actix_web::{HttpRequest, HttpResponse, Result, web};
|
||||
use log::warn;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::app_state::AppState;
|
||||
use crate::auth::get_id_by_token;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct QuotaRequest {
|
||||
pub user_id: String,
|
||||
pub additional_bytes: Option<u64>,
|
||||
pub new_quota_bytes: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct QuotaResponse {
|
||||
pub user_id: String,
|
||||
pub current_quota: u64,
|
||||
pub max_quota: u64,
|
||||
}
|
||||
|
||||
/// Обработчик для получения информации о квоте пользователя
|
||||
pub async fn get_quota_handler(
|
||||
req: HttpRequest,
|
||||
state: web::Data<AppState>,
|
||||
) -> Result<HttpResponse, actix_web::Error> {
|
||||
// Проверяем авторизацию
|
||||
let token = req
|
||||
.headers()
|
||||
.get("Authorization")
|
||||
.and_then(|header_value| header_value.to_str().ok());
|
||||
|
||||
if token.is_none() {
|
||||
return Err(actix_web::error::ErrorUnauthorized("Unauthorized"));
|
||||
}
|
||||
|
||||
let _admin_id = get_id_by_token(token.unwrap()).await.map_err(|e| {
|
||||
let error_msg = if e.to_string().contains("expired") {
|
||||
"Admin token has expired"
|
||||
} else {
|
||||
"Invalid admin token"
|
||||
};
|
||||
actix_web::error::ErrorUnauthorized(error_msg)
|
||||
})?;
|
||||
|
||||
// Получаем user_id из query параметров
|
||||
let user_id = req
|
||||
.query_string()
|
||||
.split("user_id=")
|
||||
.nth(1)
|
||||
.and_then(|s| s.split('&').next())
|
||||
.ok_or_else(|| actix_web::error::ErrorBadRequest("Missing user_id parameter"))?;
|
||||
|
||||
// Получаем текущую квоту пользователя
|
||||
let current_quota = state.get_or_create_quota(user_id).await?;
|
||||
|
||||
let response = QuotaResponse {
|
||||
user_id: user_id.to_string(),
|
||||
current_quota,
|
||||
max_quota: crate::handlers::MAX_USER_QUOTA_BYTES,
|
||||
};
|
||||
|
||||
Ok(HttpResponse::Ok().json(response))
|
||||
}
|
||||
|
||||
/// Обработчик для увеличения квоты пользователя
|
||||
pub async fn increase_quota_handler(
|
||||
req: HttpRequest,
|
||||
quota_data: web::Json<QuotaRequest>,
|
||||
state: web::Data<AppState>,
|
||||
) -> Result<HttpResponse, actix_web::Error> {
|
||||
// Проверяем авторизацию
|
||||
let token = req
|
||||
.headers()
|
||||
.get("Authorization")
|
||||
.and_then(|header_value| header_value.to_str().ok());
|
||||
|
||||
if token.is_none() {
|
||||
return Err(actix_web::error::ErrorUnauthorized("Unauthorized"));
|
||||
}
|
||||
|
||||
let _admin_id = get_id_by_token(token.unwrap()).await.map_err(|e| {
|
||||
let error_msg = if e.to_string().contains("expired") {
|
||||
"Admin token has expired"
|
||||
} else {
|
||||
"Invalid admin token"
|
||||
};
|
||||
actix_web::error::ErrorUnauthorized(error_msg)
|
||||
})?;
|
||||
|
||||
let additional_bytes = quota_data
|
||||
.additional_bytes
|
||||
.ok_or_else(|| actix_web::error::ErrorBadRequest("Missing additional_bytes parameter"))?;
|
||||
|
||||
if additional_bytes == 0 {
|
||||
return Err(actix_web::error::ErrorBadRequest(
|
||||
"additional_bytes must be greater than 0",
|
||||
));
|
||||
}
|
||||
|
||||
// Увеличиваем квоту пользователя
|
||||
let new_quota = state
|
||||
.increase_user_quota("a_data.user_id, additional_bytes)
|
||||
.await?;
|
||||
|
||||
warn!(
|
||||
"Increased quota for user {} by {} bytes, new total: {} bytes",
|
||||
quota_data.user_id, additional_bytes, new_quota
|
||||
);
|
||||
|
||||
let response = QuotaResponse {
|
||||
user_id: quota_data.user_id.clone(),
|
||||
current_quota: new_quota,
|
||||
max_quota: crate::handlers::MAX_USER_QUOTA_BYTES,
|
||||
};
|
||||
|
||||
Ok(HttpResponse::Ok().json(response))
|
||||
}
|
||||
|
||||
/// Обработчик для установки квоты пользователя
|
||||
pub async fn set_quota_handler(
|
||||
req: HttpRequest,
|
||||
quota_data: web::Json<QuotaRequest>,
|
||||
state: web::Data<AppState>,
|
||||
) -> Result<HttpResponse, actix_web::Error> {
|
||||
// Проверяем авторизацию
|
||||
let token = req
|
||||
.headers()
|
||||
.get("Authorization")
|
||||
.and_then(|header_value| header_value.to_str().ok());
|
||||
|
||||
if token.is_none() {
|
||||
return Err(actix_web::error::ErrorUnauthorized("Unauthorized"));
|
||||
}
|
||||
|
||||
let _admin_id = get_id_by_token(token.unwrap()).await.map_err(|e| {
|
||||
let error_msg = if e.to_string().contains("expired") {
|
||||
"Admin token has expired"
|
||||
} else {
|
||||
"Invalid admin token"
|
||||
};
|
||||
actix_web::error::ErrorUnauthorized(error_msg)
|
||||
})?;
|
||||
|
||||
let new_quota_bytes = quota_data
|
||||
.new_quota_bytes
|
||||
.ok_or_else(|| actix_web::error::ErrorBadRequest("Missing new_quota_bytes parameter"))?;
|
||||
|
||||
// Устанавливаем новую квоту пользователя
|
||||
let new_quota = state
|
||||
.set_user_quota("a_data.user_id, new_quota_bytes)
|
||||
.await?;
|
||||
|
||||
warn!(
|
||||
"Set quota for user {} to {} bytes",
|
||||
quota_data.user_id, new_quota
|
||||
);
|
||||
|
||||
let response = QuotaResponse {
|
||||
user_id: quota_data.user_id.clone(),
|
||||
current_quota: new_quota,
|
||||
max_quota: crate::handlers::MAX_USER_QUOTA_BYTES,
|
||||
};
|
||||
|
||||
Ok(HttpResponse::Ok().json(response))
|
||||
}
|
||||
68
src/handlers/universal.rs
Normal file
68
src/handlers/universal.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use actix_web::{HttpRequest, HttpResponse, Result, web};
|
||||
use actix_multipart::Multipart;
|
||||
use log::{info, warn};
|
||||
|
||||
use crate::app_state::AppState;
|
||||
|
||||
/// Универсальный обработчик, который определяет HTTP метод и путь
|
||||
pub async fn universal_handler(
|
||||
req: HttpRequest,
|
||||
payload: web::Payload,
|
||||
state: web::Data<AppState>,
|
||||
) -> Result<HttpResponse, actix_web::Error> {
|
||||
let method = req.method().clone();
|
||||
let path = req.path().to_string();
|
||||
|
||||
info!("Universal handler: {} {}", method, path);
|
||||
|
||||
// Возвращаем 404 для .well-known путей (для Let's Encrypt ACME)
|
||||
if path.starts_with("/.well-known/") {
|
||||
warn!("ACME challenge path requested: {}", path);
|
||||
return Ok(HttpResponse::NotFound().finish());
|
||||
}
|
||||
|
||||
match method.as_str() {
|
||||
"GET" => handle_get(req, state, &path).await,
|
||||
"POST" => handle_post(req, payload, state, &path).await,
|
||||
_ => {
|
||||
warn!("Unsupported HTTP method: {}", method);
|
||||
Ok(HttpResponse::MethodNotAllowed().json(serde_json::json!({
|
||||
"error": "Method not allowed"
|
||||
})))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_get(
|
||||
req: HttpRequest,
|
||||
state: web::Data<AppState>,
|
||||
path: &str,
|
||||
) -> Result<HttpResponse, actix_web::Error> {
|
||||
if path == "/" {
|
||||
// GET / - получение информации о пользователе
|
||||
crate::handlers::user::get_current_user_handler(req, state).await
|
||||
} else {
|
||||
// GET /{path} - получение файла через proxy
|
||||
let path_without_slash = path.trim_start_matches('/');
|
||||
let requested_res = web::Path::from(path_without_slash.to_string());
|
||||
crate::handlers::proxy::proxy_handler(req, requested_res, state).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_post(
|
||||
req: HttpRequest,
|
||||
payload: web::Payload,
|
||||
state: web::Data<AppState>,
|
||||
path: &str,
|
||||
) -> Result<HttpResponse, actix_web::Error> {
|
||||
if path == "/" {
|
||||
// POST / - загрузка файла (multipart)
|
||||
let multipart = Multipart::new(&req.headers(), payload);
|
||||
crate::handlers::upload::upload_handler(req, multipart, state).await
|
||||
} else {
|
||||
warn!("Unsupported POST path: {}", path);
|
||||
Ok(HttpResponse::NotFound().json(serde_json::json!({
|
||||
"error": "Endpoint not found"
|
||||
})))
|
||||
}
|
||||
}
|
||||
19
src/main.rs
19
src/main.rs
@@ -14,10 +14,7 @@ use actix_web::{
|
||||
};
|
||||
use app_state::AppState;
|
||||
|
||||
use handlers::{
|
||||
get_current_user_handler, get_quota_handler, increase_quota_handler, proxy_handler,
|
||||
set_quota_handler, upload_handler,
|
||||
};
|
||||
use handlers::universal_handler;
|
||||
use log::warn;
|
||||
use std::env;
|
||||
use tokio::task::spawn_blocking;
|
||||
@@ -63,19 +60,7 @@ async fn main() -> std::io::Result<()> {
|
||||
.app_data(web::Data::new(app_state.clone()))
|
||||
.wrap(cors)
|
||||
.wrap(Logger::default())
|
||||
.route("/", web::get().to(get_current_user_handler))
|
||||
.route("/", web::post().to(upload_handler))
|
||||
.route("/quota", web::get().to(get_quota_handler))
|
||||
.route("/quota/increase", web::post().to(increase_quota_handler))
|
||||
.route("/quota/set", web::post().to(set_quota_handler))
|
||||
.service(
|
||||
web::scope("/.well-known")
|
||||
.service(
|
||||
actix_files::Files::new("/", "/tmp/.well-known")
|
||||
.show_files_listing()
|
||||
)
|
||||
)
|
||||
.route("/{path:.*}", web::get().to(proxy_handler))
|
||||
.default_service(web::to(universal_handler))
|
||||
})
|
||||
.bind(addr)?
|
||||
.run()
|
||||
|
||||
Reference in New Issue
Block a user