feat(db): replace boltdb with sqlite
This commit is contained in:
parent
59a39efc4a
commit
791848f75c
16 changed files with 1749 additions and 543 deletions
|
@ -23,18 +23,38 @@ const (
|
||||||
AdminRole = "admin"
|
AdminRole = "admin"
|
||||||
UserRole = "user"
|
UserRole = "user"
|
||||||
VisitorRole = "visitor"
|
VisitorRole = "visitor"
|
||||||
|
|
||||||
|
VisitorID = uint64(1)
|
||||||
|
VisitorName = "visitor"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// users related errors
|
||||||
|
ErrReachedLimit = errors.New("reached space limit")
|
||||||
|
ErrUserNotFound = errors.New("user not found")
|
||||||
|
ErrNegtiveUsedSpace = errors.New("used space can not be negative")
|
||||||
ErrInvalidFileInfo = errors.New("invalid fileInfo")
|
ErrInvalidFileInfo = errors.New("invalid fileInfo")
|
||||||
ErrInvalidUser = errors.New("invalid user")
|
ErrInvalidUser = errors.New("invalid user")
|
||||||
ErrInvalidQuota = errors.New("invalid quota")
|
ErrInvalidQuota = errors.New("invalid quota")
|
||||||
ErrInvalidPreferences = errors.New("invalid preferences")
|
ErrInvalidPreferences = errors.New("invalid preferences")
|
||||||
ErrBucketNotFound = errors.New("bucket not found")
|
// files related errors
|
||||||
ErrKeyNotFound = errors.New("key not found")
|
ErrEmpty = errors.New("can not hash empty string")
|
||||||
ErrKeyExisting = errors.New("key is existing")
|
ErrFileInfoNotFound = errors.New("file info not found")
|
||||||
ErrCreateExisting = errors.New("create upload info which already exists")
|
ErrSharingNotFound = errors.New("sharing id not found")
|
||||||
ErrQuota = errors.New("quota limit reached")
|
ErrConflicted = errors.New("conflict found in hashing")
|
||||||
|
ErrVerNotFound = errors.New("file info schema version not found")
|
||||||
|
// uploadings
|
||||||
|
ErrGreaterThanSize = errors.New("uploaded is greater than file size")
|
||||||
|
ErrUploadNotFound = errors.New("upload info not found")
|
||||||
|
|
||||||
|
// site
|
||||||
|
ErrConfigNotFound = errors.New("site config not found")
|
||||||
|
|
||||||
|
ErrBucketNotFound = errors.New("bucket not found")
|
||||||
|
ErrKeyNotFound = errors.New("key not found")
|
||||||
|
ErrKeyExisting = errors.New("key is existing")
|
||||||
|
ErrCreateExisting = errors.New("create upload info which already exists")
|
||||||
|
ErrQuota = errors.New("quota limit reached")
|
||||||
|
|
||||||
DefaultSiteName = "Quickshare"
|
DefaultSiteName = "Quickshare"
|
||||||
DefaultSiteDesc = "Quickshare"
|
DefaultSiteDesc = "Quickshare"
|
||||||
|
@ -93,11 +113,11 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileInfo struct {
|
type FileInfo struct {
|
||||||
IsDir bool `json:"isDir" yaml:"isDir"`
|
IsDir bool `json:"isDir" yaml:"isDir"` // deprecated
|
||||||
Shared bool `json:"shared" yaml:"shared"`
|
Shared bool `json:"shared" yaml:"shared"` // deprecated
|
||||||
ShareID string `json:"shareID" yaml:"shareID"`
|
ShareID string `json:"shareID" yaml:"shareID"` // deprecated
|
||||||
Sha1 string `json:"sha1" yaml:"sha1"`
|
Sha1 string `json:"sha1" yaml:"sha1"`
|
||||||
Size int64 `json:"size" yaml:"size"`
|
Size int64 `json:"size" yaml:"size"` // deprecated
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserCfg struct {
|
type UserCfg struct {
|
||||||
|
@ -177,6 +197,30 @@ type IUserStore interface {
|
||||||
ListRoles() (map[string]bool, error)
|
ListRoles() (map[string]bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IFileInfoStore interface {
|
||||||
|
AddSharing(ctx context.Context, dirPath string) error
|
||||||
|
DelSharing(ctx context.Context, dirPath string) error
|
||||||
|
GetSharing(ctx context.Context, dirPath string) (bool, bool)
|
||||||
|
ListSharings(ctx context.Context, prefix string) (map[string]string, error)
|
||||||
|
GetFileInfo(ctx context.Context, itemPath string) (*FileInfo, error)
|
||||||
|
SetFileInfo(ctx context.Context, itemPath string, info *FileInfo) error
|
||||||
|
DelFileInfo(ctx context.Context, itemPath string) error
|
||||||
|
ListFileInfos(ctx context.Context, itemPaths []string) (map[string]*FileInfo, error)
|
||||||
|
SetSha1(ctx context.Context, itemPath, sign string) error
|
||||||
|
GetSharingDir(ctx context.Context, hashID string) (string, error)
|
||||||
|
// upload info
|
||||||
|
AddUploadInfo(ctx context.Context, user, filePath, tmpPath string, fileSize int64) error
|
||||||
|
SetUploadInfo(ctx context.Context, user, filePath string, newUploaded int64) error
|
||||||
|
GetUploadInfo(ctx context.Context, user, filePath string) (string, int64, int64, error)
|
||||||
|
DelUploadInfo(ctx context.Context, user, filePath string) error
|
||||||
|
ListUploadInfo(ctx context.Context, user string) ([]*UploadInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ISiteStore interface {
|
||||||
|
SetClientCfg(ctx context.Context, cfg *ClientConfig) error
|
||||||
|
GetCfg(ctx context.Context) (*SiteConfig, error)
|
||||||
|
}
|
||||||
|
|
||||||
func ComparePreferences(p1, p2 *Preferences) bool {
|
func ComparePreferences(p1, p2 *Preferences) bool {
|
||||||
return p1.CSSURL == p2.CSSURL &&
|
return p1.CSSURL == p2.CSSURL &&
|
||||||
p1.LanPackURL == p2.LanPackURL &&
|
p1.LanPackURL == p2.LanPackURL &&
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package fileinfostore
|
package fileinfostore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -21,12 +22,12 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrEmpty = errors.New("can not hash empty string")
|
// db.ErrEmpty = errors.New("can not hash empty string")
|
||||||
ErrNotFound = errors.New("file info not found")
|
// db.ErrFileInfoNotFound = errors.New("file info not found")
|
||||||
ErrSharingNotFound = errors.New("sharing id not found")
|
// db.ErrSharingNotFound = errors.New("sharing id not found")
|
||||||
ErrConflicted = errors.New("conflict found in hashing")
|
// db.ErrConflicted = errors.New("conflict found in hashing")
|
||||||
ErrVerNotFound = errors.New("file info schema version not found")
|
// db.ErrVerNotFound = errors.New("file info schema version not found")
|
||||||
maxHashingTime = 10
|
maxHashingTime = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
type IFileInfoStore interface {
|
type IFileInfoStore interface {
|
||||||
|
@ -77,7 +78,7 @@ func NewFileInfoStore(store kvstore.IKVStore) (*FileInfoStore, error) {
|
||||||
func (fi *FileInfoStore) getInfo(itemPath string) (*db.FileInfo, error) {
|
func (fi *FileInfoStore) getInfo(itemPath string) (*db.FileInfo, error) {
|
||||||
infoStr, ok := fi.store.GetStringIn(db.FileInfoNs, itemPath)
|
infoStr, ok := fi.store.GetStringIn(db.FileInfoNs, itemPath)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, ErrNotFound
|
return nil, db.ErrFileInfoNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
info := &db.FileInfo{}
|
info := &db.FileInfo{}
|
||||||
|
@ -92,16 +93,16 @@ func (fi *FileInfoStore) getInfo(itemPath string) (*db.FileInfo, error) {
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) GetInfo(itemPath string) (*db.FileInfo, error) {
|
func (fi *FileInfoStore) GetFileInfo(ctx context.Context, itemPath string) (*db.FileInfo, error) {
|
||||||
return fi.getInfo(itemPath)
|
return fi.getInfo(itemPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) GetInfos(itemPaths []string) (map[string]*db.FileInfo, error) {
|
func (fi *FileInfoStore) ListFileInfos(ctx context.Context, itemPaths []string) (map[string]*db.FileInfo, error) {
|
||||||
infos := map[string]*db.FileInfo{}
|
infos := map[string]*db.FileInfo{}
|
||||||
for _, itemPath := range itemPaths {
|
for _, itemPath := range itemPaths {
|
||||||
info, err := fi.getInfo(itemPath)
|
info, err := fi.getInfo(itemPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, ErrNotFound) {
|
if !errors.Is(err, db.ErrFileInfoNotFound) {
|
||||||
// TODO: try to make info data consistent with fs
|
// TODO: try to make info data consistent with fs
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -133,23 +134,23 @@ func (fi *FileInfoStore) setInfo(itemPath string, info *db.FileInfo) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) SetInfo(itemPath string, info *db.FileInfo) error {
|
func (fi *FileInfoStore) SetFileInfo(ctx context.Context, itemPath string, info *db.FileInfo) error {
|
||||||
return fi.setInfo(itemPath, info)
|
return fi.setInfo(itemPath, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) DelInfo(itemPath string) error {
|
func (fi *FileInfoStore) DelFileInfo(ctx context.Context, itemPath string) error {
|
||||||
return fi.store.DelStringIn(db.FileInfoNs, itemPath)
|
return fi.store.DelStringIn(db.FileInfoNs, itemPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sharings
|
// sharings
|
||||||
|
|
||||||
func (fi *FileInfoStore) SetSha1(itemPath, sign string) error {
|
func (fi *FileInfoStore) SetSha1(ctx context.Context, itemPath, sign string) error {
|
||||||
fi.mtx.Lock()
|
fi.mtx.Lock()
|
||||||
defer fi.mtx.Unlock()
|
defer fi.mtx.Unlock()
|
||||||
|
|
||||||
info, err := fi.getInfo(itemPath)
|
info, err := fi.getInfo(itemPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, ErrNotFound) {
|
if !errors.Is(err, db.ErrFileInfoNotFound) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
info = &db.FileInfo{
|
info = &db.FileInfo{
|
||||||
|
@ -163,7 +164,7 @@ func (fi *FileInfoStore) SetSha1(itemPath, sign string) error {
|
||||||
|
|
||||||
func (fi *FileInfoStore) getShareID(payload string) (string, error) {
|
func (fi *FileInfoStore) getShareID(payload string) (string, error) {
|
||||||
if len(payload) == 0 {
|
if len(payload) == 0 {
|
||||||
return "", ErrEmpty
|
return "", db.ErrEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < maxHashingTime; i++ {
|
for i := 0; i < maxHashingTime; i++ {
|
||||||
|
@ -183,24 +184,24 @@ func (fi *FileInfoStore) getShareID(payload string) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", ErrConflicted
|
return "", db.ErrConflicted
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) GetSharingDir(hashID string) (string, error) {
|
func (fi *FileInfoStore) GetSharingDir(ctx context.Context, hashID string) (string, error) {
|
||||||
dirPath, ok := fi.store.GetStringIn(db.ShareIDNs, hashID)
|
dirPath, ok := fi.store.GetStringIn(db.ShareIDNs, hashID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", ErrSharingNotFound
|
return "", db.ErrSharingNotFound
|
||||||
}
|
}
|
||||||
return dirPath, nil
|
return dirPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) AddSharing(dirPath string) error {
|
func (fi *FileInfoStore) AddSharing(ctx context.Context, dirPath string) error {
|
||||||
fi.mtx.Lock()
|
fi.mtx.Lock()
|
||||||
defer fi.mtx.Unlock()
|
defer fi.mtx.Unlock()
|
||||||
|
|
||||||
info, err := fi.getInfo(dirPath)
|
info, err := fi.getInfo(dirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, ErrNotFound) {
|
if !errors.Is(err, db.ErrFileInfoNotFound) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
info = &db.FileInfo{
|
info = &db.FileInfo{
|
||||||
|
@ -223,7 +224,7 @@ func (fi *FileInfoStore) AddSharing(dirPath string) error {
|
||||||
return fi.setInfo(dirPath, info)
|
return fi.setInfo(dirPath, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) DelSharing(dirPath string) error {
|
func (fi *FileInfoStore) DelSharing(ctx context.Context, dirPath string) error {
|
||||||
fi.mtx.Lock()
|
fi.mtx.Lock()
|
||||||
defer fi.mtx.Unlock()
|
defer fi.mtx.Unlock()
|
||||||
|
|
||||||
|
@ -257,7 +258,7 @@ func (fi *FileInfoStore) DelSharing(dirPath string) error {
|
||||||
return fi.setInfo(dirPath, info)
|
return fi.setInfo(dirPath, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) GetSharing(dirPath string) (bool, bool) {
|
func (fi *FileInfoStore) GetSharing(ctx context.Context, dirPath string) (bool, bool) {
|
||||||
fi.mtx.Lock()
|
fi.mtx.Lock()
|
||||||
defer fi.mtx.Unlock()
|
defer fi.mtx.Unlock()
|
||||||
|
|
||||||
|
@ -269,7 +270,7 @@ 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]string, error) {
|
func (fi *FileInfoStore) ListSharings(ctx context.Context, prefix string) (map[string]string, error) {
|
||||||
infoStrs, err := fi.store.ListStringsByPrefixIn(prefix, db.FileInfoNs)
|
infoStrs, err := fi.store.ListStringsByPrefixIn(prefix, db.FileInfoNs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package fileinfostore
|
package fileinfostore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ func (fi *FileInfoStore) setUploadInfo(user, filePath string, info *db.UploadInf
|
||||||
return fi.store.SetStringIn(db.UploadNS(user), filePath, string(newInfoBytes))
|
return fi.store.SetStringIn(db.UploadNS(user), filePath, string(newInfoBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) AddUploadInfo(user, filePath, tmpPath string, fileSize int64) error {
|
func (fi *FileInfoStore) AddUploadInfo(ctx context.Context, user, filePath, tmpPath string, fileSize int64) error {
|
||||||
fi.mtx.Lock()
|
fi.mtx.Lock()
|
||||||
defer fi.mtx.Unlock()
|
defer fi.mtx.Unlock()
|
||||||
|
|
||||||
|
@ -58,7 +59,7 @@ func (fi *FileInfoStore) AddUploadInfo(user, filePath, tmpPath string, fileSize
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) SetUploadInfo(user, filePath string, newUploaded int64) error {
|
func (fi *FileInfoStore) SetUploadInfo(ctx context.Context, user, filePath string, newUploaded int64) error {
|
||||||
fi.mtx.Lock()
|
fi.mtx.Lock()
|
||||||
defer fi.mtx.Unlock()
|
defer fi.mtx.Unlock()
|
||||||
|
|
||||||
|
@ -76,18 +77,18 @@ func (fi *FileInfoStore) SetUploadInfo(user, filePath string, newUploaded int64)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) GetUploadInfo(user, filePath string) (string, int64, int64, error) {
|
func (fi *FileInfoStore) GetUploadInfo(ctx context.Context, user, filePath string) (string, int64, int64, error) {
|
||||||
fi.mtx.Lock()
|
fi.mtx.Lock()
|
||||||
defer fi.mtx.Unlock()
|
defer fi.mtx.Unlock()
|
||||||
|
|
||||||
return fi.getUploadInfo(user, filePath)
|
return fi.getUploadInfo(user, filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) DelUploadInfo(user, filePath string) error {
|
func (fi *FileInfoStore) DelUploadInfo(ctx context.Context, user, filePath string) error {
|
||||||
return fi.store.DelInt64In(db.UploadNS(user), filePath)
|
return fi.store.DelInt64In(db.UploadNS(user), filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *FileInfoStore) ListUploadInfo(user string) ([]*db.UploadInfo, error) {
|
func (fi *FileInfoStore) ListUploadInfo(ctx context.Context, user string) ([]*db.UploadInfo, error) {
|
||||||
ns := db.UploadNS(user)
|
ns := db.UploadNS(user)
|
||||||
if !fi.store.HasNamespace(ns) {
|
if !fi.store.HasNamespace(ns) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
92
src/db/interface.go
Normal file
92
src/db/interface.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: expose more APIs if needed
|
||||||
|
type IDB interface {
|
||||||
|
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
|
||||||
|
Close() error
|
||||||
|
PingContext(ctx context.Context) error
|
||||||
|
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
|
||||||
|
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
|
||||||
|
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
|
||||||
|
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
|
||||||
|
// Conn(ctx context.Context) (*Conn, error)
|
||||||
|
// Driver() driver.Driver
|
||||||
|
// SetConnMaxIdleTime(d time.Duration)
|
||||||
|
// SetConnMaxLifetime(d time.Duration)
|
||||||
|
// SetMaxIdleConns(n int)
|
||||||
|
// SetMaxOpenConns(n int)
|
||||||
|
// Stats() DBStats
|
||||||
|
}
|
||||||
|
|
||||||
|
type IDBFunctions interface {
|
||||||
|
Init(ctx context.Context, adminName, adminPwd string, config *SiteConfig) error
|
||||||
|
IDBLockable
|
||||||
|
IUserDB
|
||||||
|
IFileDB
|
||||||
|
IUploadDB
|
||||||
|
ISharingDB
|
||||||
|
IConfigDB
|
||||||
|
}
|
||||||
|
|
||||||
|
type IDBLockable interface {
|
||||||
|
Lock()
|
||||||
|
Unlock()
|
||||||
|
RLock()
|
||||||
|
RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
type IUserDB interface {
|
||||||
|
IsInited() bool
|
||||||
|
AddUser(ctx context.Context, user *User) error
|
||||||
|
DelUser(ctx context.Context, id uint64) error
|
||||||
|
GetUser(ctx context.Context, id uint64) (*User, error)
|
||||||
|
GetUserByName(ctx context.Context, name string) (*User, error)
|
||||||
|
SetPwd(ctx context.Context, id uint64, pwd string) error
|
||||||
|
SetInfo(ctx context.Context, id uint64, user *User) error
|
||||||
|
SetPreferences(ctx context.Context, id uint64, prefers *Preferences) error
|
||||||
|
SetUsed(ctx context.Context, id uint64, incr bool, capacity int64) error
|
||||||
|
ResetUsed(ctx context.Context, id uint64, used int64) error
|
||||||
|
ListUsers(ctx context.Context) ([]*User, error)
|
||||||
|
ListUserIDs(ctx context.Context) (map[string]string, error)
|
||||||
|
AddRole(role string) error
|
||||||
|
DelRole(role string) error
|
||||||
|
ListRoles() (map[string]bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IFileDB interface {
|
||||||
|
AddFileInfo(ctx context.Context, userId uint64, itemPath string, info *FileInfo) error
|
||||||
|
// DelFileInfo(ctx context.Context, itemPath string) error
|
||||||
|
DelFileInfo(ctx context.Context, userId uint64, itemPath string) error
|
||||||
|
GetFileInfo(ctx context.Context, userId uint64, itemPath string) (*FileInfo, error)
|
||||||
|
SetSha1(ctx context.Context, userId uint64, itemPath, sign string) error
|
||||||
|
MoveFileInfos(ctx context.Context, userID uint64, oldPath, newPath string, isDir bool) error
|
||||||
|
ListFileInfos(ctx context.Context, itemPaths []string) (map[string]*FileInfo, error)
|
||||||
|
}
|
||||||
|
type IUploadDB interface {
|
||||||
|
AddUploadInfos(ctx context.Context, userId uint64, tmpPath, filePath string, info *FileInfo) error
|
||||||
|
DelUploadingInfos(ctx context.Context, userId uint64, realPath string) error
|
||||||
|
// MoveUploadingInfos(ctx context.Context, userId uint64, uploadPath, itemPath string) error
|
||||||
|
SetUploadInfo(ctx context.Context, user uint64, filePath string, newUploaded int64) error
|
||||||
|
GetUploadInfo(ctx context.Context, userId uint64, filePath string) (string, int64, int64, error)
|
||||||
|
ListUploadInfos(ctx context.Context, user uint64) ([]*UploadInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ISharingDB interface {
|
||||||
|
IsSharing(ctx context.Context, userId uint64, dirPath string) bool
|
||||||
|
GetSharingDir(ctx context.Context, hashID string) (string, error)
|
||||||
|
AddSharing(ctx context.Context, userId uint64, dirPath string) error
|
||||||
|
DelSharing(ctx context.Context, userId uint64, dirPath string) error
|
||||||
|
ListUserSharings(ctx context.Context, userId uint64) (map[string]string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IConfigDB interface {
|
||||||
|
SetClientCfg(ctx context.Context, cfg *ClientConfig) error
|
||||||
|
GetCfg(ctx context.Context) (*SiteConfig, error)
|
||||||
|
}
|
|
@ -1,25 +0,0 @@
|
||||||
package rdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: expose more APIs if needed
|
|
||||||
type IDB interface {
|
|
||||||
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
|
|
||||||
Close() error
|
|
||||||
PingContext(ctx context.Context) error
|
|
||||||
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
|
|
||||||
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
|
|
||||||
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
|
|
||||||
QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
|
|
||||||
// Conn(ctx context.Context) (*Conn, error)
|
|
||||||
// Driver() driver.Driver
|
|
||||||
// SetConnMaxIdleTime(d time.Duration)
|
|
||||||
// SetConnMaxLifetime(d time.Duration)
|
|
||||||
// SetMaxIdleConns(n int)
|
|
||||||
// SetMaxOpenConns(n int)
|
|
||||||
// Stats() DBStats
|
|
||||||
}
|
|
72
src/db/rdb/sqlite/configs.go
Normal file
72
src/db/rdb/sqlite/configs.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package sqlite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/ihexxa/quickshare/src/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (st *SQLiteStore) getCfg(ctx context.Context) (*db.SiteConfig, error) {
|
||||||
|
var configStr string
|
||||||
|
err := st.db.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 *SQLiteStore) setCfg(ctx context.Context, 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 = st.db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
`update t_config
|
||||||
|
set config=?
|
||||||
|
where id=0`,
|
||||||
|
string(cfgBytes),
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) SetClientCfg(ctx context.Context, cfg *db.ClientConfig) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
siteCfg, err := st.getCfg(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
siteCfg.ClientCfg = cfg
|
||||||
|
|
||||||
|
return st.setCfg(ctx, siteCfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) GetCfg(ctx context.Context) (*db.SiteConfig, error) {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
|
||||||
|
return st.getCfg(ctx)
|
||||||
|
}
|
276
src/db/rdb/sqlite/files.go
Normal file
276
src/db/rdb/sqlite/files.go
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
package sqlite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ihexxa/quickshare/src/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
InitNs = "Init"
|
||||||
|
InitTimeKey = "initTime"
|
||||||
|
SchemaVerKey = "SchemaVersion"
|
||||||
|
SchemaV1 = "v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
maxHashingTime = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
func (st *SQLiteStore) getFileInfo(ctx context.Context, userId uint64, itemPath string) (*db.FileInfo, error) {
|
||||||
|
var infoStr string
|
||||||
|
fInfo := &db.FileInfo{}
|
||||||
|
var isDir bool
|
||||||
|
var size int64
|
||||||
|
var shareId string
|
||||||
|
err := st.db.QueryRowContext(
|
||||||
|
ctx,
|
||||||
|
`select is_dir, size, share_id, info
|
||||||
|
from t_file_info
|
||||||
|
where path=? and user=?
|
||||||
|
`,
|
||||||
|
itemPath,
|
||||||
|
userId,
|
||||||
|
).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 *SQLiteStore) GetFileInfo(ctx context.Context, userId uint64, itemPath string) (*db.FileInfo, error) {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
|
||||||
|
return st.getFileInfo(ctx, userId, itemPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) ListFileInfos(ctx context.Context, itemPaths []string) (map[string]*db.FileInfo, error) {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
|
||||||
|
// TODO: add pagination
|
||||||
|
placeholders := []string{}
|
||||||
|
values := []any{}
|
||||||
|
for i := 0; i < len(itemPaths); i++ {
|
||||||
|
placeholders = append(placeholders, "?")
|
||||||
|
values = append(values, itemPaths[i])
|
||||||
|
}
|
||||||
|
rows, err := st.db.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 *SQLiteStore) addFileInfo(ctx context.Context, userId uint64, itemPath string, info *db.FileInfo) error {
|
||||||
|
infoStr, err := json.Marshal(info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dirPath, itemName := path.Split(itemPath)
|
||||||
|
_, err = st.db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
`insert into t_file_info
|
||||||
|
(path, user, parent, name, is_dir, size, share_id, info) values (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
itemPath,
|
||||||
|
userId,
|
||||||
|
dirPath,
|
||||||
|
itemName,
|
||||||
|
info.IsDir,
|
||||||
|
info.Size,
|
||||||
|
info.ShareID,
|
||||||
|
infoStr,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) AddFileInfo(ctx context.Context, userId uint64, itemPath string, info *db.FileInfo) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
err := st.addFileInfo(ctx, userId, itemPath, info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// increase used space
|
||||||
|
return st.setUsed(ctx, userId, true, info.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) delFileInfo(ctx context.Context, userId uint64, itemPath string) error {
|
||||||
|
_, err := st.db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
`delete from t_file_info
|
||||||
|
where path=? and user=?
|
||||||
|
`,
|
||||||
|
itemPath,
|
||||||
|
userId,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (st *SQLiteStore) DelFileInfo(ctx context.Context, itemPath string) error {
|
||||||
|
// st.Lock()
|
||||||
|
// defer st.Unlock()
|
||||||
|
// return st.delFileInfo(ctx, itemPath)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// sharings
|
||||||
|
|
||||||
|
func (st *SQLiteStore) SetSha1(ctx context.Context, userId uint64, itemPath, sign string) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
info, err := st.getFileInfo(ctx, userId, itemPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info.Sha1 = sign
|
||||||
|
|
||||||
|
infoStr, err := json.Marshal(info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = st.db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
`update t_file_info
|
||||||
|
set info=?
|
||||||
|
where path=? and user=?`,
|
||||||
|
infoStr,
|
||||||
|
itemPath,
|
||||||
|
userId,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) DelFileInfo(ctx context.Context, userID uint64, itemPath string) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
// get all children and size
|
||||||
|
rows, err := st.db.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, userID, false, decrSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete file info entries
|
||||||
|
_, err = st.db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
fmt.Sprintf(
|
||||||
|
`delete from t_file_info
|
||||||
|
where path in (%s)`,
|
||||||
|
strings.Join(placeholders, ","),
|
||||||
|
),
|
||||||
|
values...,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) MoveFileInfos(ctx context.Context, userId uint64, oldPath, newPath string, isDir bool) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
info, err := st.getFileInfo(ctx, userId, oldPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = st.delFileInfo(ctx, userId, oldPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return st.addFileInfo(ctx, userId, newPath, info)
|
||||||
|
}
|
156
src/db/rdb/sqlite/files_sharings.go
Normal file
156
src/db/rdb/sqlite/files_sharings.go
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
package sqlite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha1"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ihexxa/quickshare/src/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (st *SQLiteStore) 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 *SQLiteStore) IsSharing(ctx context.Context, userId uint64, dirPath string) bool {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
|
||||||
|
// TODO: differentiate error and not exist
|
||||||
|
info, err := st.getFileInfo(ctx, userId, dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return info.ShareID != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) GetSharingDir(ctx context.Context, hashID string) (string, error) {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
|
||||||
|
var sharedPath string
|
||||||
|
err := st.db.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 *SQLiteStore) AddSharing(ctx context.Context, userId uint64, dirPath string) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
shareID, err := st.generateShareID(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = st.getFileInfo(ctx, userId, 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 = st.db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
`insert into t_file_info
|
||||||
|
(path, user, parent, name, is_dir, size, share_id, info) values (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
dirPath, userId, parentPath, name, true, 0, shareID, infoStr,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = st.db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
`update t_file_info
|
||||||
|
set share_id=?
|
||||||
|
where path=? and user=?`,
|
||||||
|
shareID, dirPath, userId,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) DelSharing(ctx context.Context, userId uint64, dirPath string) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
_, err := st.db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
`update t_file_info
|
||||||
|
set share_id=''
|
||||||
|
where path=? and user=?`,
|
||||||
|
dirPath,
|
||||||
|
userId,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) ListUserSharings(ctx context.Context, userId uint64) (map[string]string, error) {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
|
||||||
|
rows, err := st.db.QueryContext(
|
||||||
|
ctx,
|
||||||
|
`select path, share_id
|
||||||
|
from t_file_info
|
||||||
|
where user=? and share_id <> ''`,
|
||||||
|
userId,
|
||||||
|
)
|
||||||
|
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
|
||||||
|
}
|
192
src/db/rdb/sqlite/files_uploadings.go
Normal file
192
src/db/rdb/sqlite/files_uploadings.go
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
package sqlite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/ihexxa/quickshare/src/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (st *SQLiteStore) addUploadInfoOnly(ctx context.Context, userId uint64, filePath, tmpPath string, fileSize int64) error {
|
||||||
|
_, err := st.db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
`insert into t_file_uploading
|
||||||
|
(real_path, tmp_path, user, size, uploaded) values (?, ?, ?, ?, ?)`,
|
||||||
|
filePath, tmpPath, userId, fileSize, 0,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) AddUploadInfos(ctx context.Context, userId uint64, tmpPath, filePath string, info *db.FileInfo) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
userInfo, err := st.getUser(ctx, userId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if userInfo.UsedSpace+info.Size > int64(userInfo.Quota.SpaceLimit) {
|
||||||
|
return db.ErrQuota
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, _, err = st.getUploadInfo(ctx, 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, userInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return st.addUploadInfoOnly(ctx, userId, filePath, tmpPath, info.Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) DelUploadingInfos(ctx context.Context, userId uint64, realPath string) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
return st.delUploadingInfos(ctx, userId, realPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) delUploadingInfos(ctx context.Context, userId uint64, realPath string) error {
|
||||||
|
_, size, _, err := st.getUploadInfo(ctx, userId, realPath)
|
||||||
|
if err != nil {
|
||||||
|
// info may not exist
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = st.delUploadInfoOnly(ctx, userId, realPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfo, err := st.getUser(ctx, userId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
userInfo.UsedSpace -= size
|
||||||
|
return st.setUser(ctx, userInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) delUploadInfoOnly(ctx context.Context, userId uint64, filePath string) error {
|
||||||
|
_, err := st.db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
`delete from t_file_uploading
|
||||||
|
where real_path=? and user=?`,
|
||||||
|
filePath, userId,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (st *SQLiteStore) MoveUploadingInfos(ctx context.Context, userId uint64, uploadPath, itemPath string) error {
|
||||||
|
// st.Lock()
|
||||||
|
// defer st.Unlock()
|
||||||
|
|
||||||
|
// _, size, _, err := st.getUploadInfo(ctx, userId, itemPath)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// err = st.delUploadInfoOnly(ctx, userId, itemPath)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// return st.addFileInfo(ctx, userId, itemPath, &db.FileInfo{
|
||||||
|
// Size: size,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (st *SQLiteStore) SetUploadInfo(ctx context.Context, userId uint64, filePath string, newUploaded int64) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
var size, uploaded int64
|
||||||
|
err := st.db.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 = st.db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
`update t_file_uploading
|
||||||
|
set uploaded=?
|
||||||
|
where real_path=? and user=?`,
|
||||||
|
newUploaded, filePath, userId,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) getUploadInfo(ctx context.Context, userId uint64, filePath string) (string, int64, int64, error) {
|
||||||
|
var size, uploaded int64
|
||||||
|
err := st.db.QueryRowContext(
|
||||||
|
ctx,
|
||||||
|
`select size, uploaded
|
||||||
|
from t_file_uploading
|
||||||
|
where user=? and real_path=?`,
|
||||||
|
userId, filePath,
|
||||||
|
).Scan(&size, &uploaded)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePath, size, uploaded, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) GetUploadInfo(ctx context.Context, userId uint64, filePath string) (string, int64, int64, error) {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
return st.getUploadInfo(ctx, userId, filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) ListUploadInfos(ctx context.Context, userId uint64) ([]*db.UploadInfo, error) {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
|
||||||
|
rows, err := st.db.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
|
||||||
|
}
|
|
@ -1,16 +1,21 @@
|
||||||
package sqlite
|
package sqlite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ihexxa/quickshare/src/db/rdb"
|
"github.com/ihexxa/quickshare/src/db"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SQLite struct {
|
type SQLite struct {
|
||||||
rdb.IDB
|
db.IDB
|
||||||
dbPath string
|
dbPath string
|
||||||
|
mtx *sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSQLite(dbPath string) (*SQLite, error) {
|
func NewSQLite(dbPath string) (*SQLite, error) {
|
||||||
|
@ -24,3 +29,159 @@ func NewSQLite(dbPath string) (*SQLite, error) {
|
||||||
dbPath: dbPath,
|
dbPath: dbPath,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSQLiteStore(db db.IDB) (*SQLiteStore, error) {
|
||||||
|
return &SQLiteStore{
|
||||||
|
db: db,
|
||||||
|
mtx: &sync.RWMutex{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) Lock() {
|
||||||
|
st.mtx.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) Unlock() {
|
||||||
|
st.mtx.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) RLock() {
|
||||||
|
st.mtx.RLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) RUnlock() {
|
||||||
|
st.mtx.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) IsInited() bool {
|
||||||
|
// always try to init the db
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) 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 *SQLiteStore) 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,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 *SQLiteStore) 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,
|
||||||
|
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 table if not exists t_file_uploading (
|
||||||
|
real_path varchar not null,
|
||||||
|
tmp_path varchar not null,
|
||||||
|
user bigint not null,
|
||||||
|
size bigint not null,
|
||||||
|
uploaded bigint not null,
|
||||||
|
primary key(real_path)
|
||||||
|
)`,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *SQLiteStore) 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
|
||||||
|
}
|
||||||
|
|
|
@ -5,97 +5,18 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
// "errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
// "sync"
|
"sync"
|
||||||
// "time"
|
|
||||||
|
|
||||||
"github.com/ihexxa/quickshare/src/db"
|
"github.com/ihexxa/quickshare/src/db"
|
||||||
"github.com/ihexxa/quickshare/src/db/rdb"
|
|
||||||
// "github.com/ihexxa/quickshare/src/kvstore"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: use sync.Pool instead
|
type SQLiteStore struct {
|
||||||
|
db db.IDB
|
||||||
const (
|
mtx *sync.RWMutex
|
||||||
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 SQLiteUsers struct {
|
|
||||||
db rdb.IDB
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSQLiteUsers(db rdb.IDB) (*SQLiteUsers, error) {
|
func (st *SQLiteStore) setUser(ctx context.Context, user *db.User) error {
|
||||||
return &SQLiteUsers{db: db}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *SQLiteUsers) Init(ctx context.Context, rootName, rootPwd string) error {
|
|
||||||
_, err := u.db.ExecContext(
|
|
||||||
ctx,
|
|
||||||
`create table if not exists t_user (
|
|
||||||
id bigint not null,
|
|
||||||
name varchar not null,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = u.AddUser(ctx, user)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *SQLiteUsers) IsInited() bool {
|
|
||||||
// always try to init the db
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// t_users
|
|
||||||
// id, name, pwd, role, used_space, config
|
|
||||||
func (u *SQLiteUsers) setUser(ctx context.Context, tx *sql.Tx, user *db.User) error {
|
|
||||||
var err error
|
var err error
|
||||||
if err = db.CheckUser(user, false); err != nil {
|
if err = db.CheckUser(user, false); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -109,7 +30,7 @@ func (u *SQLiteUsers) setUser(ctx context.Context, tx *sql.Tx, user *db.User) er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = tx.ExecContext(
|
_, err = st.db.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
`update t_user
|
`update t_user
|
||||||
set name=?, pwd=?, role=?, used_space=?, quota=?, preference=?
|
set name=?, pwd=?, role=?, used_space=?, quota=?, preference=?
|
||||||
|
@ -120,16 +41,15 @@ func (u *SQLiteUsers) setUser(ctx context.Context, tx *sql.Tx, user *db.User) er
|
||||||
user.UsedSpace,
|
user.UsedSpace,
|
||||||
quotaStr,
|
quotaStr,
|
||||||
preferencesStr,
|
preferencesStr,
|
||||||
|
user.ID,
|
||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) getUser(ctx context.Context, tx *sql.Tx, id uint64) (*db.User, error) {
|
func (st *SQLiteStore) getUser(ctx context.Context, id uint64) (*db.User, error) {
|
||||||
var err error
|
|
||||||
|
|
||||||
user := &db.User{}
|
user := &db.User{}
|
||||||
var quotaStr, preferenceStr string
|
var quotaStr, preferenceStr string
|
||||||
err = tx.QueryRowContext(
|
err := st.db.QueryRowContext(
|
||||||
ctx,
|
ctx,
|
||||||
`select id, name, pwd, role, used_space, quota, preference
|
`select id, name, pwd, role, used_space, quota, preference
|
||||||
from t_user
|
from t_user
|
||||||
|
@ -146,7 +66,7 @@ func (u *SQLiteUsers) getUser(ctx context.Context, tx *sql.Tx, id uint64) (*db.U
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, ErrUserNotFound
|
return nil, db.ErrUserNotFound
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -162,7 +82,10 @@ func (u *SQLiteUsers) getUser(ctx context.Context, tx *sql.Tx, id uint64) (*db.U
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) AddUser(ctx context.Context, user *db.User) error {
|
func (st *SQLiteStore) AddUser(ctx context.Context, user *db.User) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
quotaStr, err := json.Marshal(user.Quota)
|
quotaStr, err := json.Marshal(user.Quota)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -171,7 +94,7 @@ func (u *SQLiteUsers) AddUser(ctx context.Context, user *db.User) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = u.db.ExecContext(
|
_, err = st.db.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
`insert into t_user (id, name, pwd, role, used_space, quota, preference) values (?, ?, ?, ?, ?, ?, ?)`,
|
`insert into t_user (id, name, pwd, role, used_space, quota, preference) values (?, ?, ?, ?, ?, ?, ?)`,
|
||||||
user.ID,
|
user.ID,
|
||||||
|
@ -185,8 +108,11 @@ func (u *SQLiteUsers) AddUser(ctx context.Context, user *db.User) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) DelUser(ctx context.Context, id uint64) error {
|
func (st *SQLiteStore) DelUser(ctx context.Context, id uint64) error {
|
||||||
_, err := u.db.ExecContext(
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
_, err := st.db.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
`delete from t_user where id=?`,
|
`delete from t_user where id=?`,
|
||||||
id,
|
id,
|
||||||
|
@ -194,36 +120,34 @@ func (u *SQLiteUsers) DelUser(ctx context.Context, id uint64) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) GetUser(ctx context.Context, id uint64) (*db.User, error) {
|
func (st *SQLiteStore) GetUser(ctx context.Context, id uint64) (*db.User, error) {
|
||||||
tx, err := u.db.BeginTx(ctx, &sql.TxOptions{})
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
|
||||||
|
user, err := st.getUser(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := u.getUser(ctx, tx, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.Commit()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) GetUserByName(ctx context.Context, name string) (*db.User, error) {
|
func (st *SQLiteStore) GetUserByName(ctx context.Context, name string) (*db.User, error) {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
|
||||||
user := &db.User{}
|
user := &db.User{}
|
||||||
var quotaStr, preferenceStr string
|
var quotaStr, preferenceStr string
|
||||||
err := u.db.QueryRowContext(
|
err := st.db.QueryRowContext(
|
||||||
ctx,
|
ctx,
|
||||||
`select id, name, role, used_space, quota, preference
|
`select id, name, pwd, role, used_space, quota, preference
|
||||||
from t_user
|
from t_user
|
||||||
where name=?`,
|
where name=?`,
|
||||||
name,
|
name,
|
||||||
).Scan(
|
).Scan(
|
||||||
&user.ID,
|
&user.ID,
|
||||||
&user.Name,
|
&user.Name,
|
||||||
|
&user.Pwd,
|
||||||
&user.Role,
|
&user.Role,
|
||||||
&user.UsedSpace,
|
&user.UsedSpace,
|
||||||
"aStr,
|
"aStr,
|
||||||
|
@ -231,7 +155,7 @@ func (u *SQLiteUsers) GetUserByName(ctx context.Context, name string) (*db.User,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, ErrUserNotFound
|
return nil, db.ErrUserNotFound
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -247,8 +171,11 @@ func (u *SQLiteUsers) GetUserByName(ctx context.Context, name string) (*db.User,
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) SetPwd(ctx context.Context, id uint64, pwd string) error {
|
func (st *SQLiteStore) SetPwd(ctx context.Context, id uint64, pwd string) error {
|
||||||
_, err := u.db.ExecContext(
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
_, err := st.db.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
`update t_user
|
`update t_user
|
||||||
set pwd=?
|
set pwd=?
|
||||||
|
@ -260,13 +187,16 @@ func (u *SQLiteUsers) SetPwd(ctx context.Context, id uint64, pwd string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// role + quota
|
// role + quota
|
||||||
func (u *SQLiteUsers) SetInfo(ctx context.Context, id uint64, user *db.User) error {
|
func (st *SQLiteStore) SetInfo(ctx context.Context, id uint64, user *db.User) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
quotaStr, err := json.Marshal(user.Quota)
|
quotaStr, err := json.Marshal(user.Quota)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = u.db.ExecContext(
|
_, err = st.db.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
`update t_user
|
`update t_user
|
||||||
set role=?, quota=?
|
set role=?, quota=?
|
||||||
|
@ -277,13 +207,16 @@ func (u *SQLiteUsers) SetInfo(ctx context.Context, id uint64, user *db.User) err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) SetPreferences(ctx context.Context, id uint64, prefers *db.Preferences) error {
|
func (st *SQLiteStore) SetPreferences(ctx context.Context, id uint64, prefers *db.Preferences) error {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
preferenceStr, err := json.Marshal(prefers)
|
preferenceStr, err := json.Marshal(prefers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = u.db.ExecContext(
|
_, err = st.db.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
`update t_user
|
`update t_user
|
||||||
set preference=?
|
set preference=?
|
||||||
|
@ -294,31 +227,32 @@ func (u *SQLiteUsers) SetPreferences(ctx context.Context, id uint64, prefers *db
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) SetUsed(ctx context.Context, id uint64, incr bool, capacity int64) error {
|
func (st *SQLiteStore) SetUsed(ctx context.Context, id uint64, incr bool, capacity int64) error {
|
||||||
tx, err := u.db.BeginTx(ctx, &sql.TxOptions{})
|
st.Lock()
|
||||||
if err != nil {
|
defer st.Unlock()
|
||||||
return err
|
return st.setUsed(ctx, id, incr, capacity)
|
||||||
}
|
}
|
||||||
|
|
||||||
gotUser, err := u.getUser(ctx, tx, id)
|
func (st *SQLiteStore) setUsed(ctx context.Context, id uint64, incr bool, capacity int64) error {
|
||||||
|
gotUser, err := st.getUser(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if incr && gotUser.UsedSpace+capacity > int64(gotUser.Quota.SpaceLimit) {
|
if incr && gotUser.UsedSpace+capacity > int64(gotUser.Quota.SpaceLimit) {
|
||||||
return ErrReachedLimit
|
return db.ErrReachedLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
if incr {
|
if incr {
|
||||||
gotUser.UsedSpace = gotUser.UsedSpace + capacity
|
gotUser.UsedSpace = gotUser.UsedSpace + capacity
|
||||||
} else {
|
} else {
|
||||||
if gotUser.UsedSpace-capacity < 0 {
|
if gotUser.UsedSpace-capacity < 0 {
|
||||||
return ErrNegtiveUsedSpace
|
return db.ErrNegtiveUsedSpace
|
||||||
}
|
}
|
||||||
gotUser.UsedSpace = gotUser.UsedSpace - capacity
|
gotUser.UsedSpace = gotUser.UsedSpace - capacity
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.ExecContext(
|
_, err = st.db.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
`update t_user
|
`update t_user
|
||||||
set used_space=?
|
set used_space=?
|
||||||
|
@ -330,11 +264,14 @@ func (u *SQLiteUsers) SetUsed(ctx context.Context, id uint64, incr bool, capacit
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.Commit()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) ResetUsed(ctx context.Context, id uint64, used int64) error {
|
func (st *SQLiteStore) ResetUsed(ctx context.Context, id uint64, used int64) error {
|
||||||
_, err := u.db.ExecContext(
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
|
||||||
|
_, err := st.db.ExecContext(
|
||||||
ctx,
|
ctx,
|
||||||
`update t_user
|
`update t_user
|
||||||
set used_space=?
|
set used_space=?
|
||||||
|
@ -345,16 +282,19 @@ func (u *SQLiteUsers) ResetUsed(ctx context.Context, id uint64, used int64) erro
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) ListUsers(ctx context.Context) ([]*db.User, error) {
|
func (st *SQLiteStore) ListUsers(ctx context.Context) ([]*db.User, error) {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
|
||||||
// TODO: support pagination
|
// TODO: support pagination
|
||||||
rows, err := u.db.QueryContext(
|
rows, err := st.db.QueryContext(
|
||||||
ctx,
|
ctx,
|
||||||
`select id, name, role, used_space, quota, preference
|
`select id, name, role, used_space, quota, preference
|
||||||
from t_user`,
|
from t_user`,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, ErrUserNotFound
|
return nil, db.ErrUserNotFound
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -389,8 +329,11 @@ func (u *SQLiteUsers) ListUsers(ctx context.Context) ([]*db.User, error) {
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) ListUserIDs(ctx context.Context) (map[string]string, error) {
|
func (st *SQLiteStore) ListUserIDs(ctx context.Context) (map[string]string, error) {
|
||||||
users, err := u.ListUsers(ctx)
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
|
||||||
|
users, err := st.ListUsers(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -402,17 +345,17 @@ func (u *SQLiteUsers) ListUserIDs(ctx context.Context) (map[string]string, error
|
||||||
return nameToId, nil
|
return nameToId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) AddRole(role string) error {
|
func (st *SQLiteStore) AddRole(role string) error {
|
||||||
// TODO: implement this after adding grant/revoke
|
// TODO: implement this after adding grant/revoke
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) DelRole(role string) error {
|
func (st *SQLiteStore) DelRole(role string) error {
|
||||||
// TODO: implement this after adding grant/revoke
|
// TODO: implement this after adding grant/revoke
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *SQLiteUsers) ListRoles() (map[string]bool, error) {
|
func (st *SQLiteStore) ListRoles() (map[string]bool, error) {
|
||||||
// TODO: implement this after adding grant/revoke
|
// TODO: implement this after adding grant/revoke
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package sitestore
|
package sitestore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -79,7 +80,7 @@ func (st *SiteStore) setCfg(cfg *db.SiteConfig) error {
|
||||||
return st.store.SetStringIn(NsSite, KeySiteCfg, string(cfgBytes))
|
return st.store.SetStringIn(NsSite, KeySiteCfg, string(cfgBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *SiteStore) SetClientCfg(cfg *db.ClientConfig) error {
|
func (st *SiteStore) SetClientCfg(ctx context.Context, cfg *db.ClientConfig) error {
|
||||||
st.mtx.Lock()
|
st.mtx.Lock()
|
||||||
defer st.mtx.Unlock()
|
defer st.mtx.Unlock()
|
||||||
|
|
||||||
|
@ -92,7 +93,7 @@ func (st *SiteStore) SetClientCfg(cfg *db.ClientConfig) error {
|
||||||
return st.setCfg(siteCfg)
|
return st.setCfg(siteCfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *SiteStore) GetCfg() (*db.SiteConfig, error) {
|
func (st *SiteStore) GetCfg(ctx context.Context) (*db.SiteConfig, error) {
|
||||||
st.mtx.RLock()
|
st.mtx.RLock()
|
||||||
defer st.mtx.RUnlock()
|
defer st.mtx.RUnlock()
|
||||||
|
|
||||||
|
|
126
src/db/tests/config_test.go
Normal file
126
src/db/tests/config_test.go
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"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{
|
||||||
|
ClientCfg: &db.ClientConfig{
|
||||||
|
SiteName: "",
|
||||||
|
SiteDesc: "",
|
||||||
|
AllowSetBg: true,
|
||||||
|
AutoTheme: false,
|
||||||
|
Bg: &db.BgConfig{
|
||||||
|
Url: "/imgs/bg.jpg",
|
||||||
|
Repeat: "repeat",
|
||||||
|
Position: "top",
|
||||||
|
Align: "scroll",
|
||||||
|
BgColor: "#000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSiteStore(t *testing.T) {
|
||||||
|
testConfigMethods := func(t *testing.T, store db.IConfigDB) {
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.TODO()
|
||||||
|
err := store.SetClientCfg(ctx, siteCfg.ClientCfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
newSiteCfg, err := store.GetCfg(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if !reflect.DeepEqual(newSiteCfg, siteCfg) {
|
||||||
|
t.Fatalf("not equal new(%v) original(%v)", newSiteCfg, siteCfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(rootPath)
|
||||||
|
|
||||||
|
dbPath := filepath.Join(rootPath, "quickshare.sqlite")
|
||||||
|
sqliteDB, err := sqlite.NewSQLite(dbPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer sqliteDB.Close()
|
||||||
|
|
||||||
|
store, err := sqlite.NewSQLiteStore(sqliteDB)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("fail to new sqlite store", err)
|
||||||
|
}
|
||||||
|
err = store.Init(context.TODO(), "admin", "adminPwd", testSiteConfig)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testConfigMethods(t, store)
|
||||||
|
})
|
||||||
|
}
|
446
src/db/tests/files_test.go
Normal file
446
src/db/tests/files_test.go
Normal file
|
@ -0,0 +1,446 @@
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ihexxa/quickshare/src/db"
|
||||||
|
"github.com/ihexxa/quickshare/src/db/rdb/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IFilesFunctions interface {
|
||||||
|
db.IFileDB
|
||||||
|
db.IUploadDB
|
||||||
|
db.ISharingDB
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileStore(t *testing.T) {
|
||||||
|
testSharingMethods := func(t *testing.T, store db.IDBFunctions) {
|
||||||
|
dirPaths := []string{"admin/path1", "admin/path1/path2"}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := context.TODO()
|
||||||
|
adminId := uint64(0)
|
||||||
|
|
||||||
|
// add some of paths...
|
||||||
|
err = store.AddFileInfo(ctx, adminId, "admin/path1", &db.FileInfo{
|
||||||
|
// Shared: false, // deprecated
|
||||||
|
IsDir: false,
|
||||||
|
Size: int64(0),
|
||||||
|
ShareID: "",
|
||||||
|
Sha1: "",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add sharings
|
||||||
|
for _, dirPath := range dirPaths {
|
||||||
|
err = store.AddSharing(ctx, adminId, dirPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// list sharings
|
||||||
|
dirToID, err := store.ListUserSharings(ctx, adminId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(dirToID) != len(dirPaths) {
|
||||||
|
t.Fatal("share size not match")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sharingDir := range dirPaths {
|
||||||
|
if _, ok := dirToID[sharingDir]; !ok {
|
||||||
|
t.Fatalf("sharing(%s) not found", sharingDir)
|
||||||
|
}
|
||||||
|
mustTrue := store.IsSharing(ctx, adminId, sharingDir)
|
||||||
|
if !mustTrue {
|
||||||
|
t.Fatalf("get sharing(%t) should exist", mustTrue)
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := store.GetFileInfo(ctx, adminId, sharingDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(info.ShareID) != 7 {
|
||||||
|
t.Fatalf("incorrect ShareID %s", info.ShareID)
|
||||||
|
}
|
||||||
|
|
||||||
|
gotSharingDir, err := store.GetSharingDir(ctx, 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(ctx, adminId, dirPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// list sharings
|
||||||
|
dirToIDAfterDel, err := store.ListUserSharings(ctx, adminId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(dirToIDAfterDel) != 0 {
|
||||||
|
t.Fatalf("share size not match (%+v)", dirToIDAfterDel)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dirPath := range dirPaths {
|
||||||
|
if _, ok := dirToIDAfterDel[dirPath]; ok {
|
||||||
|
t.Fatalf("sharing(%s) should not exist", dirPath)
|
||||||
|
}
|
||||||
|
shared := store.IsSharing(ctx, adminId, dirPath)
|
||||||
|
if shared {
|
||||||
|
t.Fatalf("get sharing(%t) should not shared but exist", shared)
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := store.GetFileInfo(ctx, adminId, 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(ctx, originalShareID)
|
||||||
|
if !errors.Is(err, db.ErrSharingNotFound) {
|
||||||
|
t.Fatal("should return ErrSharingNotFound")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testFileInfoMethods := func(t *testing.T, store db.IDBFunctions) {
|
||||||
|
pathInfos := map[string]*db.FileInfo{
|
||||||
|
"admin/origin/item1": &db.FileInfo{
|
||||||
|
// Shared: false, // deprecated
|
||||||
|
IsDir: false,
|
||||||
|
Size: int64(7),
|
||||||
|
ShareID: "",
|
||||||
|
Sha1: "item1_sha",
|
||||||
|
},
|
||||||
|
"admin/origin/item2": &db.FileInfo{
|
||||||
|
// Shared: false, // deprecated
|
||||||
|
IsDir: false,
|
||||||
|
Size: int64(3),
|
||||||
|
ShareID: "",
|
||||||
|
Sha1: "item2_sha",
|
||||||
|
},
|
||||||
|
"admin/origin/dir": &db.FileInfo{
|
||||||
|
// Shared: true, // deprecated
|
||||||
|
IsDir: true,
|
||||||
|
Size: int64(0),
|
||||||
|
ShareID: "mockedShareID",
|
||||||
|
Sha1: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
adminId := uint64(0)
|
||||||
|
ctx := context.TODO()
|
||||||
|
// add infos
|
||||||
|
usedSpace := int64(0)
|
||||||
|
itemPaths := []string{}
|
||||||
|
for itemPath, info := range pathInfos {
|
||||||
|
err = store.AddFileInfo(ctx, adminId, itemPath, info)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
usedSpace += info.Size
|
||||||
|
itemPaths = append(itemPaths, itemPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get infos
|
||||||
|
for itemPath, expected := range pathInfos {
|
||||||
|
info, err := store.GetFileInfo(ctx, adminId, itemPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if info.ShareID != expected.ShareID ||
|
||||||
|
info.IsDir != expected.IsDir ||
|
||||||
|
info.Sha1 != expected.Sha1 ||
|
||||||
|
info.Size != expected.Size {
|
||||||
|
t.Fatalf("info not equaled (%v) (%v)", expected, info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// list infos
|
||||||
|
pathToInfo, err := store.ListFileInfos(ctx, itemPaths)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(pathToInfo) != len(pathInfos) {
|
||||||
|
t.Fatalf("list result size not match (%v) (%d)", pathToInfo, len(pathInfos))
|
||||||
|
}
|
||||||
|
for pathname, info := range pathInfos {
|
||||||
|
gotInfo, ok := pathToInfo[pathname]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("path not found (%s)", pathname)
|
||||||
|
}
|
||||||
|
if info.ShareID != gotInfo.ShareID ||
|
||||||
|
info.IsDir != gotInfo.IsDir ||
|
||||||
|
info.Sha1 != gotInfo.Sha1 ||
|
||||||
|
info.Size != gotInfo.Size {
|
||||||
|
t.Fatalf("info not equaled (%v) (%v)", gotInfo, info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set sha1
|
||||||
|
testSha1 := "sha1"
|
||||||
|
for itemPath := range pathInfos {
|
||||||
|
err := store.SetSha1(ctx, adminId, itemPath, testSha1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
info, err := store.GetFileInfo(ctx, adminId, itemPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if info.Sha1 != testSha1 {
|
||||||
|
t.Fatalf("sha not equaled (%v) (%v)", info.Sha1, testSha1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// move paths
|
||||||
|
newPaths := []string{}
|
||||||
|
for itemPath, info := range pathInfos {
|
||||||
|
newItemPath := strings.ReplaceAll(itemPath, "origin", "new")
|
||||||
|
err = store.MoveFileInfos(ctx, adminId, itemPath, newItemPath, info.IsDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
newPaths = append(newPaths, newItemPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// list infos
|
||||||
|
pathToInfo, err = store.ListFileInfos(ctx, newPaths)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(pathToInfo) != len(pathInfos) {
|
||||||
|
t.Fatalf("list result size not match (%v) (%d)", pathToInfo, len(pathInfos))
|
||||||
|
}
|
||||||
|
|
||||||
|
// check used space
|
||||||
|
adminInfo, err := store.GetUser(ctx, adminId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if adminInfo.UsedSpace != usedSpace {
|
||||||
|
t.Fatalf("used space not match (%d) (%d)", adminInfo.UsedSpace, usedSpace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// del info
|
||||||
|
for _, itemPath := range newPaths {
|
||||||
|
err = store.DelFileInfo(ctx, adminId, itemPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check used space
|
||||||
|
adminInfo, err = store.GetUser(ctx, adminId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if adminInfo.UsedSpace != int64(0) {
|
||||||
|
t.Fatalf("used space not match (%d) (%d)", adminInfo.UsedSpace, int64(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// list infos
|
||||||
|
pathToInfo, err = store.ListFileInfos(ctx, itemPaths)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(pathToInfo) != 0 {
|
||||||
|
t.Fatalf("list result should be empty (%v)", pathToInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
for itemPath := range pathInfos {
|
||||||
|
_, err := store.GetFileInfo(ctx, adminId, itemPath)
|
||||||
|
if !errors.Is(err, db.ErrFileInfoNotFound) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testUploadingMethods := func(t *testing.T, store db.IDBFunctions) {
|
||||||
|
pathInfos := map[string]*db.FileInfo{
|
||||||
|
"admin/origin/item1": &db.FileInfo{
|
||||||
|
// Shared: false, // deprecated
|
||||||
|
IsDir: false,
|
||||||
|
Size: int64(7),
|
||||||
|
ShareID: "",
|
||||||
|
Sha1: "",
|
||||||
|
},
|
||||||
|
"admin/origin/item2": &db.FileInfo{
|
||||||
|
// Shared: false, // deprecated
|
||||||
|
IsDir: false,
|
||||||
|
Size: int64(3),
|
||||||
|
ShareID: "",
|
||||||
|
Sha1: "",
|
||||||
|
},
|
||||||
|
"admin/origin/to_delete/item3": &db.FileInfo{
|
||||||
|
// Shared: false, // deprecated
|
||||||
|
IsDir: false,
|
||||||
|
Size: int64(11),
|
||||||
|
ShareID: "",
|
||||||
|
Sha1: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
adminId := uint64(0)
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
// add infos
|
||||||
|
usedSpace := int64(0)
|
||||||
|
usedSpaceAfterDeleting := int64(0)
|
||||||
|
itemPaths := []string{}
|
||||||
|
pathToTmpPath := map[string]string{}
|
||||||
|
for itemPath, info := range pathInfos {
|
||||||
|
tmpPath := strings.ReplaceAll(itemPath, "origin", "uploads")
|
||||||
|
pathToTmpPath[itemPath] = tmpPath
|
||||||
|
err = store.AddUploadInfos(ctx, adminId, tmpPath, itemPath, info)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
usedSpace += info.Size
|
||||||
|
if !strings.Contains(itemPath, "delete") {
|
||||||
|
usedSpaceAfterDeleting += info.Size
|
||||||
|
}
|
||||||
|
itemPaths = append(itemPaths, itemPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get infos
|
||||||
|
for itemPath, info := range pathInfos {
|
||||||
|
gotPath, size, uploaded, err := store.GetUploadInfo(ctx, adminId, itemPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if size != info.Size ||
|
||||||
|
uploaded != int64(0) ||
|
||||||
|
gotPath != itemPath {
|
||||||
|
t.Fatalf("info not equaled (%v)", info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// list infos
|
||||||
|
uploadInfos, err := store.ListUploadInfos(ctx, adminId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(uploadInfos) != len(pathInfos) {
|
||||||
|
t.Fatalf("list result size not match (%v) (%d)", uploadInfos, len(pathInfos))
|
||||||
|
}
|
||||||
|
for _, uploadInfo := range uploadInfos {
|
||||||
|
expected, ok := pathInfos[uploadInfo.RealFilePath]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("path not found (%s)", uploadInfo.RealFilePath)
|
||||||
|
}
|
||||||
|
if uploadInfo.Uploaded != int64(0) ||
|
||||||
|
expected.Size != uploadInfo.Size {
|
||||||
|
t.Fatalf("info not equaled (%d) (%d)", uploadInfo.Uploaded, uploadInfo.Size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check used space
|
||||||
|
adminInfo, err := store.GetUser(ctx, adminId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if adminInfo.UsedSpace != usedSpace {
|
||||||
|
t.Fatalf("used space not match (%d) (%d)", adminInfo.UsedSpace, usedSpace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set uploading
|
||||||
|
for itemPath, info := range pathInfos {
|
||||||
|
err := store.SetUploadInfo(ctx, adminId, itemPath, int64(info.Size/2))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
gotPath, size, uploaded, err := store.GetUploadInfo(ctx, adminId, itemPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if gotPath != itemPath || size != info.Size || uploaded != info.Size/2 {
|
||||||
|
t.Fatal("uploaded info not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check used space
|
||||||
|
adminInfo, err = store.GetUser(ctx, adminId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if adminInfo.UsedSpace != usedSpace {
|
||||||
|
t.Fatalf("used space not match (%d) (%d)", adminInfo.UsedSpace, usedSpace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// del info
|
||||||
|
for itemPath := range pathInfos {
|
||||||
|
err = store.DelUploadingInfos(ctx, adminId, itemPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check used space
|
||||||
|
adminInfo, err = store.GetUser(ctx, adminId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if adminInfo.UsedSpace != int64(0) {
|
||||||
|
t.Fatalf("used space not match (%d) (%d)", adminInfo.UsedSpace, int64(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// list infos
|
||||||
|
uploadInfos, err = store.ListUploadInfos(ctx, adminId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(uploadInfos) != 0 {
|
||||||
|
t.Fatalf("list result size not match (%v) (%d)", uploadInfos, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
for itemPath := range pathInfos {
|
||||||
|
_, _, _, err := store.GetUploadInfo(ctx, adminId, itemPath)
|
||||||
|
if !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("testing file info store - sqlite", func(t *testing.T) {
|
||||||
|
rootPath, err := ioutil.TempDir("./", "qs_sqlite_test_")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(rootPath)
|
||||||
|
|
||||||
|
dbPath := filepath.Join(rootPath, "files.sqlite")
|
||||||
|
sqliteDB, err := sqlite.NewSQLite(dbPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer sqliteDB.Close()
|
||||||
|
|
||||||
|
store, err := sqlite.NewSQLiteStore(sqliteDB)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("fail to new sqlite store", err)
|
||||||
|
}
|
||||||
|
err = store.Init(context.TODO(), "admin", "1234", testSiteConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("fail to init", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testSharingMethods(t, store)
|
||||||
|
testFileInfoMethods(t, store)
|
||||||
|
testUploadingMethods(t, store)
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package sqlite
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -7,16 +7,20 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ihexxa/quickshare/src/db"
|
"github.com/ihexxa/quickshare/src/db"
|
||||||
|
"github.com/ihexxa/quickshare/src/db/rdb/sqlite"
|
||||||
|
// "github.com/ihexxa/quickshare/src/db/userstore"
|
||||||
|
// "github.com/ihexxa/quickshare/src/kvstore/boltdbpvd"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUserStores(t *testing.T) {
|
func TestUserStore(t *testing.T) {
|
||||||
rootName, rootPwd := "root", "rootPwd"
|
rootName, rootPwd := "root", "rootPwd"
|
||||||
|
|
||||||
testUserMethods := func(t *testing.T, store db.IUserStore) {
|
testUserMethods := func(t *testing.T, store db.IUserDB) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
// check root
|
||||||
root, err := store.GetUser(ctx, 0)
|
root, err := store.GetUser(ctx, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -43,11 +47,12 @@ func TestUserStores(t *testing.T) {
|
||||||
t.Fatalf("incorrect preference %v %v", root.Preferences, db.DefaultPreferences)
|
t.Fatalf("incorrect preference %v %v", root.Preferences, db.DefaultPreferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check visitor
|
||||||
visitor, err := store.GetUser(ctx, 1)
|
visitor, err := store.GetUser(ctx, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if visitor.Name != VisitorName {
|
if visitor.Name != db.VisitorName {
|
||||||
t.Fatal("visitor not found")
|
t.Fatal("visitor not found")
|
||||||
}
|
}
|
||||||
if visitor.Pwd != rootPwd {
|
if visitor.Pwd != rootPwd {
|
||||||
|
@ -69,6 +74,7 @@ func TestUserStores(t *testing.T) {
|
||||||
t.Fatalf("incorrect preference")
|
t.Fatalf("incorrect preference")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add users
|
||||||
id, name1 := uint64(2), "test_user1"
|
id, name1 := uint64(2), "test_user1"
|
||||||
pwd1, pwd2 := "666", "888"
|
pwd1, pwd2 := "666", "888"
|
||||||
role1, role2 := db.UserRole, db.AdminRole
|
role1, role2 := db.UserRole, db.AdminRole
|
||||||
|
@ -91,6 +97,7 @@ func TestUserStores(t *testing.T) {
|
||||||
t.Fatal("there should be no error")
|
t.Fatal("there should be no error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// compare users
|
||||||
user, err := store.GetUser(ctx, id)
|
user, err := store.GetUser(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -114,6 +121,7 @@ func TestUserStores(t *testing.T) {
|
||||||
t.Fatalf("down limit not matched %d %d", downLimit1, user.Quota.DownloadSpeedLimit)
|
t.Fatalf("down limit not matched %d %d", downLimit1, user.Quota.DownloadSpeedLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check listing
|
||||||
users, err := store.ListUsers(ctx)
|
users, err := store.ListUsers(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -190,7 +198,6 @@ func TestUserStores(t *testing.T) {
|
||||||
t.Fatalf("used space not matched %d %d", user.UsedSpace, usedIncr-usedDecr)
|
t.Fatalf("used space not matched %d %d", user.UsedSpace, usedIncr-usedDecr)
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
newPrefer := &db.Preferences{
|
newPrefer := &db.Preferences{
|
||||||
Bg: &db.BgConfig{
|
Bg: &db.BgConfig{
|
||||||
Url: "/url",
|
Url: "/url",
|
||||||
|
@ -219,7 +226,7 @@ func TestUserStores(t *testing.T) {
|
||||||
t.Fatalf("ids not matched %d %d", id, user.ID)
|
t.Fatalf("ids not matched %d %d", id, user.ID)
|
||||||
}
|
}
|
||||||
if user.Pwd != pwd2 {
|
if user.Pwd != pwd2 {
|
||||||
t.Fatalf("passwords not match %s", err)
|
t.Fatalf("passwords not match %s %s", user.Pwd, pwd2)
|
||||||
}
|
}
|
||||||
if user.Role != role2 {
|
if user.Role != role2 {
|
||||||
t.Fatalf("roles not matched %s %s", role2, user.Role)
|
t.Fatalf("roles not matched %s %s", role2, user.Role)
|
||||||
|
@ -252,7 +259,7 @@ func TestUserStores(t *testing.T) {
|
||||||
if user.ID == 0 && user.Name != rootName && user.Role != db.AdminRole {
|
if user.ID == 0 && user.Name != rootName && user.Role != db.AdminRole {
|
||||||
t.Fatalf("incorrect root info %v", user)
|
t.Fatalf("incorrect root info %v", user)
|
||||||
}
|
}
|
||||||
if user.ID == VisitorID && user.Name != VisitorName && user.Role != db.VisitorRole {
|
if user.ID == db.VisitorID && user.Name != db.VisitorName && user.Role != db.VisitorRole {
|
||||||
t.Fatalf("incorrect visitor info %v", user)
|
t.Fatalf("incorrect visitor info %v", user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,7 +273,49 @@ func TestUserStores(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("testing UserStore sqlite", func(t *testing.T) {
|
// TODO: role functions are disabled temporarily
|
||||||
|
// testRoleMethods := func(t *testing.T, store db.IUserDB) {
|
||||||
|
// roles := []string{"role1", "role2"}
|
||||||
|
// var err error
|
||||||
|
// for _, role := range roles {
|
||||||
|
// err = store.AddRole(role)
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// roleMap, err := store.ListRoles()
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for _, role := range append(roles, []string{
|
||||||
|
// db.AdminRole, db.UserRole, db.VisitorRole,
|
||||||
|
// }...) {
|
||||||
|
// if !roleMap[role] {
|
||||||
|
// t.Fatalf("role(%s) not found", role)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for _, role := range roles {
|
||||||
|
// err = store.DelRole(role)
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// roleMap, err = store.ListRoles()
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
|
// for _, role := range roles {
|
||||||
|
// if roleMap[role] {
|
||||||
|
// t.Fatalf("role(%s) should not exist", role)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
t.Run("user store crud - sqlite", func(t *testing.T) {
|
||||||
rootPath, err := ioutil.TempDir("./", "quickshare_userstore_test_")
|
rootPath, err := ioutil.TempDir("./", "quickshare_userstore_test_")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -274,17 +323,17 @@ func TestUserStores(t *testing.T) {
|
||||||
defer os.RemoveAll(rootPath)
|
defer os.RemoveAll(rootPath)
|
||||||
|
|
||||||
dbPath := filepath.Join(rootPath, "quickshare.sqlite")
|
dbPath := filepath.Join(rootPath, "quickshare.sqlite")
|
||||||
sqliteDB, err := NewSQLite(dbPath)
|
sqliteDB, err := sqlite.NewSQLite(dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer sqliteDB.Close()
|
defer sqliteDB.Close()
|
||||||
|
|
||||||
store, err := NewSQLiteUsers(sqliteDB)
|
store, err := sqlite.NewSQLiteStore(sqliteDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("fail to new user store", err)
|
t.Fatal("fail to new user store", err)
|
||||||
}
|
}
|
||||||
if err = store.Init(context.TODO(), rootName, rootPwd); err != nil {
|
if err = store.Init(context.TODO(), rootName, rootPwd, testSiteConfig); err != nil {
|
||||||
t.Fatal("fail to init user store", err)
|
t.Fatal("fail to init user store", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,329 +0,0 @@
|
||||||
package userstore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ihexxa/quickshare/src/db"
|
|
||||||
"github.com/ihexxa/quickshare/src/kvstore/boltdbpvd"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUserStores(t *testing.T) {
|
|
||||||
rootName, rootPwd := "root", "rootPwd"
|
|
||||||
|
|
||||||
testUserMethods := func(t *testing.T, store IUserStore) {
|
|
||||||
root, err := store.GetUser(0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if root.Name != rootName {
|
|
||||||
t.Fatal("root user not found")
|
|
||||||
}
|
|
||||||
if root.Pwd != rootPwd {
|
|
||||||
t.Fatalf("passwords not match %s", err)
|
|
||||||
}
|
|
||||||
if root.Role != db.AdminRole {
|
|
||||||
t.Fatalf("incorrect root role")
|
|
||||||
}
|
|
||||||
if root.Quota.SpaceLimit != db.DefaultSpaceLimit {
|
|
||||||
t.Fatalf("incorrect root SpaceLimit")
|
|
||||||
}
|
|
||||||
if root.Quota.UploadSpeedLimit != db.DefaultUploadSpeedLimit {
|
|
||||||
t.Fatalf("incorrect root UploadSpeedLimit")
|
|
||||||
}
|
|
||||||
if root.Quota.DownloadSpeedLimit != db.DefaultDownloadSpeedLimit {
|
|
||||||
t.Fatalf("incorrect root DownloadSpeedLimit")
|
|
||||||
}
|
|
||||||
if !db.ComparePreferences(root.Preferences, &db.DefaultPreferences) {
|
|
||||||
t.Fatalf("incorrect preference %v %v", root.Preferences, db.DefaultPreferences)
|
|
||||||
}
|
|
||||||
|
|
||||||
visitor, err := store.GetUser(1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if visitor.Name != VisitorName {
|
|
||||||
t.Fatal("visitor not found")
|
|
||||||
}
|
|
||||||
if visitor.Pwd != rootPwd {
|
|
||||||
t.Fatalf("passwords not match %s", err)
|
|
||||||
}
|
|
||||||
if visitor.Role != db.VisitorRole {
|
|
||||||
t.Fatalf("incorrect visitor role")
|
|
||||||
}
|
|
||||||
if visitor.Quota.SpaceLimit != 0 {
|
|
||||||
t.Fatalf("incorrect visitor SpaceLimit")
|
|
||||||
}
|
|
||||||
if visitor.Quota.UploadSpeedLimit != db.VisitorUploadSpeedLimit {
|
|
||||||
t.Fatalf("incorrect visitor UploadSpeedLimit")
|
|
||||||
}
|
|
||||||
if visitor.Quota.DownloadSpeedLimit != db.VisitorDownloadSpeedLimit {
|
|
||||||
t.Fatalf("incorrect visitor DownloadSpeedLimit")
|
|
||||||
}
|
|
||||||
if !db.ComparePreferences(visitor.Preferences, &db.DefaultPreferences) {
|
|
||||||
t.Fatalf("incorrect preference")
|
|
||||||
}
|
|
||||||
|
|
||||||
id, name1 := uint64(2), "test_user1"
|
|
||||||
pwd1, pwd2 := "666", "888"
|
|
||||||
role1, role2 := db.UserRole, db.AdminRole
|
|
||||||
spaceLimit1, upLimit1, downLimit1 := int64(17), 5, 7
|
|
||||||
spaceLimit2, upLimit2, downLimit2 := int64(19), 13, 17
|
|
||||||
|
|
||||||
err = store.AddUser(&db.User{
|
|
||||||
ID: id,
|
|
||||||
Name: name1,
|
|
||||||
Pwd: pwd1,
|
|
||||||
Role: role1,
|
|
||||||
Quota: &db.Quota{
|
|
||||||
SpaceLimit: spaceLimit1,
|
|
||||||
UploadSpeedLimit: upLimit1,
|
|
||||||
DownloadSpeedLimit: downLimit1,
|
|
||||||
},
|
|
||||||
Preferences: &db.DefaultPreferences,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("there should be no error")
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := store.GetUser(id)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if user.Name != name1 {
|
|
||||||
t.Fatalf("names not matched %s %s", name1, user.Name)
|
|
||||||
}
|
|
||||||
if user.Pwd != pwd1 {
|
|
||||||
t.Fatalf("passwords not match %s", err)
|
|
||||||
}
|
|
||||||
if user.Role != role1 {
|
|
||||||
t.Fatalf("roles not matched %s %s", role1, user.Role)
|
|
||||||
}
|
|
||||||
if user.Quota.SpaceLimit != spaceLimit1 {
|
|
||||||
t.Fatalf("space limit not matched %d %d", spaceLimit1, user.Quota.SpaceLimit)
|
|
||||||
}
|
|
||||||
if user.Quota.UploadSpeedLimit != upLimit1 {
|
|
||||||
t.Fatalf("up limit not matched %d %d", upLimit1, user.Quota.UploadSpeedLimit)
|
|
||||||
}
|
|
||||||
if user.Quota.DownloadSpeedLimit != downLimit1 {
|
|
||||||
t.Fatalf("down limit not matched %d %d", downLimit1, user.Quota.DownloadSpeedLimit)
|
|
||||||
}
|
|
||||||
|
|
||||||
users, err := store.ListUsers()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(users) != 3 {
|
|
||||||
t.Fatalf("users size should be 3 (%d)", len(users))
|
|
||||||
}
|
|
||||||
for _, user := range users {
|
|
||||||
if user.ID == 0 {
|
|
||||||
if user.Name != rootName || user.Role != db.AdminRole {
|
|
||||||
t.Fatalf("incorrect root info %v", user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if user.ID == id {
|
|
||||||
if user.Name != name1 || user.Role != role1 {
|
|
||||||
t.Fatalf("incorrect user info %v", user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if user.Pwd != "" {
|
|
||||||
t.Fatalf("password must be empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = store.SetPwd(id, pwd2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
store.SetInfo(id, &db.User{
|
|
||||||
ID: id,
|
|
||||||
Role: role2,
|
|
||||||
Quota: &db.Quota{
|
|
||||||
SpaceLimit: spaceLimit2,
|
|
||||||
UploadSpeedLimit: upLimit2,
|
|
||||||
DownloadSpeedLimit: downLimit2,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
usedIncr, usedDecr := int64(spaceLimit2), int64(7)
|
|
||||||
err = store.SetUsed(id, true, usedIncr)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = store.SetUsed(id, false, usedDecr)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = store.SetUsed(id, true, int64(spaceLimit2)-(usedIncr-usedDecr)+1)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "reached space limit") {
|
|
||||||
t.Fatal("should reject big file")
|
|
||||||
} else {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err = store.GetUser(id)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if user.Pwd != pwd2 {
|
|
||||||
t.Fatalf("passwords not match %s", err)
|
|
||||||
}
|
|
||||||
if user.Role != role2 {
|
|
||||||
t.Fatalf("roles not matched %s %s", role2, user.Role)
|
|
||||||
}
|
|
||||||
if user.Quota.SpaceLimit != spaceLimit2 {
|
|
||||||
t.Fatalf("space limit not matched %d %d", spaceLimit2, user.Quota.SpaceLimit)
|
|
||||||
}
|
|
||||||
if user.Quota.UploadSpeedLimit != upLimit2 {
|
|
||||||
t.Fatalf("up limit not matched %d %d", upLimit2, user.Quota.UploadSpeedLimit)
|
|
||||||
}
|
|
||||||
if user.Quota.DownloadSpeedLimit != downLimit2 {
|
|
||||||
t.Fatalf("down limit not matched %d %d", downLimit2, user.Quota.DownloadSpeedLimit)
|
|
||||||
}
|
|
||||||
if user.UsedSpace != usedIncr-usedDecr {
|
|
||||||
t.Fatalf("used space not matched %d %d", user.UsedSpace, usedIncr-usedDecr)
|
|
||||||
}
|
|
||||||
|
|
||||||
newPrefer := &db.Preferences{
|
|
||||||
Bg: &db.BgConfig{
|
|
||||||
Url: "/url",
|
|
||||||
Repeat: "repeat",
|
|
||||||
Position: "center",
|
|
||||||
Align: "fixed",
|
|
||||||
BgColor: "#333",
|
|
||||||
},
|
|
||||||
CSSURL: "/cssurl",
|
|
||||||
LanPackURL: "lanPackURL",
|
|
||||||
Lan: "zhCN",
|
|
||||||
Theme: "dark",
|
|
||||||
Avatar: "/avatar",
|
|
||||||
Email: "foo@gmail.com",
|
|
||||||
}
|
|
||||||
err = store.SetPreferences(id, newPrefer)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err = store.GetUserByName(name1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if user.ID != id {
|
|
||||||
t.Fatalf("ids not matched %d %d", id, user.ID)
|
|
||||||
}
|
|
||||||
if user.Pwd != pwd2 {
|
|
||||||
t.Fatalf("passwords not match %s", err)
|
|
||||||
}
|
|
||||||
if user.Role != role2 {
|
|
||||||
t.Fatalf("roles not matched %s %s", role2, user.Role)
|
|
||||||
}
|
|
||||||
if user.Quota.SpaceLimit != spaceLimit2 {
|
|
||||||
t.Fatalf("space limit not matched %d %d", spaceLimit2, user.Quota.SpaceLimit)
|
|
||||||
}
|
|
||||||
if user.Quota.UploadSpeedLimit != upLimit2 {
|
|
||||||
t.Fatalf("up limit not matched %d %d", upLimit2, user.Quota.UploadSpeedLimit)
|
|
||||||
}
|
|
||||||
if user.Quota.DownloadSpeedLimit != downLimit2 {
|
|
||||||
t.Fatalf("down limit not matched %d %d", downLimit2, user.Quota.DownloadSpeedLimit)
|
|
||||||
}
|
|
||||||
if !db.ComparePreferences(user.Preferences, newPrefer) {
|
|
||||||
t.Fatalf("preferences not matched %v %v", user.Preferences, newPrefer)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = store.DelUser(id)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
users, err = store.ListUsers()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(users) != 2 {
|
|
||||||
t.Fatalf("users size should be 2 (%d)", len(users))
|
|
||||||
}
|
|
||||||
for _, user := range users {
|
|
||||||
if user.ID == 0 && user.Name != rootName && user.Role != db.AdminRole {
|
|
||||||
t.Fatalf("incorrect root info %v", user)
|
|
||||||
}
|
|
||||||
if user.ID == VisitorID && user.Name != VisitorName && user.Role != db.VisitorRole {
|
|
||||||
t.Fatalf("incorrect visitor info %v", user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nameToID, err := store.ListUserIDs()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if len(nameToID) != len(users) {
|
|
||||||
t.Fatalf("nameToID size (%d) should be same as (%d)", len(nameToID), len(users))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testRoleMethods := func(t *testing.T, store IUserStore) {
|
|
||||||
roles := []string{"role1", "role2"}
|
|
||||||
var err error
|
|
||||||
for _, role := range roles {
|
|
||||||
err = store.AddRole(role)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
roleMap, err := store.ListRoles()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, role := range append(roles, []string{
|
|
||||||
db.AdminRole, db.UserRole, db.VisitorRole,
|
|
||||||
}...) {
|
|
||||||
if !roleMap[role] {
|
|
||||||
t.Fatalf("role(%s) not found", role)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, role := range roles {
|
|
||||||
err = store.DelRole(role)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
roleMap, err = store.ListRoles()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
for _, role := range roles {
|
|
||||||
if roleMap[role] {
|
|
||||||
t.Fatalf("role(%s) should not exist", role)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("testing KVUserStore", 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 := NewKVUserStore(kvstore)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("fail to new kvstore", err)
|
|
||||||
}
|
|
||||||
if err = store.Init(rootName, rootPwd); err != nil {
|
|
||||||
t.Fatal("fail to init kvstore", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testUserMethods(t, store)
|
|
||||||
testRoleMethods(t, store)
|
|
||||||
})
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue