Files
netneutrality/docs/web-api.md
T
anthorob d97c4c6da2 **Add autoplay support for seamless music playback**
Introduced autoplay functionality to keep music playing when the queue ends. Added new `/autoplay` API endpoints, command handling, and service implementations. Enhanced `TrackScheduler`, queue operations, and testing to support autoplay behavior. Integrated YouTube radio mix as the initial provider.
2026-10-31 16:13:04 -04:00

93 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HTTP Web API
**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 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`, `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 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 Javalin app is stopped.
## Authentication
`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).
## Error handling
- `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). |
| `GET` / `PUT /music/{guildId}/autoplay` | Read / set autoplay (`{"enabled": true}`). In-memory, resets when the player is destroyed. |
| `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` (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
- 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.