hosted squads working on node

This commit is contained in:
Loïs BIBEHE 2022-12-25 19:09:54 +01:00
parent f2ebf8cb4c
commit 79cc675c43
Signed by: Lois
GPG Key ID: 782967C8C0F78E79
101 changed files with 258918 additions and 2605 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
cmd/data
cmd/node
cmd/node.linux
cmd/config/102ed1ef-61cb-40fa-be62-77abe79aff95
cmd/config/102ed1ef-61cb-40fa-be62-77abe79aff95.pub
cmd/config/node_config.json

View File

@ -3,6 +3,7 @@ package localserver
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"strconv" "strconv"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
@ -97,6 +98,7 @@ func (cm *ChatGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
case SYNC_CHAT: case SYNC_CHAT:
//todo: implement sync chat //todo: implement sync chat
case CHAT_WEBRTC_OFFER: case CHAT_WEBRTC_OFFER:
fmt.Println("got offer in chat")
if err := validateRequest(payload, SDP); err != nil { if err := validateRequest(payload, SDP); err != nil {
errCh <- err errCh <- err
return return
@ -115,6 +117,7 @@ func (cm *ChatGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
return return
} }
case CHAT_WEBRTC_COUNTER_OFFER: case CHAT_WEBRTC_COUNTER_OFFER:
fmt.Println("got counter offer in chat")
if err := cm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil { if err := cm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
errCh <- err errCh <- err
return return
@ -138,6 +141,7 @@ func (cm *ChatGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
return return
} }
case CHAT_WEBRTC_CANDIDATE: case CHAT_WEBRTC_CANDIDATE:
fmt.Println("got candidate in chat")
if err := validateRequest(payload, "candidate", "sdpMLineIndex", "sdpMid"); err != nil { if err := validateRequest(payload, "candidate", "sdpMLineIndex", "sdpMid"); err != nil {
errCh <- err errCh <- err
return return
@ -160,10 +164,14 @@ func (cm *ChatGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
return return
} }
default: default:
fmt.Println("no request for chat grpc middleware")
fmt.Println(req.Type)
fmt.Println(req.Payload)
logger.Println("no request for chats grpc middleware") logger.Println("no request for chats grpc middleware")
logger.Println(payload) logger.Println(payload)
logger.Println(req.Type) logger.Println(req.Type)
} }
done <- struct{}{}
}() }()
select { select {
case <-ctx.Done(): case <-ctx.Done():

View File

@ -15,6 +15,12 @@ import (
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
) )
const (
UPDATE_UNREAD_COUNT = "update_unread_count"
UPDATE_LAST_INTERACTION_TIME = "update_last_interaction_time"
DISCONNECT_USER_FROM_CHAT = "disconnect_user_from_chat"
)
type ChatsManager struct { type ChatsManager struct {
ID string ID string
Chats map[string]*NodeChat Chats map[string]*NodeChat
@ -45,8 +51,17 @@ type NodeChat struct {
func NewNodeChat(id, name, initiator, target, initiatorHost, targetHost string, initialized bool) (chat *NodeChat, err error) { func NewNodeChat(id, name, initiator, target, initiatorHost, targetHost string, initialized bool) (chat *NodeChat, err error) {
dataChannels, dataChannelFlag := make(map[string]*DataChannel), uint32(0) 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) nodeChatMessageHandler, err := NewNodeChatChannelsHandler(id, id, initiator, target, initiatorHost, targetHost, dataChannels, &dataChannelFlag)
chatScheduler, e := NewChatRequestScheduler(initiator, target, initiatorHost, targetHost, nodeChatMessageHandler) if err != nil {
fmt.Println("error occured there")
fmt.Println(err)
return
}
nodeChatNotificationsHandler, err := NewChatNotificationsHandler(id, initiator, target, initiatorHost, targetHost, dataChannels, &dataChannelFlag)
if err != nil {
return
}
chatScheduler, e := NewChatRequestScheduler(initiator, target, initiatorHost, targetHost, nodeChatMessageHandler, nodeChatNotificationsHandler)
go func() { go func() {
for schedErr := range e { for schedErr := range e {
logger.Println("chat error:", schedErr) logger.Println("chat error:", schedErr)
@ -148,11 +163,13 @@ func (cm *ChatsManager) fetchChats(nodeId string, token string) (chats []*NodeCh
if err = json.Unmarshal(bs, &payload); err != nil { if err = json.Unmarshal(bs, &payload); err != nil {
return return
} }
b, err := json.Marshal(payload["Chats"]) fmt.Println(payload)
b, err := json.Marshal(payload["chats"])
if err != nil { if err != nil {
return return
} }
err = json.Unmarshal(b, &chats) err = json.Unmarshal(b, &chats)
return return
} }
@ -210,8 +227,12 @@ func (cm *ChatsManager) CreateOffer(ctx context.Context, target string, from str
func (cm *ChatsManager) HandleOffer(ctx context.Context, from string, to string, req map[string]string, cb OnICECandidateFunc) (err error) { 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) done, errCh := make(chan struct{}), make(chan error)
go func() { go func() {
if _, ok := cm.Chats[req["chatId"]]; !ok { if id, ok := cm.Chats[req["chatId"]]; !ok {
err = fmt.Errorf("no corresponding chat") err = fmt.Errorf("no corresponding chat")
fmt.Println(req["chatId"])
fmt.Println(cm.Chats)
fmt.Println(id)
fmt.Println(err)
errCh <- err errCh <- err
return return
} }
@ -374,7 +395,7 @@ func (cm *ChatsManager) createPeerConnection(target string, from string, chatId
config := webrtc.Configuration{ config := webrtc.Configuration{
ICEServers: []webrtc.ICEServer{ ICEServers: []webrtc.ICEServer{
{ {
URLs: []string{"stun:stun.l.google.com:19302", "stun:stunserver.org:3478"}, URLs: []string{"stun:stun.l.google.com:19302"},
}, },
}, },
SDPSemantics: webrtc.SDPSemanticsUnifiedPlan, SDPSemantics: webrtc.SDPSemanticsUnifiedPlan,
@ -511,6 +532,7 @@ func (cm *ChatsManager) createPeerConnection(target string, from string, chatId
logger.Println(err) logger.Println(err)
return return
} }
fmt.Println("incoming req", req)
logger.Println("incoming request", req) logger.Println("incoming request", req)
go func() { go func() {
reqChan <- &req reqChan <- &req
@ -699,7 +721,7 @@ func (cm *ChatsManager) notifyLeavingMember(userId, chatId, hostId string) (err
em := NewEncryptionManager() em := NewEncryptionManager()
sig := em.SignRequestHMAC(NodeID) sig := em.SignRequestHMAC(NodeID)
body, err := json.Marshal(map[string]interface{}{ body, err := json.Marshal(map[string]interface{}{
"type": "DISCONNECT_CHAT_MEMBER", "type": DISCONNECT_USER_FROM_CHAT,
"mac": sig, "mac": sig,
"from": NodeID, "from": NodeID,
"peerType": "node", "peerType": "node",

View File

@ -259,6 +259,7 @@ func (ncdbh *NodeChatDBHandler) ListChatFiles(lastIndex int, limit int) (chatMes
return err return err
} }
if chatMessage.File != nil { if chatMessage.File != nil {
chatMessage.File.ID = chatMessage.ID
chatMessages = append(chatMessages, chatMessage.File) chatMessages = append(chatMessages, chatMessage.File)
l = lastIndex - x l = lastIndex - x
y++ y++

View File

@ -1,10 +1,11 @@
package localserver package localserver
import ( import (
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/fs" "io"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -51,8 +52,8 @@ type NodeChatChannelsHandler[T ZippytalFSInstance] struct {
} }
func NewNodeChatChannelsHandler(chatID, chatName, initiator, target, initiatorHostId, targetHostId string, dataChannels map[string]*DataChannel, flag *uint32) (nodeChatsHandler *NodeChatChannelsHandler[*NodeChatFSInstance], err error) { 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)) _, err = os.ReadDir(filepath.Join(dataPath, "data", "chats", chatID))
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
logger.Printf("creating chat directory for chat %s...\n", chatID) logger.Printf("creating chat directory for chat %s...\n", chatID)
@ -80,7 +81,7 @@ func NewNodeChatChannelsHandler(chatID, chatName, initiator, target, initiatorHo
return nil, writeErr return nil, writeErr
} }
_ = file.Close() _ = file.Close()
dirs, err = os.ReadDir(filepath.Join(dataPath, "data", "chats", chatID)) _, err = os.ReadDir(filepath.Join(dataPath, "data", "chats"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -89,16 +90,13 @@ func NewNodeChatChannelsHandler(chatID, chatName, initiator, target, initiatorHo
} }
} }
chats := make(map[string]*NodeChatChannel) chats := make(map[string]*NodeChatChannel)
for _, chat := range dirs {
if strings.HasPrefix(chat.Name(), ".") { nodeChatDBHandler, err := NewNodeChatDBHandler(chatID)
continue
}
nodeChatDBHandler, err := NewNodeChatDBHandler(chat.Name())
if err != nil { if err != nil {
return nil, err return nil, err
} }
var bs []byte var bs []byte
bs, err = os.ReadFile(filepath.Join(dataPath, "data", "chats", chat.Name(), "chatConfig.json")) bs, err = os.ReadFile(filepath.Join(dataPath, "data", "chats", chatID, "chatConfig.json"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -115,7 +113,7 @@ func NewNodeChatChannelsHandler(chatID, chatName, initiator, target, initiatorHo
c.DB = nodeChatDBHandler c.DB = nodeChatDBHandler
c.Tracking = nodeChatTracking c.Tracking = nodeChatTracking
chats[c.ID] = &c chats[c.ID] = &c
}
chatFlag := uint32(0) chatFlag := uint32(0)
chatFSFlag := uint32(0) chatFSFlag := uint32(0)
chatDCFlag := uint32(0) chatDCFlag := uint32(0)
@ -134,7 +132,7 @@ func NewNodeChatChannelsHandler(chatID, chatName, initiator, target, initiatorHo
return return
} }
func (zch *NodeChatChannelsHandler[T]) sendZoneRequest(reqType string, from string, payload map[string]interface{}) { func (zch *NodeChatChannelsHandler[T]) sendChatRequest(reqType string, from string, payload map[string]interface{}) {
go func() { go func() {
for _, rc := range zch.reqChans { for _, rc := range zch.reqChans {
rc <- &ChatRequest{ rc <- &ChatRequest{
@ -186,6 +184,8 @@ func (zch *NodeChatChannelsHandler[T]) Init(ctx context.Context, initiator, targ
func (zch *NodeChatChannelsHandler[T]) Subscribe(ctx context.Context, publisher <-chan *ChatRequest) (reqChan chan *ChatRequest, done chan struct{}, errCh chan error) { 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) reqChan, done, errCh = make(chan *ChatRequest), make(chan struct{}), make(chan error)
fmt.Println("subscribing...")
fmt.Println(zch)
zch.reqChans = append(zch.reqChans, reqChan) zch.reqChans = append(zch.reqChans, reqChan)
go func() { go func() {
for { for {
@ -200,6 +200,7 @@ func (zch *NodeChatChannelsHandler[T]) Subscribe(ctx context.Context, publisher
} }
} }
}() }()
fmt.Println(zch.reqChans)
return return
} }
@ -351,7 +352,7 @@ func (zch *NodeChatChannelsHandler[T]) ListLatestChatMessages(userId, chatID str
success, e := zch.sendDataChannelMessage(CHAT_MESSAGE_LIST, "node", userId, map[string]interface{}{ success, e := zch.sendDataChannelMessage(CHAT_MESSAGE_LIST, "node", userId, map[string]interface{}{
"done": done || i <= 0, "done": done || i <= 0,
"lastIndex": i - 1, "lastIndex": i - 1,
"chatID": chatID, "chatId": chatID,
"chatMessages": list, "chatMessages": list,
}) })
select { select {
@ -359,6 +360,9 @@ func (zch *NodeChatChannelsHandler[T]) ListLatestChatMessages(userId, chatID str
fmt.Println("done getting latest messages") fmt.Println("done getting latest messages")
case err = <-e: case err = <-e:
} }
go func() {
_ = zch.signalUnreadCount(context.Background(), userId, 0)
}()
return return
} }
@ -376,7 +380,7 @@ func (zch *NodeChatChannelsHandler[T]) ListLatestChatFiles(userId, chatID string
done, e := zch.sendDataChannelMessage(CHAT_FILES_LIST, "node", userId, map[string]interface{}{ done, e := zch.sendDataChannelMessage(CHAT_FILES_LIST, "node", userId, map[string]interface{}{
"done": i <= 0, "done": i <= 0,
"lastIndex": i, "lastIndex": i,
"chatID": chatID, "chatId": chatID,
"chatMessages": list, "chatMessages": list,
}) })
select { select {
@ -392,6 +396,7 @@ func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content st
return return
} }
chat := zch.Chats[chatID] chat := zch.Chats[chatID]
dateTime := time.Now().Format(time.RFC3339)
chatMessage := &ChatMessage{ chatMessage := &ChatMessage{
Content: content, Content: content,
From: userId, From: userId,
@ -399,7 +404,7 @@ func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content st
ResponseOf: nil, ResponseOf: nil,
File: file, File: file,
Tags: make([]string, 0), Tags: make([]string, 0),
Date: time.Now().Format("Mon, 02 Jan 2006 15:04:05 MST"), Date: dateTime,
} }
if err = atomicallyExecute(zch.ChatFlag, func() (err error) { if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if chat, ok := zch.Chats[chatID]; ok { if chat, ok := zch.Chats[chatID]; ok {
@ -416,7 +421,7 @@ func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content st
return return
} }
chatMessage.ID = zch.Chats[chatID].DB.PreviousId chatMessage.ID = zch.Chats[chatID].DB.PreviousId
_ = chat.Tracking.SetUserLastIndex(userId, chatMessage.ID)
} else { } else {
err = fmt.Errorf("no corresponding chats") err = fmt.Errorf("no corresponding chats")
} }
@ -425,13 +430,14 @@ func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content st
return return
} }
notifyActivity := func(done <-chan struct{}, e <-chan error, target, initiator string) { notifyActivity := func(done <-chan struct{}, e <-chan error, destination string) {
select { select {
case <-done: case <-done:
fmt.Println(destination, "is connected to this chat")
case <-e: case <-e:
_ = atomicallyExecute(zch.ChatFlag, func() (err error) { _ = atomicallyExecute(zch.ChatFlag, func() (err error) {
if chat, ok := zch.Chats[chatID]; ok { if chat, ok := zch.Chats[chatID]; ok {
li, err := chat.Tracking.GetUserLastIndex(target) li, err := chat.Tracking.GetUserLastIndex(destination)
if err != nil { if err != nil {
return err return err
} }
@ -439,23 +445,40 @@ func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content st
if err != nil { if err != nil {
return err return err
} }
fmt.Println("********_**_*")
fmt.Println(count)
bs, err := json.Marshal(map[string]any{ bs, err := json.Marshal(map[string]any{
"chatID": chatID, "chatId": chatID,
"chatHost": NodeID, "chatHost": NodeID,
}) })
if err != nil { if err != nil {
return err return err
} }
zch.sendZoneRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{ fmt.Println("noooo errrrrooooorr")
fmt.Println(destination)
fmt.Println(userId)
zch.sendChatRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{
"type": "new_chat_message", "type": "new_chat_message",
"title": "Unread messages 👀", "title": fmt.Sprintf("%d Unread messages 👀", count),
"body": fmt.Sprintf("%d new messages from %s", count, initiator), "body": fmt.Sprintf("%d new messages from %s", count, userId),
"isPushed": true, "isPushed": true,
"payload": string(bs), "payload": string(bs),
"recipients": []string{target}, "recipients": []string{destination},
}) })
go func() {
if e := zch.signalUnreadCount(context.Background(), destination, count); e != nil {
fmt.Println(e)
}
}()
fmt.Println("done without error 1")
go func() {
if e := zch.signalLastInteractionTime(context.Background(), dateTime); e != nil {
fmt.Println(e)
}
}()
fmt.Println("done without error 2")
fmt.Println("sending notification")
} }
return return
}) })
@ -463,14 +486,17 @@ func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content st
} }
d1, e1 := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", chat.Target, map[string]interface{}{ d1, e1 := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", chat.Target, map[string]interface{}{
"chatMessage": chatMessage, "chatMessage": chatMessage,
"chatID": chatID, "chatId": chatID,
}) })
go notifyActivity(d1, e1, chat.Target, chat.Initiator)
d2, e2 := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", chat.Initiator, map[string]interface{}{ d2, e2 := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", chat.Initiator, map[string]interface{}{
"chatMessage": chatMessage, "chatMessage": chatMessage,
"chatID": chatID, "chatId": chatID,
}) })
go notifyActivity(d2, e2, chat.Initiator, chat.Target) if userId == chat.Target {
go notifyActivity(d2, e2, chat.Initiator)
} else {
go notifyActivity(d1, e1, chat.Target)
}
return return
} }
@ -502,7 +528,7 @@ func (zch *NodeChatChannelsHandler[T]) DeleteChatMessage(key uint64, chatID stri
} }
for _, member := range [2]string{chat.Initiator, chat.Target} { for _, member := range [2]string{chat.Initiator, chat.Target} {
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{ d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{
"chatID": chatID, "chatId": chatID,
"messageId": key, "messageId": key,
}) })
select { select {
@ -542,7 +568,7 @@ func (zch *NodeChatChannelsHandler[T]) UpdateChatMessage(key uint64, chatID, new
} }
for _, member := range [2]string{chat.Target, chat.Initiator} { for _, member := range [2]string{chat.Target, chat.Initiator} {
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_EDITED, "node", member, map[string]interface{}{ d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_EDITED, "node", member, map[string]interface{}{
"chatID": chatID, "chatId": chatID,
"messageId": key, "messageId": key,
"newContent": newContent, "newContent": newContent,
}) })
@ -570,7 +596,7 @@ func (zch *NodeChatChannelsHandler[T]) DeleteChatFile(key uint64, fileName, chat
} }
for _, member := range [2]string{chat.Target, chat.Initiator} { for _, member := range [2]string{chat.Target, chat.Initiator} {
d, e := zch.sendDataChannelMessage(CHAT_FILE_DELETED, "node", member, map[string]interface{}{ d, e := zch.sendDataChannelMessage(CHAT_FILE_DELETED, "node", member, map[string]interface{}{
"chatID": chatID, "chatId": chatID,
"fileId": key, "fileId": key,
"fileName": fileName, "fileName": fileName,
}) })
@ -705,6 +731,24 @@ func (zch *NodeChatChannelsHandler[T]) handleDataChannel(ctx context.Context, dc
zch.ChatDataChannels[command[1]] = dc zch.ChatDataChannels[command[1]] = dc
return return
}) })
dc.DataChannel.OnOpen(func() {
bs, err := json.Marshal(map[string]any{
"type": "chat_init",
"from": NodeID,
"to": command[1],
"payload": map[string]any{
"chatId": zch.ChatID,
},
})
if err != nil {
fmt.Println(err)
}
_ = dc.DataChannel.SendText(string(bs))
})
dc.DataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
fmt.Println("received message there")
fmt.Println(msg.Data)
})
dc.DataChannel.OnClose(func() { dc.DataChannel.OnClose(func() {
fmt.Println("closing gratefully chat dc...") fmt.Println("closing gratefully chat dc...")
_ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) { _ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) {
@ -723,8 +767,65 @@ func (zch *NodeChatChannelsHandler[T]) handleDataChannel(ctx context.Context, dc
return return
} }
func (zch *NodeChatChannelsHandler[T]) signalUnreadCount(ctx context.Context, userId string, count uint) (err error) {
fmt.Println("this function has been called")
em := NewEncryptionManager()
sig := em.SignRequestHMAC(NodeID)
body, err := json.Marshal(map[string]interface{}{
"type": UPDATE_UNREAD_COUNT,
"mac": sig,
"from": NodeID,
"peerType": "node",
"payload": map[string]string{
"chatId": zch.ChatID,
"userId": userId,
"count": fmt.Sprintf("%d", count),
},
})
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 inn chat manager")
return
}
return
}
func (zch *NodeChatChannelsHandler[T]) signalLastInteractionTime(ctx context.Context, dateTime string) (err error) {
fmt.Println("this function has been called too")
em := NewEncryptionManager()
sig := em.SignRequestHMAC(NodeID)
body, err := json.Marshal(map[string]interface{}{
"type": UPDATE_LAST_INTERACTION_TIME,
"mac": sig,
"from": NodeID,
"peerType": "node",
"payload": map[string]string{
"chatId": zch.ChatID,
"lastInteractionTime": dateTime,
},
})
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
}
_, err = io.ReadAll(res.Body)
if err != nil {
return
}
return
}
func (zch *NodeChatChannelsHandler[T]) handleChatRequest(ctx context.Context, req *ChatRequest) (err error) { func (zch *NodeChatChannelsHandler[T]) handleChatRequest(ctx context.Context, req *ChatRequest) (err error) {
logger.Println("got request in zone chat handler", req) logger.Println("got request in zone chat handler", req)
fmt.Println(req)
switch req.ReqType { switch req.ReqType {
case LEAVE_CHAT: case LEAVE_CHAT:
if err = VerifyFieldsString(req.Payload, "userId"); err != nil { if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
@ -732,46 +833,46 @@ func (zch *NodeChatChannelsHandler[T]) handleChatRequest(ctx context.Context, re
} }
// err = zch.LeaveChatFSInstance(req.Payload["userId"].(string)) // err = zch.LeaveChatFSInstance(req.Payload["userId"].(string))
case DELETE_CHAT: case DELETE_CHAT:
if err = VerifyFieldsString(req.Payload, "chatID"); err != nil { if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
return return
} }
err = zch.DeleteChat(req.Payload["chatID"].(string)) err = zch.DeleteChat(req.Payload["chatId"].(string))
case CREATE_CHAT: case CREATE_CHAT:
if err = VerifyFieldsString(req.Payload, "chatID", "owner", "chatType"); err != nil { if err = VerifyFieldsString(req.Payload, "chatId", "owner", "chatType"); err != nil {
return return
} }
if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil { if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil {
return return
} }
err = zch.AddNewChat(req.Payload["chatID"].(string), req.Payload["initiator"].(string), req.Payload["target"].(string), req.Payload["initiatorHostId"].(string), req.Payload["targetHostId"].(string)) 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: case GET_CHATS:
err = zch.GetChats(req.From) err = zch.GetChats(req.From)
case LIST_LATEST_CHATS: case LIST_LATEST_CHATS:
if err = VerifyFieldsString(req.Payload, "chatID"); err != nil { if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
return return
} }
if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil { if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil {
return return
} }
err = zch.ListLatestChatMessages(req.From, req.Payload["chatID"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64)) err = zch.ListLatestChatMessages(req.From, req.Payload["chatId"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64))
return return
case LIST_LATEST_FILES: case LIST_LATEST_FILES:
if err = VerifyFieldsString(req.Payload, "chatID"); err != nil { if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
return return
} }
if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil { if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil {
return return
} }
err = zch.ListLatestChatFiles(req.From, req.Payload["chatID"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64)) err = zch.ListLatestChatFiles(req.From, req.Payload["chatId"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64))
return return
case READ_LATEST_MESSAGE: case READ_LATEST_MESSAGE:
if err = VerifyFieldsString(req.Payload, "chatID"); err != nil { if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
return return
} }
err = zch.ReadLastMessage(req.From, req.Payload["chatID"].(string)) err = zch.ReadLastMessage(req.From, req.Payload["chatId"].(string))
case ADD_CHAT_MESSAGE: case ADD_CHAT_MESSAGE:
logger.Println("got request in zone chat handler", req) 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 return
} }
if err = VerifyFieldsBool(req.Payload, "isResponse"); err != nil { if err = VerifyFieldsBool(req.Payload, "isResponse"); err != nil {
@ -797,31 +898,31 @@ func (zch *NodeChatChannelsHandler[T]) handleChatRequest(ctx context.Context, re
} }
file = &f file = &f
} }
err = zch.AddChatMessage(req.From, req.Payload["chatID"].(string), req.Payload["content"].(string), req.Payload["isResponse"].(bool), parentChatId, file) err = zch.AddChatMessage(req.From, req.Payload["chatId"].(string), req.Payload["content"].(string), req.Payload["isResponse"].(bool), parentChatId, file)
case DELETE_CHAT_MESSAGE: case DELETE_CHAT_MESSAGE:
if err = VerifyFieldsString(req.Payload, "chatID"); err != nil { if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
return return
} }
if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil { if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil {
return return
} }
err = zch.DeleteChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatID"].(string)) err = zch.DeleteChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatId"].(string))
case EDIT_CHAT_MESSAGE: case EDIT_CHAT_MESSAGE:
if err = VerifyFieldsString(req.Payload, "chatID", "newContent"); err != nil { if err = VerifyFieldsString(req.Payload, "chatId", "newContent"); err != nil {
return return
} }
if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil { if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil {
return return
} }
err = zch.UpdateChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatID"].(string), req.Payload["newContent"].(string)) err = zch.UpdateChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatId"].(string), req.Payload["newContent"].(string))
case DELETE_CHAT_FILE: case DELETE_CHAT_FILE:
if err = VerifyFieldsString(req.Payload, "chatID", "fileName"); err != nil { if err = VerifyFieldsString(req.Payload, "chatId", "fileName"); err != nil {
return return
} }
if err = VerifyFieldsFloat64(req.Payload, "fileId"); err != nil { if err = VerifyFieldsFloat64(req.Payload, "fileId"); err != nil {
return return
} }
err = zch.DeleteChatFile(uint64(req.Payload["fileId"].(float64)), req.Payload["fileName"].(string), req.Payload["chatID"].(string)) err = zch.DeleteChatFile(uint64(req.Payload["fileId"].(float64)), req.Payload["fileName"].(string), req.Payload["chatId"].(string))
} }
return return
} }

133
chatNotificationsHandler.go Normal file
View File

@ -0,0 +1,133 @@
package localserver
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
)
type ChatNotificationsHandler struct {
ChatId string
Initiator string
Target string
InitiatorHost string
TargetHost string
DataChannels map[string]*DataChannel
Flag *uint32
//DB *ChatNotificationDBHandler
Publishers []<-chan *ChatRequest
reqChans []chan<- *ChatRequest
}
func NewChatNotificationsHandler(chatId, initiator, target, initiatorHost, targetHost string, dataChannels map[string]*DataChannel, flag *uint32) (chatNotificationsHandler *ChatNotificationsHandler, err error) {
// if _, dirErr := os.ReadDir(filepath.Join(dataPath, "data", "zones", zoneId, "notifications")); os.IsNotExist(dirErr) {
// dirErr := os.MkdirAll(filepath.Join(dataPath, "data", "zones", zoneId, "notifications"), 0700)
// if dirErr != nil {
// return
// }
// }
chatNotificationsHandler = &ChatNotificationsHandler{
ChatId: chatId,
Initiator: initiator,
Target: target,
InitiatorHost: initiatorHost,
TargetHost: targetHost,
DataChannels: dataChannels,
Flag: flag,
}
return
}
func (znh *ChatNotificationsHandler) Init(ctx context.Context, initiator, target, initiatorNodeID, targetNodeID string) (err error) {
//? initialization code here
return
}
func (znh *ChatNotificationsHandler) 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)
znh.reqChans = append(znh.reqChans, reqChan)
go func() {
for {
select {
case <-ctx.Done():
done <- struct{}{}
return
case req := <-publisher:
if err := znh.handleChatRequest(ctx, req); err != nil {
errCh <- err
}
}
}
}()
return
}
func (znh *ChatNotificationsHandler) ListNotifications(userId string) {}
func (znh *ChatNotificationsHandler) CreateNotification(notificationType, title, body, payload string, isPushed bool, recipients ...string) (err error) {
if isPushed {
err = znh.PushNotification(notificationType, title, body, payload, recipients...)
}
return
}
func (znh *ChatNotificationsHandler) DeleteNotification() {}
func (znh *ChatNotificationsHandler) PushNotification(notificationType, title, body, payload string, recipients ...string) (err error) {
fmt.Println("*E*Z*E*Z*Z*E*Z**E*Z**E**Z*E****ZD*Z*")
fmt.Println("this function has been called")
em := NewEncryptionManager()
sig := em.SignRequestHMAC(NodeID)
b, err := json.Marshal(map[string]interface{}{
"type": NOTIFY,
"mac": sig,
"from": NodeID,
"peerType": "node",
"payload": map[string]interface{}{
"type": notificationType,
"title": title,
"body": body,
"recipients": recipients,
"payload": payload,
},
})
if err != nil {
return
}
_, err = http.Post("https://dev.zippytal.com/req", "application/json", bytes.NewReader(b))
if err != nil {
fmt.Println("error inn notification sender", err)
logger.Println("error come from there in zone manager")
return
}
return
}
func (znh ChatNotificationsHandler) handleDataChannel(ctx context.Context, dc *DataChannel) (catched bool) {
return
}
func (znh *ChatNotificationsHandler) handleChatRequest(ctx context.Context, req *ChatRequest) (err error) {
switch req.ReqType {
case CREATE_NOTIFICATION:
if err = VerifyFieldsString(req.Payload, "type", "title", "body", "payload"); err != nil {
return
}
if err = VerifyFieldsBool(req.Payload, "isPushed"); err != nil {
return
}
if _, ok := req.Payload["recipients"]; !ok {
err = fmt.Errorf("no field recipient in payload")
return
}
if _, ok := req.Payload["recipients"].([]string); !ok {
err = fmt.Errorf(" field recipient in payload is wrong type")
return
}
err = znh.CreateNotification(req.Payload["type"].(string), req.Payload["title"].(string), req.Payload["body"].(string), req.Payload["payload"].(string), req.Payload["isPushed"].(bool), req.Payload["recipients"].([]string)...)
}
return
}

