Initial prototype

This commit is contained in:
patwie
2024-03-28 23:57:33 +00:00
commit 36e3ecb5c7
9 changed files with 475 additions and 0 deletions
+192
View File
@@ -0,0 +1,192 @@
package main
import (
"bytes"
"excalidraw-backend/core"
"excalidraw-backend/documents/memory"
"fmt"
"io"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/go-chi/render"
"github.com/oklog/ulid/v2"
"github.com/zishang520/engine.io/v2/types"
socketio "github.com/zishang520/socket.io/v2/socket"
)
type (
DocumentCreateResponse struct {
ID string `json:"id"`
}
UserToFollow struct {
SocketId string
Username string
}
OnUserFollowedPayload struct {
UserToFollow UserToFollow
action string
}
)
func main() {
opts := socketio.DefaultServerOptions()
opts.SetMaxHttpBufferSize(5000000)
opts.SetPath("/socket.io")
opts.SetAllowEIO3(true)
opts.SetCors(&types.Cors{
Origin: "*",
Credentials: true,
})
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()
socket.On("join-room", func(datas ...any) {
room := socketio.Room(datas[0].(string))
fmt.Printf("Socket %v has joined %v\n", me, room)
socket.Join(room)
ioo.In(room).FetchSockets()(func(sockets []*socketio.RemoteSocket, _ error) {
if len(sockets) <= 1 {
ioo.To(socketio.Room(socket.Id())).Emit("first-in-room")
} else {
fmt.Printf("emit new user %v in room %v\n", me, room)
socket.Broadcast().To(room).Emit("new-user", me)
}
data := []socketio.SocketId{}
for _, osocket := range sockets {
data = append(data, osocket.Id())
}
fmt.Printf(" room %v has users %v\n", room, data)
ioo.In(room).Emit(
"room-user-change",
data,
)
})
})
socket.On("server-broadcast", func(datas ...any) {
roomID := datas[0].(string)
fmt.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)
socket.Volatile().Broadcast().To(socketio.Room(roomID)).Emit("client-broadcast", datas[1], datas[2])
})
socket.On("user-follow", func(datas ...any) {
// TODO()
})
socket.On("disconnecting", func(datas ...any) {
for _, oroom := range socket.Rooms().Keys() {
ioo.In(oroom).FetchSockets()(func(sockets []*socketio.RemoteSocket, _ error) {
otherClients := []socketio.SocketId{}
fmt.Printf("disconnecting %v from room %v", me, oroom)
for _, osocket := range sockets {
if osocket.Id() != me {
otherClients = append(otherClients, osocket.Id())
fmt.Println("other", osocket.Id())
}
}
if len(otherClients) > 0 {
ioo.In(oroom).Emit(
"room-user-change",
otherClients,
)
}
})
}
})
socket.On("disconnect", func(datas ...any) {
socket.RemoveAllListeners("")
socket.Disconnect(true)
})
})
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"https://*", "http://*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "Content-Length", "X-CSRF-Token", "Token", "session", "Origin", "Host", "Connection", "Accept-Encoding", "Accept-Language", "X-Requested-With"},
AllowCredentials: true,
MaxAge: 300, // Maximum value not ignored by any of major browsers
}))
documentStore := memory.NewDocumentStore()
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("you are all set"))
fmt.Println(ulid.Make())
render.Status(r, http.StatusOK)
})
r.Route("/api/v2", func(r chi.Router) {
r.Post("/post/", func(w http.ResponseWriter, r *http.Request) {
data := new(bytes.Buffer)
_, err := io.Copy(data, r.Body)
if err != nil {
http.Error(w, "Failed to copy", http.StatusInternalServerError)
return
}
id, err := documentStore.Create(r.Context(), &core.Document{Data: *data})
if err != nil {
http.Error(w, "Failed to save", http.StatusInternalServerError)
return
}
render.JSON(w, r, DocumentCreateResponse{ID: id})
render.Status(r, http.StatusOK)
})
r.Route("/{id}", func(r chi.Router) {
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
document, err := documentStore.FindID(r.Context(), id)
if err != nil {
http.Error(w, "not found", http.StatusNotFound)
return
}
w.Write(document.Data.Bytes())
})
})
})
r.Handle("/socket.io/", ioo.ServeHandler(nil))
go http.ListenAndServe(":3002", r)
exit := make(chan struct{})
SignalC := make(chan os.Signal)
signal.Notify(SignalC, os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
go func() {
for s := range SignalC {
switch s {
case os.Interrupt, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
close(exit)
return
}
}
}()
<-exit
ioo.Close(nil)
os.Exit(0)
}