Files
b1tsblog/internal/article/load.go
2025-05-04 17:10:38 +02:00

195 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// internal/article/load.go
package article
// internal/article/load.go (gekürzt)
import (
"bytes"
"encoding/json"
"fmt"
"html/template"
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
"time"
md "github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
)
// LoadDir liest alle *.html und *.md unter root/content,
// parst FrontMatter (JSONKommentar in der 1. Zeile) und liefert []Article.
func LoadDir(root string) ([]Article, error) {
var out []Article
seen := make(map[string]bool) // DuplikatsCheck
mdRenderer := html.NewRenderer(html.RendererOptions{
Flags: html.CommonFlags | html.HrefTargetBlank,
})
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if d.IsDir() {
return nil
}
ext := strings.ToLower(filepath.Ext(path))
if ext != ".md" && ext != ".html" {
return nil // uninteressante Datei
}
raw, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("read %s: %w", path, err)
}
parts := bytes.SplitN(raw, []byte("\n"), 2)
if len(parts) < 2 {
return fmt.Errorf("%s: missing frontmatter", path)
}
headerJSON, err := extractJSONHeader(parts[0])
if err != nil {
return fmt.Errorf("%s: %w", path, err)
}
var meta struct {
Title string `json:"title"`
Date string `json:"date"`
Slug string `json:"slug"`
Cover string `json:"cover"`
}
if err := json.Unmarshal(headerJSON, &meta); err != nil {
return fmt.Errorf("%s: %w", path, err)
}
// FallbackSlug aus Dateinamen
if meta.Slug == "" {
meta.Slug = strings.TrimSuffix(filepath.Base(path), ext)
}
if seen[meta.Slug] {
return fmt.Errorf("%s: duplicate slug %q", path, meta.Slug)
}
seen[meta.Slug] = true
body := parts[1]
if ext == ".md" {
body = md.ToHTML(body, nil, mdRenderer) // frischer Parser pro Call
}
date, err := time.Parse("2006-01-02", meta.Date)
if err != nil {
return fmt.Errorf("%s: parse date: %w", path, err)
}
out = append(out, Article{
Title: meta.Title,
Slug: meta.Slug,
Date: date,
Cover: meta.Cover,
Body: template.HTML(body),
})
return nil
})
if err != nil {
return nil, err
}
sort.Slice(out, func(i, j int) bool { return out[i].Date.After(out[j].Date) })
return out, nil
}
// ---------- NEU ----------
type StaticPage struct {
Title string
Slug string
Body template.HTML
}
// helper.go einmal zentral verwenden
func extractJSONHeader(line []byte) ([]byte, error) {
s := strings.TrimSpace(string(line))
s = strings.TrimPrefix(s, "<!--")
// alles bis zum ersten "-->" abschneiden
if idx := strings.Index(s, "-->"); idx != -1 {
s = s[:idx]
}
s = strings.TrimSpace(s)
// optional: falls doch kein JSON, besserer Fehler
if !strings.HasPrefix(s, "{") {
return nil, fmt.Errorf("no JSON frontmatter found")
}
return []byte(s), nil
}
// LoadStatic liest alle .md/.htmlDateien unter dir und liefert sie als Map[slug]StaticPage.
func LoadStatic(dir string) (map[string]StaticPage, error) {
pages := make(map[string]StaticPage)
entries, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
for _, e := range entries {
if e.IsDir() {
continue
}
ext := filepath.Ext(e.Name())
if ext != ".md" && ext != ".html" {
continue // unbekanntes Format
}
path := filepath.Join(dir, e.Name())
raw, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read %s: %w", path, err)
}
// FrontMatter (erste Zeile) herauslösen
parts := bytes.SplitN(raw, []byte("\n"), 2)
if len(parts) < 2 {
return nil, fmt.Errorf("%s: missing frontmatter", path)
}
headerJSON, err := extractJSONHeader(parts[0])
if err != nil {
return nil, fmt.Errorf("%s: %w", path, err)
}
var meta struct {
Title string `json:"title"`
Slug string `json:"slug"`
}
if err := json.Unmarshal(headerJSON, &meta); err != nil {
return nil, fmt.Errorf("%s: %w", path, err)
}
// Fallback: Slug aus Dateinamen ableiten, falls im Header leer
if meta.Slug == "" {
meta.Slug = strings.TrimSuffix(e.Name(), ext)
}
if _, dup := pages[meta.Slug]; dup {
return nil, fmt.Errorf("%s: duplicate slug %q", path, meta.Slug)
}
body := parts[1]
if ext == ".md" {
body = md.ToHTML(body, nil, nil) // eigener Parser je Aufruf
}
pages[meta.Slug] = StaticPage{
Title: meta.Title,
Slug: meta.Slug,
Body: template.HTML(body),
}
}
return pages, nil
}