View File

@ -2,6 +2,7 @@ package localserver
import ( import (
"context" "context"
"fmt"
"sync" "sync"
"time" "time"
) )
@ -41,6 +42,8 @@ func NewChatRequestScheduler(initiator, target, initiatorNodeID, targetNodeID st
publisher := make(chan *ChatRequest, 100) publisher := make(chan *ChatRequest, 100)
chatRequestScheduler.handlersPublishers = append(chatRequestScheduler.handlersPublishers, publisher) chatRequestScheduler.handlersPublishers = append(chatRequestScheduler.handlersPublishers, publisher)
chatRequestScheduler.handlersDataChannelDispatchCallbacks = append(chatRequestScheduler.handlersDataChannelDispatchCallbacks, handler.handleDataChannel) chatRequestScheduler.handlersDataChannelDispatchCallbacks = append(chatRequestScheduler.handlersDataChannelDispatchCallbacks, handler.handleDataChannel)
fmt.Println("this is the fking handler")
fmt.Println(handler)
reqChan, done, errCh := handler.Subscribe(context.Background(), publisher) reqChan, done, errCh := handler.Subscribe(context.Background(), publisher)
go func(done <-chan struct{}, errCh <-chan error) { go func(done <-chan struct{}, errCh <-chan error) {
for { for {
@ -76,16 +79,16 @@ func NewChatRequestScheduler(initiator, target, initiatorNodeID, targetNodeID st
func (crs *ChatRequestScheduler) Schedule(reqChan <-chan *ChatRequest) (done chan struct{}, errCh chan error) { func (crs *ChatRequestScheduler) Schedule(reqChan <-chan *ChatRequest) (done chan struct{}, errCh chan error) {
done, errCh = make(chan struct{}), make(chan error) done, errCh = make(chan struct{}), make(chan error)
go func() {
for req := range reqChan { for req := range reqChan {
go func(r *ChatRequest) { go func(r *ChatRequest) {
for _, publisher := range crs.handlersPublishers { for _, publisher := range crs.handlersPublishers {
go func(p chan<- *ChatRequest) { publisher <- r
p <- r
}(publisher)
} }
}(req) }(req)
} }
done <- struct{}{}
}()
return return
} }

View File

@ -1,11 +1 @@
{ {"chatId":"general 💬","chatType":"public","owner":"Dev","members":["Dev","Loïs","Pascal","Dev2"]}
"chatId": "general 💬",
"chatType": "public",
"owner": "Dev",
"members": [
"Dev",
"Loïs",
"Pascal",
"Dev2"
]
}

View File

@ -1 +0,0 @@
<>ª—¤ÓHè±7EÃ3Hello Badger

File diff suppressed because it is too large Load Diff

BIN
cmd/node

Binary file not shown.

Binary file not shown.

View File

@ -90,6 +90,9 @@ func (em *EncryptionManager) LoadPrivKey(privKeyPath string, password string) (e
} }
if password != "" { if password != "" {
privePemBytes, err = x509.DecryptPEMBlock(privPem, []byte(password)) privePemBytes, err = x509.DecryptPEMBlock(privPem, []byte(password))
if err != nil {
return
}
} else { } else {
privePemBytes = privPem.Bytes privePemBytes = privPem.Bytes
} }

View File

@ -82,32 +82,34 @@ var dataPath = ""
func NewLocalServer(grpcAddr uint, appDataPath, id, token string) (localServer *LocalServer, err error) { func NewLocalServer(grpcAddr uint, appDataPath, id, token string) (localServer *LocalServer, err error) {
dataPath = appDataPath dataPath = appDataPath
webRTCCallManager, err := NewWebRTCCallManager(id, token, NewWebrtcCallSoundManager(), NewWebrtcCallChatManager(), NewWebrtcCallVideoManager(), NewWebrtcCallFileManager())
if err != nil {
err = fmt.Errorf("error from call manager")
return
}
zoneManager, err := NewZoneManager(id, token) zoneManager, err := NewZoneManager(id, token)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
err = fmt.Errorf("error from zone manager")
return return
} }
// webrtcFsManager, err := NewWebrtcFsManager(NewP2PFSDatachannelManager()) chatManager, err := NewChatManager(id, token)
// if err != nil { if err != nil {
// err = fmt.Errorf("error from fs manager") fmt.Println(err)
// return return
// } }
webrtcGrpcMiddleware := NewWebRTCGrpcMiddleware(webRTCCallManager) squadManager, err := NewSquadManager(id, token)
ZoneGrpcMiddleware := NewZoneGrpcMiddleware(zoneManager) if err != nil {
signalingClientManager, err := NewGrpcClientManager(grpcAddr, id, webrtcGrpcMiddleware, ZoneGrpcMiddleware) fmt.Println(err)
return
}
zoneGrpcMiddleware := NewZoneGrpcMiddleware(zoneManager)
chatGrpcMiddleware := NewChatGrpcMiddleware(chatManager)
squadGrpcMiddleware := NewSquadGrpcMiddleware(squadManager)
signalingClientManager, err := NewGrpcClientManager(grpcAddr, id, zoneGrpcMiddleware, chatGrpcMiddleware, squadGrpcMiddleware)
if err != nil { if err != nil {
return return
} }
webrtcGrpcMiddleware.stream = signalingClientManager.GrpcLinkClient
webRTCCallManager.stream = signalingClientManager.GrpcLinkClient
zoneManager.stream = signalingClientManager.GrpcLinkClient zoneManager.stream = signalingClientManager.GrpcLinkClient
ZoneGrpcMiddleware.stream = signalingClientManager.GrpcLinkClient zoneGrpcMiddleware.stream = signalingClientManager.GrpcLinkClient
chatManager.stream = signalingClientManager.GrpcLinkClient
chatGrpcMiddleware.stream = signalingClientManager.GrpcLinkClient
squadManager.stream = signalingClientManager.GrpcLinkClient
squadGrpcMiddleware.stream = signalingClientManager.GrpcLinkClient
localServer = &LocalServer{ localServer = &LocalServer{
ID: id, ID: id,
GrpcClientManager: signalingClientManager, GrpcClientManager: signalingClientManager,

323
squadChatDBHandler.go Normal file
View File

@ -0,0 +1,323 @@
package localserver
import (
"encoding/binary"
"encoding/json"
"os"
"path/filepath"
"sync"
"github.com/dgraph-io/badger/v3"
)
type SquadChatDBHandler struct {
SquadID string
PreviousId uint64
ItemCount uint64
db func(func(*badger.DB) (err error)) (err error)
lock *sync.RWMutex
}
func NewSquadChatDBHandler(squadId string) (squadChatDBHandler *SquadChatDBHandler, err error) {
squadChatDBHandler = &SquadChatDBHandler{
db: func(f func(*badger.DB) (err error)) (err error) {
db, err := badger.Open(badger.DefaultOptions(filepath.Join(dataPath, "data", "squads", squadId, "chat")).WithLogger(dbLogger))
if err != nil {
return
}
defer db.Close()
err = f(db)
return
},
SquadID: squadId,
lock: new(sync.RWMutex),
}
err = squadChatDBHandler.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
}
}
squadChatDBHandler.PreviousId = counter
squadChatDBHandler.ItemCount = itemCounter
return nil
})
return
})
return
}
func (zcdbh *SquadChatDBHandler) calculateNewChatCount(previousId uint64) (count uint, err error) {
err = zcdbh.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 (zcdbh *SquadChatDBHandler) revertPreviousId() (err error) {
err = zcdbh.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
}
zcdbh.PreviousId = chatMessage.ID
return
}); err != nil {
return err
}
} else {
zcdbh.PreviousId = 1
}
return nil
})
return
})
return
}
func (zcdbh *SquadChatDBHandler) AddNewChatMessage(chatMessage *ChatMessage) (err error) {
b := make([]byte, bufferSize)
zcdbh.PreviousId++
binary.BigEndian.PutUint64(b, zcdbh.PreviousId)
chatMessage.ID = zcdbh.PreviousId
bs, err := json.Marshal(chatMessage)
if err != nil {
return
}
if err = zcdbh.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 (zcdbh *SquadChatDBHandler) DeleteChatMessage(key uint64) (err error) {
if err = zcdbh.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
}
zcdbh.ItemCount--
return
})
return
}); err != nil {
return
}
return
}
func (zcdbh *SquadChatDBHandler) DeleteChatFile(filename string, key uint64) (err error) {
if err = zcdbh.DeleteChatMessage(key); err != nil {
return
}
err = os.Remove(filepath.Join(dataPath, "data", "squads", zcdbh.SquadID, "chat", "__files__", filename))
return
}
func (zcdbh *SquadChatDBHandler) ListChatMessages(lastIndex int, limit int) (chatMessages []*ChatMessage, l int, done bool, err error) {
err = zcdbh.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(zcdbh.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 (zcdbh *SquadChatDBHandler) ListChatFiles(lastIndex int, limit int) (chatMessages []*ChatFile, l int, err error) {
err = zcdbh.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(zcdbh.PreviousId))
li = int(zcdbh.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 zcdbh.PreviousId > uint64(x) {
l = int(zcdbh.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(zcdbh.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 (zcdbh *SquadChatDBHandler) GetChatMessage(index uint64) (chatMessage *ChatMessage, err error) {
err = zcdbh.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 (zcdbh *SquadChatDBHandler) ModifyChatMessage(key uint64, newContent string) (err error) {
b := make([]byte, bufferSize)
binary.BigEndian.PutUint64(b, key)
chatMessage, err := zcdbh.GetChatMessage(key)
if err != nil {
return
}
chatMessage.Content = newContent
bs, err := json.Marshal(chatMessage)
if err != nil {
return
}
if err = zcdbh.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
}

178
squadChatFSInstance.go Normal file
View File

@ -0,0 +1,178 @@
package localserver
import (
"bufio"
"fmt"
"io"
"os"
"path/filepath"
"github.com/pion/webrtc/v3"
)
const (
SQUAD_CHAT_WEBRTC_OFFER ReqType = "squad_chat_offer"
SQUAD_CHAT_WEBRTC_ANSWER ReqType = "squad_chat_answer"
SQUAD_CHAT_WEBRTC_RENNEGOTIATION_OFFER ReqType = "squad_chat_rennegotiation_offer"
SQUAD_CHAT_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "squad_chat_rennegotiation_answer"
SQUAD_CHAT_WEBRTC_COUNTER_OFFER ReqType = "squad_chat_webrtc_counter_offer"
SQUAD_CHAT_WEBRTC_CANDIDATE ReqType = "squad_chat_webrtc_candidate"
)
type SquadFSInstance struct {
SquadID string `json:"id"`
Owner string `json:"owner"`
Members []string `json:"members"`
OpenFiles map[string]*os.File `json:"-"`
OpenFilesForUser map[string][]string `json:"-"`
filesFlag *uint32 `json:"-"`
}
func NewSquadFSInstance(id, owner string, members []string) (squadFSInstance *SquadFSInstance) {
filesFlag := uint32(0)
squadFSInstance = &SquadFSInstance{
SquadID: id,
Owner: owner,
Members: members,
OpenFiles: make(map[string]*os.File),
OpenFilesForUser: make(map[string][]string),
filesFlag: &filesFlag,
}
return
}
func (fs *SquadFSInstance) SetupFileUpload(chatId, filename, userId string, dc *webrtc.DataChannel) (writePipe chan []byte, err error) {
concretePath := filepath.Join(dataPath, "data", "squads", fs.SquadID, "chat", "__files__", filename)
if _, err = os.ReadDir(filepath.Join(dataPath, "data", "squads", fs.SquadID, "chat")); err != nil {
return
}
if _, rErr := os.ReadDir(filepath.Join(dataPath, "data", "squads", fs.SquadID, "chat", "__files__")); os.IsNotExist(rErr) {
if err = os.MkdirAll(filepath.Join(dataPath, "data", "squads", fs.SquadID, "chat", "__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 *SquadFSInstance) FileUploadFailed(chatId, filename, userId string) (err error) {
concretePath := filepath.Join(dataPath, "data", "squads", fs.SquadID, "chat", "__files__", filename)
err = os.Remove(concretePath)
return
}
func (fs *SquadFSInstance) SetupFileDownload(chatId, filename, userId string, dc *webrtc.DataChannel) (err error) {
concretePath := filepath.Join(dataPath, "data", "squads", fs.SquadID, "chat", "__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 *SquadFSInstance) 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 *SquadFSInstance) HandleDataChannelEvents(from, eventId string, payload map[string]interface{}) (err error) {
switch eventId {
}
return
}

750
squadChatHandler.go Normal file
View File

@ -0,0 +1,750 @@
package localserver
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/dgraph-io/badger/v3"
"github.com/pion/webrtc/v3"
)
type SquadChatConfig struct {
ChatId string `json:"chatId"`
ChatType string `json:"chatType"`
Owner string `json:"owner"`
Members []string `json:"members"`
}
type SquadChat struct {
ChatId string `json:"chatId"`
ChatType string `json:"chatType"`
Owner string `json:"owner"`
Members []string `json:"members"`
LastReadIndex uint `json:"lastReadIndex"`
Unread uint `json:"unread"`
DB *SquadChatDBHandler `json:"-"`
Tracking *SquadChatTrackingDB `json:"-"`
}
type SquadChatsHandler[T ZippytalFSInstance] struct {
SquadName string
SquadId string
HostId string
ChatFSInstance T
SquadMembersId []string
DataChannels map[string]*DataChannel
ChatDataChannels map[string]*DataChannel
ChatDataChannelsFlag *uint32
Flag *uint32
ChatFSInstanceFlag *uint32
ChatFlag *uint32
Chat *SquadChat
reqChans []chan<- *SquadRequest
init bool
}
func NewSquadChatsHandler(hostId, squadId, squadName, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, flag *uint32) (squadChatsHandler *SquadChatsHandler[*SquadFSInstance], err error) {
_, err = os.ReadDir(filepath.Join(dataPath, "data", "squads", squadId, "chat"))
if err != nil {
if os.IsNotExist(err) {
logger.Printf("creating chat directory for squad %s...\n", squadId)
mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "squads", squadId, "chat"), 0700)
if mkdirErr != nil {
return nil, mkdirErr
}
file, ferr := os.Create(filepath.Join(dataPath, "data", "squads", squadId, "chat", "chatConfig.json"))
if ferr != nil {
return nil, ferr
}
baseConfig := ChatConfig{
ChatId: squadName,
Owner: owner,
ChatType: "public",
Members: authorizedMembers,
}
bs, jsonErr := json.Marshal(baseConfig)
if jsonErr != nil {
return nil, jsonErr
}
if _, writeErr := file.WriteString(string(bs)); writeErr != nil {
return nil, writeErr
}
_ = file.Close()
_, err = os.ReadDir(filepath.Join(dataPath, "data", "squads", squadId, "chat"))
if err != nil {
return nil, err
}
} else {
return
}
}
squadChatDBHandler, err := NewSquadChatDBHandler(squadId)
if err != nil {
return nil, err
}
var bs []byte
bs, err = os.ReadFile(filepath.Join(dataPath, "data", "squads", squadId, "chat", "chatConfig.json"))
if err != nil {
return nil, err
}
logger.Println(string(bs))
var c SquadChat
if err = json.Unmarshal(bs, &c); err != nil {
return nil, err
}
squadChatTracking, err := NewSquadChatTracking(squadId, c.ChatId, c.Members...)
if err != nil {
return nil, err
}
logger.Println("chats data :", c.ChatId, c.ChatType, c.Owner, c.Members)
c.DB = squadChatDBHandler
c.Tracking = squadChatTracking
chatFlag := uint32(0)
chatFSFlag := uint32(0)
chatDCFlag := uint32(0)
squadChatsHandler = &SquadChatsHandler[*SquadFSInstance]{
HostId: hostId,
SquadName: squadName,
ChatFSInstance: NewSquadFSInstance(squadId, owner, authorizedMembers),
ChatFSInstanceFlag: &chatFSFlag,
SquadId: squadId,
SquadMembersId: authorizedMembers,
DataChannels: dataChannels,
ChatDataChannels: make(map[string]*DataChannel),
ChatDataChannelsFlag: &chatDCFlag,
Flag: flag,
Chat: &c,
ChatFlag: &chatFlag,
init: false,
}
return
}
func (sch *SquadChatsHandler[T]) sendSquadRequest(reqType string, from string, payload map[string]interface{}) {
go func() {
for _, rc := range sch.reqChans {
rc <- &SquadRequest{
ReqType: reqType,
From: from,
Payload: payload,
}
}
}()
}
func (sch *SquadChatsHandler[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(sch.ChatDataChannelsFlag, func() (err error) {
if _, ok := sch.ChatDataChannels[to]; ok {
bs, jsonErr := json.Marshal(&SquadResponse{
Type: reqType,
From: from,
To: to,
Payload: payload,
})
if jsonErr != nil {
return jsonErr
}
err = sch.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 (sch *SquadChatsHandler[T]) Init(ctx context.Context, authorizedMembers []string) (err error) {
sch.init = true
return
}
func (sch *SquadChatsHandler[T]) Subscribe(ctx context.Context, publisher <-chan *SquadRequest) (reqChan chan *SquadRequest, done chan struct{}, errCh chan error) {
reqChan, done, errCh = make(chan *SquadRequest), make(chan struct{}), make(chan error)
sch.reqChans = append(sch.reqChans, reqChan)
go func() {
for {
select {
case <-ctx.Done():
done <- struct{}{}
return
case req := <-publisher:
if err := sch.handleSquadRequest(ctx, req); err != nil {
errCh <- err
}
}
}
}()
return
}
func (sch *SquadChatsHandler[T]) ReadLastMessage(userId, chatId string) (err error) {
err = atomicallyExecute(sch.ChatFlag, func() (err error) {
err = sch.Chat.Tracking.SetUserLastIndex(userId, sch.Chat.DB.PreviousId)
return
})
return
}
func (sch *SquadChatsHandler[T]) ListLatestChatMessages(userId, chatId string, lastIndex, limit float64) (err error) {
var list []*ChatMessage
var i int
var done bool
if err = atomicallyExecute(sch.ChatFlag, func() (err error) {
list, i, done, err = sch.Chat.DB.ListChatMessages(int(lastIndex), int(limit))
if err != nil {
return
}
err = sch.Chat.Tracking.SetUserLastIndex(userId, sch.Chat.DB.PreviousId)
return
}); err != nil {
return
}
success, e := sch.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 (sch *SquadChatsHandler[T]) ListLatestChatFiles(userId, chatId string, lastIndex, limit float64) (err error) {
var list []*ChatFile
var i int
if err = atomicallyExecute(sch.ChatFlag, func() (err error) {
list, i, err = sch.Chat.DB.ListChatFiles(int(lastIndex), int(limit))
return
}); err != nil {
return
}
done, e := sch.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 (sch *SquadChatsHandler[T]) AddChatMessage(userId, chatId, content string, isResponse bool, chatResponseId uint64, file *ChatFile) (err error) {
chatMessage := &ChatMessage{
Content: content,
From: userId,
IsResponse: isResponse,
ResponseOf: nil,
File: file,
Tags: make([]string, 0),
Date: time.Now().Format(time.RFC3339),
}
if err = atomicallyExecute(sch.ChatFlag, func() (err error) {
if isResponse {
parentMessage, getErr := sch.Chat.DB.GetChatMessage(chatResponseId)
if err != nil {
if getErr != badger.ErrKeyNotFound {
return getErr
}
}
chatMessage.ResponseOf = parentMessage
}
if err = sch.Chat.DB.AddNewChatMessage(chatMessage); err != nil {
return
}
chatMessage.ID = sch.Chat.DB.PreviousId
return
}); err != nil {
return
}
notifyActivity := func(done <-chan struct{}, e <-chan error, member string) {
select {
case <-done:
case <-e:
chat := sch.Chat
_ = atomicallyExecute(sch.ChatFlag, func() (err error) {
li, err := chat.Tracking.GetUserLastIndex(member)
if err != nil {
return err
}
count, err := chat.DB.calculateNewChatCount(uint64(li))
if err != nil {
return err
}
if count == 1 {
bs, err := json.Marshal(map[string]any{
"squadId": sch.SquadId,
"chatId": chatId,
"squadHost": NodeID,
})
if err != nil {
return err
}
sch.sendSquadRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{
"type": "new_chat_activity",
"title": "Unread messages 👀",
"body": fmt.Sprintf("New messages in %s", sch.SquadName),
"isPushed": true,
"payload": string(bs),
"recipients": []string{member},
})
}
return
})
}
}
for _, v := range sch.SquadMembersId {
done, e := sch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", v, map[string]interface{}{
"chatMessage": chatMessage,
"chatId": chatId,
})
go notifyActivity(done, e, v)
}
return
}
// func (sch *SquadChatsHandler[T]) ConnectToChatFSInstance(channelId string, userId string, sdp string) (err error) {
// err = atomicallyExecute(sch.ChatFSInstanceFlag, func() (err error) {
// d, e := sch.ChatFSInstance.HandleOffer(context.Background(), channelId, userId, sdp, sch.HostId, sch.sendDataChannelMessage, sch.signalCandidate)
// select {
// case <-d:
// case <-e:
// }
// return
// })
// return
// }
// func (sch *SquadChatsHandler[T]) LeaveChatFSInstance(
// userId string) (err error) {
// sch.ChatFSInstance.HandleLeavingMember(userId)
// return
// }
func (sch *SquadChatsHandler[T]) DeleteChatMessage(key uint64, chatId string) (err error) {
err = atomicallyExecute(sch.ChatFlag, func() (err error) {
chat := sch.Chat
if err = chat.DB.DeleteChatMessage(key); err != nil {
return
}
if chat.ChatType == PRIVATE {
for _, member := range chat.Members {
d, e := sch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{
"chatId": chatId,
"messageId": key,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
} else {
for _, member := range sch.SquadMembersId {
d, e := sch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{
"chatId": chatId,
"messageId": key,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
}
previousId := chat.DB.PreviousId
if previousId == key {
if err = chat.DB.revertPreviousId(); err != nil {
return
}
previousId = chat.DB.PreviousId
if previousId == 1 {
previousId = 0
}
if err = chat.Tracking.RevertTrackingLastIndex(previousId); err != nil {
return
}
}
return
})
return
}
func (sch *SquadChatsHandler[T]) UpdateChatMessage(key uint64, chatId, newContent string) (err error) {
err = atomicallyExecute(sch.ChatFlag, func() (err error) {
chat := sch.Chat
if err = chat.DB.ModifyChatMessage(key, newContent); err != nil {
return
}
if chat.ChatType == PRIVATE {
for _, member := range chat.Members {
d, e := sch.sendDataChannelMessage(CHAT_MESSAGE_EDITED, "node", member, map[string]interface{}{
"chatId": chatId,
"messageId": key,
"newContent": newContent,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
} else {
for _, member := range sch.SquadMembersId {
d, e := sch.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 (sch *SquadChatsHandler[T]) DeleteChatFile(key uint64, fileName, chatId string) (err error) {
err = atomicallyExecute(sch.ChatFlag, func() (err error) {
chat := sch.Chat
if err = chat.DB.DeleteChatFile(fileName, key); err != nil {
return
}
if chat.ChatType == PRIVATE {
for _, member := range chat.Members {
d, e := sch.sendDataChannelMessage(CHAT_FILE_DELETED, "node", member, map[string]interface{}{
"chatId": chatId,
"fileId": key,
"fileName": fileName,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
} else {
for _, member := range sch.SquadMembersId {
d, e := sch.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 (sch *SquadChatsHandler[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 := sch.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 := sch.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 = sch.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 (sch *SquadChatsHandler[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 := sch.ChatFSInstance.FileDownloadFailed(chatId, filename, userId); fdf != nil {
logger.Println(fdf)
}
}
})
dc.DataChannel.OnClose(func() {
if !done {
logger.Println("abort...")
if fdf := sch.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 = sch.ChatFSInstance.SetupFileDownload(chatId, filename, userId, dc.DataChannel); initErr != nil {
fmt.Println("uwuwuwuwuwuwuwuwu")
fmt.Println(initErr)
_ = 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 (sch *SquadChatsHandler[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 sch.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 sch.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(sch.ChatDataChannelsFlag, func() (err error) {
sch.ChatDataChannels[command[1]] = dc
return
})
dc.DataChannel.OnOpen(func() {
fmt.Println("datachann in squad chat fking created")
bs, err := json.Marshal(map[string]any{
"type": "chat_init",
"from": NodeID,
"to": command[1],
"payload": map[string]any{
"chatId": sch.SquadId,
},
})
if err != nil {
fmt.Println(err)
}
_ = dc.DataChannel.SendText(string(bs))
})
dc.DataChannel.OnClose(func() {
fmt.Println("closing gratefully chat dc...")
_ = atomicallyExecute(sch.ChatDataChannelsFlag, func() (err error) {
delete(sch.ChatDataChannels, command[1])
return
})
})
dc.DataChannel.OnError(func(err error) {
fmt.Println("error in chat dc...")
_ = atomicallyExecute(sch.ChatDataChannelsFlag, func() (err error) {
delete(sch.ChatDataChannels, command[1])
return
})
})
}
return
}
func (sch *SquadChatsHandler[T]) handleSquadRequest(ctx context.Context, req *SquadRequest) (err error) {
logger.Println("got request in squad chat handler", req)
switch req.ReqType {
case LEAVE_ZONE:
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
return
}
// err = sch.LeaveChatFSInstance(req.Payload["userId"].(string))
case string(NEW_AUTHORIZED_SQUAD_MEMBER):
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
return
}
var contain bool
for _, m := range sch.SquadMembersId {
if m == req.Payload["userId"].(string) {
contain = true
break
}
}
if !contain {
sch.SquadMembersId = append(sch.SquadMembersId, req.Payload["userId"].(string))
}
return
case string(REMOVED_SQUAD_AUTHORIZED_MEMBER):
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
return
}
var index int
var found bool
for i, m := range sch.SquadMembersId {
if m == req.Payload["userId"].(string) {
index = i
found = true
break
}
}
if !found {
err = fmt.Errorf("no such user in zone")
return
}
sch.SquadMembersId = append(sch.SquadMembersId[:index], sch.SquadMembersId[index+1:]...)
return
case LIST_LATEST_CHATS:
if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
return
}
if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil {
return
}
err = sch.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 = sch.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 = sch.ReadLastMessage(req.From, req.Payload["chatId"].(string))
case ADD_CHAT_MESSAGE:
logger.Println("got request in squad 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 = sch.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 = sch.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 = sch.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 = sch.DeleteChatFile(uint64(req.Payload["fileId"].(float64)), req.Payload["fileName"].(string), req.Payload["chatId"].(string))
}
return
}

151
squadChatTracking.go Normal file
View File

@ -0,0 +1,151 @@
package localserver
import (
"encoding/binary"
"os"
"path/filepath"
"sync"
"github.com/dgraph-io/badger/v3"
)
type SquadChatTrackingDB struct {
SquadID string
db func(cb func(*badger.DB) (err error)) (err error)
lock *sync.RWMutex
}
func NewSquadChatTracking(squadId, chatId string, members ...string) (*SquadChatTrackingDB, error) {
if _, dirErr := os.ReadDir(filepath.Join(dataPath, "data", "squads", squadId, "chat", "__tracking__")); os.IsNotExist(dirErr) {
_ = os.MkdirAll(filepath.Join(dataPath, "data", "squads", squadId, "chat", "__tracking__"), 0700)
}
db := func(f func(*badger.DB) (err error)) (err error) {
db, err := badger.Open(badger.DefaultOptions(filepath.Join(dataPath, "data", "squads", squadId, "chat", "__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 members {
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 &SquadChatTrackingDB{squadId, db, lock}, nil
}
func (zctdb *SquadChatTrackingDB) Initialize(lastIndex uint64, users ...string) (err error) {
for _, user := range users {
if err = zctdb.SetUserLastIndex(user, lastIndex); err != nil {
return
}
}
return
}
func (zctdb *SquadChatTrackingDB) RevertTrackingLastIndex(lastIndex uint64) (err error) {
zctdb.lock.Lock()
defer zctdb.lock.Unlock()
err = zctdb.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 (zctdb *SquadChatTrackingDB) SetUserLastIndex(userId string, lastIndex uint64) (err error) {
zctdb.lock.Lock()
defer zctdb.lock.Unlock()
err = zctdb.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 (zctdb *SquadChatTrackingDB) GetUserLastIndex(userId string) (index uint, err error) {
zctdb.lock.Lock()
defer zctdb.lock.Unlock()
err = zctdb.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 (zctdb *SquadChatTrackingDB) DeleteUserTracking(userId string) (err error) {
zctdb.lock.Lock()
defer zctdb.lock.Unlock()
err = zctdb.db(func(d *badger.DB) (err error) {
err = d.Update(func(txn *badger.Txn) error {
updateErr := txn.Delete([]byte(userId))
return updateErr
})
return
})
return
}

238
squadGrpcMiddleware.go Normal file
View File

@ -0,0 +1,238 @@
package localserver
import (
"context"
"encoding/json"
"fmt"
"strconv"
"github.com/pion/webrtc/v3"
)
const (
HOSTED_SQUAD_OFFER ReqType = "hosted_squad_offer"
HOSTED_SQUAD_ANSWER ReqType = "hosted_squad_answer"
HOSTED_SQUAD_COUNTER_OFFER ReqType = "hosted_squad_counter_offer"
JOIN_SQUAD ReqType = "join_hosted_squad"
HOSTED_SQUAD_ACCESS_DENIED ReqType = "hosted_squad_hosted_squad_access_denied"
QUIT_SQUAD ReqType = "hosted_squad_stop_call"
HOSTED_SQUAD_ACCESS_GRANTED ReqType = "hosted_squad_access_granted"
INCOMING_HOSTED_SQUAD_MEMBER ReqType = "incoming_hosted_squad_member"
LEAVING_HOSTED_SQUAD_MEMBER ReqType = "leaving_hosted_squad_member"
HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER ReqType = "hosted_squad_rennegotiation_offer"
HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "hosted_squad_rennegotiation_answer"
HOSTED_SQUAD_WEBRTC_CANDIDATE ReqType = "hosted_squad_webrtc_candidate"
NEW_SQUAD ReqType = "new_hosted_squad"
NEW_AUTHORIZED_SQUAD_MEMBER = "new_authorized_squad_member"
REMOVED_SQUAD_AUTHORIZED_MEMBER = "removed_squad_authorized_member"
DELETE_SQUAD = "delete_hosted_squad"
DISCONNECT_SQUAD_MEMBER = "disconnect_squad_member"
)
type SquadGrpcMiddleware struct {
Manager *SquadManager
stream SignalingService_LinkClient
}
func NewSquadGrpcMiddleware(manager *SquadManager) (squadGrpcMiddleware *SquadGrpcMiddleware) {
squadGrpcMiddleware = &SquadGrpcMiddleware{
Manager: manager,
}
return
}
func (zm *SquadGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) {
bs, err := json.Marshal(map[string]string{
"from": zm.Manager.ID,
"to": to,
"candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid,
"sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
})
if err != nil {
return
}
err = zm.stream.Send(&SignalingMessage{
Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE),
From: zm.Manager.ID,
To: to,
Payload: bs,
})
return
}
func (zm *SquadGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) {
done, errCh := make(chan struct{}), make(chan error)
go func() {
defer func() {
done <- struct{}{}
}()
var payload map[string]string
if e := json.Unmarshal(req.Payload, &payload); e != nil {
errCh <- e
return
}
switch req.Type {
case string(INCOMING_HOSTED_SQUAD_MEMBER):
case string(DELETE_SQUAD):
if err := validateRequest(payload, "squadId"); err != nil {
return
}
if err := zm.Manager.DeleteSquad(payload["squadId"]); err != nil {
errCh <- err
return
}
case string(LEAVING_HOSTED_SQUAD_MEMBER):
if err := validateRequest(payload, "squadId"); err != nil {
return
}
if err := zm.Manager.HandleLeavingMember(req.GetFrom(), payload["squadId"], false); err != nil {
errCh <- err
return
}
case string(REMOVED_SQUAD_AUTHORIZED_MEMBER):
fmt.Println("got there biatch")
if err := validateRequest(payload, "squadId", "userId"); err != nil {
return
}
reqChan := make(chan *SquadRequest)
d, e := zm.Manager.Squads[payload["squadId"]].SquadRequestScheduler.Schedule(reqChan)
go func() {
defer close(reqChan)
reqChan <- &SquadRequest{
ReqType: string(REMOVED_SQUAD_AUTHORIZED_MEMBER),
From: payload["userId"],
Payload: map[string]interface{}{
"userId": payload["userId"],
},
}
}()
select {
case <-d:
fmt.Println("uwuwuwuwuwuwuw donnnnee")
case ee := <-e:
fmt.Println("uuuuuuwuuuuuu", ee)
}
return
case string(NEW_AUTHORIZED_SQUAD_MEMBER):
if err := validateRequest(payload, "squadId", "userId"); err != nil {
return
}
reqChan := make(chan *SquadRequest)
d, e := zm.Manager.Squads[payload["squadId"]].SquadRequestScheduler.Schedule(reqChan)
go func() {
defer close(reqChan)
reqChan <- &SquadRequest{
ReqType: string(NEW_AUTHORIZED_SQUAD_MEMBER),
From: payload["userId"],
Payload: map[string]interface{}{
"userId": payload["userId"],
},
}
}()
select {
case <-d:
fmt.Println("uwuwuwuwuwuwuw donnnnee")
case ee := <-e:
fmt.Println("uuuuuuwuuuuuu", ee)
}
return
case string(NEW_SQUAD):
logger.Println(payload)
if err := validateRequest(payload, "squadId", "squadName", "squadImageURL", "squadOwner", "squadCreationDate"); err != nil {
errCh <- err
return
}
squad, err := NewSquad(zm.Manager.ID, payload["squadId"], payload["squadName"], payload["squadImageURL"], payload["squadOwner"], payload["squadCreationDate"], true, []string{payload["squadOwner"]})
if err != nil {
errCh <- err
return
}
_ = atomicallyExecute(zm.Manager.squadFlag, func() (err error) {
zm.Manager.Squads[squad.ID] = squad
return
})
case string(HOSTED_SQUAD_OFFER):
if err := validateRequest(payload, SDP); err != nil {
errCh <- err
return
}
if err := zm.Manager.HandleOffer(ctx, req.GetFrom(), req.GetTo(), payload, zm.signalCandidate); err != nil {
errCh <- err
return
}
case string(HOSTED_SQUAD_ANSWER):
if err := validateRequest(payload, SDP); err != nil {
errCh <- err
return
}
if err := zm.Manager.HandleAnswer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
errCh <- err
return
}
case string(HOSTED_SQUAD_COUNTER_OFFER):
if err := zm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
errCh <- err
return
}
case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER):
logger.Println("received negotiation offer")
if err := validateRequest(payload, SDP); err != nil {
errCh <- err
return
}
if err := zm.Manager.HandleRennegotiationOffer(req.GetFrom(), payload[SDP]); err != nil {
errCh <- err
return
}
case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER):
logger.Println("received negotiation answer")
if err := validateRequest(payload, SDP); err != nil {
errCh <- err
return
}
if err := zm.Manager.HandleRennegotiationAnswer(req.GetFrom(), payload[SDP]); err != nil {
errCh <- err
return
}
case string(HOSTED_SQUAD_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 := zm.Manager.AddCandidate(&webrtc.ICECandidateInit{
Candidate: payload["candidate"],
SDPMid: &sdpMid,
SDPMLineIndex: &SDPMLineIndex,
}, req.GetFrom()); err != nil {
errCh <- err
return
}
default:
logger.Println("no request for squad grpc middleware")
logger.Println(payload)
logger.Println(req.Type)
}
}()
select {
case <-ctx.Done():
return ctx.Err()
case err = <-errCh:
return
case <-done:
return
}
}

742
squadManager.go Normal file
View File

@ -0,0 +1,742 @@
package localserver
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
sync "sync"
"github.com/google/uuid"
"github.com/pion/webrtc/v3"
)
type SquadManager struct {
ID string
Squads map[string]*Squad
LocalSD map[string]*webrtc.SessionDescription
RTCPeerConnections map[string]*RTCPeerConnection
DataChannels map[string]*DataChannel
PendingCandidates map[string][]*webrtc.ICECandidate
stream SignalingService_LinkClient
squadFlag *uint32
peerConnectionFlag *uint32
localSDFlag *uint32
dataChannelFlag *uint32
candidateFlag *uint32
}
type Squad struct {
ID string
Name string
ImageURL string
Owner string
CreationDate string
AuthorizedMembers []string
DataChannels map[string]*DataChannel
DataChannelsFlag *uint32
SquadRequestScheduler *SquadRequestScheduler
Initialized bool
}
func NewSquad(hostId string, squadId string, squadName string, imageUrl string, owner string, creationDate string, initialized bool, authorizedMembers []string) (squad *Squad, err error) {
dataChannels, dataChannelFlag := make(map[string]*DataChannel), uint32(0)
squadChatHandler, err := NewSquadChatsHandler(hostId, squadId, squadName, owner, authorizedMembers, dataChannels, &dataChannelFlag)
if err != nil {
return nil, err
}
squadVideoChannelsHandler, err := NewSquadVideoChannelsHandler(hostId, squadId, owner, authorizedMembers, dataChannels, &dataChannelFlag)
if err != nil {
return
}
squadNotificationsHandler, err := NewSquadNotificationsHandler(hostId, squadId, owner, authorizedMembers, dataChannels, &dataChannelFlag)
if err != nil {
return
}
squadScheduler, e := NewSquadRequestScheduler(authorizedMembers, squadVideoChannelsHandler, squadChatHandler, squadNotificationsHandler)
go func() {
for schedErr := range e {
logger.Println("from scheduler :", schedErr)
}
}()
squad = &Squad{
ID: squadId,
Name: squadName,
ImageURL: imageUrl,
Owner: owner,
CreationDate: creationDate,
Initialized: initialized,
SquadRequestScheduler: squadScheduler,
DataChannels: dataChannels,
DataChannelsFlag: &dataChannelFlag,
}
fmt.Println("squad fking creeaeted", squad.Name)
return
}
func NewSquadManager(id string, token string) (squadManager *SquadManager, err error) {
squadFlag := uint32(0)
peerConnectionFlag := uint32(0)
localSDFlag := uint32(0)
dataChannelFlag := uint32(0)
candidateFlag := uint32(0)
dataChannels := make(map[string]*DataChannel)
squadMap := make(map[string]*Squad)
squads, err := squadManager.fetchSquads(id, token)
if err != nil {
return
}
for _, squad := range squads {
z, err := NewSquad(id, squad.ID, squad.Name, squad.ImageURL, squad.Owner, squad.CreationDate, true, squad.AuthorizedMembers)
if err != nil {
return nil, err
}
squadMap[squad.ID] = z
}
squadsFolder, err := os.ReadDir(filepath.Join(dataPath, "data", "squads"))
if err != nil {
if os.IsNotExist(err) {
if err = os.MkdirAll(filepath.Join(dataPath, "data", "squads"), 0770); err != nil {
return
}
} else {
return nil, err
}
}
logger.Println(squadMap)
squadManager = &SquadManager{
ID: id,
Squads: squadMap,
LocalSD: make(map[string]*webrtc.SessionDescription),
RTCPeerConnections: make(map[string]*RTCPeerConnection),
DataChannels: dataChannels,
PendingCandidates: make(map[string][]*webrtc.ICECandidate),
squadFlag: &squadFlag,
peerConnectionFlag: &peerConnectionFlag,
localSDFlag: &localSDFlag,
dataChannelFlag: &dataChannelFlag,
candidateFlag: &candidateFlag,
}
for _, z := range squadsFolder {
if _, ok := squadMap[z.Name()]; !ok {
logger.Println(squadManager.DeleteSquad(z.Name()))
}
}
return
}
func (zm *SquadManager) sendSignalingMessage(messageType, from, to string, payload map[string]interface{}) (err error) {
bs, err := json.Marshal(payload)
if err != nil {
return
}
err = zm.stream.Send(&SignalingMessage{
Type: messageType,
From: from,
To: to,
Payload: bs,
})
return
}
func (zm *SquadManager) DeleteSquad(squadId string) error {
return os.RemoveAll(filepath.Join(dataPath, "data", "squads", squadId))
}
func (zm *SquadManager) fetchSquads(nodeId string, token string) (squads []*Squad, err error) {
em := NewEncryptionManager()
sig := em.SignRequestHMAC(nodeId)
body, err := json.Marshal(map[string]interface{}{
"type": LIST_HOSTED_SQUADS_BY_HOST,
"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 squad 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["squads"])
if err != nil {
return
}
err = json.Unmarshal(b, &squads)
return
}
func (zm *SquadManager) CreateOffer(ctx context.Context, target string, from string, squadId string, cb OnICECandidateFunc) (err error) {
peerConnection, err := zm.createPeerConnection(target, from, squadId, 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(zm.peerConnectionFlag, func() (err error) {
id := uuid.New().String()
logger.Println("adding for target", target)
zm.RTCPeerConnections[target] = &RTCPeerConnection{
id: id,
PeerConnection: peerConnection,
makingOffer: true,
makingOfferLock: &sync.Mutex{},
negotiate: zm.negotiate,
}
return
})
err = zm.sendSignalingMessage(string(HOSTED_SQUAD_OFFER), zm.ID, target, map[string]any{
"to": target,
"from": zm.ID,
"sdp": rawOffer.SDP,
})
return
}
func (zm *SquadManager) 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 := zm.Squads[req["squadId"]]; !ok {
err = fmt.Errorf("no corresponding squad")
errCh <- err
return
}
logger.Println("handling squad offer")
_ = atomicallyExecute(zm.squadFlag, func() (err error) {
return
})
if _, ok := zm.RTCPeerConnections[from]; ok {
if e := zm.HandleLeavingMember(from, req["squadId"], false); e != nil {
logger.Println(e)
}
}
peerConnection, err := zm.createPeerConnection(from, to, req["squadId"], webrtc.SDPTypeAnswer, cb)
if err != nil {
errCh <- err
return
}
logger.Println("peer connection created")
_ = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
id := uuid.New().String()
zm.RTCPeerConnections[from] = &RTCPeerConnection{
PeerConnection: peerConnection,
id: id,
makingOffer: false,
makingOfferLock: &sync.Mutex{},
negotiate: zm.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(zm.localSDFlag, func() (err error) {
zm.LocalSD[from] = &rawAnswer
return
})
if err = peerConnection.SetLocalDescription(rawAnswer); err != nil {
errCh <- err
return
}
if err = zm.sendSignalingMessage(string(HOSTED_SQUAD_ANSWER), zm.ID, from, map[string]any{
"to": from,
"from": zm.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 (zm *SquadManager) 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(zm.peerConnectionFlag, func() (err error) {
if _, ok := zm.RTCPeerConnections[from]; !ok {
err = fmt.Errorf("no corresponding peer connection for id : %s", from)
return
}
peerConnnection := zm.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 = zm.sendSignalingMessage(string(HOSTED_SQUAD_COUNTER_OFFER), zm.ID, from, map[string]any{
"from": zm.ID,
"to": from,
}); err != nil {
return
}
_ = atomicallyExecute(zm.candidateFlag, func() (err error) {
for _, candidate := range zm.PendingCandidates[from] {
logger.Println("sending candidate from answer to", from)
if err = zm.sendSignalingMessage(string(HOSTED_SQUAD_WEBRTC_CANDIDATE), zm.ID, from, map[string]any{
"from": zm.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(zm.PendingCandidates, from)
return
})
_ = atomicallyExecute(zm.localSDFlag, func() (err error) {
delete(zm.LocalSD, from)
return
})
return
}
func (zm *SquadManager) HandleCounterOffer(ctx context.Context, from string, to string, req map[string]string) (err error) {
_ = atomicallyExecute(zm.candidateFlag, func() (err error) {
for _, candidate := range zm.PendingCandidates[from] {
logger.Println("sending candidate to", from)
if err = zm.sendSignalingMessage(string(HOSTED_SQUAD_WEBRTC_CANDIDATE), zm.ID, from, map[string]any{
"from": zm.ID,
"to": from,
"candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid,
"sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
}); err != nil {
return
}
}
delete(zm.PendingCandidates, from)
return
})
_ = atomicallyExecute(zm.localSDFlag, func() (err error) {
delete(zm.LocalSD, from)
return
})
return
}
func (zm *SquadManager) createPeerConnection(target string, from string, squadId 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"},
},
},
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 *SquadRequest)
channel.OnOpen(func() {
logger.Println(squadId)
if _, ok := zm.Squads[squadId]; ok {
logger.Println("this squad exist")
_ = atomicallyExecute(zm.Squads[squadId].DataChannelsFlag, func() (err error) {
x := uint32(0)
zm.Squads[squadId].DataChannels[target] = &DataChannel{DataChannel: channel, bufferedAmountLowThresholdReached: make(<-chan struct{}), l: &x}
return
})
if _, ok := zm.Squads[squadId]; !ok {
err = fmt.Errorf("no corresponding squads")
return
}
done, err := zm.Squads[squadId].SquadRequestScheduler.Schedule(reqChan)
bs, jsonErr := json.Marshal(&SquadResponse{
Type: "user_squad_init",
From: squadId,
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)
//_ = zm.HandleLeavingMember(target, squadId, true)
})
channel.OnError(func(err error) {
close(reqChan)
//_ = zm.HandleLeavingMember(target, squadId, true)
})
channel.OnMessage(func(msg webrtc.DataChannelMessage) {
var req SquadRequest
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(zm.dataChannelFlag, func() (err error) {
l := uint32(0)
zm.DataChannels[target] = &DataChannel{
DataChannel: channel,
bufferedAmountLowThresholdReached: make(<-chan struct{}),
l: &l,
}
return
})
} else {
peerConnection.OnDataChannel(func(dc *webrtc.DataChannel) {
_ = atomicallyExecute(zm.dataChannelFlag, func() (err error) {
l := uint32(0)
zm.DataChannels[target] = &DataChannel{
DataChannel: dc,
l: &l,
}
return
})
reqChan := make(chan *SquadRequest, 100)
if dc.Label() == "data" {
dc.OnOpen(func() {
logger.Println(squadId)
if _, ok := zm.Squads[squadId]; ok {
logger.Println("this squad exist")
_ = atomicallyExecute(zm.Squads[squadId].DataChannelsFlag, func() (err error) {
logger.Println("adding dc to dc map")
x := uint32(0)
zm.Squads[squadId].DataChannels[target] = &DataChannel{DataChannel: dc, bufferedAmountLowThresholdReached: make(<-chan struct{}), l: &x}
return
})
if _, ok := zm.Squads[squadId]; !ok {
err = fmt.Errorf("no corresponding squads")
return
}
done, err := zm.Squads[squadId].SquadRequestScheduler.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 SquadRequest
if err := json.Unmarshal(msg.Data, &req); err != nil {
logger.Println(err)
return
}
logger.Println("incoming request", req)
go func() {
reqChan <- &req
}()
})
_ = atomicallyExecute(zm.dataChannelFlag, func() (err error) {
l := uint32(0)
zm.DataChannels[target] = &DataChannel{
DataChannel: dc,
bufferedAmountLowThresholdReached: make(<-chan struct{}),
l: &l,
}
return
})
} else {
if _, ok := zm.Squads[squadId]; ok {
fmt.Println("got new mtfking datachannel")
fmt.Println(dc.Label())
scheduler := zm.Squads[squadId].SquadRequestScheduler
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 = zm.HandleLeavingMember(target, squadId, 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(zm.candidateFlag, func() (err error) {
desc := peerConnection.RemoteDescription()
if desc == nil {
logger.Println("generated candidate appended to list : ", i)
zm.PendingCandidates[target] = append(zm.PendingCandidates[target], i)
} else {
logger.Println("generated candidate : ", i)
if iceCandidateErr := cb(target, i); iceCandidateErr != nil {
logger.Println(iceCandidateErr)
}
}
return
})
})
return
}
func (zm *SquadManager) HandleRennegotiationOffer(from, sdp string) (err error) {
err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
if _, ok := zm.RTCPeerConnections[from]; !ok {
err = fmt.Errorf("no corresponding peer connection for id %s", from)
return
}
if err = zm.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
return
}
localSd, err := zm.RTCPeerConnections[from].CreateAnswer(nil)
if err != nil {
return
}
if err = zm.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
return
}
if err = zm.sendSignalingMessage(string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER), zm.ID, from, map[string]any{
"to": from,
"sdp": localSd.SDP,
}); err != nil {
logger.Println(err)
return
}
return
})
return
}
func (zm *SquadManager) HandleRennegotiationAnswer(from string, sdp string) (err error) {
_ = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
if _, ok := zm.RTCPeerConnections[from]; !ok {
err = fmt.Errorf("no corresponding peer connection for id %s", from)
return
}
err = zm.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer})
return
})
return
}
func (zm *SquadManager) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) {
_ = atomicallyExecute(zm.candidateFlag, func() (err error) {
if candidate != nil {
if connection, ok := zm.RTCPeerConnections[from]; ok {
err = connection.AddICECandidate(*candidate)
}
}
return
})
return
}
func (zm *SquadManager) HandleLeavingMember(id, squadId string, signalLeaving bool) (err error) {
fmt.Println("uwuwuwuuwuwuwuwuuwuwuuwuwuwuwuuwwuwu")
fmt.Println("handling leaving member")
logger.Println("---------------- handling leaving member", id)
if err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
if _, ok := zm.RTCPeerConnections[id]; !ok {
err = fmt.Errorf("no correponding peerconnection for id %s", id)
return
}
return
}); err != nil {
return
}
if signalLeaving {
nerr := zm.notifyLeavingMember(id, squadId, NodeID)
fmt.Println(nerr)
}
err = atomicallyExecute(zm.squadFlag, func() (err error) {
logger.Println(err)
logger.Println("---------------- cleaning squad handlers", id)
if squad, ok := zm.Squads[squadId]; ok {
for _, handlersPublishers := range squad.SquadRequestScheduler.handlersPublishers {
go func(hp chan<- *SquadRequest) {
hp <- &SquadRequest{
ReqType: LEAVE_SQUAD,
From: id,
Payload: map[string]interface{}{
"userId": id,
},
}
}(handlersPublishers)
}
if err = atomicallyExecute(squad.DataChannelsFlag, func() (err error) {
defer delete(squad.DataChannels, id)
if dataChannel, ok := squad.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 squad for squadId %s", squadId)
}
logger.Println(err)
err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
if _, ok := zm.RTCPeerConnections[id]; ok {
defer delete(zm.RTCPeerConnections, id)
if err = zm.RTCPeerConnections[id].Close(); err != nil {
return
}
}
return
})
fmt.Println(err)
_ = atomicallyExecute(zm.candidateFlag, func() (err error) {
delete(zm.PendingCandidates, id)
return
})
return
})
return
}
func (zm *SquadManager) notifyLeavingMember(userId, squadId, hostId string) (err error) {
em := NewEncryptionManager()
sig := em.SignRequestHMAC(NodeID)
body, err := json.Marshal(map[string]interface{}{
"type": DISCONNECT_SQUAD_MEMBER,
"mac": sig,
"from": NodeID,
"peerType": "node",
"payload": map[string]string{
"squadId": squadId,
"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 squad manager")
return
}
return
}
func (zm *SquadManager) negotiate(target string, squadId string) {
_ = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
if _, ok := zm.RTCPeerConnections[target]; !ok {
err = fmt.Errorf("no peerConnections")
return
}
return
})
}

124
squadNotificationHandler.go Normal file
View File

@ -0,0 +1,124 @@
package localserver
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
)
type SquadNotificationsHandler struct {
SquadId string
SquadMembersId []string
DataChannels map[string]*DataChannel
Flag *uint32
Publishers []<-chan *SquadRequest
reqChans []chan<- *SquadRequest
}
func NewSquadNotificationsHandler(_ string, squadId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, flag *uint32) (squadNotificationsHandler *SquadNotificationsHandler, err error) {
if _, dirErr := os.ReadDir(filepath.Join(dataPath, "data", "squads", squadId, "notifications")); os.IsNotExist(dirErr) {
dirErr := os.MkdirAll(filepath.Join(dataPath, "data", "squads", squadId, "notifications"), 0700)
if dirErr != nil {
return
}
}
squadNotificationsHandler = &SquadNotificationsHandler{
SquadId: squadId,
SquadMembersId: authorizedMembers,
DataChannels: dataChannels,
Flag: flag,
}
return
}
func (znh *SquadNotificationsHandler) Init(ctx context.Context, authorizedMembers []string) (err error) {
//? initialization code here
return
}
func (znh *SquadNotificationsHandler) Subscribe(ctx context.Context, publisher <-chan *SquadRequest) (reqChan chan *SquadRequest, done chan struct{}, errCh chan error) {
reqChan, done, errCh = make(chan *SquadRequest), make(chan struct{}), make(chan error)
znh.reqChans = append(znh.reqChans, reqChan)
go func() {
for {
select {
case <-ctx.Done():
done <- struct{}{}
return
case req := <-publisher:
if err := znh.handleSquadRequest(ctx, req); err != nil {
errCh <- err
}
}
}
}()
return
}
func (znh *SquadNotificationsHandler) ListNotifications(userId string) {}
func (znh *SquadNotificationsHandler) CreateNotification(notificationType, title, body, payload string, isPushed bool, recipients ...string) (err error) {
if isPushed {
err = znh.PushNotification(notificationType, title, body, payload, recipients...)
}
return
}
func (znh *SquadNotificationsHandler) DeleteNotification() {}
func (znh *SquadNotificationsHandler) PushNotification(notificationType, title, body, payload string, recipients ...string) (err error) {
em := NewEncryptionManager()
sig := em.SignRequestHMAC(NodeID)
b, err := json.Marshal(map[string]interface{}{
"type": NOTIFY,
"mac": sig,
"from": NodeID,
"peerType": "node",
"payload": map[string]interface{}{
"type": notificationType,
"title": title,
"body": body,
"recipients": recipients,
"payload": payload,
},
})
if err != nil {
return
}
_, err = HTTPClient.Post("https://dev.zippytal.com/req", "application/json", bytes.NewReader(b))
if err != nil {
logger.Println("error come from there in squad manager")
return
}
return
}
func (znh SquadNotificationsHandler) handleDataChannel(ctx context.Context, dc *DataChannel) (catched bool) {
return
}
func (znh *SquadNotificationsHandler) handleSquadRequest(ctx context.Context, req *SquadRequest) (err error) {
switch req.ReqType {
case CREATE_NOTIFICATION:
if err = VerifyFieldsString(req.Payload, "type", "title", "body", "payload"); err != nil {
return
}
if err = VerifyFieldsBool(req.Payload, "isPushed"); err != nil {
return
}
if _, ok := req.Payload["recipients"]; !ok {
err = fmt.Errorf("no field recipient in payload")
return
}
if _, ok := req.Payload["recipients"].([]string); !ok {
err = fmt.Errorf(" field recipient in payload is wrong type")
return
}
err = znh.CreateNotification(req.Payload["type"].(string), req.Payload["title"].(string), req.Payload["body"].(string), req.Payload["payload"].(string), req.Payload["isPushed"].(bool), req.Payload["recipients"].([]string)...)
}
return
}

122
squadRequestScheduler.go Normal file
View File

@ -0,0 +1,122 @@
package localserver
import (
"context"
"sync"
"time"
)
type SquadRequestScheduler struct {
handlersPublishers []chan<- *SquadRequest
handlersDataChannelDispatchCallbacks []DispatchDataChannelCallBack
}
type SquadRequest struct {
ReqType string `json:"reqType"`
Payload map[string]any `json:"payload"`
From string `json:"from"`
}
type SquadResponse struct {
Type string `json:"type"`
To string `json:"to"`
From string `json:"from"`
Payload map[string]any `json:"payload"`
}
type SquadRequestHandler interface {
Init(ctx context.Context, authorizedMembers []string) (err error)
Subscribe(ctx context.Context, publisher <-chan *SquadRequest) (reqChan chan *SquadRequest, done chan struct{}, errCh chan error)
handleSquadRequest(ctx context.Context, req *SquadRequest) (err error)
handleDataChannel(ctx context.Context, dc *DataChannel) (catched bool)
}
func NewSquadRequestScheduler(authorizedMembers []string, handlers ...SquadRequestHandler) (squadRequestScheduler *SquadRequestScheduler, handlerErrCh chan error) {
squadRequestScheduler = new(SquadRequestScheduler)
squadRequestScheduler.handlersPublishers = make([]chan<- *SquadRequest, 0)
squadRequestScheduler.handlersDataChannelDispatchCallbacks = make([]DispatchDataChannelCallBack, 0)
handlerErrCh = make(chan error)
reqChans := []chan *SquadRequest{}
for _, handler := range handlers {
publisher := make(chan *SquadRequest, 100)
squadRequestScheduler.handlersPublishers = append(squadRequestScheduler.handlersPublishers, publisher)
squadRequestScheduler.handlersDataChannelDispatchCallbacks = append(squadRequestScheduler.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 *SquadRequest) {
done, errCh := squadRequestScheduler.Schedule(reqChan)
for {
select {
case <-done:
return
case e := <-errCh:
logger.Println("from there", e)
}
}
}(reqChan)
}
for i, handler := range handlers {
if ierr := handler.Init(context.Background(), authorizedMembers); ierr != nil {
logger.Println(ierr)
}
logger.Println("init done for handler", i)
}
return
}
func (zrs *SquadRequestScheduler) Schedule(reqChan <-chan *SquadRequest) (done chan struct{}, errCh chan error) {
done, errCh = make(chan struct{}), make(chan error)
go func() {
for req := range reqChan {
go func(r *SquadRequest) {
for _, publisher := range zrs.handlersPublishers {
publisher <- r
}
}(req)
}
done <- struct{}{}
}()
return
}
func (zrs *SquadRequestScheduler) DispatchDatachannel(ctx context.Context, dc *DataChannel) (catched bool) {
wg, lock := sync.WaitGroup{}, &sync.Mutex{}
timeoutCtx, cancel := context.WithTimeout(ctx, 15*time.Second)
done := make(chan struct{})
defer cancel()
for _, dispatchCallback := range zrs.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
}
}

828
squadVideoChannel.go Normal file
View File

@ -0,0 +1,828 @@
package localserver
// import (
// "context"
// "encoding/json"
// "errors"
// "fmt"
// "io"
// "strconv"
// sync "sync"
// "sync/atomic"
// "time"
// "github.com/google/uuid"
// "github.com/pion/rtcp"
// "github.com/pion/webrtc/v3"
// )
// type VideoChannel struct {
// ID string `json:"id"`
// Owner string `json:"owner"`
// ChannelType string `json:"channelType"`
// Members []string `json:"members"`
// CurrentMembersId []string `json:"currentMembersId"`
// CurrentMembers map[string]*VideoChannelMember `json:"currentMembers"`
// localSD map[string]*webrtc.SessionDescription `json:"-"`
// rtcPeerConnections map[string]*ZoneRTCPeerConnection `json:"-"`
// audioTransceiver map[string][]*PeerSender `json:"-"`
// videoTransceiver map[string][]*PeerSender `json:"-"`
// videoChannelDataChannels map[string]*DataChannel `json:"-"`
// pendingCandidates map[string][]*webrtc.ICECandidate `json:"-"`
// remoteTracks map[string][]*RemoteTrack `json:"-"`
// middlewares []interface{} `json:"-"`
// candidateFlag *uint32 `json:"-"`
// remoteTracksFlag *uint32 `json:"-"`
// rtcPeerConnectionMapFlag *uint32 `json:"-"`
// dataChannelMapFlag *uint32 `json:"-"`
// localSDMapFlag *uint32 `json:"-"`
// audioSenderFlag *uint32 `json:"-"`
// videoSenderFlag *uint32 `json:"-"`
// }
// type VideoChannelOnICECandidateFunc = func(string, string, *webrtc.ICECandidate) error
// func NewVideoChannel(id string, owner string, channelType string, members []string, currentMembersId []string, currentMembers map[string]*VideoChannelMember) (audioChannel *VideoChannel) {
// candidateFlag := uint32(0)
// remoteTracksFlag := uint32(0)
// rtcPeerConnectionMapFlag := uint32(0)
// dataChannelMapFlag := uint32(0)
// localSDMapFlag := uint32(0)
// audioSenderFlag := uint32(0)
// videoSenderFlag := uint32(0)
// audioChannel = &VideoChannel{
// ID: id,
// Owner: owner,
// ChannelType: channelType,
// Members: members,
// CurrentMembersId: currentMembersId,
// CurrentMembers: currentMembers,
// localSD: make(map[string]*webrtc.SessionDescription),
// videoTransceiver: make(map[string][]*PeerSender),
// rtcPeerConnections: make(map[string]*ZoneRTCPeerConnection),
// audioTransceiver: make(map[string][]*PeerSender),
// videoChannelDataChannels: make(map[string]*DataChannel),
// pendingCandidates: make(map[string][]*webrtc.ICECandidate),
// remoteTracks: make(map[string][]*RemoteTrack),
// middlewares: make([]interface{}, 0),
// candidateFlag: &candidateFlag,
// remoteTracksFlag: &remoteTracksFlag,
// rtcPeerConnectionMapFlag: &rtcPeerConnectionMapFlag,
// dataChannelMapFlag: &dataChannelMapFlag,
// localSDMapFlag: &localSDMapFlag,
// audioSenderFlag: &audioSenderFlag,
// videoSenderFlag: &videoSenderFlag,
// }
// return
// }
// func (vc *VideoChannel) HandleOffer(ctx context.Context, channelId string, userId string, sdp string, hostId string, sendDCMessage SendDCMessageFunc, cb VideoChannelOnICECandidateFunc) (done chan struct{}, errCh chan error) {
// done, errCh = make(chan struct{}), make(chan error)
// go func() {
// peerConnection, err := vc.createPeerConnection(userId, vc.ID, webrtc.SDPTypeAnswer, cb, sendDCMessage)
// if err != nil {
// errCh <- err
// return
// }
// _ = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) {
// vc.rtcPeerConnections[userId] = &ZoneRTCPeerConnection{
// PeerConnection: peerConnection,
// makingOffer: false,
// makingOfferLock: &sync.Mutex{},
// negotiate: vc.negotiate,
// }
// return
// })
// offer := webrtc.SessionDescription{
// Type: webrtc.SDPTypeOffer,
// SDP: sdp,
// }
// if err = peerConnection.SetRemoteDescription(offer); err != nil {
// errCh <- err
// return
// }
// rawAnswer, err := peerConnection.CreateAnswer(nil)
// if err != nil {
// errCh <- err
// return
// }
// if err = peerConnection.SetLocalDescription(rawAnswer); err != nil {
// errCh <- err
// return
// }
// _, _ = sendDCMessage(string(VIDEO_CHANNEL_WEBRTC_ANSWER), hostId, userId, map[string]interface{}{
// "to": userId,
// "from": vc.ID,
// "channelId": channelId,
// "sdp": rawAnswer.SDP,
// })
// done <- struct{}{}
// logger.Println("handle offer done")
// }()
// return
// }
// func (vc *VideoChannel) HandleCounterOffer(ctx context.Context, userId string, sendDCMessage SendDCMessageFunc) (err error) {
// // if err = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) {
// // if _, ok := vc.rtcPeerConnections[userId]; !ok {
// // err = fmt.Errorf("no field corresponding peer connection for id %s", userId)
// // return
// // }
// // logger.Println("handling counter offer")
// // connection := vc.rtcPeerConnections[userId]
// // err = atomicallyExecute(vc.localSDMapFlag, func() (err error) {
// // err = connection.SetLocalDescription(*vc.localSD[userId])
// // return
// // })
// // return
// // }); err != nil {
// // return
// // }
// // _ = atomicallyExecute(vc.localSDMapFlag, func() (err error) {
// // delete(vc.localSD, userId)
// // return
// // })
// if err = atomicallyExecute(vc.candidateFlag, func() (err error) {
// for _, candidate := range vc.pendingCandidates[userId] {
// logger.Println("sending candidate to", userId, candidate)
// d, e := sendDCMessage(string(VIDEO_CHANNEL_WEBRTC_CANDIDATE), "", userId, map[string]interface{}{
// "from": vc.ID,
// "to": userId,
// "candidate": candidate.ToJSON().Candidate,
// "sdpMid": *candidate.ToJSON().SDPMid,
// "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
// })
// select {
// case <-d:
// case err = <-e:
// return
// }
// }
// delete(vc.pendingCandidates, userId)
// return
// }); err != nil {
// return
// }
// return
// }
// func (vc *VideoChannel) HandleRennegotiationOffer(from string, sdp string, sendDCMessage SendDCMessageFunc) (err error) {
// err = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) {
// if _, ok := vc.rtcPeerConnections[from]; !ok {
// err = fmt.Errorf("no corresponding peer connection for id %s", from)
// return
// }
// // vc.rtcPeerConnections[from].makingOfferLock.Lock()
// // if vc.rtcPeerConnections[from].makingOffer {
// // vc.rtcPeerConnections[from].makingOfferLock.Unlock()
// // return fmt.Errorf("already making an offer or state is stable")
// // }
// // vc.rtcPeerConnections[from].makingOfferLock.Unlock()
// if err = vc.rtcPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
// return
// }
// localSd, err := vc.rtcPeerConnections[from].CreateAnswer(nil)
// if err != nil {
// return
// }
// if err = vc.rtcPeerConnections[from].SetLocalDescription(localSd); err != nil {
// return
// }
// d, e := sendDCMessage(string(VIDEO_CHANNEL_WEBRTC_RENNEGOTIATION_ANSWER), vc.ID, from, map[string]interface{}{
// "from": vc.ID,
// "to": from,
// "sdp": localSd.SDP,
// })
// select {
// case <-d:
// case err = <-e:
// }
// return
// })
// return
// }
// func (vc *VideoChannel) HandleRennegotiationAnswer(from string, sdp string) (err error) {
// logger.Println("---------------------handling rennego answer")
// err = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) {
// // vc.rtcPeerConnections[from].makingOfferLock.Lock()
// // if vc.rtcPeerConnections[from].makingOffer {
// // vc.rtcPeerConnections[from].makingOfferLock.Unlock()
// // return fmt.Errorf("already making an offer or state is stable")
// // }
// // vc.rtcPeerConnections[from].makingOfferLock.Unlock()
// // if _, ok := vc.rtcPeerConnections[from]; !ok {
// // err = fmt.Errorf("no corresponding peer connection for id %s", from)
// // return
// // }
// err = vc.rtcPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer})
// return
// })
// return
// }
// func (vc *VideoChannel) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) {
// logger.Println("adding ice candidate", candidate)
// err = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) {
// if _, ok := vc.rtcPeerConnections[from]; ok && candidate != nil {
// err = vc.rtcPeerConnections[from].AddICECandidate(*candidate)
// }
// return
// })
// return
// }
// func (vc *VideoChannel) createPeerConnection(target string, from string, peerType webrtc.SDPType, cb VideoChannelOnICECandidateFunc, sendDCMessage SendDCMessageFunc) (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:stun.l.google.com:19302"},
// },
// },
// SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
// }
// peerConnection, err = webrtc.NewPeerConnection(config)
// if err != nil {
// return
// }
// logger.Println("---------------------------------------------------")
// if peerType == webrtc.SDPTypeAnswer {
// maxRetransmits := uint16(100)
// channel, err := peerConnection.CreateDataChannel("video-channel", &webrtc.DataChannelInit{
// MaxRetransmits: &maxRetransmits,
// })
// if err != nil {
// return nil, err
// }
// channel.OnOpen(func() {
// logger.Println("channel opened")
// if chanErr := channel.SendText("yooo man this is open"); chanErr != nil {
// logger.Println(chanErr)
// }
// })
// channel.OnMessage(func(msg webrtc.DataChannelMessage) {
// var event CallEvent
// if err := json.Unmarshal(msg.Data, &event); err != nil {
// logger.Println(err)
// return
// }
// if e := vc.HandleDataChannelEvents(event.From, event.EventId, event.Payload); e != nil {
// logger.Println("*-------------- datachannel error: ", e)
// }
// })
// logger.Println("new channel for target : ", target)
// _ = atomicallyExecute(vc.dataChannelMapFlag, func() (err error) {
// logger.Println(target)
// l := uint32(0)
// vc.videoChannelDataChannels[target] = &DataChannel{
// DataChannel: channel,
// l: &l,
// }
// return
// })
// } else {
// peerConnection.OnDataChannel(func(dc *webrtc.DataChannel) {
// _ = atomicallyExecute(vc.dataChannelMapFlag, func() (err error) {
// l := uint32(0)
// vc.videoChannelDataChannels[target] = &DataChannel{
// DataChannel: dc,
// l: &l,
// }
// return
// })
// dc.OnOpen(func() {
// logger.Printf("got a new open datachannel %s\n", dc.Label())
// })
// dc.OnMessage(func(msg webrtc.DataChannelMessage) {
// var event CallEvent
// if err := json.Unmarshal(msg.Data, &event); err != nil {
// logger.Println(err)
// return
// }
// if e := vc.HandleDataChannelEvents(event.From, event.EventId, event.Payload); e != nil {
// logger.Println("*-------------- datachannel error: ", e)
// }
// })
// })
// }
// err = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) {
// err = atomicallyExecute(vc.remoteTracksFlag, func() (err error) {
// logger.Println("------------------", vc.CurrentMembersId)
// for _, id := range vc.CurrentMembersId {
// logger.Println(id)
// if id != target {
// if _, ok := vc.remoteTracks[id]; !ok {
// continue
// }
// for _, track := range vc.remoteTracks[id] {
// transceiver, err := peerConnection.AddTransceiverFromKind(track.Track.Kind(), webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly})
// if err != nil {
// logger.Println("add track error")
// continue
// }
// if err := transceiver.Sender().ReplaceTrack(track.Track); err != nil {
// logger.Println("add track error")
// continue
// }
// if track.Track.Kind() == webrtc.RTPCodecTypeVideo {
// _ = atomicallyExecute(vc.videoSenderFlag, func() (err error) {
// if len(vc.videoTransceiver) == 0 {
// vc.videoTransceiver[id] = []*PeerSender{{ID: target, Transceiver: transceiver}}
// } else {
// vc.videoTransceiver[id] = append(vc.videoTransceiver[id], &PeerSender{ID: target, Transceiver: transceiver})
// }
// return
// })
// } else if track.Track.Kind() == webrtc.RTPCodecTypeAudio {
// _ = atomicallyExecute(vc.audioSenderFlag, func() (err error) {
// if len(vc.audioTransceiver) == 0 {
// vc.audioTransceiver[id] = []*PeerSender{{ID: target, Transceiver: transceiver}}
// } else {
// vc.audioTransceiver[id] = append(vc.audioTransceiver[id], &PeerSender{ID: target, Transceiver: transceiver})
// }
// return
// })
// }
// logger.Println("track added", track)
// }
// }
// }
// return
// })
// return
// })
// peerConnection.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) {
// if pcs == webrtc.PeerConnectionStateClosed || pcs == webrtc.PeerConnectionStateDisconnected || pcs == webrtc.PeerConnectionStateFailed {
// logger.Println(pcs)
// //vc.HandleLeavingMember(target, squadId)
// }
// })
// peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) {
// logger.Printf("ICE connection state has changed %s\n", is.String())
// if is == webrtc.ICEConnectionStateDisconnected || is == webrtc.ICEConnectionStateFailed {
// logger.Println(is)
// }
// })
// peerConnection.OnTrack(func(tr *webrtc.TrackRemote, r *webrtc.RTPReceiver) {
// logger.Println("got new track")
// defer func() {
// if stopErr := r.Stop(); stopErr != nil {
// logger.Println(stopErr)
// }
// }()
// go func() {
// ticker := time.NewTicker(1500 * time.Millisecond)
// for range ticker.C {
// if rtcpSendErr := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(tr.SSRC())}}); rtcpSendErr != nil {
// logger.Println(rtcpSendErr)
// break
// }
// }
// }()
// uniqId := uuid.New()
// i := fmt.Sprintf("%s/%s", target, uniqId.String())
// logger.Println("*************************----------------", i, "-----------------------***************")
// localTrack, newTrackErr := webrtc.NewTrackLocalStaticRTP(tr.Codec().RTPCodecCapability, i, i)
// if newTrackErr != nil {
// return
// }
// logger.Println(localTrack)
// rtpbuf := make([]byte, 1400)
// flag := int32(0)
// remote := &RemoteTrack{ID: target, Track: localTrack, rdv: &flag}
// _ = atomicallyExecute(vc.remoteTracksFlag, func() (err error) {
// if len(vc.remoteTracks[target]) == 0 {
// vc.remoteTracks[target] = []*RemoteTrack{remote}
// } else {
// vc.remoteTracks[target] = append(vc.remoteTracks[target], remote)
// }
// index := len(vc.remoteTracks[target])
// logger.Println(index, vc.remoteTracks)
// return
// })
// _ = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) {
// for _, id := range vc.CurrentMembersId {
// if id != target {
// if _, ok := vc.rtcPeerConnections[id]; !ok {
// continue
// }
// connection := vc.rtcPeerConnections[id]
// transceiver, tranceiverErr := connection.AddTransceiverFromKind(localTrack.Kind(), webrtc.RTPTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly})
// if tranceiverErr != nil {
// logger.Println(tranceiverErr)
// continue
// }
// if replaceTrackErr := transceiver.Sender().ReplaceTrack(localTrack); replaceTrackErr != nil {
// logger.Println(replaceTrackErr)
// continue
// }
// go func() {
// rtcpBuf := make([]byte, 1500)
// for {
// if _, _, rtcpErr := transceiver.Sender().Read(rtcpBuf); rtcpErr != nil {
// return
// }
// }
// }()
// if localTrack.Kind() == webrtc.RTPCodecTypeAudio {
// _ = atomicallyExecute(vc.audioSenderFlag, func() (err error) {
// if len(vc.audioTransceiver) == 0 {
// vc.audioTransceiver[target] = []*PeerSender{{ID: id, Transceiver: transceiver}}
// } else {
// vc.audioTransceiver[target] = append(vc.audioTransceiver[target], &PeerSender{ID: id, Transceiver: transceiver})
// }
// return
// })
// } else if localTrack.Kind() == webrtc.RTPCodecTypeVideo {
// _ = atomicallyExecute(vc.videoSenderFlag, func() (err error) {
// if len(vc.videoTransceiver) == 0 {
// vc.videoTransceiver[target] = []*PeerSender{{ID: id, Transceiver: transceiver}}
// } else {
// vc.videoTransceiver[target] = append(vc.videoTransceiver[target], &PeerSender{ID: id, Transceiver: transceiver})
// }
// return
// })
// }
// go func() {
// <-time.After(time.Second * 1)
// connection.negotiate(id, sendDCMessage)
// }()
// }
// }
// return
// })
// d := make(chan struct{})
// go func() {
// for {
// i, _, readErr := tr.Read(rtpbuf)
// if readErr != nil {
// logger.Println(readErr)
// break
// }
// f := atomic.LoadInt32(remote.rdv)
// if f == 0 {
// if _, writeErr := localTrack.Write(rtpbuf[:i]); writeErr != nil && !errors.Is(writeErr, io.ErrClosedPipe) {
// logger.Println(writeErr)
// break
// } else {
// _ = rtpbuf[:i]
// }
// }
// }
// d <- struct{}{}
// }()
// <-d
// })
// peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
// if i == nil {
// return
// }
// _ = atomicallyExecute(vc.candidateFlag, func() (err error) {
// desc := peerConnection.RemoteDescription()
// if desc == nil {
// logger.Println("generated candidate appended to list : ", i)
// vc.pendingCandidates[target] = append(vc.pendingCandidates[target], i)
// } else {
// logger.Println("generated candidate : ", i)
// if iceCandidateErr := cb(from, target, i); iceCandidateErr != nil {
// logger.Println(iceCandidateErr)
// }
// }
// 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
// }
// func (vc *VideoChannel) HandleLeavingMember(id string) {
// if err := atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) {
// if _, ok := vc.rtcPeerConnections[id]; !ok {
// err = fmt.Errorf("no corresponding peerconnection for audio channel leaving member")
// }
// return
// }); err != nil {
// logger.Println(err)
// } else {
// defer func() {
// _ = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) {
// if _, ok := vc.rtcPeerConnections[id]; ok {
// if closeErr := vc.rtcPeerConnections[id].Close(); closeErr != nil {
// err = closeErr
// logger.Println("peer connection close error", closeErr)
// }
// }
// delete(vc.rtcPeerConnections, id)
// return
// })
// }()
// }
// logger.Printf("peer %s is leaving the squad\n", id)
// _ = atomicallyExecute(vc.dataChannelMapFlag, func() (err error) {
// if _, ok := vc.videoChannelDataChannels[id]; ok {
// vc.videoChannelDataChannels[id].DataChannel.Close()
// }
// delete(vc.videoChannelDataChannels, id)
// return
// })
// _ = atomicallyExecute(vc.localSDMapFlag, func() (err error) {
// delete(vc.localSD, id)
// return
// })
// _ = atomicallyExecute(vc.candidateFlag, func() (err error) {
// delete(vc.pendingCandidates, id)
// return
// })
// _ = atomicallyExecute(vc.audioSenderFlag, func() (err error) {
// for peerId, peerSender := range vc.audioTransceiver {
// if peerId != id {
// logger.Println("senders", peerSender)
// c := 0
// for i, sender := range peerSender {
// if sender.ID == id {
// if senderErr := sender.Transceiver.Sender().Stop(); senderErr != nil {
// logger.Println(senderErr)
// }
// if transceiverErr := sender.Transceiver.Stop(); transceiverErr != nil {
// logger.Println("transceiverErr occured with video", transceiverErr)
// }
// peerSender[len(peerSender)-i-1], peerSender[i] = peerSender[i], peerSender[len(peerSender)-i-1]
// c++
// }
// }
// vc.audioTransceiver[peerId] = vc.audioTransceiver[peerId][:len(peerSender)-(c)]
// logger.Println(vc.audioTransceiver[peerId])
// }
// }
// for _, transceiver := range vc.audioTransceiver[id] {
// if senderErr := transceiver.Transceiver.Sender().Stop(); senderErr != nil {
// logger.Println(senderErr)
// }
// if stopErr := transceiver.Transceiver.Stop(); stopErr != nil {
// logger.Println("transceiver audio stop error", stopErr)
// }
// }
// delete(vc.audioTransceiver, id)
// return
// })
// _ = atomicallyExecute(vc.videoSenderFlag, func() (err error) {
// for peerId, peerSender := range vc.videoTransceiver {
// if peerId != id {
// logger.Println("senders", peerSender)
// c := 0
// for i, sender := range peerSender {
// if sender.ID == id {
// if senderErr := sender.Transceiver.Sender().Stop(); senderErr != nil {
// logger.Println(senderErr)
// }
// if transceiverErr := sender.Transceiver.Stop(); transceiverErr != nil {
// logger.Println("transceiverErr occured with video", transceiverErr)
// }
// peerSender[len(peerSender)-i-1], peerSender[i] = peerSender[i], peerSender[len(peerSender)-i-1]
// c++
// }
// }
// vc.videoTransceiver[peerId] = vc.videoTransceiver[peerId][:len(peerSender)-(c)]
// logger.Println(vc.videoTransceiver[peerId])
// }
// }
// for _, transceiver := range vc.videoTransceiver[id] {
// if senderErr := transceiver.Transceiver.Sender().Stop(); senderErr != nil {
// logger.Println(senderErr)
// }
// if stopErr := transceiver.Transceiver.Stop(); stopErr != nil {
// logger.Println("transceiver video stop error", stopErr)
// }
// }
// delete(vc.videoTransceiver, id)
// return
// })
// _ = atomicallyExecute(vc.remoteTracksFlag, func() (err error) {
// delete(vc.remoteTracks, id)
// return
// })
// }
// func (vc *VideoChannel) negotiate(target string, sendDCMessage SendDCMessageFunc) {
// logger.Println("------------------negotiate is called")
// _ = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) {
// if _, ok := vc.rtcPeerConnections[target]; !ok {
// return
// }
// // vc.rtcPeerConnections[target].makingOfferLock.Lock()
// // vc.rtcPeerConnections[target].makingOffer = true
// // vc.rtcPeerConnections[target].makingOfferLock.Unlock()
// defer func() {
// // vc.rtcPeerConnections[target].makingOfferLock.Lock()
// // vc.rtcPeerConnections[target].makingOffer = false
// // vc.rtcPeerConnections[target].makingOfferLock.Unlock()
// }()
// for _, id := range vc.CurrentMembersId {
// logger.Println("----------------- sending renego to peer with id", id)
// if _, ok := vc.rtcPeerConnections[id]; !ok {
// continue
// }
// connection := vc.rtcPeerConnections[id]
// if connection.SignalingState() == webrtc.SignalingStateStable {
// localSd, err := connection.CreateOffer(nil)
// if err != nil {
// logger.Println(err)
// return err
// }
// if err = connection.SetLocalDescription(localSd); err != nil {
// logger.Println(err)
// return err
// }
// 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
// })
// }
// func (vc *VideoChannel) broadcastDatachannelMessage(from string, eventId string, payload map[string]interface{}) (done chan struct{}, errCh chan error) {
// done, errCh = make(chan struct{}), make(chan error)
// go func() {
// bs, jsonErr := json.Marshal(&ZoneResponse{
// Type: eventId,
// From: vc.ID,
// Payload: payload,
// })
// if jsonErr != nil {
// errCh <- jsonErr
// return
// }
// if err := atomicallyExecute(vc.dataChannelMapFlag, func() (err error) {
// for id, dc := range vc.videoChannelDataChannels {
// if from != id {
// if err = dc.DataChannel.SendText(string(bs)); err != nil {
// return
// }
// }
// }
// return
// }); err != nil {
// errCh <- err
// }
// done <- struct{}{}
// }()
// return
// }
// func (vc *VideoChannel) HandleDataChannelEvents(from string, eventId string, payload map[string]interface{}) (err error) {
// switch eventId {
// case VIDEO_CHANNEL_USER_VIDEO_STOP:
// if err = atomicallyExecute(vc.remoteTracksFlag, func() (err error) {
// if _, ok := vc.remoteTracks[from]; !ok {
// err = fmt.Errorf("no corresponding remote tracks entry for id %s", from)
// return
// }
// for _, track := range vc.remoteTracks[from] {
// if track.Track.Kind() == webrtc.RTPCodecTypeVideo {
// atomic.SwapInt32(track.rdv, 1)
// }
// }
// return
// }); err != nil {
// return
// }
// done, errCh := vc.broadcastDatachannelMessage(from, VIDEO_CHANNEL_USER_VIDEO_STOP, map[string]interface{}{
// "userId": from,
// })
// select {
// case <-done:
// case err = <-errCh:
// }
// case VIDEO_CHANNEL_USER_VIDEO_RESUME:
// if err = atomicallyExecute(vc.remoteTracksFlag, func() (err error) {
// if _, ok := vc.remoteTracks[from]; !ok {
// err = fmt.Errorf("no corresponding remote tracks entry for id %s", from)
// return
// }
// for _, track := range vc.remoteTracks[from] {
// if track.Track.Kind() == webrtc.RTPCodecTypeVideo {
// atomic.SwapInt32(track.rdv, 0)
// }
// }
// return
// }); err != nil {
// return
// }
// done, errCh := vc.broadcastDatachannelMessage(from, VIDEO_CHANNEL_USER_VIDEO_RESUME, map[string]interface{}{
// "userId": from,
// })
// select {
// case <-done:
// case err = <-errCh:
// }
// case VIDEO_CHANNEL_USER_MUTE:
// if err = atomicallyExecute(vc.remoteTracksFlag, func() (err error) {
// if _, ok := vc.remoteTracks[from]; !ok {
// err = fmt.Errorf("no corresponding remote tracks entry for id %s", from)
// return
// }
// for _, track := range vc.remoteTracks[from] {
// if track.Track.Kind() == webrtc.RTPCodecTypeAudio {
// atomic.SwapInt32(track.rdv, 1)
// }
// }
// return
// }); err != nil {
// return
// }
// done, errCh := vc.broadcastDatachannelMessage(from, VIDEO_CHANNEL_USER_MUTE, map[string]interface{}{
// "userId": from,
// })
// select {
// case <-done:
// case err = <-errCh:
// }
// case VIDEO_CHANNEL_USER_UNMUTE:
// if err = atomicallyExecute(vc.remoteTracksFlag, func() (err error) {
// if _, ok := vc.remoteTracks[from]; !ok {
// err = fmt.Errorf("no corresponding remote tracks entry for id %s", from)
// return
// }
// for _, track := range vc.remoteTracks[from] {
// if track.Track.Kind() == webrtc.RTPCodecTypeAudio {
// atomic.SwapInt32(track.rdv, 0)
// }
// }
// return
// }); err != nil {
// return
// }
// done, errCh := vc.broadcastDatachannelMessage(from, VIDEO_CHANNEL_USER_UNMUTE, map[string]interface{}{
// "userId": from,
// })
// select {
// case <-done:
// case err = <-errCh:
// }
// case VIDEO_CHANNEL_USER_SPEAKING:
// done, errCh := vc.broadcastDatachannelMessage(from, VIDEO_CHANNEL_USER_SPEAKING, map[string]interface{}{
// "userId": from,
// })
// select {
// case <-done:
// case err = <-errCh:
// }
// case VIDEO_CHANNEL_USER_STOPPED_SPEAKING:
// done, errCh := vc.broadcastDatachannelMessage(from, VIDEO_CHANNEL_USER_STOPPED_SPEAKING, map[string]interface{}{
// "userId": from,
// })
// select {
// case <-done:
// case err = <-errCh:
// }
// }
// return
// }

398
squadVideoChannelHandler.go Normal file
View File

@ -0,0 +1,398 @@
package localserver
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/pion/webrtc/v3"
)
type SquadVideoChannelsHandler struct {
SquadId string
SquadName string
HostId string
SquadMembersId []string
DataChannels map[string]*DataChannel
VideoChanDataChannels map[string]*DataChannel
VideoChanDataChannelsFlag *uint32
DataChannelsFlag *uint32
VideoChannelsFlag *uint32
VideoChannel *VideoChannel
reqChans []chan<- *SquadRequest
}
func NewSquadVideoChannelsHandler(hostId string, squadId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, dataChannelFlag *uint32) (squadVideoChannelsHandler *SquadVideoChannelsHandler, err error) {
_, err = os.ReadDir(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel"))
if err != nil {
if os.IsNotExist(err) {
logger.Printf("creating videoChannels directory for squad %s...\n", squadId)
mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel"), 0700)
if mkdirErr != nil {
return nil, mkdirErr
}
file, ferr := os.Create(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel", "videoChannelConfig.json"))
defer func() {
_ = file.Close()
}()
if ferr != nil {
return nil, ferr
}
baseConfig := VideoChannelConfig{
ID: MEETING,
Owner: owner,
ChannelType: "public",
CurrentMembersId: make([]string, 0),
Members: make([]string, 0),
}
bs, jsonErr := json.Marshal(baseConfig)
if jsonErr != nil {
return nil, jsonErr
}
if _, writeErr := file.WriteString(string(bs)); writeErr != nil {
return nil, writeErr
}
_, err = os.ReadDir(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel"))
if err != nil {
return nil, err
}
} else {
return
}
}
var bs []byte
bs, err = os.ReadFile(filepath.Join(dataPath, "data", "squads", squadId, "videoChannel", "videoChannelConfig.json"))
if err != nil {
return nil, err
}
logger.Println(string(bs))
var vcc VideoChannelConfig
if err = json.Unmarshal(bs, &vcc); err != nil {
return nil, err
}
logger.Println("videoChannels data :", vcc.ID, vcc.ChannelType, vcc.Owner, vcc.Members)
vc := NewVideoChannel(vcc.ID, vcc.Owner, vcc.ChannelType, vcc.Members, make([]string, 0), make(map[string]*VideoChannelMember))
videoChannelFlag := uint32(0)
videoChanDCFlag := uint32(0)
squadVideoChannelsHandler = &SquadVideoChannelsHandler{
SquadId: squadId,
HostId: hostId,
SquadMembersId: authorizedMembers,
DataChannels: dataChannels,
VideoChanDataChannels: make(map[string]*DataChannel),
VideoChanDataChannelsFlag: &videoChanDCFlag,
DataChannelsFlag: dataChannelFlag,
VideoChannel: vc,
VideoChannelsFlag: &videoChannelFlag,
}
return
}
func (zvch *SquadVideoChannelsHandler) 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(zvch.VideoChanDataChannelsFlag, func() (err error) {
if _, ok := zvch.VideoChanDataChannels[to]; ok {
bs, jsonErr := json.Marshal(&SquadResponse{
Type: reqType,
From: from,
To: to,
Payload: payload,
})
if jsonErr != nil {
return jsonErr
}
err = zvch.VideoChanDataChannels[to].DataChannel.SendText(string(bs))
}
return
}); err != nil {
errCh <- err
return
}
done <- struct{}{}
}()
return done, errCh
}
func (zvch *SquadVideoChannelsHandler) Init(ctx context.Context, authorizedMembers []string) (err error) {
return
}
func (zvch *SquadVideoChannelsHandler) Subscribe(ctx context.Context, publisher <-chan *SquadRequest) (reqChan chan *SquadRequest, done chan struct{}, errCh chan error) {
reqChan, done, errCh = make(chan *SquadRequest), make(chan struct{}), make(chan error)
zvch.reqChans = append(zvch.reqChans, reqChan)
go func() {
for {
select {
case <-ctx.Done():
done <- struct{}{}
return
case req := <-publisher:
if err := zvch.handleSquadRequest(ctx, req); err != nil {
errCh <- err
}
}
}
}()
return
}
func (zvch *SquadVideoChannelsHandler) signalCandidate(from string, to string, candidate *webrtc.ICECandidate) (err error) {
d, e := zvch.sendDataChannelMessage(string(VIDEO_CHANNEL_WEBRTC_CANDIDATE), from, to, map[string]interface{}{
"from": from,
"to": to,
"candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid,
"sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
})
select {
case <-d:
case err = <-e:
}
return
}
func (zvch *SquadVideoChannelsHandler) JoinVideoChannel(channelId string, userId string, sdp string) (err error) {
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
videoChannel := zvch.VideoChannel
videoChannel.CurrentMembersId = append(videoChannel.CurrentMembersId, userId)
videoChannel.CurrentMembers[userId] = &VideoChannelMember{}
signalMembers := func(members []string) {
for _, u := range members {
done, e := zvch.sendDataChannelMessage(USER_JOINED_VIDEO_CHANNEL, "node", u, map[string]interface{}{
"userId": userId,
"channelId": channelId,
})
select {
case <-done:
case err := <-e:
logger.Println(err)
}
}
}
signalMembers(zvch.SquadMembersId)
d, e := videoChannel.HandleOffer(context.Background(), channelId, userId, sdp, zvch.HostId, zvch.sendDataChannelMessage, zvch.signalCandidate)
select {
case <-d:
case err = <-e:
}
return
})
return
}
func (zvch *SquadVideoChannelsHandler) LeaveVideoChannel(channelId string, userId string) (err error) {
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
videoChannel := zvch.VideoChannel
var index int
var contain bool
for i, v := range videoChannel.CurrentMembersId {
if v == userId {
index = i
contain = true
}
}
if !contain {
err = fmt.Errorf("this channel does not contain the provided user Id")
return
}
defer videoChannel.HandleLeavingMember(userId)
if len(videoChannel.CurrentMembersId) <= 1 {
videoChannel.CurrentMembersId = make([]string, 0)
} else {
videoChannel.CurrentMembersId = append(videoChannel.CurrentMembersId[:index], videoChannel.CurrentMembersId[index+1:]...)
}
delete(videoChannel.CurrentMembers, userId)
signalMembers := func(members []string) {
for _, u := range members {
done, e := zvch.sendDataChannelMessage(USER_LEFT_VIDEO_CHANNEL, "node", u, map[string]interface{}{
"userId": userId,
"channelId": channelId,
})
select {
case <-done:
case err := <-e:
logger.Println(err)
}
}
}
signalMembers(zvch.SquadMembersId)
return
})
return
}
func (zvch *SquadVideoChannelsHandler) 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, "video_channel_data") {
command := strings.Split(label, "|")
catched = true
_ = atomicallyExecute(zvch.VideoChanDataChannelsFlag, func() (err error) {
zvch.VideoChanDataChannels[command[1]] = dc
return
})
dc.DataChannel.OnOpen(func() {
fmt.Println("datachann in squad video chann fking created")
bs, err := json.Marshal(map[string]any{
"type": "init_video_channel",
"from": NodeID,
"to": command[1],
"payload": map[string]any{
"videoChannel": zvch.VideoChannel,
},
})
if err != nil {
fmt.Println(err)
}
_ = dc.DataChannel.SendText(string(bs))
})
dc.DataChannel.OnClose(func() {
fmt.Println("closing gratefully video channel dc...")
_ = atomicallyExecute(zvch.VideoChanDataChannelsFlag, func() (err error) {
delete(zvch.VideoChanDataChannels, command[1])
fmt.Println("dc closed gracefully...")
return
})
})
dc.DataChannel.OnError(func(err error) {
fmt.Println("error in video channel dc...")
_ = atomicallyExecute(zvch.VideoChanDataChannelsFlag, func() (err error) {
delete(zvch.VideoChanDataChannels, command[1])
fmt.Println("dc closed on error...")
return
})
})
}
return
}
func (zvch *SquadVideoChannelsHandler) handleSquadRequest(ctx context.Context, req *SquadRequest) (err error) {
switch req.ReqType {
case LEAVE_ZONE:
logger.Println("*-----------------handling leaving squad---------------*")
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
return
}
vc := zvch.VideoChannel
var contain bool
var id string
for _, member := range vc.CurrentMembersId {
if member == req.Payload["userId"].(string) {
id = member
contain = true
logger.Printf("*------------------id is %s--------------*\n", id)
break
}
}
if contain {
err = zvch.LeaveVideoChannel(vc.ID, id)
break
}
case string(REMOVED_SQUAD_AUTHORIZED_MEMBER):
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
return
}
var index int
var found bool
for i, m := range zvch.SquadMembersId {
if m == req.Payload["userId"].(string) {
index = i
found = true
break
}
}
if !found {
err = fmt.Errorf("no such member in squad")
return
}
zvch.SquadMembersId = append(zvch.SquadMembersId[:index], zvch.SquadMembersId[index+1:]...)
case string(NEW_AUTHORIZED_SQUAD_MEMBER):
if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
return
}
var contain bool
for _, m := range zvch.SquadMembersId {
if m == req.Payload["userId"].(string) {
contain = true
break
}
}
if !contain {
zvch.SquadMembersId = append(zvch.SquadMembersId, req.Payload["userId"].(string))
}
case JOIN_VIDEO_CHANNEL:
fmt.Println("wuwuuwuwwuuwuw i got this")
fmt.Println(req.Payload)
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 {
return
}
err = zvch.LeaveVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string))
case string(VIDEO_CHANNEL_WEBRTC_COUNTER_OFFER):
logger.Println("handling video channel counter offer")
if err = VerifyFieldsString(req.Payload, "channelId", "userId"); err != nil {
return
}
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
err = zvch.VideoChannel.HandleCounterOffer(ctx, req.Payload["userId"].(string), zvch.sendDataChannelMessage)
return
})
case string(VIDEO_CHANNEL_WEBRTC_RENNEGOTIATION_OFFER):
if err = VerifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
return
}
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
err = zvch.VideoChannel.HandleRennegotiationOffer(req.Payload["userId"].(string), req.Payload["sdp"].(string), zvch.sendDataChannelMessage)
return
})
case string(VIDEO_CHANNEL_WEBRTC_RENNEGOTIATION_ANSWER):
if err = VerifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
return
}
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
err = zvch.VideoChannel.HandleRennegotiationAnswer(req.Payload["userId"].(string), req.Payload["sdp"].(string))
return
})
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 {
return
}
logger.Println(req.Payload)
i, convErr := strconv.Atoi(req.Payload["sdpMLineIndex"].(string))
if convErr != nil {
return convErr
}
SDPMLineIndex := uint16(i)
sdpMid := req.Payload["sdpMid"].(string)
logger.Println(sdpMid, SDPMLineIndex)
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
fmt.Println("uwuwuw adding candidate",req.Payload["candidate"].(string))
err = zvch.VideoChannel.AddCandidate(&webrtc.ICECandidateInit{
Candidate: req.Payload["candidate"].(string),
SDPMid: &sdpMid,
SDPMLineIndex: &SDPMLineIndex,
}, req.Payload["userId"].(string))
return
})
default:
logger.Println("video channel handler still in process of implementation")
}
return
}

View File

@ -2,9 +2,63 @@ package localserver
import ( import (
"fmt" "fmt"
"sync"
"sync/atomic" "sync/atomic"
"github.com/pion/webrtc/v3"
) )
const (
NAME = "name"
ID = "ID"
SDP = "sdp"
CANDIDATE = "webrtc_candidate"
SQUAD_ID = "squadId"
FROM = "from"
TO = "to"
STOP_CALL = "stop_call"
LIST_HOSTED_SQUADS_BY_HOST = "list_hosted_squads_by_host"
LEAVE_SQUAD
bufferedAmountLowThreshold uint64 = 512 * 1024
)
type OnICECandidateFunc func(string, *webrtc.ICECandidate) error
type RTCPeerConnection struct {
*webrtc.PeerConnection
id string
makingOffer bool
negotiate func(string, string)
makingOfferLock *sync.Mutex
}
type DataChannel struct {
DataChannel *webrtc.DataChannel
bufferedAmountLowThresholdReached <-chan struct{}
l *uint32
}
type PeerSender struct {
ID string
Transceiver *webrtc.RTPTransceiver
Sender *webrtc.RTPSender
}
type CallEvent struct {
EventId string `json:"eventId"`
From string `json:"from"`
Data []byte `json:"data"`
Payload map[string]interface{} `json:"payload"`
}
type RemoteTrack struct {
ID string
Track *webrtc.TrackLocalStaticRTP
rdv *int32
}
func validateRequest(req map[string]string, entries ...string) (err error) { func validateRequest(req map[string]string, entries ...string) (err error) {
for _, entry := range entries { for _, entry := range entries {
if _, ok := req[entry]; !ok { if _, ok := req[entry]; !ok {

View File

@ -1,136 +1,136 @@
package localserver package localserver
import ( // import (
"encoding/json" // "encoding/json"
"fmt" // "fmt"
"sync/atomic" // "sync/atomic"
) // )
const ( // const (
CHAT_MESSAGE_BROADCAST = "chat_message_broadcast" // CHAT_MESSAGE_BROADCAST = "chat_message_broadcast"
CHAT_MESSAGE_PRIVATE = "chat_message_private" // CHAT_MESSAGE_PRIVATE = "chat_message_private"
) // )
type WebrtcCallChatManager struct{} // type WebrtcCallChatManager struct{}
func NewWebrtcCallChatManager() *WebrtcCallChatManager { // func NewWebrtcCallChatManager() *WebrtcCallChatManager {
return new(WebrtcCallChatManager) // return new(WebrtcCallChatManager)
} // }
func (w *WebrtcCallChatManager) HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error) { // func (w *WebrtcCallChatManager) HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error) {
done, errCh := make(chan struct{}), make(chan error) // done, errCh := make(chan struct{}), make(chan error)
go func() { // go func() {
logger.Println("got an event in call chat manager", from, eventId, payload) // logger.Println("got an event in call chat manager", from, eventId, payload)
switch eventId { // switch eventId {
case CHAT_MESSAGE_BROADCAST: // case CHAT_MESSAGE_BROADCAST:
if e := validateEvent(payload, "message"); e != nil { // if e := validateEvent(payload, "message"); e != nil {
errCh <- e // errCh <- e
return // return
} // }
if e := w.sendBrodcastChatMessage(from, payload["message"].(string), squadId, manager); e != nil { // if e := w.sendBrodcastChatMessage(from, payload["message"].(string), squadId, manager); e != nil {
errCh <- e // errCh <- e
return // return
} // }
case CHAT_MESSAGE_PRIVATE: // case CHAT_MESSAGE_PRIVATE:
if e := validateEvent(payload, "message", "dst"); e != nil { // if e := validateEvent(payload, "message", "dst"); e != nil {
errCh <- e // errCh <- e
return // return
} // }
if e := w.sendPrivateChatMessage(from, payload["message"].(string), squadId, manager, payload["message"].([]string)...); e != nil { // if e := w.sendPrivateChatMessage(from, payload["message"].(string), squadId, manager, payload["message"].([]string)...); e != nil {
errCh <- e // errCh <- e
return // return
} // }
} // }
done <- struct{}{} // done <- struct{}{}
}() // }()
select { // select {
case <-done: // case <-done:
return nil // return nil
case err = <-errCh: // case err = <-errCh:
return // return
} // }
} // }
func (w WebrtcCallChatManager) sendBrodcastChatMessage(from string, message string, squadId string, manager *WebRTCCallManager) (err error) { // func (w WebrtcCallChatManager) sendBrodcastChatMessage(from string, message string, squadId string, manager *WebRTCCallManager) (err error) {
if _, ok := manager.Squads[squadId]; !ok { // if _, ok := manager.Squads[squadId]; !ok {
err = fmt.Errorf("no correponding squad found") // err = fmt.Errorf("no correponding squad found")
return // return
} // }
for _, member := range manager.Squads[squadId].Members { // for _, member := range manager.Squads[squadId].Members {
if _, ok := manager.DataChannels[member]; ok && member != from { // if _, ok := manager.DataChannels[member]; ok && member != from {
bs, marshalErr := json.Marshal(map[string]interface{}{ // bs, marshalErr := json.Marshal(map[string]interface{}{
"type": "send_chat_message", // "type": "send_chat_message",
"from": from, // "from": from,
"payload": map[string]string{ // "payload": map[string]string{
"message": message, // "message": message,
}, // },
}) // })
if marshalErr != nil { // if marshalErr != nil {
logger.Println(err) // logger.Println(err)
continue // continue
} // }
lock: // lock:
for { // for {
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) { // if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
defer atomic.SwapUint32(manager.DataChannels[member].l, 0) // defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil { // if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
logger.Println(sendErr) // logger.Println(sendErr)
} // }
break lock // break lock
} else { // } else {
continue // continue
} // }
} // }
} // }
} // }
return // return
} // }
func (w WebrtcCallChatManager) sendPrivateChatMessage(from string, message string, squadId string, manager *WebRTCCallManager, dst ...string) (err error) { // func (w WebrtcCallChatManager) sendPrivateChatMessage(from string, message string, squadId string, manager *WebRTCCallManager, dst ...string) (err error) {
if _, ok := manager.Squads[squadId]; !ok { // if _, ok := manager.Squads[squadId]; !ok {
err = fmt.Errorf("no correponding squad found") // err = fmt.Errorf("no correponding squad found")
return // return
} // }
for _, member := range manager.Squads[squadId].Members { // for _, member := range manager.Squads[squadId].Members {
for _, id := range dst { // for _, id := range dst {
if id == member { // if id == member {
if _, ok := manager.DataChannels[member]; ok && member != from { // if _, ok := manager.DataChannels[member]; ok && member != from {
bs, marshalErr := json.Marshal(map[string]interface{}{ // bs, marshalErr := json.Marshal(map[string]interface{}{
"type": "", // "type": "",
"from": from, // "from": from,
"payload": map[string]string{ // "payload": map[string]string{
"message": message, // "message": message,
}, // },
}) // })
if marshalErr != nil { // if marshalErr != nil {
logger.Println(err) // logger.Println(err)
continue // continue
} // }
lock: // lock:
for { // for {
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) { // if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
defer atomic.SwapUint32(manager.DataChannels[member].l, 0) // defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil { // if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
logger.Println(sendErr) // logger.Println(sendErr)
} // }
break lock // break lock
} else { // } else {
continue // continue
} // }
} // }
} // }
} // }
} // }
} // }
return // return
} // }
func validateEvent(event map[string]interface{}, fields ...string) (err error) { // func validateEvent(event map[string]interface{}, fields ...string) (err error) {
for _, field := range fields { // for _, field := range fields {
if _, ok := event[field]; !ok { // if _, ok := event[field]; !ok {
err = fmt.Errorf("no field %s in req payload", field) // err = fmt.Errorf("no field %s in req payload", field)
return // return
} // }
} // }
return // return
} // }

View File

@ -1,5 +1,5 @@
package localserver package localserver
type WebrtcCallEventManager interface { // type WebrtcCallEventManager interface {
HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error) // HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error)
} // }

View File

@ -1,311 +1,311 @@
package localserver package localserver
import ( // import (
"bufio" // "bufio"
"encoding/json" // "encoding/json"
"fmt" // "fmt"
"io" // "io"
"log" // "log"
"os" // "os"
"path/filepath" // "path/filepath"
"sync/atomic" // "sync/atomic"
"github.com/pion/webrtc/v3" // "github.com/pion/webrtc/v3"
) // )
const ( // const (
UPLOAD_INIT = "upload_init" // UPLOAD_INIT = "upload_init"
UPLOAD = "upload" // UPLOAD = "upload"
UPLOAD_DONE = "upload_done" // UPLOAD_DONE = "upload_done"
DOWNLOAD_INIT = "download_init" // DOWNLOAD_INIT = "download_init"
DOWNLOAD = "download" // DOWNLOAD = "download"
DOWNLOAD_DONE = "download_done" // DOWNLOAD_DONE = "download_done"
HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_INIT = "hosted_squad_download_file_response_init" // HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_INIT = "hosted_squad_download_file_response_init"
HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE = "hosted_squad_download_file_response" // HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE = "hosted_squad_download_file_response"
HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_END = "hosted_squad_download_file_response_end" // HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_END = "hosted_squad_download_file_response_end"
) // )
const ( // const (
bufferedAmountLowThreshold uint64 = 512 * 1024 // bufferedAmountLowThreshold uint64 = 512 * 1024
//maxBufferedAmount uint64 = 1024 * 1024 // //maxBufferedAmount uint64 = 1024 * 1024
) // )
type WebrtcCallFileManager struct { // type WebrtcCallFileManager struct {
files map[string]*os.File // files map[string]*os.File
l *uint32 // l *uint32
} // }
func NewWebrtcCallFileManager() *WebrtcCallFileManager { // func NewWebrtcCallFileManager() *WebrtcCallFileManager {
l := uint32(0) // l := uint32(0)
return &WebrtcCallFileManager{ // return &WebrtcCallFileManager{
files: make(map[string]*os.File), // files: make(map[string]*os.File),
l: &l, // l: &l,
} // }
} // }
func (w *WebrtcCallFileManager) HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error) { // func (w *WebrtcCallFileManager) HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error) {
done, errCh := make(chan struct{}), make(chan error) // done, errCh := make(chan struct{}), make(chan error)
go func() { // go func() {
logger.Println("got an event in call file manager", from, eventId, payload) // logger.Println("got an event in call file manager", from, eventId, payload)
switch eventId { // switch eventId {
case UPLOAD_INIT: // case UPLOAD_INIT:
if _, ok := payload["filename"]; !ok { // if _, ok := payload["filename"]; !ok {
errCh <- fmt.Errorf("no field filename in payload") // errCh <- fmt.Errorf("no field filename in payload")
return // return
} // }
if _, ok := payload["filename"].(string); !ok { // if _, ok := payload["filename"].(string); !ok {
errCh <- fmt.Errorf("field filename in payload is not a string") // errCh <- fmt.Errorf("field filename in payload is not a string")
return // return
} // }
if err = w.initUpload(squadId, from, payload["filename"].(string)); err != nil { // if err = w.initUpload(squadId, from, payload["filename"].(string)); err != nil {
errCh <- err // errCh <- err
return // return
} // }
case UPLOAD: // case UPLOAD:
if _, ok := payload["filename"]; !ok { // if _, ok := payload["filename"]; !ok {
errCh <- fmt.Errorf("no field filename in payload") // errCh <- fmt.Errorf("no field filename in payload")
return // return
} // }
if _, ok := payload["filename"].(string); !ok { // if _, ok := payload["filename"].(string); !ok {
errCh <- fmt.Errorf("field filename in payload is not a string") // errCh <- fmt.Errorf("field filename in payload is not a string")
return // return
} // }
if err = w.upload(squadId, from, payload["filename"].(string), data); err != nil { // if err = w.upload(squadId, from, payload["filename"].(string), data); err != nil {
errCh <- err // errCh <- err
return // return
} // }
case UPLOAD_DONE: // case UPLOAD_DONE:
if _, ok := payload["filename"]; !ok { // if _, ok := payload["filename"]; !ok {
errCh <- fmt.Errorf("no field filename in payload") // errCh <- fmt.Errorf("no field filename in payload")
return // return
} // }
if _, ok := payload["filename"].(string); !ok { // if _, ok := payload["filename"].(string); !ok {
errCh <- fmt.Errorf("field filename in payload is not a string") // errCh <- fmt.Errorf("field filename in payload is not a string")
return // return
} // }
if _, ok := payload["targets"]; !ok { // if _, ok := payload["targets"]; !ok {
errCh <- fmt.Errorf("no field targets in payload") // errCh <- fmt.Errorf("no field targets in payload")
return // return
} // }
if _, ok := payload["targets"].([]interface{}); !ok { // if _, ok := payload["targets"].([]interface{}); !ok {
errCh <- fmt.Errorf("field targets in payload is not a string") // errCh <- fmt.Errorf("field targets in payload is not a string")
return // return
} // }
channels := []*DataChannel{} // channels := []*DataChannel{}
manager.DataChannelMapMux.RLock() // manager.DataChannelMapMux.RLock()
for _, target := range payload["targets"].([]interface{}) { // for _, target := range payload["targets"].([]interface{}) {
if _, ok := manager.DataChannels[target.(string)]; !ok { // if _, ok := manager.DataChannels[target.(string)]; !ok {
manager.DataChannelMapMux.RUnlock() // manager.DataChannelMapMux.RUnlock()
errCh <- fmt.Errorf("no corresponding datachannel : %s", target.(string)) // errCh <- fmt.Errorf("no corresponding datachannel : %s", target.(string))
return // return
} // }
channel := manager.DataChannels[target.(string)] // channel := manager.DataChannels[target.(string)]
for { // for {
if atomic.CompareAndSwapUint32(channel.l, 0, 1) { // if atomic.CompareAndSwapUint32(channel.l, 0, 1) {
defer atomic.SwapUint32(channel.l, 0) // defer atomic.SwapUint32(channel.l, 0)
break // break
} // }
} // }
channels = append(channels, channel) // channels = append(channels, channel)
} // }
manager.DataChannelMapMux.RUnlock() // manager.DataChannelMapMux.RUnlock()
if err = w.uploadDone(squadId, from, payload["filename"].(string), channels); err != nil { // if err = w.uploadDone(squadId, from, payload["filename"].(string), channels); err != nil {
errCh <- err // errCh <- err
return // return
} // }
case DOWNLOAD: // case DOWNLOAD:
if _, ok := payload["filename"]; !ok { // if _, ok := payload["filename"]; !ok {
errCh <- fmt.Errorf("no field filename in payload") // errCh <- fmt.Errorf("no field filename in payload")
return // return
} // }
if _, ok := payload["filename"].(string); !ok { // if _, ok := payload["filename"].(string); !ok {
errCh <- fmt.Errorf("field filename in payload is not a string") // errCh <- fmt.Errorf("field filename in payload is not a string")
return // return
} // }
if _, ok := payload["peerId"]; !ok { // if _, ok := payload["peerId"]; !ok {
errCh <- fmt.Errorf("no field peerId in payload") // errCh <- fmt.Errorf("no field peerId in payload")
return // return
} // }
if _, ok := payload["peerId"].(string); !ok { // if _, ok := payload["peerId"].(string); !ok {
errCh <- fmt.Errorf("field peerId in payload is not a string") // errCh <- fmt.Errorf("field peerId in payload is not a string")
return // return
} // }
manager.DataChannelMapMux.RLock() // manager.DataChannelMapMux.RLock()
if _, ok := manager.DataChannels[payload["peerId"].(string)]; !ok { // if _, ok := manager.DataChannels[payload["peerId"].(string)]; !ok {
manager.DataChannelMapMux.RUnlock() // manager.DataChannelMapMux.RUnlock()
errCh <- fmt.Errorf("no corresponding datachannel") // errCh <- fmt.Errorf("no corresponding datachannel")
return // return
} // }
channel := manager.DataChannels[payload["peerId"].(string)] // channel := manager.DataChannels[payload["peerId"].(string)]
for { // for {
if atomic.CompareAndSwapUint32(channel.l, 0, 1) { // if atomic.CompareAndSwapUint32(channel.l, 0, 1) {
logger.Println("atomic lock unlocked") // logger.Println("atomic lock unlocked")
defer atomic.SwapUint32(channel.l, 0) // defer atomic.SwapUint32(channel.l, 0)
break // break
} // }
} // }
manager.DataChannelMapMux.RUnlock() // manager.DataChannelMapMux.RUnlock()
if err = w.download(squadId, from, payload["filename"].(string), channel.DataChannel); err != nil { // if err = w.download(squadId, from, payload["filename"].(string), channel.DataChannel); err != nil {
errCh <- err // errCh <- err
return // return
} // }
} // }
done <- struct{}{} // done <- struct{}{}
}() // }()
select { // select {
case <-done: // case <-done:
return nil // return nil
case err = <-errCh: // case err = <-errCh:
return // return
} // }
} // }
func (w *WebrtcCallFileManager) initUpload(squadId string, from string, fileName string) (err error) { // func (w *WebrtcCallFileManager) initUpload(squadId string, from string, fileName string) (err error) {
for { // for {
if atomic.CompareAndSwapUint32(w.l, 0, 1) { // if atomic.CompareAndSwapUint32(w.l, 0, 1) {
defer atomic.SwapUint32(w.l, 0) // defer atomic.SwapUint32(w.l, 0)
if _, dirErr := os.Stat(filepath.Join(dataPath, dataPath, "data", "squads", squadId)); os.IsNotExist(dirErr) { // if _, dirErr := os.Stat(filepath.Join(dataPath, dataPath, "data", "squads", squadId)); os.IsNotExist(dirErr) {
if err = os.MkdirAll(filepath.Join(dataPath, dataPath, "data", "squads", squadId), 0700); err != nil { // if err = os.MkdirAll(filepath.Join(dataPath, dataPath, "data", "squads", squadId), 0700); err != nil {
return // return
} // }
} // }
f, fErr := os.Create(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName)) // f, fErr := os.Create(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName))
if err != nil { // if err != nil {
return fErr // return fErr
} // }
f.Close() // f.Close()
f, fErr = os.OpenFile(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName), os.O_APPEND|os.O_WRONLY, 0644) // f, fErr = os.OpenFile(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName), os.O_APPEND|os.O_WRONLY, 0644)
if err != nil { // if err != nil {
return fErr // return fErr
} // }
w.files[fileName] = f // w.files[fileName] = f
break // break
} else { // } else {
continue // continue
} // }
} // }
return // return
} // }
func (w *WebrtcCallFileManager) upload(squadId string, from string, fileName string, data []byte) (err error) { // func (w *WebrtcCallFileManager) upload(squadId string, from string, fileName string, data []byte) (err error) {
for { // for {
if atomic.CompareAndSwapUint32(w.l, 0, 1) { // if atomic.CompareAndSwapUint32(w.l, 0, 1) {
defer atomic.SwapUint32(w.l, 0) // defer atomic.SwapUint32(w.l, 0)
if _, ok := w.files[fileName]; !ok { // if _, ok := w.files[fileName]; !ok {
err = fmt.Errorf("no open file with name %s", fileName) // err = fmt.Errorf("no open file with name %s", fileName)
return // return
} // }
_, err = w.files[fileName].Write(data) // _, err = w.files[fileName].Write(data)
break // break
} else { // } else {
continue // continue
} // }
} // }
return // return
} // }
func (w *WebrtcCallFileManager) uploadDone(squadId string, from string, fileName string, channels []*DataChannel) (err error) { // func (w *WebrtcCallFileManager) uploadDone(squadId string, from string, fileName string, channels []*DataChannel) (err error) {
for { // for {
if atomic.CompareAndSwapUint32(w.l, 0, 1) { // if atomic.CompareAndSwapUint32(w.l, 0, 1) {
defer atomic.SwapUint32(w.l, 0) // defer atomic.SwapUint32(w.l, 0)
if _, ok := w.files[fileName]; !ok { // if _, ok := w.files[fileName]; !ok {
err = fmt.Errorf("no open file with name %s", fileName) // err = fmt.Errorf("no open file with name %s", fileName)
return // return
} // }
err = w.files[fileName].Close() // err = w.files[fileName].Close()
delete(w.files, fileName) // delete(w.files, fileName)
bsInit, jsonErr := json.Marshal(map[string]interface{}{ // bsInit, jsonErr := json.Marshal(map[string]interface{}{
"type": UPLOAD_DONE, // "type": UPLOAD_DONE,
"from": "server", // "from": "server",
"payload": map[string]string{ // "payload": map[string]string{
"path": fileName, // "path": fileName,
}, // },
}) // })
if jsonErr != nil { // if jsonErr != nil {
return jsonErr // return jsonErr
} // }
for _, channel := range channels { // for _, channel := range channels {
if err = channel.DataChannel.SendText(string(bsInit)); err != nil { // if err = channel.DataChannel.SendText(string(bsInit)); err != nil {
return // return
} // }
} // }
break // break
} else { // } else {
continue // continue
} // }
} // }
return // return
} // }
func (w *WebrtcCallFileManager) download(squadId string, dst string, fileName string, channel *webrtc.DataChannel) (err error) { // func (w *WebrtcCallFileManager) download(squadId string, dst string, fileName string, channel *webrtc.DataChannel) (err error) {
logger.Println("got called") // logger.Println("got called")
if _, dirErr := os.Stat(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName)); os.IsNotExist(dirErr) { // if _, dirErr := os.Stat(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName)); os.IsNotExist(dirErr) {
logger.Println("file does not exist :", filepath.Join(dataPath, "data", "squads", squadId, fileName)) // logger.Println("file does not exist :", filepath.Join(dataPath, "data", "squads", squadId, fileName))
return // return
} // }
f, err := os.Open(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName)) // f, err := os.Open(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName))
if err != nil { // if err != nil {
return // return
} // }
defer f.Close() // defer f.Close()
bsInit, err := json.Marshal(map[string]interface{}{ // bsInit, err := json.Marshal(map[string]interface{}{
"type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_INIT, // "type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_INIT,
"from": "server", // "from": "server",
"payload": map[string]string{ // "payload": map[string]string{
"path": fileName, // "path": fileName,
}, // },
}) // })
if err != nil { // if err != nil {
return // return
} // }
if err = channel.SendText(string(bsInit)); err != nil { // if err = channel.SendText(string(bsInit)); err != nil {
return // return
} // }
r := bufio.NewReader(f) // r := bufio.NewReader(f)
buf := make([]byte, 0, 30000) // buf := make([]byte, 0, 30000)
logger.Println("start reading") // logger.Println("start reading")
for { // for {
n, readErr := r.Read(buf[:cap(buf)]) // n, readErr := r.Read(buf[:cap(buf)])
buf = buf[:n] // buf = buf[:n]
if n == 0 { // if n == 0 {
if err == nil { // if err == nil {
logger.Println("n is 0 weird") // logger.Println("n is 0 weird")
break // break
} // }
if err == io.EOF { // if err == io.EOF {
break // break
} // }
log.Fatal(readErr) // log.Fatal(readErr)
} // }
bs, jsonErr := json.Marshal(map[string]interface{}{ // bs, jsonErr := json.Marshal(map[string]interface{}{
"type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE, // "type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE,
"from": "server", // "from": "server",
"payload": map[string]interface{}{ // "payload": map[string]interface{}{
"path": fileName, // "path": fileName,
"content": buf, // "content": buf,
}, // },
}) // })
if jsonErr != nil { // if jsonErr != nil {
return jsonErr // return jsonErr
} // }
if err = channel.SendText(string(bs)); err != nil { // if err = channel.SendText(string(bs)); err != nil {
return // return
} // }
} // }
logger.Println("stop reading") // logger.Println("stop reading")
bs, err := json.Marshal(map[string]interface{}{ // bs, err := json.Marshal(map[string]interface{}{
"type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_END, // "type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_END,
"from": "server", // "from": "server",
"payload": map[string]string{ // "payload": map[string]string{
"path": fileName, // "path": fileName,
}, // },
}) // })
if err != nil { // if err != nil {
return // return
} // }
if err = channel.SendText(string(bs)); err != nil { // if err = channel.SendText(string(bs)); err != nil {
return // return
} // }
logger.Println("done") // logger.Println("done")
return // return
} // }

File diff suppressed because it is too large Load Diff

View File

@ -1,234 +1,234 @@
package localserver package localserver
import ( // import (
"encoding/json" // "encoding/json"
"fmt" // "fmt"
"sync/atomic" // "sync/atomic"
"github.com/pion/webrtc/v3" // "github.com/pion/webrtc/v3"
) // )
const ( // const (
SPEAKING = "speaking" // SPEAKING = "speaking"
STOP_SPEAKING = "stop_speaking" // STOP_SPEAKING = "stop_speaking"
MUTE = "mute" // MUTE = "mute"
UNMUTE = "unmute" // UNMUTE = "unmute"
) // )
type WebrtcCallSoundManager struct{} // type WebrtcCallSoundManager struct{}
func NewWebrtcCallSoundManager() *WebrtcCallSoundManager { // func NewWebrtcCallSoundManager() *WebrtcCallSoundManager {
return new(WebrtcCallSoundManager) // return new(WebrtcCallSoundManager)
} // }
func (w *WebrtcCallSoundManager) HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error) { // func (w *WebrtcCallSoundManager) HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error) {
done, errCh := make(chan struct{}), make(chan error) // done, errCh := make(chan struct{}), make(chan error)
go func() { // go func() {
logger.Println("got an event in call sound manager", from, eventId, payload) // logger.Println("got an event in call sound manager", from, eventId, payload)
switch eventId { // switch eventId {
case UNMUTE: // case UNMUTE:
if e := w.unmute(from, squadId, manager); e != nil { // if e := w.unmute(from, squadId, manager); e != nil {
errCh <- e // errCh <- e
return // return
} // }
case MUTE: // case MUTE:
if e := w.mute(from, squadId, manager); e != nil { // if e := w.mute(from, squadId, manager); e != nil {
errCh <- e // errCh <- e
return // return
} // }
case SPEAKING: // case SPEAKING:
if e := w.sendSpeakingEvent(from, squadId, manager); e != nil { // if e := w.sendSpeakingEvent(from, squadId, manager); e != nil {
errCh <- e // errCh <- e
return // return
} // }
case STOP_SPEAKING: // case STOP_SPEAKING:
if e := w.sendStopSpeakingEvent(from, squadId, manager); e != nil { // if e := w.sendStopSpeakingEvent(from, squadId, manager); e != nil {
errCh <- e // errCh <- e
return // return
} // }
} // }
done <- struct{}{} // done <- struct{}{}
}() // }()
select { // select {
case <-done: // case <-done:
return nil // return nil
case err = <-errCh: // case err = <-errCh:
return // return
} // }
} // }
func (w *WebrtcCallSoundManager) unmute(peerId string, squadId string, manager *WebRTCCallManager) (err error) { // func (w *WebrtcCallSoundManager) unmute(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
if _, ok := manager.Squads[squadId]; !ok { // if _, ok := manager.Squads[squadId]; !ok {
err = fmt.Errorf("no correponding squad found") // err = fmt.Errorf("no correponding squad found")
return // return
} // }
logger.Println("sending unnmute event", peerId) // logger.Println("sending unnmute event", peerId)
manager.RemoteTracksMux.RLock() // manager.RemoteTracksMux.RLock()
for _, v := range manager.RemoteTracks[peerId] { // for _, v := range manager.RemoteTracks[peerId] {
if v.Track.Kind() == webrtc.RTPCodecTypeAudio { // if v.Track.Kind() == webrtc.RTPCodecTypeAudio {
atomic.SwapInt32(v.rdv, 0) // atomic.SwapInt32(v.rdv, 0)
} // }
} // }
manager.RemoteTracksMux.RUnlock() // manager.RemoteTracksMux.RUnlock()
manager.SquadMapMux.Lock() // manager.SquadMapMux.Lock()
defer manager.SquadMapMux.Unlock() // defer manager.SquadMapMux.Unlock()
for _, member := range manager.Squads[squadId].Members { // for _, member := range manager.Squads[squadId].Members {
manager.DataChannelMapMux.Lock() // manager.DataChannelMapMux.Lock()
if _, ok := manager.DataChannels[member]; ok && member != peerId { // if _, ok := manager.DataChannels[member]; ok && member != peerId {
bs, marshalErr := json.Marshal(map[string]interface{}{ // bs, marshalErr := json.Marshal(map[string]interface{}{
"type": UNMUTE, // "type": UNMUTE,
"from": peerId, // "from": peerId,
"payload": map[string]interface{}{}, // "payload": map[string]interface{}{},
}) // })
if marshalErr != nil { // if marshalErr != nil {
logger.Println(err) // logger.Println(err)
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
continue // continue
} // }
lock: // lock:
for { // for {
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) { // if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
defer atomic.SwapUint32(manager.DataChannels[member].l, 0) // defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil { // if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
logger.Println(sendErr) // logger.Println(sendErr)
} // }
break lock // break lock
} else { // } else {
continue // continue
} // }
} // }
} // }
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
} // }
return // return
} // }
func (w *WebrtcCallSoundManager) mute(peerId string, squadId string, manager *WebRTCCallManager) (err error) { // func (w *WebrtcCallSoundManager) mute(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
if _, ok := manager.Squads[squadId]; !ok { // if _, ok := manager.Squads[squadId]; !ok {
err = fmt.Errorf("no correponding squad found") // err = fmt.Errorf("no correponding squad found")
return // return
} // }
logger.Println("sending mute event", peerId) // logger.Println("sending mute event", peerId)
manager.RemoteTracksMux.RLock() // manager.RemoteTracksMux.RLock()
for _, v := range manager.RemoteTracks[peerId] { // for _, v := range manager.RemoteTracks[peerId] {
if v.Track.Kind() == webrtc.RTPCodecTypeAudio { // if v.Track.Kind() == webrtc.RTPCodecTypeAudio {
atomic.SwapInt32(v.rdv, 1) // atomic.SwapInt32(v.rdv, 1)
} // }
} // }
manager.RemoteTracksMux.RUnlock() // manager.RemoteTracksMux.RUnlock()
manager.SquadMapMux.Lock() // manager.SquadMapMux.Lock()
defer manager.SquadMapMux.Unlock() // defer manager.SquadMapMux.Unlock()
for _, member := range manager.Squads[squadId].Members { // for _, member := range manager.Squads[squadId].Members {
manager.DataChannelMapMux.Lock() // manager.DataChannelMapMux.Lock()
if _, ok := manager.DataChannels[member]; ok && member != peerId { // if _, ok := manager.DataChannels[member]; ok && member != peerId {
bs, marshalErr := json.Marshal(map[string]interface{}{ // bs, marshalErr := json.Marshal(map[string]interface{}{
"type": MUTE, // "type": MUTE,
"from": peerId, // "from": peerId,
"payload": map[string]interface{}{}, // "payload": map[string]interface{}{},
}) // })
if marshalErr != nil { // if marshalErr != nil {
logger.Println(err) // logger.Println(err)
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
continue // continue
} // }
lock: // lock:
for { // for {
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) { // if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
defer atomic.SwapUint32(manager.DataChannels[member].l, 0) // defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil { // if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
logger.Println(sendErr) // logger.Println(sendErr)
} // }
break lock // break lock
} else { // } else {
continue // continue
} // }
} // }
} // }
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
} // }
return // return
} // }
func (w *WebrtcCallSoundManager) sendSpeakingEvent(peerId string, squadId string, manager *WebRTCCallManager) (err error) { // func (w *WebrtcCallSoundManager) sendSpeakingEvent(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
if _, ok := manager.Squads[squadId]; !ok { // if _, ok := manager.Squads[squadId]; !ok {
err = fmt.Errorf("no correponding squad found") // err = fmt.Errorf("no correponding squad found")
return // return
} // }
logger.Println("sending speaking event", peerId) // logger.Println("sending speaking event", peerId)
manager.SquadMapMux.Lock() // manager.SquadMapMux.Lock()
defer manager.SquadMapMux.Unlock() // defer manager.SquadMapMux.Unlock()
for _, member := range manager.Squads[squadId].Members { // for _, member := range manager.Squads[squadId].Members {
manager.DataChannelMapMux.Lock() // manager.DataChannelMapMux.Lock()
if _, ok := manager.DataChannels[member]; ok && member != peerId { // if _, ok := manager.DataChannels[member]; ok && member != peerId {
bs, marshalErr := json.Marshal(map[string]interface{}{ // bs, marshalErr := json.Marshal(map[string]interface{}{
"type": SPEAKING, // "type": SPEAKING,
"from": peerId, // "from": peerId,
"payload": map[string]interface{}{ // "payload": map[string]interface{}{
"userId": peerId, // "userId": peerId,
"speaking": true, // "speaking": true,
}, // },
}) // })
if marshalErr != nil { // if marshalErr != nil {
logger.Println(err) // logger.Println(err)
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
continue // continue
} // }
lock: // lock:
for { // for {
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) { // if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
defer atomic.SwapUint32(manager.DataChannels[member].l, 0) // defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil { // if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
logger.Println(sendErr) // logger.Println(sendErr)
} // }
break lock // break lock
} else { // } else {
continue // continue
} // }
} // }
} // }
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
} // }
return // return
} // }
func (w *WebrtcCallSoundManager) sendStopSpeakingEvent(peerId string, squadId string, manager *WebRTCCallManager) (err error) { // func (w *WebrtcCallSoundManager) sendStopSpeakingEvent(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
if _, ok := manager.Squads[squadId]; !ok { // if _, ok := manager.Squads[squadId]; !ok {
err = fmt.Errorf("no correponding squad found") // err = fmt.Errorf("no correponding squad found")
return // return
} // }
logger.Println("sending stop speaking event", peerId) // logger.Println("sending stop speaking event", peerId)
manager.SquadMapMux.Lock() // manager.SquadMapMux.Lock()
defer manager.SquadMapMux.Unlock() // defer manager.SquadMapMux.Unlock()
for _, member := range manager.Squads[squadId].Members { // for _, member := range manager.Squads[squadId].Members {
manager.DataChannelMapMux.Lock() // manager.DataChannelMapMux.Lock()
if _, ok := manager.DataChannels[member]; ok && member != peerId { // if _, ok := manager.DataChannels[member]; ok && member != peerId {
bs, marshalErr := json.Marshal(map[string]interface{}{ // bs, marshalErr := json.Marshal(map[string]interface{}{
"type": STOP_SPEAKING, // "type": STOP_SPEAKING,
"from": peerId, // "from": peerId,
"payload": map[string]interface{}{ // "payload": map[string]interface{}{
"userId": peerId, // "userId": peerId,
"speaking": false, // "speaking": false,
}, // },
}) // })
if marshalErr != nil { // if marshalErr != nil {
logger.Println(err) // logger.Println(err)
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
continue // continue
} // }
lock: // lock:
for { // for {
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) { // if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
defer atomic.SwapUint32(manager.DataChannels[member].l, 0) // defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil { // if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
logger.Println(sendErr) // logger.Println(sendErr)
} // }
break lock // break lock
} else { // } else {
continue // continue
} // }
} // }
} // }
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
} // }
return // return
} // }

View File

@ -1,131 +1,131 @@
package localserver package localserver
import ( // import (
"encoding/json" // "encoding/json"
"fmt" // "fmt"
"sync/atomic" // "sync/atomic"
"github.com/pion/webrtc/v3" // "github.com/pion/webrtc/v3"
) // )
const ( // const (
VIDEO = "video" // VIDEO = "video"
STOP_VIDEO = "stop_video" // STOP_VIDEO = "stop_video"
) // )
type WebrtcCallVideoManager struct{} // type WebrtcCallVideoManager struct{}
func NewWebrtcCallVideoManager() *WebrtcCallVideoManager { // func NewWebrtcCallVideoManager() *WebrtcCallVideoManager {
return new(WebrtcCallVideoManager) // return new(WebrtcCallVideoManager)
} // }
func (w *WebrtcCallVideoManager) HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error) { // func (w *WebrtcCallVideoManager) HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error) {
done, errCh := make(chan struct{}), make(chan error) // done, errCh := make(chan struct{}), make(chan error)
go func() { // go func() {
logger.Println("got an event in call video manager", from, eventId, payload) // logger.Println("got an event in call video manager", from, eventId, payload)
switch eventId { // switch eventId {
case VIDEO: // case VIDEO:
if e := w.video(from, squadId, manager); e != nil { // if e := w.video(from, squadId, manager); e != nil {
errCh <- e // errCh <- e
return // return
} // }
case STOP_VIDEO: // case STOP_VIDEO:
if e := w.stopVideo(from, squadId, manager); e != nil { // if e := w.stopVideo(from, squadId, manager); e != nil {
errCh <- e // errCh <- e
return // return
} // }
} // }
done <- struct{}{} // done <- struct{}{}
}() // }()
select { // select {
case <-done: // case <-done:
return nil // return nil
case err = <-errCh: // case err = <-errCh:
return // return
} // }
} // }
func (w *WebrtcCallVideoManager) video(peerId string, squadId string, manager *WebRTCCallManager) (err error) { // func (w *WebrtcCallVideoManager) video(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
if _, ok := manager.Squads[squadId]; !ok { // if _, ok := manager.Squads[squadId]; !ok {
err = fmt.Errorf("no correponding squad found") // err = fmt.Errorf("no correponding squad found")
return // return
} // }
logger.Println("sending video event", peerId) // logger.Println("sending video event", peerId)
manager.RemoteTracksMux.RLock() // manager.RemoteTracksMux.RLock()
for _, v := range manager.RemoteTracks[peerId] { // for _, v := range manager.RemoteTracks[peerId] {
if v.Track.Kind() == webrtc.RTPCodecTypeVideo { // if v.Track.Kind() == webrtc.RTPCodecTypeVideo {
atomic.SwapInt32(v.rdv, 0) // atomic.SwapInt32(v.rdv, 0)
} // }
} // }
manager.RemoteTracksMux.RUnlock() // manager.RemoteTracksMux.RUnlock()
manager.SquadMapMux.Lock() // manager.SquadMapMux.Lock()
defer manager.SquadMapMux.Unlock() // defer manager.SquadMapMux.Unlock()
for _, member := range manager.Squads[squadId].Members { // for _, member := range manager.Squads[squadId].Members {
manager.DataChannelMapMux.Lock() // manager.DataChannelMapMux.Lock()
if _, ok := manager.DataChannels[member]; ok && member != peerId { // if _, ok := manager.DataChannels[member]; ok && member != peerId {
bs, marshalErr := json.Marshal(map[string]interface{}{ // bs, marshalErr := json.Marshal(map[string]interface{}{
"type": VIDEO, // "type": VIDEO,
"from": peerId, // "from": peerId,
"payload": map[string]interface{}{}, // "payload": map[string]interface{}{},
}) // })
if marshalErr != nil { // if marshalErr != nil {
logger.Println(err) // logger.Println(err)
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
continue // continue
} // }
if sendErr := atomicallyExecute(manager.DataChannels[member].l, func() (err error) { // if sendErr := atomicallyExecute(manager.DataChannels[member].l, func() (err error) {
err = manager.DataChannels[member].DataChannel.SendText(string(bs)) // err = manager.DataChannels[member].DataChannel.SendText(string(bs))
return // return
}); sendErr != nil { // }); sendErr != nil {
logger.Println(sendErr) // logger.Println(sendErr)
} // }
} // }
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
} // }
return // return
} // }
func (w *WebrtcCallVideoManager) stopVideo(peerId string, squadId string, manager *WebRTCCallManager) (err error) { // func (w *WebrtcCallVideoManager) stopVideo(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
if _, ok := manager.Squads[squadId]; !ok { // if _, ok := manager.Squads[squadId]; !ok {
err = fmt.Errorf("no correponding squad found") // err = fmt.Errorf("no correponding squad found")
return // return
} // }
logger.Println("sending stop video event", peerId) // logger.Println("sending stop video event", peerId)
manager.RemoteTracksMux.RLock() // manager.RemoteTracksMux.RLock()
for _, v := range manager.RemoteTracks[peerId] { // for _, v := range manager.RemoteTracks[peerId] {
if v.Track.Kind() == webrtc.RTPCodecTypeVideo { // if v.Track.Kind() == webrtc.RTPCodecTypeVideo {
atomic.SwapInt32(v.rdv, 1) // atomic.SwapInt32(v.rdv, 1)
} // }
} // }
manager.RemoteTracksMux.RUnlock() // manager.RemoteTracksMux.RUnlock()
manager.SquadMapMux.Lock() // manager.SquadMapMux.Lock()
defer manager.SquadMapMux.Unlock() // defer manager.SquadMapMux.Unlock()
for _, member := range manager.Squads[squadId].Members { // for _, member := range manager.Squads[squadId].Members {
manager.DataChannelMapMux.Lock() // manager.DataChannelMapMux.Lock()
if _, ok := manager.DataChannels[member]; ok && member != peerId { // if _, ok := manager.DataChannels[member]; ok && member != peerId {
bs, marshalErr := json.Marshal(map[string]interface{}{ // bs, marshalErr := json.Marshal(map[string]interface{}{
"type": STOP_VIDEO, // "type": STOP_VIDEO,
"from": peerId, // "from": peerId,
"payload": map[string]interface{}{}, // "payload": map[string]interface{}{},
}) // })
if marshalErr != nil { // if marshalErr != nil {
logger.Println(err) // logger.Println(err)
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
continue // continue
} // }
for { // for {
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) { // if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
defer atomic.SwapUint32(manager.DataChannels[member].l, 0) // defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil { // if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
logger.Println(sendErr) // logger.Println(sendErr)
} // }
break // break
} else { // } else {
continue // continue
} // }
} // }
} // }
manager.DataChannelMapMux.Unlock() // manager.DataChannelMapMux.Unlock()
} // }
return // return
} // }

