2023-10-19 00:06:14 +01:00
|
|
|
import os
|
|
|
|
import re
|
2023-10-23 22:57:50 +01:00
|
|
|
from pathlib import Path, PurePath
|
2023-10-19 00:06:14 +01:00
|
|
|
|
|
|
|
from sanic import Sanic
|
|
|
|
|
2023-10-23 02:51:39 +01:00
|
|
|
from cista import config, server80
|
2023-10-19 00:06:14 +01:00
|
|
|
|
|
|
|
|
2023-11-01 19:36:10 +00:00
|
|
|
def run(*, dev=False):
|
2023-10-19 00:06:14 +01:00
|
|
|
"""Run Sanic main process that spawns worker processes to serve HTTP requests."""
|
|
|
|
from .app import app
|
2023-10-28 21:20:34 +01:00
|
|
|
|
2023-10-19 00:06:14 +01:00
|
|
|
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"
|
2023-10-23 22:57:50 +01:00
|
|
|
confdir = config.conffile.parent
|
|
|
|
wwwroot = PurePath(__file__).parent / "wwwroot"
|
2023-10-19 00:06:14 +01:00
|
|
|
if opts.get("ssl"):
|
|
|
|
# Run plain HTTP redirect/acme server on port 80
|
2023-10-21 17:17:09 +01:00
|
|
|
server80.app.prepare(port=80, motd=False)
|
2023-10-19 00:52:51 +01:00
|
|
|
domain = opts["host"]
|
2023-10-23 22:57:50 +01:00
|
|
|
check_cert(confdir / domain, domain)
|
|
|
|
opts["ssl"] = str(confdir / domain) # type: ignore
|
2023-10-28 21:20:34 +01:00
|
|
|
app.prepare(
|
|
|
|
**opts,
|
|
|
|
motd=False,
|
|
|
|
dev=dev,
|
|
|
|
auto_reload=dev,
|
|
|
|
reload_dir={confdir, wwwroot},
|
|
|
|
access_log=True,
|
|
|
|
) # type: ignore
|
2023-10-19 00:06:14 +01:00
|
|
|
Sanic.serve()
|
|
|
|
|
2023-10-28 21:20:34 +01:00
|
|
|
|
2023-10-23 22:57:50 +01:00
|
|
|
def check_cert(certdir, domain):
|
|
|
|
if (certdir / "privkey.pem").exist() and (certdir / "fullchain.pem").exists():
|
|
|
|
return
|
|
|
|
# TODO: Use certbot to fetch a cert
|
2023-10-28 21:20:34 +01:00
|
|
|
raise ValueError(
|
2023-11-01 19:36:10 +00:00
|
|
|
f"TLS certificate files privkey.pem and fullchain.pem needed in {certdir}",
|
2023-10-28 21:20:34 +01:00
|
|
|
)
|
|
|
|
|
2023-10-23 22:57:50 +01:00
|
|
|
|
2023-10-19 00:06:14 +01:00
|
|
|
def parse_listen(listen):
|
|
|
|
if listen.startswith("/"):
|
|
|
|
unix = Path(listen).resolve()
|
|
|
|
if not unix.parent.exists():
|
2023-10-28 21:20:34 +01:00
|
|
|
raise ValueError(
|
2023-11-01 19:36:10 +00:00
|
|
|
f"Directory for unix socket does not exist: {unix.parent}/",
|
2023-10-28 21:20:34 +01:00
|
|
|
)
|
2023-10-19 00:06:14 +01:00
|
|
|
return "http://localhost", {"unix": unix}
|
2023-11-01 19:36:10 +00:00
|
|
|
if re.fullmatch(r"(\w+(-\w+)*\.)+\w{2,}", listen, re.UNICODE):
|
2023-10-19 00:52:51 +01:00
|
|
|
return f"https://{listen}", {"host": listen, "port": 443, "ssl": True}
|
2023-11-01 19:36:10 +00:00
|
|
|
try:
|
|
|
|
addr, _port = listen.split(":", 1)
|
|
|
|
port = int(_port)
|
|
|
|
except Exception:
|
|
|
|
raise ValueError(f"Invalid listen address: {listen}") from None
|
|
|
|
return f"http://localhost:{port}", {"host": addr, "port": port}
|