Compare commits

..

5 Commits

4 changed files with 59 additions and 14 deletions

View File

@ -6,7 +6,7 @@ from sanic import Blueprint
from cista import watching
from cista.fileio import FileServer
from cista.protocol import ControlBase, FileRange, StatusMsg
from cista.protocol import ControlTypes, FileRange, StatusMsg
from cista.util.apphelpers import asend, websocket_wrapper
bp = Blueprint("api", url_prefix="/api")
@ -76,9 +76,10 @@ async def download(req, ws):
@bp.websocket("control")
@websocket_wrapper
async def control(req, ws):
cmd = msgspec.json.decode(await ws.recv(), type=ControlBase)
await asyncio.to_thread(cmd)
await asend(ws, StatusMsg(status="ack", req=cmd))
while True:
cmd = msgspec.json.decode(await ws.recv(), type=ControlTypes)
await asyncio.to_thread(cmd)
await asend(ws, StatusMsg(status="ack", req=cmd))
@bp.websocket("watch")

View File

@ -2,12 +2,13 @@ import mimetypes
from importlib.resources import files
from urllib.parse import unquote
import asyncio
import brotli
from sanic import Blueprint, Sanic, raw
from sanic.exceptions import Forbidden, NotFound
from cista import auth, config, session, watching
from cista.api import bp
from cista.util import filename
from cista.util.apphelpers import handle_sanic_exception
app = Sanic("cista", strict_slashes=True)
@ -58,15 +59,54 @@ def http_fileserver(app, _):
app.blueprint(bp)
www = {}
@app.before_server_start
def load_wwwroot(app):
global www
wwwnew = {}
base = files("cista") / "wwwroot"
paths = ["."]
while paths:
path = paths.pop(0)
current = base / path
for p in current.iterdir():
if p.is_dir():
paths.append(current / p.parts[-1])
continue
name = p.relative_to(base).as_posix()
mime = mimetypes.guess_type(name)[0] or "application/octet-stream"
data = p.read_bytes()
# Use old data if not changed
if name in www and www[name][0] == data:
wwwnew[name] = www[name]
continue
# Precompress with Brotli
br = brotli.compress(data)
if len(br) >= len(data):
br = False
wwwnew[name] = data, br, mime
www = wwwnew
@app.add_task
async def refresh_wwwroot():
while app.debug:
await asyncio.sleep(0.5)
load_wwwroot(app)
@app.get("/<path:path>", static=True)
async def wwwroot(req, path=""):
"""Frontend files only"""
name = filename.sanitize(unquote(path)) if path else "index.html"
try:
index = files("cista").joinpath("wwwroot", name).read_bytes()
except OSError as e:
raise NotFound(
f"File not found: /{path}", extra={"name": name, "exception": repr(e)}
)
mime = mimetypes.guess_type(name)[0] or "application/octet-stream"
return raw(index, content_type=mime)
name = unquote(path) or "index.html"
if name not in www:
raise NotFound(f"File not found: /{path}", extra={"name": name})
data, br, mime = www[name]
headers = {}
# Brotli compressed?
if br and "br" in req.headers.accept_encoding.split(", "):
headers["content-encoding"] = "br"
data = br
return raw(data, content_type=mime, headers=headers)

View File

@ -78,6 +78,9 @@ class Cp(ControlBase):
)
ControlTypes = MkDir | Rename | Rm | Mv | Cp
## File uploads and downloads

View File

@ -15,6 +15,7 @@ classifiers = [
]
dependencies = [
"argon2-cffi",
"brotli",
"docopt",
"inotify",
"msgspec",