View File

@ -1,332 +1,311 @@
package localserver package localserver
import ( // import (
context "context" // context "context"
"encoding/json" // "encoding/json"
"fmt" // "fmt"
"io" // "io"
sync "sync" // sync "sync"
"github.com/pion/webrtc/v3" // "github.com/pion/webrtc/v3"
) // )
type WebrtcFsManager struct { // type WebrtcFsManager struct {
stream SignalingService_LinkClient // stream SignalingService_LinkClient
DatachannelManager DataChannelManager // DatachannelManager DataChannelManager
LocalSD map[string]*webrtc.SessionDescription // LocalSD map[string]*webrtc.SessionDescription
RTCPeerConnections map[string]*webrtc.PeerConnection // RTCPeerConnections map[string]*webrtc.PeerConnection
DataChannels map[string]*webrtc.DataChannel // DataChannels map[string]*webrtc.DataChannel
PendingCandidates map[string][]*webrtc.ICECandidate // PendingCandidates map[string][]*webrtc.ICECandidate
CandidateChannel chan *IncomingCandidate // CandidateChannel chan *IncomingCandidate
CandidateMux *sync.RWMutex // CandidateMux *sync.RWMutex
RTCPeerConnectionMapMux *sync.RWMutex // RTCPeerConnectionMapMux *sync.RWMutex
DataChannelMapMux *sync.RWMutex // DataChannelMapMux *sync.RWMutex
LocalSDMapMux *sync.RWMutex // LocalSDMapMux *sync.RWMutex
} // }
func NewWebrtcFsManager(dataChannelManager DataChannelManager) (webrtcFsManager *WebrtcFsManager, err error) { // func NewWebrtcFsManager(dataChannelManager DataChannelManager) (webrtcFsManager *WebrtcFsManager, err error) {
webrtcFsManager = &WebrtcFsManager{ // webrtcFsManager = &WebrtcFsManager{
DatachannelManager: dataChannelManager, // DatachannelManager: dataChannelManager,
DataChannels: make(map[string]*webrtc.DataChannel), // DataChannels: make(map[string]*webrtc.DataChannel),
PendingCandidates: make(map[string][]*webrtc.ICECandidate), // PendingCandidates: make(map[string][]*webrtc.ICECandidate),
LocalSD: make(map[string]*webrtc.SessionDescription), // LocalSD: make(map[string]*webrtc.SessionDescription),
RTCPeerConnections: make(map[string]*webrtc.PeerConnection), // RTCPeerConnections: make(map[string]*webrtc.PeerConnection),
RTCPeerConnectionMapMux: &sync.RWMutex{}, // RTCPeerConnectionMapMux: &sync.RWMutex{},
LocalSDMapMux: &sync.RWMutex{}, // LocalSDMapMux: &sync.RWMutex{},
CandidateMux: &sync.RWMutex{}, // CandidateMux: &sync.RWMutex{},
DataChannelMapMux: &sync.RWMutex{}, // DataChannelMapMux: &sync.RWMutex{},
} // }
return // return
} // }
func (wf *WebrtcFsManager) sendSignalingMessage(messageType, from, to string, payload map[string]interface{}) (err error) { // func (wf *WebrtcFsManager) sendSignalingMessage(messageType, from, to string, payload map[string]interface{}) (err error) {
bs, err := json.Marshal(payload) // bs, err := json.Marshal(payload)
if err != nil { // if err != nil {
return // return
} // }
err = wf.stream.Send(&SignalingMessage{ // err = wf.stream.Send(&SignalingMessage{
Type: messageType, // Type: messageType,
From: from, // From: from,
To: to, // To: to,
Payload: bs, // Payload: bs,
}) // })
return // return
} // }
func (wf *WebrtcFsManager) CreateOffer(ctx context.Context, target string, from string, cb OnICECandidateFunc) (err error) { // func (wf *WebrtcFsManager) CreateOffer(ctx context.Context, target string, from string, cb OnICECandidateFunc) (err error) {
peerConnection, err := wf.createPeerConnection(target, from, webrtc.SDPTypeOffer, cb) // peerConnection, err := wf.createPeerConnection(target, from, webrtc.SDPTypeOffer, cb)
if err != nil { // if err != nil {
return // return
} // }
rawOffer, err := peerConnection.CreateOffer(nil) // rawOffer, err := peerConnection.CreateOffer(nil)
if err != nil { // if err != nil {
return // return
} // }
if err = peerConnection.SetLocalDescription(rawOffer); err != nil { // if err = peerConnection.SetLocalDescription(rawOffer); err != nil {
return // return
} // }
wf.RTCPeerConnectionMapMux.Lock() // wf.RTCPeerConnectionMapMux.Lock()
logger.Println("adding for target", target) // logger.Println("adding for target", target)
wf.RTCPeerConnections[target] = peerConnection // wf.RTCPeerConnections[target] = peerConnection
wf.RTCPeerConnectionMapMux.Unlock() // wf.RTCPeerConnectionMapMux.Unlock()
err = wf.sendSignalingMessage(string(WEBRTC_OFFER_FS), "lolo_local_serv", target, map[string]any{ // err = wf.sendSignalingMessage(string(WEBRTC_OFFER_FS), "lolo_local_serv", target, map[string]any{
"to": target, // "to": target,
"from": "lolo_local_serv",
"sdp": rawOffer.SDP,
})
return
}
func (wf *WebrtcFsManager) HandleOffer(ctx context.Context, req map[string]string, cb OnICECandidateFunc) (err error) {
done, errCh := make(chan struct{}), make(chan error)
go func() {
peerConnection, err := wf.createPeerConnection(req[FROM], req[TO], webrtc.SDPTypeAnswer, cb)
if err != nil {
errCh <- err
return
}
wf.RTCPeerConnectionMapMux.Lock()
wf.RTCPeerConnections[req[FROM]] = peerConnection
wf.RTCPeerConnectionMapMux.Unlock()
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
}
wf.LocalSDMapMux.Lock()
wf.LocalSD[req[FROM]] = &rawAnswer
wf.LocalSDMapMux.Unlock()
// if err = wf.stream.Send(&Request{
// Type: string(WEBRTC_ANSWER_FS),
// From: "lolo_local_serv",
// Token: "none",
// Payload: map[string]string{
// "to": req[FROM],
// "from": "lolo_local_serv", // "from": "lolo_local_serv",
// "sdp": rawAnswer.SDP, // "sdp": rawOffer.SDP,
// }, // })
// }); err != nil { // return
// }
// func (wf *WebrtcFsManager) HandleOffer(ctx context.Context, req map[string]string, cb OnICECandidateFunc) (err error) {
// done, errCh := make(chan struct{}), make(chan error)
// go func() {
// peerConnection, err := wf.createPeerConnection(req[FROM], req[TO], webrtc.SDPTypeAnswer, cb)
// if err != nil {
// errCh <- err // errCh <- err
// return // return
// } // }
done <- struct{}{} // wf.RTCPeerConnectionMapMux.Lock()
}() // wf.RTCPeerConnections[req[FROM]] = peerConnection
select { // wf.RTCPeerConnectionMapMux.Unlock()
case <-done: // offer := webrtc.SessionDescription{
return // Type: webrtc.SDPTypeOffer,
case err = <-errCh: // SDP: req[SDP],
return // }
case <-ctx.Done(): // if err = peerConnection.SetRemoteDescription(offer); err != nil {
err = ctx.Err() // errCh <- err
return // return
} // }
} // rawAnswer, err := peerConnection.CreateAnswer(nil)
// if err != nil {
// errCh <- err
// return
// }
// wf.LocalSDMapMux.Lock()
// wf.LocalSD[req[FROM]] = &rawAnswer
// wf.LocalSDMapMux.Unlock()
// // if err = wf.stream.Send(&Request{
// // Type: string(WEBRTC_ANSWER_FS),
// // From: "lolo_local_serv",
// // Token: "none",
// // Payload: map[string]string{
// // "to": req[FROM],
// // "from": "lolo_local_serv",
// // "sdp": rawAnswer.SDP,
// // },
// // }); err != nil {
// // errCh <- err
// // return
// // }
// done <- struct{}{}
// }()
// select {
// case <-done:
// return
// case err = <-errCh:
// return
// case <-ctx.Done():
// err = ctx.Err()
// return
// }
// }
func (wf *WebrtcFsManager) HandleAnswer(ctx context.Context, req map[string]string) (err error) { // func (wf *WebrtcFsManager) HandleAnswer(ctx context.Context, req map[string]string) (err error) {
wf.RTCPeerConnectionMapMux.RLock() // wf.RTCPeerConnectionMapMux.RLock()
defer wf.RTCPeerConnectionMapMux.RUnlock() // defer wf.RTCPeerConnectionMapMux.RUnlock()
defer func() { // defer func() {
if r := recover(); err != nil { // if r := recover(); err != nil {
logger.Printf("recover from panic : %v\n", r) // logger.Printf("recover from panic : %v\n", r)
} // }
}() // }()
if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok { // if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok {
err = fmt.Errorf("no corresponding peer connection for id : %s", req[FROM]) // err = fmt.Errorf("no corresponding peer connection for id : %s", req[FROM])
return // return
} // }
peerConnnection := wf.RTCPeerConnections[req[FROM]] // peerConnnection := wf.RTCPeerConnections[req[FROM]]
if err = peerConnnection.SetRemoteDescription(webrtc.SessionDescription{ // if err = peerConnnection.SetRemoteDescription(webrtc.SessionDescription{
Type: webrtc.SDPTypeAnswer, // Type: webrtc.SDPTypeAnswer,
SDP: req[SDP], // SDP: req[SDP],
}); err != nil {
return
}
// if err = wf.stream.Send(&Request{
// Type: string(WEBRTC_COUNTER_OFFER_FS),
// From: "lolo_local_serv",
// Token: "none",
// Payload: map[string]string{
// "from": "lolo_local_serv",
// "to": req[FROM],
// },
// }); err != nil { // }); err != nil {
// return // return
// } // }
wf.CandidateMux.RLock() // // if err = wf.stream.Send(&Request{
for range wf.PendingCandidates[req[FROM]] { // // Type: string(WEBRTC_COUNTER_OFFER_FS),
logger.Println("sending candidate to", req[FROM]) // // From: "lolo_local_serv",
// if err = wf.stream.Send(&Request{ // // Token: "none",
// Type: string(WEBRTC_CANDIDATE_FS), // // Payload: map[string]string{
// From: "lolo_local_serv", // // "from": "lolo_local_serv",
// Token: "none", // // "to": req[FROM],
// Payload: map[string]string{ // // },
// "from": "lolo_local_serv", // // }); err != nil {
// "to": req[FROM], // // return
// "candidate": candidate.ToJSON().Candidate, // // }
// "sdpMid": *candidate.ToJSON().SDPMid, // wf.CandidateMux.RLock()
// "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), // for range wf.PendingCandidates[req[FROM]] {
// },
// }); err != nil {
// return
// }
}
wf.CandidateMux.RUnlock()
wf.CandidateMux.Lock()
delete(wf.PendingCandidates, req[FROM])
wf.CandidateMux.Unlock()
wf.LocalSDMapMux.Lock()
delete(wf.LocalSD, req[FROM])
wf.LocalSDMapMux.Unlock()
return
}
func (wf *WebrtcFsManager) HandleCounterOffer(ctx context.Context, req map[string]string) (err error) {
wf.RTCPeerConnectionMapMux.RLock()
if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok {
err = fmt.Errorf("no field corresponding peer connection for id %s", req[FROM])
return
}
connection := wf.RTCPeerConnections[req[FROM]]
wf.RTCPeerConnectionMapMux.RUnlock()
wf.LocalSDMapMux.RLock()
if err = connection.SetLocalDescription(*wf.LocalSD[req[FROM]]); err != nil {
return
}
wf.LocalSDMapMux.RUnlock()
wf.CandidateMux.RLock()
for range wf.PendingCandidates[req[FROM]] {
// logger.Println("sending candidate to", req[FROM]) // logger.Println("sending candidate to", req[FROM])
// if err = wf.stream.Send(&Request{ // // if err = wf.stream.Send(&Request{
// Type: string(WEBRTC_CANDIDATE_FS), // // Type: string(WEBRTC_CANDIDATE_FS),
// From: "lolo_local_serv", // // From: "lolo_local_serv",
// Token: "none", // // Token: "none",
// Payload: map[string]string{ // // Payload: map[string]string{
// "from": "lolo_local_serv", // // "from": "lolo_local_serv",
// "to": req[FROM], // // "to": req[FROM],
// "candidate": candidate.ToJSON().Candidate, // // "candidate": candidate.ToJSON().Candidate,
// "sdpMid": *candidate.ToJSON().SDPMid, // // "sdpMid": *candidate.ToJSON().SDPMid,
// "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), // // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
// }, // // },
// }); err != nil { // // }); err != nil {
// // return
// // }
// }
// wf.CandidateMux.RUnlock()
// wf.CandidateMux.Lock()
// delete(wf.PendingCandidates, req[FROM])
// wf.CandidateMux.Unlock()
// wf.LocalSDMapMux.Lock()
// delete(wf.LocalSD, req[FROM])
// wf.LocalSDMapMux.Unlock()
// return // return
// } // }
}
wf.CandidateMux.RUnlock()
return
}
func (wf *WebrtcFsManager) createPeerConnection(target string, from string, peerType webrtc.SDPType, cb OnICECandidateFunc) (peerConnection *webrtc.PeerConnection, err error) { // func (wf *WebrtcFsManager) HandleCounterOffer(ctx context.Context, req map[string]string) (err error) {
defer func() { // wf.RTCPeerConnectionMapMux.RLock()
if r := recover(); err != nil { // if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok {
logger.Printf("recover from panic : %v\n", r) // err = fmt.Errorf("no field corresponding peer connection for id %s", req[FROM])
} // return
}() // }
s := webrtc.SettingEngine{} // connection := wf.RTCPeerConnections[req[FROM]]
s.DetachDataChannels() // wf.RTCPeerConnectionMapMux.RUnlock()
// wf.LocalSDMapMux.RLock()
// if err = connection.SetLocalDescription(*wf.LocalSD[req[FROM]]); err != nil {
// return
// }
// wf.LocalSDMapMux.RUnlock()
// wf.CandidateMux.RLock()
// for range wf.PendingCandidates[req[FROM]] {
// // logger.Println("sending candidate to", req[FROM])
// // if err = wf.stream.Send(&Request{
// // Type: string(WEBRTC_CANDIDATE_FS),
// // From: "lolo_local_serv",
// // Token: "none",
// // Payload: map[string]string{
// // "from": "lolo_local_serv",
// // "to": req[FROM],
// // "candidate": candidate.ToJSON().Candidate,
// // "sdpMid": *candidate.ToJSON().SDPMid,
// // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
// // },
// // }); err != nil {
// // return
// // }
// }
// wf.CandidateMux.RUnlock()
// return
// }
api := webrtc.NewAPI(webrtc.WithSettingEngine(s)) // func (wf *WebrtcFsManager) createPeerConnection(target string, from 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)
// }
// }()
// s := webrtc.SettingEngine{}
// s.DetachDataChannels()
config := webrtc.Configuration{ // api := webrtc.NewAPI(webrtc.WithSettingEngine(s))
ICEServers: []webrtc.ICEServer{
{ // config := webrtc.Configuration{
URLs: []string{"stun:stun.l.google.com:19302", "stun:stunserver.org:3478", "stun:stun.l.google.com:19302?transport=tcp"}, // ICEServers: []webrtc.ICEServer{
}, // {
}, // URLs: []string{"stun:stun.l.google.com:19302", "stun:stun.l.google.com:19302", "stun:stun.l.google.com:19302?transport=tcp"},
SDPSemantics: webrtc.SDPSemanticsUnifiedPlan, // },
} // },
peerConnection, err = api.NewPeerConnection(config) // SDPSemantics: webrtc.SDPSemanticsUnifiedPlan,
if err != nil { // }
return // peerConnection, err = api.NewPeerConnection(config)
} // if err != nil {
if peerType == webrtc.SDPTypeOffer { // return
maxRetransmits := uint16(100) // }
channel, err := peerConnection.CreateDataChannel("data", &webrtc.DataChannelInit{ // if peerType == webrtc.SDPTypeOffer {
MaxRetransmits: &maxRetransmits, // maxRetransmits := uint16(100)
}) // channel, err := peerConnection.CreateDataChannel("data", &webrtc.DataChannelInit{
if err != nil { // MaxRetransmits: &maxRetransmits,
return nil, err // })
} // if err != nil {
channel.OnOpen(func() { // return nil, err
logger.Println("channel opened") // }
if chanErr := channel.SendText("yooo man this is open"); chanErr != nil { // channel.OnOpen(func() {
logger.Println(chanErr) // logger.Println("channel opened")
} // if chanErr := channel.SendText("yooo man this is open"); chanErr != nil {
}) // logger.Println(chanErr)
channel.OnMessage(func(msg webrtc.DataChannelMessage) { // }
logger.Printf("new message %s\n", string(msg.Data)) // })
done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{ // channel.OnMessage(func(msg webrtc.DataChannelMessage) {
From: target, // logger.Printf("new message %s\n", string(msg.Data))
Type: "test", // done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
Payload: &DatachannelMessagePayload{}, // From: target,
}, channel) // Type: "test",
select { // Payload: &DatachannelMessagePayload{},
case <-done: // }, channel)
//logger.Println("done with success") // select {
case e := <-errCh: // case <-done:
logger.Println(e) // //logger.Println("done with success")
logger.Println("this is impossible") // case e := <-errCh:
} // logger.Println(e)
}) // logger.Println("this is impossible")
wf.DataChannelMapMux.Lock() // }
wf.DataChannels[target] = channel // })
wf.DataChannelMapMux.Unlock() // wf.DataChannelMapMux.Lock()
} // wf.DataChannels[target] = channel
peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) { // wf.DataChannelMapMux.Unlock()
logger.Printf("ICE connection state has changed %s\n", is.String()) // }
if is == webrtc.ICEConnectionStateDisconnected || is == webrtc.ICEConnectionStateFailed { // peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) {
logger.Println(is) // logger.Printf("ICE connection state has changed %s\n", is.String())
} // if is == webrtc.ICEConnectionStateDisconnected || is == webrtc.ICEConnectionStateFailed {
}) // logger.Println(is)
peerConnection.OnDataChannel(func(dc *webrtc.DataChannel) { // }
dc.OnOpen(func() { // })
logger.Printf("got a new open datachannel %s\n", dc.Label()) // peerConnection.OnDataChannel(func(dc *webrtc.DataChannel) {
dataChann, err := dc.Detach() // dc.OnOpen(func() {
if err != nil { // logger.Printf("got a new open datachannel %s\n", dc.Label())
logger.Println(err) // dataChann, err := dc.Detach()
return // if err != nil {
} // logger.Println(err)
for { // return
var x []byte = make([]byte, 2<<15) // }
n, _, err := dataChann.ReadDataChannel(x) // for {
if err != nil { // var x []byte = make([]byte, 2<<15)
logger.Println(err) // n, _, err := dataChann.ReadDataChannel(x)
if err == io.EOF { // if err != nil {
return // logger.Println(err)
} // if err == io.EOF {
continue // return
} // }
go func(msg []byte) { // continue
var dataChannelMessage DatachannelMessage // }
if unmarshalErr := json.Unmarshal(msg, &dataChannelMessage); unmarshalErr != nil { // go func(msg []byte) {
logger.Println(unmarshalErr)
return
}
done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
From: dataChannelMessage.From,
Type: dataChannelMessage.Type,
Payload: dataChannelMessage.Payload,
}, dc)
select {
case <-done:
//logger.Println("done with success")
case e := <-errCh:
logger.Println(e)
logger.Println("this is impossible")
}
}(x[:n])
}
})
// dc.OnMessage(func(msg webrtc.DataChannelMessage) {
// var dataChannelMessage DatachannelMessage // var dataChannelMessage DatachannelMessage
// if unmarshalErr := json.Unmarshal(msg.Data, &dataChannelMessage); unmarshalErr != nil { // if unmarshalErr := json.Unmarshal(msg, &dataChannelMessage); unmarshalErr != nil {
// logger.Println(unmarshalErr) // logger.Println(unmarshalErr)
// return // return
// } // }
@ -342,108 +321,129 @@ func (wf *WebrtcFsManager) createPeerConnection(target string, from string, peer
// logger.Println(e) // logger.Println(e)
// logger.Println("this is impossible") // logger.Println("this is impossible")
// } // }
// }(x[:n])
// }
// }) // })
}) // // dc.OnMessage(func(msg webrtc.DataChannelMessage) {
peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) { // // var dataChannelMessage DatachannelMessage
if i == nil { // // if unmarshalErr := json.Unmarshal(msg.Data, &dataChannelMessage); unmarshalErr != nil {
return // // logger.Println(unmarshalErr)
} // // return
wf.CandidateMux.Lock() // // }
defer wf.CandidateMux.Unlock() // // done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
desc := peerConnection.RemoteDescription() // // From: dataChannelMessage.From,
if desc == nil { // // Type: dataChannelMessage.Type,
wf.PendingCandidates[target] = append(wf.PendingCandidates[target], i) // // Payload: dataChannelMessage.Payload,
} else { // // }, dc)
logger.Println(i) // // select {
if iceCandidateErr := cb(target, i); iceCandidateErr != nil { // // case <-done:
logger.Println(iceCandidateErr) // // //logger.Println("done with success")
} // // case e := <-errCh:
} // // logger.Println(e)
}) // // logger.Println("this is impossible")
peerConnection.OnNegotiationNeeded(func() { // // }
if peerConnection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && peerConnection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer { // // })
wf.RTCPeerConnectionMapMux.Lock() // })
defer wf.RTCPeerConnectionMapMux.Unlock() // peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
for _, connection := range wf.RTCPeerConnections { // if i == nil {
if connection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && connection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer { // return
localSd, err := connection.CreateOffer(nil) // }
if err != nil { // wf.CandidateMux.Lock()
logger.Println(err) // defer wf.CandidateMux.Unlock()
return // desc := peerConnection.RemoteDescription()
} // if desc == nil {
if err = connection.SetLocalDescription(localSd); err != nil { // wf.PendingCandidates[target] = append(wf.PendingCandidates[target], i)
logger.Println(err) // } else {
return // logger.Println(i)
} // if iceCandidateErr := cb(target, i); iceCandidateErr != nil {
// if err = wf.stream.Send(&Request{ // logger.Println(iceCandidateErr)
// Type: string(WEBRTC_RENNEGOTIATION_OFFER_FS), // }
// From: "lolo_local_serv", // }
// Token: "", // })
// Payload: map[string]string{ // peerConnection.OnNegotiationNeeded(func() {
// "to": id, // if peerConnection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && peerConnection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
// "sdp": localSd.SDP, // wf.RTCPeerConnectionMapMux.Lock()
// }, // defer wf.RTCPeerConnectionMapMux.Unlock()
// }); err != nil { // for _, connection := range wf.RTCPeerConnections {
// if connection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && connection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
// localSd, err := connection.CreateOffer(nil)
// if err != nil {
// logger.Println(err) // logger.Println(err)
// return // return
// } // }
} // if err = connection.SetLocalDescription(localSd); err != nil {
}
}
})
return
}
func (wf *WebrtcFsManager) HandleRennegotiationOffer(from string, dst string, sdp string) (err error) {
wf.RTCPeerConnectionMapMux.Lock()
defer wf.RTCPeerConnectionMapMux.Unlock()
if _, ok := wf.RTCPeerConnections[from]; !ok {
err = fmt.Errorf("no corresponding peer connection for id %s", from)
return
}
if wf.RTCPeerConnections[from].SignalingState() != webrtc.SignalingStateStable {
err = fmt.Errorf("rennego called in wrong state")
return
}
if err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
return
}
localSd, err := wf.RTCPeerConnections[from].CreateAnswer(nil)
if err != nil {
return
}
if err = wf.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
return
}
// if err = wf.stream.Send(&Request{
// Type: string(WEBRTC_RENNEGOTIATION_ANSWER_FS),
// From: "lolo_local_serv",
// Token: "",
// Payload: map[string]string{
// "to": from,
// "sdp": localSd.SDP,
// },
// }); err != nil {
// logger.Println(err) // logger.Println(err)
// return // return
// } // }
return // // if err = wf.stream.Send(&Request{
} // // Type: string(WEBRTC_RENNEGOTIATION_OFFER_FS),
// // From: "lolo_local_serv",
// // Token: "",
// // Payload: map[string]string{
// // "to": id,
// // "sdp": localSd.SDP,
// // },
// // }); err != nil {
// // logger.Println(err)
// // return
// // }
// }
// }
// }
// })
// return
// }
func (wf *WebrtcFsManager) HandleRennegotiationAnswer(from string, dst string, sdp string) (err error) { // func (wf *WebrtcFsManager) HandleRennegotiationOffer(from string, dst string, sdp string) (err error) {
wf.RTCPeerConnectionMapMux.Lock() // wf.RTCPeerConnectionMapMux.Lock()
defer wf.RTCPeerConnectionMapMux.Unlock() // defer wf.RTCPeerConnectionMapMux.Unlock()
if _, ok := wf.RTCPeerConnections[from]; !ok { // if _, ok := wf.RTCPeerConnections[from]; !ok {
err = fmt.Errorf("no corresponding peer connection for id %s", from) // err = fmt.Errorf("no corresponding peer connection for id %s", from)
return // return
} // }
err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer}) // if wf.RTCPeerConnections[from].SignalingState() != webrtc.SignalingStateStable {
return // err = fmt.Errorf("rennego called in wrong state")
} // return
// }
// if err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
// return
// }
// localSd, err := wf.RTCPeerConnections[from].CreateAnswer(nil)
// if err != nil {
// return
// }
// if err = wf.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
// return
// }
// // if err = wf.stream.Send(&Request{
// // Type: string(WEBRTC_RENNEGOTIATION_ANSWER_FS),
// // From: "lolo_local_serv",
// // Token: "",
// // Payload: map[string]string{
// // "to": from,
// // "sdp": localSd.SDP,
// // },
// // }); err != nil {
// // logger.Println(err)
// // return
// // }
// return
// }
func (wf *WebrtcFsManager) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) { // func (wf *WebrtcFsManager) HandleRennegotiationAnswer(from string, dst string, sdp string) (err error) {
wf.RTCPeerConnectionMapMux.Lock() // wf.RTCPeerConnectionMapMux.Lock()
defer wf.RTCPeerConnectionMapMux.Unlock() // defer wf.RTCPeerConnectionMapMux.Unlock()
err = wf.RTCPeerConnections[from].AddICECandidate(*candidate) // if _, ok := wf.RTCPeerConnections[from]; !ok {
return // err = fmt.Errorf("no corresponding peer connection for id %s", from)
} // return
// }
// err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer})
// return
// }
// func (wf *WebrtcFsManager) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) {
// wf.RTCPeerConnectionMapMux.Lock()
// defer wf.RTCPeerConnectionMapMux.Unlock()
// err = wf.RTCPeerConnections[from].AddICECandidate(*candidate)
// return
// }

