Files
tutor-tool/app/blueprints/internal/routes.py
2026-02-22 03:08:09 +01:00

88 lines
3.0 KiB
Python

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