feat: fixing certificate and block
This commit is contained in:
@@ -2779,6 +2779,13 @@ pub async fn register(
|
||||
State(pool): State<PgPool>,
|
||||
Json(payload): Json<AuthPayload>,
|
||||
) -> Result<Json<AuthResponse>, (StatusCode, String)> {
|
||||
if payload.email.trim().is_empty() || payload.password.trim().is_empty() {
|
||||
return Err((
|
||||
StatusCode::BAD_REQUEST,
|
||||
"El email y la contraseña son obligatorios".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let password_hash = hash(payload.password, DEFAULT_COST)
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Hashing failed".into()))?;
|
||||
|
||||
|
||||
@@ -98,7 +98,9 @@ async fn upsert_organization_exercise_settings(
|
||||
organization_id: Uuid,
|
||||
payload: &UpdateOrganizationExerciseSettingsPayload,
|
||||
) -> Result<OrganizationExerciseSettings, sqlx::Error> {
|
||||
sqlx::query_as::<_, OrganizationExerciseSettings>(
|
||||
let mut tx = pool.begin().await?;
|
||||
|
||||
let settings = sqlx::query_as::<_, OrganizationExerciseSettings>(
|
||||
r#"
|
||||
INSERT INTO organization_exercise_settings (
|
||||
organization_id,
|
||||
@@ -144,8 +146,21 @@ async fn upsert_organization_exercise_settings(
|
||||
.bind(payload.mermaid_enabled)
|
||||
.bind(payload.code_lab_enabled)
|
||||
.bind(payload.certificates_enabled)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
|
||||
// Sincronizar con la tabla organizations para que el LMS reciba el valor correcto al publicar
|
||||
sqlx::query(
|
||||
"UPDATE organizations SET certificates_enabled = $1, updated_at = NOW() WHERE id = $2"
|
||||
)
|
||||
.bind(payload.certificates_enabled)
|
||||
.bind(organization_id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
pub async fn get_organization_exercise_settings(
|
||||
|
||||
@@ -60,8 +60,8 @@ pub async fn sync_sam_students(
|
||||
State(pool): State<PgPool>,
|
||||
) -> Result<Json<SamSyncResponse>, (StatusCode, String)> {
|
||||
// Conectar a la base de datos externa de SAM
|
||||
let sam_url = std::env::var("SAM_DATABASE_URL")
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "SAM_DATABASE_URL no configurada".to_string()))?;
|
||||
let sam_url = std::env::var("SAM_DIAGNOSTICO_DATABASE_URL")
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "SAM_DIAGNOSTICO_DATABASE_URL no configurada".to_string()))?;
|
||||
|
||||
let sam_pool = sqlx::PgPool::connect(&sam_url)
|
||||
.await
|
||||
@@ -191,8 +191,8 @@ pub async fn sync_sam_assignments(
|
||||
State(pool): State<PgPool>,
|
||||
) -> Result<Json<SamSyncResponse>, (StatusCode, String)> {
|
||||
// Conectar a la base de datos externa de SAM
|
||||
let sam_url = std::env::var("SAM_DATABASE_URL")
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "SAM_DATABASE_URL no configurada".to_string()))?;
|
||||
let sam_url = std::env::var("SAM_DIAGNOSTICO_DATABASE_URL")
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "SAM_DIAGNOSTICO_DATABASE_URL no configurada".to_string()))?;
|
||||
|
||||
let sam_pool = sqlx::PgPool::connect(&sam_url)
|
||||
.await
|
||||
@@ -382,8 +382,8 @@ pub async fn sync_all_sam(
|
||||
let mut assignments_synced = 0;
|
||||
|
||||
// Conectar a la base de datos externa de SAM
|
||||
let sam_url = std::env::var("SAM_DATABASE_URL")
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "SAM_DATABASE_URL no configurada".to_string()))?;
|
||||
let sam_url = std::env::var("SAM_DIAGNOSTICO_DATABASE_URL")
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "SAM_DIAGNOSTICO_DATABASE_URL no configurada".to_string()))?;
|
||||
|
||||
let sam_pool = sqlx::PgPool::connect(&sam_url)
|
||||
.await
|
||||
|
||||
@@ -161,8 +161,16 @@ async fn main() {
|
||||
])
|
||||
.expose_headers([header::CONTENT_LENGTH, header::CONTENT_TYPE, header::CONTENT_RANGE, header::ACCEPT_RANGES]);
|
||||
|
||||
// Rate limiting: Deshabilitado temporalmente por problemas de compatibilidad con tower-governor
|
||||
// Para habilitar en producción, configurar con GovernorLayer y ajustar los límites apropiadamente
|
||||
use tower_governor::{GovernorConfigBuilder, GovernorLayer};
|
||||
use std::sync::Arc;
|
||||
|
||||
let governor_conf = Arc::new(
|
||||
GovernorConfigBuilder::default()
|
||||
.per_second(5) // CMS usually has more complex operations, slightly lower limit
|
||||
.burst_size(20)
|
||||
.finish()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
// Rutas protegidas que requieren autenticación y contexto de organización
|
||||
let protected_routes = Router::new()
|
||||
@@ -500,7 +508,10 @@ async fn main() {
|
||||
)
|
||||
.route_layer(middleware::from_fn(
|
||||
common::middleware::org_extractor_middleware,
|
||||
));
|
||||
))
|
||||
.route_layer(GovernorLayer {
|
||||
config: governor_conf,
|
||||
});
|
||||
|
||||
let api_routes = Router::new()
|
||||
.route(
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
-- Migration: Automatic Enrollment Progress Update via Trigger
|
||||
-- Created: 2026-04-14
|
||||
|
||||
-- 1. Function to recalculate course progress for a student
|
||||
CREATE OR REPLACE FUNCTION fn_recalculate_enrollment_progress()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
v_total_lessons INTEGER;
|
||||
v_completed_lessons INTEGER;
|
||||
v_progress FLOAT4;
|
||||
BEGIN
|
||||
-- Get total number of lessons in the course
|
||||
-- We use the course_id from the new grade entry
|
||||
SELECT COUNT(*) INTO v_total_lessons
|
||||
FROM lessons
|
||||
WHERE module_id IN (SELECT id FROM modules WHERE course_id = NEW.course_id);
|
||||
|
||||
-- Get number of distinct lessons with a grade for this user in this course
|
||||
SELECT COUNT(DISTINCT lesson_id) INTO v_completed_lessons
|
||||
FROM user_grades
|
||||
WHERE user_id = NEW.user_id AND course_id = NEW.course_id;
|
||||
|
||||
-- Calculate progress percentage
|
||||
IF v_total_lessons > 0 THEN
|
||||
v_progress := (v_completed_lessons::FLOAT4 / v_total_lessons::FLOAT4) * 100;
|
||||
ELSE
|
||||
v_progress := 0;
|
||||
END IF;
|
||||
|
||||
-- Update the enrollments table for this specific user and course
|
||||
UPDATE enrollments
|
||||
SET progress = v_progress
|
||||
WHERE user_id = NEW.user_id AND course_id = NEW.course_id;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- 2. Trigger to execute the function after any activity is graded
|
||||
DROP TRIGGER IF EXISTS tr_update_enrollment_progress ON user_grades;
|
||||
CREATE TRIGGER tr_update_enrollment_progress
|
||||
AFTER INSERT OR UPDATE ON user_grades
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION fn_recalculate_enrollment_progress();
|
||||
|
||||
-- 3. Initial sync: Update progress for all existing enrollments
|
||||
UPDATE enrollments e
|
||||
SET progress = COALESCE(
|
||||
(
|
||||
SELECT (COUNT(DISTINCT ug.lesson_id)::FLOAT4 / NULLIF((
|
||||
SELECT COUNT(*)
|
||||
FROM lessons l
|
||||
WHERE l.module_id IN (SELECT id FROM modules m WHERE m.course_id = e.course_id)
|
||||
), 0)::FLOAT4) * 100
|
||||
FROM user_grades ug
|
||||
WHERE ug.user_id = e.user_id AND ug.course_id = e.course_id
|
||||
),
|
||||
0
|
||||
);
|
||||
@@ -119,15 +119,16 @@ async fn main() {
|
||||
])
|
||||
.expose_headers([header::CONTENT_LENGTH, header::CONTENT_TYPE]);
|
||||
|
||||
// Rate limiter DESHABILITADO debido a problemas de compatibilidad con el middleware de autenticación
|
||||
// Ver QWEN.md para más detalles
|
||||
// let governor_conf = Arc::new(
|
||||
// GovernorConfigBuilder::default()
|
||||
// .per_second(10)
|
||||
// .burst_size(50)
|
||||
// .finish()
|
||||
// .unwrap(),
|
||||
// );
|
||||
use tower_governor::{GovernorConfigBuilder, GovernorLayer};
|
||||
use std::sync::Arc;
|
||||
|
||||
let governor_conf = Arc::new(
|
||||
GovernorConfigBuilder::default()
|
||||
.per_second(10)
|
||||
.burst_size(50)
|
||||
.finish()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
// Rate limiter solo para rutas protegidas (después del middleware de autenticación)
|
||||
let protected_routes = Router::new()
|
||||
@@ -343,7 +344,10 @@ async fn main() {
|
||||
)
|
||||
.route_layer(middleware::from_fn(
|
||||
common::middleware::org_extractor_middleware,
|
||||
));
|
||||
))
|
||||
.route_layer(GovernorLayer {
|
||||
config: governor_conf,
|
||||
});
|
||||
|
||||
let public_routes = Router::new()
|
||||
.route("/api-docs/openapi.json", get(|| async {
|
||||
|
||||
Reference in New Issue
Block a user