uBrowserSync/cmd/ubsserver/main.go

235 lines
5.1 KiB
Go

package main
import (
"encoding/json"
"flag"
"fmt"
"net/http"
"os"
"regexp"
"strings"
"time"
"gitlab.com/mporrato/uBrowserSync/syncstore"
)
type serviceInfoResp struct {
Version string `json:"version"`
Message string `json:"message"`
MaxSyncSize int32 `json:"maxSyncSize"`
Status int `json:"status"`
}
type CreateReq struct {
Version string `json:"version"`
}
type UpdateReq struct {
Bookmarks string `json:"bookmarks"`
LastUpdated time.Time `json:"lastUpdated"`
}
type UpdateResp struct {
LastUpdated time.Time `json:"lastUpdated"`
}
type LastUpdatedResp struct {
LastUpdated time.Time `json:"lastUpdated"`
}
type GetSyncVerResp struct {
Version string `json:"version"`
}
func sendJSON(w http.ResponseWriter, status int, data interface{}) error {
body, err := json.Marshal(data)
if err != nil {
return err
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_, err = w.Write(body)
return err
}
func sendJSONOk(w http.ResponseWriter, data interface{}) error {
return sendJSON(w, http.StatusOK, data)
}
func sendJSONError(w http.ResponseWriter, err error) error {
switch e := err.(type) {
case syncstore.SyncError:
return sendJSON(w, e.StatusCode, e.Payload)
default:
return sendJSON(w, http.StatusInternalServerError,
syncstore.ErrorPayload{
Code: "Internal server error",
Message: e.Error()})
}
}
func info(w http.ResponseWriter, req *http.Request) {
if req.Method != "GET" || req.URL.Path != "/info" {
sendJSONError(w, syncstore.NotImplementedError)
} else {
sendJSONOk(w, serviceInfoResp{
Version: "1.1.13",
Message: "Powered by browsersync",
MaxSyncSize: 2 * 1024 * 1024,
Status: 1})
}
}
var store syncstore.Store
var invalidRequestError = syncstore.NewSyncError(
"Invalid request",
"Malformed request body",
http.StatusBadRequest)
func createSync(w http.ResponseWriter, req *http.Request) {
body := new(CreateReq)
err := json.NewDecoder(req.Body).Decode(&body)
if err != nil {
sendJSONError(w, invalidRequestError)
return
}
resp, err := store.Create(body.Version)
if err != nil {
sendJSONError(w, err)
return
}
sendJSONOk(w, resp)
}
func getSync(syncId string, w http.ResponseWriter, req *http.Request) {
resp, err := store.Get(syncId)
if err != nil {
sendJSONError(w, err)
return
}
sendJSONOk(w, resp)
}
func getLastUpdated(syncId string, w http.ResponseWriter, req *http.Request) {
resp, err := store.GetLastUpdated(syncId)
if err != nil {
sendJSONError(w, err)
return
}
sendJSONOk(w, LastUpdatedResp{LastUpdated: resp})
}
func getVersion(syncId string, w http.ResponseWriter, req *http.Request) {
resp, err := store.GetVersion(syncId)
if err != nil {
sendJSONError(w, err)
return
}
sendJSONOk(w, GetSyncVerResp{Version: resp})
}
func updateSync(syncId string, w http.ResponseWriter, req *http.Request) {
body := new(UpdateReq)
err := json.NewDecoder(req.Body).Decode(&body)
if err != nil {
sendJSONError(w, invalidRequestError)
return
}
resp, err := store.Update(syncId, body.Bookmarks, body.LastUpdated)
if err != nil {
sendJSONError(w, err)
return
}
sendJSONOk(w, UpdateResp{LastUpdated: resp})
}
var sidRe = regexp.MustCompile("^[[:xdigit:]]{32}$")
func bookmarks(w http.ResponseWriter, req *http.Request) {
elements := strings.Split(strings.Trim(req.URL.Path, "/"), "/")
if len(elements) == 1 && req.Method == "POST" {
createSync(w, req)
return
}
syncId := elements[1]
if !sidRe.MatchString(elements[1]) {
sendJSONError(w, syncstore.NotImplementedError)
return
}
if len(elements) == 2 {
if req.Method == "GET" {
getSync(syncId, w, req)
return
}
if req.Method == "PUT" {
updateSync(syncId, w, req)
return
}
// TODO: Handle HEAD requests
sendJSONError(w, syncstore.MethodNotImplementedError)
return
}
if len(elements) == 3 {
if elements[2] == "lastUpdated" {
if req.Method == "GET" {
getLastUpdated(syncId, w, req)
return
}
sendJSONError(w, syncstore.MethodNotImplementedError)
return
}
if elements[2] == "version" {
if req.Method == "GET" {
getVersion(syncId, w, req)
return
}
sendJSONError(w, syncstore.MethodNotImplementedError)
return
}
}
//sendJSON(w, http.StatusOK, map[string][]string{"elements": elements})
sendJSONError(w, syncstore.NotImplementedError)
}
var listen string
func init() {
var err error
var storeflag string
var storeDrv syncstore.StoreDriver
flag.StringVar(&listen, "listen", ":8090", "listen address and port")
flag.StringVar(&storeflag, "store", "fs:data", "blob store driver")
flag.Parse()
lstore := strings.Split(storeflag, ":")
switch lstore[0] {
case "fs":
if len(lstore) != 2 {
err = fmt.Errorf("argument required for fs store driver")
} else {
storeDrv, err = syncstore.NewFSStore(lstore[1])
}
case "mem":
storeDrv, err = syncstore.NewMemStore()
default:
err = fmt.Errorf("Invalid store driver: "+lstore[0])
}
if err != nil {
fmt.Println(err)
os.Exit(1)
}
store = syncstore.NewStore(storeDrv)
}
func main() {
http.HandleFunc("/info", info)
http.HandleFunc("/bookmarks", bookmarks)
http.HandleFunc("/bookmarks/", bookmarks)
http.ListenAndServe(listen, nil)
}