cabinet/internal/workspace/workspace.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
}