View File

@ -1,167 +1,167 @@
package localserver package localserver
import ( // import (
context "context" // context "context"
"encoding/json" // "encoding/json"
"strconv" // "strconv"
"github.com/pion/webrtc/v3" // "github.com/pion/webrtc/v3"
) // )
const ( // const (
INCOMING_PEER_FS ReqType = "incoming_peer_fs" // INCOMING_PEER_FS ReqType = "incoming_peer_fs"
LEAVING_PEER_FS ReqType = "leaving_peer_fs" // LEAVING_PEER_FS ReqType = "leaving_peer_fs"
WEBRTC_OFFER_FS ReqType = "offer_fs" // WEBRTC_OFFER_FS ReqType = "offer_fs"
WEBRTC_ANSWER_FS ReqType = "answer_fs" // WEBRTC_ANSWER_FS ReqType = "answer_fs"
WEBRTC_RENNEGOTIATION_OFFER_FS ReqType = "rennegotiation_offer_fs" // WEBRTC_RENNEGOTIATION_OFFER_FS ReqType = "rennegotiation_offer_fs"
WEBRTC_RENNEGOTIATION_ANSWER_FS ReqType = "rennegotiation_answer_fs" // WEBRTC_RENNEGOTIATION_ANSWER_FS ReqType = "rennegotiation_answer_fs"
WEBRTC_COUNTER_OFFER_FS ReqType = "webrtc_counter_offer_fs" // WEBRTC_COUNTER_OFFER_FS ReqType = "webrtc_counter_offer_fs"
WEBRTC_CANDIDATE_FS ReqType = "webrtc_candidate_fs" // WEBRTC_CANDIDATE_FS ReqType = "webrtc_candidate_fs"
) // )
type WebRTCFsMiddleware struct { // type WebRTCFsMiddleware struct {
Manager *WebrtcFsManager // Manager *WebrtcFsManager
stream SignalingService_LinkClient // stream SignalingService_LinkClient
} // }
func NewWebRTCFsMiddleware(manager *WebrtcFsManager) (webrtcFsMiddleware *WebRTCFsMiddleware) { // func NewWebRTCFsMiddleware(manager *WebrtcFsManager) (webrtcFsMiddleware *WebRTCFsMiddleware) {
webrtcFsMiddleware = &WebRTCFsMiddleware{ // webrtcFsMiddleware = &WebRTCFsMiddleware{
Manager: manager, // Manager: manager,
} // }
return // return
} // }
func (wfm *WebRTCFsMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) { // func (wfm *WebRTCFsMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) {
bs, err := json.Marshal(map[string]string{ // bs, err := json.Marshal(map[string]string{
"from": "lolo_local_serv", // "from": "lolo_local_serv",
"to": to, // "to": to,
"candidate": candidate.ToJSON().Candidate, // "candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid, // "sdpMid": *candidate.ToJSON().SDPMid,
"sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
}) // })
if err != nil { // if err != nil {
return // return
} // }
err = wfm.stream.Send(&SignalingMessage{ // err = wfm.stream.Send(&SignalingMessage{
Type: string(WEBRTC_CANDIDATE_FS), // Type: string(WEBRTC_CANDIDATE_FS),
From: "lolo_local_serv", // From: "lolo_local_serv",
Payload: bs, // Payload: bs,
}) // })
return // return
} // }
func (wfm *WebRTCFsMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) { // func (wfm *WebRTCFsMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) {
done, errCh := make(chan struct{}), make(chan error) // done, errCh := make(chan struct{}), make(chan error)
go func() { // go func() {
var payload map[string]string // var payload map[string]string
if e := json.Unmarshal(req.Payload, &payload); err != nil { // if e := json.Unmarshal(req.Payload, &payload); err != nil {
errCh <- e // errCh <- e
return // return
} // }
switch req.Type { // switch req.Type {
case string(INCOMING_PEER_FS): // case string(INCOMING_PEER_FS):
logger.Println("quit squad called") // logger.Println("quit squad called")
if from, ok := payload[FROM]; ok { // if from, ok := payload[FROM]; ok {
logger.Println(from) // logger.Println(from)
//wfm.Manager.HandleLeavingMember(from) // //wfm.Manager.HandleLeavingMember(from)
done <- struct{}{} // done <- struct{}{}
} // }
case string(PEER_CONNECTION_REQUEST): // case string(PEER_CONNECTION_REQUEST):
if err := validateRequest(payload, FROM, TO); err != nil { // if err := validateRequest(payload, FROM, TO); err != nil {
errCh <- err
return
}
// if err := wfm.Manager.CreateOffer(ctx, payload[FROM], payload[TO], wfm.signalCandidate); err != nil {
// errCh <- err // errCh <- err
// return // return
// } // }
done <- struct{}{} // // if err := wfm.Manager.CreateOffer(ctx, payload[FROM], payload[TO], wfm.signalCandidate); err != nil {
case string(WEBRTC_OFFER_FS): // // errCh <- err
if err := validateRequest(payload, FROM, TO, SDP); err != nil { // // return
errCh <- err // // }
return // done <- struct{}{}
} // case string(WEBRTC_OFFER_FS):
if err := wfm.Manager.HandleOffer(ctx, payload, wfm.signalCandidate); err != nil { // if err := validateRequest(payload, FROM, TO, SDP); err != nil {
errCh <- err // errCh <- err
return // return
} // }
done <- struct{}{} // if err := wfm.Manager.HandleOffer(ctx, payload, wfm.signalCandidate); err != nil {
case string(WEBRTC_ANSWER_FS): // errCh <- err
if err := validateRequest(payload, FROM, TO, SDP); err != nil { // return
errCh <- err // }
return // done <- struct{}{}
} // case string(WEBRTC_ANSWER_FS):
if err := wfm.Manager.HandleAnswer(ctx, payload); err != nil { // if err := validateRequest(payload, FROM, TO, SDP); err != nil {
errCh <- err // errCh <- err
return // return
} // }
done <- struct{}{} // if err := wfm.Manager.HandleAnswer(ctx, payload); err != nil {
case string(WEBRTC_COUNTER_OFFER_FS): // errCh <- err
if err := validateRequest(payload, FROM); err != nil { // return
errCh <- err // }
return // done <- struct{}{}
} // case string(WEBRTC_COUNTER_OFFER_FS):
if err := wfm.Manager.HandleCounterOffer(ctx, payload); err != nil { // if err := validateRequest(payload, FROM); err != nil {
errCh <- err // errCh <- err
return // return
} // }
done <- struct{}{} // if err := wfm.Manager.HandleCounterOffer(ctx, payload); err != nil {
case string(WEBRTC_RENNEGOTIATION_ANSWER_FS): // errCh <- err
if err := validateRequest(payload, FROM, SDP); err != nil { // return
errCh <- err // }
return // done <- struct{}{}
} // case string(WEBRTC_RENNEGOTIATION_ANSWER_FS):
if err := wfm.Manager.HandleRennegotiationAnswer(payload[FROM], "lolo_local_serv", payload[SDP]); err != nil { // if err := validateRequest(payload, FROM, SDP); err != nil {
errCh <- err // errCh <- err
return // return
} // }
done <- struct{}{} // if err := wfm.Manager.HandleRennegotiationAnswer(payload[FROM], "lolo_local_serv", payload[SDP]); err != nil {
case string(WEBRTC_RENNEGOTIATION_OFFER_FS): // errCh <- err
if err := validateRequest(payload, FROM, SDP); err != nil { // return
errCh <- err // }
return // done <- struct{}{}
} // case string(WEBRTC_RENNEGOTIATION_OFFER_FS):
if err := wfm.Manager.HandleRennegotiationOffer(payload[FROM], "", payload[SDP]); err != nil { // if err := validateRequest(payload, FROM, SDP); err != nil {
errCh <- err // errCh <- err
return // return
} // }
done <- struct{}{} // if err := wfm.Manager.HandleRennegotiationOffer(payload[FROM], "", payload[SDP]); err != nil {
case string(WEBRTC_CANDIDATE_FS): // errCh <- err
if err := validateRequest(payload, FROM, "candidate", "sdpMLineIndex", "sdpMid"); err != nil { // return
errCh <- err // }
return // done <- struct{}{}
} // case string(WEBRTC_CANDIDATE_FS):
logger.Println(payload) // if err := validateRequest(payload, FROM, "candidate", "sdpMLineIndex", "sdpMid"); err != nil {
i, err := strconv.Atoi(payload["sdpMLineIndex"]) // errCh <- err
if err != nil { // return
errCh <- err // }
return // logger.Println(payload)
} // i, err := strconv.Atoi(payload["sdpMLineIndex"])
SDPMLineIndex := uint16(i) // if err != nil {
sdpMid := payload["sdpMid"] // errCh <- err
logger.Println(sdpMid, SDPMLineIndex) // return
if err := wfm.Manager.AddCandidate(&webrtc.ICECandidateInit{ // }
Candidate: payload["candidate"], // SDPMLineIndex := uint16(i)
SDPMid: &sdpMid, // sdpMid := payload["sdpMid"]
SDPMLineIndex: &SDPMLineIndex, // logger.Println(sdpMid, SDPMLineIndex)
}, payload[FROM]); err != nil { // if err := wfm.Manager.AddCandidate(&webrtc.ICECandidateInit{
errCh <- err // Candidate: payload["candidate"],
return // SDPMid: &sdpMid,
} // SDPMLineIndex: &SDPMLineIndex,
done <- struct{}{} // }, payload[FROM]); err != nil {
default: // errCh <- err
logger.Println("fs is correctly linked") // return
done <- struct{}{} // }
} // done <- struct{}{}
done <- struct{}{} // default:
}() // logger.Println("fs is correctly linked")
select { // done <- struct{}{}
case <-ctx.Done(): // }
err = ctx.Err() // done <- struct{}{}
return // }()
case <-done: // select {
return // case <-ctx.Done():
case err = <-errCh: // err = ctx.Err()
return // return
} // case <-done:
} // return
// case err = <-errCh:
// return
// }
// }

