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
import (
"crypto/sha1"
"encoding/json"
"errors"
"fmt"
"io"
"strings"
"sync"
"time"
@ -13,11 +16,16 @@ import (
const (
InitNs = "Init"
InfoNs = "sharing"
ShareIDNs = "sharingKey"
InitTimeKey = "initTime"
)
var (
ErrEmpty = errors.New("can not hash empty string")
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 {
@ -27,6 +35,7 @@ func IsNotFound(err error) bool {
type FileInfo struct {
IsDir bool `json:"isDir"`
Shared bool `json:"shared"`
ShareID string `json:"shareID"` // for short url
Sha1 string `json:"sha1"`
}
@ -34,12 +43,13 @@ type IFileInfoStore interface {
AddSharing(dirPath string) error
DelSharing(dirPath string) error
GetSharing(dirPath string) (bool, bool)
ListSharings(prefix string) (map[string]bool, error)
ListSharings(prefix string) (map[string]string, error)
GetInfo(itemPath string) (*FileInfo, error)
SetInfo(itemPath string, info *FileInfo) error
DelInfo(itemPath string) error
SetSha1(itemPath, sign string) error
GetInfos(itemPaths []string) (map[string]*FileInfo, error)
GetSharingDir(hashID string) (string, error)
}
type FileInfoStore struct {
@ -54,6 +64,7 @@ func NewFileInfoStore(store kvstore.IKVStore) (*FileInfoStore, error) {
for _, nsName := range []string{
InitNs,
InfoNs,
ShareIDNs,
} {
if err = store.AddNamespace(nsName); err != nil {
return nil, err
@ -85,7 +96,19 @@ func (fi *FileInfoStore) AddSharing(dirPath string) error {
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.ShareID = shareID
return fi.SetInfo(dirPath, info)
}
@ -98,6 +121,16 @@ func (fi *FileInfoStore) DelSharing(dirPath string) error {
return err
}
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)
}
@ -105,6 +138,7 @@ 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
@ -112,21 +146,21 @@ func (fi *FileInfoStore) GetSharing(dirPath string) (bool, bool) {
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)
if err != nil {
return nil, err
}
info := &FileInfo{}
sharings := map[string]bool{}
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] = true
sharings[itemPath] = info.ShareID
}
}
@ -197,3 +231,33 @@ func (fi *FileInfoStore) SetSha1(itemPath, sign string) error {
info.Sha1 = sign
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
prefix := "admin"
sharingMap, err := store.ListSharings(prefix)
dirToID, err := store.ListSharings(prefix)
if err != nil {
t.Fatal(err)
}
for _, sharingDir := range dirPaths {
if !sharingMap[sharingDir] {
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
@ -46,18 +60,30 @@ func TestUserStores(t *testing.T) {
}
}
sharingMap, err = store.ListSharings(prefix)
dirToID, err = store.ListSharings(prefix)
if err != nil {
t.Fatal(err)
}
for _, dirPath := range dirPaths {
if _, ok := sharingMap[dirPath]; ok {
if _, ok := dirToID[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)
}
_, err = store.GetSharingDir(info.ShareID)
if err != ErrSharingNotFound {
t.Fatal("should return ErrSharingNotFound")
}
}
}