123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- package kvdb
- import (
- "encoding/json"
- "errors"
- "fmt"
- "os"
- "path/filepath"
- "sync"
- )
- // InMemoryKVDB is a simple in-memory key-value database
- type InMemoryKVDB struct {
- 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
- func NewInMemoryKVDB() *InMemoryKVDB {
- return &InMemoryKVDB{
- users: make(map[string]*User),
- bucketConfigs: make(map[string]*BucketConfig),
- }
- }
- // GetUser retrieves a user by access key ID
- func (db *InMemoryKVDB) GetUser(accessKeyID string) (*User, error) {
- db.mu.RLock()
- defer db.mu.RUnlock()
- user, exists := db.users[accessKeyID]
- if !exists {
- return nil, ErrUserNotFound
- }
- // Return a copy to prevent external modifications
- userCopy := *user
- return &userCopy, nil
- }
- // CreateUser creates a new user
- func (db *InMemoryKVDB) CreateUser(user *User) error {
- db.mu.Lock()
- defer db.mu.Unlock()
- if _, exists := db.users[user.AccessKeyID]; exists {
- return ErrUserAlreadyExists
- }
- // Store a copy to prevent external modifications
- userCopy := *user
- db.users[user.AccessKeyID] = &userCopy
- return nil
- }
- // UpdateUser updates an existing user
- func (db *InMemoryKVDB) UpdateUser(user *User) error {
- db.mu.Lock()
- defer db.mu.Unlock()
- if _, exists := db.users[user.AccessKeyID]; !exists {
- return ErrUserNotFound
- }
- userCopy := *user
- db.users[user.AccessKeyID] = &userCopy
- return nil
- }
- // DeleteUser deletes a user
- func (db *InMemoryKVDB) DeleteUser(accessKeyID string) error {
- db.mu.Lock()
- defer db.mu.Unlock()
- if _, exists := db.users[accessKeyID]; !exists {
- return ErrUserNotFound
- }
- delete(db.users, accessKeyID)
- return nil
- }
- // ListUsers returns all users
- func (db *InMemoryKVDB) ListUsers() ([]*User, error) {
- db.mu.RLock()
- defer db.mu.RUnlock()
- users := make([]*User, 0, len(db.users))
- for _, user := range db.users {
- userCopy := *user
- users = append(users, &userCopy)
- }
- return users, nil
- }
- // ValidateCredentials validates user credentials
- func (db *InMemoryKVDB) ValidateCredentials(accessKeyID, secretAccessKey string) (*User, error) {
- user, err := db.GetUser(accessKeyID)
- if err != nil {
- return nil, err
- }
- if user.SecretAccessKey != secretAccessKey {
- return nil, errors.New("invalid credentials")
- }
- 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)
- func (db *InMemoryKVDB) Close() error {
- return nil
- }
- // FileBasedKVDB is a file-based key-value database with persistence
- type FileBasedKVDB struct {
- filePath string
- users map[string]*User
- bucketConfigs map[string]*BucketConfig
- mu sync.RWMutex
- }
- // NewFileBasedKVDB creates a new file-based key-value database
- func NewFileBasedKVDB(filePath string) (*FileBasedKVDB, error) {
- db := &FileBasedKVDB{
- filePath: filePath,
- users: make(map[string]*User),
- bucketConfigs: make(map[string]*BucketConfig),
- }
- // Create directory if it doesn't exist
- dir := filepath.Dir(filePath)
- if err := os.MkdirAll(dir, 0755); err != nil {
- return nil, err
- }
- // Load existing data if file exists
- if err := db.load(); err != nil && !os.IsNotExist(err) {
- return nil, err
- }
- return db, nil
- }
- type fileBasedData struct {
- Users []*User `json:"users"`
- BucketConfigs []*BucketConfig `json:"bucket_configs"`
- }
- // load reads the database from disk
- func (db *FileBasedKVDB) load() error {
- data, err := os.ReadFile(db.filePath)
- if err != nil {
- return err
- }
- var fileData fileBasedData
- if err := json.Unmarshal(data, &fileData); err != nil {
- return err
- }
- db.mu.Lock()
- defer db.mu.Unlock()
- for _, user := range fileData.Users {
- db.users[user.AccessKeyID] = user
- }
- for _, config := range fileData.BucketConfigs {
- key := config.AccountID + ":" + config.BucketID
- db.bucketConfigs[key] = config
- }
- return nil
- }
- // save writes the database to disk
- func (db *FileBasedKVDB) save() error {
- users := make([]*User, 0, len(db.users))
- for _, user := range db.users {
- users = append(users, user)
- }
- 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 {
- return err
- }
- return os.WriteFile(db.filePath, data, 0644)
- }
- // GetUser retrieves a user by access key ID
- func (db *FileBasedKVDB) GetUser(accessKeyID string) (*User, error) {
- db.mu.RLock()
- defer db.mu.RUnlock()
- user, exists := db.users[accessKeyID]
- if !exists {
- return nil, ErrUserNotFound
- }
- userCopy := *user
- return &userCopy, nil
- }
- // CreateUser creates a new user
- func (db *FileBasedKVDB) CreateUser(user *User) error {
- db.mu.Lock()
- defer db.mu.Unlock()
- if _, exists := db.users[user.AccessKeyID]; exists {
- return ErrUserAlreadyExists
- }
- userCopy := *user
- db.users[user.AccessKeyID] = &userCopy
- return db.save()
- }
- // UpdateUser updates an existing user
- func (db *FileBasedKVDB) UpdateUser(user *User) error {
- db.mu.Lock()
- defer db.mu.Unlock()
- if _, exists := db.users[user.AccessKeyID]; !exists {
- return ErrUserNotFound
- }
- userCopy := *user
- db.users[user.AccessKeyID] = &userCopy
- return db.save()
- }
- // DeleteUser deletes a user
- func (db *FileBasedKVDB) DeleteUser(accessKeyID string) error {
- db.mu.Lock()
- defer db.mu.Unlock()
- if _, exists := db.users[accessKeyID]; !exists {
- return ErrUserNotFound
- }
- delete(db.users, accessKeyID)
- return db.save()
- }
- // ListUsers returns all users
- func (db *FileBasedKVDB) ListUsers() ([]*User, error) {
- db.mu.RLock()
- defer db.mu.RUnlock()
- users := make([]*User, 0, len(db.users))
- for _, user := range db.users {
- userCopy := *user
- users = append(users, &userCopy)
- }
- return users, nil
- }
- // ValidateCredentials validates user credentials
- func (db *FileBasedKVDB) ValidateCredentials(accessKeyID, secretAccessKey string) (*User, error) {
- user, err := db.GetUser(accessKeyID)
- if err != nil {
- return nil, err
- }
- if user.SecretAccessKey != secretAccessKey {
- return nil, errors.New("invalid credentials")
- }
- 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
- func (db *FileBasedKVDB) Close() error {
- db.mu.Lock()
- defer db.mu.Unlock()
- return db.save()
- }
|