github.com/rpdict/ponzu@v0.10.1-0.20190226054626-477f29d6bf5e/system/db/upload.go (about) 1 package db 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/json" 7 "fmt" 8 "log" 9 "net/url" 10 "strconv" 11 "strings" 12 "time" 13 14 "github.com/rpdict/ponzu/system/item" 15 16 "github.com/boltdb/bolt" 17 "github.com/gofrs/uuid" 18 "github.com/gorilla/schema" 19 ) 20 21 // SetUpload stores information about files uploaded to the system 22 func SetUpload(target string, data url.Values) (int, error) { 23 parts := strings.Split(target, ":") 24 if parts[0] != "__uploads" { 25 return 0, fmt.Errorf("cannot call SetUpload with target type: %s", parts[0]) 26 } 27 pid := parts[1] 28 29 if data.Get("uuid") == "" || 30 data.Get("uuid") == (uuid.UUID{}).String() { 31 32 // set new UUID for upload 33 uid, err := uuid.NewV4() 34 if err != nil { 35 return 0, err 36 } 37 data.Set("uuid", uid.String()) 38 } 39 40 if data.Get("slug") == "" { 41 // create slug based on filename and timestamp/updated fields 42 slug := data.Get("name") 43 slug, err := checkSlugForDuplicate(slug) 44 if err != nil { 45 return 0, err 46 } 47 data.Set("slug", slug) 48 } 49 50 ts := fmt.Sprintf("%d", time.Now().Unix()*1000) 51 if data.Get("timestamp") == "" { 52 data.Set("timestamp", ts) 53 } 54 55 data.Set("updated", ts) 56 57 // store in database 58 var id uint64 59 var err error 60 err = store.Update(func(tx *bolt.Tx) error { 61 b, err := tx.CreateBucketIfNotExists([]byte("__uploads")) 62 if err != nil { 63 return err 64 } 65 66 if pid == "-1" { 67 // get sequential ID for item 68 id, err = b.NextSequence() 69 if err != nil { 70 return err 71 } 72 data.Set("id", fmt.Sprintf("%d", id)) 73 } else { 74 uid, err := strconv.ParseInt(pid, 10, 64) 75 if err != nil { 76 return err 77 } 78 id = uint64(uid) 79 data.Set("id", fmt.Sprintf("%d", id)) 80 } 81 82 file := &item.FileUpload{} 83 dec := schema.NewDecoder() 84 dec.SetAliasTag("json") // allows simpler struct tagging when creating a content type 85 dec.IgnoreUnknownKeys(true) // will skip over form values submitted, but not in struct 86 err = dec.Decode(file, data) 87 if err != nil { 88 return err 89 } 90 91 // marshal data to json for storage 92 j, err := json.Marshal(file) 93 if err != nil { 94 return err 95 } 96 97 uploadKey, err := key(data.Get("id")) 98 if err != nil { 99 return err 100 } 101 err = b.Put(uploadKey, j) 102 if err != nil { 103 return err 104 } 105 106 // add slug to __contentIndex for lookup 107 b, err = tx.CreateBucketIfNotExists([]byte("__contentIndex")) 108 if err != nil { 109 return err 110 } 111 112 k := []byte(data.Get("slug")) 113 v := []byte(fmt.Sprintf("__uploads:%d", id)) 114 115 err = b.Put(k, v) 116 if err != nil { 117 return err 118 } 119 120 return nil 121 }) 122 if err != nil { 123 return 0, err 124 } 125 126 return int(id), nil 127 } 128 129 // Upload returns the value for an upload by its target (__uploads:{id}) 130 func Upload(target string) ([]byte, error) { 131 val := &bytes.Buffer{} 132 parts := strings.Split(target, ":") 133 if len(parts) < 2 { 134 return nil, fmt.Errorf("invalid target for upload: %s", target) 135 } 136 137 id, err := key(parts[1]) 138 if err != nil { 139 return nil, err 140 } 141 142 err = store.View(func(tx *bolt.Tx) error { 143 b := tx.Bucket([]byte("__uploads")) 144 if b == nil { 145 return bolt.ErrBucketNotFound 146 } 147 148 j := b.Get(id) 149 _, err := val.Write(j) 150 return err 151 }) 152 153 return val.Bytes(), err 154 } 155 156 // UploadBySlug returns the value for an upload by its slug 157 func UploadBySlug(slug string) ([]byte, error) { 158 val := &bytes.Buffer{} 159 // get target from __contentIndex or return nil if not exists 160 err := store.View(func(tx *bolt.Tx) error { 161 b := tx.Bucket([]byte("__contentIndex")) 162 if b == nil { 163 return bolt.ErrBucketNotFound 164 } 165 166 v := b.Get([]byte(slug)) 167 if v == nil { 168 return fmt.Errorf("no value for key '%s' in __contentIndex", slug) 169 } 170 171 j, err := Upload(string(v)) 172 if err != nil { 173 return err 174 } 175 176 _, err = val.Write(j) 177 178 return err 179 }) 180 181 return val.Bytes(), err 182 } 183 184 // UploadAll returns a [][]byte containing all upload data from the system 185 func UploadAll() [][]byte { 186 var uploads [][]byte 187 err := store.View(func(tx *bolt.Tx) error { 188 b := tx.Bucket([]byte("__uploads")) 189 if b == nil { 190 return bolt.ErrBucketNotFound 191 } 192 193 numKeys := b.Stats().KeyN 194 uploads = make([][]byte, 0, numKeys) 195 196 return b.ForEach(func(k, v []byte) error { 197 uploads = append(uploads, v) 198 return nil 199 }) 200 }) 201 if err != nil { 202 log.Println("Error in UploadAll:", err) 203 return nil 204 } 205 206 return uploads 207 } 208 209 // DeleteUpload removes the value for an upload at its key id, based on the 210 // target provided i.e. __uploads:{id} 211 func DeleteUpload(target string) error { 212 parts := strings.Split(target, ":") 213 if len(parts) < 2 { 214 return fmt.Errorf("Error deleting upload, invalid target %s", target) 215 } 216 id, err := key(parts[1]) 217 if err != nil { 218 return err 219 } 220 221 return store.Update(func(tx *bolt.Tx) error { 222 b := tx.Bucket([]byte(parts[0])) 223 if b == nil { 224 return bolt.ErrBucketNotFound 225 } 226 227 return b.Delete(id) 228 }) 229 } 230 231 func key(sid string) ([]byte, error) { 232 id, err := strconv.Atoi(sid) 233 if err != nil { 234 return nil, err 235 } 236 237 b := make([]byte, 8) 238 binary.BigEndian.PutUint64(b, uint64(id)) 239 return b, err 240 }