package s3browser import ( "context" "fmt" "io" "mime" "net/http" "path/filepath" "github.com/gorilla/mux" "github.com/graphql-go/graphql" "github.com/graphql-go/graphql/gqlerrors" "github.com/graphql-go/handler" "github.com/minio/minio-go/v7" log "github.com/sirupsen/logrus" ) // initHttp setup and start the http server. Blocking func initHttp(resolveContext context.Context, schema graphql.Schema, address string) error { r := mux.NewRouter() gqlHandler := handler.New(&handler.Config{ Schema: &schema, Pretty: true, GraphiQL: false, Playground: true, FormatErrorFn: func(err error) gqlerrors.FormattedError { switch err := err.(type) { case gqlerrors.FormattedError: log.Error("GQL: ", err.Message) case *gqlerrors.Error: log.Errorf("GQL: '%s' at '%v'", err.Message, err.Path) } return gqlerrors.FormatError(err) }, }) r.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { // TODO: handle auth h.ServeHTTP(rw, r) }) }) r.HandleFunc("/api/graphql", func(rw http.ResponseWriter, r *http.Request) { gqlHandler.ContextHandler(resolveContext, rw, r) }) r.HandleFunc("/api/file", func(rw http.ResponseWriter, r *http.Request) { httpGetFile(resolveContext, rw, r) }).Methods("GET") r.HandleFunc("/api/file", func(rw http.ResponseWriter, r *http.Request) { httpPostFile(resolveContext, rw, r) }).Methods("POST") // Init the embedded static files initStatic(r) return http.ListenAndServe(address, r) } func httpGetFile(ctx context.Context, rw http.ResponseWriter, r *http.Request) { s3Client := ctx.Value("s3Client").(*minio.Client) id := r.URL.Query().Get("id") log.Debug("S3 call 'StatObject': ", id) objInfo, err := s3Client.StatObject(context.Background(), bucketName, id, minio.GetObjectOptions{}) if err != nil { rw.WriteHeader(http.StatusInternalServerError) return } reqEtag := r.Header.Get("If-None-Match") if reqEtag == objInfo.ETag { rw.WriteHeader(http.StatusNotModified) return } log.Debug("S3 call 'GetObject': ", id) obj, err := s3Client.GetObject(context.Background(), bucketName, id, minio.GetObjectOptions{}) if err != nil { rw.WriteHeader(http.StatusInternalServerError) return } rw.Header().Set("Cache-Control", "must-revalidate") rw.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filepath.Base((objInfo.Key)))) rw.Header().Set("Content-Type", objInfo.ContentType) rw.Header().Set("ETag", objInfo.ETag) _, err = io.Copy(rw, obj) if err != nil { rw.WriteHeader(http.StatusInternalServerError) return } } func httpPostFile(ctx context.Context, rw http.ResponseWriter, r *http.Request) { s3Client := ctx.Value("s3Client").(*minio.Client) id := r.URL.Query().Get("id") log.Debug("Upload file: ", id) contentType := r.Header.Get("Content-Type") mimeType, _, _ := mime.ParseMediaType(contentType) log.Debug("S3 call 'PutObject': ", id) info, err := s3Client.PutObject(context.Background(), bucketName, id, r.Body, r.ContentLength, minio.PutObjectOptions{ ContentType: mimeType, }) if err != nil { rw.WriteHeader(http.StatusInternalServerError) return } // Invalidate cache invalidateCache(ctx, info.Key) rw.WriteHeader(http.StatusCreated) }