Arch/routes/user_routes.py

232 lines
7.0 KiB
Python
Raw Permalink Normal View History

2025-03-03 17:50:11 -03:00
from flask import (
Blueprint,
render_template,
redirect,
url_for,
flash,
request,
jsonify,
current_app,
)
from flask_login import login_required, current_user
from flask_wtf import FlaskForm
from wtforms import (
StringField,
PasswordField,
SelectField,
IntegerField,
EmailField,
BooleanField,
SubmitField,
)
from wtforms.validators import DataRequired, Email, Length, Optional, EqualTo
from services.auth_service import (
get_all_users,
get_user_by_username,
create_user,
update_user,
delete_user,
)
from services.user_service import (
filter_users,
get_user_stats,
check_username_availability,
)
from utils.validators import validate_user_data
from utils.security import permission_required
from utils.logger import log_user_management
# Definir Blueprint
users_bp = Blueprint("users", __name__, url_prefix="/users")
# Formularios
class UserForm(FlaskForm):
"""Formulario para crear/editar usuarios."""
nombre = StringField("Nombre completo", validators=[DataRequired(), Length(1, 100)])
username = StringField(
"Nombre de usuario", validators=[DataRequired(), Length(3, 20)]
)
email = EmailField("Email", validators=[DataRequired(), Email()])
password = PasswordField("Contraseña", validators=[Optional(), Length(8, 64)])
password_confirm = PasswordField(
"Confirmar contraseña",
validators=[
Optional(),
EqualTo("password", message="Las contraseñas deben coincidir"),
],
)
nivel = IntegerField("Nivel de acceso", validators=[DataRequired()])
idioma = SelectField("Idioma", choices=[("es", "Español"), ("en", "Inglés")])
empresa = StringField("Empresa", validators=[Optional(), Length(0, 100)])
estado = SelectField(
"Estado", choices=[("activo", "Activo"), ("inactivo", "Inactivo")]
)
fecha_caducidad = StringField(
"Fecha de caducidad (YYYY-MM-DD)", validators=[Optional()]
)
submit = SubmitField("Guardar")
class UserFilterForm(FlaskForm):
"""Formulario para filtrar usuarios."""
empresa = StringField("Empresa", validators=[Optional()])
estado = SelectField(
"Estado",
choices=[("", "Todos"), ("activo", "Activo"), ("inactivo", "Inactivo")],
validators=[Optional()],
)
nivel_min = IntegerField("Nivel mínimo", validators=[Optional()])
nivel_max = IntegerField("Nivel máximo", validators=[Optional()])
submit = SubmitField("Filtrar")
# Rutas
@users_bp.route("/")
@login_required
2025-03-04 07:46:15 -03:00
@permission_required(1000) # Minimum permission level
2025-03-03 17:50:11 -03:00
def list():
"""List all users."""
users = get_all_users()
return render_template("users/list.html", users=users)
@users_bp.route("/create", methods=["GET", "POST"])
@login_required
2025-03-04 07:46:15 -03:00
@permission_required(9000) # Admin level permission required
def create(): # Changed from create_user to create
"""Create a new user."""
2025-03-03 17:50:11 -03:00
form = UserForm()
2025-03-04 07:46:15 -03:00
if request.method == "POST" and form.validate_on_submit():
try:
# Extract user data from form
user_data = {
"username": form.username.data,
"nombre": form.nombre.data,
"email": form.email.data,
"password": form.password.data,
"nivel": form.nivel.data,
"empresa": form.empresa.data,
"idioma": form.idioma.data,
}
# Create the user
result = create_user(user_data) # This is a function call, not a route
if result["success"]:
flash("Usuario creado exitosamente.", "success")
return redirect(url_for("users.list"))
else:
flash(f'Error al crear usuario: {result["error"]}', "danger")
except Exception as e:
flash(f"Error inesperado: {str(e)}", "danger")
# If GET request or form validation failed
2025-03-03 17:50:11 -03:00
return render_template("users/create.html", form=form)
@users_bp.route("/edit/<username>", methods=["GET", "POST"])
@login_required
@permission_required(9000) # Solo administradores
def edit(username):
"""Editar usuario existente."""
user = get_user_by_username(username)
if not user:
flash(f"Usuario {username} no encontrado.", "danger")
2025-03-04 07:46:15 -03:00
return redirect(url_for("users.list"))
2025-03-03 17:50:11 -03:00
form = UserForm(obj=user)
if form.validate_on_submit():
# Validar datos
data = {
"nombre": form.nombre.data,
"email": form.email.data,
"password": form.password.data,
"nivel": form.nivel.data,
"idioma": form.idioma.data,
"empresa": form.empresa.data,
"estado": form.estado.data,
"fecha_caducidad": (
form.fecha_caducidad.data if form.fecha_caducidad.data else None
),
}
is_valid, errors = validate_user_data(data, is_new_user=False)
if not is_valid:
for field, error in errors.items():
flash(f"Error en {field}: {error}", "danger")
return render_template("users/edit.html", form=form, username=username)
# Actualizar usuario
success, message = update_user(username, data)
if success:
flash(message, "success")
# Registrar actividad
log_user_management(
admin_id=current_user.id,
target_user_id=username,
action="update",
details={"fields_updated": list(data.keys())},
)
2025-03-04 07:46:15 -03:00
return redirect(url_for("users.list"))
2025-03-03 17:50:11 -03:00
else:
flash(message, "danger")
return render_template("users/edit.html", form=form, username=username)
@users_bp.route("/delete/<username>", methods=["POST"])
@login_required
@permission_required(9000) # Solo administradores
def delete(username):
"""Eliminar usuario."""
if username == current_user.id:
flash("No puede eliminar su propio usuario.", "danger")
2025-03-04 07:46:15 -03:00
return redirect(url_for("users.list"))
2025-03-03 17:50:11 -03:00
if username == "admin":
flash("No se puede eliminar el usuario administrador.", "danger")
2025-03-04 07:46:15 -03:00
return redirect(url_for("users.list"))
2025-03-03 17:50:11 -03:00
success, message = delete_user(username)
if success:
flash(message, "success")
# Registrar actividad
log_user_management(
admin_id=current_user.id, target_user_id=username, action="delete"
)
else:
flash(message, "danger")
2025-03-04 07:46:15 -03:00
return redirect(url_for("users.list"))
2025-03-03 17:50:11 -03:00
# API para verificar disponibilidad de nombre de usuario
@users_bp.route("/api/check_username", methods=["POST"])
@login_required
@permission_required(9000) # Solo administradores
def api_check_username():
"""Verificar disponibilidad de nombre de usuario."""
data = request.get_json()
if not data or "username" not in data:
return jsonify({"error": "Se requiere nombre de usuario"}), 400
username = data["username"]
available = check_username_availability(username)
return jsonify({"available": available})