package localserver import ( "encoding/binary" "os" "path/filepath" "sync" "github.com/dgraph-io/badger/v3" ) type ZoneChatTrackingDB struct { ZoneID string db func(cb func(*badger.DB) (err error)) (err error) lock *sync.RWMutex } func NewZoneChatTracking(zoneId, chatId string, members ...string) (*ZoneChatTrackingDB, error) { if _, dirErr := os.ReadDir(filepath.Join(dataPath, "data", "zones", zoneId, "chats", chatId, "__tracking__")); os.IsNotExist(dirErr) { _ = os.MkdirAll(filepath.Join(dataPath, "data", "zones", zoneId, "chats", chatId, "__tracking__"), 0700) } db := func(f func(*badger.DB) (err error)) (err error) { db, err := badger.Open(badger.DefaultOptions(filepath.Join(dataPath, "data", "zones", zoneId, "chats", chatId, "__tracking__")).WithLogger(dbLogger)) if err != nil { return } defer db.Close() err = f(db) return } lock := new(sync.RWMutex) if err := db(func(d *badger.DB) (err error) { err = d.Update(func(txn *badger.Txn) error { b := make([]byte, bufferSize) binary.BigEndian.PutUint64(b, 0) for _, member := range members { if _, rerr := txn.Get([]byte(member)); rerr == badger.ErrKeyNotFound { _ = txn.Set([]byte(member), b) } else if rerr != nil { return rerr } } return nil }) if err != nil { return } return }); err != nil { return nil, err } return &ZoneChatTrackingDB{zoneId, db, lock}, nil } func (zctdb *ZoneChatTrackingDB) Initialize(lastIndex uint64, users ...string) (err error) { for _, user := range users { if err = zctdb.SetUserLastIndex(user, lastIndex); err != nil { return } } return } func (zctdb *ZoneChatTrackingDB) updateDBCallbackFolder(newChatId string) { db := func(f func(*badger.DB) (err error)) (err error) { db, err := badger.Open(badger.DefaultOptions(filepath.Join(dataPath, "data", "zones", zctdb.ZoneID, "chats", newChatId, "__tracking__")).WithLogger(dbLogger)) if err != nil { return } defer db.Close() err = f(db) return } zctdb.db = db } func (zctdb *ZoneChatTrackingDB) RevertTrackingLastIndex(lastIndex uint64) (err error) { zctdb.lock.Lock() defer zctdb.lock.Unlock() err = zctdb.db(func(d *badger.DB) (err error) { keys := [][]byte{} err = d.View(func(txn *badger.Txn) error { opt := badger.DefaultIteratorOptions it := txn.NewIterator(opt) defer it.Close() for it.Rewind(); it.Valid(); it.Next() { item := it.Item() if err = item.Value(func(val []byte) error { li := binary.BigEndian.Uint64(val) if li >= lastIndex { keys = append(keys, item.Key()) } return nil }); err != nil { return err } } return nil }) if err != nil { return } err = d.Update(func(txn *badger.Txn) error { for _, key := range keys { b := make([]byte, bufferSize) binary.BigEndian.PutUint64(b, lastIndex) if updateErr := txn.Set(key, b); updateErr != nil { return updateErr } } return nil }) return }) return } func (zctdb *ZoneChatTrackingDB) SetUserLastIndex(userId string, lastIndex uint64) (err error) { zctdb.lock.Lock() defer zctdb.lock.Unlock() err = zctdb.db(func(d *badger.DB) (err error) { err = d.Update(func(txn *badger.Txn) error { b := make([]byte, bufferSize) binary.BigEndian.PutUint64(b, lastIndex) updateErr := txn.Set([]byte(userId), b) return updateErr }) return }) return } func (zctdb *ZoneChatTrackingDB) GetUserLastIndex(userId string) (index uint, err error) { zctdb.lock.Lock() defer zctdb.lock.Unlock() err = zctdb.db(func(d *badger.DB) (err error) { err = d.Update(func(txn *badger.Txn) error { item, rerr := txn.Get([]byte(userId)) if rerr != nil { return rerr } _ = item.Value(func(val []byte) error { index = uint(binary.BigEndian.Uint64(val)) return nil }) return nil }) return }) return } func (zctdb *ZoneChatTrackingDB) DeleteUserTracking(userId string) (err error) { zctdb.lock.Lock() defer zctdb.lock.Unlock() err = zctdb.db(func(d *badger.DB) (err error) { err = d.Update(func(txn *badger.Txn) error { updateErr := txn.Delete([]byte(userId)) return updateErr }) return }) return }