|
@@ -3,6 +3,7 @@ package kvdb
|
|
import (
|
|
import (
|
|
"encoding/json"
|
|
"encoding/json"
|
|
"errors"
|
|
"errors"
|
|
|
|
+ "fmt"
|
|
"os"
|
|
"os"
|
|
"path/filepath"
|
|
"path/filepath"
|
|
"sync"
|
|
"sync"
|
|
@@ -10,14 +11,16 @@ import (
|
|
|
|
|
|
// InMemoryKVDB is a simple in-memory key-value database
|
|
// InMemoryKVDB is a simple in-memory key-value database
|
|
type InMemoryKVDB struct {
|
|
type InMemoryKVDB struct {
|
|
- users map[string]*User // key: AccessKeyID
|
|
|
|
- mu sync.RWMutex
|
|
|
|
|
|
+ users map[string]*User // key: AccessKeyID
|
|
|
|
+ bucketConfigs map[string]*BucketConfig // key: accountID:bucketID
|
|
|
|
+ mu sync.RWMutex
|
|
}
|
|
}
|
|
|
|
|
|
// NewInMemoryKVDB creates a new in-memory key-value database
|
|
// NewInMemoryKVDB creates a new in-memory key-value database
|
|
func NewInMemoryKVDB() *InMemoryKVDB {
|
|
func NewInMemoryKVDB() *InMemoryKVDB {
|
|
return &InMemoryKVDB{
|
|
return &InMemoryKVDB{
|
|
- users: make(map[string]*User),
|
|
|
|
|
|
+ users: make(map[string]*User),
|
|
|
|
+ bucketConfigs: make(map[string]*BucketConfig),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -106,6 +109,78 @@ func (db *InMemoryKVDB) ValidateCredentials(accessKeyID, secretAccessKey string)
|
|
return user, nil
|
|
return user, nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// SetBucketConfig sets the configuration for a bucket
|
|
|
|
+func (db *InMemoryKVDB) SetBucketConfig(config *BucketConfig) error {
|
|
|
|
+ db.mu.Lock()
|
|
|
|
+ defer db.mu.Unlock()
|
|
|
|
+
|
|
|
|
+ key := config.AccountID + ":" + config.BucketID
|
|
|
|
+ configCopy := *config
|
|
|
|
+ db.bucketConfigs[key] = &configCopy
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// GetBucketConfig gets the configuration for a bucket
|
|
|
|
+func (db *InMemoryKVDB) GetBucketConfig(accountID, bucketID string) (*BucketConfig, error) {
|
|
|
|
+ db.mu.RLock()
|
|
|
|
+ defer db.mu.RUnlock()
|
|
|
|
+
|
|
|
|
+ key := accountID + ":" + bucketID
|
|
|
|
+ config, exists := db.bucketConfigs[key]
|
|
|
|
+ if !exists {
|
|
|
|
+ return nil, fmt.Errorf("bucket config not found")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ configCopy := *config
|
|
|
|
+ return &configCopy, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// DeleteBucketConfig deletes the configuration for a bucket
|
|
|
|
+func (db *InMemoryKVDB) DeleteBucketConfig(accountID, bucketID string) error {
|
|
|
|
+ db.mu.Lock()
|
|
|
|
+ defer db.mu.Unlock()
|
|
|
|
+
|
|
|
|
+ key := accountID + ":" + bucketID
|
|
|
|
+ if _, exists := db.bucketConfigs[key]; !exists {
|
|
|
|
+ return fmt.Errorf("bucket config not found")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ delete(db.bucketConfigs, key)
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// ListBucketConfigs lists all bucket configurations for an account
|
|
|
|
+func (db *InMemoryKVDB) ListBucketConfigs(accountID string) ([]*BucketConfig, error) {
|
|
|
|
+ db.mu.RLock()
|
|
|
|
+ defer db.mu.RUnlock()
|
|
|
|
+
|
|
|
|
+ var configs []*BucketConfig
|
|
|
|
+ prefix := accountID + ":"
|
|
|
|
+
|
|
|
|
+ for key, config := range db.bucketConfigs {
|
|
|
|
+ if len(key) >= len(prefix) && key[:len(prefix)] == prefix {
|
|
|
|
+ configCopy := *config
|
|
|
|
+ configs = append(configs, &configCopy)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return configs, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// ResolveBucketName resolves a bucket name to accountID and bucketID
|
|
|
|
+func (db *InMemoryKVDB) ResolveBucketName(bucketName string) (string, string, error) {
|
|
|
|
+ db.mu.RLock()
|
|
|
|
+ defer db.mu.RUnlock()
|
|
|
|
+
|
|
|
|
+ for _, config := range db.bucketConfigs {
|
|
|
|
+ if config.BucketName == bucketName {
|
|
|
|
+ return config.AccountID, config.BucketID, nil
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return "", "", fmt.Errorf("bucket not found: %s", bucketName)
|
|
|
|
+}
|
|
|
|
+
|
|
// Close closes the database (no-op for in-memory)
|
|
// Close closes the database (no-op for in-memory)
|
|
func (db *InMemoryKVDB) Close() error {
|
|
func (db *InMemoryKVDB) Close() error {
|
|
return nil
|
|
return nil
|
|
@@ -113,16 +188,18 @@ func (db *InMemoryKVDB) Close() error {
|
|
|
|
|
|
// FileBasedKVDB is a file-based key-value database with persistence
|
|
// FileBasedKVDB is a file-based key-value database with persistence
|
|
type FileBasedKVDB struct {
|
|
type FileBasedKVDB struct {
|
|
- filePath string
|
|
|
|
- users map[string]*User
|
|
|
|
- mu sync.RWMutex
|
|
|
|
|
|
+ filePath string
|
|
|
|
+ users map[string]*User
|
|
|
|
+ bucketConfigs map[string]*BucketConfig
|
|
|
|
+ mu sync.RWMutex
|
|
}
|
|
}
|
|
|
|
|
|
// NewFileBasedKVDB creates a new file-based key-value database
|
|
// NewFileBasedKVDB creates a new file-based key-value database
|
|
func NewFileBasedKVDB(filePath string) (*FileBasedKVDB, error) {
|
|
func NewFileBasedKVDB(filePath string) (*FileBasedKVDB, error) {
|
|
db := &FileBasedKVDB{
|
|
db := &FileBasedKVDB{
|
|
- filePath: filePath,
|
|
|
|
- users: make(map[string]*User),
|
|
|
|
|
|
+ filePath: filePath,
|
|
|
|
+ users: make(map[string]*User),
|
|
|
|
+ bucketConfigs: make(map[string]*BucketConfig),
|
|
}
|
|
}
|
|
|
|
|
|
// Create directory if it doesn't exist
|
|
// Create directory if it doesn't exist
|
|
@@ -139,6 +216,11 @@ func NewFileBasedKVDB(filePath string) (*FileBasedKVDB, error) {
|
|
return db, nil
|
|
return db, nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+type fileBasedData struct {
|
|
|
|
+ Users []*User `json:"users"`
|
|
|
|
+ BucketConfigs []*BucketConfig `json:"bucket_configs"`
|
|
|
|
+}
|
|
|
|
+
|
|
// load reads the database from disk
|
|
// load reads the database from disk
|
|
func (db *FileBasedKVDB) load() error {
|
|
func (db *FileBasedKVDB) load() error {
|
|
data, err := os.ReadFile(db.filePath)
|
|
data, err := os.ReadFile(db.filePath)
|
|
@@ -146,18 +228,23 @@ func (db *FileBasedKVDB) load() error {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
- var users []*User
|
|
|
|
- if err := json.Unmarshal(data, &users); err != nil {
|
|
|
|
|
|
+ var fileData fileBasedData
|
|
|
|
+ if err := json.Unmarshal(data, &fileData); err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
db.mu.Lock()
|
|
db.mu.Lock()
|
|
defer db.mu.Unlock()
|
|
defer db.mu.Unlock()
|
|
|
|
|
|
- for _, user := range users {
|
|
|
|
|
|
+ for _, user := range fileData.Users {
|
|
db.users[user.AccessKeyID] = user
|
|
db.users[user.AccessKeyID] = user
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ for _, config := range fileData.BucketConfigs {
|
|
|
|
+ key := config.AccountID + ":" + config.BucketID
|
|
|
|
+ db.bucketConfigs[key] = config
|
|
|
|
+ }
|
|
|
|
+
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
@@ -168,7 +255,17 @@ func (db *FileBasedKVDB) save() error {
|
|
users = append(users, user)
|
|
users = append(users, user)
|
|
}
|
|
}
|
|
|
|
|
|
- data, err := json.MarshalIndent(users, "", " ")
|
|
|
|
|
|
+ configs := make([]*BucketConfig, 0, len(db.bucketConfigs))
|
|
|
|
+ for _, config := range db.bucketConfigs {
|
|
|
|
+ configs = append(configs, config)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fileData := fileBasedData{
|
|
|
|
+ Users: users,
|
|
|
|
+ BucketConfigs: configs,
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ data, err := json.MarshalIndent(fileData, "", " ")
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
@@ -262,6 +359,80 @@ func (db *FileBasedKVDB) ValidateCredentials(accessKeyID, secretAccessKey string
|
|
return user, nil
|
|
return user, nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// SetBucketConfig sets the configuration for a bucket
|
|
|
|
+func (db *FileBasedKVDB) SetBucketConfig(config *BucketConfig) error {
|
|
|
|
+ db.mu.Lock()
|
|
|
|
+ defer db.mu.Unlock()
|
|
|
|
+
|
|
|
|
+ key := config.AccountID + ":" + config.BucketID
|
|
|
|
+ configCopy := *config
|
|
|
|
+ db.bucketConfigs[key] = &configCopy
|
|
|
|
+
|
|
|
|
+ return db.save()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// GetBucketConfig gets the configuration for a bucket
|
|
|
|
+func (db *FileBasedKVDB) GetBucketConfig(accountID, bucketID string) (*BucketConfig, error) {
|
|
|
|
+ db.mu.RLock()
|
|
|
|
+ defer db.mu.RUnlock()
|
|
|
|
+
|
|
|
|
+ key := accountID + ":" + bucketID
|
|
|
|
+ config, exists := db.bucketConfigs[key]
|
|
|
|
+ if !exists {
|
|
|
|
+ return nil, fmt.Errorf("bucket config not found")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ configCopy := *config
|
|
|
|
+ return &configCopy, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// DeleteBucketConfig deletes the configuration for a bucket
|
|
|
|
+func (db *FileBasedKVDB) DeleteBucketConfig(accountID, bucketID string) error {
|
|
|
|
+ db.mu.Lock()
|
|
|
|
+ defer db.mu.Unlock()
|
|
|
|
+
|
|
|
|
+ key := accountID + ":" + bucketID
|
|
|
|
+ if _, exists := db.bucketConfigs[key]; !exists {
|
|
|
|
+ return fmt.Errorf("bucket config not found")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ delete(db.bucketConfigs, key)
|
|
|
|
+
|
|
|
|
+ return db.save()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// ListBucketConfigs lists all bucket configurations for an account
|
|
|
|
+func (db *FileBasedKVDB) ListBucketConfigs(accountID string) ([]*BucketConfig, error) {
|
|
|
|
+ db.mu.RLock()
|
|
|
|
+ defer db.mu.RUnlock()
|
|
|
|
+
|
|
|
|
+ var configs []*BucketConfig
|
|
|
|
+ prefix := accountID + ":"
|
|
|
|
+
|
|
|
|
+ for key, config := range db.bucketConfigs {
|
|
|
|
+ if len(key) >= len(prefix) && key[:len(prefix)] == prefix {
|
|
|
|
+ configCopy := *config
|
|
|
|
+ configs = append(configs, &configCopy)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return configs, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// ResolveBucketName resolves a bucket name to accountID and bucketID
|
|
|
|
+func (db *FileBasedKVDB) ResolveBucketName(bucketName string) (string, string, error) {
|
|
|
|
+ db.mu.RLock()
|
|
|
|
+ defer db.mu.RUnlock()
|
|
|
|
+
|
|
|
|
+ for _, config := range db.bucketConfigs {
|
|
|
|
+ if config.BucketName == bucketName {
|
|
|
|
+ return config.AccountID, config.BucketID, nil
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return "", "", fmt.Errorf("bucket not found: %s", bucketName)
|
|
|
|
+}
|
|
|
|
+
|
|
// Close closes the database
|
|
// Close closes the database
|
|
func (db *FileBasedKVDB) Close() error {
|
|
func (db *FileBasedKVDB) Close() error {
|
|
db.mu.Lock()
|
|
db.mu.Lock()
|