Files
triviathang/backend/routes/admin.py
2025-12-22 14:47:25 -05:00

305 lines
9.0 KiB
Python

from flask import Blueprint, request, jsonify
from backend.models import db, Game, Team
from backend.services import game_service
from backend.app import socketio
from backend.auth.middleware import require_auth
bp = Blueprint('admin', __name__, url_prefix='/api/admin')
@bp.route('/game/<int:game_id>/start', methods=['POST'])
@require_auth
def start_game(game_id):
"""Start/activate a game"""
game = Game.query.get_or_404(game_id)
try:
game_service.start_game(game, socketio)
return jsonify({'message': 'Game started successfully', 'game': game.to_dict()}), 200
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/next', methods=['POST'])
@require_auth
def next_question(game_id):
"""Move to next question"""
game = Game.query.get_or_404(game_id)
try:
if game_service.next_question(game, socketio):
return jsonify({'message': 'Moved to next question', 'current_index': game.current_question_index}), 200
else:
return jsonify({'error': 'Already at last question'}), 400
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/prev', methods=['POST'])
@require_auth
def previous_question(game_id):
"""Move to previous question"""
game = Game.query.get_or_404(game_id)
try:
if game_service.previous_question(game, socketio):
return jsonify({'message': 'Moved to previous question', 'current_index': game.current_question_index}), 200
else:
return jsonify({'error': 'Already at first question'}), 400
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/award', methods=['POST'])
@require_auth
def award_points(game_id):
"""Award points to a team
Expected JSON: { "team_id": int, "points": int }
"""
game = Game.query.get_or_404(game_id)
data = request.get_json()
if not data or 'team_id' not in data or 'points' not in data:
return jsonify({'error': 'team_id and points are required'}), 400
team_id = data['team_id']
points = data['points']
team = Team.query.get_or_404(team_id)
# Verify team belongs to this game
if team.game_id != game_id:
return jsonify({'error': 'Team does not belong to this game'}), 400
try:
game_service.award_points(game, team, points, socketio)
return jsonify({'message': 'Points awarded successfully', 'team': team.to_dict()}), 200
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/current', methods=['GET'])
@require_auth
def get_current_state(game_id):
"""Get current game state with answer (admin only)"""
game = Game.query.get_or_404(game_id)
try:
state = game_service.get_admin_game_state(game)
return jsonify(state), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/toggle-answer', methods=['POST'])
@require_auth
def toggle_answer_visibility(game_id):
"""Toggle answer visibility on contestant screen
Expected JSON: { "show_answer": bool }
"""
game = Game.query.get_or_404(game_id)
data = request.get_json()
if not data or 'show_answer' not in data:
return jsonify({'error': 'show_answer is required'}), 400
show_answer = data['show_answer']
try:
game_service.toggle_answer_visibility(game, show_answer, socketio)
return jsonify({'message': 'Answer visibility toggled', 'show_answer': show_answer}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/pause-timer', methods=['POST'])
@require_auth
def pause_timer(game_id):
"""Pause or resume the timer
Expected JSON: { "paused": bool }
"""
game = Game.query.get_or_404(game_id)
data = request.get_json()
if not data or 'paused' not in data:
return jsonify({'error': 'paused is required'}), 400
paused = data['paused']
try:
game_service.toggle_timer_pause(game, paused, socketio)
return jsonify({'message': 'Timer pause state updated', 'paused': paused}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/reset-timer', methods=['POST'])
@require_auth
def reset_timer(game_id):
"""Reset the timer to 30 seconds"""
game = Game.query.get_or_404(game_id)
try:
game_service.reset_timer(game, socketio)
return jsonify({'message': 'Timer reset'}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/end', methods=['POST'])
@require_auth
def end_game(game_id):
"""End/deactivate a game"""
game = Game.query.get_or_404(game_id)
try:
game_service.end_game(game, socketio)
return jsonify({'message': 'Game ended successfully', 'game': game.to_dict()}), 200
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/restart', methods=['POST'])
@require_auth
def restart_game(game_id):
"""Restart a game (clear scores and reset to waiting state)"""
game = Game.query.get_or_404(game_id)
try:
game_service.restart_game(game, socketio)
return jsonify({'message': 'Game restarted successfully', 'game': game.to_dict()}), 200
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/team/<int:team_id>/use-lifeline', methods=['POST'])
@require_auth
def use_lifeline(game_id, team_id):
"""Use a phone-a-friend lifeline for a team"""
game = Game.query.get_or_404(game_id)
team = Team.query.get_or_404(team_id)
# Verify team belongs to this game
if team.game_id != game_id:
return jsonify({'error': 'Team does not belong to this game'}), 400
if team.phone_a_friend_count <= 0:
return jsonify({'error': 'No lifelines remaining'}), 400
try:
team.phone_a_friend_count -= 1
db.session.commit()
# Broadcast lifeline update
game_service.broadcast_lifeline_update(game, team, socketio)
return jsonify({'message': 'Lifeline used', 'team': team.to_dict()}), 200
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/team/<int:team_id>/add-lifeline', methods=['POST'])
@require_auth
def add_lifeline(game_id, team_id):
"""Add a phone-a-friend lifeline to a team"""
game = Game.query.get_or_404(game_id)
team = Team.query.get_or_404(team_id)
# Verify team belongs to this game
if team.game_id != game_id:
return jsonify({'error': 'Team does not belong to this game'}), 400
try:
team.phone_a_friend_count += 1
db.session.commit()
# Broadcast lifeline update
game_service.broadcast_lifeline_update(game, team, socketio)
return jsonify({'message': 'Lifeline added', 'team': team.to_dict()}), 200
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/audio/play', methods=['POST'])
@require_auth
def play_audio(game_id):
"""Admin controls audio playback for contestants"""
game = Game.query.get_or_404(game_id)
try:
game_service.broadcast_audio_play(game, socketio)
return jsonify({'message': 'Audio play command sent'}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/audio/pause', methods=['POST'])
@require_auth
def pause_audio(game_id):
"""Pause audio for all contestants"""
game = Game.query.get_or_404(game_id)
try:
game_service.broadcast_audio_pause(game, socketio)
return jsonify({'message': 'Audio pause command sent'}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/audio/stop', methods=['POST'])
@require_auth
def stop_audio(game_id):
"""Stop and reset audio for all contestants"""
game = Game.query.get_or_404(game_id)
try:
game_service.broadcast_audio_stop(game, socketio)
return jsonify({'message': 'Audio stop command sent'}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/game/<int:game_id>/audio/seek', methods=['POST'])
@require_auth
def seek_audio(game_id):
"""Seek audio to specific position
Expected JSON: { "position": float }
"""
game = Game.query.get_or_404(game_id)
data = request.get_json()
if not data or 'position' not in data:
return jsonify({'error': 'position is required'}), 400
try:
position = float(data['position'])
game_service.broadcast_audio_seek(game, position, socketio)
return jsonify({'message': f'Audio seeked to {position}s'}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500