Merge pull request #3341 from openimsdk/cherry-pick-56c5c1f
fix: delete token by correct platformID && feat: adminToken can be re… [Created by @icey-yu from #3313]pull/3343/head
commit
c7a934e8db
@ -0,0 +1,166 @@
|
|||||||
|
package mcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/cachekey"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||||
|
"github.com/openimsdk/tools/errs"
|
||||||
|
"github.com/openimsdk/tools/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTokenCacheModel(cache database.Cache, accessExpire int64) cache.TokenModel {
|
||||||
|
c := &tokenCache{cache: cache}
|
||||||
|
c.accessExpire = c.getExpireTime(accessExpire)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenCache struct {
|
||||||
|
cache database.Cache
|
||||||
|
accessExpire time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *tokenCache) getTokenKey(userID string, platformID int, token string) string {
|
||||||
|
return cachekey.GetTokenKey(userID, platformID) + ":" + token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *tokenCache) SetTokenFlag(ctx context.Context, userID string, platformID int, token string, flag int) error {
|
||||||
|
return x.cache.Set(ctx, x.getTokenKey(userID, platformID, token), strconv.Itoa(flag), x.accessExpire)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTokenFlagEx set token and flag with expire time
|
||||||
|
func (x *tokenCache) SetTokenFlagEx(ctx context.Context, userID string, platformID int, token string, flag int) error {
|
||||||
|
return x.SetTokenFlag(ctx, userID, platformID, token, flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *tokenCache) GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error) {
|
||||||
|
prefix := x.getTokenKey(userID, platformID, "")
|
||||||
|
m, err := x.cache.Prefix(ctx, prefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.Wrap(err)
|
||||||
|
}
|
||||||
|
mm := make(map[string]int)
|
||||||
|
for k, v := range m {
|
||||||
|
state, err := strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
log.ZError(ctx, "token value is not int", err, "value", v, "userID", userID, "platformID", platformID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mm[strings.TrimPrefix(k, prefix)] = state
|
||||||
|
}
|
||||||
|
return mm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *tokenCache) HasTemporaryToken(ctx context.Context, userID string, platformID int, token string) error {
|
||||||
|
key := cachekey.GetTemporaryTokenKey(userID, platformID, token)
|
||||||
|
if _, err := x.cache.Get(ctx, []string{key}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *tokenCache) GetAllTokensWithoutError(ctx context.Context, userID string) (map[int]map[string]int, error) {
|
||||||
|
prefix := cachekey.UidPidToken + userID + ":"
|
||||||
|
tokens, err := x.cache.Prefix(ctx, prefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res := make(map[int]map[string]int)
|
||||||
|
for key, flagStr := range tokens {
|
||||||
|
flag, err := strconv.Atoi(flagStr)
|
||||||
|
if err != nil {
|
||||||
|
log.ZError(ctx, "token value is not int", err, "key", key, "value", flagStr, "userID", userID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
arr := strings.SplitN(strings.TrimPrefix(key, prefix), ":", 2)
|
||||||
|
if len(arr) != 2 {
|
||||||
|
log.ZError(ctx, "token value is not int", err, "key", key, "value", flagStr, "userID", userID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
platformID, err := strconv.Atoi(arr[0])
|
||||||
|
if err != nil {
|
||||||
|
log.ZError(ctx, "token value is not int", err, "key", key, "value", flagStr, "userID", userID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
token := arr[1]
|
||||||
|
if token == "" {
|
||||||
|
log.ZError(ctx, "token value is not int", err, "key", key, "value", flagStr, "userID", userID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tk, ok := res[platformID]
|
||||||
|
if !ok {
|
||||||
|
tk = make(map[string]int)
|
||||||
|
res[platformID] = tk
|
||||||
|
}
|
||||||
|
tk[token] = flag
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *tokenCache) SetTokenMapByUidPid(ctx context.Context, userID string, platformID int, m map[string]int) error {
|
||||||
|
for token, flag := range m {
|
||||||
|
err := x.SetTokenFlag(ctx, userID, platformID, token, flag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *tokenCache) BatchSetTokenMapByUidPid(ctx context.Context, tokens map[string]map[string]any) error {
|
||||||
|
for prefix, tokenFlag := range tokens {
|
||||||
|
for token, flag := range tokenFlag {
|
||||||
|
flagStr := fmt.Sprintf("%v", flag)
|
||||||
|
if err := x.cache.Set(ctx, prefix+":"+token, flagStr, x.accessExpire); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *tokenCache) DeleteTokenByUidPid(ctx context.Context, userID string, platformID int, fields []string) error {
|
||||||
|
keys := make([]string, 0, len(fields))
|
||||||
|
for _, token := range fields {
|
||||||
|
keys = append(keys, x.getTokenKey(userID, platformID, token))
|
||||||
|
}
|
||||||
|
return x.cache.Del(ctx, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *tokenCache) getExpireTime(t int64) time.Duration {
|
||||||
|
return time.Hour * 24 * time.Duration(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *tokenCache) DeleteTokenByTokenMap(ctx context.Context, userID string, tokens map[int][]string) error {
|
||||||
|
keys := make([]string, 0, len(tokens))
|
||||||
|
for platformID, ts := range tokens {
|
||||||
|
for _, t := range ts {
|
||||||
|
keys = append(keys, x.getTokenKey(userID, platformID, t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x.cache.Del(ctx, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *tokenCache) DeleteAndSetTemporary(ctx context.Context, userID string, platformID int, fields []string) error {
|
||||||
|
keys := make([]string, 0, len(fields))
|
||||||
|
for _, f := range fields {
|
||||||
|
keys = append(keys, x.getTokenKey(userID, platformID, f))
|
||||||
|
}
|
||||||
|
if err := x.cache.Del(ctx, keys); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range fields {
|
||||||
|
k := cachekey.GetTemporaryTokenKey(userID, platformID, f)
|
||||||
|
if err := x.cache.Set(ctx, k, "", time.Minute*5); err != nil {
|
||||||
|
return errs.Wrap(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,183 @@
|
|||||||
|
package mgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
||||||
|
"github.com/openimsdk/tools/db/mongoutil"
|
||||||
|
"github.com/openimsdk/tools/errs"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCacheMgo(db *mongo.Database) (*CacheMgo, error) {
|
||||||
|
coll := db.Collection(database.CacheName)
|
||||||
|
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
||||||
|
{
|
||||||
|
Keys: bson.D{
|
||||||
|
{Key: "key", Value: 1},
|
||||||
|
},
|
||||||
|
Options: options.Index().SetUnique(true),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Keys: bson.D{
|
||||||
|
{Key: "expire_at", Value: 1},
|
||||||
|
},
|
||||||
|
Options: options.Index().SetExpireAfterSeconds(0),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.Wrap(err)
|
||||||
|
}
|
||||||
|
return &CacheMgo{coll: coll}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CacheMgo struct {
|
||||||
|
coll *mongo.Collection
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CacheMgo) findToMap(res []model.Cache, now time.Time) map[string]string {
|
||||||
|
kv := make(map[string]string)
|
||||||
|
for _, re := range res {
|
||||||
|
if re.ExpireAt != nil && re.ExpireAt.Before(now) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
kv[re.Key] = re.Value
|
||||||
|
}
|
||||||
|
return kv
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CacheMgo) Get(ctx context.Context, key []string) (map[string]string, error) {
|
||||||
|
if len(key) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
res, err := mongoutil.Find[model.Cache](ctx, x.coll, bson.M{
|
||||||
|
"key": bson.M{"$in": key},
|
||||||
|
"$or": []bson.M{
|
||||||
|
{"expire_at": bson.M{"$gt": now}},
|
||||||
|
{"expire_at": nil},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x.findToMap(res, now), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CacheMgo) Prefix(ctx context.Context, prefix string) (map[string]string, error) {
|
||||||
|
now := time.Now()
|
||||||
|
res, err := mongoutil.Find[model.Cache](ctx, x.coll, bson.M{
|
||||||
|
"key": bson.M{"$regex": "^" + prefix},
|
||||||
|
"$or": []bson.M{
|
||||||
|
{"expire_at": bson.M{"$gt": now}},
|
||||||
|
{"expire_at": nil},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x.findToMap(res, now), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CacheMgo) Set(ctx context.Context, key string, value string, expireAt time.Duration) error {
|
||||||
|
cv := &model.Cache{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
if expireAt > 0 {
|
||||||
|
now := time.Now().Add(expireAt)
|
||||||
|
cv.ExpireAt = &now
|
||||||
|
}
|
||||||
|
opt := options.Update().SetUpsert(true)
|
||||||
|
return mongoutil.UpdateOne(ctx, x.coll, bson.M{"key": key}, bson.M{"$set": cv}, false, opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CacheMgo) Incr(ctx context.Context, key string, value int) (int, error) {
|
||||||
|
pipeline := mongo.Pipeline{
|
||||||
|
{
|
||||||
|
{"$set", bson.M{
|
||||||
|
"value": bson.M{
|
||||||
|
"$toString": bson.M{
|
||||||
|
"$add": bson.A{
|
||||||
|
bson.M{"$toInt": "$value"},
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
opt := options.FindOneAndUpdate().SetReturnDocument(options.After)
|
||||||
|
res, err := mongoutil.FindOneAndUpdate[model.Cache](ctx, x.coll, bson.M{"key": key}, pipeline, opt)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return strconv.Atoi(res.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CacheMgo) Del(ctx context.Context, key []string) error {
|
||||||
|
if len(key) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := x.coll.DeleteMany(ctx, bson.M{"key": bson.M{"$in": key}})
|
||||||
|
return errs.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CacheMgo) lockKey(key string) string {
|
||||||
|
return "LOCK_" + key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CacheMgo) Lock(ctx context.Context, key string, duration time.Duration) (string, error) {
|
||||||
|
tmp, err := uuid.NewUUID()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if duration <= 0 || duration > time.Minute*10 {
|
||||||
|
duration = time.Minute * 10
|
||||||
|
}
|
||||||
|
cv := &model.Cache{
|
||||||
|
Key: x.lockKey(key),
|
||||||
|
Value: tmp.String(),
|
||||||
|
ExpireAt: nil,
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
|
||||||
|
defer cancel()
|
||||||
|
wait := func() error {
|
||||||
|
timeout := time.NewTimer(time.Millisecond * 100)
|
||||||
|
defer timeout.Stop()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-timeout.C:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
if err := mongoutil.DeleteOne(ctx, x.coll, bson.M{"key": key, "expire_at": bson.M{"$lt": time.Now()}}); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
expireAt := time.Now().Add(duration)
|
||||||
|
cv.ExpireAt = &expireAt
|
||||||
|
if err := mongoutil.InsertMany[*model.Cache](ctx, x.coll, []*model.Cache{cv}); err != nil {
|
||||||
|
if mongo.IsDuplicateKeyError(err) {
|
||||||
|
if err := wait(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return cv.Value, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CacheMgo) Unlock(ctx context.Context, key string, value string) error {
|
||||||
|
return mongoutil.DeleteOne(ctx, x.coll, bson.M{"key": x.lockKey(key), "value": value})
|
||||||
|
}
|
Loading…
Reference in new issue