135 lines
2.6 KiB
Go
135 lines
2.6 KiB
Go
|
package workspace
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"io"
|
||
|
"io/fs"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
|
||
|
"github.com/bwmarrin/snowflake"
|
||
|
"go.etcd.io/bbolt"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
metaBucket = []byte("meta")
|
||
|
tokenBucket = []byte("token")
|
||
|
)
|
||
|
|
||
|
type Workspace struct {
|
||
|
path string
|
||
|
db *bbolt.DB
|
||
|
|
||
|
sf *snowflake.Node
|
||
|
}
|
||
|
|
||
|
func (w *Workspace) Close() error {
|
||
|
return w.db.Close()
|
||
|
}
|
||
|
|
||
|
func (w *Workspace) List() ([]os.FileInfo, error) {
|
||
|
files := make([]os.FileInfo, 0)
|
||
|
if err := filepath.Walk(w.path, func(walkPath string, walkInfo fs.FileInfo, walkErr error) error {
|
||
|
if walkErr != nil {
|
||
|
return walkErr
|
||
|
}
|
||
|
if walkInfo.IsDir() {
|
||
|
return filepath.SkipDir
|
||
|
}
|
||
|
if walkInfo.Name() == "cabinet.db" {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
files = append(files, walkInfo)
|
||
|
|
||
|
return nil
|
||
|
}); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return files, nil
|
||
|
}
|
||
|
|
||
|
func (w *Workspace) Meta(id string) (meta Meta, err error) {
|
||
|
return meta, w.db.View(func(tx *bbolt.Tx) error {
|
||
|
data := tx.Bucket(metaBucket).Get([]byte(id))
|
||
|
return json.Unmarshal(data, &meta)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func (w *Workspace) Read(id string) (io.ReadCloser, error) {
|
||
|
return os.Open(filepath.Join(w.path, id))
|
||
|
}
|
||
|
|
||
|
func (w *Workspace) Add(r io.Reader, meta Meta) (string, error) {
|
||
|
id := w.sf.Generate().String()
|
||
|
meta.id = id
|
||
|
|
||
|
fi, err := os.Create(filepath.Join(w.path, id))
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
defer fi.Close()
|
||
|
|
||
|
if _, err := io.Copy(fi, r); err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
return id, w.db.Update(func(tx *bbolt.Tx) error {
|
||
|
data, err := json.Marshal(meta)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return tx.Bucket(metaBucket).Put([]byte(id), data)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func (w *Workspace) Delete(id string) error {
|
||
|
if err := os.Remove(filepath.Join(w.path, id)); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return w.db.Update(func(tx *bbolt.Tx) error {
|
||
|
return tx.Bucket(metaBucket).Delete([]byte(id))
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func (w *Workspace) IsProtected() (protected bool, err error) {
|
||
|
return protected, w.db.View(func(tx *bbolt.Tx) error {
|
||
|
protected = tx.Bucket(tokenBucket).Stats().KeyN > 0
|
||
|
return nil
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func New(path string) (*Workspace, error) {
|
||
|
if err := os.MkdirAll(path, os.ModePerm); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
db, err := bbolt.Open(filepath.Join(path, "cabinet.db"), os.ModePerm, bbolt.DefaultOptions)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if err := db.Update(func(tx *bbolt.Tx) error {
|
||
|
if _, err := tx.CreateBucketIfNotExists(metaBucket); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if _, err := tx.CreateBucketIfNotExists(tokenBucket); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
sf, err := snowflake.NewNode(1)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &Workspace{
|
||
|
path: path,
|
||
|
db: db,
|
||
|
sf: sf,
|
||
|
}, nil
|
||
|
}
|