diff --git a/.hooks/pre-create b/.hooks/pre-create new file mode 100755 index 0000000..06215b2 --- /dev/null +++ b/.hooks/pre-create @@ -0,0 +1,8 @@ +#!/bin/bash + +filename=$(cat /dev/stdin | jq .MetaData.filename) + +if [ -n "$filename" ]; then + echo "Error: no filename provided" + exit 1 +fi diff --git a/cmd/tusd/cli/hooks.go b/cmd/tusd/cli/hooks.go index c705185..0baf3e3 100644 --- a/cmd/tusd/cli/hooks.go +++ b/cmd/tusd/cli/hooks.go @@ -3,6 +3,7 @@ package cli import ( "bytes" "encoding/json" + "fmt" "os" "os/exec" "strconv" @@ -15,9 +16,27 @@ type HookType string const ( HookPostFinish HookType = "post-finish" HookPostTerminate HookType = "post-terminate" + HookPreCreate HookType = "pre-create" ) -func SetupHooks(handler *tusd.Handler) { +type hookDataStore struct { + tusd.DataStore +} + +func (store hookDataStore) NewUpload(info tusd.FileInfo) (id string, err error) { + if output, err := invokeHookSync(HookPreCreate, info, true); err != nil { + return "", fmt.Errorf("pre-create hook failed: %s\n%s", err, string(output)) + } + return store.DataStore.NewUpload(info) +} + +func SetupPreHooks(composer *tusd.StoreComposer) { + composer.UseCore(hookDataStore{ + DataStore: composer.Core, + }) +} + +func SetupPostHooks(handler *tusd.Handler) { go func() { for { select { @@ -31,6 +50,15 @@ func SetupHooks(handler *tusd.Handler) { } func invokeHook(typ HookType, info tusd.FileInfo) { + go func() { + _, err := invokeHookSync(typ, info, false) + if err != nil { + stderr.Printf("Error running %s hook for %s: %s", string(typ), info.ID, err) + } + }() +} + +func invokeHookSync(typ HookType, info tusd.FileInfo, captureOutput bool) ([]byte, error) { switch typ { case HookPostFinish: stdout.Printf("Upload %s (%d bytes) finished\n", info.ID, info.Size) @@ -39,7 +67,7 @@ func invokeHook(typ HookType, info tusd.FileInfo) { } if !Flags.HooksInstalled { - return + return nil, nil } name := string(typ) @@ -52,7 +80,7 @@ func invokeHook(typ HookType, info tusd.FileInfo) { jsonInfo, err := json.Marshal(info) if err != nil { - stderr.Printf("Error encoding JSON for hook: %s", err) + return nil, err } reader := bytes.NewReader(jsonInfo) @@ -60,13 +88,12 @@ func invokeHook(typ HookType, info tusd.FileInfo) { cmd.Env = env cmd.Dir = Flags.HooksDir - cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - go func() { - err := cmd.Run() - if err != nil { - stderr.Printf("Error running %s hook for %s: %s", name, info.ID, err) - } - }() + if !captureOutput { + cmd.Stdout = os.Stdout + return nil, cmd.Run() + } else { + return cmd.Output() + } } diff --git a/cmd/tusd/cli/serve.go b/cmd/tusd/cli/serve.go index 9b086ed..53cad7e 100644 --- a/cmd/tusd/cli/serve.go +++ b/cmd/tusd/cli/serve.go @@ -8,6 +8,8 @@ import ( ) func Serve() { + SetupPreHooks(Composer) + handler, err := tusd.NewHandler(tusd.Config{ MaxSize: Flags.MaxSize, BasePath: Flags.Basepath, @@ -27,7 +29,7 @@ func Serve() { stdout.Printf("Using %s as the base path.\n", basepath) stdout.Printf(Composer.Capabilities()) - SetupHooks(handler) + SetupPostHooks(handler) if Flags.ExposeMetrics { SetupMetrics(handler)