mirror of https://github.com/jlelse/GoBlog
Custom webmention sending implementation and other improvements
This commit is contained in:
parent
6dd99289ad
commit
3a866fb3c0
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
2
api.go
2
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)
|
||||
}
|
||||
|
|
2
feeds.go
2
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()),
|
||||
|
|
10
go.mod
10
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
|
||||
)
|
||||
|
|
26
go.sum
26
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=
|
||||
|
|
6
hooks.go
6
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)
|
||||
|
|
3
http.go
3
http.go
|
@ -27,6 +27,9 @@ const (
|
|||
contentTypeHTMLUTF8 = contentTypeHTML + charsetUtf8Suffix
|
||||
contentTypeJSONUTF8 = contentTypeJSON + charsetUtf8Suffix
|
||||
contentTypeASUTF8 = contentTypeAS + charsetUtf8Suffix
|
||||
|
||||
userAgent = "User-Agent"
|
||||
appUserAgent = "GoBlog"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
53
posts.go
53
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
|
||||
|
@ -151,7 +139,9 @@ func servePhotos(blog string, path string) func(w http.ResponseWriter, r *http.R
|
|||
blog: blog,
|
||||
path: path,
|
||||
parameter: appConfig.Blogs[blog].Photos.Parameter,
|
||||
template: templatePhotos,
|
||||
title: appConfig.Blogs[blog].Photos.Title,
|
||||
description: appConfig.Blogs[blog].Photos.Description,
|
||||
summaryTemplate: templatePhotosSummary,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -159,7 +149,6 @@ func serveSearchResults(blog string, path string) func(w http.ResponseWriter, r
|
|||
return serveIndex(&indexConfig{
|
||||
blog: blog,
|
||||
path: path,
|
||||
template: templateIndex,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -170,7 +159,9 @@ type indexConfig struct {
|
|||
tax *taxonomy
|
||||
taxValue string
|
||||
parameter string
|
||||
template 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,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,9 @@
|
|||
<hr>
|
||||
{{ end }}
|
||||
{{ $blog := .Blog }}
|
||||
{{ $summaryTemplate := .Data.SummaryTemplate }}
|
||||
{{ range $i, $post := .Data.Posts }}
|
||||
{{ include "summary" $blog $post }}
|
||||
{{ include $summaryTemplate $blog $post }}
|
||||
{{ end }}
|
||||
{{ if .Data.HasPrev }}
|
||||
<p><a href="{{ .Data.Prev }}">{{ string .Blog.Lang "prev" }}</a></p>
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
{{ define "title" }}
|
||||
<title>{{ with .Blog.Photos.Title }}{{ . }} - {{ end }}{{ .Blog.Title }}</title>
|
||||
{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
<main>
|
||||
{{ with .Blog.Photos.Title }}<h1>{{ . }}</h1>{{ end }}
|
||||
{{ with .Blog.Photos.Description }}{{ md . }}{{ end }}
|
||||
{{ if (or .Blog.Photos.Title .Blog.Photos.Description) }}
|
||||
<hr>
|
||||
{{ end }}
|
||||
{{ $blog := .Blog }}
|
||||
{{ range $i, $post := .Data.Posts }}
|
||||
{{ include "photosummary" $blog $post }}
|
||||
{{ end }}
|
||||
{{ if .Data.HasPrev }}
|
||||
<p><a href="{{ .Data.Prev }}">{{ string .Blog.Lang "prev" }}</a></p>
|
||||
{{ end }}
|
||||
{{ if .Data.HasNext }}
|
||||
<p><a href="{{ .Data.Next }}">{{ string .Blog.Lang "next" }}</a></p>
|
||||
{{ end }}
|
||||
</main>
|
||||
{{ end }}
|
||||
|
||||
{{ define "photos" }}
|
||||
{{ template "base" . }}
|
||||
{{ end }}
|
|
@ -1,23 +0,0 @@
|
|||
{{ define "title" }}{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
<main class=h-entry>
|
||||
<article>
|
||||
<data class="u-url hide" value="{{ absolute .Data.Path }}"></data>
|
||||
{{ with title .Data }}<h1 class=p-name>{{ . }}</h1>{{ end }}
|
||||
{{ include "postmeta" . }}
|
||||
{{ if .Data.Content }}
|
||||
<div class=e-content>
|
||||
{{ content .Data }}
|
||||
{{ with p .Data "link" }}
|
||||
<p><a class="u-bookmark-of" href="{{ . }}" target="_blank" rel="noopener">{{ . }}</a></p>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</article>
|
||||
</main>
|
||||
{{ end }}
|
||||
|
||||
{{ define "postbasic" }}
|
||||
{{ template "base" . }}
|
||||
{{ end }}
|
|
@ -9,7 +9,11 @@
|
|||
{{ $blog := .Blog }}
|
||||
{{ range $i, $mention := .Data.Verified }}
|
||||
<div class="p">
|
||||
<p>From: {{ $mention.Source }}<br/>To: {{ $mention.Target }}<br/>Created: {{ unixtodate $mention.Created }}</p>
|
||||
<p>
|
||||
From: <a href="{{ $mention.Source }}" target="_blank" rel="noopener noreferrer">{{ $mention.Source }}</a><br/>
|
||||
To: <a href="{{ $mention.Target }}" target="_blank">{{ $mention.Target }}</a><br/>
|
||||
Created: {{ unixtodate $mention.Created }}
|
||||
</p>
|
||||
<form method="post">
|
||||
<input type="submit" formaction="/webmention/admin/approve/{{ $mention.ID }}" value="{{ string $blog.Lang "approve" }}">
|
||||
<input type="submit" formaction="/webmention/admin/delete/{{ $mention.ID }}" value="{{ string $blog.Lang "delete" }}">
|
||||
|
|
27
utils.go
27
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
Loading…
Reference in New Issue