"""HTTP server implementation using aiohttp and jinja2.""" import asyncio import json import time import urllib.parse import os import logging from aiohttp import web from fastapi.templating import Jinja2Templates import jinja2 logger = logging.getLogger(__name__) def _render_template(html_str: str, **context) -> str: tmpl = jinja2.Template(html_str) return tmpl.render(**context) async def start( host: str, port: int, config, hbdclass, msgs_getter, log=None, email=None, pushmsg=None, msg_to_websockets=None, tcss=None, DEBUG=0, verbose=False, get_now=None, VER="", ): """Start an aiohttp web server and block until cancelled. This function is intended to be awaited inside the main asyncio event loop. """ get_now = get_now or (lambda: time.time()) async def index(request): res = [] res.append('') res.append("") res.append("
") res.append(f"%s (%s)
" % (time.strftime("%H:%M:%S", time.localtime(get_now())), config.get("tz", "CET-1CDT")) ) res.append("") body = "\n".join(res) return web.Response(text=body, content_type="text/html") async def api_hosts(request): lst = [hbdclass.Host.hosts[h].jsons() for h in hbdclass.Host.hosts] return web.json_response(json.loads("[" + ",".join(lst) + "]")) async def api_messages(request): lst = msgs_getter()[-30:] return web.json_response(lst) async def cmd(request): qa = request.rel_url.query uname = qa.get("h") ucmd = qa.get("c") if not ucmd or not uname: return web.Response(status=400, text="need h= and c= arguments") if uname not in hbdclass.Host.hosts: return web.Response(status=400, text=f"h={uname} not found") hbdclass.Host.hosts[uname].cmds.append(("CMD", {"cmd": urllib.parse.unquote(ucmd)})) return web.Response(text=f"cmd {uname} queued") async def drop(request): qa = request.rel_url.query uname = qa.get("h") if not uname: return web.Response(status=400, text="need h= argument") if uname not in hbdclass.Host.hosts: return web.Response(status=400, text=f"h={uname} not found") if log: log(uname, "dropped") del hbdclass.Host.hosts[uname] return web.Response(text="Done") async def register(request): qa = request.rel_url.query uname = qa.get("h") if not uname: return web.Response(status=400, text="need h= argument") if uname not in hbdclass.Host.hosts: return web.Response(status=400, text=f"h={uname} not found") ll = hbdclass.Host.hosts[uname].registerDns() if log: log(uname, ll) return web.Response(text=str(ll)) async def update(request): qa = request.rel_url.query uname = urllib.parse.unquote(qa.get("h", "")) ucode = qa.get("c") if not ucode or not uname: return web.Response(status=400, text="need h= and c= arguments") if uname != "All" and uname not in hbdclass.Host.hosts: return web.Response(status=400, text=f"h={uname} not found") if uname != "All": names = [uname] else: names = [n for n in hbdclass.Host.hosts if hbdclass.Host.hosts[n].cver >= 2] out = [] for n in names: err = None try: r = {"csum": None, "code": ucode} hbdclass.Host.hosts[n].cmds.append(("UPD", r)) except Exception as e: err = str(e) out.append(f"update started for {n}: {err if err else 'OK'}") return web.Response(text="\n".join(out)) async def restart(request): # signal main application to perform restart if needed # not implemented here - return OK if log: log(None, "restart request") return web.Response(text="restart request") async def live(request): # render template from templates/live.html using Jinja2 env = jinja2.Environment(loader=jinja2.FileSystemLoader(config.get("templates_dir", "templates"))) host = config.get("hb_host", "localhost") extra_scripts = config.get("http_extra_scripts", "") heartbeat_ws_url = f"ws://{host}:{config.get('ws_port', 50005)}/hbd" tmpl = env.get_template("live.html") body = tmpl.render( title="Heartbeat", header="Heartbeat", request=request, heartbeat_ws_url=heartbeat_ws_url, extra_scripts=extra_scripts, hosts=[hbdclass.Host.hosts[h].stateinfo() for h in sorted(hbdclass.Host.hosts)], messages=msgs_getter()[-30:], ) return web.Response(text=body, content_type="text/html") async def static(request): """Serve files from the package static directory. URL form: /static/