From b619b70d22e6f5accf976c6a2ac242c1d3564120 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sun, 23 Jun 2019 00:08:05 -0600 Subject: [PATCH] WIP: saves, finally --- .gitignore | 3 ++ cmd/again/again.go | 29 +++++++++++++---- data/jsondb/jsondb.go | 73 ++++++++++++++++++++++++++++++++++--------- public/app.js | 42 ++++++++++++++++++------- public/index.html | 23 +++----------- webhooks/webhooks.go | 13 ++++---- 6 files changed, 125 insertions(+), 58 deletions(-) diff --git a/.gitignore b/.gitignore index c49fe25..8502325 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ db.json +*.bak +*.tmp +.*.sw* /cmd/again/again /again diff --git a/cmd/again/again.go b/cmd/again/again.go index d44ddde..81291bd 100644 --- a/cmd/again/again.go +++ b/cmd/again/again.go @@ -140,19 +140,36 @@ func (s *scheduler) Create(w http.ResponseWriter, r *http.Request) { // TODO validate user accessID := r.Context().Value("token").(string) + /* + br, bw := io.Pipe() + b := io.TeeReader(r.Body, bw) + go func() { + fmt.Println("reading from reader...") + x, _ := ioutil.ReadAll(b) + fmt.Println("cool beans and all") + fmt.Println(string(x)) + bw.Close() + }() + decoder := json.NewDecoder(br) + */ decoder := json.NewDecoder(r.Body) sched := &again.Schedule{} - err := decoder.Decode(s) + err := decoder.Decode(sched) + if nil != err { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + fmt.Printf("New Schedule:\n%#v\n", sched) + + // TODO validate and modify + sched.AccessID = accessID + sched2, err := s.DB.Set(*sched) if nil != err { http.Error(w, err.Error(), http.StatusInternalServerError) return } - // TODO validate and modify - sched.AccessID = accessID - s.DB.Set(*sched) - - buf, err := json.Marshal(sched) + buf, err := json.Marshal(sched2) if nil != err { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/data/jsondb/jsondb.go b/data/jsondb/jsondb.go index 3e7620c..4aad138 100644 --- a/data/jsondb/jsondb.go +++ b/data/jsondb/jsondb.go @@ -1,6 +1,9 @@ package jsondb import ( + "crypto/rand" + "crypto/subtle" + "encoding/hex" "encoding/json" "fmt" "net/url" @@ -89,11 +92,15 @@ type Schedule struct { Webhooks []again.Webhook `json:"webhooks" db"webhooks"` } +func ctcmp(x string, y string) bool { + return 1 == subtle.ConstantTimeCompare([]byte(x), []byte(y)) +} + func (db *JSONDB) List(accessID string) ([]*again.Schedule, error) { schedules := []*again.Schedule{} for i := range db.json.Schedules { s := db.json.Schedules[i] - if !s.Disabled && accessID == s.AccessID { + if !s.Disabled && ctcmp(accessID, s.AccessID) { schedules = append(schedules, &again.Schedule{ ID: s.ID, AccessID: s.AccessID, @@ -108,25 +115,41 @@ func (db *JSONDB) List(accessID string) ([]*again.Schedule, error) { return schedules, nil } -func (db *JSONDB) get(id string) *Schedule { +func (db *JSONDB) get(id string) (int, *Schedule) { for i := range db.json.Schedules { schedule := db.json.Schedules[i] - if id == schedule.AccessID { - return &schedule + if ctcmp(id, schedule.ID) { + return i, &schedule } } - return nil + return -1, nil +} + +func genID() (string, error) { + b := make([]byte, 16) + _, err := rand.Read(b) + if nil != err { + return "", err + } + return hex.EncodeToString(b), nil } func (db *JSONDB) Set(s again.Schedule) (*again.Schedule, error) { - newSchedules := []Schedule{} - - for i := range db.json.Schedules { - old := db.json.Schedules[i] - if s.ID == old.AccessID { - continue + exists := false + index := -1 + if "" == s.ID { + id, err := genID() + if nil != err { + return nil, err + } + s.ID = id + } else { + i, old := db.get(s.ID) + index = i + exists = nil != old + if !exists || !ctcmp(old.AccessID, s.AccessID) { + return nil, fmt.Errorf("invalid id") } - newSchedules = append(newSchedules, old) } schedule := Schedule{ @@ -138,7 +161,12 @@ func (db *JSONDB) Set(s again.Schedule) (*again.Schedule, error) { NextRunAt: s.NextRunAt, Webhooks: s.Webhooks, } - newSchedules = append(newSchedules, schedule) + + if exists { + db.json.Schedules[index] = schedule + } else { + db.json.Schedules = append(db.json.Schedules, schedule) + } err := db.save(s.AccessID) if nil != err { @@ -150,17 +178,32 @@ func (db *JSONDB) Set(s again.Schedule) (*again.Schedule, error) { func (db *JSONDB) save(accessID string) error { // TODO per-user files (w/ mutex lock or channel on open and write) - f, err := os.OpenFile(db.path, os.O_RDWR|os.O_CREATE, 0700) + tmppath := db.path + ".tmp" + bakpath := db.path + ".bak" + + os.Remove(tmppath) // ignore error + f, err := os.OpenFile(tmppath, os.O_RDWR|os.O_CREATE, 0700) if nil != err { return err } encoder := json.NewEncoder(f) - err = encoder.Encode(db.json.Schedules) + err = encoder.Encode(db.json) f.Close() if nil != err { return err } + os.Remove(bakpath) // ignore error + err = os.Rename(db.path, bakpath) + if nil != err { + return err + } + + err = os.Rename(tmppath, db.path) + if nil != err { + return err + } + return nil } diff --git a/public/app.js b/public/app.js index 4d56ec2..8059066 100644 --- a/public/app.js +++ b/public/app.js @@ -40,6 +40,7 @@ d = new Date(d.valueOf() + minutes * 60 * 1000); $('.js-date').value = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()); $('.js-time').value = pad(d.getHours()) + ':' + pad(d.getMinutes()); + $('.js-url').value = 'https://enfqtbjh5ghw.x.pipedream.net'; console.log('hello'); @@ -88,11 +89,9 @@ var $hook = $('.js-schedule'); //var deviceId = $hook.closest('.js-schedule').querySelector('.js-id').value; var schedule = { - schedule: { - date: $('.js-date', $hook).value, - time: $('.js-time', $hook).value, - tz: $('.js-tz', $hook).value - }, + date: $('.js-date', $hook).value, + time: $('.js-time', $hook).value, + tz: $('.js-tz', $hook).value, webhooks: [] }; var hook = { @@ -139,11 +138,12 @@ return resp .json() .then(function(data) { - if (!data.schedule) { + if (!data.date || !data.webhooks) { + console.error(data); throw new Error('something bad happened'); } - state.account.schedules.webhooks.push(resp.data.schedule); + state.account.schedules.push(resp.data); displayAccount(state.account); }) @@ -318,16 +318,34 @@ }) ); $('.js-schedules-list').hidden = true; - $('.js-schedules').hidden = false; return window .fetch('/api/v0/schedules', { headers: { Authorization: getToken() } }) .then(function(resp) { - return resp.json().then(function(schedules) { - allSchedules = schedules; - renderSchedules(schedules); - }); + return resp + .clone() + .json() + .then(function(schedules) { + allSchedules = schedules; + renderSchedules(schedules); + $('.js-schedules').hidden = false; + }) + .catch(function(e) { + console.error("Didn't parse JSON:"); + console.error(e); + console.log(resp); + $('.js-schedules-list').hidden = false; + window.alert(resp.status + ': ' + resp.statusText); + return resp.text().then(function(text) { + window.alert(text); + }); + }); + }) + .catch(function(e) { + console.error('Request Error'); + console.error(e); + window.alert('Network error. Are you online?'); }); } diff --git a/public/index.html b/public/index.html index 549029e..7fc0865 100644 --- a/public/index.html +++ b/public/index.html @@ -22,9 +22,7 @@
- +