- cabana.BackendUser is an attach.Owner (MorphName Backend\Models\User,
cabana.BackendUserMorphType) with one public attachOne avatar
(cabana.BackendUserAvatarField), as WinterCMS stores it
- POST /account/avatar stores one file_data image (jpg, jpeg, png, gif or
webp, image guard, at most cabana.AvatarMaxBytes = 2 MiB, under a
server-generated name) and replaces the previous avatar: old rows are
deleted in the transaction, their blobs after the commit
- DELETE /account/avatar removes the avatar and is idempotent
- GET /auth/me and the account routes return avatar {url, thumb_url}
(250x250 crop); cabana.BackendUserAvatar lets plugins read it
- swag annotations, regenerated admin.json and schema.d.ts; route
inventories, CSRF walk (29) and OpenAPI conformance learn both routes
- SPA: avatar upload and remove on My account, avatar image in the user
menu trigger and header; backend::lang.myaccount avatar strings
- rebuilt boardwalk dist; cabana README, users-and-permissions and
admin-spa docs
boardwalk
HTTP handler that serves the embedded admin SPA build under a configurable path prefix.
import "git.golem15.com/golem15/summercms/modules/boardwalk"
Overview
boardwalk embeds the compiled admin SPA (dist/, produced by npm --prefix admin run build) into the binary and serves it. The build is path-agnostic: index.html carries a placeholder token that the handler replaces once, at construction, with the prefix the admin is mounted under, so one build works at any backend URI. cabana mounts it when it activates the admin routes. It stands in for the server-rendered backend layouts of WinterCMS, which the Go port replaces with a single-page app.
Features
- Serves the embedded build under any prefix, rewriting relative asset URLs and the admin base meta in
index.htmlfor that prefix (boardwalk.RewriteIndex). - Fails at boot when
index.htmllacks theboardwalk.BaseTokenplaceholder, which catches a stale or hand-edited build. - Falls back to
index.htmlfor client-side routes and directories; missing files with an extension get a plain 404. - Hands every request whose path under the prefix is
apior starts withapi/to a caller-supplied handler, so admin API misses stay JSON instead of returning the SPA. - Long-lived immutable caching for hashed files under
assets/,no-cachefor other files andno-storeforindex.html. - Security headers on every response: a restrictive Content-Security-Policy, frame denial,
nosniff, a same-origin referrer policy andnoindex, nofollow. - Explicit content types for scripts, styles, fonts, SVG and JSON, with a MIME lookup fallback.
Usage
notFoundAPI := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"error":"not found"}`))
})
spa, err := boardwalk.Handler("/backend", notFoundAPI)
if err != nil {
return err
}
mux.Handle("/backend/", spa)
API reference
| Identifier | Description |
|---|---|
boardwalk.Handler |
Builds the SPA handler for a prefix; the second argument answers unmatched api/ paths. |
boardwalk.Dist |
Returns the embedded build as an fs.FS rooted at dist/. |
boardwalk.RewriteIndex |
Rewrites raw index.html bytes for a prefix; errors when the placeholder token is missing. |
boardwalk.BaseToken |
The placeholder in dist/index.html that is replaced by the prefix. |
boardwalk.ContentType |
The Content-Type served for a file name, with explicit UTF-8 JavaScript and CSS types. Shared with the plugin asset route in cabana. |
boardwalk.SetSecurityHeaders |
Sets the admin security headers (nosniff, referrer policy, no framing, the script-src 'self' CSP, noindex) on a response. |
Dependencies
- SummerCMS modules: none.
- Third-party: none.
- Standard library:
bytes,embed,errors,fmt,html,io/fs,mime,net/http,path,strings,time.
The embedded dist/ tree is generated from the admin/ Vite project, whose build writes to modules/boardwalk/dist.
Testing
go test ./modules/boardwalk/...
The tests run the handler through net/http/httptest against the embedded build and in-memory file systems; they need no external services.