parent
84c1322c44
commit
7878db5c95
@ -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 |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
# Index |
||||||
|
|
||||||
|
=> /posts/index.gmi See all posts |
@ -0,0 +1,18 @@ |
|||||||
|
# mediocregopher's Posts |
||||||
|
|
||||||
|
{{ $page := .GetQueryIntValue "page" 0 -}} |
||||||
|
{{ $getPostsRes := .GetPosts $page 20 -}} |
||||||
|
|
||||||
|
{{ if gt $page 0 -}} |
||||||
|
=> /posts.gmi?page={{ .Add $page -1 }} Previous Page |
||||||
|
|
||||||
|
{{ end -}} |
||||||
|
|
||||||
|
{{ range $getPostsRes.Posts -}} |
||||||
|
=> /posts/{{ .ID }}.gmi {{ .PublishedAt.Format "2006-01-02" }} - {{ .Title }} |
||||||
|
|
||||||
|
{{ end -}} |
||||||
|
|
||||||
|
{{ if $getPostsRes.HasMore -}} |
||||||
|
=> /posts.gmi?page={{ .Add $page 1 }} Next page |
||||||
|
{{ end -}} |
@ -0,0 +1,9 @@ |
|||||||
|
{{ $post := .GetPostByID (.GetQueryValue "id" "") -}} |
||||||
|
|
||||||
|
# {{ $post.Title }} |
||||||
|
|
||||||
|
{{ if ne $post.Description "" -}} |
||||||
|
> {{ $post.Description }} |
||||||
|
{{ end -}} |
||||||
|
|
||||||
|
{{ $post.Body }} |
Loading…
Reference in new issue