feat: caching headers
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package images
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
)
|
||||
|
||||
func UIDFromPath(path string) (uid string, err error) {
|
||||
var b bytes.Buffer
|
||||
// NewWriter with NoDict (nil) creates a raw DEFLATE compressor
|
||||
zw, _ := flate.NewWriter(&b, flate.BestCompression)
|
||||
|
||||
_, err = zw.Write([]byte(path))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = zw.Close() // Essential to flush the final bits
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return base64.RawURLEncoding.EncodeToString(b.Bytes()), nil
|
||||
}
|
||||
|
||||
func PathFromUID(uid string) (string, error) {
|
||||
// 1. Decode the Base64 string back to compressed bytes
|
||||
compressedBytes, err := base64.RawURLEncoding.DecodeString(uid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 2. Create a flate reader (NoDict/nil since you used nil in the writer)
|
||||
// flate.NewReader returns an io.ReadCloser
|
||||
reader := flate.NewReader(bytes.NewReader(compressedBytes))
|
||||
defer reader.Close()
|
||||
|
||||
// 3. Read the decompressed data into a buffer
|
||||
var out bytes.Buffer
|
||||
_, err = io.Copy(&out, reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return out.String(), nil
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package page
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PageHeader struct {
|
||||
UID string
|
||||
@@ -13,29 +17,41 @@ type Page struct {
|
||||
Content string
|
||||
}
|
||||
|
||||
func (s *Service) GetPages() (pages []PageHeader, err error) {
|
||||
pages = []PageHeader{
|
||||
{
|
||||
UID: "asdf",
|
||||
Title: "asdfAasdf!",
|
||||
CoverImageUID: "500-1000",
|
||||
},
|
||||
func (s *Service) GetPages(ctx context.Context) (pages []PageHeader, err error) {
|
||||
paths, err := s.fs.ReadDir("/")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
if strings.HasPrefix(path.Name(), ".") || // hidden files
|
||||
strings.HasPrefix(path.Name(), "_") { // hidden pages
|
||||
continue
|
||||
}
|
||||
page, err := s.getPageHeaders(ctx, path.Name())
|
||||
if err != nil { // just ignore missing pages
|
||||
slog.Error("could not open path", "path", path.Name(), "err", err)
|
||||
continue
|
||||
}
|
||||
pages = append(pages, page)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Service) GetPage(ctx context.Context, uid string) (page Page, err error) {
|
||||
p, err := s.getPageHeaders(ctx, uid)
|
||||
slog.Debug("got page", "page", p, "err", err)
|
||||
content := `
|
||||
<h1>LOL</h1>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
||||
<img src="https://picsum.photos/500/1000">
|
||||
<img fetchpriority="low" loading="lazy" src="https://picsum.photos/500/1000">
|
||||
<h2>LOL</h2>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
||||
|
||||
<img src="https://picsum.photos/600/500">
|
||||
<img fetchpriority="low" loading="lazy" src="https://picsum.photos/600/500">
|
||||
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
||||
@@ -43,6 +59,25 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel n
|
||||
<h3>LOL</h3>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
||||
<h2>LOL</h2>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
||||
|
||||
<img fetchpriority="low" loading="lazy" src="https://picsum.photos/601/500">
|
||||
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
||||
|
||||
<h2>LOL</h2>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
||||
|
||||
<img fetchpriority="low" loading="lazy" src="https://picsum.photos/500/500">
|
||||
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis ligula vel nisl pellentesque aliquet. Maecenas eu congue tortor. Nunc mattis ligula vel augue viverra cursus. Suspendisse potenti. Vivamus in risus vitae mi molestie feugiat. Cras sapien tortor, elementum ut tempus vel, commodo at dui. In hac habitasse platea dictumst. Integer nec ullamcorper purus, tincidunt elementum felis. Duis hendrerit tempor vestibulum. Nunc ut finibus tellus. Donec eu lacus sit amet sem suscipit imperdiet. Quisque cursus vulputate sodales. In sit amet nisl a est lacinia sagittis. </p>
|
||||
|
||||
|
||||
`
|
||||
page = Page{
|
||||
PageHeader: PageHeader{
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package page
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"git.schreifuchs.ch/schreifuchs/schreifuchs.ch/internal/components/images"
|
||||
"git.schreifuchs.ch/schreifuchs/schreifuchs.ch/internal/components/translate"
|
||||
"github.com/studio-b12/gowebdav"
|
||||
)
|
||||
|
||||
var titleRGX = regexp.MustCompile(`(?m)^#\s+(.+)$`)
|
||||
|
||||
var (
|
||||
ErrMalFormedInput = errors.New("mal formed input")
|
||||
ErrNotFound = errors.New("not found")
|
||||
)
|
||||
|
||||
func (s *Service) getPageHeaders(ctx context.Context, uid string) (page PageHeader, err error) {
|
||||
info, err := s.fs.ReadDir(uid)
|
||||
if gowebdav.IsErrNotFound(err) { // try private
|
||||
err = ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
lang := translate.Language(ctx)
|
||||
|
||||
var coverImage *gowebdav.File
|
||||
var contentMD *gowebdav.File
|
||||
|
||||
for _, f := range info {
|
||||
file := f.(gowebdav.File)
|
||||
|
||||
if strings.HasPrefix(file.ContentType(), "image/") {
|
||||
coverImage = &file
|
||||
}
|
||||
|
||||
parts := strings.Split(file.Name(), ".")
|
||||
|
||||
// fallback content
|
||||
if contentMD == nil && len(parts) == 2 && parts[1] == "md" {
|
||||
contentMD = &file
|
||||
}
|
||||
// language content
|
||||
if len(parts) == 3 && parts[1] == lang && parts[2] == "md" {
|
||||
contentMD = &file
|
||||
}
|
||||
}
|
||||
|
||||
slog.Debug("loaded page infos", "cover", coverImage, "content", contentMD)
|
||||
|
||||
content, err := s.fs.Read(contentMD.Path())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
title := titleRGX.FindStringSubmatch(string(content))
|
||||
if len(title) < 2 {
|
||||
err = fmt.Errorf("%w: no matches for title", ErrMalFormedInput)
|
||||
return
|
||||
}
|
||||
|
||||
page.UID = uid
|
||||
page.Title = title[1]
|
||||
page.CoverImageUID, err = images.UIDFromPath(coverImage.Path())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getImageUID(path string) string {
|
||||
var b bytes.Buffer
|
||||
// NewWriter with NoDict (nil) creates a raw DEFLATE compressor
|
||||
zw, _ := flate.NewWriter(&b, flate.BestCompression)
|
||||
|
||||
zw.Write([]byte(path))
|
||||
zw.Close() // Essential to flush the final bits
|
||||
|
||||
return base64.RawURLEncoding.EncodeToString(b.Bytes())
|
||||
}
|
||||
@@ -1,7 +1,23 @@
|
||||
package page
|
||||
|
||||
type Service struct{}
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
func New() *Service {
|
||||
return &Service{}
|
||||
"github.com/studio-b12/gowebdav"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
fs *gowebdav.Client
|
||||
}
|
||||
|
||||
func New(fs *gowebdav.Client) *Service {
|
||||
info, err := fs.ReadDir("/")
|
||||
if err != nil {
|
||||
slog.Error("could not read dir", "err", err)
|
||||
}
|
||||
|
||||
slog.Info("file info", "info", info)
|
||||
return &Service{
|
||||
fs: fs,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user