from __future__ import annotations import asyncio import json from my_modules.app.setup import LIMITER from my_modules.decoratory.header import token_required from quart import Blueprint, Response, current_app, stream_with_context api_events_bp = Blueprint('api_events', __name__) async def _snapshot(user_id: str) -> str: files = await current_app.convex.get_files(user_id) rows = [] for item in files or []: if not isinstance(item, dict): continue rows.append({ 'file_id': item.get('file_id'), 'file_name': item.get('file_name'), 'file_path': item.get('file_path') or '', 'file_size': item.get('file_size'), 'expires_at': item.get('expires_at'), 'updated_at': item.get('updated_at') or item.get('uploaded_at'), }) rows.sort(key=lambda row: str(row.get('file_id') or '')) return json.dumps(rows, sort_keys=True, separators=(',', ':')) @api_events_bp.get('/api/files/events') @LIMITER.limit('6 per minute;60 per hour;') @token_required(['files', 'mesh']) async def api_file_events(user: dict): user_id = user['sub'] @stream_with_context async def stream(): previous = await _snapshot(user_id) yield 'event: ready\ndata: {}\n\n' while True: await asyncio.sleep(30) current = await _snapshot(user_id) if current != previous: previous = current yield f'event: files.changed\ndata: {current}\n\n' else: yield ': keepalive\n\n' return Response(stream(), content_type='text/event-stream', headers={ 'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no', })