Compare commits
4 Commits
c1c1bb2c4c
...
3f97311514
Author | SHA1 | Date | |
---|---|---|---|
|
3f97311514 | ||
|
b766eefe7c | ||
|
6b3933282e | ||
|
63cc6cb217 |
@ -112,6 +112,7 @@ func main() {
|
||||
httpParams.PostAssetStore = postAssetStore
|
||||
httpParams.PostDraftStore = postDraftStore
|
||||
httpParams.MailingList = ml
|
||||
httpParams.GeminiPublicURL = gmiParams.PublicURL
|
||||
|
||||
logger.Info(ctx, "starting http api")
|
||||
httpAPI, err := http.New(httpParams)
|
||||
|
@ -161,6 +161,9 @@ func (a *api) tplHandler() (gemini.Handler, error) {
|
||||
BlogHTTPURL: func(path string) string {
|
||||
return blogURL(a.params.HTTPPublicURL, path, true)
|
||||
},
|
||||
BlogGeminiURL: func(path string) string {
|
||||
return blogURL(a.params.PublicURL, path, true)
|
||||
},
|
||||
AssetURL: func(id string) string {
|
||||
path := filepath.Join("assets", id)
|
||||
return blogURL(a.params.PublicURL, path, false)
|
||||
@ -193,16 +196,17 @@ func (a *api) tplHandler() (gemini.Handler, error) {
|
||||
|
||||
allTpls := template.New("")
|
||||
|
||||
allTpls.Funcs(preprocessFuncs.ToFuncsMap())
|
||||
allTpls.Funcs(preprocessFuncs.ToFuncMap())
|
||||
|
||||
allTpls.Funcs(template.FuncMap{
|
||||
"BlogURLAbs": func(path string) string {
|
||||
return blogURL(a.params.PublicURL, path, true)
|
||||
},
|
||||
"PostURLAbs": func(id string) string {
|
||||
path := filepath.Join("posts", id) + ".gmi"
|
||||
return blogURL(a.params.PublicURL, path, true)
|
||||
},
|
||||
"PostHTTPURL": func(id string) string {
|
||||
path := filepath.Join("posts", id)
|
||||
return preprocessFuncs.BlogHTTPURL(path)
|
||||
},
|
||||
})
|
||||
|
||||
err := fs.WalkDir(tplFS, "tpl", func(path string, d fs.DirEntry, err error) error {
|
||||
|
@ -3,11 +3,11 @@
|
||||
|
||||
<?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>
|
||||
<id>{{ BlogGeminiURL "/" }}</id>
|
||||
{{ if gt (len $posts) 0 -}}
|
||||
<updated>{{ (index $posts 0).PublishedAt.Format "2006-01-02T15:04:05Z07:00" }}</updated>
|
||||
{{ end -}}
|
||||
<link href="{{ BlogURLAbs "/" }}"></link>
|
||||
<link href="{{ BlogGeminiURL "/" }}"></link>
|
||||
<author>
|
||||
<name>mediocregopher</name>
|
||||
</author>
|
||||
|
@ -1,5 +1,13 @@
|
||||
{{ $post := .GetPostByID (.GetQueryValue "id" "") -}}
|
||||
|
||||
{{ if eq $post.Format "md" -}}
|
||||
This post has been translated from it's original markdown format, if it seems
|
||||
busted it might appear better over HTTP:
|
||||
|
||||
=> {{ PostHTTPURL $post.ID }}
|
||||
|
||||
{{ end -}}
|
||||
|
||||
# {{ $post.Title }}
|
||||
|
||||
{{ if ne $post.Description "" -}}
|
||||
|
@ -3,7 +3,6 @@ package http
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gorilla/feeds"
|
||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/http/apiutil"
|
||||
@ -54,7 +53,7 @@ func (a *api) renderFeedHandler() http.Handler {
|
||||
feed.Updated = post.LastUpdatedAt
|
||||
}
|
||||
|
||||
postURL := publicURL + filepath.Join("/posts", post.ID)
|
||||
postURL := a.postURL(post.ID, true)
|
||||
|
||||
item := &feeds.Item{
|
||||
Title: post.Title,
|
||||
|
@ -44,6 +44,10 @@ type Params struct {
|
||||
// PublicURL is the base URL which site visitors can navigate to.
|
||||
PublicURL *url.URL
|
||||
|
||||
// GeminiPublicURL is the base URL which gemini site visitors can navigate
|
||||
// to.
|
||||
GeminiPublicURL *url.URL
|
||||
|
||||
// ListenProto and ListenAddr are passed into net.Listen to create the
|
||||
// API's listener. Both "tcp" and "unix" protocols are explicitly
|
||||
// supported.
|
||||
|
@ -66,14 +66,16 @@ type postTplPayload struct {
|
||||
Body template.HTML
|
||||
}
|
||||
|
||||
func (a *api) postToPostTplPayload(storedPost post.StoredPost) (postTplPayload, error) {
|
||||
|
||||
preprocessFuncs := post.PreprocessFunctions{
|
||||
func (a *api) postPreprocessFuncs() post.PreprocessFunctions {
|
||||
return post.PreprocessFunctions{
|
||||
BlogURL: func(path string) string {
|
||||
return a.blogURL(path, false)
|
||||
return a.blogURL(a.params.PublicURL, path, false)
|
||||
},
|
||||
BlogHTTPURL: func(path string) string {
|
||||
return a.blogURL(path, false)
|
||||
return a.blogURL(a.params.PublicURL, path, true)
|
||||
},
|
||||
BlogGeminiURL: func(path string) string {
|
||||
return a.blogURL(a.params.GeminiPublicURL, path, true)
|
||||
},
|
||||
AssetURL: func(id string) string {
|
||||
return a.assetURL(id, false)
|
||||
@ -83,10 +85,15 @@ func (a *api) postToPostTplPayload(storedPost post.StoredPost) (postTplPayload,
|
||||
},
|
||||
StaticURL: func(path string) string {
|
||||
path = filepath.Join("static", path)
|
||||
return a.blogURL(path, false)
|
||||
return a.blogURL(a.params.PublicURL, path, false)
|
||||
},
|
||||
Image: a.postPreprocessFuncImage,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *api) postToPostTplPayload(storedPost post.StoredPost) (postTplPayload, error) {
|
||||
|
||||
preprocessFuncs := a.postPreprocessFuncs()
|
||||
|
||||
bodyBuf := new(bytes.Buffer)
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@ -27,28 +28,28 @@ func mustReadTplFile(fileName string) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (a *api) blogURL(path string, abs bool) string {
|
||||
func (a *api) blogURL(base *url.URL, path string, abs bool) string {
|
||||
// filepath.Join strips trailing slash, but we want to keep it
|
||||
trailingSlash := strings.HasSuffix(path, "/")
|
||||
|
||||
res := filepath.Join("/", a.params.PublicURL.Path, path)
|
||||
path = filepath.Join("/", base.Path, path)
|
||||
|
||||
if trailingSlash && res != "/" {
|
||||
res += "/"
|
||||
if trailingSlash && path != "/" {
|
||||
path += "/"
|
||||
}
|
||||
|
||||
if abs {
|
||||
u := *a.params.PublicURL
|
||||
u.Path = res
|
||||
res = u.String()
|
||||
if !abs {
|
||||
return path
|
||||
}
|
||||
|
||||
return res
|
||||
u := *base
|
||||
u.Path = path
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (a *api) postURL(id string, abs bool) string {
|
||||
path := filepath.Join("posts", id)
|
||||
return a.blogURL(path, abs)
|
||||
return a.blogURL(a.params.PublicURL, path, abs)
|
||||
}
|
||||
|
||||
func (a *api) editPostURL(id string, abs bool) string {
|
||||
@ -56,21 +57,21 @@ func (a *api) editPostURL(id string, abs bool) string {
|
||||
}
|
||||
|
||||
func (a *api) managePostsURL(abs bool) string {
|
||||
return a.blogURL("posts?method=manage", abs)
|
||||
return a.blogURL(a.params.PublicURL, "posts?method=manage", abs)
|
||||
}
|
||||
|
||||
func (a *api) manageAssetsURL(abs bool) string {
|
||||
return a.blogURL("assets?method=manage", abs)
|
||||
return a.blogURL(a.params.PublicURL, "assets?method=manage", abs)
|
||||
}
|
||||
|
||||
func (a *api) assetURL(id string, abs bool) string {
|
||||
path := filepath.Join("assets", id)
|
||||
return a.blogURL(path, false)
|
||||
return a.blogURL(a.params.PublicURL, path, false)
|
||||
}
|
||||
|
||||
func (a *api) draftPostURL(id string, abs bool) string {
|
||||
path := filepath.Join("drafts", id)
|
||||
return a.blogURL(path, abs)
|
||||
return a.blogURL(a.params.PublicURL, path, abs)
|
||||
}
|
||||
|
||||
func (a *api) editDraftPostURL(id string, abs bool) string {
|
||||
@ -78,42 +79,29 @@ func (a *api) editDraftPostURL(id string, abs bool) string {
|
||||
}
|
||||
|
||||
func (a *api) manageDraftPostsURL(abs bool) string {
|
||||
return a.blogURL("drafts", abs) + "?method=manage"
|
||||
return a.blogURL(a.params.PublicURL, "drafts", abs) + "?method=manage"
|
||||
}
|
||||
|
||||
func (a *api) draftsURL(abs bool) string {
|
||||
return a.blogURL("drafts", abs)
|
||||
return a.blogURL(a.params.PublicURL, "drafts", abs)
|
||||
}
|
||||
|
||||
func (a *api) tplFuncs() template.FuncMap {
|
||||
return template.FuncMap{
|
||||
"BlogURL": func(path string) string {
|
||||
return a.blogURL(path, false)
|
||||
},
|
||||
"BlogURLAbs": func(path string) string {
|
||||
return a.blogURL(path, true)
|
||||
},
|
||||
"StaticURL": func(path string) string {
|
||||
path = filepath.Join("static", path)
|
||||
return a.blogURL(path, false)
|
||||
},
|
||||
"StaticInlineCSS": func(path string) (template.CSS, error) {
|
||||
path = filepath.Join("static", path)
|
||||
b, err := staticFS.ReadFile(path)
|
||||
return template.CSS(b), err
|
||||
},
|
||||
"PostURL": func(id string) string {
|
||||
return a.postURL(id, false)
|
||||
},
|
||||
"AssetURL": func(id string) string {
|
||||
return a.assetURL(id, false)
|
||||
},
|
||||
"DraftURL": func(id string) string {
|
||||
return a.draftPostURL(id, false)
|
||||
},
|
||||
"DateTimeFormat": func(t time.Time) string {
|
||||
return t.Format("2006-01-02")
|
||||
},
|
||||
"SafeURL": func(u string) template.URL {
|
||||
return template.URL(u)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,6 +109,7 @@ func (a *api) parseTpl(name, tplBody string) (*template.Template, error) {
|
||||
|
||||
tpl := template.New(name)
|
||||
tpl = tpl.Funcs(a.tplFuncs())
|
||||
tpl = tpl.Funcs(template.FuncMap(a.postPreprocessFuncs().ToFuncMap()))
|
||||
|
||||
var err error
|
||||
|
||||
@ -137,6 +126,7 @@ func (a *api) mustParseTpl(name string) *template.Template {
|
||||
|
||||
func (a *api) mustParseBasedTpl(name string) *template.Template {
|
||||
tpl := a.mustParseTpl(name)
|
||||
tpl = template.Must(tpl.New("gemini-cta.html").Parse(mustReadTplFile("gemini-cta.html")))
|
||||
tpl = template.Must(tpl.New("base.html").Parse(mustReadTplFile("base.html")))
|
||||
return tpl
|
||||
}
|
||||
@ -145,15 +135,19 @@ type tplData struct {
|
||||
Payload interface{}
|
||||
}
|
||||
|
||||
func newTPLData(r *http.Request, payload interface{}) tplData {
|
||||
return tplData{
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// executeTemplate expects to be the final action in an http.Handler
|
||||
func executeTemplate(
|
||||
rw http.ResponseWriter, r *http.Request,
|
||||
tpl *template.Template, payload interface{},
|
||||
) {
|
||||
|
||||
tplData := tplData{
|
||||
Payload: payload,
|
||||
}
|
||||
tplData := newTPLData(r, payload)
|
||||
|
||||
if err := tpl.Execute(rw, tplData); err != nil {
|
||||
apiutil.InternalServerError(
|
||||
@ -178,7 +172,10 @@ func (a *api) renderDumbTplHandler(tplName string) http.Handler {
|
||||
tpl := a.mustParseBasedTpl(tplName)
|
||||
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
if err := tpl.Execute(rw, nil); err != nil {
|
||||
|
||||
tplData := newTPLData(r, nil)
|
||||
|
||||
if err := tpl.Execute(rw, tplData); err != nil {
|
||||
apiutil.InternalServerError(
|
||||
rw, r, fmt.Errorf("rendering %q: %w", tplName, err),
|
||||
)
|
||||
|
@ -114,7 +114,7 @@ emailSubscribe.onclick = async () => {
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="{{ BlogURLAbs "feed.xml" }}">{{ BlogURLAbs "feed.xml" }}</a>
|
||||
<a href="{{ BlogHTTPURL "feed.xml" }}">{{ BlogHTTPURL "feed.xml" }}</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@ -145,6 +145,8 @@ emailSubscribe.onclick = async () => {
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ template "gemini-cta.html" . }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ template "base.html" . }}
|
||||
|
12
src/http/tpl/gemini-cta.html
Normal file
12
src/http/tpl/gemini-cta.html
Normal file
@ -0,0 +1,12 @@
|
||||
<hr/>
|
||||
|
||||
<p>
|
||||
This site can also be accessed via the gemini protocol:
|
||||
<a href="{{ BlogGeminiURL "/" | SafeURL }}">
|
||||
{{ BlogGeminiURL "/" }}
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ PostURL "gemspace-tour" }}">What is gemini?</a>
|
||||
</p>
|
||||
|
@ -50,6 +50,8 @@
|
||||
<li><a href="https://yamakan.place/palestine/#">Radio alHara</a> is another great internet radio station.</li>
|
||||
</ul>
|
||||
|
||||
{{ template "gemini-cta.html" . }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ template "base.html" . }}
|
||||
|
@ -38,6 +38,8 @@
|
||||
</em></p>
|
||||
{{ end }}
|
||||
|
||||
{{ template "gemini-cta.html" . }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ template "base.html" . }}
|
||||
|
@ -13,6 +13,12 @@
|
||||
{{ end }}
|
||||
|
||||
<table>
|
||||
<colgroup>
|
||||
<col span="1" style="width: auto;">
|
||||
<col span="1" style="width: auto;">
|
||||
<col span="1" style="width: 5rem;">
|
||||
<col span="1" style="width: auto;">
|
||||
</colgroup>
|
||||
|
||||
{{ range .Payload.Posts }}
|
||||
<tr>
|
||||
|
@ -30,6 +30,8 @@
|
||||
</p>
|
||||
{{ end }}
|
||||
|
||||
{{ template "gemini-cta.html" . }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ template "base.html" . }}
|
||||
|
@ -17,12 +17,19 @@ type PreprocessFunctions struct {
|
||||
// The given path should not have a leading slash.
|
||||
BlogURL func(path string) string
|
||||
|
||||
// BlogURL returns the given string, rooted to the base URL of the blog's
|
||||
// HTTP server (which may or may not include path components itself).
|
||||
// BlogHTTPURL returns the given string, rooted to the base URL of the
|
||||
// blog's HTTP server (which may or may not include path components itself).
|
||||
//
|
||||
// The given path should not have a leading slash.
|
||||
BlogHTTPURL func(path string) string
|
||||
|
||||
// BlogGeminiURL returns the given string, rooted to the base URL of the
|
||||
// blog's gemini server (which may or may not include path components
|
||||
// itself).
|
||||
//
|
||||
// The given path should not have a leading slash.
|
||||
BlogGeminiURL func(path string) string
|
||||
|
||||
// AssetURL returns the URL of the asset with the given ID.
|
||||
AssetURL func(id string) string
|
||||
|
||||
@ -43,14 +50,15 @@ type PreprocessFunctions struct {
|
||||
Image func(args ...string) (string, error)
|
||||
}
|
||||
|
||||
func (funcs PreprocessFunctions) ToFuncsMap() template.FuncMap {
|
||||
func (funcs PreprocessFunctions) ToFuncMap() template.FuncMap {
|
||||
return template.FuncMap{
|
||||
"BlogURL": funcs.BlogURL,
|
||||
"BlogHTTPURL": funcs.BlogHTTPURL,
|
||||
"AssetURL": funcs.AssetURL,
|
||||
"PostURL": funcs.PostURL,
|
||||
"StaticURL": funcs.StaticURL,
|
||||
"Image": funcs.Image,
|
||||
"BlogURL": funcs.BlogURL,
|
||||
"BlogHTTPURL": funcs.BlogHTTPURL,
|
||||
"BlogGeminiURL": funcs.BlogGeminiURL,
|
||||
"AssetURL": funcs.AssetURL,
|
||||
"PostURL": funcs.PostURL,
|
||||
"StaticURL": funcs.StaticURL,
|
||||
"Image": funcs.Image,
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,7 +69,7 @@ func (p Post) PreprocessBody(into io.Writer, funcs PreprocessFunctions) error {
|
||||
|
||||
tpl := template.New("")
|
||||
|
||||
tpl.Funcs(funcs.ToFuncsMap())
|
||||
tpl.Funcs(funcs.ToFuncMap())
|
||||
|
||||
tpl, err := tpl.Parse(p.Body)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user