diff --git a/internal/config/session.go b/internal/config/session.go index 33ebc50e..e7472478 100644 --- a/internal/config/session.go +++ b/internal/config/session.go @@ -9,6 +9,8 @@ type Session struct { Password string AdminPassword string ImplicitHosting bool + DatabaseAdapter string + FilePath string } func (Session) Init(cmd *cobra.Command) error { @@ -27,6 +29,16 @@ func (Session) Init(cmd *cobra.Command) error { return err } + cmd.PersistentFlags().String("database_adapter", "file", "choose database adapter for members") + if err := viper.BindPFlag("database_adapter", cmd.PersistentFlags().Lookup("database_adapter")); err != nil { + return err + } + + cmd.PersistentFlags().String("file_path", "/home/neko/members.json", "file adapter: specify file path") + if err := viper.BindPFlag("file_path", cmd.PersistentFlags().Lookup("file_path")); err != nil { + return err + } + return nil } @@ -34,4 +46,6 @@ func (s *Session) Set() { s.Password = viper.GetString("password") s.AdminPassword = viper.GetString("password_admin") s.ImplicitHosting = viper.GetBool("implicit_hosting") + s.DatabaseAdapter = viper.GetString("database_adapter") + s.FilePath = viper.GetString("file_path") } diff --git a/internal/session/database/file/adapter.go b/internal/session/database/file/adapter.go new file mode 100644 index 00000000..7577aa00 --- /dev/null +++ b/internal/session/database/file/adapter.go @@ -0,0 +1,127 @@ +package file + +import ( + "encoding/json" + "io/ioutil" + "os" + "fmt" + "sync" + + "github.com/rs/zerolog/log" + + "demodesk/neko/internal/types" +) + +func New(file string) types.MembersDatabase { + return &MembersDatabaseCtx{ + file: file, + mu: sync.Mutex{}, + } +} + +type MembersDatabaseCtx struct { + file string + mu sync.Mutex +} + +func (manager *MembersDatabaseCtx) Insert(id string, profile types.MemberProfile) error { + manager.mu.Lock() + defer manager.mu.Unlock() + + profiles, err := manager.deserialize() + if err != nil { + return err + } + + _, ok := profiles[id] + if ok { + return fmt.Errorf("Member ID already exists.") + } + + profiles[id] = profile + + return manager.serialize(profiles) +} + +func (manager *MembersDatabaseCtx) Update(id string, profile types.MemberProfile) error { + manager.mu.Lock() + defer manager.mu.Unlock() + + profiles, err := manager.deserialize() + if err != nil { + return err + } + + _, ok := profiles[id] + if !ok { + return fmt.Errorf("Member ID does not exist.") + } + + profiles[id] = profile + + return manager.serialize(profiles) +} + +func (manager *MembersDatabaseCtx) Delete(id string) error { + manager.mu.Lock() + defer manager.mu.Unlock() + + profiles, err := manager.deserialize() + if err != nil { + return err + } + + _, ok := profiles[id] + if !ok { + return fmt.Errorf("Member ID does not exist.") + } + + delete(profiles, id) + + return manager.serialize(profiles) +} + +func (manager *MembersDatabaseCtx) Select() map[string]types.MemberProfile { + manager.mu.Lock() + defer manager.mu.Unlock() + + profiles, err := manager.deserialize() + if err != nil { + // TODO: Refactor. + log.Panic().Err(err).Msg("could not read members file") + } + + return profiles +} + +func (manager *MembersDatabaseCtx) deserialize() (map[string]types.MemberProfile, error) { + file, err := os.OpenFile(manager.file, os.O_RDONLY|os.O_CREATE, os.ModePerm) + if err != nil { + return nil, err + } + + raw, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + + if len(raw) == 0 { + return map[string]types.MemberProfile{}, nil + } + + var profiles map[string]types.MemberProfile + if err := json.Unmarshal([]byte(raw), &profiles); err != nil { + return nil, err + } + + return profiles, nil +} + +func (manager *MembersDatabaseCtx) serialize(data map[string]types.MemberProfile) error { + raw, err := json.Marshal(data) + if err != nil { + return err + } + + return ioutil.WriteFile(manager.file, raw, os.ModePerm) +} diff --git a/internal/session/database/manager.go b/internal/session/database/manager.go index 29797a87..5a470afe 100644 --- a/internal/session/database/manager.go +++ b/internal/session/database/manager.go @@ -2,16 +2,16 @@ package database import ( "demodesk/neko/internal/session/database/dummy" + "demodesk/neko/internal/session/database/file" "demodesk/neko/internal/session/database/object" "demodesk/neko/internal/types" "demodesk/neko/internal/config" ) func New(config *config.Session) types.MembersDatabase { - // TODO: Load from config. - adapter := "object" - - switch adapter { + switch config.DatabaseAdapter { + case "file": + return file.New(config.FilePath) case "object": return object.New() case "dummy":