Skip to content

Commit

Permalink
feat: upload resource simple
Browse files Browse the repository at this point in the history
  • Loading branch information
Miguel Ramos authored and Miguel Ramos committed Feb 12, 2021
1 parent c380705 commit f62d746
Show file tree
Hide file tree
Showing 13 changed files with 214 additions and 15 deletions.
2 changes: 1 addition & 1 deletion api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func NewPrivateAPI(api *API, router fiber.Router) {

bucketRouter := router.Group("/bucket")
bucketRouter.Post("", api.CreateBucket)
bucketRouter.Post("/upload", api.PutObject)
bucketRouter.Post("/:bucket", api.PutObject)

userRouter := router.Group("/user")
userRouter.Post("", api.CreateUser)
Expand Down
78 changes: 75 additions & 3 deletions api/bucket.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package api

import (
"encoding/json"
"fmt"
"net/url"
"os"
"path"
"strings"

"github.com/barasher/go-exiftool"
"github.com/gobuffalo/pop/nulls"
"github.com/gofiber/fiber/v2"
"github.com/gofrs/uuid"
"github.com/gosimple/slug"
"github.com/minio/minio-go/v7"
"github.com/websublime/barrel/config"
Expand Down Expand Up @@ -79,8 +85,74 @@ func (api *API) CreateBucket(ctx *fiber.Ctx) error {
})
}

// PutObject upload to bucket
func (api *API) PutObject(ctx *fiber.Ctx) error {
return ctx.JSON(fiber.Map{
"data": "object",
})
bucketName := ctx.Params("bucket")
medias := []*models.Media{}
//TODO: user
bucket, err := models.FindBucket(api.db, bucketName)
if err != nil {
return utils.NewException(utils.ErrorBucketMissing, fiber.StatusBadRequest, err.Error())
}

if et, err := exiftool.NewExiftool(); err == nil {
defer et.Close()

if form, err := ctx.MultipartForm(); err == nil {
files := form.File["asset"]

for _, file := range files {
ctx.SaveFile(file, fmt.Sprintf("./temp/%s", file.Filename))
data := et.ExtractMetadata(fmt.Sprintf("./temp/%s", file.Filename))

meta, err := json.Marshal(data[0].Fields)
if err != nil {
return utils.NewException(utils.ErrorResourceMetaFailure, fiber.StatusBadRequest, err.Error())
}

metafile := new(config.MetaFile)
json.Unmarshal([]byte(meta), &metafile)

bucketFile, err := api.store.FPutObject(ctx.Context(), bucketName, metafile.FileName, fmt.Sprintf("./temp/%s", metafile.FileName), minio.PutObjectOptions{
ContentType: metafile.MIMEType,
UserMetadata: map[string]string{},
})

if err != nil {
return utils.NewException(utils.ErrorResourceBucketFailure, fiber.StatusBadRequest, err.Error())
}

u, _ := url.Parse(api.config.BarrelBaseURL)
u.Path = path.Join(u.Path, bucketName, metafile.FileName)
bucketFileJSON, _ := json.Marshal(bucketFile)
metaFileJSON, _ := json.Marshal(metafile)

media, _ := models.NewMedia(u.String(), nulls.NewUUID(uuid.Nil), nulls.NewString(string(bucketFileJSON)), nulls.NewString(string(metaFileJSON)))
bucketMedia, _ := models.NewBucketMedia(bucket.ID, media.ID)

err = api.db.Transaction(func(tx *storage.Connection) error {
terr := tx.Create(media)
terr = tx.Create(bucketMedia)

return terr
})
if err != nil {
return utils.NewException(utils.ErrorResourceModelSave, fiber.StatusBadRequest, err.Error())
}

os.Remove(fmt.Sprintf("./temp/%s", file.Filename))

medias = append(medias, media)
}

return ctx.JSON(fiber.Map{
"data": medias,
})
} else {
return utils.NewException(utils.ErrorResourceInvalidForm, fiber.StatusBadRequest, err.Error())
}
} else {
return utils.NewException(utils.ErrorExifMissing, fiber.StatusBadRequest, err.Error())
}

}
10 changes: 9 additions & 1 deletion api/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (api *API) AuthorizedMiddleware(ctx *fiber.Ctx) error {
}

