Post-post and post-delete hooks

This commit is contained in:
Jan-Lukas Else 2020-10-19 20:25:30 +02:00
parent 141fe3485f
commit 96259912cb
10 changed files with 74 additions and 33 deletions

View File

@ -43,7 +43,7 @@ func servePostActivityStreams(w http.ResponseWriter, r *http.Request) {
// Remove ".as" from path again // Remove ".as" from path again
r.URL.Path = strings.TrimSuffix(r.URL.Path, ".as") r.URL.Path = strings.TrimSuffix(r.URL.Path, ".as")
// Fetch post from db // Fetch post from db
p, err := getPost(r.Context(), slashTrimmedPath(r)) p, err := getPost(slashTrimmedPath(r))
if err == errPostNotFound { if err == errPostNotFound {
serve404(w, r) serve404(w, r)
return return

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"context"
"database/sql" "database/sql"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -61,7 +60,7 @@ func cacheMiddleware(next http.Handler) http.Handler {
// Get cache // Get cache
cm := cacheMutexes[path] cm := cacheMutexes[path]
cm.Lock() cm.Lock()
cacheTime, header, body := getCache(r.Context(), path) cacheTime, header, body := getCache(path)
cm.Unlock() cm.Unlock()
if cacheTime == 0 { if cacheTime == 0 {
cm.Lock() cm.Lock()
@ -122,10 +121,10 @@ func renderCache(path string, next http.Handler, w http.ResponseWriter, r *http.
} }
} }
func getCache(context context.Context, path string) (creationTime int64, header map[string][]string, body []byte) { func getCache(path string) (creationTime int64, header map[string][]string, body []byte) {
var headerBytes []byte var headerBytes []byte
allowedTime := time.Now().Unix() - appConfig.Cache.Expiration allowedTime := time.Now().Unix() - appConfig.Cache.Expiration
row := cacheDb.QueryRowContext(context, "select COALESCE(time, 0), header, body from cache where path=? and time>=?", path, allowedTime) row := cacheDb.QueryRow("select COALESCE(time, 0), header, body from cache where path=? and time>=?", path, allowedTime)
_ = row.Scan(&creationTime, &headerBytes, &body) _ = row.Scan(&creationTime, &headerBytes, &body)
header = make(map[string][]string) header = make(map[string][]string)
_ = json.Unmarshal(headerBytes, &header) _ = json.Unmarshal(headerBytes, &header)

View File

@ -108,6 +108,9 @@ type configHooks struct {
Shell string `mapstructure:"shell"` Shell string `mapstructure:"shell"`
Hourly []string `mapstructure:"hourly"` Hourly []string `mapstructure:"hourly"`
PreStart []string `mapstructure:"prestart"` PreStart []string `mapstructure:"prestart"`
// Can use template
PostPost []string `mapstructure:"postpost"`
PostDelete []string `mapstructure:"postdelete"`
} }
type configHugo struct { type configHugo struct {

View File

@ -1,6 +1,8 @@
package main package main
import ( import (
"bytes"
"html/template"
"log" "log"
"os/exec" "os/exec"
"time" "time"
@ -8,11 +10,50 @@ import (
func preStartHooks() { func preStartHooks() {
for _, cmd := range appConfig.Hooks.PreStart { for _, cmd := range appConfig.Hooks.PreStart {
log.Println("Executing pre-start hook:", cmd) go func(cmd string) {
executeCommand(cmd) log.Println("Executing pre-start hook:", cmd)
executeCommand(cmd)
}(cmd)
} }
} }
func postPostHooks(path string) {
for _, cmdTmplString := range appConfig.Hooks.PostPost {
go func(path, cmdTmplString string) {
executeTemplateCommand("post-post", cmdTmplString, &hookTemplateData{
URL: appConfig.Server.PublicAddress + path,
})
}(path, cmdTmplString)
}
}
func postDeleteHooks(path string) {
for _, cmdTmplString := range appConfig.Hooks.PostDelete {
go func(path, cmdTmplString string) {
executeTemplateCommand("post-delete", cmdTmplString, &hookTemplateData{
URL: appConfig.Server.PublicAddress + path,
})
}(path, cmdTmplString)
}
}
type hookTemplateData struct {
URL string
}
func executeTemplateCommand(hookType string, tmpl string, data *hookTemplateData) {
cmdTmpl, err := template.New("cmd").Parse(tmpl)
if err != nil {
log.Println("Failed to parse cmd template:", err.Error())
return
}
var cmdBuf bytes.Buffer
cmdTmpl.Execute(&cmdBuf, data)
cmd := cmdBuf.String()
log.Println("Executing "+hookType+" hook:", cmd)
executeCommand(cmd)
}
func startHourlyHooks() { func startHourlyHooks() {
for _, cmd := range appConfig.Hooks.Hourly { for _, cmd := range appConfig.Hooks.Hourly {
go func(cmd string) { go func(cmd string) {
@ -21,11 +62,11 @@ func startHourlyHooks() {
executeCommand(cmd) executeCommand(cmd)
} }
// Execute once // Execute once
run() go run()
// Start ticker and execute regularly // Start ticker and execute regularly
ticker := time.NewTicker(1 * time.Hour) ticker := time.NewTicker(1 * time.Hour)
for range ticker.C { for range ticker.C {
run() go run()
} }
}(cmd) }(cmd)
} }

View File

@ -38,14 +38,14 @@ func serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
p, err := getPost(r.Context(), u.Path) p, err := getPost(u.Path)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
mf = p.toMfItem() mf = p.toMfItem()
} else { } else {
posts, err := getPosts(r.Context(), &postsRequestConfig{}) posts, err := getPosts(&postsRequestConfig{})
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
@ -391,7 +391,7 @@ func micropubUpdate(w http.ResponseWriter, r *http.Request, u *url.URL, mf *micr
if !strings.Contains(r.Context().Value("scope").(string), "update") { if !strings.Contains(r.Context().Value("scope").(string), "update") {
http.Error(w, "update scope missing", http.StatusForbidden) http.Error(w, "update scope missing", http.StatusForbidden)
} }
p, err := getPost(r.Context(), u.Path) p, err := getPost(u.Path)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
@ -35,7 +34,7 @@ func servePost(w http.ResponseWriter, r *http.Request) {
return return
} }
path := slashTrimmedPath(r) path := slashTrimmedPath(r)
p, err := getPost(r.Context(), path) p, err := getPost(path)
if err == errPostNotFound { if err == errPostNotFound {
serve404(w, r) serve404(w, r)
return return
@ -62,14 +61,13 @@ type indexTemplateData struct {
} }
type postPaginationAdapter struct { type postPaginationAdapter struct {
context context.Context config *postsRequestConfig
config *postsRequestConfig nums int
nums int
} }
func (p *postPaginationAdapter) Nums() int { func (p *postPaginationAdapter) Nums() int {
if p.nums == 0 { if p.nums == 0 {
p.nums, _ = countPosts(p.context, p.config) p.nums, _ = countPosts(p.config)
} }
return p.nums return p.nums
} }
@ -83,7 +81,7 @@ func (p *postPaginationAdapter) Slice(offset, length int, data interface{}) erro
modifiedConfig.offset = offset modifiedConfig.offset = offset
modifiedConfig.limit = length modifiedConfig.limit = length
posts, err := getPosts(p.context, &modifiedConfig) posts, err := getPosts(&modifiedConfig)
reflect.ValueOf(data).Elem().Set(reflect.ValueOf(&posts).Elem()) reflect.ValueOf(data).Elem().Set(reflect.ValueOf(&posts).Elem())
return err return err
} }
@ -163,7 +161,7 @@ func serveIndex(ic *indexConfig) func(w http.ResponseWriter, r *http.Request) {
sections = append(sections, sectionKey) sections = append(sections, sectionKey)
} }
} }
p := paginator.New(&postPaginationAdapter{context: r.Context(), config: &postsRequestConfig{ p := paginator.New(&postPaginationAdapter{config: &postsRequestConfig{
blog: ic.blog, blog: ic.blog,
sections: sections, sections: sections,
taxonomy: ic.tax, taxonomy: ic.tax,
@ -219,8 +217,8 @@ func serveIndex(ic *indexConfig) func(w http.ResponseWriter, r *http.Request) {
} }
} }
func getPost(context context.Context, path string) (*post, error) { func getPost(path string) (*post, error) {
posts, err := getPosts(context, &postsRequestConfig{path: path}) posts, err := getPosts(&postsRequestConfig{path: path})
if err != nil { if err != nil {
return nil, err return nil, err
} else if len(posts) == 0 { } else if len(posts) == 0 {
@ -281,9 +279,9 @@ func buildQuery(config *postsRequestConfig) (query string, params []interface{})
return return
} }
func getPosts(context context.Context, config *postsRequestConfig) (posts []*post, err error) { func getPosts(config *postsRequestConfig) (posts []*post, err error) {
query, queryParams := buildQuery(config) query, queryParams := buildQuery(config)
rows, err := appDb.QueryContext(context, query, queryParams...) rows, err := appDb.Query(query, queryParams...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -311,10 +309,10 @@ func getPosts(context context.Context, config *postsRequestConfig) (posts []*pos
return posts, nil return posts, nil
} }
func countPosts(context context.Context, config *postsRequestConfig) (count int, err error) { func countPosts(config *postsRequestConfig) (count int, err error) {
query, params := buildQuery(config) query, params := buildQuery(config)
query = "select count(distinct path) from (" + query + ")" query = "select count(distinct path) from (" + query + ")"
row := appDb.QueryRowContext(context, query, params...) row := appDb.QueryRow(query, params...)
err = row.Scan(&count) err = row.Scan(&count)
return return
} }

View File

@ -156,6 +156,7 @@ func (p *post) createOrReplace(new bool) error {
} }
finishWritingToDb() finishWritingToDb()
go purgeCache() go purgeCache()
defer postPostHooks(p.Path)
return reloadRouter() return reloadRouter()
} }
@ -188,5 +189,6 @@ func deletePost(path string) error {
} }
finishWritingToDb() finishWritingToDb()
go purgeCache() go purgeCache()
defer postDeleteHooks(path)
return reloadRouter() return reloadRouter()
} }

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"context"
"html/template" "html/template"
"log" "log"
"strings" "strings"
@ -56,7 +55,7 @@ func (p *post) translations() []*post {
if translationkey == "" { if translationkey == "" {
return nil return nil
} }
posts, err := getPosts(context.Background(), &postsRequestConfig{ posts, err := getPosts(&postsRequestConfig{
parameter: "translationkey", parameter: "translationkey",
parameterValue: translationkey, parameterValue: translationkey,
}) })

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"context"
"database/sql" "database/sql"
"errors" "errors"
"net/http" "net/http"
@ -11,7 +10,7 @@ import (
var errRedirectNotFound = errors.New("redirect not found") var errRedirectNotFound = errors.New("redirect not found")
func serveRedirect(w http.ResponseWriter, r *http.Request) { func serveRedirect(w http.ResponseWriter, r *http.Request) {
redirect, err := getRedirect(r.Context(), slashTrimmedPath(r)) redirect, err := getRedirect(slashTrimmedPath(r))
if err == errRedirectNotFound { if err == errRedirectNotFound {
serve404(w, r) serve404(w, r)
return return
@ -27,9 +26,9 @@ func serveRedirect(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusFound) w.WriteHeader(http.StatusFound)
} }
func getRedirect(context context.Context, fromPath string) (string, error) { func getRedirect(fromPath string) (string, error) {
var toPath string var toPath string
row := appDb.QueryRowContext(context, "with recursive f (i, fp, tp) as (select 1, fromPath, toPath from redirects where fromPath = ? union all select f.i + 1, r.fromPath, r.toPath from redirects as r join f on f.tp = r.fromPath) select tp from f order by i desc limit 1", fromPath) row := appDb.QueryRow("with recursive f (i, fp, tp) as (select 1, fromPath, toPath from redirects where fromPath = ? union all select f.i + 1, r.fromPath, r.toPath from redirects as r join f on f.tp = r.fromPath) select tp from f order by i desc limit 1", fromPath)
err := row.Scan(&toPath) err := row.Scan(&toPath)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return "", errRedirectNotFound return "", errRedirectNotFound

View File

@ -11,7 +11,7 @@ import (
const sitemapPath = "/sitemap.xml" const sitemapPath = "/sitemap.xml"
func serveSitemap(w http.ResponseWriter, r *http.Request) { func serveSitemap(w http.ResponseWriter, r *http.Request) {
posts, err := getPosts(r.Context(), &postsRequestConfig{}) posts, err := getPosts(&postsRequestConfig{})
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
} }