<#814 osctrl-api: API extensions for a React admin...
# osctrl
g
#814 osctrl-api: API extensions for a React admin frontend (round 2 of 3) Pull request opened by alvarofraguas Summary Round 2 of 3. Adds the API surface a React admin frontend needs to fully cover what the current
osctrl-admin
templates do. Every new endpoint is additive — no existing routes are removed or repurposed. The legacy admin keeps working unchanged. End-to-end tested against a Kali docker deployment. ⚠️ Stacked on #813 (security hardening). Merge #813 first, then this. If #813 is merged before review starts here, this branch will be cleanly rebased on the new
main
HEAD with no conflicts. New endpoints Stats / dashboard | Method | Path | Purpose | | ------ | ---------------------------------------- | ------------------------------------- | | GET | /api/v1/stats | Cross-env summary KPIs | | GET | /api/v1/stats/osquery-versions | Fleet agent versions | | GET | /api/v1/stats/activity/{env} | Env-scoped audit-log activity heatmap | | GET | /api/v1/stats/activity/node/{env}/{uuid} | Per-node activity heatmap | | GET | /api/v1/stats/activity/node-batch/{env} | Per-node heatmap, up to 100 uuids | Logs (live log viewer) | Method | Path | Purpose | | ------ | -------------------------------- | ------------------------------------------- | | GET | /api/v1/logs/{type}/{env}/{uuid} | Paginated, since-aware, optional ?q= search | Saved queries (full CRUD) | Method | Path | | ------ | ---------------------------------- | | GET | /api/v1/saved-queries/{env} | | POST | /api/v1/saved-queries/{env} | | PATCH | /api/v1/saved-queries/{env}/{name} | | DELETE | /api/v1/saved-queries/{env}/{name} | User profile + permissions + token | Method | Path | | ------ | -------------------------------------- | | GET | /api/v1/users/me | | PATCH | /api/v1/users/me | | POST | /api/v1/users/me/password | | POST | /api/v1/users/{username}/permissions | | POST | /api/v1/users/{username}/token/refresh | | DELETE | /api/v1/users/{username}/token | Environment CRUD + config PATCHes | Method | Path | | ------ | ------------------------------------- | | POST | /api/v1/environments | | PATCH | /api/v1/environments/{env} | | DELETE | /api/v1/environments/{env} | | GET | /api/v1/environments/{env}/config | | PATCH | /api/v1/environments/{env}/config | | PATCH | /api/v1/environments/{env}/intervals | | PATCH | /api/v1/environments/{env}/expiration | Settings PATCH | Method | Path | | ------ | --------------------------------- | | PATCH | /api/v1/settings/{service}/{name} | Audit log filters + pagination | Method | Path | | ------ | ---------------------------------------------------------------------------------- | | GET | /api/v1/audit-logs?service=&username=&type=&envUuid=&since=&until=&page=&pageSize= | Login envs (pre-auth env list for the SPA's login page env picker) | Method | Path | | ------ | -------------------------------------------------------------------------------- | | GET | /api/v1/login/environments (UUID + name only; everything else stays behind auth) | Sample libraries (operator starter packs) | Method | Path | | ------ | ----------------------- | | GET | /api/v1/queries/samples | | GET | /api/v1/carves/samples | | GET | /api/v1/osquery/tables | Pagination, sort, search conventions Every list endpoint accepts
?page=&page_size=
(default 50, max 500) and returns the envelope: { "items": [...], "page": N, "page_size": N, "total_items": N, "total_pages": N } Sortable fields use a per-resource
SortableColumns
allowlist enforced at the package layer (
pkg/nodes
,
pkg/queries
,
pkg/carves
). Unknown sort keys fall back to the resource's default order without 400ing. Search is
?q=
free-text against a per-resource field set (case-insensitive LIKE). Wildcards are escaped server-side. New package:
pkg/dbutil
Dialect-aware SQL bucket-expression helper (postgres / mysql / sqlite) used by the activity heatmap endpoints. Each category (status logs / result logs / distributed queries / carves) issues a single SQL
GROUP BY
rather than plucking every timestamp — at 50k+ nodes the table-page heatmap query is bounded by the index instead of the chatty-row count. Package-layer additions
pkg/nodes
GetByEnvPaged
,
NodeView
projection,
SortableColumns
, platform-bucket helpers,
GetOsqueryVersionCounts
. •
pkg/queries
GetByEnvTargetPaged
,
GetSaved*
CRUD,
SortableColumns
, sample-template loader,
GetNodeQueryBucketed
. •
pkg/carves
GetByEnvPaged
, sample-template loader,
GetNodeCarveBucketed
. •
pkg/environments
Create
/
Update
/
Delete
,
UpdateConfig
/
UpdateIntervals
/
UpdateExpiration
helpers. •
pkg/auditlog
GetPaged
with `PageFilter`;
GetEnvActivityBucketed
for the heatmap. •
pkg/logging
GetNodeLogs
with
?q=
search filter,
GetNode{Status,Result}Bucketed
for the heatmap. •
pkg/osquery
LoadTables
(osquery schema for the SPA query editor). •
pkg/types
NodeView
, paginated response envelopes,
EnvCreate
/
EnvUpdate
/
EnvConfig*
request types,
SettingPatchRequest
,
SavedQueryView
,
AdminUserView
. Test plan
go build ./...
— clean •
go vet ./...
— clean •
go test ./...
— all packages pass •
gofmt -l ./...
— empty • End-to-end smoke against a Kali docker deployment (paginated lists, activity heatmap, env CRUD, saved-queries CRUD, profile flow) • New tests added:
cmd/api/handlers/stats_test.go
,
pkg/nodes/nodes_test.go
,
pkg/queries/queries_test.go
,
pkg/queries/saved_test.go
What this enables Round 3 will land the React admin SPA under a new
frontend/
directory at the repo root. The SPA consumes only the endpoints in this PR — no admin-template surface is touched. jmpsec/osctrl