Explorar o código

Intercept calls to firebase API

Instead of calling any third-party or home, we provide the stub for
firebase API calls.
patwie %!s(int64=2) %!d(string=hai) anos
pai
achega
eea09906f4
Modificáronse 3 ficheiros con 210 adicións e 17 borrados
  1. 0 0
      handlers/api/documents/documents.go
  2. 131 0
      handlers/api/firebase/firebase.go
  3. 79 17
      main.go

+ 0 - 0
handlers/api/documents.go → handlers/api/documents/documents.go


+ 131 - 0
handlers/api/firebase/firebase.go

@@ -0,0 +1,131 @@
+package firebase
+
+import (
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/go-chi/chi/v5"
+	"github.com/go-chi/render"
+)
+
+type (
+	BatchGetRequest struct {
+		Documents []string `json:"documents"`
+	}
+	BatchGetEmptyResponse struct {
+		Missing  string `json:"missing"`
+		ReadTime string `json:"readTime"`
+	}
+
+	FoundInfoResponse struct {
+		Name       string      `json:"name"`
+		Fields     interface{} `json:"fields"`
+		CreateTime string      `json:"createTime"`
+		UpdateTime string      `json:"updateTime"`
+	}
+	BatchGetExistsResponse struct {
+		Found    FoundInfoResponse `json:"found"`
+		ReadTime string            `json:"readTime"`
+	}
+
+	UpdateRequest struct {
+		Name   string      `json:"name"`
+		Fields interface{} `json:"fields"`
+	}
+	WriteRequest struct {
+		Update UpdateRequest `json:"update"`
+	}
+	BatchCommitRequest struct {
+		Writes []WriteRequest `json:"writes"`
+	}
+
+	WriteResult struct {
+		UpdateTime string `json:"updateTime"`
+	}
+	BatchCommitResponse struct {
+		WriteResults []WriteResult `json:"writeResults"`
+		CommitTime   string        `json:"commitTime"`
+	}
+)
+
+var savedItems = make(map[string]interface{})
+
+func (body *BatchGetRequest) Bind(r *http.Request) (err error) {
+	return nil
+}
+func (body *BatchCommitRequest) Bind(r *http.Request) (err error) {
+	return nil
+}
+func HandleBatchCommit() http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		projectId := chi.URLParam(r, "project_id")
+		databaseId := chi.URLParam(r, "database_id")
+		_ = projectId
+		_ = databaseId
+
+		data := &BatchCommitRequest{}
+		// Seems like requests is text/plain but content is json ...
+		if err := render.DecodeJSON(r.Body, data); err != nil {
+			fmt.Println(err)
+			render.Status(r, http.StatusBadRequest)
+			return
+		}
+
+		savedItems[data.Writes[0].Update.Name] = data.Writes[0].Update.Fields
+
+		render.JSON(w, r, BatchCommitResponse{
+			CommitTime: time.Now().Format(time.RFC3339),
+			WriteResults: []WriteResult{
+				WriteResult{UpdateTime: time.Now().Format(time.RFC3339)},
+			},
+		})
+		render.Status(r, http.StatusOK)
+		return
+
+	}
+}
+
+func HandleBatchGet() http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+
+		projectId := chi.URLParam(r, "project_id")
+		databaseId := chi.URLParam(r, "database_id")
+		fmt.Printf("Got %v and %v\n", projectId, databaseId)
+		data := &BatchGetRequest{}
+
+		// Seems like requests is text/plain but content is json ...
+		if err := render.DecodeJSON(r.Body, data); err != nil {
+			fmt.Println(err)
+			render.Status(r, http.StatusBadRequest)
+			return
+		}
+		key := data.Documents[0]
+		fmt.Printf("Got key %v \n", key)
+
+		fields, ok := savedItems[key]
+
+		if !ok {
+			fmt.Println("missing key")
+			render.JSON(w, r, []BatchGetEmptyResponse{BatchGetEmptyResponse{
+				Missing:  key,
+				ReadTime: time.Now().Format(time.RFC3339),
+			}})
+			render.Status(r, http.StatusOK)
+			return
+		}
+		fmt.Println("existing key")
+		render.JSON(w, r, []BatchGetExistsResponse{BatchGetExistsResponse{
+			Found: FoundInfoResponse{
+				Name:       key,
+				Fields:     fields,
+				CreateTime: time.Now().Format(time.RFC3339),
+				UpdateTime: time.Now().Format(time.RFC3339),
+			},
+			ReadTime: time.Now().Format(time.RFC3339),
+		}})
+		render.Status(r, http.StatusOK)
+		return
+
+	}
+}

