Build and Push Docker Container / build-and-push (push) Successful in 2m25s
- Require Python 3.15 for the server and Python 3.14 for the CLI. - Match the server requirement to quart_common's Python 3.15 features. - Replace future annotations with native deferred evaluation. - Defer CLI command dependencies and QR imports on Python 3.15. - Enable argparse suggestions for mistyped CLI commands. - Remove the obsolete tomllib import fallback. - Update my_helpers and quart_common submodule references. - Lock test dependencies and exclude them from Docker installations. - Add regression coverage for lazy imports and deferred annotations. - Document runtime requirements and standalone CLI validation. - Validate 276 tests on Python 3.15 and 18 CLI tests on Python 3.14. BREAKING CHANGE: The server now requires Python 3.15+ and the standalone CLI requires Python 3.14+.
52 lines
1.6 KiB
Python
52 lines
1.6 KiB
Python
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(15)
|
|
current = await _snapshot(user_id)
|
|
if current != previous:
|
|
previous = current
|
|
yield f'event: files.changed\ndata: {current}\n\n'
|
|
else:
|
|
yield ': keepalive\n\n'
|
|
|
|
response = Response(stream(), content_type='text/event-stream', headers={
|
|
'Cache-Control': 'no-cache',
|
|
'X-Accel-Buffering': 'no',
|
|
})
|
|
response.timeout = None
|
|
return response
|