Split embedded and standalone chart surfaces

This commit is contained in:
Mikhail Chusavitin
2026-03-15 21:41:38 +03:00
parent df91e24fea
commit 5ce37f9997
15 changed files with 1039 additions and 86 deletions

114
docs/embedding.md Normal file
View File

@@ -0,0 +1,114 @@
# Embedding Chart
`chart` is intended to be embedded into another Go application that already obtains a Reanimator JSON snapshot.
The embedding application remains the source of truth for:
- where the JSON comes from
- when the snapshot is collected
- how routing, auth, and navigation work
`chart` only renders one snapshot as a read-only HTML view.
## Integration Modes
There are two supported integration styles:
1. Render HTML directly from bytes:
```go
html, err := viewer.RenderHTML(snapshotBytes, "Hardware Snapshot")
if err != nil {
return fmt.Errorf("render chart html: %w", err)
}
```
2. Mount the embedded viewer HTTP handler:
```go
mux.Handle("/chart/", http.StripPrefix("/chart", viewer.NewHandler(viewer.HandlerOptions{
Title: "Hardware Snapshot",
})))
```
Use `viewer.NewHandler(...)` inside another application.
Use `viewer.NewStandaloneHandler(...)` only for the standalone `chart` binary or for a dedicated upload page where the viewer itself owns the first-screen file picker.
## Embedded Handler Behavior
`viewer.NewHandler(...)` is the embedded mode.
Routes:
- `GET /` renders the viewer shell with no upload controls
- `POST /render` accepts one snapshot and returns rendered HTML
- `GET /healthz` returns process health
- `GET /static/...` serves embedded CSS assets
The embedded handler does not show a file-upload screen on `GET /`.
This is intentional: the host application decides how the snapshot is selected and passed in.
## Snapshot Submission
`POST /render` accepts:
- raw `application/json`
- `multipart/form-data` with `snapshot_file`
Example with raw JSON:
```go
req, err := http.NewRequest(http.MethodPost, "/chart/render", bytes.NewReader(snapshotBytes))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
```
Example with server-side render without an HTTP round-trip:
```go
func chartPage(w http.ResponseWriter, r *http.Request) {
snapshotBytes := loadSnapshotForRequest(r)
html, err := viewer.RenderHTML(snapshotBytes, "Hardware Snapshot")
if err != nil {
http.Error(w, "failed to render snapshot", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = w.Write(html)
}
```
## Recommended Host Responsibilities
When embedding `chart`, keep these responsibilities in the host application:
- authentication and authorization
- selecting the target host or audit record
- loading snapshot bytes from disk, database, object storage, or another service
- page layout outside the chart surface
- links back to the rest of the host application
## Constraints
- Input must be one Reanimator JSON snapshot.
- `chart` is read-only.
- `chart` preserves unknown fields where possible and does not compute new health summaries.
- Presentation formatting is allowed, but payload semantics must not change.
## Standalone Binary
The standalone binary uses `viewer.NewStandaloneHandler(...)`.
That mode adds a first-screen upload form on `GET /` for local manual use.
Local run:
```bash
make run
```