+ 79 - 17
main.go

@@ -4,19 +4,23 @@ import (
 	"embed"
 	_ "embed"
 	"excalidraw-complete/core"
-	documents "excalidraw-complete/handlers/api"
+	"excalidraw-complete/handlers/api/documents"
+	"excalidraw-complete/handlers/api/firebase"
 	"excalidraw-complete/stores"
 	"fmt"
+	"io"
 	"io/fs"
 	"net/http"
 	"os"
 	"os/signal"
+	"strings"
 	"syscall"
 
 	"github.com/go-chi/chi/v5"
 	"github.com/go-chi/chi/v5/middleware"
 	"github.com/go-chi/cors"
 	"github.com/zishang520/engine.io/v2/types"
+	"github.com/zishang520/engine.io/v2/utils"
 	socketio "github.com/zishang520/socket.io/v2/socket"
 )
 
@@ -40,7 +44,58 @@ func handleUI() http.Handler {
 	if err != nil {
 		panic(err)
 	}
-	return http.FileServer(http.FS(sub))
+    // Let's hot-patch all calls to firebase DB
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		originalPath := r.URL.Path
+		originalPath = strings.TrimPrefix(originalPath, "/")
+		fmt.Println(originalPath)
+		file, err := sub.Open(originalPath)
+		if err != nil {
+			http.Error(w, "File not found", http.StatusNotFound)
+			return
+		}
+		defer file.Close()
+
+		fileContent, err := io.ReadAll(file)
+		if err != nil {
+			http.Error(w, "Error reading file", http.StatusInternalServerError)
+			return
+		}
+		modifiedContent := strings.ReplaceAll(string(fileContent), "firestore.googleapis.com", "localhost:3002")
+		modifiedContent = strings.ReplaceAll(modifiedContent, "ssl=!0", "ssl=0")
+		modifiedContent = strings.ReplaceAll(modifiedContent, "ssl:!0", "ssl:0")
+		if modifiedContent != string(fileContent) {
+			fmt.Println("has replace")
+		}
+
+		// Set the correct Content-Type based on the file extension
+		contentType := http.DetectContentType([]byte(modifiedContent))
+		switch {
+		case strings.HasSuffix(originalPath, ".js"):
+			contentType = "application/javascript"
+		case strings.HasSuffix(originalPath, ".html"):
+			contentType = "text/html"
+		case strings.HasSuffix(originalPath, ".css"):
+			contentType = "text/css"
+		case strings.HasSuffix(originalPath, ".wasm"):
+			contentType = "application/wasm"
+		case strings.HasSuffix(originalPath, ".tsx"):
+			contentType = "text/typescript"
+		case strings.HasSuffix(originalPath, ".png"):
+			contentType = "image/png"
+		case strings.HasSuffix(originalPath, ".woff2"):
+			contentType = "font/woff2"
+		}
+
+		// Serve the modified content
+		w.Header().Set("Content-Type", contentType)
+		_, err = w.Write([]byte(modifiedContent))
+		if err != nil {
+			http.Error(w, "Error serving file", http.StatusInternalServerError)
+			return
+		}
+		return
+	})
 }
 
 func setupRouter(documentStore core.DocumentStore) *chi.Mux {
@@ -55,6 +110,11 @@ func setupRouter(documentStore core.DocumentStore) *chi.Mux {
 		MaxAge:           300, // Maximum value not ignored by any of major browsers
 	}))
 
