Create a cache package
All checks were successful
ci/woodpecker/push/build Pipeline was successful

Signed-off-by: Nikolai Rodionov <iam@allanger.xyz>
This commit is contained in:
2026-05-17 22:29:16 +02:00
parent 1ca66a885b
commit 732f70238a
3 changed files with 97 additions and 9 deletions

View File

@@ -1 +1,25 @@
package cache
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/v9"
)
const (
CacheFolderToken = "token"
)
func buildKey(folder, key string) string {
return fmt.Sprintf("%s:%s", folder, key)
}
func GetFromCache(ctx context.Context, redis *redis.Client, folder, key string) string {
return redis.Get(ctx, buildKey(folder, key)).Val()
}
func SaveToCache(ctx context.Context, redis *redis.Client, folder, key, value string, ttl time.Duration) error {
return redis.Set(ctx, buildKey(folder, key), value, ttl).Err()
}

View File

@@ -7,11 +7,11 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"regexp"
"testing"
"time"
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/cache"
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/logger"
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/helpers/token"
"gitea.badhouseplants.net/softplayer/softplayer-backend/internal/repository"
@@ -73,9 +73,7 @@ func (ctrl *TokenController) VerifyTokenOwner(ctx context.Context, userID, token
log := logger.FromContext(ctx).WithValues("uuid", tokenID, "user_id", userID)
log.V(2).Info("Verifying the token owner")
// First try to get from the redis
redisKey := fmt.Sprintf("token:%s", tokenID)
realUserID := ctrl.Redis.Get(ctx, redisKey).Val()
realUserID := cache.GetFromCache(ctx, ctrl.Redis, cache.CacheFolderToken, tokenID)
// If not found in cache, get from postgres
if realUserID == "" {
query := "SELECT user_id FROM tokens WHERE uuid = $1;"
@@ -90,8 +88,7 @@ func (ctrl *TokenController) VerifyTokenOwner(ctx context.Context, userID, token
if realUserID != userID {
return ErrUserTokenMismatch
}
err := ctrl.Redis.Set(ctx, redisKey, realUserID, time.Hour)
if err != nil {
if err := cache.SaveToCache(ctx, ctrl.Redis, cache.CacheFolderToken, realUserID, tokenID, time.Hour); err != nil {
log.Info("Couldn't write to cache", "error", err)
}
return nil

View File

@@ -17,7 +17,7 @@ func newTestTokensController(ctx context.Context) *controllers.TokenController {
}
}
func TestCreateToken_Success(t *testing.T) {
func TestIntegrationCreateToken_Success(t *testing.T) {
// Create a user for the token
ctrlAccount := newTestAccountController(t.Context())
accountData := &controllers.AccountData{
@@ -43,7 +43,7 @@ func TestCreateToken_Success(t *testing.T) {
assert.NotEmpty(t, tokenVal)
}
func TestCreateToken_UserNotExist(t *testing.T) {
func TestIntegrationCreateToken_UserNotExist(t *testing.T) {
tokenData := &controllers.TokenData{
Name: "Test Token",
UserID: uuid.NewString(),
@@ -62,7 +62,7 @@ func TestCreateToken_UserNotExist(t *testing.T) {
assert.Empty(t, tokenVal)
}
func TestGetToken_Success(t *testing.T) {
func TestIntegrationGetToken_Success(t *testing.T) {
// Create a user for the token
ctrlAccount := newTestAccountController(t.Context())
accountData := &controllers.AccountData{
@@ -95,3 +95,70 @@ func TestGetToken_Success(t *testing.T) {
assert.Equal(t, tokenData.Name, token.Name)
assert.Equal(t, tokenData.ExpiresAt.Truncate(time.Second), token.ExpiresAt.Truncate(time.Second))
}
func TestIntegrationGetToken_NotExists(t *testing.T) {
ctrl := newTestTokensController(t.Context())
token, err := ctrl.Get(t.Context(), uuid.NewString(), uuid.NewString())
assert.Error(t, err)
assert.ErrorIs(t, err, controllers.ErrTokenNotFound)
assert.Empty(t, token)
}
func TestIntegrationVerifyTokenOwner_Success(t *testing.T) {
// Create a user for the token
ctrlAccount := newTestAccountController(t.Context())
accountData := &controllers.AccountData{
Password: "qwertyu9",
Email: newTestUniqueEmail("accounts"),
}
userID, err := ctrlAccount.Create(t.Context(), accountData)
assert.NoError(t, err)
tokenData := &controllers.TokenData{
Name: "Test Token",
UserID: userID,
ExpiresAt: time.Now().Add(time.Second * 5),
Scopes: map[string][]string{
"Test": {"test", "test2"},
},
}
ctrl := newTestTokensController(t.Context())
_, tokenID, err := ctrl.Create(t.Context(), tokenData)
assert.NoError(t, err)
assert.NoError(t, ctrl.VerifyTokenOwner(t.Context(), userID, tokenID))
}
func TestIntegrationVerifyTokenOwner_WrongOwner(t *testing.T) {
// Create a user for the token
ctrlAccount := newTestAccountController(t.Context())
accountData := &controllers.AccountData{
Password: "qwertyu9",
Email: newTestUniqueEmail("accounts"),
}
secondAccountData := &controllers.AccountData{
Password: "qwertyu9",
Email: newTestUniqueEmail("accounts"),
}
userID, err := ctrlAccount.Create(t.Context(), accountData)
assert.NoError(t, err)
secondUserID, err := ctrlAccount.Create(t.Context(), secondAccountData)
assert.NoError(t, err)
tokenData := &controllers.TokenData{
Name: "Test Token",
UserID: userID,
ExpiresAt: time.Now().Add(time.Second * 5),
Scopes: map[string][]string{
"Test": {"test", "test2"},
},
}
ctrl := newTestTokensController(t.Context())
_, tokenID, err := ctrl.Create(t.Context(), tokenData)
assert.NoError(t, err)
assert.ErrorIs(t, ctrl.VerifyTokenOwner(t.Context(), secondUserID, tokenID), controllers.ErrUserTokenMismatch)
}