View File

@ -1,186 +1,186 @@
package localserver package localserver
import ( // import (
"context" // "context"
"encoding/json" // "encoding/json"
"strconv" // "strconv"
"github.com/pion/webrtc/v3" // "github.com/pion/webrtc/v3"
) // )
type GrpcRequestType string // type GrpcRequestType string
const ( // const (
PEER_CONNECTION_REQUEST GrpcRequestType = "peer_connection_request" // PEER_CONNECTION_REQUEST GrpcRequestType = "peer_connection_request"
) // )
const ( // const (
OFFER ReqType = "offer" // OFFER ReqType = "offer"
ANSWER ReqType = "answer" // ANSWER ReqType = "answer"
COUNTER_OFFER ReqType = "webrtc_counter_offer" // COUNTER_OFFER ReqType = "webrtc_counter_offer"
JOIN_HOSTED_SQUAD ReqType = "join_hosted_squad" // JOIN_HOSTED_SQUAD ReqType = "join_hosted_squad"
HOSTED_SQUAD_ACCESS_DENIED ReqType = "hosted_squad_access_denied" // HOSTED_SQUAD_ACCESS_DENIED ReqType = "hosted_squad_access_denied"
HOSTED_SQUAD_STOP_CALL ReqType = "hosted_squad_stop_call" // HOSTED_SQUAD_STOP_CALL ReqType = "hosted_squad_stop_call"
HOSTED_SQUAD_ACCESS_GRANTED ReqType = "hosted_squad_access_granted" // HOSTED_SQUAD_ACCESS_GRANTED ReqType = "hosted_squad_access_granted"
LEAVE_HOSTED_SQUAD ReqType = "leave_hosted_squad" // LEAVE_HOSTED_SQUAD ReqType = "leave_hosted_squad"
INCOMING_MEMBER_HOSTED ReqType = "incoming_member_hosted" // INCOMING_MEMBER_HOSTED ReqType = "incoming_member_hosted"
LEAVING_MEMBER_HOSTED ReqType = "leaving_member_hosted" // LEAVING_MEMBER_HOSTED ReqType = "leaving_member_hosted"
HOSTED_SQUAD_WEBRTC_OFFER ReqType = "hosted_squad_offer" // HOSTED_SQUAD_WEBRTC_OFFER ReqType = "hosted_squad_offer"
HOSTED_SQUAD_WEBRTC_ANSWER ReqType = "hosted_squad_answer" // HOSTED_SQUAD_WEBRTC_ANSWER ReqType = "hosted_squad_answer"
HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER ReqType = "hosted_squad_rennegotiation_offer" // HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER ReqType = "hosted_squad_rennegotiation_offer"
HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "hosted_squad_rennegotiation_answer" // HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "hosted_squad_rennegotiation_answer"
HOSTED_SQUAD_WEBRTC_COUNTER_OFFER ReqType = "hosted_squad_webrtc_counter_offer" // HOSTED_SQUAD_WEBRTC_COUNTER_OFFER ReqType = "hosted_squad_webrtc_counter_offer"
HOSTED_SQUAD_WEBRTC_CANDIDATE ReqType = "hosted_squad_webrtc_candidate" // HOSTED_SQUAD_WEBRTC_CANDIDATE ReqType = "hosted_squad_webrtc_candidate"
HOSTED_SQUAD_REMOVE_VIDEO ReqType = "hosted_squad_remove_video" // HOSTED_SQUAD_REMOVE_VIDEO ReqType = "hosted_squad_remove_video"
GET_HOSTED_SQUAD_TRACKS ReqType = "hosted_squad_get_tracks" // GET_HOSTED_SQUAD_TRACKS ReqType = "hosted_squad_get_tracks"
NEW_HOSTED_SQUAD = "new_hosted_squad" // NEW_HOSTED_SQUAD = "new_hosted_squad"
) // )
type WebRTCGrpcMiddleware struct { // type WebRTCGrpcMiddleware struct {
Manager *WebRTCCallManager // Manager *WebRTCCallManager
stream SignalingService_LinkClient // stream SignalingService_LinkClient
} // }
func NewWebRTCGrpcMiddleware(manager *WebRTCCallManager) (webrtcGrpcMiddleware *WebRTCGrpcMiddleware) { // func NewWebRTCGrpcMiddleware(manager *WebRTCCallManager) (webrtcGrpcMiddleware *WebRTCGrpcMiddleware) {
webrtcGrpcMiddleware = &WebRTCGrpcMiddleware{ // webrtcGrpcMiddleware = &WebRTCGrpcMiddleware{
Manager: manager, // Manager: manager,
} // }
return // return
} // }
func (wgm *WebRTCGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) { // func (wgm *WebRTCGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) {
bs, err := json.Marshal(map[string]string{ // bs, err := json.Marshal(map[string]string{
"from": wgm.Manager.ID, // "from": wgm.Manager.ID,
"to": to, // "to": to,
"candidate": candidate.ToJSON().Candidate, // "candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid, // "sdpMid": *candidate.ToJSON().SDPMid,
"sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
}) // })
if err != nil { // if err != nil {
return // return
} // }
err = wgm.stream.Send(&SignalingMessage{ // err = wgm.stream.Send(&SignalingMessage{
Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE), // Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE),
From: wgm.Manager.ID, // From: wgm.Manager.ID,
To: to, // To: to,
Payload: bs, // Payload: bs,
}) // })
return // return
} // }
func (wgm *WebRTCGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) { // func (wgm *WebRTCGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) {
done, errCh := make(chan struct{}), make(chan error) // done, errCh := make(chan struct{}), make(chan error)
go func() { // go func() {
defer func() { // defer func() {
done <- struct{}{} // done <- struct{}{}
}() // }()
var payload map[string]string // var payload map[string]string
if e := json.Unmarshal(req.Payload, &payload); err != nil { // if e := json.Unmarshal(req.Payload, &payload); err != nil {
errCh <- e // errCh <- e
return // return
} // }
switch req.Type { // switch req.Type {
case NEW_HOSTED_SQUAD: // case NEW_HOSTED_SQUAD:
if err := validateRequest(payload, "ID"); err != nil { // if err := validateRequest(payload, "ID"); err != nil {
errCh <- err // errCh <- err
return // return
} // }
logger.Println("new squad incoming") // logger.Println("new squad incoming")
wgm.Manager.SquadMapMux.Lock() // wgm.Manager.SquadMapMux.Lock()
wgm.Manager.Squads[payload["ID"]] = &Squad{ // wgm.Manager.Squads[payload["ID"]] = &Squad{
ID: payload["ID"], // ID: payload["ID"],
Members: []string{}, // Members: []string{},
} // }
wgm.Manager.SquadMapMux.Unlock() // wgm.Manager.SquadMapMux.Unlock()
case string(HOSTED_SQUAD_STOP_CALL): // case string(HOSTED_SQUAD_STOP_CALL):
logger.Println("quit squad called") // logger.Println("quit squad called")
if err := validateRequest(payload, "squadId"); err != nil { // if err := validateRequest(payload, "squadId"); err != nil {
errCh <- err // errCh <- err
return // return
} // }
wgm.Manager.HandleLeavingMember(req.GetFrom(), payload["squadId"]) // wgm.Manager.HandleLeavingMember(req.GetFrom(), payload["squadId"])
done <- struct{}{} // done <- struct{}{}
case string(PEER_CONNECTION_REQUEST): // case string(PEER_CONNECTION_REQUEST):
logger.Println("creating offer for peer") // logger.Println("creating offer for peer")
if err := wgm.Manager.CreateOffer(ctx, req.GetFrom(), req.GetTo(), wgm.signalCandidate); err != nil { // if err := wgm.Manager.CreateOffer(ctx, req.GetFrom(), req.GetTo(), wgm.signalCandidate); err != nil {
errCh <- err // errCh <- err
return // return
} // }
case string(HOSTED_SQUAD_WEBRTC_OFFER): // case string(HOSTED_SQUAD_WEBRTC_OFFER):
if err := validateRequest(payload, SDP, SQUAD_ID); err != nil { // if err := validateRequest(payload, SDP, SQUAD_ID); err != nil {
errCh <- err // errCh <- err
return // return
} // }
if err := wgm.Manager.HandleOffer(ctx, req.GetFrom(), req.GetTo(), payload, wgm.signalCandidate); err != nil { // if err := wgm.Manager.HandleOffer(ctx, req.GetFrom(), req.GetTo(), payload, wgm.signalCandidate); err != nil {
errCh <- err // errCh <- err
return // return
} // }
case string(HOSTED_SQUAD_WEBRTC_ANSWER): // case string(HOSTED_SQUAD_WEBRTC_ANSWER):
if err := validateRequest(payload, SDP); err != nil { // if err := validateRequest(payload, SDP); err != nil {
errCh <- err // errCh <- err
return // return
} // }
if err := wgm.Manager.HandleAnswer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil { // if err := wgm.Manager.HandleAnswer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
errCh <- err // errCh <- err
return // return
} // }
case string(HOSTED_SQUAD_WEBRTC_COUNTER_OFFER): // case string(HOSTED_SQUAD_WEBRTC_COUNTER_OFFER):
if err := wgm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil { // if err := wgm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
errCh <- err // errCh <- err
return // return
} // }
case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER): // case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER):
logger.Println("received negotiation answer") // logger.Println("received negotiation answer")
if err := validateRequest(payload, SDP); err != nil { // if err := validateRequest(payload, SDP); err != nil {
errCh <- err // errCh <- err
return // return
} // }
if err := wgm.Manager.HandleRennegotiationAnswer(req.GetFrom(), payload[SDP]); err != nil { // if err := wgm.Manager.HandleRennegotiationAnswer(req.GetFrom(), payload[SDP]); err != nil {
errCh <- err // errCh <- err
return // return
} // }
case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER): // case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER):
logger.Println("received negotiation offer") // logger.Println("received negotiation offer")
if err := validateRequest(payload, SDP); err != nil { // if err := validateRequest(payload, SDP); err != nil {
errCh <- err // errCh <- err
return // return
} // }
if err := wgm.Manager.HandleRennegotiationOffer(req.GetFrom(), payload[SDP]); err != nil { // if err := wgm.Manager.HandleRennegotiationOffer(req.GetFrom(), payload[SDP]); err != nil {
errCh <- err // errCh <- err
return // return
} // }
case string(HOSTED_SQUAD_WEBRTC_CANDIDATE): // case string(HOSTED_SQUAD_WEBRTC_CANDIDATE):
if err := validateRequest(payload, "candidate", "sdpMLineIndex", "sdpMid"); err != nil { // if err := validateRequest(payload, "candidate", "sdpMLineIndex", "sdpMid"); err != nil {
errCh <- err // errCh <- err
return // return
} // }
logger.Println(payload) // logger.Println(payload)
i, err := strconv.Atoi(payload["sdpMLineIndex"]) // i, err := strconv.Atoi(payload["sdpMLineIndex"])
if err != nil { // if err != nil {
errCh <- err // errCh <- err
return // return
} // }
SDPMLineIndex := uint16(i) // SDPMLineIndex := uint16(i)
sdpMid := payload["sdpMid"] // sdpMid := payload["sdpMid"]
logger.Println(sdpMid, SDPMLineIndex) // logger.Println(sdpMid, SDPMLineIndex)
if err := wgm.Manager.AddCandidate(&webrtc.ICECandidateInit{ // if err := wgm.Manager.AddCandidate(&webrtc.ICECandidateInit{
Candidate: payload["candidate"], // Candidate: payload["candidate"],
SDPMid: &sdpMid, // SDPMid: &sdpMid,
SDPMLineIndex: &SDPMLineIndex, // SDPMLineIndex: &SDPMLineIndex,
}, req.GetFrom()); err != nil { // }, req.GetFrom()); err != nil {
errCh <- err // errCh <- err
return // return
} // }
default: // default:
} // }
}() // }()
select { // select {
case <-ctx.Done(): // case <-ctx.Done():
err = ctx.Err() // err = ctx.Err()
return // return
case <-done: // case <-done:
return // return
case err = <-errCh: // case err = <-errCh:
return // return
} // }
} // }

