Last active
May 15, 2021 00:21
-
-
Save mrinalwahal/3a2e270e215f7218c314bbeb853ac59b to your computer and use it in GitHub Desktop.
Resumable File Uploader with Min.io & Tus
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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