Add SECURITY_TRIAGE.md for vulnerability assessment and remediation plan

- Document current state of vulnerabilities in Rust and frontend dependencies
- Outline active vulnerabilities and their remediation status
- Include notes on resolved issues and remaining bugs
- Define a remediation plan with prioritized actions
This commit is contained in:
2026-04-28 15:47:20 -04:00
parent 2c8bfaa20e
commit 42620cc9ac
42 changed files with 2032 additions and 1869 deletions
+43 -41
View File
@@ -33,7 +33,6 @@ use reqwest::header::HeaderMap;
use uuid::Uuid;
use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType};
use openidconnect::reqwest::async_http_client;
use openidconnect::{
AuthenticationFlow, AuthorizationCode, ClientId, ClientSecret, CsrfToken, IssuerUrl, Nonce,
RedirectUrl, Scope, TokenResponse,
@@ -606,7 +605,7 @@ pub async fn update_course(
let mut tx = pool
.begin()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Establecer contexto de auditoría
sqlx::query(
@@ -616,7 +615,7 @@ pub async fn update_course(
.bind(org_ctx.id.to_string())
.execute(&mut *tx)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let course = sqlx::query_as::<_, Course>(
"SELECT * FROM fn_update_course($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)",
@@ -645,7 +644,7 @@ pub async fn update_course(
tx.commit()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(course))
}
@@ -1756,7 +1755,7 @@ pub async fn get_grading_categories(
.bind(org_ctx.id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(categories))
}
@@ -1779,7 +1778,7 @@ pub async fn create_grading_category(
.bind(payload.tipo_nota_id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(category))
}
@@ -1816,7 +1815,7 @@ pub async fn delete_grading_category(
.bind(org_ctx.id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(StatusCode::OK)
}
@@ -2793,7 +2792,7 @@ pub async fn register(
// Validar formato de email (validación básica)
let email_regex = regex::Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Email validation error".into()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if !email_regex.is_match(&payload.email) {
return Err((StatusCode::BAD_REQUEST, "Formato de email inválido".into()));
}
@@ -2817,7 +2816,7 @@ pub async fn register(
let mut tx = pool
.begin()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let user = sqlx::query_as::<_, User>("SELECT * FROM fn_register_user($1, $2, $3, $4, $5)")
.bind(&payload.email)
@@ -2837,7 +2836,7 @@ pub async fn register(
tx.commit()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let token = create_jwt(user.id, user.organization_id, &user.role).map_err(|_| {
(
@@ -3049,7 +3048,7 @@ pub async fn get_course_analytics(
let analytics = res
.json::<CourseAnalytics>()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(analytics))
}
@@ -3097,7 +3096,7 @@ pub async fn get_advanced_analytics(
let analytics = res
.json::<common::models::AdvancedAnalytics>()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(analytics))
}
@@ -3128,7 +3127,7 @@ pub async fn get_lesson_heatmap(
let heatmap = res
.json::<Vec<common::models::HeatmapPoint>>()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(heatmap))
}
@@ -3172,7 +3171,7 @@ pub async fn get_audit_logs(
.bind(org_ctx.id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(logs))
}
@@ -3247,7 +3246,7 @@ pub async fn get_sso_config(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(config))
}
@@ -3306,7 +3305,7 @@ pub async fn update_sso_config(
// We use fetch_all + next for slightly better error handling in this complex query
let config = config
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.into_iter()
.next()
.ok_or((
@@ -3327,7 +3326,7 @@ pub async fn sso_login_init(
.bind(org_id)
.fetch_optional(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.ok_or((
StatusCode::NOT_FOUND,
"SSO no configurado o deshabilitado para esta organización".to_string(),
@@ -3340,7 +3339,8 @@ pub async fn sso_login_init(
)
})?;
let provider_metadata = CoreProviderMetadata::discover_async(issuer_url, async_http_client)
let http_client = reqwest::Client::new();
let provider_metadata = CoreProviderMetadata::discover_async(issuer_url, &http_client)
.await
.map_err(|e| {
(
@@ -3359,7 +3359,7 @@ pub async fn sso_login_init(
"{}/auth/sso/callback",
env::var("CMS_API_URL").unwrap_or_else(|_| "http://localhost:3001".to_string())
))
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?,
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?,
);
let (auth_url, csrf_token, nonce) = client
@@ -3380,7 +3380,7 @@ pub async fn sso_login_init(
.bind(nonce.secret())
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(axum::response::Redirect::to(auth_url.as_str()))
}
@@ -3413,15 +3413,16 @@ pub async fn sso_callback(
.bind(org_id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// 3. Exchange code for token
let issuer_url = IssuerUrl::new(config.issuer_url.clone())
.map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?;
let provider_metadata = CoreProviderMetadata::discover_async(issuer_url, async_http_client)
let http_client = reqwest::Client::new();
let provider_metadata = CoreProviderMetadata::discover_async(issuer_url, &http_client)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let client = CoreClient::from_provider_metadata(
provider_metadata,
@@ -3433,12 +3434,13 @@ pub async fn sso_callback(
"{}/auth/sso/callback",
env::var("CMS_API_URL").unwrap_or_else(|_| "http://localhost:3001".to_string())
))
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?,
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?,
);
let token_response = client
.exchange_code(AuthorizationCode::new(params.code))
.request_async(async_http_client)
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.request_async(&http_client)
.await
.map_err(|e| {
(
@@ -3464,7 +3466,7 @@ pub async fn sso_callback(
.to_string();
let name = claims
.name()
.and_then(|n| n.get(None))
.and_then(|n| n.get(None::<&openidconnect::LanguageTag>))
.map(|n| n.to_string())
.unwrap_or_else(|| email.split('@').next().unwrap_or("User").to_string());
@@ -3472,7 +3474,7 @@ pub async fn sso_callback(
let mut tx = pool
.begin()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let user = sqlx::query_as::<_, User>(
"SELECT * FROM users WHERE organization_id = $1 AND lower(email) = lower($2)",
@@ -3481,7 +3483,7 @@ pub async fn sso_callback(
.bind(&email)
.fetch_optional(&mut *tx)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let user = match user {
Some(u) => u,
@@ -3499,13 +3501,13 @@ pub async fn sso_callback(
.bind("student") // Default role
.fetch_one(&mut *tx)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
}
};
tx.commit()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// 6. Generate JWT
let token =
@@ -3857,7 +3859,7 @@ pub async fn update_user(
.fetch_one(&pool)
.await
}
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
log_action(
&pool,
@@ -3913,7 +3915,7 @@ pub async fn delete_user(
.execute(&pool)
.await
}
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "User not found".into()));
@@ -3959,7 +3961,7 @@ pub async fn get_webhooks(
.bind(org_ctx.id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(webhooks))
}
@@ -3987,7 +3989,7 @@ pub async fn create_webhook(
.bind(payload.secret)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
log_action(
&pool,
@@ -4018,7 +4020,7 @@ pub async fn delete_webhook(
.bind(org_ctx.id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Webhook not found".into()));
@@ -4575,7 +4577,7 @@ pub async fn check_course_access(
.bind(user_id)
.fetch_one(pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(exists)
}
@@ -4612,7 +4614,7 @@ pub async fn get_course_team(
.bind(course_id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(team))
}
@@ -4641,7 +4643,7 @@ pub async fn add_team_member(
.bind(claims.sub)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
};
if !is_authorized {
@@ -4686,7 +4688,7 @@ pub async fn remove_team_member(
.bind(claims.sub)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
};
if !is_authorized && claims.sub != user_id {
@@ -4698,7 +4700,7 @@ pub async fn remove_team_member(
.bind(user_id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(StatusCode::NO_CONTENT)
}
@@ -4715,7 +4717,7 @@ pub async fn create_course_preview_token(
}
let token = create_preview_token(claims.sub, claims.org, id)
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(json!({ "token": token })))
}
+6 -6
View File
@@ -109,7 +109,7 @@ pub async fn retry_task(
.bind(id)
.fetch_optional(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if let Some(l) = lesson {
let pool_clone = pool.clone();
@@ -134,7 +134,7 @@ pub async fn retry_task(
.bind(id)
.fetch_optional(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if let Some(task) = zip_task {
let zip_batch_id_from_metadata = task
@@ -176,7 +176,7 @@ pub async fn retry_task(
.bind(task.created_at)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if candidates.len() == 1 {
candidates[0].zip_batch_id
@@ -200,7 +200,7 @@ pub async fn retry_task(
.bind(zip_batch_id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if assets.is_empty() {
return Err((
@@ -306,7 +306,7 @@ pub async fn cancel_task(
.bind(id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if lesson_result.rows_affected() > 0 {
return Ok(StatusCode::NO_CONTENT);
@@ -327,7 +327,7 @@ pub async fn cancel_task(
.bind(id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if task_result.rows_affected() > 0 {
return Ok(StatusCode::NO_CONTENT);
+16 -16
View File
@@ -40,7 +40,7 @@ pub async fn get_token_usage(
)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener el uso: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Calcular estadísticas
let total_tokens: i64 = usage.iter().map(|u| u.total_tokens).sum();
@@ -123,7 +123,7 @@ pub async fn get_ai_usage_dashboard(
.bind(filters.end_date.clone())
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener el uso diario: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Obtener uso por punto de conexión/función
let by_endpoint: Vec<UsageByEndpoint> = sqlx::query_as(
@@ -149,7 +149,7 @@ pub async fn get_ai_usage_dashboard(
.bind(filters.end_date.clone())
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener el uso del punto de conexión: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Obtener usuarios principales
let top_users: Vec<TopUserUsage> = sqlx::query_as(
@@ -179,7 +179,7 @@ pub async fn get_ai_usage_dashboard(
.bind(filters.end_date.clone())
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los usuarios principales: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Calcular estadísticas de resumen
let total_tokens: i64 = daily_usage.iter().map(|d| d.total_tokens).sum();
@@ -256,7 +256,7 @@ pub async fn get_ai_usage_logs(
.bind(offset as i64)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los logs: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Obtener el conteo total para la paginación
let count: (i64,) = sqlx::query_as(
@@ -274,7 +274,7 @@ pub async fn get_ai_usage_logs(
.bind(filters.user_id.clone())
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al contar los logs: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(UsageLogsResponse {
logs,
@@ -459,7 +459,7 @@ pub async fn get_ai_usage_global(
.bind(filters.end_date.clone())
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener el uso diario: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Obtener uso por punto de conexión/función
let by_endpoint: Vec<UsageByEndpoint> = sqlx::query_as(
@@ -483,7 +483,7 @@ pub async fn get_ai_usage_global(
.bind(filters.end_date.clone())
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener el uso del punto de conexión: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Obtener uso por organización
let by_organization: Vec<UsageByOrganization> = sqlx::query_as(
@@ -509,7 +509,7 @@ pub async fn get_ai_usage_global(
.bind(filters.end_date.clone())
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener el uso de la organización: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Obtener usuarios principales en todas las organizaciones
let top_users: Vec<TopUserUsage> = sqlx::query_as(
@@ -539,7 +539,7 @@ pub async fn get_ai_usage_global(
.bind(filters.end_date.clone())
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los usuarios principales: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Obtener uso por tipo de solicitud (para gráfico circular)
let by_request_type: Vec<UsageByRequestType> = sqlx::query_as(
@@ -560,7 +560,7 @@ pub async fn get_ai_usage_global(
.bind(filters.end_date.clone())
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener el uso del tipo de solicitud: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Calcular estadísticas de resumen
let total_tokens: i64 = daily_usage.iter().map(|d| d.total_tokens).sum();
@@ -593,7 +593,7 @@ pub async fn get_ai_usage_global(
.bind(filters.end_date.clone())
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al contar los usuarios: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Calcular uso específico por estudiante (interacciones de chat)
let student_chat_usage: Vec<StudentChatUsage> = sqlx::query_as(
@@ -623,7 +623,7 @@ pub async fn get_ai_usage_global(
.bind(filters.end_date.clone())
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener el uso de chat de estudiantes: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Calcular totales de chat de estudiantes
let total_student_chat_tokens: i64 = student_chat_usage.iter().map(|s| s.total_tokens).sum();
@@ -757,7 +757,7 @@ pub async fn set_user_token_limit(
.bind(user_id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al establecer el límite: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(StatusCode::OK)
}
@@ -792,7 +792,7 @@ pub async fn get_user_token_usage(
.bind(user_id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener el uso: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(usage))
}
@@ -810,7 +810,7 @@ pub async fn check_user_token_limit(
.bind(user_id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to check limit: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(result))
}
+37 -37
View File
@@ -234,7 +234,7 @@ async fn maybe_push_local_file_to_s3(
let bytes = tokio::fs::read(local_path)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al leer el archivo local: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let client = build_s3_client(&settings).await?;
let key = build_s3_object_key(org_id, course_id, storage_filename);
@@ -339,13 +339,13 @@ pub async fn public_s3_proxy(
header::CONTENT_TYPE,
"application/octet-stream"
.parse()
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Cabecera inválida: {}", e)))?,
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?,
);
headers.insert(
header::CACHE_CONTROL,
"public, max-age=3600"
.parse()
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Cabecera inválida: {}", e)))?,
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?,
);
Ok((headers, bytes))
@@ -375,7 +375,7 @@ async fn read_storage_bytes(storage_path: &str) -> Result<Vec<u8>, (StatusCode,
tokio::fs::read(storage_path)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error de lectura: {}", e)))
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))
}
/// POST /api/assets/upload - Subir un archivo a la biblioteca global
@@ -408,7 +408,7 @@ pub async fn upload_asset(
data = field
.bytes()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.to_vec();
} else if name == "course_id" {
if let Ok(txt) = field.text().await {
@@ -447,7 +447,7 @@ pub async fn upload_asset(
// Asegurar que el directorio de subidas existe
tokio::fs::create_dir_all("uploads")
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let (storage_filename, storage_path, stored_filename, stored_mimetype) =
if is_flv_media(&filename, &mimetype) {
@@ -455,7 +455,7 @@ pub async fn upload_asset(
let temp_storage_path = format!("uploads/{}", temp_storage_filename);
tokio::fs::write(&temp_storage_path, data)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let final_storage_filename = format!("{}.mp4", asset_id);
let final_storage_path = format!("uploads/{}", final_storage_filename);
@@ -483,7 +483,7 @@ pub async fn upload_asset(
tokio::fs::write(&storage_path, data)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
(storage_filename, storage_path, filename.clone(), mimetype.clone())
};
@@ -528,7 +528,7 @@ pub async fn upload_asset(
.bind(size_bytes)
.execute(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(AssetUploadResponse {
id: asset_id,
@@ -614,7 +614,7 @@ pub async fn list_assets(
.bind(offset)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(assets))
}
@@ -645,7 +645,7 @@ pub async fn list_asset_import_history(
.bind(org_ctx.id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(items))
}
@@ -664,7 +664,7 @@ pub async fn delete_asset(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Activo no encontrado".to_string()))?;
// 2. Eliminar de la base de datos
@@ -672,7 +672,7 @@ pub async fn delete_asset(
.bind(id)
.execute(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// 3. Eliminar archivo físico u objeto de S3
let _ = delete_storage_path(&asset.storage_path).await;
@@ -790,7 +790,7 @@ pub async fn ingest_asset_for_rag(
.bind(org_ctx.id)
.fetch_one(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let source_kind = if asset.mimetype.starts_with("audio/") || asset.mimetype.starts_with("video/") {
"audio-transcription"
@@ -1214,17 +1214,17 @@ pub async fn import_assets_zip(
let temp_name = format!("uploads/tmp/import-{}.zip", Uuid::new_v4());
tokio::fs::create_dir_all("uploads/tmp")
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to create temp dir: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let mut temp_file = tokio::fs::File::create(&temp_name)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to create temp zip file: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let mut received_bytes: u64 = 0;
while let Some(chunk) = field
.chunk()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to read upload chunk: {}", e)))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
{
received_bytes = received_bytes.saturating_add(chunk.len() as u64);
if received_bytes > max_upload_bytes {
@@ -1241,13 +1241,13 @@ pub async fn import_assets_zip(
temp_file
.write_all(&chunk)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to write temp zip file: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
}
temp_file
.flush()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to flush temp zip file: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
zip_temp_path = Some(temp_name);
} else if name == "course_id" {
@@ -1315,7 +1315,7 @@ pub async fn import_assets_zip(
let source_zip_name = zip_original_name.unwrap_or_else(|| "import.zip".to_string());
let zip_file = std::fs::File::open(&zip_path)
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to open temp zip file: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let mut archive = zip::ZipArchive::new(zip_file)
.map_err(|_| (StatusCode::BAD_REQUEST, "Invalid ZIP file".to_string()))?;
@@ -1334,7 +1334,7 @@ pub async fn import_assets_zip(
for i in 0..len {
let mut file = archive
.by_index(i)
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("ZIP read error: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if !file.is_file() {
continue;
@@ -1377,7 +1377,7 @@ pub async fn import_assets_zip(
let mut content = Vec::new();
std::io::Read::read_to_end(&mut file, &mut content)
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("ZIP entry read failed: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let guessed_mimetype = mime_guess::from_path(&safe_filename)
.first_or_octet_stream()
@@ -1435,7 +1435,7 @@ pub async fn import_assets_zip(
tokio::fs::create_dir_all("uploads")
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// ── Phase 2: process entries ───────────────────────────────────────────────
let mut imported_assets = 0usize;
@@ -1564,7 +1564,7 @@ pub async fn import_assets_zip(
let temp_storage_path = format!("uploads/tmp/{}", temp_storage_filename);
tokio::fs::create_dir_all("uploads/tmp")
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error creating temp dir: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if let Err(e) = tokio::fs::write(&temp_storage_path, &content).await {
failed_entries.push(format!("{}: local write failed ({})", entry_name, e));
continue;
@@ -1573,7 +1573,7 @@ pub async fn import_assets_zip(
let storage_path = build_ready_for_rag_path(org_ctx.id, asset_id, &format!("{}.mp4", asset_id));
tokio::fs::create_dir_all(StdPath::new(&storage_path).parent().unwrap_or(StdPath::new(".")))
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error creating ready-for-rag dir: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if let Err((_, msg)) = transcode_flv_to_mp4(&temp_storage_path, &storage_path).await {
let _ = tokio::fs::remove_file(&temp_storage_path).await;
@@ -1591,7 +1591,7 @@ pub async fn import_assets_zip(
let temp_storage_path = format!("uploads/{}", temp_storage_filename);
tokio::fs::write(&temp_storage_path, &content)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let final_storage_filename = format!("{}.mp4", asset_id);
let final_storage_path = format!("uploads/{}", final_storage_filename);
@@ -1609,7 +1609,7 @@ pub async fn import_assets_zip(
let storage_path = build_ready_for_rag_path(org_ctx.id, asset_id, &safe_filename);
tokio::fs::create_dir_all(StdPath::new(&storage_path).parent().unwrap_or(StdPath::new(".")))
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error creating ready-for-rag dir: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if let Err(e) = tokio::fs::write(&storage_path, &content).await {
failed_entries.push(format!("{}: local write failed ({})", entry_name, e));
continue;
@@ -2124,7 +2124,7 @@ async fn normalize_flv_asset_for_rag(
tokio::fs::create_dir_all("uploads/tmp")
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error creating temp dir: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let input_path = format!("uploads/tmp/flv-normalize-in-{}.flv", asset.id);
let output_path = format!("uploads/tmp/flv-normalize-out-{}.mp4", asset.id);
@@ -2132,7 +2132,7 @@ async fn normalize_flv_asset_for_rag(
let source_bytes = read_storage_bytes(&asset.storage_path).await?;
tokio::fs::write(&input_path, source_bytes)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error writing temp FLV: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if let Err(e) = transcode_flv_to_mp4(&input_path, &output_path).await {
let _ = tokio::fs::remove_file(&input_path).await;
@@ -2144,7 +2144,7 @@ async fn normalize_flv_asset_for_rag(
let output_bytes = tokio::fs::read(&output_path)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error reading temp MP4: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let _ = tokio::fs::remove_file(&output_path).await;
let next_storage_path = replace_last_path_extension(&asset.storage_path, "mp4");
@@ -2172,7 +2172,7 @@ async fn normalize_flv_asset_for_rag(
} else {
tokio::fs::write(&next_storage_path, &output_bytes)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error writing normalized MP4: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if asset.storage_path != next_storage_path {
let _ = tokio::fs::remove_file(&asset.storage_path).await;
@@ -2199,7 +2199,7 @@ async fn normalize_flv_asset_for_rag(
.bind(asset.id)
.execute(pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error updating normalized asset: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
asset.filename = next_filename;
asset.storage_path = next_storage_path;
@@ -2307,7 +2307,7 @@ async fn ingest_chunks_to_question_bank(
.bind(unit_number)
.fetch_one(pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Insert failed: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if let Ok(embedding_res) = generate_embedding(client, ollama_url, model, chunk).await {
let pgvector = ai::embedding_to_pgvector(&embedding_res.embedding);
@@ -2371,10 +2371,10 @@ async fn extract_pdf_text_from_bytes(bytes: Vec<u8>) -> Result<String, (StatusCo
let temp_name = format!("uploads/tmp-pdf-{}.pdf", Uuid::new_v4());
tokio::fs::create_dir_all("uploads")
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Create temp dir failed: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
tokio::fs::write(&temp_name, bytes)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Write temp pdf failed: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let output = Command::new("pdftotext")
.arg("-layout")
@@ -2434,7 +2434,7 @@ async fn transcribe_media_bytes_with_override(
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(300))
.build()
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Whisper HTTP client error: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let mut last_error = String::new();
@@ -2466,7 +2466,7 @@ async fn transcribe_media_bytes_with_override(
let transcription: serde_json::Value = response
.json()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Invalid Whisper response: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let text = transcription
.get("text")
+10 -10
View File
@@ -56,7 +56,7 @@ pub async fn generate_question_embeddings(
.danger_accept_invalid_certs(true)
.danger_accept_invalid_hostnames(true)
.build()
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error del cliente HTTP: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let ollama_url = ai::get_ollama_url();
let model = ai::get_embedding_model();
@@ -74,7 +74,7 @@ pub async fn generate_question_embeddings(
.bind(org_ctx.id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let _total = questions.len();
let mut processed = 0;
@@ -168,7 +168,7 @@ pub async fn regenerate_question_embedding(
.danger_accept_invalid_certs(true)
.danger_accept_invalid_hostnames(true)
.build()
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error del cliente HTTP: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let ollama_url = ai::get_ollama_url();
let model = ai::get_embedding_model();
@@ -181,7 +181,7 @@ pub async fn regenerate_question_embedding(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Pregunta no encontrada".to_string()))?;
// Generar texto de la incrustación
@@ -209,7 +209,7 @@ pub async fn regenerate_question_embedding(
// Generar incrustación
let response = generate_embedding(&client, &ollama_url, &model, &embedding_text)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error de IA: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let pgvector = ai::embedding_to_pgvector(&response.embedding);
@@ -226,7 +226,7 @@ pub async fn regenerate_question_embedding(
.bind(question_id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(StatusCode::OK)
}
@@ -244,7 +244,7 @@ pub async fn semantic_search(
.danger_accept_invalid_certs(true)
.danger_accept_invalid_hostnames(true)
.build()
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error del cliente HTTP: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let ollama_url = ai::get_ollama_url();
let model = ai::get_embedding_model();
@@ -252,7 +252,7 @@ pub async fn semantic_search(
// Generar incrustación para la consulta
let embedding_response = generate_embedding(&client, &ollama_url, &model, &filters.query)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error de IA: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let pgvector = ai::embedding_to_pgvector(&embedding_response.embedding);
@@ -310,7 +310,7 @@ pub async fn semantic_search(
let results = sql_query
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(results))
}
@@ -348,7 +348,7 @@ pub async fn find_similar_questions(
.bind(limit)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.into_iter()
.filter(|r| r.similarity >= threshold)
.collect();
+8 -8
View File
@@ -40,7 +40,7 @@ pub async fn create_library_block(
.bind(payload.tags.as_deref())
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(block))
}
@@ -98,7 +98,7 @@ pub async fn list_library_blocks(
let blocks = sql_query
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(blocks))
}
@@ -116,7 +116,7 @@ pub async fn get_library_block(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
match block {
Some(b) => Ok(Json(b)),
@@ -137,7 +137,7 @@ pub async fn update_library_block(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if existing.is_none() {
return Err((StatusCode::NOT_FOUND, "Bloque no encontrado".to_string()));
@@ -163,7 +163,7 @@ pub async fn update_library_block(
.bind(org_ctx.id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
} else {
sqlx::query_as(
r#"
@@ -181,7 +181,7 @@ pub async fn update_library_block(
.bind(org_ctx.id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
};
Ok(Json(updated))
@@ -198,7 +198,7 @@ pub async fn delete_library_block(
.bind(org_ctx.id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Bloque no encontrado".to_string()));
@@ -218,7 +218,7 @@ pub async fn increment_block_usage(
.bind(org_ctx.id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Bloque no encontrado".to_string()));
+6 -6
View File
@@ -63,7 +63,7 @@ pub async fn list_plugins(
.bind(org_ctx.id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let plugins = rows
.into_iter()
@@ -101,7 +101,7 @@ pub async fn list_enabled_plugins(
.bind(org_ctx.id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let plugins = rows
.into_iter()
@@ -155,7 +155,7 @@ pub async fn create_plugin(
.bind(payload.config.unwrap_or(serde_json::json!({})))
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok((StatusCode::CREATED, Json(OrgPlugin {
id: row.get("id"),
@@ -190,7 +190,7 @@ pub async fn update_plugin(
.bind(org_ctx.id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if !exists {
return Err((StatusCode::NOT_FOUND, "Plugin no encontrado".to_string()));
@@ -229,7 +229,7 @@ pub async fn update_plugin(
.bind(payload.enabled)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(OrgPlugin {
id: row.get("id"),
@@ -262,7 +262,7 @@ pub async fn delete_plugin(
.bind(org_ctx.id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Plugin no encontrado".to_string()));
@@ -92,7 +92,7 @@ async fn connect_mysql_pool(env_var: &str) -> Result<sqlx::MySqlPool, (StatusCod
use sqlx::mysql::MySqlPoolOptions;
let mysql_url = std::env::var(env_var)
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, format!("{} no configurada", env_var)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let mut last_error = String::new();
@@ -379,7 +379,7 @@ pub async fn create_question(
.bind(media_type.as_deref())
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(question))
}
@@ -501,7 +501,7 @@ pub async fn list_questions(
.fetch_all(&pool)
.await
}
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(questions))
}
@@ -627,7 +627,7 @@ pub async fn delete_question(
.bind(org_ctx.id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Pregunta no encontrada".to_string()));
@@ -690,7 +690,7 @@ pub async fn import_from_mysql(
)
.fetch_all(&mysql_pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los cursos: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
tracing::info!("Se obtuvieron {} cursos de MySQL", mysql_courses.len());
@@ -698,7 +698,7 @@ pub async fn import_from_mysql(
tracing::info!("Guardando planes y cursos en PostgreSQL...");
save_mysql_courses_and_plans(&pool, org_ctx.id, mysql_plans, mysql_courses)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al guardar cursos/planes: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Obtener preguntas de MySQL
let mysql_questions: Vec<MySqlQuestion> = if payload.import_all.unwrap_or(false) {
@@ -718,7 +718,7 @@ pub async fn import_from_mysql(
)
.fetch_all(&mysql_pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener las preguntas: {}", e)))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
} else if let Some(course_id) = payload.mysql_course_id {
// Importar todas las preguntas para un curso específico (sin límite)
sqlx::query_as(
@@ -737,7 +737,7 @@ pub async fn import_from_mysql(
.bind(course_id)
.fetch_all(&mysql_pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener las preguntas: {}", e)))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
} else if let Some(question_ids) = payload.question_ids {
// Obtener IDs de preguntas específicas - usar enfoque simple
let mut imported_questions: Vec<QuestionBank> = vec![];
@@ -758,7 +758,7 @@ pub async fn import_from_mysql(
.bind(q_id)
.fetch_optional(&mysql_pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener la pregunta {}: {}", q_id, e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if let Some(question) = mq {
// Mapear el tipo de pregunta de MySQL al tipo de pregunta de la plataforma
@@ -806,7 +806,7 @@ pub async fn import_from_mysql(
.bind(&source_metadata)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al importar la pregunta {}: {}", question.id_pregunta, e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
imported_questions.push(qb);
}
@@ -837,7 +837,7 @@ pub async fn import_from_mysql(
.bind(org_ctx.id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al comprobar existencia: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if exists.0 {
skipped_count += 1;
@@ -893,7 +893,7 @@ pub async fn import_from_mysql(
.bind(mq.id_cursos)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al importar la pregunta {}: {}", mq.id_pregunta, e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
imported_questions.push(question);
}
@@ -1041,7 +1041,7 @@ pub async fn list_mysql_courses(
)
.fetch_all(&mysql_pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los cursos: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
mysql_pool.close().await;
@@ -1067,7 +1067,7 @@ pub async fn get_mysql_plans(
.bind(org_ctx.id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los planes: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Respaldo compatible con versiones anteriores: si el reflejo SAM está vacío, usar el reflejo de metadatos heredado.
if plans.is_empty() {
@@ -1084,7 +1084,7 @@ pub async fn get_mysql_plans(
.bind(org_ctx.id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los planes heredados: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
}
// Auto-sincronización de último recurso: si sigue vacío, obtener metadatos de MySQL y persistirlos.
@@ -1208,7 +1208,7 @@ pub async fn get_mysql_courses_by_plan(
.bind(filters.plan_id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los cursos: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Intentar refrescar desde MySQL (fuente SAM) para evitar listas incompletas por espejo desactualizado.
if let Ok(mysql_pool) = connect_mysql_pool("MYSQL_DATABASE_URL").await {
@@ -1315,7 +1315,7 @@ pub async fn get_mysql_courses_by_plan(
.bind(filters.plan_id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los cursos heredados: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
}
Ok(Json(courses))
@@ -1408,7 +1408,7 @@ pub async fn import_all_from_mysql(
)
.fetch_all(&mysql_pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los cursos: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
tracing::info!("Se obtuvieron {} cursos de MySQL", mysql_courses.len());
@@ -1416,7 +1416,7 @@ pub async fn import_all_from_mysql(
tracing::info!("Guardando planes y cursos en PostgreSQL...");
save_mysql_courses_and_plans(&pool, org_ctx.id, mysql_plans.clone(), mysql_courses.clone())
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al guardar cursos/planes: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Si solo se solicita la importación de metadatos, salir antes
if import_metadata_only {
@@ -1477,7 +1477,7 @@ pub async fn import_all_from_mysql(
)
.fetch_all(&mysql_pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener las preguntas: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
mysql_pool.close().await;
@@ -1505,7 +1505,7 @@ pub async fn import_all_from_mysql(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Fallo en la comprobación: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
match existing {
Some((id, is_active)) => {
@@ -1724,14 +1724,14 @@ pub async fn ai_generate_question(
}))
.send()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("La solicitud de IA falló: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if !response.status().is_success() {
return Err((StatusCode::INTERNAL_SERVER_ERROR, format!("Error de la API de IA: {}", response.status())));
return Err((StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()));
}
let result: serde_json::Value = response.json().await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al analizar la respuesta de la IA: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Extraer contenido de la respuesta de Ollama
let content = result
@@ -1742,7 +1742,7 @@ pub async fn ai_generate_question(
// Analizar la respuesta de la IA como JSON
let ai_question: AIQuestionResponse = serde_json::from_str(content)
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al analizar el JSON de la pregunta: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(ai_question))
}
@@ -1801,7 +1801,7 @@ pub async fn import_course_from_mysql(
if let sqlx::Error::RowNotFound = e {
(StatusCode::NOT_FOUND, format!("Curso con ID {} no encontrado en MySQL", payload.mysql_course_id))
} else {
(StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener el curso de MySQL: {}", e))
(StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string())
}
})?;
@@ -1809,7 +1809,7 @@ pub async fn import_course_from_mysql(
// Iniciar transacción
let mut tx = pool.begin().await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al iniciar la transacción: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Determinar el tipo y nivel del curso para la generación de la estructura
let course_type = calculate_course_type_from_duration(mysql_course.duracion);
@@ -1845,7 +1845,7 @@ pub async fn import_course_from_mysql(
.bind(mysql_course.id_cursos)
.fetch_one(&mut *tx)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al crear el curso: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
tracing::info!("Curso creado en PostgreSQL: {}", new_course.id);
@@ -1863,7 +1863,7 @@ pub async fn import_course_from_mysql(
// Confirmar transacción
tx.commit().await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al confirmar la transacción: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
tracing::info!(
"Curso {} importado con éxito con {} módulos y {} lecciones",
+22 -22
View File
@@ -121,7 +121,7 @@ pub async fn create_rubric(
.bind(&payload.description)
.fetch_one(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(rubric))
}
@@ -144,7 +144,7 @@ pub async fn list_course_rubrics(
.bind(course_id)
.fetch_all(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(rubrics))
}
@@ -167,7 +167,7 @@ pub async fn get_rubric_with_details(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Rúbrica no encontrada".to_string()))?;
// Get criteria
@@ -182,7 +182,7 @@ pub async fn get_rubric_with_details(
.bind(rubric_id)
.fetch_all(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Get levels for each criterion
let mut criteria_with_levels = Vec::new();
@@ -198,7 +198,7 @@ pub async fn get_rubric_with_details(
.bind(criterion.id)
.fetch_all(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
criteria_with_levels.push(CriterionWithLevels { criterion, levels });
}
@@ -232,7 +232,7 @@ pub async fn update_rubric(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Rúbrica no encontrada".to_string()))?;
Ok(Json(rubric))
@@ -249,7 +249,7 @@ pub async fn delete_rubric(
.bind(org_ctx.id)
.execute(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Rúbrica no encontrada".to_string()));
@@ -273,7 +273,7 @@ pub async fn create_criterion(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Rúbrica no encontrada".to_string()))?;
let position = payload.position.unwrap_or(0);
@@ -292,7 +292,7 @@ pub async fn create_criterion(
.bind(position)
.fetch_one(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Update rubric total_points
let _= sqlx::query(
@@ -306,7 +306,7 @@ pub async fn create_criterion(
.bind(rubric_id)
.execute(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(criterion))
}
@@ -338,7 +338,7 @@ pub async fn update_criterion(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Criterio no encontrado".to_string()))?;
// Update rubric total_points if max_points changed
@@ -354,7 +354,7 @@ pub async fn update_criterion(
.bind(criterion.rubric_id)
.execute(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
}
Ok(Json(criterion))
@@ -371,7 +371,7 @@ pub async fn delete_criterion(
.bind(criterion_id)
.fetch_optional(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Criterio no encontrado".to_string()))?;
let rubric_id: Uuid = criterion_row.get("rubric_id");
@@ -387,7 +387,7 @@ pub async fn delete_criterion(
.bind(org_ctx.id)
.execute(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Criterio no encontrado".to_string()));
@@ -405,7 +405,7 @@ pub async fn delete_criterion(
.bind(rubric_id)
.execute(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(StatusCode::NO_CONTENT)
}
@@ -425,7 +425,7 @@ pub async fn create_level(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Criterio no encontrado".to_string()))?;
let position = payload.position.unwrap_or(0);
@@ -444,7 +444,7 @@ pub async fn create_level(
.bind(position)
.fetch_one(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(level))
}
@@ -479,7 +479,7 @@ pub async fn update_level(
.bind(org_ctx.id)
.fetch_optional(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Nivel no encontrado".to_string()))?;
Ok(Json(level))
@@ -505,7 +505,7 @@ pub async fn delete_level(
.bind(org_ctx.id)
.execute(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Nivel no encontrado".to_string()));
@@ -534,7 +534,7 @@ pub async fn assign_rubric_to_lesson(
.bind(rubric_id)
.fetch_one(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(lesson_rubric))
}
@@ -550,7 +550,7 @@ pub async fn unassign_rubric_from_lesson(
.bind(rubric_id)
.execute(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Vínculo lección-rúbrica no encontrado".to_string()));
@@ -578,7 +578,7 @@ pub async fn get_lesson_rubrics(
.bind(org_ctx.id)
.fetch_all(&pool)
.await
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(rubrics))
}
+9 -9
View File
@@ -65,7 +65,7 @@ pub async fn sync_sam_students(
let sam_pool = sqlx::PgPool::connect(&sam_url)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al conectar con la base de datos SAM: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let mut errors = Vec::new();
let mut students_synced = 0;
@@ -85,7 +85,7 @@ pub async fn sync_sam_students(
)
.fetch_all(&sam_pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener estudiantes de SAM: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Convertir a SamStudentInfo
let sam_students: Vec<SamStudentInfo> = rows.iter().map(|row| {
@@ -196,7 +196,7 @@ pub async fn sync_sam_assignments(
let sam_pool = sqlx::PgPool::connect(&sam_url)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al conectar con la base de datos SAM: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let mut errors = Vec::new();
let mut assignments_synced = 0;
@@ -215,7 +215,7 @@ pub async fn sync_sam_assignments(
)
.fetch_all(&sam_pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener asignaciones: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Convertir a SamAssignmentInfo
let sam_assignments: Vec<SamAssignmentInfo> = rows.iter().map(|row| {
@@ -317,7 +317,7 @@ pub async fn list_sam_students(
let rows = sqlx::query(&query)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los estudiantes: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let students: Vec<serde_json::Value> = rows.iter().map(|row| {
serde_json::json!({
@@ -354,7 +354,7 @@ pub async fn get_sam_student_courses(
.bind(&sam_student_id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los cursos: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let courses: Vec<serde_json::Value> = rows.iter().map(|row| {
serde_json::json!({
@@ -386,7 +386,7 @@ pub async fn sync_all_sam(
let sam_pool = sqlx::PgPool::connect(&sam_url)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al conectar con la base de datos SAM: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Sincronizar estudiantes primero
{
@@ -400,7 +400,7 @@ pub async fn sync_all_sam(
)
.fetch_all(&sam_pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener estudiantes de SAM: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let sam_students: Vec<SamStudentInfo> = rows.iter().map(|row| {
SamStudentInfo {
@@ -470,7 +470,7 @@ pub async fn sync_all_sam(
)
.fetch_all(&sam_pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener asignaciones: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let sam_assignments: Vec<SamAssignmentInfo> = rows.iter().map(|row| {
SamAssignmentInfo {
@@ -108,7 +108,7 @@ pub async fn create_test_template(
.bind(payload.tags.as_deref())
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(template))
}
@@ -198,7 +198,7 @@ pub async fn list_test_templates(
let templates = sql_query
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(templates))
}
@@ -240,7 +240,7 @@ pub async fn get_test_template(
.bind(template_id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Obtener preguntas
let questions: Vec<TestTemplateQuestion> = sqlx::query_as(
@@ -255,7 +255,7 @@ pub async fn get_test_template(
.bind(template_id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(TestTemplateWithQuestions {
template,
@@ -341,7 +341,7 @@ pub async fn delete_test_template(
.bind(org_ctx.id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Plantilla no encontrada".to_string()));
@@ -367,7 +367,7 @@ pub async fn create_template_question(
.bind(org_ctx.id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if !exists.0 {
return Err((StatusCode::NOT_FOUND, "Plantilla no encontrada".to_string()));
@@ -396,7 +396,7 @@ pub async fn create_template_question(
.bind(payload.metadata.as_ref())
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(question))
}
@@ -428,7 +428,7 @@ pub async fn delete_template_question(
.bind(org_ctx.id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if !exists.0 {
return Err((StatusCode::NOT_FOUND, "Plantilla no encontrada".to_string()));
@@ -444,7 +444,7 @@ pub async fn delete_template_question(
.bind(template_id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Pregunta no encontrada".to_string()));
@@ -470,7 +470,7 @@ pub async fn create_template_section(
.bind(org_ctx.id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if !exists.0 {
return Err((StatusCode::NOT_FOUND, "Plantilla no encontrada".to_string()));
@@ -494,7 +494,7 @@ pub async fn create_template_section(
.bind(payload.section_data.as_ref())
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
Ok(Json(section))
}
@@ -525,7 +525,7 @@ pub async fn delete_template_section(
.bind(template_id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if result.rows_affected() == 0 {
return Err((StatusCode::NOT_FOUND, "Sección no encontrada".to_string()));
@@ -571,7 +571,7 @@ pub async fn apply_template_to_lesson(
.bind(org_ctx.id)
.fetch_one(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if !lesson_exists.0 {
return Err((StatusCode::NOT_FOUND, "Lección no encontrada".to_string()));
@@ -590,7 +590,7 @@ pub async fn apply_template_to_lesson(
.bind(template_id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if template_questions.is_empty() {
return Err((StatusCode::BAD_REQUEST, "La plantilla no tiene preguntas".to_string()));
@@ -647,14 +647,14 @@ pub async fn apply_template_to_lesson(
.bind(org_ctx.id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
// Incrementar el contador de uso de la plantilla
sqlx::query("SELECT increment_template_usage($1)")
.bind(template_id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
tracing::info!(
"Plantilla '{}' aplicada a la lección '{}' con {} preguntas",
@@ -1019,7 +1019,7 @@ pub async fn generate_questions_with_rag(
.danger_accept_invalid_certs(true)
.danger_accept_invalid_hostnames(true)
.build()
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error del cliente HTTP: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
let ollama_url = ai::get_ollama_url();
let model = ai::get_embedding_model();
@@ -1070,7 +1070,7 @@ pub async fn generate_questions_with_rag(
.bind(requested_num_questions * 3) // Get more for diversity
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("La búsqueda semántica falló: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
tracing::info!("La búsqueda semántica encontró {} preguntas similares", mysql_questions.len());
@@ -1123,7 +1123,7 @@ pub async fn generate_questions_with_rag(
.bind(requested_num_questions * 3)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("El recurso a palabras clave falló: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if mysql_questions.is_empty() {
tracing::info!(
@@ -1220,7 +1220,7 @@ pub async fn generate_questions_with_rag(
.bind(requested_num_questions * 3)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("La búsqueda por palabras clave falló: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
if mysql_questions.is_empty() {
tracing::info!(
@@ -1309,7 +1309,7 @@ pub async fn generate_questions_with_rag(
.bind(course_id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener las preguntas: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
} else {
// Fetch all imported MySQL questions for this organization
// NO LIMIT - fetch all questions for better RAG context
@@ -1339,7 +1339,7 @@ pub async fn generate_questions_with_rag(
.bind(org_ctx.id)
.fetch_all(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener las preguntas: {}", e)))?;
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
}
if mysql_questions.is_empty() {
@@ -1487,7 +1487,7 @@ pub async fn generate_questions_with_rag(
let response = request.send().await.map_err(|e| {
tracing::error!("AI request failed after timeout: {}", e);
(StatusCode::INTERNAL_SERVER_ERROR, format!("Ollama timeout - el equipo t-800 está tardando en responder. Intenta nuevamente: {}", e))
(StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string())
})?;
tracing::info!("Estado de la respuesta de Ollama: {}", response.status());