50 lines
1.5 KiB
Go
50 lines
1.5 KiB
Go
package handler
|
|
|
|
import (
|
|
_ "embed"
|
|
"encoding/json"
|
|
)
|
|
|
|
// reservedSlugs are workspace slugs that would collide with frontend top-level
|
|
// routes, platform features, or web standards. The frontend URL shape is
|
|
// /{workspaceSlug}/... so any slug that matches a top-level route or a
|
|
// system-significant name is rejected at workspace creation time.
|
|
//
|
|
// The list is loaded from reserved_slugs.json (embedded at build time), which
|
|
// is the single source of truth shared with the TypeScript side. Edit only
|
|
// the JSON; packages/core/paths/reserved-slugs.ts is regenerated from it by
|
|
// `pnpm generate:reserved-slugs` and CI fails on any drift.
|
|
|
|
//go:embed reserved_slugs.json
|
|
var reservedSlugsJSON []byte
|
|
|
|
var reservedSlugs = loadReservedSlugs()
|
|
|
|
type reservedSlugsFile struct {
|
|
Groups []struct {
|
|
Slugs []string `json:"slugs"`
|
|
} `json:"groups"`
|
|
}
|
|
|
|
func loadReservedSlugs() map[string]bool {
|
|
var data reservedSlugsFile
|
|
if err := json.Unmarshal(reservedSlugsJSON, &data); err != nil {
|
|
// reserved_slugs.json is checked into the repo and embedded into the
|
|
// binary; a parse failure is a programming error caught at the very
|
|
// first request that touches workspace creation, which is too late.
|
|
// Panic at init so the binary refuses to start instead.
|
|
panic("handler: parse reserved_slugs.json: " + err.Error())
|
|
}
|
|
out := make(map[string]bool)
|
|
for _, g := range data.Groups {
|
|
for _, slug := range g.Slugs {
|
|
out[slug] = true
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func isReservedSlug(slug string) bool {
|
|
return reservedSlugs[slug]
|
|
}
|