- Implemented initialization for the Setting Registry during the Load process. - Added reload functionality to refresh the Setting Registry as needed. - Enhanced error handling to capture and report issues during initialization and reloading.
178 lines
3.7 KiB
Go
178 lines
3.7 KiB
Go
package setting
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/yaoapp/gou/store"
|
|
)
|
|
|
|
const keyPrefix = "setting:"
|
|
|
|
func scopePrefix(scope ScopeID) string {
|
|
switch scope.Scope {
|
|
case ScopeTeam:
|
|
return "t" + scope.TeamID + ":"
|
|
case ScopeUser:
|
|
return "u" + scope.UserID + ":"
|
|
default:
|
|
return "s:"
|
|
}
|
|
}
|
|
|
|
func storeKey(scope ScopeID, ns string) string {
|
|
return keyPrefix + scopePrefix(scope) + ns
|
|
}
|
|
|
|
func indexKey(scope ScopeID) string {
|
|
return keyPrefix + "idx:" + scopePrefix(scope)
|
|
}
|
|
|
|
// storeGet reads a namespace entry from cache first, then persistent store.
|
|
func storeGet(s, c store.Store, scope ScopeID, ns string) (map[string]interface{}, error) {
|
|
sk := storeKey(scope, ns)
|
|
|
|
if c != nil {
|
|
if val, ok := c.Get(sk); ok {
|
|
if m, ok := val.(map[string]interface{}); ok {
|
|
return m, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
val, ok := s.Get(sk)
|
|
if !ok {
|
|
return nil, fmt.Errorf("setting %s/%s not found", scopePrefix(scope), ns)
|
|
}
|
|
|
|
m, err := toMap(val)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setting %s/%s: %w", scopePrefix(scope), ns, err)
|
|
}
|
|
|
|
if c != nil {
|
|
c.Set(sk, m, 0)
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// storeSet writes a namespace entry to both persistent store and cache.
|
|
func storeSet(s, c store.Store, scope ScopeID, ns string, data map[string]interface{}) error {
|
|
sk := storeKey(scope, ns)
|
|
if err := s.Set(sk, data, 0); err != nil {
|
|
return err
|
|
}
|
|
if c != nil {
|
|
c.Set(sk, data, 0)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// storeDel removes a namespace entry from both persistent store and cache.
|
|
func storeDel(s, c store.Store, scope ScopeID, ns string) error {
|
|
sk := storeKey(scope, ns)
|
|
if err := s.Del(sk); err != nil {
|
|
return err
|
|
}
|
|
if c != nil {
|
|
c.Del(sk)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// indexGet returns all namespace names for a given scope.
|
|
func indexGet(s, c store.Store, scope ScopeID) ([]string, error) {
|
|
ik := indexKey(scope)
|
|
var raw interface{}
|
|
var ok bool
|
|
|
|
if c != nil {
|
|
raw, ok = c.Get(ik)
|
|
}
|
|
if !ok {
|
|
raw, ok = s.Get(ik)
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
if c != nil {
|
|
c.Set(ik, raw, 0)
|
|
}
|
|
}
|
|
|
|
switch v := raw.(type) {
|
|
case []interface{}:
|
|
keys := make([]string, 0, len(v))
|
|
for _, item := range v {
|
|
if str, ok := item.(string); ok {
|
|
keys = append(keys, str)
|
|
}
|
|
}
|
|
return keys, nil
|
|
case []string:
|
|
return v, nil
|
|
default:
|
|
return nil, fmt.Errorf("unexpected index type %T", raw)
|
|
}
|
|
}
|
|
|
|
// indexSet writes the full namespace index.
|
|
func indexSet(s, c store.Store, scope ScopeID, keys []string) error {
|
|
ik := indexKey(scope)
|
|
iface := make([]interface{}, len(keys))
|
|
for i, k := range keys {
|
|
iface[i] = k
|
|
}
|
|
if err := s.Set(ik, iface, 0); err != nil {
|
|
return err
|
|
}
|
|
if c != nil {
|
|
c.Set(ik, iface, 0)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// indexAdd appends a namespace to the index if not already present.
|
|
func indexAdd(s, c store.Store, scope ScopeID, ns string) error {
|
|
keys, err := indexGet(s, c, scope)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, k := range keys {
|
|
if k == ns {
|
|
return nil
|
|
}
|
|
}
|
|
return indexSet(s, c, scope, append(keys, ns))
|
|
}
|
|
|
|
// indexRemove removes a namespace from the index.
|
|
func indexRemove(s, c store.Store, scope ScopeID, ns string) error {
|
|
keys, err := indexGet(s, c, scope)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
filtered := make([]string, 0, len(keys))
|
|
for _, k := range keys {
|
|
if k != ns {
|
|
filtered = append(filtered, k)
|
|
}
|
|
}
|
|
return indexSet(s, c, scope, filtered)
|
|
}
|
|
|
|
// toMap normalizes a store value to map[string]interface{}.
|
|
// The xun store may return values that need re-serialization.
|
|
func toMap(val interface{}) (map[string]interface{}, error) {
|
|
if m, ok := val.(map[string]interface{}); ok {
|
|
return m, nil
|
|
}
|
|
raw, err := json.Marshal(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var m map[string]interface{}
|
|
if err := json.Unmarshal(raw, &m); err != nil {
|
|
return nil, err
|
|
}
|
|
return m, nil
|
|
}
|