semblance_backup/backend/app/routes/folders.py
2025-12-19 19:26:16 +00:00

278 lines
No EOL
11 KiB
Python
Executable file

from quart import Blueprint, request, jsonify
from app.auth.quart_jwt import jwt_required, get_jwt_identity
from app.models.folder import Folder
from bson import ObjectId
import datetime
# Helper function to make MongoDB documents JSON serializable
def make_serializable(obj):
if isinstance(obj, dict):
return {k: make_serializable(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [make_serializable(item) for item in obj]
elif isinstance(obj, ObjectId):
return str(obj)
elif isinstance(obj, datetime.datetime):
return obj.isoformat()
else:
return obj
folders_bp = Blueprint('folders', __name__)
@folders_bp.route('', methods=['GET'])
@folders_bp.route('/', methods=['GET'])
@jwt_required(optional=True) # Make JWT optional for development
async def get_folders():
"""Get all folders in hierarchical tree structure - shared across all users."""
try:
# Return folders in hierarchical tree structure
folders = await Folder.get_folder_tree()
# Make folders serializable
serializable_folders = make_serializable(folders)
return jsonify(serializable_folders), 200
except Exception as e:
print(f"Error in get_folders: {e}")
return jsonify({"error": str(e)}), 500
@folders_bp.route('/<folder_id>', methods=['GET'])
@jwt_required(optional=True) # Make JWT optional for development
async def get_folder(folder_id):
"""Get a specific folder by ID."""
try:
folder = await Folder.find_by_id(folder_id)
if not folder:
return jsonify({"message": "Folder not found"}), 404
# Make folder serializable
serializable_folder = make_serializable(folder)
return jsonify(serializable_folder), 200
except Exception as e:
print(f"Error in get_folder: {e}")
return jsonify({"error": str(e)}), 500
@folders_bp.route('', methods=['POST'])
@folders_bp.route('/', methods=['POST'])
@jwt_required()
async def create_folder():
"""Create a new folder."""
user_id = get_jwt_identity()
data = await request.get_json()
if not data:
return jsonify({"message": "No data provided"}), 400
if not data.get('name'):
return jsonify({"message": "Folder name is required"}), 400
folder_id = await Folder.create(data, user_id)
return jsonify({
"message": "Folder created successfully",
"folder_id": folder_id
}), 201
@folders_bp.route('/<folder_id>', methods=['PUT'])
@jwt_required()
async def update_folder(folder_id):
"""Update a folder."""
try:
data = await request.get_json()
if not data:
return jsonify({"message": "No data provided"}), 400
folder = await Folder.find_by_id(folder_id)
if not folder:
return jsonify({"message": "Folder not found"}), 404
# Folder operations are shared across all users in this system
# Ensure _id is not being modified
if '_id' in data:
del data['_id']
# Ensure id is not being used for update
if 'id' in data:
del data['id']
success = await Folder.update(folder_id, data)
if success:
# Get the updated folder and return it
updated_folder = await Folder.find_by_id(folder_id)
return jsonify({
"message": "Folder updated successfully",
"folder": make_serializable(updated_folder)
}), 200
else:
return jsonify({"message": "No changes made to folder"}), 200
except Exception as e:
print(f"Error updating folder: {e}")
return jsonify({"message": f"Failed to update folder: {str(e)}"}), 500
@folders_bp.route('/<folder_id>', methods=['DELETE'])
@jwt_required(optional=True) # Make JWT optional for development
async def delete_folder(folder_id):
"""Delete a folder and its entire hierarchy."""
user_id = get_jwt_identity()
# Folder operations are shared across all users in this system
try:
success, message = await Folder.delete_hierarchy(folder_id, user_id)
if success:
return jsonify({"message": message}), 200
else:
return jsonify({"message": message}), 400
except Exception as e:
print(f"Error deleting folder hierarchy: {e}")
return jsonify({"message": f"Failed to delete folder: {str(e)}"}), 500
@folders_bp.route('/<folder_id>/personas', methods=['POST'])
@jwt_required()
async def add_persona_to_folder(folder_id):
"""Add a persona to a folder (supports multiple folders per persona)."""
try:
data = await request.get_json()
if not data or not data.get('persona_id'):
return jsonify({"message": "Persona ID is required"}), 400
folder = await Folder.find_by_id(folder_id)
if not folder:
return jsonify({"message": "Folder not found"}), 404
# Folder operations are shared across all users in this system
persona_id = data['persona_id']
success = await Folder.add_persona(folder_id, persona_id)
if success:
return jsonify({"message": "Persona added to folder successfully"}), 200
else:
return jsonify({"message": "Persona was already in folder or update failed"}), 200
except Exception as e:
print(f"Error adding persona to folder: {e}")
return jsonify({"message": f"Failed to add persona to folder: {str(e)}"}), 500
@folders_bp.route('/<folder_id>/personas/<persona_id>', methods=['DELETE'])
@jwt_required()
async def remove_persona_from_folder(folder_id, persona_id):
"""Remove a persona from a folder (persona can remain in other folders)."""
try:
folder = await Folder.find_by_id(folder_id)
if not folder:
return jsonify({"message": "Folder not found"}), 404
# Folder operations are shared across all users in this system
success = await Folder.remove_persona(folder_id, persona_id)
if success:
return jsonify({"message": "Persona removed from folder successfully"}), 200
else:
return jsonify({"message": "Persona was not in folder or removal failed"}), 200
except Exception as e:
print(f"Error removing persona from folder: {e}")
return jsonify({"message": f"Failed to remove persona from folder: {str(e)}"}), 500
@folders_bp.route('/<folder_id>/personas/batch', methods=['POST'])
@jwt_required()
async def add_personas_to_folder_batch(folder_id):
"""Add multiple personas to a folder (personas can be in multiple folders)."""
try:
data = await request.get_json()
if not data or not data.get('persona_ids'):
return jsonify({"message": "Persona IDs are required"}), 400
folder = await Folder.find_by_id(folder_id)
if not folder:
return jsonify({"message": "Folder not found"}), 404
# Folder operations are shared across all users in this system
persona_ids = data['persona_ids']
if not isinstance(persona_ids, list):
return jsonify({"message": "persona_ids must be a list"}), 400
success = await Folder.add_personas_batch(folder_id, persona_ids)
if success:
return jsonify({"message": f"Successfully added {len(persona_ids)} personas to folder"}), 200
else:
return jsonify({"message": "Update failed or no changes made"}), 200
except Exception as e:
print(f"Error adding personas to folder: {e}")
return jsonify({"message": f"Failed to add personas to folder: {str(e)}"}), 500
@folders_bp.route('/<folder_id>/personas/remove-batch', methods=['POST'])
@jwt_required()
async def remove_personas_from_folder_batch(folder_id):
"""Remove multiple personas from a folder (personas remain in other folders)."""
print(f"🌐 BACKEND: POST /folders/{folder_id}/personas/remove-batch endpoint hit")
try:
data = await request.get_json()
print(f"🌐 BACKEND: Raw request data: {data}")
print(f"🌐 BACKEND: Request content type: {request.content_type}")
print(f"🌐 BACKEND: Request method: {request.method}")
if not data or not data.get('persona_ids'):
print(f"❌ BACKEND: Missing persona_ids in data: {data}")
return jsonify({"message": "Persona IDs are required"}), 400
folder = await Folder.find_by_id(folder_id)
if not folder:
return jsonify({"message": "Folder not found"}), 404
# Folder operations are shared across all users in this system
persona_ids = data['persona_ids']
if not isinstance(persona_ids, list):
return jsonify({"message": "persona_ids must be a list"}), 400
success = await Folder.remove_personas_batch(folder_id, persona_ids)
if success:
return jsonify({"message": f"Successfully removed {len(persona_ids)} personas from folder"}), 200
else:
return jsonify({"message": "Update failed or no changes made"}), 200
except Exception as e:
print(f"Error removing personas from folder: {e}")
return jsonify({"message": f"Failed to remove personas from folder: {str(e)}"}), 500
@folders_bp.route('/<folder_id>/move', methods=['PUT'])
@jwt_required(optional=True) # Make JWT optional for development
async def move_folder(folder_id):
"""Move a folder to a new parent."""
try:
data = await request.get_json()
user_id = get_jwt_identity()
new_parent_id = data.get('parent_folder_id') # None for root level
# Folder operations are shared across all users in this system
success, message = await Folder.move_folder(folder_id, new_parent_id, user_id)
if success:
return jsonify({"message": message}), 200
else:
return jsonify({"message": message}), 400
except Exception as e:
print(f"Error moving folder: {e}")
return jsonify({"message": f"Failed to move folder: {str(e)}"}), 500
@folders_bp.route('/<folder_id>/descendants', methods=['GET'])
@jwt_required(optional=True)
async def get_folder_descendants(folder_id):
"""Get all descendant folders of a given folder."""
try:
descendants = await Folder.get_descendants(folder_id)
serializable_descendants = make_serializable(descendants)
return jsonify(serializable_descendants), 200
except Exception as e:
print(f"Error getting folder descendants: {e}")
return jsonify({"error": str(e)}), 500