Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,16 @@ jepsen/.ssh/
.cache/
.golangci-cache/
server

# Admin SPA build outputs. The placeholder internal/admin/dist/index.html
# is committed so `go build` succeeds in a fresh clone (the //go:embed
# directive needs at least one matching file). Everything else under
# dist/ is regenerated by `cd web/admin && npm run build` and must not
# be committed — committed bundles invariably drift from source.
/internal/admin/dist/assets/
/internal/admin/dist/index-*.js
/internal/admin/dist/index-*.css

# Admin SPA source toolchain
/web/admin/node_modules/
/web/admin/.vite/
24 changes: 24 additions & 0 deletions internal/admin/dist/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>elastickv admin (bundle missing)</title>
<style>
body { font-family: ui-sans-serif, system-ui, sans-serif; max-width: 40rem; margin: 4rem auto; padding: 0 1rem; color: #1f2937; }
code, pre { background: #f3f4f6; padding: 0.1rem 0.3rem; border-radius: 0.25rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
pre { padding: 0.75rem 1rem; overflow-x: auto; }
h1 { font-size: 1.25rem; margin-bottom: 0.5rem; }
p { line-height: 1.5; }
</style>
</head>
<body>
<h1>elastickv admin SPA bundle is missing</h1>
<p>This is the placeholder <code>index.html</code> shipped in the repository so that <code>go build</code> succeeds before the React bundle is built.</p>
<p>To populate the real dashboard:</p>
<pre>cd web/admin
npm install
npm run build</pre>
<p>The Vite build writes its output into <code>internal/admin/dist/</code>, replacing this placeholder. Rebuild the Go binary afterwards.</p>
</body>
</html>
39 changes: 39 additions & 0 deletions internal/admin/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package admin

import (
"embed"
"errors"
"io/fs"
)

// distFS holds the Vite build output for the admin SPA. The directory
// is populated by `npm run build` under web/admin/, which writes its
// output straight into internal/admin/dist (see web/admin/vite.config.ts).
//
// We embed `dist` as a directory rather than a glob so that adding a new
// asset under dist/assets/ does not require touching this file. The
// `all:` prefix is intentional — Vite occasionally emits files whose
// names start with `.` (sourcemaps, tooling artefacts), and the default
// embed selector would silently drop them.
//
//go:embed all:dist
var distFS embed.FS

// StaticFS returns the io/fs.FS that backs /admin/assets/* and the SPA
// fallback. The returned FS is rooted at the embedded `dist` directory,
// so `index.html` resolves to `dist/index.html` and assets resolve to
// `dist/assets/*` — matching the pathing the Router expects.
//
// When the SPA bundle has not been built (only the placeholder
// index.html that ships with the repo is present), the FS is still
// returned: the placeholder renders a short message telling the
// operator how to populate the bundle. Returning nil here would have
// the router answer with JSON 404, which is more confusing than a
// page that explains itself.
func StaticFS() (fs.FS, error) {
sub, err := fs.Sub(distFS, "dist")
if err != nil {
return nil, errors.Join(errors.New("admin: open embedded dist subtree"), err)
}
return sub, nil
}
6 changes: 5 additions & 1 deletion main_admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,10 @@ func buildAdminHTTPServer(adminCfg *admin.Config, creds map[string]string, clust
if err != nil {
return nil, errors.Wrap(err, "build admin verifier")
}
staticFS, err := admin.StaticFS()
if err != nil {
return nil, errors.Wrap(err, "open embedded admin SPA")
}
server, err := admin.NewServer(admin.ServerDeps{
Signer: signer,
Verifier: verifier,
Expand All @@ -453,7 +457,7 @@ func buildAdminHTTPServer(adminCfg *admin.Config, creds map[string]string, clust
ClusterInfo: cluster,
Tables: tables,
Forwarder: forwarder,
StaticFS: nil,
StaticFS: staticFS,
AuthOpts: admin.AuthServiceOpts{
InsecureCookie: adminCfg.AllowInsecureDevCookie,
},
Expand Down
5 changes: 5 additions & 0 deletions web/admin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.vite
dist
*.log
*.tsbuildinfo
13 changes: 13 additions & 0 deletions web/admin/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="referrer" content="no-referrer" />
<title>elastickv admin</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading
Loading