113 lines
2.3 KiB
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
|
|
}
|