diff --git a/pkg/cache/api.go b/pkg/cache/api.go index c40df64..6dbac77 100644 --- a/pkg/cache/api.go +++ b/pkg/cache/api.go @@ -6,8 +6,23 @@ import ( ) type Cache interface { + // string + Get(ctx context.Context, key string) ([]byte, error) Set(ctx context.Context, key string, value []byte, expire time.Duration) error Delete(ctx context.Context, key string) error + + // set + + IsInSet(ctx context.Context, key string, member string) (bool, error) + AddToSet(ctx context.Context, key string, member string) error + DeleteFromSet(ctx context.Context, key string, member string) error + + // hashmap + + GetFromHash(ctx context.Context, key string, field string) ([]byte, error) + SetToHash(ctx context.Context, key string, field string, value []byte) error + DeleteFromHash(ctx context.Context, key string, field string) error + Close(ctx context.Context) error } diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 5791866..d4b80bb 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -13,6 +13,7 @@ var ( var ( ErrCacheMiss = errors.New("cache miss") ErrCacheFull = errors.New("cache full") + ErrKeyType = errors.New("invalid key type") ) // SetGlobalCache sets the global cache. @@ -25,6 +26,10 @@ func GetGlobalCache() Cache { return globalCache } +/* + * string + */ + // Get is wrapper for global cache.Get. func Get(ctx context.Context, key string) ([]byte, error) { return globalCache.Get(ctx, key) @@ -40,6 +45,44 @@ func Delete(ctx context.Context, key string) error { return globalCache.Delete(ctx, key) } +/* + * set + */ + +// IsInSet is wrapper for global cache.IsInSet. +func IsInSet(ctx context.Context, key string, member string) (bool, error) { + return globalCache.IsInSet(ctx, key, member) +} + +// AddToSet is wrapper for global cache.AddToSet. +func AddToSet(ctx context.Context, key string, member string) error { + return globalCache.AddToSet(ctx, key, member) +} + +// DeleteFromSet is wrapper for global cache.DeleteFromSet. +func DeleteFromSet(ctx context.Context, key string, member string) error { + return globalCache.DeleteFromSet(ctx, key, member) +} + +/* + * hashmap + */ + +// GetFromHash is wrapper for global cache.GetFromHash. +func GetFromHash(ctx context.Context, key string, field string) ([]byte, error) { + return globalCache.GetFromHash(ctx, key, field) +} + +// SetToHash is wrapper for global cache.SetToHash. +func SetToHash(ctx context.Context, key string, field string, value []byte) error { + return globalCache.SetToHash(ctx, key, field, value) +} + +// DeleteFromHash is wrapper for global cache.DeleteFromHash. +func DeleteFromHash(ctx context.Context, key string, field string) error { + return globalCache.DeleteFromHash(ctx, key, field) +} + // Close is wrapper for global cache.Close. func Close(ctx context.Context) error { return globalCache.Close(ctx) diff --git a/pkg/cache/memory.go b/pkg/cache/memory.go index 7c0432a..09a6450 100644 --- a/pkg/cache/memory.go +++ b/pkg/cache/memory.go @@ -80,6 +80,164 @@ func (m *memoryCache) Delete(_ context.Context, key string) error { return nil } +/* + * set + */ + +type set struct { + items map[string]bool +} + +func (s *set) has(key string) bool { + _, ok := s.items[key] + return ok +} + +func (s *set) add(key string) { + s.items[key] = true +} + +func (m *memoryCache) IsInSet(_ context.Context, key string, member string) (bool, error) { + m.mu.RLock() + defer m.mu.RUnlock() + + item, ok := m.items[key] + if !ok { + return false, ErrCacheMiss + } + + s := item.value.(*set) + return s.has(member), nil +} + +func (m *memoryCache) AddToSet(_ context.Context, key string, member string) error { + m.mu.Lock() + defer m.mu.Unlock() + + item, ok := m.items[key] + if !ok { + return ErrCacheMiss + } + + s := item.value.(*set) + if s.has(member) { + return nil + } + + s.add(member) + return nil +} + +func (m *memoryCache) DeleteFromSet(_ context.Context, key string, member string) error { + m.mu.Lock() + defer m.mu.Unlock() + + item, ok := m.items[key] + if !ok { + return ErrCacheMiss + } + + s := item.value.(*set) + if !s.has(member) { + return nil + } + + delete(s.items, member) + return nil +} + +/* + * hashmap + */ + +type hashmap struct { + items map[string]map[string][]byte +} + +func (h *hashmap) set(key string, field string, value []byte) { + if _, ok := h.items[key]; !ok { + h.items[key] = make(map[string][]byte) + } + h.items[key][field] = value +} + +func (h *hashmap) get(key string, field string) ([]byte, bool) { + if _, ok := h.items[key]; !ok { + return nil, false + } + + value, ok := h.items[key][field] + return value, ok +} + +func (m *memoryCache) GetFromHash(_ context.Context, key string, field string) ([]byte, error) { + m.mu.RLock() + defer m.mu.RUnlock() + + item, ok := m.items[key] + if !ok { + return nil, ErrCacheMiss + } + + h, ok := item.value.(*hashmap) + if !ok { + return nil, ErrKeyType + } + + value, ok := h.get(key, field) + if !ok { + return nil, ErrCacheMiss + } + + return value, nil +} + +func (m *memoryCache) SetToHash(_ context.Context, key string, field string, value []byte) error { + m.mu.Lock() + defer m.mu.Unlock() + + h := &hashmap{ + items: make(map[string]map[string][]byte), + } + item, ok := m.items[key] + if ok { + h, ok = item.value.(*hashmap) + if !ok { + return ErrKeyType + } + } else { + m.items[key] = &memoryCacheItem{ + value: h, + } + } + + h.set(key, field, value) + return nil +} + +func (m *memoryCache) DeleteFromHash(_ context.Context, key string, field string) error { + m.mu.Lock() + defer m.mu.Unlock() + + item, ok := m.items[key] + if !ok { + return ErrCacheMiss + } + + h, ok := item.value.(*hashmap) + if !ok { + return ErrKeyType + } + + _, ok = h.items[key] + if !ok { + return ErrCacheMiss + } + + delete(h.items[key], field) + return nil +} + func (m *memoryCache) Close(_ context.Context) error { m.mu.Lock() defer m.mu.Unlock() diff --git a/pkg/cache/redis.go b/pkg/cache/redis.go index 3d6c6f1..d266042 100644 --- a/pkg/cache/redis.go +++ b/pkg/cache/redis.go @@ -27,7 +27,10 @@ func (r *redisCache) Get(ctx context.Context, key string) ([]byte, error) { ctx = context.Background() } - b, err := r.client.Get(ctx, key).Bytes() + ctx2, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + b, err := r.client.Get(ctx2, key).Bytes() if err != nil { if err == redisv8.Nil { return nil, ErrCacheMiss @@ -43,8 +46,10 @@ func (r *redisCache) Set(ctx context.Context, key string, value []byte, expire t if ctx == nil { ctx = context.Background() } + ctx2, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() - return r.client.Set(ctx, key, value, expire).Err() + return r.client.Set(ctx2, key, value, expire).Err() } func (r *redisCache) Delete(ctx context.Context, key string) error { @@ -52,7 +57,10 @@ func (r *redisCache) Delete(ctx context.Context, key string) error { ctx = context.Background() } - err := r.client.Del(ctx, key).Err() + ctx2, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + err := r.client.Del(ctx2, key).Err() if err != nil { if err == redisv8.Nil { return nil @@ -63,6 +71,80 @@ func (r *redisCache) Delete(ctx context.Context, key string) error { return nil } +func (r *redisCache) IsInSet(ctx context.Context, key string, member string) (bool, error) { + if ctx == nil { + ctx = context.Background() + } + + ctx2, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + return r.client.SIsMember(ctx2, key, member).Val(), nil +} + +func (r *redisCache) AddToSet(ctx context.Context, key string, member string) error { + if ctx == nil { + ctx = context.Background() + } + + ctx2, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + return r.client.SAdd(ctx2, key, member).Err() +} + +func (r *redisCache) DeleteFromSet(ctx context.Context, key string, member string) error { + if ctx == nil { + ctx = context.Background() + } + + ctx2, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + return r.client.SRem(ctx2, key, member).Err() +} + +func (r *redisCache) GetFromHash(ctx context.Context, key string, field string) ([]byte, error) { + if ctx == nil { + ctx = context.Background() + } + + ctx2, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + b, err := r.client.HGet(ctx2, key, field).Bytes() + if err != nil { + if err == redisv8.Nil { + return nil, ErrCacheMiss + } + + return nil, err + } + + return b, nil +} + +func (r *redisCache) SetToHash(ctx context.Context, key string, field string, value []byte) error { + if ctx == nil { + ctx = context.Background() + } + ctx2, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + return r.client.HSet(ctx2, key, field, value).Err() +} + +func (r *redisCache) DeleteFromHash(ctx context.Context, key string, field string) error { + if ctx == nil { + ctx = context.Background() + } + + ctx2, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + return r.client.HDel(ctx2, key, field).Err() +} + func (r *redisCache) Close(_ context.Context) error { return r.client.Close() } diff --git a/pkg/mid/auth.go b/pkg/mid/auth.go index 99ea0f6..b5eee8a 100644 --- a/pkg/mid/auth.go +++ b/pkg/mid/auth.go @@ -77,7 +77,7 @@ func SetJwtToHeader(c *gin.Context, userID string) error { return nil } -func AuthJwtCookie(c *gin.Context) { +func AuthJwt(c *gin.Context) { token := c.Request.Header.Get("Authorization") if token == "" { c.AbortWithStatus(401) diff --git a/pkg/util/group.go b/pkg/util/group.go new file mode 100644 index 0000000..3f6a8fa --- /dev/null +++ b/pkg/util/group.go @@ -0,0 +1,5 @@ +package util + +func IsGroupUID(uid string) bool { + return len(uid) > 1 && uid[:2] == "g_" +}