[0.6.1] - 2025-09-02
### 🚀 Изменено - Упрощение архитектуры - **Генерация миниатюр**: Полностью удалена из 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:
82
src/auth.rs
82
src/auth.rs
@@ -87,7 +87,7 @@ pub fn validate_token(token: &str) -> Result<bool, Box<dyn Error>> {
|
||||
/// Получает user_id из JWT токена и базовые данные пользователя с таймаутом
|
||||
pub async fn get_user_by_token(
|
||||
token: &str,
|
||||
redis: &mut MultiplexedConnection,
|
||||
mut redis: Option<&mut MultiplexedConnection>,
|
||||
timeout: Duration,
|
||||
) -> Result<Author, Box<dyn Error>> {
|
||||
// Декодируем JWT токен для получения user_id
|
||||
@@ -97,42 +97,50 @@ pub async fn get_user_by_token(
|
||||
info!("Extracted user_id from JWT token: {}", user_id);
|
||||
|
||||
// Проверяем валидность токена через сессию в Redis (опционально) с таймаутом
|
||||
let token_key = format!("session:{}:{}", user_id, token);
|
||||
let session_exists: bool = tokio::time::timeout(timeout, redis.exists(&token_key))
|
||||
.await
|
||||
.map_err(|_| {
|
||||
warn!("Redis timeout checking session existence");
|
||||
// Не критичная ошибка, продолжаем с базовыми данными
|
||||
})
|
||||
.unwrap_or(Ok(false))
|
||||
.map_err(|e| {
|
||||
warn!("Failed to check session existence in Redis: {}", e);
|
||||
// Не критичная ошибка, продолжаем с базовыми данными
|
||||
})
|
||||
.unwrap_or(false);
|
||||
let session_exists = if let Some(ref mut redis) = redis {
|
||||
let token_key = format!("session:{}:{}", user_id, token);
|
||||
tokio::time::timeout(timeout, redis.exists(&token_key))
|
||||
.await
|
||||
.map_err(|_| {
|
||||
warn!("Redis timeout checking session existence");
|
||||
// Не критичная ошибка, продолжаем с базовыми данными
|
||||
})
|
||||
.unwrap_or(Ok(false))
|
||||
.map_err(|e| {
|
||||
warn!("Failed to check session existence in Redis: {}", e);
|
||||
// Не критичная ошибка, продолжаем с базовыми данными
|
||||
})
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
warn!("⚠️ Redis not available, skipping session validation");
|
||||
false
|
||||
};
|
||||
|
||||
if session_exists {
|
||||
// Обновляем last_activity если сессия существует
|
||||
let current_time = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
if let Some(redis) = redis {
|
||||
let current_time = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
let _: () = tokio::time::timeout(
|
||||
timeout,
|
||||
redis.hset(&token_key, "last_activity", current_time.to_string()),
|
||||
)
|
||||
.await
|
||||
.map_err(|_| {
|
||||
warn!("Redis timeout updating last_activity");
|
||||
})
|
||||
.unwrap_or(Ok(()))
|
||||
.map_err(|e| {
|
||||
warn!("Failed to update last_activity: {}", e);
|
||||
})
|
||||
.unwrap_or(());
|
||||
let token_key = format!("session:{}:{}", user_id, token);
|
||||
let _: () = tokio::time::timeout(
|
||||
timeout,
|
||||
redis.hset(&token_key, "last_activity", current_time.to_string()),
|
||||
)
|
||||
.await
|
||||
.map_err(|_| {
|
||||
warn!("Redis timeout updating last_activity");
|
||||
})
|
||||
.unwrap_or(Ok(()))
|
||||
.map_err(|e| {
|
||||
warn!("Failed to update last_activity: {}", e);
|
||||
})
|
||||
.unwrap_or(());
|
||||
}
|
||||
|
||||
info!("Updated last_activity for session: {}", token_key);
|
||||
info!("Updated last_activity for session: {}", user_id);
|
||||
} else {
|
||||
info!("Session not found in Redis, proceeding with JWT-only data");
|
||||
}
|
||||
@@ -160,10 +168,18 @@ pub async fn get_user_by_token(
|
||||
|
||||
/// Сохраняет имя файла в Redis для пользователя
|
||||
pub async fn user_added_file(
|
||||
redis: &mut MultiplexedConnection,
|
||||
redis: Option<&mut MultiplexedConnection>,
|
||||
user_id: &str,
|
||||
filename: &str,
|
||||
) -> Result<(), actix_web::Error> {
|
||||
let Some(redis) = redis else {
|
||||
log::warn!(
|
||||
"⚠️ Redis not available, skipping file tracking for user {}",
|
||||
user_id
|
||||
);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
redis
|
||||
.sadd::<&str, &str, ()>(user_id, filename)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user