LabCodeHub öffentliche Ansicht
Anmelden
Küpper / Quildrop öffentlich
Branch: main
Quildrop / internal / content / page.go
Verlauf Rohdaten
R Rüdiger Küpper fix: first commit
06fa7c25 vor 6 Monaten
internal/content/page.go 102 Zeilen · 2.6 KB · Go
1
package content
2
3
import (
4
	"html/template"
5
	"log"
6
	"os"
7
	"path/filepath"
8
	"strings"
9
)
10
11
// Page represents a static site page (e.g. Impressum, Datenschutz, Projekte).
12
type Page struct {
13
	Title       string `yaml:"title"`
14
	Description string `yaml:"description"`
15
16
	// Derived
17
	Slug        string        // URL path relative to /sites/, e.g. "impressum" or "projekte/vm-tracker"
18
	Content     string        // raw markdown
19
	HTMLContent template.HTML // rendered HTML
20
}
21
22
// ParsePage reads a markdown file and returns a Page.
23
func ParsePage(path string, basePath string) (*Page, error) {
24
	data, err := os.ReadFile(path)
25
	if err != nil {
26
		return nil, err
27
	}
28
29
	raw := strings.TrimLeft(string(data), " \t\n\r")
30
	page := &Page{}
31
32
	if strings.HasPrefix(raw, "---") {
33
		// Has frontmatter
34
		rest := raw[3:]
35
		idx := strings.Index(rest, "\n---")
36
		if idx >= 0 {
37
			frontmatter := rest[:idx]
38
			body := rest[idx+4:]
39
			if err := yamlUnmarshal([]byte(frontmatter), page); err != nil {
40
				log.Printf("Warning: frontmatter parse error in %s: %v", path, err)
41
			}
42
			raw = body
43
		}
44
	}
45
46
	// Derive slug from relative path
47
	rel, _ := filepath.Rel(basePath, path)
48
	slug := strings.TrimSuffix(rel, filepath.Ext(rel))
49
	slug = strings.TrimSuffix(slug, ".md") // double .md handling
50
	// Normalize: if file is named index.md, use parent dir as slug
51
	if filepath.Base(slug) == "index" {
52
		slug = filepath.Dir(slug)
53
		if slug == "." {
54
			slug = ""
55
		}
56
	}
57
	page.Slug = slug
58
59
	// If no title from frontmatter, derive from slug
60
	if page.Title == "" {
61
		parts := strings.Split(slug, "/")
62
		last := parts[len(parts)-1]
63
		page.Title = strings.Title(strings.ReplaceAll(last, "-", " "))
64
	}
65
66
	// Render markdown
67
	rawContent := preprocessContent(raw)
68
	page.Content = rawContent
69
70
	var buf strings.Builder
71
	if err := mdConvert([]byte(rawContent), &buf); err != nil {
72
		log.Printf("Warning: markdown render error in %s: %v", path, err)
73
	}
74
	page.HTMLContent = template.HTML(buf.String())
75
76
	return page, nil
77
}
78
79
// LoadAllPages recursively reads all markdown files from the sites directory.
80
func LoadAllPages(sitesDir string) ([]*Page, error) {
81
	if _, err := os.Stat(sitesDir); os.IsNotExist(err) {
82
		return nil, nil // sites dir doesn't exist yet, that's ok
83
	}
84
85
	var pages []*Page
86
	err := filepath.WalkDir(sitesDir, func(path string, d os.DirEntry, err error) error {
87
		if err != nil {
88
			return err
89
		}
90
		if d.IsDir() || !strings.HasSuffix(d.Name(), ".md") {
91
			return nil
92
		}
93
		page, err := ParsePage(path, sitesDir)
94
		if err != nil {
95
			log.Printf("Warning: skipping page %s: %v", path, err)
96
			return nil
97
		}
98
		pages = append(pages, page)
99
		return nil
100
	})
101
	return pages, err
102
}