View File

@ -1,66 +1,66 @@
package localserver package localserver
import ( // import (
"context" // "context"
"encoding/json" // "encoding/json"
"io" // "io"
"net/http" // "net/http"
) // )
type ( // type (
WebRTCHttpMiddleware struct { // WebRTCHttpMiddleware struct {
//menuItem *systray.MenuItem // //menuItem *systray.MenuItem
} // }
) // )
const ( // const (
CREATE_HOSTED_SQUAD = "create_hosted_squad" // CREATE_HOSTED_SQUAD = "create_hosted_squad"
) // )
// func NewWebRTCHttpMiddleware(menuItem *systray.MenuItem) (webRTCHttpMiddleware *WebRTCHttpMiddleware) { // // func NewWebRTCHttpMiddleware(menuItem *systray.MenuItem) (webRTCHttpMiddleware *WebRTCHttpMiddleware) {
// webRTCHttpMiddleware = &WebRTCHttpMiddleware{ // // webRTCHttpMiddleware = &WebRTCHttpMiddleware{
// menuItem: menuItem, // // menuItem: menuItem,
// // }
// // return
// // }
// func (wm *WebRTCHttpMiddleware) Process(ctx context.Context, req *http.Request) (err error) {
// done, errCh := make(chan struct{}), make(chan error)
// go func() {
// localServerReq, err := wm.unmarshallBody(req)
// if err != nil {
// errCh <- err
// return
// }
// switch localServerReq.ReqType {
// case CREATE_HOSTED_SQUAD:
// default:
// }
// }()
// select {
// case <-done:
// return
// case err = <-errCh:
// return
// case <-ctx.Done():
// err = ctx.Err()
// return
// }
// }
// func (wm *WebRTCHttpMiddleware) unmarshallBody(req *http.Request) (localServerReq *LocalServerRequest, err error) {
// reqBody, err := req.GetBody()
// if err != nil {
// return
// }
// bs, err := io.ReadAll(reqBody)
// if err != nil {
// return
// }
// err = json.Unmarshal(bs, &localServerReq)
// if err != nil {
// return
// } // }
// return // return
// } // }
func (wm *WebRTCHttpMiddleware) Process(ctx context.Context, req *http.Request) (err error) {
done, errCh := make(chan struct{}), make(chan error)
go func() {
localServerReq, err := wm.unmarshallBody(req)
if err != nil {
errCh <- err
return
}
switch localServerReq.ReqType {
case CREATE_HOSTED_SQUAD:
default:
}
}()
select {
case <-done:
return
case err = <-errCh:
return
case <-ctx.Done():
err = ctx.Err()
return
}
}
func (wm *WebRTCHttpMiddleware) unmarshallBody(req *http.Request) (localServerReq *LocalServerRequest, err error) {
reqBody, err := req.GetBody()
if err != nil {
return
}
bs, err := io.ReadAll(reqBody)
if err != nil {
return
}
err = json.Unmarshal(bs, &localServerReq)
if err != nil {
return
}
return
}

