diff --git a/src/db/boltstore/bolt_store.go b/src/db/boltstore/bolt_store.go deleted file mode 100644 index 9087318..0000000 --- a/src/db/boltstore/bolt_store.go +++ /dev/null @@ -1,398 +0,0 @@ -package boltstore - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - - "github.com/boltdb/bolt" - "github.com/ihexxa/quickshare/src/db" -) - -type BoltStore struct { - boltdb *bolt.DB -} - -func NewBoltStore(boltdb *bolt.DB) (*BoltStore, error) { - bs := &BoltStore{ - boltdb: boltdb, - } - return bs, nil -} - -func (bs *BoltStore) getUserInfo(tx *bolt.Tx, userID uint64) (*db.User, error) { - var err error - - usersBucket := tx.Bucket([]byte(db.UsersNs)) - if usersBucket == nil { - return nil, db.ErrBucketNotFound - } - - uidStr := fmt.Sprint(userID) - infoBytes := usersBucket.Get([]byte(uidStr)) - if infoBytes == nil { - return nil, db.ErrKeyNotFound - } - - userInfo := &db.User{} - err = json.Unmarshal(infoBytes, userInfo) - if err != nil { - return nil, err - } else if userInfo.ID != userID { - return nil, fmt.Errorf("user id key(%d) info(%d) does match", userID, userInfo.ID) - } - - if err = db.CheckUser(userInfo, true); err != nil { - return nil, err - } - - return userInfo, nil -} - -func (bs *BoltStore) setUserInfo(tx *bolt.Tx, userID uint64, userInfo *db.User) error { - var err error - - if err = db.CheckUser(userInfo, false); err != nil { - return err - } - - usersBucket := tx.Bucket([]byte(db.UsersNs)) - if usersBucket == nil { - return db.ErrBucketNotFound - } - - userInfoBytes, err := json.Marshal(userInfo) - if err != nil { - return err - } - - uidStr := fmt.Sprint(userID) - return usersBucket.Put([]byte(uidStr), userInfoBytes) -} - -func (bs *BoltStore) getUploadInfo(tx *bolt.Tx, userID uint64, itemPath string) (*db.UploadInfo, error) { - var err error - - uidStr := fmt.Sprint(userID) - userUploadNS := db.UploadNS(uidStr) - - uploadInfoBucket := tx.Bucket([]byte(userUploadNS)) - if uploadInfoBucket == nil { - return nil, db.ErrBucketNotFound - - } - - uploadInfoBytes := uploadInfoBucket.Get([]byte(itemPath)) - if uploadInfoBytes == nil { - return nil, db.ErrKeyNotFound - } - - uploadInfo := &db.UploadInfo{} - err = json.Unmarshal(uploadInfoBytes, uploadInfo) - if err != nil { - return nil, err - } - return uploadInfo, nil -} - -func (bs *BoltStore) setUploadInfo(tx *bolt.Tx, userID uint64, uploadPath string, uploadInfo *db.UploadInfo, overWrite bool) error { - var err error - - uidStr := fmt.Sprint(userID) - userUploadNS := db.UploadNS(uidStr) - uploadInfoBucket := tx.Bucket([]byte(userUploadNS)) - if uploadInfoBucket == nil { - uploadInfoBucket, err = tx.CreateBucket([]byte(userUploadNS)) - if err != nil { - return err - } - } - - existingInfoBytes := uploadInfoBucket.Get([]byte(uploadPath)) - if existingInfoBytes != nil { - return nil - } - uploadInfoBytes, err := json.Marshal(uploadInfo) - if err != nil { - return err - } - - return uploadInfoBucket.Put([]byte(uploadPath), uploadInfoBytes) -} - -func (bs *BoltStore) getFileInfo(tx *bolt.Tx, userID uint64, itemPath string) (*db.FileInfo, error) { - var err error - - fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs)) - if fileInfoBucket == nil { - return nil, db.ErrBucketNotFound - } - - fileInfoBytes := fileInfoBucket.Get([]byte(itemPath)) - if fileInfoBytes == nil { - return nil, db.ErrKeyNotFound - } - - fileInfo := &db.FileInfo{} - err = json.Unmarshal(fileInfoBytes, fileInfo) - if err != nil { - return nil, err - } - - if err = db.CheckFileInfo(fileInfo, true); err != nil { - return nil, err - } - return fileInfo, nil -} - -func (bs *BoltStore) setFileInfo(tx *bolt.Tx, userID uint64, itemPath string, fileInfo *db.FileInfo) error { - var err error - - if err = db.CheckFileInfo(fileInfo, false); err != nil { - return err - } - - fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs)) - if fileInfoBucket == nil { - return db.ErrBucketNotFound - } - - fileInfoBytes, err := json.Marshal(fileInfo) - if err != nil { - return err - } - - return fileInfoBucket.Put([]byte(itemPath), fileInfoBytes) -} - -func (bs *BoltStore) AddUploadInfos(userID uint64, tmpPath, filePath string, info *db.FileInfo) error { - return bs.boltdb.Update(func(tx *bolt.Tx) error { - var err error - - _, err = bs.getUploadInfo(tx, userID, tmpPath) - if err == nil { - return db.ErrKeyExisting - } else if !errors.Is(err, db.ErrBucketNotFound) && !errors.Is(err, db.ErrKeyNotFound) { - return err - } - - // check space quota - userInfo, err := bs.getUserInfo(tx, userID) - if err != nil { - return err - } - - if userInfo.UsedSpace+info.Size > int64(userInfo.Quota.SpaceLimit) { - return db.ErrQuota - } - - // update used space - userInfo.UsedSpace += info.Size - err = bs.setUserInfo(tx, userID, userInfo) - if err != nil { - return err - } - - // add upload info - uploadInfo := &db.UploadInfo{ - RealFilePath: filePath, - Size: info.Size, - Uploaded: 0, - } - return bs.setUploadInfo(tx, userID, tmpPath, uploadInfo, false) - }) -} - -func (bs *BoltStore) DelUploadingInfos(userID uint64, uploadPath string) error { - return bs.boltdb.Update(func(tx *bolt.Tx) error { - var err error - - // delete upload info - uidStr := fmt.Sprint(userID) - userUploadNS := db.UploadNS(uidStr) - - uploadInfoBucket := tx.Bucket([]byte(userUploadNS)) - if uploadInfoBucket == nil { - return db.ErrBucketNotFound - } - - uploadInfoBytes := uploadInfoBucket.Get([]byte(uploadPath)) - if uploadInfoBytes == nil { - return db.ErrKeyNotFound - } - - uploadInfo := &db.UploadInfo{} - err = json.Unmarshal(uploadInfoBytes, uploadInfo) - if err != nil { - return err - } - - err = uploadInfoBucket.Delete([]byte(uploadPath)) - if err != nil { - return err - } - - // decr used space - userInfo, err := bs.getUserInfo(tx, userID) - if err != nil { - return err - } - - userInfo.UsedSpace -= uploadInfo.Size - return bs.setUserInfo(tx, userID, userInfo) - }) -} - -func (bs *BoltStore) MoveUploadingInfos(userID uint64, uploadPath, itemPath string) error { - return bs.boltdb.Update(func(tx *bolt.Tx) error { - var err error - - // delete upload info - uidStr := fmt.Sprint(userID) - userUploadNS := db.UploadNS(uidStr) - - uploadInfoBucket := tx.Bucket([]byte(userUploadNS)) - if uploadInfoBucket == nil { - return db.ErrBucketNotFound - } - - uploadInfoBytes := uploadInfoBucket.Get([]byte(uploadPath)) - if uploadInfoBytes == nil { - return db.ErrKeyNotFound - } - - uploadInfo := &db.UploadInfo{} - err = json.Unmarshal(uploadInfoBytes, uploadInfo) - if err != nil { - return err - } - - err = uploadInfoBucket.Delete([]byte(uploadPath)) - if err != nil { - return err - } - - // create file info - fileInfo := &db.FileInfo{ - IsDir: false, - Size: uploadInfo.Size, - } - return bs.setFileInfo(tx, userID, itemPath, fileInfo) - }) -} - -func (bs *BoltStore) delShareID(tx *bolt.Tx, itemPath string) error { - var err error - - shareIDBucket := tx.Bucket([]byte(db.ShareIDNs)) - if shareIDBucket == nil { - return db.ErrBucketNotFound - } - - shareIDtoDir := map[string]string{} - shareIDBucket.ForEach(func(k, v []byte) error { - shareIDtoDir[string(k)] = string(v) - return nil - }) - - // because before this version, shareIDs are not removed correctly - // so it iterates all shareIDs and cleans remaining entries - for shareID, shareDir := range shareIDtoDir { - if shareDir == itemPath { - err = shareIDBucket.Delete([]byte(shareID)) - if err != nil { - return err - } - } - } - - return nil -} - -func (bs *BoltStore) DelInfos(userID uint64, itemPath string) error { - return bs.boltdb.Update(func(tx *bolt.Tx) error { - var err error - - fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs)) - if fileInfoBucket == nil { - return db.ErrBucketNotFound - } - - // delete children - prefixBytes := []byte(itemPath) - cur := fileInfoBucket.Cursor() - usedSpaceDecr := int64(0) - for k, v := cur.Seek(prefixBytes); k != nil && bytes.HasPrefix(k, prefixBytes); k, v = cur.Next() { - fileInfo := &db.FileInfo{} - err = json.Unmarshal(v, fileInfo) - if err != nil { - return err - } - - usedSpaceDecr += fileInfo.Size - childPath := string(k) - err = fileInfoBucket.Delete([]byte(childPath)) - if err != nil { - return err - } - - err = bs.delShareID(tx, childPath) - if err != nil { - return err - } - } - - // decr used space - userInfo, err := bs.getUserInfo(tx, userID) - if err != nil { - return err - } - userInfo.UsedSpace -= usedSpaceDecr - err = bs.setUserInfo(tx, userID, userInfo) - if err != nil { - return err - } - - return nil - }) -} - -func (bs *BoltStore) MoveInfos(userID uint64, oldPath, newPath string, isDir bool) error { - return bs.boltdb.Update(func(tx *bolt.Tx) error { - var err error - - fileInfoBucket := tx.Bucket([]byte(db.FileInfoNs)) - if fileInfoBucket == nil { - return db.ErrBucketNotFound - } - - fileInfo, err := bs.getFileInfo(tx, userID, oldPath) - if err != nil { - if errors.Is(err, db.ErrKeyNotFound) && isDir { - // the file info for the dir does not exist - return nil - } - return err - } - - // delete old shareID - if isDir { - fileInfo.Shared = false - fileInfo.ShareID = "" - err = bs.delShareID(tx, oldPath) - if err != nil { - return err - } - } - - // delete old info - err = fileInfoBucket.Delete([]byte(oldPath)) - if err != nil { - return err - } - - // add new info - return bs.setFileInfo(tx, userID, newPath, fileInfo) - }) -} diff --git a/src/db/fileinfostore/file_info_store.go b/src/db/fileinfostore/file_info_store.go deleted file mode 100644 index 93633b7..0000000 --- a/src/db/fileinfostore/file_info_store.go +++ /dev/null @@ -1,293 +0,0 @@ -package fileinfostore - -import ( - "context" - "crypto/sha1" - "encoding/json" - "errors" - "fmt" - "io" - "strings" - "sync" - - "github.com/ihexxa/quickshare/src/db" - "github.com/ihexxa/quickshare/src/kvstore" -) - -const ( - InitNs = "Init" - InitTimeKey = "initTime" - SchemaVerKey = "SchemaVersion" - SchemaV1 = "v1" -) - -var ( - // db.ErrEmpty = errors.New("can not hash empty string") - // db.ErrFileInfoNotFound = errors.New("file info not found") - // db.ErrSharingNotFound = errors.New("sharing id not found") - // db.ErrConflicted = errors.New("conflict found in hashing") - // db.ErrVerNotFound = errors.New("file info schema version not found") - maxHashingTime = 10 -) - -type IFileInfoStore interface { - AddSharing(dirPath string) error - DelSharing(dirPath string) error - GetSharing(dirPath string) (bool, bool) - ListSharings(prefix string) (map[string]string, error) - GetInfo(itemPath string) (*db.FileInfo, error) - SetInfo(itemPath string, info *db.FileInfo) error - DelInfo(itemPath string) error - SetSha1(itemPath, sign string) error - GetInfos(itemPaths []string) (map[string]*db.FileInfo, error) - GetSharingDir(hashID string) (string, error) - // upload info - AddUploadInfo(user, filePath, tmpPath string, fileSize int64) error - SetUploadInfo(user, filePath string, newUploaded int64) error - GetUploadInfo(user, filePath string) (string, int64, int64, error) - DelUploadInfo(user, filePath string) error - ListUploadInfo(user string) ([]*db.UploadInfo, error) -} - -type FileInfoStore struct { - mtx *sync.RWMutex - store kvstore.IKVStore -} - -func NewFileInfoStore(store kvstore.IKVStore) (*FileInfoStore, error) { - var err error - for _, nsName := range []string{ - db.FileSchemaNs, - db.FileInfoNs, - db.ShareIDNs, - } { - if !store.HasNamespace(nsName) { - if err = store.AddNamespace(nsName); err != nil { - return nil, err - } - } - } - - fi := &FileInfoStore{ - store: store, - mtx: &sync.RWMutex{}, - } - return fi, nil -} - -func (fi *FileInfoStore) getInfo(itemPath string) (*db.FileInfo, error) { - infoStr, ok := fi.store.GetStringIn(db.FileInfoNs, itemPath) - if !ok { - return nil, db.ErrFileInfoNotFound - } - - info := &db.FileInfo{} - err := json.Unmarshal([]byte(infoStr), info) - if err != nil { - return nil, fmt.Errorf("get file info: %w", err) - } - - if err = db.CheckFileInfo(info, true); err != nil { - return nil, err - } - return info, nil -} - -func (fi *FileInfoStore) GetFileInfo(ctx context.Context, itemPath string) (*db.FileInfo, error) { - return fi.getInfo(itemPath) -} - -func (fi *FileInfoStore) ListFileInfos(ctx context.Context, itemPaths []string) (map[string]*db.FileInfo, error) { - infos := map[string]*db.FileInfo{} - for _, itemPath := range itemPaths { - info, err := fi.getInfo(itemPath) - if err != nil { - if !errors.Is(err, db.ErrFileInfoNotFound) { - // TODO: try to make info data consistent with fs - return nil, err - } - continue - } - if err = db.CheckFileInfo(info, true); err != nil { - return nil, err - } - infos[itemPath] = info - } - - return infos, nil -} - -func (fi *FileInfoStore) setInfo(itemPath string, info *db.FileInfo) error { - if err := db.CheckFileInfo(info, false); err != nil { - return err - } - - infoStr, err := json.Marshal(info) - if err != nil { - return fmt.Errorf("set file info: %w", err) - } - - 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) SetFileInfo(ctx context.Context, itemPath string, info *db.FileInfo) error { - return fi.setInfo(itemPath, info) -} - -func (fi *FileInfoStore) DelFileInfo(ctx context.Context, itemPath string) error { - return fi.store.DelStringIn(db.FileInfoNs, itemPath) -} - -// sharings - -func (fi *FileInfoStore) SetSha1(ctx context.Context, itemPath, sign string) error { - fi.mtx.Lock() - defer fi.mtx.Unlock() - - info, err := fi.getInfo(itemPath) - if err != nil { - if !errors.Is(err, db.ErrFileInfoNotFound) { - return err - } - info = &db.FileInfo{ - IsDir: false, - Shared: false, - } - } - info.Sha1 = sign - return fi.setInfo(itemPath, info) -} - -func (fi *FileInfoStore) getShareID(payload string) (string, error) { - if len(payload) == 0 { - return "", db.ErrEmpty - } - - for i := 0; i < maxHashingTime; i++ { - msg := strings.Repeat(payload, i+1) - h := sha1.New() - _, err := io.WriteString(h, msg) - if err != nil { - return "", err - } - - shareID := fmt.Sprintf("%x", h.Sum(nil))[:7] - shareDir, ok := fi.store.GetStringIn(db.ShareIDNs, shareID) - if !ok { - return shareID, nil - } else if ok && shareDir == payload { - return shareID, nil - } - } - - return "", db.ErrConflicted -} - -func (fi *FileInfoStore) GetSharingDir(ctx context.Context, hashID string) (string, error) { - dirPath, ok := fi.store.GetStringIn(db.ShareIDNs, hashID) - if !ok { - return "", db.ErrSharingNotFound - } - return dirPath, nil -} - -func (fi *FileInfoStore) AddSharing(ctx context.Context, dirPath string) error { - fi.mtx.Lock() - defer fi.mtx.Unlock() - - info, err := fi.getInfo(dirPath) - if err != nil { - if !errors.Is(err, db.ErrFileInfoNotFound) { - 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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, 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 -} diff --git a/src/db/fileinfostore/file_info_store_test.go b/src/db/fileinfostore/file_info_store_test.go deleted file mode 100644 index 09dbdf2..0000000 --- a/src/db/fileinfostore/file_info_store_test.go +++ /dev/null @@ -1,171 +0,0 @@ -package fileinfostore - -import ( - "errors" - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/ihexxa/quickshare/src/db" - "github.com/ihexxa/quickshare/src/kvstore/boltdbpvd" -) - -func TestUserStores(t *testing.T) { - - testSharingMethods := func(t *testing.T, store IFileInfoStore) { - dirPaths := []string{"admin/path1", "admin/path1/path2"} - var err error - - // add sharings - for _, dirPath := range dirPaths { - err = store.AddSharing(dirPath) - if err != nil { - t.Fatal(err) - } - } - - // list sharings - prefix := "admin" - dirToID, err := store.ListSharings(prefix) - if err != nil { - t.Fatal(err) - } - for _, sharingDir := range dirPaths { - if _, ok := dirToID[sharingDir]; !ok { - t.Fatalf("sharing(%s) not found", sharingDir) - } - mustTrue, exist := store.GetSharing(sharingDir) - if !mustTrue || !exist { - t.Fatalf("get sharing(%t %t) should exist", mustTrue, exist) - } - - info, err := store.GetInfo(sharingDir) - if err != nil { - t.Fatal(err) - } else if len(info.ShareID) != 7 { - t.Fatalf("incorrect ShareID %s", info.ShareID) - } - - gotSharingDir, err := store.GetSharingDir(info.ShareID) - if err != nil { - t.Fatal(err) - } else if sharingDir != gotSharingDir { - t.Fatalf("sharing dir not consist: (%s) (%s)", sharingDir, gotSharingDir) - } - } - - // del sharings - for _, dirPath := range dirPaths { - err = store.DelSharing(dirPath) - if err != nil { - t.Fatal(err) - } - } - - dirToIDAfterDel, err := store.ListSharings(prefix) - if err != nil { - t.Fatal(err) - } - for _, dirPath := range dirPaths { - if _, ok := dirToIDAfterDel[dirPath]; ok { - t.Fatalf("sharing(%s) should not exist", dirPath) - } - shared, exist := store.GetSharing(dirPath) - if shared { - t.Fatalf("get sharing(%t, %t) should not shared but exist", shared, exist) - } - - info, err := store.GetInfo(dirPath) - if err != nil { - t.Fatal(err) - } else if len(info.ShareID) != 0 { - t.Fatalf("ShareID should be empty %s", info.ShareID) - } - - // shareIDs are removed, use original dirToID to get shareID - originalShareID, ok := dirToID[dirPath] - if !ok { - t.Fatalf("dir (%s) should exist in originalShareID", dirPath) - } - - _, err = store.GetSharingDir(originalShareID) - if err != ErrSharingNotFound { - t.Fatal("should return ErrSharingNotFound") - } - } - } - - testInfoMethods := func(t *testing.T, store IFileInfoStore) { - pathInfos := map[string]*db.FileInfo{ - "admin/item": &db.FileInfo{ - Shared: false, - ShareID: "", - IsDir: false, - Sha1: "file", - }, - "admin/dir": &db.FileInfo{ - Shared: true, - ShareID: "mockedShareID", - IsDir: true, - Sha1: "dir", - }, - } - var err error - - // add infos - for itemPath, info := range pathInfos { - err = store.SetInfo(itemPath, info) - if err != nil { - t.Fatal(err) - } - } - - // get infos - for itemPath, expected := range pathInfos { - info, err := store.GetInfo(itemPath) - if err != nil { - t.Fatal(err) - } - if info.Shared != expected.Shared || info.IsDir != expected.IsDir || info.Sha1 != expected.Sha1 { - t.Fatalf("info not equaled (%v) (%v)", expected, info) - } - } - - // del sharings - for itemPath := range pathInfos { - err = store.DelInfo(itemPath) - if err != nil { - t.Fatal(err) - } - } - - // get infos - for itemPath := range pathInfos { - _, err := store.GetInfo(itemPath) - if !errors.Is(err, ErrNotFound) { - t.Fatal(err) - } - } - } - - t.Run("testing FileInfoStore", func(t *testing.T) { - rootPath, err := ioutil.TempDir("./", "quickshare_userstore_test_") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(rootPath) - - dbPath := filepath.Join(rootPath, "quickshare.db") - kvstore := boltdbpvd.New(dbPath, 1024) - defer kvstore.Close() - - store, err := NewFileInfoStore(kvstore) - if err != nil { - t.Fatal("fail to new kvstore", err) - } - - testSharingMethods(t, store) - testInfoMethods(t, store) - }) -} diff --git a/src/db/fileinfostore/upload_info_store.go b/src/db/fileinfostore/upload_info_store.go deleted file mode 100644 index 3c815bb..0000000 --- a/src/db/fileinfostore/upload_info_store.go +++ /dev/null @@ -1,113 +0,0 @@ -package fileinfostore - -import ( - "context" - "encoding/json" - "errors" - - "github.com/ihexxa/quickshare/src/db" -) - -var ( - ErrGreaterThanSize = errors.New("uploaded is greater than file size") - ErrUploadNotFound = errors.New("upload info not found") -) - -func (fi *FileInfoStore) getUploadInfo(user, filePath string) (string, int64, int64, error) { - ns := db.UploadNS(user) - infoBytes, ok := fi.store.GetStringIn(ns, filePath) - if !ok { - return "", 0, 0, ErrUploadNotFound - } - - info := &db.UploadInfo{} - err := json.Unmarshal([]byte(infoBytes), info) - if err != nil { - return "", 0, 0, err - } - - 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(ctx context.Context, 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(ctx context.Context, 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(ctx context.Context, user, filePath string) (string, int64, int64, error) { - fi.mtx.Lock() - defer fi.mtx.Unlock() - - return fi.getUploadInfo(user, filePath) -} - -func (fi *FileInfoStore) DelUploadInfo(ctx context.Context, user, filePath string) error { - return fi.store.DelInt64In(db.UploadNS(user), filePath) -} - -func (fi *FileInfoStore) ListUploadInfo(ctx context.Context, user string) ([]*db.UploadInfo, error) { - ns := db.UploadNS(user) - if !fi.store.HasNamespace(ns) { - return nil, nil - } - - infoMap, err := fi.store.ListStringsIn(ns) - if err != nil { - return nil, err - } - - infos := []*db.UploadInfo{} - for _, infoStr := range infoMap { - info := &db.UploadInfo{} - err = json.Unmarshal([]byte(infoStr), info) - if err != nil { - return nil, err - } - infos = append(infos, info) - } - - return infos, nil -} diff --git a/src/db/rdb/default/configs.go b/src/db/rdb/default/configs.go deleted file mode 100644 index 01d5379..0000000 --- a/src/db/rdb/default/configs.go +++ /dev/null @@ -1,84 +0,0 @@ -package default - -import ( - "context" - "database/sql" - "encoding/json" - - "github.com/ihexxa/quickshare/src/db" -) - -func (st *DefaultStore) getCfg(ctx context.Context, tx *sql.Tx) (*db.SiteConfig, error) { - var configStr string - err := tx.QueryRowContext( - ctx, - `select config - from t_config - where id=0`, - ).Scan(&configStr) - if err != nil { - return nil, err - } - - config := &db.SiteConfig{} - err = json.Unmarshal([]byte(configStr), config) - if err != nil { - return nil, err - } - - if err = db.CheckSiteCfg(config, true); err != nil { - return nil, err - } - return config, nil -} - -func (st *DefaultStore) setCfg(ctx context.Context, tx *sql.Tx, cfg *db.SiteConfig) error { - if err := db.CheckSiteCfg(cfg, false); err != nil { - return err - } - - cfgBytes, err := json.Marshal(cfg) - if err != nil { - return err - } - - _, err = tx.ExecContext( - ctx, - `update t_config - set config=? - where id=0`, - string(cfgBytes), - ) - return err -} - -func (st *DefaultStore) SetClientCfg(ctx context.Context, cfg *db.ClientConfig) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - siteCfg, err := st.getCfg(ctx, tx) - if err != nil { - return err - } - siteCfg.ClientCfg = cfg - - err = st.setCfg(ctx, tx, siteCfg) - if err != nil { - return err - } - - return tx.Commit() -} - -func (st *DefaultStore) GetCfg(ctx context.Context) (*db.SiteConfig, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return nil, err - } - defer tx.Rollback() - - return st.getCfg(ctx, tx) -} diff --git a/src/db/rdb/default/files.go b/src/db/rdb/default/files.go deleted file mode 100644 index bdc186f..0000000 --- a/src/db/rdb/default/files.go +++ /dev/null @@ -1,307 +0,0 @@ -package default - -import ( - "context" - "database/sql" - "encoding/json" - "errors" - "fmt" - "path" - "strings" - - "github.com/ihexxa/quickshare/src/db" -) - -func (st *DefaultStore) getFileInfo(ctx context.Context, tx *sql.Tx, itemPath string) (*db.FileInfo, error) { - var infoStr string - fInfo := &db.FileInfo{} - var isDir bool - var size int64 - var shareId string - err := tx.QueryRowContext( - ctx, - `select is_dir, size, share_id, info - from t_file_info - where path=?`, - itemPath, - ).Scan( - &isDir, - &size, - &shareId, - &infoStr, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, db.ErrFileInfoNotFound - } - return nil, err - } - - err = json.Unmarshal([]byte(infoStr), &fInfo) - if err != nil { - return nil, err - } - fInfo.IsDir = isDir - fInfo.Size = size - fInfo.ShareID = shareId - fInfo.Shared = shareId != "" - return fInfo, nil -} - -func (st *DefaultStore) GetFileInfo(ctx context.Context, itemPath string) (*db.FileInfo, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return nil, err - } - defer tx.Rollback() - - return st.getFileInfo(ctx, tx, itemPath) -} - -func (st *DefaultStore) ListFileInfos(ctx context.Context, itemPaths []string) (map[string]*db.FileInfo, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return nil, err - } - defer tx.Rollback() - - // TODO: add pagination - placeholders := []string{} - values := []any{} - for i := 0; i < len(itemPaths); i++ { - placeholders = append(placeholders, "?") - values = append(values, itemPaths[i]) - } - rows, err := tx.QueryContext( - ctx, - fmt.Sprintf( - `select path, is_dir, size, share_id, info - from t_file_info - where path in (%s) - `, - strings.Join(placeholders, ","), - ), - values..., - ) - if err != nil { - return nil, err - } - defer rows.Close() - - var fInfoStr, itemPath, shareId string - var isDir bool - var size int64 - fInfos := map[string]*db.FileInfo{} - for rows.Next() { - fInfo := &db.FileInfo{} - err = rows.Scan(&itemPath, &isDir, &size, &shareId, &fInfoStr) - if err != nil { - return nil, err - } - - err = json.Unmarshal([]byte(fInfoStr), fInfo) - if err != nil { - return nil, err - } - fInfo.IsDir = isDir - fInfo.Size = size - fInfo.ShareID = shareId - fInfo.Shared = shareId != "" - fInfos[itemPath] = fInfo - } - if rows.Err() != nil { - return nil, rows.Err() - } - - return fInfos, nil -} - -func (st *DefaultStore) addFileInfo(ctx context.Context, tx *sql.Tx, userId uint64, itemPath string, info *db.FileInfo) error { - infoStr, err := json.Marshal(info) - if err != nil { - return err - } - - location, err := getLocation(itemPath) - if err != nil { - return err - } - - dirPath, itemName := path.Split(itemPath) - _, err = tx.ExecContext( - ctx, - `insert into t_file_info ( - path, user, location, parent, name, - is_dir, size, share_id, info - ) - values ( - ?, ?, ?, ?, ?, - ?, ?, ?, ? - )`, - itemPath, userId, location, dirPath, itemName, - info.IsDir, info.Size, info.ShareID, infoStr, - ) - return err -} - -func (st *DefaultStore) AddFileInfo(ctx context.Context, userId uint64, itemPath string, info *db.FileInfo) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - err = st.addFileInfo(ctx, tx, userId, itemPath, info) - if err != nil { - return err - } - // increase used space - err = st.setUsed(ctx, tx, userId, true, info.Size) - if err != nil { - return err - } - - return tx.Commit() -} - -func (st *DefaultStore) delFileInfo(ctx context.Context, tx *sql.Tx, itemPath string) error { - _, err := tx.ExecContext( - ctx, - `delete from t_file_info - where path=? - `, - itemPath, - ) - return err -} - -func (st *DefaultStore) SetSha1(ctx context.Context, itemPath, sign string) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - info, err := st.getFileInfo(ctx, tx, itemPath) - if err != nil { - return err - } - info.Sha1 = sign - - infoStr, err := json.Marshal(info) - if err != nil { - return err - } - - _, err = tx.ExecContext( - ctx, - `update t_file_info - set info=? - where path=?`, - infoStr, - itemPath, - ) - if err != nil { - return err - } - return tx.Commit() -} - -func (st *DefaultStore) DelFileInfo(ctx context.Context, userID uint64, itemPath string) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - // get all children and size - rows, err := tx.QueryContext( - ctx, - `select path, size - from t_file_info - where path = ? or path like ? - `, - itemPath, - fmt.Sprintf("%s/%%", itemPath), - ) - if err != nil { - return err - } - defer rows.Close() - - var childrenPath string - var itemSize int64 - placeholders := []string{} - values := []any{} - decrSize := int64(0) - for rows.Next() { - err = rows.Scan(&childrenPath, &itemSize) - if err != nil { - return err - } - placeholders = append(placeholders, "?") - values = append(values, childrenPath) - decrSize += itemSize - } - - // decrease used space - err = st.setUsed(ctx, tx, userID, false, decrSize) - if err != nil { - return err - } - - // delete file info entries - _, err = tx.ExecContext( - ctx, - fmt.Sprintf( - `delete from t_file_info - where path in (%s)`, - strings.Join(placeholders, ","), - ), - values..., - ) - if err != nil { - return err - } - return tx.Commit() -} - -func (st *DefaultStore) MoveFileInfo(ctx context.Context, userId uint64, oldPath, newPath string, isDir bool) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - info, err := st.getFileInfo(ctx, tx, oldPath) - if err != nil { - if errors.Is(err, db.ErrFileInfoNotFound) { - // info for file does not exist so no need to move it - // e.g. folder info is not created before - // TODO: but sometimes it could be a bug - return nil - } - return err - } - - err = st.delFileInfo(ctx, tx, oldPath) - if err != nil { - return err - } - - err = st.addFileInfo(ctx, tx, userId, newPath, info) - if err != nil { - return err - } - - return tx.Commit() -} - -func getLocation(itemPath string) (string, error) { - // location is taken from item path - itemPathParts := strings.Split(itemPath, "/") - if len(itemPathParts) == 0 { - return "", fmt.Errorf("invalid item path '%s'", itemPath) - } - return itemPathParts[0], nil -} diff --git a/src/db/rdb/default/files_sharings.go b/src/db/rdb/default/files_sharings.go deleted file mode 100644 index 3bcc858..0000000 --- a/src/db/rdb/default/files_sharings.go +++ /dev/null @@ -1,203 +0,0 @@ -package default - -import ( - "context" - "crypto/sha1" - "database/sql" - "encoding/json" - "errors" - "fmt" - "io" - "path" - "time" - - "github.com/ihexxa/quickshare/src/db" -) - -func (st *DefaultStore) generateShareID(payload string) (string, error) { - if len(payload) == 0 { - return "", db.ErrEmpty - } - - msg := fmt.Sprintf("%s-%d", payload, time.Now().Unix()) - h := sha1.New() - _, err := io.WriteString(h, msg) - if err != nil { - return "", err - } - - return fmt.Sprintf("%x", h.Sum(nil))[:7], nil -} - -func (st *DefaultStore) IsSharing(ctx context.Context, dirPath string) (bool, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return false, err - } - defer tx.Rollback() - - var shareId string - err = tx.QueryRowContext( - ctx, - `select share_id - from t_file_info - where path=?`, - dirPath, - ).Scan( - &shareId, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return false, db.ErrFileInfoNotFound - } - return false, err - } - - return shareId != "", nil -} - -func (st *DefaultStore) GetSharingDir(ctx context.Context, hashID string) (string, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return "", err - } - defer tx.Rollback() - - var sharedPath string - err = tx.QueryRowContext( - ctx, - `select path - from t_file_info - where share_id=? - `, - hashID, - ).Scan( - &sharedPath, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return "", db.ErrSharingNotFound - } - return "", err - } - - return sharedPath, nil -} - -func (st *DefaultStore) AddSharing(ctx context.Context, userId uint64, dirPath string) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - shareID, err := st.generateShareID(dirPath) - if err != nil { - return err - } - - location, err := getLocation(dirPath) - if err != nil { - return err - } - - _, err = st.getFileInfo(ctx, tx, dirPath) - if err != nil && !errors.Is(err, db.ErrFileInfoNotFound) { - return err - } - - if errors.Is(err, db.ErrFileInfoNotFound) { - // insert new - parentPath, name := path.Split(dirPath) - info := &db.FileInfo{Shared: true} // TODO: deprecate shared in info - infoStr, err := json.Marshal(info) - if err != nil { - return err - } - - _, err = tx.ExecContext( - ctx, - `insert into t_file_info ( - path, user, location, parent, name, - is_dir, size, share_id, info - ) - values ( - ?, ?, ?, ?, ?, - ?, ?, ?, ? - )`, - dirPath, userId, location, parentPath, name, - true, 0, shareID, infoStr, - ) - if err != nil { - return err - } - } else { - _, err = tx.ExecContext( - ctx, - `update t_file_info - set share_id=? - where path=?`, - shareID, dirPath, - ) - if err != nil { - return err - } - } - - return tx.Commit() -} - -func (st *DefaultStore) DelSharing(ctx context.Context, userId uint64, dirPath string) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - _, err = tx.ExecContext( - ctx, - `update t_file_info - set share_id='' - where path=?`, - dirPath, - ) - if err != nil { - return err - } - return tx.Commit() -} - -func (st *DefaultStore) ListSharingsByLocation(ctx context.Context, location string) (map[string]string, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return nil, err - } - defer tx.Rollback() - - rows, err := tx.QueryContext( - ctx, - `select path, share_id - from t_file_info - where share_id<>'' and location=?`, - location, - ) - if err != nil { - return nil, err - } - defer rows.Close() - - var pathname, shareId string - pathToShareId := map[string]string{} - for rows.Next() { - err = rows.Scan(&pathname, &shareId) - if err != nil { - return nil, err - } - pathToShareId[pathname] = shareId - } - if rows.Err() != nil { - return nil, rows.Err() - } - - return pathToShareId, nil -} diff --git a/src/db/rdb/default/files_uploadings.go b/src/db/rdb/default/files_uploadings.go deleted file mode 100644 index 1065441..0000000 --- a/src/db/rdb/default/files_uploadings.go +++ /dev/null @@ -1,235 +0,0 @@ -package default - -import ( - "context" - "database/sql" - "errors" - - "github.com/ihexxa/quickshare/src/db" -) - -func (st *DefaultStore) addUploadInfoOnly(ctx context.Context, tx *sql.Tx, userId uint64, tmpPath, filePath string, fileSize int64) error { - _, err := tx.ExecContext( - ctx, - `insert into t_file_uploading ( - real_path, tmp_path, user, size, uploaded - ) - values ( - ?, ?, ?, ?, ? - )`, - filePath, tmpPath, userId, fileSize, 0, - ) - return err -} - -func (st *DefaultStore) AddUploadInfos(ctx context.Context, userId uint64, tmpPath, filePath string, info *db.FileInfo) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - userInfo, err := st.getUser(ctx, tx, userId) - if err != nil { - return err - } else if userInfo.UsedSpace+info.Size > int64(userInfo.Quota.SpaceLimit) { - return db.ErrQuota - } - - _, _, _, err = st.getUploadInfo(ctx, tx, userId, filePath) - if err == nil { - return db.ErrKeyExisting - } else if err != nil && !errors.Is(err, sql.ErrNoRows) { - return err - } - - userInfo.UsedSpace += info.Size - err = st.setUser(ctx, tx, userInfo) - if err != nil { - return err - } - - err = st.addUploadInfoOnly(ctx, tx, userId, tmpPath, filePath, info.Size) - if err != nil { - return err - } - - return tx.Commit() -} - -func (st *DefaultStore) DelUploadingInfos(ctx context.Context, userId uint64, realPath string) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - err = st.delUploadingInfos(ctx, tx, userId, realPath) - if err != nil { - return err - } - - return tx.Commit() -} - -func (st *DefaultStore) delUploadingInfos(ctx context.Context, tx *sql.Tx, userId uint64, realPath string) error { - _, size, _, err := st.getUploadInfo(ctx, tx, userId, realPath) - if err != nil { - // info may not exist - return err - } - - err = st.delUploadInfoOnly(ctx, tx, userId, realPath) - if err != nil { - return err - } - - userInfo, err := st.getUser(ctx, tx, userId) - if err != nil { - return err - } - userInfo.UsedSpace -= size - return st.setUser(ctx, tx, userInfo) -} - -func (st *DefaultStore) delUploadInfoOnly(ctx context.Context, tx *sql.Tx, userId uint64, filePath string) error { - _, err := tx.ExecContext( - ctx, - `delete from t_file_uploading - where real_path=? and user=?`, - filePath, userId, - ) - return err -} - -func (st *DefaultStore) MoveUploadingInfos(ctx context.Context, userId uint64, uploadPath, itemPath string) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - _, size, _, err := st.getUploadInfo(ctx, tx, userId, itemPath) - if err != nil { - return err - } - err = st.delUploadInfoOnly(ctx, tx, userId, itemPath) - if err != nil { - return err - } - err = st.addFileInfo(ctx, tx, userId, itemPath, &db.FileInfo{ - Size: size, - }) - if err != nil { - return err - } - - return tx.Commit() -} - -func (st *DefaultStore) SetUploadInfo(ctx context.Context, userId uint64, filePath string, newUploaded int64) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - var size, uploaded int64 - err = tx.QueryRowContext( - ctx, - `select size, uploaded - from t_file_uploading - where real_path=? and user=?`, - filePath, userId, - ).Scan(&size, &uploaded) - if err != nil { - return err - } else if newUploaded > size { - return db.ErrGreaterThanSize - } - - _, err = tx.ExecContext( - ctx, - `update t_file_uploading - set uploaded=? - where real_path=? and user=?`, - newUploaded, filePath, userId, - ) - if err != nil { - return err - } - return tx.Commit() -} - -func (st *DefaultStore) getUploadInfo( - ctx context.Context, tx *sql.Tx, userId uint64, filePath string, -) (string, int64, int64, error) { - var size, uploaded int64 - err := tx.QueryRowContext( - ctx, - `select size, uploaded - from t_file_uploading - where real_path=? and user=?`, - filePath, userId, - ).Scan(&size, &uploaded) - if err != nil { - return "", 0, 0, err - } - - return filePath, size, uploaded, nil -} - -func (st *DefaultStore) GetUploadInfo(ctx context.Context, userId uint64, filePath string) (string, int64, int64, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return "", 0, 0, err - } - defer tx.Rollback() - - return st.getUploadInfo(ctx, tx, userId, filePath) -} - -func (st *DefaultStore) ListUploadInfos(ctx context.Context, userId uint64) ([]*db.UploadInfo, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return nil, err - } - defer tx.Rollback() - - rows, err := tx.QueryContext( - ctx, - `select real_path, size, uploaded - from t_file_uploading - where user=?`, - userId, - ) - if err != nil { - return nil, err - } - defer rows.Close() - - var pathname string - var size, uploaded int64 - infos := []*db.UploadInfo{} - for rows.Next() { - err = rows.Scan( - &pathname, - &size, - &uploaded, - ) - if err != nil { - return nil, err - } - - infos = append(infos, &db.UploadInfo{ - RealFilePath: pathname, - Size: size, - Uploaded: uploaded, - }) - } - if rows.Err() != nil { - return nil, rows.Err() - } - - return infos, nil -} diff --git a/src/db/rdb/default/provider.go b/src/db/rdb/default/provider.go deleted file mode 100644 index 434a191..0000000 --- a/src/db/rdb/default/provider.go +++ /dev/null @@ -1,226 +0,0 @@ -package default - -import ( - "context" - "database/sql" - "encoding/json" - "fmt" - "sync" - "time" - - "github.com/ihexxa/quickshare/src/db" - _ "github.com/mattn/go-sqlite3" -) - -type DefaultDB struct { - db.IDB - dbPath string -} - -func NewDefaultDB(driverName, dbPath string) (*DefaultDB, error) { - db, err := sql.Open(driverName, dbPath) - if err != nil { - return nil, err - } - - return &DefaultDB{ - IDB: db, - dbPath: dbPath, - }, nil -} - -type DefaultStore struct { - db db.IDB -} - -func NewDefaultStore(db db.IDB) (*DefaultStore, error) { - return &DefaultStore{ - db: db, - }, nil -} - -func (st *DefaultStore) Close() error { - return st.db.Close() -} - -func (st *DefaultStore) Lock() { - st.mtx.Lock() -} - -func (st *DefaultStore) Unlock() { - st.mtx.Unlock() -} - -func (st *DefaultStore) RLock() { - st.mtx.RLock() -} - -func (st *DefaultStore) RUnlock() { - st.mtx.RUnlock() -} - -func (st *DefaultStore) IsInited() bool { - // always try to init the db - return false -} - -func (st *DefaultStore) Init(ctx context.Context, rootName, rootPwd string, cfg *db.SiteConfig) error { - err := st.InitUserTable(ctx, rootName, rootPwd) - if err != nil { - return err - } - - if err = st.InitFileTables(ctx); err != nil { - return err - } - - return st.InitConfigTable(ctx, cfg) -} - -func (st *DefaultStore) InitUserTable(ctx context.Context, rootName, rootPwd string) error { - _, err := st.db.ExecContext( - ctx, - `create table if not exists t_user ( - id bigint not null, - name varchar not null unique, - pwd varchar not null, - role integer not null, - used_space bigint not null, - quota varchar not null, - preference varchar not null, - primary key(id) - )`, - ) - if err != nil { - return err - } - - _, err = st.db.ExecContext( - ctx, - `create index if not exists i_user_name on t_user (name)`, - ) - if err != nil { - return err - } - - admin := &db.User{ - ID: 0, - Name: rootName, - Pwd: rootPwd, - Role: db.AdminRole, - Quota: &db.Quota{ - SpaceLimit: db.DefaultSpaceLimit, - UploadSpeedLimit: db.DefaultUploadSpeedLimit, - DownloadSpeedLimit: db.DefaultDownloadSpeedLimit, - }, - Preferences: &db.DefaultPreferences, - } - visitor := &db.User{ - ID: db.VisitorID, - Name: db.VisitorName, - Pwd: rootPwd, - Role: db.VisitorRole, - Quota: &db.Quota{ - SpaceLimit: 0, - UploadSpeedLimit: db.VisitorUploadSpeedLimit, - DownloadSpeedLimit: db.VisitorDownloadSpeedLimit, - }, - Preferences: &db.DefaultPreferences, - } - for _, user := range []*db.User{admin, visitor} { - err = st.AddUser(ctx, user) - if err != nil { - return err - } - } - - return nil -} - -func (st *DefaultStore) InitFileTables(ctx context.Context) error { - _, err := st.db.ExecContext( - ctx, - `create table if not exists t_file_info ( - path varchar not null, - user bigint not null, - location varchar not null, - parent varchar not null, - name varchar not null, - is_dir boolean not null, - size bigint not null, - share_id varchar not null, - info varchar not null, - primary key(path) - )`, - ) - if err != nil { - return err - } - - _, err = st.db.ExecContext( - ctx, - `create index if not exists t_file_share on t_file_info (share_id, location)`, - ) - if err != nil { - return err - } - - _, err = st.db.ExecContext( - ctx, - `create table if not exists t_file_uploading ( - real_path varchar not null, - tmp_path varchar not null unique, - user bigint not null, - size bigint not null, - uploaded bigint not null, - primary key(real_path) - )`, - ) - if err != nil { - return err - } - - _, err = st.db.ExecContext( - ctx, - `create index if not exists t_file_uploading_path on t_file_uploading (real_path, user)`, - ) - if err != nil { - return err - } - - _, err = st.db.ExecContext( - ctx, - `create index if not exists t_file_uploading_user on t_file_uploading (user)`, - ) - return err -} - -func (st *DefaultStore) InitConfigTable(ctx context.Context, cfg *db.SiteConfig) error { - st.Lock() - defer st.Unlock() - - _, err := st.db.ExecContext( - ctx, - `create table if not exists t_config ( - id bigint not null, - config varchar not null, - modified datetime not null, - primary key(id) - )`, - ) - if err != nil { - return err - } - - cfgStr, err := json.Marshal(cfg) - if err != nil { - return err - } - _, err = st.db.ExecContext( - ctx, - `insert into t_config - (id, config, modified) values (?, ?, ?)`, - 0, cfgStr, time.Now(), - ) - return err -} diff --git a/src/db/rdb/default/users.go b/src/db/rdb/default/users.go deleted file mode 100644 index d6599b2..0000000 --- a/src/db/rdb/default/users.go +++ /dev/null @@ -1,418 +0,0 @@ -package default - -import ( - "context" - "database/sql" - "encoding/json" - "errors" - "fmt" - "sync" - - "github.com/ihexxa/quickshare/src/db" -) - -func (st *DefaultStore) setUser(ctx context.Context, tx *sql.Tx, user *db.User) error { - var err error - if err = db.CheckUser(user, false); err != nil { - return err - } - - quotaStr, err := json.Marshal(user.Quota) - if err != nil { - return err - } - preferencesStr, err := json.Marshal(user.Preferences) - if err != nil { - return err - } - _, err = tx.ExecContext( - ctx, - `update t_user - set name=?, pwd=?, role=?, used_space=?, quota=?, preference=? - where id=?`, - user.Name, - user.Pwd, - user.Role, - user.UsedSpace, - quotaStr, - preferencesStr, - user.ID, - ) - return err -} - -func (st *DefaultStore) getUser(ctx context.Context, tx *sql.Tx, id uint64) (*db.User, error) { - user := &db.User{} - var quotaStr, preferenceStr string - err := tx.QueryRowContext( - ctx, - `select id, name, pwd, role, used_space, quota, preference - from t_user - where id=?`, - id, - ).Scan( - &user.ID, - &user.Name, - &user.Pwd, - &user.Role, - &user.UsedSpace, - "aStr, - &preferenceStr, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, db.ErrUserNotFound - } - return nil, err - } - - err = json.Unmarshal([]byte(quotaStr), &user.Quota) - if err != nil { - return nil, err - } - err = json.Unmarshal([]byte(preferenceStr), &user.Preferences) - if err != nil { - return nil, err - } - return user, nil -} - -func (st *DefaultStore) AddUser(ctx context.Context, user *db.User) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - quotaStr, err := json.Marshal(user.Quota) - if err != nil { - return err - } - preferenceStr, err := json.Marshal(user.Preferences) - if err != nil { - return err - } - _, err = tx.ExecContext( - ctx, - `insert into t_user (id, name, pwd, role, used_space, quota, preference) values (?, ?, ?, ?, ?, ?, ?)`, - user.ID, - user.Name, - user.Pwd, - user.Role, - user.UsedSpace, - quotaStr, - preferenceStr, - ) - if err != nil { - return err - } - - return tx.Commit() -} - -func (st *DefaultStore) DelUser(ctx context.Context, id uint64) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - _, err = tx.ExecContext( - ctx, - `delete from t_user where id=?`, - id, - ) - if err != nil { - return err - } - - return tx.Commit() -} - -func (st *DefaultStore) GetUser(ctx context.Context, id uint64) (*db.User, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return nil, err - } - defer tx.Rollback() - - user, err := st.getUser(ctx, tx, id) - if err != nil { - return nil, err - } - - return user, err -} - -func (st *DefaultStore) GetUserByName(ctx context.Context, name string) (*db.User, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return nil, err - } - defer tx.Rollback() - - user := &db.User{} - var quotaStr, preferenceStr string - err = tx.QueryRowContext( - ctx, - `select id, name, pwd, role, used_space, quota, preference - from t_user - where name=?`, - name, - ).Scan( - &user.ID, - &user.Name, - &user.Pwd, - &user.Role, - &user.UsedSpace, - "aStr, - &preferenceStr, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, db.ErrUserNotFound - } - return nil, err - } - - err = json.Unmarshal([]byte(quotaStr), &user.Quota) - if err != nil { - return nil, err - } - err = json.Unmarshal([]byte(preferenceStr), &user.Preferences) - if err != nil { - return nil, err - } - return user, nil -} - -func (st *DefaultStore) SetPwd(ctx context.Context, id uint64, pwd string) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - _, err = tx.ExecContext( - ctx, - `update t_user - set pwd=? - where id=?`, - pwd, - id, - ) - if err != nil { - return err - } - - return tx.Commit() -} - -// role + quota -func (st *DefaultStore) SetInfo(ctx context.Context, id uint64, user *db.User) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - quotaStr, err := json.Marshal(user.Quota) - if err != nil { - return err - } - - _, err = tx.ExecContext( - ctx, - `update t_user - set role=?, quota=? - where id=?`, - user.Role, quotaStr, - id, - ) - if err != nil { - return err - } - - return tx.Commit() -} - -func (st *DefaultStore) SetPreferences(ctx context.Context, id uint64, prefers *db.Preferences) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - preferenceStr, err := json.Marshal(prefers) - if err != nil { - return err - } - - _, err = tx.ExecContext( - ctx, - `update t_user - set preference=? - where id=?`, - preferenceStr, - id, - ) - if err != nil { - return err - } - - return tx.Commit() -} - -func (st *DefaultStore) SetUsed(ctx context.Context, id uint64, incr bool, capacity int64) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - err = st.setUsed(ctx, tx, id, incr, capacity) - if err != nil { - return err - } - return tx.Commit() -} - -func (st *DefaultStore) setUsed(ctx context.Context, tx *sql.Tx, id uint64, incr bool, capacity int64) error { - gotUser, err := st.getUser(ctx, tx, id) - if err != nil { - return err - } - - if incr && gotUser.UsedSpace+capacity > int64(gotUser.Quota.SpaceLimit) { - return db.ErrReachedLimit - } - - if incr { - gotUser.UsedSpace = gotUser.UsedSpace + capacity - } else { - if gotUser.UsedSpace-capacity < 0 { - return db.ErrNegtiveUsedSpace - } - gotUser.UsedSpace = gotUser.UsedSpace - capacity - } - - _, err = tx.ExecContext( - ctx, - `update t_user - set used_space=? - where id=?`, - gotUser.UsedSpace, - gotUser.ID, - ) - return err -} - -func (st *DefaultStore) ResetUsed(ctx context.Context, id uint64, used int64) error { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return err - } - defer tx.Rollback() - - _, err = tx.ExecContext( - ctx, - `update t_user - set used_space=? - where id=?`, - used, - id, - ) - if err != nil { - return err - } - - return tx.Commit() -} - -func (st *DefaultStore) listUsers(ctx context.Context, tx *sql.Tx) ([]*db.User, error) { - // TODO: support pagination - rows, err := tx.QueryContext( - ctx, - `select id, name, role, used_space, quota, preference - from t_user`, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, db.ErrUserNotFound - } - return nil, err - } - defer rows.Close() // TODO: check error - - users := []*db.User{} - for rows.Next() { - user := &db.User{} - var quotaStr, preferenceStr string - err = rows.Scan( - &user.ID, - &user.Name, - &user.Role, - &user.UsedSpace, - "aStr, - &preferenceStr, - ) - err = json.Unmarshal([]byte(quotaStr), &user.Quota) - if err != nil { - return nil, err - } - err = json.Unmarshal([]byte(preferenceStr), &user.Preferences) - if err != nil { - return nil, err - } - - users = append(users, user) - } - if rows.Err() != nil { - return nil, rows.Err() - } - return users, nil -} - -func (st *DefaultStore) ListUsers(ctx context.Context) ([]*db.User, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return nil, err - } - defer tx.Rollback() - - return st.listUsers(ctx, tx) -} - -func (st *DefaultStore) ListUserIDs(ctx context.Context) (map[string]string, error) { - tx, err := st.db.BeginTx(ctx, &sql.TxOptions{}) - if err != nil { - return nil, err - } - defer tx.Rollback() - - users, err := st.listUsers(ctx, tx) - if err != nil { - return nil, err - } - - nameToId := map[string]string{} - for _, user := range users { - nameToId[user.Name] = fmt.Sprint(user.ID) - } - return nameToId, nil -} - -func (st *DefaultStore) AddRole(role string) error { - // TODO: implement this after adding grant/revoke - panic("not implemented") -} - -func (st *DefaultStore) DelRole(role string) error { - // TODO: implement this after adding grant/revoke - panic("not implemented") -} - -func (st *DefaultStore) ListRoles() (map[string]bool, error) { - // TODO: implement this after adding grant/revoke - panic("not implemented") -} diff --git a/src/db/sitestore/site_store.go b/src/db/sitestore/site_store.go deleted file mode 100644 index d8812db..0000000 --- a/src/db/sitestore/site_store.go +++ /dev/null @@ -1,101 +0,0 @@ -package sitestore - -import ( - "context" - "encoding/json" - "errors" - "sync" - - "github.com/ihexxa/quickshare/src/db" - "github.com/ihexxa/quickshare/src/kvstore" -) - -const ( - NsSite = "NsSite" - KeySiteCfg = "KeySiteCfg" -) - -var ( - ErrNotFound = errors.New("site config not found") -) - -type ISiteStore interface { - SetClientCfg(cfg *db.ClientConfig) error - GetCfg() (*db.SiteConfig, error) -} - -type SiteStore struct { - mtx *sync.RWMutex - store kvstore.IKVStore -} - -func NewSiteStore(store kvstore.IKVStore) (*SiteStore, error) { - return &SiteStore{ - store: store, - mtx: &sync.RWMutex{}, - }, nil -} - -func (st *SiteStore) Init(cfg *db.SiteConfig) error { - _, ok := st.store.GetStringIn(NsSite, KeySiteCfg) - if !ok { - var err error - if err = st.store.AddNamespace(NsSite); err != nil { - return err - } - - return st.setCfg(cfg) - } - return nil -} - -func (st *SiteStore) getCfg() (*db.SiteConfig, error) { - cfgStr, ok := st.store.GetStringIn(NsSite, KeySiteCfg) - if !ok { - return nil, ErrNotFound - } - - cfg := &db.SiteConfig{} - err := json.Unmarshal([]byte(cfgStr), cfg) - if err != nil { - return nil, err - } - - if err = db.CheckSiteCfg(cfg, true); err != nil { - return nil, err - } - - return cfg, nil -} - -func (st *SiteStore) setCfg(cfg *db.SiteConfig) error { - if err := db.CheckSiteCfg(cfg, false); err != nil { - return err - } - - cfgBytes, err := json.Marshal(cfg) - if err != nil { - return err - } - return st.store.SetStringIn(NsSite, KeySiteCfg, string(cfgBytes)) -} - -func (st *SiteStore) SetClientCfg(ctx context.Context, cfg *db.ClientConfig) error { - st.mtx.Lock() - defer st.mtx.Unlock() - - siteCfg, err := st.getCfg() - if err != nil { - return err - } - siteCfg.ClientCfg = cfg - - return st.setCfg(siteCfg) -} - -func (st *SiteStore) GetCfg(ctx context.Context) (*db.SiteConfig, error) { - st.mtx.RLock() - defer st.mtx.RUnlock() - - return st.getCfg() -} diff --git a/src/db/sitestore/site_store_test.go b/src/db/sitestore/site_store_test.go deleted file mode 100644 index ff757ae..0000000 --- a/src/db/sitestore/site_store_test.go +++ /dev/null @@ -1,175 +0,0 @@ -package sitestore - -import ( - "encoding/json" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "testing" - - "github.com/ihexxa/quickshare/src/db" - "github.com/ihexxa/quickshare/src/kvstore/boltdbpvd" -) - -func TestSiteStore(t *testing.T) { - - testSiteMethods := func(t *testing.T, store ISiteStore) { - siteCfg := &db.SiteConfig{ - ClientCfg: &db.ClientConfig{ - SiteName: "quickshare", - SiteDesc: "simpel file sharing", - AllowSetBg: true, - AutoTheme: true, - Bg: &db.BgConfig{ - Url: "/imgs/bg.jpg", - Repeat: "no-repeat", - Position: "center", - Align: "fixed", - BgColor: "#ccc", - }, - }, - } - - err := store.SetClientCfg(siteCfg.ClientCfg) - if err != nil { - t.Fatal(err) - } - newSiteCfg, err := store.GetCfg() - if err != nil { - t.Fatal(err) - } else if !reflect.DeepEqual(newSiteCfg, siteCfg) { - t.Fatalf("not equal new(%v) original(%v)", newSiteCfg, siteCfg) - } - } - - t.Run("Get/Set config", func(t *testing.T) { - rootPath, err := ioutil.TempDir("./", "quickshare_sitestore_test_") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(rootPath) - - dbPath := filepath.Join(rootPath, "quickshare.db") - kvstore := boltdbpvd.New(dbPath, 1024) - defer kvstore.Close() - - store, err := NewSiteStore(kvstore) - if err != nil { - t.Fatal("fail to new kvstore", err) - } - err = store.Init(&db.SiteConfig{ - ClientCfg: &db.ClientConfig{ - SiteName: "", - SiteDesc: "", - AllowSetBg: true, - AutoTheme: false, - Bg: &db.BgConfig{ - Url: "/imgs/bg.jpg", - Repeat: "repeat", - Position: "top", - Align: "scroll", - BgColor: "#000", - }, - }, - }) - if err != nil { - panic(err) - } - - testSiteMethods(t, store) - }) - - testMigrations := func(t *testing.T, store ISiteStore) { - autoFilledCfg := &db.SiteConfig{ - ClientCfg: &db.ClientConfig{ - SiteName: "Quickshare", - SiteDesc: "Quickshare", - AllowSetBg: false, - AutoTheme: false, - Bg: &db.BgConfig{ - Url: "/imgs/bg.jpg", - Repeat: "repeat", - Position: "top", - Align: "scroll", - BgColor: "#000", - }, - }, - } - - siteCfg := &db.SiteConfig{ - ClientCfg: &db.ClientConfig{ - SiteName: "quickshare", - SiteDesc: "simpel file sharing", - AllowSetBg: true, - AutoTheme: true, - Bg: &db.BgConfig{ - Url: "/imgs/bg.jpg", - Repeat: "no-repeat", - Position: "center", - Align: "fixed", - BgColor: "#ccc", - }, - }, - } - - oldSiteCfg, err := store.GetCfg() - if err != nil { - t.Fatal(err) - } else if !reflect.DeepEqual(oldSiteCfg, autoFilledCfg) { - oldSiteCfgBytes, _ := json.Marshal(oldSiteCfg) - autoFilledCfgBytes, _ := json.Marshal(autoFilledCfg) - t.Fatalf("not equal old \n%s\n filled\n%s\n", oldSiteCfgBytes, autoFilledCfgBytes) - } - - err = store.SetClientCfg(siteCfg.ClientCfg) - if err != nil { - t.Fatal(err) - } - newSiteCfg, err := store.GetCfg() - if err != nil { - t.Fatal(err) - } else if !reflect.DeepEqual(newSiteCfg, siteCfg) { - t.Fatalf("not equal new(%v) original(%v)", newSiteCfg, siteCfg) - } - } - - t.Run("Test Migrations", func(t *testing.T) { - rootPath, err := ioutil.TempDir("./", "quickshare_sitestore_test_") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(rootPath) - - dbPath := filepath.Join(rootPath, "quickshare.db") - kvstore := boltdbpvd.New(dbPath, 1024) - defer kvstore.Close() - - store, err := NewSiteStore(kvstore) - if err != nil { - t.Fatal("fail to new kvstore", err) - } - - // this config does not contain some fields - err = store.Init(&db.SiteConfig{ - ClientCfg: &db.ClientConfig{ - SiteName: "", - SiteDesc: "", - // AllowSetBg: true, - // AutoTheme: false, - Bg: &db.BgConfig{ - Url: "/imgs/bg.jpg", - Repeat: "repeat", - Position: "top", - Align: "scroll", - BgColor: "#000", - }, - }, - }) - if err != nil { - panic(err) - } - - testMigrations(t, store) - }) -} diff --git a/src/db/tests/config_test.go b/src/db/tests/config_test.go index 7f44117..1b57426 100644 --- a/src/db/tests/config_test.go +++ b/src/db/tests/config_test.go @@ -10,8 +10,6 @@ import ( "github.com/ihexxa/quickshare/src/db" "github.com/ihexxa/quickshare/src/db/rdb/sqlite" - "github.com/ihexxa/quickshare/src/db/sitestore" - "github.com/ihexxa/quickshare/src/kvstore/boltdbpvd" ) var testSiteConfig = &db.SiteConfig{ @@ -61,43 +59,6 @@ func TestSiteStore(t *testing.T) { } } - t.Run("config methods basic tests - boltdb", func(t *testing.T) { - rootPath, err := ioutil.TempDir("./", "quickshare_sitestore_test_") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(rootPath) - - dbPath := filepath.Join(rootPath, "quickshare.db") - kvstore := boltdbpvd.New(dbPath, 1024) - defer kvstore.Close() - - store, err := sitestore.NewSiteStore(kvstore) - if err != nil { - t.Fatal("fail to new kvstore", err) - } - err = store.Init(&db.SiteConfig{ - ClientCfg: &db.ClientConfig{ - SiteName: "", - SiteDesc: "", - AllowSetBg: true, - AutoTheme: false, - Bg: &db.BgConfig{ - Url: "/imgs/bg.jpg", - Repeat: "repeat", - Position: "top", - Align: "scroll", - BgColor: "#000", - }, - }, - }) - if err != nil { - panic(err) - } - - testConfigMethods(t, store) - }) - t.Run("config methods basic tests - sqlite", func(t *testing.T) { rootPath, err := ioutil.TempDir("./", "qs_sqlite_config_") if err != nil { diff --git a/src/db/userstore/user_store.go b/src/db/userstore/user_store.go deleted file mode 100644 index b2bc5b7..0000000 --- a/src/db/userstore/user_store.go +++ /dev/null @@ -1,375 +0,0 @@ -package userstore - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "sync" - "time" - - "github.com/ihexxa/quickshare/src/db" - "github.com/ihexxa/quickshare/src/kvstore" -) - -// TODO: use sync.Pool instead - -const ( - VisitorID = uint64(1) - VisitorName = "visitor" -) - -var ( - ErrReachedLimit = errors.New("reached space limit") - ErrUserNotFound = errors.New("user not found") - ErrNegtiveUsedSpace = errors.New("used space can not be negative") -) - -type KVUserStore struct { - store kvstore.IKVStore - mtx *sync.RWMutex -} - -func NewKVUserStore(store kvstore.IKVStore) (*KVUserStore, error) { - return &KVUserStore{ - store: store, - mtx: &sync.RWMutex{}, - }, nil -} - -func (us *KVUserStore) Init(ctx context.Context, rootName, rootPwd string) error { - var err error - - for _, namespace := range []string{ - db.UserSchemaNs, - db.UserIDsNs, - db.UsersNs, - db.RolesNs, - } { - _, ok := us.store.GetStringIn(namespace, db.KeyInitTime) - if !ok { - if err = us.store.AddNamespace(namespace); err != nil { - return err - } - } - } - - admin := &db.User{ - ID: 0, - Name: rootName, - Pwd: rootPwd, - Role: db.AdminRole, - Quota: &db.Quota{ - SpaceLimit: db.DefaultSpaceLimit, - UploadSpeedLimit: db.DefaultUploadSpeedLimit, - DownloadSpeedLimit: db.DefaultDownloadSpeedLimit, - }, - Preferences: &db.DefaultPreferences, - } - - visitor := &db.User{ - ID: VisitorID, - Name: VisitorName, - Pwd: rootPwd, - Role: db.VisitorRole, - Quota: &db.Quota{ - SpaceLimit: 0, - UploadSpeedLimit: db.VisitorUploadSpeedLimit, - DownloadSpeedLimit: db.VisitorDownloadSpeedLimit, - }, - Preferences: &db.DefaultPreferences, - } - - for _, user := range []*db.User{admin, visitor} { - err = us.AddUser(context.TODO(), user) - if err != nil { - return err - } - } - - for _, role := range []string{db.AdminRole, db.UserRole, db.VisitorRole} { - err = us.AddRole(role) - if err != nil { - return err - } - } - - return us.store.SetStringIn(db.UserSchemaNs, db.KeyInitTime, fmt.Sprintf("%d", time.Now().Unix())) -} - -func (us *KVUserStore) IsInited() bool { - _, ok := us.store.GetStringIn(db.UserSchemaNs, db.KeyInitTime) - return ok -} - -func (us *KVUserStore) setUser(user *db.User) error { - var err error - - if err = db.CheckUser(user, false); err != nil { - return err - } - - userID := fmt.Sprint(user.ID) - err = us.store.SetStringIn(db.UserIDsNs, user.Name, userID) - if err != nil { - return err - } - userBytes, err := json.Marshal(user) - if err != nil { - return err - } - return us.store.SetStringIn(db.UsersNs, userID, string(userBytes)) -} - -func (us *KVUserStore) getUser(id uint64) (*db.User, error) { - userID := fmt.Sprint(id) - userBytes, ok := us.store.GetStringIn(db.UsersNs, userID) - if !ok { - return nil, ErrUserNotFound - } - - user := &db.User{} - err := json.Unmarshal([]byte(userBytes), user) - if err != nil { - return nil, err - } - - if err = db.CheckUser(user, true); err != nil { - return nil, err - } - return user, nil -} - -func (us *KVUserStore) getUserByName(name string) (*db.User, error) { - userID, ok := us.store.GetStringIn(db.UserIDsNs, name) - if !ok { - return nil, ErrUserNotFound - } - - userBytes, ok := us.store.GetStringIn(db.UsersNs, userID) - if !ok { - return nil, ErrUserNotFound - } - - user := &db.User{} - err := json.Unmarshal([]byte(userBytes), user) - if err != nil { - return nil, err - } - - if err = db.CheckUser(user, true); err != nil { - return nil, err - } - return user, nil -} - -func (us *KVUserStore) AddUser(ctx context.Context, user *db.User) error { - us.mtx.Lock() - defer us.mtx.Unlock() - - return us.setUser(user) -} - -func (us *KVUserStore) DelUser(ctx context.Context, id uint64) error { - us.mtx.Lock() - defer us.mtx.Unlock() - - user, err := us.getUser(id) - if err != nil { - return err - } - - // TODO: add complement operations if part of the actions fails - err1 := us.store.DelStringIn(db.UserIDsNs, user.Name) - err2 := us.store.DelStringIn(db.UsersNs, fmt.Sprint(user.ID)) - if err1 != nil || err2 != nil { - return fmt.Errorf("DelUser: err1(%s) err2(%s)", err1, err2) - } - return nil -} - -func (us *KVUserStore) GetUser(ctx context.Context, id uint64) (*db.User, error) { - us.mtx.RLock() - defer us.mtx.RUnlock() - - return us.getUser(id) -} - -func (us *KVUserStore) GetUserByName(ctx context.Context, name string) (*db.User, error) { - us.mtx.RLock() - defer us.mtx.RUnlock() - - return us.getUserByName(name) -} - -func (us *KVUserStore) SetPwd(ctx context.Context, id uint64, pwd string) error { - us.mtx.Lock() - defer us.mtx.Unlock() - - user, err := us.getUser(id) - if err != nil { - return err - } - - user.Pwd = pwd - return us.setUser(user) -} - -func (us *KVUserStore) SetInfo(ctx context.Context, id uint64, user *db.User) error { - us.mtx.Lock() - defer us.mtx.Unlock() - - gotUser, err := us.getUser(id) - if err != nil { - return err - } - - gotUser.Role = user.Role - gotUser.Quota = user.Quota - gotUser.UsedSpace = user.UsedSpace - return us.setUser(gotUser) -} - -func (us *KVUserStore) SetPreferences(ctx context.Context, id uint64, prefers *db.Preferences) error { - us.mtx.Lock() - defer us.mtx.Unlock() - - user, err := us.getUser(id) - if err != nil { - return err - } - - user.Preferences = prefers - return us.setUser(user) -} - -func (us *KVUserStore) SetUsed(ctx context.Context, id uint64, incr bool, capacity int64) error { - us.mtx.Lock() - defer us.mtx.Unlock() - - gotUser, err := us.getUser(id) - if err != nil { - return err - } - - if incr && gotUser.UsedSpace+capacity > int64(gotUser.Quota.SpaceLimit) { - return ErrReachedLimit - } - - if incr { - gotUser.UsedSpace = gotUser.UsedSpace + capacity - } else { - if gotUser.UsedSpace-capacity < 0 { - return ErrNegtiveUsedSpace - } - gotUser.UsedSpace = gotUser.UsedSpace - capacity - } - - return us.setUser(gotUser) -} - -func (us *KVUserStore) ResetUsed(ctx context.Context, id uint64, used int64) error { - us.mtx.Lock() - defer us.mtx.Unlock() - - gotUser, err := us.getUser(id) - if err != nil { - return err - } - - gotUser.UsedSpace = used - return us.setUser(gotUser) -} - -func (us *KVUserStore) ListUsers(ctx context.Context) ([]*db.User, error) { - us.mtx.RLock() - defer us.mtx.RUnlock() - - idToInfo, err := us.store.ListStringsIn(db.UsersNs) - if err != nil { - return nil, err - } - nameToID, err := us.store.ListStringsIn(db.UserIDsNs) - if err != nil { - return nil, err - } - - users := []*db.User{} - for _, infoStr := range idToInfo { - user := &db.User{} - err = json.Unmarshal([]byte(infoStr), user) - if err != nil { - return nil, err - } - user.Pwd = "" - - if err = db.CheckUser(user, true); err != nil { - return nil, err - } - users = append(users, user) - } - - // redundant check - if len(idToInfo) != len(nameToID) { - if len(idToInfo) > len(nameToID) { - for _, user := range users { - _, ok := nameToID[user.Name] - if !ok { - err = us.store.DelStringIn(db.UsersNs, fmt.Sprint(user.ID)) - if err != nil { - return nil, err - } - } - } - } else { - for name, id := range nameToID { - _, ok := idToInfo[id] - if !ok { - err = us.store.DelStringIn(db.UserIDsNs, name) - if err != nil { - return nil, err - } - } - } - } - } - - return users, nil -} - -func (us *KVUserStore) ListUserIDs(ctx context.Context) (map[string]string, error) { - us.mtx.RLock() - defer us.mtx.RUnlock() - - return us.store.ListStringsIn(db.UserIDsNs) -} - -func (us *KVUserStore) AddRole(role string) error { - us.mtx.Lock() - defer us.mtx.Unlock() - - _, ok := us.store.GetBoolIn(db.RolesNs, role) - if ok { - return fmt.Errorf("role (%s) exists", role) - } - - return us.store.SetBoolIn(db.RolesNs, role, true) -} - -func (us *KVUserStore) DelRole(role string) error { - us.mtx.Lock() - defer us.mtx.Unlock() - - if role == db.AdminRole || role == db.UserRole || role == db.VisitorRole { - return errors.New("predefined roles can not be deleted") - } - - return us.store.DelBoolIn(db.RolesNs, role) -} - -func (us *KVUserStore) ListRoles() (map[string]bool, error) { - us.mtx.Lock() - defer us.mtx.Unlock() - - return us.store.ListBoolsIn(db.RolesNs) -} diff --git a/src/handlers/fileshdr/handlers.go b/src/handlers/fileshdr/handlers.go index 3d5da31..5c4b81f 100644 --- a/src/handlers/fileshdr/handlers.go +++ b/src/handlers/fileshdr/handlers.go @@ -20,7 +20,6 @@ import ( "github.com/ihexxa/fsearch" "github.com/ihexxa/quickshare/src/db" - "github.com/ihexxa/quickshare/src/db/userstore" "github.com/ihexxa/quickshare/src/depidx" q "github.com/ihexxa/quickshare/src/handlers" "github.com/ihexxa/quickshare/src/worker/localworker" @@ -679,7 +678,7 @@ func (h *FileHandlers) Download(c *gin.Context) { dirPath := filepath.Dir(filePath) var err error - userId := userstore.VisitorID + userId := db.VisitorID if role != db.VisitorRole { userId, err = q.GetUserId(c) if err != nil { diff --git a/src/handlers/multiusers/handlers.go b/src/handlers/multiusers/handlers.go index 00018c0..3f665a0 100644 --- a/src/handlers/multiusers/handlers.go +++ b/src/handlers/multiusers/handlers.go @@ -15,7 +15,6 @@ import ( "golang.org/x/crypto/bcrypt" "github.com/ihexxa/quickshare/src/db" - "github.com/ihexxa/quickshare/src/db/userstore" "github.com/ihexxa/quickshare/src/depidx" q "github.com/ihexxa/quickshare/src/handlers" "github.com/ihexxa/quickshare/src/worker/localworker" @@ -248,7 +247,7 @@ func (h *MultiUsersSvc) Login(c *gin.Context) { user, err := h.deps.Users().GetUserByName(c, req.User) if err != nil { - if errors.Is(err, userstore.ErrUserNotFound) { + if errors.Is(err, db.ErrUserNotFound) { c.JSON(q.ErrResp(c, 403, err)) return }