Files
simple-nanoshare/routes/api/events.py
T
daniel156161 d3c914285c
Build and Push Docker Container / build-and-push (push) Successful in 2m3s
feat(cli): watch files with event streams
- Add a private SSE endpoint that emits file change events for authenticated clients.
- Replace the watch sleep loop with watchdog local file events and remote SSE triggers.
- Debounce local event bursts and keep watch running after transient sync failures.
- Add watchdog as a standalone CLI dependency and cover watch behavior in tests.
- Bump NanoShare to 1.25.0 and the standalone CLI to 0.4.0.
2026-07-27 22:18:09 +02:00

52 lines
1.6 KiB
Python

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',
})