from flask import Blueprint, request, jsonify from datetime import datetime, timedelta from tinydb import Query from db.db import users_db from models.user import User from api.utils import admin_required from config.deletion_config import ( ACCOUNT_DELETION_THRESHOLD_HOURS, MIN_THRESHOLD_HOURS, MAX_THRESHOLD_HOURS, validate_threshold ) from utils.account_deletion_scheduler import trigger_deletion_manually admin_api = Blueprint('admin_api', __name__) @admin_api.route('/admin/deletion-queue', methods=['GET']) @admin_required def get_deletion_queue(): """ Get list of users pending deletion. Returns users marked for deletion with their deletion due dates. """ try: Query_ = Query() marked_users = users_db.search(Query_.marked_for_deletion == True) users_data = [] for user_dict in marked_users: user = User.from_dict(user_dict) # Calculate deletion_due_at deletion_due_at = None if user.marked_for_deletion_at: try: marked_at = datetime.fromisoformat(user.marked_for_deletion_at) due_at = marked_at + timedelta(hours=ACCOUNT_DELETION_THRESHOLD_HOURS) deletion_due_at = due_at.isoformat() except (ValueError, TypeError): pass users_data.append({ 'id': user.id, 'email': user.email, 'marked_for_deletion_at': user.marked_for_deletion_at, 'deletion_due_at': deletion_due_at, 'deletion_in_progress': user.deletion_in_progress, 'deletion_attempted_at': user.deletion_attempted_at }) return jsonify({ 'count': len(users_data), 'users': users_data }), 200 except Exception as e: return jsonify({'error': str(e), 'code': 'SERVER_ERROR'}), 500 @admin_api.route('/admin/deletion-threshold', methods=['GET']) @admin_required def get_deletion_threshold(): """ Get current deletion threshold configuration. """ return jsonify({ 'threshold_hours': ACCOUNT_DELETION_THRESHOLD_HOURS, 'threshold_min': MIN_THRESHOLD_HOURS, 'threshold_max': MAX_THRESHOLD_HOURS }), 200 @admin_api.route('/admin/deletion-threshold', methods=['PUT']) @admin_required def update_deletion_threshold(): """ Update deletion threshold. Note: This updates the runtime value but doesn't persist to environment variables. For permanent changes, update the ACCOUNT_DELETION_THRESHOLD_HOURS env variable. """ try: data = request.get_json() if not data or 'threshold_hours' not in data: return jsonify({ 'error': 'threshold_hours is required', 'code': 'MISSING_THRESHOLD' }), 400 new_threshold = data['threshold_hours'] # Validate type if not isinstance(new_threshold, int): return jsonify({ 'error': 'threshold_hours must be an integer', 'code': 'INVALID_TYPE' }), 400 # Validate range if new_threshold < MIN_THRESHOLD_HOURS: return jsonify({ 'error': f'threshold_hours must be at least {MIN_THRESHOLD_HOURS}', 'code': 'THRESHOLD_TOO_LOW' }), 400 if new_threshold > MAX_THRESHOLD_HOURS: return jsonify({ 'error': f'threshold_hours must be at most {MAX_THRESHOLD_HOURS}', 'code': 'THRESHOLD_TOO_HIGH' }), 400 # Update the global config import config.deletion_config as config config.ACCOUNT_DELETION_THRESHOLD_HOURS = new_threshold # Validate and log warning if needed validate_threshold() return jsonify({ 'message': 'Deletion threshold updated successfully', 'threshold_hours': new_threshold }), 200 except Exception as e: return jsonify({'error': str(e), 'code': 'SERVER_ERROR'}), 500 @admin_api.route('/admin/deletion-queue/trigger', methods=['POST']) @admin_required def trigger_deletion_queue(): """ Manually trigger the deletion scheduler to process the queue immediately. Returns stats about the run. """ try: # Trigger the deletion process result = trigger_deletion_manually() # Get updated queue stats Query_ = Query() marked_users = users_db.search(Query_.marked_for_deletion == True) # Count users that were just processed (this is simplified) processed = result.get('queued_users', 0) # In a real implementation, you'd return actual stats from the deletion run # For now, we'll return simplified stats return jsonify({ 'message': 'Deletion scheduler triggered', 'processed': processed, 'deleted': 0, # TODO: Track this in the deletion function 'failed': 0 # TODO: Track this in the deletion function }), 200 except Exception as e: return jsonify({'error': str(e), 'code': 'SERVER_ERROR'}), 500