# WsPage Per-user GenServer that caches and serves the WebSocket test page HTML. ## Overview `WsPage` reads `asset/html/wstest.html` from disk once at init and caches it in memory. Each user gets their own process, registered in `WsRegistry` under `{:page, username}`. The process is started by the `/wstest` route or by `WsHandler` on WebSocket connect, and stopped by `WsHandler.terminate/2` on disconnect. ## Features | Feature | Description | |---------|-------------| | File caching | Reads HTML once at init, serves from memory | | Per-user isolation | One GenServer per username via WsRegistry | | Lifecycle managed | Started on page load / WS connect, stopped on WS disconnect | | Idempotent start | Handles `{:error, {:already_started, pid}}` gracefully | ## Public API ### start_link/1 Starts a `WsPage` GenServer for the given username. ```elixir {:ok, pid} = WsPage.start_link("bob") # If already running: {:error, {:already_started, pid}} = WsPage.start_link("bob") ``` **Parameters:** - `username` (string) — username to register the page process under **Returns:** `{:ok, pid}` or `{:error, {:already_started, pid}}` ### render/1 Returns the cached HTML string for the WebSocket test page. ```elixir html = WsPage.render("bob") #=> "\n..." ``` **Parameters:** - `username` (string) — username whose WsPage process to query **Returns:** `binary()` — full HTML content of `asset/html/wstest.html` ### via/1 Returns the `{:via, Registry, ...}` tuple for process registration and lookup. ```elixir WsPage.via("bob") #=> {:via, Registry, {WsRegistry, {:page, "bob"}}} ``` **Parameters:** - `username` (string) — username to build the via tuple for **Returns:** `{:via, Registry, {WsRegistry, {:page, username}}}` ## GenServer State | Key | Type | Description | |-----|------|-------------| | `:email` | string | Username (historical naming) | | `:html` | binary | Cached contents of `asset/html/wstest.html` | ## WsRegistry Keys | Key Pattern | Owner | Purpose | |-------------|-------|---------| | `{:page, username}` | WsPage | HTML cache process per user | | `{:handler, username}` | WsHandler | Active WebSocket connection per user | | `{:ws, service_name}` | Various | GenServers exposing a WebSocket service | ## Lifecycle 1. User navigates to `/wstest` → router calls `WsPage.start_link(username)` then `WsPage.render(username)` 2. Browser JavaScript opens WebSocket to `/ws` → `WsHandler.init/1` calls `WsPage.start_link(username)` (idempotent) 3. WebSocket disconnects → `WsHandler.terminate/2` calls `GenServer.stop(page_pid)` ## Dependencies | Module | Purpose | |--------|---------| | `WsHandler` | Manages WsPage lifecycle on connect/disconnect | | `WsRegistry` | Elixir Registry for process name registration | | `MCPServer.Router` | Routes `/wstest` and `/ws` endpoints |