Add post-terminate hook
This commit is contained in:
parent
5aa7928111
commit
3ee1c61d80
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "Upload $TUS_ID terminated"
|
||||||
|
cat /dev/stdin | jq .
|
|
@ -134,10 +134,11 @@ func main() {
|
||||||
stdout.Printf("Using %.2fMB as maximum size.\n", float64(maxSize)/1024/1024)
|
stdout.Printf("Using %.2fMB as maximum size.\n", float64(maxSize)/1024/1024)
|
||||||
|
|
||||||
handler, err := tusd.NewHandler(tusd.Config{
|
handler, err := tusd.NewHandler(tusd.Config{
|
||||||
MaxSize: maxSize,
|
MaxSize: maxSize,
|
||||||
BasePath: basepath,
|
BasePath: basepath,
|
||||||
StoreComposer: composer,
|
StoreComposer: composer,
|
||||||
NotifyCompleteUploads: true,
|
NotifyCompleteUploads: true,
|
||||||
|
NotifyTerminatedUploads: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stderr.Fatalf("Unable to create handler: %s", err)
|
stderr.Fatalf("Unable to create handler: %s", err)
|
||||||
|
@ -152,7 +153,9 @@ func main() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case info := <-handler.CompleteUploads:
|
case info := <-handler.CompleteUploads:
|
||||||
invokeHook(info)
|
invokeHook("post-finish", info)
|
||||||
|
case info := <-handler.TerminatedUploads:
|
||||||
|
invokeHook("post-terminate", info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -176,16 +179,21 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func invokeHook(info tusd.FileInfo) {
|
func invokeHook(name string, info tusd.FileInfo) {
|
||||||
stdout.Printf("Upload %s (%d bytes) finished\n", info.ID, info.Size)
|
switch name {
|
||||||
|
case "post-finish":
|
||||||
|
stdout.Printf("Upload %s (%d bytes) finished\n", info.ID, info.Size)
|
||||||
|
case "post-terminate":
|
||||||
|
stdout.Printf("Upload %s terminated\n", info.ID)
|
||||||
|
}
|
||||||
|
|
||||||
if !hookInstalled {
|
if !hookInstalled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
stdout.Println("Invoking hooks…")
|
stdout.Printf("Invoking %s hook…\n", name)
|
||||||
|
|
||||||
cmd := exec.Command(hooksDir + "/post-finish")
|
cmd := exec.Command(hooksDir + "/" + name)
|
||||||
env := os.Environ()
|
env := os.Environ()
|
||||||
env = append(env, "TUS_ID="+info.ID)
|
env = append(env, "TUS_ID="+info.ID)
|
||||||
env = append(env, "TUS_SIZE="+strconv.FormatInt(info.Size, 10))
|
env = append(env, "TUS_SIZE="+strconv.FormatInt(info.Size, 10))
|
||||||
|
@ -206,7 +214,7 @@ func invokeHook(info tusd.FileInfo) {
|
||||||
go func() {
|
go func() {
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stderr.Printf("Error running postfinish hook for %s: %s", info.ID, err)
|
stderr.Printf("Error running %s hook for %s: %s", name, info.ID, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ type Config struct {
|
||||||
// DataStore implementation used to store and retrieve the single uploads.
|
// DataStore implementation used to store and retrieve the single uploads.
|
||||||
// The usage of this field is deprecated and should be avoided in favor of
|
// The usage of this field is deprecated and should be avoided in favor of
|
||||||
// StoreComposer.
|
// StoreComposer.
|
||||||
DataStore DataStore
|
DataStore DataStore
|
||||||
// StoreComposer points to the store composer from which the core data store
|
// StoreComposer points to the store composer from which the core data store
|
||||||
// and optional dependencies should be taken. May only be nil if DataStore is
|
// and optional dependencies should be taken. May only be nil if DataStore is
|
||||||
// set.
|
// set.
|
||||||
|
@ -28,6 +28,9 @@ type Config struct {
|
||||||
// Initiate the CompleteUploads channel in the Handler struct in order to
|
// Initiate the CompleteUploads channel in the Handler struct in order to
|
||||||
// be notified about complete uploads
|
// be notified about complete uploads
|
||||||
NotifyCompleteUploads bool
|
NotifyCompleteUploads bool
|
||||||
|
// NotifyTerminatedUploads indicates whether sending notifications about
|
||||||
|
// terminated uploads using the TerminatedUploads channel should be enabled.
|
||||||
|
NotifyTerminatedUploads bool
|
||||||
// Logger the logger to use internally
|
// Logger the logger to use internally
|
||||||
Logger *log.Logger
|
Logger *log.Logger
|
||||||
// Respect the X-Forwarded-Host, X-Forwarded-Proto and Forwarded headers
|
// Respect the X-Forwarded-Host, X-Forwarded-Proto and Forwarded headers
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/tus/tusd"
|
. "github.com/tus/tusd"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
type terminateStore struct {
|
type terminateStore struct {
|
||||||
|
@ -12,6 +14,13 @@ type terminateStore struct {
|
||||||
zeroStore
|
zeroStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s terminateStore) GetInfo(id string) (FileInfo, error) {
|
||||||
|
return FileInfo{
|
||||||
|
ID: id,
|
||||||
|
Size: 10,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s terminateStore) Terminate(id string) error {
|
func (s terminateStore) Terminate(id string) error {
|
||||||
if id != "foo" {
|
if id != "foo" {
|
||||||
s.t.Fatal("unexpected id")
|
s.t.Fatal("unexpected id")
|
||||||
|
@ -24,8 +33,12 @@ func TestTerminate(t *testing.T) {
|
||||||
DataStore: terminateStore{
|
DataStore: terminateStore{
|
||||||
t: t,
|
t: t,
|
||||||
},
|
},
|
||||||
|
NotifyTerminatedUploads: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
c := make(chan FileInfo, 1)
|
||||||
|
handler.TerminatedUploads = c
|
||||||
|
|
||||||
(&httpTest{
|
(&httpTest{
|
||||||
Name: "Successful OPTIONS request",
|
Name: "Successful OPTIONS request",
|
||||||
Method: "OPTIONS",
|
Method: "OPTIONS",
|
||||||
|
@ -45,6 +58,12 @@ func TestTerminate(t *testing.T) {
|
||||||
},
|
},
|
||||||
Code: http.StatusNoContent,
|
Code: http.StatusNoContent,
|
||||||
}).Run(handler, t)
|
}).Run(handler, t)
|
||||||
|
|
||||||
|
info := <-c
|
||||||
|
|
||||||
|
a := assert.New(t)
|
||||||
|
a.Equal("foo", info.ID)
|
||||||
|
a.Equal(int64(10), info.Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTerminateNotImplemented(t *testing.T) {
|
func TestTerminateNotImplemented(t *testing.T) {
|
||||||
|
|
|
@ -66,6 +66,12 @@ type UnroutedHandler struct {
|
||||||
// this unbuffered channel. The NotifyCompleteUploads property in the Config
|
// this unbuffered channel. The NotifyCompleteUploads property in the Config
|
||||||
// struct must be set to true in order to work.
|
// struct must be set to true in order to work.
|
||||||
CompleteUploads chan FileInfo
|
CompleteUploads chan FileInfo
|
||||||
|
// TerminatedUploads is used to send notifications whenever an upload is
|
||||||
|
// terminated by a user. The FileInfo will contain information about This
|
||||||
|
// upload gathered before the termination. Sending to this channel will only
|
||||||
|
// happen if the NotifyTerminatedUploads field is set to true in the Config
|
||||||
|
// structure.
|
||||||
|
TerminatedUploads chan FileInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUnroutedHandler creates a new handler without routing using the given
|
// NewUnroutedHandler creates a new handler without routing using the given
|
||||||
|
@ -87,13 +93,14 @@ func NewUnroutedHandler(config Config) (*UnroutedHandler, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := &UnroutedHandler{
|
handler := &UnroutedHandler{
|
||||||
config: config,
|
config: config,
|
||||||
composer: config.StoreComposer,
|
composer: config.StoreComposer,
|
||||||
basePath: config.BasePath,
|
basePath: config.BasePath,
|
||||||
isBasePathAbs: config.isAbs,
|
isBasePathAbs: config.isAbs,
|
||||||
CompleteUploads: make(chan FileInfo),
|
CompleteUploads: make(chan FileInfo),
|
||||||
logger: config.Logger,
|
TerminatedUploads: make(chan FileInfo),
|
||||||
extensions: extensions,
|
logger: config.Logger,
|
||||||
|
extensions: extensions,
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler, nil
|
return handler, nil
|
||||||
|
@ -461,6 +468,15 @@ func (handler *UnroutedHandler) DelFile(w http.ResponseWriter, r *http.Request)
|
||||||
defer locker.UnlockUpload(id)
|
defer locker.UnlockUpload(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var info FileInfo
|
||||||
|
if handler.config.NotifyTerminatedUploads {
|
||||||
|
info, err = handler.composer.Core.GetInfo(id)
|
||||||
|
if err != nil {
|
||||||
|
handler.sendError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = handler.composer.Terminater.Terminate(id)
|
err = handler.composer.Terminater.Terminate(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handler.sendError(w, r, err)
|
handler.sendError(w, r, err)
|
||||||
|
@ -468,6 +484,10 @@ func (handler *UnroutedHandler) DelFile(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
|
||||||
|
if handler.config.NotifyTerminatedUploads {
|
||||||
|
handler.TerminatedUploads <- info
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the error in the response body. The status code will be looked up in
|
// Send the error in the response body. The status code will be looked up in
|
||||||
|
|
Loading…
Reference in New Issue