[0.6.1] - 2025-09-02
Some checks failed
CI / lint (push) Successful in 2m11s
Deploy / deploy (push) Has been skipped
CI / test (push) Failing after 9m6s

### 🚀 Изменено - Упрощение архитектуры
- **Генерация миниатюр**: Полностью удалена из Quoter, теперь управляется Vercel Edge API
- **Очистка legacy кода**: Удалены все функции генерации миниатюр и сложность
- **Документация**: Сокращена с 17 файлов до 7, следуя принципам KISS/DRY
- **Смена фокуса**: Quoter теперь сосредоточен на upload + storage, Vercel обрабатывает миниатюры
- **Логирование запросов**: Добавлена аналитика источников для оптимизации CORS whitelist
- **Реализация таймаутов**: Добавлены настраиваемые таймауты для S3, Redis и внешних операций
- **Упрощенная безопасность**: Удален сложный rate limiting, оставлена только необходимая защита upload
- **Vercel интеграция**: Добавлена поддержка Vercel Edge API с CORS и оптимизированными заголовками
- **Redis graceful fallback**: Приложение теперь работает без Redis с предупреждениями вместо паники
- **Умная логика ответов**: Автоматическое определение Vercel запросов и оптимизированные заголовки
- **Консолидация документации**: Объединены 4 Vercel документа в один comprehensive guide

### 📝 Обновлено
- Консолидирована документация в практическую структуру:
  - Основной README.md с быстрым стартом
  - docs/SETUP.md для конфигурации и развертывания
  - Упрощенный features.md с фокусом на основную функциональность
  - docs/vercel-frontend-migration.md - единый comprehensive guide для Vercel интеграции
- Добавлен акцент на Vercel по всему коду и документации
- Обновлены URL patterns в документации: quoter.discours.io → files.dscrs.site

### 🗑️ Удалено
- Избыточные файлы документации (api-reference, deployment, development, и т.д.)
- Дублирующийся контент в нескольких документах
- Излишне детальная документация для простого файлового прокси
- 4 отдельных Vercel документа: vercel-thumbnails.md, vercel-integration.md, hybrid-architecture.md, vercel-og-integration.md

💋 **Упрощение**: KISS принцип применен - убрали избыточность, оставили суть.
This commit is contained in:
2025-09-02 14:39:54 +03:00
parent 7973ba0027
commit 7746d1f38e
21 changed files with 458 additions and 863 deletions

View File

@@ -35,7 +35,7 @@ impl Default for SecurityConfig {
fn default() -> Self {
Self {
max_payload_size: 500 * 1024 * 1024, // 500MB
request_timeout_seconds: 300, // 5 минут
request_timeout_seconds: 300, // 5 минут
max_path_length: 1000,
max_headers_count: 50,
max_header_value_length: 8192,
@@ -51,7 +51,7 @@ impl SecurityConfig {
/// Валидирует запрос на базовые параметры безопасности
pub fn validate_request(&self, req: &HttpRequest) -> Result<(), actix_web::Error> {
let path = req.path();
// Проверка длины пути
if path.len() > self.max_path_length {
warn!("Path too long: {} chars", path.len());
@@ -79,7 +79,8 @@ impl SecurityConfig {
}
// Проверка на подозрительные символы в пути
if path.contains("..") || path.contains('\0') || path.contains('\r') || path.contains('\n') {
if path.contains("..") || path.contains('\0') || path.contains('\r') || path.contains('\n')
{
warn!("Suspicious characters in path: {}", path);
return Err(actix_web::error::ErrorBadRequest(
"Invalid characters in path",
@@ -98,7 +99,7 @@ impl SecurityConfig {
pub fn check_suspicious_patterns(&self, path: &str) -> bool {
let suspicious_patterns = [
"/admin",
"/wp-admin",
"/wp-admin",
"/phpmyadmin",
"/.env",
"/config",
@@ -136,26 +137,34 @@ impl SecurityConfig {
.as_secs();
let mut counts = self.upload_protection.upload_counts.write().await;
// Очищаем старые записи (старше минуты)
counts.retain(|_, (_, timestamp)| current_time - *timestamp < 60);
// Проверяем текущий IP
let current_count = counts.get(ip).map(|(count, _)| *count).unwrap_or(0);
let first_upload_time = counts.get(ip).map(|(_, time)| *time).unwrap_or(current_time);
let first_upload_time = counts
.get(ip)
.map(|(_, time)| *time)
.unwrap_or(current_time);
if current_time - first_upload_time < 60 {
// В пределах минуты
if current_count >= self.upload_protection.max_uploads_per_minute {
warn!("Upload limit exceeded for IP {}: {} uploads in minute", ip, current_count);
return Err(actix_web::error::ErrorTooManyRequests("Upload limit exceeded"));
warn!(
"Upload limit exceeded for IP {}: {} uploads in minute",
ip, current_count
);
return Err(actix_web::error::ErrorTooManyRequests(
"Upload limit exceeded",
));
}
counts.insert(ip.to_string(), (current_count + 1, first_upload_time));
} else {
// Новая минута, сбрасываем счетчик
counts.insert(ip.to_string(), (1, current_time));
}
Ok(())
}
@@ -169,16 +178,18 @@ impl SecurityConfig {
}
}
}
// Проверяем X-Real-IP
if let Some(real_ip) = req.headers().get("x-real-ip") {
if let Ok(real_ip_str) = real_ip.to_str() {
return real_ip_str.to_string();
}
}
// Fallback на connection info
req.connection_info().peer_addr().unwrap_or("unknown").to_string()
req.connection_info()
.peer_addr()
.unwrap_or("unknown")
.to_string()
}
}