fix(fileinfo_store): refactor file info store
This commit is contained in:
parent
d65f1c4356
commit
41827f20c0
7 changed files with 194 additions and 175 deletions
|
@ -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) {
|
||||
var err error
|
||||
|
||||
fileInfoBucket := tx.Bucket([]byte(db.InfoNs))
|
||||
fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs))
|
||||
if fileInfoBucket == nil {
|
||||
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 {
|
||||
var err error
|
||||
|
||||
fileInfoBucket := tx.Bucket([]byte(db.InfoNs))
|
||||
fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs))
|
||||
if fileInfoBucket == nil {
|
||||
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 {
|
||||
var err error
|
||||
|
||||
fileInfoBucket := tx.Bucket([]byte(db.InfoNs))
|
||||
fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs))
|
||||
if fileInfoBucket == nil {
|
||||
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 {
|
||||
var err error
|
||||
|
||||
fileInfoBucket := tx.Bucket([]byte(db.InfoNs))
|
||||
fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs))
|
||||
if fileInfoBucket == nil {
|
||||
return db.ErrBucketNotFound
|
||||
}
|
||||
|
|
|
@ -10,10 +10,11 @@ const (
|
|||
SchemaV2 = "v2" // add size to file info
|
||||
|
||||
UserSchemaNs = "UserSchemaNs"
|
||||
FileSchemaNs = "FileSchemaNs"
|
||||
UserIDsNs = "UserIDsNs"
|
||||
UsersNs = "UsersNs"
|
||||
RolesNs = "RolesNs"
|
||||
InfoNs = "InfoNs"
|
||||
FileInfoNs = "FileInfoNs"
|
||||
ShareIDNs = "ShareIDNs"
|
||||
|
||||
uploadsPrefix = "uploads"
|
||||
|
@ -290,17 +291,17 @@ func CheckBgConfig(cfg *BgConfig, fillDefault bool) error {
|
|||
|
||||
func CheckUser(user *User, fillDefault bool) error {
|
||||
if user.ID == 0 && user.Role != AdminRole {
|
||||
return ErrInvalidUser
|
||||
return fmt.Errorf("invalid ID: (%w)", ErrInvalidUser)
|
||||
}
|
||||
// TODO: add length check
|
||||
if user.Name == "" || user.Pwd == "" || user.Role == "" {
|
||||
return ErrInvalidUser
|
||||
if user.Name == "" || user.Role == "" {
|
||||
return fmt.Errorf("invalid Name/pwd/role: (%w)", ErrInvalidUser)
|
||||
}
|
||||
if user.UsedSpace < 0 {
|
||||
return ErrInvalidUser
|
||||
return fmt.Errorf("invalid UsedSpace: (%w)", ErrInvalidUser)
|
||||
}
|
||||
if user.Quota == nil || user.Preferences == nil {
|
||||
return ErrInvalidUser
|
||||
return fmt.Errorf("invalid Quota: (%w)", ErrInvalidUser)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"github.com/ihexxa/quickshare/src/db"
|
||||
"github.com/ihexxa/quickshare/src/kvstore"
|
||||
"github.com/ihexxa/quickshare/src/kvstore/boltdbpvd"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -50,16 +49,15 @@ type IFileInfoStore interface {
|
|||
}
|
||||
|
||||
type FileInfoStore struct {
|
||||
mtx *sync.RWMutex
|
||||
store kvstore.IKVStore
|
||||
boltdb boltdbpvd.BoltProvider
|
||||
mtx *sync.RWMutex
|
||||
store kvstore.IKVStore
|
||||
}
|
||||
|
||||
func NewFileInfoStore(store kvstore.IKVStore) (*FileInfoStore, error) {
|
||||
var err error
|
||||
for _, nsName := range []string{
|
||||
InitNs,
|
||||
db.InfoNs,
|
||||
db.FileSchemaNs,
|
||||
db.FileInfoNs,
|
||||
db.ShareIDNs,
|
||||
} {
|
||||
if !store.HasNamespace(nsName) {
|
||||
|
@ -69,115 +67,15 @@ func NewFileInfoStore(store kvstore.IKVStore) (*FileInfoStore, error) {
|
|||
}
|
||||
}
|
||||
|
||||
boltdb := store.(boltdbpvd.BoltProvider)
|
||||
|
||||
fi := &FileInfoStore{
|
||||
store: store,
|
||||
boltdb: boltdb,
|
||||
mtx: &sync.RWMutex{},
|
||||
store: store,
|
||||
mtx: &sync.RWMutex{},
|
||||
}
|
||||
return fi, 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.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)
|
||||
func (fi *FileInfoStore) getInfo(itemPath string) (*db.FileInfo, error) {
|
||||
infoStr, ok := fi.store.GetStringIn(db.FileInfoNs, itemPath)
|
||||
if !ok {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
@ -190,10 +88,14 @@ func (fi *FileInfoStore) GetInfo(itemPath string) (*db.FileInfo, error) {
|
|||
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) {
|
||||
infos := map[string]*db.FileInfo{}
|
||||
for _, itemPath := range itemPaths {
|
||||
info, err := fi.GetInfo(itemPath)
|
||||
info, err := fi.getInfo(itemPath)
|
||||
if err != nil {
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
return nil, err
|
||||
|
@ -206,28 +108,34 @@ func (fi *FileInfoStore) GetInfos(itemPaths []string) (map[string]*db.FileInfo,
|
|||
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)
|
||||
if err != nil {
|
||||
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 {
|
||||
return fmt.Errorf("set file info: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fi *FileInfoStore) DelInfo(itemPath string) error {
|
||||
return fi.store.DelStringIn(db.InfoNs, itemPath)
|
||||
func (fi *FileInfoStore) SetInfo(itemPath string, info *db.FileInfo) error {
|
||||
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 {
|
||||
fi.mtx.Lock()
|
||||
defer fi.mtx.Unlock()
|
||||
|
||||
info, err := fi.GetInfo(itemPath)
|
||||
info, err := fi.getInfo(itemPath)
|
||||
if err != nil {
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
return err
|
||||
|
@ -238,7 +146,7 @@ func (fi *FileInfoStore) SetSha1(itemPath, sign string) error {
|
|||
}
|
||||
}
|
||||
info.Sha1 = sign
|
||||
return fi.SetInfo(itemPath, info)
|
||||
return fi.setInfo(itemPath, info)
|
||||
}
|
||||
|
||||
func (fi *FileInfoStore) getShareID(payload string) (string, error) {
|
||||
|
@ -273,3 +181,100 @@ func (fi *FileInfoStore) GetSharingDir(hashID string) (string, error) {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package fileinfostore
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -140,7 +141,7 @@ func TestUserStores(t *testing.T) {
|
|||
// get infos
|
||||
for itemPath := range pathInfos {
|
||||
_, err := store.GetInfo(itemPath)
|
||||
if !IsNotFound(err) {
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,52 +12,7 @@ var (
|
|||
ErrUploadNotFound = errors.New("upload info not found")
|
||||
)
|
||||
|
||||
func (fi *FileInfoStore) AddUploadInfo(user, filePath, tmpPath string, fileSize 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) {
|
||||
func (fi *FileInfoStore) getUploadInfo(user, filePath string) (string, int64, int64, error) {
|
||||
ns := db.UploadNS(user)
|
||||
infoBytes, ok := fi.store.GetStringIn(ns, filePath)
|
||||
if !ok {
|
||||
|
@ -73,6 +28,61 @@ func (fi *FileInfoStore) GetUploadInfo(user, filePath string) (string, int64, in
|
|||
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 {
|
||||
return fi.store.DelInt64In(db.UploadNS(user), filePath)
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ type IUserStore interface {
|
|||
GetUser(id uint64) (*db.User, error)
|
||||
GetUserByName(name string) (*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
|
||||
ResetUsed(id uint64, used int64) error
|
||||
SetPwd(id uint64, pwd string) error
|
||||
|
@ -322,6 +321,9 @@ func (us *KVUserStore) ListUsers() ([]*db.User, error) {
|
|||
}
|
||||
user.Pwd = ""
|
||||
|
||||
if err = db.CheckUser(user, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
|
|
BIN
src/server/testdata/test_quickshare.db
vendored
BIN
src/server/testdata/test_quickshare.db
vendored
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue