feat(fileinfostore): enable shareID

This commit is contained in:
hexxa 2022-01-15 14:33:19 +08:00 committed by Hexxa
parent 29d5f9d9e6
commit 772ec7992c
2 changed files with 102 additions and 12 deletions

View file

@ -1,9 +1,12 @@
package fileinfostore package fileinfostore
import ( import (
"crypto/sha1"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"strings"
"sync" "sync"
"time" "time"
@ -13,11 +16,16 @@ import (
const ( const (
InitNs = "Init" InitNs = "Init"
InfoNs = "sharing" InfoNs = "sharing"
ShareIDNs = "sharingKey"
InitTimeKey = "initTime" InitTimeKey = "initTime"
) )
var ( var (
ErrEmpty = errors.New("can not hash empty string")
ErrNotFound = errors.New("file info not found") ErrNotFound = errors.New("file info not found")
ErrSharingNotFound = errors.New("sharing id not found")
ErrConflicted = errors.New("conflict found in hashing")
maxHashingTime = 10
) )
func IsNotFound(err error) bool { func IsNotFound(err error) bool {
@ -27,6 +35,7 @@ func IsNotFound(err error) bool {
type FileInfo struct { type FileInfo struct {
IsDir bool `json:"isDir"` IsDir bool `json:"isDir"`
Shared bool `json:"shared"` Shared bool `json:"shared"`
ShareID string `json:"shareID"` // for short url
Sha1 string `json:"sha1"` Sha1 string `json:"sha1"`
} }
@ -34,12 +43,13 @@ type IFileInfoStore interface {
AddSharing(dirPath string) error AddSharing(dirPath string) error
DelSharing(dirPath string) error DelSharing(dirPath string) error
GetSharing(dirPath string) (bool, bool) GetSharing(dirPath string) (bool, bool)
ListSharings(prefix string) (map[string]bool, error) ListSharings(prefix string) (map[string]string, error)
GetInfo(itemPath string) (*FileInfo, error) GetInfo(itemPath string) (*FileInfo, error)
SetInfo(itemPath string, info *FileInfo) error SetInfo(itemPath string, info *FileInfo) error
DelInfo(itemPath string) error DelInfo(itemPath string) error
SetSha1(itemPath, sign string) error SetSha1(itemPath, sign string) error
GetInfos(itemPaths []string) (map[string]*FileInfo, error) GetInfos(itemPaths []string) (map[string]*FileInfo, error)
GetSharingDir(hashID string) (string, error)
} }
type FileInfoStore struct { type FileInfoStore struct {
@ -54,6 +64,7 @@ func NewFileInfoStore(store kvstore.IKVStore) (*FileInfoStore, error) {
for _, nsName := range []string{ for _, nsName := range []string{
InitNs, InitNs,
InfoNs, InfoNs,
ShareIDNs,
} { } {
if err = store.AddNamespace(nsName); err != nil { if err = store.AddNamespace(nsName); err != nil {
return nil, err return nil, err
@ -85,7 +96,19 @@ func (fi *FileInfoStore) AddSharing(dirPath string) error {
IsDir: true, IsDir: true,
} }
} }
// TODO: ensure Atomicity
shareID, err := fi.getShareID(dirPath)
if err != nil {
return err
}
err = fi.store.SetStringIn(ShareIDNs, shareID, dirPath)
if err != nil {
return err
}
info.Shared = true info.Shared = true
info.ShareID = shareID
return fi.SetInfo(dirPath, info) return fi.SetInfo(dirPath, info)
} }
@ -98,6 +121,16 @@ func (fi *FileInfoStore) DelSharing(dirPath string) error {
return err return err
} }
info.Shared = false info.Shared = false
info.ShareID = ""
// TODO: ensure Atomicity
// In the bolt, if the key does not exist
// then nothing is done and a nil error is returned
err = fi.store.DelStringIn(ShareIDNs, info.ShareID)
if err != nil {
return err
}
return fi.SetInfo(dirPath, info) return fi.SetInfo(dirPath, info)
} }
@ -105,6 +138,7 @@ func (fi *FileInfoStore) GetSharing(dirPath string) (bool, bool) {
fi.mtx.Lock() fi.mtx.Lock()
defer fi.mtx.Unlock() defer fi.mtx.Unlock()
// TODO: differentiate error and not exist
info, err := fi.GetInfo(dirPath) info, err := fi.GetInfo(dirPath)
if err != nil { if err != nil {
return false, false return false, false
@ -112,21 +146,21 @@ func (fi *FileInfoStore) GetSharing(dirPath string) (bool, bool) {
return info.IsDir && info.Shared, true return info.IsDir && info.Shared, true
} }
func (fi *FileInfoStore) ListSharings(prefix string) (map[string]bool, error) { func (fi *FileInfoStore) ListSharings(prefix string) (map[string]string, error) {
infoStrs, err := fi.store.ListStringsByPrefixIn(prefix, InfoNs) infoStrs, err := fi.store.ListStringsByPrefixIn(prefix, InfoNs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := &FileInfo{} info := &FileInfo{}
sharings := map[string]bool{} sharings := map[string]string{}
for itemPath, infoStr := range infoStrs { for itemPath, infoStr := range infoStrs {
err = json.Unmarshal([]byte(infoStr), info) err = json.Unmarshal([]byte(infoStr), info)
if err != nil { if err != nil {
return nil, fmt.Errorf("list sharing error: %w", err) return nil, fmt.Errorf("list sharing error: %w", err)
} }
if info.IsDir && info.Shared { if info.IsDir && info.Shared {
sharings[itemPath] = true sharings[itemPath] = info.ShareID
} }
} }
@ -197,3 +231,33 @@ 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) {
if len(payload) == 0 {
return "", 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]
if _, ok := fi.store.GetStringIn(ShareIDNs, shareID); !ok {
return shareID, nil
}
}
return "", ErrConflicted
}
func (fi *FileInfoStore) GetSharingDir(hashID string) (string, error) {
dirPath, ok := fi.store.GetStringIn(ShareIDNs, hashID)
if !ok {
return "", ErrSharingNotFound
}
return dirPath, nil
}

View file

@ -24,18 +24,32 @@ func TestUserStores(t *testing.T) {
// list sharings // list sharings
prefix := "admin" prefix := "admin"
sharingMap, err := store.ListSharings(prefix) dirToID, err := store.ListSharings(prefix)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
for _, sharingDir := range dirPaths { for _, sharingDir := range dirPaths {
if !sharingMap[sharingDir] { if _, ok := dirToID[sharingDir]; !ok {
t.Fatalf("sharing(%s) not found", sharingDir) t.Fatalf("sharing(%s) not found", sharingDir)
} }
mustTrue, exist := store.GetSharing(sharingDir) mustTrue, exist := store.GetSharing(sharingDir)
if !mustTrue || !exist { if !mustTrue || !exist {
t.Fatalf("get sharing(%t %t) should exist", 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 // del sharings
@ -46,18 +60,30 @@ func TestUserStores(t *testing.T) {
} }
} }
sharingMap, err = store.ListSharings(prefix) dirToID, err = store.ListSharings(prefix)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
for _, dirPath := range dirPaths { for _, dirPath := range dirPaths {
if _, ok := sharingMap[dirPath]; ok { if _, ok := dirToID[dirPath]; ok {
t.Fatalf("sharing(%s) should not exist", dirPath) t.Fatalf("sharing(%s) should not exist", dirPath)
} }
shared, exist := store.GetSharing(dirPath) shared, exist := store.GetSharing(dirPath)
if shared { if shared {
t.Fatalf("get sharing(%t, %t) should not shared but exist", shared, exist) 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)
}
_, err = store.GetSharingDir(info.ShareID)
if err != ErrSharingNotFound {
t.Fatal("should return ErrSharingNotFound")
}
} }
} }