Compare commits
5 Commits
acdd776b92
...
ba36eaec1b
Author | SHA1 | Date | |
---|---|---|---|
ba36eaec1b | |||
a435a30c88 | |||
742b05ed66 | |||
a26dc42d88 | |||
9002afbc7e |
|
@ -6,7 +6,7 @@ from sanic import Blueprint
|
||||||
|
|
||||||
from cista import watching
|
from cista import watching
|
||||||
from cista.fileio import FileServer
|
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
|
from cista.util.apphelpers import asend, websocket_wrapper
|
||||||
|
|
||||||
bp = Blueprint("api", url_prefix="/api")
|
bp = Blueprint("api", url_prefix="/api")
|
||||||
|
@ -76,7 +76,8 @@ async def download(req, ws):
|
||||||
@bp.websocket("control")
|
@bp.websocket("control")
|
||||||
@websocket_wrapper
|
@websocket_wrapper
|
||||||
async def control(req, ws):
|
async def control(req, ws):
|
||||||
cmd = msgspec.json.decode(await ws.recv(), type=ControlBase)
|
while True:
|
||||||
|
cmd = msgspec.json.decode(await ws.recv(), type=ControlTypes)
|
||||||
await asyncio.to_thread(cmd)
|
await asyncio.to_thread(cmd)
|
||||||
await asend(ws, StatusMsg(status="ack", req=cmd))
|
await asend(ws, StatusMsg(status="ack", req=cmd))
|
||||||
|
|
||||||
|
|
60
cista/app.py
60
cista/app.py
|
@ -2,12 +2,13 @@ import mimetypes
|
||||||
from importlib.resources import files
|
from importlib.resources import files
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import brotli
|
||||||
from sanic import Blueprint, Sanic, raw
|
from sanic import Blueprint, Sanic, raw
|
||||||
from sanic.exceptions import Forbidden, NotFound
|
from sanic.exceptions import Forbidden, NotFound
|
||||||
|
|
||||||
from cista import auth, config, session, watching
|
from cista import auth, config, session, watching
|
||||||
from cista.api import bp
|
from cista.api import bp
|
||||||
from cista.util import filename
|
|
||||||
from cista.util.apphelpers import handle_sanic_exception
|
from cista.util.apphelpers import handle_sanic_exception
|
||||||
|
|
||||||
app = Sanic("cista", strict_slashes=True)
|
app = Sanic("cista", strict_slashes=True)
|
||||||
|
@ -58,15 +59,54 @@ def http_fileserver(app, _):
|
||||||
app.blueprint(bp)
|
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)
|
@app.get("/<path:path>", static=True)
|
||||||
async def wwwroot(req, path=""):
|
async def wwwroot(req, path=""):
|
||||||
"""Frontend files only"""
|
"""Frontend files only"""
|
||||||
name = filename.sanitize(unquote(path)) if path else "index.html"
|
name = unquote(path) or "index.html"
|
||||||
try:
|
if name not in www:
|
||||||
index = files("cista").joinpath("wwwroot", name).read_bytes()
|
raise NotFound(f"File not found: /{path}", extra={"name": name})
|
||||||
except OSError as e:
|
data, br, mime = www[name]
|
||||||
raise NotFound(
|
headers = {}
|
||||||
f"File not found: /{path}", extra={"name": name, "exception": repr(e)}
|
# Brotli compressed?
|
||||||
)
|
if br and "br" in req.headers.accept_encoding.split(", "):
|
||||||
mime = mimetypes.guess_type(name)[0] or "application/octet-stream"
|
headers["content-encoding"] = "br"
|
||||||
return raw(index, content_type=mime)
|
data = br
|
||||||
|
return raw(data, content_type=mime, headers=headers)
|
||||||
|
|
|
@ -78,6 +78,9 @@ class Cp(ControlBase):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
ControlTypes = MkDir | Rename | Rm | Mv | Cp
|
||||||
|
|
||||||
|
|
||||||
## File uploads and downloads
|
## File uploads and downloads
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ classifiers = [
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2-cffi",
|
"argon2-cffi",
|
||||||
|
"brotli",
|
||||||
"docopt",
|
"docopt",
|
||||||
"inotify",
|
"inotify",
|
||||||
"msgspec",
|
"msgspec",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user