aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-25 16:31:52 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-25 16:31:52 +0200
commit8bb94a39609f57be2c486f579849eebb04606808 (patch)
tree46d5553f514619d8f1f33af6eb32a6de314a03d0 /packages/meshbay-hub/src/meshbay_hub/api/webapp.py
parent10c58792c7591accdb84c92201f77843ec39ee7b (diff)
downloadmeshbay-8bb94a39609f57be2c486f579849eebb04606808.tar.gz
feat(hub): friendlier welcome page, link previews, robots.txt and favicon
Welcome page: privacy said once, a three-step "how it works", a documentation box, download (green) and legal links under the sign-in form, on a dark gradient backdrop covering the whole page. Link previews: Open Graph tags in the app shell, rendered for identity.id, with the square icon as image. robots.txt, favicon and touch icon served at the origin root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/api/webapp.py')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/webapp.py43
1 files changed, 40 insertions, 3 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/webapp.py b/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
index 626c639..c9cb8d5 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/webapp.py
@@ -12,6 +12,7 @@ The root route (/) returns the SPA HTML shell.
"""
import hashlib
+import html
from pathlib import Path
from fastapi import APIRouter
@@ -142,17 +143,49 @@ CSP = "; ".join([
@router.get("/app", response_class=HTMLResponse)
async def app_root():
- return HTMLResponse(_HTML, headers=_NO_STORE)
+ return HTMLResponse(_shell, headers=_NO_STORE)
@router.get("/app/{path:path}", response_class=HTMLResponse)
async def app_catchall(path: str):
- return HTMLResponse(_HTML, headers=_NO_STORE)
+ return HTMLResponse(_shell, headers=_NO_STORE)
@router.get("/", response_class=HTMLResponse)
async def index():
- return HTMLResponse(_HTML, headers=_NO_STORE)
+ return HTMLResponse(_shell, headers=_NO_STORE)
+
+
+# What a messenger draws for a link to this hub: an invitation, or the address
+# itself. Signal and WhatsApp read these tags and nothing else, and resolve only
+# an absolute og:image, so the shell is rendered for the hub's public name —
+# `identity.id`, the name mail links already use. Short on purpose: a preview
+# shows a line or two of the description and cuts the rest. The image is the
+# square icon, which is the shape a thumbnail is cut to anyway.
+PREVIEW_DESCRIPTION = "Your files stay at home. Reach them from anywhere."
+
+
+def _preview_tags(hub_id: str) -> str:
+ origin = html.escape(f"https://{hub_id}", quote=True)
+ return f"""\
+ <meta name="description" content="{PREVIEW_DESCRIPTION}">
+ <meta property="og:type" content="website">
+ <meta property="og:site_name" content="MeshBay">
+ <meta property="og:title" content="MeshBay">
+ <meta property="og:description" content="{PREVIEW_DESCRIPTION}">
+ <meta property="og:url" content="{origin}/">
+ <meta property="og:image" content="{origin}/og-image.jpg">
+ <meta property="og:image:type" content="image/jpeg">
+ <meta property="og:image:width" content="600">
+ <meta property="og:image:height" content="600">
+ <meta property="og:image:alt" content="The MeshBay logo">
+"""
+
+
+def configure(hub_id: str) -> None:
+ """Render the shell for this hub's public name (`identity.id`)."""
+ global _shell
+ _shell = _HTML.replace("{preview}", _preview_tags(hub_id))
_HTML = """\
@@ -171,6 +204,8 @@ _HTML = """\
<meta name="viewport"
content="width=device-width, initial-scale=1, interactive-widget=resizes-content">
<title>MeshBay</title>
+{preview} <link rel="icon" href="/a/{v}/favicon.ico" sizes="16x16 32x32 48x48">
+ <link rel="apple-touch-icon" href="/a/{v}/apple-touch-icon.png">
<link rel="stylesheet" href="/a/{v}/style.css">
</head>
<body>
@@ -205,3 +240,5 @@ _HTML = """\
</body>
</html>
""".replace("{v}", ASSET_V)
+
+_shell = _HTML.replace("{preview}", _preview_tags("meshbay.org"))