diff options
| author | Anhgelus Morhtuuzh <william@herges.fr> | 2025-10-27 16:32:58 +0000 |
|---|---|---|
| committer | Anhgelus Morhtuuzh <william@herges.fr> | 2025-10-27 16:32:58 +0000 |
| commit | db0b5c34432b4c0135af8c5c885fd6ad348c3691 (patch) | |
| tree | 8f38b973a8dd3fc22bbd28498004940451621b41 | |
| parent | 7bd309f3ca44930c5207b94acc2d425b24d4b369 (diff) | |
| parent | 1e2ad3a8f8cd2c12786b92210616325a33d1b209 (diff) | |
Merge pull request '[Feat] Custom sections' (#1) from feat/custom-sections into main
Reviewed-on: https://git.anhgelus.world/anhgelus/small-web/pulls/1
| -rw-r--r-- | backend/config.go | 11 | ||||
| -rw-r--r-- | backend/data.go | 22 | ||||
| -rw-r--r-- | backend/home.go | 65 | ||||
| -rw-r--r-- | backend/logs.go | 201 | ||||
| -rw-r--r-- | backend/router.go | 2 | ||||
| -rw-r--r-- | backend/section.go | 277 | ||||
| -rw-r--r-- | backend/templates/base.html | 2 | ||||
| -rw-r--r-- | backend/templates/components.html | 15 | ||||
| -rw-r--r-- | backend/templates/data.html (renamed from backend/templates/log.html) | 2 | ||||
| -rw-r--r-- | backend/templates/home.html | 12 | ||||
| -rw-r--r-- | backend/templates/home_log.html | 3 | ||||
| -rw-r--r-- | backend/templates/home_section.html | 10 | ||||
| -rw-r--r-- | backend/templates/rss.xml | 16 | ||||
| -rw-r--r-- | frontend/scss/home.scss | 6 | ||||
| -rw-r--r-- | main.go | 12 |
15 files changed, 401 insertions, 255 deletions
diff --git a/backend/config.go b/backend/config.go index b59c00b..cd24592 100644 --- a/backend/config.go +++ b/backend/config.go @@ -29,8 +29,10 @@ type Config struct { Description string `toml:"description"` DefaultImage string `toml:"default_image"` Quotes []string `toml:"quotes"` + Language string `toml:"language"` + + Sections []Section `toml:"section"` - LogFolder string `toml:"log_folder"` RootFolder string `toml:"root_folder"` PublicFolder string `toml:"public_folder"` @@ -56,7 +58,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/data.go b/backend/data.go index 6ca6020..809e6e6 100644 --- a/backend/data.go +++ b/backend/data.go @@ -36,6 +36,7 @@ type data struct { Links []Link Logo *Logo Quote string + Language string } func (d *data) SetData(data *data) { @@ -74,6 +75,9 @@ func (d *data) merge(cfg *Config, r *http.Request) { } d.URL = r.URL.Path } + if d.Language == "" { + d.Language = cfg.Language + } } func (d *data) handleGeneric(w http.ResponseWriter, r *http.Request, name string, custom dataUsable) { @@ -92,6 +96,7 @@ func (d *data) handleGeneric(w http.ResponseWriter, r *http.Request, name string }, "next": func(i int) int { return i + 1 }, "before": func(i int) int { return i - 1 }, + "first": templateFirst, }).ParseFS(templates, "templates/components.html", fmt.Sprintf("templates/%s.html", name), "templates/base.html") if err != nil { panic(err) @@ -116,7 +121,15 @@ func (d *data) handleGeneric(w http.ResponseWriter, r *http.Request, name string func (d *data) handleRSS(w http.ResponseWriter, r *http.Request, custom dataUsable) { cfg := r.Context().Value(configKey).(*Config) d.merge(cfg, r) - t, err := txt.ParseFS(templates, "templates/rss.xml") + t, err := txt.New("").Funcs(txt.FuncMap{ + "first": templateFirst, + "uri": func(s string) string { + if s == "" { + return "" + } + return s + "/" + }, + }).ParseFS(templates, "templates/rss.xml") if err != nil { panic(err) } @@ -197,3 +210,10 @@ func getAsset(ctx context.Context, path string) *assetData { assets[path] = asset return asset } + +func templateFirst(a []*Section) *Section { + if len(a) == 0 { + return nil + } + return a[0] +} diff --git a/backend/home.go b/backend/home.go index b945313..8f274e8 100644 --- a/backend/home.go +++ b/backend/home.go @@ -2,25 +2,22 @@ package backend import ( "html/template" - "log/slog" + "iter" "net/http" "os" "path/filepath" - "strconv" + "slices" "github.com/go-chi/chi/v5" ) var ( - sortedLogs []*logData rootContent = map[string]*rootData{} ) type homeData struct { *data - Logs []*logData - PagesNumber int - CurrentPage int + Sections []*Section } func (h *homeData) SetData(d *data) { @@ -29,7 +26,8 @@ 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) + cfg := r.Context().Value(configKey).(*Config) + d := handleGenericSectionDisplay(w, r, cfg.Sections, 3) if d == nil { return } @@ -62,6 +60,8 @@ func HandleRoot(r *chi.Mux, cfg *Config) { if err != nil && !os.IsExist(err) { panic(err) } + r.Get("/rss", handleGenericRSS) + r.Get("/rss/", handleGenericRSS) r.Get("/{name:[a-zA-Z-]+}", func(w http.ResponseWriter, r *http.Request) { handleGenericRoot(w, r, chi.URLParam(r, "name")) }) @@ -93,29 +93,42 @@ 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 handleGenericRSS(w http.ResponseWriter, r *http.Request) { + cfg := r.Context().Value(configKey).(*Config) + var data iter.Seq[*sectionData] + for _, sec := range cfg.Sections { + if len(sec.Data) == 0 { + sec.sort() + } + var sl []*sectionData + for _, d := range sec.Data[:min(3, len(sec.Data))] { + dd := *d + dd.Slug = sec.URI + "/" + dd.Slug + sl = append(sl, &dd) + } + if data == nil { + data = slices.Values(sl) + } else { + data = slices.Values(slices.AppendSeq(sl, data)) } } + var s Section + s.Data = sort(data) + s.Name = cfg.Name + s.Description = cfg.Description + s.URI = "" + s.handleRSS(w, r) +} + +func handleGenericSectionDisplay(_ http.ResponseWriter, _ *http.Request, sections []Section, 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 + for _, sec := range 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/router.go b/backend/router.go index 59a02e3..fd72711 100644 --- a/backend/router.go +++ b/backend/router.go @@ -17,7 +17,7 @@ import ( ) const ( - Version = "0.3.0" + Version = "0.4.0" configKey = "config" isUpdateKey = "is_update" assetsFSKey = "assets_fs" diff --git a/backend/section.go b/backend/section.go new file mode 100644 index 0000000..a22d286 --- /dev/null +++ b/backend/section.go @@ -0,0 +1,277 @@ +package backend + +import ( + "fmt" + "html/template" + "iter" + "log/slog" + "maps" + "net/http" + "os" + "path/filepath" + "slices" + "strconv" + "strings" + "sync" + "time" + + "github.com/go-chi/chi/v5" +) + +var ( + sections = map[string]map[string]*sectionData{} +) + +type Section struct { + Name string `toml:"name"` + Folder string `toml:"folder"` + Description string `toml:"description"` + URI string `toml:"uri"` + Data []*sectionData `toml:"-"` + Paginate bool `toml:"-"` + PagesNumber int `toml:"-"` + CurrentPage int `toml:"-"` +} + +type sectionData struct { + *data + EntryInfo + DataTitle string + Content template.HTML + Slug string +} + +func (d *sectionData) SetData(dt *data) { + d.data = dt +} + +func (d *sectionData) PubDate() string { + return d.PubLocalDate.String() +} + +func (d *sectionData) PubDateRSS() string { + return d.PubLocalDate.AsTime(time.Local).Format(time.RFC1123Z) // because RFC822 in go isn't RFC822??? +} + +func (d *sectionData) Title() string { + return d.data.Title() +} + +type image struct { + Src string `toml:"src"` + Alt string `toml:"alt"` + Legend string `toml:"legend"` +} + +func (s *Section) Load(_ *Config) bool { + dir, err := os.ReadDir(s.Folder) + logger := slog.With("folder", s.Folder) + if err != nil { + if !os.IsNotExist(err) { + logger.Error("reading directory", "error", err) + return false + } + logger.Info("log directory does not exist, creating...") + err = os.MkdirAll(s.Folder, 0774) + if err != nil { + slog.Error("creating directory", "error", err) + } + return false + } + logger.Info("checking directory...") + err = s.readDir(s.Folder, dir) + if err != nil { + slog.Error("reading directory", "error", err) + return false + } + logger.Info("all data loaded") + return true +} + +func (s *Section) readDir(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 = s.readDir(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") + sec, ok := sections[s.Name] + if !ok { + sec = make(map[string]*sectionData, 2) + sections[s.Name] = sec + } + _, ok = sec[slug] + if ok { + return fmt.Errorf("data already exists: %s", d.Name()) + } + dd := new(sectionData) + dd.data = new(data) + + wg.Add(1) + go func(p string, d os.DirEntry) { + defer wg.Done() + ok = s.parse(dd, &mu, slug, strings.TrimSuffix(d.Name(), ".md")) + if ok { + slog.Debug("data parsed", "path", p) + } else { + slog.Debug("data skipped", "path", p) + } + }(p, d) + } + } + wg.Wait() + s.sort() + return nil +} + +func (s *Section) Handle(r *chi.Mux) { + base := "/" + s.URI + r.Get(base, s.handleList) + r.Route(base, func(r chi.Router) { + r.Get("/", s.handleList) + + r.Get("/rss", s.handleRSS) + r.Get("/rss/", s.handleRSS) + + r.Get("/{slug:[a-zA-Z0-9-]+}", s.handleOne) + r.Get("/{slug:[a-zA-Z0-9-]+}/", s.handleOne) + }) +} + +func (s *Section) handleList(w http.ResponseWriter, r *http.Request) { + p := s.handlePagination(w, r, 5) + if p == nil { + return + } + d := new(homeData) + d.data = new(data) + d.title = s.Name + sec := *s + sec.Data = sec.Data[p.Start:p.End] + sec.Paginate = true + sec.CurrentPage = p.Current + sec.PagesNumber = p.Max + d.Sections = append(d.Sections, &sec) + d.PageDescription = sec.Description + d.handleGeneric(w, r, "home_section", d) +} + +func (s *Section) handleRSS(w http.ResponseWriter, r *http.Request) { + d := handleGenericSectionDisplay(w, r, []Section{*s}, 5) + if d == nil { + return + } + d.title = s.Name + d.PageDescription = s.Description + d.handleRSS(w, r, d) +} + +func (s *Section) handleOne(w http.ResponseWriter, r *http.Request) { + slug := chi.URLParam(r, "slug") + path := filepath.Join(s.Folder, slug) + sec, ok := sections[s.Name] + var d *sectionData + if ok { + d, ok = sec[path] + } + if !ok { + d = new(sectionData) + d.data = new(data) + if ok = s.parse(d, new(sync.Mutex), path, slug); !ok { + notFound(w, r) + return + } + } + d.handleGeneric(w, r, "data", d) +} + +func (s *Section) parse(d *sectionData, mu *sync.Mutex, path, slug string) bool { + d.Article = true + d.DataTitle = 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.DataTitle = d.EntryInfo.Title + mu.Lock() + sec, ok := sections[s.Name] + if !ok { + sec = make(map[string]*sectionData, 2) + sections[s.Name] = sec + } + sec[path] = d + mu.Unlock() + return true +} + +func (s *Section) sort() { + s.Data = sort(maps.Values(sections[s.Name])) +} + +func sort(values iter.Seq[*sectionData]) []*sectionData { + return slices.SortedFunc(values, func(l *sectionData, l2 *sectionData) 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 + }) +} + +type pagination struct { + Current int + Max int + Start int + End int +} + +func (s *Section) handlePagination(w http.ResponseWriter, r *http.Request, maxLogsPerPage int) *pagination { + 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 + } + } + if len(s.Data) == 0 { + s.sort() + } + p := new(pagination) + p.Current = page + p.Max = max(1, (len(s.Data)-1)/maxLogsPerPage+1) + if p.Max < page { + notFound(w, r) + return nil + } + p.Start = (page - 1) * maxLogsPerPage + p.End = min(page*maxLogsPerPage, len(s.Data)) + return p +} diff --git a/backend/templates/base.html b/backend/templates/base.html index 0a54096..436a1c2 100644 --- a/backend/templates/base.html +++ b/backend/templates/base.html @@ -1,5 +1,5 @@ <!doctype html> -<html lang="fr" prefix="og: https://ogp.me/ns/article#"> +<html lang="{{ .Language }}" prefix="og: https://ogp.me/ns/article#"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> diff --git a/backend/templates/components.html b/backend/templates/components.html index da133a6..5365445 100644 --- a/backend/templates/components.html +++ b/backend/templates/components.html @@ -1,16 +1,17 @@ -{{define "logs_display"}} +{{define "section_display"}} <article> - {{ range .Logs }} + {{ $uri := .URI }} {{ range .Data }} <article> - <h2><a href="/logs/{{ .Slug }}">{{ .LogTitle }}</a></h2> + <h3><a href="/{{ $uri }}/{{ .Slug }}">{{ .DataTitle }}</a></h3> <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 }} + {{ end }} <div class="pagination"> + {{ if .Paginate }} <nav> {{ if ne .CurrentPage 1 }}<a href="?page={{ before .CurrentPage }}">Précédent</a>{{else}} <p></p> @@ -20,7 +21,9 @@ <p></p> {{end}} </nav> + {{ else }} + <a href="/{{ $uri }}/">Voir plus</a> + {{ end }} </div> - {{ end }} </article> {{end}} diff --git a/backend/templates/log.html b/backend/templates/data.html index 75e0a42..a384459 100644 --- a/backend/templates/log.html +++ b/backend/templates/data.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/home.html b/backend/templates/home.html index 7a76e48..d574525 100644 --- a/backend/templates/home.html +++ b/backend/templates/home.html @@ -1,9 +1,17 @@ {{define "body"}} <main id="content"> <div class="introduction"> - <h1>logs</h1> + <h1>{{ .Name }}</h1> <p>{{ .PageDescription }}</p> </div> - {{ template "logs_display" . }} + <div class="sections"> + {{ range .Sections }} + <section> + <h2>{{ .Name }}</h2> + <p>{{ .Description }}</p> + {{ template "section_display" . }} + </section> + {{ end }} + </div> </main> {{end}} diff --git a/backend/templates/home_log.html b/backend/templates/home_log.html deleted file mode 100644 index 485661c..0000000 --- a/backend/templates/home_log.html +++ /dev/null @@ -1,3 +0,0 @@ -{{define "body"}} -<main id="content">{{ template "logs_display" . }}</main> -{{end}} diff --git a/backend/templates/home_section.html b/backend/templates/home_section.html new file mode 100644 index 0000000..cf7595d --- /dev/null +++ b/backend/templates/home_section.html @@ -0,0 +1,10 @@ +{{define "body"}} +<main id="content"> + {{ range .Sections }} + <div class="introduction"> + <h1>{{ .Name }}</h1> + <p>{{ .Description }}</p> + </div> + {{ template "section_display" . }} {{ end }} +</main> +{{end}} diff --git a/backend/templates/rss.xml b/backend/templates/rss.xml index c8390da..039a2f7 100644 --- a/backend/templates/rss.xml +++ b/backend/templates/rss.xml @@ -3,18 +3,20 @@ <channel> <atom:link href="https://{{ .Domain }}{{ .URL }}" rel="self" type="application/rss+xml" /> <title>{{ .Title }}</title> - <link>https://{{ .Domain }}/logs/</link> + {{ $sec := first .Sections }} + {{ $uri := uri $sec.URI }} + <link>https://{{ .Domain }}/{{ $uri }}</link> <description>{{ .PageDescription }}</description> - <language>fr-fr</language> + <language>{{ .Language }}</language> {{ $domain := .Domain }} - {{ range .Logs }} + {{ range $sec.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> diff --git a/frontend/scss/home.scss b/frontend/scss/home.scss index 6fa4192..075896e 100644 --- a/frontend/scss/home.scss +++ b/frontend/scss/home.scss @@ -2,6 +2,12 @@ margin: var(--margin-header) 0; } +.sections { + display: flex; + flex-direction: column; + gap: calc(1.5 * var(--margin-header)); +} + article article { margin-bottom: var(--margin-header); &:last-child { @@ -58,9 +58,11 @@ func main() { os.Exit(1) } - if ok = backend.LoadLogs(cfg); !ok { - slog.Info("exiting") - os.Exit(2) + for _, sec := range cfg.Sections { + if ok = sec.Load(cfg); !ok { + slog.Info("exiting") + os.Exit(2) + } } assetsFS := backend.UsableEmbedFS("dist", embeds) @@ -72,7 +74,9 @@ func main() { backend.HandleHome(r) backend.HandleRoot(r, cfg) - backend.HandleLogs(r) + for _, sec := range cfg.Sections { + sec.Handle(r) + } backend.Handle404(r) backend.HandleStaticFiles(r, "/assets", assetsFS) |
