package client import ( "context" "errors" "math/rand" "path/filepath" "time" "git.kapelle.org/niklas/s3share/internal/db" "git.kapelle.org/niklas/s3share/internal/s3" "git.kapelle.org/niklas/s3share/internal/types" ) var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") type Client struct { db db.DB s3 s3.S3 } func NewClient(db db.DB, s3 s3.S3) *Client { rand.Seed(time.Now().UnixNano()) return &Client{ db: db, s3: s3, } } // createRandomString creates a random string of length 6 to be used as a slug func createRandomString() string { s := make([]rune, 6) for i := range s { s[i] = letters[rand.Intn(len(letters))] } return string(s) } // createValidSlug creates a valid slug that is not yet in use func (c *Client) createValidSlug(ctx context.Context) (string, error) { for i := 0; i < 10; i++ { slug := createRandomString() res, err := c.db.GetShare(ctx, slug) if err != nil { return "", err } if res == nil { return slug, nil } } return "", errors.New("could not create valid slug after 10 tries") } // GetShare returns the share with the given slug, nil if not found func (c *Client) GetShare(ctx context.Context, slug string) (*types.Share, error) { return c.db.GetShare(ctx, slug) } // CreateShare creates a new share with the given key and returns the share with the slug func (c *Client) CreateShare(ctx context.Context, key string) (*types.Share, error) { slug, err := c.createValidSlug(ctx) if err != nil { return nil, err } share := &types.Share{ Slug: slug, Key: key, } exists, err := c.s3.KeyExists(ctx, key) if err != nil { return nil, err } if !exists { return nil, types.ErrKeyNotFound } err = c.db.CreateShare(ctx, share) if err != nil { return nil, err } return share, nil } // GetObjectFromShare returns the s3 object to the given share func (c *Client) GetObjectFromShare(ctx context.Context, share *types.Share) (s3.ObjectReader, error) { return c.s3.GetObject(ctx, share.Key) } // DeleteShare deletes the share with the given slug func (c *Client) DeleteShare(ctx context.Context, slug string) error { return c.db.DeleteShare(ctx, slug) } // GetObjectMetadata returns the metadata of the object with the given key func (c *Client) GetObjectMetadata(ctx context.Context, key string) (*types.Metadata, error) { metadata, err := c.s3.GetObjectMetadata(ctx, key) if err != nil { return nil, err } if metadata == nil { return nil, nil } if metadata.Filename == "" { metadata.Filename = filepath.Base(key) } return metadata, nil } // GetAllShares returns all shares func (c *Client) GetAllShares(ctx context.Context) ([]*types.Share, error) { return c.db.GetAllShares(ctx) }