"
f'"
f""
f'back',
use_default=True,
)
def handle_style_submit(body, gateway_host="", scheme="http"):
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 "")
_set_flash("Template saved.")
return _redirect("/style")
def handle_field_save(body):
key = body.get("key", [""])[0].strip()
value = body.get("value", [""])[0].strip()
if not key:
return _json_response({"status": "error", "message": "No key provided."}, 400)
if key == "forum_enabled":
from handlers import forum_plugin
if value == "1" and forum_plugin is None:
return _json_response({"status": "error", "message": "Forum plugin not installed."}, 400)
if value == "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
templates_mod.FORUM_ENABLED = (value == "1")
set_setting(key, value)
return _json_response({"status": "ok", "message": ""})
def handle_about():
name = get_site_name()
dest_hash = get_setting("dest_hash")
sharing = get_setting("sharing_enabled", "0") == "1"
db = get_db()
try:
page_count = db.execute("SELECT count(*) FROM pages").fetchone()[0]
tag_count = db.execute("SELECT count(DISTINCT tag_id) FROM page_tags").fetchone()[0]
sub_count = db.execute("SELECT count(*) FROM subscriptions").fetchone()[0]
finally:
return_db(db)
sharing_html = (
'
This instance shares its index publicly. Subscribe to join the network.
'
if sharing else
'
This instance is private.
'
)
hash_html = ""
if dest_hash:
hash_html = (
f'
subscribe
'
f'
To subscribe to this instance, add this destination hash in your TinyWeb:
'
f'
{esc(dest_hash)}
'
)
return _respond(
f'
{esc(name)}
'
f'
A personal, decentralized search engine.
'
f'
You save pages you find. They are stored locally and shared over a mesh network '
f'so other people can find them too.
'
f'
Search results come from your index and the indexes of people you are connected to.
'
f'
'
f'
{page_count} page(s) indexed
'
f'
{tag_count} tag(s)
'
f'
{sub_count} subscription(s)
'
f'
'
f'{sharing_html}'
f'{hash_html}'
f'
your data
'
f'
Everything is stored locally under ~/.tinyweb/:
'
f'
'
f'
tinyweb_identity — your permanent mesh identity. '
f'If you lose this file, your destination hash changes and subscribers '
f'have to re-subscribe to the new one.
'
f'
index.db — your full reading history: every page, '
f'note, tag, and synced remote page.
'
f'
models/ — the semantic search model if you enabled it '
f'(redownloadable, safe to delete).
'
f'
'
f'
Back up ~/.tinyweb/ periodically. '
f'Copying the whole directory to another device preserves your identity and index together. '
f'The export page gives you a JSON dump of pages only — '
f'it does not preserve your identity or subscription state, so it is a migration aid, '
f'not a substitute for a full backup.