vibe-proxy/backend/internal/home/kv_helpers.go
2026-08-24 00:10:41 +02:00

189 lines
4.9 KiB
Go

package home
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
"time"
log "github.com/sirupsen/logrus"
)
func HashKeyPart(value string) string {
sum := sha256.Sum256([]byte(value))
return hex.EncodeToString(sum[:])
}
func CurrentKVClient() (*Client, bool, error) {
client := Current()
if client == nil {
return nil, false, nil
}
if !client.Enabled() {
return nil, true, fmt.Errorf("home kv store unavailable: %w", ErrDisabled)
}
if !client.HeartbeatOK() {
return nil, true, fmt.Errorf("home kv store unavailable: %w", ErrNotConnected)
}
return client, true, nil
}
func KVGetJSONRequired(ctx context.Context, key string, out any) (bool, bool, error) {
client, homeMode, errClient := CurrentKVClient()
if !homeMode || errClient != nil {
return homeMode, false, errClient
}
raw, found, errGet := client.KVGet(ctx, key)
if errGet != nil || !found {
return true, false, errGet
}
if errUnmarshal := json.Unmarshal(raw, out); errUnmarshal != nil {
return true, false, errUnmarshal
}
return true, true, nil
}
func KVSetJSONRequired(ctx context.Context, key string, value any, ttl time.Duration) (bool, error) {
raw, errMarshal := json.Marshal(value)
if errMarshal != nil {
return false, errMarshal
}
return KVSetBytesRequired(ctx, key, raw, ttl)
}
func KVSetBytesRequired(ctx context.Context, key string, value []byte, ttl time.Duration) (bool, error) {
client, homeMode, errClient := CurrentKVClient()
if !homeMode || errClient != nil {
return homeMode, errClient
}
written, errSet := client.KVSet(ctx, key, value, kvSetOptionsForTTL(ttl))
if errSet != nil {
return true, errSet
}
if !written {
return true, fmt.Errorf("home kv store unavailable")
}
return true, nil
}
func KVSetNXRequired(ctx context.Context, key string, value []byte, ttl time.Duration) (bool, bool, error) {
client, homeMode, errClient := CurrentKVClient()
if !homeMode || errClient != nil {
return homeMode, false, errClient
}
written, errSet := client.KVSetNX(ctx, key, value, ttl)
return true, written, errSet
}
func KVDelRequired(ctx context.Context, keys ...string) (bool, int64, error) {
client, homeMode, errClient := CurrentKVClient()
if !homeMode || errClient != nil {
return homeMode, 0, errClient
}
deleted, errDel := client.KVDel(ctx, keys...)
return true, deleted, errDel
}
func KVExpireRequired(ctx context.Context, key string, ttl time.Duration) (bool, error) {
client, homeMode, errClient := CurrentKVClient()
if !homeMode || errClient != nil {
return homeMode, errClient
}
_, errExpire := client.KVExpire(ctx, key, ttl)
return true, errExpire
}
func KVGetJSONBestEffort(ctx context.Context, key string, out any) (bool, bool) {
homeMode, found, errGet := KVGetJSONRequired(ctx, key, out)
if errGet != nil {
log.Errorf("home kv best-effort get failed prefix=%s: %v", kvLogPrefix(key), errGet)
return homeMode, false
}
return homeMode, found
}
func KVSetJSONBestEffort(ctx context.Context, key string, value any, ttl time.Duration) bool {
raw, errMarshal := json.Marshal(value)
if errMarshal != nil {
log.Errorf("home kv best-effort set failed prefix=%s: %v", kvLogPrefix(key), errMarshal)
return false
}
return KVSetBytesBestEffort(ctx, key, raw, ttl)
}
func KVSetBytesBestEffort(ctx context.Context, key string, value []byte, ttl time.Duration) bool {
homeMode, errSet := KVSetBytesRequired(ctx, key, value, ttl)
if !homeMode {
return false
}
if errSet != nil {
log.Errorf("home kv best-effort set failed prefix=%s: %v", kvLogPrefix(key), errSet)
return false
}
return true
}
func KVSetNXBestEffort(ctx context.Context, key string, value []byte, ttl time.Duration) bool {
homeMode, written, errSet := KVSetNXRequired(ctx, key, value, ttl)
if !homeMode {
return false
}
if errSet != nil {
log.Errorf("home kv best-effort setnx failed prefix=%s: %v", kvLogPrefix(key), errSet)
return false
}
return written
}
func KVDelBestEffort(ctx context.Context, keys ...string) bool {
homeMode, _, errDel := KVDelRequired(ctx, keys...)
if !homeMode {
return false
}
if errDel != nil {
log.Errorf("home kv best-effort del failed prefix=%s: %v", kvLogPrefix(firstKVKey(keys)), errDel)
return false
}
return true
}
func KVExpireBestEffort(ctx context.Context, key string, ttl time.Duration) bool {
homeMode, errExpire := KVExpireRequired(ctx, key, ttl)
if !homeMode {
return false
}
if errExpire != nil {
log.Errorf("home kv best-effort expire failed prefix=%s: %v", kvLogPrefix(key), errExpire)
return false
}
return true
}
func kvSetOptionsForTTL(ttl time.Duration) KVSetOptions {
if ttl <= 0 {
return KVSetOptions{}
}
return KVSetOptions{EX: ttl}
}
func kvLogPrefix(key string) string {
key = strings.TrimSpace(key)
if key == "" {
return "unknown"
}
parts := strings.Split(key, ":")
if len(parts) >= 2 {
return parts[0] + ":" + parts[1] + ":*"
}
return parts[0] + ":*"
}
func firstKVKey(keys []string) string {
if len(keys) == 0 {
return ""
}
return keys[0]
}