Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
489 lines
13 KiB
Go
489 lines
13 KiB
Go
package redis
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
// ===== Command output / return value types
|
|
|
|
// Nil represents the redis nil value
|
|
const Nil = redis.Nil
|
|
|
|
// Result generic interface
|
|
type Result interface {
|
|
Int() int64
|
|
String() string
|
|
Bool() bool
|
|
Duration() time.Duration
|
|
Result() (int64, error)
|
|
ResultString() (string, error)
|
|
Multi() ([]string, error)
|
|
MultiInterface() ([]interface{}, error)
|
|
Err() error
|
|
MapStringString() (map[string]string, error)
|
|
}
|
|
|
|
// ResultImpl generic interface
|
|
type ResultImpl struct {
|
|
value int64
|
|
valueString string
|
|
valueBool bool
|
|
valueDuration time.Duration
|
|
err error
|
|
multi []string
|
|
multiInterface []interface{}
|
|
mapStringString map[string]string
|
|
}
|
|
|
|
// Int implementation
|
|
func (r *ResultImpl) Int() int64 {
|
|
return r.value
|
|
}
|
|
|
|
// String implementation
|
|
func (r *ResultImpl) String() string {
|
|
return r.valueString
|
|
}
|
|
|
|
// Bool implementation
|
|
func (r *ResultImpl) Bool() bool {
|
|
return r.valueBool
|
|
}
|
|
|
|
// Duration implementation
|
|
func (r *ResultImpl) Duration() time.Duration {
|
|
return r.valueDuration
|
|
}
|
|
|
|
// Err implementation
|
|
func (r *ResultImpl) Err() error {
|
|
return r.err
|
|
}
|
|
|
|
// Result implementation
|
|
func (r *ResultImpl) Result() (int64, error) {
|
|
return r.value, r.err
|
|
}
|
|
|
|
// ResultString implementation
|
|
func (r *ResultImpl) ResultString() (string, error) {
|
|
return r.valueString, r.err
|
|
}
|
|
|
|
// Multi implementation
|
|
func (r *ResultImpl) Multi() ([]string, error) {
|
|
return r.multi, r.err
|
|
}
|
|
|
|
// MultiInterface implementation
|
|
func (r *ResultImpl) MultiInterface() ([]interface{}, error) {
|
|
return r.multiInterface, r.err
|
|
}
|
|
|
|
// MapStringString implementation
|
|
func (r *ResultImpl) MapStringString() (map[string]string, error) {
|
|
return r.mapStringString, r.err
|
|
}
|
|
|
|
// Pipeline defines the interface of a redis pipeline
|
|
type Pipeline interface {
|
|
LRange(key string, start, stop int64)
|
|
LTrim(key string, start, stop int64)
|
|
LLen(key string)
|
|
HIncrBy(key string, field string, value int64)
|
|
HLen(key string)
|
|
Set(key string, value interface{}, expiration time.Duration)
|
|
Incr(key string)
|
|
Decr(key string)
|
|
SAdd(key string, members ...interface{})
|
|
SRem(key string, members ...interface{})
|
|
SMembers(key string)
|
|
Del(keys ...string)
|
|
Exec() ([]Result, error)
|
|
}
|
|
|
|
// PipelineImpl Wrapper
|
|
type PipelineImpl struct {
|
|
wrapped redis.Pipeliner
|
|
}
|
|
|
|
// LRange schedules an lrange operation on this pipeline
|
|
func (p *PipelineImpl) LRange(key string, start, stop int64) {
|
|
p.wrapped.LRange(context.TODO(), key, start, stop)
|
|
}
|
|
|
|
// LTrim schedules an ltrim operation on this pipeline
|
|
func (p *PipelineImpl) LTrim(key string, start, stop int64) {
|
|
p.wrapped.LTrim(context.TODO(), key, start, stop)
|
|
}
|
|
|
|
// LLen schedules an llen operation on this pipeline
|
|
func (p *PipelineImpl) LLen(key string) {
|
|
p.wrapped.LLen(context.TODO(), key)
|
|
}
|
|
|
|
// HIncrBy schedules an hincrby operation on this pipeline
|
|
func (p *PipelineImpl) HIncrBy(key string, field string, value int64) {
|
|
p.wrapped.HIncrBy(context.TODO(), key, field, value)
|
|
}
|
|
|
|
// HLen schedules an HLen operation on this pipeline
|
|
func (p *PipelineImpl) HLen(key string) {
|
|
p.wrapped.HLen(context.TODO(), key)
|
|
}
|
|
|
|
// Set schedules a Set operation on this pipeline
|
|
func (p *PipelineImpl) Set(key string, value interface{}, expiration time.Duration) {
|
|
p.wrapped.Set(context.TODO(), key, value, expiration)
|
|
}
|
|
|
|
// Incr schedules an Incr operation on this pipeline
|
|
func (p *PipelineImpl) Incr(key string) {
|
|
p.wrapped.Incr(context.TODO(), key)
|
|
}
|
|
|
|
// Decr schedules a Decr operation on this pipeline
|
|
func (p *PipelineImpl) Decr(key string) {
|
|
p.wrapped.Decr(context.TODO(), key)
|
|
}
|
|
|
|
// SAdd schedules a SAdd operation on this pipeline
|
|
func (p *PipelineImpl) SAdd(key string, members ...interface{}) {
|
|
p.wrapped.SAdd(context.TODO(), key, members...)
|
|
}
|
|
|
|
// SRem schedules a SRem operation on this pipeline
|
|
func (p *PipelineImpl) SRem(key string, members ...interface{}) {
|
|
p.wrapped.SRem(context.TODO(), key, members...)
|
|
}
|
|
|
|
// SMembers schedules a SMembers operation on this pipeline
|
|
func (p *PipelineImpl) SMembers(key string) {
|
|
p.wrapped.SMembers(context.TODO(), key)
|
|
}
|
|
|
|
// Del schedules a Del operation on this pipeline
|
|
func (p *PipelineImpl) Del(keys ...string) {
|
|
p.wrapped.Del(context.TODO(), keys...)
|
|
}
|
|
|
|
// Exec executes the pipeline
|
|
func (p *PipelineImpl) Exec() ([]Result, error) {
|
|
res, err := p.wrapped.Exec(context.TODO())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
toRet := make([]Result, 0, len(res))
|
|
for idx := range res {
|
|
toRet = append(toRet, wrapResult(res[idx]))
|
|
}
|
|
|
|
return toRet, nil
|
|
}
|
|
|
|
// ====== Client
|
|
|
|
// Client interface which specifies the currently used subset of redis operations
|
|
type Client interface {
|
|
ClusterMode() bool
|
|
ClusterCountKeysInSlot(slot int) Result
|
|
ClusterSlotForKey(key string) Result
|
|
ClusterKeysInSlot(slot int, count int) Result
|
|
Del(keys ...string) Result
|
|
Exists(keys ...string) Result
|
|
Get(key string) Result
|
|
Set(key string, value interface{}, expiration time.Duration) Result
|
|
Ping() Result
|
|
Keys(pattern string) Result
|
|
SMembers(key string) Result
|
|
SIsMember(key string, member interface{}) Result
|
|
SAdd(key string, members ...interface{}) Result
|
|
SRem(key string, members ...interface{}) Result
|
|
Incr(key string) Result
|
|
Decr(key string) Result
|
|
RPush(key string, values ...interface{}) Result
|
|
LRange(key string, start, stop int64) Result
|
|
LTrim(key string, start, stop int64) Result
|
|
LLen(key string) Result
|
|
Expire(key string, value time.Duration) Result
|
|
TTL(key string) Result
|
|
MGet(keys []string) Result
|
|
SCard(key string) Result
|
|
Eval(script string, keys []string, args ...interface{}) Result
|
|
HIncrBy(key string, field string, value int64) Result
|
|
HSet(key string, hashKey string, value interface{}) Result
|
|
HGetAll(key string) Result
|
|
Type(key string) Result
|
|
Pipeline() Pipeline
|
|
Scan(cursor uint64, match string, count int64) Result
|
|
}
|
|
|
|
// ClientImpl wrapps redis client
|
|
type ClientImpl struct {
|
|
wrapped redis.UniversalClient
|
|
clusterMode bool
|
|
}
|
|
|
|
// ClusterMode returns true if the client is running in cluster mode
|
|
func (c *ClientImpl) ClusterMode() bool {
|
|
return c.clusterMode
|
|
}
|
|
|
|
// ClusterSlotForKey returns the slot for the supplied key
|
|
func (c *ClientImpl) ClusterSlotForKey(key string) Result {
|
|
res := c.wrapped.ClusterKeySlot(context.TODO(), key)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// ClusterCountKeysInSlot returns the number of keys in slot
|
|
func (c *ClientImpl) ClusterCountKeysInSlot(slot int) Result {
|
|
res := c.wrapped.ClusterCountKeysInSlot(context.TODO(), slot)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// ClusterKeysInSlot returns all the keys in the supplied slot
|
|
func (c *ClientImpl) ClusterKeysInSlot(slot int, count int) Result {
|
|
res := c.wrapped.ClusterGetKeysInSlot(context.TODO(), slot, count)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Del implements Del wrapper for redis
|
|
func (c *ClientImpl) Del(keys ...string) Result {
|
|
res := c.wrapped.Del(context.TODO(), keys...)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Exists implements Exists wrapper for redis
|
|
func (c *ClientImpl) Exists(keys ...string) Result {
|
|
res := c.wrapped.Exists(context.TODO(), keys...)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Get implements Get wrapper for redis
|
|
func (c *ClientImpl) Get(key string) Result {
|
|
res := c.wrapped.Get(context.TODO(), key)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Set implements Set wrapper for redis
|
|
func (c *ClientImpl) Set(key string, value interface{}, expiration time.Duration) Result {
|
|
res := c.wrapped.Set(context.TODO(), key, value, expiration)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Ping implements Ping wrapper for redis
|
|
func (c *ClientImpl) Ping() Result {
|
|
res := c.wrapped.Ping(context.TODO())
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Keys implements Keys wrapper for redis
|
|
func (c *ClientImpl) Keys(pattern string) Result {
|
|
res := c.wrapped.Keys(context.TODO(), pattern)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// SMembers implements SMembers wrapper for redis
|
|
func (c *ClientImpl) SMembers(key string) Result {
|
|
res := c.wrapped.SMembers(context.TODO(), key)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// SIsMember implements SIsMember wrapper for redis
|
|
func (c *ClientImpl) SIsMember(key string, member interface{}) Result {
|
|
res := c.wrapped.SIsMember(context.TODO(), key, member)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// SAdd implements SAdd wrapper for redis
|
|
func (c *ClientImpl) SAdd(key string, members ...interface{}) Result {
|
|
res := c.wrapped.SAdd(context.TODO(), key, members...)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// SRem implements SRem wrapper for redis
|
|
func (c *ClientImpl) SRem(key string, members ...interface{}) Result {
|
|
res := c.wrapped.SRem(context.TODO(), key, members...)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Incr implements Incr wrapper for redis
|
|
func (c *ClientImpl) Incr(key string) Result {
|
|
res := c.wrapped.Incr(context.TODO(), key)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Decr implements Decr wrapper for redis
|
|
func (c *ClientImpl) Decr(key string) Result {
|
|
res := c.wrapped.Decr(context.TODO(), key)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// RPush implements RPush wrapper for redis
|
|
func (c *ClientImpl) RPush(key string, values ...interface{}) Result {
|
|
res := c.wrapped.RPush(context.TODO(), key, values...)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// LRange implements LRange wrapper for redis
|
|
func (c *ClientImpl) LRange(key string, start, stop int64) Result {
|
|
res := c.wrapped.LRange(context.TODO(), key, start, stop)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// LTrim implements LTrim wrapper for redis
|
|
func (c *ClientImpl) LTrim(key string, start, stop int64) Result {
|
|
res := c.wrapped.LTrim(context.TODO(), key, start, stop)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// LLen implements LLen wrapper for redis
|
|
func (c *ClientImpl) LLen(key string) Result {
|
|
res := c.wrapped.LLen(context.TODO(), key)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Expire implements Expire wrapper for redis
|
|
func (c *ClientImpl) Expire(key string, value time.Duration) Result {
|
|
res := c.wrapped.Expire(context.TODO(), key, value)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// TTL implements TTL wrapper for redis
|
|
func (c *ClientImpl) TTL(key string) Result {
|
|
res := c.wrapped.TTL(context.TODO(), key)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// MGet implements MGet wrapper for redis
|
|
func (c *ClientImpl) MGet(keys []string) Result {
|
|
res := c.wrapped.MGet(context.TODO(), keys...)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// SCard implements SCard wrapper for redis
|
|
func (c *ClientImpl) SCard(key string) Result {
|
|
res := c.wrapped.SCard(context.TODO(), key)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Eval implements Eval wrapper for redis
|
|
func (c *ClientImpl) Eval(script string, keys []string, args ...interface{}) Result {
|
|
res := c.wrapped.Eval(context.TODO(), script, keys, args...)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// HIncrBy implements HIncrBy wrapper for redis
|
|
func (c *ClientImpl) HIncrBy(key string, field string, value int64) Result {
|
|
res := c.wrapped.HIncrBy(context.TODO(), key, field, value)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// HSet implements HSet wrapper for redis
|
|
func (c *ClientImpl) HSet(key string, hashKey string, value interface{}) Result {
|
|
res := c.wrapped.HSet(context.TODO(), key, hashKey, value)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// HGetAll implements HGetAll wrapper for redis
|
|
func (c *ClientImpl) HGetAll(key string) Result {
|
|
res := c.wrapped.HGetAll(context.TODO(), key)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Type implements Type wrapper for redis
|
|
func (c *ClientImpl) Type(key string) Result {
|
|
res := c.wrapped.Type(context.TODO(), key)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// Pipeline implements Pipeline wrapper for redis
|
|
func (c *ClientImpl) Pipeline() Pipeline {
|
|
res := c.wrapped.Pipeline()
|
|
return &PipelineImpl{wrapped: res}
|
|
}
|
|
|
|
// Scan implements Scan wrapper for redis
|
|
func (c *ClientImpl) Scan(cursor uint64, match string, count int64) Result {
|
|
res := c.wrapped.Scan(context.TODO(), cursor, match, count)
|
|
return wrapResult(res)
|
|
}
|
|
|
|
// NewClient returns new client implementation
|
|
func NewClient(options *UniversalOptions) (Client, error) {
|
|
if options.ForceClusterMode {
|
|
return &ClientImpl{wrapped: redis.NewClusterClient(options.toRedisClusterOpts()),
|
|
clusterMode: true,
|
|
}, nil
|
|
}
|
|
|
|
return &ClientImpl{wrapped: redis.NewUniversalClient(options.toRedisUniversalOpts()),
|
|
clusterMode: len(options.Addrs) > 1 && options.MasterName == "",
|
|
}, nil
|
|
}
|
|
|
|
func wrapResult(result interface{}) Result {
|
|
if result == nil {
|
|
return nil
|
|
}
|
|
switch v := result.(type) {
|
|
case *redis.StatusCmd:
|
|
return &ResultImpl{
|
|
valueString: v.Val(),
|
|
err: v.Err(),
|
|
}
|
|
case *redis.IntCmd:
|
|
return &ResultImpl{
|
|
value: v.Val(),
|
|
err: v.Err(),
|
|
}
|
|
case *redis.StringCmd:
|
|
return &ResultImpl{
|
|
valueString: v.Val(),
|
|
err: v.Err(),
|
|
}
|
|
case *redis.StringSliceCmd:
|
|
return &ResultImpl{
|
|
err: v.Err(),
|
|
multi: v.Val(),
|
|
}
|
|
case *redis.BoolCmd:
|
|
return &ResultImpl{
|
|
valueBool: v.Val(),
|
|
err: v.Err(),
|
|
}
|
|
case *redis.DurationCmd:
|
|
return &ResultImpl{
|
|
valueDuration: v.Val(),
|
|
err: v.Err(),
|
|
}
|
|
case *redis.SliceCmd:
|
|
return &ResultImpl{
|
|
err: v.Err(),
|
|
multiInterface: v.Val(),
|
|
}
|
|
case *redis.Cmd:
|
|
return &ResultImpl{
|
|
err: v.Err(),
|
|
}
|
|
case *redis.MapStringStringCmd:
|
|
return &ResultImpl{
|
|
err: v.Err(),
|
|
mapStringString: v.Val(),
|
|
}
|
|
case *redis.ScanCmd:
|
|
keys, cursor := v.Val()
|
|
return &ResultImpl{
|
|
err: v.Err(),
|
|
multi: keys,
|
|
value: int64(cursor),
|
|
}
|
|
default:
|
|
return nil
|
|
}
|
|
}
|