fix(classrooms): blindar vistas presenciales y virtuales sin aulas + feat(admin): reinicializacion de sistema con clave de admin
This commit is contained in:
@@ -2565,3 +2565,136 @@ def delete_building(id):
|
||||
'status': 'success',
|
||||
'message': f'Edificio "{building.name}" eliminado correctamente.'
|
||||
}), 200
|
||||
|
||||
|
||||
@api_admin_bp.route('/system/reset', methods=['POST'])
|
||||
@api_admin_bp.route('/settings/reset_system', methods=['POST'])
|
||||
@jwt_required
|
||||
def reset_entire_system():
|
||||
"""
|
||||
Reinicializa el sistema depurando historiales, reservas, espacios, sedes y académica.
|
||||
PRESERVA: Todas las cuentas de usuario, roles RBAC, permisos, configuraciones globales
|
||||
y tipos de hitos (pestaña Administración intacta).
|
||||
REQUISITO CRÍTICO DE SEGURIDAD: Debe solicitar y validar la contraseña del perfil administrador en sesión.
|
||||
"""
|
||||
from app.models.grade import MilestoneGrade
|
||||
from app.models.enrollment import StudentEnrollment
|
||||
from app.models.classroom import ClassroomResource
|
||||
from app.models.building import Building
|
||||
from app.models.sync_task import MoodleSyncTask
|
||||
|
||||
acting_user = getattr(g, 'jwt_user', None)
|
||||
if not (acting_user and (acting_user.is_admin() or getattr(acting_user, 'role', '').upper() == 'ADMIN')):
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'error': 'Forbidden',
|
||||
'message': 'Acceso denegado. Solo un usuario con perfil Administrador puede solicitar la reinicialización del sistema.'
|
||||
}), 403
|
||||
|
||||
data = request.get_json(silent=True) or request.form.to_dict() or {}
|
||||
password = str(data.get('password') or '').strip()
|
||||
|
||||
if not password:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'error': 'BadRequest',
|
||||
'message': 'Debe ingresar la contraseña de su usuario administrador para autorizar la reinicialización.'
|
||||
}), 400
|
||||
|
||||
if not acting_user.check_password(password):
|
||||
# Registrar intento fallido de reinicio en bitácora de auditoría
|
||||
try:
|
||||
failed_audit = AuditLog(
|
||||
user_id=acting_user.id,
|
||||
user_email=acting_user.email,
|
||||
action='SYSTEM_RESET_FAILED',
|
||||
module='admin',
|
||||
details='Intento de reinicialización del sistema cancelado: contraseña incorrecta.',
|
||||
ip_address=request.remote_addr
|
||||
)
|
||||
db.session.add(failed_audit)
|
||||
db.session.commit()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'error': 'Unauthorized',
|
||||
'message': 'Contraseña de administrador incorrecta. Operación cancelada por seguridad.'
|
||||
}), 401
|
||||
|
||||
try:
|
||||
# Purga referencial ordenada de entidades operativas
|
||||
# 1. Reservas
|
||||
reservations_deleted = Reservation.query.delete()
|
||||
|
||||
# 2. Calificaciones e Hitos de cursadas
|
||||
grades_deleted = MilestoneGrade.query.delete()
|
||||
milestones_deleted = AcademicMilestone.query.delete()
|
||||
|
||||
# 3. Matrículas de alumnos y asignaciones docentes a comisiones
|
||||
enrollments_deleted = StudentEnrollment.query.delete()
|
||||
teachers_deleted = CommissionTeacher.query.delete()
|
||||
|
||||
# 4. Comisiones, Materias y Carreras
|
||||
commissions_deleted = Commission.query.delete()
|
||||
subjects_deleted = Subject.query.delete()
|
||||
careers_deleted = Career.query.delete()
|
||||
|
||||
# 5. Recursos de aula, Aulas y Edificios/Sedes
|
||||
resources_deleted = ClassroomResource.query.delete()
|
||||
classrooms_deleted = Classroom.query.delete()
|
||||
buildings_deleted = Building.query.delete()
|
||||
|
||||
# 6. Ciclos Lectivos / Términos Académicos
|
||||
terms_deleted = AcademicTerm.query.delete()
|
||||
|
||||
# 7. Cola de eventos de sincronización Moodle huérfanos
|
||||
moodle_tasks_deleted = MoodleSyncTask.query.delete()
|
||||
|
||||
# 8. Historiales: Limpiar auditorías anteriores
|
||||
logs_deleted = AuditLog.query.delete()
|
||||
|
||||
# 9. Asentar registro oficial inmutable del reinicio en AuditLog
|
||||
reset_audit = AuditLog(
|
||||
user_id=acting_user.id,
|
||||
user_email=acting_user.email,
|
||||
action='SYSTEM_RESET',
|
||||
module='admin',
|
||||
details=(
|
||||
f"Reinicialización total ejecutada por administrador {acting_user.email}. "
|
||||
f"Entidades purgadas: {reservations_deleted} reservas, {classrooms_deleted} aulas, "
|
||||
f"{buildings_deleted} sedes, {commissions_deleted} comisiones, {subjects_deleted} materias, "
|
||||
f"{careers_deleted} carreras, {terms_deleted} ciclos lectivos, {enrollments_deleted} inscripciones, "
|
||||
f"{grades_deleted} notas, {logs_deleted} logs históricos. Cuentas y configuración administrativa preservadas."
|
||||
),
|
||||
ip_address=request.remote_addr
|
||||
)
|
||||
db.session.add(reset_audit)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
'message': 'Sistema reinicializado correctamente. Se han depurado los historiales, reservas, espacios, sedes y académica. Las cuentas de usuario y configuraciones se encuentran intactas.',
|
||||
'purged_stats': {
|
||||
'reservations': reservations_deleted,
|
||||
'classrooms': classrooms_deleted,
|
||||
'buildings': buildings_deleted,
|
||||
'commissions': commissions_deleted,
|
||||
'subjects': subjects_deleted,
|
||||
'careers': careers_deleted,
|
||||
'academic_terms': terms_deleted,
|
||||
'enrollments': enrollments_deleted,
|
||||
'grades': grades_deleted,
|
||||
'prior_audit_logs': logs_deleted
|
||||
}
|
||||
}), 200
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'error': 'InternalServerError',
|
||||
'message': f'Error durante la reinicialización de la base de datos: {str(e)}'
|
||||
}), 500
|
||||
|
||||
|
||||
Reference in New Issue
Block a user