// Используем модули из библиотеки use quoter::{app_state, handlers, security}; use actix_cors::Cors; use actix_web::{ App, HttpServer, http::header, middleware::{DefaultHeaders, Logger}, web, }; use app_state::AppState; use security::SecurityConfig; use handlers::universal_handler; use log::{info, warn}; use std::env; use tokio::task::spawn_blocking; #[actix_web::main] async fn main() -> std::io::Result<()> { env_logger::init(); warn!("Started"); let port = env::var("PORT").unwrap_or_else(|_| "8080".to_string()); let addr = format!("0.0.0.0:{}", port); let app_state = AppState::new().await; let app_state_clone = app_state.clone(); // Используем spawn_blocking для работы, которая не совместима с Send spawn_blocking(move || { let rt = tokio::runtime::Handle::current(); rt.block_on(async move { app_state_clone.cache_filelist().await; }); }); // Запускаем периодическую очистку кэша app_state.start_cache_cleanup_task(); // Конфигурация безопасности let security_config = SecurityConfig::default(); info!( "Security config: max_payload={} MB, timeout={}s", security_config.max_payload_size / (1024 * 1024), security_config.request_timeout_seconds ); HttpServer::new(move || { // Настройка CORS middleware - более гибкая для разработки let cors = Cors::default() .allowed_origin("https://discours.io") .allowed_origin("https://new.discours.io") .allowed_origin("https://testing.discours.io") .allowed_origin("http://localhost:3000") .allowed_origin("https://localhost:3000") // HTTPS для разработки .allowed_origin("https://files.dscrs.site") // Добавляем домен файлов .allowed_methods(vec!["GET", "POST", "OPTIONS"]) .allowed_headers(vec![ header::CONTENT_TYPE, header::AUTHORIZATION, header::IF_NONE_MATCH, header::CACHE_CONTROL, header::RANGE, // Для аудио стриминга ]) .expose_headers(vec![ header::CONTENT_LENGTH, header::ETAG, header::CONTENT_RANGE, // Для аудио стриминга header::ACCEPT_RANGES, ]) .supports_credentials() .max_age(86400); // 1 день // Заголовки безопасности let security_headers = DefaultHeaders::new() .add(("X-Content-Type-Options", "nosniff")) .add(("X-Frame-Options", "DENY")) .add(("X-XSS-Protection", "1; mode=block")) .add(("Referrer-Policy", "strict-origin-when-cross-origin")) .add(( "Content-Security-Policy", "default-src 'self'; img-src 'self' data: https:; object-src 'none';", )) .add(( "Strict-Transport-Security", "max-age=31536000; includeSubDomains", )); App::new() .app_data(web::Data::new(app_state.clone())) .app_data(web::PayloadConfig::new(security_config.max_payload_size)) .app_data(web::JsonConfig::default().limit(1024 * 1024)) // 1MB для JSON .wrap(security_headers) .wrap(cors) .wrap(Logger::default()) .default_service(web::to(universal_handler)) }) .bind(addr)? .run() .await }