From 7a81b519bb1fbe037435d787799f127a1ce7d975 Mon Sep 17 00:00:00 2001
From: blankie
Date: Tue, 16 Jun 2026 05:11:37 +0000
Subject: [PATCH] remove demo pages, rename themes, add Content-Length header
---
gateway.py | 9 +-
handlers/__init__.py | 10 +-
handlers/_helpers.py | 4 +-
handlers/customize.py | 339 +--
handlers/pages.py | 15 +-
handlers/subscriptions.py | 17 +-
templates.py | 35 +-
themes/{tinyweb-site.html => default.html} | 0
themes/kodama.html | 2169 +++++++++++++-------
themes/kodama2.html | 1424 -------------
10 files changed, 1652 insertions(+), 2370 deletions(-)
rename themes/{tinyweb-site.html => default.html} (100%)
delete mode 100644 themes/kodama2.html
diff --git a/gateway.py b/gateway.py
index 02a2d95..fa4b076 100644
--- a/gateway.py
+++ b/gateway.py
@@ -165,12 +165,15 @@ class GatewayHandler(BaseHTTPRequestHandler):
"style-src 'self' 'unsafe-inline'; "
"script-src 'self' 'unsafe-inline'; "
"img-src 'self' data:")
+ resp_body = resp.get("body", "")
+ encoded = resp_body.encode() if isinstance(resp_body, str) else resp_body
+ if encoded:
+ self.send_header("Content-Length", str(len(encoded)))
for k, v in resp.get("headers", {}).items():
self.send_header(k, v)
self.end_headers()
- resp_body = resp.get("body", "")
- if resp_body:
- self.wfile.write(resp_body.encode() if isinstance(resp_body, str) else resp_body)
+ if encoded:
+ self.wfile.write(encoded)
except ConnectionError as e:
GatewayState.link = None
diff --git a/handlers/__init__.py b/handlers/__init__.py
index e7eda3e..228508e 100644
--- a/handlers/__init__.py
+++ b/handlers/__init__.py
@@ -32,7 +32,7 @@ from .subscriptions import (
handle_subscription_delete, handle_subscription_syncall,
_sync_threads,
)
-from .customize import handle_style_form, handle_style_submit, handle_style_template_submit, handle_field_save, handle_about
+from .customize import handle_style_form, handle_style_submit, handle_style_template_submit, handle_field_save, handle_about, _set_flash
from .tags import handle_tags, handle_tag_browse
from .data import (
handle_export, handle_import_form, handle_import_submit,
@@ -66,7 +66,7 @@ def _dispatch_inner(data):
return handle_pages(query)
elif path.startswith("/edit/"):
pid = extract_id("/edit/")
- return handle_edit_form(pid) if pid is not None else _error(400)
+ return handle_edit_form(pid, page=query.get("p", [""])[0]) if pid is not None else _error(400)
elif path.startswith("/delete/"):
pid = extract_id("/delete/")
return handle_delete_confirm(pid) if pid is not None else _error(400)
@@ -129,11 +129,13 @@ def _dispatch_inner(data):
return handle_field_save(body)
elif path == "/style/reset":
set_setting("custom_template", "")
- return handle_style_form("Template reset to default.", gateway_host=gateway_host, scheme=scheme)
+ _set_flash("Template reset to default.")
+ return _redirect("/style")
elif path == "/style/vacuum":
from db import vacuum_db
vacuum_db()
- return handle_style_form("Database vacuumed.", gateway_host=gateway_host, scheme=scheme)
+ _set_flash("Database vacuumed.")
+ return _redirect("/style")
elif path == "/import":
return handle_import_submit(body)
elif path == "/reindex":
diff --git a/handlers/_helpers.py b/handlers/_helpers.py
index 3fecb71..2611ce7 100644
--- a/handlers/_helpers.py
+++ b/handlers/_helpers.py
@@ -65,11 +65,11 @@ def _get_bookmark_token():
return token
-def _respond(body_html, status=200, use_default=False):
+def _respond(body_html, status=200, use_default=False, head_html=""):
return {
"status": status,
"content_type": "text/html; charset=utf-8",
- "body": wrap_page(body_html, use_default=use_default),
+ "body": wrap_page(body_html, use_default=use_default, head_html=head_html),
"headers": {},
}
diff --git a/handlers/customize.py b/handlers/customize.py
index 6808046..6b3786d 100644
--- a/handlers/customize.py
+++ b/handlers/customize.py
@@ -1,9 +1,19 @@
from db import get_db, return_db, get_setting, set_setting, get_site_name
import templates as templates_mod
from templates import esc, DEFAULT_TEMPLATE
-from ._helpers import _respond, _json_response, _csrf_field, _get_bookmark_token
+from ._helpers import _respond, _redirect, _json_response, _csrf_field, _get_bookmark_token, _request_local
from .subscriptions import _count_shared_pages
+_flash = {}
+
+
+def _set_flash(msg):
+ _flash[_request_local.csrf_token] = msg
+
+
+def _get_flash():
+ return _flash.pop(_request_local.csrf_token, "")
+
def handle_style_form(msg="", gateway_host="", scheme="http"):
template = get_setting("custom_template") or DEFAULT_TEMPLATE
@@ -21,7 +31,6 @@ def handle_style_form(msg="", gateway_host="", scheme="http"):
reranker = get_setting("use_reranker", "0")
reranker_checked = " checked" if reranker == "1" else ""
disabled = "" if semantic == "1" else " disabled"
- dimmed = ' style="opacity:0.4"' if semantic != "1" else ""
tcp_enabled = get_setting("tcp_enabled", "1")
tcp_checked = " checked" if tcp_enabled == "1" else ""
tcp_disabled = "" if tcp_enabled == "1" else " disabled"
@@ -32,31 +41,65 @@ def handle_style_form(msg="", gateway_host="", scheme="http"):
lora_enabled = get_setting("lora_enabled", "0")
lora_checked = " checked" if lora_enabled == "1" else ""
lora_disabled = "" if lora_enabled == "1" else " disabled"
- lora_dimmed = ' style="opacity:0.4"' if lora_enabled != "1" else ""
lora_port = get_setting("lora_port", "")
lora_frequency = get_setting("lora_frequency", "867200000")
lora_bandwidth = get_setting("lora_bandwidth", "125000")
lora_txpower = get_setting("lora_txpower", "7")
lora_sf = get_setting("lora_sf", "8")
lora_cr = get_setting("lora_cr", "5")
+ csrf = _csrf_field()
from handlers import forum_plugin as _fp
if _fp is not None:
- forum_section = (
+ forum_body = (
+ f""
)
+ forum_nav = ' · forum '
else:
- forum_section = ""
+ forum_body = ""
+ forum_nav = ""
+
+ msg = _get_flash() or msg
+ msg_html = ""
+ if msg:
+ msg_html = '{msg}
'.format(msg=esc(msg))
+
return _respond(
f"customize "
- f"name your search engine "
- f'
"
+ f' '
+ f""
+ f""
+ f" "
+ f"{forum_body}"
+ f" "
+ f""
+ f" "
+ f""
+ f" "
+ f""
+ f" "
+ f""
f'back ',
use_default=True,
)
def handle_style_submit(body, gateway_host="", scheme="http"):
- template = body.get("template", [""])[0].replace("\r\n", "\n").replace("\r", "\n")
- name = body.get("site_name", ["tinyweb"])[0].strip()
- sharing = "1" if body.get("sharing_enabled") else "0"
- sharing_mode = body.get("sharing_mode", ["exclude_private"])[0]
- if sharing_mode not in ("exclude_private", "require_public"):
- sharing_mode = "exclude_private"
- set_setting("sharing_mode", sharing_mode)
- semantic = "1" if body.get("semantic_search") else "0"
- reranker = "1" if body.get("use_reranker") else "0"
- compress = "1" if body.get("compress_embeddings") else "0"
- tcp_enabled = "1" if body.get("tcp_enabled") else "0"
- transport_host = body.get("transport_host", [""])[0].strip()
- transport_port = body.get("transport_port", [""])[0].strip()
- set_setting("custom_template", template if template.strip() != DEFAULT_TEMPLATE.strip() else "")
- set_setting("site_name", name or "tinyweb")
- set_setting("sharing_enabled", sharing)
- set_setting("semantic_search", semantic)
- set_setting("use_reranker", reranker)
- set_setting("compress_embeddings", compress)
- set_setting("tcp_enabled", tcp_enabled)
- if transport_host:
- set_setting("transport_host", transport_host)
- if transport_port:
- set_setting("transport_port", transport_port)
- lora_enabled = "1" if body.get("lora_enabled") else "0"
- set_setting("lora_enabled", lora_enabled)
- set_setting("lora_port", body.get("lora_port", [""])[0].strip())
- set_setting("lora_frequency", body.get("lora_frequency", ["867200000"])[0].strip())
- set_setting("lora_bandwidth", body.get("lora_bandwidth", ["125000"])[0].strip())
- set_setting("lora_txpower", body.get("lora_txpower", ["7"])[0].strip())
- set_setting("lora_sf", body.get("lora_sf", ["8"])[0].strip())
- set_setting("lora_cr", body.get("lora_cr", ["5"])[0].strip())
- forum_enabled = "1" if body.get("forum_enabled") else "0"
- current_forum = get_setting("forum_enabled", "0")
- if forum_enabled != current_forum:
- from handlers import forum_plugin
- if forum_enabled == "1" and forum_plugin is None:
- return handle_style_form(
- "Forum plugin not installed. Run: pip install tinyweb-forum",
- gateway_host=gateway_host, scheme=scheme,
- )
- if forum_enabled == "1":
- forum_plugin.enable()
- try:
- forum_plugin.fdb.set_setting("forum_enabled", "1")
- except Exception:
- pass
- else:
- forum_plugin.disable()
- try:
- forum_plugin.fdb.set_setting("forum_enabled", "0")
- except Exception:
- pass
- set_setting("forum_enabled", forum_enabled)
- templates_mod.FORUM_ENABLED = (forum_enabled == "1")
- return handle_style_form("Saved. Restart TinyWeb for mesh network changes to take effect.",
- gateway_host=gateway_host, scheme=scheme)
+ action = body.get("_action", [""])[0]
+
+ if action == "name":
+ name = body.get("site_name", ["tinyweb"])[0].strip()
+ set_setting("site_name", name or "tinyweb")
+ _set_flash("Saved.")
+ return _redirect("/style")
+
+ if action == "sharing":
+ sharing = "1" if body.get("sharing_enabled") else "0"
+ sharing_mode = body.get("sharing_mode", ["exclude_private"])[0]
+ if sharing_mode not in ("exclude_private", "require_public"):
+ sharing_mode = "exclude_private"
+ set_setting("sharing_mode", sharing_mode)
+ set_setting("sharing_enabled", sharing)
+ _set_flash("Saved.")
+ return _redirect("/style")
+
+ if action == "forum":
+ forum_enabled = "1" if body.get("forum_enabled") else "0"
+ current_forum = get_setting("forum_enabled", "0")
+ if forum_enabled != current_forum:
+ from handlers import forum_plugin
+ if forum_enabled == "1" and forum_plugin is None:
+ _set_flash("Forum plugin not installed. Run: pip install tinyweb-forum")
+ return _redirect("/style")
+ if forum_enabled == "1":
+ forum_plugin.enable()
+ try:
+ forum_plugin.fdb.set_setting("forum_enabled", "1")
+ except Exception:
+ pass
+ else:
+ forum_plugin.disable()
+ try:
+ forum_plugin.fdb.set_setting("forum_enabled", "0")
+ except Exception:
+ pass
+ set_setting("forum_enabled", forum_enabled)
+ templates_mod.FORUM_ENABLED = (forum_enabled == "1")
+ _set_flash("Saved.")
+ return _redirect("/style")
+
+ if action == "search":
+ semantic = "1" if body.get("semantic_search") else "0"
+ reranker = "1" if body.get("use_reranker") else "0"
+ compress = "1" if body.get("compress_embeddings") else "0"
+ set_setting("semantic_search", semantic)
+ set_setting("use_reranker", reranker)
+ set_setting("compress_embeddings", compress)
+ _set_flash("Saved.")
+ return _redirect("/style")
+
+ if action == "mesh":
+ tcp_enabled = "1" if body.get("tcp_enabled") else "0"
+ transport_host = body.get("transport_host", [""])[0].strip()
+ transport_port = body.get("transport_port", [""])[0].strip()
+ set_setting("tcp_enabled", tcp_enabled)
+ if transport_host:
+ set_setting("transport_host", transport_host)
+ if transport_port:
+ set_setting("transport_port", transport_port)
+ lora_enabled = "1" if body.get("lora_enabled") else "0"
+ set_setting("lora_enabled", lora_enabled)
+ set_setting("lora_port", body.get("lora_port", [""])[0].strip())
+ set_setting("lora_frequency", body.get("lora_frequency", ["867200000"])[0].strip())
+ set_setting("lora_bandwidth", body.get("lora_bandwidth", ["125000"])[0].strip())
+ set_setting("lora_txpower", body.get("lora_txpower", ["7"])[0].strip())
+ set_setting("lora_sf", body.get("lora_sf", ["8"])[0].strip())
+ set_setting("lora_cr", body.get("lora_cr", ["5"])[0].strip())
+ _set_flash("Saved.")
+ return _redirect("/style")
def handle_style_template_submit(body, gateway_host="", scheme="http"):
template = body.get("template", [""])[0].replace("\r\n", "\n").replace("\r", "\n")
set_setting("custom_template", template if template.strip() != DEFAULT_TEMPLATE.strip() else "")
- return handle_style_form("Template saved.", gateway_host=gateway_host, scheme=scheme)
+ _set_flash("Template saved.")
+ return _redirect("/style")
def handle_field_save(body):
diff --git a/handlers/pages.py b/handlers/pages.py
index cd3efba..0799251 100644
--- a/handlers/pages.py
+++ b/handlers/pages.py
@@ -172,7 +172,7 @@ def handle_pages(query=None):
f' '
f'{esc(r["title"])} {note_html}{tags_html} '
f'({esc(r["url"])} ) '
- f'edit '
+ f'edit '
f'remove '
)
finally:
@@ -282,7 +282,7 @@ def handle_bulk_action(body):
return _redirect("/pages")
-def handle_edit_form(page_id, msg=""):
+def handle_edit_form(page_id, msg="", page=""):
db = get_db()
try:
row = db.execute("SELECT id, url, title, body, note, summary FROM pages WHERE id = ?", (page_id,)).fetchone()
@@ -292,12 +292,15 @@ def handle_edit_form(page_id, msg=""):
finally:
return_db(db)
+ back = "/pages" + ("?p=" + page if page else "")
+
return _respond(
f"edit page "
f"{esc(row['title'])} "
f"{esc(row['url'])}
"
f''
f'{_csrf_field()}'
+ f' '
f'Title: '
f' '
f'Summary (shown in search results): '
@@ -310,7 +313,7 @@ def handle_edit_form(page_id, msg=""):
f'save '
f" "
f"{msg}
"
- f'back '
+ f'back '
)
@@ -335,7 +338,11 @@ def handle_edit_submit(page_id, body):
finally:
return_db(db)
- return _redirect("/pages")
+ page = body.get("_page", [""])[0].strip()
+ target = "/pages"
+ if page:
+ target += "?p=" + page
+ return _redirect(target)
def handle_delete_confirm(page_id):
diff --git a/handlers/subscriptions.py b/handlers/subscriptions.py
index e0ecf68..97f20b2 100644
--- a/handlers/subscriptions.py
+++ b/handlers/subscriptions.py
@@ -162,7 +162,7 @@ def handle_subscriptions(msg=""):
sync_btn = 'syncing... '
else:
sync_btn = (
- f''
+ f' '
f'{_csrf_field()}sync now '
)
@@ -173,18 +173,22 @@ def handle_subscriptions(msg=""):
f'last sync: {esc(last)}
'
f'{status_html}'
f''
- f'
browse '
+ f'
browse '
f'{sync_btn}'
- f'
'
+ f' '
f'{_csrf_field()}auto-sync: {auto_label} '
- f'
'
+ f' '
f'{_csrf_field()}remove '
f'
'
f''
)
+ any_syncing = any(
+ s["id"] in _sync_threads and _sync_threads[s["id"]].is_alive()
+ for s in subs
+ )
+ head_html = ' ' if any_syncing else ""
listing = ""
if subs:
- any_syncing = any(sid in _sync_threads and _sync_threads[sid].is_alive() for sid in [s["id"] for s in subs])
syncall_btn = 'syncing... ' if any_syncing else 'sync all '
listing = (
f'{cards}'
@@ -201,7 +205,8 @@ def handle_subscriptions(msg=""):
f'or subscribe to an instance
'
f'{msg}
'
f' {listing}'
- f'back '
+ f'back ',
+ head_html=head_html,
)
diff --git a/templates.py b/templates.py
index 89dadd7..27a1a6b 100644
--- a/templates.py
+++ b/templates.py
@@ -7,33 +7,6 @@ def esc(s):
return html.escape(str(s))
-FORUM_CSS = """
-"""
-
def _nav_html():
name = esc(get_setting("site_name", "tinyweb"))
forum_link = ' | forum ' if FORUM_ENABLED else ""
@@ -56,7 +29,7 @@ def _default_template():
)
-def wrap_page(body_html, use_default=False):
+def wrap_page(body_html, use_default=False, head_html=""):
if use_default:
template = _default_template()
else:
@@ -67,8 +40,6 @@ def wrap_page(body_html, use_default=False):
template = template.replace("{{forum_link}}", forum_link)
template = template.replace("{{site_name}}", esc(get_setting("site_name", "tinyweb")))
template = template.replace("{{nav}}", _nav_html())
- # Inject forum layout CSS into for any template
- head_end = ""
- if head_end in template and FORUM_CSS not in template:
- template = template.replace(head_end, FORUM_CSS + head_end)
+ if head_html:
+ template = template.replace("", head_html + "")
return template.replace("{{content}}", body_html)
diff --git a/themes/tinyweb-site.html b/themes/default.html
similarity index 100%
rename from themes/tinyweb-site.html
rename to themes/default.html
diff --git a/themes/kodama.html b/themes/kodama.html
index 503ac9a..34fc125 100644
--- a/themes/kodama.html
+++ b/themes/kodama.html
@@ -1,747 +1,1424 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- tinyweb
-
-
-
-
- {{content}}
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/themes/kodama2.html b/themes/kodama2.html
deleted file mode 100644
index 34fc125..0000000
--- a/themes/kodama2.html
+++ /dev/null
@@ -1,1424 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file