diff --git a/db/factory.go b/db/factory.go index 93f88c8..2ba75a7 100644 --- a/db/factory.go +++ b/db/factory.go @@ -3,6 +3,7 @@ package db import ( "github.com/spf13/viper" + "github.com/elyby/chrly/api/mojang/queue" "github.com/elyby/chrly/interfaces" ) @@ -13,6 +14,7 @@ type StorageFactory struct { type RepositoriesCreator interface { CreateSkinsRepository() (interfaces.SkinsRepository, error) CreateCapesRepository() (interfaces.CapesRepository, error) + CreateMojangUuidsRepository() (queue.UuidsStorage, error) } func (factory *StorageFactory) CreateFactory(backend string) RepositoriesCreator { @@ -25,7 +27,7 @@ func (factory *StorageFactory) CreateFactory(backend string) RepositoriesCreator } case "filesystem": return &FilesystemFactory{ - BasePath : factory.Config.GetString("storage.filesystem.basePath"), + BasePath: factory.Config.GetString("storage.filesystem.basePath"), CapesDirName: factory.Config.GetString("storage.filesystem.capesDirName"), } } diff --git a/db/filesystem.go b/db/filesystem.go index cbc6251..4674652 100644 --- a/db/filesystem.go +++ b/db/filesystem.go @@ -5,12 +5,13 @@ import ( "path" "strings" + "github.com/elyby/chrly/api/mojang/queue" "github.com/elyby/chrly/interfaces" "github.com/elyby/chrly/model" ) type FilesystemFactory struct { - BasePath string + BasePath string CapesDirName string } @@ -26,6 +27,10 @@ func (f FilesystemFactory) CreateCapesRepository() (interfaces.CapesRepository, return &filesStorage{path: path.Join(f.BasePath, f.CapesDirName)}, nil } +func (f FilesystemFactory) CreateMojangUuidsRepository() (queue.UuidsStorage, error) { + panic("implement me") +} + func (f FilesystemFactory) validateFactoryConfig() error { if f.BasePath == "" { return &ParamRequired{"basePath"} @@ -47,7 +52,7 @@ func (repository *filesStorage) FindByUsername(username string) (*model.Cape, er return nil, &CapeNotFoundError{username} } - capePath := path.Join(repository.path, strings.ToLower(username) + ".png") + capePath := path.Join(repository.path, strings.ToLower(username)+".png") file, err := os.Open(capePath) if err != nil { return nil, &CapeNotFoundError{username} diff --git a/db/redis.go b/db/redis.go index 08f1359..0a8253b 100644 --- a/db/redis.go +++ b/db/redis.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "log" + "strconv" "strings" "time" @@ -14,6 +15,7 @@ import ( "github.com/mediocregopher/radix.v2/redis" "github.com/mediocregopher/radix.v2/util" + "github.com/elyby/chrly/api/mojang/queue" "github.com/elyby/chrly/interfaces" "github.com/elyby/chrly/model" ) @@ -27,7 +29,20 @@ type RedisFactory struct { // TODO: maybe we should manually return connection to the pool? +// TODO: Why isn't a pointer used here? func (f RedisFactory) CreateSkinsRepository() (interfaces.SkinsRepository, error) { + return f.createInstance() +} + +func (f RedisFactory) CreateCapesRepository() (interfaces.CapesRepository, error) { + panic("capes repository not supported for this storage type") +} + +func (f RedisFactory) CreateMojangUuidsRepository() (queue.UuidsStorage, error) { + return f.createInstance() +} + +func (f RedisFactory) createInstance() (*redisDb, error) { connection, err := f.getConnection() if err != nil { return nil, err @@ -36,10 +51,6 @@ func (f RedisFactory) CreateSkinsRepository() (interfaces.SkinsRepository, error return &redisDb{connection}, nil } -func (f RedisFactory) CreateCapesRepository() (interfaces.CapesRepository, error) { - panic("capes repository not supported for this storage type") -} - func (f RedisFactory) getConnection() (*pool.Pool, error) { if f.connection == nil { if f.Host == "" { @@ -89,7 +100,9 @@ type redisDb struct { } const accountIdToUsernameKey = "hash:username-to-account-id" +const mojangUsernameToUuidKey = "hash:mojang-username-to-uuid" +// TODO: return connection to the pool after usage func (db *redisDb) FindByUsername(username string) (*model.Skin, error) { return findByUsername(username, db.getConn()) } @@ -110,6 +123,14 @@ func (db *redisDb) RemoveByUsername(username string) error { return removeByUsername(username, db.getConn()) } +func (db *redisDb) GetUuid(username string) (string, error) { + return findMojangUuidByUsername(username, db.getConn()) +} + +func (db *redisDb) StoreUuid(username string, uuid string) { + storeMojangUuid(username, uuid, db.getConn()) +} + func (db *redisDb) getConn() util.Cmder { conn, _ := db.conn.Get() return conn @@ -221,6 +242,28 @@ func save(skin *model.Skin, conn util.Cmder) error { return nil } +func findMojangUuidByUsername(username string, conn util.Cmder) (string, error) { + response := conn.Cmd("HGET", mojangUsernameToUuidKey, strings.ToLower(username)) + if response.IsType(redis.Nil) { + return "", &queue.ValueNotFound{} + } + + data, _ := response.Str() + parts := strings.Split(data, ":") + timestamp, _ := strconv.ParseInt(parts[1], 10, 64) + storedAt := time.Unix(timestamp, 0) + if storedAt.Add(time.Hour * 24 * 30).Before(time.Now()) { + return "", &queue.ValueNotFound{} + } + + return parts[0], nil +} + +func storeMojangUuid(username string, uuid string, conn util.Cmder) { + value := uuid + ":" + strconv.FormatInt(time.Now().Unix(), 10) + conn.Cmd("HSET", mojangUsernameToUuidKey, strings.ToLower(username), value) +} + func buildUsernameKey(username string) string { return "username:" + strings.ToLower(username) }