ctx.Locals("claims", claims)
ctx.Locals("token", auth)
ctx.Locals("token", token)
} else {
return utils.NewException(utils.ErrorOrgStatusForbidden, fiber.StatusForbidden, "Only authorized requests are permitted")
}
Expand Down Expand Up @@ -85,6 +85,7 @@ func (api *API) AdminMiddleware(ctx *fiber.Ctx) error {
//CanAccessMiddleware check if user is register to access private endpoints
func (api *API) CanAccessMiddleware(ctx *fiber.Ctx) error {
isAdmin := ctx.Locals("admin").(bool)
token := ctx.Locals("token").(*jwt.Token)
headerKey := ctx.Get("X-BARREL-KEY")

if !isAdmin {
Expand All @@ -93,6 +94,13 @@ func (api *API) CanAccessMiddleware(ctx *fiber.Ctx) error {
return err
}

if user.Status == "disabled" {
return utils.NewException(utils.ErrorOrgStatusForbidden, fiber.StatusForbidden, "User is disabled")
}
// TODO: we need to save user key/secret on database
client, _ := config.NewClient(api.config, headerKey, "20894b322d824da6a6ad32cd1be2e368", token.Raw)
api.store = client

ctx.Locals("user", user)
}

Expand Down
2 changes: 1 addition & 1 deletion config/minio.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func OpenAdminClient(conf *EnvironmentConfig) (*madmin.AdminClient, error) {
// NewClient create a new client connection
func NewClient(conf *EnvironmentConfig, key string, secret string, token string) (*minio.Client, error) {
minioClient, err := minio.New(conf.BarrelMinioURL, &minio.Options{
Creds: credentials.NewStaticV4(key, secret, token),
Creds: credentials.NewStaticV4(key, secret, ""),
Secure: false,
})

Expand Down
13 changes: 13 additions & 0 deletions database/00-gotrue-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ CREATE TABLE auth.identities (
user_id uuid NOT NULL,
CONSTRAINT identities_pkey PRIMARY KEY (id)
);
-- auth.templates definition
CREATE TABLE auth.templates
(
id uuid NOT NULL,
aud varchar(255),
type varchar(50),
subject text,
url text DEFAULT '/',
base_url varchar(255),
url_template text,
CONSTRAINT templates_pkey PRIMARY KEY (id)
);
-- Gets the User ID from the request cookie
create or replace function auth.uid() returns uuid as $$
select nullif(current_setting('request.jwt.claim.sub', true), '')::uuid;
Expand All @@ -92,6 +104,7 @@ $$ language sql stable;
create or replace function auth.role() returns text as $$
select nullif(current_setting('request.jwt.claim.role', true), '')::text;
$$ language sql stable;

GRANT ALL PRIVILEGES ON SCHEMA auth TO postgres;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA auth TO postgres;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA auth TO postgres;
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/websublime/barrel
go 1.15

require (
github.com/barasher/go-exiftool v1.3.2
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gobuffalo/pop v4.13.1+incompatible
github.com/gobuffalo/pop/v5 v5.3.3
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQ
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.35.20/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/barasher/go-exiftool v1.3.2 h1:yWUIGOsM6PLbbHxe84ASTo/eyORMTyMH/5Qv1yBcC7s=
github.com/barasher/go-exiftool v1.3.2/go.mod h1:F9s/a3uHSM8YniVfwF+sbQUtP8Gmh9nyzigNF+8vsWo=
github.com/bcicen/jstream v1.0.1 h1:BXY7Cu4rdmc0rhyTVyT3UkxAiX3bnLpKLas9btbH5ck=
github.com/bcicen/jstream v1.0.1/go.mod h1:9ielPxqFry7Y4Tg3j4BfjPocfJ3TbsRtXOAYXYmRuAQ=
github.com/beevik/ntp v0.3.0 h1:xzVrPrE4ziasFXgBVBZJDP0Wg/KpMwk2KHJ4Ba8GrDw=
Expand Down
19 changes: 10 additions & 9 deletions models/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@ import (

// Bucket model type
type Bucket struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Bucket nulls.String `json:"bucket,omitempty" db:"bucket"`
OrgID nulls.String `json:"orgId,omitempty" db:"org_id"`
IsPrivate bool `json:"isPrivate" db:"is_private"`
Policy string `json:"policy" db:"-"`
CreatedAt time.Time `json:"createdAt" db:"created_at"`
UpdatedAt time.Time `json:"updatedAt" db:"updated_at"`
DeletedAt nulls.Time `json:"deleteAt,omitempty" db:"deleted_at"`
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Bucket nulls.String `json:"bucket,omitempty" db:"bucket"`
OrgID nulls.String `json:"orgId,omitempty" db:"org_id"`
IsPrivate bool `json:"isPrivate" db:"is_private"`
Policy string `json:"policy" db:"-"`
CreatedAt time.Time `json:"createdAt" db:"created_at"`
UpdatedAt time.Time `json:"updatedAt" db:"updated_at"`
DeletedAt nulls.Time `json:"deleteAt,omitempty" db:"deleted_at"`
BucketMedia []*BucketMedia `json:"medias,omitempty" many_to_many:"bucket_media" db:"-" fk_id:"bucket_id"`
}

// NewBucket creates new Bucket
Expand Down
38 changes: 38 additions & 0 deletions models/bucket_media.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package models

import (
"github.com/gobuffalo/uuid"
"github.com/pkg/errors"
"github.com/websublime/barrel/storage/namespace"
)

type BucketMedia struct {
ID uuid.UUID `json:"id" db:"id" primary_id:"id"`
BucketID uuid.UUID `json:"bucketId" db:"bucket_id"`
MediaID uuid.UUID `json:"mediaId" db:"media_id"`
}

func (BucketMedia) TableName() string {
tableName := "bucket_media"

if namespace.GetNamespace() != "" {
return namespace.GetNamespace() + "." + tableName
}

return tableName
}

func NewBucketMedia(bucket uuid.UUID, media uuid.UUID) (*BucketMedia, error) {
uid, err := uuid.NewV4()
if err != nil {
return nil, errors.Wrap(err, "Error generating unique id")
}

bucketMedia := &BucketMedia{
ID: uid,
BucketID: bucket,
MediaID: media,
}

return bucketMedia, nil
}
57 changes: 57 additions & 0 deletions models/media.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package models

import (
"time"

"github.com/gobuffalo/pop/nulls"
"github.com/gobuffalo/uuid"
"github.com/gobuffalo/validate/v3"
"github.com/gobuffalo/validate/v3/validators"
"github.com/pkg/errors"
"github.com/websublime/barrel/storage/namespace"
)

type Media struct {
ID uuid.UUID `json:"id" db:"id"`
URL string `json:"url" db:"url"`
Owner nulls.UUID `json:"ownerId" db:"owner"`
BucketFile nulls.String `json:"bucketFile" db:"bucket_file"`
Metafile nulls.String `json:"metadata" db:"meta_file"`
CreatedAt time.Time `json:"createdAt" db:"created_at"`
UpdatedAt time.Time `json:"updatedAt" db:"updated_at"`
DeletedAt nulls.Time `json:"deletedAt" db:"deleted_at"`
BucketMedia []*BucketMedia `json:"medias,omitempty" many_to_many:"bucket_media" db:"-" fk_id:"media_id" primary_id:"id"`
}

func (Media) TableName() string {
tableName := "medias"

if namespace.GetNamespace() != "" {
return namespace.GetNamespace() + "." + tableName
}

return tableName
}

func NewMedia(url string, owner nulls.UUID, bucketFile nulls.String, metadata nulls.String) (*Media, error) {
uid, err := uuid.NewV4()
if err != nil {
return nil, errors.Wrap(err, "Error generating unique id")
}

media := &Media{
ID: uid,
URL: url,
Owner: owner,
BucketFile: bucketFile,
Metafile: metadata,
}

return media, nil
}

func (m *Media) Validate() *validate.Errors {
return validate.Validate(
&validators.StringIsPresent{Field: m.URL, Name: "URL", Message: "Url is missign"},
)
}
1 change: 1 addition & 0 deletions storage/dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func Dial(conf *config.EnvironmentConfig) (*Connection, error) {
if logrus.StandardLogger().Level == logrus.DebugLevel {
pop.Debug = true
}
pop.Debug = true

return &Connection{db}, nil
}
Expand Down
Binary file added temp/me.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions utils/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const (
ErrorUserCreation = "EUSER_CREATEFORBIDDEN"
ErrorUserBodyParse = "EUSER_BODYPARSE"
ErrorBucketModel = "EBUCKET_MODEL"
ErrorBucketMissing = "EBUCKET_MISSING"
ErrorBucketBodyParse = "EBUCKET_BODYPARSE"
ErrorBucketExist = "EBUCKET_EXIST"
ErrorBucketCreation = "EBUCKET_CREATE"
Expand All @@ -23,6 +24,11 @@ const (
ErrorParseJson = "EJSON_PARSE"
ErrorResourceForbidden = "ERESOURCE_FORBIDDEN"
ErrorResourceInvalidBody = "ERESOURCE_BODYINVALID"
ErrorResourceInvalidForm = "ERESOURCE_FORMINVALID"
ErrorResourceMetaFailure = "ERESOURCE_METAFAILURE"
ErrorResourceBucketFailure = "ERESOURCE_BUCKETFAILURE"
ErrorResourceModelSave = "ERESOURCE_MODELSAVE"
ErrorExifMissing = "EEXIF_MISSING"
)

// Contains checks if a string is present in a slice
Expand Down

0 comments on commit f62d746

Please sign in to comment.