This commit is contained in:
2025-05-04 13:40:31 +02:00
parent afd8edaa6c
commit 92d272b36e
10 changed files with 293 additions and 0 deletions

97
internal/article/load.go Normal file
View File

@@ -0,0 +1,97 @@
// internal/article/load.go
package article
// internal/article/load.go (gekürzt)
import (
"bufio"
"encoding/json"
"fmt"
"html/template"
"io"
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
"time"
md "github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"
)
func LoadDir(root string) ([]Article, error) {
var out []Article
// gültige ExtensionMaske
exts := parser.CommonExtensions | parser.AutoHeadingIDs | parser.DefinitionLists
mdParser := parser.NewWithExtensions(exts)
mdRenderer := html.NewRenderer(html.RendererOptions{
Flags: html.CommonFlags | html.HrefTargetBlank,
})
err := filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return err
}
ext := filepath.Ext(p)
if ext != ".html" && ext != ".md" {
return nil
}
f, err := os.Open(p)
if err != nil {
return err
}
defer f.Close()
r := bufio.NewReader(f)
headerLine, err := r.ReadBytes('\n')
if err != nil && err != io.EOF {
return err
}
var meta struct {
Title string `json:"title"`
Date string `json:"date"`
Slug string `json:"slug"`
}
clean := strings.TrimSpace(string(headerLine))
clean = strings.TrimPrefix(clean, "<!--")
clean = strings.TrimSpace(clean)
// alles nach dem ersten "-->" abschneiden (egal ob Leerzeichen davor)
if idx := strings.Index(clean, "-->"); idx != -1 {
clean = clean[:idx]
}
clean = strings.TrimSpace(clean)
if err := json.Unmarshal([]byte(clean), &meta); err != nil {
return fmt.Errorf("FrontMatter in %s: %w (%q)", p, err, clean)
}
bodyBytes, err := io.ReadAll(r)
if err != nil {
return err
}
htmlBody := bodyBytes
if ext == ".md" {
htmlBody = md.ToHTML(bodyBytes, mdParser, mdRenderer)
}
date, _ := time.Parse("2006-01-02", meta.Date)
out = append(out, Article{
Title: meta.Title,
Slug: meta.Slug,
Date: date,
Body: template.HTML(htmlBody),
})
return nil
})
sort.Slice(out, func(i, j int) bool { return out[i].Date.After(out[j].Date) })
return out, err
}

14
internal/article/model.go Normal file
View File

@@ -0,0 +1,14 @@
// internal/article/model.go
package article
import (
"html/template"
"time"
)
type Article struct {
Title string
Slug string
Date time.Time
Body template.HTML // already trusted
}

View File

@@ -0,0 +1,20 @@
{{ define "article" }}
{{ template "base.html" . }}
{{ end }}
{{ define "title" }}{{ .Title }}  B1tsblog{{ end }}
{{ define "body" }}
<article class="article">
<header>
<h1>{{ .Title }}</h1>
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02.01.2006" }}</time>
</header>
<div class="article-content">
{{ .Body }}
</div>
<p><a href="/"> Alle Artikel</a></p>
</article>
{{ end }}

View File

@@ -0,0 +1,15 @@
<!DOCTYPE html><html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{{ block "title" . }}B1tsblog{{ end }}</title>
<link rel="stylesheet" href="/static/main.css">
<link rel="preconnect" href="https://fonts.gstatic.com">
<!-- MonospaceFont für Code (optional) -->
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
</head>
<body>
<header><h1><a href="/">B1tsblog</a></h1></header>
<main>{{ block "body" . }}{{ end }}</main>
<footer>© {{ now.Year }}</footer>
</body></html>

View File

@@ -0,0 +1,16 @@
{{ define "list" }} {{/* ausführbarer EntryPoint */}}
{{ template "base.html" . }} {{/* ruft das Layout auf */}}
{{ end }}
{{ define "title" }}Alle Artikel{{ end }}
{{ define "body" }}
<ul>
{{ range . }}
<li>
<a href="/post/{{ .Slug }}">{{ .Title }}</a>
— {{ .Date.Format "02.01.2006" }}
</li>
{{ end }}
</ul>
{{ end }}