Files
Oxicloud/src/main.rs
T

334 lines
13 KiB
Rust
Raw Normal View History

2025-03-17 21:28:08 +01:00
use std::net::SocketAddr;
use std::path::PathBuf;
use std::sync::Arc;
use axum::Router;
use tower_http::trace::TraceLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
2025-03-19 00:44:27 +01:00
mod common;
2025-03-17 21:28:08 +01:00
mod domain;
mod application;
mod infrastructure;
mod interfaces;
use application::services::folder_service::FolderService;
use application::services::file_service::FileService;
use application::services::i18n_application_service::I18nApplicationService;
2025-03-19 00:44:27 +01:00
use application::services::storage_mediator::FileSystemStorageMediator;
use domain::services::path_service::PathService;
2025-03-17 21:28:08 +01:00
use infrastructure::repositories::folder_fs_repository::FolderFsRepository;
use infrastructure::repositories::file_fs_repository::FileFsRepository;
2025-03-19 00:44:27 +01:00
use infrastructure::repositories::parallel_file_processor::ParallelFileProcessor;
2025-03-17 21:28:08 +01:00
use infrastructure::services::file_system_i18n_service::FileSystemI18nService;
2025-03-19 00:44:27 +01:00
use infrastructure::services::id_mapping_service::IdMappingService;
use infrastructure::services::id_mapping_optimizer::IdMappingOptimizer;
use infrastructure::services::file_metadata_cache::FileMetadataCache;
use infrastructure::services::buffer_pool::BufferPool;
use infrastructure::services::compression_service::GzipCompressionService;
2025-03-17 21:28:08 +01:00
use interfaces::{create_api_routes, web::create_web_routes};
2025-03-20 09:22:31 +01:00
use common::db::create_database_pool;
use common::auth_factory::create_auth_services;
use common::di::AppState;
2025-03-17 21:28:08 +01:00
#[tokio::main]
2025-03-20 09:22:31 +01:00
async fn main() -> Result<(), Box<dyn std::error::Error>> {
2025-03-17 21:28:08 +01:00
// Initialize tracing
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(
std::env::var("RUST_LOG").unwrap_or_else(|_| "info".into()),
))
.with(tracing_subscriber::fmt::layer())
.init();
2025-03-20 09:22:31 +01:00
// Load configuration from environment variables
let config = common::config::AppConfig::from_env();
2025-03-17 21:28:08 +01:00
// Set up storage directory
2025-03-20 09:22:31 +01:00
let storage_path = config.storage_path.clone();
2025-03-17 21:28:08 +01:00
if !storage_path.exists() {
std::fs::create_dir_all(&storage_path).expect("Failed to create storage directory");
}
// Set up locales directory
let locales_path = PathBuf::from("./static/locales");
if !locales_path.exists() {
std::fs::create_dir_all(&locales_path).expect("Failed to create locales directory");
}
2025-03-20 09:22:31 +01:00
// Initialize database if auth is enabled
let db_pool = if config.features.enable_auth {
match create_database_pool(&config).await {
Ok(pool) => {
tracing::info!("PostgreSQL database pool initialized successfully");
Some(Arc::new(pool))
},
Err(e) => {
tracing::error!("Failed to initialize database pool: {}", e);
None
}
}
} else {
None
};
2025-03-17 21:28:08 +01:00
2025-03-19 00:44:27 +01:00
// Initialize path service
let path_service = Arc::new(PathService::new(storage_path.clone()));
2025-03-23 22:44:18 +01:00
// Initialize ID mapping service for folders
let folder_id_mapping_path = storage_path.join("folder_ids.json");
let folder_id_mapping_service = Arc::new(
IdMappingService::new(folder_id_mapping_path).await
.expect("Failed to initialize folder ID mapping service")
2025-03-19 00:44:27 +01:00
);
2025-03-23 22:44:18 +01:00
// Initialize ID mapping service for files
let file_id_mapping_path = storage_path.join("file_ids.json");
let file_id_mapping_service = Arc::new(
IdMappingService::new(file_id_mapping_path).await
.expect("Failed to initialize file ID mapping service")
);
// For backward compatibility, use folder ID service as the base ID mapping service
let base_id_mapping_service = folder_id_mapping_service.clone();
2025-03-19 00:44:27 +01:00
// Create optimized ID mapping service with batch processing and caching
let id_mapping_optimizer = Arc::new(
IdMappingOptimizer::new(base_id_mapping_service.clone())
);
// Initialize folder repository with all required components
let folder_repository = Arc::new(FolderFsRepository::new(
storage_path.clone(),
Arc::new(FileSystemStorageMediator::new_stub()), // Temporary stub (will be replaced)
base_id_mapping_service.clone(),
path_service.clone()
));
// Initialize storage mediator
let storage_mediator = Arc::new(FileSystemStorageMediator::new(
folder_repository.clone(),
path_service.clone(),
id_mapping_optimizer.clone()
));
// Update folder repository with proper storage mediator
// This replaces the stub we initialized it with
let folder_repository = Arc::new(FolderFsRepository::new(
storage_path.clone(),
storage_mediator.clone(),
base_id_mapping_service.clone(),
path_service.clone()
));
// Start cleanup task for ID mapping optimizer
IdMappingOptimizer::start_cleanup_task(id_mapping_optimizer.clone());
tracing::info!("ID mapping optimizer initialized with batch processing and caching");
// Initialize the metadata cache
let config = common::config::AppConfig::default();
let metadata_cache = Arc::new(FileMetadataCache::default_with_config(config.clone()));
// Start the periodic cleanup task for cache maintenance
let cache_clone = metadata_cache.clone();
tokio::spawn(async move {
FileMetadataCache::start_cleanup_task(cache_clone).await;
});
// Initialize the buffer pool for memory optimization
// Use larger buffer size for better performance with large files
let buffer_pool = BufferPool::new(256 * 1024, 50, 120); // 256KB buffers, 50 max, 2 min TTL
// Start the buffer pool cleanup task
BufferPool::start_cleaner(buffer_pool.clone());
tracing::info!("Buffer pool initialized with 50 buffers of 256KB each");
// Initialize parallel file processor with buffer pool
let parallel_processor = Arc::new(ParallelFileProcessor::new_with_buffer_pool(
config.clone(),
buffer_pool.clone()
));
// Initialize compression service with buffer pool
let _compression_service = Arc::new(GzipCompressionService::new_with_buffer_pool(
buffer_pool.clone()
));
// Initialize file repository with mediator, ID mapping service, metadata cache, and parallel processor
let file_repository = Arc::new(FileFsRepository::new_with_processor(
storage_path.clone(),
storage_mediator,
2025-03-23 22:44:18 +01:00
file_id_mapping_service.clone(), // Use the file-specific ID mapping service
2025-03-19 00:44:27 +01:00
path_service.clone(),
metadata_cache.clone(), // Clone to keep a reference for later use
parallel_processor
));
2025-03-17 21:28:08 +01:00
2025-03-19 00:44:27 +01:00
// Initialize application services
2025-03-17 21:28:08 +01:00
let folder_service = Arc::new(FolderService::new(folder_repository));
let file_service = Arc::new(FileService::new(file_repository));
// Initialize i18n service
2025-03-20 09:22:31 +01:00
let i18n_repository = Arc::new(FileSystemI18nService::new(locales_path.clone()));
let i18n_service = Arc::new(I18nApplicationService::new(i18n_repository.clone()));
2025-03-17 21:28:08 +01:00
// Preload translations
if let Err(e) = i18n_service.load_translations(domain::services::i18n_service::Locale::English).await {
tracing::warn!("Failed to load English translations: {}", e);
}
if let Err(e) = i18n_service.load_translations(domain::services::i18n_service::Locale::Spanish).await {
tracing::warn!("Failed to load Spanish translations: {}", e);
}
2025-03-19 00:44:27 +01:00
tracing::info!("Compression service initialized with buffer pool support");
2025-03-20 09:22:31 +01:00
// Initialize auth services if enabled and database connection is available
let auth_services = if config.features.enable_auth && db_pool.is_some() {
2025-03-23 22:44:18 +01:00
match create_auth_services(
&config,
db_pool.as_ref().unwrap().clone(),
Some(folder_service.clone()) // Pasar el servicio de carpetas para creación automática de carpetas de usuario
).await {
2025-03-20 09:22:31 +01:00
Ok(services) => {
2025-03-23 22:44:18 +01:00
tracing::info!("Authentication services initialized successfully with folder service");
2025-03-20 09:22:31 +01:00
Some(services)
},
Err(e) => {
tracing::error!("Failed to initialize authentication services: {}", e);
None
}
}
} else {
None
};
// Create AppState for DI container
let core_services = common::di::CoreServices {
path_service: path_service.clone(),
cache_manager: Arc::new(infrastructure::services::cache_manager::StorageCacheManager::default()),
2025-03-23 22:44:18 +01:00
id_mapping_service: base_id_mapping_service.clone(), // We keep using the folder ID mapping service for core services
2025-03-20 09:22:31 +01:00
config: config.clone(),
};
// Crear stubs para los repositorios
let file_read_stub = Arc::new(infrastructure::repositories::FileFsReadRepository::default_stub());
let file_write_stub = Arc::new(infrastructure::repositories::FileFsWriteRepository::default_stub());
let storage_mediator_stub = Arc::new(application::services::storage_mediator::FileSystemStorageMediator::new_stub());
let metadata_manager = Arc::new(infrastructure::repositories::FileMetadataManager::default());
let path_resolver_stub = Arc::new(infrastructure::repositories::FilePathResolver::default_stub());
let repository_services = common::di::RepositoryServices {
folder_repository: Arc::new(FolderFsRepository::new(
storage_path.clone(),
storage_mediator_stub.clone(),
2025-03-23 22:44:18 +01:00
folder_id_mapping_service.clone(),
2025-03-20 09:22:31 +01:00
path_service.clone()
)),
file_repository: Arc::new(FileFsRepository::new(
storage_path.clone(),
storage_mediator_stub.clone(),
2025-03-23 22:44:18 +01:00
file_id_mapping_service.clone(),
2025-03-20 09:22:31 +01:00
path_service.clone(),
metadata_cache.clone(),
)),
file_read_repository: file_read_stub,
file_write_repository: file_write_stub,
i18n_repository: i18n_repository.clone(),
storage_mediator: storage_mediator_stub,
metadata_manager,
path_resolver: path_resolver_stub,
};
let application_services = common::di::ApplicationServices {
folder_service: folder_service.clone(),
file_service: file_service.clone(),
file_upload_service: Arc::new(application::services::file_upload_service::FileUploadService::default_stub()),
file_retrieval_service: Arc::new(application::services::file_retrieval_service::FileRetrievalService::default_stub()),
file_management_service: Arc::new(application::services::file_management_service::FileManagementService::default_stub()),
file_use_case_factory: Arc::new(application::services::file_use_case_factory::AppFileUseCaseFactory::default_stub()),
i18n_service: i18n_service.clone(),
};
// Create the AppState without Arc first
let mut app_state = AppState::new(
core_services,
repository_services,
application_services,
);
// Add database pool if available
if let Some(pool) = db_pool {
app_state = app_state.with_database(pool);
}
// Add auth services if available
let have_auth_services = auth_services.is_some();
if let Some(services) = auth_services {
app_state = app_state.with_auth_services(services);
}
// Wrap in Arc after all modifications
let app_state = Arc::new(app_state);
2025-03-17 21:28:08 +01:00
// Build application router
let api_routes = create_api_routes(folder_service, file_service, Some(i18n_service));
let web_routes = create_web_routes();
2025-03-20 09:22:31 +01:00
// Build the app router
// Import auth handler
use interfaces::api::handlers::auth_handler::auth_routes;
// Create basic app router
let mut app = Router::new()
2025-03-17 21:28:08 +01:00
.nest("/api", api_routes)
.merge(web_routes)
.layer(TraceLayer::new_for_http());
2025-03-20 09:22:31 +01:00
// Add auth routes if auth is enabled
if config.features.enable_auth && have_auth_services {
// Create auth routes with app state
let auth_router = auth_routes().with_state(app_state.clone());
// Add auth routes at /api/auth
app = app.nest("/api/auth", auth_router);
}
2025-03-17 21:28:08 +01:00
2025-03-19 00:44:27 +01:00
// Preload common directories to warm the cache
tracing::info!("Preloading common directories to warm up cache...");
if let Ok(count) = metadata_cache.preload_directory(&storage_path, true, 1).await {
tracing::info!("Preloaded {} directory entries into cache", count);
}
2025-03-20 09:22:31 +01:00
// Start server with clear message
2025-03-23 22:44:18 +01:00
let addr = SocketAddr::from(([127, 0, 0, 1], 8086));
2025-03-20 09:22:31 +01:00
tracing::info!("Starting OxiCloud server on http://{}", addr);
// Start the server
tracing::info!("Authentication system initialized successfully");
2025-03-23 22:44:18 +01:00
// Import the redirect middleware
use crate::interfaces::middleware::redirect::redirect_middleware;
2025-03-20 09:22:31 +01:00
2025-03-23 22:44:18 +01:00
// Apply the redirect middleware to handle legacy routes
app = app.layer(axum::middleware::from_fn(redirect_middleware));
2025-03-17 21:28:08 +01:00
2025-03-23 22:44:18 +01:00
// Create a standard TCP listener
let listener = tokio::net::TcpListener::bind(addr).await?;
tracing::info!("Server binding to http://{}", addr);
tracing::info!("Starting server with Axum routes...");
2025-03-20 09:22:31 +01:00
2025-03-23 22:44:18 +01:00
// For Axum 0.8, we need to properly handle state
// Add global state to the router
let app = app.with_state(app_state);
2025-03-20 09:22:31 +01:00
2025-03-23 22:44:18 +01:00
// Use axum's serve function with the router with state
axum::serve(listener, app).await?;
2025-03-20 09:22:31 +01:00
tracing::info!("Server shutdown completed");
Ok(())
}