import hmac from datetime import datetime import pytz from flask import request, jsonify, current_app from app.models import db, Deck, Course, Password from . import internal_bp @internal_bp.route('/register-deck', methods=['POST']) def register_deck(): # Verify shared secret token = request.headers.get('X-Internal-Token', '') if not hmac.compare_digest(token, current_app.config['INTERNAL_TOKEN']): return jsonify({'error': 'Unauthorized'}), 401 data = request.get_json() if not data: return jsonify({'error': 'Invalid JSON'}), 400 for field in ('course', 'deck', 'name'): if not data.get(field): return jsonify({'error': f'Missing required field: {field}'}), 400 # Look up course by folder name course = db.session.execute( db.select(Course).where(Course.folder == data['course']) ).scalar_one_or_none() if not course: return jsonify({'error': f"Course '{data['course']}' not found"}), 404 # Upsert deck # NOTE: Deck.__init__ takes course= (object), not course_id= deck = db.session.execute( db.select(Deck).where( Deck.deck == data['deck'], Deck.course_id == course.id ) ).scalar_one_or_none() if deck: deck.name = data['name'] deck.description = data['description'] deck.export_file = data.get('export_file') or None else: deck = Deck( name = data['name'], deck = data['deck'], course = course, # pass object, not course_id description = data['description'], index_file = data.get('index_file', 'index.html'), export_file = data.get('export_file') or None, ) db.session.add(deck) db.session.flush() # populate deck.id before linking password # Upsert password if provided if data.get('password'): expires_at = None if data.get('password_expires'): try: # Parse ISO string and convert to UTC naive (matches UTCDateTime model) expires_at = datetime.fromisoformat( data['password_expires'].replace('Z', '+00:00') ).astimezone(pytz.utc) except ValueError: current_app.logger.warning( f"Invalid password_expires format: {data['password_expires']}" ) existing_pw = db.session.execute( db.select(Password).where(Password.value == data['password']) ).scalar_one_or_none() if existing_pw: existing_pw.expires_at = expires_at if deck not in existing_pw.decks: existing_pw.decks.append(deck) else: pw = Password(value=data['password'], expires_at=expires_at) pw.decks.append(deck) db.session.add(pw) db.session.commit() current_app.logger.info( f"Registered deck '{data['deck']}' for course '{data['course']}'" ) return jsonify({'status': 'ok', 'deck': data['deck']}), 200