Adds a Notifications tab to Settings so an operator can pick which events
reach their phone, published to a single self-hosted ntfy topic.
The notifier subscribes to the realtime event hub the dashboard already
consumes, so every notifiable event keeps exactly one publisher. Inbound
media had no event, so `media.received` is added and published from the
existing durable `gotImage` gate in the chat service — one notification
per conversation, never repeated when the room re-syncs. It feeds the
in-app notification bell as well.
- Best-effort delivery: a rejecting or unreachable ntfy server is logged
and never propagated into the path that produced the event.
- Events buffered before the notifier attaches are skipped, so a restart
cannot replay a backlog to somebody's phone.
- Settings are applied field by field onto the live config object, so a
save takes effect without a restart.
- The access token is environment-owned (NTFY_AUTH_TOKEN) like the other
secrets: stripped before the settings record is written, never returned
by the API, which reports only `hasAuthToken`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The upstream openresty proxy in front of Dokploy strips the
WebSocket Upgrade header, which kills noVNC. Publishing noVNC
directly on a host port (16080) avoids the proxy entirely.
Auth is still gated by VNC_PASSWORD. The dashboard continues
to go through Traefik on its existing domain.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
A ./data bind mount points at the deploy directory, which Dokploy
rebuilds on every redeploy — wiping the SQLite db, the persistent
browser session, and the cached Matrix credentials. That forced the
slow re-login + chat.reddit.com token sniff on every container start.
Switch to a named docker volume (reddit_bot_data) so the data
directory survives rebuilds.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
On Dokploy, traffic is routed through Traefik via the configured
domain — publishing host ports caused a collision with another
service already bound to 3000 on the same host. Switch to `expose`
so the ports are reachable on the internal Docker network only.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Chromium in the container was failing with ERR_INTERNET_DISCONNECTED
when navigating to Reddit. Override container DNS to public resolvers
so the browser can reach reddit.com / chat.reddit.com.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Split monolithic chat service into modular structure with Matrix client
support, extract web server into per-resource route modules, introduce
bootstrap/main-loop separation, and add comprehensive vitest coverage
across services, repositories, and utilities. Adds project docs and
CLAUDE.md agent instructions.