3.2 KiB
3.2 KiB
Phase 3: Server Integration
Objective
Attach drive operations to the Quart application's routing layer and WebSocket handlers. This phase implements the API endpoints (/files/<int:id> and /files/<int:id>/meta) and handles WebSocket binary uploads and list requests.
Concepts
Server Actions & Protocol
We leverage the existing WebSocket action protocol for interactions and REST for byte streams:
| Action | Method | Payload | Description |
|---|---|---|---|
list-drives |
WS | {} |
Lists available drives for the logged-in user. |
list-files |
WS | {driveName: "..."} |
Returns array of DB metadata objects for the specified drive. |
upload-file |
WS Binary | {driveName, fileName, mime?} |
Sends JSON header followed by binary stream. Returns fileId. |
/files/<int:id> |
GET | N/A | Serves raw file download. Uses resolved path. Auth required. |
/files/<int:id>/meta |
GET | N/A | Lightweight metadata endpoint for existence checks. |
WebSocket Upload Sequence Example:
// 1. Send metadata
ws.send(JSON.stringify({action: "upload-file", nonce: "n1", body: {driveName: "Docs", fileName: "report.pdf"}}));
// 2. Immediately follow with binary payload
ws.send(blob);
// 3. Server responds
{action: "file-uploaded", fileId: 42, nonce: "n1"}
Tasks
3.1 Register Routes
Extend backend/src/app.py with the following route handlers:
from quart import Quart, request, send_file, make_response
from . import drive_service
app = Quart(__name__)
@app.route("/files/<int:id>", methods=["GET"])
async def serve_file(id):
try:
current_user_id = request.headers.get("Authorization").split(" ")[1] # or via auth middleware
file_path, mime_type = await drive_service.resolve_drive_file_for_serving(id, current_user_id)
return await send_file(file_path, mimetype=mime_type)
except FileNotFoundError as e:
return make_response("Not found", 404)
except PermissionError:
return make_response("Forbidden", 403)
@app.route("/files/<int:id>/meta", methods=["GET"])
async def get_file_meta(id):
try:
current_user_id = ... # same auth logic
# Verify ownership briefly before returning meta
file = await db.DriveFile.filter(id=id).select_related("drive__user").get_or_none()
if not file or file.drive.user_id != current_user_id:
raise Exception("Denied")
return drive_service.get_drive_file_metadata(id)
except Exception as e:
return make_response({"error": str(e)}, 404)
Note: Replace explicit auth extraction with your framework's actual token validation mechanism.
3.2 Extend WebSocket Handler
In backend/src/app.py, update the existing server.websocket('/ws') handler to cover new actions:
list-drives: Querydb.Drive.filter(user_id=user_id). Return a list of names and IDs.list-files: Querydb.DriveFile.filter(drive__name=drive_name)filtered by the authenticated user.upload-file: Parse the first message for JSON headers (driveName,fileName,mime). Open the stream, calldrive_service.upload_file(...). Reply with{action: "file-uploaded", fileId: ..., nonce: ...}.