package cache import ( "context" "fmt" "time" "github.com/eko/gocache/lib/v4/cache" "github.com/eko/gocache/lib/v4/marshaler" "github.com/eko/gocache/lib/v4/store" "github.com/eko/gocache/store/redis/v4" "github.com/vmihailenco/msgpack/v5" "github.com/netbirdio/netbird/management/server/idp" ) const ( DefaultIDPCacheExpirationMax = 7 * 24 * time.Hour // 7 days DefaultIDPCacheExpirationMin = 3 * 24 * time.Hour // 3 days DefaultIDPCacheCleanupInterval = 30 * time.Minute ) // UserDataCache is an interface that wraps the basic Get, Set and Delete methods for idp.UserData objects. type UserDataCache interface { Get(ctx context.Context, key string) (*idp.UserData, error) Set(ctx context.Context, key string, value *idp.UserData, expiration time.Duration) error Delete(ctx context.Context, key string) error } // UserDataCacheImpl is a struct that implements the UserDataCache interface. type UserDataCacheImpl struct { cache Marshaler } func (u *UserDataCacheImpl) Get(ctx context.Context, key string) (*idp.UserData, error) { v, err := u.cache.Get(ctx, key, new(idp.UserData)) if err != nil { return nil, err } data := v.(*idp.UserData) return data, nil } func (u *UserDataCacheImpl) Set(ctx context.Context, key string, value *idp.UserData, expiration time.Duration) error { return u.cache.Set(ctx, key, value, store.WithExpiration(expiration)) } func (u *UserDataCacheImpl) Delete(ctx context.Context, key string) error { return u.cache.Delete(ctx, key) } // NewUserDataCache creates a new UserDataCacheImpl object. func NewUserDataCache(store store.StoreInterface) *UserDataCacheImpl { simpleCache := cache.New[any](store) if store.GetType() == redis.RedisType { m := marshaler.New(simpleCache) return &UserDataCacheImpl{cache: m} } return &UserDataCacheImpl{cache: &marshalerWraper{simpleCache}} } // AccountUserDataCache wraps the basic Get, Set and Delete methods for []*idp.UserData objects. type AccountUserDataCache struct { cache Marshaler } func (a *AccountUserDataCache) Get(ctx context.Context, key string) ([]*idp.UserData, error) { var m []*idp.UserData v, err := a.cache.Get(ctx, key, &m) if err != nil { return nil, err } switch v := v.(type) { case []*idp.UserData: return v, nil case *[]*idp.UserData: return *v, nil case []byte: return unmarshalUserData(v) } return nil, fmt.Errorf("unexpected type: %T", v) } func unmarshalUserData(data []byte) ([]*idp.UserData, error) { returnObj := &[]*idp.UserData{} err := msgpack.Unmarshal(data, returnObj) if err != nil { return nil, err } return *returnObj, nil } func (a *AccountUserDataCache) Set(ctx context.Context, key string, value []*idp.UserData, expiration time.Duration) error { return a.cache.Set(ctx, key, value, store.WithExpiration(expiration)) } func (a *AccountUserDataCache) Delete(ctx context.Context, key string) error { return a.cache.Delete(ctx, key) } // NewAccountUserDataCache creates a new AccountUserDataCache object. func NewAccountUserDataCache(loadableFunc cache.LoadFunction[any], store store.StoreInterface) *AccountUserDataCache { simpleCache := cache.New[any](store) loadable := cache.NewLoadable[any](loadableFunc, simpleCache) if store.GetType() == redis.RedisType { m := marshaler.New(loadable) return &AccountUserDataCache{cache: m} } return &AccountUserDataCache{cache: &marshalerWraper{loadable}} }