summaryrefslogtreecommitdiff
path: root/system/db/upload.go
diff options
context:
space:
mode:
authorSteve Manuel <nilslice@gmail.com>2017-05-01 01:51:46 -0500
committerGitHub <noreply@github.com>2017-05-01 01:51:46 -0500
commit8319420935c223e432bc4c386bd7d26b03f01b47 (patch)
tree8ebe1e3f0780f66530b723ce9b48f69ae664d764 /system/db/upload.go
parent7092fb8979869f3c09b364d454d8d8081bb7c0bc (diff)
parent8a1d091a0c6d55dbd6c713b2240bee8550541e7b (diff)
Merge pull request #137 from ponzu-cms/ponzu-dev
[core] adding file upload metadata type and API
Diffstat (limited to 'system/db/upload.go')
-rw-r--r--system/db/upload.go235
1 files changed, 235 insertions, 0 deletions
diff --git a/system/db/upload.go b/system/db/upload.go
new file mode 100644
index 0000000..beeee2d
--- /dev/null
+++ b/system/db/upload.go
@@ -0,0 +1,235 @@
+package db
+
+import (
+ "bytes"
+ "encoding/binary"
+ "encoding/json"
+ "fmt"
+ "log"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/ponzu-cms/ponzu/system/item"
+
+ "github.com/boltdb/bolt"
+ "github.com/gorilla/schema"
+ uuid "github.com/satori/go.uuid"
+)
+
+// SetUpload stores information about files uploaded to the system
+func SetUpload(target string, data url.Values) (int, error) {
+ parts := strings.Split(target, ":")
+ if parts[0] != "__uploads" {
+ return 0, fmt.Errorf("cannot call SetUpload with target type: %s", parts[0])
+ }
+ pid := parts[1]
+
+ if data.Get("uuid") == "" ||
+ data.Get("uuid") == (uuid.UUID{}).String() {
+ // set new UUID for upload
+ data.Set("uuid", uuid.NewV4().String())
+ }
+
+ if data.Get("slug") == "" {
+ // create slug based on filename and timestamp/updated fields
+ slug := data.Get("name")
+ slug, err := checkSlugForDuplicate(slug)
+ if err != nil {
+ return 0, err
+ }
+ data.Set("slug", slug)
+ }
+
+ ts := fmt.Sprintf("%d", time.Now().Unix()*1000)
+ if data.Get("timestamp") == "" {
+ data.Set("timestamp", ts)
+ }
+
+ data.Set("updated", ts)
+
+ // store in database
+ var id uint64
+ var err error
+ err = store.Update(func(tx *bolt.Tx) error {
+ b, err := tx.CreateBucketIfNotExists([]byte("__uploads"))
+ if err != nil {
+ return err
+ }
+
+ if pid == "-1" {
+ // get sequential ID for item
+ id, err = b.NextSequence()
+ if err != nil {
+ return err
+ }
+ data.Set("id", fmt.Sprintf("%d", id))
+ } else {
+ uid, err := strconv.ParseInt(pid, 10, 64)
+ if err != nil {
+ return err
+ }
+ id = uint64(uid)
+ data.Set("id", fmt.Sprintf("%d", id))
+ }
+
+ file := &item.FileUpload{}
+ 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(file, data)
+ if err != nil {
+ return err
+ }
+
+ // marshal data to json for storage
+ j, err := json.Marshal(file)
+ if err != nil {
+ return err
+ }
+
+ uploadKey, err := key(data.Get("id"))
+ if err != nil {
+ return err
+ }
+ err = b.Put(uploadKey, j)
+ if err != nil {
+ return err
+ }
+
+ // add slug to __contentIndex for lookup
+ b, err = tx.CreateBucketIfNotExists([]byte("__contentIndex"))
+ if err != nil {
+ return err
+ }
+
+ k := []byte(data.Get("slug"))
+ v := []byte(fmt.Sprintf("__uploads:%d", id))
+
+ err = b.Put(k, v)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ })
+ if err != nil {
+ return 0, err
+ }
+
+ return int(id), nil
+}
+
+// Upload returns the value for an upload by its target (__uploads:{id})
+func Upload(target string) ([]byte, error) {
+ val := &bytes.Buffer{}
+ parts := strings.Split(target, ":")
+ if len(parts) < 2 {
+ return nil, fmt.Errorf("invalid target for upload: %s", target)
+ }
+
+ id, err := key(parts[1])
+ if err != nil {
+ return nil, err
+ }
+
+ err = store.View(func(tx *bolt.Tx) error {
+ b := tx.Bucket([]byte("__uploads"))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
+
+ j := b.Get(id)
+ _, err := val.Write(j)
+ return err
+ })
+
+ return val.Bytes(), err
+}
+
+// UploadBySlug returns the value for an upload by its slug
+func UploadBySlug(slug string) ([]byte, error) {
+ val := &bytes.Buffer{}
+ // get target from __contentIndex or return nil if not exists
+ err := store.View(func(tx *bolt.Tx) error {
+ b := tx.Bucket([]byte("__contentIndex"))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
+
+ v := b.Get([]byte(slug))
+ if v == nil {
+ return fmt.Errorf("no value for key '%s' in __contentIndex", slug)
+ }
+
+ j, err := Upload(string(v))
+ if err != nil {
+ return err
+ }
+
+ _, err = val.Write(j)
+
+ return err
+ })
+
+ return val.Bytes(), err
+}
+
+// UploadAll returns a [][]byte containing all upload data from the system
+func UploadAll() [][]byte {
+ var uploads [][]byte
+ err := store.View(func(tx *bolt.Tx) error {
+ b := tx.Bucket([]byte("__uploads"))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
+
+ numKeys := b.Stats().KeyN
+ uploads = make([][]byte, 0, numKeys)
+
+ return b.ForEach(func(k, v []byte) error {
+ uploads = append(uploads, v)
+ return nil
+ })
+ })
+ if err != nil {
+ log.Println("Error in UploadAll:", err)
+ return nil
+ }
+
+ return uploads
+}
+
+// DeleteUpload removes the value for an upload at its key id, based on the
+// target provided i.e. __uploads:{id}
+func DeleteUpload(target string) error {
+ parts := strings.Split(target, ":")
+ if len(parts) < 2 {
+ return fmt.Errorf("Error deleting upload, invalid target %s", target)
+ }
+ id, err := key(parts[1])
+ if err != nil {
+ return err
+ }
+
+ return store.Update(func(tx *bolt.Tx) error {
+ b := tx.Bucket([]byte(parts[0]))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
+
+ return b.Delete(id)
+ })
+}
+
+func key(sid string) ([]byte, error) {
+ id, err := strconv.Atoi(sid)
+ if err != nil {
+ return nil, err
+ }
+
+ b := make([]byte, 8)
+ binary.BigEndian.PutUint64(b, uint64(id))
+ return b, err
+}