Files
hub/docs/drives-phase-3.md
T
2026-09-21 16:18:42 -05:00

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: Query db.Drive.filter(user_id=user_id). Return a list of names and IDs.
  • list-files: Query db.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, call drive_service.upload_file(...). Reply with {action: "file-uploaded", fileId: ..., nonce: ...}.