Skip to content

Instantly share code, notes, and snippets.

@mrinalwahal
Last active May 15, 2021 00:21
Show Gist options
  • Save mrinalwahal/3a2e270e215f7218c314bbeb853ac59b to your computer and use it in GitHub Desktop.
Save mrinalwahal/3a2e270e215f7218c314bbeb853ac59b to your computer and use it in GitHub Desktop.
Resumable File Uploader with Min.io & Tus
package main
import (
"crypto/rsa"
"io/ioutil"
"log"
"net/http"
"os"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/dgrijalva/jwt-go"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
tusd "github.com/tus/tusd/pkg/handler"
"github.com/tus/tusd/pkg/s3store"
)
// Initialize default variables and echo instance
var port = "8080"
// Access and return the public key to authenticate the requests
func getKey() *rsa.PublicKey {
pubKeyBytes, _ := ioutil.ReadFile(os.Getenv("PUBLIC_KEY_PATH"))
pubKey, _ := jwt.ParseRSAPublicKeyFromPEM(pubKeyBytes)
return pubKey
}
func main() {
// Load new port if stored in env vars
if envPort, ok := os.LookupEnv("PORT"); ok {
port = envPort
}
// List the env vars absolutely required
envRequired := []string{
"MINIO_SERVER",
"MINIO_ACCESS_KEY_ID",
"MINIO_SECRET_ACCESS_KEY",
"MINIO_BUCKET_NAME",
"AWS_REGION",
}
// Stop the service if any required env if absent
for _, env := range envRequired {
if _, ok := os.LookupEnv(env); !ok {
log.Fatalln("environment variable \"" + env + "\" not found")
}
}
/*
// [TODO] Check wether the object storage is reachable
if ping, err := http.Get(os.Getenv("MINIO_SERVER") + "/minio/health/live"); ping.StatusCode != 200 || err != nil {
log.Fatalln("storage server is either unreachable or broken")
}
*/
// Create a new FileStore instance which is responsible for
// storing the uploaded file on disk in the specified directory.
// This path _must_ exist before tusd will store uploads in it.
// If you want to save them on a different medium, for example
// a remote FTP server, you can implement your own storage backend
// by implementing the tusd.DataStore interface.
// S3 acces configuration
s3Config := &aws.Config{
Region: aws.String(os.Getenv("AWS_REGION")),
Endpoint: aws.String(os.Getenv("MINIO_SERVER")),
Credentials: credentials.NewStaticCredentials(os.Getenv("MINIO_ACCESS_KEY_ID"), os.Getenv("MINIO_SECRET_ACCESS_KEY"), ""),
DisableSSL: aws.Bool(true),
S3ForcePathStyle: aws.Bool(true),
}
// Setting up the s3 storage
store := s3store.New(os.Getenv("MINIO_BUCKET_NAME"), s3.New(session.Must(session.NewSession()), s3Config))
// A storage backend for tusd may consist of multiple different parts which
// handle upload creation, locking, termination and so on. The composer is a
// place where all those separated pieces are joined together. In this example
// we only use the file store but you may plug in multiple.
composer := tusd.NewStoreComposer()
store.UseIn(composer)
// Create a new HTTP handler for the tusd server by providing a configuration.
// The StoreComposer property must be set to allow the handler to function.
handler, err := tusd.NewHandler(tusd.Config{
BasePath: "/files/",
StoreComposer: composer,
NotifyCompleteUploads: true,
})
if err != nil {
log.Fatalln("unable to create handler: %s", err)
}
// Start another goroutine for receiving events from the handler whenever
// an upload is completed. The event will contains details about the upload
// itself and the relevant HTTP request.
go func() {
for {
event := <-handler.CompleteUploads
log.Println("Upload %s finished\n", event.Upload.ID)
}
}()
// Initialize the Echo server instance
e := echo.New()
// Activate required middlewares for logging the requests,
// enabling CORS, and fault recovery to keep the server running
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
//AllowCredentials: true,
AllowOrigins: []string{"http://localhost:3000", "http://localhost:3000/*", "*"},
AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderAuthorization, echo.HeaderAccessControlAllowOrigin, echo.HeaderContentType, echo.HeaderAccept, echo.HeaderAccessControlAllowHeaders, echo.HeaderAccessControlAllowMethods, echo.HeaderAccessControlAllowCredentials},
}))
// Activate the JWT middleware to authenticate the requests
e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
SigningKey: getKey(),
SigningMethod: "RS256",
//TokenLookup: "cookie:auth.token",
Skipper: func(c echo.Context) bool {
return c.Path() == "/"
},
}))
// Set endpoint for file upload
e.POST("/files/", echo.WrapHandler(http.StripPrefix("/files/", handler)))
e.PATCH("/files/:id", echo.WrapHandler(http.StripPrefix("/files/", handler)))
//http.Handle("/files/", http.StripPrefix("/files/", handler))
e.GET("/", func(c echo.Context) error {
return c.HTML(http.StatusOK, `
<h1>Danger Zone!</h1>
<h3>This is NewFang's file uploader micro-service.</h3>
<p>Turn back to where you came from otherwise you shall regret!</p>
`)
})
// [OPTIONAL] Uncomment the below to cache certificates on th server,
// and add domains to host whitelist.
// e.AutoTLSManager.HostPolicy = autocert.HostWhitelist("<DOMAIN>")
// e.AutoTLSManager.Cache = autocert.DirCache("/var/www/.cache")
// Right now, nothing has happened since we need to start the HTTP server on
// our own. In the end, tusd will start listening on and accept request
e.Logger.Fatal(e.Start(":" + port))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment