fix: resolve all clippy warnings and convert integration_tests to custom cfg

- Add type aliases (FileRow, FolderRow, FolderRowPaginated, FolderRowOptUser) to reduce type complexity
- Simplify redundant closures in app_password_handler and webdav_handler
- Remove needless borrow in auth_handler
- Collapse nested if/let chains in login_lockout, webdav_lock, auth, rate_limit
- Box LockEntry in acquire() Err variant to fix large enum variant warning
- Rename DeviceCodeStatus::from_str to parse to avoid should_implement_trait lint
- Add #[allow(clippy::too_many_arguments)] and #[allow(clippy::result_unit_err)] where appropriate
- Convert integration_tests from cargo feature to custom cfg attribute
- Add check-cfg lint config in Cargo.toml for integration_tests cfg
This commit is contained in:
Diocrafts
2026-03-04 23:55:08 +01:00
parent b81b7f7a0e
commit ee86c3a128
53 changed files with 871 additions and 956 deletions
+1 -1
View File
@@ -11,10 +11,10 @@ use crate::application::dtos::settings_dto::{
SaveOidcSettingsDto, TestOidcConnectionDto, UpdateUserActiveDto, UpdateUserQuotaDto,
UpdateUserRoleDto,
};
use crate::application::ports::auth_ports::TokenServicePort;
use crate::common::di::AppState;
use crate::interfaces::errors::AppError;
use std::sync::Arc;
use crate::application::ports::auth_ports::TokenServicePort;
/// Admin API routes — all require admin role.
pub fn admin_routes() -> Router<Arc<AppState>> {
@@ -1,84 +1,81 @@
//! HTTP handlers for App Password management.
//!
//! All endpoints require JWT authentication (the user must be logged in to
//! create/list/revoke their app passwords).
use crate::application::dtos::app_password_dto::CreateAppPasswordRequestDto;
use crate::common::di::AppState;
use crate::interfaces::errors::AppError;
use crate::interfaces::middleware::auth::CurrentUser;
use axum::extract::State;
use axum::routing::{delete, get, post};
use axum::{Json, Router};
use std::sync::Arc;
/// Protected routes — require JWT auth middleware.
pub fn app_password_routes() -> Router<Arc<AppState>> {
Router::new()
.route("/app-passwords", post(create_app_password))
.route("/app-passwords", get(list_app_passwords))
.route("/app-passwords/{id}", delete(revoke_app_password))
}
/// POST /api/auth/app-passwords — Create a new app password.
///
/// Returns the plain-text password ONCE. The user must copy it immediately.
async fn create_app_password(
State(state): State<Arc<AppState>>,
axum::Extension(user): axum::Extension<CurrentUser>,
Json(request): Json<CreateAppPasswordRequestDto>,
) -> Result<Json<crate::application::dtos::app_password_dto::AppPasswordCreatedResponseDto>, AppError>
{
let service = state
.app_password_service
.as_ref()
.ok_or_else(|| AppError::internal_error("App password service not configured"))?;
let response = service
.create(&user.id, request)
.await
.map_err(|e| AppError::from(e))?;
Ok(Json(response))
}
/// GET /api/auth/app-passwords — List all app passwords for the current user.
///
/// Never returns plain-text passwords (only prefix + metadata).
async fn list_app_passwords(
State(state): State<Arc<AppState>>,
axum::Extension(user): axum::Extension<CurrentUser>,
) -> Result<Json<crate::application::dtos::app_password_dto::AppPasswordListResponseDto>, AppError>
{
let service = state
.app_password_service
.as_ref()
.ok_or_else(|| AppError::internal_error("App password service not configured"))?;
let response = service
.list(&user.id)
.await
.map_err(|e| AppError::from(e))?;
Ok(Json(response))
}
/// DELETE /api/auth/app-passwords/:id — Revoke an app password.
async fn revoke_app_password(
State(state): State<Arc<AppState>>,
axum::Extension(user): axum::Extension<CurrentUser>,
axum::extract::Path(id): axum::extract::Path<String>,
) -> Result<Json<crate::application::dtos::app_password_dto::AppPasswordRevokeResponseDto>, AppError>
{
let service = state
.app_password_service
.as_ref()
.ok_or_else(|| AppError::internal_error("App password service not configured"))?;
let response = service
.revoke(&user.id, &id)
.await
.map_err(|e| AppError::from(e))?;
Ok(Json(response))
}
//! HTTP handlers for App Password management.
//!
//! All endpoints require JWT authentication (the user must be logged in to
//! create/list/revoke their app passwords).
use crate::application::dtos::app_password_dto::CreateAppPasswordRequestDto;
use crate::common::di::AppState;
use crate::interfaces::errors::AppError;
use crate::interfaces::middleware::auth::CurrentUser;
use axum::extract::State;
use axum::routing::{delete, get, post};
use axum::{Json, Router};
use std::sync::Arc;
/// Protected routes — require JWT auth middleware.
pub fn app_password_routes() -> Router<Arc<AppState>> {
Router::new()
.route("/app-passwords", post(create_app_password))
.route("/app-passwords", get(list_app_passwords))
.route("/app-passwords/{id}", delete(revoke_app_password))
}
/// POST /api/auth/app-passwords — Create a new app password.
///
/// Returns the plain-text password ONCE. The user must copy it immediately.
async fn create_app_password(
State(state): State<Arc<AppState>>,
axum::Extension(user): axum::Extension<CurrentUser>,
Json(request): Json<CreateAppPasswordRequestDto>,
) -> Result<Json<crate::application::dtos::app_password_dto::AppPasswordCreatedResponseDto>, AppError>
{
let service = state
.app_password_service
.as_ref()
.ok_or_else(|| AppError::internal_error("App password service not configured"))?;
let response = service
.create(&user.id, request)
.await
.map_err(AppError::from)?;
Ok(Json(response))
}
/// GET /api/auth/app-passwords — List all app passwords for the current user.
///
/// Never returns plain-text passwords (only prefix + metadata).
async fn list_app_passwords(
State(state): State<Arc<AppState>>,
axum::Extension(user): axum::Extension<CurrentUser>,
) -> Result<Json<crate::application::dtos::app_password_dto::AppPasswordListResponseDto>, AppError>
{
let service = state
.app_password_service
.as_ref()
.ok_or_else(|| AppError::internal_error("App password service not configured"))?;
let response = service.list(&user.id).await.map_err(AppError::from)?;
Ok(Json(response))
}
/// DELETE /api/auth/app-passwords/:id — Revoke an app password.
async fn revoke_app_password(
State(state): State<Arc<AppState>>,
axum::Extension(user): axum::Extension<CurrentUser>,
axum::extract::Path(id): axum::extract::Path<String>,
) -> Result<Json<crate::application::dtos::app_password_dto::AppPasswordRevokeResponseDto>, AppError>
{
let service = state
.app_password_service
.as_ref()
.ok_or_else(|| AppError::internal_error("App password service not configured"))?;
let response = service
.revoke(&user.id, &id)
.await
.map_err(AppError::from)?;
Ok(Json(response))
}
+1 -1
View File
@@ -150,7 +150,7 @@ async fn login(
);
return Err(AppError::new(
StatusCode::TOO_MANY_REQUESTS,
&format!(
format!(
"Account temporarily locked due to too many failed attempts. Try again in {} seconds.",
lockout_secs
),
@@ -32,10 +32,10 @@ use crate::application::dtos::calendar_dto::{
CreateCalendarDto, CreateEventICalDto, UpdateCalendarDto,
};
use crate::application::ports::calendar_ports::CalendarUseCase;
use crate::application::services::calendar_service::CalendarService;
use crate::common::di::AppState;
use crate::interfaces::errors::AppError;
use crate::interfaces::middleware::auth::CurrentUser;
use crate::application::services::calendar_service::CalendarService;
const HEADER_DAV: HeaderName = HeaderName::from_static("dav");
@@ -33,9 +33,9 @@ use crate::application::dtos::address_book_dto::{CreateAddressBookDto, UpdateAdd
use crate::application::dtos::contact_dto::CreateContactVCardDto;
use crate::application::ports::carddav_ports::{AddressBookUseCase, ContactUseCase};
use crate::common::di::AppState;
use crate::infrastructure::adapters::contact_storage_adapter::ContactStorageAdapter;
use crate::interfaces::errors::AppError;
use crate::interfaces::middleware::auth::CurrentUser;
use crate::infrastructure::adapters::contact_storage_adapter::ContactStorageAdapter;
const HEADER_DAV: HeaderName = HeaderName::from_static("dav");
@@ -17,13 +17,13 @@ use bytes::Bytes;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use crate::application::ports::chunked_upload_ports::ChunkedUploadPort;
use crate::application::ports::chunked_upload_ports::DEFAULT_CHUNK_SIZE;
use crate::application::ports::file_ports::FileUploadUseCase;
use crate::application::ports::storage_ports::StorageUsagePort;
use crate::common::di::AppState;
use crate::domain::errors::ErrorKind;
use crate::interfaces::middleware::auth::AuthUser;
use crate::application::ports::chunked_upload_ports::ChunkedUploadPort;
use crate::application::ports::file_ports::FileUploadUseCase;
use crate::application::ports::storage_ports::StorageUsagePort;
/// Request body for creating an upload session
#[derive(Debug, Deserialize)]
@@ -9,8 +9,8 @@ use std::sync::Arc;
use tracing::{error, info};
use crate::application::ports::favorites_ports::FavoritesUseCase;
use crate::interfaces::middleware::auth::AuthUser;
use crate::application::services::favorites_service::FavoritesService;
use crate::interfaces::middleware::auth::AuthUser;
/// Single item in a batch-add-favorites request.
#[derive(Debug, Deserialize)]
+15 -6
View File
@@ -11,12 +11,14 @@ use serde::Deserialize;
use std::collections::HashMap;
use crate::application::ports::file_ports::OptimizedFileContent;
use crate::application::ports::file_ports::{
FileManagementUseCase, FileRetrievalUseCase, FileUploadUseCase,
};
use crate::application::ports::storage_ports::StorageUsagePort;
use crate::application::ports::thumbnail_ports::ThumbnailPort;
use crate::common::di::AppState;
use crate::interfaces::middleware::auth::AuthUser;
use std::sync::Arc;
use crate::application::ports::file_ports::{FileManagementUseCase, FileRetrievalUseCase, FileUploadUseCase};
use crate::application::ports::storage_ports::StorageUsagePort;
use crate::application::ports::thumbnail_ports::ThumbnailPort;
/**
* Type aliases for dependency injection state.
@@ -283,7 +285,10 @@ impl FileHandler {
}
};
let file = match file_retrieval_service.get_file_owned(&id, &auth_user.id).await {
let file = match file_retrieval_service
.get_file_owned(&id, &auth_user.id)
.await
{
Ok(f) => f,
Err(err) => {
return (
@@ -678,7 +683,8 @@ impl FileHandler {
let mgmt = &state.applications.file_management_service;
// Auth required: trash-first with dedup cleanup + ownership verification
let result = mgmt.delete_with_cleanup(&id, &auth_user.id)
let result = mgmt
.delete_with_cleanup(&id, &auth_user.id)
.await
.map(|was_trashed| {
if was_trashed {
@@ -771,7 +777,10 @@ impl FileHandler {
let mgmt = &state.applications.file_management_service;
match mgmt.move_file_owned(&id, &auth_user.id, payload.folder_id).await {
match mgmt
.move_file_owned(&id, &auth_user.id, payload.folder_id)
.await
{
Ok(file) => (StatusCode::OK, Json(file)).into_response(),
Err(err) => {
tracing::error!("Error moving file: {}", err);
@@ -13,13 +13,13 @@ use tokio_util::io::ReaderStream;
use crate::application::dtos::folder_dto::{CreateFolderDto, MoveFolderDto, RenameFolderDto};
use crate::application::dtos::folder_listing_dto::FolderListingDto;
use crate::application::dtos::pagination::PaginationRequestDto;
use crate::application::ports::file_ports::FileRetrievalUseCase;
use crate::application::ports::inbound::FolderUseCase;
use crate::application::ports::trash_ports::TrashUseCase;
use crate::application::services::folder_service::FolderService;
use crate::common::di::AppState as GlobalAppState;
use crate::common::errors::ErrorKind;
use crate::interfaces::middleware::auth::AuthUser;
use crate::application::ports::file_ports::FileRetrievalUseCase;
use crate::application::ports::trash_ports::TrashUseCase;
type AppState = Arc<FolderService>;
@@ -9,8 +9,8 @@ use std::sync::Arc;
use tracing::{error, info};
use crate::application::ports::recent_ports::RecentItemsUseCase;
use crate::interfaces::middleware::auth::AuthUser;
use crate::application::services::recent_service::RecentService;
use crate::interfaces::middleware::auth::AuthUser;
/// Query parameters for getting recent items
#[derive(Deserialize)]
@@ -7,10 +7,10 @@ use serde_json::json;
use tracing::{error, info};
use crate::application::dtos::search_dto::SearchCriteriaDto;
use crate::application::ports::inbound::SearchUseCase;
use crate::common::di::AppState;
use crate::interfaces::middleware::auth::AuthUser;
use std::sync::Arc;
use crate::application::ports::inbound::SearchUseCase;
/**
* Handler for search operations through the API.
+1 -1
View File
@@ -9,6 +9,7 @@ use axum::{
use serde::Deserialize;
use serde_json::json;
use crate::application::services::share_service::ShareService;
use crate::{
application::{
dtos::share_dto::{CreateShareDto, UpdateShareDto},
@@ -18,7 +19,6 @@ use crate::{
domain::entities::share::ShareItemType,
interfaces::middleware::auth::OptionalAuthUser,
};
use crate::application::services::share_service::ShareService;
#[derive(Debug, Deserialize)]
pub struct GetSharesQuery {
@@ -17,22 +17,20 @@ use chrono::Utc;
use quick_xml::Writer;
use uuid::Uuid;
use crate::application::adapters::webdav_adapter::{
LockInfo, PropFindRequest, WebDavAdapter,
};
use crate::application::adapters::webdav_adapter::{LockInfo, PropFindRequest, WebDavAdapter};
use crate::application::dtos::file_dto::FileDto;
use crate::application::dtos::folder_dto::FolderDto;
use crate::application::ports::file_ports::FileRetrievalUseCase;
use crate::application::ports::file_ports::{FileManagementUseCase, FileUploadUseCase};
use crate::application::ports::inbound::FolderUseCase;
use crate::application::services::file_retrieval_service::FileRetrievalService;
use crate::application::services::folder_service::FolderService;
use crate::common::di::AppState;
use crate::infrastructure::services::path_resolver_service::ResolvedResource;
use crate::interfaces::errors::AppError;
use crate::interfaces::middleware::auth::CurrentUser;
use crate::application::services::file_retrieval_service::FileRetrievalService;
use crate::application::services::folder_service::FolderService;
use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, percent_decode_str, utf8_percent_encode};
use std::sync::Arc;
use crate::application::ports::file_ports::{FileManagementUseCase, FileUploadUseCase};
/// Characters that MUST NOT be percent-encoded inside a URI path segment.
/// RFC 3986 §3.3 pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
@@ -65,7 +63,7 @@ fn encode_path_segment(segment: &str) -> String {
/// Percent-encode a full slash-separated path, encoding each segment individually.
pub(crate) fn encode_uri_path(path: &str) -> String {
path.split('/')
.map(|seg| encode_path_segment(seg))
.map(encode_path_segment)
.collect::<Vec<_>>()
.join("/")
}
+1 -1
View File
@@ -20,10 +20,10 @@ use axum::{
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use crate::application::ports::file_ports::{FileRetrievalUseCase, FileUploadUseCase};
use crate::application::services::wopi_lock_service::WopiLockService;
use crate::application::services::wopi_token_service::WopiTokenService;
use crate::infrastructure::services::wopi_discovery_service::WopiDiscoveryService;
use crate::application::ports::file_ports::{FileRetrievalUseCase, FileUploadUseCase};
/// Shared state for WOPI handlers.
#[derive(Clone)]
+1 -2
View File
@@ -1,3 +1,4 @@
use crate::application::services::batch_operations::BatchOperationService;
use crate::common::di::AppState;
use axum::{
Router,
@@ -8,7 +9,6 @@ use axum::{
use serde_json::json;
use std::sync::Arc;
use tower_http::{compression::CompressionLayer, trace::TraceLayer};
use crate::application::services::batch_operations::BatchOperationService;
/// Returns the application version from Cargo.toml (compile-time constant)
async fn get_version() -> AxumJson<serde_json::Value> {
@@ -18,7 +18,6 @@ async fn get_version() -> AxumJson<serde_json::Value> {
}))
}
use crate::interfaces::api::handlers::admin_handler;
use crate::interfaces::api::handlers::batch_handler::{self, BatchHandlerState};
use crate::interfaces::api::handlers::chunked_upload_handler::ChunkedUploadHandler;