diff --git a/activityPub.go b/activityPub.go index 4cf0966..5f4159a 100644 --- a/activityPub.go +++ b/activityPub.go @@ -221,7 +221,7 @@ func apGetRemoteActor(iri string) (*asPerson, int, error) { return nil, 0, err } req.Header.Set("Accept", contentTypeAS) - req.Header.Set("User-Agent", "GoBlog") + req.Header.Set(userAgent, appUserAgent) resp, err := http.DefaultClient.Do(req) if err != nil { return nil, 0, err @@ -273,7 +273,7 @@ func (p *post) apPost() { createActivity := make(map[string]interface{}) createActivity["@context"] = asContext createActivity["actor"] = appConfig.Blogs[p.Blog].apIri() - createActivity["id"] = appConfig.Server.PublicAddress + p.Path + createActivity["id"] = p.fullURL() createActivity["published"] = n.Published createActivity["type"] = "Create" createActivity["object"] = n @@ -293,7 +293,7 @@ func (p *post) apUpdate() { updateActivity := make(map[string]interface{}) updateActivity["@context"] = asContext updateActivity["actor"] = appConfig.Blogs[p.Blog].apIri() - updateActivity["id"] = appConfig.Server.PublicAddress + p.Path + updateActivity["id"] = p.fullURL() updateActivity["published"] = time.Now().Format("2006-01-02T15:04:05-07:00") updateActivity["type"] = "Update" updateActivity["object"] = n @@ -307,10 +307,10 @@ func (p *post) apAnnounce() { announceActivity := make(map[string]interface{}) announceActivity["@context"] = asContext announceActivity["actor"] = appConfig.Blogs[p.Blog].apIri() - announceActivity["id"] = appConfig.Server.PublicAddress + p.Path + "#announce" + announceActivity["id"] = p.fullURL() + "#announce" announceActivity["published"] = p.toASNote().Published announceActivity["type"] = "Announce" - announceActivity["object"] = appConfig.Server.PublicAddress + p.Path + announceActivity["object"] = p.fullURL() apSendToAllFollowers(p.Blog, announceActivity) } @@ -321,10 +321,10 @@ func (p *post) apDelete() { deleteActivity := make(map[string]interface{}) deleteActivity["@context"] = asContext deleteActivity["actor"] = appConfig.Blogs[p.Blog].apIri() - deleteActivity["id"] = appConfig.Server.PublicAddress + p.Path + "#delete" + deleteActivity["id"] = p.fullURL() + "#delete" deleteActivity["type"] = "Delete" deleteActivity["object"] = map[string]string{ - "id": appConfig.Server.PublicAddress + p.Path, + "id": p.fullURL(), "type": "Tombstone", } apSendToAllFollowers(p.Blog, deleteActivity) @@ -405,7 +405,7 @@ func apSendSigned(blog *configBlog, activity interface{}, to string) error { } r.Header.Set("Accept-Charset", "utf-8") r.Header.Set("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05")+" GMT") - r.Header.Set("User-Agent", "GoBlog") + r.Header.Set(userAgent, appUserAgent) r.Header.Set("Accept", contentTypeASUTF8) r.Header.Set(contentType, contentTypeASUTF8) r.Header.Set("Host", iri.Host) diff --git a/activityStreams.go b/activityStreams.go index 8ca20c1..cac1a50 100644 --- a/activityStreams.go +++ b/activityStreams.go @@ -74,8 +74,8 @@ func (p *post) toASNote() *asNote { Context: asContext, To: []string{"https://www.w3.org/ns/activitystreams#Public"}, MediaType: contentTypeHTML, - ID: appConfig.Server.PublicAddress + p.Path, - URL: appConfig.Server.PublicAddress + p.Path, + ID: p.fullURL(), + URL: p.fullURL(), AttributedTo: appConfig.Blogs[p.Blog].apIri(), } // Name and Type @@ -133,8 +133,8 @@ func (b *configBlog) serveActivityStreams(blog string, w http.ResponseWriter) { PreferredUsername: blog, Inbox: appConfig.Server.PublicAddress + "/activitypub/inbox/" + blog, PublicKey: &asPublicKey{ - Owner: appConfig.Server.PublicAddress + b.Path, - ID: appConfig.Server.PublicAddress + b.Path + "#main-key", + Owner: b.apIri(), + ID: b.apIri() + "#main-key", PublicKeyPem: string(pem.EncodeToMemory(&pem.Block{ Type: "PUBLIC KEY", Headers: nil, diff --git a/api.go b/api.go index 3eebd37..9dd3561 100644 --- a/api.go +++ b/api.go @@ -55,6 +55,6 @@ func apiPostCreateHugo(w http.ResponseWriter, r *http.Request) { return } } - w.Header().Set("Location", appConfig.Server.PublicAddress+p.Path) + w.Header().Set("Location", p.fullURL()) w.WriteHeader(http.StatusCreated) } diff --git a/feeds.go b/feeds.go index d84f2f9..04937d4 100644 --- a/feeds.go +++ b/feeds.go @@ -55,7 +55,7 @@ func generateFeed(blog string, f feedType, w http.ResponseWriter, r *http.Reques } feed.Add(&feeds.Item{ Title: p.title(), - Link: &feeds.Link{Href: appConfig.Server.PublicAddress + p.Path}, + Link: &feeds.Link{Href: p.fullURL()}, Description: p.summary(), Id: p.Path, Content: string(p.html()), diff --git a/go.mod b/go.mod index a83fa6a..15130da 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/kyokomi/emoji v2.2.4+incompatible github.com/lopezator/migrator v0.3.0 github.com/magiconair/properties v1.8.4 // indirect - github.com/mattn/go-sqlite3 v1.14.4 + github.com/mattn/go-sqlite3 v1.14.5 github.com/miekg/dns v1.1.35 // indirect github.com/mitchellh/mapstructure v1.3.3 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect @@ -37,23 +37,23 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.7.1 github.com/tdewolff/minify/v2 v2.9.10 + github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 github.com/vcraescu/go-paginator v1.0.0 github.com/yuin/goldmark v1.2.1 github.com/yuin/goldmark-emoji v1.0.1 go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.16.0 // indirect - golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 + golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582 golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 - golang.org/x/sys v0.0.0-20201113233024-12cec1faf1ba // indirect + golang.org/x/sys v0.0.0-20201116161645-c061ba923fbb // indirect golang.org/x/text v0.3.4 // indirect - golang.org/x/tools v0.0.0-20201116002733-ac45abd4c88c // indirect + golang.org/x/tools v0.0.0-20201116172350-d68bbb546781 // indirect gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 honnef.co/go/tools v0.0.1-2020.1.6 // indirect willnorris.com/go/microformats v1.1.1 - willnorris.com/go/webmention v0.0.0-20200623235404-057ea514ab98 ) diff --git a/go.sum b/go.sum index 3a38088..316bd41 100644 --- a/go.sum +++ b/go.sum @@ -208,8 +208,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= -github.com/mattn/go-sqlite3 v1.14.4 h1:4rQjbDxdu9fSgI/r3KN72G3c2goxknAqHHgPWWs8UlI= -github.com/mattn/go-sqlite3 v1.14.4/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= +github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ= +github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mholt/acmez v0.1.1 h1:KQODCqk+hBn3O7qfCRPj6L96uG65T5BSS95FKNEqtdA= @@ -315,10 +315,10 @@ github.com/tdewolff/parse/v2 v2.5.5/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1I github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= +github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= github.com/vcraescu/go-paginator v1.0.0 h1:ilNmRhlgG8N44LuxfGoPI2u8guXMA6gUqaPGA5BmRFs= github.com/vcraescu/go-paginator v1.0.0/go.mod h1:caZCjjt2qcA1O2aDzW7lwAcK4Rxw3LNvdEVF/ONxZWw= -github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ= -github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= @@ -357,8 +357,8 @@ golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU= -golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582 h1:0WDrJ1E7UolDk1KhTXxxw3Fc8qtk5x7dHP431KHEJls= +golang.org/x/crypto v0.0.0-20201116153603-4be66e5b6582/go.mod h1:tCqSYrHVcf3i63Co2FzBkTCo2gdF6Zak62921dSfraU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -387,7 +387,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -438,12 +437,15 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c= golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201113233024-12cec1faf1ba h1:xmhUJGQGbxlod18iJGqVEp9cHIPLl7QiX2aA3to708s= -golang.org/x/sys v0.0.0-20201113233024-12cec1faf1ba/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201116161645-c061ba923fbb h1:+EHGEcgeA7ESswi5i4ojbo7sRzlz7vWoxFGcMuEZtu8= +golang.org/x/sys v0.0.0-20201116161645-c061ba923fbb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201113234701-d7a72108b828 h1:htWEtQEuEVJ4tU/Ngx7Cd/4Q7e3A5Up1owgyBtVsTwk= +golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -478,8 +480,8 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnf golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201116002733-ac45abd4c88c h1:quJUizHRFn7XriXTIOCLKSr76x2cMbNGfvfy9ubOO0g= -golang.org/x/tools v0.0.0-20201116002733-ac45abd4c88c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201116172350-d68bbb546781 h1:pupwog4teA+VTW6Kpi3Z6f0AR4+5MBi0+AN5ym9fzaQ= +golang.org/x/tools v0.0.0-20201116172350-d68bbb546781/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -544,5 +546,3 @@ honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzE rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= willnorris.com/go/microformats v1.1.1 h1:h5tk2luq6KBIRcwMGdksxdeea4GGuWrRFie5460OAbo= willnorris.com/go/microformats v1.1.1/go.mod h1:kvVnWrkkEscVAIITCEoiTX66Hcyg59C7q0E49mb9TJ0= -willnorris.com/go/webmention v0.0.0-20200623235404-057ea514ab98 h1:06Zf8bMVQ+OBHceFvlr/SYZyYnkyVlWIkvediXHyQQU= -willnorris.com/go/webmention v0.0.0-20200623235404-057ea514ab98/go.mod h1:p+ZRAsZS2pzZ6kX3GKWYurf3WZI2ygj7VbR8NM8qwfM= diff --git a/hooks.go b/hooks.go index ca13f11..a38c448 100644 --- a/hooks.go +++ b/hooks.go @@ -21,7 +21,7 @@ func (p *post) postPostHooks() { for _, cmdTmplString := range appConfig.Hooks.PostPost { go func(p *post, cmdTmplString string) { executeTemplateCommand("post-post", cmdTmplString, map[string]interface{}{ - "URL": appConfig.Server.PublicAddress + p.Path, + "URL": p.fullURL(), "Post": p, }) }(p, cmdTmplString) @@ -38,7 +38,7 @@ func (p *post) postUpdateHooks() { for _, cmdTmplString := range appConfig.Hooks.PostUpdate { go func(p *post, cmdTmplString string) { executeTemplateCommand("post-update", cmdTmplString, map[string]interface{}{ - "URL": appConfig.Server.PublicAddress + p.Path, + "URL": p.fullURL(), "Post": p, }) }(p, cmdTmplString) @@ -54,7 +54,7 @@ func (p *post) postDeleteHooks() { for _, cmdTmplString := range appConfig.Hooks.PostDelete { go func(p *post, cmdTmplString string) { executeTemplateCommand("post-delete", cmdTmplString, map[string]interface{}{ - "URL": appConfig.Server.PublicAddress + p.Path, + "URL": p.fullURL(), "Post": p, }) }(p, cmdTmplString) diff --git a/http.go b/http.go index 1418373..d8fdbb7 100644 --- a/http.go +++ b/http.go @@ -27,6 +27,9 @@ const ( contentTypeHTMLUTF8 = contentTypeHTML + charsetUtf8Suffix contentTypeJSONUTF8 = contentTypeJSON + charsetUtf8Suffix contentTypeASUTF8 = contentTypeAS + charsetUtf8Suffix + + userAgent = "User-Agent" + appUserAgent = "GoBlog" ) var ( diff --git a/micropub.go b/micropub.go index 0a1bd3f..c8fea15 100644 --- a/micropub.go +++ b/micropub.go @@ -100,7 +100,7 @@ func (p *post) toMfItem() *microformatItem { Updated: []string{p.Updated}, Category: p.Parameters[appConfig.Micropub.CategoryParam], Content: []string{content}, - URL: []string{appConfig.Server.PublicAddress + p.Path}, + URL: []string{p.fullURL()}, InReplyTo: p.Parameters[appConfig.Micropub.ReplyParam], LikeOf: p.Parameters[appConfig.Micropub.LikeParam], BookmarkOf: p.Parameters[appConfig.Micropub.BookmarkParam], @@ -181,7 +181,7 @@ func serveMicropubPost(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } - w.Header().Add("Location", appConfig.Server.PublicAddress+p.Path) + w.Header().Add("Location", p.fullURL()) w.WriteHeader(http.StatusAccepted) return } diff --git a/posts.go b/posts.go index 8b9b1f5..c532cc1 100644 --- a/posts.go +++ b/posts.go @@ -47,7 +47,7 @@ func servePost(w http.ResponseWriter, r *http.Request) { } canonical := p.firstParameter("original") if canonical == "" { - canonical = appConfig.Server.PublicAddress + p.Path + canonical = p.fullURL() } render(w, templatePost, &renderData{ blogString: p.Blog, @@ -56,18 +56,6 @@ func servePost(w http.ResponseWriter, r *http.Request) { }) } -type indexTemplateData struct { - Blog string - Title string - Description string - Posts []*post - HasPrev bool - HasNext bool - First string - Prev string - Next string -} - type postPaginationAdapter struct { config *postsRequestConfig nums int @@ -148,29 +136,32 @@ func serveTaxonomyValue(blog string, path string, tax *taxonomy, value string) f func servePhotos(blog string, path string) func(w http.ResponseWriter, r *http.Request) { return serveIndex(&indexConfig{ - blog: blog, - path: path, - parameter: appConfig.Blogs[blog].Photos.Parameter, - template: templatePhotos, + blog: blog, + path: path, + parameter: appConfig.Blogs[blog].Photos.Parameter, + title: appConfig.Blogs[blog].Photos.Title, + description: appConfig.Blogs[blog].Photos.Description, + summaryTemplate: templatePhotosSummary, }) } func serveSearchResults(blog string, path string) func(w http.ResponseWriter, r *http.Request) { return serveIndex(&indexConfig{ - blog: blog, - path: path, - template: templateIndex, + blog: blog, + path: path, }) } type indexConfig struct { - blog string - path string - section *section - tax *taxonomy - taxValue string - parameter string - template string + blog string + path string + section *section + tax *taxonomy + taxValue string + parameter string + title string + description string + summaryTemplate string } func serveIndex(ic *indexConfig) func(w http.ResponseWriter, r *http.Request) { @@ -205,7 +196,8 @@ func serveIndex(ic *indexConfig) func(w http.ResponseWriter, r *http.Request) { return } // Meta - var title, description string + title := ic.title + description := ic.description if ic.tax != nil { title = fmt.Sprintf("%s: %s", ic.tax.Title, ic.taxValue) } else if ic.section != nil { @@ -238,22 +230,23 @@ func serveIndex(ic *indexConfig) func(w http.ResponseWriter, r *http.Request) { nextPage = p.Page() } nextPath := fmt.Sprintf("%s/page/%d", path, nextPage) - template := ic.template - if len(template) == 0 { - template = templateIndex + summaryTemplate := ic.summaryTemplate + if summaryTemplate == "" { + summaryTemplate = templateSummary } - render(w, template, &renderData{ + render(w, templateIndex, &renderData{ blogString: ic.blog, Canonical: appConfig.Server.PublicAddress + getBlogRelativePath(ic.blog, path), - Data: &indexTemplateData{ - Title: title, - Description: description, - Posts: posts, - HasPrev: p.HasPrev(), - HasNext: p.HasNext(), - First: getBlogRelativePath(ic.blog, path), - Prev: getBlogRelativePath(ic.blog, prevPath), - Next: getBlogRelativePath(ic.blog, nextPath), + Data: map[string]interface{}{ + "Title": title, + "Description": description, + "Posts": posts, + "HasPrev": p.HasPrev(), + "HasNext": p.HasNext(), + "First": getBlogRelativePath(ic.blog, path), + "Prev": getBlogRelativePath(ic.blog, prevPath), + "Next": getBlogRelativePath(ic.blog, nextPath), + "SummaryTemplate": summaryTemplate, }, }) } diff --git a/postsFuncs.go b/postsFuncs.go index 68f28b0..63e9f0b 100644 --- a/postsFuncs.go +++ b/postsFuncs.go @@ -8,6 +8,10 @@ import ( "github.com/PuerkitoBio/goquery" ) +func (p *post) fullURL() string { + return appConfig.Server.PublicAddress + p.Path +} + func (p *post) firstParameter(parameter string) (result string) { if pp := p.Parameters[parameter]; len(pp) > 0 { result = pp[0] diff --git a/render.go b/render.go index eff0bf7..fd097bf 100644 --- a/render.go +++ b/render.go @@ -27,8 +27,9 @@ const templatePost = "post" const templateError = "error" const templateIndex = "index" const templateTaxonomy = "taxonomy" -const templatePhotos = "photos" const templateSearch = "search" +const templateSummary = "summary" +const templatePhotosSummary = "photosummary" var templates map[string]*template.Template var templateFunctions template.FuncMap @@ -76,7 +77,7 @@ func initRendering() error { }, "postmentions": func(p *post) []*mention { mentions, _ := getWebmentions(&webmentionsRequestConfig{ - target: appConfig.Server.PublicAddress + p.Path, + target: p.fullURL(), status: webmentionStatusApproved, asc: true, }) diff --git a/sitemap.go b/sitemap.go index f4e5c0e..cb8064b 100644 --- a/sitemap.go +++ b/sitemap.go @@ -19,7 +19,7 @@ func serveSitemap(w http.ResponseWriter, r *http.Request) { sm.Minify = true for _, p := range posts { item := &sitemap.URL{ - Loc: appConfig.Server.PublicAddress + p.Path} + Loc: p.fullURL()} var lastMod time.Time if p.Updated != "" { lastMod, _ = dateparse.ParseIn(p.Updated, time.Local) diff --git a/telegram.go b/telegram.go index 8053d5b..3819a2b 100644 --- a/telegram.go +++ b/telegram.go @@ -18,7 +18,7 @@ func (p *post) tgPost() { message.WriteString(title) message.WriteString("\n\n") } - message.WriteString(appConfig.Server.PublicAddress + p.Path) + message.WriteString(p.fullURL()) sendTelegramMessage(message.String(), appConfig.Blogs[p.Blog].Telegram.BotToken, appConfig.Blogs[p.Blog].Telegram.ChatID) } diff --git a/templates/index.gohtml b/templates/index.gohtml index dc57001..d9c1513 100644 --- a/templates/index.gohtml +++ b/templates/index.gohtml @@ -13,8 +13,9 @@
{{ end }} {{ $blog := .Blog }} + {{ $summaryTemplate := .Data.SummaryTemplate }} {{ range $i, $post := .Data.Posts }} - {{ include "summary" $blog $post }} + {{ include $summaryTemplate $blog $post }} {{ end }} {{ if .Data.HasPrev }}

{{ string .Blog.Lang "prev" }}

diff --git a/templates/photos.gohtml b/templates/photos.gohtml deleted file mode 100644 index 25d5437..0000000 --- a/templates/photos.gohtml +++ /dev/null @@ -1,27 +0,0 @@ -{{ define "title" }} - {{ with .Blog.Photos.Title }}{{ . }} - {{ end }}{{ .Blog.Title }} -{{ end }} - -{{ define "main" }} -
- {{ with .Blog.Photos.Title }}

{{ . }}

{{ end }} - {{ with .Blog.Photos.Description }}{{ md . }}{{ end }} - {{ if (or .Blog.Photos.Title .Blog.Photos.Description) }} -
- {{ end }} - {{ $blog := .Blog }} - {{ range $i, $post := .Data.Posts }} - {{ include "photosummary" $blog $post }} - {{ end }} - {{ if .Data.HasPrev }} -

{{ string .Blog.Lang "prev" }}

- {{ end }} - {{ if .Data.HasNext }} -

{{ string .Blog.Lang "next" }}

- {{ end }} -
-{{ end }} - -{{ define "photos" }} - {{ template "base" . }} -{{ end }} \ No newline at end of file diff --git a/templates/postbasic.gohtml b/templates/postbasic.gohtml deleted file mode 100644 index 8f8006d..0000000 --- a/templates/postbasic.gohtml +++ /dev/null @@ -1,23 +0,0 @@ -{{ define "title" }}{{ end }} - -{{ define "main" }} -
-
- - {{ with title .Data }}

{{ . }}

{{ end }} - {{ include "postmeta" . }} - {{ if .Data.Content }} -
- {{ content .Data }} - {{ with p .Data "link" }} -

{{ . }}

- {{ end }} -
- {{ end }} -
-
-{{ end }} - -{{ define "postbasic" }} - {{ template "base" . }} -{{ end }} \ No newline at end of file diff --git a/templates/webmentionadmin.gohtml b/templates/webmentionadmin.gohtml index 96b5380..c4aabed 100644 --- a/templates/webmentionadmin.gohtml +++ b/templates/webmentionadmin.gohtml @@ -9,7 +9,11 @@ {{ $blog := .Blog }} {{ range $i, $mention := .Data.Verified }}
-

From: {{ $mention.Source }}
To: {{ $mention.Target }}
Created: {{ unixtodate $mention.Created }}

+

+ From: {{ $mention.Source }}
+ To: {{ $mention.Target }}
+ Created: {{ unixtodate $mention.Created }} +

diff --git a/utils.go b/utils.go index a93d770..4848e13 100644 --- a/utils.go +++ b/utils.go @@ -71,10 +71,6 @@ func isAbsoluteURL(s string) bool { } func allLinksFromHTML(r io.Reader, baseURL string) ([]string, error) { - bu, err := url.Parse(baseURL) - if err != nil { - return nil, err - } doc, err := goquery.NewDocumentFromReader(r) if err != nil { return nil, err @@ -82,10 +78,25 @@ func allLinksFromHTML(r io.Reader, baseURL string) ([]string, error) { links := []string{} doc.Find("a[href]").Each(func(_ int, item *goquery.Selection) { if href, exists := item.Attr("href"); exists { - if ref, err := url.Parse(href); err == nil { - links = append(links, bu.ResolveReference(ref).String()) - } + links = append(links, href) } }) - return links, nil + links, err = resolveURLReferences(baseURL, links...) + return links, err +} + +func resolveURLReferences(base string, refs ...string) ([]string, error) { + b, err := url.Parse(base) + if err != nil { + return nil, err + } + var urls []string + for _, r := range refs { + u, err := url.Parse(r) + if err != nil { + continue + } + urls = append(urls, b.ResolveReference(u).String()) + } + return urls, nil } diff --git a/webmentions.go b/webmention.go similarity index 71% rename from webmentions.go rename to webmention.go index 03af080..c1e517e 100644 --- a/webmentions.go +++ b/webmention.go @@ -4,7 +4,6 @@ import ( "database/sql" "errors" "fmt" - "log" "net/http" "net/http/httptest" "strconv" @@ -12,7 +11,6 @@ import ( "time" "github.com/go-chi/chi" - "willnorris.com/go/webmention" ) type webmentionStatus string @@ -38,15 +36,6 @@ func initWebmention() { startWebmentionVerifier() } -func startWebmentionVerifier() { - go func() { - for { - time.Sleep(30 * time.Second) - verifyNextWebmention() - } - }() -} - func handleWebmention(w http.ResponseWriter, r *http.Request) { m, err := extractMention(r) if err != nil { @@ -149,37 +138,6 @@ func webmentionExists(source, target string) bool { return result == 1 } -func verifyNextWebmention() error { - m := &mention{} - oldStatus := "" - row, err := appDbQueryRow("select id, source, target, status from webmentions where (status = ? or status = ?) limit 1", webmentionStatusNew, webmentionStatusRenew) - if err != nil { - return err - } - if err := row.Scan(&m.ID, &m.Source, &m.Target, &oldStatus); err == sql.ErrNoRows { - return nil - } else if err != nil { - return err - } - if err := wmVerify(m); err != nil { - // Invalid - return deleteWebmention(m.ID) - } - if len(m.Content) > 500 { - m.Content = m.Content[0:497] + "…" - } - newStatus := webmentionStatusVerified - if strings.HasPrefix(m.Source, appConfig.Server.PublicAddress) { - // Approve if it's server-intern - newStatus = webmentionStatusApproved - } - _, err = appDbExec("update webmentions set status = ?, title = ?, content = ?, author = ? where id = ?", newStatus, m.Title, m.Content, m.Author, m.ID) - if oldStatus == string(webmentionStatusNew) { - sendNotification(fmt.Sprintf("New webmention from %s to %s", m.Source, m.Target)) - } - return err -} - func createWebmention(source, target string) (err error) { if webmentionExists(source, target) { _, err = appDbExec("update webmentions set status = ? where source = ? and target = ?", webmentionStatusRenew, source, target) @@ -241,36 +199,3 @@ func getWebmentions(config *webmentionsRequestConfig) ([]*mention, error) { } return mentions, nil } - -func (p *post) sendWebmentions() error { - url := appConfig.Server.PublicAddress + p.Path - recorder := httptest.NewRecorder() - // Render basic post data - render(recorder, "postbasic", &renderData{ - blogString: p.Blog, - Data: p, - }) - discovered, err := webmention.DiscoverLinksFromReader(recorder.Result().Body, url, ".h-entry") - if err != nil { - return err - } - client := webmention.New(nil) - for _, link := range discovered { - if strings.HasPrefix(link, appConfig.Server.PublicAddress) { - // Save mention directly - createWebmention(url, link) - continue - } - endpoint, err := client.DiscoverEndpoint(link) - if err != nil || len(endpoint) < 1 { - continue - } - _, err = client.SendWebmention(endpoint, url, link) - if err != nil { - log.Println("Sending webmention to " + link + " failed") - continue - } - log.Println("Sent webmention to " + link) - } - return nil -} diff --git a/webmentionSending.go b/webmentionSending.go new file mode 100644 index 0000000..04c69a8 --- /dev/null +++ b/webmentionSending.go @@ -0,0 +1,131 @@ +package main + +import ( + "fmt" + "io" + "log" + "net/http" + "net/url" + "strings" + + "github.com/PuerkitoBio/goquery" + "github.com/tomnomnom/linkheader" +) + +func (p *post) sendWebmentions() error { + links := []string{} + contentLinks, err := allLinksFromHTML(strings.NewReader(string(p.html())), p.fullURL()) + if err != nil { + return err + } + links = append(links, contentLinks...) + links = append(links, p.firstParameter(appConfig.Micropub.LikeParam), p.firstParameter(appConfig.Micropub.ReplyParam), p.firstParameter(appConfig.Micropub.BookmarkParam)) + for _, link := range links { + if link == "" { + continue + } + if strings.HasPrefix(link, appConfig.Server.PublicAddress) { + // Save mention directly + createWebmention(p.fullURL(), link) + continue + } + endpoint := discoverEndpoint(link) + if endpoint == "" { + continue + } + _, err = sendWebmention(endpoint, p.fullURL(), link) + if err != nil { + log.Println("Sending webmention to " + link + " failed") + continue + } + log.Println("Sent webmention to " + link) + } + return nil +} + +func sendWebmention(endpoint, source, target string) (*http.Response, error) { + req, err := http.NewRequest(http.MethodPost, endpoint, strings.NewReader(url.Values{ + "source": []string{source}, + "target": []string{target}, + }.Encode())) + if err != nil { + return nil, err + } + req.Header.Set(contentType, contentTypeWWWForm) + req.Header.Set(userAgent, appUserAgent) + res, err := http.DefaultClient.Do(req) + if err != nil { + return res, err + } + if code := res.StatusCode; code < 200 || 300 <= code { + return res, fmt.Errorf("response error: %v", res.StatusCode) + } + return res, nil +} + +func discoverEndpoint(urlStr string) string { + doRequest := func(method, urlStr string) string { + req, err := http.NewRequest(method, urlStr, nil) + if err != nil { + return "" + } + req.Header.Set(userAgent, appUserAgent) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "" + } + if code := resp.StatusCode; code < 200 || 300 <= code { + return "" + } + defer resp.Body.Close() + endpoint, err := extractEndpoint(resp) + if err != nil || endpoint == "" { + return "" + } + if urls, err := resolveURLReferences(urlStr, endpoint); err == nil && len(urls) > 0 && urls[0] != "" { + return urls[0] + } + return "" + } + headEndpoint := doRequest(http.MethodHead, urlStr) + if headEndpoint != "" { + return headEndpoint + } + getEndpoint := doRequest(http.MethodGet, urlStr) + if getEndpoint != "" { + return getEndpoint + } + return "" +} + +func extractEndpoint(resp *http.Response) (string, error) { + // first check http link headers + if endpoint := wmEndpointHTTPLink(resp.Header); endpoint != "" { + return endpoint, nil + } + // then look in the HTML body + endpoint, err := wmEndpointHTMLLink(resp.Body) + if err != nil { + return "", err + } + return endpoint, nil +} + +func wmEndpointHTTPLink(headers http.Header) string { + links := linkheader.ParseMultiple(headers[http.CanonicalHeaderKey("Link")]).FilterByRel("webmention") + for _, link := range links { + if u := link.URL; u != "" { + return u + } + } + return "" +} + +func wmEndpointHTMLLink(r io.Reader) (string, error) { + doc, err := goquery.NewDocumentFromReader(r) + if err != nil { + return "", err + } + href, _ := doc.Find("a[href][rel=webmention],link[href][rel=webmention]").Attr("href") + return href, nil +} diff --git a/webmentionsVerify.go b/webmentionVerification.go similarity index 65% rename from webmentionsVerify.go rename to webmentionVerification.go index 70a79d7..ad2fefe 100644 --- a/webmentionsVerify.go +++ b/webmentionVerification.go @@ -2,29 +2,66 @@ package main import ( "bytes" + "database/sql" "errors" + "fmt" "io" "net/http" "net/url" + "strings" + "time" "github.com/PuerkitoBio/goquery" "willnorris.com/go/microformats" ) -func wmVerify(m *mention) error { - client := &http.Client{} - client.CheckRedirect = func(r *http.Request, via []*http.Request) error { - if len(via) > 15 { - return errors.New("too many redirects") +func startWebmentionVerifier() { + go func() { + for { + time.Sleep(30 * time.Second) + verifyNextWebmention() } - return nil + }() +} + +func verifyNextWebmention() error { + m := &mention{} + oldStatus := "" + row, err := appDbQueryRow("select id, source, target, status from webmentions where (status = ? or status = ?) limit 1", webmentionStatusNew, webmentionStatusRenew) + if err != nil { + return err } + if err := row.Scan(&m.ID, &m.Source, &m.Target, &oldStatus); err == sql.ErrNoRows { + return nil + } else if err != nil { + return err + } + if err := wmVerify(m); err != nil { + // Invalid + return deleteWebmention(m.ID) + } + if len(m.Content) > 500 { + m.Content = m.Content[0:497] + "…" + } + newStatus := webmentionStatusVerified + if strings.HasPrefix(m.Source, appConfig.Server.PublicAddress) { + // Approve if it's server-intern + newStatus = webmentionStatusApproved + } + _, err = appDbExec("update webmentions set status = ?, title = ?, content = ?, author = ? where id = ?", newStatus, m.Title, m.Content, m.Author, m.ID) + if oldStatus == string(webmentionStatusNew) { + sendNotification(fmt.Sprintf("New webmention from %s to %s", m.Source, m.Target)) + } + return err +} + +func wmVerify(m *mention) error { req, err := http.NewRequest(http.MethodGet, m.Source, nil) if err != nil { return err } - req.Header.Set("User-Agent", "GoBlog") - resp, err := client.Do(req) + req.Header.Set(userAgent, appUserAgent) + resp, err := http.DefaultClient.Do(req) if err != nil { return err }