fix(fileinfo_store): refactor file info store

This commit is contained in:
hexxa 2022-03-24 16:44:34 +08:00 committed by Hexxa
parent d65f1c4356
commit 41827f20c0
7 changed files with 194 additions and 175 deletions

View file

@ -116,7 +116,7 @@ func (bs *BoltStore) setUploadInfo(tx *bolt.Tx, userID uint64, uploadPath string
func (bs *BoltStore) getFileInfo(tx *bolt.Tx, userID uint64, itemPath string) (*db.FileInfo, error) { func (bs *BoltStore) getFileInfo(tx *bolt.Tx, userID uint64, itemPath string) (*db.FileInfo, error) {
var err error var err error
fileInfoBucket := tx.Bucket([]byte(db.InfoNs)) fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs))
if fileInfoBucket == nil { if fileInfoBucket == nil {
return nil, db.ErrBucketNotFound return nil, db.ErrBucketNotFound
} }
@ -137,7 +137,7 @@ func (bs *BoltStore) getFileInfo(tx *bolt.Tx, userID uint64, itemPath string) (*
func (bs *BoltStore) setFileInfo(tx *bolt.Tx, userID uint64, itemPath string, fileInfo *db.FileInfo) error { func (bs *BoltStore) setFileInfo(tx *bolt.Tx, userID uint64, itemPath string, fileInfo *db.FileInfo) error {
var err error var err error
fileInfoBucket := tx.Bucket([]byte(db.InfoNs)) fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs))
if fileInfoBucket == nil { if fileInfoBucket == nil {
return db.ErrBucketNotFound return db.ErrBucketNotFound
} }
@ -290,7 +290,7 @@ func (bs *BoltStore) DelInfos(userID uint64, itemPath string, isDir bool) error
return bs.boltdb.Update(func(tx *bolt.Tx) error { return bs.boltdb.Update(func(tx *bolt.Tx) error {
var err error var err error
fileInfoBucket := tx.Bucket([]byte(db.InfoNs)) fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs))
if fileInfoBucket == nil { if fileInfoBucket == nil {
return db.ErrBucketNotFound return db.ErrBucketNotFound
} }
@ -339,7 +339,7 @@ func (bs *BoltStore) MoveInfos(userID uint64, oldPath, newPath string, isDir boo
return bs.boltdb.Update(func(tx *bolt.Tx) error { return bs.boltdb.Update(func(tx *bolt.Tx) error {
var err error var err error
fileInfoBucket := tx.Bucket([]byte(db.InfoNs)) fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs))
if fileInfoBucket == nil { if fileInfoBucket == nil {
return db.ErrBucketNotFound return db.ErrBucketNotFound
} }

View file

@ -10,10 +10,11 @@ const (
SchemaV2 = "v2" // add size to file info SchemaV2 = "v2" // add size to file info
UserSchemaNs = "UserSchemaNs" UserSchemaNs = "UserSchemaNs"
FileSchemaNs = "FileSchemaNs"
UserIDsNs = "UserIDsNs" UserIDsNs = "UserIDsNs"
UsersNs = "UsersNs" UsersNs = "UsersNs"
RolesNs = "RolesNs" RolesNs = "RolesNs"
InfoNs = "InfoNs" FileInfoNs = "FileInfoNs"
ShareIDNs = "ShareIDNs" ShareIDNs = "ShareIDNs"
uploadsPrefix = "uploads" uploadsPrefix = "uploads"
@ -290,17 +291,17 @@ func CheckBgConfig(cfg *BgConfig, fillDefault bool) error {
func CheckUser(user *User, fillDefault bool) error { func CheckUser(user *User, fillDefault bool) error {
if user.ID == 0 && user.Role != AdminRole { if user.ID == 0 && user.Role != AdminRole {
return ErrInvalidUser return fmt.Errorf("invalid ID: (%w)", ErrInvalidUser)
} }
// TODO: add length check // TODO: add length check
if user.Name == "" || user.Pwd == "" || user.Role == "" { if user.Name == "" || user.Role == "" {
return ErrInvalidUser return fmt.Errorf("invalid Name/pwd/role: (%w)", ErrInvalidUser)
} }
if user.UsedSpace < 0 { if user.UsedSpace < 0 {
return ErrInvalidUser return fmt.Errorf("invalid UsedSpace: (%w)", ErrInvalidUser)
} }
if user.Quota == nil || user.Preferences == nil { if user.Quota == nil || user.Preferences == nil {
return ErrInvalidUser return fmt.Errorf("invalid Quota: (%w)", ErrInvalidUser)
} }
var err error var err error

View file

@ -11,7 +11,6 @@ import (
"github.com/ihexxa/quickshare/src/db" "github.com/ihexxa/quickshare/src/db"
"github.com/ihexxa/quickshare/src/kvstore" "github.com/ihexxa/quickshare/src/kvstore"
"github.com/ihexxa/quickshare/src/kvstore/boltdbpvd"
) )
const ( const (
@ -50,16 +49,15 @@ type IFileInfoStore interface {
} }
type FileInfoStore struct { type FileInfoStore struct {
mtx *sync.RWMutex mtx *sync.RWMutex
store kvstore.IKVStore store kvstore.IKVStore
boltdb boltdbpvd.BoltProvider
} }
func NewFileInfoStore(store kvstore.IKVStore) (*FileInfoStore, error) { func NewFileInfoStore(store kvstore.IKVStore) (*FileInfoStore, error) {
var err error var err error
for _, nsName := range []string{ for _, nsName := range []string{
InitNs, db.FileSchemaNs,
db.InfoNs, db.FileInfoNs,
db.ShareIDNs, db.ShareIDNs,
} { } {
if !store.HasNamespace(nsName) { if !store.HasNamespace(nsName) {
@ -69,115 +67,15 @@ func NewFileInfoStore(store kvstore.IKVStore) (*FileInfoStore, error) {
} }
} }
boltdb := store.(boltdbpvd.BoltProvider)
fi := &FileInfoStore{ fi := &FileInfoStore{
store: store, store: store,
boltdb: boltdb, mtx: &sync.RWMutex{},
mtx: &sync.RWMutex{},
} }
return fi, nil return fi, nil
} }
func (fi *FileInfoStore) AddSharing(dirPath string) error { func (fi *FileInfoStore) getInfo(itemPath string) (*db.FileInfo, error) {
fi.mtx.Lock() infoStr, ok := fi.store.GetStringIn(db.FileInfoNs, itemPath)
defer fi.mtx.Unlock()
info, err := fi.GetInfo(dirPath)
if err != nil {
if !errors.Is(err, ErrNotFound) {
return err
}
info = &db.FileInfo{
IsDir: true,
}
}
// TODO: ensure Atomicity
shareID, err := fi.getShareID(dirPath)
if err != nil {
return err
}
err = fi.store.SetStringIn(db.ShareIDNs, shareID, dirPath)
if err != nil {
return err
}
info.Shared = true
info.ShareID = shareID
return fi.SetInfo(dirPath, info)
}
func (fi *FileInfoStore) DelSharing(dirPath string) error {
fi.mtx.Lock()
defer fi.mtx.Unlock()
info, err := fi.GetInfo(dirPath)
if err != nil {
return err
}
// TODO: ensure Atomicity
// In the bolt, if the key does not exist
// then nothing is done and a nil error is returned
// because before this version, shareIDs are not removed correctly
// so it iterates all shareIDs and cleans remaining entries
shareIDtoDir, err := fi.store.ListStringsIn(db.ShareIDNs)
if err != nil {
return err
}
for shareID, shareDir := range shareIDtoDir {
if shareDir == dirPath {
err = fi.store.DelStringIn(db.ShareIDNs, shareID)
if err != nil {
return err
}
}
}
info.ShareID = ""
info.Shared = false
return fi.SetInfo(dirPath, info)
}
func (fi *FileInfoStore) GetSharing(dirPath string) (bool, bool) {
fi.mtx.Lock()
defer fi.mtx.Unlock()
// TODO: differentiate error and not exist
info, err := fi.GetInfo(dirPath)
if err != nil {
return false, false
}
return info.IsDir && info.Shared, true
}
func (fi *FileInfoStore) ListSharings(prefix string) (map[string]string, error) {
infoStrs, err := fi.store.ListStringsByPrefixIn(prefix, db.InfoNs)
if err != nil {
return nil, err
}
info := &db.FileInfo{}
sharings := map[string]string{}
for itemPath, infoStr := range infoStrs {
err = json.Unmarshal([]byte(infoStr), info)
if err != nil {
return nil, fmt.Errorf("list sharing error: %w", err)
}
if info.IsDir && info.Shared {
sharings[itemPath] = info.ShareID
}
}
return sharings, nil
}
func (fi *FileInfoStore) GetInfo(itemPath string) (*db.FileInfo, error) {
infoStr, ok := fi.store.GetStringIn(db.InfoNs, itemPath)
if !ok { if !ok {
return nil, ErrNotFound return nil, ErrNotFound
} }
@ -190,10 +88,14 @@ func (fi *FileInfoStore) GetInfo(itemPath string) (*db.FileInfo, error) {
return info, nil return info, nil
} }
func (fi *FileInfoStore) GetInfo(itemPath string) (*db.FileInfo, error) {
return fi.getInfo(itemPath)
}
func (fi *FileInfoStore) GetInfos(itemPaths []string) (map[string]*db.FileInfo, error) { func (fi *FileInfoStore) GetInfos(itemPaths []string) (map[string]*db.FileInfo, error) {
infos := map[string]*db.FileInfo{} infos := map[string]*db.FileInfo{}
for _, itemPath := range itemPaths { for _, itemPath := range itemPaths {
info, err := fi.GetInfo(itemPath) info, err := fi.getInfo(itemPath)
if err != nil { if err != nil {
if !errors.Is(err, ErrNotFound) { if !errors.Is(err, ErrNotFound) {
return nil, err return nil, err
@ -206,28 +108,34 @@ func (fi *FileInfoStore) GetInfos(itemPaths []string) (map[string]*db.FileInfo,
return infos, nil return infos, nil
} }
func (fi *FileInfoStore) SetInfo(itemPath string, info *db.FileInfo) error { func (fi *FileInfoStore) setInfo(itemPath string, info *db.FileInfo) error {
infoStr, err := json.Marshal(info) infoStr, err := json.Marshal(info)
if err != nil { if err != nil {
return fmt.Errorf("set file info: %w", err) return fmt.Errorf("set file info: %w", err)
} }
err = fi.store.SetStringIn(db.InfoNs, itemPath, string(infoStr)) err = fi.store.SetStringIn(db.FileInfoNs, itemPath, string(infoStr))
if err != nil { if err != nil {
return fmt.Errorf("set file info: %w", err) return fmt.Errorf("set file info: %w", err)
} }
return nil return nil
} }
func (fi *FileInfoStore) DelInfo(itemPath string) error { func (fi *FileInfoStore) SetInfo(itemPath string, info *db.FileInfo) error {
return fi.store.DelStringIn(db.InfoNs, itemPath) return fi.setInfo(itemPath, info)
} }
func (fi *FileInfoStore) DelInfo(itemPath string) error {
return fi.store.DelStringIn(db.FileInfoNs, itemPath)
}
// sharings
func (fi *FileInfoStore) SetSha1(itemPath, sign string) error { func (fi *FileInfoStore) SetSha1(itemPath, sign string) error {
fi.mtx.Lock() fi.mtx.Lock()
defer fi.mtx.Unlock() defer fi.mtx.Unlock()
info, err := fi.GetInfo(itemPath) info, err := fi.getInfo(itemPath)
if err != nil { if err != nil {
if !errors.Is(err, ErrNotFound) { if !errors.Is(err, ErrNotFound) {
return err return err
@ -238,7 +146,7 @@ func (fi *FileInfoStore) SetSha1(itemPath, sign string) error {
} }
} }
info.Sha1 = sign info.Sha1 = sign
return fi.SetInfo(itemPath, info) return fi.setInfo(itemPath, info)
} }
func (fi *FileInfoStore) getShareID(payload string) (string, error) { func (fi *FileInfoStore) getShareID(payload string) (string, error) {
@ -273,3 +181,100 @@ func (fi *FileInfoStore) GetSharingDir(hashID string) (string, error) {
} }
return dirPath, nil return dirPath, nil
} }
func (fi *FileInfoStore) AddSharing(dirPath string) error {
fi.mtx.Lock()
defer fi.mtx.Unlock()
info, err := fi.getInfo(dirPath)
if err != nil {
if !errors.Is(err, ErrNotFound) {
return err
}
info = &db.FileInfo{
IsDir: true,
}
}
// TODO: ensure Atomicity
shareID, err := fi.getShareID(dirPath)
if err != nil {
return err
}
err = fi.store.SetStringIn(db.ShareIDNs, shareID, dirPath)
if err != nil {
return err
}
info.Shared = true
info.ShareID = shareID
return fi.setInfo(dirPath, info)
}
func (fi *FileInfoStore) DelSharing(dirPath string) error {
fi.mtx.Lock()
defer fi.mtx.Unlock()
info, err := fi.getInfo(dirPath)
if err != nil {
return err
}
// TODO: ensure Atomicity
// In the bolt, if the key does not exist
// then nothing is done and a nil error is returned
// because before this version, shareIDs are not removed correctly
// so it iterates all shareIDs and cleans remaining entries
shareIDtoDir, err := fi.store.ListStringsIn(db.ShareIDNs)
if err != nil {
return err
}
for shareID, shareDir := range shareIDtoDir {
if shareDir == dirPath {
err = fi.store.DelStringIn(db.ShareIDNs, shareID)
if err != nil {
return err
}
}
}
info.ShareID = ""
info.Shared = false
return fi.setInfo(dirPath, info)
}
func (fi *FileInfoStore) GetSharing(dirPath string) (bool, bool) {
fi.mtx.Lock()
defer fi.mtx.Unlock()
// TODO: differentiate error and not exist
info, err := fi.getInfo(dirPath)
if err != nil {
return false, false
}
return info.IsDir && info.Shared, true
}
func (fi *FileInfoStore) ListSharings(prefix string) (map[string]string, error) {
infoStrs, err := fi.store.ListStringsByPrefixIn(prefix, db.FileInfoNs)
if err != nil {
return nil, err
}
info := &db.FileInfo{}
sharings := map[string]string{}
for itemPath, infoStr := range infoStrs {
err = json.Unmarshal([]byte(infoStr), info)
if err != nil {
return nil, fmt.Errorf("list sharing error: %w", err)
}
if info.IsDir && info.Shared {
sharings[itemPath] = info.ShareID
}
}
return sharings, nil
}

View file

@ -1,6 +1,7 @@
package fileinfostore package fileinfostore
import ( import (
"errors"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -140,7 +141,7 @@ func TestUserStores(t *testing.T) {
// get infos // get infos
for itemPath := range pathInfos { for itemPath := range pathInfos {
_, err := store.GetInfo(itemPath) _, err := store.GetInfo(itemPath)
if !IsNotFound(err) { if !errors.Is(err, ErrNotFound) {
t.Fatal(err) t.Fatal(err)
} }
} }

View file

@ -12,52 +12,7 @@ var (
ErrUploadNotFound = errors.New("upload info not found") ErrUploadNotFound = errors.New("upload info not found")
) )
func (fi *FileInfoStore) AddUploadInfo(user, filePath, tmpPath string, fileSize int64) error { func (fi *FileInfoStore) getUploadInfo(user, filePath string) (string, int64, int64, error) {
ns := db.UploadNS(user)
err := fi.store.AddNamespace(ns)
if err != nil {
return err
}
_, ok := fi.store.GetStringIn(ns, tmpPath)
if ok {
return db.ErrCreateExisting
}
info := &db.UploadInfo{
RealFilePath: filePath,
Size: fileSize,
Uploaded: 0,
}
infoBytes, err := json.Marshal(info)
if err != nil {
return err
}
return fi.store.SetStringIn(ns, tmpPath, string(infoBytes))
}
func (fi *FileInfoStore) SetUploadInfo(user, filePath string, newUploaded int64) error {
realFilePath, fileSize, _, err := fi.GetUploadInfo(user, filePath)
if err != nil {
return err
} else if newUploaded > fileSize {
return ErrGreaterThanSize
}
newInfo := &db.UploadInfo{
RealFilePath: realFilePath,
Size: fileSize,
Uploaded: newUploaded,
}
newInfoBytes, err := json.Marshal(newInfo)
if err != nil {
return err
}
return fi.store.SetStringIn(db.UploadNS(user), filePath, string(newInfoBytes))
}
func (fi *FileInfoStore) GetUploadInfo(user, filePath string) (string, int64, int64, error) {
ns := db.UploadNS(user) ns := db.UploadNS(user)
infoBytes, ok := fi.store.GetStringIn(ns, filePath) infoBytes, ok := fi.store.GetStringIn(ns, filePath)
if !ok { if !ok {
@ -73,6 +28,61 @@ func (fi *FileInfoStore) GetUploadInfo(user, filePath string) (string, int64, in
return info.RealFilePath, info.Size, info.Uploaded, nil return info.RealFilePath, info.Size, info.Uploaded, nil
} }
func (fi *FileInfoStore) setUploadInfo(user, filePath string, info *db.UploadInfo) error {
newInfoBytes, err := json.Marshal(info)
if err != nil {
return err
}
return fi.store.SetStringIn(db.UploadNS(user), filePath, string(newInfoBytes))
}
func (fi *FileInfoStore) AddUploadInfo(user, filePath, tmpPath string, fileSize int64) error {
fi.mtx.Lock()
defer fi.mtx.Unlock()
ns := db.UploadNS(user)
err := fi.store.AddNamespace(ns)
if err != nil {
return err
}
_, _, _, err = fi.getUploadInfo(user, tmpPath)
if err == nil {
return db.ErrCreateExisting
}
return fi.setUploadInfo(user, filePath, &db.UploadInfo{
RealFilePath: filePath,
Size: fileSize,
Uploaded: 0,
})
}
func (fi *FileInfoStore) SetUploadInfo(user, filePath string, newUploaded int64) error {
fi.mtx.Lock()
defer fi.mtx.Unlock()
realFilePath, fileSize, _, err := fi.getUploadInfo(user, filePath)
if err != nil {
return err
} else if newUploaded > fileSize {
return ErrGreaterThanSize
}
return fi.setUploadInfo(user, filePath, &db.UploadInfo{
RealFilePath: realFilePath,
Size: fileSize,
Uploaded: newUploaded,
})
}
func (fi *FileInfoStore) GetUploadInfo(user, filePath string) (string, int64, int64, error) {
fi.mtx.Lock()
defer fi.mtx.Unlock()
return fi.getUploadInfo(user, filePath)
}
func (fi *FileInfoStore) DelUploadInfo(user, filePath string) error { func (fi *FileInfoStore) DelUploadInfo(user, filePath string) error {
return fi.store.DelInt64In(db.UploadNS(user), filePath) return fi.store.DelInt64In(db.UploadNS(user), filePath)
} }

View file

@ -32,7 +32,6 @@ type IUserStore interface {
GetUser(id uint64) (*db.User, error) GetUser(id uint64) (*db.User, error)
GetUserByName(name string) (*db.User, error) GetUserByName(name string) (*db.User, error)
SetInfo(id uint64, user *db.User) error SetInfo(id uint64, user *db.User) error
// CanIncrUsed(id uint64, capacity int64) (bool, error)
SetUsed(id uint64, incr bool, capacity int64) error SetUsed(id uint64, incr bool, capacity int64) error
ResetUsed(id uint64, used int64) error ResetUsed(id uint64, used int64) error
SetPwd(id uint64, pwd string) error SetPwd(id uint64, pwd string) error
@ -322,6 +321,9 @@ func (us *KVUserStore) ListUsers() ([]*db.User, error) {
} }
user.Pwd = "" user.Pwd = ""
if err = db.CheckUser(user, true); err != nil {
return nil, err
}
users = append(users, user) users = append(users, user)
} }

Binary file not shown.