diff options
-rw-r--r-- | system/api/update.go | 6 | ||||
-rw-r--r-- | system/db/content.go | 125 |
2 files changed, 50 insertions, 81 deletions
diff --git a/system/api/update.go b/system/api/update.go index 00933fb..507d3af 100644 --- a/system/api/update.go +++ b/system/api/update.go @@ -52,8 +52,8 @@ func updateContentHandler(res http.ResponseWriter, req *http.Request) { } id := req.URL.Query().Get("id") - if id == "" { - log.Println("[Update] attempt to submit update with missing id from:", req.RemoteAddr) + if !db.IsValidID(id) { + log.Println("[Update] attempt to submit update with missing or invalid id from:", req.RemoteAddr) res.WriteHeader(http.StatusBadRequest) return } @@ -160,7 +160,7 @@ func updateContentHandler(res http.ResponseWriter, req *http.Request) { // set specifier for db bucket in case content is/isn't Trustable var spec string - _, err = db.UpdateContent(t+spec+":"+id, req.PostForm) + _, err = db.SetContent(t+spec+":"+id, req.PostForm) if err != nil { log.Println("[Update] error calling UpdateContent:", err) res.WriteHeader(http.StatusInternalServerError) diff --git a/system/db/content.go b/system/db/content.go index 8064b03..4517fbe 100644 --- a/system/db/content.go +++ b/system/db/content.go @@ -3,6 +3,7 @@ package db import ( "bytes" "encoding/json" + "errors" "fmt" "log" "net/url" @@ -17,29 +18,18 @@ import ( uuid "github.com/satori/go.uuid" ) -// SetContent inserts or updates values in the database. -// The `target` argument is a string made up of namespace:id (string:int) -func SetContent(target string, data url.Values) (int, error) { - t := strings.Split(target, ":") - ns, id := t[0], t[1] - - // check if content id == -1 (indicating new post). - // if so, run an insert which will assign the next auto incremented int. - // this is done because boltdb begins its bucket auto increment value at 0, - // which is the zero-value of an int in the Item struct field for ID. - // this is a problem when the original first post (with auto ID = 0) gets - // overwritten by any new post, originally having no ID, defauting to 0. - if id == "-1" { - return insert(ns, data) +func IsValidID(id string) bool { + // ID should be a non-negative integer. + // ID of -1 is special for new posts, not updates. + if i, err := strconv.Atoi(id); err != nil || i < 0 { + return false } - - return update(ns, id, data) + return true } -// UpdateContent inserts or updates values in the database. -// Updated content will be merged into existing values from the database. +// SetContent inserts or updates values in the database. // The `target` argument is a string made up of namespace:id (string:int) -func UpdateContent(target string, data url.Values) (int, error) { +func SetContent(target string, data url.Values) (int, error) { t := strings.Split(target, ":") ns, id := t[0], t[1] @@ -58,16 +48,11 @@ func UpdateContent(target string, data url.Values) (int, error) { if err != nil { return 0, err } + return update(ns, id, data, &existingContent) +} - // Unmarsal the existing values - s := item.Types[ns]() - - err = json.Unmarshal(existingContent, &s) - if err != nil { - log.Println("Error decoding json while updating", ns, ":", err) - return 0, err - } - +// update can support merge or replace behavior +func update(ns, id string, data url.Values, existingContent *[]byte) (int, error) { var specifier string // i.e. __pending, __sorted, etc. if strings.Contains(ns, "__") { spec := strings.Split(ns, "__") @@ -80,22 +65,17 @@ func UpdateContent(target string, data url.Values) (int, error) { return 0, err } - // Don't allow the Item fields to be updated from form values - data.Del("id") - data.Del("uuid") - data.Del("slug") - - dec := schema.NewDecoder() - dec.SetAliasTag("json") // allows simpler struct tagging when creating a content type - dec.IgnoreUnknownKeys(true) // will skip over form values submitted, but not in struct - err = dec.Decode(s, data) - if err != nil { - return 0, err - } - - j, err := json.Marshal(s) - if err != nil { - return 0, err + var j []byte + if existingContent == nil { + j, err = postToJSON(ns, data) + if err != nil { + return 0, err + } + } else { + j, err = mergeData(ns, data, *existingContent) + if err != nil { + return 0, err + } } err = store.Update(func(tx *bolt.Tx) error { @@ -128,52 +108,41 @@ func UpdateContent(target string, data url.Values) (int, error) { return cid, nil } -func update(ns, id string, data url.Values) (int, error) { - var specifier string // i.e. __pending, __sorted, etc. - if strings.Contains(ns, "__") { - spec := strings.Split(ns, "__") - ns = spec[0] - specifier = "__" + spec[1] - } - - cid, err := strconv.Atoi(id) - if err != nil { - return 0, err +func mergeData(ns string, data url.Values, existingContent []byte) ([]byte, error) { + var j []byte + t, ok := item.Types[ns] + if !ok { + return j, errors.New("Invalid type.") + // handle } - j, err := postToJSON(ns, data) + // Unmarsal the existing values + s := t() + err := json.Unmarshal(existingContent, &s) if err != nil { - return 0, err + log.Println("Error decoding json while updating", ns, ":", err) + return j, err } - err = store.Update(func(tx *bolt.Tx) error { - b, err := tx.CreateBucketIfNotExists([]byte(ns + specifier)) - if err != nil { - return err - } - - err = b.Put([]byte(fmt.Sprintf("%d", cid)), j) - if err != nil { - return err - } + // Don't allow the Item fields to be updated from form values + data.Del("id") + data.Del("uuid") + data.Del("slug") - return nil - }) + dec := schema.NewDecoder() + dec.SetAliasTag("json") // allows simpler struct tagging when creating a content type + dec.IgnoreUnknownKeys(true) // will skip over form values submitted, but not in struct + err = dec.Decode(s, data) if err != nil { - return 0, nil - } - - if specifier == "" { - go SortContent(ns) + return j, err } - // update changes data, so invalidate client caching - err = InvalidateCache() + j, err = json.Marshal(s) if err != nil { - return 0, err + return j, err } - return cid, nil + return j, nil } func insert(ns string, data url.Values) (int, error) { |