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 }