diff --git a/chatGrpcMiddleware.go b/chatGrpcMiddleware.go new file mode 100644 index 0000000..46fdec8 --- /dev/null +++ b/chatGrpcMiddleware.go @@ -0,0 +1,176 @@ +package localserver + +import ( + "context" + "encoding/json" + "strconv" + + "github.com/pion/webrtc/v3" +) + +const ( + NEW_CHAT = "new_chat" + CHAT_DELETED = "chat_deleted" + CHAT_SYNCED = "chat_synced" + SYNC_CHAT = "sync_chat" + EXPORT_CHAT = "export_chat" + CHAT_EXPORTED = "chat_exported" + GET_NODE_CHATS = "get_node_chats" + LEAVE_CHAT = "leave_chat" + CHAT_WEBRTC_OFFER = "chat_webrtc_offer" + CHAT_WEBRTC_ANSWER = "chat_webrtc_answer" + CHAT_WEBRTC_COUNTER_OFFER = "chat_webrtc_counter_offer" + CHAT_WEBRTC_RENNEGOTIATION_OFFER = "chat_webrtc_rennegotiation_offer" + CHAT_WEBRTC_RENNEGOTIATION_ANSWER = "chat_werbtc_rennegotiation_answer" + CHAT_WEBRTC_CANDIDATE = "chat_webrtc_candidate" +) + +type ChatGrpcMiddleware struct { + Manager *ChatsManager + stream SignalingService_LinkClient +} + +func NewChatGrpcMiddleware(manager *ChatsManager) (chatGrpcMiddleware *ChatGrpcMiddleware) { + chatGrpcMiddleware = &ChatGrpcMiddleware{ + Manager: manager, + } + return +} + +func (cm *ChatGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) { + bs, err := json.Marshal((map[string]string{ + "from": NodeID, + "to": to, + "candidate": candidate.ToJSON().Candidate, + "sdpMid": *candidate.ToJSON().SDPMid, + "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), + })) + if err != nil { + return + } + err = cm.stream.Send(&SignalingMessage{ + Type: CHAT_WEBRTC_CANDIDATE, + From: NodeID, + To: to, + Payload: bs, + }) + return +} + +func (cm *ChatGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, streram SignalingService_LinkClient) (err error) { + done, errCh := make(chan struct{}), make(chan error) + go func() { + var payload map[string]string + if e := json.Unmarshal(req.Payload, &payload); e != nil { + errCh <- e + return + } + switch req.GetType() { + case NEW_CHAT: + if err := validateRequest(payload, "chatID", "initiator", "target", "initiatorHostId", "targetHostId"); err != nil { + errCh <- err + return + } + chat, err := NewNodeChat(payload["chatID"], payload["chatID"], payload["initiator"], payload["target"], payload["initiatorHostId"], payload["targetHostId"], false) + if err != nil { + errCh <- err + return + } + _ = atomicallyExecute(cm.Manager.chatsFlag, func() (err error) { + cm.Manager.Chats[chat.ID] = chat + return + }) + case CHAT_DELETED: + if err := validateRequest(payload, "chatID"); err != nil { + return + } + if err := cm.Manager.DeleteChat(payload["chatID"]); err != nil { + errCh <- err + return + } + case CHAT_EXPORTED: + //todo: implement chat exported + case EXPORT_CHAT: + //todo: implement export chat exported + case CHAT_SYNCED: + //todo: implement chat synced + case SYNC_CHAT: + //todo: implement sync chat + case CHAT_WEBRTC_OFFER: + if err := validateRequest(payload, SDP); err != nil { + errCh <- err + return + } + if err := cm.Manager.HandleOffer(ctx, req.GetFrom(), req.GetTo(), payload, cm.signalCandidate); err != nil { + errCh <- err + return + } + case CHAT_WEBRTC_ANSWER: + if err := validateRequest(payload, SDP); err != nil { + errCh <- err + return + } + if err := cm.Manager.HandleAnswer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil { + errCh <- err + return + } + case CHAT_WEBRTC_COUNTER_OFFER: + if err := cm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil { + errCh <- err + return + } + case CHAT_WEBRTC_RENNEGOTIATION_OFFER: + if err := validateRequest(payload, SDP); err != nil { + errCh <- err + return + } + if err := cm.Manager.HandleRennegotiationOffer(req.GetFrom(), payload[SDP]); err != nil { + errCh <- err + return + } + case CHAT_WEBRTC_RENNEGOTIATION_ANSWER: + if err := validateRequest(payload, SDP); err != nil { + errCh <- err + return + } + if err := cm.Manager.HandleRennegotiationAnswer(req.GetFrom(), payload[SDP]); err != nil { + errCh <- err + return + } + case CHAT_WEBRTC_CANDIDATE: + if err := validateRequest(payload, "candidate", "sdpMLineIndex", "sdpMid"); err != nil { + errCh <- err + return + } + logger.Println(payload) + i, err := strconv.Atoi(payload["sdpMLineIndex"]) + if err != nil { + errCh <- err + return + } + SDPMLineIndex := uint16(i) + sdpMid := payload["sdpMid"] + logger.Println(sdpMid, SDPMLineIndex) + if err := cm.Manager.AddCandidate(&webrtc.ICECandidateInit{ + Candidate: payload["candidate"], + SDPMid: &sdpMid, + SDPMLineIndex: &SDPMLineIndex, + }, req.GetFrom()); err != nil { + errCh <- err + return + } + default: + logger.Println("no request for chats grpc middleware") + logger.Println(payload) + logger.Println(req.Type) + } + }() + select { + case <-ctx.Done(): + return ctx.Err() + case err = <-errCh: + return + case <-done: + return + } +} diff --git a/chatManager.go b/chatManager.go new file mode 100644 index 0000000..d1456a2 --- /dev/null +++ b/chatManager.go @@ -0,0 +1,730 @@ +package localserver + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strconv" + "sync" + + "github.com/google/uuid" + "github.com/pion/webrtc/v3" +) + +type ChatsManager struct { + ID string + Chats map[string]*NodeChat + LocalSD map[string]*webrtc.SessionDescription + RTCPeerConnections map[string]*RTCPeerConnection + DataChannels map[string]*DataChannel + PendingCandidates map[string][]*webrtc.ICECandidate + stream SignalingService_LinkClient + chatsFlag *uint32 + peerConnectionFlag *uint32 + localSDFlag *uint32 + dataChannelFlag *uint32 + candidateFlag *uint32 +} + +type NodeChat struct { + ID string `json:"id"` + Name string `json:"name"` + Initiator string `json:"initiator"` + Target string `json:"target"` + InitiatorHost string `json:"initiatorHost"` + TargetHost string `json:"targetHost"` + DataChannels map[string]*DataChannel `json:"-"` + DataChannelsFlag *uint32 `json:"-"` + ChatRequestScheduler *ChatRequestScheduler `json:"-"` + Initialized bool `json:"-"` +} + +func NewNodeChat(id, name, initiator, target, initiatorHost, targetHost string, initialized bool) (chat *NodeChat, err error) { + dataChannels, dataChannelFlag := make(map[string]*DataChannel), uint32(0) + nodeChatMessageHandler, err := NewNodeChatChannelsHandler(chat.ID, chat.ID, chat.Initiator, chat.Target, chat.InitiatorHost, chat.TargetHost, dataChannels, &dataChannelFlag) + chatScheduler, e := NewChatRequestScheduler(initiator, target, initiatorHost, targetHost, nodeChatMessageHandler) + go func() { + for schedErr := range e { + logger.Println("chat error:", schedErr) + } + }() + chat = &NodeChat{ + ID: id, + Name: name, + Initiator: initiator, + Target: target, + InitiatorHost: initiatorHost, + TargetHost: targetHost, + DataChannels: dataChannels, + DataChannelsFlag: &dataChannelFlag, + ChatRequestScheduler: chatScheduler, + } + return +} + +func NewChatManager(id, token string) (chatManager *ChatsManager, err error) { + chatsFlag := uint32(0) + peerConnectionFlag := uint32(0) + localSDFlag := uint32(0) + dataChannelFlag := uint32(0) + candidateFlag := uint32(0) + dataChannels := make(map[string]*DataChannel) + chatsMap := make(map[string]*NodeChat) + chats, err := chatManager.fetchChats(id, token) + if err != nil { + return + } + for _, chat := range chats { + c, err := NewNodeChat(chat.ID, chat.Name, chat.Initiator, chat.Target, chat.InitiatorHost, chat.TargetHost, false) + if err != nil { + return nil, err + } + _ = atomicallyExecute(&chatsFlag, func() (err error) { + chatsMap[chat.ID] = c + return + }) + } + chatsFolder, err := os.ReadDir(filepath.Join(dataPath, "data", "chats")) + if err != nil { + if os.IsNotExist(err) { + if err = os.MkdirAll(filepath.Join(dataPath, "data", "chats"), 0770); err != nil { + return + } + } else { + return nil, err + } + } + chatManager = &ChatsManager{ + ID: id, + Chats: chatsMap, + LocalSD: make(map[string]*webrtc.SessionDescription), + RTCPeerConnections: make(map[string]*RTCPeerConnection), + PendingCandidates: make(map[string][]*webrtc.ICECandidate), + DataChannels: dataChannels, + chatsFlag: &chatsFlag, + peerConnectionFlag: &peerConnectionFlag, + localSDFlag: &localSDFlag, + dataChannelFlag: &dataChannelFlag, + candidateFlag: &candidateFlag, + } + for _, c := range chatsFolder { + if _, ok := chatsMap[c.Name()]; !ok { + logger.Println(chatManager.DeleteChat(c.Name())) + } + } + return +} + +func (cm *ChatsManager) fetchChats(nodeId string, token string) (chats []*NodeChat, err error) { + em := NewEncryptionManager() + sig := em.SignRequestHMAC(nodeId) + body, err := json.Marshal(map[string]interface{}{ + "type": GET_NODE_CHATS, + "mac": sig, + "from": nodeId, + "peerType": "node", + "payload": map[string]string{ + "host": nodeId, + "lastIndex": "0", + }, + }) + if err != nil { + return + } + res, err := HTTPClient.Post("https://dev.zippytal.com/req", "application/json", bytes.NewReader(body)) + if err != nil { + logger.Println("error come from there inn chat manager") + return + } + bs, err := io.ReadAll(res.Body) + if err != nil { + return + } + var payload map[string]any + if err = json.Unmarshal(bs, &payload); err != nil { + return + } + b, err := json.Marshal(payload["Chats"]) + if err != nil { + return + } + err = json.Unmarshal(b, &chats) + return +} + +func (cm *ChatsManager) sendSignalingMessage(messageType, from, to string, payload map[string]interface{}) (err error) { + bs, err := json.Marshal(payload) + if err != nil { + return + } + err = cm.stream.Send(&SignalingMessage{ + Type: messageType, + From: from, + To: to, + Payload: bs, + }) + return +} + +func (cm *ChatsManager) DeleteChat(chatId string) error { + return os.RemoveAll(filepath.Join(dataPath, "data", "chats", chatId)) +} + +func (cm *ChatsManager) CreateOffer(ctx context.Context, target string, from string, chatId string, cb OnICECandidateFunc) (err error) { + peerConnection, err := cm.createPeerConnection(target, from, chatId, webrtc.SDPTypeOffer, cb) + if err != nil { + return + } + logger.Println("connection created") + rawOffer, err := peerConnection.CreateOffer(nil) + if err != nil { + return + } + if err = peerConnection.SetLocalDescription(rawOffer); err != nil { + return + } + _ = atomicallyExecute(cm.peerConnectionFlag, func() (err error) { + id := uuid.New().String() + logger.Println("adding for target", target) + cm.RTCPeerConnections[target] = &RTCPeerConnection{ + id: id, + PeerConnection: peerConnection, + makingOffer: true, + makingOfferLock: &sync.Mutex{}, + negotiate: cm.negotiate, + } + return + }) + err = cm.sendSignalingMessage(string(CHAT_WEBRTC_OFFER), cm.ID, target, map[string]any{ + "to": target, + "from": cm.ID, + "sdp": rawOffer.SDP, + }) + return +} + +func (cm *ChatsManager) HandleOffer(ctx context.Context, from string, to string, req map[string]string, cb OnICECandidateFunc) (err error) { + done, errCh := make(chan struct{}), make(chan error) + go func() { + if _, ok := cm.Chats[req["chatId"]]; !ok { + err = fmt.Errorf("no corresponding chat") + errCh <- err + return + } + logger.Println("handling chat offer") + _ = atomicallyExecute(cm.chatsFlag, func() (err error) { + return + }) + if _, ok := cm.RTCPeerConnections[from]; ok { + if e := cm.HandleLeavingMember(from, req["chatId"], false); e != nil { + logger.Println(e) + } + } + peerConnection, err := cm.createPeerConnection(from, to, req["chatId"], webrtc.SDPTypeAnswer, cb) + if err != nil { + errCh <- err + return + } + logger.Println("peer connection created") + _ = atomicallyExecute(cm.peerConnectionFlag, func() (err error) { + id := uuid.New().String() + cm.RTCPeerConnections[from] = &RTCPeerConnection{ + PeerConnection: peerConnection, + id: id, + makingOffer: false, + makingOfferLock: &sync.Mutex{}, + negotiate: cm.negotiate, + } + + logger.Println("peer connection added to map") + offer := webrtc.SessionDescription{ + Type: webrtc.SDPTypeOffer, + SDP: req[SDP], + } + if err = peerConnection.SetRemoteDescription(offer); err != nil { + errCh <- err + return + } + rawAnswer, err := peerConnection.CreateAnswer(nil) + if err != nil { + errCh <- err + return + } + _ = atomicallyExecute(cm.localSDFlag, func() (err error) { + cm.LocalSD[from] = &rawAnswer + return + }) + if err = peerConnection.SetLocalDescription(rawAnswer); err != nil { + errCh <- err + return + } + if err = cm.sendSignalingMessage(string(CHAT_WEBRTC_ANSWER), cm.ID, from, map[string]any{ + "to": from, + "from": cm.ID, + "sdp": rawAnswer.SDP, + }); err != nil { + errCh <- err + return + } + done <- struct{}{} + return + }) + }() + select { + case <-done: + return + case err = <-errCh: + return + case <-ctx.Done(): + err = ctx.Err() + return + } +} + +func (cm *ChatsManager) HandleAnswer(ctx context.Context, from string, to string, req map[string]string) (err error) { + defer func() { + if r := recover(); err != nil { + logger.Printf("recover from panic in handle answer : %v\n", r) + } + }() + if err = atomicallyExecute(cm.peerConnectionFlag, func() (err error) { + if _, ok := cm.RTCPeerConnections[from]; !ok { + err = fmt.Errorf("no corresponding peer connection for id : %s", from) + return + } + peerConnnection := cm.RTCPeerConnections[from] + logger.Println("---------------------") + logger.Println(req[SDP]) + logger.Println("---------------------") + if err = peerConnnection.SetRemoteDescription(webrtc.SessionDescription{ + Type: webrtc.SDPTypeAnswer, + SDP: req[SDP], + }); err != nil { + logger.Println("error occured while setting remote description in handle answer") + return + } + return + }); err != nil { + return + } + if err = cm.sendSignalingMessage(string(CHAT_WEBRTC_COUNTER_OFFER), cm.ID, from, map[string]any{ + "from": cm.ID, + "to": from, + }); err != nil { + return + } + _ = atomicallyExecute(cm.candidateFlag, func() (err error) { + for _, candidate := range cm.PendingCandidates[from] { + logger.Println("sending candidate from answer to", from) + if err = cm.sendSignalingMessage(string(CHAT_WEBRTC_CANDIDATE), cm.ID, from, map[string]any{ + "from": cm.ID, + "to": from, + "candidate": candidate.ToJSON().Candidate, + "sdpMid": *candidate.ToJSON().SDPMid, + "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), + }); err != nil { + logger.Println(err) + continue + } + } + delete(cm.PendingCandidates, from) + return + }) + _ = atomicallyExecute(cm.localSDFlag, func() (err error) { + delete(cm.LocalSD, from) + return + }) + return +} + +func (cm *ChatsManager) HandleCounterOffer(ctx context.Context, from string, to string, req map[string]string) (err error) { + _ = atomicallyExecute(cm.candidateFlag, func() (err error) { + for _, candidate := range cm.PendingCandidates[from] { + logger.Println("sending candidate to", from) + if err = cm.sendSignalingMessage(string(CHAT_WEBRTC_CANDIDATE), cm.ID, from, map[string]any{ + "from": cm.ID, + "to": from, + "candidate": candidate.ToJSON().Candidate, + "sdpMid": *candidate.ToJSON().SDPMid, + "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), + }); err != nil { + return + } + } + delete(cm.PendingCandidates, from) + return + }) + _ = atomicallyExecute(cm.localSDFlag, func() (err error) { + delete(cm.LocalSD, from) + return + }) + return +} + +func (cm *ChatsManager) createPeerConnection(target string, from string, chatId string, peerType webrtc.SDPType, cb OnICECandidateFunc) (peerConnection *webrtc.PeerConnection, err error) { + defer func() { + if r := recover(); err != nil { + logger.Printf("recover from panic : %v\n", r) + } + }() + config := webrtc.Configuration{ + ICEServers: []webrtc.ICEServer{ + { + URLs: []string{"stun:stun.l.google.com:19302", "stun:stunserver.org:3478"}, + }, + }, + SDPSemantics: webrtc.SDPSemanticsUnifiedPlan, + } + + peerConnection, err = webrtc.NewPeerConnection(config) + if err != nil { + return + } + logger.Println("---------------------------------------------------") + if peerType == webrtc.SDPTypeOffer { + channel, err := peerConnection.CreateDataChannel("data", &webrtc.DataChannelInit{}) + if err != nil { + return nil, err + } + reqChan := make(chan *ChatRequest) + channel.OnOpen(func() { + logger.Println(chatId) + if _, ok := cm.Chats[chatId]; ok { + logger.Println("this chat exist") + _ = atomicallyExecute(cm.Chats[chatId].DataChannelsFlag, func() (err error) { + x := uint32(0) + cm.Chats[chatId].DataChannels[target] = &DataChannel{DataChannel: channel, bufferedAmountLowThresholdReached: make(<-chan struct{}), l: &x} + return + }) + if _, ok := cm.Chats[chatId]; !ok { + err = fmt.Errorf("no corresponding Chats") + return + } + done, err := cm.Chats[chatId].ChatRequestScheduler.Schedule(reqChan) + bs, jsonErr := json.Marshal(&ZoneResponse{ + Type: "user_chat_init", + From: chatId, + To: target, + Payload: map[string]interface{}{}, + }) + if jsonErr != nil { + logger.Println("error in open channel", jsonErr) + return + } + if sendErr := channel.SendText(string(bs)); sendErr != nil { + logger.Println("error in open channel send", sendErr) + return + } + go func() { + for { + select { + case <-done: + return + case e := <-err: + logger.Println("----- error from scheduler:", e) + } + } + }() + } + }) + channel.OnClose(func() { + close(reqChan) + //_ = cm.HandleLeavingMember(target, chatId, true) + }) + channel.OnError(func(err error) { + close(reqChan) + //_ = cm.HandleLeavingMember(target, chatId, true) + }) + channel.OnMessage(func(msg webrtc.DataChannelMessage) { + var req ChatRequest + if err := json.Unmarshal(msg.Data, &req); err != nil { + logger.Println(err) + return + } + logger.Println("incoming request", req) + reqChan <- &req + }) + logger.Println("new channel for target : ", target) + logger.Println(target) + _ = atomicallyExecute(cm.dataChannelFlag, func() (err error) { + l := uint32(0) + cm.DataChannels[target] = &DataChannel{ + DataChannel: channel, + bufferedAmountLowThresholdReached: make(<-chan struct{}), + l: &l, + } + return + }) + } else { + peerConnection.OnDataChannel(func(dc *webrtc.DataChannel) { + _ = atomicallyExecute(cm.dataChannelFlag, func() (err error) { + l := uint32(0) + cm.DataChannels[target] = &DataChannel{ + DataChannel: dc, + l: &l, + } + return + }) + reqChan := make(chan *ChatRequest, 100) + if dc.Label() == "data" { + dc.OnOpen(func() { + logger.Println(chatId) + if _, ok := cm.Chats[chatId]; ok { + logger.Println("this chat exist") + _ = atomicallyExecute(cm.Chats[chatId].DataChannelsFlag, func() (err error) { + logger.Println("adding dc to dc map") + x := uint32(0) + cm.Chats[chatId].DataChannels[target] = &DataChannel{DataChannel: dc, bufferedAmountLowThresholdReached: make(<-chan struct{}), l: &x} + return + }) + if _, ok := cm.Chats[chatId]; !ok { + err = fmt.Errorf("no corresponding Chats") + return + } + done, err := cm.Chats[chatId].ChatRequestScheduler.Schedule(reqChan) + go func() { + for { + select { + case <-done: + return + case <-err: + } + } + }() + } + }) + dc.OnClose(func() { + fmt.Println("closing gracefully event dc...") + close(reqChan) + }) + dc.OnError(func(err error) { + logger.Println("--------------- error in dc:", err) + close(reqChan) + }) + dc.OnMessage(func(msg webrtc.DataChannelMessage) { + var req ChatRequest + if err := json.Unmarshal(msg.Data, &req); err != nil { + logger.Println(err) + return + } + logger.Println("incoming request", req) + go func() { + reqChan <- &req + }() + }) + _ = atomicallyExecute(cm.dataChannelFlag, func() (err error) { + l := uint32(0) + cm.DataChannels[target] = &DataChannel{ + DataChannel: dc, + bufferedAmountLowThresholdReached: make(<-chan struct{}), + l: &l, + } + return + }) + } else { + if _, ok := cm.Chats[chatId]; ok { + fmt.Println("got new mtfking datachannel") + fmt.Println(dc.Label()) + scheduler := cm.Chats[chatId].ChatRequestScheduler + l := uint32(0) + datachannel := &DataChannel{ + DataChannel: dc, + bufferedAmountLowThresholdReached: make(<-chan struct{}), + l: &l, + } + catched := scheduler.DispatchDataChannel(context.Background(), datachannel) + if !catched { + if closeErr := datachannel.DataChannel.Close(); closeErr != nil { + logger.Println(closeErr) + } + } + } + } + }) + } + peerConnection.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) { + if pcs == webrtc.PeerConnectionStateDisconnected || pcs == webrtc.PeerConnectionStateFailed { + logger.Println(pcs) + if err = cm.HandleLeavingMember(target, chatId, true); err != nil { + logger.Println(err) + } + } + }) + peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) { + logger.Printf("ICE connection state has changed %s\n", is.String()) + }) + peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) { + if i == nil { + return + } + _ = atomicallyExecute(cm.candidateFlag, func() (err error) { + desc := peerConnection.RemoteDescription() + if desc == nil { + logger.Println("generated candidate appended to list : ", i) + cm.PendingCandidates[target] = append(cm.PendingCandidates[target], i) + } else { + logger.Println("generated candidate : ", i) + if iceCandidateErr := cb(target, i); iceCandidateErr != nil { + logger.Println(iceCandidateErr) + } + } + return + }) + }) + return +} + +func (cm *ChatsManager) HandleRennegotiationOffer(from, sdp string) (err error) { + err = atomicallyExecute(cm.peerConnectionFlag, func() (err error) { + if _, ok := cm.RTCPeerConnections[from]; !ok { + err = fmt.Errorf("no corresponding peer connection for id %s", from) + return + } + if err = cm.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil { + return + } + localSd, err := cm.RTCPeerConnections[from].CreateAnswer(nil) + if err != nil { + return + } + if err = cm.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil { + return + } + if err = cm.sendSignalingMessage(string(CHAT_WEBRTC_RENNEGOTIATION_ANSWER), cm.ID, from, map[string]any{ + "to": from, + "sdp": localSd.SDP, + }); err != nil { + logger.Println(err) + return + } + return + }) + return +} + +func (cm *ChatsManager) HandleRennegotiationAnswer(from string, sdp string) (err error) { + _ = atomicallyExecute(cm.peerConnectionFlag, func() (err error) { + if _, ok := cm.RTCPeerConnections[from]; !ok { + err = fmt.Errorf("no corresponding peer connection for id %s", from) + return + } + err = cm.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer}) + return + }) + return +} + +func (cm *ChatsManager) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) { + _ = atomicallyExecute(cm.candidateFlag, func() (err error) { + if candidate != nil { + if connection, ok := cm.RTCPeerConnections[from]; ok { + err = connection.AddICECandidate(*candidate) + } + } + return + }) + return +} + +func (cm *ChatsManager) HandleLeavingMember(id, chatId string, signalLeaving bool) (err error) { + logger.Println("---------------- handling leaving member", id) + if err = atomicallyExecute(cm.peerConnectionFlag, func() (err error) { + if _, ok := cm.RTCPeerConnections[id]; !ok { + err = fmt.Errorf("no correponding peerconnection for id %s", id) + return + } + return + }); err != nil { + return + } + if signalLeaving { + nerr := cm.notifyLeavingMember(id, chatId, NodeID) + fmt.Println(nerr) + } + err = atomicallyExecute(cm.chatsFlag, func() (err error) { + logger.Println(err) + logger.Println("---------------- cleaning chat handlers", id) + if chat, ok := cm.Chats[chatId]; ok { + for _, handlersPublishers := range chat.ChatRequestScheduler.handlersPublishers { + go func(hp chan<- *ChatRequest) { + hp <- &ChatRequest{ + ReqType: LEAVE_CHAT, + From: id, + Payload: map[string]interface{}{ + "userId": id, + }, + } + }(handlersPublishers) + } + if err = atomicallyExecute(chat.DataChannelsFlag, func() (err error) { + defer delete(chat.DataChannels, id) + if dataChannel, ok := chat.DataChannels[id]; ok { + if err = dataChannel.DataChannel.Close(); err != nil { + return + } + } + return + }); err != nil { + fmt.Println(err) + } + logger.Println("datachannels cleaned", id) + } else { + err = fmt.Errorf("no corresponding chat for chatId %s", chatId) + } + logger.Println(err) + err = atomicallyExecute(cm.peerConnectionFlag, func() (err error) { + if _, ok := cm.RTCPeerConnections[id]; ok { + defer delete(cm.RTCPeerConnections, id) + if err = cm.RTCPeerConnections[id].Close(); err != nil { + return + } + } + return + }) + fmt.Println(err) + _ = atomicallyExecute(cm.candidateFlag, func() (err error) { + delete(cm.PendingCandidates, id) + return + }) + return + }) + return +} + +func (cm *ChatsManager) notifyLeavingMember(userId, chatId, hostId string) (err error) { + em := NewEncryptionManager() + sig := em.SignRequestHMAC(NodeID) + body, err := json.Marshal(map[string]interface{}{ + "type": "DISCONNECT_CHAT_MEMBER", + "mac": sig, + "from": NodeID, + "peerType": "node", + "payload": map[string]string{ + "chatId": chatId, + "userId": userId, + }, + }) + if err != nil { + return + } + _, err = HTTPClient.Post("https://dev.zippytal.com/req", "application/json", bytes.NewReader(body)) + if err != nil { + logger.Println("error come from there in chat manager") + return + } + return +} + +func (cm *ChatsManager) negotiate(target string, chatId string) { + _ = atomicallyExecute(cm.peerConnectionFlag, func() (err error) { + if _, ok := cm.RTCPeerConnections[target]; !ok { + err = fmt.Errorf("no peerConnections") + return + } + return + }) +} diff --git a/chatMessagesDBHandler.go b/chatMessagesDBHandler.go new file mode 100644 index 0000000..1a2778b --- /dev/null +++ b/chatMessagesDBHandler.go @@ -0,0 +1,322 @@ +package localserver + +import ( + "encoding/binary" + "encoding/json" + "os" + "path/filepath" + "sync" + + "github.com/dgraph-io/badger/v3" +) + +type NodeChatDBHandler struct { + ChatID string + PreviousId uint64 + ItemCount uint64 + db func(func(*badger.DB) (err error)) (err error) + lock *sync.RWMutex +} + +func NewNodeChatDBHandler(chatID string) (nodeChatDBHandler *NodeChatDBHandler, err error) { + nodeChatDBHandler = &NodeChatDBHandler{ + db: func(f func(*badger.DB) (err error)) (err error) { + db, err := badger.Open(badger.DefaultOptions(filepath.Join(dataPath, "data", "chats", chatID, "messages")).WithLogger(dbLogger)) + if err != nil { + return + } + defer db.Close() + err = f(db) + return + }, + ChatID: chatID, + lock: new(sync.RWMutex), + } + err = nodeChatDBHandler.db(func(d *badger.DB) (err error) { + err = d.View(func(txn *badger.Txn) error { + opt := badger.DefaultIteratorOptions + it := txn.NewIterator(opt) + defer it.Close() + var counter uint64 = 1 + var itemCounter uint64 = 0 + for it.Rewind(); it.Valid(); it.Next() { + item := it.Item() + if err = item.Value(func(val []byte) (err error) { + var chatMessage *ChatMessage + if err = json.Unmarshal(val, &chatMessage); err != nil { + return err + } + counter = chatMessage.ID + itemCounter++ + return + }); err != nil { + return err + } + } + nodeChatDBHandler.PreviousId = counter + nodeChatDBHandler.ItemCount = itemCounter + return nil + }) + return + }) + return +} + +func (ncdbh *NodeChatDBHandler) calculateNewChatCount(previousId uint64) (count uint, err error) { + err = ncdbh.db(func(d *badger.DB) (err error) { + err = d.View(func(txn *badger.Txn) error { + opt := badger.DefaultIteratorOptions + it := txn.NewIterator(opt) + defer it.Close() + count = 0 + b := make([]byte, bufferSize) + binary.BigEndian.PutUint64(b, previousId) + if previousId == 0 || previousId == 1 { + count++ + for it.Rewind(); it.Valid(); it.Next() { + count++ + } + } else { + for it.Seek(b); it.Valid(); it.Next() { + count++ + } + } + if count > 0 { + count-- + } + return nil + }) + return + }) + return +} + +func (ncdbh *NodeChatDBHandler) revertPreviousId() (err error) { + err = ncdbh.db(func(d *badger.DB) (err error) { + err = d.View(func(txn *badger.Txn) error { + opt := badger.DefaultIteratorOptions + opt.Reverse = true + it := txn.NewIterator(opt) + defer it.Close() + it.Rewind() + if it.Valid() { + item := it.Item() + if err = item.Value(func(val []byte) (err error) { + var chatMessage *ChatMessage + if err = json.Unmarshal(val, &chatMessage); err != nil { + return err + } + ncdbh.PreviousId = chatMessage.ID + return + }); err != nil { + return err + } + } else { + ncdbh.PreviousId = 1 + } + return nil + }) + return + }) + return +} + +func (ncdbh *NodeChatDBHandler) AddNewChatMessage(chatMessage *ChatMessage) (err error) { + b := make([]byte, bufferSize) + ncdbh.PreviousId++ + binary.BigEndian.PutUint64(b, ncdbh.PreviousId) + chatMessage.ID = ncdbh.PreviousId + bs, err := json.Marshal(chatMessage) + if err != nil { + return + } + if err = ncdbh.db(func(d *badger.DB) (err error) { + err = d.Update(func(txn *badger.Txn) (err error) { + if updateErr := txn.Set(b, bs); updateErr != nil { + return updateErr + } + return nil + }) + return + }); err != nil { + return + } + return +} + +func (ncdbh *NodeChatDBHandler) DeleteChatMessage(key uint64) (err error) { + if err = ncdbh.db(func(d *badger.DB) (err error) { + err = d.Update(func(txn *badger.Txn) (err error) { + b := make([]byte, bufferSize) + binary.BigEndian.PutUint64(b, key) + if err = txn.Delete(b); err != nil { + return + } + ncdbh.ItemCount-- + return + }) + return + }); err != nil { + return + } + return +} + +func (ncdbh *NodeChatDBHandler) DeleteChatFile(filename string, key uint64) (err error) { + if err = ncdbh.DeleteChatMessage(key); err != nil { + return + } + err = os.Remove(filepath.Join(dataPath, "data", "chats", ncdbh.ChatID, "__files__", filename)) + return +} + +func (ncdbh *NodeChatDBHandler) ListChatMessages(lastIndex int, limit int) (chatMessages []*ChatMessage, l int, done bool, err error) { + err = ncdbh.db(func(d *badger.DB) (err error) { + err = d.View(func(txn *badger.Txn) (err error) { + opt := badger.DefaultIteratorOptions + opt.Reverse = true + it := txn.NewIterator(opt) + defer it.Close() + b := make([]byte, bufferSize) + if lastIndex <= 0 { + binary.BigEndian.PutUint64(b, uint64(ncdbh.PreviousId)) + } else { + binary.BigEndian.PutUint64(b, uint64(lastIndex)) + } + x := 0 + defer func() { + if x < limit { + done = true + } + }() + chatMessages = make([]*ChatMessage, 0) + for it.Seek(b); it.Valid(); it.Next() { + if x >= limit { + break + } + item := it.Item() + if err = item.Value(func(val []byte) (err error) { + var chatMessage *ChatMessage + if err = json.Unmarshal(val, &chatMessage); err != nil { + return err + } + l = int(chatMessage.ID) + chatMessages = append(chatMessages, chatMessage) + return + }); err != nil { + return err + } + x++ + } + return + }) + return + }) + return +} + +func (ncdbh *NodeChatDBHandler) ListChatFiles(lastIndex int, limit int) (chatMessages []*ChatFile, l int, err error) { + err = ncdbh.db(func(d *badger.DB) (err error) { + err = d.View(func(txn *badger.Txn) (err error) { + opt := badger.DefaultIteratorOptions + opt.Reverse = true + it := txn.NewIterator(opt) + defer it.Close() + b := make([]byte, bufferSize) + var li int + if lastIndex <= 0 { + binary.BigEndian.PutUint64(b, uint64(ncdbh.PreviousId)) + li = int(ncdbh.PreviousId) + } else { + binary.BigEndian.PutUint64(b, uint64(lastIndex)) + li = lastIndex + } + x := 0 + y := 0 + defer func() { + if li > x { + l = li - x + } else if li == 0 { + if ncdbh.PreviousId > uint64(x) { + l = int(ncdbh.PreviousId) - x + } else { + l = 0 + } + } else { + l = 0 + } + }() + defer it.Close() + chatMessages = make([]*ChatFile, 0) + for it.Seek(b); it.Valid(); it.Next() { + if y >= limit || x >= int(ncdbh.PreviousId) { + break + } + item := it.Item() + if err = item.Value(func(val []byte) (err error) { + var chatMessage *ChatMessage + if err = json.Unmarshal(val, &chatMessage); err != nil { + return err + } + if chatMessage.File != nil { + chatMessages = append(chatMessages, chatMessage.File) + l = lastIndex - x + y++ + } + return + }); err != nil { + return err + } + x++ + } + return + }) + return + }) + return +} + +func (ncdbh *NodeChatDBHandler) GetChatMessage(index uint64) (chatMessage *ChatMessage, err error) { + err = ncdbh.db(func(d *badger.DB) (err error) { + err = d.View(func(txn *badger.Txn) (err error) { + b := make([]byte, bufferSize) + binary.BigEndian.PutUint64(b, uint64(index)) + item, err := txn.Get(b) + if err != nil { + return + } + err = item.Value(func(val []byte) error { + return json.Unmarshal(val, &chatMessage) + }) + return + }) + return + }) + return +} + +func (ncdbh *NodeChatDBHandler) ModifyChatMessage(key uint64, newContent string) (err error) { + b := make([]byte, bufferSize) + binary.BigEndian.PutUint64(b, key) + chatMessage, err := ncdbh.GetChatMessage(key) + if err != nil { + return + } + chatMessage.Content = newContent + bs, err := json.Marshal(chatMessage) + if err != nil { + return + } + if err = ncdbh.db(func(d *badger.DB) (err error) { + err = d.Update(func(txn *badger.Txn) (err error) { + if updateErr := txn.Set(b, bs); updateErr != nil { + return updateErr + } + return nil + }) + return + }); err != nil { + return + } + return +} diff --git a/chatMessagesFSInstance.go b/chatMessagesFSInstance.go new file mode 100644 index 0000000..ceeb3c6 --- /dev/null +++ b/chatMessagesFSInstance.go @@ -0,0 +1,164 @@ +package localserver + +import ( + "bufio" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/pion/webrtc/v3" +) + +type NodeChatFSInstance struct { + ChatID string `json:"id"` + OpenFiles map[string]*os.File `json:"-"` + OpenFilesForUser map[string][]string `json:"-"` + filesFlag *uint32 `json:"-"` +} + +func NewNodeChatFSInstance(chatID string) (chatFSInstance *NodeChatFSInstance) { + filesFlag := uint32(0) + chatFSInstance = &NodeChatFSInstance{ + ChatID: chatID, + OpenFiles: make(map[string]*os.File), + OpenFilesForUser: make(map[string][]string), + filesFlag: &filesFlag, + } + return +} + +func (fs *NodeChatFSInstance) SetupFileUpload(chatId, filename, userId string, dc *webrtc.DataChannel) (writePipe chan []byte, err error) { + concretePath := filepath.Join(dataPath, "data", "chats", chatId, "__files__", filename) + if _, err = os.ReadDir(filepath.Join(dataPath, "data", "chats", chatId)); err != nil { + return + } + if _, rErr := os.ReadDir(filepath.Join(dataPath, "data", "chats", chatId, "__files__")); os.IsNotExist(rErr) { + if err = os.MkdirAll(filepath.Join(dataPath, "data", "chats", chatId, "__files__"), 0700); err != nil { + return + } + } else if rErr != nil { + return nil, rErr + } + if err = os.Remove(concretePath); err != nil { + logger.Println(err) + } + file, err := os.OpenFile(concretePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0755) + if err != nil { + return + } + writePipe = make(chan []byte) + go func(f *os.File) { + defer func() { + _ = f.Close() + }() + for chunk := range writePipe { + if _, err = file.Write(chunk); err != nil { + return + } + } + }(file) + return +} + +func (fs *NodeChatFSInstance) FileUploadFailed(chatId, filename, userId string) (err error) { + concretePath := filepath.Join(dataPath, "data", "chats", chatId, "__files__", filename) + err = os.Remove(concretePath) + return +} + +func (fs *NodeChatFSInstance) SetupFileDownload(chatId, filename, userId string, dc *webrtc.DataChannel) (err error) { + concretePath := filepath.Join(dataPath, "data", "chats", chatId, "__files__", filename) + file, err := os.OpenFile(concretePath, os.O_RDONLY, 0755) + if err != nil { + return + } + if dc != nil { + dc.SetBufferedAmountLowThreshold(12000000) + bufferedAmountLock := make(chan struct{}) + go func(f *os.File) { + defer func() { + close(bufferedAmountLock) + bufferedAmountLock = nil + _ = f.Close() + }() + r := bufio.NewReader(file) + buf := make([]byte, 0, 50000) + sendingLoop: + for { + n, readErr := r.Read(buf[:cap(buf)]) + buf = buf[:n] + if n == 0 { + if err == nil { + logger.Println("n is 0 weird") + break sendingLoop + } + if err == io.EOF { + break sendingLoop + } + logger.Println(readErr) + return + } + if err = dc.Send(buf); err != nil { + dc.Close() + logger.Println(err) + break sendingLoop + } + if dc.BufferedAmount() > dc. + BufferedAmountLowThreshold() { + <-bufferedAmountLock + } + } + logger.Println("done") + if err = dc.SendText("download_done"); err != nil { + logger.Println(err) + } + }(file) + dc.OnBufferedAmountLow(func() { + if bufferedAmountLock != nil { + bufferedAmountLock <- struct{}{} + } + }) + } else { + err = fmt.Errorf("datachannel not created") + _ = atomicallyExecute(fs.filesFlag, func() (err error) { + if file, ok := fs.OpenFiles[fmt.Sprintf("%s/%s", filename, userId)]; ok { + file.Close() + } + delete(fs.OpenFiles, fmt.Sprintf("%s/%s", filename, userId)) + return + }) + } + return +} + +func (fs *NodeChatFSInstance) FileDownloadFailed(chatId, filename, userId string) (err error) { + err = atomicallyExecute(fs.filesFlag, func() (err error) { + if file, ok := fs.OpenFiles[fmt.Sprintf("%s/%s", filename, userId)]; ok { + file.Close() + } + delete(fs.OpenFiles, fmt.Sprintf("%s/%s", filename, userId)) + if _, ok := fs.OpenFilesForUser[userId]; ok { + var index int + for i, v := range fs.OpenFilesForUser[userId] { + if v == fmt.Sprintf("%s/%s", filename, userId) { + index = i + break + } + } + if len(fs.OpenFilesForUser[userId]) > 1 { + fs.OpenFilesForUser[userId] = append(fs.OpenFilesForUser[userId][:index], fs.OpenFilesForUser[userId][:index+1]...) + } else { + delete(fs.OpenFilesForUser, userId) + } + } + return + }) + return +} + +func (fs *NodeChatFSInstance) HandleDataChannelEvents(from, eventId string, payload map[string]interface{}) (err error) { + switch eventId { + } + return +} diff --git a/chatMessagesHandler.go b/chatMessagesHandler.go new file mode 100644 index 0000000..610e6e7 --- /dev/null +++ b/chatMessagesHandler.go @@ -0,0 +1,827 @@ +package localserver + +import ( + "context" + "encoding/json" + "fmt" + "io/fs" + "os" + "path/filepath" + "strings" + "time" + + "github.com/dgraph-io/badger/v3" + "github.com/pion/webrtc/v3" +) + +type NodeChatConfig struct { + ID string `json:"id"` + Name string `json:"name"` + Initiator string `json:"initiator"` + Target string `json:"target"` + InitiatorHost string `json:"initiatorHost"` + TargetHost string `json:"targetHost"` +} + +type NodeChatChannel struct { + ID string `json:"id"` + Name string `json:"name"` + Initiator string `json:"initiator"` + Target string `json:"target"` + InitiatorHost string `json:"initiatorHost"` + TargetHost string `json:"targetHost"` + LastReadIndex uint `json:"lastReadIndex"` + Unread uint `json:"unread"` + DB *NodeChatDBHandler `json:"-"` + Tracking *NodeChatTrackingDB `json:"-"` +} + +type NodeChatChannelsHandler[T ZippytalFSInstance] struct { + ChatID string + ChatFSInstance T + DataChannels map[string]*DataChannel + ChatDataChannels map[string]*DataChannel + ChatDataChannelsFlag *uint32 + Flag *uint32 + ChatFSInstanceFlag *uint32 + ChatFlag *uint32 + Chats map[string]*NodeChatChannel + reqChans []chan<- *ChatRequest + init bool +} + +func NewNodeChatChannelsHandler(chatID, chatName, initiator, target, initiatorHostId, targetHostId string, dataChannels map[string]*DataChannel, flag *uint32) (nodeChatsHandler *NodeChatChannelsHandler[*NodeChatFSInstance], err error) { + var dirs []fs.DirEntry + dirs, err = os.ReadDir(filepath.Join(dataPath, "data", "chats", chatID)) + if err != nil { + if os.IsNotExist(err) { + logger.Printf("creating chat directory for chat %s...\n", chatID) + mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "chats", chatID), 0700) + if mkdirErr != nil { + return nil, mkdirErr + } + file, ferr := os.Create(filepath.Join(dataPath, "data", "chats", chatID, "chatConfig.json")) + if ferr != nil { + return nil, ferr + } + baseConfig := NodeChatConfig{ + ID: chatID, + Name: chatName, + Initiator: initiator, + InitiatorHost: initiatorHostId, + Target: target, + TargetHost: targetHostId, + } + bs, jsonErr := json.Marshal(baseConfig) + if jsonErr != nil { + return nil, jsonErr + } + if _, writeErr := file.WriteString(string(bs)); writeErr != nil { + return nil, writeErr + } + _ = file.Close() + dirs, err = os.ReadDir(filepath.Join(dataPath, "data", "chats", chatID)) + if err != nil { + return nil, err + } + } else { + return + } + } + chats := make(map[string]*NodeChatChannel) + for _, chat := range dirs { + if strings.HasPrefix(chat.Name(), ".") { + continue + } + nodeChatDBHandler, err := NewNodeChatDBHandler(chat.Name()) + if err != nil { + return nil, err + } + var bs []byte + bs, err = os.ReadFile(filepath.Join(dataPath, "data", "chats", chat.Name(), "chatConfig.json")) + if err != nil { + return nil, err + } + logger.Println(string(bs)) + var c NodeChatChannel + if err = json.Unmarshal(bs, &c); err != nil { + return nil, err + } + nodeChatTracking, err := NewNodeChatTracking(chatID, initiator, target) + if err != nil { + return nil, err + } + logger.Println("chats data :", c.ID, c.Initiator, c.InitiatorHost, c.Target) + c.DB = nodeChatDBHandler + c.Tracking = nodeChatTracking + chats[c.ID] = &c + } + chatFlag := uint32(0) + chatFSFlag := uint32(0) + chatDCFlag := uint32(0) + nodeChatsHandler = &NodeChatChannelsHandler[*NodeChatFSInstance]{ + ChatID: chatID, + ChatFSInstance: NewNodeChatFSInstance(chatID), + ChatFSInstanceFlag: &chatFSFlag, + DataChannels: dataChannels, + ChatDataChannels: make(map[string]*DataChannel), + ChatDataChannelsFlag: &chatDCFlag, + Flag: flag, + Chats: chats, + ChatFlag: &chatFlag, + init: false, + } + return +} + +func (zch *NodeChatChannelsHandler[T]) sendZoneRequest(reqType string, from string, payload map[string]interface{}) { + go func() { + for _, rc := range zch.reqChans { + rc <- &ChatRequest{ + ReqType: reqType, + From: from, + Payload: payload, + } + } + }() +} + +func (zch *NodeChatChannelsHandler[T]) sendDataChannelMessage(reqType string, from string, to string, payload map[string]interface{}) (<-chan struct{}, <-chan error) { + done, errCh := make(chan struct{}), make(chan error) + go func() { + if err := atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) { + if _, ok := zch.ChatDataChannels[to]; ok { + bs, jsonErr := json.Marshal(&ZoneResponse{ + Type: reqType, + From: from, + To: to, + Payload: payload, + }) + if jsonErr != nil { + return jsonErr + } + err = zch.ChatDataChannels[to].DataChannel.SendText(string(bs)) + } else { + err = fmt.Errorf("no corresponding dataChannel") + } + return + }); err != nil { + errCh <- err + return + } + done <- struct{}{} + }() + return done, errCh +} + +func (zch *NodeChatChannelsHandler[T]) Init(ctx context.Context, initiator, target, initiatorNodeID, targetNodeID string) (err error) { + // for _, member := range authorizedMembers { + // if serr := zch.SetAllPublicChatForUser(member); serr != nil { + // logger.Println(serr) + // } + // } + zch.init = true + return +} + +func (zch *NodeChatChannelsHandler[T]) Subscribe(ctx context.Context, publisher <-chan *ChatRequest) (reqChan chan *ChatRequest, done chan struct{}, errCh chan error) { + reqChan, done, errCh = make(chan *ChatRequest), make(chan struct{}), make(chan error) + zch.reqChans = append(zch.reqChans, reqChan) + go func() { + for { + select { + case <-ctx.Done(): + done <- struct{}{} + return + case req := <-publisher: + if err := zch.handleChatRequest(ctx, req); err != nil { + errCh <- err + } + } + } + }() + return +} + +func (zch *NodeChatChannelsHandler[T]) GetChats(userId string) (err error) { + chats := make([]*NodeChatChannel, 0) + for _, chat := range zch.Chats { + if chat.Initiator == userId || chat.Target == userId { + chats = append(chats, chat) + } + } + for _, chat := range chats { + index, err := chat.Tracking.GetUserLastIndex(userId) + if err != nil { + return err + } + unread, err := chat.DB.calculateNewChatCount(uint64(index)) + if err != nil { + return err + } + chat.LastReadIndex = index + chat.Unread = unread + } + done, e := zch.sendDataChannelMessage(GET_CHATS_RESPONSE, "node", userId, map[string]interface{}{ + "chats": chats, + }) + select { + case <-done: + case err = <-e: + } + return +} + +func (zch *NodeChatChannelsHandler[T]) ListChats() (chats []*NodeChatChannel, err error) { + chats = make([]*NodeChatChannel, 0) + _ = atomicallyExecute(zch.ChatFlag, func() (err error) { + for _, chat := range zch.Chats { + chats = append(chats, chat) + } + return + }) + return +} + +func (zch *NodeChatChannelsHandler[T]) AddNewChat(chatID, initiator, target, initiatorHostId, targetHostId string) (err error) { + if chatID == "" { + err = fmt.Errorf("not a valid chat name provided") + return + } + if _, ok := zch.Chats[chatID]; ok { + err = fmt.Errorf("a chat with this name already exist") + return + } + mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "chats", chatID), 0700) + if mkdirErr != nil { + return mkdirErr + } + mkdirErr = os.Mkdir(filepath.Join(dataPath, "data", "chats", chatID, "__tracking__"), 0700) + if mkdirErr != nil { + return mkdirErr + } + file, ferr := os.Create(filepath.Join(dataPath, "data", "chats", chatID, "chatConfig.json")) + defer func() { + _ = file.Close() + }() + if ferr != nil { + return ferr + } + baseConfig := &NodeChatConfig{ + ID: chatID, + } + bs, jsonErr := json.Marshal(baseConfig) + if jsonErr != nil { + return jsonErr + } + if _, writeErr := file.WriteString(string(bs)); writeErr != nil { + return writeErr + } + err = file.Close() + if err != nil { + return err + } + nodeChatDBHandler, err := NewNodeChatDBHandler(chatID) + if err != nil { + return err + } + nodeChatTrackingDB, err := NewNodeChatTracking(chatID, initiator, target) + if err != nil { + return err + } + if err = nodeChatTrackingDB.Initialize(1, initiator, target); err != nil { + return err + } + var c NodeChatChannel + if err = json.Unmarshal(bs, &c); err != nil { + return err + } + c.DB = nodeChatDBHandler + c.Tracking = nodeChatTrackingDB + _ = atomicallyExecute(zch.ChatFlag, func() (err error) { + zch.Chats[c.ID] = &c + return + }) + return +} + +func (zch *NodeChatChannelsHandler[T]) DeleteChat(chatID string) (err error) { + if err = atomicallyExecute(zch.ChatFlag, func() (err error) { + defer delete(zch.Chats, chatID) + if _, ok := zch.Chats[chatID]; !ok { + err = fmt.Errorf("no corresponding chat") + return + } + return + }); err != nil { + return + } + if err = os.RemoveAll(filepath.Join(dataPath, "data", "chats", chatID)); err != nil { + return + } + return +} + +func (zch *NodeChatChannelsHandler[T]) ReadLastMessage(userId, chatID string) (err error) { + err = atomicallyExecute(zch.ChatFlag, func() (err error) { + if chat, ok := zch.Chats[chatID]; ok { + err = chat.Tracking.SetUserLastIndex(userId, chat.DB.PreviousId) + } + return + }) + return +} + +func (zch *NodeChatChannelsHandler[T]) ListLatestChatMessages(userId, chatID string, lastIndex, limit float64) (err error) { + var list []*ChatMessage + var i int + var done bool + if err = atomicallyExecute(zch.ChatFlag, func() (err error) { + if chat, ok := zch.Chats[chatID]; ok { + list, i, done, err = zch.Chats[chatID].DB.ListChatMessages(int(lastIndex), int(limit)) + if err != nil { + return + } + err = chat.Tracking.SetUserLastIndex(userId, chat.DB.PreviousId) + } + return + }); err != nil { + return + } + success, e := zch.sendDataChannelMessage(CHAT_MESSAGE_LIST, "node", userId, map[string]interface{}{ + "done": done || i <= 0, + "lastIndex": i - 1, + "chatID": chatID, + "chatMessages": list, + }) + select { + case <-success: + fmt.Println("done getting latest messages") + case err = <-e: + } + return +} + +func (zch *NodeChatChannelsHandler[T]) ListLatestChatFiles(userId, chatID string, lastIndex, limit float64) (err error) { + var list []*ChatFile + var i int + if err = atomicallyExecute(zch.ChatFlag, func() (err error) { + if _, ok := zch.Chats[chatID]; ok { + list, i, err = zch.Chats[chatID].DB.ListChatFiles(int(lastIndex), int(limit)) + } + return + }); err != nil { + return + } + done, e := zch.sendDataChannelMessage(CHAT_FILES_LIST, "node", userId, map[string]interface{}{ + "done": i <= 0, + "lastIndex": i, + "chatID": chatID, + "chatMessages": list, + }) + select { + case <-done: + case err = <-e: + } + return +} + +func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content string, isResponse bool, chatResponseId uint64, file *ChatFile) (err error) { + if _, ok := zch.Chats[chatID]; !ok { + err = fmt.Errorf("no such chat") + return + } + chat := zch.Chats[chatID] + chatMessage := &ChatMessage{ + Content: content, + From: userId, + IsResponse: isResponse, + ResponseOf: nil, + File: file, + Tags: make([]string, 0), + Date: time.Now().Format("Mon, 02 Jan 2006 15:04:05 MST"), + } + if err = atomicallyExecute(zch.ChatFlag, func() (err error) { + if chat, ok := zch.Chats[chatID]; ok { + if isResponse { + parentMessage, getErr := chat.DB.GetChatMessage(chatResponseId) + if err != nil { + if getErr != badger.ErrKeyNotFound { + return getErr + } + } + chatMessage.ResponseOf = parentMessage + } + if err = zch.Chats[chatID].DB.AddNewChatMessage(chatMessage); err != nil { + return + } + chatMessage.ID = zch.Chats[chatID].DB.PreviousId + + } else { + err = fmt.Errorf("no corresponding chats") + } + return + }); err != nil { + return + } + + notifyActivity := func(done <-chan struct{}, e <-chan error, target, initiator string) { + select { + case <-done: + case <-e: + _ = atomicallyExecute(zch.ChatFlag, func() (err error) { + if chat, ok := zch.Chats[chatID]; ok { + li, err := chat.Tracking.GetUserLastIndex(target) + if err != nil { + return err + } + count, err := chat.DB.calculateNewChatCount(uint64(li)) + if err != nil { + return err + } + + bs, err := json.Marshal(map[string]any{ + "chatID": chatID, + "chatHost": NodeID, + }) + if err != nil { + return err + } + zch.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{ + "type": "new_chat_message", + "title": "Unread messages 👀", + "body": fmt.Sprintf("%d new messages from %s", count, initiator), + "isPushed": true, + "payload": string(bs), + "recipients": []string{target}, + }) + + } + return + }) + } + } + d1, e1 := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", chat.Target, map[string]interface{}{ + "chatMessage": chatMessage, + "chatID": chatID, + }) + go notifyActivity(d1, e1, chat.Target, chat.Initiator) + d2, e2 := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", chat.Initiator, map[string]interface{}{ + "chatMessage": chatMessage, + "chatID": chatID, + }) + go notifyActivity(d2, e2, chat.Initiator, chat.Target) + return +} + +func (zch *NodeChatChannelsHandler[T]) RemoveAllUserChat(userId string) (err error) { + chats, err := zch.ListChats() + if err != nil { + return + } + for _, chat := range chats { + if chat.Initiator == userId || chat.Target == userId { + if derr := zch.DeleteChat(chat.ID); derr != nil { + logger.Println("**************", derr) + } + logger.Println("deleted chat", chat.ID) + } + } + return +} + +func (zch *NodeChatChannelsHandler[T]) DeleteChatMessage(key uint64, chatID string) (err error) { + err = atomicallyExecute(zch.ChatFlag, func() (err error) { + if _, ok := zch.Chats[chatID]; !ok { + err = fmt.Errorf("no file corresponding to id %s", chatID) + return + } + chat := zch.Chats[chatID] + if err = chat.DB.DeleteChatMessage(key); err != nil { + return + } + for _, member := range [2]string{chat.Initiator, chat.Target} { + d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{ + "chatID": chatID, + "messageId": key, + }) + select { + case <-d: + case tempErr := <-e: + logger.Println(tempErr) + } + } + + previousId := zch.Chats[chatID].DB.PreviousId + if previousId == key { + if err = zch.Chats[chatID].DB.revertPreviousId(); err != nil { + return + } + previousId = zch.Chats[chatID].DB.PreviousId + if previousId == 1 { + previousId = 0 + } + if err = zch.Chats[chatID].Tracking.RevertTrackingLastIndex(previousId); err != nil { + return + } + } + return + }) + return +} + +func (zch *NodeChatChannelsHandler[T]) UpdateChatMessage(key uint64, chatID, newContent string) (err error) { + err = atomicallyExecute(zch.ChatFlag, func() (err error) { + if _, ok := zch.Chats[chatID]; !ok { + err = fmt.Errorf("no chat corresponding to id %s", chatID) + return + } + chat := zch.Chats[chatID] + if err = chat.DB.ModifyChatMessage(key, newContent); err != nil { + return + } + for _, member := range [2]string{chat.Target, chat.Initiator} { + d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_EDITED, "node", member, map[string]interface{}{ + "chatID": chatID, + "messageId": key, + "newContent": newContent, + }) + select { + case <-d: + case tempErr := <-e: + logger.Println(tempErr) + } + } + + return + }) + return +} + +func (zch *NodeChatChannelsHandler[T]) DeleteChatFile(key uint64, fileName, chatID string) (err error) { + err = atomicallyExecute(zch.ChatFlag, func() (err error) { + if _, ok := zch.Chats[chatID]; !ok { + err = fmt.Errorf("no file corresponding to id %s", chatID) + return + } + chat := zch.Chats[chatID] + if err = chat.DB.DeleteChatFile(fileName, key); err != nil { + return + } + for _, member := range [2]string{chat.Target, chat.Initiator} { + d, e := zch.sendDataChannelMessage(CHAT_FILE_DELETED, "node", member, map[string]interface{}{ + "chatID": chatID, + "fileId": key, + "fileName": fileName, + }) + select { + case <-d: + case tempErr := <-e: + logger.Println(tempErr) + } + } + + return + }) + return +} + +func (zch *NodeChatChannelsHandler[T]) chatFileUpload(chatID string, filename string, userId string, dc *DataChannel) { + var writePipe chan<- []byte + var done bool + uploadDone := func() { + if writePipe != nil { + close(writePipe) + writePipe = nil + } + done = true + } + dc.DataChannel.OnError(func(err error) { + logger.Println(err) + logger.Println("abort...") + if !done { + uploadDone() + } + if fufErr := zch.ChatFSInstance.FileUploadFailed(chatID, filename, userId); fufErr != nil { + logger.Println(fufErr) + } + }) + dc.DataChannel.OnClose(func() { + if done { + logger.Println("closing gracefully...") + } else { + logger.Println("abort...") + uploadDone() + if fufErr := zch.ChatFSInstance.FileUploadFailed(chatID, filename, userId); fufErr != nil { + logger.Println(fufErr) + } + } + }) + dc.DataChannel.OnMessage(func(msg webrtc.DataChannelMessage) { + if msg.IsString { + if string(msg.Data) == "init_upload" { + logger.Println("init upload....") + var initErr error + if writePipe, initErr = zch.ChatFSInstance.SetupFileUpload(chatID, filename, userId, dc.DataChannel); initErr != nil { + _ = dc.DataChannel.SendText("abort") + _ = dc.DataChannel.Close() + return + } + logger.Println("upload ready !") + _ = dc.DataChannel.SendText("upload_ready") + } else if string(msg.Data) == "upload_done" { + uploadDone() + } + } else { + writePipe <- msg.Data + } + }) + dc.DataChannel.OnOpen(func() { + _ = dc.DataChannel.SendText("channel_ready") + }) +} + +func (zch *NodeChatChannelsHandler[T]) chatFileDownload(chatID string, filename string, userId string, dc *DataChannel) { + var done bool + dc.DataChannel.OnError(func(err error) { + if !done { + logger.Println("abort...") + if fdf := zch.ChatFSInstance.FileDownloadFailed(chatID, filename, userId); fdf != nil { + logger.Println(fdf) + } + } + }) + dc.DataChannel.OnClose(func() { + if !done { + logger.Println("abort...") + if fdf := zch.ChatFSInstance.FileDownloadFailed(chatID, filename, userId); fdf != nil { + logger.Println(fdf) + } + } + }) + dc.DataChannel.OnMessage(func(msg webrtc.DataChannelMessage) { + if msg.IsString { + if string(msg.Data) == "init_download" { + logger.Println("init download....") + var initErr error + if initErr = zch.ChatFSInstance.SetupFileDownload(chatID, filename, userId, dc.DataChannel); initErr != nil { + _ = dc.DataChannel.SendText("abort") + _ = dc.DataChannel.Close() + return + } + logger.Println("download started !") + } else if string(msg.Data) == "download_done" { + done = true + } + } + }) + dc.DataChannel.OnOpen(func() { + _ = dc.DataChannel.SendText("channel_ready") + }) +} + +func (zch *NodeChatChannelsHandler[T]) handleDataChannel(ctx context.Context, dc *DataChannel) (catched bool) { + var label string + _ = atomicallyExecute(dc.l, func() (err error) { + label = dc.DataChannel.Label() + return + }) + if strings.Contains(label, "chat_upload") { + command := strings.Split(label, "|") + if len(command) == 4 { + catched = true + go zch.chatFileUpload(command[1], command[2], command[3], dc) + } + logger.Println(command) + } else if strings.Contains(label, "chat_download") { + command := strings.Split(label, "|") + catched = true + go zch.chatFileDownload(command[1], command[2], command[3], dc) + logger.Println(command) + } else if strings.Contains(label, "chat_data") { + command := strings.Split(label, "|") + catched = true + _ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) { + zch.ChatDataChannels[command[1]] = dc + return + }) + dc.DataChannel.OnClose(func() { + fmt.Println("closing gratefully chat dc...") + _ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) { + delete(zch.ChatDataChannels, command[1]) + return + }) + }) + dc.DataChannel.OnError(func(err error) { + fmt.Println("error in chat dc...") + _ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) { + delete(zch.ChatDataChannels, command[1]) + return + }) + }) + } + return +} + +func (zch *NodeChatChannelsHandler[T]) handleChatRequest(ctx context.Context, req *ChatRequest) (err error) { + logger.Println("got request in zone chat handler", req) + switch req.ReqType { + case LEAVE_CHAT: + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { + return + } + // err = zch.LeaveChatFSInstance(req.Payload["userId"].(string)) + case DELETE_CHAT: + if err = VerifyFieldsString(req.Payload, "chatID"); err != nil { + return + } + err = zch.DeleteChat(req.Payload["chatID"].(string)) + case CREATE_CHAT: + if err = VerifyFieldsString(req.Payload, "chatID", "owner", "chatType"); err != nil { + return + } + if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil { + return + } + err = zch.AddNewChat(req.Payload["chatID"].(string), req.Payload["initiator"].(string), req.Payload["target"].(string), req.Payload["initiatorHostId"].(string), req.Payload["targetHostId"].(string)) + case GET_CHATS: + err = zch.GetChats(req.From) + case LIST_LATEST_CHATS: + if err = VerifyFieldsString(req.Payload, "chatID"); err != nil { + return + } + if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil { + return + } + err = zch.ListLatestChatMessages(req.From, req.Payload["chatID"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64)) + return + case LIST_LATEST_FILES: + if err = VerifyFieldsString(req.Payload, "chatID"); err != nil { + return + } + if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil { + return + } + err = zch.ListLatestChatFiles(req.From, req.Payload["chatID"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64)) + return + case READ_LATEST_MESSAGE: + if err = VerifyFieldsString(req.Payload, "chatID"); err != nil { + return + } + err = zch.ReadLastMessage(req.From, req.Payload["chatID"].(string)) + case ADD_CHAT_MESSAGE: + logger.Println("got request in zone chat handler", req) + if err = VerifyFieldsString(req.Payload, "chatID", "content"); err != nil { + return + } + if err = VerifyFieldsBool(req.Payload, "isResponse"); err != nil { + return + } + var parentChatId uint64 + if req.Payload["isResponse"].(bool) { + if err = VerifyFieldsFloat64(req.Payload, "parentChatId"); err != nil { + return + } + parentChatId = uint64(req.Payload["parentChatId"].(float64)) + } + var file *ChatFile = nil + if _, ok := req.Payload["file"]; ok { + bs, jsonErr := json.Marshal(req.Payload["file"]) + if jsonErr != nil { + return jsonErr + } + var f ChatFile + if err = json.Unmarshal(bs, &f); err != nil { + err = fmt.Errorf("the file payload dont match ChatFile struct pattern : %v", err) + return + } + file = &f + } + err = zch.AddChatMessage(req.From, req.Payload["chatID"].(string), req.Payload["content"].(string), req.Payload["isResponse"].(bool), parentChatId, file) + case DELETE_CHAT_MESSAGE: + if err = VerifyFieldsString(req.Payload, "chatID"); err != nil { + return + } + if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil { + return + } + err = zch.DeleteChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatID"].(string)) + case EDIT_CHAT_MESSAGE: + if err = VerifyFieldsString(req.Payload, "chatID", "newContent"); err != nil { + return + } + if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil { + return + } + err = zch.UpdateChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatID"].(string), req.Payload["newContent"].(string)) + case DELETE_CHAT_FILE: + if err = VerifyFieldsString(req.Payload, "chatID", "fileName"); err != nil { + return + } + if err = VerifyFieldsFloat64(req.Payload, "fileId"); err != nil { + return + } + err = zch.DeleteChatFile(uint64(req.Payload["fileId"].(float64)), req.Payload["fileName"].(string), req.Payload["chatID"].(string)) + } + return +} diff --git a/chatMessagesTracking.go b/chatMessagesTracking.go new file mode 100644 index 0000000..a3cbed2 --- /dev/null +++ b/chatMessagesTracking.go @@ -0,0 +1,151 @@ +package localserver + +import ( + "encoding/binary" + "os" + "path/filepath" + "sync" + + "github.com/dgraph-io/badger/v3" +) + +type NodeChatTrackingDB struct { + ChatID string + db func(cb func(*badger.DB) (err error)) (err error) + lock *sync.RWMutex +} + +func NewNodeChatTracking(chatId, initiator, target string) (*NodeChatTrackingDB, error) { + if _, dirErr := os.ReadDir(filepath.Join(dataPath, "data", "chats", chatId, "__tracking__")); os.IsNotExist(dirErr) { + _ = os.MkdirAll(filepath.Join(dataPath, "data", "chats", chatId, "__tracking__"), 0700) + } + db := func(f func(*badger.DB) (err error)) (err error) { + db, err := badger.Open(badger.DefaultOptions(filepath.Join(dataPath, "data", "chats", chatId, "__tracking__")).WithLogger(dbLogger)) + if err != nil { + return + } + defer db.Close() + err = f(db) + return + } + lock := new(sync.RWMutex) + if err := db(func(d *badger.DB) (err error) { + err = d.Update(func(txn *badger.Txn) error { + b := make([]byte, bufferSize) + binary.BigEndian.PutUint64(b, 0) + for _, member := range [2]string{initiator, target} { + if _, rerr := txn.Get([]byte(member)); rerr == badger.ErrKeyNotFound { + _ = txn.Set([]byte(member), b) + } else if rerr != nil { + return rerr + } + } + return nil + }) + if err != nil { + return + } + return + }); err != nil { + return nil, err + } + return &NodeChatTrackingDB{chatId, db, lock}, nil +} + +func (nctdb *NodeChatTrackingDB) Initialize(lastIndex uint64, users ...string) (err error) { + for _, user := range users { + if err = nctdb.SetUserLastIndex(user, lastIndex); err != nil { + return + } + } + return +} + +func (nctdb *NodeChatTrackingDB) RevertTrackingLastIndex(lastIndex uint64) (err error) { + nctdb.lock.Lock() + defer nctdb.lock.Unlock() + err = nctdb.db(func(d *badger.DB) (err error) { + keys := [][]byte{} + err = d.View(func(txn *badger.Txn) error { + opt := badger.DefaultIteratorOptions + it := txn.NewIterator(opt) + defer it.Close() + for it.Rewind(); it.Valid(); it.Next() { + item := it.Item() + if err = item.Value(func(val []byte) error { + li := binary.BigEndian.Uint64(val) + if li >= lastIndex { + keys = append(keys, item.Key()) + } + return nil + }); err != nil { + return err + } + } + return nil + }) + if err != nil { + return + } + err = d.Update(func(txn *badger.Txn) error { + for _, key := range keys { + b := make([]byte, bufferSize) + binary.BigEndian.PutUint64(b, lastIndex) + if updateErr := txn.Set(key, b); updateErr != nil { + return updateErr + } + } + return nil + }) + return + }) + return +} + +func (nctdb *NodeChatTrackingDB) SetUserLastIndex(userId string, lastIndex uint64) (err error) { + nctdb.lock.Lock() + defer nctdb.lock.Unlock() + err = nctdb.db(func(d *badger.DB) (err error) { + err = d.Update(func(txn *badger.Txn) error { + b := make([]byte, bufferSize) + binary.BigEndian.PutUint64(b, lastIndex) + updateErr := txn.Set([]byte(userId), b) + return updateErr + }) + return + }) + return +} + +func (nctdb *NodeChatTrackingDB) GetUserLastIndex(userId string) (index uint, err error) { + nctdb.lock.Lock() + defer nctdb.lock.Unlock() + err = nctdb.db(func(d *badger.DB) (err error) { + err = d.Update(func(txn *badger.Txn) error { + item, rerr := txn.Get([]byte(userId)) + if rerr != nil { + return rerr + } + _ = item.Value(func(val []byte) error { + index = uint(binary.BigEndian.Uint64(val)) + return nil + }) + return nil + }) + return + }) + return +} + +func (nctdb *NodeChatTrackingDB) DeleteUserTracking(userId string) (err error) { + nctdb.lock.Lock() + defer nctdb.lock.Unlock() + err = nctdb.db(func(d *badger.DB) (err error) { + err = d.Update(func(txn *badger.Txn) error { + updateErr := txn.Delete([]byte(userId)) + return updateErr + }) + return + }) + return +} diff --git a/chatRequestScheduler.go b/chatRequestScheduler.go new file mode 100644 index 0000000..f974498 --- /dev/null +++ b/chatRequestScheduler.go @@ -0,0 +1,120 @@ +package localserver + +import ( + "context" + "sync" + "time" +) + +type ChatRequestScheduler struct { + handlersPublishers []chan<- *ChatRequest + handlersDataChannelDispatchCallbacks []DispatchDataChannelCallBack +} + +type ChatRequest struct { + ReqType string `json:"reqType"` + Payload map[string]any + From string +} + +type ChatResponse struct { + Type string `json:"type"` + To string `json:"to"` + From string `json:"from"` + Payload map[string]any `json:"payload"` +} + +type ChatRequestHandler interface { + Init(ctx context.Context, initiator, target, initiatorNodeID, targetNodeID string) (err error) + Subscribe(ctx context.Context, publisher <-chan *ChatRequest) (reqChan chan *ChatRequest, done chan struct{}, errCh chan error) + handleChatRequest(ctx context.Context, req *ChatRequest) (err error) + handleDataChannel(ctx context.Context, dc *DataChannel) (catched bool) +} + +func NewChatRequestScheduler(initiator, target, initiatorNodeID, targetNodeID string, handlers ...ChatRequestHandler) (chatRequestScheduler *ChatRequestScheduler, handlerErrCh chan error) { + chatRequestScheduler = new(ChatRequestScheduler) + chatRequestScheduler.handlersPublishers = make([]chan<- *ChatRequest, 0) + chatRequestScheduler.handlersDataChannelDispatchCallbacks = make([]DispatchDataChannelCallBack, 0) + handlerErrCh = make(chan error) + reqChans := []chan *ChatRequest{} + for _, handler := range handlers { + publisher := make(chan *ChatRequest, 100) + chatRequestScheduler.handlersPublishers = append(chatRequestScheduler.handlersPublishers, publisher) + chatRequestScheduler.handlersDataChannelDispatchCallbacks = append(chatRequestScheduler.handlersDataChannelDispatchCallbacks, handler.handleDataChannel) + reqChan, done, errCh := handler.Subscribe(context.Background(), publisher) + go func(done <-chan struct{}, errCh <-chan error) { + for { + select { + case <-done: + return + case handlerErrCh <- <-errCh: + } + } + }(done, errCh) + reqChans = append(reqChans, reqChan) + } + for _, reqChan := range reqChans { + go func(reqChan <-chan *ChatRequest) { + done, errCh := chatRequestScheduler.Schedule(reqChan) + for { + select { + case <-done: + return + case e := <-errCh: + logger.Println(e) + } + } + }(reqChan) + } + for _, handler := range handlers { + if ierr := handler.Init(context.Background(), initiator, target, initiatorNodeID, targetNodeID); ierr != nil { + logger.Println(ierr) + } + } + return +} + +func (crs *ChatRequestScheduler) Schedule(reqChan <-chan *ChatRequest) (done chan struct{}, errCh chan error) { + done, errCh = make(chan struct{}), make(chan error) + for req := range reqChan { + go func(r *ChatRequest) { + for _, publisher := range crs.handlersPublishers { + go func(p chan<- *ChatRequest) { + p <- r + }(publisher) + } + }(req) + } + + return +} + +func (crs *ChatRequestScheduler) DispatchDataChannel(ctx context.Context, dc *DataChannel) (catched bool) { + wg, lock := sync.WaitGroup{}, &sync.Mutex{} + timeoutCtx, cancel := context.WithTimeout(ctx, 15*time.Second) + defer cancel() + done := make(chan struct{}) + for _, dispatchCallback := range crs.handlersDataChannelDispatchCallbacks { + wg.Add(1) + go func(w *sync.WaitGroup, dispatchCallback DispatchDataChannelCallBack) { + defer w.Done() + val := dispatchCallback(timeoutCtx, dc) + if val { + lock.Lock() + catched = true + lock.Unlock() + return + } + }(&wg, dispatchCallback) + } + go func() { + wg.Wait() + done <- struct{}{} + }() + select { + case <-done: + return + case <-timeoutCtx.Done(): + return false + } +} diff --git a/cmd/data/zones/092f1f32-3152-45f6-a01e-b23c93f252b6/chats/general 💬/chatConfig.json b/cmd/data/zones/092f1f32-3152-45f6-a01e-b23c93f252b6/chats/general 💬/chatConfig.json index 23d7055..df4ec01 100644 --- a/cmd/data/zones/092f1f32-3152-45f6-a01e-b23c93f252b6/chats/general 💬/chatConfig.json +++ b/cmd/data/zones/092f1f32-3152-45f6-a01e-b23c93f252b6/chats/general 💬/chatConfig.json @@ -1 +1,11 @@ -{"chatId":"general 💬","chatType":"public","owner":"Dev","members":["Dev","Loïs","Pascal","Dev2"]} \ No newline at end of file +{ + "chatId": "general 💬", + "chatType": "public", + "owner": "Dev", + "members": [ + "Dev", + "Loïs", + "Pascal", + "Dev2" + ] +} \ No newline at end of file diff --git a/cmd/data/zones/092f1f32-3152-45f6-a01e-b23c93f252b6/chats/test/chatConfig.json b/cmd/data/zones/092f1f32-3152-45f6-a01e-b23c93f252b6/chats/test/chatConfig.json index b176399..0e32d3d 100644 --- a/cmd/data/zones/092f1f32-3152-45f6-a01e-b23c93f252b6/chats/test/chatConfig.json +++ b/cmd/data/zones/092f1f32-3152-45f6-a01e-b23c93f252b6/chats/test/chatConfig.json @@ -1 +1,8 @@ -{"chatId":"test","chatType":"private","owner":"Dev2","members":["Dev2"]} \ No newline at end of file +{ + "chatId": "test", + "chatType": "private", + "owner": "Dev2", + "members": [ + "Dev2" + ] +} \ No newline at end of file diff --git a/cmd/go.mod b/cmd/go.mod new file mode 100644 index 0000000..4409f68 --- /dev/null +++ b/cmd/go.mod @@ -0,0 +1,50 @@ +module github.com/loisBN/zippytal_node/Zippytal-Node/cmd + +replace github.com/loisBN/zippytal_node/localserver => ../ + +go 1.18 + +require github.com/loisBN/zippytal_node/localserver v0.0.0-00010101000000-000000000000 + +require ( + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/dgraph-io/badger/v3 v3.2103.2 // indirect + github.com/dgraph-io/ristretto v0.1.0 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect + github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.3 // indirect + github.com/google/flatbuffers v1.12.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/klauspost/compress v1.12.3 // indirect + github.com/pion/datachannel v1.5.2 // indirect + github.com/pion/dtls/v2 v2.0.10 // indirect + github.com/pion/ice/v2 v2.1.14 // indirect + github.com/pion/interceptor v0.1.2 // indirect + github.com/pion/logging v0.2.2 // indirect + github.com/pion/mdns v0.0.5 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtcp v1.2.9 // indirect + github.com/pion/rtp v1.7.4 // indirect + github.com/pion/sctp v1.8.0 // indirect + github.com/pion/sdp/v3 v3.0.4 // indirect + github.com/pion/srtp/v2 v2.0.5 // indirect + github.com/pion/stun v0.3.5 // indirect + github.com/pion/transport v0.12.3 // indirect + github.com/pion/turn/v2 v2.0.5 // indirect + github.com/pion/udp v0.1.1 // indirect + github.com/pion/webrtc/v3 v3.1.11 // indirect + github.com/pkg/errors v0.9.1 // indirect + go.opencensus.io v0.22.5 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/net v0.2.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect + google.golang.org/grpc v1.42.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect +) diff --git a/cmd/go.sum b/cmd/go.sum new file mode 100644 index 0000000..7f12efe --- /dev/null +++ b/cmd/go.sum @@ -0,0 +1,303 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8= +github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M= +github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= +github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E= +github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ= +github.com/pion/dtls/v2 v2.0.9/go.mod h1:O0Wr7si/Zj5/EBFlDzDd6UtVxx25CE1r7XM7BQKYQho= +github.com/pion/dtls/v2 v2.0.10 h1:wgys7gPR1NMbWjmjJ3CW7lkUGaun8djgH8nahpNLnxI= +github.com/pion/dtls/v2 v2.0.10/go.mod h1:00OxfeCRWHShcqT9jx8pKKmBWuTt0NCZoVPCaC4VKvU= +github.com/pion/ice/v2 v2.1.14 h1:nD9GZs3MiR1/dPa5EiMRMe8hLBG3/qqCdx/hTS2g8VE= +github.com/pion/ice/v2 v2.1.14/go.mod h1:ovgYHUmwYLlRvcCLI67PnQ5YGe+upXZbGgllBDG/ktU= +github.com/pion/interceptor v0.1.2 h1:1IfrJ+AQ0HhwxNl4hqh9hMvl1hBKiNhAAr7DrUHsC6s= +github.com/pion/interceptor v0.1.2/go.mod h1:Lh3JSl/cbJ2wP8I3ccrjh1K/deRGRn3UlSPuOTiHb6U= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw= +github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0= +github.com/pion/rtcp v1.2.9 h1:1ujStwg++IOLIEoOiIQ2s+qBuJ1VN81KW+9pMPsif+U= +github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= +github.com/pion/rtp v1.7.0/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.7.4 h1:4dMbjb1SuynU5OpA3kz1zHK+u+eOCQjW3MAeVHf1ODA= +github.com/pion/rtp v1.7.4/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/sctp v1.8.0 h1:6erMF2qmQwXr+0iB1lm0AUSmDr9LdmpaBzgSVAEgehw= +github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= +github.com/pion/sdp/v3 v3.0.4 h1:2Kf+dgrzJflNCSw3TV5v2VLeI0s/qkzy2r5jlR0wzf8= +github.com/pion/sdp/v3 v3.0.4/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk= +github.com/pion/srtp/v2 v2.0.5 h1:ks3wcTvIUE/GHndO3FAvROQ9opy0uLELpwHJaQ1yqhQ= +github.com/pion/srtp/v2 v2.0.5/go.mod h1:8k6AJlal740mrZ6WYxc4Dg6qDqqhxoRG2GSjlUhDF0A= +github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg= +github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA= +github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A= +github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= +github.com/pion/transport v0.12.3 h1:vdBfvfU/0Wq8kd2yhUMSDB/x+O4Z9MYVl2fJ5BT4JZw= +github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= +github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA= +github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw= +github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= +github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= +github.com/pion/webrtc/v3 v3.1.11 h1:8Q5BEsxvlDn3botM8U8n/Haln745FBa5TWgm8v2c2FA= +github.com/pion/webrtc/v3 v3.1.11/go.mod h1:h9pbP+CADYb/99s5rfjflEcBLgdVKm55Rm7heQ/gIvY= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/cmd/node.linux b/cmd/node.linux index fbad2c2..51d07e7 100755 Binary files a/cmd/node.linux and b/cmd/node.linux differ diff --git a/go.mod b/go.mod index 5af58e6..504e7db 100644 --- a/go.mod +++ b/go.mod @@ -16,13 +16,17 @@ require ( github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect + github.com/fogleman/gg v1.3.0 // indirect + github.com/goccy/go-graphviz v0.0.9 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.3 // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/klauspost/compress v1.12.3 // indirect + github.com/ofabry/go-callvis v0.6.1 // indirect github.com/pion/datachannel v1.5.2 // indirect github.com/pion/dtls/v2 v2.0.10 // indirect github.com/pion/ice/v2 v2.1.14 // indirect @@ -38,12 +42,16 @@ require ( github.com/pion/transport v0.12.3 // indirect github.com/pion/turn/v2 v2.0.5 // indirect github.com/pion/udp v0.1.1 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect go.opencensus.io v0.22.5 // indirect golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/net v0.0.0-20211020060615-d418f374d309 // indirect - golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect - golang.org/x/text v0.3.6 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/image v0.1.0 // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/net v0.2.0 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect + golang.org/x/tools v0.3.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect ) diff --git a/go.sum b/go.sum index 4ee81a3..22069bf 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,7 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/corona10/goimagehash v1.0.2/go.mod h1:/l9umBhvcHQXVtQO1V6Gp1yD20STawkhRnnX0D1bvVI= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -38,12 +39,19 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/goccy/go-graphviz v0.0.6/go.mod h1:wXVsXxmyMQU6TN3zGRttjNn3h+iCAS7xQFC6TlNvLhk= +github.com/goccy/go-graphviz v0.0.9 h1:s/FMMJ1Joj6La3S5ApO3Jk2cwM4LpXECC2muFx3IPQQ= +github.com/goccy/go-graphviz v0.0.9/go.mod h1:wXVsXxmyMQU6TN3zGRttjNn3h+iCAS7xQFC6TlNvLhk= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= @@ -83,6 +91,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= @@ -93,8 +102,11 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/ofabry/go-callvis v0.6.1 h1:JSVl4QdS5+997C38Bu0lMPXOs5NLOoKaY32sgKcgdUI= +github.com/ofabry/go-callvis v0.6.1/go.mod h1:0rRJ5J4UM3gOuKEWcoSng8RJmDUGJCJRAd+nY+zT2KE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.1/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= @@ -141,6 +153,9 @@ github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= github.com/pion/webrtc/v3 v3.1.11 h1:8Q5BEsxvlDn3botM8U8n/Haln745FBa5TWgm8v2c2FA= github.com/pion/webrtc/v3 v3.1.11/go.mod h1:h9pbP+CADYb/99s5rfjflEcBLgdVKm55Rm7heQ/gIvY= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -169,22 +184,30 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk= +golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -207,6 +230,9 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211020060615-d418f374d309 h1:A0lJIi+hcTR6aajJH4YqKWwohY4aW9RO7oRMcdv+HKI= golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -216,6 +242,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -233,27 +260,42 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200305224536-de023d59a5d1/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/utitlity.go b/utitlity.go index 0d1f350..7b86a21 100644 --- a/utitlity.go +++ b/utitlity.go @@ -1,6 +1,19 @@ package localserver -import "fmt" +import ( + "fmt" + "sync/atomic" +) + +func validateRequest(req map[string]string, entries ...string) (err error) { + for _, entry := range entries { + if _, ok := req[entry]; !ok { + err = fmt.Errorf("no field %s in req payload", entry) + return + } + } + return +} func ToStringSlice(slice []any) (strSlice []string, err error) { strSlice = []string{} @@ -14,3 +27,66 @@ func ToStringSlice(slice []any) (strSlice []string, err error) { } return } + +func atomicallyExecute(flag *uint32, job func() (err error)) (err error) { + for { + if atomic.CompareAndSwapUint32(flag, 0, 1) { + defer atomic.SwapUint32(flag, 0) + err = job() + break + } + } + return +} + +func VerifyFieldsString(payload map[string]any, fields ...string) (err error) { + for _, field := range fields { + if _, ok := payload[field]; !ok { + err = fmt.Errorf("no field %s in payload", field) + return + } else if _, ok := payload[field].(string); !ok { + err = fmt.Errorf("field %s in payload is not a string", field) + return + } + } + return +} + +func VerifyFieldsBool(payload map[string]any, fields ...string) (err error) { + for _, field := range fields { + if _, ok := payload[field]; !ok { + err = fmt.Errorf("no field %s in payload", field) + return + } else if _, ok := payload[field].(bool); !ok { + err = fmt.Errorf("field %s in payload is not a bool", field) + return + } + } + return +} + +func VerifyFieldsFloat64(payload map[string]any, fields ...string) (err error) { + for _, field := range fields { + if _, ok := payload[field]; !ok { + err = fmt.Errorf("no field %s in payload", field) + return + } else if _, ok := payload[field].(float64); !ok { + err = fmt.Errorf("field %s in payload is not a float64", field) + return + } + } + return +} + +func VerifyFieldsSliceInterface(payload map[string]any, fields ...string) (err error) { + for _, field := range fields { + if _, ok := payload[field]; !ok { + err = fmt.Errorf("no field %s in payload", field) + return + } else if _, ok := payload[field].([]any); !ok { + err = fmt.Errorf("field %s in payload is not a []any", field) + return + } + } + return +} diff --git a/webrtcGrpcMiddleware.go b/webrtcGrpcMiddleware.go index 8f089d1..3d6c397 100644 --- a/webrtcGrpcMiddleware.go +++ b/webrtcGrpcMiddleware.go @@ -3,7 +3,6 @@ package localserver import ( "context" "encoding/json" - "fmt" "strconv" "github.com/pion/webrtc/v3" @@ -49,16 +48,6 @@ func NewWebRTCGrpcMiddleware(manager *WebRTCCallManager) (webrtcGrpcMiddleware * return } -func validateRequest(req map[string]string, entries ...string) (err error) { - for _, entry := range entries { - if _, ok := req[entry]; !ok { - err = fmt.Errorf("no field %s in req payload", entry) - return - } - } - return -} - func (wgm *WebRTCGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) { bs, err := json.Marshal(map[string]string{ "from": wgm.Manager.ID, diff --git a/zoneAudioChannelshandler.go b/zoneAudioChannelshandler.go index 112d3ed..e953767 100644 --- a/zoneAudioChannelshandler.go +++ b/zoneAudioChannelshandler.go @@ -884,7 +884,7 @@ func (zach *ZoneAudioChannelsHandler) handleZoneRequest(ctx context.Context, req switch req.ReqType { case LEAVE_ZONE: logger.Println("*-----------------handling leaving zone---------------*") - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } for _, ac := range zach.AudioChannels { @@ -904,7 +904,7 @@ func (zach *ZoneAudioChannelsHandler) handleZoneRequest(ctx context.Context, req } } case string(REMOVED_ZONE_AUTHORIZED_MEMBER): - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } var index int @@ -926,7 +926,7 @@ func (zach *ZoneAudioChannelsHandler) handleZoneRequest(ctx context.Context, req } err = zach.RemoveUserFromAllAudioChannels(req.Payload["userId"].(string)) case string(NEW_AUTHORIZED_ZONE_MEMBER): - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } var contain bool @@ -941,25 +941,25 @@ func (zach *ZoneAudioChannelsHandler) handleZoneRequest(ctx context.Context, req } err = zach.SetAllPublicAudioChannelForUser(req.Payload["userId"].(string)) case JOIN_AUDIO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { return } err = zach.JoinAudioChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string), req.Payload["sdp"].(string)) case LEAVE_AUDIO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zach.LeaveAudioChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) case ADD_AUDIO_CHANNEL_MEMBERS: - if err = verifyFieldsString(req.Payload, "channelId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId"); err != nil { return } - if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } err = zach.AddAudioChannelsMembers(req.Payload["channelId"].(string), req.Payload["members"].([]interface{})) case REMOVE_AUDIO_CHANNEL_MEMBER: - if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zach.RemoveAudioChannelMember(req.Payload["channelId"].(string), req.Payload["userId"].(string)) @@ -968,40 +968,40 @@ func (zach *ZoneAudioChannelsHandler) handleZoneRequest(ctx context.Context, req return } case EDIT_AUDIO_CHANNEL_NAME: - if err = verifyFieldsString(req.Payload, "channelId", "newAudioChannelId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "newAudioChannelId"); err != nil { return } if err = zach.EditAudioChannelName(req.Payload["channelId"].(string), req.Payload["newAudioChannelId"].(string)); err != nil { return } case EDIT_AUDIO_CHANNEL_TYPE: - if err = verifyFieldsString(req.Payload, "channelId", "channelType"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "channelType"); err != nil { return } if err = zach.EditAudioChannelType(req.Payload["channelId"].(string), req.Payload["channelType"].(string)); err != nil { return } case DELETE_AUDIO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId"); err != nil { return } err = zach.DeleteAudioChannel(req.Payload["channelId"].(string)) case CREATE_AUDIO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId", "owner", "channelType"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "owner", "channelType"); err != nil { return } - if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } err = zach.AddNewAudioChannel(req.Payload["channelId"].(string), req.Payload["owner"].(string), req.Payload["channelType"].(string), req.Payload["members"].([]interface{})) case GET_AUDIO_CHANNELS: - if err = verifyFieldsSliceInterface(req.Payload, "channelsId"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "channelsId"); err != nil { return } err = zach.GetAudioChannels(req.From, req.Payload["channelsId"].([]interface{})...) case string(AUDIO_CHANNEL_WEBRTC_COUNTER_OFFER): logger.Println("handling audio channel counter offer") - if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { @@ -1013,7 +1013,7 @@ func (zach *ZoneAudioChannelsHandler) handleZoneRequest(ctx context.Context, req return }) case string(AUDIO_CHANNEL_WEBRTC_RENNEGOTIATION_OFFER): - if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { return } err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { @@ -1025,7 +1025,7 @@ func (zach *ZoneAudioChannelsHandler) handleZoneRequest(ctx context.Context, req return }) case string(AUDIO_CHANNEL_WEBRTC_RENNEGOTIATION_ANSWER): - if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { return } err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { @@ -1039,7 +1039,7 @@ func (zach *ZoneAudioChannelsHandler) handleZoneRequest(ctx context.Context, req case string(AUDIO_CHANNEL_WEBRTC_CANDIDATE): logger.Println("handling audio channel webrtc candidate") logger.Println(req.Payload) - if err = verifyFieldsString(req.Payload, FROM, "candidate", "sdpMLineIndex", "sdpMid", "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, FROM, "candidate", "sdpMLineIndex", "sdpMid", "channelId", "userId"); err != nil { return } logger.Println(req.Payload) diff --git a/zoneChatsHandler.go b/zoneChatsHandler.go index 9f7b8b8..ac8999e 100644 --- a/zoneChatsHandler.go +++ b/zoneChatsHandler.go @@ -1364,12 +1364,12 @@ func (zch *ZoneChatsHandler[T]) handleZoneRequest(ctx context.Context, req *Zone logger.Println("got request in zone chat handler", req) switch req.ReqType { case LEAVE_ZONE: - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } // err = zch.LeaveChatFSInstance(req.Payload["userId"].(string)) case string(REMOVED_ZONE_AUTHORIZED_MEMBER): - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } var index int @@ -1391,7 +1391,7 @@ func (zch *ZoneChatsHandler[T]) handleZoneRequest(ctx context.Context, req *Zone } err = zch.RemoveUserFromAllChats(req.Payload["userId"].(string)) case string(NEW_AUTHORIZED_ZONE_MEMBER): - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } var contain bool @@ -1406,15 +1406,15 @@ func (zch *ZoneChatsHandler[T]) handleZoneRequest(ctx context.Context, req *Zone } err = zch.SetAllPublicChatForUser(req.Payload["userId"].(string)) case ADD_CHAT_MEMBERS: - if err = verifyFieldsString(req.Payload, "chatId"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId"); err != nil { return } - if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } err = zch.AddChatMembers(req.Payload["chatId"].(string), req.Payload["members"].([]interface{})) case REMOVE_CHAT_MEMBER: - if err = verifyFieldsString(req.Payload, "chatId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId", "userId"); err != nil { return } err = zch.RemoveChatMember(req.Payload["chatId"].(string), req.Payload["userId"].(string)) @@ -1423,71 +1423,71 @@ func (zch *ZoneChatsHandler[T]) handleZoneRequest(ctx context.Context, req *Zone return } case EDIT_CHAT_NAME: - if err = verifyFieldsString(req.Payload, "chatId", "newChatId"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId", "newChatId"); err != nil { return } if err = zch.EditChatName(req.Payload["chatId"].(string), req.Payload["newChatId"].(string)); err != nil { return } case EDIT_CHAT_TYPE: - if err = verifyFieldsString(req.Payload, "chatId", "chatType"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId", "chatType"); err != nil { return } if err = zch.EditChatType(req.Payload["chatId"].(string), req.Payload["chatType"].(string)); err != nil { return } case DELETE_CHAT: - if err = verifyFieldsString(req.Payload, "chatId"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId"); err != nil { return } err = zch.DeleteChat(req.Payload["chatId"].(string)) case CREATE_CHAT: - if err = verifyFieldsString(req.Payload, "chatId", "owner", "chatType"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId", "owner", "chatType"); err != nil { return } - if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } err = zch.AddNewChat(req.Payload["chatId"].(string), req.Payload["owner"].(string), req.Payload["chatType"].(string), req.Payload["members"].([]interface{})) case GET_CHATS: - if err = verifyFieldsSliceInterface(req.Payload, "chatsId"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "chatsId"); err != nil { return } err = zch.GetChats(req.From, req.Payload["chatsId"].([]interface{})...) case LIST_LATEST_CHATS: - if err = verifyFieldsString(req.Payload, "chatId"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId"); err != nil { return } - if err = verifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil { + if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil { return } err = zch.ListLatestChatMessages(req.From, req.Payload["chatId"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64)) return case LIST_LATEST_FILES: - if err = verifyFieldsString(req.Payload, "chatId"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId"); err != nil { return } - if err = verifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil { + if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil { return } err = zch.ListLatestChatFiles(req.From, req.Payload["chatId"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64)) return case READ_LATEST_MESSAGE: - if err = verifyFieldsString(req.Payload, "chatId"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId"); err != nil { return } err = zch.ReadLastMessage(req.From, req.Payload["chatId"].(string)) case ADD_CHAT_MESSAGE: logger.Println("got request in zone chat handler", req) - if err = verifyFieldsString(req.Payload, "chatId", "content"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId", "content"); err != nil { return } - if err = verifyFieldsBool(req.Payload, "isResponse"); err != nil { + if err = VerifyFieldsBool(req.Payload, "isResponse"); err != nil { return } var parentChatId uint64 if req.Payload["isResponse"].(bool) { - if err = verifyFieldsFloat64(req.Payload, "parentChatId"); err != nil { + if err = VerifyFieldsFloat64(req.Payload, "parentChatId"); err != nil { return } parentChatId = uint64(req.Payload["parentChatId"].(float64)) @@ -1507,26 +1507,26 @@ func (zch *ZoneChatsHandler[T]) handleZoneRequest(ctx context.Context, req *Zone } err = zch.AddChatMessage(req.From, req.Payload["chatId"].(string), req.Payload["content"].(string), req.Payload["isResponse"].(bool), parentChatId, file) case DELETE_CHAT_MESSAGE: - if err = verifyFieldsString(req.Payload, "chatId"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId"); err != nil { return } - if err = verifyFieldsFloat64(req.Payload, "messageId"); err != nil { + if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil { return } err = zch.DeleteChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatId"].(string)) case EDIT_CHAT_MESSAGE: - if err = verifyFieldsString(req.Payload, "chatId", "newContent"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId", "newContent"); err != nil { return } - if err = verifyFieldsFloat64(req.Payload, "messageId"); err != nil { + if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil { return } err = zch.UpdateChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatId"].(string), req.Payload["newContent"].(string)) case DELETE_CHAT_FILE: - if err = verifyFieldsString(req.Payload, "chatId", "fileName"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId", "fileName"); err != nil { return } - if err = verifyFieldsFloat64(req.Payload, "fileId"); err != nil { + if err = VerifyFieldsFloat64(req.Payload, "fileId"); err != nil { return } err = zch.DeleteChatFile(uint64(req.Payload["fileId"].(float64)), req.Payload["fileName"].(string), req.Payload["chatId"].(string)) diff --git a/zoneFSHandler.go b/zoneFSHandler.go index e2c86c3..3e990cb 100644 --- a/zoneFSHandler.go +++ b/zoneFSHandler.go @@ -479,7 +479,7 @@ func (zfh *ZoneFileHandler[T]) AddFolderMember(userId string, path string, folde logger.Println(sendErr) } } - for _,member := range members { + for _, member := range members { go func(m string) { bs, err := json.Marshal(map[string]any{ "zoneId": zfh.ZoneId, @@ -604,14 +604,14 @@ func (zfh *ZoneFileHandler[T]) RemoveFolderMember(userId string, path string, fo for _, member := range members { if _, ok := member.(string); ok { go func(m string) { - fmt.Println("sending unshare notification",m) + fmt.Println("sending unshare notification", m) bs, err := json.Marshal(map[string]any{ "zoneId": zfh.ZoneId, "folderName": folderName, "zoneHost": NodeID, }) if err != nil { - fmt.Println("error there",err) + fmt.Println("error there", err) return } zfh.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{ @@ -641,15 +641,15 @@ func (zfh *ZoneFileHandler[T]) RemoveFileMember(path, filename, members, read, w } func (zfh *ZoneFileHandler[T]) ClearUserFolders(userId string) (err error) { - fsEntities,_,err := zfh.DB.ListZoneFSEntity("",userId,0,100) + fsEntities, _, err := zfh.DB.ListZoneFSEntity("", userId, 0, 100) if err != nil { return } for _, fsEntity := range fsEntities { go func(f *ZoneFSEntity) { - if e := zfh.RemoveFolderMember(f.Owner,"",f.Name,[]any{userId}); e != nil { - logger.Println(e) - } + if e := zfh.RemoveFolderMember(f.Owner, "", f.Name, []any{userId}); e != nil { + logger.Println(e) + } }(fsEntity) } return @@ -1073,7 +1073,7 @@ func (zfh *ZoneFileHandler[T]) handleDataChannel(ctx context.Context, dc *DataCh func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneRequest) (err error) { switch req.ReqType { case string(REMOVED_ZONE_AUTHORIZED_MEMBER): - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } var index int @@ -1091,7 +1091,7 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq zfh.ZoneMembersId = append(zfh.ZoneMembersId[:index], zfh.ZoneMembersId[index+1:]...) err = zfh.DeleteFolder("", req.Payload["userId"].(string)) case string(NEW_AUTHORIZED_ZONE_MEMBER): - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } var contain bool @@ -1123,18 +1123,18 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq }) case LEAVE_ZONE: logger.Println("*-----------------handling leaving zone---------------*") - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } //err = zfh.LeaveFSInstance(req.Payload["userId"].(string)) case ZONE_UPLOAD_FILE_DONE: - if err = verifyFieldsBool(req.Payload, "failed"); err != nil { + if err = VerifyFieldsBool(req.Payload, "failed"); err != nil { return } - if err = verifyFieldsString(req.Payload, "path", "userId", "fileName", "type"); err != nil { + if err = VerifyFieldsString(req.Payload, "path", "userId", "fileName", "type"); err != nil { return } - if err = verifyFieldsFloat64(req.Payload, "size"); err != nil { + if err = VerifyFieldsFloat64(req.Payload, "size"); err != nil { return } if req.Payload["failed"].(bool) { @@ -1173,20 +1173,20 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq // err = zfh.FSInstance.SetupFileDownload(req.Payload["path"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string)) case GET_FS_ENTITIES: logger.Println("getting files entities") - if err = verifyFieldsString(req.Payload, "path", "userId", "section"); err != nil { + if err = VerifyFieldsString(req.Payload, "path", "userId", "section"); err != nil { return } var backward bool - if err = verifyFieldsBool(req.Payload, "backward"); err == nil { + if err = VerifyFieldsBool(req.Payload, "backward"); err == nil { backward = req.Payload["backward"].(bool) } - if err = verifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil { + if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil { return } err = zfh.GetFSEntities(req.Payload["userId"].(string), req.Payload["section"].(string), req.Payload["path"].(string), 0, 100, backward) case CREATE_FOLDER: logger.Println("creating folder") - if err = verifyFieldsString(req.Payload, "path"); err != nil { + if err = VerifyFieldsString(req.Payload, "path"); err != nil { return } if _, ok := req.Payload["config"]; !ok { @@ -1204,17 +1204,17 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq } err = zfh.CreateFolder(req.Payload["path"].(string), &config) case RENAME_FOLDER: - if err = verifyFieldsString(req.Payload, "path", "name", "newName"); err != nil { + if err = VerifyFieldsString(req.Payload, "path", "name", "newName"); err != nil { return } err = zfh.RenameFolder(req.Payload["path"].(string), req.Payload["name"].(string), req.Payload["newName"].(string)) case RENAME_FILE: - if err = verifyFieldsString(req.Payload, "path", "name", "newName"); err != nil { + if err = VerifyFieldsString(req.Payload, "path", "name", "newName"); err != nil { return } err = zfh.RenameFile(req.Payload["path"].(string), req.Payload["name"].(string), req.Payload["newName"].(string)) case COPY_FOLDER: - if err = verifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { return } if _, ok := req.Payload["config"]; !ok { @@ -1235,7 +1235,7 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq logger.Println(err) }() case COPY_FILE: - if err = verifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { return } if _, ok := req.Payload["config"]; !ok { @@ -1256,7 +1256,7 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq logger.Println(err) }() case CUT_FOLDER: - if err = verifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { return } if _, ok := req.Payload["config"]; !ok { @@ -1277,7 +1277,7 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq logger.Println(err) }() case CUT_FILE: - if err = verifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId", "path", "dstPath"); err != nil { return } if _, ok := req.Payload["config"]; !ok { @@ -1298,10 +1298,10 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq logger.Println(err) }() case UPDATE_PERMISSIONS: - if err = verifyFieldsString(req.Payload, "path", "name", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "path", "name", "userId"); err != nil { return } - if err = verifyFieldsBool(req.Payload, "read", "write", "download", "rename"); err != nil { + if err = VerifyFieldsBool(req.Payload, "read", "write", "download", "rename"); err != nil { return } fsAcessRights := &FSEntityAccessRights{ @@ -1312,31 +1312,31 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq } err = zfh.UpdatePermissions(req.Payload["path"].(string), req.Payload["name"].(string), req.Payload["userId"].(string), fsAcessRights) case DELETE_FOLDER: - if err = verifyFieldsString(req.Payload, "path", "name"); err != nil { + if err = VerifyFieldsString(req.Payload, "path", "name"); err != nil { return } err = zfh.DeleteFolder(req.Payload["path"].(string), req.Payload["name"].(string)) case DELETE_FILE: - if err = verifyFieldsString(req.Payload, "path", "name"); err != nil { + if err = VerifyFieldsString(req.Payload, "path", "name"); err != nil { return } err = zfh.DeleteFile(req.Payload["path"].(string), req.Payload["name"].(string)) case ADD_FS_ENTITY_MEMBERS: - if err = verifyFieldsString(req.Payload, "path", "userId", "folderName"); err != nil { + if err = VerifyFieldsString(req.Payload, "path", "userId", "folderName"); err != nil { return } - if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } - if err = verifyFieldsBool(req.Payload, "read", "write", "download", "rename"); err != nil { + if err = VerifyFieldsBool(req.Payload, "read", "write", "download", "rename"); err != nil { return } err = zfh.AddFolderMember(req.Payload["userId"].(string), req.Payload["path"].(string), req.Payload["folderName"].(string), req.Payload["members"].([]interface{}), req.Payload["read"].(bool), req.Payload["write"].(bool), req.Payload["download"].(bool), req.Payload["rename"].(bool)) case REMOVE_FS_ENTITY_MEMBERS: - if err = verifyFieldsString(req.Payload, "path", "userId", "folderName"); err != nil { + if err = VerifyFieldsString(req.Payload, "path", "userId", "folderName"); err != nil { return } - if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } err = zfh.RemoveFolderMember(req.Payload["userId"].(string), req.Payload["path"].(string), req.Payload["folderName"].(string), req.Payload["members"].([]interface{})) diff --git a/zoneGrpcMiddleware.go b/zoneGrpcMiddleware.go index b890e7c..b927bab 100644 --- a/zoneGrpcMiddleware.go +++ b/zoneGrpcMiddleware.go @@ -64,7 +64,7 @@ func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage done, errCh := make(chan struct{}), make(chan error) go func() { var payload map[string]string - if e := json.Unmarshal(req.Payload, &payload); err != nil { + if e := json.Unmarshal(req.Payload, &payload); e != nil { errCh <- e return } @@ -233,7 +233,7 @@ func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage return } default: - logger.Println("no request for zon grpc middleware") + logger.Println("no request for zone grpc middleware") logger.Println(payload) logger.Println(req.Type) } diff --git a/zoneManager.go b/zoneManager.go index 801c589..666ed04 100644 --- a/zoneManager.go +++ b/zoneManager.go @@ -10,7 +10,6 @@ import ( "path/filepath" "strconv" sync "sync" - "sync/atomic" "github.com/google/uuid" "github.com/pion/webrtc/v3" @@ -146,17 +145,6 @@ func NewZoneManager(id string, token string) (zoneManager *ZoneManager, err erro return } -func atomicallyExecute(flag *uint32, job func() (err error)) (err error) { - for { - if atomic.CompareAndSwapUint32(flag, 0, 1) { - defer atomic.SwapUint32(flag, 0) - err = job() - break - } - } - return -} - func (zm *ZoneManager) sendSignalingMessage(messageType, from, to string, payload map[string]interface{}) (err error) { bs, err := json.Marshal(payload) if err != nil { @@ -380,28 +368,6 @@ func (zm *ZoneManager) HandleAnswer(ctx context.Context, from string, to string, } func (zm *ZoneManager) HandleCounterOffer(ctx context.Context, from string, to string, req map[string]string) (err error) { - logger.Println("handling counter offer 1") - // if err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) { - // logger.Println("start job") - // if _, ok := zm.RTCPeerConnections[from]; !ok { - // logger.Println("error here") - // err = fmt.Errorf("no field corresponding peer connection for id %s", from) - // return - // } - // logger.Println("handling counter offer") - // connection := zm.RTCPeerConnections[from] - // err = atomicallyExecute(zm.localSDFlag, func() (err error) { - // if err = connection.SetLocalDescription(*zm.LocalSD[from]); err != nil { - // logger.Println(err) - // return - // } - // return - // }) - // return - // }); err != nil { - // return - // } - logger.Println("handling counter offer 2") _ = atomicallyExecute(zm.candidateFlag, func() (err error) { for _, candidate := range zm.PendingCandidates[from] { logger.Println("sending candidate to", from) @@ -529,7 +495,7 @@ func (zm *ZoneManager) createPeerConnection(target string, from string, zoneId s } return }) - reqChan := make(chan *ZoneRequest,100) + reqChan := make(chan *ZoneRequest, 100) if dc.Label() == "data" { dc.OnOpen(func() { logger.Println(zoneId) @@ -546,21 +512,6 @@ func (zm *ZoneManager) createPeerConnection(target string, from string, zoneId s return } done, err := zm.Zones[zoneId].ZoneRequestScheduler.Schedule(reqChan) - - // bs, jsonErr := json.Marshal(&ZoneResponse{ - // Type: "user_zone_init", - // From: zoneId, - // To: target, - // Payload: map[string]interface{}{}, - // }) - // if jsonErr != nil { - // logger.Println("error in open channel", jsonErr) - // return - // } - // if sendErr := dc.SendText(string(bs)); sendErr != nil { - // logger.Println("error in open channel send", sendErr) - // return - // } go func() { for { select { @@ -575,14 +526,10 @@ func (zm *ZoneManager) createPeerConnection(target string, from string, zoneId s dc.OnClose(func() { fmt.Println("closing gracefully event dc...") close(reqChan) - - //_ = zm.HandleLeavingMember(target, zoneId, true) - }) dc.OnError(func(err error) { logger.Println("--------------- error in dc:", err) close(reqChan) - //_ = zm.HandleLeavingMember(target, zoneId, true) }) dc.OnMessage(func(msg webrtc.DataChannelMessage) { var req ZoneRequest @@ -633,39 +580,8 @@ func (zm *ZoneManager) createPeerConnection(target string, from string, zoneId s } } }) - // peerConnection.OnNegotiationNeeded(func() { - // logger.Println("------------------- negotiation is needed --------------------") - // if pc, ok := zm.RTCPeerConnections[target]; ok { - // if pc.SignalingState() == webrtc.ICETransportStateConnected { - // localSd, err := pc.CreateOffer(nil) - // if err != nil { - // logger.Println(err) - // return - // } - // if err = pc.SetLocalDescription(localSd); err != nil { - // logger.Println(err) - // return - // } - // if err = zm.stream.Send(&Request{ - // Type: string(ZONE_WEBRTC_RENNEGOTIATION_OFFER), - // From: zm.ID, - // Token: "", - // Payload: map[string]string{ - // "to": target, - // "sdp": localSd.SDP, - // }, - // }); err != nil { - // logger.Println(err) - // return - // } - // } - // } - // }) peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) { logger.Printf("ICE connection state has changed %s\n", is.String()) - // if is == webrtc.ICEConnectionStateDisconnected || is == webrtc.ICEConnectionStateFailed { - // _ = zm.HandleLeavingMember(target, zoneId,true) - // } }) peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) { if i == nil { @@ -694,13 +610,6 @@ func (zm *ZoneManager) HandleRennegotiationOffer(from, sdp string) (err error) { err = fmt.Errorf("no corresponding peer connection for id %s", from) return } - //zm.RTCPeerConnections[from].makingOfferLock.Lock() - // if zm.RTCPeerConnections[from].makingOffer { - // //zm.RTCPeerConnections[from].makingOfferLock.Unlock() - // err = fmt.Errorf("already making an offer or state is stable") - // return - // } - //zm.RTCPeerConnections[from].makingOfferLock.Unlock() if err = zm.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil { return } @@ -839,16 +748,9 @@ func (zm *ZoneManager) notifyLeavingMember(userId, zoneId, hostId string) (err e func (zm *ZoneManager) negotiate(target string, zoneId string) { _ = atomicallyExecute(zm.peerConnectionFlag, func() (err error) { if _, ok := zm.RTCPeerConnections[target]; !ok { + err = fmt.Errorf("no peerConnections") return } - //zm.RTCPeerConnections[target].makingOfferLock.Lock() - //zm.RTCPeerConnections[target].makingOffer = true - //zm.RTCPeerConnections[target].makingOfferLock.Unlock() - defer func() { - //zm.RTCPeerConnections[target].makingOfferLock.Lock() - //zm.RTCPeerConnections[target].makingOffer = false - //zm.RTCPeerConnections[target].makingOfferLock.Unlock() - }() return }) } diff --git a/zoneNotificationsHandler.go b/zoneNotificationsHandler.go index 68860ec..dd80ca4 100644 --- a/zoneNotificationsHandler.go +++ b/zoneNotificationsHandler.go @@ -155,10 +155,10 @@ func (znh ZoneNotificationsHandler) handleDataChannel(ctx context.Context, dc *D func (znh *ZoneNotificationsHandler) handleZoneRequest(ctx context.Context, req *ZoneRequest) (err error) { switch req.ReqType { case CREATE_NOTIFICATION: - if err = verifyFieldsString(req.Payload, "type", "title", "body", "payload"); err != nil { + if err = VerifyFieldsString(req.Payload, "type", "title", "body", "payload"); err != nil { return } - if err = verifyFieldsBool(req.Payload, "isPushed"); err != nil { + if err = VerifyFieldsBool(req.Payload, "isPushed"); err != nil { return } if _, ok := req.Payload["recipients"]; !ok { diff --git a/zoneRequestScheduler.go b/zoneRequestScheduler.go index 532ef74..71e47f3 100644 --- a/zoneRequestScheduler.go +++ b/zoneRequestScheduler.go @@ -2,7 +2,6 @@ package localserver import ( "context" - "fmt" "sync" "time" ) @@ -15,16 +14,16 @@ type ZoneRequestScheduler struct { } type ZoneRequest struct { - ReqType string `json:"reqType"` - Payload map[string]interface{} `json:"payload"` - From string `json:"from"` + ReqType string `json:"reqType"` + Payload map[string]any `json:"payload"` + From string `json:"from"` } type ZoneResponse struct { - Type string `json:"type"` - To string `json:"to"` - From string `json:"from"` - Payload map[string]interface{} `json:"payload"` + Type string `json:"type"` + To string `json:"to"` + From string `json:"from"` + Payload map[string]any `json:"payload"` } type ZoneRequestHandler interface { @@ -34,58 +33,6 @@ type ZoneRequestHandler interface { handleDataChannel(ctx context.Context, dc *DataChannel) (catched bool) } -func verifyFieldsString(payload map[string]interface{}, fields ...string) (err error) { - for _, field := range fields { - if _, ok := payload[field]; !ok { - err = fmt.Errorf("no field %s in payload", field) - return - } else if _, ok := payload[field].(string); !ok { - err = fmt.Errorf("field %s in payload is not a string", field) - return - } - } - return -} - -func verifyFieldsBool(payload map[string]interface{}, fields ...string) (err error) { - for _, field := range fields { - if _, ok := payload[field]; !ok { - err = fmt.Errorf("no field %s in payload", field) - return - } else if _, ok := payload[field].(bool); !ok { - err = fmt.Errorf("field %s in payload is not a bool", field) - return - } - } - return -} - -func verifyFieldsFloat64(payload map[string]interface{}, fields ...string) (err error) { - for _, field := range fields { - if _, ok := payload[field]; !ok { - err = fmt.Errorf("no field %s in payload", field) - return - } else if _, ok := payload[field].(float64); !ok { - err = fmt.Errorf("field %s in payload is not a float64", field) - return - } - } - return -} - -func verifyFieldsSliceInterface(payload map[string]interface{}, fields ...string) (err error) { - for _, field := range fields { - if _, ok := payload[field]; !ok { - err = fmt.Errorf("no field %s in payload", field) - return - } else if _, ok := payload[field].([]any); !ok { - err = fmt.Errorf("field %s in payload is not a []interface{}", field) - return - } - } - return -} - func NewZoneRequestScheduler(authorizedMembers []string, handlers ...ZoneRequestHandler) (zoneRequestScheduler *ZoneRequestScheduler, handlerErrCh chan error) { zoneRequestScheduler = new(ZoneRequestScheduler) zoneRequestScheduler.handlersPublishers = make([]chan<- *ZoneRequest, 0) @@ -93,7 +40,7 @@ func NewZoneRequestScheduler(authorizedMembers []string, handlers ...ZoneRequest handlerErrCh = make(chan error) reqChans := []chan *ZoneRequest{} for _, handler := range handlers { - publisher := make(chan *ZoneRequest,100) + publisher := make(chan *ZoneRequest, 100) zoneRequestScheduler.handlersPublishers = append(zoneRequestScheduler.handlersPublishers, publisher) zoneRequestScheduler.handlersDataChannelDispatchCallbacks = append(zoneRequestScheduler.handlersDataChannelDispatchCallbacks, handler.handleDataChannel) reqChan, done, errCh := handler.Subscribe(context.Background(), publisher) @@ -133,18 +80,16 @@ func NewZoneRequestScheduler(authorizedMembers []string, handlers ...ZoneRequest func (zrs *ZoneRequestScheduler) Schedule(reqChan <-chan *ZoneRequest) (done chan struct{}, errCh chan error) { done, errCh = make(chan struct{}), make(chan error) - go func() { - for req := range reqChan { - go func(r *ZoneRequest) { - for _, publisher := range zrs.handlersPublishers { - go func(p chan<- *ZoneRequest) { - p <- r - }(publisher) - } - }(req) - } - done <- struct{}{} - }() + for req := range reqChan { + go func(r *ZoneRequest) { + for _, publisher := range zrs.handlersPublishers { + go func(p chan<- *ZoneRequest) { + p <- r + }(publisher) + } + }(req) + } + done <- struct{}{} return } diff --git a/zoneUsersHandler.go b/zoneUsersHandler.go index a3b0fd1..954da8e 100644 --- a/zoneUsersHandler.go +++ b/zoneUsersHandler.go @@ -626,7 +626,7 @@ func (zuh *ZoneUsersHandler) handleZoneRequest(ctx context.Context, req *ZoneReq } err = zuh.EditDefaultRights(config.DefaultChatRights, config.DefaultAudioChannelRights, config.DefaultVideoChannelRights, config.DefaultMediaRights, config.DefaultFileRights) case EDIT_USERS_RIGHTS: - if err = verifyFieldsSliceInterface(req.Payload, "users"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "users"); err != nil { return } if _, ok := req.Payload["defaultRights"]; !ok { @@ -644,52 +644,52 @@ func (zuh *ZoneUsersHandler) handleZoneRequest(ctx context.Context, req *ZoneReq } err = zuh.EditUsersRights(req.Payload["users"].([]interface{}), config.DefaultChatRights, config.DefaultAudioChannelRights, config.DefaultVideoChannelRights, config.DefaultMediaRights, config.DefaultFileRights) case REMOVE_USER: - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } err = zuh.RemoveUser(req.Payload["userId"].(string)) case ADD_USER: - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } err = zuh.AddUser(req.Payload["userId"].(string)) case REMOVE_KNOWN_CHAT: - if err = verifyFieldsString(req.Payload, "chatId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId", "userId"); err != nil { return } err = zuh.RemoveKnownChat(req.Payload["chatId"].(string), req.Payload["userId"].(string)) case ADD_KNOWN_CHAT: - if err = verifyFieldsString(req.Payload, "chatId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "chatId", "userId"); err != nil { return } err = zuh.AddKnownChat(req.Payload["chatId"].(string), req.Payload["userId"].(string)) case REMOVE_KNOWN_AUDIO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zuh.RemoveKnownAudioChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) case ADD_KNOWN_AUDIO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zuh.AddKnownAudioChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) case REMOVE_KNOWN_VIDEO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zuh.RemoveKnownVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) case ADD_KNOWN_VIDEO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zuh.AddKnownVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) case GET_DEFAULT_RIGHTS: - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } err = zuh.GetDefaultRights(req.Payload["userId"].(string)) case GET_USER: - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } if _, ok := req.Payload["init"]; !ok { diff --git a/zoneVideoChannel.go b/zoneVideoChannel.go index 9f33daa..d1d005d 100644 --- a/zoneVideoChannel.go +++ b/zoneVideoChannel.go @@ -520,39 +520,39 @@ func (vc *VideoChannel) createPeerConnection(target string, from string, peerTyp return }) }) - peerConnection.OnNegotiationNeeded(func() { - logger.Println("---------------- rennego is needed -----------") - // _ = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) { - // for _, id := range vc.CurrentMembersId { - // logger.Println("----------------- sending renego to peer with id", id) - // if _, ok := vc.rtcPeerConnections[id]; !ok { - // continue - // } - // if peerConnection.SignalingState() == webrtc.SignalingStateStable { - // localSd, localSdErr := peerConnection.CreateOffer(nil) - // if localSdErr != nil { - // logger.Println(localSdErr) - // return localSdErr - // } - // if err = peerConnection.SetLocalDescription(localSd); err != nil { - // logger.Println(err) - // return - // } - // d, e := sendDCMessage(string(VIDEO_CHANNEL_WEBRTC_RENNEGOTIATION_OFFER), vc.ID, id, map[string]interface{}{ - // "from": vc.ID, - // "to": id, - // "sdp": localSd.SDP, - // }) - // select { - // case <-d: - // case err = <-e: - // logger.Println(err) - // } - // } - // } - // return - // }) - }) + // peerConnection.OnNegotiationNeeded(func() { + // logger.Println("---------------- rennego is needed -----------") + // // _ = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) { + // // for _, id := range vc.CurrentMembersId { + // // logger.Println("----------------- sending renego to peer with id", id) + // // if _, ok := vc.rtcPeerConnections[id]; !ok { + // // continue + // // } + // // if peerConnection.SignalingState() == webrtc.SignalingStateStable { + // // localSd, localSdErr := peerConnection.CreateOffer(nil) + // // if localSdErr != nil { + // // logger.Println(localSdErr) + // // return localSdErr + // // } + // // if err = peerConnection.SetLocalDescription(localSd); err != nil { + // // logger.Println(err) + // // return + // // } + // // d, e := sendDCMessage(string(VIDEO_CHANNEL_WEBRTC_RENNEGOTIATION_OFFER), vc.ID, id, map[string]interface{}{ + // // "from": vc.ID, + // // "to": id, + // // "sdp": localSd.SDP, + // // }) + // // select { + // // case <-d: + // // case err = <-e: + // // logger.Println(err) + // // } + // // } + // // } + // // return + // // }) + // }) return } diff --git a/zoneVideoChannelsHandler.go b/zoneVideoChannelsHandler.go index ef110da..0e974ce 100644 --- a/zoneVideoChannelsHandler.go +++ b/zoneVideoChannelsHandler.go @@ -886,7 +886,7 @@ func (zvch *ZoneVideoChannelsHandler) handleZoneRequest(ctx context.Context, req switch req.ReqType { case LEAVE_ZONE: logger.Println("*-----------------handling leaving zone---------------*") - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } for _, vc := range zvch.VideoChannels { @@ -906,7 +906,7 @@ func (zvch *ZoneVideoChannelsHandler) handleZoneRequest(ctx context.Context, req } } case string(REMOVED_ZONE_AUTHORIZED_MEMBER): - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } var index int @@ -928,7 +928,7 @@ func (zvch *ZoneVideoChannelsHandler) handleZoneRequest(ctx context.Context, req } err = zvch.RemoveUserFromAllVideoChannels(req.Payload["userId"].(string)) case string(NEW_AUTHORIZED_ZONE_MEMBER): - if err = verifyFieldsString(req.Payload, "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "userId"); err != nil { return } var contain bool @@ -943,25 +943,25 @@ func (zvch *ZoneVideoChannelsHandler) handleZoneRequest(ctx context.Context, req } err = zvch.SetAllPublicVideoChannelForUser(req.Payload["userId"].(string)) case JOIN_VIDEO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { return } err = zvch.JoinVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string), req.Payload["sdp"].(string)) case LEAVE_VIDEO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zvch.LeaveVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) case ADD_VIDEO_CHANNEL_MEMBERS: - if err = verifyFieldsString(req.Payload, "channelId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId"); err != nil { return } - if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } err = zvch.AddVideoChannelsMembers(req.Payload["channelId"].(string), req.Payload["members"].([]interface{})) case REMOVE_VIDEO_CHANNEL_MEMBER: - if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zvch.RemoveVideoChannelMember(req.Payload["channelId"].(string), req.Payload["userId"].(string)) @@ -970,40 +970,40 @@ func (zvch *ZoneVideoChannelsHandler) handleZoneRequest(ctx context.Context, req return } case EDIT_VIDEO_CHANNEL_NAME: - if err = verifyFieldsString(req.Payload, "channelId", "newVideoChannelId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "newVideoChannelId"); err != nil { return } if err = zvch.EditVideoChannelName(req.Payload["channelId"].(string), req.Payload["newVideoChannelId"].(string)); err != nil { return } case EDIT_VIDEO_CHANNEL_TYPE: - if err = verifyFieldsString(req.Payload, "channelId", "channelType"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "channelType"); err != nil { return } if err = zvch.EditVideoChannelType(req.Payload["channelId"].(string), req.Payload["channelType"].(string)); err != nil { return } case DELETE_VIDEO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId"); err != nil { return } err = zvch.DeleteVideoChannel(req.Payload["channelId"].(string)) case CREATE_VIDEO_CHANNEL: - if err = verifyFieldsString(req.Payload, "channelId", "owner", "channelType"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "owner", "channelType"); err != nil { return } - if err = verifyFieldsSliceInterface(req.Payload, "members"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil { return } err = zvch.AddNewVideoChannel(req.Payload["channelId"].(string), req.Payload["owner"].(string), req.Payload["channelType"].(string), req.Payload["members"].([]interface{})) case GET_VIDEO_CHANNELS: - if err = verifyFieldsSliceInterface(req.Payload, "channelsId"); err != nil { + if err = VerifyFieldsSliceInterface(req.Payload, "channelsId"); err != nil { return } err = zvch.GetVideoChannels(req.From, req.Payload["channelsId"].([]interface{})...) case string(VIDEO_CHANNEL_WEBRTC_COUNTER_OFFER): logger.Println("handling video channel counter offer") - if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) { @@ -1015,7 +1015,7 @@ func (zvch *ZoneVideoChannelsHandler) handleZoneRequest(ctx context.Context, req return }) case string(VIDEO_CHANNEL_WEBRTC_RENNEGOTIATION_OFFER): - if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { return } err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) { @@ -1027,7 +1027,7 @@ func (zvch *ZoneVideoChannelsHandler) handleZoneRequest(ctx context.Context, req return }) case string(VIDEO_CHANNEL_WEBRTC_RENNEGOTIATION_ANSWER): - if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { + if err = VerifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil { return } err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) { @@ -1041,7 +1041,7 @@ func (zvch *ZoneVideoChannelsHandler) handleZoneRequest(ctx context.Context, req case string(VIDEO_CHANNEL_WEBRTC_CANDIDATE): logger.Println("handling video channel webrtc candidate") logger.Println(req.Payload) - if err = verifyFieldsString(req.Payload, FROM, "candidate", "sdpMLineIndex", "sdpMid", "channelId", "userId"); err != nil { + if err = VerifyFieldsString(req.Payload, FROM, "candidate", "sdpMLineIndex", "sdpMid", "channelId", "userId"); err != nil { return } logger.Println(req.Payload)