mirror of https://github.com/jlelse/GoBlog
Various improvements; Remove Hugo import API; Add feeds for photos
This commit is contained in:
parent
82ace66544
commit
99789efcd3
|
@ -84,8 +84,7 @@ func apHandleWebfinger(w http.ResponseWriter, r *http.Request) {
|
||||||
serveError(w, r, "Blog not found", http.StatusNotFound)
|
serveError(w, r, "Blog not found", http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set(contentType, "application/jrd+json"+charsetUtf8Suffix)
|
b, _ := json.Marshal(map[string]interface{}{
|
||||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{
|
|
||||||
"subject": "acct:" + name + "@" + appConfig.Server.publicHostname,
|
"subject": "acct:" + name + "@" + appConfig.Server.publicHostname,
|
||||||
"links": []map[string]string{
|
"links": []map[string]string{
|
||||||
{
|
{
|
||||||
|
@ -95,6 +94,8 @@ func apHandleWebfinger(w http.ResponseWriter, r *http.Request) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
w.Header().Set(contentType, "application/jrd+json"+charsetUtf8Suffix)
|
||||||
|
_, _ = writeMinified(w, contentTypeJSON, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func apHandleInbox(w http.ResponseWriter, r *http.Request) {
|
func apHandleInbox(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
|
@ -14,9 +14,11 @@ var asContext = []string{"https://www.w3.org/ns/activitystreams"}
|
||||||
|
|
||||||
func manipulateAsPath(next http.Handler) http.Handler {
|
func manipulateAsPath(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
if lowerAccept := strings.ToLower(r.Header.Get("Accept")); (strings.Contains(lowerAccept, contentTypeAS) || strings.Contains(lowerAccept, "application/ld+json")) && !strings.Contains(lowerAccept, contentTypeHTML) {
|
if ap := appConfig.ActivityPub; ap != nil && ap.Enabled {
|
||||||
// Is ActivityStream, add ".as" to differentiate cache and also trigger as function
|
if lowerAccept := strings.ToLower(r.Header.Get("Accept")); (strings.Contains(lowerAccept, contentTypeAS) || strings.Contains(lowerAccept, "application/ld+json")) && !strings.Contains(lowerAccept, contentTypeHTML) {
|
||||||
r.URL.Path += ".as"
|
// Is ActivityStream, add ".as" to differentiate cache and also trigger as function
|
||||||
|
r.URL.Path += ".as"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
next.ServeHTTP(rw, r)
|
next.ServeHTTP(rw, r)
|
||||||
})
|
})
|
||||||
|
@ -68,9 +70,9 @@ type asEndpoints struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *post) serveActivityStreams(w http.ResponseWriter) {
|
func (p *post) serveActivityStreams(w http.ResponseWriter) {
|
||||||
// Send JSON
|
b, _ := json.Marshal(p.toASNote())
|
||||||
w.Header().Add(contentType, contentTypeASUTF8)
|
w.Header().Set(contentType, contentTypeASUTF8)
|
||||||
_ = json.NewEncoder(w).Encode(p.toASNote())
|
_, _ = writeMinified(w, contentTypeAS, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *post) toASNote() *asNote {
|
func (p *post) toASNote() *asNote {
|
||||||
|
@ -126,8 +128,6 @@ func (b *configBlog) serveActivityStreams(blog string, w http.ResponseWriter, r
|
||||||
serveError(w, r, "Failed to marshal public key", http.StatusInternalServerError)
|
serveError(w, r, "Failed to marshal public key", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Send JSON
|
|
||||||
w.Header().Add(contentType, contentTypeASUTF8)
|
|
||||||
asBlog := &asPerson{
|
asBlog := &asPerson{
|
||||||
Context: asContext,
|
Context: asContext,
|
||||||
Type: "Person",
|
Type: "Person",
|
||||||
|
@ -154,5 +154,7 @@ func (b *configBlog) serveActivityStreams(blog string, w http.ResponseWriter, r
|
||||||
URL: appConfig.User.Picture,
|
URL: appConfig.User.Picture,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = json.NewEncoder(w).Encode(asBlog)
|
jb, _ := json.Marshal(asBlog)
|
||||||
|
w.Header().Set(contentType, contentTypeASUTF8)
|
||||||
|
_, _ = writeMinified(w, contentTypeAS, jb)
|
||||||
}
|
}
|
||||||
|
|
61
api.go
61
api.go
|
@ -1,61 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Not tested anymore
|
|
||||||
|
|
||||||
func apiPostCreateHugo(w http.ResponseWriter, r *http.Request) {
|
|
||||||
blog := r.URL.Query().Get("blog")
|
|
||||||
path := r.URL.Query().Get("path")
|
|
||||||
section := r.URL.Query().Get("section")
|
|
||||||
slug := r.URL.Query().Get("slug")
|
|
||||||
alias := r.URL.Query().Get("alias")
|
|
||||||
defer func() {
|
|
||||||
_ = r.Body.Close()
|
|
||||||
}()
|
|
||||||
bodyContent, err := ioutil.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
serveError(w, r, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p, aliases, err := parseHugoFile(string(bodyContent))
|
|
||||||
if err != nil {
|
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p.Blog = blog
|
|
||||||
p.Path = path
|
|
||||||
p.Section = section
|
|
||||||
p.Slug = slug
|
|
||||||
err = p.create()
|
|
||||||
if err != nil {
|
|
||||||
serveError(w, r, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
aliases = append(aliases, alias)
|
|
||||||
for i, alias := range aliases {
|
|
||||||
// Fix relativ paths
|
|
||||||
if !strings.HasPrefix(alias, "/") {
|
|
||||||
splittedPostPath := strings.Split(p.Path, "/")
|
|
||||||
alias = strings.TrimSuffix(p.Path, splittedPostPath[len(splittedPostPath)-1]) + alias
|
|
||||||
}
|
|
||||||
alias = strings.TrimSuffix(alias, "/")
|
|
||||||
if alias == p.Path {
|
|
||||||
alias = ""
|
|
||||||
}
|
|
||||||
aliases[i] = alias
|
|
||||||
}
|
|
||||||
if len(aliases) > 0 {
|
|
||||||
p.Parameters["aliases"] = aliases
|
|
||||||
err = p.replace(p.Path, p.Status)
|
|
||||||
if err != nil {
|
|
||||||
serveError(w, r, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
http.Redirect(w, r, p.fullURL(), http.StatusCreated)
|
|
||||||
}
|
|
11
config.go
11
config.go
|
@ -16,7 +16,6 @@ type config struct {
|
||||||
Blogs map[string]*configBlog `mapstructure:"blogs"`
|
Blogs map[string]*configBlog `mapstructure:"blogs"`
|
||||||
User *configUser `mapstructure:"user"`
|
User *configUser `mapstructure:"user"`
|
||||||
Hooks *configHooks `mapstructure:"hooks"`
|
Hooks *configHooks `mapstructure:"hooks"`
|
||||||
Hugo *configHugo `mapstructure:"hugo"`
|
|
||||||
Micropub *configMicropub `mapstructure:"micropub"`
|
Micropub *configMicropub `mapstructure:"micropub"`
|
||||||
PathRedirects []*configRegexRedirect `mapstructure:"pathRedirects"`
|
PathRedirects []*configRegexRedirect `mapstructure:"pathRedirects"`
|
||||||
ActivityPub *configActivityPub `mapstructure:"activityPub"`
|
ActivityPub *configActivityPub `mapstructure:"activityPub"`
|
||||||
|
@ -151,15 +150,6 @@ type configHooks struct {
|
||||||
PostDelete []string `mapstructure:"postdelete"`
|
PostDelete []string `mapstructure:"postdelete"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type configHugo struct {
|
|
||||||
Frontmatter []*frontmatter `mapstructure:"frontmatter"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type frontmatter struct {
|
|
||||||
Meta string `mapstructure:"meta"`
|
|
||||||
Parameter string `mapstructure:"parameter"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type configMicropub struct {
|
type configMicropub struct {
|
||||||
CategoryParam string `mapstructure:"categoryParam"`
|
CategoryParam string `mapstructure:"categoryParam"`
|
||||||
ReplyParam string `mapstructure:"replyParam"`
|
ReplyParam string `mapstructure:"replyParam"`
|
||||||
|
@ -223,7 +213,6 @@ func initConfig() error {
|
||||||
viper.SetDefault("user.nick", "admin")
|
viper.SetDefault("user.nick", "admin")
|
||||||
viper.SetDefault("user.password", "secret")
|
viper.SetDefault("user.password", "secret")
|
||||||
viper.SetDefault("hooks.shell", "/bin/bash")
|
viper.SetDefault("hooks.shell", "/bin/bash")
|
||||||
viper.SetDefault("hugo.frontmatter", []*frontmatter{{Meta: "title", Parameter: "title"}, {Meta: "tags", Parameter: "tags"}})
|
|
||||||
viper.SetDefault("micropub.categoryParam", "tags")
|
viper.SetDefault("micropub.categoryParam", "tags")
|
||||||
viper.SetDefault("micropub.replyParam", "replylink")
|
viper.SetDefault("micropub.replyParam", "replylink")
|
||||||
viper.SetDefault("micropub.likeParam", "likelink")
|
viper.SetDefault("micropub.likeParam", "likelink")
|
||||||
|
|
|
@ -3,7 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -103,6 +103,6 @@ func editorMicropubPost(w http.ResponseWriter, r *http.Request, media bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(result.StatusCode)
|
w.WriteHeader(result.StatusCode)
|
||||||
body, _ := ioutil.ReadAll(result.Body)
|
_, _ = io.Copy(w, result.Body)
|
||||||
_, _ = w.Write(body)
|
_ = result.Body.Close()
|
||||||
}
|
}
|
||||||
|
|
15
feeds.go
15
feeds.go
|
@ -66,16 +66,17 @@ func generateFeed(blog string, f feedType, w http.ResponseWriter, r *http.Reques
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
|
var feedString, feedMediaType string
|
||||||
switch f {
|
switch f {
|
||||||
case rssFeed:
|
case rssFeed:
|
||||||
w.Header().Set(contentType, "application/rss+xml; charset=utf-8")
|
feedMediaType = contentTypeRSS
|
||||||
err = feed.WriteRss(w)
|
feedString, err = feed.ToRss()
|
||||||
case atomFeed:
|
case atomFeed:
|
||||||
w.Header().Set(contentType, "application/atom+xml; charset=utf-8")
|
feedMediaType = contentTypeATOM
|
||||||
err = feed.WriteAtom(w)
|
feedString, err = feed.ToAtom()
|
||||||
case jsonFeed:
|
case jsonFeed:
|
||||||
w.Header().Set(contentType, "application/feed+json; charset=utf-8")
|
feedMediaType = contentTypeJSONFeed
|
||||||
err = feed.WriteJSON(w)
|
feedString, err = feed.ToJSON()
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -84,4 +85,6 @@ func generateFeed(blog string, f feedType, w http.ResponseWriter, r *http.Reques
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
w.Header().Set(contentType, feedMediaType+charsetUtf8Suffix)
|
||||||
|
_, _ = writeMinified(w, feedMediaType, []byte(feedString))
|
||||||
}
|
}
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -20,7 +20,6 @@ require (
|
||||||
github.com/gorilla/feeds v1.1.1
|
github.com/gorilla/feeds v1.1.1
|
||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
github.com/hashicorp/golang-lru v0.5.4
|
github.com/hashicorp/golang-lru v0.5.4
|
||||||
github.com/jeremywohl/flatten v1.0.1
|
|
||||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||||
github.com/joncrlsn/dque v0.0.0-20200702023911-3e80e3146ce5
|
github.com/joncrlsn/dque v0.0.0-20200702023911-3e80e3146ce5
|
||||||
github.com/klauspost/cpuid v1.3.1 // indirect
|
github.com/klauspost/cpuid v1.3.1 // indirect
|
||||||
|
@ -43,8 +42,7 @@ require (
|
||||||
github.com/spf13/cast v1.3.1
|
github.com/spf13/cast v1.3.1
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/viper v1.7.1
|
github.com/spf13/viper v1.7.1
|
||||||
github.com/tdewolff/minify/v2 v2.9.12
|
github.com/tdewolff/minify/v2 v2.9.13
|
||||||
github.com/tdewolff/parse/v2 v2.5.10 // indirect
|
|
||||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
||||||
github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2
|
github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2
|
||||||
github.com/yuin/goldmark v1.3.2
|
github.com/yuin/goldmark v1.3.2
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -142,8 +142,6 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/jeremywohl/flatten v1.0.1 h1:LrsxmB3hfwJuE+ptGOijix1PIfOoKLJ3Uee/mzbgtrs=
|
|
||||||
github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ=
|
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
@ -284,10 +282,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/tdewolff/minify/v2 v2.9.12 h1:BKnbg3kcAuTLAhDeF3vSUxjvyj5lBRxaBvgxGCVLRRY=
|
github.com/tdewolff/minify/v2 v2.9.13 h1:RrwQhgGoYBhKN/ezStGB+crU64wPK1ZE5Jmkl63lif0=
|
||||||
github.com/tdewolff/minify/v2 v2.9.12/go.mod h1:yuntVVAFuGyi9VmiRoUqAYEQnFCGO929ytj2ITMZuB8=
|
github.com/tdewolff/minify/v2 v2.9.13/go.mod h1:faNOp+awAoo+fhFHD+NAkBOaXBAvJI2X2SDERGKnARo=
|
||||||
github.com/tdewolff/parse/v2 v2.5.9 h1:9wCXRT3OcgYDNatgU+HUTOoGhE9WcnY5UxLNoJUe1yw=
|
|
||||||
github.com/tdewolff/parse/v2 v2.5.9/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
|
|
||||||
github.com/tdewolff/parse/v2 v2.5.10 h1:vj35n+ljq8LuYUx436s4qB18wuwP7thrLv+t1syE39M=
|
github.com/tdewolff/parse/v2 v2.5.10 h1:vj35n+ljq8LuYUx436s4qB18wuwP7thrLv+t1syE39M=
|
||||||
github.com/tdewolff/parse/v2 v2.5.10/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
|
github.com/tdewolff/parse/v2 v2.5.10/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
|
||||||
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
||||||
|
|
253
http.go
253
http.go
|
@ -23,6 +23,9 @@ const (
|
||||||
contentTypeWWWForm = "application/x-www-form-urlencoded"
|
contentTypeWWWForm = "application/x-www-form-urlencoded"
|
||||||
contentTypeMultipartForm = "multipart/form-data"
|
contentTypeMultipartForm = "multipart/form-data"
|
||||||
contentTypeAS = "application/activity+json"
|
contentTypeAS = "application/activity+json"
|
||||||
|
contentTypeRSS = "application/rss+xml"
|
||||||
|
contentTypeATOM = "application/atom+xml"
|
||||||
|
contentTypeJSONFeed = "application/feed+json"
|
||||||
|
|
||||||
contentTypeHTMLUTF8 = contentTypeHTML + charsetUtf8Suffix
|
contentTypeHTMLUTF8 = contentTypeHTML + charsetUtf8Suffix
|
||||||
contentTypeJSONUTF8 = contentTypeJSON + charsetUtf8Suffix
|
contentTypeJSONUTF8 = contentTypeJSON + charsetUtf8Suffix
|
||||||
|
@ -99,15 +102,9 @@ func buildHandler() (http.Handler, error) {
|
||||||
r.Mount("/debug", middleware.Profiler())
|
r.Mount("/debug", middleware.Profiler())
|
||||||
}
|
}
|
||||||
|
|
||||||
// API
|
|
||||||
r.Route("/api", func(apiRouter chi.Router) {
|
|
||||||
apiRouter.Use(middleware.NoCache, authMiddleware)
|
|
||||||
apiRouter.Post("/hugo", apiPostCreateHugo)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Micropub
|
// Micropub
|
||||||
r.Route(micropubPath, func(mpRouter chi.Router) {
|
r.Route(micropubPath, func(mpRouter chi.Router) {
|
||||||
mpRouter.Use(checkIndieAuth, middleware.NoCache, minifier.Middleware)
|
mpRouter.Use(checkIndieAuth)
|
||||||
mpRouter.Get("/", serveMicropubQuery)
|
mpRouter.Get("/", serveMicropubQuery)
|
||||||
mpRouter.Post("/", serveMicropubPost)
|
mpRouter.Post("/", serveMicropubPost)
|
||||||
mpRouter.Post(micropubMediaSubPath, serveMicropubMedia)
|
mpRouter.Post(micropubMediaSubPath, serveMicropubMedia)
|
||||||
|
@ -115,7 +112,6 @@ func buildHandler() (http.Handler, error) {
|
||||||
|
|
||||||
// IndieAuth
|
// IndieAuth
|
||||||
r.Route("/indieauth", func(indieauthRouter chi.Router) {
|
r.Route("/indieauth", func(indieauthRouter chi.Router) {
|
||||||
indieauthRouter.Use(middleware.NoCache, minifier.Middleware)
|
|
||||||
indieauthRouter.Get("/", indieAuthRequest)
|
indieauthRouter.Get("/", indieAuthRequest)
|
||||||
indieauthRouter.With(authMiddleware).Post("/accept", indieAuthAccept)
|
indieauthRouter.With(authMiddleware).Post("/accept", indieAuthAccept)
|
||||||
indieauthRouter.Post("/", indieAuthVerification)
|
indieauthRouter.Post("/", indieAuthVerification)
|
||||||
|
@ -124,23 +120,26 @@ func buildHandler() (http.Handler, error) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// ActivityPub and stuff
|
// ActivityPub and stuff
|
||||||
if appConfig.ActivityPub.Enabled {
|
if ap := appConfig.ActivityPub; ap != nil && ap.Enabled {
|
||||||
r.Post("/activitypub/inbox/{blog}", apHandleInbox)
|
r.Post("/activitypub/inbox/{blog}", apHandleInbox)
|
||||||
r.Post("/activitypub/{blog}/inbox", apHandleInbox)
|
r.Post("/activitypub/{blog}/inbox", apHandleInbox)
|
||||||
r.Get("/.well-known/webfinger", apHandleWebfinger)
|
r.With(cacheMiddleware).Get("/.well-known/webfinger", apHandleWebfinger)
|
||||||
r.With(cacheMiddleware).Get("/.well-known/host-meta", handleWellKnownHostMeta)
|
r.With(cacheMiddleware).Get("/.well-known/host-meta", handleWellKnownHostMeta)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get("/.well-known/nodeinfo", serveNodeInfoDiscover)
|
r.With(cacheMiddleware).Get("/.well-known/nodeinfo", serveNodeInfoDiscover)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get("/nodeinfo", serveNodeInfo)
|
r.With(cacheMiddleware).Get("/nodeinfo", serveNodeInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Webmentions
|
// Webmentions
|
||||||
r.Route(webmentionPath, func(webmentionRouter chi.Router) {
|
r.Route(webmentionPath, func(webmentionRouter chi.Router) {
|
||||||
webmentionRouter.Use(middleware.NoCache)
|
|
||||||
webmentionRouter.Post("/", handleWebmention)
|
webmentionRouter.Post("/", handleWebmention)
|
||||||
webmentionRouter.With(minifier.Middleware, authMiddleware).Get("/", webmentionAdmin)
|
webmentionRouter.Group(func(r chi.Router) {
|
||||||
webmentionRouter.With(minifier.Middleware, authMiddleware).Get(paginationPath, webmentionAdmin)
|
// Authenticated routes
|
||||||
webmentionRouter.With(authMiddleware).Post("/delete", webmentionAdminDelete)
|
r.Use(authMiddleware)
|
||||||
webmentionRouter.With(authMiddleware).Post("/approve", webmentionAdminApprove)
|
r.Get("/", webmentionAdmin)
|
||||||
|
r.Get(paginationPath, webmentionAdmin)
|
||||||
|
r.Post("/delete", webmentionAdminDelete)
|
||||||
|
r.Post("/approve", webmentionAdminApprove)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Posts
|
// Posts
|
||||||
|
@ -148,39 +147,36 @@ func buildHandler() (http.Handler, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var postMW []func(http.Handler) http.Handler
|
r.Group(func(r chi.Router) {
|
||||||
if appConfig.ActivityPub.Enabled {
|
r.Use(manipulateAsPath, cacheMiddleware)
|
||||||
postMW = []func(http.Handler) http.Handler{manipulateAsPath, cacheMiddleware, minifier.Middleware}
|
for _, path := range pp {
|
||||||
} else {
|
r.Get(path, servePost)
|
||||||
postMW = []func(http.Handler) http.Handler{cacheMiddleware, minifier.Middleware}
|
|
||||||
}
|
|
||||||
for _, path := range pp {
|
|
||||||
if path != "" {
|
|
||||||
r.With(postMW...).Get(path, servePost)
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
// Drafts
|
// Drafts
|
||||||
dp, err := allPostPaths(statusDraft)
|
dp, err := allPostPaths(statusDraft)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, path := range dp {
|
r.Group(func(r chi.Router) {
|
||||||
if path != "" {
|
r.Use(authMiddleware, cacheMiddleware)
|
||||||
r.With(middleware.NoCache, minifier.Middleware, authMiddleware).Get(path, servePost)
|
for _, path := range dp {
|
||||||
|
r.Get(path, servePost)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
// Post aliases
|
// Post aliases
|
||||||
allPostAliases, err := allPostAliases()
|
allPostAliases, err := allPostAliases()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, path := range allPostAliases {
|
r.Group(func(r chi.Router) {
|
||||||
if path != "" {
|
r.Use(cacheMiddleware)
|
||||||
r.With(cacheMiddleware).Get(path, servePostAlias)
|
for _, path := range allPostAliases {
|
||||||
|
r.Get(path, servePostAlias)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
// Assets
|
// Assets
|
||||||
for _, path := range allAssetPaths() {
|
for _, path := range allAssetPaths() {
|
||||||
|
@ -209,60 +205,79 @@ func buildHandler() (http.Handler, error) {
|
||||||
blogPath = ""
|
blogPath = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indexes, Feeds
|
r.Group(func(r chi.Router) {
|
||||||
for _, section := range blogConfig.Sections {
|
|
||||||
if section.Name != "" {
|
|
||||||
path := blogPath + "/" + section.Name
|
|
||||||
handler := serveSection(blog, path, section)
|
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(path, handler)
|
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(path+feedPath, handler)
|
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(path+paginationPath, handler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
// Sections
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Use(cacheMiddleware)
|
||||||
|
for _, section := range blogConfig.Sections {
|
||||||
|
if section.Name != "" {
|
||||||
|
secPath := blogPath + "/" + section.Name
|
||||||
|
handler := serveSection(blog, secPath, section)
|
||||||
|
r.Get(secPath, handler)
|
||||||
|
r.Get(secPath+feedPath, handler)
|
||||||
|
r.Get(secPath+paginationPath, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Taxonomies
|
||||||
for _, taxonomy := range blogConfig.Taxonomies {
|
for _, taxonomy := range blogConfig.Taxonomies {
|
||||||
if taxonomy.Name != "" {
|
if taxonomy.Name != "" {
|
||||||
path := blogPath + "/" + taxonomy.Name
|
taxPath := blogPath + "/" + taxonomy.Name
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(path, serveTaxonomy(blog, taxonomy))
|
taxValues, err := allTaxonomyValues(blog, taxonomy.Name)
|
||||||
values, err := allTaxonomyValues(blog, taxonomy.Name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, tv := range values {
|
r.Group(func(r chi.Router) {
|
||||||
vPath := path + "/" + urlize(tv)
|
r.Use(cacheMiddleware)
|
||||||
handler := serveTaxonomyValue(blog, vPath, taxonomy, tv)
|
r.Get(taxPath, serveTaxonomy(blog, taxonomy))
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(vPath, handler)
|
for _, tv := range taxValues {
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(vPath+feedPath, handler)
|
vPath := taxPath + "/" + urlize(tv)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(vPath+paginationPath, handler)
|
handler := serveTaxonomyValue(blog, vPath, taxonomy, tv)
|
||||||
}
|
r.Get(vPath, handler)
|
||||||
|
r.Get(vPath+feedPath, handler)
|
||||||
|
r.Get(vPath+paginationPath, handler)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Photos
|
// Photos
|
||||||
if blogConfig.Photos != nil && blogConfig.Photos.Enabled {
|
if blogConfig.Photos != nil && blogConfig.Photos.Enabled {
|
||||||
photoPath := blogPath + blogConfig.Photos.Path
|
r.Group(func(r chi.Router) {
|
||||||
handler := servePhotos(blog, photoPath)
|
r.Use(cacheMiddleware)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(photoPath, handler)
|
photoPath := blogPath + blogConfig.Photos.Path
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(photoPath+paginationPath, handler)
|
handler := servePhotos(blog, photoPath)
|
||||||
|
r.Get(photoPath, handler)
|
||||||
|
r.Get(photoPath+feedPath, handler)
|
||||||
|
r.Get(photoPath+paginationPath, handler)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
if blogConfig.Search != nil && blogConfig.Search.Enabled {
|
if blogConfig.Search != nil && blogConfig.Search.Enabled {
|
||||||
searchPath := blogPath + blogConfig.Search.Path
|
r.Group(func(r chi.Router) {
|
||||||
handler := serveSearch(blog, searchPath)
|
r.Use(cacheMiddleware)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(searchPath, handler)
|
searchPath := blogPath + blogConfig.Search.Path
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Post(searchPath, handler)
|
handler := serveSearch(blog, searchPath)
|
||||||
searchResultPath := searchPath + "/" + searchPlaceholder
|
r.Get(searchPath, handler)
|
||||||
resultHandler := serveSearchResults(blog, searchResultPath)
|
r.Post(searchPath, handler)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(searchResultPath, resultHandler)
|
searchResultPath := searchPath + "/" + searchPlaceholder
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(searchResultPath+feedPath, resultHandler)
|
resultHandler := serveSearchResults(blog, searchResultPath)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(searchResultPath+paginationPath, resultHandler)
|
r.Get(searchResultPath, resultHandler)
|
||||||
|
r.Get(searchResultPath+feedPath, resultHandler)
|
||||||
|
r.Get(searchResultPath+paginationPath, resultHandler)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stats
|
// Stats
|
||||||
if blogConfig.BlogStats != nil && blogConfig.BlogStats.Enabled {
|
if blogConfig.BlogStats != nil && blogConfig.BlogStats.Enabled {
|
||||||
statsPath := blogPath + blogConfig.BlogStats.Path
|
statsPath := blogPath + blogConfig.BlogStats.Path
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(statsPath, serveBlogStats(blog, statsPath))
|
r.With(cacheMiddleware).Get(statsPath, serveBlogStats(blog, statsPath))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Year / month archives
|
// Year / month archives
|
||||||
|
@ -270,60 +285,57 @@ func buildHandler() (http.Handler, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, d := range dates {
|
r.Group(func(r chi.Router) {
|
||||||
// Year
|
r.Use(cacheMiddleware)
|
||||||
yearPath := blogPath + "/" + fmt.Sprintf("%0004d", d.year)
|
for _, d := range dates {
|
||||||
yearHandler := serveDate(blog, yearPath, d.year, 0, 0)
|
// Year
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(yearPath, yearHandler)
|
yearPath := blogPath + "/" + fmt.Sprintf("%0004d", d.year)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(yearPath+feedPath, yearHandler)
|
yearHandler := serveDate(blog, yearPath, d.year, 0, 0)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(yearPath+paginationPath, yearHandler)
|
r.Get(yearPath, yearHandler)
|
||||||
// Specific month
|
r.Get(yearPath+feedPath, yearHandler)
|
||||||
monthPath := yearPath + "/" + fmt.Sprintf("%02d", d.month)
|
r.Get(yearPath+paginationPath, yearHandler)
|
||||||
monthHandler := serveDate(blog, monthPath, d.year, d.month, 0)
|
// Specific month
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(monthPath, monthHandler)
|
monthPath := yearPath + "/" + fmt.Sprintf("%02d", d.month)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(monthPath+feedPath, monthHandler)
|
monthHandler := serveDate(blog, monthPath, d.year, d.month, 0)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(monthPath+paginationPath, monthHandler)
|
r.Get(monthPath, monthHandler)
|
||||||
// Specific day
|
r.Get(monthPath+feedPath, monthHandler)
|
||||||
dayPath := monthPath + "/" + fmt.Sprintf("%02d", d.day)
|
r.Get(monthPath+paginationPath, monthHandler)
|
||||||
dayHandler := serveDate(blog, monthPath, d.year, d.month, d.day)
|
// Specific day
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(dayPath, dayHandler)
|
dayPath := monthPath + "/" + fmt.Sprintf("%02d", d.day)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(dayPath+feedPath, dayHandler)
|
dayHandler := serveDate(blog, monthPath, d.year, d.month, d.day)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(dayPath+paginationPath, dayHandler)
|
r.Get(dayPath, dayHandler)
|
||||||
// Generic month
|
r.Get(dayPath+feedPath, dayHandler)
|
||||||
genericMonthPath := blogPath + "/x/" + fmt.Sprintf("%02d", d.month)
|
r.Get(dayPath+paginationPath, dayHandler)
|
||||||
genericMonthHandler := serveDate(blog, genericMonthPath, 0, d.month, 0)
|
// Generic month
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(genericMonthPath, genericMonthHandler)
|
genericMonthPath := blogPath + "/x/" + fmt.Sprintf("%02d", d.month)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(genericMonthPath+feedPath, genericMonthHandler)
|
genericMonthHandler := serveDate(blog, genericMonthPath, 0, d.month, 0)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(genericMonthPath+paginationPath, genericMonthHandler)
|
r.Get(genericMonthPath, genericMonthHandler)
|
||||||
// Specific day
|
r.Get(genericMonthPath+feedPath, genericMonthHandler)
|
||||||
genericMonthDayPath := genericMonthPath + "/" + fmt.Sprintf("%02d", d.day)
|
r.Get(genericMonthPath+paginationPath, genericMonthHandler)
|
||||||
genericMonthDayHandler := serveDate(blog, genericMonthDayPath, 0, d.month, d.day)
|
// Specific day
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(genericMonthDayPath, genericMonthDayHandler)
|
genericMonthDayPath := genericMonthPath + "/" + fmt.Sprintf("%02d", d.day)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(genericMonthDayPath+feedPath, genericMonthDayHandler)
|
genericMonthDayHandler := serveDate(blog, genericMonthDayPath, 0, d.month, d.day)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(genericMonthDayPath+paginationPath, genericMonthDayHandler)
|
r.Get(genericMonthDayPath, genericMonthDayHandler)
|
||||||
}
|
r.Get(genericMonthDayPath+feedPath, genericMonthDayHandler)
|
||||||
|
r.Get(genericMonthDayPath+paginationPath, genericMonthDayHandler)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Blog
|
// Blog
|
||||||
if !blogConfig.PostAsHome {
|
if !blogConfig.PostAsHome {
|
||||||
var mw []func(http.Handler) http.Handler
|
|
||||||
if appConfig.ActivityPub.Enabled {
|
|
||||||
mw = []func(http.Handler) http.Handler{manipulateAsPath, cacheMiddleware, minifier.Middleware}
|
|
||||||
} else {
|
|
||||||
mw = []func(http.Handler) http.Handler{cacheMiddleware, minifier.Middleware}
|
|
||||||
}
|
|
||||||
handler := serveHome(blog, blogPath)
|
handler := serveHome(blog, blogPath)
|
||||||
r.With(mw...).Get(fullBlogPath, handler)
|
r.With(manipulateAsPath, cacheMiddleware).Get(fullBlogPath, handler)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(fullBlogPath+feedPath, handler)
|
r.With(cacheMiddleware).Get(fullBlogPath+feedPath, handler)
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(blogPath+paginationPath, handler)
|
r.With(cacheMiddleware).Get(blogPath+paginationPath, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom pages
|
// Custom pages
|
||||||
for _, cp := range blogConfig.CustomPages {
|
for _, cp := range blogConfig.CustomPages {
|
||||||
handler := serveCustomPage(blogConfig, cp)
|
handler := serveCustomPage(blogConfig, cp)
|
||||||
if cp.Cache {
|
if cp.Cache {
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(cp.Path, handler)
|
r.With(cacheMiddleware).Get(cp.Path, handler)
|
||||||
} else {
|
} else {
|
||||||
r.With(minifier.Middleware).Get(cp.Path, handler)
|
r.Get(cp.Path, handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +350,7 @@ func buildHandler() (http.Handler, error) {
|
||||||
|
|
||||||
// Editor
|
// Editor
|
||||||
r.Route(blogPath+"/editor", func(mpRouter chi.Router) {
|
r.Route(blogPath+"/editor", func(mpRouter chi.Router) {
|
||||||
mpRouter.Use(middleware.NoCache, minifier.Middleware, authMiddleware)
|
mpRouter.Use(authMiddleware)
|
||||||
mpRouter.Get("/", serveEditor(blog))
|
mpRouter.Get("/", serveEditor(blog))
|
||||||
mpRouter.Post("/", serveEditorPost(blog))
|
mpRouter.Post("/", serveEditorPost(blog))
|
||||||
})
|
})
|
||||||
|
@ -347,25 +359,28 @@ func buildHandler() (http.Handler, error) {
|
||||||
if commentsConfig := blogConfig.Comments; commentsConfig != nil && commentsConfig.Enabled {
|
if commentsConfig := blogConfig.Comments; commentsConfig != nil && commentsConfig.Enabled {
|
||||||
commentsPath := blogPath + "/comment"
|
commentsPath := blogPath + "/comment"
|
||||||
r.Route(commentsPath, func(cr chi.Router) {
|
r.Route(commentsPath, func(cr chi.Router) {
|
||||||
cr.With(cacheMiddleware, minifier.Middleware).Get("/{id:[0-9]+}", serveComment(blog))
|
cr.With(cacheMiddleware).Get("/{id:[0-9]+}", serveComment(blog))
|
||||||
cr.With(captchaMiddleware).Post("/", createComment(blog, commentsPath))
|
cr.With(captchaMiddleware).Post("/", createComment(blog, commentsPath))
|
||||||
// Admin
|
// Admin
|
||||||
cr.With(minifier.Middleware, authMiddleware).Get("/", commentsAdmin)
|
cr.Group(func(r chi.Router) {
|
||||||
cr.With(authMiddleware).Post("/delete", commentsAdminDelete)
|
r.Use(authMiddleware)
|
||||||
|
r.Get("/", commentsAdmin)
|
||||||
|
r.Post("/delete", commentsAdminDelete)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sitemap
|
// Sitemap
|
||||||
r.With(cacheMiddleware, minifier.Middleware).Get(sitemapPath, serveSitemap)
|
r.With(cacheMiddleware).Get(sitemapPath, serveSitemap)
|
||||||
|
|
||||||
// Robots.txt - doesn't need cache, because it's too simple
|
// Robots.txt - doesn't need cache, because it's too simple
|
||||||
r.Get("/robots.txt", serveRobotsTXT)
|
r.Get("/robots.txt", serveRobotsTXT)
|
||||||
|
|
||||||
// Check redirects, then serve 404
|
// Check redirects, then serve 404
|
||||||
r.With(cacheMiddleware, checkRegexRedirects, minifier.Middleware).NotFound(serve404)
|
r.With(cacheMiddleware, checkRegexRedirects).NotFound(serve404)
|
||||||
|
|
||||||
r.With(minifier.Middleware).MethodNotAllowed(func(rw http.ResponseWriter, r *http.Request) {
|
r.MethodNotAllowed(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
serveError(rw, r, "", http.StatusMethodNotAllowed)
|
serveError(rw, r, "", http.StatusMethodNotAllowed)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
71
hugo.go
71
hugo.go
|
@ -1,71 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/jeremywohl/flatten"
|
|
||||||
"github.com/spf13/cast"
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseHugoFile(fileContent string) (p *post, aliases []string, e error) {
|
|
||||||
frontmatterSep := "---\n"
|
|
||||||
frontmatter := ""
|
|
||||||
if split := strings.Split(fileContent, frontmatterSep); len(split) > 2 {
|
|
||||||
frontmatter = split[1]
|
|
||||||
}
|
|
||||||
p = &post{
|
|
||||||
Content: strings.TrimPrefix(fileContent, frontmatterSep+frontmatter+frontmatterSep),
|
|
||||||
Parameters: map[string][]string{},
|
|
||||||
}
|
|
||||||
// Parse frontmatter
|
|
||||||
meta := map[string]interface{}{}
|
|
||||||
err := yaml.Unmarshal([]byte(frontmatter), &meta)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
flat, err := flatten.Flatten(meta, "", flatten.DotStyle)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
// Read dates
|
|
||||||
p.Published = cast.ToString(flat["date"])
|
|
||||||
p.Updated = cast.ToString(flat["lastmod"])
|
|
||||||
// Read parameters
|
|
||||||
for _, fm := range appConfig.Hugo.Frontmatter {
|
|
||||||
var values []string
|
|
||||||
for fk, value := range flat {
|
|
||||||
if strings.HasPrefix(fk, fm.Meta) {
|
|
||||||
trimmed := strings.TrimPrefix(fk, fm.Meta)
|
|
||||||
if len(trimmed) == 0 {
|
|
||||||
values = append(values, cast.ToString(value))
|
|
||||||
} else {
|
|
||||||
trimmed = strings.TrimPrefix(trimmed, ".")
|
|
||||||
if _, e := strconv.Atoi(trimmed); e == nil {
|
|
||||||
values = append(values, cast.ToString(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(values) > 0 {
|
|
||||||
p.Parameters[fm.Parameter] = values
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Parse redirects
|
|
||||||
for fk, value := range flat {
|
|
||||||
if strings.HasPrefix(fk, "aliases") {
|
|
||||||
trimmed := strings.TrimPrefix(fk, "aliases")
|
|
||||||
if len(trimmed) == 0 {
|
|
||||||
aliases = append(aliases, cast.ToString(value))
|
|
||||||
} else {
|
|
||||||
trimmed = strings.TrimPrefix(trimmed, ".")
|
|
||||||
if _, e := strconv.Atoi(trimmed); e == nil {
|
|
||||||
aliases = append(aliases, cast.ToString(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Return post
|
|
||||||
return p, aliases, nil
|
|
||||||
}
|
|
|
@ -134,16 +134,11 @@ func indieAuthVerification(w http.ResponseWriter, r *http.Request) {
|
||||||
serveError(w, r, "Authentication not valid", http.StatusForbidden)
|
serveError(w, r, "Authentication not valid", http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res := &tokenResponse{
|
b, _ := json.Marshal(tokenResponse{
|
||||||
Me: appConfig.Server.PublicAddress,
|
Me: appConfig.Server.PublicAddress,
|
||||||
}
|
})
|
||||||
w.Header().Add(contentType, contentTypeJSONUTF8)
|
w.Header().Set(contentType, contentTypeJSONUTF8)
|
||||||
err = json.NewEncoder(w).Encode(res)
|
_, _ = writeMinified(w, contentTypeJSON, b)
|
||||||
if err != nil {
|
|
||||||
w.Header().Del(contentType)
|
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func indieAuthToken(w http.ResponseWriter, r *http.Request) {
|
func indieAuthToken(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -159,13 +154,9 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
|
||||||
Me: appConfig.Server.PublicAddress,
|
Me: appConfig.Server.PublicAddress,
|
||||||
ClientID: data.ClientID,
|
ClientID: data.ClientID,
|
||||||
}
|
}
|
||||||
w.Header().Add(contentType, contentTypeJSONUTF8)
|
b, _ := json.Marshal(res)
|
||||||
err = json.NewEncoder(w).Encode(res)
|
w.Header().Set(contentType, contentTypeJSONUTF8)
|
||||||
if err != nil {
|
_, _ = writeMinified(w, contentTypeJSON, b)
|
||||||
w.Header().Del(contentType)
|
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
} else if r.Method == http.MethodPost {
|
} else if r.Method == http.MethodPost {
|
||||||
if err := r.ParseForm(); err != nil {
|
if err := r.ParseForm(); err != nil {
|
||||||
|
@ -216,13 +207,9 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
|
||||||
Scope: strings.Join(data.Scopes, " "),
|
Scope: strings.Join(data.Scopes, " "),
|
||||||
Me: appConfig.Server.PublicAddress,
|
Me: appConfig.Server.PublicAddress,
|
||||||
}
|
}
|
||||||
w.Header().Add(contentType, contentTypeJSONUTF8)
|
b, _ := json.Marshal(res)
|
||||||
err = json.NewEncoder(w).Encode(res)
|
w.Header().Set(contentType, contentTypeJSONUTF8)
|
||||||
if err != nil {
|
_, _ = writeMinified(w, contentTypeJSON, b)
|
||||||
w.Header().Del(contentType)
|
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
serveError(w, r, "", http.StatusBadRequest)
|
serveError(w, r, "", http.StatusBadRequest)
|
||||||
|
|
15
micropub.go
15
micropub.go
|
@ -25,11 +25,12 @@ type micropubConfig struct {
|
||||||
func serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
|
func serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
|
||||||
switch r.URL.Query().Get("q") {
|
switch r.URL.Query().Get("q") {
|
||||||
case "config":
|
case "config":
|
||||||
w.Header().Add(contentType, contentTypeJSONUTF8)
|
w.Header().Set(contentType, contentTypeJSONUTF8)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
_ = json.NewEncoder(w).Encode(µpubConfig{
|
b, _ := json.Marshal(µpubConfig{
|
||||||
MediaEndpoint: appConfig.Server.PublicAddress + micropubPath + micropubMediaSubPath,
|
MediaEndpoint: appConfig.Server.PublicAddress + micropubPath + micropubMediaSubPath,
|
||||||
})
|
})
|
||||||
|
_, _ = writeMinified(w, contentTypeJSON, b)
|
||||||
case "source":
|
case "source":
|
||||||
var mf interface{}
|
var mf interface{}
|
||||||
if urlString := r.URL.Query().Get("url"); urlString != "" {
|
if urlString := r.URL.Query().Get("url"); urlString != "" {
|
||||||
|
@ -61,9 +62,10 @@ func serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
mf = list
|
mf = list
|
||||||
}
|
}
|
||||||
w.Header().Add(contentType, contentTypeJSONUTF8)
|
w.Header().Set(contentType, contentTypeJSONUTF8)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
_ = json.NewEncoder(w).Encode(mf)
|
b, _ := json.Marshal(mf)
|
||||||
|
_, _ = writeMinified(w, contentTypeJSON, b)
|
||||||
case "category":
|
case "category":
|
||||||
allCategories := []string{}
|
allCategories := []string{}
|
||||||
for blog := range appConfig.Blogs {
|
for blog := range appConfig.Blogs {
|
||||||
|
@ -74,11 +76,12 @@ func serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
allCategories = append(allCategories, values...)
|
allCategories = append(allCategories, values...)
|
||||||
}
|
}
|
||||||
w.Header().Add(contentType, contentTypeJSONUTF8)
|
w.Header().Set(contentType, contentTypeJSONUTF8)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{
|
b, _ := json.Marshal(map[string]interface{}{
|
||||||
"categories": allCategories,
|
"categories": allCategories,
|
||||||
})
|
})
|
||||||
|
_, _ = writeMinified(w, contentTypeJSON, b)
|
||||||
default:
|
default:
|
||||||
serve404(w, r)
|
serve404(w, r)
|
||||||
}
|
}
|
||||||
|
|
15
minify.go
15
minify.go
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/tdewolff/minify/v2"
|
"github.com/tdewolff/minify/v2"
|
||||||
mCss "github.com/tdewolff/minify/v2/css"
|
mCss "github.com/tdewolff/minify/v2/css"
|
||||||
mHtml "github.com/tdewolff/minify/v2/html"
|
mHtml "github.com/tdewolff/minify/v2/html"
|
||||||
|
@ -17,7 +19,14 @@ func initMinify() {
|
||||||
minifier.AddFunc("text/css", mCss.Minify)
|
minifier.AddFunc("text/css", mCss.Minify)
|
||||||
minifier.AddFunc("text/xml", mXml.Minify)
|
minifier.AddFunc("text/xml", mXml.Minify)
|
||||||
minifier.AddFunc("application/javascript", mJs.Minify)
|
minifier.AddFunc("application/javascript", mJs.Minify)
|
||||||
minifier.AddFunc("application/rss+xml", mXml.Minify)
|
minifier.AddFunc(contentTypeRSS, mXml.Minify)
|
||||||
minifier.AddFunc("application/atom+xml", mXml.Minify)
|
minifier.AddFunc(contentTypeATOM, mXml.Minify)
|
||||||
minifier.AddFunc("application/feed+json", mJson.Minify)
|
minifier.AddFunc(contentTypeJSONFeed, mJson.Minify)
|
||||||
|
minifier.AddFunc(contentTypeAS, mJson.Minify)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeMinified(w io.Writer, mediatype string, b []byte) (int, error) {
|
||||||
|
mw := minifier.Writer(mediatype, w)
|
||||||
|
defer func() { mw.Close() }()
|
||||||
|
return mw.Write(b)
|
||||||
}
|
}
|
||||||
|
|
16
nodeinfo.go
16
nodeinfo.go
|
@ -6,24 +6,23 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) {
|
func serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set(contentType, contentTypeJSONUTF8)
|
b, _ := json.Marshal(map[string]interface{}{
|
||||||
nid := map[string]interface{}{
|
|
||||||
"links": []map[string]interface{}{
|
"links": []map[string]interface{}{
|
||||||
{
|
{
|
||||||
"href": appConfig.Server.PublicAddress + "/nodeinfo",
|
"href": appConfig.Server.PublicAddress + "/nodeinfo",
|
||||||
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.1",
|
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
_ = json.NewEncoder(w).Encode(&nid)
|
w.Header().Set(contentType, contentTypeJSONUTF8)
|
||||||
|
_, _ = writeMinified(w, contentTypeJSON, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveNodeInfo(w http.ResponseWriter, r *http.Request) {
|
func serveNodeInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set(contentType, contentTypeJSONUTF8)
|
|
||||||
localPosts, _ := countPosts(&postsRequestConfig{
|
localPosts, _ := countPosts(&postsRequestConfig{
|
||||||
status: statusPublished,
|
status: statusPublished,
|
||||||
})
|
})
|
||||||
nid := map[string]interface{}{
|
b, _ := json.Marshal(map[string]interface{}{
|
||||||
"version": "2.1",
|
"version": "2.1",
|
||||||
"software": map[string]interface{}{
|
"software": map[string]interface{}{
|
||||||
"name": "goblog",
|
"name": "goblog",
|
||||||
|
@ -41,6 +40,7 @@ func serveNodeInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
"webmention",
|
"webmention",
|
||||||
},
|
},
|
||||||
"metadata": map[string]interface{}{},
|
"metadata": map[string]interface{}{},
|
||||||
}
|
})
|
||||||
_ = json.NewEncoder(w).Encode(&nid)
|
w.Header().Set(contentType, contentTypeJSONUTF8)
|
||||||
|
_, _ = writeMinified(w, contentTypeJSON, b)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,9 @@ func allPostAliases() ([]string, error) {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var path string
|
var path string
|
||||||
_ = rows.Scan(&path)
|
_ = rows.Scan(&path)
|
||||||
aliases = append(aliases, path)
|
if path != "" {
|
||||||
|
aliases = append(aliases, path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return aliases, nil
|
return aliases, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -396,7 +396,9 @@ func allPostPaths(status postStatus) ([]string, error) {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var path string
|
var path string
|
||||||
_ = rows.Scan(&path)
|
_ = rows.Scan(&path)
|
||||||
postPaths = append(postPaths, path)
|
if path != "" {
|
||||||
|
postPaths = append(postPaths, path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return postPaths, nil
|
return postPaths, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,8 +264,8 @@ func render(w http.ResponseWriter, template string, data *renderData) {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Set content type (needed for minification middleware
|
// Set content type
|
||||||
w.Header().Set(contentType, contentTypeHTMLUTF8)
|
w.Header().Set(contentType, contentTypeHTMLUTF8)
|
||||||
// Write buffered response
|
// Write buffered response
|
||||||
_, _ = w.Write(buffer.Bytes())
|
_, _ = writeMinified(w, contentTypeHTML, buffer.Bytes())
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,5 +35,5 @@ func serveSitemap(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
sm.Add(item)
|
sm.Add(item)
|
||||||
}
|
}
|
||||||
_, _ = sm.WriteTo(w)
|
_, _ = sm.WriteTo(w) // Already minified
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,6 @@ func serveAsset(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Cache-Control", "public,max-age=31536000,immutable")
|
w.Header().Set("Cache-Control", "public,max-age=31536000,immutable")
|
||||||
w.Header().Set(contentType, af.contentType)
|
w.Header().Set(contentType, af.contentType+charsetUtf8Suffix)
|
||||||
_, _ = w.Write(af.body)
|
_, _ = w.Write(af.body)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue