Skip to content

Commit

Permalink
all: pass username around for saving per-user state/config
Browse files Browse the repository at this point in the history
  • Loading branch information
igungor committed Dec 5, 2016
1 parent 83d924c commit 34eb223
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 115 deletions.
34 changes: 17 additions & 17 deletions http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (d ByDate) Less(i, j int) bool {
}

func (h *Handler) handleListDownloads(w http.ResponseWriter, r *http.Request) {
states, err := h.sync.Store.States()
states, err := h.sync.Store.States(h.sync.User.Username)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
Expand Down Expand Up @@ -185,6 +185,8 @@ func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) {
}
}

// New configuration POST'ed

var c sync.Config
err := json.NewDecoder(r.Body).Decode(&c)
if err != nil {
Expand All @@ -193,13 +195,10 @@ func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) {
return
}

// cant write configuration if there is no token
// configuration is associated with a valid and authenticated user.
// if there is no token, there is no user.

// Just write the user configuration. Internal fields must stay as is.
if c.OAuth2Token != "" {
h.sync.Config.OAuth2Token = c.OAuth2Token
// RenewToken is called here since a new OAuth2 token is inplace and a
// new client associated with this token must be created.
h.sync.RenewToken()
}

Expand All @@ -224,13 +223,10 @@ func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) {
}
h.sync.Config.IsPaused = c.IsPaused

