diff --git a/cista/api.py b/cista/api.py index 93aaa37..aa764b9 100644 --- a/cista/api.py +++ b/cista/api.py @@ -1,5 +1,4 @@ import asyncio -import typing from pathlib import PurePosixPath from secrets import token_bytes @@ -9,7 +8,7 @@ from sanic.exceptions import BadRequest from cista import __version__, auth, config, sso, watching from cista.fileio import FileServer -from cista.protocol import ControlTypes, FileRange, StatusMsg +from cista.protocol import ControlTypes, StatusMsg from cista.util.apphelpers import asend, websocket_wrapper bp = Blueprint("api", url_prefix="/api") @@ -26,65 +25,6 @@ async def stop_fileserver(app): await fileserver.stop() -@bp.websocket("upload") -@websocket_wrapper -async def upload(req, ws): - alink = fileserver.alink - while True: - req = None - text = await ws.recv() - if not isinstance(text, str): - raise ValueError( - f"Expected JSON control, got binary len(data) = {len(text)}", - ) - req = msgspec.json.decode(text, type=FileRange) - pos = req.start - while True: - data = await ws.recv() - if not isinstance(data, bytes): - break - if len(data) > req.end - pos: - raise ValueError( - f"Expected up to {req.end - pos} bytes, got {len(data)} bytes" - ) - sentsize = await alink(("upload", req.name, pos, data, req.size)) - pos += typing.cast(int, sentsize) - if pos >= req.end: - break - if pos != req.end: - d = f"{len(data)} bytes" if isinstance(data, bytes) else data - raise ValueError(f"Expected {req.end - pos} more bytes, got {d}") - # Signal the watcher about the uploaded file and its parent directories - path = PurePosixPath(req.name) - watching.notify_change(path, *path.parents) - # Report success - res = StatusMsg(status="ack", req=req) - await asend(ws, res) - - -@bp.websocket("download") -@websocket_wrapper -async def download(req, ws): - alink = fileserver.alink - while True: - req = None - text = await ws.recv() - if not isinstance(text, str): - raise ValueError( - f"Expected JSON control, got binary len(data) = {len(text)}", - ) - req = msgspec.json.decode(text, type=FileRange) - pos = req.start - while pos < req.end: - end = min(req.end, pos + (1 << 20)) - data = typing.cast(bytes, await alink(("download", req.name, pos, end))) - await asend(ws, data) - pos += len(data) - # Report success - res = StatusMsg(status="ack", req=req) - await asend(ws, res) - - @bp.websocket("control") @websocket_wrapper async def control(req, ws): diff --git a/cista/fileio.py b/cista/fileio.py index 663bb4e..8b340da 100644 --- a/cista/fileio.py +++ b/cista/fileio.py @@ -1,10 +1,8 @@ -import asyncio import os import threading from cista import config from cista.util import filename -from cista.util.asynclink import AsyncLink from cista.util.lrucache import LRUCache @@ -63,34 +61,12 @@ class File: class FileServer: async def start(self): - self.alink = AsyncLink() - self.worker = asyncio.get_event_loop().run_in_executor( - None, - self.worker_thread, - self.alink.to_sync, - ) self.cache = LRUCache(File, capacity=10, maxage=5.0) self.cache_lock = threading.Lock() self.file_locks: dict[str, threading.Lock] = {} async def stop(self): - await self.alink.stop() - await self.worker - - def worker_thread(self, slink): - try: - for req in slink: - with req as (command, *args): - if command == "upload": - req.set_result(self.upload(*args)) - elif command == "upload_info": - req.set_result(self.upload_info(*args)) - elif command == "download": - req.set_result(self.download(*args)) - else: - raise NotImplementedError(f"Unhandled {command=} {args}") - finally: - self.cache.close() + self.cache.close() @staticmethod def _stat_size(path): @@ -99,15 +75,6 @@ class FileServer: except FileNotFoundError: return None - def upload(self, name, pos, data, file_size): - name = filename.sanitize(name) - with self.cache_lock: - f = self.cache[name] - lock = self.file_locks.setdefault(name, threading.Lock()) - with lock: - f.write(pos, data, file_size=file_size) - return len(data) - def upload_info(self, name, pos, data, file_size): name = filename.sanitize(name) with self.cache_lock: @@ -123,11 +90,3 @@ class FileServer: "size_before": size_before, "size_after": size_after, } - - def download(self, name, start, end): - name = filename.sanitize(name) - with self.cache_lock: - f = self.cache[name] - lock = self.file_locks.setdefault(name, threading.Lock()) - with lock: - return f[start:end] diff --git a/cista/protocol.py b/cista/protocol.py index c04147a..220e887 100644 --- a/cista/protocol.py +++ b/cista/protocol.py @@ -12,7 +12,6 @@ from cista.util import filename ## Control commands - class ControlBase(msgspec.Struct, tag_field="op", tag=str.lower): def __call__(self): raise NotImplementedError @@ -118,19 +117,9 @@ class Cp(ControlBase): ControlTypes = MkDir | Rename | Rm | Mv | Cp -## File uploads and downloads - - -class FileRange(msgspec.Struct): - name: str - size: int - start: int - end: int - - class StatusMsg(msgspec.Struct): status: str - req: FileRange + req: Any class ErrorMsg(msgspec.Struct): diff --git a/frontend/src/repositories/WS.ts b/frontend/src/repositories/WS.ts index 3c8d37d..69e7da8 100644 --- a/frontend/src/repositories/WS.ts +++ b/frontend/src/repositories/WS.ts @@ -3,7 +3,6 @@ import { showAuthIframe, AuthCancelledError, isAuthIframeOpen } from 'paskia' import type { FileEntry, UpdateEntry, errorEvent } from "./Document" export const controlUrl = '/api/control' -export const uploadUrl = '/api/upload' export const watchUrl = '/api/watch' let tree = [] as FileEntry[]