uBrowserSync/syncstore/store.go

113 lines
2.3 KiB
Go

package syncstore
import (
"crypto/rand"
"encoding/hex"
"fmt"
"time"
)
type StoreDriver interface {
RawSave(s *Blob) error
RawLoad(id string) (*Blob, error)
Exists(id string) bool
}
type Store struct {
drv StoreDriver
}
func NewStore(drv StoreDriver) Store {
return Store{drv}
}
type CreateResp struct {
ID string `json:"id"`
LastUpdated time.Time `json:"lastUpdated"`
Version string `json:"version"`
}
type GetResp struct {
Bookmarks string `json:"bookmarks"`
LastUpdated time.Time `json:"lastUpdated"`
Version string `json:"version"`
}
func (store *Store) Create(version string) (*CreateResp, error) {
var sid string
failed := true
for i := 0; i < 5; i++ {
var idb = make([]byte, 16)
var ids = make([]byte, 32)
_, err := rand.Read(idb)
if err != nil {
return nil, err
}
hex.Encode(ids, idb)
sid = string(ids[:])
if !store.drv.Exists(sid) {
failed = false
break
}
}
if failed {
return nil, fmt.Errorf("cannot generate unique sync ID")
}
s := new(Blob)
s.ID = sid
s.Version = version
s.Created = time.Now().UTC()
s.LastUpdated = s.Created
s.LastAccessed = s.Created
return &CreateResp{
ID: s.ID,
LastUpdated: s.LastUpdated,
Version: s.Version}, store.drv.RawSave(s)
}
func (store *Store) Update(id string, bookmarks string, lastUpdated time.Time) (time.Time, error) {
t, err := store.drv.RawLoad(id)
if err != nil {
return time.Time{}, err
}
if !lastUpdated.IsZero() {
if !t.LastUpdated.Equal(lastUpdated) {
return time.Time{}, SyncConflictError
}
}
t.ID = id
t.Bookmarks = bookmarks
t.LastUpdated = time.Now().UTC()
t.LastAccessed = t.LastUpdated
return t.LastUpdated, store.drv.RawSave(t)
}
func (store *Store) Get(id string) (*GetResp, error) {
s, err := store.drv.RawLoad(id)
if err != nil {
return nil, err
}
s.ID = id
s.LastAccessed = time.Now().UTC()
return &GetResp{
Version: s.Version,
Bookmarks: s.Bookmarks,
LastUpdated: s.LastUpdated}, store.drv.RawSave(s)
}
func (store *Store) GetLastUpdated(id string) (time.Time, error) {
r, err := store.Get(id)
if err != nil {
return time.Time{}, err
}
return r.LastUpdated, nil
}
func (store *Store) GetVersion(id string) (string, error) {
r, err := store.Get(id)
if err != nil {
return "", err
}
return r.Version, nil
}