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) }