summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBrian Picciano <mediocregopher@gmail.com>2023-01-21 16:01:52 +0100
committerBrian Picciano <mediocregopher@gmail.com>2023-01-21 16:01:52 +0100
commit7878db5c95e5eb430b7b878fe6eb8084f15441a7 (patch)
treeba440b7d7ee8c67aa40fe5d0a0b5331b3d693312 /src
parent84c1322c44c68c19e88f9695ded286b26d1621aa (diff)
Initial implementation of post rendering over gmi
Diffstat (limited to 'src')
-rw-r--r--src/cmd/mediocre-blog/main.go2
-rw-r--r--src/gmi/gmi.go55
-rw-r--r--src/gmi/tpl.go134
-rw-r--r--src/gmi/tpl/index.gmi3
-rw-r--r--src/gmi/tpl/posts/index.gmi18
-rw-r--r--src/gmi/tpl/posts/post.gmi9
6 files changed, 215 insertions, 6 deletions
diff --git a/src/cmd/mediocre-blog/main.go b/src/cmd/mediocre-blog/main.go
index 8c6939f..cdd9b1b 100644
--- a/src/cmd/mediocre-blog/main.go
+++ b/src/cmd/mediocre-blog/main.go
@@ -124,6 +124,8 @@ func main() {
}()
gmiParams.Logger = logger.WithNamespace("gmi")
+ gmiParams.PostStore = postStore
+ gmiParams.PostAssetStore = postAssetStore
logger.Info(ctx, "starting gmi api")
gmiAPI, err := gmi.New(gmiParams)
diff --git a/src/gmi/gmi.go b/src/gmi/gmi.go
index 6e2d79f..7d4d186 100644
--- a/src/gmi/gmi.go
+++ b/src/gmi/gmi.go
@@ -12,6 +12,7 @@ import (
"git.sr.ht/~adnano/go-gemini"
"git.sr.ht/~adnano/go-gemini/certificate"
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
+ "github.com/mediocregopher/blog.mediocregopher.com/srv/post"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
)
@@ -19,7 +20,11 @@ import (
// Params are used to instantiate a new API instance. All fields are required
// unless otherwise noted.
type Params struct {
- Logger *mlog.Logger
+ Logger *mlog.Logger
+
+ PostStore post.Store
+ PostAssetStore post.AssetStore
+
PublicURL *url.URL
ListenAddr string
CertificatesPath string
@@ -86,9 +91,14 @@ func New(params Params) (API, error) {
params: params,
}
+ handler, err := a.handler()
+ if err != nil {
+ return nil, fmt.Errorf("constructing handler: %w", err)
+ }
+
a.srv = &gemini.Server{
Addr: params.ListenAddr,
- Handler: a.handler(),
+ Handler: handler,
GetCertificate: certStore.Get,
}
@@ -109,14 +119,47 @@ func (a *api) Shutdown(ctx context.Context) error {
return a.srv.Shutdown(ctx)
}
-func (a *api) handler() gemini.Handler {
+func postsMiddleware(tplHandler gemini.Handler) gemini.Handler {
+
return gemini.HandlerFunc(func(
ctx context.Context,
rw gemini.ResponseWriter,
r *gemini.Request,
) {
- fmt.Fprintf(rw, "# Test\n\n")
- fmt.Fprintf(rw, "HELLO WORLD\n\n")
- fmt.Fprintf(rw, "=> gemini://midnight.pub Hit the pub\n\n")
+
+ id := strings.TrimPrefix(r.URL.Path, "/posts/")
+ id = strings.TrimSuffix(id, ".gmi")
+
+ if id == "index" {
+ tplHandler.ServeGemini(ctx, rw, r)
+ return
+ }
+
+ query := r.URL.Query()
+ query.Set("id", id)
+ r.URL.RawQuery = query.Encode()
+
+ r.URL.Path = "/posts/post.gmi"
+
+ tplHandler.ServeGemini(ctx, rw, r)
})
}
+
+func (a *api) handler() (gemini.Handler, error) {
+
+ tplHandler, err := a.tplHandler()
+ if err != nil {
+ return nil, fmt.Errorf("generating tpl handler: %w", err)
+ }
+
+ mux := new(gemini.Mux)
+ mux.Handle("/posts/", postsMiddleware(tplHandler))
+ mux.Handle("/", tplHandler)
+
+ h := mux
+
+ // TODO logging
+ // TODO caching
+
+ return h, nil
+}
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
+}
diff --git a/src/gmi/tpl/index.gmi b/src/gmi/tpl/index.gmi
new file mode 100644
index 0000000..7c68b92
--- /dev/null
+++ b/src/gmi/tpl/index.gmi
@@ -0,0 +1,3 @@
+# Index
+
+=> /posts/index.gmi See all posts
diff --git a/src/gmi/tpl/posts/index.gmi b/src/gmi/tpl/posts/index.gmi
new file mode 100644
index 0000000..12eb39c
--- /dev/null
+++ b/src/gmi/tpl/posts/index.gmi
@@ -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 -}}
diff --git a/src/gmi/tpl/posts/post.gmi b/src/gmi/tpl/posts/post.gmi
new file mode 100644
index 0000000..52ac5ff
--- /dev/null
+++ b/src/gmi/tpl/posts/post.gmi
@@ -0,0 +1,9 @@
+{{ $post := .GetPostByID (.GetQueryValue "id" "") -}}
+
+# {{ $post.Title }}
+
+{{ if ne $post.Description "" -}}
+> {{ $post.Description }}
+{{ end -}}
+
+{{ $post.Body }}