from re import A from flask import Blueprint, request, jsonify, send_from_directory from server.permissions import editor_or_higher from server.database import Session from models.models import EventMedia, MediaType, Conversion, ConversionStatus from server.task_queue import get_queue from server.worker import convert_event_media_to_pdf import hashlib import os eventmedia_bp = Blueprint('eventmedia', __name__, url_prefix='/api/eventmedia') BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) MEDIA_ROOT = os.path.join(BASE_DIR, 'media') def get_param(key, default=None): # Reihenfolge: form > json > args if request.form and key in request.form: return request.form.get(key, default) if request.is_json and request.json and key in request.json: return request.json.get(key, default) return request.args.get(key, default) # --- FileManager: List, Create Folder, Rename, Delete, Move --- @eventmedia_bp.route('/filemanager/operations', methods=['GET', 'POST']) @editor_or_higher def filemanager_operations(): action = get_param('action') path = get_param('path', '/') name = get_param('name') new_name = get_param('newName') target_path = get_param('targetPath') full_path = os.path.join(MEDIA_ROOT, path.lstrip('/')) print(action, path, name, new_name, target_path, full_path) # Debug-Ausgabe # Superadmin-only protection for the converted folder from flask import session as flask_session user_role = flask_session.get('role') is_superadmin = user_role == 'superadmin' # Normalize path for checks norm_path = os.path.normpath('/' + path.lstrip('/')) under_converted = norm_path == '/converted' or norm_path.startswith('/converted/') if action == 'read': # Block listing inside converted for non-superadmins if under_converted and not is_superadmin: return jsonify({'files': [], 'cwd': {'name': os.path.basename(full_path), 'path': path}}) # List files and folders items = [] session = Session() for entry in os.scandir(full_path): item = { 'name': entry.name, 'isFile': entry.is_file(), 'size': entry.stat().st_size, 'type': os.path.splitext(entry.name)[1][1:] if entry.is_file() else '', 'hasChild': entry.is_dir() } # Wenn Datei, versuche Upload-Datum aus DB zu holen if entry.is_file(): media = session.query(EventMedia).filter_by( url=entry.name).first() if media and media.uploaded_at: # FileManager erwartet UNIX-Timestamp (Sekunden) item['dateModified'] = int(media.uploaded_at.timestamp()) else: item['dateModified'] = entry.stat().st_mtime else: item['dateModified'] = entry.stat().st_mtime # Hide the converted folder at root for non-superadmins if not (not is_superadmin and not entry.is_file() and entry.name == 'converted' and (norm_path == '/' or norm_path == '')): items.append(item) session.close() return jsonify({'files': items, 'cwd': {'name': os.path.basename(full_path), 'path': path}}) elif action == 'details': # Details für eine oder mehrere Dateien zurückgeben names = request.form.getlist('names[]') or (request.json.get( 'names') if request.is_json and request.json else []) path = get_param('path', '/') details = [] session = Session() for name in names: file_path = os.path.join(MEDIA_ROOT, path.lstrip('/'), name) media = session.query(EventMedia).filter_by(url=name).first() if os.path.isfile(file_path): detail = { 'name': name, 'size': os.path.getsize(file_path), 'dateModified': int(media.uploaded_at.timestamp()) if media and media.uploaded_at else int(os.path.getmtime(file_path)), 'type': os.path.splitext(name)[1][1:], 'hasChild': False, 'isFile': True, 'description': media.message_content if media else '', # weitere Felder nach Bedarf } details.append(detail) session.close() return jsonify({'details': details}) elif action == 'delete': if under_converted and not is_superadmin: return jsonify({'error': 'Insufficient permissions'}), 403 for item in request.form.getlist('names[]'): item_path = os.path.join(full_path, item) if os.path.isdir(item_path): os.rmdir(item_path) else: os.remove(item_path) return jsonify({'success': True}) elif action == 'rename': if under_converted and not is_superadmin: return jsonify({'error': 'Insufficient permissions'}), 403 src = os.path.join(full_path, name) dst = os.path.join(full_path, new_name) os.rename(src, dst) return jsonify({'success': True}) elif action == 'move': # Prevent moving into converted if not superadmin if (target_path and target_path.strip('/').split('/')[0] == 'converted') and not is_superadmin: return jsonify({'error': 'Insufficient permissions'}), 403 src = os.path.join(full_path, name) dst = os.path.join(MEDIA_ROOT, target_path.lstrip('/'), name) os.rename(src, dst) return jsonify({'success': True}) elif action == 'create': if under_converted and not is_superadmin: return jsonify({'error': 'Insufficient permissions'}), 403 os.makedirs(os.path.join(full_path, name), exist_ok=True) return jsonify({'success': True}) else: return jsonify({'error': 'Unknown action'}), 400 # --- FileManager: Upload --- @eventmedia_bp.route('/filemanager/upload', methods=['POST']) @editor_or_higher def filemanager_upload(): session = Session() # Korrigiert: Erst aus request.form, dann aus request.args lesen path = request.form.get('path') or request.args.get('path', '/') from flask import session as flask_session user_role = flask_session.get('role') is_superadmin = user_role == 'superadmin' norm_path = os.path.normpath('/' + path.lstrip('/')) if (norm_path == '/converted' or norm_path.startswith('/converted/')) and not is_superadmin: return jsonify({'error': 'Insufficient permissions'}), 403 upload_path = os.path.join(MEDIA_ROOT, path.lstrip('/')) os.makedirs(upload_path, exist_ok=True) for file in request.files.getlist('uploadFiles'): file_path = os.path.join(upload_path, file.filename) file.save(file_path) ext = os.path.splitext(file.filename)[1][1:].lower() try: media_type = MediaType(ext) except ValueError: media_type = MediaType.other from datetime import datetime, timezone media = EventMedia( media_type=media_type, url=file.filename, file_path=os.path.relpath(file_path, MEDIA_ROOT), uploaded_at=datetime.now(timezone.utc) ) session.add(media) session.commit() # Enqueue conversion for office presentation types if media_type in {MediaType.ppt, MediaType.pptx, MediaType.odp}: # compute file hash h = hashlib.sha256() with open(file_path, 'rb') as f: for chunk in iter(lambda: f.read(8192), b""): h.update(chunk) file_hash = h.hexdigest() # upsert Conversion row conv = ( session.query(Conversion) .filter_by( source_event_media_id=media.id, target_format='pdf', file_hash=file_hash, ) .one_or_none() ) if not conv: conv = Conversion( source_event_media_id=media.id, target_format='pdf', status=ConversionStatus.pending, file_hash=file_hash, ) session.add(conv) session.commit() if conv.status in {ConversionStatus.pending, ConversionStatus.failed}: q = get_queue() q.enqueue(convert_event_media_to_pdf, conv.id) session.commit() return jsonify({'success': True}) # --- FileManager: Download --- @eventmedia_bp.route('/filemanager/download', methods=['GET']) def filemanager_download(): path = request.args.get('path', '/') from flask import session as flask_session user_role = flask_session.get('role') is_superadmin = user_role == 'superadmin' norm_path = os.path.normpath('/' + path.lstrip('/')) names = request.args.getlist('names[]') # Nur Einzel-Download für Beispiel if names: # Block access to converted for non-superadmins if (norm_path == '/converted' or norm_path.startswith('/converted/')) and not is_superadmin: return jsonify({'error': 'Insufficient permissions'}), 403 file_path = os.path.join(MEDIA_ROOT, path.lstrip('/'), names[0]) return send_from_directory(os.path.dirname(file_path), os.path.basename(file_path), as_attachment=True) return jsonify({'error': 'No file specified'}), 400 # --- FileManager: Get Image (optional, für Thumbnails) --- @eventmedia_bp.route('/filemanager/get-image', methods=['GET']) def filemanager_get_image(): path = request.args.get('path', '/') from flask import session as flask_session user_role = flask_session.get('role') is_superadmin = user_role == 'superadmin' norm_path = os.path.normpath('/' + path.lstrip('/')) if (norm_path == '/converted' or norm_path.startswith('/converted/')) and not is_superadmin: return jsonify({'error': 'Insufficient permissions'}), 403 file_path = os.path.join(MEDIA_ROOT, path.lstrip('/')) return send_from_directory(os.path.dirname(file_path), os.path.basename(file_path)) # --- EventMedia-API: Metadaten-Liste (wie gehabt) --- @eventmedia_bp.route('', methods=['GET']) def list_media(): session = Session() media = session.query(EventMedia).all() return jsonify([m.to_dict() for m in media]) # --- EventMedia-API: Metadaten-Update --- @eventmedia_bp.route('/', methods=['PUT']) @editor_or_higher def update_media(media_id): session = Session() media = session.query(EventMedia).get(media_id) if not media: return jsonify({'error': 'Not found'}), 404 data = request.json media.url = data.get('title', media.url) media.message_content = data.get('description', media.message_content) # Event-Zuordnung ggf. ergänzen session.commit() return jsonify(media.to_dict()) @eventmedia_bp.route('/find_by_filename', methods=['GET']) def find_by_filename(): filename = request.args.get('filename') if not filename: return jsonify({'error': 'Missing filename'}), 400 session = Session() # Suche nach exaktem Dateinamen in url oder file_path media = session.query(EventMedia).filter( (EventMedia.url == filename) | ( EventMedia.file_path.like(f"%{filename}")) ).first() if not media: return jsonify({'error': 'Not found'}), 404 return jsonify({ 'id': media.id, 'file_path': media.file_path, 'url': media.url }) @eventmedia_bp.route('/', methods=['GET']) def get_media_by_id(media_id): session = Session() media = session.query(EventMedia).get(media_id) if not media: session.close() return jsonify({'error': 'Not found'}), 404 result = { 'id': media.id, 'file_path': media.file_path, 'url': media.url, 'name': media.url, # oder ein anderes Feld für den Namen 'media_type': media.media_type.name if media.media_type else None } session.close() return jsonify(result)