feat(api): Add authenticated Caddy management API
All checks were successful
Caddy Manager CI build / docker (push) Successful in 4m24s

This commit is contained in:
2026-07-26 18:34:02 +07:00
parent a94abca127
commit 9f7cb79bad
9 changed files with 430 additions and 0 deletions

View File

@@ -161,6 +161,8 @@ services:
# Path to the Caddyfile as seen from inside the caddy container. Must match the container side of the
# caddy container's config volume. Defaults to /etc/caddy/Caddyfile, so this line is optional above.
DockerService__CaddyConfigPathInContainer: "/etc/caddy/Caddyfile"
# Shared key for the HTTP API (see "HTTP API" below). Leave it out to keep the API closed.
Api__Key: "change-me"
# To have the access to the caddy config file
user: "1000:1000"
# The .NET GC sizes its heap against the cgroup limit, so this both caps the worst
@@ -190,6 +192,49 @@ Currently, the Caddy Manager is able to:
configuration first, so a broken file is reported back instead of taking the proxy down)
- Restart caddy container on demand
- Parse simple information from the caddy configurations
- Do all of the above over HTTP, for scripts and other services (see below)
### HTTP API
The same operations are exposed as a JSON API, documented with OpenAPI:
- Interactive documentation: `/scalar`
- OpenAPI document: `/openapi/v1.json`
Every request needs the shared key in the `X-Api-Key` header. The key comes from `Api:Key`
(environment variable `Api__Key`). **While no key is configured the API is disabled and every
endpoint answers `503`** — nothing is exposed by accident.
| Method | Endpoint | Description |
| --- | --- | --- |
| `GET` | `/api/configurations` | List the reverse proxy configurations |
| `GET` | `/api/configurations/{name}` | Get one configuration with its raw content |
| `POST` | `/api/configurations` | Create a configuration (`{ "fileName": "...", "content": "..." }`) |
| `PUT` | `/api/configurations/{name}` | Replace a configuration's content (`{ "content": "..." }`) |
| `POST` | `/api/configurations/{name}/rename` | Rename a configuration (`{ "newFileName": "..." }`) |
| `DELETE` | `/api/configurations/{name}` | Delete a configuration |
| `GET` | `/api/caddyfile` | Get the global Caddyfile |
| `PUT` | `/api/caddyfile` | Replace the global Caddyfile (`{ "content": "..." }`) |
| `POST` | `/api/caddy/reload` | Graceful `caddy reload` |
| `POST` | `/api/caddy/restart` | Restart the Caddy container |
`{name}` is the file name without the `.caddy` extension, as shown in the UI.
```shell
curl -H "X-Api-Key: change-me" http://localhost:8080/api/configurations
curl -X POST http://localhost:8080/api/configurations \
-H "X-Api-Key: change-me" -H "Content-Type: application/json" \
-d '{"fileName":"example","content":"example.com {\n\treverse_proxy 10.0.0.2:8080\n}"}'
curl -X POST -H "X-Api-Key: change-me" http://localhost:8080/api/caddy/reload
```
Renaming moves the file only; if the global Caddyfile imports the old name, update that import
yourself (the UI warns about this too).
> Note: the app redirects HTTP to HTTPS, so a direct `curl http://...` against the container port
> gets a `307`. Add `-L`, call it over HTTPS, or go through your reverse proxy.
<p align="right">(<a href="#readme-top">back to top</a>)</p>