Add post upload hook

This commit is contained in:
Melon 2025-01-08 22:31:57 +00:00
parent e778f6c855
commit eb36349ce5
Signed by: melon
GPG Key ID: 6C9D970C50D26A25
6 changed files with 60 additions and 8 deletions

View File

@ -7,6 +7,7 @@ import (
"github.com/1f349/bluebell"
"github.com/1f349/bluebell/api"
"github.com/1f349/bluebell/conf"
"github.com/1f349/bluebell/hook"
"github.com/1f349/bluebell/logger"
"github.com/1f349/bluebell/serve"
"github.com/1f349/bluebell/upload"
@ -69,15 +70,21 @@ func main() {
wd := filepath.Dir(*configPath)
sitesDir := filepath.Join(wd, "sites")
sitesPostHookDir := filepath.Join(wd, "hooks/post")
keyStore, err := mjwt.NewKeyStoreFromPath(filepath.Join(wd, "keystore"))
if err != nil {
logger.Logger.Fatal("Failed to load MJWT keystore", "dir", filepath.Join(wd, "keystore"), "err", err)
}
_, err = os.Stat(sitesDir)
err = os.MkdirAll(sitesDir, 0770)
if err != nil {
logger.Logger.Fatal("Failed to find sites, does the directory exist? Error: ", err)
logger.Logger.Fatal("Failed to find or create sites directory. Error: ", err)
}
err = os.MkdirAll(sitesPostHookDir, 0770)
if err != nil {
logger.Logger.Fatal("Failed to find or create sites directory. Error: ", err)
}
sitesFs := afero.NewBasePathFs(afero.NewOsFs(), sitesDir)
@ -112,7 +119,8 @@ func main() {
}
serveHandler := serve.New(sitesFs, db)
uploadHandler := upload.New(sitesFs, db)
postHook := hook.New(sitesPostHookDir, sitesDir)
uploadHandler := upload.New(sitesFs, db, postHook)
apiHandler := api.New(uploadHandler, keyStore, db)
serverHttp := &http.Server{

1
go.mod
View File

@ -7,6 +7,7 @@ require (
github.com/1f349/syncmap v0.0.3
github.com/charmbracelet/log v0.4.0
github.com/cloudflare/tableflip v1.2.3
github.com/cyphar/filepath-securejoin v0.3.6
github.com/dustin/go-humanize v1.0.1
github.com/golang-migrate/migrate/v4 v4.18.1
github.com/google/uuid v1.6.0

2
go.sum
View File

@ -16,6 +16,8 @@ github.com/charmbracelet/x/ansi v0.6.0 h1:qOznutrb93gx9oMiGf7caF7bqqubh6YIM0SWKy
github.com/charmbracelet/x/ansi v0.6.0/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q=
github.com/cloudflare/tableflip v1.2.3 h1:8I+B99QnnEWPHOY3fWipwVKxS70LGgUsslG7CSfmHMw=
github.com/cloudflare/tableflip v1.2.3/go.mod h1:P4gRehmV6Z2bY5ao5ml9Pd8u6kuEnlB37pUFMmv7j2E=
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=

33
hook/hook.go Normal file
View File

@ -0,0 +1,33 @@
package hook
import (
"github.com/cyphar/filepath-securejoin"
"os/exec"
"path/filepath"
)
type Hook struct {
hookDir string
sitesDir string
}
func New(hookDir string, sitesDir string) *Hook {
return &Hook{hookDir: hookDir, sitesDir: sitesDir}
}
func (h *Hook) Run(site, branch string) error {
sitePath, err := securejoin.SecureJoin(h.sitesDir, site+"/@"+branch)
if err != nil {
return err
}
scriptPath, err := securejoin.SecureJoin(h.hookDir, site)
if err != nil {
return err
}
cmd := exec.Cmd{
Path: scriptPath,
Args: []string{filepath.Base(scriptPath)},
Dir: sitePath,
}
return cmd.Run()
}

View File

@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"github.com/1f349/bluebell/database"
"github.com/1f349/bluebell/hook"
"github.com/1f349/bluebell/validation"
"github.com/1f349/syncmap"
"github.com/dustin/go-humanize"
@ -34,8 +35,8 @@ type uploadQueries interface {
UpdateBranch(ctx context.Context, arg database.UpdateBranchParams) error
}
func New(storage afero.Fs, db uploadQueries) *Handler {
return &Handler{storageFs: storage, db: db}
func New(storage afero.Fs, db uploadQueries, hook *hook.Hook) *Handler {
return &Handler{storageFs: storage, db: db, postHook: hook}
}
const maxFileSize = 1 * humanize.GiByte
@ -44,6 +45,7 @@ type Handler struct {
storageFs afero.Fs
db uploadQueries
mu syncmap.Map[string, *sync.Mutex]
postHook *hook.Hook
}
func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
@ -165,6 +167,12 @@ func (h *Handler) extractTarGzUpload(fileData io.Reader, site, branch string) er
}
}
// call the post hook script
err = h.postHook.Run(site, branch)
if err != nil {
return err
}
// TODO(melon): I would love to use unix.Renameat2 but due to afero this will not work
err = h.storageFs.Rename(siteBranchPath, siteBranchOldPath)

View File

@ -117,7 +117,7 @@ func (f *fakeUploadDB) UpdateBranch(ctx context.Context, arg database.UpdateBran
func TestHandler_Handle(t *testing.T) {
fs := afero.NewMemMapFs()
h := New(fs, new(fakeUploadDB))
h := New(fs, new(fakeUploadDB), nil)
r := httprouter.New()
r.POST("/u/:site/:branch", h.Handle)
@ -168,7 +168,7 @@ func extractTarGzUploadTest(t *testing.T, db uploadQueries) {
for _, branch := range []string{"main", "test", "dev"} {
t.Run(branch+" branch", func(t *testing.T) {
fs := afero.NewMemMapFs()
h := New(fs, db)
h := New(fs, db, nil)
buffer := bytes.NewBuffer(testArchiveTarGz)
assert.NoError(t, h.extractTarGzUpload(buffer, "example.com", branch))
@ -188,7 +188,7 @@ func TestHandler_extractTarGzUpload_memoryDB_multiple(t *testing.T) {
func extractTarGzUploadMultipleTest(t *testing.T, db uploadQueries) {
fs := afero.NewMemMapFs()
h := New(fs, db)
h := New(fs, db, nil)
sig := new(atomic.Bool)
wg := new(sync.WaitGroup)