hosted squads working on node
This commit is contained in:
parent
f2ebf8cb4c
commit
79cc675c43
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal 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
|
||||||
@ -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():
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -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++
|
||||||
|
|||||||
@ -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,33 +90,30 @@ 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
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
nodeChatDBHandler, err := NewNodeChatDBHandler(chat.Name())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var bs []byte
|
|
||||||
bs, err = os.ReadFile(filepath.Join(dataPath, "data", "chats", chat.Name(), "chatConfig.json"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
logger.Println(string(bs))
|
|
||||||
var c NodeChatChannel
|
|
||||||
if err = json.Unmarshal(bs, &c); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
nodeChatTracking, err := NewNodeChatTracking(chatID, initiator, target)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
logger.Println("chats data :", c.ID, c.Initiator, c.InitiatorHost, c.Target)
|
|
||||||
c.DB = nodeChatDBHandler
|
|
||||||
c.Tracking = nodeChatTracking
|
|
||||||
chats[c.ID] = &c
|
|
||||||
}
|
}
|
||||||
|
var bs []byte
|
||||||
|
bs, err = os.ReadFile(filepath.Join(dataPath, "data", "chats", chatID, "chatConfig.json"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logger.Println(string(bs))
|
||||||
|
var c NodeChatChannel
|
||||||
|
if err = json.Unmarshal(bs, &c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nodeChatTracking, err := NewNodeChatTracking(chatID, initiator, target)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logger.Println("chats data :", c.ID, c.Initiator, c.InitiatorHost, c.Target)
|
||||||
|
c.DB = nodeChatDBHandler
|
||||||
|
c.Tracking = nodeChatTracking
|
||||||
|
chats[c.ID] = &c
|
||||||
|
|
||||||
chatFlag := uint32(0)
|
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
133
chatNotificationsHandler.go
Normal 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
|
||||||
|
}
|
||||||
@ -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)
|
||||||
for req := range reqChan {
|
go func() {
|
||||||
go func(r *ChatRequest) {
|
for req := range reqChan {
|
||||||
for _, publisher := range crs.handlersPublishers {
|
go func(r *ChatRequest) {
|
||||||
go func(p chan<- *ChatRequest) {
|
for _, publisher := range crs.handlersPublishers {
|
||||||
p <- r
|
publisher <- r
|
||||||
}(publisher)
|
}
|
||||||
}
|
}(req)
|
||||||
}(req)
|
}
|
||||||
}
|
done <- struct{}{}
|
||||||
|
}()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +0,0 @@
|
|||||||
:§<>ª—¤ÓHè±7EÃ3Hello Badger
|
|
||||||
Binary file not shown.
252170
cmd/local_server.log
252170
cmd/local_server.log
File diff suppressed because it is too large
Load Diff
BIN
cmd/node.linux
BIN
cmd/node.linux
Binary file not shown.
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
36
server.go
36
server.go
@ -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
323
squadChatDBHandler.go
Normal 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
178
squadChatFSInstance.go
Normal 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
750
squadChatHandler.go
Normal 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
151
squadChatTracking.go
Normal 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
238
squadGrpcMiddleware.go
Normal 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
742
squadManager.go
Normal 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
124
squadNotificationHandler.go
Normal 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
122
squadRequestScheduler.go
Normal 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
828
squadVideoChannel.go
Normal 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
398
squadVideoChannelHandler.go
Normal 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
|
||||||
|
}
|
||||||
54
utitlity.go
54
utitlity.go
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
// }
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
// }
|
||||||
|
|||||||
@ -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
|
||||||
}
|
// }
|
||||||
|
|||||||
1620
webrtcCallManager.go
1620
webrtcCallManager.go
File diff suppressed because it is too large
Load Diff
@ -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
|
||||||
}
|
// }
|
||||||
|
|||||||
@ -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
|
||||||
}
|
// }
|
||||||
|
|||||||
@ -1,449 +1,449 @@
|
|||||||
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",
|
// "from": "lolo_local_serv",
|
||||||
"sdp": rawOffer.SDP,
|
// "sdp": rawOffer.SDP,
|
||||||
})
|
// })
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (wf *WebrtcFsManager) HandleOffer(ctx context.Context, req map[string]string, cb OnICECandidateFunc) (err error) {
|
// func (wf *WebrtcFsManager) HandleOffer(ctx context.Context, 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() {
|
||||||
peerConnection, err := wf.createPeerConnection(req[FROM], req[TO], webrtc.SDPTypeAnswer, cb)
|
// peerConnection, err := wf.createPeerConnection(req[FROM], req[TO], webrtc.SDPTypeAnswer, cb)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
wf.RTCPeerConnectionMapMux.Lock()
|
// wf.RTCPeerConnectionMapMux.Lock()
|
||||||
wf.RTCPeerConnections[req[FROM]] = peerConnection
|
// wf.RTCPeerConnections[req[FROM]] = peerConnection
|
||||||
wf.RTCPeerConnectionMapMux.Unlock()
|
// wf.RTCPeerConnectionMapMux.Unlock()
|
||||||
offer := webrtc.SessionDescription{
|
// offer := webrtc.SessionDescription{
|
||||||
Type: webrtc.SDPTypeOffer,
|
// Type: webrtc.SDPTypeOffer,
|
||||||
SDP: req[SDP],
|
// SDP: req[SDP],
|
||||||
}
|
// }
|
||||||
if err = peerConnection.SetRemoteDescription(offer); err != nil {
|
// if err = peerConnection.SetRemoteDescription(offer); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
rawAnswer, err := peerConnection.CreateAnswer(nil)
|
// rawAnswer, err := peerConnection.CreateAnswer(nil)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
wf.LocalSDMapMux.Lock()
|
// wf.LocalSDMapMux.Lock()
|
||||||
wf.LocalSD[req[FROM]] = &rawAnswer
|
// wf.LocalSD[req[FROM]] = &rawAnswer
|
||||||
wf.LocalSDMapMux.Unlock()
|
// wf.LocalSDMapMux.Unlock()
|
||||||
// if err = wf.stream.Send(&Request{
|
// // if err = wf.stream.Send(&Request{
|
||||||
// Type: string(WEBRTC_ANSWER_FS),
|
// // Type: string(WEBRTC_ANSWER_FS),
|
||||||
// From: "lolo_local_serv",
|
// // From: "lolo_local_serv",
|
||||||
// Token: "none",
|
// // Token: "none",
|
||||||
// Payload: map[string]string{
|
// // Payload: map[string]string{
|
||||||
// "to": req[FROM],
|
// // "to": req[FROM],
|
||||||
// "from": "lolo_local_serv",
|
// // "from": "lolo_local_serv",
|
||||||
// "sdp": rawAnswer.SDP,
|
// // "sdp": rawAnswer.SDP,
|
||||||
// },
|
// // },
|
||||||
// }); err != nil {
|
// // }); err != nil {
|
||||||
// errCh <- err
|
// // errCh <- err
|
||||||
// return
|
// // return
|
||||||
// }
|
// // }
|
||||||
done <- struct{}{}
|
// done <- struct{}{}
|
||||||
}()
|
// }()
|
||||||
select {
|
// select {
|
||||||
case <-done:
|
// case <-done:
|
||||||
return
|
// return
|
||||||
case err = <-errCh:
|
// case err = <-errCh:
|
||||||
return
|
// return
|
||||||
case <-ctx.Done():
|
// case <-ctx.Done():
|
||||||
err = ctx.Err()
|
// err = ctx.Err()
|
||||||
return
|
// 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 {
|
// }); err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
// if err = wf.stream.Send(&Request{
|
// // if err = wf.stream.Send(&Request{
|
||||||
// Type: string(WEBRTC_COUNTER_OFFER_FS),
|
// // Type: string(WEBRTC_COUNTER_OFFER_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],
|
||||||
// },
|
// // },
|
||||||
// }); err != nil {
|
// // }); err != nil {
|
||||||
// return
|
// // return
|
||||||
// }
|
// // }
|
||||||
wf.CandidateMux.RLock()
|
// wf.CandidateMux.RLock()
|
||||||
for range wf.PendingCandidates[req[FROM]] {
|
// 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
|
// // return
|
||||||
// }
|
// // }
|
||||||
}
|
// }
|
||||||
wf.CandidateMux.RUnlock()
|
// wf.CandidateMux.RUnlock()
|
||||||
wf.CandidateMux.Lock()
|
// wf.CandidateMux.Lock()
|
||||||
delete(wf.PendingCandidates, req[FROM])
|
// delete(wf.PendingCandidates, req[FROM])
|
||||||
wf.CandidateMux.Unlock()
|
// wf.CandidateMux.Unlock()
|
||||||
wf.LocalSDMapMux.Lock()
|
// wf.LocalSDMapMux.Lock()
|
||||||
delete(wf.LocalSD, req[FROM])
|
// delete(wf.LocalSD, req[FROM])
|
||||||
wf.LocalSDMapMux.Unlock()
|
// wf.LocalSDMapMux.Unlock()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (wf *WebrtcFsManager) HandleCounterOffer(ctx context.Context, req map[string]string) (err error) {
|
// func (wf *WebrtcFsManager) HandleCounterOffer(ctx context.Context, req map[string]string) (err error) {
|
||||||
wf.RTCPeerConnectionMapMux.RLock()
|
// wf.RTCPeerConnectionMapMux.RLock()
|
||||||
if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok {
|
// if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok {
|
||||||
err = fmt.Errorf("no field corresponding peer connection for id %s", req[FROM])
|
// err = fmt.Errorf("no field corresponding peer connection for id %s", req[FROM])
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
connection := wf.RTCPeerConnections[req[FROM]]
|
// connection := wf.RTCPeerConnections[req[FROM]]
|
||||||
wf.RTCPeerConnectionMapMux.RUnlock()
|
// wf.RTCPeerConnectionMapMux.RUnlock()
|
||||||
wf.LocalSDMapMux.RLock()
|
// wf.LocalSDMapMux.RLock()
|
||||||
if err = connection.SetLocalDescription(*wf.LocalSD[req[FROM]]); err != nil {
|
// if err = connection.SetLocalDescription(*wf.LocalSD[req[FROM]]); err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
wf.LocalSDMapMux.RUnlock()
|
// wf.LocalSDMapMux.RUnlock()
|
||||||
wf.CandidateMux.RLock()
|
// wf.CandidateMux.RLock()
|
||||||
for range wf.PendingCandidates[req[FROM]] {
|
// 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
|
// // return
|
||||||
// }
|
// // }
|
||||||
}
|
// }
|
||||||
wf.CandidateMux.RUnlock()
|
// wf.CandidateMux.RUnlock()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (wf *WebrtcFsManager) createPeerConnection(target string, from string, peerType webrtc.SDPType, cb OnICECandidateFunc) (peerConnection *webrtc.PeerConnection, err error) {
|
// func (wf *WebrtcFsManager) createPeerConnection(target string, from string, peerType webrtc.SDPType, cb OnICECandidateFunc) (peerConnection *webrtc.PeerConnection, err error) {
|
||||||
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)
|
||||||
}
|
// }
|
||||||
}()
|
// }()
|
||||||
s := webrtc.SettingEngine{}
|
// s := webrtc.SettingEngine{}
|
||||||
s.DetachDataChannels()
|
// s.DetachDataChannels()
|
||||||
|
|
||||||
api := webrtc.NewAPI(webrtc.WithSettingEngine(s))
|
// api := webrtc.NewAPI(webrtc.WithSettingEngine(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", "stun:stun.l.google.com:19302?transport=tcp"},
|
// 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,
|
// SDPSemantics: webrtc.SDPSemanticsUnifiedPlan,
|
||||||
}
|
// }
|
||||||
peerConnection, err = api.NewPeerConnection(config)
|
// peerConnection, err = api.NewPeerConnection(config)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if peerType == webrtc.SDPTypeOffer {
|
// if peerType == webrtc.SDPTypeOffer {
|
||||||
maxRetransmits := uint16(100)
|
// maxRetransmits := uint16(100)
|
||||||
channel, err := peerConnection.CreateDataChannel("data", &webrtc.DataChannelInit{
|
// channel, err := peerConnection.CreateDataChannel("data", &webrtc.DataChannelInit{
|
||||||
MaxRetransmits: &maxRetransmits,
|
// MaxRetransmits: &maxRetransmits,
|
||||||
})
|
// })
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
channel.OnOpen(func() {
|
// channel.OnOpen(func() {
|
||||||
logger.Println("channel opened")
|
// logger.Println("channel opened")
|
||||||
if chanErr := channel.SendText("yooo man this is open"); chanErr != nil {
|
// if chanErr := channel.SendText("yooo man this is open"); chanErr != nil {
|
||||||
logger.Println(chanErr)
|
// logger.Println(chanErr)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
channel.OnMessage(func(msg webrtc.DataChannelMessage) {
|
// channel.OnMessage(func(msg webrtc.DataChannelMessage) {
|
||||||
logger.Printf("new message %s\n", string(msg.Data))
|
// logger.Printf("new message %s\n", string(msg.Data))
|
||||||
done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
// done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
||||||
From: target,
|
// From: target,
|
||||||
Type: "test",
|
// Type: "test",
|
||||||
Payload: &DatachannelMessagePayload{},
|
// Payload: &DatachannelMessagePayload{},
|
||||||
}, channel)
|
// }, channel)
|
||||||
select {
|
// select {
|
||||||
case <-done:
|
// case <-done:
|
||||||
//logger.Println("done with success")
|
// //logger.Println("done with success")
|
||||||
case e := <-errCh:
|
// case e := <-errCh:
|
||||||
logger.Println(e)
|
// logger.Println(e)
|
||||||
logger.Println("this is impossible")
|
// logger.Println("this is impossible")
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
wf.DataChannelMapMux.Lock()
|
// wf.DataChannelMapMux.Lock()
|
||||||
wf.DataChannels[target] = channel
|
// wf.DataChannels[target] = channel
|
||||||
wf.DataChannelMapMux.Unlock()
|
// wf.DataChannelMapMux.Unlock()
|
||||||
}
|
// }
|
||||||
peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) {
|
// peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) {
|
||||||
logger.Printf("ICE connection state has changed %s\n", is.String())
|
// logger.Printf("ICE connection state has changed %s\n", is.String())
|
||||||
if is == webrtc.ICEConnectionStateDisconnected || is == webrtc.ICEConnectionStateFailed {
|
// if is == webrtc.ICEConnectionStateDisconnected || is == webrtc.ICEConnectionStateFailed {
|
||||||
logger.Println(is)
|
// logger.Println(is)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
peerConnection.OnDataChannel(func(dc *webrtc.DataChannel) {
|
// peerConnection.OnDataChannel(func(dc *webrtc.DataChannel) {
|
||||||
dc.OnOpen(func() {
|
// dc.OnOpen(func() {
|
||||||
logger.Printf("got a new open datachannel %s\n", dc.Label())
|
// logger.Printf("got a new open datachannel %s\n", dc.Label())
|
||||||
dataChann, err := dc.Detach()
|
// dataChann, err := dc.Detach()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
logger.Println(err)
|
// logger.Println(err)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
for {
|
// for {
|
||||||
var x []byte = make([]byte, 2<<15)
|
// var x []byte = make([]byte, 2<<15)
|
||||||
n, _, err := dataChann.ReadDataChannel(x)
|
// n, _, err := dataChann.ReadDataChannel(x)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
logger.Println(err)
|
// logger.Println(err)
|
||||||
if err == io.EOF {
|
// if err == io.EOF {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
go func(msg []byte) {
|
// go func(msg []byte) {
|
||||||
var dataChannelMessage DatachannelMessage
|
// var dataChannelMessage DatachannelMessage
|
||||||
if unmarshalErr := json.Unmarshal(msg, &dataChannelMessage); unmarshalErr != nil {
|
// if unmarshalErr := json.Unmarshal(msg, &dataChannelMessage); unmarshalErr != nil {
|
||||||
logger.Println(unmarshalErr)
|
// logger.Println(unmarshalErr)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
// done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
||||||
From: dataChannelMessage.From,
|
// From: dataChannelMessage.From,
|
||||||
Type: dataChannelMessage.Type,
|
// Type: dataChannelMessage.Type,
|
||||||
Payload: dataChannelMessage.Payload,
|
// Payload: dataChannelMessage.Payload,
|
||||||
}, dc)
|
// }, dc)
|
||||||
select {
|
// select {
|
||||||
case <-done:
|
// case <-done:
|
||||||
//logger.Println("done with success")
|
// //logger.Println("done with success")
|
||||||
case e := <-errCh:
|
// case e := <-errCh:
|
||||||
logger.Println(e)
|
// logger.Println(e)
|
||||||
logger.Println("this is impossible")
|
// logger.Println("this is impossible")
|
||||||
}
|
// }
|
||||||
}(x[:n])
|
// }(x[:n])
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
// dc.OnMessage(func(msg webrtc.DataChannelMessage) {
|
// // 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.Data, &dataChannelMessage); unmarshalErr != nil {
|
||||||
// logger.Println(unmarshalErr)
|
// // logger.Println(unmarshalErr)
|
||||||
// return
|
// // return
|
||||||
// }
|
// // }
|
||||||
// done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
// // done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
||||||
// From: dataChannelMessage.From,
|
// // From: dataChannelMessage.From,
|
||||||
// Type: dataChannelMessage.Type,
|
// // Type: dataChannelMessage.Type,
|
||||||
// Payload: dataChannelMessage.Payload,
|
// // Payload: dataChannelMessage.Payload,
|
||||||
// }, dc)
|
// // }, dc)
|
||||||
// select {
|
// // select {
|
||||||
// case <-done:
|
// // case <-done:
|
||||||
// //logger.Println("done with success")
|
// // //logger.Println("done with success")
|
||||||
// case e := <-errCh:
|
// // case e := <-errCh:
|
||||||
// logger.Println(e)
|
// // logger.Println(e)
|
||||||
// logger.Println("this is impossible")
|
// // logger.Println("this is impossible")
|
||||||
// }
|
// // }
|
||||||
// })
|
// // })
|
||||||
})
|
// })
|
||||||
peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
|
// peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
|
||||||
if i == nil {
|
// if i == nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
wf.CandidateMux.Lock()
|
// wf.CandidateMux.Lock()
|
||||||
defer wf.CandidateMux.Unlock()
|
// defer wf.CandidateMux.Unlock()
|
||||||
desc := peerConnection.RemoteDescription()
|
// desc := peerConnection.RemoteDescription()
|
||||||
if desc == nil {
|
// if desc == nil {
|
||||||
wf.PendingCandidates[target] = append(wf.PendingCandidates[target], i)
|
// wf.PendingCandidates[target] = append(wf.PendingCandidates[target], i)
|
||||||
} else {
|
// } else {
|
||||||
logger.Println(i)
|
// logger.Println(i)
|
||||||
if iceCandidateErr := cb(target, i); iceCandidateErr != nil {
|
// if iceCandidateErr := cb(target, i); iceCandidateErr != nil {
|
||||||
logger.Println(iceCandidateErr)
|
// logger.Println(iceCandidateErr)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
peerConnection.OnNegotiationNeeded(func() {
|
// peerConnection.OnNegotiationNeeded(func() {
|
||||||
if peerConnection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && peerConnection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
|
// if peerConnection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && peerConnection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
|
||||||
wf.RTCPeerConnectionMapMux.Lock()
|
// wf.RTCPeerConnectionMapMux.Lock()
|
||||||
defer wf.RTCPeerConnectionMapMux.Unlock()
|
// defer wf.RTCPeerConnectionMapMux.Unlock()
|
||||||
for _, connection := range wf.RTCPeerConnections {
|
// for _, connection := range wf.RTCPeerConnections {
|
||||||
if connection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && connection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
|
// if connection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && connection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
|
||||||
localSd, err := connection.CreateOffer(nil)
|
// localSd, err := connection.CreateOffer(nil)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
logger.Println(err)
|
// logger.Println(err)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if err = connection.SetLocalDescription(localSd); err != nil {
|
// if err = connection.SetLocalDescription(localSd); err != nil {
|
||||||
logger.Println(err)
|
// logger.Println(err)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
// if err = wf.stream.Send(&Request{
|
// // if err = wf.stream.Send(&Request{
|
||||||
// Type: string(WEBRTC_RENNEGOTIATION_OFFER_FS),
|
// // Type: string(WEBRTC_RENNEGOTIATION_OFFER_FS),
|
||||||
// From: "lolo_local_serv",
|
// // From: "lolo_local_serv",
|
||||||
// Token: "",
|
// // Token: "",
|
||||||
// Payload: map[string]string{
|
// // Payload: map[string]string{
|
||||||
// "to": id,
|
// // "to": id,
|
||||||
// "sdp": localSd.SDP,
|
// // "sdp": localSd.SDP,
|
||||||
// },
|
// // },
|
||||||
// }); err != nil {
|
// // }); err != nil {
|
||||||
// logger.Println(err)
|
// // logger.Println(err)
|
||||||
// return
|
// // return
|
||||||
// }
|
// // }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (wf *WebrtcFsManager) HandleRennegotiationOffer(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
|
||||||
}
|
// }
|
||||||
if wf.RTCPeerConnections[from].SignalingState() != webrtc.SignalingStateStable {
|
// if wf.RTCPeerConnections[from].SignalingState() != webrtc.SignalingStateStable {
|
||||||
err = fmt.Errorf("rennego called in wrong state")
|
// err = fmt.Errorf("rennego called in wrong state")
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
|
// if err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
localSd, err := wf.RTCPeerConnections[from].CreateAnswer(nil)
|
// localSd, err := wf.RTCPeerConnections[from].CreateAnswer(nil)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if err = wf.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
|
// if err = wf.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
// if err = wf.stream.Send(&Request{
|
// // if err = wf.stream.Send(&Request{
|
||||||
// Type: string(WEBRTC_RENNEGOTIATION_ANSWER_FS),
|
// // Type: string(WEBRTC_RENNEGOTIATION_ANSWER_FS),
|
||||||
// From: "lolo_local_serv",
|
// // From: "lolo_local_serv",
|
||||||
// Token: "",
|
// // Token: "",
|
||||||
// Payload: map[string]string{
|
// // Payload: map[string]string{
|
||||||
// "to": from,
|
// // "to": from,
|
||||||
// "sdp": localSd.SDP,
|
// // "sdp": localSd.SDP,
|
||||||
// },
|
// // },
|
||||||
// }); err != nil {
|
// // }); err != nil {
|
||||||
// logger.Println(err)
|
// // logger.Println(err)
|
||||||
// return
|
// // return
|
||||||
// }
|
// // }
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (wf *WebrtcFsManager) HandleRennegotiationAnswer(from string, dst string, sdp 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()
|
||||||
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})
|
// err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer})
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (wf *WebrtcFsManager) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) {
|
// func (wf *WebrtcFsManager) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) {
|
||||||
wf.RTCPeerConnectionMapMux.Lock()
|
// wf.RTCPeerConnectionMapMux.Lock()
|
||||||
defer wf.RTCPeerConnectionMapMux.Unlock()
|
// defer wf.RTCPeerConnectionMapMux.Unlock()
|
||||||
err = wf.RTCPeerConnections[from].AddICECandidate(*candidate)
|
// err = wf.RTCPeerConnections[from].AddICECandidate(*candidate)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|||||||
@ -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
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
// if err := wfm.Manager.CreateOffer(ctx, payload[FROM], payload[TO], wfm.signalCandidate); err != nil {
|
// // if err := wfm.Manager.CreateOffer(ctx, payload[FROM], payload[TO], wfm.signalCandidate); err != nil {
|
||||||
// errCh <- err
|
// // errCh <- err
|
||||||
// return
|
// // return
|
||||||
// }
|
// // }
|
||||||
done <- struct{}{}
|
// done <- struct{}{}
|
||||||
case string(WEBRTC_OFFER_FS):
|
// case string(WEBRTC_OFFER_FS):
|
||||||
if err := validateRequest(payload, FROM, TO, SDP); err != nil {
|
// if err := validateRequest(payload, FROM, TO, SDP); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if err := wfm.Manager.HandleOffer(ctx, payload, wfm.signalCandidate); err != nil {
|
// if err := wfm.Manager.HandleOffer(ctx, payload, wfm.signalCandidate); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
done <- struct{}{}
|
// done <- struct{}{}
|
||||||
case string(WEBRTC_ANSWER_FS):
|
// case string(WEBRTC_ANSWER_FS):
|
||||||
if err := validateRequest(payload, FROM, TO, SDP); err != nil {
|
// if err := validateRequest(payload, FROM, TO, SDP); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if err := wfm.Manager.HandleAnswer(ctx, payload); err != nil {
|
// if err := wfm.Manager.HandleAnswer(ctx, payload); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
done <- struct{}{}
|
// done <- struct{}{}
|
||||||
case string(WEBRTC_COUNTER_OFFER_FS):
|
// case string(WEBRTC_COUNTER_OFFER_FS):
|
||||||
if err := validateRequest(payload, FROM); err != nil {
|
// if err := validateRequest(payload, FROM); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if err := wfm.Manager.HandleCounterOffer(ctx, payload); err != nil {
|
// if err := wfm.Manager.HandleCounterOffer(ctx, payload); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
done <- struct{}{}
|
// done <- struct{}{}
|
||||||
case string(WEBRTC_RENNEGOTIATION_ANSWER_FS):
|
// case string(WEBRTC_RENNEGOTIATION_ANSWER_FS):
|
||||||
if err := validateRequest(payload, FROM, SDP); err != nil {
|
// if err := validateRequest(payload, FROM, SDP); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if err := wfm.Manager.HandleRennegotiationAnswer(payload[FROM], "lolo_local_serv", payload[SDP]); err != nil {
|
// if err := wfm.Manager.HandleRennegotiationAnswer(payload[FROM], "lolo_local_serv", payload[SDP]); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
done <- struct{}{}
|
// done <- struct{}{}
|
||||||
case string(WEBRTC_RENNEGOTIATION_OFFER_FS):
|
// case string(WEBRTC_RENNEGOTIATION_OFFER_FS):
|
||||||
if err := validateRequest(payload, FROM, SDP); err != nil {
|
// if err := validateRequest(payload, FROM, SDP); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if err := wfm.Manager.HandleRennegotiationOffer(payload[FROM], "", payload[SDP]); err != nil {
|
// if err := wfm.Manager.HandleRennegotiationOffer(payload[FROM], "", payload[SDP]); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
done <- struct{}{}
|
// done <- struct{}{}
|
||||||
case string(WEBRTC_CANDIDATE_FS):
|
// case string(WEBRTC_CANDIDATE_FS):
|
||||||
if err := validateRequest(payload, FROM, "candidate", "sdpMLineIndex", "sdpMid"); err != nil {
|
// if err := validateRequest(payload, FROM, "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 := wfm.Manager.AddCandidate(&webrtc.ICECandidateInit{
|
// if err := wfm.Manager.AddCandidate(&webrtc.ICECandidateInit{
|
||||||
Candidate: payload["candidate"],
|
// Candidate: payload["candidate"],
|
||||||
SDPMid: &sdpMid,
|
// SDPMid: &sdpMid,
|
||||||
SDPMLineIndex: &SDPMLineIndex,
|
// SDPMLineIndex: &SDPMLineIndex,
|
||||||
}, payload[FROM]); err != nil {
|
// }, payload[FROM]); err != nil {
|
||||||
errCh <- err
|
// errCh <- err
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
done <- struct{}{}
|
// done <- struct{}{}
|
||||||
default:
|
// default:
|
||||||
logger.Println("fs is correctly linked")
|
// logger.Println("fs is correctly linked")
|
||||||
done <- struct{}{}
|
// done <- struct{}{}
|
||||||
}
|
// }
|
||||||
done <- struct{}{}
|
// done <- struct{}{}
|
||||||
}()
|
// }()
|
||||||
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
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@ -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
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"},
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
// }
|
// }
|
||||||
|
|||||||
@ -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"`
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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): {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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)...)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
for req := range reqChan {
|
go func() {
|
||||||
go func(r *ZoneRequest) {
|
for req := range reqChan {
|
||||||
for _, publisher := range zrs.handlersPublishers {
|
go func(r *ZoneRequest) {
|
||||||
go func(p chan<- *ZoneRequest) {
|
for _, publisher := range zrs.handlersPublishers {
|
||||||
p <- r
|
publisher <- 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
Loading…
Reference in New Issue
Block a user