Files
summercms/cabana/prefix.go
Jakub Zych 5f9353841b feat(10-01): serve the embedded admin SPA at backend.uri with cookie login
- backend.uri prefix (default /backend) mounts the admin API at {prefix}/api/v1
  and the embedded SPA shell at {prefix} with an api/ JSON 404 fallback
- cookie transport: an X-Requested-With login sets the HttpOnly summer_admin
  cookie and returns no token; the backend guard reads the cookie after Bearer
- CSRF wrapper refuses cookie-only POST/PUT/DELETE without X-Requested-With
- boardwalk package embeds boardwalk/dist, rewrites index.html once per prefix
  and sets cache and security headers
- framework admin OpenAPI pipeline (swag, swagger2openapi, openapi-typescript)
  with prefix-relative paths and typed envelopes for the tracer routes
- admin/ Vite SPA: login, plugin rail, section panel and read-only list
  through the openapi-fetch client typed by the generated schema
2026-09-27 15:21:48 +02:00

50 lines
1.6 KiB
Go

package cabana
import (
"fmt"
"regexp"
"strings"
"git.golem15.com/golem15/summercms/backpack"
)
// DefaultAdminPrefix is the admin mount path when backend.uri is unset.
// It matches WinterCMS's backendUri default.
const DefaultAdminPrefix = "/backend"
// AdminCookieName carries the admin JWT for the embedded SPA (D-19).
const AdminCookieName = "summer_admin"
// adminAPIVersion is appended to the prefix for every admin API route.
const adminAPIVersion = "/api/v1"
var adminPrefixPattern = regexp.MustCompile(`^(/[a-z0-9][a-z0-9_-]*)+$`)
// AdminPrefix reads backend.uri and returns the normalized admin mount path.
// Spaces are trimmed, a leading slash is added and trailing slashes are
// removed; an empty value falls back to DefaultAdminPrefix. Every segment
// must be lowercase letters, digits, '-' or '_' and start with a letter or
// digit, so "/" alone, uppercase, spaces and dot segments are rejected.
func AdminPrefix(app *backpack.App) (string, error) {
raw := ""
if app != nil && app.Config != nil {
raw = app.Config.String("backend.uri")
}
return normalizeAdminPrefix(raw)
}
func normalizeAdminPrefix(raw string) (string, error) {
value := strings.TrimSpace(raw)
if value == "" {
return DefaultAdminPrefix, nil
}
if !strings.HasPrefix(value, "/") {
value = "/" + value
}
value = strings.TrimRight(value, "/")
if value == "" || !adminPrefixPattern.MatchString(value) {
return "", fmt.Errorf("cabana: backend.uri %q is invalid: use one or more lowercase path segments such as /backend (set SUMMER_BACKEND__URI)", raw)
}
return value, nil
}