View File

@ -264,7 +264,7 @@ func (ac *AudioChannel) createPeerConnection(target string, from string, peerTyp
config := webrtc.Configuration{ config := webrtc.Configuration{
ICEServers: []webrtc.ICEServer{ ICEServers: []webrtc.ICEServer{
{ {
URLs: []string{"stun:stun.l.google.com:19302", "stun:stunserver.org:3478"}, URLs: []string{"stun:stun.l.google.com:19302"},
}, },
}, },
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback, SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
@ -459,10 +459,10 @@ func (ac *AudioChannel) createPeerConnection(target string, from string, peerTyp
} else if localTrack.Kind() == webrtc.RTPCodecTypeVideo { } else if localTrack.Kind() == webrtc.RTPCodecTypeVideo {
logger.Println("track of wrong type") logger.Println("track of wrong type")
} }
go func() { go func(id string) {
<-time.After(time.Millisecond * 500) <-time.After(time.Millisecond * 500)
connection.negotiate(id, sendDCMessage) connection.negotiate(id, sendDCMessage)
}() }(id)
} }
} }
return return

View File

@ -413,7 +413,7 @@ func (fs *ChatFSInstance) FileDownloadFailed(chatId, filename, userId string) (e
// config := webrtc.Configuration{ // config := webrtc.Configuration{
// ICEServers: []webrtc.ICEServer{ // ICEServers: []webrtc.ICEServer{
// { // {
// URLs: []string{"stun:stun.l.google.com:19302", "stun:stunserver.org:3478"}, // URLs: []string{"stun:stun.l.google.com:19302", "stun:stun.l.google.com:19302"},
// }, // },
// }, // },
// } // }

View File

@ -11,6 +11,7 @@ import (
) )
type ChatFile struct { type ChatFile struct {
ID uint64
ChatId string `json:"chatId"` ChatId string `json:"chatId"`
Owner string `json:"owner"` Owner string `json:"owner"`
Name string `json:"name"` Name string `json:"name"`

View File

@ -937,7 +937,7 @@ func (zch *ZoneChatsHandler[T]) AddChatMessage(userId, chatId, content string, i
ResponseOf: nil, ResponseOf: nil,
File: file, File: file,
Tags: make([]string, 0), Tags: make([]string, 0),
Date: time.Now().Format("Mon, 02 Jan 2006 15:04:05 MST"), Date: time.Now().Format(time.RFC3339),
} }
var chatType string var chatType string
var chatMembers []string var chatMembers []string

View File

@ -1109,8 +1109,8 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq
Owner: req.Payload["userId"].(string), Owner: req.Payload["userId"].(string),
Type: PRIVATE, Type: PRIVATE,
Folder: true, Folder: true,
ModTime: time.Now().Format(time.UnixDate), ModTime: time.Now().Format(time.RFC3339),
CreationTime: time.Now().Format(time.UnixDate), CreationTime: time.Now().Format(time.RFC3339),
Size: 0, Size: 0,
Members: map[string]*FSEntityAccessRights{ Members: map[string]*FSEntityAccessRights{
req.Payload["userId"].(string): { req.Payload["userId"].(string): {
@ -1148,8 +1148,8 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq
Owner: req.Payload["userId"].(string), Owner: req.Payload["userId"].(string),
Type: req.Payload["type"].(string), Type: req.Payload["type"].(string),
Size: uint64(req.Payload["size"].(float64)), Size: uint64(req.Payload["size"].(float64)),
ModTime: time.Now().Format(time.UnixDate), ModTime: time.Now().Format(time.RFC3339),
CreationTime: time.Now().Format(time.UnixDate), CreationTime: time.Now().Format(time.RFC3339),
Folder: false, Folder: false,
Members: map[string]*FSEntityAccessRights{ Members: map[string]*FSEntityAccessRights{
req.Payload["userId"].(string): { req.Payload["userId"].(string): {

View File

@ -416,7 +416,7 @@ func (fs *FSInstance) FileDownloadFailed(path, filename, userId string) (err err
// config := webrtc.Configuration{ // config := webrtc.Configuration{
// ICEServers: []webrtc.ICEServer{ // ICEServers: []webrtc.ICEServer{
// { // {
// URLs: []string{"stun:stun.l.google.com:19302", "stun:stunserver.org:3478"}, // URLs: []string{"stun:stun.l.google.com:19302", "stun:stun.l.google.com:19302"},
// }, // },
// }, // },
// SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback, // SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,

View File

@ -3,6 +3,7 @@ package localserver
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"strconv" "strconv"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
@ -62,6 +63,7 @@ func (zm *ZoneGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECa
func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) { func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) {
done, errCh := make(chan struct{}), make(chan error) done, errCh := make(chan struct{}), make(chan error)
fmt.Println("got req", req.Type, string(req.Payload))
go func() { go func() {
var payload map[string]string var payload map[string]string
if e := json.Unmarshal(req.Payload, &payload); e != nil { if e := json.Unmarshal(req.Payload, &payload); e != nil {
@ -92,16 +94,20 @@ func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
} }
if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) { if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) {
reqChan := make(chan *ZoneRequest) reqChan := make(chan *ZoneRequest)
if _, ok := zm.Manager.Zones[payload["zoneId"]]; !ok {
err = fmt.Errorf("no such zone")
return
}
done, e := zm.Manager.Zones[payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan) done, e := zm.Manager.Zones[payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan)
go func() { go func() {
defer close(reqChan) defer close(reqChan)
reqChan <- &ZoneRequest{ // reqChan <- &ZoneRequest{
ReqType: string(REMOVE_USER), // ReqType: string(REMOVE_USER),
From: payload["userId"], // From: payload["userId"],
Payload: map[string]interface{}{ // Payload: map[string]interface{}{
"userId": payload["userId"], // "userId": payload["userId"],
}, // },
} // }
reqChan <- &ZoneRequest{ reqChan <- &ZoneRequest{
ReqType: string(REMOVED_ZONE_AUTHORIZED_MEMBER), ReqType: string(REMOVED_ZONE_AUTHORIZED_MEMBER),
From: payload["userId"], From: payload["userId"],
@ -125,16 +131,13 @@ func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
} }
if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) { if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) {
reqChan := make(chan *ZoneRequest) reqChan := make(chan *ZoneRequest)
if _, ok := zm.Manager.Zones[payload["zoneId"]]; !ok {
err = fmt.Errorf("no such zone")
return
}
done, e := zm.Manager.Zones[payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan) done, e := zm.Manager.Zones[payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan)
go func() { go func() {
defer close(reqChan) defer close(reqChan)
reqChan <- &ZoneRequest{
ReqType: string(ADD_USER),
From: payload["userId"],
Payload: map[string]interface{}{
"userId": payload["userId"],
},
}
reqChan <- &ZoneRequest{ reqChan <- &ZoneRequest{
ReqType: string(NEW_AUTHORIZED_ZONE_MEMBER), ReqType: string(NEW_AUTHORIZED_ZONE_MEMBER),
From: payload["userId"], From: payload["userId"],
@ -142,6 +145,13 @@ func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
"userId": payload["userId"], "userId": payload["userId"],
}, },
} }
// reqChan <- &ZoneRequest{
// ReqType: string(ADD_USER),
// From: payload["userId"],
// Payload: map[string]interface{}{
// "userId": payload["userId"],
// },
// }
}() }()
select { select {
case <-done: case <-done:

View File

@ -400,7 +400,7 @@ func (zm *ZoneManager) createPeerConnection(target string, from string, zoneId s
config := webrtc.Configuration{ config := webrtc.Configuration{
ICEServers: []webrtc.ICEServer{ ICEServers: []webrtc.ICEServer{
{ {
URLs: []string{"stun:stun.l.google.com:19302", "stun:stunserver.org:3478"}, URLs: []string{"stun:stun.l.google.com:19302"},
}, },
}, },
SDPSemantics: webrtc.SDPSemanticsUnifiedPlan, SDPSemantics: webrtc.SDPSemanticsUnifiedPlan,

View File

@ -48,44 +48,6 @@ func NewZoneNotificationsHandler(_ string, zoneId string, owner string, authoriz
return return
} }
// func (znh *ZoneNotificationsHandler) sendZoneRequest(reqType string, from string, payload map[string]interface{}) {
// go func() {
// for _, rc := range znh.reqChans {
// rc <- &ZoneRequest{
// ReqType: reqType,
// From: from,
// Payload: payload,
// }
// }
// }()
// }
// func (znh *ZoneNotificationsHandler) 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(znh.Flag, func() (err error) {
// if _, ok := znh.DataChannels[to]; ok {
// bs, jsonErr := json.Marshal(&ZoneResponse{
// Type: reqType,
// From: from,
// To: to,
// Payload: payload,
// })
// if jsonErr != nil {
// return jsonErr
// }
// err = znh.DataChannels[to].DataChannel.SendText(string(bs))
// }
// return
// }); err != nil {
// errCh <- err
// return
// }
// done <- struct{}{}
// }()
// return done, errCh
// }
func (znh *ZoneNotificationsHandler) Init(ctx context.Context, authorizedMembers []string) (err error) { func (znh *ZoneNotificationsHandler) Init(ctx context.Context, authorizedMembers []string) (err error) {
//? initialization code here //? initialization code here
return return
@ -169,12 +131,6 @@ func (znh *ZoneNotificationsHandler) handleZoneRequest(ctx context.Context, req
err = fmt.Errorf(" field recipient in payload is wrong type") err = fmt.Errorf(" field recipient in payload is wrong type")
return return
} }
// recipients := []string{}
// for _, recipient := range req.Payload["recipients"].([]any) {
// if r, ok := recipient.(string); ok {
// recipients = append(recipients, r)
// }
// }
err = znh.CreateNotification(req.Payload["type"].(string), req.Payload["title"].(string), req.Payload["body"].(string), req.Payload["payload"].(string), req.Payload["isPushed"].(bool), req.Payload["recipients"].([]string)...) err = znh.CreateNotification(req.Payload["type"].(string), req.Payload["title"].(string), req.Payload["body"].(string), req.Payload["payload"].(string), req.Payload["isPushed"].(bool), req.Payload["recipients"].([]string)...)
} }

View File

@ -80,16 +80,16 @@ func NewZoneRequestScheduler(authorizedMembers []string, handlers ...ZoneRequest
func (zrs *ZoneRequestScheduler) Schedule(reqChan <-chan *ZoneRequest) (done chan struct{}, errCh chan error) { func (zrs *ZoneRequestScheduler) Schedule(reqChan <-chan *ZoneRequest) (done chan struct{}, errCh chan error) {
done, errCh = make(chan struct{}), make(chan error) done, errCh = make(chan struct{}), make(chan error)
go func() {
for req := range reqChan { for req := range reqChan {
go func(r *ZoneRequest) { go func(r *ZoneRequest) {
for _, publisher := range zrs.handlersPublishers { for _, publisher := range zrs.handlersPublishers {
go func(p chan<- *ZoneRequest) { publisher <- r
p <- r
}(publisher)
} }
}(req) }(req)
} }
done <- struct{}{} done <- struct{}{}
}()
return return
} }

Some files were not shown because too many files have changed in this diff Show More