feat: add mkdir
This commit is contained in:
parent
a2de278199
commit
deb3abe394
84
api_directory.go
Normal file
84
api_directory.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gosvc/httpserver"
|
||||||
|
"gosvc/validator"
|
||||||
|
"robotfs/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Entry struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
IsDir bool `json:"is_dir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListResult struct {
|
||||||
|
Entries []Entry `json:"entries"`
|
||||||
|
LastFileName string `json:"last_file_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MkdirParams struct {
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MkdirParams) Validate() error {
|
||||||
|
return validator.WithIf(
|
||||||
|
m.Path == "", "Path is empty",
|
||||||
|
).Validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) HandleMkdir(
|
||||||
|
req *httpserver.Request,
|
||||||
|
resp *httpserver.Response,
|
||||||
|
) *httpserver.Response {
|
||||||
|
params := req.Binded.(*MkdirParams)
|
||||||
|
fullPath := utils.FullPath(params.Path)
|
||||||
|
|
||||||
|
err := s.FileSystemManager.MakeDirectory(req.Context(), fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return resp.InternalServerError("mkdir failed, " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.NoContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) HandleListDirectory(
|
||||||
|
req *httpserver.Request,
|
||||||
|
resp *httpserver.Response,
|
||||||
|
) *httpserver.Response {
|
||||||
|
// path := req.QueryString("path")
|
||||||
|
// startFileName := req.QueryString("startFileName")
|
||||||
|
// limit := req.QueryInt("limit")
|
||||||
|
entries := []Entry{
|
||||||
|
{Name: "file1.txt", IsDir: false},
|
||||||
|
{Name: "folder1", IsDir: true},
|
||||||
|
}
|
||||||
|
lastFileName := "file1.txt"
|
||||||
|
|
||||||
|
result := ListResult{
|
||||||
|
Entries: entries,
|
||||||
|
LastFileName: lastFileName,
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.OK(result).JSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) HandleDeleteDirectory(
|
||||||
|
req *httpserver.Request,
|
||||||
|
resp *httpserver.Response,
|
||||||
|
) *httpserver.Response {
|
||||||
|
return resp.NoContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) HandleRenameDirectory(
|
||||||
|
req *httpserver.Request,
|
||||||
|
resp *httpserver.Response,
|
||||||
|
) *httpserver.Response {
|
||||||
|
return resp.NoContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) HandleCopyDirectory(
|
||||||
|
req *httpserver.Request,
|
||||||
|
resp *httpserver.Response,
|
||||||
|
) *httpserver.Response {
|
||||||
|
return resp.NoContent()
|
||||||
|
}
|
||||||
84
api_file.go
Normal file
84
api_file.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gosvc/httpserver"
|
||||||
|
"gosvc/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeleteParams struct {
|
||||||
|
Path string
|
||||||
|
IsDir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeleteParams) Validate() error {
|
||||||
|
return validator.WithIf(
|
||||||
|
d.Path == "", "Path is empty",
|
||||||
|
).Validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeneralParams struct {
|
||||||
|
SrcPath string
|
||||||
|
DstPath string
|
||||||
|
IsDir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GeneralParams) Validate() error {
|
||||||
|
return validator.ChainValidate(
|
||||||
|
validator.WithIf(
|
||||||
|
g.SrcPath == "", "SrcPath is empty",
|
||||||
|
),
|
||||||
|
validator.WithIf(
|
||||||
|
g.DstPath == "", "DstPath is empty",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CopyParams struct {
|
||||||
|
GeneralParams
|
||||||
|
}
|
||||||
|
|
||||||
|
type RenameParams struct {
|
||||||
|
GeneralParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) HandleUploadFile(
|
||||||
|
req *httpserver.Request,
|
||||||
|
resp *httpserver.Response,
|
||||||
|
) *httpserver.Response {
|
||||||
|
return resp.NoContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) HandleInfoFile(
|
||||||
|
req *httpserver.Request,
|
||||||
|
resp *httpserver.Response,
|
||||||
|
) *httpserver.Response {
|
||||||
|
return resp.NoContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) HandleDownloadFile(
|
||||||
|
req *httpserver.Request,
|
||||||
|
resp *httpserver.Response,
|
||||||
|
) *httpserver.Response {
|
||||||
|
return resp.NoContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) HandleDeleteFile(
|
||||||
|
req *httpserver.Request,
|
||||||
|
resp *httpserver.Response,
|
||||||
|
) *httpserver.Response {
|
||||||
|
return resp.NoContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) HandleRenameFile(
|
||||||
|
req *httpserver.Request,
|
||||||
|
resp *httpserver.Response,
|
||||||
|
) *httpserver.Response {
|
||||||
|
return resp.NoContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) HandleCopyFile(
|
||||||
|
req *httpserver.Request,
|
||||||
|
resp *httpserver.Response,
|
||||||
|
) *httpserver.Response {
|
||||||
|
return resp.NoContent()
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ import (
|
|||||||
"gosvc/httpserver"
|
"gosvc/httpserver"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Service) API_Ping(
|
func (s *Service) HandlePing(
|
||||||
req *httpserver.Request,
|
req *httpserver.Request,
|
||||||
resp *httpserver.Response,
|
resp *httpserver.Response,
|
||||||
) *httpserver.Response {
|
) *httpserver.Response {
|
||||||
|
|||||||
0
config.yml
Normal file
0
config.yml
Normal file
33
config/robotfs.go
Normal file
33
config/robotfs.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type RobotFSConfig struct {
|
||||||
|
Address string `mapstructure:"address"`
|
||||||
|
LogPath string `mapstructure:"log_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RedisConfig struct {
|
||||||
|
Address string `mapstructure:"address"`
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
DB int `mapstructure:"db"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type S3Config struct {
|
||||||
|
Region string `mapstructure:"region"`
|
||||||
|
Endpoint string `mapstructure:"endpoint"`
|
||||||
|
AccessKeyID string `mapstructure:"access_key_id"`
|
||||||
|
SecretAccessKey string `mapstructure:"secret_access_key"`
|
||||||
|
BucketName string `mapstructure:"bucket_name"`
|
||||||
|
UsePathStyle bool `mapstructure:"use_path_style"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
RobotFS RobotFSConfig `mapstructure:"robotfs"`
|
||||||
|
Redis RedisConfig `mapstructure:"redis"`
|
||||||
|
S3 S3Config `mapstructure:"s3"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetConfig() *Config {
|
||||||
|
config := &Config{}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
@ -1,29 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Robotfs struct {
|
|
||||||
Address string `mapstructure:"ADDRESS"`
|
|
||||||
LogPath string `mapstructure:"LOGPATH"`
|
|
||||||
} `mapstructure:"robotfs"`
|
|
||||||
|
|
||||||
Redis struct {
|
|
||||||
Address string `mapstructure:"address"`
|
|
||||||
Password string `mapstructure:"password"`
|
|
||||||
DB int `mapstructure:"db"`
|
|
||||||
} `mapstructure:"redis"`
|
|
||||||
|
|
||||||
S3 struct {
|
|
||||||
Region string `mapstructure:"region"`
|
|
||||||
Endpoint string `mapstructure:"endpoint"`
|
|
||||||
AccessKeyID string `mapstructure:"access_key_id"`
|
|
||||||
SecretAccessKey string `mapstructure:"secret_access_key"`
|
|
||||||
BucketName string `mapstructure:"bucket_name"`
|
|
||||||
UsePathStyle bool `mapstructure:"use_path_style"`
|
|
||||||
} `mapstructure:"s3"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetConfig() *Config {
|
|
||||||
config := &Config{}
|
|
||||||
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
@ -8,8 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
metadataManager *MetaDataManager
|
FileSystemManager *FileSystemManager
|
||||||
storageManager *StorageManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEngine() *Engine {
|
func NewEngine() *Engine {
|
||||||
@ -17,31 +16,25 @@ func NewEngine() *Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) Start() error {
|
func (e *Engine) Start() error {
|
||||||
store := redis_lua.NewRedisLuaStore()
|
|
||||||
if err := store.Initialize(utils.GetViper(), "redis."); err != nil {
|
|
||||||
return fmt.Errorf("failed to initialize meta store: %v", err)
|
|
||||||
}
|
|
||||||
metadata, err := NewMetaDataManager(store)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create metadata manager: %v", err)
|
|
||||||
}
|
|
||||||
e.metadataManager = metadata
|
|
||||||
|
|
||||||
s3Config, err := Initialize(utils.GetViper(), "s3.")
|
s3Config, err := Initialize(utils.GetViper(), "s3.")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to initialize s3 config: %v", err)
|
return fmt.Errorf("failed to initialize s3 config: %v", err)
|
||||||
}
|
}
|
||||||
storageManager, err := NewStorageManager(s3Config)
|
object, err := NewStorageManager(s3Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create storage manager: %v", err)
|
return fmt.Errorf("failed to create storage manager: %v", err)
|
||||||
}
|
}
|
||||||
e.storageManager = storageManager
|
|
||||||
|
meta := redis_lua.NewRedisLuaStore()
|
||||||
|
if err := meta.Initialize(utils.GetViper(), "redis."); err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize meta store: %v", err)
|
||||||
|
}
|
||||||
|
filesystem := NewFileSystemManager(meta, object)
|
||||||
|
e.FileSystemManager = filesystem
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) Stop() {
|
func (e *Engine) Stop() {
|
||||||
if e.metadataManager != nil {
|
e.FileSystemManager.Shutdown()
|
||||||
e.metadataManager.Shutdown()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
85
engine/filesystem_manager.go
Normal file
85
engine/filesystem_manager.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"robotfs/pb"
|
||||||
|
"robotfs/store"
|
||||||
|
"robotfs/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileSystemManager struct {
|
||||||
|
meta store.MetaStore
|
||||||
|
storage *StorageManager
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFileSystemManager(meta store.MetaStore, storage *StorageManager) *FileSystemManager {
|
||||||
|
return &FileSystemManager{
|
||||||
|
meta: meta,
|
||||||
|
storage: storage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileSystemManager) BeginTransaction(ctx context.Context) (context.Context, error) {
|
||||||
|
return f.meta.BeginTransaction(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileSystemManager) CommitTransaction(ctx context.Context) error {
|
||||||
|
return f.meta.CommitTransaction(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileSystemManager) RollbackTransaction(ctx context.Context) error {
|
||||||
|
return f.meta.RollbackTransaction(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
Root = &utils.Entry{
|
||||||
|
FullPath: utils.FullPath("/"),
|
||||||
|
IsDir: true,
|
||||||
|
CreateTime: time.Now().Unix(),
|
||||||
|
LastModificationTime: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *FileSystemManager) FindEntry(ctx context.Context, p utils.FullPath) (entry *pb.FileEntry, err error) {
|
||||||
|
if p == "/" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
entry, err = f.meta.FindEntry(ctx, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileSystemManager) MakeDirectory(ctx context.Context, path utils.FullPath) error {
|
||||||
|
if string(path) == "/" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry, _ := f.FindEntry(ctx, path); entry != nil {
|
||||||
|
return fmt.Errorf("directory %s already exists", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
parentDir, _ := path.DirAndName()
|
||||||
|
if parentDir != "/" {
|
||||||
|
if parentEntry, _ := f.FindEntry(ctx, utils.FullPath(parentDir)); parentEntry == nil {
|
||||||
|
return fmt.Errorf("parent directory %s does not exist", parentDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry := utils.MakeEntry(path, true)
|
||||||
|
return f.meta.InsertEntry(ctx, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileSystemManager) Shutdown() error {
|
||||||
|
if f.meta != nil {
|
||||||
|
f.meta.Shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -1,28 +0,0 @@
|
|||||||
package engine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"robotfs/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MetaDataManager struct {
|
|
||||||
store store.MetaStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMetaDataManager(store store.MetaStore) (*MetaDataManager, error) {
|
|
||||||
if store == nil {
|
|
||||||
return nil, fmt.Errorf("meta store is required")
|
|
||||||
}
|
|
||||||
return &MetaDataManager{
|
|
||||||
store: store,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MetaDataManager) Shutdown() error {
|
|
||||||
if m.store != nil {
|
|
||||||
m.store.Shutdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
package engine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type WorkSpaceManager struct{}
|
|
||||||
|
|
||||||
func NewWorkSpaceManager() *WorkSpaceManager {
|
|
||||||
return &WorkSpaceManager{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type StorageSpace struct {
|
|
||||||
ID string
|
|
||||||
Name string
|
|
||||||
OwnerID string
|
|
||||||
Type string
|
|
||||||
CreateTime time.Time
|
|
||||||
UpdateTime time.Time
|
|
||||||
InviteCode string
|
|
||||||
RootPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wsm *WorkSpaceManager) CreateWorkSpace(ctx context.Context, ownerID, name, visibility string) (string, error) {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wsm *WorkSpaceManager) GetWorkSpace(ctx context.Context, spaceID string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wsm *WorkSpaceManager) UpdateWorkSpace(ctx context.Context, spaceID string) (*StorageSpace, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wsm *WorkSpaceManager) GenerateInviteCode(ctx context.Context, spaceID string) (string, error) {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wsm *WorkSpaceManager) ImportWorkSpace(ctx context.Context, code string, newOwnerID string) (string, error) {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
14
main.go
14
main.go
@ -20,14 +20,17 @@ Options:
|
|||||||
Example:
|
Example:
|
||||||
robotfs --config=/path/to/config.yaml`
|
robotfs --config=/path/to/config.yaml`
|
||||||
|
|
||||||
|
var (
|
||||||
|
configFile = flag.String("config", "", "path to config file")
|
||||||
|
version = flag.Bool("version", false, "show version information")
|
||||||
|
help = flag.Bool("help", false, "show help message")
|
||||||
|
)
|
||||||
|
|
||||||
func printUsage() {
|
func printUsage() {
|
||||||
fmt.Println(usage)
|
fmt.Println(usage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
configFile := flag.String("config", "", "path to config file")
|
|
||||||
version := flag.Bool("version", false, "show version information")
|
|
||||||
help := flag.Bool("help", false, "show help message")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *help {
|
if *help {
|
||||||
@ -41,8 +44,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *configFile != "" {
|
if *configFile != "" {
|
||||||
utils.ConfigurationFileDirectory.Set(*configFile)
|
err := utils.LoadConfiguration(*configFile)
|
||||||
if !utils.LoadConfiguration("config", true) {
|
if err != nil {
|
||||||
|
fmt.Println("load config file failed, " + err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
110
router.go
110
router.go
@ -12,7 +12,115 @@ func (s *Service) RegisterRouteRules() {
|
|||||||
{
|
{
|
||||||
Path: "/ping",
|
Path: "/ping",
|
||||||
Method: http.MethodGet,
|
Method: http.MethodGet,
|
||||||
Handler: s.API_Ping,
|
Handler: s.HandlePing,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/robotfs",
|
||||||
|
SubRules: []*httpserver.RouteRule{
|
||||||
|
{
|
||||||
|
Path: "/mkdir",
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Handler: s.HandleMkdir,
|
||||||
|
Bind: &MkdirParams{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/dir/list",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Handler: s.HandleListDirectory,
|
||||||
|
QueryRules: []*httpserver.QueryRule{
|
||||||
|
{
|
||||||
|
Key: "path",
|
||||||
|
Type: httpserver.QueryTypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "startFileName",
|
||||||
|
Type: httpserver.QueryTypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "limit",
|
||||||
|
Type: httpserver.QueryTypeInt,
|
||||||
|
Required: true,
|
||||||
|
Min: 1,
|
||||||
|
Max: 2048,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/dir/delete",
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Handler: s.HandleDeleteDirectory,
|
||||||
|
Bind: &DeleteParams{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/dir/rename",
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Handler: s.HandleRenameDirectory,
|
||||||
|
Bind: &RenameParams{},
|
||||||
|
},
|
||||||
|
// Todo: need juicefs clone
|
||||||
|
{
|
||||||
|
Path: "/dir/copy",
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Handler: s.HandleCopyDirectory,
|
||||||
|
Bind: &CopyParams{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/file/upload",
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Handler: s.HandleUploadFile,
|
||||||
|
QueryRules: []*httpserver.QueryRule{
|
||||||
|
{
|
||||||
|
Key: "path",
|
||||||
|
Type: httpserver.QueryTypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/info",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Handler: s.HandleInfoFile,
|
||||||
|
QueryRules: []*httpserver.QueryRule{
|
||||||
|
{
|
||||||
|
Key: "path",
|
||||||
|
Type: httpserver.QueryTypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/file/download",
|
||||||
|
Method: http.MethodGet,
|
||||||
|
Handler: s.HandleDownloadFile,
|
||||||
|
QueryRules: []*httpserver.QueryRule{
|
||||||
|
{
|
||||||
|
Key: "path",
|
||||||
|
Type: httpserver.QueryTypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/file/delete",
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Handler: s.HandleDeleteFile,
|
||||||
|
Bind: &DeleteParams{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/file/rename",
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Handler: s.HandleRenameFile,
|
||||||
|
Bind: &RenameParams{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Path: "/file/copy",
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Handler: s.HandleCopyFile,
|
||||||
|
Bind: &CopyParams{},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
11
service.go
11
service.go
@ -10,17 +10,17 @@ import (
|
|||||||
type Service struct {
|
type Service struct {
|
||||||
gosvc.ServiceBase
|
gosvc.ServiceBase
|
||||||
|
|
||||||
engine *engine.Engine
|
*engine.Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateService() (*Service, error) {
|
func CreateService() (*Service, error) {
|
||||||
return &Service{
|
return &Service{
|
||||||
engine: engine.NewEngine(),
|
Engine: engine.NewEngine(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Start() error {
|
func (s *Service) Start() error {
|
||||||
if err := s.engine.Start(); err != nil {
|
if err := s.Engine.Start(); err != nil {
|
||||||
return fmt.Errorf("failed to start engine: %v", err)
|
return fmt.Errorf("failed to start engine: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,8 +28,5 @@ func (s *Service) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Stop() {
|
func (s *Service) Stop() {
|
||||||
s.engine.Stop()
|
s.Engine.Stop()
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) Tick() {
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,20 +2,27 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"robotfs/pb"
|
"robotfs/pb"
|
||||||
"robotfs/utils"
|
"robotfs/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ListEachEntryFunc func(entry *pb.FileEntry) bool
|
var (
|
||||||
|
ErrUnsupportedListDirectoryPrefixed = errors.New("unsupported directory prefix listing")
|
||||||
|
ErrKvNotImplemented = errors.New("kv not implemented yet")
|
||||||
|
ErrKvNotFound = errors.New("kv: not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListEachEntryFunc func(entry *utils.Entry) bool
|
||||||
|
|
||||||
type MetaStore interface {
|
type MetaStore interface {
|
||||||
GetName() string
|
GetName() string
|
||||||
|
|
||||||
Initialize(configuration utils.Configuration, prefix string) error
|
Initialize(configuration utils.Configuration, prefix string) error
|
||||||
|
|
||||||
InsertEntry(context.Context, *pb.FileEntry) error
|
InsertEntry(context.Context, *utils.Entry) error
|
||||||
UpdateEntry(context.Context, *pb.FileEntry) (err error)
|
UpdateEntry(context.Context, *utils.Entry) (err error)
|
||||||
FindEntry(context.Context, utils.FullPath) (entry *pb.FileEntry, err error)
|
FindEntry(context.Context, utils.FullPath) (entry *pb.FileEntry, err error)
|
||||||
DeleteEntry(context.Context, utils.FullPath) (err error)
|
DeleteEntry(context.Context, utils.FullPath) (err error)
|
||||||
DeleteFolderChildren(context.Context, utils.FullPath) (err error)
|
DeleteFolderChildren(context.Context, utils.FullPath) (err error)
|
||||||
|
|||||||
@ -23,14 +23,16 @@ func (store *RedisLuaStore) Initialize(configuration utils.Configuration, prefix
|
|||||||
configuration.GetString(prefix+"address"),
|
configuration.GetString(prefix+"address"),
|
||||||
configuration.GetString(prefix+"password"),
|
configuration.GetString(prefix+"password"),
|
||||||
configuration.GetInt(prefix+"database"),
|
configuration.GetInt(prefix+"database"),
|
||||||
|
configuration.GetStringSlice(prefix+"superLargeDirectories"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *RedisLuaStore) initialize(hostPort string, password string, database int) (err error) {
|
func (store *RedisLuaStore) initialize(hostPort string, password string, database int, superLargeDirectories []string) (err error) {
|
||||||
store.Client = redis.NewClient(&redis.Options{
|
store.Client = redis.NewClient(&redis.Options{
|
||||||
Addr: hostPort,
|
Addr: hostPort,
|
||||||
Password: password,
|
Password: password,
|
||||||
DB: database,
|
DB: database,
|
||||||
})
|
})
|
||||||
|
store.loadSuperLargeDirectories(superLargeDirectories)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,32 +43,27 @@ func (store *UniversalRedisLuaStore) RollbackTransaction(ctx context.Context) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *UniversalRedisLuaStore) InsertEntry(ctx context.Context, entry *pb.FileEntry) (err error) {
|
func (store *UniversalRedisLuaStore) InsertEntry(ctx context.Context, entry *utils.Entry) (err error) {
|
||||||
|
value, err := entry.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("encoding %s: %v", entry.FullPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
// value, err := entry.EncodeAttributesAndChunks()
|
dir, name := entry.FullPath.DirAndName()
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("encoding %s %+v: %v", entry.FullPath, entry.Attr, err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if len(entry.GetChunks()) > CountEntryChunksForGzip {
|
err = stored_procedure.InsertEntryScript.Run(ctx, store.Client,
|
||||||
// value = util.MaybeGzipData(value)
|
[]string{string(entry.FullPath), genDirectoryListKey(dir)},
|
||||||
// }
|
value, 0,
|
||||||
|
store.isSuperLargeDirectory(dir), 0, name).Err()
|
||||||
|
|
||||||
// dir, name := entry.FullPath.DirAndName()
|
if err != nil {
|
||||||
|
return fmt.Errorf("persisting %s : %v", entry.FullPath, err)
|
||||||
// err = stored_procedure.InsertEntryScript.Run(ctx, store.Client,
|
}
|
||||||
// []string{string(entry.FullPath), genDirectoryListKey(dir)},
|
|
||||||
// value, entry.TtlSec,
|
|
||||||
// store.isSuperLargeDirectory(dir), 0, name).Err()
|
|
||||||
|
|
||||||
// if err != nil {
|
|
||||||
// return fmt.Errorf("persisting %s : %v", entry.FullPath, err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *UniversalRedisLuaStore) UpdateEntry(ctx context.Context, entry *pb.FileEntry) (err error) {
|
func (store *UniversalRedisLuaStore) UpdateEntry(ctx context.Context, entry *utils.Entry) (err error) {
|
||||||
|
|
||||||
return store.InsertEntry(ctx, entry)
|
return store.InsertEntry(ctx, entry)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,13 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gosvc/logger"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
ConfigurationFileDirectory DirectoryValueType
|
|
||||||
loadSecurityConfigOnce sync.Once
|
|
||||||
)
|
|
||||||
|
|
||||||
type DirectoryValueType string
|
type DirectoryValueType string
|
||||||
|
|
||||||
func (s *DirectoryValueType) Set(value string) error {
|
func (s *DirectoryValueType) Set(value string) error {
|
||||||
@ -32,33 +26,29 @@ type Configuration interface {
|
|||||||
SetDefault(key string, value interface{})
|
SetDefault(key string, value interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadSecurityConfiguration() {
|
func LoadConfiguration(configFilePath string) error {
|
||||||
loadSecurityConfigOnce.Do(func() {
|
dir := filepath.Dir(configFilePath)
|
||||||
LoadConfiguration("security", false)
|
base := filepath.Base(configFilePath)
|
||||||
})
|
ext := filepath.Ext(base)
|
||||||
}
|
|
||||||
|
|
||||||
func LoadConfiguration(configFileName string, required bool) (loaded bool) {
|
configName := strings.TrimSuffix(base, ext)
|
||||||
viper.SetConfigName(configFileName) // name of config file (without extension)
|
viper.SetConfigName(configName)
|
||||||
viper.AddConfigPath(".") // look for config in the working directory
|
viper.AddConfigPath(dir)
|
||||||
|
|
||||||
if err := viper.MergeInConfig(); err != nil { // Handle errors reading the config file
|
switch ext {
|
||||||
if strings.Contains(err.Error(), "Not Found") {
|
case ".yaml", ".yml":
|
||||||
logger.Info("Reading %s: %v", viper.ConfigFileUsed(), err)
|
viper.SetConfigType("yaml")
|
||||||
} else {
|
case ".json":
|
||||||
logger.Error("Reading %s: %v", viper.ConfigFileUsed(), err)
|
viper.SetConfigType("json")
|
||||||
}
|
case ".toml":
|
||||||
if required {
|
viper.SetConfigType("toml")
|
||||||
logger.Error("Failed to load %s.yaml file from current directory\n"+
|
|
||||||
"\nPlease use this command to run the program:\n"+
|
|
||||||
" robotfs --config=%s.yaml\n\n\n",
|
|
||||||
configFileName, configFileName)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
logger.Info("Reading %s.yaml from %s", configFileName, viper.ConfigFileUsed())
|
|
||||||
|
|
||||||
return true
|
if err := viper.MergeInConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ViperProxy struct {
|
type ViperProxy struct {
|
||||||
|
|||||||
74
utils/entry.go
Normal file
74
utils/entry.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"robotfs/pb"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Entry struct {
|
||||||
|
FullPath FullPath
|
||||||
|
IsDir bool
|
||||||
|
Size uint64
|
||||||
|
CreateTime int64
|
||||||
|
S3Key string
|
||||||
|
ContentType string
|
||||||
|
Etag string
|
||||||
|
VersionID string
|
||||||
|
LastModificationTime int64
|
||||||
|
Extended map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeEntry(fullPath FullPath, isDirectory bool) *Entry {
|
||||||
|
return &Entry{
|
||||||
|
FullPath: fullPath,
|
||||||
|
IsDir: isDirectory,
|
||||||
|
CreateTime: time.Now().Unix(),
|
||||||
|
LastModificationTime: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Encode() ([]byte, error) {
|
||||||
|
message := entry.ToProto()
|
||||||
|
return proto.Marshal(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Decode(blob []byte) error {
|
||||||
|
message := &pb.FileEntry{}
|
||||||
|
if err := proto.Unmarshal(blob, message); err != nil {
|
||||||
|
return fmt.Errorf("decoding value blob for %s: %v", entry.FullPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.FromProto(message)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) ToProto() *pb.FileEntry {
|
||||||
|
return &pb.FileEntry{
|
||||||
|
FullPath: string(entry.FullPath),
|
||||||
|
IsDir: entry.IsDir,
|
||||||
|
Size: entry.Size,
|
||||||
|
CreateTime: entry.CreateTime,
|
||||||
|
S3Key: entry.S3Key,
|
||||||
|
ContentType: entry.ContentType,
|
||||||
|
Etag: entry.Etag,
|
||||||
|
VersionId: entry.VersionID,
|
||||||
|
LastModificationTime: entry.LastModificationTime,
|
||||||
|
Extended: entry.Extended,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) FromProto(pb *pb.FileEntry) {
|
||||||
|
entry.FullPath = FullPath(pb.FullPath)
|
||||||
|
entry.IsDir = pb.IsDir
|
||||||
|
entry.Size = pb.Size
|
||||||
|
entry.CreateTime = pb.CreateTime
|
||||||
|
entry.S3Key = pb.S3Key
|
||||||
|
entry.ContentType = pb.ContentType
|
||||||
|
entry.Etag = pb.Etag
|
||||||
|
entry.VersionID = pb.VersionId
|
||||||
|
entry.LastModificationTime = pb.LastModificationTime
|
||||||
|
entry.Extended = pb.Extended
|
||||||
|
}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NormalizePath(path string) string {
|
func NormalizePath(path string) string {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user