diff --git a/cmd/main.go b/cmd/main.go index f1e121a..f988aad 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -95,6 +95,8 @@ func run(cfg *config.Config) { router.GET(apiBase+"/config", handlers.HandleConfig) router.POST(apiBase+"/initiate", handlers.HandleInitiate) router.POST(apiBase+"/complete", handlers.HandleComplete) + router.GET(apiBase+"/download/*key", handlers.HandleDownload) + router.HEAD(apiBase+"/download/*key", handlers.HandleDownload) server := &http.Server{ Addr: cfg.Server.Listen, diff --git a/pkg/config/config.go b/pkg/config/config.go index 0965e9c..2ffe51c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -56,7 +56,10 @@ type ServerConfig struct { // Host is the local machine IP Address to bind the HTTP Server to Listen string `mapstructure:"listen"` + // Directory of frontend static assets Static string `mapstructure:"static"` + + BaseURL string `mapstructure:"base_url"` } // ShortenerConfig contains Link-shortener specific configuration diff --git a/pkg/handlers/download.go b/pkg/handlers/download.go new file mode 100644 index 0000000..2b44012 --- /dev/null +++ b/pkg/handlers/download.go @@ -0,0 +1,58 @@ +package handlers + +import ( + "fmt" + "net/http" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "github.com/stv0g/gose/pkg/config" + "github.com/stv0g/gose/pkg/notifier" +) + +func HandleDownload(c *gin.Context) { + var err error + + svc, _ := c.MustGet("s3").(*s3.S3) + cfg, _ := c.MustGet("cfg").(*config.Config) + notifier, _ := c.MustGet("notifier").(*notifier.Notifier) + + key := c.Param("key") + key = key[1:] + + parts := strings.Split(key, "/") + if len(parts) != 2 { + c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid key"}) + return + } + + if _, err := uuid.Parse(parts[0]); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid uuid in key"}) + return + } + + req, _ := svc.GetObjectRequest(&s3.GetObjectInput{ + Bucket: aws.String(cfg.S3.Bucket), + Key: aws.String(key), + }) + + u, _, err := req.PresignRequest(10 * time.Second) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to presign request: %s", err)}) + return + } + + if notifier != nil && cfg.Notification.Downloads { + go func(s3svc *s3.S3, cfg *config.Config, key string) { + if err := notifier.Notify(svc, cfg, key, "New download"); err != nil { + fmt.Printf("Failed to send notification: %s", err) + } + }(svc, cfg, key) + } + + c.Redirect(http.StatusTemporaryRedirect, u) +} diff --git a/pkg/handlers/initiate.go b/pkg/handlers/initiate.go index 5ecb9fc..fbf5a7b 100644 --- a/pkg/handlers/initiate.go +++ b/pkg/handlers/initiate.go @@ -71,7 +71,8 @@ func HandleInitiate(c *gin.Context) { key := path.Join(uid.String(), req.Filename) - u := cfg.S3.GetObjectURL(key) + u, _ := url.Parse(cfg.Server.BaseURL) + u.Path += "api/v1/download/" + key if req.Shorten { if shortener == nil { c.JSON(http.StatusBadRequest, gin.H{"error": "shortened URL requested but nut supported"}) @@ -94,7 +95,6 @@ func HandleInitiate(c *gin.Context) { reqCreateMPU := &s3.CreateMultipartUploadInput{ Bucket: aws.String(cfg.S3.Bucket), Key: aws.String(key), - ACL: aws.String("public-read"), Metadata: aws.StringMap(map[string]string{ "uploaded-by": c.ClientIP(), "url": u.String(),