diff options
author | Steve Manuel <nilslice@gmail.com> | 2016-10-06 23:01:43 -0700 |
---|---|---|
committer | Steve Manuel <nilslice@gmail.com> | 2016-10-06 23:01:43 -0700 |
commit | db3943b43eb77ee7ffeb583afb5046a138632b40 (patch) | |
tree | 2f7ed37148ba68bf32d98c70fb8612604584f38d | |
parent | 0dcd5073d193256a8b41a4811b930407cf2dbdef (diff) |
moved all handlers outside of callers. may need to split up handlers in to grouped files
-rw-r--r-- | system/admin/handlers.go | 486 | ||||
-rw-r--r-- | system/admin/server.go | 352 |
2 files changed, 488 insertions, 350 deletions
diff --git a/system/admin/handlers.go b/system/admin/handlers.go index 9ff39c3..3ef0e7d 100644 --- a/system/admin/handlers.go +++ b/system/admin/handlers.go @@ -1,12 +1,18 @@ package admin import ( + "bytes" + "encoding/base64" "encoding/json" "fmt" "net/http" "strings" "time" + "github.com/nilslice/cms/content" + "github.com/nilslice/cms/management/editor" + "github.com/nilslice/cms/management/manager" + "github.com/nilslice/cms/system/admin/config" "github.com/nilslice/cms/system/admin/user" "github.com/nilslice/cms/system/db" "github.com/nilslice/jwt" @@ -24,6 +30,154 @@ func adminHandler(res http.ResponseWriter, req *http.Request) { res.Write(view) } +func initHandler(res http.ResponseWriter, req *http.Request) { + if db.SystemInitComplete() { + http.Redirect(res, req, req.URL.Scheme+req.URL.Host+"/admin", http.StatusFound) + return + } + + switch req.Method { + case http.MethodGet: + view, err := Init() + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + res.Header().Set("Content-Type", "text/html") + res.Write(view) + + case http.MethodPost: + err := req.ParseForm() + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + // get the site name from post to encode and use as secret + name := []byte(req.FormValue("name")) + secret := base64.StdEncoding.EncodeToString(name) + req.Form.Set("client_secret", secret) + + // generate an Etag to use for response caching + etag := db.NewEtag() + req.Form.Set("etag", etag) + + err = db.SetConfig(req.Form) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + email := req.FormValue("email") + password := req.FormValue("password") + usr := user.NewUser(email, password) + + _, err = db.SetUser(usr) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + // add _token cookie for login persistence + week := time.Now().Add(time.Hour * 24 * 7) + claims := map[string]interface{}{ + "exp": week.Unix(), + "user": usr.Email, + } + + jwt.Secret([]byte(secret)) + token, err := jwt.New(claims) + + http.SetCookie(res, &http.Cookie{ + Name: "_token", + Value: token, + Expires: week, + }) + + redir := strings.TrimSuffix(req.URL.String(), "/init") + http.Redirect(res, req, redir, http.StatusFound) + + default: + res.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func configHandler(res http.ResponseWriter, req *http.Request) { + switch req.Method { + case http.MethodGet: + data, err := db.ConfigAll() + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + c := &config.Config{} + + err = json.Unmarshal(data, c) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + cfg, err := c.MarshalEditor() + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + adminView, err := Admin(cfg) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + res.Header().Set("Content-Type", "text/html") + res.Write(adminView) + + case http.MethodPost: + err := req.ParseForm() + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + err = db.SetConfig(req.Form) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + http.Redirect(res, req, req.URL.String(), http.StatusFound) + + default: + res.WriteHeader(http.StatusMethodNotAllowed) + } + +} + +func configUsersHandler(res http.ResponseWriter, req *http.Request) { + switch req.Method { + case http.MethodGet: + // list all users and delete buttons + + case http.MethodPost: + // create new user + + default: + res.WriteHeader(http.StatusMethodNotAllowed) + } +} + func loginHandler(res http.ResponseWriter, req *http.Request) { if !db.SystemInitComplete() { redir := req.URL.Scheme + req.URL.Host + "/admin/init" @@ -57,25 +211,20 @@ func loginHandler(res http.ResponseWriter, req *http.Request) { err := req.ParseForm() if err != nil { fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) + http.Redirect(res, req, req.URL.String(), http.StatusFound) return } - fmt.Println(req.FormValue("email")) - fmt.Println(req.FormValue("password")) - // check email & password j, err := db.User(req.FormValue("email")) if err != nil { fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) + http.Redirect(res, req, req.URL.String(), http.StatusFound) return } if j == nil { - fmt.Println(err) - res.WriteHeader(http.StatusBadRequest) - fmt.Println("j == nil") + http.Redirect(res, req, req.URL.String(), http.StatusFound) return } @@ -83,13 +232,12 @@ func loginHandler(res http.ResponseWriter, req *http.Request) { err = json.Unmarshal(j, usr) if err != nil { fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) + http.Redirect(res, req, req.URL.String(), http.StatusFound) return } if !user.IsUser(usr, req.FormValue("password")) { - res.WriteHeader(http.StatusBadRequest) - fmt.Println("!IsUser") + http.Redirect(res, req, req.URL.String(), http.StatusFound) return } // create new token @@ -101,7 +249,7 @@ func loginHandler(res http.ResponseWriter, req *http.Request) { token, err := jwt.New(claims) if err != nil { fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) + http.Redirect(res, req, req.URL.String(), http.StatusFound) return } @@ -125,3 +273,317 @@ func logoutHandler(res http.ResponseWriter, req *http.Request) { http.Redirect(res, req, req.URL.Scheme+req.URL.Host+"/admin/login", http.StatusFound) } + +func postsHandler(res http.ResponseWriter, req *http.Request) { + q := req.URL.Query() + t := q.Get("type") + if t == "" { + res.WriteHeader(http.StatusBadRequest) + errView, err := Error405() + if err != nil { + return + } + + res.Write(errView) + return + } + + posts := db.ContentAll(t) + b := &bytes.Buffer{} + p, ok := content.Types[t]().(editor.Editable) + if !ok { + res.WriteHeader(http.StatusInternalServerError) + errView, err := Error500() + if err != nil { + return + } + + res.Write(errView) + return + } + + html := `<div class="col s9 card"> + <div class="card-content"> + <div class="row"> + <div class="card-title col s7">` + t + ` Items</div> + <form class="col s5" action="/admin/posts/search" method="get"> + <div class="input-field post-search inline"> + <i class="right material-icons search-icon">search</i> + <input class="search" name="q" type="text" placeholder="Search for ` + t + ` content" class="search"/> + <input type="hidden" name="type" value="` + t + `" /> + </div> + </form> + </div> + <ul class="posts row">` + + for i := range posts { + json.Unmarshal(posts[i], &p) + post := `<li class="col s12"><a href="/admin/edit?type=` + + t + `&id=` + fmt.Sprintf("%d", p.ContentID()) + + `">` + p.ContentName() + `</a></li>` + b.Write([]byte(post)) + } + + b.Write([]byte(`</ul></div></div>`)) + + btn := `<div class="col s3"><a href="/admin/edit?type=` + t + `" class="btn new-post waves-effect waves-light">New ` + t + `</a></div></div>` + html = html + b.String() + btn + + adminView, err := Admin([]byte(html)) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + res.Header().Set("Content-Type", "text/html") + res.Write(adminView) +} + +func editHandler(res http.ResponseWriter, req *http.Request) { + switch req.Method { + case http.MethodGet: + q := req.URL.Query() + i := q.Get("id") + t := q.Get("type") + contentType, ok := content.Types[t] + if !ok { + fmt.Fprintf(res, content.ErrTypeNotRegistered, t) + return + } + post := contentType() + + if i != "" { + data, err := db.Content(t + ":" + i) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + errView, err := Error500() + if err != nil { + return + } + + res.Write(errView) + return + } + + if len(data) < 1 || data == nil { + res.WriteHeader(http.StatusNotFound) + errView, err := Error404() + if err != nil { + return + } + + res.Write(errView) + return + } + + err = json.Unmarshal(data, post) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + errView, err := Error500() + if err != nil { + return + } + + res.Write(errView) + return + } + } else { + post.(editor.Editable).SetContentID(-1) + } + + m, err := manager.Manage(post.(editor.Editable), t) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + errView, err := Error500() + if err != nil { + return + } + + res.Write(errView) + return + } + + adminView, err := Admin(m) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + res.Header().Set("Content-Type", "text/html") + res.Write(adminView) + + case http.MethodPost: + err := req.ParseMultipartForm(1024 * 1024 * 4) // maxMemory 4MB + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusBadRequest) + errView, err := Error405() + if err != nil { + return + } + + res.Write(errView) + return + } + + cid := req.FormValue("id") + t := req.FormValue("type") + ts := req.FormValue("timestamp") + + // create a timestamp if one was not set + date := make(map[string]int) + if ts == "" { + now := time.Now() + date["year"] = now.Year() + date["month"] = int(now.Month()) + date["day"] = now.Day() + + // create timestamp format 'yyyy-mm-dd' and set in PostForm for + // db insertion + ts = fmt.Sprintf("%d-%02d-%02d", date["year"], date["month"], date["day"]) + req.PostForm.Set("timestamp", ts) + } + + urlPaths, err := storeFileUploads(req) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + errView, err := Error500() + if err != nil { + return + } + + res.Write(errView) + return + } + + for name, urlPath := range urlPaths { + req.PostForm.Add(name, urlPath) + } + + // 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 req.PostForm { + if strings.Contains(k, ".") { + key := strings.Split(k, ".")[0] + + if req.PostForm.Get(key) == "" { + req.PostForm.Set(key, v[0]) + discardKeys = append(discardKeys, k) + } else { + req.PostForm.Add(key, v[0]) + } + } + } + + for _, discardKey := range discardKeys { + req.PostForm.Del(discardKey) + } + + id, err := db.SetContent(t+":"+cid, req.PostForm) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + errView, err := Error500() + if err != nil { + return + } + + res.Write(errView) + return + } + + scheme := req.URL.Scheme + host := req.URL.Host + path := req.URL.Path + sid := fmt.Sprintf("%d", id) + desURL := scheme + host + path + "?type=" + t + "&id=" + sid + http.Redirect(res, req, desURL, http.StatusFound) + + default: + res.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func editUploadHandler(res http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodPost { + res.WriteHeader(http.StatusMethodNotAllowed) + return + } + + urlPaths, err := storeFileUploads(req) + if err != nil { + fmt.Println("Couldn't store file uploads.", err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + res.Header().Set("Content-Type", "application/json") + res.Write([]byte(`{"data": [{"url": "` + urlPaths["file"] + `"}]}`)) +} + +func searchHandler(res http.ResponseWriter, req *http.Request) { + q := req.URL.Query() + t := q.Get("type") + search := q.Get("q") + + if t == "" || search == "" { + http.Redirect(res, req, req.URL.Scheme+req.URL.Host+"/admin", http.StatusFound) + return + } + + posts := db.ContentAll(t) + b := &bytes.Buffer{} + p := content.Types[t]().(editor.Editable) + + html := `<div class="col s9 card"> + <div class="card-content"> + <div class="row"> + <div class="card-title col s7">` + t + ` Results</div> + <form class="col s5" action="/admin/posts/search" method="get"> + <div class="input-field post-search inline"> + <i class="right material-icons search-icon">search</i> + <input class="search" name="q" type="text" placeholder="Search for ` + t + ` content" class="search"/> + <input type="hidden" name="type" value="` + t + `" /> + </div> + </form> + </div> + <ul class="posts row">` + + for i := range posts { + // skip posts that don't have any matching search criteria + match := strings.ToLower(search) + all := strings.ToLower(string(posts[i])) + if !strings.Contains(all, match) { + continue + } + + json.Unmarshal(posts[i], &p) + post := `<li class="col s12"><a href="/admin/edit?type=` + + t + `&id=` + fmt.Sprintf("%d", p.ContentID()) + + `">` + p.ContentName() + `</a></li>` + b.Write([]byte(post)) + } + + b.Write([]byte(`</ul></div></div>`)) + + btn := `<div class="col s3"><a href="/admin/edit?type=` + t + `" class="btn new-post waves-effect waves-light">New ` + t + `</a></div></div>` + html = html + b.String() + btn + + adminView, err := Admin([]byte(html)) + if err != nil { + fmt.Println(err) + res.WriteHeader(http.StatusInternalServerError) + return + } + + res.Header().Set("Content-Type", "text/html") + res.Write(adminView) +} diff --git a/system/admin/server.go b/system/admin/server.go index 92c86c3..9461759 100644 --- a/system/admin/server.go +++ b/system/admin/server.go @@ -1,9 +1,6 @@ package admin import ( - "bytes" - "encoding/base64" - "encoding/json" "fmt" "io" "log" @@ -14,19 +11,27 @@ import ( "strings" "time" - "github.com/nilslice/cms/content" - "github.com/nilslice/cms/management/editor" - "github.com/nilslice/cms/management/manager" - "github.com/nilslice/cms/system/admin/config" "github.com/nilslice/cms/system/admin/user" - "github.com/nilslice/cms/system/db" - "github.com/nilslice/jwt" ) // Run adds Handlers to default http listener for Admin func Run() { http.HandleFunc("/admin", user.Auth(adminHandler)) + http.HandleFunc("/admin/init", initHandler) + + http.HandleFunc("/admin/login", loginHandler) + http.HandleFunc("/admin/logout", logoutHandler) + + http.HandleFunc("/admin/configure", user.Auth(configHandler)) + http.HandleFunc("/admin/configure/users", user.Auth(configUsersHandler)) + + http.HandleFunc("/admin/posts", user.Auth(postsHandler)) + http.HandleFunc("/admin/posts/search", user.Auth(searchHandler)) + + http.HandleFunc("/admin/edit", user.Auth(editHandler)) + http.HandleFunc("/admin/edit/upload", user.Auth(editUploadHandler)) + http.HandleFunc("/admin/static/", func(res http.ResponseWriter, req *http.Request) { path := req.URL.Path pathParts := strings.Split(path, "/")[1:] @@ -43,335 +48,6 @@ func Run() { http.ServeFile(res, req, filepath.Join(filePathParts...)) }) - http.HandleFunc("/admin/init", func(res http.ResponseWriter, req *http.Request) { - if db.SystemInitComplete() { - http.Redirect(res, req, req.URL.Scheme+req.URL.Host+"/admin", http.StatusFound) - return - } - - switch req.Method { - case http.MethodGet: - view, err := Init() - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - res.Header().Set("Content-Type", "text/html") - res.Write(view) - - case http.MethodPost: - err := req.ParseForm() - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - // get the site name from post to encode and use as secret - name := []byte(req.FormValue("name")) - secret := base64.StdEncoding.EncodeToString(name) - req.Form.Set("client_secret", secret) - - err = db.SetConfig(req.Form) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - email := req.FormValue("email") - password := req.FormValue("password") - usr := user.NewUser(email, password) - - _, err = db.SetUser(usr) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - // add _token cookie for login persistence - week := time.Now().Add(time.Hour * 24 * 7) - claims := map[string]interface{}{ - "exp": week.Unix(), - "user": usr.Email, - } - - jwt.Secret([]byte(secret)) - token, err := jwt.New(claims) - - http.SetCookie(res, &http.Cookie{ - Name: "_token", - Value: token, - Expires: week, - }) - - redir := strings.TrimSuffix(req.URL.String(), "/init") - http.Redirect(res, req, redir, http.StatusFound) - - default: - res.WriteHeader(http.StatusMethodNotAllowed) - } - }) - - http.HandleFunc("/admin/login", loginHandler) - http.HandleFunc("/admin/logout", logoutHandler) - - http.HandleFunc("/admin/configure", func(res http.ResponseWriter, req *http.Request) { - switch req.Method { - case http.MethodGet: - data, err := db.ConfigAll() - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - c := &config.Config{} - - err = json.Unmarshal(data, c) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - cfg, err := c.MarshalEditor() - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - adminView, err := Admin(cfg) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - res.Header().Set("Content-Type", "text/html") - res.Write(adminView) - - case http.MethodPost: - err := req.ParseForm() - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - err = db.SetConfig(req.Form) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - http.Redirect(res, req, req.URL.String(), http.StatusFound) - - default: - res.WriteHeader(http.StatusMethodNotAllowed) - } - - }) - - http.HandleFunc("/admin/configure/users", func(res http.ResponseWriter, req *http.Request) { - switch req.Method { - case http.MethodGet: - // list all users and delete buttons - - case http.MethodPost: - // create new user - - default: - res.WriteHeader(http.StatusMethodNotAllowed) - } - }) - - http.HandleFunc("/admin/posts", func(res http.ResponseWriter, req *http.Request) { - q := req.URL.Query() - t := q.Get("type") - if t == "" { - res.WriteHeader(http.StatusBadRequest) - } - - posts := db.ContentAll(t) - b := &bytes.Buffer{} - p := content.Types[t]().(editor.Editable) - - html := `<div class="col s9 card"> - <div class="card-content row"> - <div class="card-title">` + t + ` Items</div> - <ul class="posts">` - - for i := range posts { - json.Unmarshal(posts[i], &p) - post := `<li class="col s12"><a href="/admin/edit?type=` + - t + `&id=` + fmt.Sprintf("%d", p.ContentID()) + - `">` + p.ContentName() + `</a></li>` - b.Write([]byte(post)) - } - - b.Write([]byte(`</ul></div></div>`)) - - btn := `<div class="col s3"><a href="/admin/edit?type=` + t + `" class="btn new-post waves-effect waves-light">New ` + t + `</a></div></div>` - html = html + b.String() + btn - - adminView, err := Admin([]byte(html)) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - res.Header().Set("Content-Type", "text/html") - res.Write(adminView) - }) - - http.HandleFunc("/admin/edit", func(res http.ResponseWriter, req *http.Request) { - switch req.Method { - case http.MethodGet: - q := req.URL.Query() - i := q.Get("id") - t := q.Get("type") - contentType, ok := content.Types[t] - if !ok { - fmt.Fprintf(res, content.ErrTypeNotRegistered, t) - return - } - post := contentType() - - if i != "" { - data, err := db.Content(t + ":" + i) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - err = json.Unmarshal(data, post) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - } else { - post.(editor.Editable).SetContentID(-1) - } - - m, err := manager.Manage(post.(editor.Editable), t) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - adminView, err := Admin(m) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - res.Header().Set("Content-Type", "text/html") - res.Write(adminView) - - case http.MethodPost: - err := req.ParseMultipartForm(1024 * 1024 * 4) // maxMemory 4MB - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusBadRequest) - return - } - - cid := req.FormValue("id") - t := req.FormValue("type") - ts := req.FormValue("timestamp") - - // create a timestamp if one was not set - date := make(map[string]int) - if ts == "" { - now := time.Now() - date["year"] = now.Year() - date["month"] = int(now.Month()) - date["day"] = now.Day() - - // create timestamp format 'yyyy-mm-dd' and set in PostForm for - // db insertion - ts = fmt.Sprintf("%d-%02d-%02d", date["year"], date["month"], date["day"]) - req.PostForm.Set("timestamp", ts) - } - - urlPaths, err := storeFileUploads(req) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - for name, urlPath := range urlPaths { - req.PostForm.Add(name, urlPath) - } - - // 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 req.PostForm { - if strings.Contains(k, ".") { - key := strings.Split(k, ".")[0] - - if req.PostForm.Get(key) == "" { - req.PostForm.Set(key, v[0]) - discardKeys = append(discardKeys, k) - } else { - req.PostForm.Add(key, v[0]) - } - } - } - - for _, discardKey := range discardKeys { - req.PostForm.Del(discardKey) - } - - id, err := db.SetContent(t+":"+cid, req.PostForm) - if err != nil { - fmt.Println(err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - scheme := req.URL.Scheme - host := req.URL.Host - path := req.URL.Path - sid := fmt.Sprintf("%d", id) - desURL := scheme + host + path + "?type=" + t + "&id=" + sid - http.Redirect(res, req, desURL, http.StatusFound) - - default: - res.WriteHeader(http.StatusMethodNotAllowed) - } - }) - - http.HandleFunc("/admin/edit/upload", func(res http.ResponseWriter, req *http.Request) { - if req.Method != http.MethodPost { - res.WriteHeader(http.StatusMethodNotAllowed) - return - } - - urlPaths, err := storeFileUploads(req) - if err != nil { - fmt.Println("Couldn't store file uploads.", err) - res.WriteHeader(http.StatusInternalServerError) - return - } - - res.Header().Set("Content-Type", "application/json") - res.Write([]byte(`{"data": [{"url": "` + urlPaths["file"] + `"}]}`)) - }) - // API path needs to be registered within server package so that it is handled // even if the API server is not running. Otherwise, images/files uploaded // through the editor will not load within the admin system. |