From a50da4215c88efcdcab1da2835459b64a5b341b4 Mon Sep 17 00:00:00 2001 From: Nathan Lasseter Date: Tue, 22 Sep 2015 17:22:43 +0100 Subject: Initial Commit --- .gitignore | 1 + LICENCE | 19 +++++++++++++ README | 5 ++++ TODO | 2 ++ cache.go | 60 ++++++++++++++++++++++++++++++++++++++++ db.go | 55 +++++++++++++++++++++++++++++++++++++ files.go | 23 ++++++++++++++++ handlers.go | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ logger.go | 23 ++++++++++++++++ main.go | 13 +++++++++ router.go | 24 ++++++++++++++++ routes.go | 39 ++++++++++++++++++++++++++ 12 files changed, 355 insertions(+) create mode 100644 .gitignore create mode 100644 LICENCE create mode 100644 README create mode 100644 TODO create mode 100644 cache.go create mode 100644 db.go create mode 100644 files.go create mode 100644 handlers.go create mode 100644 logger.go create mode 100644 main.go create mode 100644 router.go create mode 100644 routes.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a7359c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +audiocache diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..6a1adda --- /dev/null +++ b/LICENCE @@ -0,0 +1,19 @@ +Copyright (c) 2015 Audiocache + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README b/README new file mode 100644 index 0000000..51ff7f6 --- /dev/null +++ b/README @@ -0,0 +1,5 @@ +The Audiocache Golang server software + +1. go get github.com/Audiocache/audiocache-server +2. go build +3. ./audiocache diff --git a/TODO b/TODO new file mode 100644 index 0000000..523ead7 --- /dev/null +++ b/TODO @@ -0,0 +1,2 @@ +* Remove things that should be configy to a config file +* Remove mp3 assumption diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..6eed536 --- /dev/null +++ b/cache.go @@ -0,0 +1,60 @@ +package main + +import ( + "time" +) + +var FileRoot string = "http://localhost:8080/files/" + +type APICache struct { + Id uint64 `json:"id"` + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + Created time.Time `json:"created"` + URI string `json:"uri"` +} + +type APICaches []APICache + +type DBCache struct { + Id uint64 + Latitude float64 + Longitude float64 + Created time.Time + Path string +} + +type DBCaches []DBCache + +type PostCache struct { + Latitude float64 + Longitude float64 + Data string +} + +func PathToURI(path string) string { + return FileRoot + path +} + +func DBToAPI(db DBCache) APICache { + var api APICache + + api.Id = db.Id + api.Latitude = db.Latitude + api.Longitude = db.Longitude + api.Created = db.Created + api.URI = PathToURI(db.Path) + + return api +} + +func PostToDB(post PostCache, filename string) DBCache { + var db DBCache + + db.Latitude = post.Latitude + db.Longitude = post.Longitude + db.Created = time.Now() + db.Path = filename + + return db +} diff --git a/db.go b/db.go new file mode 100644 index 0000000..a2c1d1e --- /dev/null +++ b/db.go @@ -0,0 +1,55 @@ +package main + +import ( + "github.com/jmoiron/sqlx" + _ "github.com/lib/pq" +) + +func getCaches() (DBCaches, error) { + var caches DBCaches + + db, err := sqlx.Connect("postgres", "user=audiocache password=audiocache dbname=audiocache sslmode=disable") + if err != nil { + return caches, err + } + + err = db.Select(&caches, "SELECT * FROM caches ORDER BY id ASC") + if err != nil { + return caches, err + } + + err = db.Close() + return caches, err +} + +func getCache(id uint64) (DBCache, error) { + var cache DBCache + + db, err := sqlx.Connect("postgres", "user=audiocache password=audiocache dbname=audiocache sslmode=disable") + if err != nil { + return cache, err + } + + err = db.Get(&cache, "SELECT * FROM caches WHERE id=$1", id) + if err != nil { + return cache, err + } + + err = db.Close() + return cache, err +} + +func postCache(dbcache DBCache) error { + db, err := sqlx.Connect("postgres", "user=audiocache password=audiocache dbname=audiocache sslmode=disable") + if err != nil { + return err + } + + _, err = db.NamedExec("INSERT INTO caches (latitude, longitude, created, path) VALUES (:latitude, :longitude, :created, :path)", dbcache) + if err != nil { + return err + } + + err = db.Close() + return err +} diff --git a/files.go b/files.go new file mode 100644 index 0000000..f1a3d6c --- /dev/null +++ b/files.go @@ -0,0 +1,23 @@ +package main + +import ( + "encoding/base64" + "io/ioutil" +) + +func writeFile(postcache PostCache, filename string) error { + data := postcache.Data + var binary []byte + + n, err := base64.StdEncoding.Decode(binary, []byte(data)) + if n == 0 || err != nil { + return err + } + + err = ioutil.WriteFile("files/"+filename, binary, 0644) + if err != nil { + return err + } + + return nil +} diff --git a/handlers.go b/handlers.go new file mode 100644 index 0000000..c0ec657 --- /dev/null +++ b/handlers.go @@ -0,0 +1,91 @@ +package main + +import ( + "code.google.com/p/go-uuid/uuid" + "encoding/json" + "fmt" + "github.com/gorilla/mux" + "io" + "io/ioutil" + "net/http" + "strconv" +) + +func Index(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Welcome!") +} + +func CacheIndex(w http.ResponseWriter, r *http.Request) { + dbcaches, err := getCaches() + if err != nil { + panic(err) + } + + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(http.StatusOK) + + var apicaches APICaches + for db := range dbcaches { + apicaches = append(apicaches, DBToAPI(dbcaches[db])) + } + + if err := json.NewEncoder(w).Encode(apicaches); err != nil { + panic(err) + } +} + +func CacheShow(w http.ResponseWriter, r *http.Request) { + cacheId, err := strconv.ParseUint(mux.Vars(r)["cacheId"], 10, 64) + if err != nil { + panic(err) + } + + cache, err := getCache(cacheId) + if err != nil { + panic(err) + } + + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(http.StatusOK) + + if err := json.NewEncoder(w).Encode(DBToAPI(cache)); err != nil { + panic(err) + } +} + +func CacheCreate(w http.ResponseWriter, r *http.Request) { + var postcache PostCache + + body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576)) + if err != nil { + panic(err) + } + if err := r.Body.Close(); err != nil { + panic(err) + } + if err := json.Unmarshal(body, &postcache); err != nil { + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(422) // unprocessable entity + if err := json.NewEncoder(w).Encode(err); err != nil { + panic(err) + } + } + + filename := uuid.New() + ".mp3" + dbcache := PostToDB(postcache, filename) + + err = postCache(dbcache) + if err != nil { + panic(err) + } + + err = writeFile(postcache, filename) + if err != nil { + panic(err) + } + + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintln(w, "OK") +} diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..3f002e3 --- /dev/null +++ b/logger.go @@ -0,0 +1,23 @@ +package main + +import ( + "log" + "net/http" + "time" +) + +func Logger(inner http.Handler, name string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + + inner.ServeHTTP(w, r) + + log.Printf( + "%s\t%s\t%s\t%s", + r.Method, + r.RequestURI, + name, + time.Since(start), + ) + }) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..3fcb722 --- /dev/null +++ b/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "log" + "net/http" +) + +func main() { + + router := NewRouter() + + log.Fatal(http.ListenAndServe(":8080", router)) +} diff --git a/router.go b/router.go new file mode 100644 index 0000000..32caba3 --- /dev/null +++ b/router.go @@ -0,0 +1,24 @@ +package main + +import ( + "net/http" + + "github.com/gorilla/mux" +) + +func NewRouter() *mux.Router { + router := mux.NewRouter().StrictSlash(true) + for _, route := range routes { + var handler http.Handler + handler = route.HandlerFunc + handler = Logger(handler, route.Name) + + router. + Methods(route.Method). + Path(route.Pattern). + Name(route.Name). + Handler(handler) + + } + return router +} diff --git a/routes.go b/routes.go new file mode 100644 index 0000000..830b0d0 --- /dev/null +++ b/routes.go @@ -0,0 +1,39 @@ +package main + +import "net/http" + +type Route struct { + Name string + Method string + Pattern string + HandlerFunc http.HandlerFunc +} + +type Routes []Route + +var routes = Routes{ + Route{ + "Index", + "GET", + "/", + Index, + }, + Route{ + "CacheIndex", + "GET", + "/caches", + CacheIndex, + }, + Route{ + "CacheShow", + "GET", + "/caches/{cacheId}", + CacheShow, + }, + Route{ + "CacheCreate", + "POST", + "/caches", + CacheCreate, + }, +} -- cgit v1.2.1