Watcher cleanup. Restart server on config changes and if frontend is modified.

This commit is contained in:
Leo Vasanko 2023-10-24 00:57:50 +03:00 committed by Leo Vasanko
parent 5d3f419508
commit 4a13f642b2
3 changed files with 26 additions and 11 deletions

View File

@ -2,11 +2,11 @@ import asyncio
import typing
import msgspec
from sanic import Blueprint, SanicException
from sanic import Blueprint
from cista import watching
from cista.fileio import FileServer
from cista.protocol import ControlBase, ErrorMsg, FileRange, StatusMsg
from cista.protocol import ControlBase, FileRange, StatusMsg
from cista.util.apphelpers import asend, websocket_wrapper
bp = Blueprint("api", url_prefix="/api")

View File

@ -1,6 +1,6 @@
import os
import re
from pathlib import Path
from pathlib import Path, PurePath
from sanic import Sanic
@ -13,14 +13,23 @@ def run(dev=False):
url, opts = parse_listen(config.config.listen)
# Silence Sanic's warning about running in production rather than debug
os.environ["SANIC_IGNORE_PRODUCTION_WARNING"] = "1"
confdir = config.conffile.parent
wwwroot = PurePath(__file__).parent / "wwwroot"
if opts.get("ssl"):
# Run plain HTTP redirect/acme server on port 80
server80.app.prepare(port=80, motd=False)
domain = opts["host"]
opts["ssl"] = str(config.conffile.parent / domain) # type: ignore
app.prepare(**opts, motd=False, dev=dev, auto_reload=dev, access_log=True) # type: ignore
check_cert(confdir / domain, domain)
opts["ssl"] = str(confdir / domain) # type: ignore
app.prepare(**opts, motd=False, dev=dev, auto_reload=dev, reload_dir={confdir, wwwroot}, access_log=True) # type: ignore
Sanic.serve()
def check_cert(certdir, domain):
if (certdir / "privkey.pem").exist() and (certdir / "fullchain.pem").exists():
return
# TODO: Use certbot to fetch a cert
raise ValueError(f"TLS certificate files privkey.pem and fullchain.pem needed in {certdir}")
def parse_listen(listen):
if listen.startswith("/"):
unix = Path(listen).resolve()

View File

@ -13,7 +13,7 @@ from cista.protocol import DirEntry, FileEntry, UpdateEntry
pubsub = {}
tree = {"": None}
tree_lock = threading.Lock()
rootpath = None
rootpath: Path = None # type: ignore
quit = False
modified_flags = "IN_CREATE", "IN_DELETE", "IN_DELETE_SELF", "IN_MODIFY", "IN_MOVE_SELF", "IN_MOVED_FROM", "IN_MOVED_TO"
disk_usage = None
@ -22,6 +22,7 @@ def watcher_thread(loop):
global disk_usage
while True:
rootpath = config.config.path
i = inotify.adapters.InotifyTree(rootpath.as_posix())
old = format_tree() if tree[""] else None
with tree_lock:
@ -36,18 +37,25 @@ def watcher_thread(loop):
for event in i.event_gen():
if quit: return
# Disk usage update
du = shutil.disk_usage(rootpath)
if du != disk_usage:
disk_usage = du
asyncio.run_coroutine_threadsafe(broadcast(format_du()), loop)
break
# Do a full refresh?
if time.monotonic() > refreshdl: break
if event is None: continue
_, flags, path, filename = event
if not any(f in modified_flags for f in flags):
continue
# Update modified path
path = PurePosixPath(path) / filename
#print(path, flags)
try:
update(path.relative_to(rootpath), loop)
except Exception as e:
print("Watching error", e)
break
i = None # Free the inotify object
def format_du():
@ -84,7 +92,7 @@ def walk(path: Path) -> DirEntry | FileEntry | None:
print("OS error walking path", path, e)
return None
def update(relpath: Path, loop):
def update(relpath: PurePosixPath, loop):
"""Called by inotify updates, check the filesystem and broadcast any changes."""
new = walk(rootpath / relpath)
with tree_lock:
@ -149,9 +157,7 @@ async def broadcast(msg):
await queue.put_nowait(msg)
async def start(app, loop):
global rootpath
config.load_config()
rootpath = config.config.path
app.ctx.watcher = threading.Thread(target=watcher_thread, args=[loop])
app.ctx.watcher.start()