package main import ( "encoding/json" "fmt" "html/template" "net/http" "os" "os/signal" "strconv" "strings" "syscall" "time" "git.send.nrw/sendnrw/b1tsblog/internal/article" ) var TickCatalog []TickEntry func SaveTickCatalog(filename string) error { file, err := os.Create(filename) if err != nil { return err } defer file.Close() encoder := json.NewEncoder(file) encoder.SetIndent("", " ") return encoder.Encode(TickCatalog) } func LoadTickCatalog(filename string) error { file, err := os.Open(filename) if err != nil { return err } defer file.Close() return json.NewDecoder(file).Decode(&TickCatalog) } func IncTick(xSlug string) error { for a, b := range TickCatalog { if b.Slug == xSlug { TickCatalog[a].Count = TickCatalog[a].Count + 1 return nil } } newEntry := TickEntry{Slug: xSlug, Count: 1} TickCatalog = append(TickCatalog, newEntry) return fmt.Errorf("") } func getTick(xSlug string) string { for _, b := range TickCatalog { if b.Slug == xSlug { var n int64 = b.Count return strconv.FormatInt(n, 10) } } return "0" } type TickEntry struct { Slug string `json:"slug"` Count int64 `json:"count"` } func getenv(k, d string) string { if v := os.Getenv(k); v != "" { return v } return d } func enabled(k string, def bool) bool { b, err := strconv.ParseBool(strings.ToLower(os.Getenv(k))) if err != nil { return def } return b } func cacheControl(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", "public, max-age=31536000, immutable") next.ServeHTTP(w, r) }) } var ( tplList *template.Template tplArticle *template.Template ) func main() { // Signal-Kanal einrichten stop := make(chan os.Signal, 1) signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) // Goroutine, die auf Signale wartet go func() { <-stop fmt.Println("Stop Sign...") prepareExit() os.Exit(0) }() // --- Verzeichnisse konfigurierbar machen ------------------------- contentDir := getenv("BLOG_CONTENT_DIR", "/content") staticDir := getenv("BLOG_STATIC_DIR", "/app/internal/web/static") pagesDir := getenv("BLOG_PAGES_DIR", "/pages") templatesDir := getenv("BLOG_TEMPLATES_DIR", "/templates") ticksDir := getenv("BLOG_TICKS_DIR", "/ticks") TickCatalog = nil if err := LoadTickCatalog(ticksDir + "/ticks.json"); err != nil { fmt.Println("Fehler beim Laden:", err) } fmt.Println("Geladener Katalog:", TickCatalog) funcs := template.FuncMap{ "now": time.Now, // jetzt‑Zeit bereitstellen } // Basislayout zuerst parsen layout := template.Must(template.New("base").Funcs(funcs).ParseFiles(templatesDir + "/base.html")) // LIST‑Seite: base + list.html tplList = template.Must(layout.Clone()) template.Must(tplList.Funcs(funcs).ParseFiles(templatesDir + "/list.html")) // ARTICLE‑Instanz tplArticle = template.Must(layout.Clone()) template.Must(tplArticle.Funcs(funcs).ParseFiles(templatesDir + "/article.html")) tplPage := template.Must(layout.Clone()) template.Must(tplPage.ParseFiles(templatesDir + "/page.html")) mux := http.NewServeMux() articles, err := article.LoadDir(contentDir) if err != nil { fmt.Println(err) } staticPages, err := article.LoadStatic(pagesDir) if err != nil { fmt.Println(err) } else { fmt.Println(staticPages) } // Handler für / mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } /* */ for a, b := range articles { articles[a].Counter = getTick(b.Slug) } /* */ if err := tplList.ExecuteTemplate(w, "layout", article.ListPage{ Title: "Startseite", Description: "Alle Artikel im Überblick", Articles: articles, }); err != nil { http.Error(w, err.Error(), 500) } }) mux.HandleFunc("/post/", func(w http.ResponseWriter, r *http.Request) { slug := strings.TrimPrefix(r.URL.Path, "/post/") for _, a := range articles { if a.Slug == slug { IncTick(slug) t := getTick(slug) a.Counter = t if err := tplArticle.ExecuteTemplate(w, "layout", a); err != nil { http.Error(w, err.Error(), 500) } return } } http.NotFound(w, r) }) mux.HandleFunc("/page/", func(w http.ResponseWriter, r *http.Request) { slug := strings.TrimPrefix(r.URL.Path, "/page/") p, ok := staticPages[slug] if !ok { http.NotFound(w, r) return } // "layout" kommt aus deinem Template‑Pool (list/article nutzen es ja auch) if err := tplPage.ExecuteTemplate(w, "page", p); err != nil { http.Error(w, err.Error(), 500) } }) mux.Handle("/static/", cacheControl(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))) StopServer(http.ListenAndServe(":8080", mux)) } func prepareExit() { fmt.Println("~", "Running exit tasks...") if err := SaveTickCatalog(getenv("BLOG_TICKS_DIR", "/ticks") + "/ticks.json"); err != nil { fmt.Println("Fehler beim Speichern:", err) } fmt.Println("Geladener Katalog:", TickCatalog) fmt.Println("~", "Exit completed.") } func StopServer(e error) { fmt.Println("~", "Stopping server...") prepareExit() fmt.Println("~", "Server stopped!") }