From cb364dc2f28a8243abce1f8b093bad8c86e59dc3 Mon Sep 17 00:00:00 2001 From: Olivier Breuleux Date: Thu, 4 Apr 2024 11:41:39 -0400 Subject: [PATCH] Add more admin operations (#56) * /admin/restart => /admin/operations * Add a link to download the database * Add a SQL editor to query and modify the database in a pinch --- paperoni/webapp/admin/operations.py | 34 +++++++++++++++++++ paperoni/webapp/admin/restart.py | 30 ----------------- paperoni/webapp/admin/sql.py | 52 +++++++++++++++++++++++++++++ paperoni/webapp/app-style.css | 26 +++++++++++++++ paperoni/webapp/help.md | 4 +-- 5 files changed, 114 insertions(+), 32 deletions(-) create mode 100644 paperoni/webapp/admin/operations.py delete mode 100644 paperoni/webapp/admin/restart.py create mode 100644 paperoni/webapp/admin/sql.py diff --git a/paperoni/webapp/admin/operations.py b/paperoni/webapp/admin/operations.py new file mode 100644 index 0000000..96e6cf1 --- /dev/null +++ b/paperoni/webapp/admin/operations.py @@ -0,0 +1,34 @@ +import asyncio +import os +import signal + +from hrepr import H +from starbear import Queue + +from ...config import papconf +from ..common import mila_template + + +@mila_template(help="/help#operations") +async def app(page, box): + """Admin operations.""" + q = Queue() + + box.print(H.p(H.button("Restart server", onclick=q.tag("restart")))) + box.print(H.p(H.a("Download database", href=papconf.paths.database))) + + async for event in q: + match event.tag: + case "restart": + box.set("Restarting. Try to refresh in a few seconds.") + await asyncio.sleep( + 0 + ) # Make sure we send the feedback before the kill() + try: + os.kill(os.getpid(), signal.SIGTERM) + except Exception as exc: + box.print(H.div["error"]("An error occurred")) + box.print(H.div["error"](exc)) + + +ROUTES = app diff --git a/paperoni/webapp/admin/restart.py b/paperoni/webapp/admin/restart.py deleted file mode 100644 index 4ae3a61..0000000 --- a/paperoni/webapp/admin/restart.py +++ /dev/null @@ -1,30 +0,0 @@ -import asyncio -import os -import signal - -from hrepr import H -from starbear import Queue - -from ..common import mila_template - - -@mila_template(help="/help#restart") -async def app(page, box): - """Restart the server.""" - q = Queue() - - box.print(H.button("Restart server", onclick=q)) - - async for _ in q: - box.set("Restarting. Try to refresh in a few seconds.") - await asyncio.sleep( - 0 - ) # Make sure we send the feedback before the kill() - try: - os.kill(os.getpid(), signal.SIGTERM) - except Exception as exc: - box.print(H.div["error"]("An error occurred")) - box.print(H.div["error"](exc)) - - -ROUTES = app diff --git a/paperoni/webapp/admin/sql.py b/paperoni/webapp/admin/sql.py new file mode 100644 index 0000000..7f3b69d --- /dev/null +++ b/paperoni/webapp/admin/sql.py @@ -0,0 +1,52 @@ +from hrepr import H +import sqlalchemy +from starbear import Queue + +from ...config import papconf +from ..common import mila_template + + +@mila_template(help="/help#sql") +async def app(page, box): + """Query and manipulate the database.""" + q = Queue() + + form = H.form["sql-edit"]( + H.div(H.textarea(name="sql")), + H.div(H.button("Run")), + onsubmit=q.wrap(form=True), + ) + + box.print(H.div(form)) + box.print(result_area := H.div().autoid()) + + with papconf.database as db: + async for event in q: + try: + result = db.session.execute(event["sql"]) + except Exception as exc: + page[result_area].set(exc) + continue + + try: + t = H.table["sql-results"]() + t = t(H.tr(H.th(row_name) for row_name in result.keys())) + t = t( + H.tr( + H.td( + H.code(value.hex()) + if isinstance(value, bytes) + else str(value) + ) + for value in row + ) + for row in result + ) + page[result_area].set(t) + + except sqlalchemy.exc.ResourceClosedError: + # Happens when we try to iterate over DELETE results + page[result_area].set("done") + + +ROUTES = app diff --git a/paperoni/webapp/app-style.css b/paperoni/webapp/app-style.css index feb1cd9..10892e0 100644 --- a/paperoni/webapp/app-style.css +++ b/paperoni/webapp/app-style.css @@ -486,3 +486,29 @@ div.log-stream textarea { width: 100%; height: 500px; } + +form.sql-edit { + margin-left: 0px; +} + +form.sql-edit textarea { + display: block; + width: 100%; + height: 100px; + box-sizing: border-box; +} + +table.sql-results { + border-collapse: collapse; + width: 100%; +} + +table.sql-results th, td { + padding-right: 5px; +} + +table.sql-results th { + text-align: left; + color: white; + background: black; +} diff --git a/paperoni/webapp/help.md b/paperoni/webapp/help.md index 7f7680a..47f659e 100644 --- a/paperoni/webapp/help.md +++ b/paperoni/webapp/help.md @@ -80,9 +80,9 @@ Notes: The configuration is defined as YAML. The `version_tag` field represents the tag on the Paperoni GitHub repository to use for this app. To update the software, you can change this value and then restart the server by going on [/admin/restart](/admin/restart). -## Restart {: #restart} +## Operations {: #operations} -When the server restarts, it checks out the version of the code represented by the `version_tag` key in `config.yaml`. +**Restart:** When the server restarts, it checks out the version of the code represented by the `version_tag` key in `config.yaml`. # Troubleshooting