Updated Configs and Claud v2
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,3 +6,5 @@ venv/
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
config.toml
|
||||
.claude/
|
||||
|
||||
62
CLAUDE.md
Normal file
62
CLAUDE.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## What this is
|
||||
|
||||
A Pygame framebuffer clock for a Le Potato (ARM Debian) connected to an auditorium TV. Renders directly to `/dev/fb0` — no X11/Wayland. Staff push announcements via a browser dashboard or API webhook.
|
||||
|
||||
## Running locally
|
||||
|
||||
**Server + dashboard only (no pygame needed — works on WSL/Windows):**
|
||||
```bash
|
||||
python3 main.py --no-display
|
||||
```
|
||||
|
||||
**Windowed display mode (requires a graphical environment):**
|
||||
```bash
|
||||
python3 main.py --dev
|
||||
```
|
||||
Press **Escape** to close. On WSL2 this needs WSLg or VcXsrv.
|
||||
|
||||
**First run:** if `config.toml` has no `token`, one is auto-generated and saved. If `password_hash` is empty, a setup wizard appears at `http://localhost:8080/setup`.
|
||||
|
||||
**Copy config from example before first run:**
|
||||
```bash
|
||||
cp config.toml.example config.toml
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
The app has two concurrent execution contexts that communicate through `MessageState`:
|
||||
|
||||
- **`DisplayThread`** (`display.py`) — a daemon thread running pygame. Polls `state.get()` on every frame tick. When a message is active it replaces the clock entirely; font size is binary-searched to fill 88% of the screen.
|
||||
- **`ClockServer`** (`server.py`) — aiohttp running in the asyncio main thread. Updates `state` in response to dashboard/API requests.
|
||||
|
||||
`MessageState` (`state.py`) is the only shared object between the two contexts. It uses a `threading.Lock` and stores expiry as a `time.monotonic()` timestamp. Expiry is checked lazily on `get()` — there is no background timer.
|
||||
|
||||
`AppConfig` (`config.py`) is loaded once at startup from `config.toml`. The only post-startup writes are `save_password_hash()` (on first-run setup) and auto-generated token (on first run with empty token). Both use regex replacement on the raw TOML file to avoid reformatting.
|
||||
|
||||
## Server routes
|
||||
|
||||
| Route | Auth | Purpose |
|
||||
|---|---|---|
|
||||
| `GET /` | Session cookie | Serve dashboard `index.html` |
|
||||
| `GET /setup`, `POST /setup` | None | First-run password wizard |
|
||||
| `GET /login`, `POST /login` | None | Dashboard login |
|
||||
| `POST /logout` | Session cookie | Dashboard logout |
|
||||
| `POST /dashboard/message` | Session cookie | Send message (from dashboard JS) |
|
||||
| `DELETE /dashboard/message` | Session cookie | Clear message (from dashboard JS) |
|
||||
| `GET /dashboard/status` | Session cookie | Polled every 5 s by dashboard JS |
|
||||
| `POST /api/message` | Bearer token + rate limit | Webhook / external integrations |
|
||||
| `DELETE /api/message` | Bearer token + rate limit | Clear via API |
|
||||
| `GET /api/status` | Bearer token | Check state via API |
|
||||
|
||||
The dashboard (`dashboard/index.html`, `style.css`, `app.js`) calls the `/dashboard/*` routes, not `/api/*`. Static assets are served from `dashboard/` at `/static/`.
|
||||
|
||||
## Key behaviours to preserve
|
||||
|
||||
- `display.py` is **not imported** when `--no-display` is set — this allows the server to run without pygame installed.
|
||||
- Message font layout is **cached** in `DisplayThread.run()` and only recomputed when the message text changes.
|
||||
- `_update_config_field` in `config.py` uses regex on the raw TOML — it only rewrites the matched field, leaving all comments and formatting intact.
|
||||
- `secrets.compare_digest` is used for bearer token comparison to prevent timing attacks.
|
||||
35
config.toml.example
Normal file
35
config.toml.example
Normal file
@@ -0,0 +1,35 @@
|
||||
[display]
|
||||
# Screen resolution. Leave commented to auto-detect from the framebuffer.
|
||||
# width = 1920
|
||||
# height = 1080
|
||||
|
||||
# Display refresh rate in frames per second. 10 is plenty for a clock.
|
||||
fps = 10
|
||||
|
||||
# Paths to TTF font files. Leave blank to auto-detect from system fonts.
|
||||
# The clock uses a monospace font; messages use a sans-serif bold font.
|
||||
clock_font_path = ""
|
||||
message_font_path = ""
|
||||
|
||||
[server]
|
||||
port = 8080
|
||||
|
||||
# Default message duration in seconds when the caller doesn't specify one.
|
||||
default_duration_seconds = 20
|
||||
|
||||
[api]
|
||||
# Bearer token for /api/* routes.
|
||||
# Leave blank — a token is auto-generated on first run and saved here.
|
||||
token = ""
|
||||
|
||||
[rate_limit]
|
||||
# Maximum API requests per minute per source IP.
|
||||
requests_per_minute = 20
|
||||
|
||||
[dashboard]
|
||||
# Bcrypt hash of the dashboard password.
|
||||
# Leave blank — a setup wizard runs on the first browser visit to set the password.
|
||||
password_hash = ""
|
||||
|
||||
# How many hours of inactivity before a dashboard session expires.
|
||||
session_timeout_hours = 8
|
||||
Reference in New Issue
Block a user