mirror of
https://github.com/mrvasil/ctf-ad-mirror-tool-rust.git
synced 2026-10-23 15:31:01 +00:00
61 lines
6.0 KiB
Markdown
61 lines
6.0 KiB
Markdown
# HTTP contract
|
|
|
|
JSON API, UTF-8. Authenticated sessions use an HttpOnly SameSite=Strict cookie. Mutating authenticated requests require `X-CSRF-Token` from login or session response. Browser requests must be same-origin. No secrets returned in snapshots. Application-handled failures return `{ "error": "human readable explanation" }` with non-2xx status. Framework routing errors (for example 405) may have a plain-text body; clients must tolerate this. A snapshot is the source of truth; a 2xx response confirms completion. After admission under the serialized operation lock, topology mutations run independently of the HTTP connection through commit/rollback and cleanup. A lost response does not imply an uncommitted change: refresh the snapshot before retrying. Requests cancelled while waiting for admission make no change. Shutdown rejects new admissions and waits for admitted work within the documented process deadline.
|
|
|
|
## Routes
|
|
- `GET /` — embedded UI shell (login shown until session validated).
|
|
- `GET /licenses.txt` — licenses and attribution for the bundled Bootstrap/Font Awesome assets.
|
|
- `GET /healthz` — process readiness `{status:"ok"}` (not upstream checker health).
|
|
- `POST /api/login` JSON `{password:string}` → `{authenticated:true,csrf:string}`, Set-Cookie. 401 invalid, 429 limited.
|
|
- `GET /api/session` → `{authenticated:true,csrf:string}`, 401 expired/missing.
|
|
- `POST /api/logout` → `{ok:true}`, clears cookie.
|
|
- `GET /api/snapshot` → full snapshot below.
|
|
- `GET /api/events` — SSE, event name `snapshot`, full snapshot every second, heartbeat 15s. Session expiry closes stream. EventSource reconnects automatically; recheck session after errors.
|
|
- `PUT /api/services/{id}` — full ServiceConfig JSON; id must match path. Atomically save and hot-apply. → `{ok:true}`. Validation/bind error: 400; persistence failure: 400 with explanation, runtime rollback attempted. Stopped listener drains established sessions. Changing primary, replicas, policy or auto mode does not restart a running listener. Existing sessions keep their previous route.
|
|
- `DELETE /api/services/{id}` — requires `enabled:false` first; drains/removes then → `{ok:true}`.
|
|
- `PUT /api/teams/{id}` — full Team JSON; new team or rename. Addresses of existing ids are immutable; add another id instead. → `{ok:true}`.
|
|
|
|
ServiceConfig:
|
|
```json
|
|
{"id":"notes","name":"Notes","listen":"0.0.0.0:18080","upstream_port":8080,"primary":"a","replicas":["b"],"enabled":false,"auto_switch":false,"replica_policy":"isolate"}
|
|
```
|
|
Team:
|
|
```json
|
|
{"id":"a","name":"Team A","address":"192.0.2.10"}
|
|
```
|
|
|
|
Backward-compatible optional fields: ServiceConfig has `custom:false` and `custom_ip:null`; Team has `hidden:false`. The legacy add dialog sends `custom:true`, equal listen/upstream ports and an optional `custom_ip`. A non-null IP resolves/reuses its team, sets primary, and atomically saves team plus service. New anonymous IP targets have `hidden:true`; the UI shows them only in services that reference them. No separate operator team-creation step is required. Clearing `custom_ip` leaves the submitted primary/replicas authoritative. Manual quick promotion retains the previous primary as replica; automatic promotion does the same and clears the direct-IP override.
|
|
|
|
Configured/saved topology containing a non-null `custom_ip` must also contain its matching primary team. The PUT adapter creates that pair; raw TOML/state files are validated rather than silently repaired.
|
|
|
|
## Snapshot
|
|
```json
|
|
{
|
|
"now_ms": 1792700000000,
|
|
"main_team_ip": "192.0.2.10",
|
|
"health_max_age_ms": 12000,
|
|
"teams": [{"id":"a","name":"Team A","address":"192.0.2.10"}],
|
|
"services": [{
|
|
"id":"notes","name":"Notes","listen":"0.0.0.0:18080","upstream_port":8080,
|
|
"primary":"a","replicas":[],"enabled":true,"auto_switch":false,"replica_policy":"isolate",
|
|
"engine":{"running":true,"last_error":null,"stats":{"accepted":5,"active":1,"completed":4,"connect_errors":0,"io_errors":0,"rejected":0,"accept_errors":0,"bytes_up":1234,"bytes_down":5678,"replica_disconnects":0,"replica_errors":0,"replica_bytes":0}},
|
|
"health":{"a":{"reachable":true,"healthy":true,"consecutive_failures":0,"consecutive_successes":2,"checked_at_ms":1792700000000,"latency_ms":2}},
|
|
"scoreboard":{"a":{"status":101,"stolen":3,"lost":0}}
|
|
}],
|
|
"scoreboard":{"enabled":true,"connected":true,"kind":"forcad","round":5,"updated_at_ms":1792700000000,"stale":false,"last_error":null,"teams":[],"tasks":[],"entries":{}},
|
|
"events":[{"at_ms":1792700000000,"service":"notes","message":"Настройки сохранены"}]
|
|
}
|
|
```
|
|
|
|
Compute displayed bandwidth from positive byte-counter deltas / `now_ms` deltas, reset baseline when counters decrease. Show unknown health until checked. Scoreboard badges must indicate stale data; never imply stale checker state is current. Replica counters mean explicit disconnections/errors, not dropped arbitrary packets. `replica_policy`: `strict` uses backpressure, `isolate` disconnects a lagging replica per session.
|
|
|
|
The restored panel retains the legacy four connection counters; byte totals and replica counters are available in their tooltip and the snapshot API. TCP freshness uses `health_max_age_ms`; the UI additionally marks the whole snapshot stale and disables mutations after five seconds without updates. The top service strip shows the configured `main_team_ip` checker status, independently of the selected upstream, as in the legacy panel. Unset main IP gives an uncolored strip. Checker names and lost-flag chips retain the original color/sort behavior.
|
|
|
|
UI follow-up: the Teams dialog exposes existing team PUT (create/rename, preserving
|
|
hidden status; existing address read-only). Configuration help displays existing
|
|
scoreboard snapshot fields without adding endpoints. Invalid custom-IP drafts stay
|
|
local across snapshots; Enter retries save and Escape restores the saved address.
|
|
Changing a direct IP preserves other replicas; the backend removes any new-primary
|
|
overlap. Add-proxy drafts persist across dialog cancel/reopen, until successful save
|
|
or page reload. Process configuration remains TOML-only and requires a restart.
|