From 8a14ef707a5e38fa573a15d94cd9de51d8347273 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Sun, 9 Jun 2024 03:11:01 +0200 Subject: [PATCH 1/6] WebHost: refactor display_log, allow range --- WebHostLib/misc.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/WebHostLib/misc.py b/WebHostLib/misc.py index 5072f113bd54..f9223a8347c1 100644 --- a/WebHostLib/misc.py +++ b/WebHostLib/misc.py @@ -1,6 +1,6 @@ import datetime import os -from typing import List, Dict, Union +from typing import Dict, Iterator, List, Tuple, Union import jinja2.exceptions from flask import request, redirect, url_for, render_template, Response, session, abort, send_from_directory @@ -97,25 +97,37 @@ def new_room(seed: UUID): return redirect(url_for("host_room", room=room.id)) -def _read_log(path: str): - if os.path.exists(path): - with open(path, encoding="utf-8-sig") as log: - yield from log - else: - yield f"Logfile {path} does not exist. " \ - f"Likely a crash during spinup of multiworld instance or it is still spinning up." +def _read_log(path: str, offset: int = 0) -> Iterator[bytes]: + with open(path, "rb") as log: + marker = log.read(3) # skip optional BOM + if marker != b'\xEF\xBB\xBF': + log.seek(0, os.SEEK_SET) + log.seek(offset, os.SEEK_CUR) + yield from log @app.route('/log/') -def display_log(room: UUID): +def display_log(room: UUID) -> Union[str, Response, Tuple[str, int]]: room = Room.get(id=room) if room is None: return abort(404) if room.owner == session["_id"]: file_path = os.path.join("logs", str(room.id) + ".txt") - if os.path.exists(file_path): + try: + range_header = request.headers.get("Range") + if range_header: + range_type, range_values = range_header.split('=') + start, end = map(str.strip, range_values.split('-', 1)) + if range_type != "bytes" or end != "": + return "Unsupported range", 500 + # NOTE: we skip Content-Range in the response here, which isn't great but works for our JS + return Response(_read_log(file_path, int(start)), mimetype="text/plain;charset=UTF-8", status=206) return Response(_read_log(file_path), mimetype="text/plain;charset=UTF-8") - return "Log File does not exist." + except FileNotFoundError: + return Response(f"Logfile {file_path} does not exist. " + f"Likely a crash during spinup of multiworld instance or it is still spinning up." + .encode("utf-8"), + mimetype="text/plain;charset=UTF-8") return "Access Denied", 403 From eea125de08a2c008f74985875cf549b2d9ff72b1 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Sun, 9 Jun 2024 03:12:26 +0200 Subject: [PATCH 2/6] WebHost: make room use fetch api, range and smooth scroll also fetch instead of post and faster refresh after command --- WebHostLib/templates/hostRoom.html | 77 ++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/WebHostLib/templates/hostRoom.html b/WebHostLib/templates/hostRoom.html index 2bbfe4ad0169..8ffa3bc22f23 100644 --- a/WebHostLib/templates/hostRoom.html +++ b/WebHostLib/templates/hostRoom.html @@ -44,7 +44,7 @@ {{ macros.list_patches_room(room) }} {% if room.owner == session["_id"] %}
-
+
-
- {% endif %}
From ca5f90308fa0988d96ec1595107290e4b391564b Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 10 Jun 2024 00:02:32 +0200 Subject: [PATCH 3/6] WebHost: don't get after redirect in hostRoom --- WebHostLib/templates/hostRoom.html | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/WebHostLib/templates/hostRoom.html b/WebHostLib/templates/hostRoom.html index 8ffa3bc22f23..19dd24b0f3de 100644 --- a/WebHostLib/templates/hostRoom.html +++ b/WebHostLib/templates/hostRoom.html @@ -104,20 +104,29 @@ } async function postForm(ev) { + /** @type {HTMLInputElement} */ + let cmd = document.getElementById("cmd"); + if (cmd.value === "") { + ev.preventDefault(); + return; + } /** @type {HTMLFormElement} */ let form = document.getElementById("command-form"); - ev.preventDefault(); // has to happen before first await - let response = await fetch(form.action || window.location.href, { + let req = fetch(form.action || window.location.href, { method: form.method, body: new FormData(form), + redirect: "manual", }); - if (response.ok) { + ev.preventDefault(); // has to happen before first await + form.reset(); + let res = await req; + if (res.ok || res.type === 'opaqueredirect') { awaitingCommandResponse = true; window.clearTimeout(updateLogTimeout); updateLogTimeout = window.setTimeout(updateLog, 100); + } else { + window.alert(res.statusText); } - - form.reset(); } document.getElementById("command-form").addEventListener("submit", postForm); From 873b59170e96562148c44c9f69cd18c3b73c7095 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 10 Jun 2024 00:04:04 +0200 Subject: [PATCH 4/6] WebHost: put the first 1MB of log into room html --- WebHostLib/misc.py | 16 +++++++++++++++- WebHostLib/templates/hostRoom.html | 15 +++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/WebHostLib/misc.py b/WebHostLib/misc.py index f9223a8347c1..87802a263858 100644 --- a/WebHostLib/misc.py +++ b/WebHostLib/misc.py @@ -151,7 +151,21 @@ def host_room(room: UUID): with db_session: room.last_activity = now # will trigger a spinup, if it's not already running - return render_template("hostRoom.html", room=room, should_refresh=should_refresh) + def get_log(max_size: int = 1024000) -> str: + try: + raw_size = 0 + fragments: List[str] = [] + for block in _read_log(os.path.join("logs", str(room.id) + ".txt")): + if raw_size + len(block) > max_size: + fragments += "…" + break + raw_size += len(block) + fragments.append(block.decode("utf-8")) + return "".join(fragments) + except FileNotFoundError: + return "" + + return render_template("hostRoom.html", room=room, should_refresh=should_refresh, get_log=get_log) @app.route('/favicon.ico') diff --git a/WebHostLib/templates/hostRoom.html b/WebHostLib/templates/hostRoom.html index 19dd24b0f3de..fa8e26c2cbf8 100644 --- a/WebHostLib/templates/hostRoom.html +++ b/WebHostLib/templates/hostRoom.html @@ -55,18 +55,22 @@ Open Log File... -
+ {% set log = get_log() -%} + {%- set log_len = log | length - 1 if log.endswith("…") else log | length -%} +
{{ log }}
{% endif %} From 0acdcd1128d6407ed49ffac69d50d6fcff98de89 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 10 Jun 2024 00:41:43 +0200 Subject: [PATCH 5/6] WebHost: display_log: remove diplicate charset definition Charset is separate from mimetype in flaask, so this gets appended automatically (and defaults to utf-8) --- WebHostLib/misc.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/WebHostLib/misc.py b/WebHostLib/misc.py index 87802a263858..f13f813dab38 100644 --- a/WebHostLib/misc.py +++ b/WebHostLib/misc.py @@ -121,13 +121,12 @@ def display_log(room: UUID) -> Union[str, Response, Tuple[str, int]]: if range_type != "bytes" or end != "": return "Unsupported range", 500 # NOTE: we skip Content-Range in the response here, which isn't great but works for our JS - return Response(_read_log(file_path, int(start)), mimetype="text/plain;charset=UTF-8", status=206) - return Response(_read_log(file_path), mimetype="text/plain;charset=UTF-8") + return Response(_read_log(file_path, int(start)), mimetype="text/plain", status=206) + return Response(_read_log(file_path), mimetype="text/plain") except FileNotFoundError: return Response(f"Logfile {file_path} does not exist. " - f"Likely a crash during spinup of multiworld instance or it is still spinning up." - .encode("utf-8"), - mimetype="text/plain;charset=UTF-8") + f"Likely a crash during spinup of multiworld instance or it is still spinning up.", + mimetype="text/plain") return "Access Denied", 403 From 72fc8892b9cd89f6a69ba3864f1e7fce57268aa6 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 10 Jun 2024 01:20:48 +0200 Subject: [PATCH 6/6] WebHost: host_room, minor cleanup --- WebHostLib/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebHostLib/misc.py b/WebHostLib/misc.py index f13f813dab38..4b7cb475fc8b 100644 --- a/WebHostLib/misc.py +++ b/WebHostLib/misc.py @@ -156,7 +156,7 @@ def get_log(max_size: int = 1024000) -> str: fragments: List[str] = [] for block in _read_log(os.path.join("logs", str(room.id) + ".txt")): if raw_size + len(block) > max_size: - fragments += "…" + fragments.append("…") break raw_size += len(block) fragments.append(block.decode("utf-8"))