feat: OpenGraph metadata + auto-generated OG card images
- Per-post OG tags (title, description, image, date) in template head - Twitter Card support (summary_large_image) - Auto-generate 1200x630 card images from embedded base PNG - Title, description, domain, date drawn on image via fogleman/gg - Fonts embedded via go:embed (OpenSans Bold + Regular) - Optional og-image: and og-description: front matter overrides - Layout constants in ogimage.go for easy repositioning - Generated images cached in content/og/ (gitignored) 👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta Code <noreply@letta.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -13,6 +14,25 @@ const pageTpl = `<!DOCTYPE html>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{.Title}}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
{{if .Post.HTML}}
|
||||
<meta property="og:title" content="{{.Post.Title}}">
|
||||
<meta property="og:description" content="{{.Post.Description}}">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:url" content="https://chaosmith.systems/blog/posts/{{.Post.Slug}}">
|
||||
<meta property="og:image" content="https://chaosmith.systems{{.Post.OGImage}}">
|
||||
<meta property="og:site_name" content="Chaosmith Systems">
|
||||
<meta property="article:published_time" content="{{.Post.Date.Format "2006-01-02T15:04:05Z07:00"}}">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="{{.Post.Title}}">
|
||||
<meta name="twitter:description" content="{{.Post.Description}}">
|
||||
<meta name="twitter:image" content="https://chaosmith.systems{{.Post.OGImage}}">
|
||||
{{else}}
|
||||
<meta property="og:title" content="{{.Title}}">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://chaosmith.systems/blog/">
|
||||
<meta property="og:site_name" content="Chaosmith Systems">
|
||||
<meta name="twitter:card" content="summary">
|
||||
{{end}}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Inter:wght@300;500;700&display=swap" rel="stylesheet">
|
||||
@@ -176,10 +196,14 @@ func render(w http.ResponseWriter, data pageData) {
|
||||
}
|
||||
|
||||
func RegisterRoutes(mux *http.ServeMux, contentDir string) error {
|
||||
ogDir := filepath.Join(contentDir, "..", "og")
|
||||
store := NewStore()
|
||||
if err := store.Load(contentDir); err != nil {
|
||||
log.Printf("blog: warning: could not load posts from %s: %v", contentDir, err)
|
||||
}
|
||||
store.GenerateOGImages(ogDir)
|
||||
|
||||
mux.Handle("/blog/og/", http.StripPrefix("/blog/og/", http.FileServer(http.Dir(ogDir))))
|
||||
|
||||
mux.HandleFunc("/blog/admin/reload", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
@@ -190,6 +214,7 @@ func RegisterRoutes(mux *http.ServeMux, contentDir string) error {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
store.GenerateOGImages(ogDir)
|
||||
w.Write([]byte("reloaded\n"))
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user