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  }