add support for avatar store uri
This commit is contained in:
+5
-2
@@ -34,7 +34,7 @@ Example with chi router:
|
||||
```go
|
||||
|
||||
func main() {
|
||||
/// define options
|
||||
// define options
|
||||
options := auth.Opts{
|
||||
SecretReader: token.SecretFunc(func(id string) (string, error) { // secret key for JWT
|
||||
return "secret", nil
|
||||
@@ -129,7 +129,10 @@ Direct links to avatars won't survive any real-life usage if they linked from a
|
||||
- `avatar.GridFS` - external [GridFS](https://docs.mongodb.com/manual/core/gridfs/) (mongo db).
|
||||
- In case of need custom implementations of other stores can be passed in and used by `auth` library. Each store has to implement `avatar.Store` [interface](https://github.com/go-pkgz/auth/blob/master/avatar/store.go#L25).
|
||||
- All avatar-related setup done as a part of `auth.Opts` and needs:
|
||||
- `AvatarStore` - avatar store to use, i.e. `avatar.NewLocalFS("/tmp/avatars")`
|
||||
- `AvatarStore` - avatar store to use, i.e. `avatar.NewLocalFS("/tmp/avatars")` or more generic `avatar.NewStore(uri)`
|
||||
- file system uri - `file:///tmp/location` or just `/tmp/location`
|
||||
- boltdb - `bolt://tmp/avatars.bdb`
|
||||
- mongo - `"mongodb://127.0.0.1:27017/test?ava_db=db1&ava_coll=coll1`
|
||||
- `AvatarRoutePath` - route prefix for direct links to proxied avatar. For example `/api/v1/avatars` will make full links like this - `http://example.com/api/v1/avatars/1234567890123.image`. The url will be stored in user's token and retrieved by middleware (see "User Info")
|
||||
- `AvatarResizeLimit` - size (in pixels) used to resize the avatar. Pls note - resize happens once as a part of `Put` call, i.e. on login. 0 size (default) disables resizing.
|
||||
|
||||
|
||||
+5
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@@ -126,6 +127,10 @@ func (b *BoltDB) Close() error {
|
||||
return errors.Wrapf(b.db.Close(), "failed to close %s", b.fileName)
|
||||
}
|
||||
|
||||
func (b *BoltDB) String() string {
|
||||
return fmt.Sprintf("boltdb, path=%s", b.fileName)
|
||||
}
|
||||
|
||||
func (b *BoltDB) sha1(data []byte, avatarID string) (id string) {
|
||||
h := sha1.New()
|
||||
if _, err := h.Write(data); err != nil {
|
||||
|
||||
+5
@@ -2,6 +2,7 @@ package avatar
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@@ -116,3 +117,7 @@ func (gf *GridFS) List() (ids []string, err error) {
|
||||
func (gf *GridFS) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gf *GridFS) String() string {
|
||||
return fmt.Sprintf("mongo (grid fs), conn=%s", gf.Connection)
|
||||
}
|
||||
|
||||
+4
@@ -108,6 +108,10 @@ func (fs *LocalFS) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fs *LocalFS) String() string {
|
||||
return fmt.Sprintf("localfs, path=%s", fs.storePath)
|
||||
}
|
||||
|
||||
// get location (directory) for user id by adding partition to final path in order to keep files
|
||||
// in different subdirectories and avoid too many files in a single place.
|
||||
// the end result is a full path like this - /tmp/avatars.test/92
|
||||
|
||||
+54
@@ -4,15 +4,21 @@ package avatar
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
_ "image/gif" // initializing packages for supporting GIF
|
||||
_ "image/jpeg" // initializing packages for supporting JPEG.
|
||||
_ "image/png" // initializing packages for supporting PNG.
|
||||
"io"
|
||||
"log"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
bolt "github.com/coreos/bbolt"
|
||||
"github.com/go-pkgz/auth/token"
|
||||
"github.com/go-pkgz/mongo"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// imgSfx for avatars
|
||||
@@ -22,6 +28,7 @@ var reValidAvatarID = regexp.MustCompile(`^[a-fA-F0-9]{40}\.image$`)
|
||||
|
||||
// Store defines interface to store and and load avatars
|
||||
type Store interface {
|
||||
fmt.Stringer
|
||||
Put(userID string, reader io.Reader) (avatarID string, err error) // save avatar data from the reader and return base name
|
||||
Get(avatarID string) (reader io.ReadCloser, size int, err error) // load avatar via reader
|
||||
ID(avatarID string) (id string) // unique id of stored avatar's data
|
||||
@@ -30,6 +37,30 @@ type Store interface {
|
||||
Close() error // close store
|
||||
}
|
||||
|
||||
// NewStore provides factory for all supported stores making the one
|
||||
// based on uri protocol. Default (no protocol) is file-system
|
||||
func NewStore(uri string) (Store, error) {
|
||||
switch {
|
||||
case strings.HasPrefix(uri, "file://"):
|
||||
return NewLocalFS(strings.TrimPrefix(uri, "file://")), nil
|
||||
case !strings.Contains(uri, "://"):
|
||||
return NewLocalFS(uri), nil
|
||||
case strings.HasPrefix(uri, "mongodb://"):
|
||||
db, coll, u, err := parseExtMongoURI(uri)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't parse mongo store uri %s", uri)
|
||||
}
|
||||
mg, err := mongo.NewServerWithURL(u, time.Second)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to make mongo server")
|
||||
}
|
||||
return NewGridFS(mongo.NewConnection(mg, db, coll)), nil
|
||||
case strings.HasPrefix(uri, "bolt://"):
|
||||
return NewBoltDB(strings.TrimPrefix(uri, "bolt://"), bolt.Options{})
|
||||
}
|
||||
return nil, errors.Errorf("can't parse store url %s", uri)
|
||||
}
|
||||
|
||||
// Migrate avatars between stores
|
||||
func Migrate(dst, src Store) (int, error) {
|
||||
ids, err := src.List()
|
||||
@@ -59,3 +90,26 @@ func encodeID(id string) string {
|
||||
}
|
||||
return token.HashID(sha1.New(), id)
|
||||
}
|
||||
|
||||
// parseExtMongoURI extracts extra params ava_db and ava_coll and remove
|
||||
// from the url. Input example: mongodb://user:password@127.0.0.1:27017/test?ssl=true&ava_db=db1&ava_coll=coll1
|
||||
func parseExtMongoURI(uri string) (db, collection, cleanURI string, err error) {
|
||||
|
||||
db, collection = "test", "avatars_fs"
|
||||
u, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
if val := u.Query().Get("ava_db"); val != "" {
|
||||
db = val
|
||||
}
|
||||
if val := u.Query().Get("ava_coll"); val != "" {
|
||||
collection = val
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
q.Del("ava_db")
|
||||
q.Del("ava_coll")
|
||||
u.RawQuery = q.Encode()
|
||||
return db, collection, u.String(), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user