<commit_before>package sessions

import (
	"sync"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/go-redis/redis"
)

type codeStorage interface {
	Add(*Code) error
	FindAndDelete(value string, app string) *Code
}

type memCodeStorage struct {
	codes []*Code
}

func (store *memCodeStorage) Add(code *Code) error {
	code.ExpiresAt = time.Now().UTC().Add(CodeTTL).Unix()
	store.codes = append(store.codes, code)
	return nil
}

func (store *memCodeStorage) FindAndDelete(value string, app string) *Code {
	var found *Code
	if len(store.codes) == 0 {
		return nil
	}

	// See https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
	validCodes := store.codes[:0]
	now := time.Now().UTC().Unix()
	for _, c := range store.codes {
		if now < c.ExpiresAt {
			if c.Value == value && c.AppHost == app {
				found = c
			} else {
				validCodes = append(validCodes, c)
			}
		}
	}
	store.codes = validCodes
	return found
}

type subRedisInterface interface {
	Eval(script string, keys []string, args ...interface{}) *redis.Cmd
	Set(key string, value interface{}, expiration time.Duration) *redis.StatusCmd
}

type redisCodeStorage struct {
	cl subRedisInterface
}

func (store *redisCodeStorage) Add(c *Code) error {
	return store.cl.Set(c.Value+":"+c.AppHost, c.SessionID, CodeTTL).Err()
}

const luaGetAndDelete = `local v = redis.call("GET", KEYS[1]); redis.call("DEL", KEYS[1]); return v`

func (store *redisCodeStorage) FindAndDelete(value, app string) *Code {
	sessionID, err := store.cl.Eval(luaGetAndDelete, []string{value + ":" + app}).Result()
	if err != nil || sessionID == redis.Nil {
		return nil
	}

	return &Code{
		SessionID: sessionID.(string),
		Value:     value,
		AppHost:   app,
	}
}

var globalStorage codeStorage
var globalStorageMutex sync.Mutex<commit_msg>Add logs for sessions code (redis storage)<commit_after>package sessions

import (
	"sync"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/go-redis/redis"
)

type codeStorage interface {
	Add(*Code) error
	FindAndDelete(value string, app string) *Code
}

type memCodeStorage struct {
	codes []*Code
}

func (store *memCodeStorage) Add(code *Code) error {
	code.ExpiresAt = time.Now().UTC().Add(CodeTTL).Unix()
	store.codes = append(store.codes, code)
	return nil
}

func (store *memCodeStorage) FindAndDelete(value string, app string) *Code {
	var found *Code
	if len(store.codes) == 0 {
		return nil
	}

	// See https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating
	validCodes := store.codes[:0]
	now := time.Now().UTC().Unix()
	for _, c := range store.codes {
		if now < c.ExpiresAt {
			if c.Value == value && c.AppHost == app {
				found = c
			} else {
				validCodes = append(validCodes, c)
			}
		}
	}
	store.codes = validCodes
	return found
}

var codelog = logger.WithNamespace("sessions-code")

type subRedisInterface interface {
	Eval(script string, keys []string, args ...interface{}) *redis.Cmd
	Set(key string, value interface{}, expiration time.Duration) *redis.StatusCmd
}

type redisCodeStorage struct {
	cl subRedisInterface
}

func (store *redisCodeStorage) Add(c *Code) error {
	err := store.cl.Set(c.Value+":"+c.AppHost, c.SessionID, CodeTTL).Err()
	if err != nil {
		codelog.Errorf("Cannot save a sessions code: %s", err)
	}
	return err
}

const luaGetAndDelete = `local v = redis.call("GET", KEYS[1]); redis.call("DEL", KEYS[1]); return v`

func (store *redisCodeStorage) FindAndDelete(value, app string) *Code {
	sessionID, err := store.cl.Eval(luaGetAndDelete, []string{value + ":" + app}).Result()
	if err != nil {
		codelog.Errorf("Error while fetching a sessions code: %s", err)
		return nil
	}
	if sessionID == redis.Nil {
		return nil
	}

	return &Code{
		SessionID: sessionID.(string),
		Value:     value,
		AppHost:   app,
	}
}

var globalStorage codeStorage
var globalStorageMutex sync.Mutex
{'fraction_non_alphanumeric': 0.1456360378807269, 'fraction_numerical': 0.002047606859482979, 'mean_word_length': 4.176158940397351, 'pattern_counts': {'":': 4, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3489230', 'n_tokens_mistral': 1442, 'n_tokens_neox': 1393, 'n_words': 340}