From 7878db5c95e5eb430b7b878fe6eb8084f15441a7 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Sat, 21 Jan 2023 16:01:52 +0100 Subject: Initial implementation of post rendering over gmi --- src/gmi/tpl.go | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/gmi/tpl.go (limited to 'src/gmi/tpl.go') diff --git a/src/gmi/tpl.go b/src/gmi/tpl.go new file mode 100644 index 0000000..7aa1a2f --- /dev/null +++ b/src/gmi/tpl.go @@ -0,0 +1,134 @@ +package gmi + +import ( + "bytes" + "context" + "embed" + "fmt" + "io" + "io/fs" + "net/url" + "strconv" + "strings" + "text/template" + + "git.sr.ht/~adnano/go-gemini" + "github.com/mediocregopher/blog.mediocregopher.com/srv/post" + "github.com/mediocregopher/mediocre-go-lib/v2/mctx" +) + +//go:embed tpl +var tplFS embed.FS + +type rendererGetPostsRes struct { + Posts []post.StoredPost + HasMore bool +} + +type renderer struct { + url *url.URL + postStore post.Store +} + +func (r renderer) GetPosts(page, count int) (rendererGetPostsRes, error) { + posts, hasMore, err := r.postStore.Get(page, count) + return rendererGetPostsRes{posts, hasMore}, err +} + +func (r renderer) GetPostByID(id string) (post.StoredPost, error) { + p, err := r.postStore.GetByID(id) + if err != nil { + return post.StoredPost{}, fmt.Errorf("fetching post %q: %w", id, err) + } + return p, nil +} + +func (r renderer) GetQueryValue(key, def string) string { + v := r.url.Query().Get(key) + if v == "" { + v = def + } + return v +} + +func (r renderer) GetQueryIntValue(key string, def int) (int, error) { + vStr := r.GetQueryValue(key, strconv.Itoa(def)) + return strconv.Atoi(vStr) +} + +func (r renderer) Add(a, b int) int { return a + b } + +func (a *api) tplHandler() (gemini.Handler, error) { + + allTpls := template.New("") + + err := fs.WalkDir(tplFS, "tpl", func(path string, d fs.DirEntry, err error) error { + + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + + body, err := fs.ReadFile(tplFS, path) + if err != nil { + panic(err) + } + + name := strings.TrimPrefix(path, "tpl/") + + allTpls, err = allTpls.New(name).Parse(string(body)) + if err != nil { + return fmt.Errorf("parsing %q as template: %w", path, err) + } + + return nil + }) + + if err != nil { + return nil, fmt.Errorf("parsing templates: %w", err) + } + + return gemini.HandlerFunc(func( + ctx context.Context, + rw gemini.ResponseWriter, + r *gemini.Request, + ) { + + if strings.HasSuffix(r.URL.Path, "/") { + r.URL.Path += "index.gmi" + } + + tplPath := strings.TrimPrefix(r.URL.Path, "/") + + ctx = mctx.Annotate(ctx, + "url", r.URL, + "tplPath", tplPath, + ) + + tpl := allTpls.Lookup(tplPath) + + if tpl == nil { + a.params.Logger.WarnString(ctx, "page not found") + rw.WriteHeader(gemini.StatusNotFound, "Page not found, sorry!") + return + } + + buf := new(bytes.Buffer) + + err := tpl.Execute(buf, renderer{ + url: r.URL, + postStore: a.params.PostStore, + }) + + if err != nil { + a.params.Logger.Error(ctx, "rendering error", err) + rw.WriteHeader(gemini.StatusTemporaryFailure, err.Error()) + return + } + + io.Copy(rw, buf) + }), nil +} -- cgit v1.2.3