Implement gemini atom feed
This commit is contained in:
parent
1cfdac5e8c
commit
5b5a043868
@ -37,8 +37,7 @@ type rendererGetPostSeriesNextPreviousRes struct {
|
|||||||
type renderer struct {
|
type renderer struct {
|
||||||
url *url.URL
|
url *url.URL
|
||||||
postStore post.Store
|
postStore post.Store
|
||||||
gmiPublicURL *url.URL
|
preprocessFuncs post.PreprocessFunctions
|
||||||
httpPublicURL *url.URL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r renderer) GetPosts(page, count int) (rendererGetPostsRes, error) {
|
func (r renderer) GetPosts(page, count int) (rendererGetPostsRes, error) {
|
||||||
@ -91,39 +90,9 @@ func (r renderer) GetPostSeriesNextPrevious(p post.StoredPost) (rendererGetPostS
|
|||||||
|
|
||||||
func (r renderer) PostBody(p post.StoredPost) (string, error) {
|
func (r renderer) PostBody(p post.StoredPost) (string, error) {
|
||||||
|
|
||||||
preprocessFuncs := post.PreprocessFunctions{
|
|
||||||
BlogURL: func(path string) string {
|
|
||||||
return filepath.Join("/", r.gmiPublicURL.Path, path)
|
|
||||||
},
|
|
||||||
AssetURL: func(id string) string {
|
|
||||||
return filepath.Join("/assets", id)
|
|
||||||
},
|
|
||||||
PostURL: func(id string) string {
|
|
||||||
return filepath.Join("/posts", id)
|
|
||||||
},
|
|
||||||
StaticURL: func(path string) string {
|
|
||||||
httpPublicURL := *r.httpPublicURL
|
|
||||||
httpPublicURL.Path = filepath.Join(httpPublicURL.Path, "/static", path)
|
|
||||||
return httpPublicURL.String()
|
|
||||||
},
|
|
||||||
Image: func(args ...string) (string, error) {
|
|
||||||
|
|
||||||
var (
|
|
||||||
id = args[0]
|
|
||||||
descr = "Image"
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(args) > 1 {
|
|
||||||
descr = args[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("=> %s %s", filepath.Join("/assets", id), descr), nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
if err := p.PreprocessBody(buf, preprocessFuncs); err != nil {
|
if err := p.PreprocessBody(buf, r.preprocessFuncs); err != nil {
|
||||||
return "", fmt.Errorf("preprocessing post body: %w", err)
|
return "", fmt.Errorf("preprocessing post body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,8 +128,66 @@ func (r renderer) Add(a, b int) int { return a + b }
|
|||||||
|
|
||||||
func (a *api) tplHandler() (gemini.Handler, error) {
|
func (a *api) tplHandler() (gemini.Handler, error) {
|
||||||
|
|
||||||
|
blogURL := func(path string, abs bool) string {
|
||||||
|
path = filepath.Join(a.params.PublicURL.Path, path)
|
||||||
|
|
||||||
|
if !abs {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
u := *a.params.PublicURL
|
||||||
|
u.Path = path
|
||||||
|
return u.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocessFuncs := post.PreprocessFunctions{
|
||||||
|
BlogURL: func(path string) string {
|
||||||
|
return blogURL(path, false)
|
||||||
|
},
|
||||||
|
AssetURL: func(id string) string {
|
||||||
|
path := filepath.Join("assets", id)
|
||||||
|
return blogURL(path, false)
|
||||||
|
},
|
||||||
|
PostURL: func(id string) string {
|
||||||
|
path := filepath.Join("posts", id) + ".gmi"
|
||||||
|
return blogURL(path, false)
|
||||||
|
},
|
||||||
|
StaticURL: func(path string) string {
|
||||||
|
httpPublicURL := *a.params.HTTPPublicURL
|
||||||
|
httpPublicURL.Path = filepath.Join(httpPublicURL.Path, "/static", path)
|
||||||
|
return httpPublicURL.String()
|
||||||
|
},
|
||||||
|
Image: func(args ...string) (string, error) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
id = args[0]
|
||||||
|
descr = "Image"
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(args) > 1 {
|
||||||
|
descr = args[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
path := blogURL(filepath.Join("assets", id), false)
|
||||||
|
|
||||||
|
return fmt.Sprintf("=> %s %s", path, descr), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
allTpls := template.New("")
|
allTpls := template.New("")
|
||||||
|
|
||||||
|
allTpls.Funcs(preprocessFuncs.ToFuncsMap())
|
||||||
|
|
||||||
|
allTpls.Funcs(template.FuncMap{
|
||||||
|
"BlogURLAbs": func(path string) string {
|
||||||
|
return blogURL(path, true)
|
||||||
|
},
|
||||||
|
"PostURLAbs": func(id string) string {
|
||||||
|
path := filepath.Join("posts", id) + ".gmi"
|
||||||
|
return blogURL(path, true)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
err := fs.WalkDir(tplFS, "tpl", func(path string, d fs.DirEntry, err error) error {
|
err := fs.WalkDir(tplFS, "tpl", func(path string, d fs.DirEntry, err error) error {
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -221,8 +248,7 @@ func (a *api) tplHandler() (gemini.Handler, error) {
|
|||||||
err := tpl.Execute(buf, renderer{
|
err := tpl.Execute(buf, renderer{
|
||||||
url: r.URL,
|
url: r.URL,
|
||||||
postStore: a.params.PostStore,
|
postStore: a.params.PostStore,
|
||||||
gmiPublicURL: a.params.PublicURL,
|
preprocessFuncs: preprocessFuncs,
|
||||||
httpPublicURL: a.params.HTTPPublicURL,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
28
src/gmi/tpl/feed.xml
Normal file
28
src/gmi/tpl/feed.xml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{{ $getPostsRes := .GetPosts 0 15 -}}
|
||||||
|
{{ $posts := $getPostsRes.Posts -}}
|
||||||
|
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
<title>mediocregopher's lil web corner</title>
|
||||||
|
<id>{{ BlogURLAbs "/" }}</id>
|
||||||
|
{{ if gt (len $posts) 0 -}}
|
||||||
|
<updated>{{ (index $posts 0).PublishedAt.Format "2006-01-02T15:04:05Z07:00" }}</updated>
|
||||||
|
{{ end -}}
|
||||||
|
<link href="{{ BlogURLAbs "/" }}"></link>
|
||||||
|
<author>
|
||||||
|
<name>mediocregopher</name>
|
||||||
|
</author>
|
||||||
|
{{ range $posts -}}
|
||||||
|
<entry>
|
||||||
|
<title>{{ .Title }}</title>
|
||||||
|
<updated>{{ .PublishedAt.Format "2006-01-02T15:04:05Z07:00" }}</updated>
|
||||||
|
<id>{{ PostURLAbs .ID }}</id>
|
||||||
|
<link href="{{ PostURLAbs .ID }}" rel="alternate"></link>
|
||||||
|
{{ if .Description -}}
|
||||||
|
<summary type="html">{{ .Description }}</summary>
|
||||||
|
{{ end -}}
|
||||||
|
<author>
|
||||||
|
<name>mediocregopher</name>
|
||||||
|
</author>
|
||||||
|
</entry>
|
||||||
|
{{ end -}}
|
||||||
|
</feed>
|
@ -9,7 +9,7 @@
|
|||||||
{{ end -}}
|
{{ end -}}
|
||||||
|
|
||||||
{{ range $getPostsRes.Posts -}}
|
{{ range $getPostsRes.Posts -}}
|
||||||
=> /posts/{{ .ID }}.gmi {{ .PublishedAt.Format "2006-01-02" }} - {{ .Title }}
|
=> {{ PostURL .ID }} {{ .PublishedAt.Format "2006-01-02" }} - {{ .Title }}
|
||||||
|
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
|
|
||||||
|
@ -39,9 +39,8 @@ func (a *api) renderFeedHandler() http.Handler {
|
|||||||
publicURL := a.params.PublicURL.String()
|
publicURL := a.params.PublicURL.String()
|
||||||
|
|
||||||
feed := feeds.Feed{
|
feed := feeds.Feed{
|
||||||
Title: "Mediocre Blog",
|
Title: "mediocregopher's lil web corner",
|
||||||
Link: &feeds.Link{Href: publicURL + "/"},
|
Link: &feeds.Link{Href: publicURL + "/"},
|
||||||
Description: "A mix of tech, art, travel, and who knows what else.",
|
|
||||||
Author: author,
|
Author: author,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
|
<title>mediocregopher's lil web corner</title>
|
||||||
<style>{{ StaticInlineCSS "new.css" }}</style>
|
<style>{{ StaticInlineCSS "new.css" }}</style>
|
||||||
<style>{{ StaticInlineCSS "mediocre.css" }}</style>
|
<style>{{ StaticInlineCSS "mediocre.css" }}</style>
|
||||||
|
|
||||||
|
@ -37,6 +37,16 @@ type PreprocessFunctions struct {
|
|||||||
Image func(args ...string) (string, error)
|
Image func(args ...string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (funcs PreprocessFunctions) ToFuncsMap() template.FuncMap {
|
||||||
|
return template.FuncMap{
|
||||||
|
"BlogURL": funcs.BlogURL,
|
||||||
|
"AssetURL": funcs.AssetURL,
|
||||||
|
"PostURL": funcs.PostURL,
|
||||||
|
"StaticURL": funcs.StaticURL,
|
||||||
|
"Image": funcs.Image,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PreprocessBody interprets the Post's Body as a text template which may use
|
// PreprocessBody interprets the Post's Body as a text template which may use
|
||||||
// any of the functions found in PreprocessFunctions (all must be set). It
|
// any of the functions found in PreprocessFunctions (all must be set). It
|
||||||
// executes the template and writes the result to the given writer.
|
// executes the template and writes the result to the given writer.
|
||||||
@ -44,13 +54,7 @@ func (p Post) PreprocessBody(into io.Writer, funcs PreprocessFunctions) error {
|
|||||||
|
|
||||||
tpl := template.New("")
|
tpl := template.New("")
|
||||||
|
|
||||||
tpl.Funcs(template.FuncMap{
|
tpl.Funcs(funcs.ToFuncsMap())
|
||||||
"BlogURL": funcs.BlogURL,
|
|
||||||
"AssetURL": funcs.AssetURL,
|
|
||||||
"PostURL": funcs.PostURL,
|
|
||||||
"StaticURL": funcs.StaticURL,
|
|
||||||
"Image": funcs.Image,
|
|
||||||
})
|
|
||||||
|
|
||||||
tpl, err := tpl.Parse(p.Body)
|
tpl, err := tpl.Parse(p.Body)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user