diff options
| author | William Hergès <william@herges.fr> | 2025-10-27 13:32:39 +0100 |
|---|---|---|
| committer | William Hergès <william@herges.fr> | 2025-10-27 13:32:39 +0100 |
| commit | f1e008670cd865520eb5f21fe6e7b56f02076a23 (patch) | |
| tree | 33f099bced477c3e84fa60b20136354535d0c18c /backend | |
| parent | 7bd309f3ca44930c5207b94acc2d425b24d4b369 (diff) | |
feat(config): supports multiple sections
Diffstat (limited to 'backend')
| -rw-r--r-- | backend/config.go | 10 | ||||
| -rw-r--r-- | backend/home.go | 39 | ||||
| -rw-r--r-- | backend/logs.go | 201 | ||||
| -rw-r--r-- | backend/templates/components.html | 20 | ||||
| -rw-r--r-- | backend/templates/home.html | 4 | ||||
| -rw-r--r-- | backend/templates/log.html | 2 | ||||
| -rw-r--r-- | backend/templates/rss.xml | 12 |
7 files changed, 35 insertions, 253 deletions
diff --git a/backend/config.go b/backend/config.go index b59c00b..29e1397 100644 --- a/backend/config.go +++ b/backend/config.go @@ -30,7 +30,8 @@ type Config struct { DefaultImage string `toml:"default_image"` Quotes []string `toml:"quotes"` - LogFolder string `toml:"log_folder"` + Sections []Section `toml:"section"` + RootFolder string `toml:"root_folder"` PublicFolder string `toml:"public_folder"` @@ -56,7 +57,12 @@ func (c *Config) DefaultValues() { Header: "logo.jpg", Favicon: "favicon.jpg", } - c.LogFolder = "data/logs" + c.Sections = []Section{{ + Name: "logs", + Description: "Aut maxime voluptatibus ut dicta voluptates et ut alias. Sunt et incidunt similique et doloremque nostrum fugit autem. Ut omnis quo nisi. Accusantium voluptas fugit autem maiores numquam doloribus.", + Folder: "data/logs", + URI: "logs", + }} c.RootFolder = "data" c.PublicFolder = "public" c.Quotes = []string{"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do."} diff --git a/backend/home.go b/backend/home.go index b945313..2f0643f 100644 --- a/backend/home.go +++ b/backend/home.go @@ -2,25 +2,21 @@ package backend import ( "html/template" - "log/slog" "net/http" "os" "path/filepath" - "strconv" "github.com/go-chi/chi/v5" ) var ( - sortedLogs []*logData + //sortedSections = map[string][]*sectionData{} rootContent = map[string]*rootData{} ) type homeData struct { *data - Logs []*logData - PagesNumber int - CurrentPage int + Sections []*Section } func (h *homeData) SetData(d *data) { @@ -29,7 +25,7 @@ func (h *homeData) SetData(d *data) { func HandleHome(r *chi.Mux) { r.Get("/", func(w http.ResponseWriter, r *http.Request) { - d := handleGenericLogsDisplay(w, r, 3) + d := handleGenericSectionDisplay(w, r, 3) if d == nil { return } @@ -93,29 +89,16 @@ func handleGenericRoot(w http.ResponseWriter, r *http.Request, name string) { d.handleGeneric(w, r, "simple", d) } -func handleGenericLogsDisplay(w http.ResponseWriter, r *http.Request, maxLogsPerPage int) *homeData { - rawPage := r.URL.Query().Get("page") - page := 1 - if rawPage != "" { - var err error - page, err = strconv.Atoi(rawPage) - if err != nil || page < 1 { - slog.Warn("invalid page number", "rawPage", rawPage) - w.WriteHeader(http.StatusBadRequest) - return nil - } - } +func handleGenericSectionDisplay(_ http.ResponseWriter, r *http.Request, maxLogsPerPage int) *homeData { d := new(homeData) d.data = new(data) - if sortedLogs == nil { - sortLogs() - } - d.CurrentPage = page - d.PagesNumber = max(1, (len(sortedLogs)-1)/maxLogsPerPage+1) - if d.PagesNumber < page { - notFound(w, r) - return nil + cfg := r.Context().Value(configKey).(*Config) + for _, sec := range cfg.Sections { + if len(sec.Data) == 0 { + sec.sort() + } + sec.Data = sec.Data[:min(maxLogsPerPage, len(sec.Data))] + d.Sections = append(d.Sections, &sec) } - d.Logs = sortedLogs[(page-1)*maxLogsPerPage : min(page*maxLogsPerPage, len(sortedLogs))] return d } diff --git a/backend/logs.go b/backend/logs.go deleted file mode 100644 index 03d4f1f..0000000 --- a/backend/logs.go +++ /dev/null @@ -1,201 +0,0 @@ -package backend - -import ( - "fmt" - "html/template" - "log/slog" - "maps" - "net/http" - "os" - "path/filepath" - "slices" - "strings" - "sync" - "time" - - "github.com/go-chi/chi/v5" -) - -var ( - logs = map[string]*logData{} -) - -type logData struct { - *data - EntryInfo - LogTitle string - Content template.HTML - Slug string -} - -func (d *logData) SetData(dt *data) { - d.data = dt -} - -func (d *logData) PubDate() string { - return d.PubLocalDate.String() -} - -func (d *logData) PubDateRSS() string { - return d.PubLocalDate.AsTime(time.Local).Format(time.RFC1123Z) // because RFC822 in go isn't RFC822??? -} - -func (d *logData) Title() string { - return d.data.Title() -} - -type image struct { - Src string `toml:"src"` - Alt string `toml:"alt"` - Legend string `toml:"legend"` -} - -func LoadLogs(cfg *Config) bool { - dir, err := os.ReadDir(cfg.LogFolder) - if err != nil { - if !os.IsNotExist(err) { - slog.Error("reading log directory", "error", err) - return false - } - slog.Info("log directory does not exist, creating...") - err = os.MkdirAll(cfg.LogFolder, 0774) - if err != nil { - slog.Error("creating log directory", "error", err) - } - return false - } - slog.Info("checking log directory...", "path", cfg.LogFolder) - err = readLogDir(cfg.LogFolder, dir) - if err != nil { - slog.Error("reading log directory", "error", err, "path", cfg.LogFolder) - return false - } - slog.Info("all logs loaded") - return true -} - -func readLogDir(path string, dir []os.DirEntry) error { - var wg sync.WaitGroup - var mu sync.Mutex - for _, d := range dir { - p := filepath.Join(path, d.Name()) - if d.IsDir() { - dd, err := os.ReadDir(p) - if err != nil { - return err - } - if err = readLogDir(p, dd); err != nil { - return err - } - } else { - if !strings.HasSuffix(d.Name(), ".md") { - return fmt.Errorf("file %s is not a markdown file", d.Name()) - } - slug := strings.TrimSuffix(p, ".md") - _, ok := logs[slug] - if ok { - return fmt.Errorf("log already exists: %s", d.Name()) - } - dd := new(logData) - dd.data = new(data) - - wg.Add(1) - go func(p string, d os.DirEntry) { - defer wg.Done() - ok = parseLog(dd, &mu, slug, strings.TrimSuffix(d.Name(), ".md")) - if ok { - slog.Debug("log parsed", "path", p) - } else { - slog.Debug("log skipped", "path", p) - } - }(p, d) - } - } - wg.Wait() - sortLogs() - return nil -} - -func HandleLogs(r *chi.Mux) { - r.Get("/logs", handleLogList) - r.Route("/logs", func(r chi.Router) { - r.Get("/", handleLogList) - - r.Get("/rss", handleLogRSS) - r.Get("/rss/", handleLogRSS) - - r.Get("/{slug:[a-zA-Z0-9-]+}", handleLog) - r.Get("/{slug:[a-zA-Z0-9-]+}/", handleLog) - }) -} - -func handleLogList(w http.ResponseWriter, r *http.Request) { - d := handleGenericLogsDisplay(w, r, 5) - if d == nil { - return - } - d.title = "logs" - d.handleGeneric(w, r, "home_log", d) -} - -func handleLogRSS(w http.ResponseWriter, r *http.Request) { - d := handleGenericLogsDisplay(w, r, 5) - if d == nil { - return - } - d.title = "logs" - d.handleRSS(w, r, d) -} - -func handleLog(w http.ResponseWriter, r *http.Request) { - cfg := r.Context().Value(configKey).(*Config) - slug := chi.URLParam(r, "slug") - path := filepath.Join(cfg.LogFolder, slug) - d, ok := logs[path] - if !ok { - d = new(logData) - d.data = new(data) - if ok = parseLog(d, new(sync.Mutex), path, slug); !ok { - notFound(w, r) - return - } - } - d.handleGeneric(w, r, "log", d) -} - -func parseLog(d *logData, mu *sync.Mutex, path, slug string) bool { - d.Article = true - d.title = slug - d.Slug = slug - b, err := os.ReadFile(path + ".md") - if err != nil { - if os.IsNotExist(err) { - return false - } - panic(err) - } - var ok bool - d.Content, ok = parse(b, &d.EntryInfo, d.data) - if !ok { - return false - } - d.LogTitle = d.EntryInfo.Title - mu.Lock() - logs[path] = d - mu.Unlock() - return true -} - -func sortLogs() { - sortedLogs = slices.SortedFunc(maps.Values(logs), func(l *logData, l2 *logData) int { - lt := l.PubLocalDate.AsTime(time.UTC) - l2t := l2.PubLocalDate.AsTime(time.UTC) - // we want it reversed - if lt.Before(l2t) { - return 1 - } else if lt.After(l2t) { - return -1 - } - return 0 - }) -} diff --git a/backend/templates/components.html b/backend/templates/components.html index da133a6..9792784 100644 --- a/backend/templates/components.html +++ b/backend/templates/components.html @@ -1,26 +1,16 @@ {{define "logs_display"}} <article> - {{ range .Logs }} + {{ $uri := .URI }} + {{ range .Data }} <article> - <h2><a href="/logs/{{ .Slug }}">{{ .LogTitle }}</a></h2> + <h2><a href="/{{ $uri }}/{{ .Slug }}">{{ .DataTitle }}</a></h2> <figure> - <a href="/logs/{{ .Slug }}"><img src="{{ static .Img.Src }}" alt="{{ .Img.Alt }}" /></a> + <a href="/{{ $uri }}/{{ .Slug }}"><img src="{{ static .Img.Src }}" alt="{{ .Img.Alt }}" /></a> <figcaption>{{ .Img.Legend }}</figcaption> </figure> <p>{{ .Description }}</p> </article> - {{ end }} {{ if ne .PagesNumber 1 }} - <div class="pagination"> - <nav> - {{ if ne .CurrentPage 1 }}<a href="?page={{ before .CurrentPage }}">Précédent</a>{{else}} - <p></p> - {{end}} - <p>{{ .CurrentPage }}/{{ .PagesNumber }}</p> - {{ if ne .CurrentPage .PagesNumber }}<a href="?page={{ next .CurrentPage }}">Suivant</a>{{else}} - <p></p> - {{end}} - </nav> - </div> {{ end }} + <a href="/{{ $uri }}/">Voir plus</a> </article> {{end}} diff --git a/backend/templates/home.html b/backend/templates/home.html index 7a76e48..ce4f4e8 100644 --- a/backend/templates/home.html +++ b/backend/templates/home.html @@ -4,6 +4,10 @@ <h1>logs</h1> <p>{{ .PageDescription }}</p> </div> + {{ range .Sections }} + <div> {{ template "logs_display" . }} + </div> + {{ end }} </main> {{end}} diff --git a/backend/templates/log.html b/backend/templates/log.html index 75e0a42..a384459 100644 --- a/backend/templates/log.html +++ b/backend/templates/log.html @@ -1,6 +1,6 @@ {{define "body"}} <article id="content"> - <h1>{{ .LogTitle }}</h1> + <h1>{{ .DataTitle }}</h1> <p>{{ .Description }}</p> <figure> <img src="{{ static .Img.Src }}" alt="{{ .Img.Alt }}" class="large" /> diff --git a/backend/templates/rss.xml b/backend/templates/rss.xml index c8390da..6d99272 100644 --- a/backend/templates/rss.xml +++ b/backend/templates/rss.xml @@ -3,18 +3,18 @@ <channel> <atom:link href="https://{{ .Domain }}{{ .URL }}" rel="self" type="application/rss+xml" /> <title>{{ .Title }}</title> - <link>https://{{ .Domain }}/logs/</link> + <link>https://{{ .Domain }}/{{ .URI }}/</link> <description>{{ .PageDescription }}</description> <language>fr-fr</language> {{ $domain := .Domain }} - {{ range .Logs }} + {{ range .Section.Data }} <item> - <title>{{ .LogTitle }}</title> - <link>https://{{ $domain }}/logs/{{ .Slug }}</link> - <guid>https://{{ $domain }}/logs/{{ .Slug }}</guid> + <title>{{ .DataTitle }}</title> + <link>https://{{ $domain }}/{{ .URI }}/{{ .Slug }}</link> + <guid>https://{{ $domain }}/{{ .URI }}/{{ .Slug }}</guid> <description>{{ .Description }}</description> <pubDate>{{ .PubDateRSS }}</pubDate> </item> {{ end }} </channel> -</rss>
\ No newline at end of file +</rss> |
