admin.py 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. from flask import Blueprint, render_template, request
  2. from flask_login import login_required, current_user
  3. from functools import wraps
  4. from flask import abort
  5. admin_bp = Blueprint('admin', __name__)
  6. def admin_required(f):
  7. @wraps(f)
  8. def decorated_function(*args, **kwargs):
  9. if not current_user.is_authenticated or not current_user.is_admin:
  10. abort(403)
  11. return f(*args, **kwargs)
  12. return decorated_function
  13. @admin_bp.route('/')
  14. @admin_required
  15. def dashboard():
  16. return render_template('admin/dashboard.html')
  17. @admin_bp.route('/users', methods=['GET', 'POST'])
  18. @login_required
  19. @admin_required
  20. def user_search():
  21. from models import User
  22. users = []
  23. query = request.args.get('q', '')
  24. if query:
  25. # Fuzzy search for Discord or Minecraft username
  26. # utilizing ILIKE for case-insensitive search if supported, or just simple LIKE
  27. search_term = f"%{query}%"
  28. users = User.query.filter(
  29. (User.username.ilike(search_term)) |
  30. (User.minecraft_username.ilike(search_term))
  31. ).all()
  32. return render_template('admin/user_search.html', users=users, query=query)
  33. @admin_bp.route('/users/<int:user_id>')
  34. @login_required
  35. @admin_required
  36. def user_detail(user_id):
  37. from models import User, Ticket, TicketAssignment
  38. user = User.query.get_or_404(user_id)
  39. # Find relevant tickets
  40. relevant_tickets = Ticket.query.outerjoin(TicketAssignment).filter(
  41. (Ticket.user_id == user.id) | (TicketAssignment.user_id == user.id)
  42. ).all()
  43. return render_template('admin/user_detail.html', user=user, tickets=relevant_tickets)
  44. @admin_bp.route('/settings/events', methods=['GET', 'POST'])
  45. @login_required
  46. @admin_required
  47. def event_settings():
  48. from models import EventConfig
  49. from extensions import db, socketio
  50. if request.method == 'POST':
  51. # Update configs
  52. all_configs = EventConfig.query.all()
  53. for config in all_configs:
  54. # Checkbox: if present, it's 'on'. keys are event_name
  55. config.is_enabled = request.form.get(config.event_name) == 'on'
  56. db.session.commit()
  57. # Send update to plugin
  58. enabled_events = [c.event_name for c in EventConfig.query.filter_by(is_enabled=True).all()]
  59. socketio.emit('config_update', {'enabled_events': enabled_events})
  60. # Flash message ideally, but we'll specificy return to page
  61. return render_template('admin/event_settings.html',
  62. configs=group_configs(EventConfig.query.all()),
  63. success=True)
  64. configs = EventConfig.query.all()
  65. if not configs:
  66. # Prompt user to restart/connect server if empty
  67. pass
  68. return render_template('admin/event_settings.html', configs=group_configs(configs))
  69. def group_configs(configs):
  70. # Group by package
  71. grouped = {}
  72. for config in configs:
  73. pkg = config.package_name
  74. if pkg not in grouped:
  75. grouped[pkg] = []
  76. grouped[pkg].append(config)
  77. # Sort keys
  78. return dict(sorted(grouped.items()))