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:
@@ -154,7 +154,7 @@ pub async fn get_me(
|
||||
.bind(claims.sub)
|
||||
.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()))?;
|
||||
|
||||
if let Some(user) = user {
|
||||
return Ok(Json(UserResponse {
|
||||
@@ -513,7 +513,7 @@ pub async fn export_course_grades(
|
||||
.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()))?;
|
||||
|
||||
// 2. Obtener datos generales de los estudiantes
|
||||
#[derive(sqlx::FromRow)]
|
||||
@@ -546,7 +546,7 @@ pub async fn export_course_grades(
|
||||
.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()))?;
|
||||
|
||||
// 3. Obtener calificaciones detalladas por usuario/categoría
|
||||
#[derive(sqlx::FromRow)]
|
||||
@@ -571,7 +571,7 @@ pub async fn export_course_grades(
|
||||
.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()))?;
|
||||
|
||||
// 4. Construir CSV
|
||||
let mut csv = "Name,Email,Cohort,Progress,Overall Score".to_string();
|
||||
@@ -830,7 +830,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()));
|
||||
}
|
||||
@@ -855,7 +855,7 @@ pub async fn register(
|
||||
let mut tx = pool
|
||||
.begin()
|
||||
.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 organization = if let Some(org_name) = payload.organization_name {
|
||||
sqlx::query_as::<_, Organization>(
|
||||
@@ -864,7 +864,7 @@ pub async fn register(
|
||||
.bind(&org_name)
|
||||
.fetch_one(&mut *tx)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al buscar o crear la organización: {}", e)))?
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
|
||||
} else {
|
||||
sqlx::query_as::<_, Organization>(
|
||||
"SELECT * FROM organizations WHERE id = '00000000-0000-0000-0000-000000000001'",
|
||||
@@ -892,7 +892,7 @@ pub async fn register(
|
||||
|
||||
tx.commit()
|
||||
.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 token = create_jwt(user.id, user.organization_id, "student").map_err(|_| {
|
||||
(
|
||||
@@ -1872,7 +1872,7 @@ pub async fn submit_lesson_score(
|
||||
let mut tx = pool
|
||||
.begin()
|
||||
.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 ip = headers
|
||||
.get("x-forwarded-for")
|
||||
@@ -1894,7 +1894,7 @@ pub async fn submit_lesson_score(
|
||||
Some("EVENTO_DEL_SISTEMA".to_string()),
|
||||
)
|
||||
.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()))?;
|
||||
|
||||
// 1. Obtener reglas de intentos de la lección
|
||||
let max_attempts: Option<Option<i32>> =
|
||||
@@ -1902,7 +1902,7 @@ pub async fn submit_lesson_score(
|
||||
.bind(payload.lesson_id)
|
||||
.fetch_optional(&mut *tx)
|
||||
.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 max_attempts.is_none() {
|
||||
return Err((StatusCode::NOT_FOUND, "Lección no encontrada".into()));
|
||||
@@ -1916,7 +1916,7 @@ pub async fn submit_lesson_score(
|
||||
.bind(org_ctx.id)
|
||||
.fetch_optional(&mut *tx)
|
||||
.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 let Some(count) = existing_attempts {
|
||||
if let Some(max) = max_attempts {
|
||||
@@ -1941,7 +1941,7 @@ pub async fn submit_lesson_score(
|
||||
.bind(payload.metadata)
|
||||
.fetch_one(&mut *tx)
|
||||
.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.1 Sincronizar con MySQL externo si está disponible
|
||||
if let Some(mysql_pool) = mysql_pool {
|
||||
@@ -2006,7 +2006,7 @@ pub async fn submit_lesson_score(
|
||||
|
||||
tx.commit()
|
||||
.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()))?;
|
||||
|
||||
// 4. Enviar Webhooks
|
||||
let webhook_service = common::webhooks::WebhookService::new(pool.clone());
|
||||
@@ -2217,7 +2217,7 @@ pub async fn get_course_analytics(
|
||||
.bind(filter.cohort_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()))?;
|
||||
|
||||
// 2. Puntaje promedio del curso (General)
|
||||
let average_score: Option<f32> = sqlx::query_scalar(
|
||||
@@ -2236,7 +2236,7 @@ pub async fn get_course_analytics(
|
||||
.bind(filter.cohort_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()))?;
|
||||
|
||||
// 3. Analítica por lección
|
||||
// Nota: Convertimos AVG a float4 para compatibilidad con PostgreSQL
|
||||
@@ -2262,7 +2262,7 @@ pub async fn get_course_analytics(
|
||||
.bind(filter.cohort_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()))?;
|
||||
|
||||
let lessons = rows
|
||||
.into_iter()
|
||||
@@ -2305,7 +2305,7 @@ pub async fn notify_student(
|
||||
.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 role.as_deref() {
|
||||
Some("instructor") | Some("admin") => {}
|
||||
@@ -2320,7 +2320,7 @@ pub async fn notify_student(
|
||||
.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 !enrolled {
|
||||
return Err((StatusCode::NOT_FOUND, "El alumno no está inscrito en este curso".to_string()));
|
||||
@@ -2337,7 +2337,7 @@ pub async fn notify_student(
|
||||
.bind(&payload.link_url)
|
||||
.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)
|
||||
}
|
||||
@@ -2368,7 +2368,7 @@ pub async fn get_course_progress(
|
||||
.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()))?
|
||||
.flatten();
|
||||
|
||||
let progress_percentage = enrollment_progress
|
||||
@@ -2737,7 +2737,7 @@ pub async fn toggle_bookmark(
|
||||
.bind(lesson_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()))?;
|
||||
|
||||
if let Some(id) = existing_id {
|
||||
// Eliminar marcador
|
||||
@@ -2745,7 +2745,7 @@ pub async fn toggle_bookmark(
|
||||
.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()))?;
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
} else {
|
||||
// Añadir marcador
|
||||
@@ -2758,7 +2758,7 @@ pub async fn toggle_bookmark(
|
||||
.bind(lesson_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::CREATED)
|
||||
}
|
||||
}
|
||||
@@ -2780,7 +2780,7 @@ pub async fn get_user_bookmarks(
|
||||
// Wait, let's create a better filter for this.
|
||||
.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(bookmarks))
|
||||
}
|
||||
@@ -2812,7 +2812,7 @@ pub async fn update_user(
|
||||
.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()))?;
|
||||
|
||||
Ok(Json(UserResponse {
|
||||
id: user.id,
|
||||
@@ -2846,7 +2846,7 @@ pub async fn get_recommendations(
|
||||
.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()))?;
|
||||
|
||||
// 2. Obtener metadatos de la lección (títulos y etiquetas) para contexto
|
||||
#[derive(sqlx::FromRow)]
|
||||
@@ -2878,7 +2878,7 @@ pub async fn get_recommendations(
|
||||
.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()))?;
|
||||
|
||||
// 3. Preparar contexto de IA con Análisis de Habilidades
|
||||
use std::collections::HashMap;
|
||||
@@ -3080,7 +3080,7 @@ pub async fn evaluate_audio_response(
|
||||
}))
|
||||
.send()
|
||||
.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 ai_data: serde_json::Value = response.json().await.map_err(|e| {
|
||||
(
|
||||
@@ -3101,7 +3101,7 @@ pub async fn evaluate_audio_response(
|
||||
"feedback": "Lo siento, tuve un problema analizando tu respuesta. ¡Sigue practicando!"
|
||||
})
|
||||
})
|
||||
).map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Mapping failed: {}", e)))?;
|
||||
).map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
Ok(Json(grading))
|
||||
}
|
||||
@@ -3152,7 +3152,7 @@ pub async fn evaluate_audio_file(
|
||||
audio_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();
|
||||
tracing::info!("Received audio file: {} bytes", audio_data.len());
|
||||
}
|
||||
@@ -3322,7 +3322,7 @@ pub async fn evaluate_audio_file(
|
||||
"feedback": "Lo siento, tuve un problema analizando tu respuesta con Whisper. ¡Sigue practicando!"
|
||||
})
|
||||
})
|
||||
).map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Mapping failed: {}", e)))?;
|
||||
).map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
grading.transcript = Some(transcript.clone());
|
||||
|
||||
// 3. Guardar respuesta de audio en la base de datos
|
||||
@@ -4242,7 +4242,7 @@ pub async fn chat_with_tutor(
|
||||
.build()
|
||||
.map_err(|e| {
|
||||
tracing::warn!("Failed to create HTTP client for embeddings: {}", e);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, format!("HTTP client error: {}", e))
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string())
|
||||
})?;
|
||||
|
||||
let ollama_url = ai::get_ollama_url();
|
||||
@@ -4666,11 +4666,11 @@ pub async fn chat_role_play(
|
||||
"temperature": 0.8
|
||||
}))
|
||||
.send().await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error en la solicitud de IA: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let err_body = response.text().await.unwrap_or_default();
|
||||
return Err((StatusCode::INTERNAL_SERVER_ERROR, format!("Error de la API de IA: {}", err_body)));
|
||||
let _err_body = response.text().await.unwrap_or_default();
|
||||
return Err((StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()));
|
||||
}
|
||||
|
||||
let ai_data: serde_json::Value = response.json().await.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error al analizar la respuesta de la IA".into()))?;
|
||||
@@ -4714,7 +4714,7 @@ pub async fn get_lesson_feedback(
|
||||
.bind(lesson_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::BAD_REQUEST,
|
||||
"No se encontró calificación para esta lección".into(),
|
||||
@@ -5197,7 +5197,7 @@ pub async fn update_lesson_collaborative_doc(
|
||||
.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 {
|
||||
return Err((StatusCode::NOT_FOUND, "Lección no encontrada".into()));
|
||||
@@ -5220,7 +5220,7 @@ pub async fn update_lesson_collaborative_doc(
|
||||
.bind(payload.base_revision)
|
||||
.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()))?
|
||||
.rows_affected();
|
||||
|
||||
if rows_updated == 1 {
|
||||
@@ -5241,7 +5241,7 @@ pub async fn update_lesson_collaborative_doc(
|
||||
.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() && payload.base_revision == 0 {
|
||||
// Primer guardado
|
||||
@@ -5258,7 +5258,7 @@ pub async fn update_lesson_collaborative_doc(
|
||||
.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()))?;
|
||||
|
||||
sqlx::query(
|
||||
r#"
|
||||
@@ -5273,7 +5273,7 @@ pub async fn update_lesson_collaborative_doc(
|
||||
.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()))?;
|
||||
|
||||
return Ok(Json(UpdateCollaborativeDocResponse {
|
||||
lesson_id: id,
|
||||
@@ -5294,7 +5294,7 @@ pub async fn update_lesson_collaborative_doc(
|
||||
.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()))?;
|
||||
|
||||
let (sc, sr) = server.map(|r| (r.content, r.revision)).unwrap_or_default();
|
||||
|
||||
@@ -5428,7 +5428,7 @@ pub async fn list_lesson_annotations(
|
||||
.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(rows))
|
||||
}
|
||||
|
||||
@@ -5464,7 +5464,7 @@ pub async fn create_lesson_annotation(
|
||||
.bind(payload.position_data)
|
||||
.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(row)))
|
||||
}
|
||||
|
||||
@@ -5511,7 +5511,7 @@ pub async fn delete_lesson_annotation(
|
||||
.bind(lesson_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()))?
|
||||
.rows_affected();
|
||||
if affected == 0 {
|
||||
Err((StatusCode::NOT_FOUND, "Anotación no encontrada".to_string()))
|
||||
@@ -5535,7 +5535,7 @@ pub async fn get_my_annotations(
|
||||
.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(rows))
|
||||
}
|
||||
|
||||
@@ -5595,7 +5595,7 @@ pub async fn assign_mentor(
|
||||
.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 role.as_deref() {
|
||||
Some("instructor") | Some("admin") => {}
|
||||
@@ -5620,7 +5620,7 @@ pub async fn assign_mentor(
|
||||
.bind(&payload.notes)
|
||||
.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(row))
|
||||
}
|
||||
@@ -5654,7 +5654,7 @@ pub async fn list_course_mentorships(
|
||||
.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(rows))
|
||||
}
|
||||
@@ -5673,7 +5673,7 @@ pub async fn delete_mentorship(
|
||||
.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()))?
|
||||
.rows_affected();
|
||||
|
||||
if affected == 0 {
|
||||
@@ -5714,7 +5714,7 @@ pub async fn get_my_mentor(
|
||||
.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(row))
|
||||
}
|
||||
@@ -5750,7 +5750,7 @@ pub async fn get_my_mentees(
|
||||
.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(rows))
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ pub async fn list_ai_audit_logs(
|
||||
.bind(offset)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al listar auditoría de IA: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
let mut items = Vec::new();
|
||||
|
||||
@@ -371,7 +371,7 @@ pub async fn review_ai_audit_log(
|
||||
.bind(note_or_null)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al actualizar auditoría IA: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
if updated.rows_affected() == 0 {
|
||||
return Err((StatusCode::NOT_FOUND, "Registro de auditoría no encontrado".to_string()));
|
||||
@@ -437,7 +437,7 @@ pub async fn get_ai_audit_metrics(
|
||||
.bind(days)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error métricas de auditoría: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
let total_chat_logs: i64 = totals.get("total_chat");
|
||||
let total_reviewed: i64 = totals.get("reviewed");
|
||||
@@ -456,7 +456,7 @@ pub async fn get_ai_audit_metrics(
|
||||
.bind(days)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error escaneando logs: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
let mut signal_counts: std::collections::HashMap<String, i64> = std::collections::HashMap::new();
|
||||
let mut total_flagged: i64 = 0;
|
||||
|
||||
@@ -49,7 +49,7 @@ async fn ensure_announcement_author_exists(
|
||||
.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()))?;
|
||||
|
||||
if exists {
|
||||
return Ok(());
|
||||
@@ -71,7 +71,7 @@ async fn ensure_announcement_author_exists(
|
||||
.bind(normalized_role)
|
||||
.execute(pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("No se pudo provisionar usuario LMS: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -97,7 +97,7 @@ pub async fn list_announcements(
|
||||
.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()))?;
|
||||
|
||||
// Adjuntar cohort_ids a cada anuncio
|
||||
for a in &mut announcements {
|
||||
@@ -107,7 +107,7 @@ pub async fn list_announcements(
|
||||
.bind(a.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 !cohorts.is_empty() {
|
||||
a.cohort_ids = Some(cohorts.into_iter().map(|c| c.0).collect());
|
||||
@@ -136,7 +136,7 @@ pub async fn create_announcement(
|
||||
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()))?;
|
||||
|
||||
// 1. Crear anuncio
|
||||
let mut announcement = sqlx::query_as::<_, CourseAnnouncement>(
|
||||
@@ -152,7 +152,7 @@ pub async fn create_announcement(
|
||||
.bind(payload.is_pinned.unwrap_or(false))
|
||||
.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()))?;
|
||||
|
||||
// 2. Vincular cohortes si se proporcionan
|
||||
if let Some(ref cohort_ids) = payload.cohort_ids {
|
||||
@@ -164,14 +164,14 @@ pub async fn create_announcement(
|
||||
.bind(cohort_id)
|
||||
.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()))?;
|
||||
}
|
||||
announcement.cohort_ids = Some(cohort_ids.clone());
|
||||
}
|
||||
|
||||
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()))?;
|
||||
|
||||
// 3. Obtener estudiantes objetivo para notificaciones
|
||||
let enrolled_students = if let Some(ref cohort_ids) = payload.cohort_ids {
|
||||
@@ -207,7 +207,7 @@ pub async fn create_announcement(
|
||||
.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()))?;
|
||||
|
||||
// Crear notificación para cada estudiante inscrito
|
||||
for (student_id,) in enrolled_students {
|
||||
@@ -276,7 +276,7 @@ pub async fn update_announcement(
|
||||
.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(announcement))
|
||||
}
|
||||
@@ -302,7 +302,7 @@ pub async fn delete_announcement(
|
||||
.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)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ pub async fn list_cohorts(
|
||||
.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(cohorts))
|
||||
}
|
||||
@@ -43,7 +43,7 @@ pub async fn create_cohort(
|
||||
.bind(payload.description)
|
||||
.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(cohort))
|
||||
}
|
||||
@@ -63,7 +63,7 @@ pub async fn add_cohort_member(
|
||||
.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, "Cohorte no encontrada".to_string()));
|
||||
@@ -81,7 +81,7 @@ pub async fn add_cohort_member(
|
||||
.bind(payload.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(Json(member))
|
||||
}
|
||||
@@ -100,7 +100,7 @@ pub async fn remove_cohort_member(
|
||||
.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, "Cohorte no encontrada".to_string()));
|
||||
@@ -111,7 +111,7 @@ pub async fn remove_cohort_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)
|
||||
}
|
||||
@@ -130,7 +130,7 @@ pub async fn get_cohort_members(
|
||||
.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, "Cohorte no encontrada".to_string()));
|
||||
@@ -140,7 +140,7 @@ pub async fn get_cohort_members(
|
||||
.bind(cohort_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(members))
|
||||
}
|
||||
|
||||
@@ -454,7 +454,7 @@ pub async fn list_threads(
|
||||
let threads = 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(threads))
|
||||
}
|
||||
@@ -497,7 +497,7 @@ pub async fn create_thread(
|
||||
.bind(&payload.content)
|
||||
.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()))?;
|
||||
|
||||
// Suscribir automáticamente al autor al hilo
|
||||
let _ = sqlx::query(
|
||||
@@ -654,7 +654,7 @@ fn get_thread_posts_recursive<'a>(
|
||||
let mut posts = 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()))?;
|
||||
|
||||
// Obtener respuestas recursivamente para cada mensaje
|
||||
for post in &mut posts {
|
||||
@@ -691,7 +691,7 @@ pub async fn pin_thread(
|
||||
.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)
|
||||
}
|
||||
@@ -721,7 +721,7 @@ pub async fn lock_thread(
|
||||
.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)
|
||||
}
|
||||
@@ -779,7 +779,7 @@ pub async fn create_post(
|
||||
.bind(payload.content)
|
||||
.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()))?;
|
||||
|
||||
// Notificar a los suscritos al hilo (excepto autor de la respuesta)
|
||||
let mut recipients = sqlx::query_as::<_, (Uuid, String, Option<String>)>(
|
||||
@@ -883,7 +883,7 @@ pub async fn endorse_post(
|
||||
.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)
|
||||
}
|
||||
@@ -912,7 +912,7 @@ pub async fn vote_post(
|
||||
.bind(&payload.vote_type)
|
||||
.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()))?;
|
||||
|
||||
// Recalcular votos positivos
|
||||
let upvote_count: i64 = sqlx::query_scalar(
|
||||
@@ -928,7 +928,7 @@ pub async fn vote_post(
|
||||
.bind(post_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)
|
||||
}
|
||||
@@ -951,7 +951,7 @@ pub async fn subscribe_thread(
|
||||
.bind(claims.sub)
|
||||
.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)
|
||||
}
|
||||
@@ -971,7 +971,7 @@ pub async fn unsubscribe_thread(
|
||||
.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)
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ pub async fn forgot_password(
|
||||
.bind(&email)
|
||||
.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()))?;
|
||||
|
||||
let Some(user) = user else {
|
||||
// Respuesta genérica — no revelar si el email existe
|
||||
@@ -210,7 +210,7 @@ pub async fn forgot_password(
|
||||
.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()))?;
|
||||
|
||||
// Insertar nuevo token (expira en 1 hora)
|
||||
sqlx::query(
|
||||
@@ -220,7 +220,7 @@ pub async fn forgot_password(
|
||||
.bind(&token)
|
||||
.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()))?;
|
||||
|
||||
// Intentar enviar email (fire-and-forget en caso de error SMTP)
|
||||
let base_url = env::var("EXPERIENCE_URL").unwrap_or_else(|_| "https://openccb.local".to_string());
|
||||
@@ -283,7 +283,7 @@ pub async fn reset_password(
|
||||
.bind(&token)
|
||||
.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()))?;
|
||||
|
||||
let Some((user_id,)) = row else {
|
||||
return Err((
|
||||
@@ -303,14 +303,14 @@ pub async fn reset_password(
|
||||
.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()))?;
|
||||
|
||||
// Marcar token como usado
|
||||
sqlx::query("UPDATE password_reset_tokens SET used_at = NOW() WHERE token = $1")
|
||||
.bind(&token)
|
||||
.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(Json(MessageResponse {
|
||||
message: "Contraseña actualizada correctamente. Ya puedes iniciar sesión.".to_string(),
|
||||
|
||||
@@ -55,7 +55,7 @@ pub async fn generate_knowledge_embeddings(
|
||||
.danger_accept_invalid_certs(true)
|
||||
.danger_accept_invalid_hostnames(true)
|
||||
.build()
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("HTTP client error: {}", 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();
|
||||
@@ -73,7 +73,7 @@ pub async fn generate_knowledge_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 = entries.len();
|
||||
let mut processed = 0;
|
||||
@@ -144,7 +144,7 @@ pub async fn regenerate_knowledge_embedding(
|
||||
.danger_accept_invalid_certs(true)
|
||||
.danger_accept_invalid_hostnames(true)
|
||||
.build()
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("HTTP client error: {}", 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();
|
||||
@@ -157,13 +157,13 @@ pub async fn regenerate_knowledge_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, "Entrada de la base de conocimientos no encontrada".to_string()))?;
|
||||
|
||||
// Generar embedding
|
||||
let response = generate_embedding(&client, &ollama_url, &model, &entry.content_chunk)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("AI error: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
let pgvector = ai::embedding_to_pgvector(&response.embedding);
|
||||
|
||||
@@ -180,7 +180,7 @@ pub async fn regenerate_knowledge_embedding(
|
||||
.bind(entry_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)
|
||||
}
|
||||
@@ -198,7 +198,7 @@ pub async fn semantic_search_knowledge(
|
||||
.danger_accept_invalid_certs(true)
|
||||
.danger_accept_invalid_hostnames(true)
|
||||
.build()
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("HTTP client error: {}", 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();
|
||||
@@ -206,7 +206,7 @@ pub async fn semantic_search_knowledge(
|
||||
// Generar embedding para la consulta
|
||||
let embedding_response = generate_embedding(&client, &ollama_url, &model, &filters.query)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("AI error: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
let pgvector = ai::embedding_to_pgvector(&embedding_response.embedding);
|
||||
|
||||
@@ -264,7 +264,7 @@ pub async fn semantic_search_knowledge(
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ pub async fn import_faq_candidates(
|
||||
.bind(limit)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al importar candidatos FAQ: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
let imported: i64 = row.get("imported");
|
||||
let skipped: i64 = row.get("skipped");
|
||||
@@ -250,7 +250,7 @@ pub async fn list_faq_review_queue(
|
||||
.bind(offset)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al listar la cola FAQ: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
let total: i64 = sqlx::query_scalar(
|
||||
r#"
|
||||
@@ -264,7 +264,7 @@ pub async fn list_faq_review_queue(
|
||||
.bind(filters.status.clone())
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al contar cola FAQ: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
Ok(Json(FaqReviewQueueResponse {
|
||||
items,
|
||||
@@ -292,7 +292,7 @@ pub async fn answer_faq_review_item(
|
||||
let mut tx = pool
|
||||
.begin()
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("No se pudo iniciar transacción FAQ: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
let queue_item: (String, String) = sqlx::query_as(
|
||||
"SELECT status, question_text FROM faq_review_queue WHERE id = $1 AND organization_id = $2"
|
||||
@@ -301,7 +301,7 @@ pub async fn answer_faq_review_item(
|
||||
.bind(org_ctx.id)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al buscar item FAQ: {}", e)))?
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?
|
||||
.ok_or((StatusCode::NOT_FOUND, "Item de revisión no encontrado".to_string()))?;
|
||||
|
||||
if queue_item.0 == "dismissed" {
|
||||
@@ -335,7 +335,7 @@ pub async fn answer_faq_review_item(
|
||||
.bind(claims.sub)
|
||||
.fetch_one(&mut *tx)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al crear FAQ: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
sqlx::query(
|
||||
r#"
|
||||
@@ -358,7 +358,7 @@ pub async fn answer_faq_review_item(
|
||||
.bind(org_ctx.id)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al actualizar cola FAQ: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
} else {
|
||||
sqlx::query(
|
||||
r#"
|
||||
@@ -379,12 +379,12 @@ pub async fn answer_faq_review_item(
|
||||
.bind(org_ctx.id)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al responder cola FAQ: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
}
|
||||
|
||||
tx.commit()
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("No se pudo confirmar transacción FAQ: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
@@ -416,7 +416,7 @@ pub async fn dismiss_faq_review_item(
|
||||
.bind(org_ctx.id)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al descartar item FAQ: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
if result.rows_affected() == 0 {
|
||||
return Err((StatusCode::NOT_FOUND, "Item de revisión no encontrado".to_string()));
|
||||
@@ -475,7 +475,7 @@ pub async fn list_faq_entries(
|
||||
.bind(offset)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al listar FAQ: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
Ok(Json(rows))
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ pub async fn list_course_lti_tools(
|
||||
.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()))?;
|
||||
|
||||
let tools = rows
|
||||
.into_iter()
|
||||
@@ -139,7 +139,7 @@ pub async fn create_course_lti_tool(
|
||||
.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,
|
||||
@@ -206,7 +206,7 @@ pub async fn update_course_lti_tool(
|
||||
.bind(payload.config)
|
||||
.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, "Herramienta LTI no encontrada".to_string()))?;
|
||||
|
||||
Ok(Json(LtiExternalTool {
|
||||
@@ -236,7 +236,7 @@ pub async fn delete_course_lti_tool(
|
||||
.bind(course_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 res.rows_affected() == 0 {
|
||||
return Err((StatusCode::NOT_FOUND, "Herramienta LTI no encontrada".to_string()));
|
||||
@@ -276,7 +276,7 @@ pub async fn lti_grade_passback(
|
||||
.bind(tool_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, "Herramienta LTI no encontrada".to_string()))?;
|
||||
|
||||
let organization_id: Uuid = tool_row.get("organization_id");
|
||||
@@ -323,7 +323,7 @@ pub async fn lti_grade_passback(
|
||||
.bind(organization_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 !user_exists {
|
||||
return Err((StatusCode::UNPROCESSABLE_ENTITY, "user_id inválido para esta organización".to_string()));
|
||||
@@ -348,7 +348,7 @@ pub async fn lti_grade_passback(
|
||||
.bind(organization_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_ok {
|
||||
return Err((StatusCode::UNPROCESSABLE_ENTITY, "lesson_id no pertenece al curso".to_string()));
|
||||
@@ -384,7 +384,7 @@ pub async fn lti_grade_passback(
|
||||
.bind(payload.metadata.clone().unwrap_or(serde_json::json!({})))
|
||||
.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()))?;
|
||||
|
||||
// Sincronizar con gradebook solo cuando hay lesson_id
|
||||
if let Some(lesson_id) = payload.lesson_id {
|
||||
@@ -419,7 +419,7 @@ pub async fn lti_grade_passback(
|
||||
.bind(metadata)
|
||||
.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(Json(LtiGradePassbackResponse {
|
||||
@@ -458,7 +458,7 @@ pub async fn rotate_lti_tool_secret(
|
||||
.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 !tool_exists {
|
||||
return Err((StatusCode::NOT_FOUND, "Herramienta LTI no encontrada".to_string()));
|
||||
@@ -481,7 +481,7 @@ pub async fn rotate_lti_tool_secret(
|
||||
.bind(tool_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!(
|
||||
"rotate_lti_tool_secret: rotated secret for tool {} in course {} org {}",
|
||||
@@ -634,7 +634,7 @@ pub async fn lti_ags_score_passback(
|
||||
.bind(tool_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, "Herramienta LTI no encontrada".to_string()))?;
|
||||
|
||||
let client_id = config.ags_client_id.as_deref().unwrap_or("");
|
||||
|
||||
@@ -20,7 +20,7 @@ pub async fn get_note(
|
||||
.bind(lesson_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(note))
|
||||
}
|
||||
@@ -47,7 +47,7 @@ pub async fn save_note(
|
||||
.bind(payload.content)
|
||||
.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(note))
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ pub async fn create_payment_preference(
|
||||
.bind(&course.currency)
|
||||
.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()))?;
|
||||
|
||||
// 3. Llamar a la API de Mercado Pago
|
||||
let mp_access_token = std::env::var("MP_ACCESS_TOKEN").unwrap_or_default();
|
||||
@@ -125,7 +125,7 @@ pub async fn create_payment_preference(
|
||||
.bind(transaction_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(Json(PaymentPreferenceResponse {
|
||||
preference_id,
|
||||
|
||||
@@ -88,7 +88,7 @@ pub async fn get_lesson_quality_metrics(
|
||||
.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 enrolled == 0 {
|
||||
return Ok(Json(CourseQualityMetrics {
|
||||
@@ -146,7 +146,7 @@ pub async fn get_lesson_quality_metrics(
|
||||
.bind(enrolled)
|
||||
.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 lessons = rows
|
||||
.into_iter()
|
||||
@@ -211,7 +211,7 @@ pub async fn get_quiz_discrimination_index(
|
||||
.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()))?;
|
||||
|
||||
if rows.is_empty() {
|
||||
return Ok(Json(CourseDiscriminationReport {
|
||||
@@ -300,7 +300,7 @@ pub async fn get_curricular_suggestions(
|
||||
.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 enrolled < 5 {
|
||||
return Ok(Json(CurricularSuggestionsReport {
|
||||
@@ -335,7 +335,7 @@ pub async fn get_curricular_suggestions(
|
||||
.bind(enrolled)
|
||||
.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 mut suggestions: Vec<CurricularSuggestion> = vec![];
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ pub async fn submit_assignment(
|
||||
.bind(lesson_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()))?;
|
||||
|
||||
if let Some(_) = existing {
|
||||
// Actualizar entrega existente
|
||||
@@ -98,7 +98,7 @@ pub async fn submit_assignment(
|
||||
.bind(lesson_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()))?;
|
||||
|
||||
return Ok(Json(updated));
|
||||
}
|
||||
@@ -118,7 +118,7 @@ pub async fn submit_assignment(
|
||||
.bind(&payload.content)
|
||||
.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(submission))
|
||||
}
|
||||
@@ -158,7 +158,7 @@ pub async fn get_peer_review_assignment(
|
||||
.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(Json(submission))
|
||||
}
|
||||
@@ -177,7 +177,7 @@ pub async fn submit_peer_review(
|
||||
.bind(payload.submission_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()))?;
|
||||
|
||||
let submission_user_id = match submission_row {
|
||||
Some(row) => row.get::<Uuid, _>("user_id"),
|
||||
@@ -199,7 +199,7 @@ pub async fn submit_peer_review(
|
||||
.bind(claims.sub)
|
||||
.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()))?;
|
||||
|
||||
if existing.is_some() {
|
||||
return Err((
|
||||
@@ -223,7 +223,7 @@ pub async fn submit_peer_review(
|
||||
.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()))?;
|
||||
|
||||
// Recalcular nota final ponderada tras nueva revisión de par
|
||||
let lesson_id_for_calc: Uuid = sqlx::query_scalar(
|
||||
@@ -232,7 +232,7 @@ pub async fn submit_peer_review(
|
||||
.bind(payload.submission_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()))?;
|
||||
|
||||
let _ = recalculate_final_score(&pool, payload.submission_id, lesson_id_for_calc).await;
|
||||
|
||||
@@ -258,7 +258,7 @@ pub async fn get_my_submission_feedback(
|
||||
.bind(lesson_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(reviews))
|
||||
}
|
||||
@@ -287,7 +287,7 @@ pub async fn list_lesson_submissions(
|
||||
.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(submissions))
|
||||
}
|
||||
@@ -304,7 +304,7 @@ pub async fn get_submission_reviews(
|
||||
.bind(submission_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(reviews))
|
||||
}
|
||||
@@ -325,7 +325,7 @@ pub async fn get_peer_review_settings(
|
||||
.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(settings))
|
||||
}
|
||||
@@ -376,7 +376,7 @@ pub async fn upsert_peer_review_settings(
|
||||
.bind(auto_assign)
|
||||
.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(settings))
|
||||
}
|
||||
@@ -400,7 +400,7 @@ pub async fn auto_assign_peer_reviews(
|
||||
.bind(lesson_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()))?
|
||||
.unwrap_or(2);
|
||||
|
||||
// Entregar todas las submissions de esta lección
|
||||
@@ -411,7 +411,7 @@ pub async fn auto_assign_peer_reviews(
|
||||
.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 mut assignments_created: i64 = 0;
|
||||
|
||||
@@ -423,7 +423,7 @@ pub async fn auto_assign_peer_reviews(
|
||||
.bind(sub_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()))?;
|
||||
|
||||
let needed = (required as i64) - existing_count;
|
||||
if needed <= 0 {
|
||||
@@ -437,7 +437,7 @@ pub async fn auto_assign_peer_reviews(
|
||||
.bind(sub_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()))?;
|
||||
|
||||
// Candidatos: otros alumnos que no sean el autor y no hayan revisado ya
|
||||
let candidates: Vec<Uuid> = submissions
|
||||
@@ -486,7 +486,7 @@ pub async fn auto_assign_peer_reviews(
|
||||
.bind(lesson_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(Json(serde_json::json!({
|
||||
"lesson_id": lesson_id,
|
||||
@@ -517,7 +517,7 @@ pub async fn instructor_grade_submission(
|
||||
.bind(lesson_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 sub_exists.is_none() {
|
||||
return Err((StatusCode::NOT_FOUND, "Entrega no encontrada".to_string()));
|
||||
@@ -542,7 +542,7 @@ pub async fn instructor_grade_submission(
|
||||
.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()))?;
|
||||
|
||||
// Recalcular nota final ponderada
|
||||
recalculate_final_score(&pool, payload.submission_id, lesson_id).await?;
|
||||
@@ -570,7 +570,7 @@ pub async fn get_my_submission(
|
||||
.bind(lesson_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(sub))
|
||||
}
|
||||
@@ -594,7 +594,7 @@ async fn recalculate_final_score(
|
||||
.bind(submission_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()))?
|
||||
.unwrap_or((70i32, 30i32, 2i32));
|
||||
|
||||
// Promedio de revisiones de pares (no instructor)
|
||||
@@ -604,7 +604,7 @@ async fn recalculate_final_score(
|
||||
.bind(submission_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()))?
|
||||
.flatten();
|
||||
|
||||
// Calificación del instructor
|
||||
@@ -614,7 +614,7 @@ async fn recalculate_final_score(
|
||||
.bind(submission_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()))?
|
||||
.flatten();
|
||||
|
||||
// Solo calcular nota final si hay suficientes revisiones de pares O hay nota del instructor
|
||||
@@ -624,7 +624,7 @@ async fn recalculate_final_score(
|
||||
.bind(submission_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()))?;
|
||||
|
||||
let has_enough_peers = peer_count >= required as i64;
|
||||
let final_score = match (peer_avg, instructor_score, has_enough_peers) {
|
||||
@@ -653,7 +653,7 @@ async fn recalculate_final_score(
|
||||
.bind(submission_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()))?;
|
||||
|
||||
// También actualizar review_count en todas las submissions del mismo lesson
|
||||
let _ = sqlx::query(
|
||||
|
||||
@@ -74,7 +74,7 @@ pub async fn track_xapi_statement(
|
||||
.bind(payload.raw_statement)
|
||||
.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(Json(XapiStatementResponse {
|
||||
id: statement_id,
|
||||
|
||||
@@ -66,7 +66,7 @@ pub async fn global_search(
|
||||
.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()))?;
|
||||
|
||||
for (id, title, description) in courses {
|
||||
let snippet = description.map(|d| truncate(&d, 150));
|
||||
@@ -99,7 +99,7 @@ pub async fn global_search(
|
||||
.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()))?;
|
||||
|
||||
for (id, title, summary, course_id, course_title) in lessons {
|
||||
let snippet = summary.map(|s| truncate(&s, 150));
|
||||
|
||||
@@ -102,7 +102,7 @@ pub async fn list_course_study_rooms(
|
||||
.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 rooms = rows
|
||||
.into_iter()
|
||||
@@ -212,7 +212,7 @@ pub async fn create_study_room(
|
||||
.bind(now)
|
||||
.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,
|
||||
@@ -259,7 +259,7 @@ pub async fn join_study_room(
|
||||
.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, "Sala no encontrada".to_string()))?;
|
||||
|
||||
if room.status == "ended" {
|
||||
@@ -322,7 +322,7 @@ pub async fn end_study_room(
|
||||
.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, "Sala no encontrada".to_string()))?;
|
||||
|
||||
// Solo el creador puede terminar la sala
|
||||
@@ -351,7 +351,7 @@ pub async fn end_study_room(
|
||||
.bind(room_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(Json(EndStudyRoomResponse {
|
||||
room_id,
|
||||
@@ -373,7 +373,7 @@ pub async fn delete_study_room(
|
||||
.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, "Sala no encontrada".to_string()))?;
|
||||
|
||||
if claims.sub != created_by {
|
||||
@@ -384,7 +384,7 @@ pub async fn delete_study_room(
|
||||
.bind(room_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)
|
||||
}
|
||||
@@ -417,7 +417,7 @@ pub async fn get_study_room_recordings(
|
||||
.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()))?
|
||||
.flatten()
|
||||
.ok_or((StatusCode::NOT_FOUND, "Sala no encontrada o sin ID BBB".to_string()))?;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ pub async fn get_course_meetings(
|
||||
.bind(claims.org)
|
||||
.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(meetings))
|
||||
}
|
||||
@@ -65,7 +65,7 @@ pub async fn create_meeting(
|
||||
.bind(join_url)
|
||||
.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(meeting))
|
||||
}
|
||||
@@ -84,7 +84,7 @@ pub async fn delete_meeting(
|
||||
.bind(claims.org)
|
||||
.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)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ pub async fn lti_login_initiation(
|
||||
.bind(¶ms.client_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::BAD_REQUEST, "Registro LTI no encontrado".to_string()))?;
|
||||
|
||||
// 2. Generar estado y nonce
|
||||
@@ -45,7 +45,7 @@ pub async fn lti_login_initiation(
|
||||
.bind(&nonce)
|
||||
.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()))?;
|
||||
|
||||
// 4. Construir URL de redirección
|
||||
let mut url = format!(
|
||||
@@ -130,7 +130,7 @@ pub async fn lti_launch(
|
||||
.bind(aud)
|
||||
.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, "Registro LTI no encontrado para emisor/audiencia".to_string()))?;
|
||||
|
||||
// 3. Validar JWT
|
||||
@@ -143,7 +143,7 @@ pub async fn lti_launch(
|
||||
.bind(<i_claims.nonce)
|
||||
.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()))?
|
||||
.rows_affected() > 0;
|
||||
|
||||
if !nonce_exists {
|
||||
@@ -161,7 +161,7 @@ pub async fn lti_launch(
|
||||
.bind(registration.organization_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 user.is_none() {
|
||||
let new_user_id = Uuid::new_v4();
|
||||
@@ -182,7 +182,7 @@ pub async fn lti_launch(
|
||||
.bind(role)
|
||||
.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()))?;
|
||||
|
||||
user = Some(User {
|
||||
id: new_user_id,
|
||||
@@ -211,7 +211,7 @@ pub async fn lti_launch(
|
||||
let studio_url = std::env::var("NEXT_PUBLIC_STUDIO_URL").unwrap_or_else(|_| "http://localhost:3001".to_string());
|
||||
|
||||
let token = common::auth::create_jwt(user.id, user.organization_id, &user.role)
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al crear el token: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
let redirect_target = lti_claims.resource_link.as_ref().map(|rl| rl.id.clone()).unwrap_or_default();
|
||||
|
||||
if lti_claims.message_type == "LtiDeepLinkingRequest" {
|
||||
@@ -228,7 +228,7 @@ pub async fn lti_launch(
|
||||
.bind(&settings.data)
|
||||
.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(Redirect::to(&format!("{}/lti/deep-linking?token={}&dl_token={}", studio_url, token, dl_request_id)))
|
||||
} else {
|
||||
@@ -258,7 +258,7 @@ pub async fn lti_deep_linking_response(
|
||||
.bind(dl_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::UNAUTHORIZED, "Solicitud de DL inválida o expirada".to_string()))?;
|
||||
|
||||
// Mapeo manual ya que no podemos usar query!/query_as! fácilmente para RETURNING sin una estructura
|
||||
@@ -274,7 +274,7 @@ pub async fn lti_deep_linking_response(
|
||||
.bind(registration_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()))?;
|
||||
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let response_claims = common::models::LtiDeepLinkingResponseClaims {
|
||||
@@ -301,7 +301,7 @@ pub async fn lti_deep_linking_response(
|
||||
&response_claims,
|
||||
&private_key,
|
||||
)
|
||||
.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!({
|
||||
"jwt": response_jwt,
|
||||
|
||||
@@ -18,7 +18,7 @@ pub async fn get_public_profile(
|
||||
.bind(user_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, "Usuario no encontrado".to_string()))?;
|
||||
|
||||
let is_public: bool = user.get("is_public_profile");
|
||||
@@ -44,13 +44,13 @@ pub async fn get_public_profile(
|
||||
.bind(user_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()))?;
|
||||
|
||||
let completed_courses: i64 = sqlx::query("SELECT COUNT(*) FROM enrollments WHERE user_id = $1 AND progress >= 100")
|
||||
.bind(user_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()))?
|
||||
.get(0);
|
||||
|
||||
Ok(Json(PublicProfile {
|
||||
@@ -87,7 +87,7 @@ pub async fn get_my_badges(
|
||||
.bind(claims.sub)
|
||||
.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(badges))
|
||||
}
|
||||
@@ -106,7 +106,7 @@ pub async fn award_badge(
|
||||
.bind(payload.badge_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::CREATED)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ pub async fn get_course_dropout_risks(
|
||||
}
|
||||
|
||||
calculate_risks_for_course(&pool, course_id, claims.org).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 rows = sqlx::query(
|
||||
r#"
|
||||
@@ -34,7 +34,7 @@ pub async fn get_course_dropout_risks(
|
||||
.bind(claims.org)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, format!("Error al obtener los riesgos: {}", e)))?;
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Error interno del servidor".to_string()))?;
|
||||
|
||||
let risks: Vec<DropoutRisk> = rows.into_iter().map(|row| {
|
||||
DropoutRisk {
|
||||
|
||||
Reference in New Issue
Block a user