if r.Method == "GET" {
err := json.NewEncoder(w).Encode(h.sync.Config)
if err != nil {
h.Printf("Error encoding config: %v\n", err)
http.Error(w, "", http.StatusInternalServerError)
return
}
err = h.sync.Store.SaveConfig(h.sync.Config, h.sync.User.Username)
if err != nil {
h.Printf("Error saving config: %v\n", err)
http.Error(w, "", http.StatusInternalServerError)
return
}

Expand All @@ -255,7 +251,7 @@ func (h *Handler) handleLogout(w http.ResponseWriter, r *http.Request) {
_ = h.sync.Stop()
h.sync.Config.OAuth2Token = ""

err := h.sync.Store.SaveConfig(h.sync.Config)
err := h.sync.Store.SaveConfig(h.sync.Config, h.sync.User.Username)
if err != nil {
h.Printf("Error saving config: %v\n", err)
http.Error(w, "", http.StatusInternalServerError)
Expand All @@ -281,7 +277,7 @@ func (h *Handler) handleClear(w http.ResponseWriter, r *http.Request) {
return
}

states, err := h.sync.Store.States()
states, err := h.sync.Store.States(h.sync.User.Username)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
Expand All @@ -292,7 +288,7 @@ func (h *Handler) handleClear(w http.ResponseWriter, r *http.Request) {
continue
}
state.IsHidden = true
_ = h.sync.Store.SaveState(state)
_ = h.sync.Store.SaveState(state, h.sync.User.Username)
}

response := struct {
Expand All @@ -301,6 +297,10 @@ func (h *Handler) handleClear(w http.ResponseWriter, r *http.Request) {
Status: "ok",
}
err = json.NewEncoder(w).Encode(&response)
if err != nil {
h.Printf("Error encoding response: %v\n", err)
http.Error(w, "Error encoding response", http.StatusInternalServerError)
}
return
}

Expand Down Expand Up @@ -428,7 +428,7 @@ func (h *Handler) handleGoToFile(w http.ResponseWriter, r *http.Request) {
return
}

state, err := h.sync.Store.State(fileID)
state, err := h.sync.Store.State(fileID, h.sync.User.Username)
if err == sync.ErrStateNotFound {
h.Debugf("fetching state failed for %v: %v\n", fileID, err)
http.Error(w, "file not found", http.StatusBadRequest)
Expand Down
171 changes: 115 additions & 56 deletions sync/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@ import (
"time"

"github.com/boltdb/bolt"
"github.com/igungor/go-putio/putio"
)

// buckets
var (
downloadItemsBucket = []byte("download-items")
watchedTorrentsBucket = []byte("watched-torrents")
userAccountBucket = []byte("user-account")
configBucket = []byte("configuration")
defaultsBucket = []byte("defaults")
)

type Error string
Expand Down Expand Up @@ -47,10 +45,7 @@ func (s *Store) Open() error {
s.db = db

err = s.db.Update(func(tx *bolt.Tx) error {
_, err = tx.CreateBucketIfNotExists(downloadItemsBucket)
_, err = tx.CreateBucketIfNotExists(watchedTorrentsBucket)
_, err = tx.CreateBucketIfNotExists(userAccountBucket)
_, err = tx.CreateBucketIfNotExists(configBucket)
_, err = tx.CreateBucketIfNotExists(defaultsBucket)
return err
})
if err != nil {
Expand All @@ -63,29 +58,55 @@ func (s *Store) Close() error { return s.db.Close() }

func (s *Store) Path() string { return s.path }

func (s *Store) CreateBuckets(forUser string) error {
return s.db.Update(func(tx *bolt.Tx) error {
userBkt, err := tx.CreateBucketIfNotExists([]byte(forUser))
if err != nil {
return err
}

buckets := [][]byte{
downloadItemsBucket,
watchedTorrentsBucket,
}

for _, bucket := range buckets {
_, err = userBkt.CreateBucketIfNotExists(bucket)
if err != nil {
return err
}
}
return nil
})
}

// SaveState inserts or updates the given state.
func (s *Store) SaveState(state *State) error {
func (s *Store) SaveState(state *State, forUser string) error {
return s.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(downloadItemsBucket)
key := itob(state.FileID)
userBkt := tx.Bucket([]byte(forUser))
downloadsBkt := userBkt.Bucket(downloadItemsBucket)

key := itob(state.FileID)
var value bytes.Buffer

err := gob.NewEncoder(&value).Encode(state)
if err != nil {
return err
}

return bucket.Put(key, value.Bytes())
return downloadsBkt.Put(key, value.Bytes())
})
}

// State returns a state by the given file ID.
func (s *Store) State(id int64) (*State, error) {
func (s *Store) State(id int64, forUser string) (*State, error) {
var state State
err := s.db.View(func(tx *bolt.Tx) error {
fileid := itob(id)
userBkt := tx.Bucket([]byte(forUser))
downloadsBkt := userBkt.Bucket(downloadItemsBucket)
fileID := itob(id)

value := tx.Bucket(downloadItemsBucket).Get(fileid)
value := downloadsBkt.Get(fileID)
if value == nil {
return ErrStateNotFound
}
Expand All @@ -96,11 +117,18 @@ func (s *Store) State(id int64) (*State, error) {
}

// States returns all the states in the store.
func (s *Store) States() ([]*State, error) {
func (s *Store) States(forUser string) ([]*State, error) {
states := make([]*State, 0)

if forUser == "" {
return states, nil
}

err := s.db.View(func(tx *bolt.Tx) error {
cursor := tx.Bucket(downloadItemsBucket).Cursor()
userBkt := tx.Bucket([]byte(forUser))
downloadsBkt := userBkt.Bucket(downloadItemsBucket)

cursor := downloadsBkt.Cursor()
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
var state State
err := gob.NewDecoder(bytes.NewReader(v)).Decode(&state)
Expand All @@ -120,10 +148,17 @@ func (s *Store) States() ([]*State, error) {
}

// StateN returns the number of states in the store.
func (s *Store) StateN() (int, error) {
func (s *Store) StateN(forUser string) (int, error) {
if forUser == "" {
return 0, nil
}

var n int
err := s.db.View(func(tx *bolt.Tx) error {
cursor := tx.Bucket(downloadItemsBucket).Cursor()
userBkt := tx.Bucket([]byte(forUser))
downloadsBkt := userBkt.Bucket(downloadItemsBucket)

cursor := downloadsBkt.Cursor()
for k, _ := cursor.First(); k != nil; k, _ = cursor.Next() {
n++
}
Expand All @@ -132,54 +167,36 @@ func (s *Store) StateN() (int, error) {
return n, err
}

func (s *Store) SaveUserAccount(info putio.AccountInfo) error {
return s.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(userAccountBucket)
key := []byte(info.Username)
var value bytes.Buffer

err := gob.NewEncoder(&value).Encode(info)
if err != nil {
return err
}

return bucket.Put(key, value.Bytes())
})
}
func (s *Store) Config(forUser string) (*Config, error) {
if forUser == "" {
return s.DefaultConfig()
}

func (s *Store) Config() (*Config, error) {
var cfg Config
var cfg *Config
err := s.db.View(func(tx *bolt.Tx) error {
userBkt := tx.Bucket([]byte(forUser))

key := []byte("config")
value := tx.Bucket(configBucket).Get(key)
if value == nil {
u, err := user.Current()
if err != nil {
return err
}
value := userBkt.Get(key)

cfg = Config{
PollInterval: Duration(defaultPollInterval),
DownloadTo: filepath.Join(u.HomeDir, "putio-sync"),
DownloadFrom: defaultDownloadFrom,
SegmentsPerFile: defaultSegmentsPerFile,
MaxParallelFiles: defaultMaxParallelFiles,
WatchTorrentsFolder: false,
TorrentsFolder: "",
IsPaused: true,
}
return nil
if value == nil {
return ErrConfigNotFound
}

return gob.NewDecoder(bytes.NewReader(value)).Decode(&cfg)
return gob.NewDecoder(bytes.NewReader(value)).Decode(cfg)
})

return &cfg, err
if err == ErrConfigNotFound {
cfg, err = s.DefaultConfig()
}

return cfg, err
}

func (s *Store) SaveConfig(cfg *Config) error {
func (s *Store) SaveConfig(cfg *Config, forUser string) error {
return s.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(configBucket)
userBkt := tx.Bucket([]byte(forUser))

key := []byte("config")
var value bytes.Buffer

Expand All @@ -188,7 +205,49 @@ func (s *Store) SaveConfig(cfg *Config) error {
return err
}

return bucket.Put(key, value.Bytes())
return userBkt.Put(key, value.Bytes())
})
}

func (s *Store) DefaultConfig() (*Config, error) {
u, err := user.Current()
if err != nil {
return nil, err
}
return &Config{
PollInterval: Duration(defaultPollInterval),
DownloadTo: filepath.Join(u.HomeDir, "putio-sync"),
DownloadFrom: defaultDownloadFrom,
SegmentsPerFile: defaultSegmentsPerFile,
MaxParallelFiles: defaultMaxParallelFiles,
IsPaused: true,
WatchTorrentsFolder: false,
TorrentsFolder: "",
}, nil
}

func (s *Store) CurrentUser() (string, error) {
var username []byte
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(defaultsBucket)
username = bkt.Get([]byte("current-user"))
return nil
})
if err != nil {
return "", err
}

if username == nil {
return "", nil
}
return string(username), nil
}

func (s *Store) SaveCurrentUser(username string) error {
return s.db.Update(func(tx *bolt.Tx) error {
bkt := tx.Bucket(defaultsBucket)
key := []byte("current-user")
return bkt.Put(key, []byte(username))
})
}

Expand Down
Loading

0 comments on commit 34eb223

Please sign in to comment.