2020-04-21 00:48:27 +05:30
|
|
|
package redis
|
2017-06-30 21:10:25 +05:30
|
|
|
|
|
|
|
import (
|
2023-12-14 06:45:59 +05:30
|
|
|
"context"
|
2021-03-03 06:02:38 +05:30
|
|
|
"fmt"
|
2017-08-10 05:44:28 +05:30
|
|
|
"strings"
|
2017-06-30 21:10:25 +05:30
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
"github.com/mediocregopher/radix/v4"
|
2017-08-10 05:44:28 +05:30
|
|
|
|
2024-02-01 12:42:34 +05:30
|
|
|
"ely.by/chrly/internal/db"
|
2017-06-30 21:10:25 +05:30
|
|
|
)
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
const usernameToProfileKey = "hash:username-to-profile"
|
|
|
|
const userUuidToUsernameKey = "hash:uuid-to-username"
|
2020-04-21 04:50:45 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
type Redis struct {
|
|
|
|
client radix.Client
|
|
|
|
context context.Context
|
|
|
|
serializer db.ProfileSerializer
|
|
|
|
}
|
|
|
|
|
|
|
|
func New(ctx context.Context, profileSerializer db.ProfileSerializer, addr string, poolSize int) (*Redis, error) {
|
2023-12-14 06:45:59 +05:30
|
|
|
client, err := (radix.PoolConfig{Size: poolSize}).New(ctx, "tcp", addr)
|
2017-08-10 05:44:28 +05:30
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-04-21 00:48:27 +05:30
|
|
|
return &Redis{
|
2024-01-30 13:35:04 +05:30
|
|
|
client: client,
|
|
|
|
context: ctx,
|
|
|
|
serializer: profileSerializer,
|
2020-04-21 00:48:27 +05:30
|
|
|
}, nil
|
2017-08-14 23:36:22 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (r *Redis) FindProfileByUsername(username string) (*db.Profile, error) {
|
|
|
|
var profile *db.Profile
|
|
|
|
err := r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
|
2023-12-14 06:45:59 +05:30
|
|
|
var err error
|
2024-01-30 13:35:04 +05:30
|
|
|
profile, err = r.findProfileByUsername(ctx, conn, username)
|
2023-12-14 06:45:59 +05:30
|
|
|
|
|
|
|
return err
|
|
|
|
}))
|
2019-05-01 04:40:11 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
return profile, err
|
2018-01-23 21:13:37 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (r *Redis) findProfileByUsername(ctx context.Context, conn radix.Conn, username string) (*db.Profile, error) {
|
2023-12-14 06:45:59 +05:30
|
|
|
var encodedResult []byte
|
2024-01-30 13:35:04 +05:30
|
|
|
err := conn.Do(ctx, radix.Cmd(&encodedResult, "HGET", usernameToProfileKey, usernameHashKey(username)))
|
2023-12-14 06:45:59 +05:30
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(encodedResult) == 0 {
|
2020-04-21 00:48:27 +05:30
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
return r.serializer.Deserialize(encodedResult)
|
2018-01-23 21:13:37 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (r *Redis) FindProfileByUuid(uuid string) (*db.Profile, error) {
|
|
|
|
var skin *db.Profile
|
|
|
|
err := r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
|
2023-12-14 06:45:59 +05:30
|
|
|
var err error
|
2024-01-30 13:35:04 +05:30
|
|
|
skin, err = r.findProfileByUuid(ctx, conn, uuid)
|
2019-05-01 04:40:11 +05:30
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
return err
|
|
|
|
}))
|
2018-01-23 21:13:37 +05:30
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
return skin, err
|
|
|
|
}
|
2020-04-21 00:48:27 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (r *Redis) findProfileByUuid(ctx context.Context, conn radix.Conn, uuid string) (*db.Profile, error) {
|
|
|
|
username, err := r.findUsernameHashKeyByUuid(ctx, conn, uuid)
|
2019-05-06 19:42:37 +05:30
|
|
|
if err != nil {
|
2020-04-21 00:48:27 +05:30
|
|
|
return nil, err
|
2019-05-06 19:42:37 +05:30
|
|
|
}
|
2019-04-25 04:53:10 +05:30
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
if username == "" {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
return r.findProfileByUsername(ctx, conn, username)
|
2019-04-25 04:53:10 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (r *Redis) findUsernameHashKeyByUuid(ctx context.Context, conn radix.Conn, uuid string) (string, error) {
|
|
|
|
var username string
|
|
|
|
return username, conn.Do(ctx, radix.FlatCmd(&username, "HGET", userUuidToUsernameKey, normalizeUuid(uuid)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Redis) SaveProfile(profile *db.Profile) error {
|
|
|
|
return r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
|
|
|
|
return r.saveProfile(ctx, conn, profile)
|
2023-12-14 06:45:59 +05:30
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (r *Redis) saveProfile(ctx context.Context, conn radix.Conn, profile *db.Profile) error {
|
|
|
|
newUsernameHashKey := usernameHashKey(profile.Username)
|
|
|
|
existsUsernameHashKey, err := r.findUsernameHashKeyByUuid(ctx, conn, profile.Uuid)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = conn.Do(ctx, radix.Cmd(nil, "MULTI"))
|
2019-05-06 19:42:37 +05:30
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-06-30 21:10:25 +05:30
|
|
|
|
2020-04-21 00:48:27 +05:30
|
|
|
// If user has changed username, then we must delete his old username record
|
2024-01-30 13:35:04 +05:30
|
|
|
if existsUsernameHashKey != "" && existsUsernameHashKey != newUsernameHashKey {
|
|
|
|
err = conn.Do(ctx, radix.Cmd(nil, "HDEL", usernameToProfileKey, existsUsernameHashKey))
|
2023-12-14 06:45:59 +05:30
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-17 05:17:35 +05:30
|
|
|
}
|
2017-06-30 21:10:25 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
err = conn.Do(ctx, radix.FlatCmd(nil, "HSET", userUuidToUsernameKey, normalizeUuid(profile.Uuid), newUsernameHashKey))
|
2023-12-14 06:45:59 +05:30
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-04-21 00:48:27 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
serializedProfile, err := r.serializer.Serialize(profile)
|
2023-12-14 06:45:59 +05:30
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-06-30 21:10:25 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
err = conn.Do(ctx, radix.FlatCmd(nil, "HSET", usernameToProfileKey, newUsernameHashKey, serializedProfile))
|
2020-04-20 17:46:15 +05:30
|
|
|
if err != nil {
|
2020-04-21 00:48:27 +05:30
|
|
|
return err
|
2020-04-20 17:46:15 +05:30
|
|
|
}
|
2017-06-30 21:10:25 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
err = conn.Do(ctx, radix.Cmd(nil, "EXEC"))
|
2023-12-14 06:45:59 +05:30
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-17 05:17:35 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
return nil
|
2018-01-23 21:13:37 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (r *Redis) RemoveProfileByUuid(uuid string) error {
|
|
|
|
return r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
|
|
|
|
return r.removeProfileByUuid(ctx, conn, uuid)
|
2023-12-14 06:45:59 +05:30
|
|
|
}))
|
2020-04-21 00:48:27 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (r *Redis) removeProfileByUuid(ctx context.Context, conn radix.Conn, uuid string) error {
|
|
|
|
username, err := r.findUsernameHashKeyByUuid(ctx, conn, uuid)
|
2018-01-23 21:13:37 +05:30
|
|
|
if err != nil {
|
2019-05-06 19:42:37 +05:30
|
|
|
return err
|
2018-01-23 21:13:37 +05:30
|
|
|
}
|
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
err = conn.Do(ctx, radix.Cmd(nil, "MULTI"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-01-23 21:13:37 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
err = conn.Do(ctx, radix.FlatCmd(nil, "HDEL", userUuidToUsernameKey, normalizeUuid(uuid)))
|
2023-12-14 06:45:59 +05:30
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-01-23 21:13:37 +05:30
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
if username != "" {
|
|
|
|
err = conn.Do(ctx, radix.Cmd(nil, "HDEL", usernameToProfileKey, usernameHashKey(username)))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-12-14 06:45:59 +05:30
|
|
|
}
|
2018-01-23 21:13:37 +05:30
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
return conn.Do(ctx, radix.Cmd(nil, "EXEC"))
|
2018-01-23 21:13:37 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (r *Redis) GetUuidForMojangUsername(username string) (string, string, error) {
|
2023-12-14 06:45:59 +05:30
|
|
|
var uuid string
|
2024-01-10 06:12:10 +05:30
|
|
|
foundUsername := username
|
2024-01-30 13:35:04 +05:30
|
|
|
err := r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
|
2023-12-14 06:45:59 +05:30
|
|
|
var err error
|
2024-01-10 06:12:10 +05:30
|
|
|
uuid, foundUsername, err = findMojangUuidByUsername(ctx, conn, username)
|
2017-08-17 05:17:35 +05:30
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
return err
|
|
|
|
}))
|
|
|
|
|
2024-01-10 06:12:10 +05:30
|
|
|
return uuid, foundUsername, err
|
2017-08-17 05:17:35 +05:30
|
|
|
}
|
|
|
|
|
2024-01-10 06:12:10 +05:30
|
|
|
func findMojangUuidByUsername(ctx context.Context, conn radix.Conn, username string) (string, string, error) {
|
2024-01-30 13:35:04 +05:30
|
|
|
key := buildMojangUsernameKey(username)
|
2023-12-14 06:45:59 +05:30
|
|
|
var result string
|
2024-01-30 13:35:04 +05:30
|
|
|
err := conn.Do(ctx, radix.Cmd(&result, "GET", key))
|
2023-12-14 06:45:59 +05:30
|
|
|
if err != nil {
|
2024-01-10 06:12:10 +05:30
|
|
|
return "", "", err
|
2023-12-14 06:45:59 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
if result == "" {
|
2024-01-10 06:12:10 +05:30
|
|
|
return "", "", nil
|
2019-04-25 04:53:10 +05:30
|
|
|
}
|
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
parts := strings.Split(result, ":")
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
return parts[1], parts[0], nil
|
2019-04-25 04:53:10 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (r *Redis) StoreMojangUuid(username string, uuid string) error {
|
|
|
|
return r.client.Do(r.context, radix.WithConn("", func(ctx context.Context, conn radix.Conn) error {
|
2023-12-14 06:45:59 +05:30
|
|
|
return storeMojangUuid(ctx, conn, username, uuid)
|
|
|
|
}))
|
2020-04-21 00:48:27 +05:30
|
|
|
}
|
|
|
|
|
2023-12-14 06:45:59 +05:30
|
|
|
func storeMojangUuid(ctx context.Context, conn radix.Conn, username string, uuid string) error {
|
2024-01-30 13:35:04 +05:30
|
|
|
value := fmt.Sprintf("%s:%s", username, uuid)
|
|
|
|
err := conn.Do(ctx, radix.FlatCmd(nil, "SET", buildMojangUsernameKey(username), value, "EX", 60*60*24*30))
|
2023-12-14 06:45:59 +05:30
|
|
|
if err != nil {
|
|
|
|
return err
|
2019-05-06 19:42:37 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2019-04-25 04:53:10 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func (r *Redis) Ping() error {
|
|
|
|
return r.client.Do(r.context, radix.Cmd(nil, "PING"))
|
2020-05-01 05:16:12 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func normalizeUuid(uuid string) string {
|
|
|
|
return strings.ToLower(strings.ReplaceAll(uuid, "-", ""))
|
2017-08-10 05:44:28 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func usernameHashKey(username string) string {
|
|
|
|
return strings.ToLower(username)
|
2017-08-10 05:44:28 +05:30
|
|
|
}
|
|
|
|
|
2024-01-30 13:35:04 +05:30
|
|
|
func buildMojangUsernameKey(username string) string {
|
|
|
|
return fmt.Sprintf("mojang:uuid:%s", usernameHashKey(username))
|
2017-08-10 05:44:28 +05:30
|
|
|
}
|