aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
authorWilliam Hergès <william@herges.fr>2025-10-27 13:32:39 +0100
committerWilliam Hergès <william@herges.fr>2025-10-27 13:32:39 +0100
commitf1e008670cd865520eb5f21fe6e7b56f02076a23 (patch)
tree33f099bced477c3e84fa60b20136354535d0c18c /backend
parent7bd309f3ca44930c5207b94acc2d425b24d4b369 (diff)
feat(config): supports multiple sections
Diffstat (limited to 'backend')
-rw-r--r--backend/config.go10
-rw-r--r--backend/home.go39
-rw-r--r--backend/logs.go201
-rw-r--r--backend/templates/components.html20
-rw-r--r--backend/templates/home.html4
-rw-r--r--backend/templates/log.html2
-rw-r--r--backend/templates/rss.xml12
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>