+	r.Route("/v1/projects/{project_id}/databases/{database_id}", func(r chi.Router) {
+		r.Post("/documents:commit", firebase.HandleBatchCommit())
+		r.Post("/documents:batchGet", firebase.HandleBatchGet())
+	})
+
 	r.Route("/api/v2", func(r chi.Router) {
 		r.Post("/post/", documents.HandleCreate(documentStore))
 		r.Route("/{id}", func(r chi.Router) {
@@ -72,21 +132,24 @@ func setupSocketIO() *socketio.Server {
 		Origin:      "*",
 		Credentials: true,
 	})
+	opts.SetTransports(types.NewSet("polling", "webtransport"))
 	ioo := socketio.NewServer(nil, opts)
 
 	ioo.On("connection", func(clients ...any) {
 		socket := clients[0].(*socketio.Socket)
-		ioo.To(socketio.Room(socket.Id())).Emit("init-room")
 		me := socket.Id()
+		myRoom := socketio.Room(me)
+		ioo.To(myRoom).Emit("init-room")
+		utils.Log().Println("init room ", myRoom)
 		socket.On("join-room", func(datas ...any) {
 			room := socketio.Room(datas[0].(string))
-			fmt.Printf("Socket %v has joined %v\n", me, room)
+			utils.Log().Printf("Socket %v has joined %v\n", me, room)
 			socket.Join(room)
 			ioo.In(room).FetchSockets()(func(usersInRoom []*socketio.RemoteSocket, _ error) {
 				if len(usersInRoom) <= 1 {
-					ioo.To(socketio.Room(me)).Emit("first-in-room")
+					ioo.To(myRoom).Emit("first-in-room")
 				} else {
-					fmt.Printf("emit new user %v in room %v\n", me, room)
+					utils.Log().Printf("emit new user %v in room %v\n", me, room)
 					socket.Broadcast().To(room).Emit("new-user", me)
 				}
 
@@ -95,7 +158,7 @@ func setupSocketIO() *socketio.Server {
 				for _, user := range usersInRoom {
 					newRoomUsers = append(newRoomUsers, user.Id())
 				}
-				fmt.Printf(" room %v has users %v\n", room, newRoomUsers)
+				utils.Log().Println(" room ", room, " has users ", newRoomUsers)
 				ioo.In(room).Emit(
 					"room-user-change",
 					newRoomUsers,
@@ -105,12 +168,12 @@ func setupSocketIO() *socketio.Server {
 		})
 		socket.On("server-broadcast", func(datas ...any) {
 			roomID := datas[0].(string)
-			fmt.Printf(" user %v sends update to room %v\n", me, roomID)
+			utils.Log().Printf(" user %v sends update to room %v\n", me, roomID)
 			socket.Broadcast().To(socketio.Room(roomID)).Emit("client-broadcast", datas[1], datas[2])
 		})
 		socket.On("server-volatile-broadcast", func(datas ...any) {
 			roomID := datas[0].(string)
-			fmt.Printf(" user %v sends volatile update to room %v\n", me, roomID)
+			utils.Log().Printf(" user %v sends volatile update to room %v\n", me, roomID)
 			socket.Volatile().Broadcast().To(socketio.Room(roomID)).Emit("client-broadcast", datas[1], datas[2])
 		})
 
@@ -121,20 +184,18 @@ func setupSocketIO() *socketio.Server {
 		socket.On("disconnecting", func(datas ...any) {
 			for _, currentRoom := range socket.Rooms().Keys() {
 				ioo.In(currentRoom).FetchSockets()(func(usersInRoom []*socketio.RemoteSocket, _ error) {
-					allUsers := []socketio.SocketId{}
-					remainingUsers := []socketio.SocketId{}
-					fmt.Printf("disconnecting %v from room %v\n", me, currentRoom)
+					otherClients := []socketio.SocketId{}
+					utils.Log().Printf("disconnecting %v from room %v\n", me, currentRoom)
 					for _, userInRoom := range usersInRoom {
-						allUsers = append(allUsers, userInRoom.Id())
 						if userInRoom.Id() != me {
-							remainingUsers = append(remainingUsers, userInRoom.Id())
+							otherClients = append(otherClients, userInRoom.Id())
 						}
 					}
-					if len(remainingUsers) > 0 {
-						fmt.Printf("leaving user, room %v has users %v -> %v\n", currentRoom, allUsers, remainingUsers)
+					if len(otherClients) > 0 {
+						utils.Log().Printf("leaving user, room %v has users  %v\n", currentRoom, otherClients)
 						ioo.In(currentRoom).Emit(
 							"room-user-change",
-							remainingUsers,
+							otherClients,
 						)
 
 					}
@@ -149,6 +210,7 @@ func setupSocketIO() *socketio.Server {
 			socket.Disconnect(true)
 		})
 	})
+	utils.Log().Println("%v", ioo)
 	return ioo
 
 }