serve a bundle at its permalink
-site (or KHOSRA_SITE) opens the site root through content.OpenSite, so every
read keeps the os.Root guarantee. A path is a bundle key: /{section}/{slug}/
serves, the slashless form redirects permanently to it (ADR-0008), anything
unknown is 404. Render failure logs and returns a bare 500 rather than leaking a
template or filesystem detail.
internal/render holds goldmark plus the embedded reference theme (ADR-0026):
base.html with a redefinable "main" block, and one stylesheet inlined through
.Style. Serving it at an asset route would have been a second routing case for no
gain, and static serving belongs to a later entry.
Evidence beyond the tests: the binary against a real site root returns 200 with
<h1>About</h1> and the rendered body, 301 from /pages/about to /pages/about/, and
404 for /nope/. A Bengali variant is scanned but not yet reachable — that is the
next entry.
theme-contract.md gains a "Live today" section listing the six fields and two
named templates a theme may now rely on; the rest stays marked as shape.
This commit is contained in:
@@ -215,3 +215,30 @@ func dropCollisions(all []Bundle) []Bundle {
|
||||
}
|
||||
return kept
|
||||
}
|
||||
|
||||
// Site is a set of bundles indexed for lookup by permalink key.
|
||||
type Site struct {
|
||||
byKeyLang map[string]Bundle
|
||||
}
|
||||
|
||||
// NewSite indexes bundles for lookup. Later variants of a key and language cannot occur, because Scan
|
||||
// drops ambiguity before this sees it.
|
||||
func NewSite(bundles []Bundle) *Site {
|
||||
s := &Site{byKeyLang: make(map[string]Bundle, len(bundles))}
|
||||
for _, b := range bundles {
|
||||
s.byKeyLang[b.Key+"\x00"+b.Lang] = b
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Lookup returns the default-locale variant of a key.
|
||||
//
|
||||
// Other languages are reachable only once language routing exists; until then a key means its default
|
||||
// variant (ADR-0021).
|
||||
func (s *Site) Lookup(key string) (Bundle, bool) {
|
||||
b, ok := s.byKeyLang[key+"\x00"+DefaultLang]
|
||||
return b, ok
|
||||
}
|
||||
|
||||
// Len reports how many bundles the site holds.
|
||||
func (s *Site) Len() int { return len(s.byKeyLang) }
|
||||
|
||||
@@ -153,3 +153,25 @@ func TestOpenSiteRefusesSymlinkEscape(t *testing.T) {
|
||||
t.Fatal("traversal with .. succeeded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSiteLookupFindsDefaultVariant(t *testing.T) {
|
||||
fsys := fstest.MapFS{
|
||||
"content/pages/about.md": {Data: []byte("---\ntitle: About\n---\nhi\n")},
|
||||
"content/pages/about.bn.md": {Data: []byte("---\ntitle: পরিচিতি\n---\nহাই\n")},
|
||||
}
|
||||
bundles, err := Scan(fsys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
site := NewSite(bundles)
|
||||
if site.Len() != 2 {
|
||||
t.Fatalf("indexed %d bundles, want 2", site.Len())
|
||||
}
|
||||
b, ok := site.Lookup("pages/about")
|
||||
if !ok || b.Title != "About" {
|
||||
t.Fatalf("Lookup gave %+v %v, want the English variant", b, ok)
|
||||
}
|
||||
if _, ok := site.Lookup("pages/missing"); ok {
|
||||
t.Error("Lookup invented a bundle")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user