uBrowserSync/cmd/ubsserver/main.go

235 lines
5.5 KiB
Go
Raw Normal View History

2021-05-08 16:03:21 +00:00
package main
import (
"encoding/json"
"flag"
"fmt"
2021-05-16 09:12:38 +00:00
"log"
2021-05-08 16:03:21 +00:00
"net/http"
"regexp"
"strings"
"gitlab.com/mporrato/uBrowserSync/syncstore"
)
2021-05-17 15:31:34 +00:00
const (
apiVersion = "1.1.13"
infoMessage = "Powered by uBrowserSync"
defaultMaxSyncSize = 512000
)
var (
store syncstore.Store
maxSyncSize = defaultMaxSyncSize
listen string
serviceStatus = 1
sidRe = regexp.MustCompile("^[[:xdigit:]]{32}$")
invalidRequestError = syncstore.NewSyncError(
"Invalid request",
"Malformed request body",
http.StatusBadRequest)
)
2021-05-16 15:46:05 +00:00
2021-05-16 12:51:39 +00:00
func sendJSON(w http.ResponseWriter, status int, data interface{}) {
2021-05-08 16:03:21 +00:00
body, err := json.Marshal(data)
if err != nil {
2021-05-16 12:51:39 +00:00
log.Printf("sendJSON(%v): json.Marshal() failed: %v", data, err)
return
2021-05-08 16:03:21 +00:00
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_, err = w.Write(body)
2021-05-16 12:51:39 +00:00
if err != nil {
log.Println("sendJSON() failed:", err)
}
2021-05-08 16:03:21 +00:00
}
2021-05-16 12:51:39 +00:00
func sendJSONOk(w http.ResponseWriter, data interface{}) {
sendJSON(w, http.StatusOK, data)
2021-05-08 16:03:21 +00:00
}
2021-05-16 12:51:39 +00:00
func sendJSONError(w http.ResponseWriter, err error) {
2021-05-08 16:03:21 +00:00
switch e := err.(type) {
case syncstore.SyncError:
2021-05-16 12:51:39 +00:00
sendJSON(w, e.StatusCode, e.Payload)
2021-05-08 16:03:21 +00:00
default:
2021-05-16 12:51:39 +00:00
sendJSON(w, http.StatusInternalServerError,
2021-05-08 16:03:21 +00:00
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 {
2021-05-16 09:12:38 +00:00
log.Println("info()")
serviceInfo := syncstore.ServiceInfoResp{
2021-05-16 19:11:45 +00:00
Version: apiVersion,
Message: infoMessage,
Status: serviceStatus,
2021-05-16 15:46:05 +00:00
MaxSyncSize: maxSyncSize}
sendJSONOk(w, serviceInfo)
2021-05-08 16:03:21 +00:00
}
}
func createSync(w http.ResponseWriter, req *http.Request) {
body := new(syncstore.CreateReq)
2021-05-08 16:03:21 +00:00
err := json.NewDecoder(req.Body).Decode(&body)
if err != nil {
sendJSONError(w, invalidRequestError)
return
}
2021-05-16 15:46:05 +00:00
if len(body.Version) > 20 {
sendJSONError(w, invalidRequestError)
return
}
2021-05-08 16:03:21 +00:00
resp, err := store.Create(body.Version)
if err != nil {
sendJSONError(w, err)
return
}
sendJSONOk(w, resp)
}
2021-05-16 12:51:39 +00:00
func getSync(syncId string, w http.ResponseWriter, _ *http.Request) {
2021-05-08 16:03:21 +00:00
resp, err := store.Get(syncId)
if err != nil {
sendJSONError(w, err)
return
}
sendJSONOk(w, resp)
}
2021-05-16 12:51:39 +00:00
func getLastUpdated(syncId string, w http.ResponseWriter, _ *http.Request) {
2021-05-08 16:03:21 +00:00
resp, err := store.GetLastUpdated(syncId)
if err != nil {
sendJSONError(w, err)
return
}
sendJSONOk(w, syncstore.LastUpdatedResp{LastUpdated: resp})
2021-05-08 16:03:21 +00:00
}
2021-05-16 12:51:39 +00:00
func getVersion(syncId string, w http.ResponseWriter, _ *http.Request) {
2021-05-08 16:03:21 +00:00
resp, err := store.GetVersion(syncId)
if err != nil {
sendJSONError(w, err)
return
}
sendJSONOk(w, syncstore.GetSyncVerResp{Version: resp})
2021-05-08 16:03:21 +00:00
}
func updateSync(syncId string, w http.ResponseWriter, req *http.Request) {
body := new(syncstore.UpdateReq)
2021-05-08 16:03:21 +00:00
err := json.NewDecoder(req.Body).Decode(&body)
if err != nil {
sendJSONError(w, invalidRequestError)
return
}
2021-05-16 15:46:05 +00:00
if len(body.Bookmarks) > maxSyncSize {
sendJSONError(w, syncstore.SyncDataLimitExceededError)
return
}
2021-05-08 16:03:21 +00:00
resp, err := store.Update(syncId, body.Bookmarks, body.LastUpdated)
if err != nil {
sendJSONError(w, err)
return
}
sendJSONOk(w, syncstore.UpdateResp{LastUpdated: resp})
2021-05-08 16:03:21 +00:00
}
func bookmarks(w http.ResponseWriter, req *http.Request) {
elements := strings.Split(strings.Trim(req.URL.Path, "/"), "/")
if len(elements) == 1 && req.Method == "POST" {
2021-05-16 09:12:38 +00:00
log.Println("createSync()")
2021-05-08 16:03:21 +00:00
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" {
2021-05-16 09:12:38 +00:00
log.Printf("getSync(%s)", syncId)
2021-05-08 16:03:21 +00:00
getSync(syncId, w, req)
return
}
if req.Method == "PUT" {
2021-05-16 09:12:38 +00:00
log.Printf("updateSync(%s)", syncId)
2021-05-08 16:03:21 +00:00
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" {
2021-05-16 09:12:38 +00:00
log.Printf("getLastUpdated(%s)", syncId)
2021-05-08 16:03:21 +00:00
getLastUpdated(syncId, w, req)
return
}
sendJSONError(w, syncstore.MethodNotImplementedError)
return
}
if elements[2] == "version" {
if req.Method == "GET" {
2021-05-16 09:12:38 +00:00
log.Printf("getVersion(%s)", syncId)
2021-05-08 16:03:21 +00:00
getVersion(syncId, w, req)
return
}
sendJSONError(w, syncstore.MethodNotImplementedError)
return
}
}
sendJSONError(w, syncstore.NotImplementedError)
}
func init() {
2021-05-17 15:31:34 +00:00
var (
err error
storeFlag string
storeDrv syncstore.StoreDriver
)
2021-05-08 16:03:21 +00:00
flag.StringVar(&listen, "listen", ":8090", "listen address and port")
2021-05-16 12:51:39 +00:00
flag.StringVar(&storeFlag, "store", "fs:data", "blob store driver")
2021-05-16 15:46:05 +00:00
flag.IntVar(&maxSyncSize, "maxsize", defaultMaxSyncSize, "maximum size of a sync in bytes")
2021-05-08 16:03:21 +00:00
flag.Parse()
2021-05-16 12:51:39 +00:00
storeFlagTokens := strings.Split(storeFlag, ":")
2021-05-08 16:03:21 +00:00
2021-05-16 12:51:39 +00:00
switch storeFlagTokens[0] {
2021-05-08 16:03:21 +00:00
case "fs":
2021-05-16 12:51:39 +00:00
if len(storeFlagTokens) != 2 {
2021-05-08 16:03:21 +00:00
err = fmt.Errorf("argument required for fs store driver")
} else {
2021-05-16 12:51:39 +00:00
storeDrv, err = syncstore.NewFSStore(storeFlagTokens[1])
2021-05-08 16:03:21 +00:00
}
case "mem":
storeDrv, err = syncstore.NewMemStore()
default:
2021-05-16 19:11:45 +00:00
err = fmt.Errorf("Invalid store driver: " + storeFlagTokens[0])
2021-05-08 16:03:21 +00:00
}
if err != nil {
2021-05-16 09:12:38 +00:00
log.Fatalf("store initialization failed: %v", err)
2021-05-08 16:03:21 +00:00
}
store = syncstore.NewStore(storeDrv)
}
func main() {
http.HandleFunc("/info", info)
http.HandleFunc("/bookmarks", bookmarks)
http.HandleFunc("/bookmarks/", bookmarks)
2021-05-16 09:12:38 +00:00
log.Println("HTTP server listening on", listen)
err := http.ListenAndServe(listen, nil)
log.Println("HTTP server terminated", err)
2021-05-08 16:03:21 +00:00
}