summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--system/api/handlers.go54
-rw-r--r--system/api/server.go4
-rw-r--r--system/db/addon.go22
-rw-r--r--system/db/config.go78
-rw-r--r--system/db/content.go46
-rw-r--r--system/db/init.go3
-rw-r--r--system/db/user.go20
7 files changed, 162 insertions, 65 deletions
diff --git a/system/api/handlers.go b/system/api/handlers.go
index 2473c24..f073e16 100644
--- a/system/api/handlers.go
+++ b/system/api/handlers.go
@@ -2,6 +2,7 @@ package api
import (
"bytes"
+ "compress/gzip"
"encoding/json"
"log"
"net/http"
@@ -13,6 +14,7 @@ import (
"github.com/ponzu-cms/ponzu/system/item"
)
+// deprecating from API, but going to provide code here in case someone wants it
func typesHandler(res http.ResponseWriter, req *http.Request) {
var types = []string{}
for t, fn := range item.Types {
@@ -27,7 +29,7 @@ func typesHandler(res http.ResponseWriter, req *http.Request) {
return
}
- sendData(res, j, http.StatusOK)
+ sendData(res, req, j, http.StatusOK)
}
func contentsHandler(res http.ResponseWriter, req *http.Request) {
@@ -91,7 +93,7 @@ func contentsHandler(res http.ResponseWriter, req *http.Request) {
return
}
- sendData(res, j, http.StatusOK)
+ sendData(res, req, j, http.StatusOK)
}
func contentHandler(res http.ResponseWriter, req *http.Request) {
@@ -134,7 +136,7 @@ func contentHandler(res http.ResponseWriter, req *http.Request) {
return
}
- sendData(res, j, http.StatusOK)
+ sendData(res, req, j, http.StatusOK)
}
func contentHandlerBySlug(res http.ResponseWriter, req *http.Request) {
@@ -171,7 +173,7 @@ func contentHandlerBySlug(res http.ResponseWriter, req *http.Request) {
return
}
- sendData(res, j, http.StatusOK)
+ sendData(res, req, j, http.StatusOK)
}
func hide(it interface{}, res http.ResponseWriter, req *http.Request) bool {
@@ -233,8 +235,8 @@ func toJSON(data []string) ([]byte, error) {
// sendData should be used any time you want to communicate
// data back to a foreign client
-func sendData(res http.ResponseWriter, data []byte, code int) {
- res, cors := responseWithCORS(res)
+func sendData(res http.ResponseWriter, req *http.Request, data []byte, code int) {
+ res, cors := responseWithCORS(res, req)
if !cors {
return
}
@@ -256,21 +258,34 @@ func sendPreflight(res http.ResponseWriter) {
return
}
-func responseWithCORS(res http.ResponseWriter) (http.ResponseWriter, bool) {
+func responseWithCORS(res http.ResponseWriter, req *http.Request) (http.ResponseWriter, bool) {
if db.ConfigCache("cors_disabled").(bool) == true {
+ // check origin matches config domain and Allow
+ domain := db.ConfigCache("domain").(string)
+
+ // currently, this will check for exact match. will need feedback to
+ // determine if subdomains should be allowed or allow multiple domains
+ // in config
+ if req.Origin == domain {
+ // apply limited CORS headers and return
+ res.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type")
+ res.Header().Set("Access-Control-Allow-Origin", domain)
+ return res, true
+ }
+
// disallow request
res.WriteHeader(http.StatusForbidden)
return res, false
}
- // apply CORS headers and return
+ // apply full CORS headers and return
res.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type")
res.Header().Set("Access-Control-Allow-Origin", "*")
return res, true
}
-// CORS wraps a HandleFunc to respond to OPTIONS requests properly
+// CORS wraps a HandlerFunc to respond to OPTIONS requests properly
func CORS(next http.HandlerFunc) http.HandlerFunc {
return db.CacheControl(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodOptions {
@@ -282,7 +297,7 @@ func CORS(next http.HandlerFunc) http.HandlerFunc {
}))
}
-// Record wraps a HandleFunc to record API requests for analytical purposes
+// Record wraps a HandlerFunc to record API requests for analytical purposes
func Record(next http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
go analytics.Record(req)
@@ -290,3 +305,22 @@ func Record(next http.HandlerFunc) http.HandlerFunc {
next.ServeHTTP(res, req)
})
}
+
+// Gzip wraps a HandlerFunc to compress responses when possible
+func Gzip(next http.HandlerFunc) http.HandlerFunc {
+ return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
+ // check if req header content-encoding supports gzip
+ if strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") {
+ // gzip response data
+ gzres, err := gzip.NewWriter(res)
+ if err != nil {
+ log.Println("Error creating gzip writer in Gzip middleware.")
+ next.ServeHTTP(res, req)
+ }
+
+ next.ServeHTTP(gzres, req)
+ }
+
+ next.ServeHTTP(res, req)
+ })
+}
diff --git a/system/api/server.go b/system/api/server.go
index 4b8b22e..0bdc48a 100644
--- a/system/api/server.go
+++ b/system/api/server.go
@@ -4,11 +4,9 @@ import "net/http"
// Run adds Handlers to default http listener for API
func Run() {
- http.HandleFunc("/api/types", Record(CORS(typesHandler)))
-
http.HandleFunc("/api/contents", Record(CORS(contentsHandler)))
http.HandleFunc("/api/content", Record(CORS(contentHandler)))
- http.HandleFunc("/api/content/external", Record(CORS(externalContentHandler)))
+ http.HandleFunc("/api/content/external", Record(externalContentHandler))
}
diff --git a/system/db/addon.go b/system/db/addon.go
index f4621fa..a145293 100644
--- a/system/db/addon.go
+++ b/system/db/addon.go
@@ -24,6 +24,9 @@ func Addon(key string) ([]byte, error) {
err := store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__addons"))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
val := b.Get([]byte(key))
@@ -56,12 +59,16 @@ func SetAddon(data url.Values, kind interface{}) error {
v, err := json.Marshal(kind)
+ k := data.Get("addon_reverse_dns")
+ if k == "" {
+ name := data.Get("addon_name")
+ return fmt.Errorf(`Addon "%s" has no identifier to use as key.`, name)
+ }
+
err = store.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__addons"))
- k := data.Get("addon_reverse_dns")
- if k == "" {
- name := data.Get("addon_name")
- return fmt.Errorf(`Addon "%s" has no identifier to use as key.`, name)
+ if b == nil {
+ return bolt.ErrBucketNotFound
}
err := b.Put([]byte(k), v)
@@ -84,6 +91,10 @@ func AddonAll() [][]byte {
err := store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__addons"))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
+
err := b.ForEach(func(k, v []byte) error {
all = append(all, v)
@@ -107,6 +118,9 @@ func AddonAll() [][]byte {
func DeleteAddon(key string) error {
err := store.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__addons"))
+ if b == nil {
+ bolt.ErrBucketNotFound
+ }
if err := b.Delete([]byte(key)); err != nil {
return err
diff --git a/system/db/config.go b/system/db/config.go
index 48da4b0..5a93353 100644
--- a/system/db/config.go
+++ b/system/db/config.go
@@ -22,49 +22,53 @@ func init() {
// SetConfig sets key:value pairs in the db for configuration settings
func SetConfig(data url.Values) error {
var j []byte
- err := store.Update(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte("__config"))
- // check for any multi-value fields (ex. checkbox fields)
- // and correctly format for db storage. Essentially, we need
- // fieldX.0: value1, fieldX.1: value2 => fieldX: []string{value1, value2}
- var discardKeys []string
- for k, v := range data {
- if strings.Contains(k, ".") {
- key := strings.Split(k, ".")[0]
-
- if data.Get(key) == "" {
- data.Set(key, v[0])
- } else {
- data.Add(key, v[0])
- }
-
- discardKeys = append(discardKeys, k)
+ // check for any multi-value fields (ex. checkbox fields)
+ // and correctly format for db storage. Essentially, we need
+ // fieldX.0: value1, fieldX.1: value2 => fieldX: []string{value1, value2}
+ var discardKeys []string
+ for k, v := range data {
+ if strings.Contains(k, ".") {
+ key := strings.Split(k, ".")[0]
+
+ if data.Get(key) == "" {
+ data.Set(key, v[0])
+ } else {
+ data.Add(key, v[0])
}
- }
- for _, discardKey := range discardKeys {
- data.Del(discardKey)
+ discardKeys = append(discardKeys, k)
}
+ }
- cfg := &config.Config{}
- 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(cfg, data)
- if err != nil {
- return err
- }
+ for _, discardKey := range discardKeys {
+ data.Del(discardKey)
+ }
- // check for "invalidate" value to reset the Etag
- if len(cfg.CacheInvalidate) > 0 && cfg.CacheInvalidate[0] == "invalidate" {
- cfg.Etag = NewEtag()
- cfg.CacheInvalidate = []string{}
- }
+ cfg := &config.Config{}
+ 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(cfg, data)
+ if err != nil {
+ return err
+ }
- j, err = json.Marshal(cfg)
- if err != nil {
- return err
+ // check for "invalidate" value to reset the Etag
+ if len(cfg.CacheInvalidate) > 0 && cfg.CacheInvalidate[0] == "invalidate" {
+ cfg.Etag = NewEtag()
+ cfg.CacheInvalidate = []string{}
+ }
+
+ j, err = json.Marshal(cfg)
+ if err != nil {
+ return err
+ }
+
+ err = store.Update(func(tx *bolt.Tx) error {
+ b := tx.Bucket([]byte("__config"))
+ if b == nil {
+ return bolt.ErrBucketNotFound
}
err = b.Put([]byte("settings"), j)
@@ -117,7 +121,7 @@ func ConfigAll() ([]byte, error) {
err := store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__config"))
if b == nil {
- return fmt.Errorf("Error finding bucket: %s", "__config")
+ return bolt.ErrBucketNotFound
}
_, err := val.Write(b.Get([]byte("settings")))
if err != nil {
diff --git a/system/db/content.go b/system/db/content.go
index 010e5cb..d9096ae 100644
--- a/system/db/content.go
+++ b/system/db/content.go
@@ -49,17 +49,17 @@ func update(ns, id string, data url.Values) (int, error) {
return 0, err
}
+ j, err := postToJSON(ns, data)
+ if err != nil {
+ return 0, err
+ }
+
err = store.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte(ns + specifier))
if err != nil {
return err
}
- j, err := postToJSON(ns, data)
- if err != nil {
- return err
- }
-
err = b.Put([]byte(fmt.Sprintf("%d", cid)), j)
if err != nil {
return err
@@ -134,6 +134,10 @@ func insert(ns string, data url.Values) (int, error) {
// store the slug,type:id in contentIndex if public content
if specifier == "" {
ci := tx.Bucket([]byte("__contentIndex"))
+ if ci == nil {
+ return bolt.ErrBucketNotFound
+ }
+
k := []byte(data.Get("slug"))
v := []byte(fmt.Sprintf("%s:%d", ns, effectedID))
err := ci.Put(k, v)
@@ -168,7 +172,12 @@ func DeleteContent(target string, data url.Values) error {
ns, id := t[0], t[1]
err := store.Update(func(tx *bolt.Tx) error {
- err := tx.Bucket([]byte(ns)).Delete([]byte(id))
+ b := tx.Bucket([]byte(ns))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
+
+ err := b.Delete([]byte(id))
if err != nil {
return err
}
@@ -176,7 +185,12 @@ func DeleteContent(target string, data url.Values) error {
// if content has a slug, also delete it from __contentIndex
slug := data.Get("slug")
if slug != "" {
- err := tx.Bucket([]byte("__contentIndex")).Delete([]byte(slug))
+ ci := tx.Bucket([]byte("__contentIndex"))
+ if ci == nil {
+ return bolt.ErrBucketNotFound
+ }
+
+ err := ci.Delete([]byte(slug))
if err != nil {
return err
}
@@ -212,6 +226,10 @@ func Content(target string) ([]byte, error) {
val := &bytes.Buffer{}
err := store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(ns))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
+
_, err := val.Write(b.Get([]byte(id)))
if err != nil {
log.Println(err)
@@ -235,6 +253,9 @@ func ContentBySlug(slug string) (string, []byte, error) {
var t, id string
err := store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__contentIndex"))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
idx := b.Get([]byte(slug))
if idx != nil {
@@ -248,6 +269,9 @@ func ContentBySlug(slug string) (string, []byte, error) {
}
c := tx.Bucket([]byte(t))
+ if c == nil {
+ return bolt.ErrBucketNotFound
+ }
_, err := val.Write(c.Get([]byte(id)))
if err != nil {
return err
@@ -267,9 +291,8 @@ func ContentAll(namespace string) [][]byte {
var posts [][]byte
store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(namespace))
-
if b == nil {
- return nil
+ return bolt.ErrBucketNotFound
}
numKeys := b.Stats().KeyN
@@ -313,7 +336,7 @@ func Query(namespace string, opts QueryOptions) (int, [][]byte) {
store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(namespace))
if b == nil {
- return nil
+ return bolt.ErrBucketNotFound
}
c := b.Cursor()
@@ -535,6 +558,9 @@ func checkSlugForDuplicate(slug string) (string, error) {
// check for existing slug in __contentIndex
err := store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__contentIndex"))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
original := slug
exists := true
i := 0
diff --git a/system/db/init.go b/system/db/init.go
index 0e640b1..9125d3b 100644
--- a/system/db/init.go
+++ b/system/db/init.go
@@ -92,6 +92,9 @@ func SystemInitComplete() bool {
err := store.View(func(tx *bolt.Tx) error {
users := tx.Bucket([]byte("__users"))
+ if users == nil {
+ return bolt.ErrBucketNotFound
+ }
err := users.ForEach(func(k, v []byte) error {
complete = true
diff --git a/system/db/user.go b/system/db/user.go
index 02fda95..164ae7b 100644
--- a/system/db/user.go
+++ b/system/db/user.go
@@ -26,6 +26,9 @@ func SetUser(usr *user.User) (int, error) {
err := store.Update(func(tx *bolt.Tx) error {
email := []byte(usr.Email)
users := tx.Bucket([]byte("__users"))
+ if users == nil {
+ return bolt.ErrBucketNotFound
+ }
// check if user is found by email, fail if nil
exists := users.Get(email)
@@ -69,6 +72,9 @@ func UpdateUser(usr, updatedUsr *user.User) error {
err := store.Update(func(tx *bolt.Tx) error {
users := tx.Bucket([]byte("__users"))
+ if users == nil {
+ return bolt.ErrBucketNotFound
+ }
// check if user is found by email, fail if nil
exists := users.Get([]byte(usr.Email))
@@ -110,6 +116,10 @@ func UpdateUser(usr, updatedUsr *user.User) error {
func DeleteUser(email string) error {
err := store.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__users"))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
+
err := b.Delete([]byte(email))
if err != nil {
return err
@@ -129,6 +139,10 @@ func User(email string) ([]byte, error) {
val := &bytes.Buffer{}
err := store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__users"))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
+
usr := b.Get([]byte(email))
_, err := val.Write(usr)
@@ -154,6 +168,10 @@ func UserAll() ([][]byte, error) {
var users [][]byte
err := store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__users"))
+ if b == nil {
+ return bolt.ErrBucketNotFound
+ }
+
err := b.ForEach(func(k, v []byte) error {
users = append(users, v)
return nil
@@ -230,7 +248,7 @@ func RecoveryKey(email string) (string, error) {
err := store.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("__recoveryKeys"))
if b == nil {
- return errors.New("No database found for checking keys.")
+ return bolt.ErrBucketNotFound
}
_, err := key.Write(b.Get([]byte(email)))