update docs

This commit is contained in:
anthorob committed 2026-10-19 08:21:42 -04:00
1 parent d3211c6702
commit 389af563b9
11 files changed
+181 -69

No files matched your search

+70 -31
View File
@@ -1,52 +1,91 @@
# HTTP Web API
**Package:** `me.bobert.netneutrality.platform.web` (client module) and
`me.bobert.netneutrality.platform.web.server` (server).
**Server:** JDK built-in `com.sun.net.httpserver.HttpServer` (no external web framework).
**Package:** `me.bobert.netneutrality.http` (outbound client) and
`me.bobert.netneutrality.http.server` (server).
**Server:** embedded [Javalin](https://javalin.io) 7 with the OpenAPI and Swagger UI plugins.
The bot exposes a small internal HTTP API for health monitoring, control, and guild introspection —
intended for an external dashboard/companion, not for end users in Discord.
The bot exposes an internal HTTP API for health monitoring, control, guild introspection, and music
control — intended for an external dashboard/companion, not for end users in Discord.
## Server startup
`WebApi` is a [`Lifecycle`](utilities-and-bootstrap.md#applicationcontext--lifecycle) component
(priority 70) registered in [`App.buildContext()`](architecture.md#startup-lifecycle).
It receives `SettingsProvider`, `GuildServiceProvider`, and suppliers for the live `JDA` /
shutdown callback via its constructor. On `start()` it:
It receives `SettingsProvider`, `GuildServiceProvider`, `MusicService`, and suppliers for the live
`JDA` / shutdown callback via its constructor. On `start()` it:
- reads the port from `NETNEUTRALITY_API_PORT` (default `9000`),
- creates an `HttpServer` bound to that port,
- installs an `Executor` backed by a fixed thread pool of 10 threads named via
[`BaseThreadFactory`](threading.md),
- registers the handler contexts (below), and
- starts the server.
- creates a `Javalin` app (Javalin 7 requires routes and exception/error handlers to be registered
upfront inside `Javalin.create`),
- registers the `OpenApiPlugin` (title "Net Neutrality API", `X-API-Key` security scheme) and the
`SwaggerPlugin`,
- installs the `ApiKeyFilter` as a `before` handler,
- registers the route groups (below), the exception handlers and the 404 handler, and
- starts the server on the port.
On `stop()` the server and its executor are shut down gracefully.
On `stop()` the Javalin app is stopped.
## Handler model
## Authentication
`AbstractHandler` (implements `HttpHandler`) is the base for all handlers and provides helpers such as
`sendJsonResponse(...)` and `sendStringResponse(...)`. Concrete handlers extend it and are mounted at
a path prefix.
`ApiKeyFilter` compares the `X-API-Key` request header with the `NETNEUTRALITY_API_KEY` setting.
A missing/invalid key — or no configured key at all — yields `401` ("Missing or invalid API key").
Public paths that skip the check: `/`, and anything starting with `/openapi`, `/swagger` or
`/webjars` (Swagger UI and its assets).
| Handler | Base path | Endpoints |
|---|---|---|
| `RootHandler` | `/` | Simple HTML "OK" landing response. |
| `GuildHandler` | `/guilds` | `/guilds/owner` — guild ids + owner ids (JSON); `/guilds/{guildId}/channels` — text channels (JSON); `/guilds/{guildId}/roles` — roles (JSON). |
| `ActionHandler` | `/actions` | `/actions/status` — JDA health + gateway ping; `/actions/shutdown` — graceful shutdown. |
| `SettingsHandler` | `/settings` | `/settings/catalog` — every known setting with its default value and description; `/settings/current/{guildId}` — current guild settings; `/settings/reload/{guildId}` — invalidate & reload the settings cache. |
## Error handling
The action/guild handlers read live state from the [`JDA`](architecture.md) instance and from the
[persistence services](persistence.md#service-layer) (e.g. `GuildSettingService` for the settings
endpoints).
- `HttpResponseException` (e.g. `BadRequestResponse`, `NotFoundResponse`) →
`{"status":"error","message":"..."}` with the exception's status.
- Any other exception → logged, `500` with `{"status":"error"}`.
- Unknown paths → `404` with `{"status":"error","message":"Not found"}`.
## Route groups
Each group is a `*Routes` class with a static `register(RoutesConfig, ...)` method; handlers are
annotated with `@OpenApi` so they appear in the generated OpenAPI document. Request/response shapes
live in the `http.server.dto` package.
| Class | Endpoints |
|---|---|
| `WebApi` | `GET /` — HTML liveness message. |
| `GuildRoutes` | `GET /guilds` (optional `user_id` query restricts to mutual guilds); `GET /guilds/owner`; `GET /guilds/{guildId}/owner`; `/channels`; `/roles`; `/members/{userId}`, `/members/{userId}/access`, `/members/{userId}/roles`, `/members/{userId}/admin`; `/voice-channels`, `/voice-channels/{channelId}`; `GET /guilds/{guildId}/config`; `PUT /guilds/{guildId}/debug`, `/logging`, `/log-channel`. |
| `ActionRoutes` | `GET /actions/status` — JDA health + gateway ping; `POST /actions/shutdown` — graceful shutdown. |
| `SettingsRoutes` | `GET /settings/catalog` — every known setting with default and description; `GET /settings/current/{guildId}` — current guild settings; `PUT /settings/current/{guildId}/{name}` — update a setting; `PUT /settings/reload/{guildId}` — invalidate & reload the settings cache. |
| `StatsRoutes` | `GET /stats/bot`; `GET /stats/guilds/{guildId}`. |
| `MusicRoutes` | See below. |
### Music endpoints
Mirror the slash commands (see [music.md](music.md)); announcements are also posted to the guild's
music channel when a context is attached.
| Method & path | Purpose |
|---|---|
| `GET /music/sources` | Supported music sources. |
| `GET /music/{guildId}/status` | Connection, playback, current track, position, queue size. |
| `GET /music/{guildId}/queue` | Queued tracks. |
| `POST /music/{guildId}/search` | Resolve a query (optional `searchEngine`) into candidates; returns a `search_id`. Nothing is queued. |
| `POST /music/{guildId}/queue` | Queue a candidate by `searchId` + `index` (or all with `queueAll=true`) for `userId`; the bot must already be in a voice channel. |
| `DELETE /music/{guildId}/queue` | Clear the queue, or remove one track with `?position=` (1-based). |
| `PATCH /music/{guildId}/queue/reorder` | Move a track (`fromPosition` → `toPosition`). |
| `POST /music/{guildId}/skip`, `/stop`, `/pause`, `/resume`, `/shuffle`, `/leave` | Playback controls. |
| `GET` / `PUT /music/{guildId}/volume` | Read / set volume (0–100). |
| `PUT /music/{guildId}/seek` | Seek to `positionMs`. |
| `GET /music/{guildId}/vote-skip` | Vote-skip status. |
| `POST /music/{guildId}/vote-skip/{userId}` | Cast a vote on behalf of a member. |
| `GET /music/{guildId}/dj/{userId}` | Whether the member is currently allowed to act as DJ. |
Search results are held in `PendingSearchResults` and expire after a TTL (returned as
`search_id_ttl_seconds`).
## HTTP client — `WebModule`
`WebModule` wraps an `OkHttpClient` for the bot's own outbound HTTP calls (e.g. checking for updates
via GitLab). It keeps a small `LinkedHashMap`-based response cache limited to 50 entries.
`WebModule` (package `me.bobert.netneutrality.http`) wraps an `OkHttpClient` for the bot's own
outbound HTTP calls (e.g. checking for updates via GitLab). Responses are cached in
`ResponseCache`, a `LinkedHashMap`-based LRU limited to 50 entries.
## Notes
- There is no authentication layer documented on these endpoints; the API is expected to be bound to a
trusted/internal network (e.g. within the Docker network). `/actions/shutdown` is destructive, so the
port should not be exposed publicly.
- All routes except the public ones require the API key. Even so, the API is expected to be bound to
a trusted/internal network (e.g. within the Docker network); `/actions/shutdown` is destructive,
so the port should not be exposed publicly.