Add settings page, PIN manager, and auto-config
Build Android APK / build (push) Failing after 4m21s
Build Server .exe / build (push) Failing after 2m34s

- New settings page at /settings (localhost-only) accessible via tray icon
- PIN manager with 6-digit codes, 5-min TTL, single-use
- API token uses secrets.token_urlsafe(32) for 256-bit entropy
- Config auto-creates config.yaml on first run with detected local IP
- Default PIN and token generated randomly on first startup
- Added pin_ttl setting (default 300s)
- Updated build_exe.py for onefile exe with settings assets bundled
- Added start.bat and start.sh for easy development startup
This commit is contained in:
Kareem Horstink
2026-07-03 08:21:46 +00:00
parent 4e3befbb82
commit 331de4bc9e
12 changed files with 652 additions and 19 deletions
+35 -3
View File
@@ -8,7 +8,10 @@ from __future__ import annotations
import logging
import multiprocessing
import os
import signal
import subprocess
import sys
import time
import pystray
@@ -42,9 +45,9 @@ def _run_server() -> None:
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8921,
log_level="info",
host=CONFIG_HOST,
port=CONFIG_PORT,
log_level=CONFIG_LOG_LEVEL,
reload=False,
)
@@ -55,6 +58,21 @@ def _on_quit(icon: pystray.Icon, item: pystray.MenuItem) -> None:
icon.stop()
def _on_settings(icon: pystray.Icon, item: pystray.MenuItem) -> None:
"""Open the settings page in the default browser."""
logger.info("Opening settings page...")
url = "http://127.0.0.1:8921/settings"
try:
if sys.platform == "win32":
os.startfile(url)
elif sys.platform == "darwin":
subprocess.run(["open", url])
else:
subprocess.run(["xdg-open", url])
except Exception as e:
logger.error(f"Failed to open settings page: {e}")
def main() -> None:
"""Entry point for the compiled .exe."""
logger.info("PaperDash starting...")
@@ -72,6 +90,8 @@ def main() -> None:
menu = pystray.Menu(
pystray.MenuItem("PaperDash", pystray.MenuItem.default, enabled=False),
pystray.Menu.Separator(),
pystray.MenuItem("Settings", _on_settings),
pystray.Menu.Separator(),
pystray.MenuItem("Quit", _on_quit),
)
@@ -100,5 +120,17 @@ def main() -> None:
server_process.join(timeout=5)
def _load_config_values() -> tuple[str, int, str]:
"""Load config and return (host, port, log_level) for the server."""
from .config import config
config.load()
return config.host or "127.0.0.1", config.port, config.log_level.lower()
# Load config at module level so _run_server uses detected values
CONFIG_HOST, CONFIG_PORT, CONFIG_LOG_LEVEL = _load_config_values()
if __name__ == "__main__":
main()