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 (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/pion/webrtc/v3"
|
||||
@ -97,6 +98,7 @@ func (cm *ChatGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
|
||||
case SYNC_CHAT:
|
||||
//todo: implement sync chat
|
||||
case CHAT_WEBRTC_OFFER:
|
||||
fmt.Println("got offer in chat")
|
||||
if err := validateRequest(payload, SDP); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
@ -115,6 +117,7 @@ func (cm *ChatGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
|
||||
return
|
||||
}
|
||||
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 {
|
||||
errCh <- err
|
||||
return
|
||||
@ -138,6 +141,7 @@ func (cm *ChatGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
|
||||
return
|
||||
}
|
||||
case CHAT_WEBRTC_CANDIDATE:
|
||||
fmt.Println("got candidate in chat")
|
||||
if err := validateRequest(payload, "candidate", "sdpMLineIndex", "sdpMid"); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
@ -160,10 +164,14 @@ func (cm *ChatGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
|
||||
return
|
||||
}
|
||||
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(payload)
|
||||
logger.Println(req.Type)
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
||||
@ -15,6 +15,12 @@ import (
|
||||
"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 {
|
||||
ID string
|
||||
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) {
|
||||
dataChannels, dataChannelFlag := make(map[string]*DataChannel), uint32(0)
|
||||
nodeChatMessageHandler, err := NewNodeChatChannelsHandler(chat.ID, chat.ID, chat.Initiator, chat.Target, chat.InitiatorHost, chat.TargetHost, dataChannels, &dataChannelFlag)
|
||||
chatScheduler, e := NewChatRequestScheduler(initiator, target, initiatorHost, targetHost, nodeChatMessageHandler)
|
||||
nodeChatMessageHandler, err := NewNodeChatChannelsHandler(id, id, initiator, target, initiatorHost, targetHost, dataChannels, &dataChannelFlag)
|
||||
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() {
|
||||
for schedErr := range e {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
b, err := json.Marshal(payload["Chats"])
|
||||
fmt.Println(payload)
|
||||
b, err := json.Marshal(payload["chats"])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(b, &chats)
|
||||
|
||||
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) {
|
||||
done, errCh := make(chan struct{}), make(chan error)
|
||||
go func() {
|
||||
if _, ok := cm.Chats[req["chatId"]]; !ok {
|
||||
if id, ok := cm.Chats[req["chatId"]]; !ok {
|
||||
err = fmt.Errorf("no corresponding chat")
|
||||
fmt.Println(req["chatId"])
|
||||
fmt.Println(cm.Chats)
|
||||
fmt.Println(id)
|
||||
fmt.Println(err)
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
@ -374,7 +395,7 @@ func (cm *ChatsManager) createPeerConnection(target string, from string, chatId
|
||||
config := webrtc.Configuration{
|
||||
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,
|
||||
@ -511,6 +532,7 @@ func (cm *ChatsManager) createPeerConnection(target string, from string, chatId
|
||||
logger.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println("incoming req", req)
|
||||
logger.Println("incoming request", req)
|
||||
go func() {
|
||||
reqChan <- &req
|
||||
@ -699,7 +721,7 @@ func (cm *ChatsManager) notifyLeavingMember(userId, chatId, hostId string) (err
|
||||
em := NewEncryptionManager()
|
||||
sig := em.SignRequestHMAC(NodeID)
|
||||
body, err := json.Marshal(map[string]interface{}{
|
||||
"type": "DISCONNECT_CHAT_MEMBER",
|
||||
"type": DISCONNECT_USER_FROM_CHAT,
|
||||
"mac": sig,
|
||||
"from": NodeID,
|
||||
"peerType": "node",
|
||||
|
||||
@ -259,6 +259,7 @@ func (ncdbh *NodeChatDBHandler) ListChatFiles(lastIndex int, limit int) (chatMes
|
||||
return err
|
||||
}
|
||||
if chatMessage.File != nil {
|
||||
chatMessage.File.ID = chatMessage.ID
|
||||
chatMessages = append(chatMessages, chatMessage.File)
|
||||
l = lastIndex - x
|
||||
y++
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
package localserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"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) {
|
||||
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 os.IsNotExist(err) {
|
||||
logger.Printf("creating chat directory for chat %s...\n", chatID)
|
||||
@ -80,7 +81,7 @@ func NewNodeChatChannelsHandler(chatID, chatName, initiator, target, initiatorHo
|
||||
return nil, writeErr
|
||||
}
|
||||
_ = file.Close()
|
||||
dirs, err = os.ReadDir(filepath.Join(dataPath, "data", "chats", chatID))
|
||||
_, err = os.ReadDir(filepath.Join(dataPath, "data", "chats"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -89,33 +90,30 @@ func NewNodeChatChannelsHandler(chatID, chatName, initiator, target, initiatorHo
|
||||
}
|
||||
}
|
||||
chats := make(map[string]*NodeChatChannel)
|
||||
for _, chat := range dirs {
|
||||
if strings.HasPrefix(chat.Name(), ".") {
|
||||
continue
|
||||
}
|
||||
nodeChatDBHandler, err := NewNodeChatDBHandler(chat.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var bs []byte
|
||||
bs, err = os.ReadFile(filepath.Join(dataPath, "data", "chats", chat.Name(), "chatConfig.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Println(string(bs))
|
||||
var c NodeChatChannel
|
||||
if err = json.Unmarshal(bs, &c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeChatTracking, err := NewNodeChatTracking(chatID, initiator, target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Println("chats data :", c.ID, c.Initiator, c.InitiatorHost, c.Target)
|
||||
c.DB = nodeChatDBHandler
|
||||
c.Tracking = nodeChatTracking
|
||||
chats[c.ID] = &c
|
||||
|
||||
nodeChatDBHandler, err := NewNodeChatDBHandler(chatID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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)
|
||||
chatFSFlag := uint32(0)
|
||||
chatDCFlag := uint32(0)
|
||||
@ -134,7 +132,7 @@ func NewNodeChatChannelsHandler(chatID, chatName, initiator, target, initiatorHo
|
||||
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() {
|
||||
for _, rc := range zch.reqChans {
|
||||
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) {
|
||||
reqChan, done, errCh = make(chan *ChatRequest), make(chan struct{}), make(chan error)
|
||||
fmt.Println("subscribing...")
|
||||
fmt.Println(zch)
|
||||
zch.reqChans = append(zch.reqChans, reqChan)
|
||||
go func() {
|
||||
for {
|
||||
@ -200,6 +200,7 @@ func (zch *NodeChatChannelsHandler[T]) Subscribe(ctx context.Context, publisher
|
||||
}
|
||||
}
|
||||
}()
|
||||
fmt.Println(zch.reqChans)
|
||||
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{}{
|
||||
"done": done || i <= 0,
|
||||
"lastIndex": i - 1,
|
||||
"chatID": chatID,
|
||||
"chatId": chatID,
|
||||
"chatMessages": list,
|
||||
})
|
||||
select {
|
||||
@ -359,6 +360,9 @@ func (zch *NodeChatChannelsHandler[T]) ListLatestChatMessages(userId, chatID str
|
||||
fmt.Println("done getting latest messages")
|
||||
case err = <-e:
|
||||
}
|
||||
go func() {
|
||||
_ = zch.signalUnreadCount(context.Background(), userId, 0)
|
||||
}()
|
||||
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": i <= 0,
|
||||
"lastIndex": i,
|
||||
"chatID": chatID,
|
||||
"chatId": chatID,
|
||||
"chatMessages": list,
|
||||
})
|
||||
select {
|
||||
@ -392,6 +396,7 @@ func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content st
|
||||
return
|
||||
}
|
||||
chat := zch.Chats[chatID]
|
||||
dateTime := time.Now().Format(time.RFC3339)
|
||||
chatMessage := &ChatMessage{
|
||||
Content: content,
|
||||
From: userId,
|
||||
@ -399,7 +404,7 @@ func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content st
|
||||
ResponseOf: nil,
|
||||
File: file,
|
||||
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 chat, ok := zch.Chats[chatID]; ok {
|
||||
@ -416,7 +421,7 @@ func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content st
|
||||
return
|
||||
}
|
||||
chatMessage.ID = zch.Chats[chatID].DB.PreviousId
|
||||
|
||||
_ = chat.Tracking.SetUserLastIndex(userId, chatMessage.ID)
|
||||
} else {
|
||||
err = fmt.Errorf("no corresponding chats")
|
||||
}
|
||||
@ -425,13 +430,14 @@ func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content st
|
||||
return
|
||||
}
|
||||
|
||||
notifyActivity := func(done <-chan struct{}, e <-chan error, target, initiator string) {
|
||||
notifyActivity := func(done <-chan struct{}, e <-chan error, destination string) {
|
||||
select {
|
||||
case <-done:
|
||||
fmt.Println(destination, "is connected to this chat")
|
||||
case <-e:
|
||||
_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
|
||||
if chat, ok := zch.Chats[chatID]; ok {
|
||||
li, err := chat.Tracking.GetUserLastIndex(target)
|
||||
li, err := chat.Tracking.GetUserLastIndex(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -439,23 +445,40 @@ func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content st
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("********_**_*")
|
||||
fmt.Println(count)
|
||||
bs, err := json.Marshal(map[string]any{
|
||||
"chatID": chatID,
|
||||
"chatId": chatID,
|
||||
"chatHost": NodeID,
|
||||
})
|
||||
if err != nil {
|
||||
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",
|
||||
"title": "Unread messages 👀",
|
||||
"body": fmt.Sprintf("%d new messages from %s", count, initiator),
|
||||
"title": fmt.Sprintf("%d Unread messages 👀", count),
|
||||
"body": fmt.Sprintf("%d new messages from %s", count, userId),
|
||||
"isPushed": true,
|
||||
"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
|
||||
})
|
||||
@ -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{}{
|
||||
"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{}{
|
||||
"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
|
||||
}
|
||||
|
||||
@ -502,7 +528,7 @@ func (zch *NodeChatChannelsHandler[T]) DeleteChatMessage(key uint64, chatID stri
|
||||
}
|
||||
for _, member := range [2]string{chat.Initiator, chat.Target} {
|
||||
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{
|
||||
"chatID": chatID,
|
||||
"chatId": chatID,
|
||||
"messageId": key,
|
||||
})
|
||||
select {
|
||||
@ -542,7 +568,7 @@ func (zch *NodeChatChannelsHandler[T]) UpdateChatMessage(key uint64, chatID, new
|
||||
}
|
||||
for _, member := range [2]string{chat.Target, chat.Initiator} {
|
||||
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_EDITED, "node", member, map[string]interface{}{
|
||||
"chatID": chatID,
|
||||
"chatId": chatID,
|
||||
"messageId": key,
|
||||
"newContent": newContent,
|
||||
})
|
||||
@ -570,7 +596,7 @@ func (zch *NodeChatChannelsHandler[T]) DeleteChatFile(key uint64, fileName, chat
|
||||
}
|
||||
for _, member := range [2]string{chat.Target, chat.Initiator} {
|
||||
d, e := zch.sendDataChannelMessage(CHAT_FILE_DELETED, "node", member, map[string]interface{}{
|
||||
"chatID": chatID,
|
||||
"chatId": chatID,
|
||||
"fileId": key,
|
||||
"fileName": fileName,
|
||||
})
|
||||
@ -705,6 +731,24 @@ func (zch *NodeChatChannelsHandler[T]) handleDataChannel(ctx context.Context, dc
|
||||
zch.ChatDataChannels[command[1]] = dc
|
||||
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() {
|
||||
fmt.Println("closing gratefully chat dc...")
|
||||
_ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) {
|
||||
@ -723,8 +767,65 @@ func (zch *NodeChatChannelsHandler[T]) handleDataChannel(ctx context.Context, dc
|
||||
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) {
|
||||
logger.Println("got request in zone chat handler", req)
|
||||
|
||||
fmt.Println(req)
|
||||
switch req.ReqType {
|
||||
case LEAVE_CHAT:
|
||||
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))
|
||||
case DELETE_CHAT:
|
||||
if err = VerifyFieldsString(req.Payload, "chatID"); err != nil {
|
||||
if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
|
||||
return
|
||||
}
|
||||
err = zch.DeleteChat(req.Payload["chatID"].(string))
|
||||
err = zch.DeleteChat(req.Payload["chatId"].(string))
|
||||
case CREATE_CHAT:
|
||||
if err = VerifyFieldsString(req.Payload, "chatID", "owner", "chatType"); err != nil {
|
||||
if err = VerifyFieldsString(req.Payload, "chatId", "owner", "chatType"); err != nil {
|
||||
return
|
||||
}
|
||||
if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil {
|
||||
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:
|
||||
err = zch.GetChats(req.From)
|
||||
case LIST_LATEST_CHATS:
|
||||
if err = VerifyFieldsString(req.Payload, "chatID"); err != nil {
|
||||
if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
|
||||
return
|
||||
}
|
||||
if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil {
|
||||
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
|
||||
case LIST_LATEST_FILES:
|
||||
if err = VerifyFieldsString(req.Payload, "chatID"); err != nil {
|
||||
if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
|
||||
return
|
||||
}
|
||||
if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil {
|
||||
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
|
||||
case READ_LATEST_MESSAGE:
|
||||
if err = VerifyFieldsString(req.Payload, "chatID"); err != nil {
|
||||
if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
|
||||
return
|
||||
}
|
||||
err = zch.ReadLastMessage(req.From, req.Payload["chatID"].(string))
|
||||
err = zch.ReadLastMessage(req.From, req.Payload["chatId"].(string))
|
||||
case ADD_CHAT_MESSAGE:
|
||||
logger.Println("got request in zone chat handler", req)
|
||||
if err = VerifyFieldsString(req.Payload, "chatID", "content"); err != nil {
|
||||
if err = VerifyFieldsString(req.Payload, "chatId", "content"); err != nil {
|
||||
return
|
||||
}
|
||||
if err = VerifyFieldsBool(req.Payload, "isResponse"); err != nil {
|
||||
@ -797,31 +898,31 @@ func (zch *NodeChatChannelsHandler[T]) handleChatRequest(ctx context.Context, re
|
||||
}
|
||||
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:
|
||||
if err = VerifyFieldsString(req.Payload, "chatID"); err != nil {
|
||||
if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
|
||||
return
|
||||
}
|
||||
if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil {
|
||||
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:
|
||||
if err = VerifyFieldsString(req.Payload, "chatID", "newContent"); err != nil {
|
||||
if err = VerifyFieldsString(req.Payload, "chatId", "newContent"); err != nil {
|
||||
return
|
||||
}
|
||||
if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil {
|
||||
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:
|
||||
if err = VerifyFieldsString(req.Payload, "chatID", "fileName"); err != nil {
|
||||
if err = VerifyFieldsString(req.Payload, "chatId", "fileName"); err != nil {
|
||||
return
|
||||
}
|
||||
if err = VerifyFieldsFloat64(req.Payload, "fileId"); err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@ -41,6 +42,8 @@ func NewChatRequestScheduler(initiator, target, initiatorNodeID, targetNodeID st
|
||||
publisher := make(chan *ChatRequest, 100)
|
||||
chatRequestScheduler.handlersPublishers = append(chatRequestScheduler.handlersPublishers, publisher)
|
||||
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)
|
||||
go func(done <-chan struct{}, errCh <-chan error) {
|
||||
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) {
|
||||
done, errCh = make(chan struct{}), make(chan error)
|
||||
for req := range reqChan {
|
||||
go func(r *ChatRequest) {
|
||||
for _, publisher := range crs.handlersPublishers {
|
||||
go func(p chan<- *ChatRequest) {
|
||||
p <- r
|
||||
}(publisher)
|
||||
}
|
||||
}(req)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for req := range reqChan {
|
||||
go func(r *ChatRequest) {
|
||||
for _, publisher := range crs.handlersPublishers {
|
||||
publisher <- r
|
||||
}
|
||||
}(req)
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
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 != "" {
|
||||
privePemBytes, err = x509.DecryptPEMBlock(privPem, []byte(password))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
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)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
err = fmt.Errorf("error from zone manager")
|
||||
return
|
||||
}
|
||||
// webrtcFsManager, err := NewWebrtcFsManager(NewP2PFSDatachannelManager())
|
||||
// if err != nil {
|
||||
// err = fmt.Errorf("error from fs manager")
|
||||
// return
|
||||
// }
|
||||
webrtcGrpcMiddleware := NewWebRTCGrpcMiddleware(webRTCCallManager)
|
||||
ZoneGrpcMiddleware := NewZoneGrpcMiddleware(zoneManager)
|
||||
signalingClientManager, err := NewGrpcClientManager(grpcAddr, id, webrtcGrpcMiddleware, ZoneGrpcMiddleware)
|
||||
chatManager, err := NewChatManager(id, token)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
squadManager, err := NewSquadManager(id, token)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
zoneGrpcMiddleware := NewZoneGrpcMiddleware(zoneManager)
|
||||
chatGrpcMiddleware := NewChatGrpcMiddleware(chatManager)
|
||||
squadGrpcMiddleware := NewSquadGrpcMiddleware(squadManager)
|
||||
signalingClientManager, err := NewGrpcClientManager(grpcAddr, id, zoneGrpcMiddleware, chatGrpcMiddleware, squadGrpcMiddleware)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
webrtcGrpcMiddleware.stream = signalingClientManager.GrpcLinkClient
|
||||
webRTCCallManager.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{
|
||||
ID: id,
|
||||
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 (
|
||||
"fmt"
|
||||
"sync"
|
||||
"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) {
|
||||
for _, entry := range entries {
|
||||
if _, ok := req[entry]; !ok {
|
||||
|
||||
@ -1,136 +1,136 @@
|
||||
package localserver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
)
|
||||
// import (
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
// "sync/atomic"
|
||||
// )
|
||||
|
||||
const (
|
||||
CHAT_MESSAGE_BROADCAST = "chat_message_broadcast"
|
||||
CHAT_MESSAGE_PRIVATE = "chat_message_private"
|
||||
)
|
||||
// const (
|
||||
// CHAT_MESSAGE_BROADCAST = "chat_message_broadcast"
|
||||
// CHAT_MESSAGE_PRIVATE = "chat_message_private"
|
||||
// )
|
||||
|
||||
type WebrtcCallChatManager struct{}
|
||||
// type WebrtcCallChatManager struct{}
|
||||
|
||||
func NewWebrtcCallChatManager() *WebrtcCallChatManager {
|
||||
return new(WebrtcCallChatManager)
|
||||
}
|
||||
// func NewWebrtcCallChatManager() *WebrtcCallChatManager {
|
||||
// return new(WebrtcCallChatManager)
|
||||
// }
|
||||
|
||||
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)
|
||||
go func() {
|
||||
logger.Println("got an event in call chat manager", from, eventId, payload)
|
||||
switch eventId {
|
||||
case CHAT_MESSAGE_BROADCAST:
|
||||
if e := validateEvent(payload, "message"); e != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
if e := w.sendBrodcastChatMessage(from, payload["message"].(string), squadId, manager); e != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
case CHAT_MESSAGE_PRIVATE:
|
||||
if e := validateEvent(payload, "message", "dst"); e != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
if e := w.sendPrivateChatMessage(from, payload["message"].(string), squadId, manager, payload["message"].([]string)...); e != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
return nil
|
||||
case err = <-errCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
// go func() {
|
||||
// logger.Println("got an event in call chat manager", from, eventId, payload)
|
||||
// switch eventId {
|
||||
// case CHAT_MESSAGE_BROADCAST:
|
||||
// if e := validateEvent(payload, "message"); e != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// if e := w.sendBrodcastChatMessage(from, payload["message"].(string), squadId, manager); e != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// case CHAT_MESSAGE_PRIVATE:
|
||||
// if e := validateEvent(payload, "message", "dst"); e != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// if e := w.sendPrivateChatMessage(from, payload["message"].(string), squadId, manager, payload["message"].([]string)...); e != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// done <- struct{}{}
|
||||
// }()
|
||||
// select {
|
||||
// case <-done:
|
||||
// return nil
|
||||
// case err = <-errCh:
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
func (w WebrtcCallChatManager) sendBrodcastChatMessage(from string, message string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
if _, ok := manager.Squads[squadId]; !ok {
|
||||
err = fmt.Errorf("no correponding squad found")
|
||||
return
|
||||
}
|
||||
for _, member := range manager.Squads[squadId].Members {
|
||||
if _, ok := manager.DataChannels[member]; ok && member != from {
|
||||
bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
"type": "send_chat_message",
|
||||
"from": from,
|
||||
"payload": map[string]string{
|
||||
"message": message,
|
||||
},
|
||||
})
|
||||
if marshalErr != nil {
|
||||
logger.Println(err)
|
||||
continue
|
||||
}
|
||||
lock:
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
logger.Println(sendErr)
|
||||
}
|
||||
break lock
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// func (w WebrtcCallChatManager) sendBrodcastChatMessage(from string, message string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
// if _, ok := manager.Squads[squadId]; !ok {
|
||||
// err = fmt.Errorf("no correponding squad found")
|
||||
// return
|
||||
// }
|
||||
// for _, member := range manager.Squads[squadId].Members {
|
||||
// if _, ok := manager.DataChannels[member]; ok && member != from {
|
||||
// bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
// "type": "send_chat_message",
|
||||
// "from": from,
|
||||
// "payload": map[string]string{
|
||||
// "message": message,
|
||||
// },
|
||||
// })
|
||||
// if marshalErr != nil {
|
||||
// logger.Println(err)
|
||||
// continue
|
||||
// }
|
||||
// lock:
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
// defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
// if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
// logger.Println(sendErr)
|
||||
// }
|
||||
// break lock
|
||||
// } else {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (w WebrtcCallChatManager) sendPrivateChatMessage(from string, message string, squadId string, manager *WebRTCCallManager, dst ...string) (err error) {
|
||||
if _, ok := manager.Squads[squadId]; !ok {
|
||||
err = fmt.Errorf("no correponding squad found")
|
||||
return
|
||||
}
|
||||
for _, member := range manager.Squads[squadId].Members {
|
||||
for _, id := range dst {
|
||||
if id == member {
|
||||
if _, ok := manager.DataChannels[member]; ok && member != from {
|
||||
bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
"type": "",
|
||||
"from": from,
|
||||
"payload": map[string]string{
|
||||
"message": message,
|
||||
},
|
||||
})
|
||||
if marshalErr != nil {
|
||||
logger.Println(err)
|
||||
continue
|
||||
}
|
||||
lock:
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
logger.Println(sendErr)
|
||||
}
|
||||
break lock
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// func (w WebrtcCallChatManager) sendPrivateChatMessage(from string, message string, squadId string, manager *WebRTCCallManager, dst ...string) (err error) {
|
||||
// if _, ok := manager.Squads[squadId]; !ok {
|
||||
// err = fmt.Errorf("no correponding squad found")
|
||||
// return
|
||||
// }
|
||||
// for _, member := range manager.Squads[squadId].Members {
|
||||
// for _, id := range dst {
|
||||
// if id == member {
|
||||
// if _, ok := manager.DataChannels[member]; ok && member != from {
|
||||
// bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
// "type": "",
|
||||
// "from": from,
|
||||
// "payload": map[string]string{
|
||||
// "message": message,
|
||||
// },
|
||||
// })
|
||||
// if marshalErr != nil {
|
||||
// logger.Println(err)
|
||||
// continue
|
||||
// }
|
||||
// lock:
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
// defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
// if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
// logger.Println(sendErr)
|
||||
// }
|
||||
// break lock
|
||||
// } else {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func validateEvent(event map[string]interface{}, fields ...string) (err error) {
|
||||
for _, field := range fields {
|
||||
if _, ok := event[field]; !ok {
|
||||
err = fmt.Errorf("no field %s in req payload", field)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// func validateEvent(event map[string]interface{}, fields ...string) (err error) {
|
||||
// for _, field := range fields {
|
||||
// if _, ok := event[field]; !ok {
|
||||
// err = fmt.Errorf("no field %s in req payload", field)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
package localserver
|
||||
|
||||
type WebrtcCallEventManager interface {
|
||||
HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error)
|
||||
}
|
||||
// type WebrtcCallEventManager interface {
|
||||
// HandleCallEvent(from string, squadId string, eventId string, payload map[string]interface{}, data []byte, manager *WebRTCCallManager) (err error)
|
||||
// }
|
||||
|
||||
@ -1,311 +1,311 @@
|
||||
package localserver
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
// import (
|
||||
// "bufio"
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
// "io"
|
||||
// "log"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "sync/atomic"
|
||||
|
||||
"github.com/pion/webrtc/v3"
|
||||
)
|
||||
// "github.com/pion/webrtc/v3"
|
||||
// )
|
||||
|
||||
const (
|
||||
UPLOAD_INIT = "upload_init"
|
||||
UPLOAD = "upload"
|
||||
UPLOAD_DONE = "upload_done"
|
||||
DOWNLOAD_INIT = "download_init"
|
||||
DOWNLOAD = "download"
|
||||
DOWNLOAD_DONE = "download_done"
|
||||
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_END = "hosted_squad_download_file_response_end"
|
||||
)
|
||||
// const (
|
||||
// UPLOAD_INIT = "upload_init"
|
||||
// UPLOAD = "upload"
|
||||
// UPLOAD_DONE = "upload_done"
|
||||
// DOWNLOAD_INIT = "download_init"
|
||||
// DOWNLOAD = "download"
|
||||
// DOWNLOAD_DONE = "download_done"
|
||||
// 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_END = "hosted_squad_download_file_response_end"
|
||||
// )
|
||||
|
||||
const (
|
||||
bufferedAmountLowThreshold uint64 = 512 * 1024
|
||||
//maxBufferedAmount uint64 = 1024 * 1024
|
||||
)
|
||||
// const (
|
||||
// bufferedAmountLowThreshold uint64 = 512 * 1024
|
||||
// //maxBufferedAmount uint64 = 1024 * 1024
|
||||
// )
|
||||
|
||||
type WebrtcCallFileManager struct {
|
||||
files map[string]*os.File
|
||||
l *uint32
|
||||
}
|
||||
// type WebrtcCallFileManager struct {
|
||||
// files map[string]*os.File
|
||||
// l *uint32
|
||||
// }
|
||||
|
||||
func NewWebrtcCallFileManager() *WebrtcCallFileManager {
|
||||
l := uint32(0)
|
||||
return &WebrtcCallFileManager{
|
||||
files: make(map[string]*os.File),
|
||||
l: &l,
|
||||
}
|
||||
}
|
||||
// func NewWebrtcCallFileManager() *WebrtcCallFileManager {
|
||||
// l := uint32(0)
|
||||
// return &WebrtcCallFileManager{
|
||||
// files: make(map[string]*os.File),
|
||||
// l: &l,
|
||||
// }
|
||||
// }
|
||||
|
||||
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)
|
||||
go func() {
|
||||
logger.Println("got an event in call file manager", from, eventId, payload)
|
||||
switch eventId {
|
||||
case UPLOAD_INIT:
|
||||
if _, ok := payload["filename"]; !ok {
|
||||
errCh <- fmt.Errorf("no field filename in payload")
|
||||
return
|
||||
}
|
||||
if _, ok := payload["filename"].(string); !ok {
|
||||
errCh <- fmt.Errorf("field filename in payload is not a string")
|
||||
return
|
||||
}
|
||||
if err = w.initUpload(squadId, from, payload["filename"].(string)); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
case UPLOAD:
|
||||
if _, ok := payload["filename"]; !ok {
|
||||
errCh <- fmt.Errorf("no field filename in payload")
|
||||
return
|
||||
}
|
||||
if _, ok := payload["filename"].(string); !ok {
|
||||
errCh <- fmt.Errorf("field filename in payload is not a string")
|
||||
return
|
||||
}
|
||||
if err = w.upload(squadId, from, payload["filename"].(string), data); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
case UPLOAD_DONE:
|
||||
if _, ok := payload["filename"]; !ok {
|
||||
errCh <- fmt.Errorf("no field filename in payload")
|
||||
return
|
||||
}
|
||||
if _, ok := payload["filename"].(string); !ok {
|
||||
errCh <- fmt.Errorf("field filename in payload is not a string")
|
||||
return
|
||||
}
|
||||
if _, ok := payload["targets"]; !ok {
|
||||
errCh <- fmt.Errorf("no field targets in payload")
|
||||
return
|
||||
}
|
||||
if _, ok := payload["targets"].([]interface{}); !ok {
|
||||
errCh <- fmt.Errorf("field targets in payload is not a string")
|
||||
return
|
||||
}
|
||||
channels := []*DataChannel{}
|
||||
manager.DataChannelMapMux.RLock()
|
||||
for _, target := range payload["targets"].([]interface{}) {
|
||||
if _, ok := manager.DataChannels[target.(string)]; !ok {
|
||||
manager.DataChannelMapMux.RUnlock()
|
||||
errCh <- fmt.Errorf("no corresponding datachannel : %s", target.(string))
|
||||
return
|
||||
}
|
||||
channel := manager.DataChannels[target.(string)]
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(channel.l, 0, 1) {
|
||||
defer atomic.SwapUint32(channel.l, 0)
|
||||
break
|
||||
}
|
||||
}
|
||||
channels = append(channels, channel)
|
||||
}
|
||||
manager.DataChannelMapMux.RUnlock()
|
||||
if err = w.uploadDone(squadId, from, payload["filename"].(string), channels); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
case DOWNLOAD:
|
||||
if _, ok := payload["filename"]; !ok {
|
||||
errCh <- fmt.Errorf("no field filename in payload")
|
||||
return
|
||||
}
|
||||
if _, ok := payload["filename"].(string); !ok {
|
||||
errCh <- fmt.Errorf("field filename in payload is not a string")
|
||||
return
|
||||
}
|
||||
if _, ok := payload["peerId"]; !ok {
|
||||
errCh <- fmt.Errorf("no field peerId in payload")
|
||||
return
|
||||
}
|
||||
if _, ok := payload["peerId"].(string); !ok {
|
||||
errCh <- fmt.Errorf("field peerId in payload is not a string")
|
||||
return
|
||||
}
|
||||
manager.DataChannelMapMux.RLock()
|
||||
if _, ok := manager.DataChannels[payload["peerId"].(string)]; !ok {
|
||||
manager.DataChannelMapMux.RUnlock()
|
||||
errCh <- fmt.Errorf("no corresponding datachannel")
|
||||
return
|
||||
}
|
||||
channel := manager.DataChannels[payload["peerId"].(string)]
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(channel.l, 0, 1) {
|
||||
logger.Println("atomic lock unlocked")
|
||||
defer atomic.SwapUint32(channel.l, 0)
|
||||
break
|
||||
}
|
||||
}
|
||||
manager.DataChannelMapMux.RUnlock()
|
||||
if err = w.download(squadId, from, payload["filename"].(string), channel.DataChannel); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
return nil
|
||||
case err = <-errCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
// go func() {
|
||||
// logger.Println("got an event in call file manager", from, eventId, payload)
|
||||
// switch eventId {
|
||||
// case UPLOAD_INIT:
|
||||
// if _, ok := payload["filename"]; !ok {
|
||||
// errCh <- fmt.Errorf("no field filename in payload")
|
||||
// return
|
||||
// }
|
||||
// if _, ok := payload["filename"].(string); !ok {
|
||||
// errCh <- fmt.Errorf("field filename in payload is not a string")
|
||||
// return
|
||||
// }
|
||||
// if err = w.initUpload(squadId, from, payload["filename"].(string)); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// case UPLOAD:
|
||||
// if _, ok := payload["filename"]; !ok {
|
||||
// errCh <- fmt.Errorf("no field filename in payload")
|
||||
// return
|
||||
// }
|
||||
// if _, ok := payload["filename"].(string); !ok {
|
||||
// errCh <- fmt.Errorf("field filename in payload is not a string")
|
||||
// return
|
||||
// }
|
||||
// if err = w.upload(squadId, from, payload["filename"].(string), data); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// case UPLOAD_DONE:
|
||||
// if _, ok := payload["filename"]; !ok {
|
||||
// errCh <- fmt.Errorf("no field filename in payload")
|
||||
// return
|
||||
// }
|
||||
// if _, ok := payload["filename"].(string); !ok {
|
||||
// errCh <- fmt.Errorf("field filename in payload is not a string")
|
||||
// return
|
||||
// }
|
||||
// if _, ok := payload["targets"]; !ok {
|
||||
// errCh <- fmt.Errorf("no field targets in payload")
|
||||
// return
|
||||
// }
|
||||
// if _, ok := payload["targets"].([]interface{}); !ok {
|
||||
// errCh <- fmt.Errorf("field targets in payload is not a string")
|
||||
// return
|
||||
// }
|
||||
// channels := []*DataChannel{}
|
||||
// manager.DataChannelMapMux.RLock()
|
||||
// for _, target := range payload["targets"].([]interface{}) {
|
||||
// if _, ok := manager.DataChannels[target.(string)]; !ok {
|
||||
// manager.DataChannelMapMux.RUnlock()
|
||||
// errCh <- fmt.Errorf("no corresponding datachannel : %s", target.(string))
|
||||
// return
|
||||
// }
|
||||
// channel := manager.DataChannels[target.(string)]
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(channel.l, 0, 1) {
|
||||
// defer atomic.SwapUint32(channel.l, 0)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// channels = append(channels, channel)
|
||||
// }
|
||||
// manager.DataChannelMapMux.RUnlock()
|
||||
// if err = w.uploadDone(squadId, from, payload["filename"].(string), channels); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// case DOWNLOAD:
|
||||
// if _, ok := payload["filename"]; !ok {
|
||||
// errCh <- fmt.Errorf("no field filename in payload")
|
||||
// return
|
||||
// }
|
||||
// if _, ok := payload["filename"].(string); !ok {
|
||||
// errCh <- fmt.Errorf("field filename in payload is not a string")
|
||||
// return
|
||||
// }
|
||||
// if _, ok := payload["peerId"]; !ok {
|
||||
// errCh <- fmt.Errorf("no field peerId in payload")
|
||||
// return
|
||||
// }
|
||||
// if _, ok := payload["peerId"].(string); !ok {
|
||||
// errCh <- fmt.Errorf("field peerId in payload is not a string")
|
||||
// return
|
||||
// }
|
||||
// manager.DataChannelMapMux.RLock()
|
||||
// if _, ok := manager.DataChannels[payload["peerId"].(string)]; !ok {
|
||||
// manager.DataChannelMapMux.RUnlock()
|
||||
// errCh <- fmt.Errorf("no corresponding datachannel")
|
||||
// return
|
||||
// }
|
||||
// channel := manager.DataChannels[payload["peerId"].(string)]
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(channel.l, 0, 1) {
|
||||
// logger.Println("atomic lock unlocked")
|
||||
// defer atomic.SwapUint32(channel.l, 0)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// manager.DataChannelMapMux.RUnlock()
|
||||
// if err = w.download(squadId, from, payload["filename"].(string), channel.DataChannel); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// done <- struct{}{}
|
||||
// }()
|
||||
// select {
|
||||
// case <-done:
|
||||
// return nil
|
||||
// case err = <-errCh:
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
func (w *WebrtcCallFileManager) initUpload(squadId string, from string, fileName string) (err error) {
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(w.l, 0, 1) {
|
||||
defer atomic.SwapUint32(w.l, 0)
|
||||
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 {
|
||||
return
|
||||
}
|
||||
}
|
||||
f, fErr := os.Create(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName))
|
||||
if err != nil {
|
||||
return fErr
|
||||
}
|
||||
f.Close()
|
||||
f, fErr = os.OpenFile(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName), os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return fErr
|
||||
}
|
||||
w.files[fileName] = f
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// func (w *WebrtcCallFileManager) initUpload(squadId string, from string, fileName string) (err error) {
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(w.l, 0, 1) {
|
||||
// defer atomic.SwapUint32(w.l, 0)
|
||||
// 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 {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// f, fErr := os.Create(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName))
|
||||
// if err != nil {
|
||||
// return fErr
|
||||
// }
|
||||
// f.Close()
|
||||
// f, fErr = os.OpenFile(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName), os.O_APPEND|os.O_WRONLY, 0644)
|
||||
// if err != nil {
|
||||
// return fErr
|
||||
// }
|
||||
// w.files[fileName] = f
|
||||
// break
|
||||
// } else {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (w *WebrtcCallFileManager) upload(squadId string, from string, fileName string, data []byte) (err error) {
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(w.l, 0, 1) {
|
||||
defer atomic.SwapUint32(w.l, 0)
|
||||
if _, ok := w.files[fileName]; !ok {
|
||||
err = fmt.Errorf("no open file with name %s", fileName)
|
||||
return
|
||||
}
|
||||
_, err = w.files[fileName].Write(data)
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// func (w *WebrtcCallFileManager) upload(squadId string, from string, fileName string, data []byte) (err error) {
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(w.l, 0, 1) {
|
||||
// defer atomic.SwapUint32(w.l, 0)
|
||||
// if _, ok := w.files[fileName]; !ok {
|
||||
// err = fmt.Errorf("no open file with name %s", fileName)
|
||||
// return
|
||||
// }
|
||||
// _, err = w.files[fileName].Write(data)
|
||||
// break
|
||||
// } else {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (w *WebrtcCallFileManager) uploadDone(squadId string, from string, fileName string, channels []*DataChannel) (err error) {
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(w.l, 0, 1) {
|
||||
defer atomic.SwapUint32(w.l, 0)
|
||||
if _, ok := w.files[fileName]; !ok {
|
||||
err = fmt.Errorf("no open file with name %s", fileName)
|
||||
return
|
||||
}
|
||||
err = w.files[fileName].Close()
|
||||
delete(w.files, fileName)
|
||||
bsInit, jsonErr := json.Marshal(map[string]interface{}{
|
||||
"type": UPLOAD_DONE,
|
||||
"from": "server",
|
||||
"payload": map[string]string{
|
||||
"path": fileName,
|
||||
},
|
||||
})
|
||||
if jsonErr != nil {
|
||||
return jsonErr
|
||||
}
|
||||
for _, channel := range channels {
|
||||
if err = channel.DataChannel.SendText(string(bsInit)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// func (w *WebrtcCallFileManager) uploadDone(squadId string, from string, fileName string, channels []*DataChannel) (err error) {
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(w.l, 0, 1) {
|
||||
// defer atomic.SwapUint32(w.l, 0)
|
||||
// if _, ok := w.files[fileName]; !ok {
|
||||
// err = fmt.Errorf("no open file with name %s", fileName)
|
||||
// return
|
||||
// }
|
||||
// err = w.files[fileName].Close()
|
||||
// delete(w.files, fileName)
|
||||
// bsInit, jsonErr := json.Marshal(map[string]interface{}{
|
||||
// "type": UPLOAD_DONE,
|
||||
// "from": "server",
|
||||
// "payload": map[string]string{
|
||||
// "path": fileName,
|
||||
// },
|
||||
// })
|
||||
// if jsonErr != nil {
|
||||
// return jsonErr
|
||||
// }
|
||||
// for _, channel := range channels {
|
||||
// if err = channel.DataChannel.SendText(string(bsInit)); err != nil {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// break
|
||||
// } else {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (w *WebrtcCallFileManager) download(squadId string, dst string, fileName string, channel *webrtc.DataChannel) (err error) {
|
||||
logger.Println("got called")
|
||||
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))
|
||||
return
|
||||
}
|
||||
f, err := os.Open(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
bsInit, err := json.Marshal(map[string]interface{}{
|
||||
"type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_INIT,
|
||||
"from": "server",
|
||||
"payload": map[string]string{
|
||||
"path": fileName,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = channel.SendText(string(bsInit)); err != nil {
|
||||
return
|
||||
}
|
||||
r := bufio.NewReader(f)
|
||||
buf := make([]byte, 0, 30000)
|
||||
logger.Println("start reading")
|
||||
for {
|
||||
n, readErr := r.Read(buf[:cap(buf)])
|
||||
buf = buf[:n]
|
||||
if n == 0 {
|
||||
if err == nil {
|
||||
logger.Println("n is 0 weird")
|
||||
break
|
||||
}
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
log.Fatal(readErr)
|
||||
}
|
||||
bs, jsonErr := json.Marshal(map[string]interface{}{
|
||||
"type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE,
|
||||
"from": "server",
|
||||
"payload": map[string]interface{}{
|
||||
"path": fileName,
|
||||
"content": buf,
|
||||
},
|
||||
})
|
||||
if jsonErr != nil {
|
||||
return jsonErr
|
||||
}
|
||||
if err = channel.SendText(string(bs)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
logger.Println("stop reading")
|
||||
bs, err := json.Marshal(map[string]interface{}{
|
||||
"type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_END,
|
||||
"from": "server",
|
||||
"payload": map[string]string{
|
||||
"path": fileName,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = channel.SendText(string(bs)); err != nil {
|
||||
return
|
||||
}
|
||||
logger.Println("done")
|
||||
return
|
||||
}
|
||||
// func (w *WebrtcCallFileManager) download(squadId string, dst string, fileName string, channel *webrtc.DataChannel) (err error) {
|
||||
// logger.Println("got called")
|
||||
// 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))
|
||||
// return
|
||||
// }
|
||||
// f, err := os.Open(filepath.Join(dataPath, dataPath, "data", "squads", squadId, fileName))
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// defer f.Close()
|
||||
// bsInit, err := json.Marshal(map[string]interface{}{
|
||||
// "type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_INIT,
|
||||
// "from": "server",
|
||||
// "payload": map[string]string{
|
||||
// "path": fileName,
|
||||
// },
|
||||
// })
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// if err = channel.SendText(string(bsInit)); err != nil {
|
||||
// return
|
||||
// }
|
||||
// r := bufio.NewReader(f)
|
||||
// buf := make([]byte, 0, 30000)
|
||||
// logger.Println("start reading")
|
||||
// for {
|
||||
// n, readErr := r.Read(buf[:cap(buf)])
|
||||
// buf = buf[:n]
|
||||
// if n == 0 {
|
||||
// if err == nil {
|
||||
// logger.Println("n is 0 weird")
|
||||
// break
|
||||
// }
|
||||
// if err == io.EOF {
|
||||
// break
|
||||
// }
|
||||
// log.Fatal(readErr)
|
||||
// }
|
||||
// bs, jsonErr := json.Marshal(map[string]interface{}{
|
||||
// "type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE,
|
||||
// "from": "server",
|
||||
// "payload": map[string]interface{}{
|
||||
// "path": fileName,
|
||||
// "content": buf,
|
||||
// },
|
||||
// })
|
||||
// if jsonErr != nil {
|
||||
// return jsonErr
|
||||
// }
|
||||
// if err = channel.SendText(string(bs)); err != nil {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// logger.Println("stop reading")
|
||||
// bs, err := json.Marshal(map[string]interface{}{
|
||||
// "type": HOSTED_SQUAD_DOWNLOAD_FILE_RESPONSE_END,
|
||||
// "from": "server",
|
||||
// "payload": map[string]string{
|
||||
// "path": fileName,
|
||||
// },
|
||||
// })
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// if err = channel.SendText(string(bs)); err != nil {
|
||||
// return
|
||||
// }
|
||||
// logger.Println("done")
|
||||
// return
|
||||
// }
|
||||
|
||||
1620
webrtcCallManager.go
1620
webrtcCallManager.go
File diff suppressed because it is too large
Load Diff
@ -1,234 +1,234 @@
|
||||
package localserver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
// import (
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
// "sync/atomic"
|
||||
|
||||
"github.com/pion/webrtc/v3"
|
||||
)
|
||||
// "github.com/pion/webrtc/v3"
|
||||
// )
|
||||
|
||||
const (
|
||||
SPEAKING = "speaking"
|
||||
STOP_SPEAKING = "stop_speaking"
|
||||
MUTE = "mute"
|
||||
UNMUTE = "unmute"
|
||||
)
|
||||
// const (
|
||||
// SPEAKING = "speaking"
|
||||
// STOP_SPEAKING = "stop_speaking"
|
||||
// MUTE = "mute"
|
||||
// UNMUTE = "unmute"
|
||||
// )
|
||||
|
||||
type WebrtcCallSoundManager struct{}
|
||||
// type WebrtcCallSoundManager struct{}
|
||||
|
||||
func NewWebrtcCallSoundManager() *WebrtcCallSoundManager {
|
||||
return new(WebrtcCallSoundManager)
|
||||
}
|
||||
// func NewWebrtcCallSoundManager() *WebrtcCallSoundManager {
|
||||
// return new(WebrtcCallSoundManager)
|
||||
// }
|
||||
|
||||
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)
|
||||
go func() {
|
||||
logger.Println("got an event in call sound manager", from, eventId, payload)
|
||||
switch eventId {
|
||||
case UNMUTE:
|
||||
if e := w.unmute(from, squadId, manager); e != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
case MUTE:
|
||||
if e := w.mute(from, squadId, manager); e != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
case SPEAKING:
|
||||
if e := w.sendSpeakingEvent(from, squadId, manager); e != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
case STOP_SPEAKING:
|
||||
if e := w.sendStopSpeakingEvent(from, squadId, manager); e != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
return nil
|
||||
case err = <-errCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
// go func() {
|
||||
// logger.Println("got an event in call sound manager", from, eventId, payload)
|
||||
// switch eventId {
|
||||
// case UNMUTE:
|
||||
// if e := w.unmute(from, squadId, manager); e != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// case MUTE:
|
||||
// if e := w.mute(from, squadId, manager); e != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// case SPEAKING:
|
||||
// if e := w.sendSpeakingEvent(from, squadId, manager); e != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// case STOP_SPEAKING:
|
||||
// if e := w.sendStopSpeakingEvent(from, squadId, manager); e != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// done <- struct{}{}
|
||||
// }()
|
||||
// select {
|
||||
// case <-done:
|
||||
// return nil
|
||||
// case err = <-errCh:
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
func (w *WebrtcCallSoundManager) unmute(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
if _, ok := manager.Squads[squadId]; !ok {
|
||||
err = fmt.Errorf("no correponding squad found")
|
||||
return
|
||||
}
|
||||
logger.Println("sending unnmute event", peerId)
|
||||
manager.RemoteTracksMux.RLock()
|
||||
for _, v := range manager.RemoteTracks[peerId] {
|
||||
if v.Track.Kind() == webrtc.RTPCodecTypeAudio {
|
||||
atomic.SwapInt32(v.rdv, 0)
|
||||
}
|
||||
}
|
||||
manager.RemoteTracksMux.RUnlock()
|
||||
manager.SquadMapMux.Lock()
|
||||
defer manager.SquadMapMux.Unlock()
|
||||
for _, member := range manager.Squads[squadId].Members {
|
||||
manager.DataChannelMapMux.Lock()
|
||||
if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
"type": UNMUTE,
|
||||
"from": peerId,
|
||||
"payload": map[string]interface{}{},
|
||||
})
|
||||
if marshalErr != nil {
|
||||
logger.Println(err)
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
continue
|
||||
}
|
||||
lock:
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
logger.Println(sendErr)
|
||||
}
|
||||
break lock
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
// func (w *WebrtcCallSoundManager) unmute(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
// if _, ok := manager.Squads[squadId]; !ok {
|
||||
// err = fmt.Errorf("no correponding squad found")
|
||||
// return
|
||||
// }
|
||||
// logger.Println("sending unnmute event", peerId)
|
||||
// manager.RemoteTracksMux.RLock()
|
||||
// for _, v := range manager.RemoteTracks[peerId] {
|
||||
// if v.Track.Kind() == webrtc.RTPCodecTypeAudio {
|
||||
// atomic.SwapInt32(v.rdv, 0)
|
||||
// }
|
||||
// }
|
||||
// manager.RemoteTracksMux.RUnlock()
|
||||
// manager.SquadMapMux.Lock()
|
||||
// defer manager.SquadMapMux.Unlock()
|
||||
// for _, member := range manager.Squads[squadId].Members {
|
||||
// manager.DataChannelMapMux.Lock()
|
||||
// if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
// bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
// "type": UNMUTE,
|
||||
// "from": peerId,
|
||||
// "payload": map[string]interface{}{},
|
||||
// })
|
||||
// if marshalErr != nil {
|
||||
// logger.Println(err)
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// continue
|
||||
// }
|
||||
// lock:
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
// defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
// if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
// logger.Println(sendErr)
|
||||
// }
|
||||
// break lock
|
||||
// } else {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (w *WebrtcCallSoundManager) mute(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
if _, ok := manager.Squads[squadId]; !ok {
|
||||
err = fmt.Errorf("no correponding squad found")
|
||||
return
|
||||
}
|
||||
logger.Println("sending mute event", peerId)
|
||||
manager.RemoteTracksMux.RLock()
|
||||
for _, v := range manager.RemoteTracks[peerId] {
|
||||
if v.Track.Kind() == webrtc.RTPCodecTypeAudio {
|
||||
atomic.SwapInt32(v.rdv, 1)
|
||||
}
|
||||
}
|
||||
manager.RemoteTracksMux.RUnlock()
|
||||
manager.SquadMapMux.Lock()
|
||||
defer manager.SquadMapMux.Unlock()
|
||||
for _, member := range manager.Squads[squadId].Members {
|
||||
manager.DataChannelMapMux.Lock()
|
||||
if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
"type": MUTE,
|
||||
"from": peerId,
|
||||
"payload": map[string]interface{}{},
|
||||
})
|
||||
if marshalErr != nil {
|
||||
logger.Println(err)
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
continue
|
||||
}
|
||||
lock:
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
logger.Println(sendErr)
|
||||
}
|
||||
break lock
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
// func (w *WebrtcCallSoundManager) mute(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
// if _, ok := manager.Squads[squadId]; !ok {
|
||||
// err = fmt.Errorf("no correponding squad found")
|
||||
// return
|
||||
// }
|
||||
// logger.Println("sending mute event", peerId)
|
||||
// manager.RemoteTracksMux.RLock()
|
||||
// for _, v := range manager.RemoteTracks[peerId] {
|
||||
// if v.Track.Kind() == webrtc.RTPCodecTypeAudio {
|
||||
// atomic.SwapInt32(v.rdv, 1)
|
||||
// }
|
||||
// }
|
||||
// manager.RemoteTracksMux.RUnlock()
|
||||
// manager.SquadMapMux.Lock()
|
||||
// defer manager.SquadMapMux.Unlock()
|
||||
// for _, member := range manager.Squads[squadId].Members {
|
||||
// manager.DataChannelMapMux.Lock()
|
||||
// if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
// bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
// "type": MUTE,
|
||||
// "from": peerId,
|
||||
// "payload": map[string]interface{}{},
|
||||
// })
|
||||
// if marshalErr != nil {
|
||||
// logger.Println(err)
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// continue
|
||||
// }
|
||||
// lock:
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
// defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
// if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
// logger.Println(sendErr)
|
||||
// }
|
||||
// break lock
|
||||
// } else {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (w *WebrtcCallSoundManager) sendSpeakingEvent(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
if _, ok := manager.Squads[squadId]; !ok {
|
||||
err = fmt.Errorf("no correponding squad found")
|
||||
return
|
||||
}
|
||||
logger.Println("sending speaking event", peerId)
|
||||
manager.SquadMapMux.Lock()
|
||||
defer manager.SquadMapMux.Unlock()
|
||||
for _, member := range manager.Squads[squadId].Members {
|
||||
manager.DataChannelMapMux.Lock()
|
||||
if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
"type": SPEAKING,
|
||||
"from": peerId,
|
||||
"payload": map[string]interface{}{
|
||||
"userId": peerId,
|
||||
"speaking": true,
|
||||
},
|
||||
})
|
||||
if marshalErr != nil {
|
||||
logger.Println(err)
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
continue
|
||||
}
|
||||
lock:
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
logger.Println(sendErr)
|
||||
}
|
||||
break lock
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
// func (w *WebrtcCallSoundManager) sendSpeakingEvent(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
// if _, ok := manager.Squads[squadId]; !ok {
|
||||
// err = fmt.Errorf("no correponding squad found")
|
||||
// return
|
||||
// }
|
||||
// logger.Println("sending speaking event", peerId)
|
||||
// manager.SquadMapMux.Lock()
|
||||
// defer manager.SquadMapMux.Unlock()
|
||||
// for _, member := range manager.Squads[squadId].Members {
|
||||
// manager.DataChannelMapMux.Lock()
|
||||
// if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
// bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
// "type": SPEAKING,
|
||||
// "from": peerId,
|
||||
// "payload": map[string]interface{}{
|
||||
// "userId": peerId,
|
||||
// "speaking": true,
|
||||
// },
|
||||
// })
|
||||
// if marshalErr != nil {
|
||||
// logger.Println(err)
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// continue
|
||||
// }
|
||||
// lock:
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
// defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
// if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
// logger.Println(sendErr)
|
||||
// }
|
||||
// break lock
|
||||
// } else {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (w *WebrtcCallSoundManager) sendStopSpeakingEvent(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
if _, ok := manager.Squads[squadId]; !ok {
|
||||
err = fmt.Errorf("no correponding squad found")
|
||||
return
|
||||
}
|
||||
logger.Println("sending stop speaking event", peerId)
|
||||
manager.SquadMapMux.Lock()
|
||||
defer manager.SquadMapMux.Unlock()
|
||||
for _, member := range manager.Squads[squadId].Members {
|
||||
manager.DataChannelMapMux.Lock()
|
||||
if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
"type": STOP_SPEAKING,
|
||||
"from": peerId,
|
||||
"payload": map[string]interface{}{
|
||||
"userId": peerId,
|
||||
"speaking": false,
|
||||
},
|
||||
})
|
||||
if marshalErr != nil {
|
||||
logger.Println(err)
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
continue
|
||||
}
|
||||
lock:
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
logger.Println(sendErr)
|
||||
}
|
||||
break lock
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
// func (w *WebrtcCallSoundManager) sendStopSpeakingEvent(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
// if _, ok := manager.Squads[squadId]; !ok {
|
||||
// err = fmt.Errorf("no correponding squad found")
|
||||
// return
|
||||
// }
|
||||
// logger.Println("sending stop speaking event", peerId)
|
||||
// manager.SquadMapMux.Lock()
|
||||
// defer manager.SquadMapMux.Unlock()
|
||||
// for _, member := range manager.Squads[squadId].Members {
|
||||
// manager.DataChannelMapMux.Lock()
|
||||
// if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
// bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
// "type": STOP_SPEAKING,
|
||||
// "from": peerId,
|
||||
// "payload": map[string]interface{}{
|
||||
// "userId": peerId,
|
||||
// "speaking": false,
|
||||
// },
|
||||
// })
|
||||
// if marshalErr != nil {
|
||||
// logger.Println(err)
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// continue
|
||||
// }
|
||||
// lock:
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
// defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
// if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
// logger.Println(sendErr)
|
||||
// }
|
||||
// break lock
|
||||
// } else {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
@ -1,131 +1,131 @@
|
||||
package localserver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
// import (
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
// "sync/atomic"
|
||||
|
||||
"github.com/pion/webrtc/v3"
|
||||
)
|
||||
// "github.com/pion/webrtc/v3"
|
||||
// )
|
||||
|
||||
const (
|
||||
VIDEO = "video"
|
||||
STOP_VIDEO = "stop_video"
|
||||
)
|
||||
// const (
|
||||
// VIDEO = "video"
|
||||
// STOP_VIDEO = "stop_video"
|
||||
// )
|
||||
|
||||
type WebrtcCallVideoManager struct{}
|
||||
// type WebrtcCallVideoManager struct{}
|
||||
|
||||
func NewWebrtcCallVideoManager() *WebrtcCallVideoManager {
|
||||
return new(WebrtcCallVideoManager)
|
||||
}
|
||||
// func NewWebrtcCallVideoManager() *WebrtcCallVideoManager {
|
||||
// return new(WebrtcCallVideoManager)
|
||||
// }
|
||||
|
||||
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)
|
||||
go func() {
|
||||
logger.Println("got an event in call video manager", from, eventId, payload)
|
||||
switch eventId {
|
||||
case VIDEO:
|
||||
if e := w.video(from, squadId, manager); e != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
case STOP_VIDEO:
|
||||
if e := w.stopVideo(from, squadId, manager); e != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
return nil
|
||||
case err = <-errCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
// go func() {
|
||||
// logger.Println("got an event in call video manager", from, eventId, payload)
|
||||
// switch eventId {
|
||||
// case VIDEO:
|
||||
// if e := w.video(from, squadId, manager); e != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// case STOP_VIDEO:
|
||||
// if e := w.stopVideo(from, squadId, manager); e != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// done <- struct{}{}
|
||||
// }()
|
||||
// select {
|
||||
// case <-done:
|
||||
// return nil
|
||||
// case err = <-errCh:
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
func (w *WebrtcCallVideoManager) video(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
if _, ok := manager.Squads[squadId]; !ok {
|
||||
err = fmt.Errorf("no correponding squad found")
|
||||
return
|
||||
}
|
||||
logger.Println("sending video event", peerId)
|
||||
manager.RemoteTracksMux.RLock()
|
||||
for _, v := range manager.RemoteTracks[peerId] {
|
||||
if v.Track.Kind() == webrtc.RTPCodecTypeVideo {
|
||||
atomic.SwapInt32(v.rdv, 0)
|
||||
}
|
||||
}
|
||||
manager.RemoteTracksMux.RUnlock()
|
||||
manager.SquadMapMux.Lock()
|
||||
defer manager.SquadMapMux.Unlock()
|
||||
for _, member := range manager.Squads[squadId].Members {
|
||||
manager.DataChannelMapMux.Lock()
|
||||
if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
"type": VIDEO,
|
||||
"from": peerId,
|
||||
"payload": map[string]interface{}{},
|
||||
})
|
||||
if marshalErr != nil {
|
||||
logger.Println(err)
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
continue
|
||||
}
|
||||
if sendErr := atomicallyExecute(manager.DataChannels[member].l, func() (err error) {
|
||||
err = manager.DataChannels[member].DataChannel.SendText(string(bs))
|
||||
return
|
||||
}); sendErr != nil {
|
||||
logger.Println(sendErr)
|
||||
}
|
||||
}
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
// func (w *WebrtcCallVideoManager) video(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
// if _, ok := manager.Squads[squadId]; !ok {
|
||||
// err = fmt.Errorf("no correponding squad found")
|
||||
// return
|
||||
// }
|
||||
// logger.Println("sending video event", peerId)
|
||||
// manager.RemoteTracksMux.RLock()
|
||||
// for _, v := range manager.RemoteTracks[peerId] {
|
||||
// if v.Track.Kind() == webrtc.RTPCodecTypeVideo {
|
||||
// atomic.SwapInt32(v.rdv, 0)
|
||||
// }
|
||||
// }
|
||||
// manager.RemoteTracksMux.RUnlock()
|
||||
// manager.SquadMapMux.Lock()
|
||||
// defer manager.SquadMapMux.Unlock()
|
||||
// for _, member := range manager.Squads[squadId].Members {
|
||||
// manager.DataChannelMapMux.Lock()
|
||||
// if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
// bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
// "type": VIDEO,
|
||||
// "from": peerId,
|
||||
// "payload": map[string]interface{}{},
|
||||
// })
|
||||
// if marshalErr != nil {
|
||||
// logger.Println(err)
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// continue
|
||||
// }
|
||||
// if sendErr := atomicallyExecute(manager.DataChannels[member].l, func() (err error) {
|
||||
// err = manager.DataChannels[member].DataChannel.SendText(string(bs))
|
||||
// return
|
||||
// }); sendErr != nil {
|
||||
// logger.Println(sendErr)
|
||||
// }
|
||||
// }
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (w *WebrtcCallVideoManager) stopVideo(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
if _, ok := manager.Squads[squadId]; !ok {
|
||||
err = fmt.Errorf("no correponding squad found")
|
||||
return
|
||||
}
|
||||
logger.Println("sending stop video event", peerId)
|
||||
manager.RemoteTracksMux.RLock()
|
||||
for _, v := range manager.RemoteTracks[peerId] {
|
||||
if v.Track.Kind() == webrtc.RTPCodecTypeVideo {
|
||||
atomic.SwapInt32(v.rdv, 1)
|
||||
}
|
||||
}
|
||||
manager.RemoteTracksMux.RUnlock()
|
||||
manager.SquadMapMux.Lock()
|
||||
defer manager.SquadMapMux.Unlock()
|
||||
for _, member := range manager.Squads[squadId].Members {
|
||||
manager.DataChannelMapMux.Lock()
|
||||
if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
"type": STOP_VIDEO,
|
||||
"from": peerId,
|
||||
"payload": map[string]interface{}{},
|
||||
})
|
||||
if marshalErr != nil {
|
||||
logger.Println(err)
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
continue
|
||||
}
|
||||
for {
|
||||
if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
logger.Println(sendErr)
|
||||
}
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
manager.DataChannelMapMux.Unlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
// func (w *WebrtcCallVideoManager) stopVideo(peerId string, squadId string, manager *WebRTCCallManager) (err error) {
|
||||
// if _, ok := manager.Squads[squadId]; !ok {
|
||||
// err = fmt.Errorf("no correponding squad found")
|
||||
// return
|
||||
// }
|
||||
// logger.Println("sending stop video event", peerId)
|
||||
// manager.RemoteTracksMux.RLock()
|
||||
// for _, v := range manager.RemoteTracks[peerId] {
|
||||
// if v.Track.Kind() == webrtc.RTPCodecTypeVideo {
|
||||
// atomic.SwapInt32(v.rdv, 1)
|
||||
// }
|
||||
// }
|
||||
// manager.RemoteTracksMux.RUnlock()
|
||||
// manager.SquadMapMux.Lock()
|
||||
// defer manager.SquadMapMux.Unlock()
|
||||
// for _, member := range manager.Squads[squadId].Members {
|
||||
// manager.DataChannelMapMux.Lock()
|
||||
// if _, ok := manager.DataChannels[member]; ok && member != peerId {
|
||||
// bs, marshalErr := json.Marshal(map[string]interface{}{
|
||||
// "type": STOP_VIDEO,
|
||||
// "from": peerId,
|
||||
// "payload": map[string]interface{}{},
|
||||
// })
|
||||
// if marshalErr != nil {
|
||||
// logger.Println(err)
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// continue
|
||||
// }
|
||||
// for {
|
||||
// if atomic.CompareAndSwapUint32(manager.DataChannels[member].l, 0, 1) {
|
||||
// defer atomic.SwapUint32(manager.DataChannels[member].l, 0)
|
||||
// if sendErr := manager.DataChannels[member].DataChannel.SendText(string(bs)); sendErr != nil {
|
||||
// logger.Println(sendErr)
|
||||
// }
|
||||
// break
|
||||
// } else {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// manager.DataChannelMapMux.Unlock()
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
@ -1,449 +1,449 @@
|
||||
package localserver
|
||||
|
||||
import (
|
||||
context "context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
sync "sync"
|
||||
// import (
|
||||
// context "context"
|
||||
// "encoding/json"
|
||||
// "fmt"
|
||||
// "io"
|
||||
// sync "sync"
|
||||
|
||||
"github.com/pion/webrtc/v3"
|
||||
)
|
||||
// "github.com/pion/webrtc/v3"
|
||||
// )
|
||||
|
||||
type WebrtcFsManager struct {
|
||||
stream SignalingService_LinkClient
|
||||
DatachannelManager DataChannelManager
|
||||
LocalSD map[string]*webrtc.SessionDescription
|
||||
RTCPeerConnections map[string]*webrtc.PeerConnection
|
||||
DataChannels map[string]*webrtc.DataChannel
|
||||
PendingCandidates map[string][]*webrtc.ICECandidate
|
||||
CandidateChannel chan *IncomingCandidate
|
||||
CandidateMux *sync.RWMutex
|
||||
RTCPeerConnectionMapMux *sync.RWMutex
|
||||
DataChannelMapMux *sync.RWMutex
|
||||
LocalSDMapMux *sync.RWMutex
|
||||
}
|
||||
// type WebrtcFsManager struct {
|
||||
// stream SignalingService_LinkClient
|
||||
// DatachannelManager DataChannelManager
|
||||
// LocalSD map[string]*webrtc.SessionDescription
|
||||
// RTCPeerConnections map[string]*webrtc.PeerConnection
|
||||
// DataChannels map[string]*webrtc.DataChannel
|
||||
// PendingCandidates map[string][]*webrtc.ICECandidate
|
||||
// CandidateChannel chan *IncomingCandidate
|
||||
// CandidateMux *sync.RWMutex
|
||||
// RTCPeerConnectionMapMux *sync.RWMutex
|
||||
// DataChannelMapMux *sync.RWMutex
|
||||
// LocalSDMapMux *sync.RWMutex
|
||||
// }
|
||||
|
||||
func NewWebrtcFsManager(dataChannelManager DataChannelManager) (webrtcFsManager *WebrtcFsManager, err error) {
|
||||
webrtcFsManager = &WebrtcFsManager{
|
||||
DatachannelManager: dataChannelManager,
|
||||
DataChannels: make(map[string]*webrtc.DataChannel),
|
||||
PendingCandidates: make(map[string][]*webrtc.ICECandidate),
|
||||
LocalSD: make(map[string]*webrtc.SessionDescription),
|
||||
RTCPeerConnections: make(map[string]*webrtc.PeerConnection),
|
||||
RTCPeerConnectionMapMux: &sync.RWMutex{},
|
||||
LocalSDMapMux: &sync.RWMutex{},
|
||||
CandidateMux: &sync.RWMutex{},
|
||||
DataChannelMapMux: &sync.RWMutex{},
|
||||
}
|
||||
return
|
||||
}
|
||||
// func NewWebrtcFsManager(dataChannelManager DataChannelManager) (webrtcFsManager *WebrtcFsManager, err error) {
|
||||
// webrtcFsManager = &WebrtcFsManager{
|
||||
// DatachannelManager: dataChannelManager,
|
||||
// DataChannels: make(map[string]*webrtc.DataChannel),
|
||||
// PendingCandidates: make(map[string][]*webrtc.ICECandidate),
|
||||
// LocalSD: make(map[string]*webrtc.SessionDescription),
|
||||
// RTCPeerConnections: make(map[string]*webrtc.PeerConnection),
|
||||
// RTCPeerConnectionMapMux: &sync.RWMutex{},
|
||||
// LocalSDMapMux: &sync.RWMutex{},
|
||||
// CandidateMux: &sync.RWMutex{},
|
||||
// DataChannelMapMux: &sync.RWMutex{},
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wf *WebrtcFsManager) sendSignalingMessage(messageType, from, to string, payload map[string]interface{}) (err error) {
|
||||
bs, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = wf.stream.Send(&SignalingMessage{
|
||||
Type: messageType,
|
||||
From: from,
|
||||
To: to,
|
||||
Payload: bs,
|
||||
})
|
||||
return
|
||||
}
|
||||
// func (wf *WebrtcFsManager) sendSignalingMessage(messageType, from, to string, payload map[string]interface{}) (err error) {
|
||||
// bs, err := json.Marshal(payload)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// err = wf.stream.Send(&SignalingMessage{
|
||||
// Type: messageType,
|
||||
// From: from,
|
||||
// To: to,
|
||||
// Payload: bs,
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wf *WebrtcFsManager) CreateOffer(ctx context.Context, target string, from string, cb OnICECandidateFunc) (err error) {
|
||||
peerConnection, err := wf.createPeerConnection(target, from, webrtc.SDPTypeOffer, cb)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rawOffer, err := peerConnection.CreateOffer(nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = peerConnection.SetLocalDescription(rawOffer); err != nil {
|
||||
return
|
||||
}
|
||||
wf.RTCPeerConnectionMapMux.Lock()
|
||||
logger.Println("adding for target", target)
|
||||
wf.RTCPeerConnections[target] = peerConnection
|
||||
wf.RTCPeerConnectionMapMux.Unlock()
|
||||
err = wf.sendSignalingMessage(string(WEBRTC_OFFER_FS), "lolo_local_serv", target, map[string]any{
|
||||
"to": target,
|
||||
"from": "lolo_local_serv",
|
||||
"sdp": rawOffer.SDP,
|
||||
})
|
||||
return
|
||||
}
|
||||
// func (wf *WebrtcFsManager) CreateOffer(ctx context.Context, target string, from string, cb OnICECandidateFunc) (err error) {
|
||||
// peerConnection, err := wf.createPeerConnection(target, from, webrtc.SDPTypeOffer, cb)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// rawOffer, err := peerConnection.CreateOffer(nil)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// if err = peerConnection.SetLocalDescription(rawOffer); err != nil {
|
||||
// return
|
||||
// }
|
||||
// wf.RTCPeerConnectionMapMux.Lock()
|
||||
// logger.Println("adding for target", target)
|
||||
// wf.RTCPeerConnections[target] = peerConnection
|
||||
// wf.RTCPeerConnectionMapMux.Unlock()
|
||||
// err = wf.sendSignalingMessage(string(WEBRTC_OFFER_FS), "lolo_local_serv", target, map[string]any{
|
||||
// "to": target,
|
||||
// "from": "lolo_local_serv",
|
||||
// "sdp": rawOffer.SDP,
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wf *WebrtcFsManager) HandleOffer(ctx context.Context, req map[string]string, cb OnICECandidateFunc) (err error) {
|
||||
done, errCh := make(chan struct{}), make(chan error)
|
||||
go func() {
|
||||
peerConnection, err := wf.createPeerConnection(req[FROM], req[TO], webrtc.SDPTypeAnswer, cb)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
wf.RTCPeerConnectionMapMux.Lock()
|
||||
wf.RTCPeerConnections[req[FROM]] = peerConnection
|
||||
wf.RTCPeerConnectionMapMux.Unlock()
|
||||
offer := webrtc.SessionDescription{
|
||||
Type: webrtc.SDPTypeOffer,
|
||||
SDP: req[SDP],
|
||||
}
|
||||
if err = peerConnection.SetRemoteDescription(offer); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
rawAnswer, err := peerConnection.CreateAnswer(nil)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
wf.LocalSDMapMux.Lock()
|
||||
wf.LocalSD[req[FROM]] = &rawAnswer
|
||||
wf.LocalSDMapMux.Unlock()
|
||||
// if err = wf.stream.Send(&Request{
|
||||
// Type: string(WEBRTC_ANSWER_FS),
|
||||
// From: "lolo_local_serv",
|
||||
// Token: "none",
|
||||
// Payload: map[string]string{
|
||||
// "to": req[FROM],
|
||||
// "from": "lolo_local_serv",
|
||||
// "sdp": rawAnswer.SDP,
|
||||
// },
|
||||
// }); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
done <- struct{}{}
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case err = <-errCh:
|
||||
return
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
return
|
||||
}
|
||||
}
|
||||
// func (wf *WebrtcFsManager) HandleOffer(ctx context.Context, req map[string]string, cb OnICECandidateFunc) (err error) {
|
||||
// done, errCh := make(chan struct{}), make(chan error)
|
||||
// go func() {
|
||||
// peerConnection, err := wf.createPeerConnection(req[FROM], req[TO], webrtc.SDPTypeAnswer, cb)
|
||||
// if err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// wf.RTCPeerConnectionMapMux.Lock()
|
||||
// wf.RTCPeerConnections[req[FROM]] = peerConnection
|
||||
// wf.RTCPeerConnectionMapMux.Unlock()
|
||||
// offer := webrtc.SessionDescription{
|
||||
// Type: webrtc.SDPTypeOffer,
|
||||
// SDP: req[SDP],
|
||||
// }
|
||||
// if err = peerConnection.SetRemoteDescription(offer); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// rawAnswer, err := peerConnection.CreateAnswer(nil)
|
||||
// if err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// wf.LocalSDMapMux.Lock()
|
||||
// wf.LocalSD[req[FROM]] = &rawAnswer
|
||||
// wf.LocalSDMapMux.Unlock()
|
||||
// // if err = wf.stream.Send(&Request{
|
||||
// // Type: string(WEBRTC_ANSWER_FS),
|
||||
// // From: "lolo_local_serv",
|
||||
// // Token: "none",
|
||||
// // Payload: map[string]string{
|
||||
// // "to": req[FROM],
|
||||
// // "from": "lolo_local_serv",
|
||||
// // "sdp": rawAnswer.SDP,
|
||||
// // },
|
||||
// // }); err != nil {
|
||||
// // errCh <- err
|
||||
// // return
|
||||
// // }
|
||||
// done <- struct{}{}
|
||||
// }()
|
||||
// select {
|
||||
// case <-done:
|
||||
// return
|
||||
// case err = <-errCh:
|
||||
// return
|
||||
// case <-ctx.Done():
|
||||
// err = ctx.Err()
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
func (wf *WebrtcFsManager) HandleAnswer(ctx context.Context, req map[string]string) (err error) {
|
||||
wf.RTCPeerConnectionMapMux.RLock()
|
||||
defer wf.RTCPeerConnectionMapMux.RUnlock()
|
||||
defer func() {
|
||||
if r := recover(); err != nil {
|
||||
logger.Printf("recover from panic : %v\n", r)
|
||||
}
|
||||
}()
|
||||
if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok {
|
||||
err = fmt.Errorf("no corresponding peer connection for id : %s", req[FROM])
|
||||
return
|
||||
}
|
||||
peerConnnection := wf.RTCPeerConnections[req[FROM]]
|
||||
if err = peerConnnection.SetRemoteDescription(webrtc.SessionDescription{
|
||||
Type: webrtc.SDPTypeAnswer,
|
||||
SDP: req[SDP],
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
// if err = wf.stream.Send(&Request{
|
||||
// Type: string(WEBRTC_COUNTER_OFFER_FS),
|
||||
// From: "lolo_local_serv",
|
||||
// Token: "none",
|
||||
// Payload: map[string]string{
|
||||
// "from": "lolo_local_serv",
|
||||
// "to": req[FROM],
|
||||
// },
|
||||
// }); err != nil {
|
||||
// return
|
||||
// }
|
||||
wf.CandidateMux.RLock()
|
||||
for range wf.PendingCandidates[req[FROM]] {
|
||||
logger.Println("sending candidate to", req[FROM])
|
||||
// if err = wf.stream.Send(&Request{
|
||||
// Type: string(WEBRTC_CANDIDATE_FS),
|
||||
// From: "lolo_local_serv",
|
||||
// Token: "none",
|
||||
// Payload: map[string]string{
|
||||
// "from": "lolo_local_serv",
|
||||
// "to": req[FROM],
|
||||
// "candidate": candidate.ToJSON().Candidate,
|
||||
// "sdpMid": *candidate.ToJSON().SDPMid,
|
||||
// "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
||||
// },
|
||||
// }); err != nil {
|
||||
// return
|
||||
// }
|
||||
}
|
||||
wf.CandidateMux.RUnlock()
|
||||
wf.CandidateMux.Lock()
|
||||
delete(wf.PendingCandidates, req[FROM])
|
||||
wf.CandidateMux.Unlock()
|
||||
wf.LocalSDMapMux.Lock()
|
||||
delete(wf.LocalSD, req[FROM])
|
||||
wf.LocalSDMapMux.Unlock()
|
||||
return
|
||||
}
|
||||
// func (wf *WebrtcFsManager) HandleAnswer(ctx context.Context, req map[string]string) (err error) {
|
||||
// wf.RTCPeerConnectionMapMux.RLock()
|
||||
// defer wf.RTCPeerConnectionMapMux.RUnlock()
|
||||
// defer func() {
|
||||
// if r := recover(); err != nil {
|
||||
// logger.Printf("recover from panic : %v\n", r)
|
||||
// }
|
||||
// }()
|
||||
// if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok {
|
||||
// err = fmt.Errorf("no corresponding peer connection for id : %s", req[FROM])
|
||||
// return
|
||||
// }
|
||||
// peerConnnection := wf.RTCPeerConnections[req[FROM]]
|
||||
// if err = peerConnnection.SetRemoteDescription(webrtc.SessionDescription{
|
||||
// Type: webrtc.SDPTypeAnswer,
|
||||
// SDP: req[SDP],
|
||||
// }); err != nil {
|
||||
// return
|
||||
// }
|
||||
// // if err = wf.stream.Send(&Request{
|
||||
// // Type: string(WEBRTC_COUNTER_OFFER_FS),
|
||||
// // From: "lolo_local_serv",
|
||||
// // Token: "none",
|
||||
// // Payload: map[string]string{
|
||||
// // "from": "lolo_local_serv",
|
||||
// // "to": req[FROM],
|
||||
// // },
|
||||
// // }); err != nil {
|
||||
// // return
|
||||
// // }
|
||||
// wf.CandidateMux.RLock()
|
||||
// for range wf.PendingCandidates[req[FROM]] {
|
||||
// logger.Println("sending candidate to", req[FROM])
|
||||
// // if err = wf.stream.Send(&Request{
|
||||
// // Type: string(WEBRTC_CANDIDATE_FS),
|
||||
// // From: "lolo_local_serv",
|
||||
// // Token: "none",
|
||||
// // Payload: map[string]string{
|
||||
// // "from": "lolo_local_serv",
|
||||
// // "to": req[FROM],
|
||||
// // "candidate": candidate.ToJSON().Candidate,
|
||||
// // "sdpMid": *candidate.ToJSON().SDPMid,
|
||||
// // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
||||
// // },
|
||||
// // }); err != nil {
|
||||
// // return
|
||||
// // }
|
||||
// }
|
||||
// wf.CandidateMux.RUnlock()
|
||||
// wf.CandidateMux.Lock()
|
||||
// delete(wf.PendingCandidates, req[FROM])
|
||||
// wf.CandidateMux.Unlock()
|
||||
// wf.LocalSDMapMux.Lock()
|
||||
// delete(wf.LocalSD, req[FROM])
|
||||
// wf.LocalSDMapMux.Unlock()
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wf *WebrtcFsManager) HandleCounterOffer(ctx context.Context, req map[string]string) (err error) {
|
||||
wf.RTCPeerConnectionMapMux.RLock()
|
||||
if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok {
|
||||
err = fmt.Errorf("no field corresponding peer connection for id %s", req[FROM])
|
||||
return
|
||||
}
|
||||
connection := wf.RTCPeerConnections[req[FROM]]
|
||||
wf.RTCPeerConnectionMapMux.RUnlock()
|
||||
wf.LocalSDMapMux.RLock()
|
||||
if err = connection.SetLocalDescription(*wf.LocalSD[req[FROM]]); err != nil {
|
||||
return
|
||||
}
|
||||
wf.LocalSDMapMux.RUnlock()
|
||||
wf.CandidateMux.RLock()
|
||||
for range wf.PendingCandidates[req[FROM]] {
|
||||
// logger.Println("sending candidate to", req[FROM])
|
||||
// if err = wf.stream.Send(&Request{
|
||||
// Type: string(WEBRTC_CANDIDATE_FS),
|
||||
// From: "lolo_local_serv",
|
||||
// Token: "none",
|
||||
// Payload: map[string]string{
|
||||
// "from": "lolo_local_serv",
|
||||
// "to": req[FROM],
|
||||
// "candidate": candidate.ToJSON().Candidate,
|
||||
// "sdpMid": *candidate.ToJSON().SDPMid,
|
||||
// "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
||||
// },
|
||||
// }); err != nil {
|
||||
// return
|
||||
// }
|
||||
}
|
||||
wf.CandidateMux.RUnlock()
|
||||
return
|
||||
}
|
||||
// func (wf *WebrtcFsManager) HandleCounterOffer(ctx context.Context, req map[string]string) (err error) {
|
||||
// wf.RTCPeerConnectionMapMux.RLock()
|
||||
// if _, ok := wf.RTCPeerConnections[req[FROM]]; !ok {
|
||||
// err = fmt.Errorf("no field corresponding peer connection for id %s", req[FROM])
|
||||
// return
|
||||
// }
|
||||
// connection := wf.RTCPeerConnections[req[FROM]]
|
||||
// wf.RTCPeerConnectionMapMux.RUnlock()
|
||||
// wf.LocalSDMapMux.RLock()
|
||||
// if err = connection.SetLocalDescription(*wf.LocalSD[req[FROM]]); err != nil {
|
||||
// return
|
||||
// }
|
||||
// wf.LocalSDMapMux.RUnlock()
|
||||
// wf.CandidateMux.RLock()
|
||||
// for range wf.PendingCandidates[req[FROM]] {
|
||||
// // logger.Println("sending candidate to", req[FROM])
|
||||
// // if err = wf.stream.Send(&Request{
|
||||
// // Type: string(WEBRTC_CANDIDATE_FS),
|
||||
// // From: "lolo_local_serv",
|
||||
// // Token: "none",
|
||||
// // Payload: map[string]string{
|
||||
// // "from": "lolo_local_serv",
|
||||
// // "to": req[FROM],
|
||||
// // "candidate": candidate.ToJSON().Candidate,
|
||||
// // "sdpMid": *candidate.ToJSON().SDPMid,
|
||||
// // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
||||
// // },
|
||||
// // }); err != nil {
|
||||
// // return
|
||||
// // }
|
||||
// }
|
||||
// wf.CandidateMux.RUnlock()
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wf *WebrtcFsManager) createPeerConnection(target string, from string, peerType webrtc.SDPType, cb OnICECandidateFunc) (peerConnection *webrtc.PeerConnection, err error) {
|
||||
defer func() {
|
||||
if r := recover(); err != nil {
|
||||
logger.Printf("recover from panic : %v\n", r)
|
||||
}
|
||||
}()
|
||||
s := webrtc.SettingEngine{}
|
||||
s.DetachDataChannels()
|
||||
// func (wf *WebrtcFsManager) createPeerConnection(target string, from string, peerType webrtc.SDPType, cb OnICECandidateFunc) (peerConnection *webrtc.PeerConnection, err error) {
|
||||
// defer func() {
|
||||
// if r := recover(); err != nil {
|
||||
// logger.Printf("recover from panic : %v\n", r)
|
||||
// }
|
||||
// }()
|
||||
// s := webrtc.SettingEngine{}
|
||||
// s.DetachDataChannels()
|
||||
|
||||
api := webrtc.NewAPI(webrtc.WithSettingEngine(s))
|
||||
// api := webrtc.NewAPI(webrtc.WithSettingEngine(s))
|
||||
|
||||
config := webrtc.Configuration{
|
||||
ICEServers: []webrtc.ICEServer{
|
||||
{
|
||||
URLs: []string{"stun:stun.l.google.com:19302", "stun:stunserver.org:3478", "stun:stun.l.google.com:19302?transport=tcp"},
|
||||
},
|
||||
},
|
||||
SDPSemantics: webrtc.SDPSemanticsUnifiedPlan,
|
||||
}
|
||||
peerConnection, err = api.NewPeerConnection(config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if peerType == webrtc.SDPTypeOffer {
|
||||
maxRetransmits := uint16(100)
|
||||
channel, err := peerConnection.CreateDataChannel("data", &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) {
|
||||
logger.Printf("new message %s\n", string(msg.Data))
|
||||
done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
||||
From: target,
|
||||
Type: "test",
|
||||
Payload: &DatachannelMessagePayload{},
|
||||
}, channel)
|
||||
select {
|
||||
case <-done:
|
||||
//logger.Println("done with success")
|
||||
case e := <-errCh:
|
||||
logger.Println(e)
|
||||
logger.Println("this is impossible")
|
||||
}
|
||||
})
|
||||
wf.DataChannelMapMux.Lock()
|
||||
wf.DataChannels[target] = channel
|
||||
wf.DataChannelMapMux.Unlock()
|
||||
}
|
||||
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.OnDataChannel(func(dc *webrtc.DataChannel) {
|
||||
dc.OnOpen(func() {
|
||||
logger.Printf("got a new open datachannel %s\n", dc.Label())
|
||||
dataChann, err := dc.Detach()
|
||||
if err != nil {
|
||||
logger.Println(err)
|
||||
return
|
||||
}
|
||||
for {
|
||||
var x []byte = make([]byte, 2<<15)
|
||||
n, _, err := dataChann.ReadDataChannel(x)
|
||||
if err != nil {
|
||||
logger.Println(err)
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
go func(msg []byte) {
|
||||
var dataChannelMessage DatachannelMessage
|
||||
if unmarshalErr := json.Unmarshal(msg, &dataChannelMessage); unmarshalErr != nil {
|
||||
logger.Println(unmarshalErr)
|
||||
return
|
||||
}
|
||||
done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
||||
From: dataChannelMessage.From,
|
||||
Type: dataChannelMessage.Type,
|
||||
Payload: dataChannelMessage.Payload,
|
||||
}, dc)
|
||||
select {
|
||||
case <-done:
|
||||
//logger.Println("done with success")
|
||||
case e := <-errCh:
|
||||
logger.Println(e)
|
||||
logger.Println("this is impossible")
|
||||
}
|
||||
}(x[:n])
|
||||
}
|
||||
})
|
||||
// dc.OnMessage(func(msg webrtc.DataChannelMessage) {
|
||||
// var dataChannelMessage DatachannelMessage
|
||||
// if unmarshalErr := json.Unmarshal(msg.Data, &dataChannelMessage); unmarshalErr != nil {
|
||||
// logger.Println(unmarshalErr)
|
||||
// return
|
||||
// }
|
||||
// done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
||||
// From: dataChannelMessage.From,
|
||||
// Type: dataChannelMessage.Type,
|
||||
// Payload: dataChannelMessage.Payload,
|
||||
// }, dc)
|
||||
// select {
|
||||
// case <-done:
|
||||
// //logger.Println("done with success")
|
||||
// case e := <-errCh:
|
||||
// logger.Println(e)
|
||||
// logger.Println("this is impossible")
|
||||
// }
|
||||
// })
|
||||
})
|
||||
peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
|
||||
if i == nil {
|
||||
return
|
||||
}
|
||||
wf.CandidateMux.Lock()
|
||||
defer wf.CandidateMux.Unlock()
|
||||
desc := peerConnection.RemoteDescription()
|
||||
if desc == nil {
|
||||
wf.PendingCandidates[target] = append(wf.PendingCandidates[target], i)
|
||||
} else {
|
||||
logger.Println(i)
|
||||
if iceCandidateErr := cb(target, i); iceCandidateErr != nil {
|
||||
logger.Println(iceCandidateErr)
|
||||
}
|
||||
}
|
||||
})
|
||||
peerConnection.OnNegotiationNeeded(func() {
|
||||
if peerConnection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && peerConnection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
|
||||
wf.RTCPeerConnectionMapMux.Lock()
|
||||
defer wf.RTCPeerConnectionMapMux.Unlock()
|
||||
for _, connection := range wf.RTCPeerConnections {
|
||||
if connection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && connection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
|
||||
localSd, err := connection.CreateOffer(nil)
|
||||
if err != nil {
|
||||
logger.Println(err)
|
||||
return
|
||||
}
|
||||
if err = connection.SetLocalDescription(localSd); err != nil {
|
||||
logger.Println(err)
|
||||
return
|
||||
}
|
||||
// if err = wf.stream.Send(&Request{
|
||||
// Type: string(WEBRTC_RENNEGOTIATION_OFFER_FS),
|
||||
// From: "lolo_local_serv",
|
||||
// Token: "",
|
||||
// Payload: map[string]string{
|
||||
// "to": id,
|
||||
// "sdp": localSd.SDP,
|
||||
// },
|
||||
// }); err != nil {
|
||||
// logger.Println(err)
|
||||
// return
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
// config := webrtc.Configuration{
|
||||
// ICEServers: []webrtc.ICEServer{
|
||||
// {
|
||||
// URLs: []string{"stun:stun.l.google.com:19302", "stun:stun.l.google.com:19302", "stun:stun.l.google.com:19302?transport=tcp"},
|
||||
// },
|
||||
// },
|
||||
// SDPSemantics: webrtc.SDPSemanticsUnifiedPlan,
|
||||
// }
|
||||
// peerConnection, err = api.NewPeerConnection(config)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// if peerType == webrtc.SDPTypeOffer {
|
||||
// maxRetransmits := uint16(100)
|
||||
// channel, err := peerConnection.CreateDataChannel("data", &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) {
|
||||
// logger.Printf("new message %s\n", string(msg.Data))
|
||||
// done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
||||
// From: target,
|
||||
// Type: "test",
|
||||
// Payload: &DatachannelMessagePayload{},
|
||||
// }, channel)
|
||||
// select {
|
||||
// case <-done:
|
||||
// //logger.Println("done with success")
|
||||
// case e := <-errCh:
|
||||
// logger.Println(e)
|
||||
// logger.Println("this is impossible")
|
||||
// }
|
||||
// })
|
||||
// wf.DataChannelMapMux.Lock()
|
||||
// wf.DataChannels[target] = channel
|
||||
// wf.DataChannelMapMux.Unlock()
|
||||
// }
|
||||
// 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.OnDataChannel(func(dc *webrtc.DataChannel) {
|
||||
// dc.OnOpen(func() {
|
||||
// logger.Printf("got a new open datachannel %s\n", dc.Label())
|
||||
// dataChann, err := dc.Detach()
|
||||
// if err != nil {
|
||||
// logger.Println(err)
|
||||
// return
|
||||
// }
|
||||
// for {
|
||||
// var x []byte = make([]byte, 2<<15)
|
||||
// n, _, err := dataChann.ReadDataChannel(x)
|
||||
// if err != nil {
|
||||
// logger.Println(err)
|
||||
// if err == io.EOF {
|
||||
// return
|
||||
// }
|
||||
// continue
|
||||
// }
|
||||
// go func(msg []byte) {
|
||||
// var dataChannelMessage DatachannelMessage
|
||||
// if unmarshalErr := json.Unmarshal(msg, &dataChannelMessage); unmarshalErr != nil {
|
||||
// logger.Println(unmarshalErr)
|
||||
// return
|
||||
// }
|
||||
// done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
||||
// From: dataChannelMessage.From,
|
||||
// Type: dataChannelMessage.Type,
|
||||
// Payload: dataChannelMessage.Payload,
|
||||
// }, dc)
|
||||
// select {
|
||||
// case <-done:
|
||||
// //logger.Println("done with success")
|
||||
// case e := <-errCh:
|
||||
// logger.Println(e)
|
||||
// logger.Println("this is impossible")
|
||||
// }
|
||||
// }(x[:n])
|
||||
// }
|
||||
// })
|
||||
// // dc.OnMessage(func(msg webrtc.DataChannelMessage) {
|
||||
// // var dataChannelMessage DatachannelMessage
|
||||
// // if unmarshalErr := json.Unmarshal(msg.Data, &dataChannelMessage); unmarshalErr != nil {
|
||||
// // logger.Println(unmarshalErr)
|
||||
// // return
|
||||
// // }
|
||||
// // done, errCh := wf.DatachannelManager.HandleMessage(&DatachannelMessage{
|
||||
// // From: dataChannelMessage.From,
|
||||
// // Type: dataChannelMessage.Type,
|
||||
// // Payload: dataChannelMessage.Payload,
|
||||
// // }, dc)
|
||||
// // select {
|
||||
// // case <-done:
|
||||
// // //logger.Println("done with success")
|
||||
// // case e := <-errCh:
|
||||
// // logger.Println(e)
|
||||
// // logger.Println("this is impossible")
|
||||
// // }
|
||||
// // })
|
||||
// })
|
||||
// peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
|
||||
// if i == nil {
|
||||
// return
|
||||
// }
|
||||
// wf.CandidateMux.Lock()
|
||||
// defer wf.CandidateMux.Unlock()
|
||||
// desc := peerConnection.RemoteDescription()
|
||||
// if desc == nil {
|
||||
// wf.PendingCandidates[target] = append(wf.PendingCandidates[target], i)
|
||||
// } else {
|
||||
// logger.Println(i)
|
||||
// if iceCandidateErr := cb(target, i); iceCandidateErr != nil {
|
||||
// logger.Println(iceCandidateErr)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// peerConnection.OnNegotiationNeeded(func() {
|
||||
// if peerConnection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && peerConnection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
|
||||
// wf.RTCPeerConnectionMapMux.Lock()
|
||||
// defer wf.RTCPeerConnectionMapMux.Unlock()
|
||||
// for _, connection := range wf.RTCPeerConnections {
|
||||
// if connection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && connection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
|
||||
// localSd, err := connection.CreateOffer(nil)
|
||||
// if err != nil {
|
||||
// logger.Println(err)
|
||||
// return
|
||||
// }
|
||||
// if err = connection.SetLocalDescription(localSd); err != nil {
|
||||
// logger.Println(err)
|
||||
// return
|
||||
// }
|
||||
// // if err = wf.stream.Send(&Request{
|
||||
// // Type: string(WEBRTC_RENNEGOTIATION_OFFER_FS),
|
||||
// // From: "lolo_local_serv",
|
||||
// // Token: "",
|
||||
// // Payload: map[string]string{
|
||||
// // "to": id,
|
||||
// // "sdp": localSd.SDP,
|
||||
// // },
|
||||
// // }); err != nil {
|
||||
// // logger.Println(err)
|
||||
// // return
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wf *WebrtcFsManager) HandleRennegotiationOffer(from string, dst string, sdp string) (err error) {
|
||||
wf.RTCPeerConnectionMapMux.Lock()
|
||||
defer wf.RTCPeerConnectionMapMux.Unlock()
|
||||
if _, ok := wf.RTCPeerConnections[from]; !ok {
|
||||
err = fmt.Errorf("no corresponding peer connection for id %s", from)
|
||||
return
|
||||
}
|
||||
if wf.RTCPeerConnections[from].SignalingState() != webrtc.SignalingStateStable {
|
||||
err = fmt.Errorf("rennego called in wrong state")
|
||||
return
|
||||
}
|
||||
if err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
|
||||
return
|
||||
}
|
||||
localSd, err := wf.RTCPeerConnections[from].CreateAnswer(nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = wf.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
|
||||
return
|
||||
}
|
||||
// if err = wf.stream.Send(&Request{
|
||||
// Type: string(WEBRTC_RENNEGOTIATION_ANSWER_FS),
|
||||
// From: "lolo_local_serv",
|
||||
// Token: "",
|
||||
// Payload: map[string]string{
|
||||
// "to": from,
|
||||
// "sdp": localSd.SDP,
|
||||
// },
|
||||
// }); err != nil {
|
||||
// logger.Println(err)
|
||||
// return
|
||||
// }
|
||||
return
|
||||
}
|
||||
// func (wf *WebrtcFsManager) HandleRennegotiationOffer(from string, dst string, sdp string) (err error) {
|
||||
// wf.RTCPeerConnectionMapMux.Lock()
|
||||
// defer wf.RTCPeerConnectionMapMux.Unlock()
|
||||
// if _, ok := wf.RTCPeerConnections[from]; !ok {
|
||||
// err = fmt.Errorf("no corresponding peer connection for id %s", from)
|
||||
// return
|
||||
// }
|
||||
// if wf.RTCPeerConnections[from].SignalingState() != webrtc.SignalingStateStable {
|
||||
// err = fmt.Errorf("rennego called in wrong state")
|
||||
// return
|
||||
// }
|
||||
// if err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
|
||||
// return
|
||||
// }
|
||||
// localSd, err := wf.RTCPeerConnections[from].CreateAnswer(nil)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// if err = wf.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
|
||||
// return
|
||||
// }
|
||||
// // if err = wf.stream.Send(&Request{
|
||||
// // Type: string(WEBRTC_RENNEGOTIATION_ANSWER_FS),
|
||||
// // From: "lolo_local_serv",
|
||||
// // Token: "",
|
||||
// // Payload: map[string]string{
|
||||
// // "to": from,
|
||||
// // "sdp": localSd.SDP,
|
||||
// // },
|
||||
// // }); err != nil {
|
||||
// // logger.Println(err)
|
||||
// // return
|
||||
// // }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wf *WebrtcFsManager) HandleRennegotiationAnswer(from string, dst string, sdp string) (err error) {
|
||||
wf.RTCPeerConnectionMapMux.Lock()
|
||||
defer wf.RTCPeerConnectionMapMux.Unlock()
|
||||
if _, ok := wf.RTCPeerConnections[from]; !ok {
|
||||
err = fmt.Errorf("no corresponding peer connection for id %s", from)
|
||||
return
|
||||
}
|
||||
err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer})
|
||||
return
|
||||
}
|
||||
// func (wf *WebrtcFsManager) HandleRennegotiationAnswer(from string, dst string, sdp string) (err error) {
|
||||
// wf.RTCPeerConnectionMapMux.Lock()
|
||||
// defer wf.RTCPeerConnectionMapMux.Unlock()
|
||||
// if _, ok := wf.RTCPeerConnections[from]; !ok {
|
||||
// err = fmt.Errorf("no corresponding peer connection for id %s", from)
|
||||
// return
|
||||
// }
|
||||
// err = wf.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer})
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wf *WebrtcFsManager) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) {
|
||||
wf.RTCPeerConnectionMapMux.Lock()
|
||||
defer wf.RTCPeerConnectionMapMux.Unlock()
|
||||
err = wf.RTCPeerConnections[from].AddICECandidate(*candidate)
|
||||
return
|
||||
}
|
||||
// func (wf *WebrtcFsManager) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) {
|
||||
// wf.RTCPeerConnectionMapMux.Lock()
|
||||
// defer wf.RTCPeerConnectionMapMux.Unlock()
|
||||
// err = wf.RTCPeerConnections[from].AddICECandidate(*candidate)
|
||||
// return
|
||||
// }
|
||||
|
||||
@ -1,167 +1,167 @@
|
||||
package localserver
|
||||
|
||||
import (
|
||||
context "context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
// import (
|
||||
// context "context"
|
||||
// "encoding/json"
|
||||
// "strconv"
|
||||
|
||||
"github.com/pion/webrtc/v3"
|
||||
)
|
||||
// "github.com/pion/webrtc/v3"
|
||||
// )
|
||||
|
||||
const (
|
||||
INCOMING_PEER_FS ReqType = "incoming_peer_fs"
|
||||
LEAVING_PEER_FS ReqType = "leaving_peer_fs"
|
||||
WEBRTC_OFFER_FS ReqType = "offer_fs"
|
||||
WEBRTC_ANSWER_FS ReqType = "answer_fs"
|
||||
WEBRTC_RENNEGOTIATION_OFFER_FS ReqType = "rennegotiation_offer_fs"
|
||||
WEBRTC_RENNEGOTIATION_ANSWER_FS ReqType = "rennegotiation_answer_fs"
|
||||
WEBRTC_COUNTER_OFFER_FS ReqType = "webrtc_counter_offer_fs"
|
||||
WEBRTC_CANDIDATE_FS ReqType = "webrtc_candidate_fs"
|
||||
)
|
||||
// const (
|
||||
// INCOMING_PEER_FS ReqType = "incoming_peer_fs"
|
||||
// LEAVING_PEER_FS ReqType = "leaving_peer_fs"
|
||||
// WEBRTC_OFFER_FS ReqType = "offer_fs"
|
||||
// WEBRTC_ANSWER_FS ReqType = "answer_fs"
|
||||
// WEBRTC_RENNEGOTIATION_OFFER_FS ReqType = "rennegotiation_offer_fs"
|
||||
// WEBRTC_RENNEGOTIATION_ANSWER_FS ReqType = "rennegotiation_answer_fs"
|
||||
// WEBRTC_COUNTER_OFFER_FS ReqType = "webrtc_counter_offer_fs"
|
||||
// WEBRTC_CANDIDATE_FS ReqType = "webrtc_candidate_fs"
|
||||
// )
|
||||
|
||||
type WebRTCFsMiddleware struct {
|
||||
Manager *WebrtcFsManager
|
||||
stream SignalingService_LinkClient
|
||||
}
|
||||
// type WebRTCFsMiddleware struct {
|
||||
// Manager *WebrtcFsManager
|
||||
// stream SignalingService_LinkClient
|
||||
// }
|
||||
|
||||
func NewWebRTCFsMiddleware(manager *WebrtcFsManager) (webrtcFsMiddleware *WebRTCFsMiddleware) {
|
||||
webrtcFsMiddleware = &WebRTCFsMiddleware{
|
||||
Manager: manager,
|
||||
}
|
||||
return
|
||||
}
|
||||
// func NewWebRTCFsMiddleware(manager *WebrtcFsManager) (webrtcFsMiddleware *WebRTCFsMiddleware) {
|
||||
// webrtcFsMiddleware = &WebRTCFsMiddleware{
|
||||
// Manager: manager,
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wfm *WebRTCFsMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) {
|
||||
bs, err := json.Marshal(map[string]string{
|
||||
"from": "lolo_local_serv",
|
||||
"to": to,
|
||||
"candidate": candidate.ToJSON().Candidate,
|
||||
"sdpMid": *candidate.ToJSON().SDPMid,
|
||||
"sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = wfm.stream.Send(&SignalingMessage{
|
||||
Type: string(WEBRTC_CANDIDATE_FS),
|
||||
From: "lolo_local_serv",
|
||||
Payload: bs,
|
||||
})
|
||||
return
|
||||
}
|
||||
// func (wfm *WebRTCFsMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) {
|
||||
// bs, err := json.Marshal(map[string]string{
|
||||
// "from": "lolo_local_serv",
|
||||
// "to": to,
|
||||
// "candidate": candidate.ToJSON().Candidate,
|
||||
// "sdpMid": *candidate.ToJSON().SDPMid,
|
||||
// "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
||||
// })
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// err = wfm.stream.Send(&SignalingMessage{
|
||||
// Type: string(WEBRTC_CANDIDATE_FS),
|
||||
// From: "lolo_local_serv",
|
||||
// Payload: bs,
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wfm *WebRTCFsMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) {
|
||||
done, errCh := make(chan struct{}), make(chan error)
|
||||
go func() {
|
||||
var payload map[string]string
|
||||
if e := json.Unmarshal(req.Payload, &payload); err != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
switch req.Type {
|
||||
case string(INCOMING_PEER_FS):
|
||||
logger.Println("quit squad called")
|
||||
if from, ok := payload[FROM]; ok {
|
||||
logger.Println(from)
|
||||
//wfm.Manager.HandleLeavingMember(from)
|
||||
done <- struct{}{}
|
||||
}
|
||||
case string(PEER_CONNECTION_REQUEST):
|
||||
if err := validateRequest(payload, FROM, TO); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
// if err := wfm.Manager.CreateOffer(ctx, payload[FROM], payload[TO], wfm.signalCandidate); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
done <- struct{}{}
|
||||
case string(WEBRTC_OFFER_FS):
|
||||
if err := validateRequest(payload, FROM, TO, SDP); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if err := wfm.Manager.HandleOffer(ctx, payload, wfm.signalCandidate); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
done <- struct{}{}
|
||||
case string(WEBRTC_ANSWER_FS):
|
||||
if err := validateRequest(payload, FROM, TO, SDP); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if err := wfm.Manager.HandleAnswer(ctx, payload); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
done <- struct{}{}
|
||||
case string(WEBRTC_COUNTER_OFFER_FS):
|
||||
if err := validateRequest(payload, FROM); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if err := wfm.Manager.HandleCounterOffer(ctx, payload); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
done <- struct{}{}
|
||||
case string(WEBRTC_RENNEGOTIATION_ANSWER_FS):
|
||||
if err := validateRequest(payload, FROM, SDP); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if err := wfm.Manager.HandleRennegotiationAnswer(payload[FROM], "lolo_local_serv", payload[SDP]); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
done <- struct{}{}
|
||||
case string(WEBRTC_RENNEGOTIATION_OFFER_FS):
|
||||
if err := validateRequest(payload, FROM, SDP); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if err := wfm.Manager.HandleRennegotiationOffer(payload[FROM], "", payload[SDP]); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
done <- struct{}{}
|
||||
case string(WEBRTC_CANDIDATE_FS):
|
||||
if err := validateRequest(payload, FROM, "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 := wfm.Manager.AddCandidate(&webrtc.ICECandidateInit{
|
||||
Candidate: payload["candidate"],
|
||||
SDPMid: &sdpMid,
|
||||
SDPMLineIndex: &SDPMLineIndex,
|
||||
}, payload[FROM]); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
done <- struct{}{}
|
||||
default:
|
||||
logger.Println("fs is correctly linked")
|
||||
done <- struct{}{}
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
return
|
||||
case <-done:
|
||||
return
|
||||
case err = <-errCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
// func (wfm *WebRTCFsMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) {
|
||||
// done, errCh := make(chan struct{}), make(chan error)
|
||||
// go func() {
|
||||
// var payload map[string]string
|
||||
// if e := json.Unmarshal(req.Payload, &payload); err != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// switch req.Type {
|
||||
// case string(INCOMING_PEER_FS):
|
||||
// logger.Println("quit squad called")
|
||||
// if from, ok := payload[FROM]; ok {
|
||||
// logger.Println(from)
|
||||
// //wfm.Manager.HandleLeavingMember(from)
|
||||
// done <- struct{}{}
|
||||
// }
|
||||
// case string(PEER_CONNECTION_REQUEST):
|
||||
// if err := validateRequest(payload, FROM, TO); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// // if err := wfm.Manager.CreateOffer(ctx, payload[FROM], payload[TO], wfm.signalCandidate); err != nil {
|
||||
// // errCh <- err
|
||||
// // return
|
||||
// // }
|
||||
// done <- struct{}{}
|
||||
// case string(WEBRTC_OFFER_FS):
|
||||
// if err := validateRequest(payload, FROM, TO, SDP); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// if err := wfm.Manager.HandleOffer(ctx, payload, wfm.signalCandidate); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// done <- struct{}{}
|
||||
// case string(WEBRTC_ANSWER_FS):
|
||||
// if err := validateRequest(payload, FROM, TO, SDP); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// if err := wfm.Manager.HandleAnswer(ctx, payload); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// done <- struct{}{}
|
||||
// case string(WEBRTC_COUNTER_OFFER_FS):
|
||||
// if err := validateRequest(payload, FROM); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// if err := wfm.Manager.HandleCounterOffer(ctx, payload); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// done <- struct{}{}
|
||||
// case string(WEBRTC_RENNEGOTIATION_ANSWER_FS):
|
||||
// if err := validateRequest(payload, FROM, SDP); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// if err := wfm.Manager.HandleRennegotiationAnswer(payload[FROM], "lolo_local_serv", payload[SDP]); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// done <- struct{}{}
|
||||
// case string(WEBRTC_RENNEGOTIATION_OFFER_FS):
|
||||
// if err := validateRequest(payload, FROM, SDP); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// if err := wfm.Manager.HandleRennegotiationOffer(payload[FROM], "", payload[SDP]); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// done <- struct{}{}
|
||||
// case string(WEBRTC_CANDIDATE_FS):
|
||||
// if err := validateRequest(payload, FROM, "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 := wfm.Manager.AddCandidate(&webrtc.ICECandidateInit{
|
||||
// Candidate: payload["candidate"],
|
||||
// SDPMid: &sdpMid,
|
||||
// SDPMLineIndex: &SDPMLineIndex,
|
||||
// }, payload[FROM]); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// done <- struct{}{}
|
||||
// default:
|
||||
// logger.Println("fs is correctly linked")
|
||||
// done <- struct{}{}
|
||||
// }
|
||||
// done <- struct{}{}
|
||||
// }()
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// err = ctx.Err()
|
||||
// return
|
||||
// case <-done:
|
||||
// return
|
||||
// case err = <-errCh:
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -1,186 +1,186 @@
|
||||
package localserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
// import (
|
||||
// "context"
|
||||
// "encoding/json"
|
||||
// "strconv"
|
||||
|
||||
"github.com/pion/webrtc/v3"
|
||||
)
|
||||
// "github.com/pion/webrtc/v3"
|
||||
// )
|
||||
|
||||
type GrpcRequestType string
|
||||
// type GrpcRequestType string
|
||||
|
||||
const (
|
||||
PEER_CONNECTION_REQUEST GrpcRequestType = "peer_connection_request"
|
||||
)
|
||||
// const (
|
||||
// PEER_CONNECTION_REQUEST GrpcRequestType = "peer_connection_request"
|
||||
// )
|
||||
|
||||
const (
|
||||
OFFER ReqType = "offer"
|
||||
ANSWER ReqType = "answer"
|
||||
COUNTER_OFFER ReqType = "webrtc_counter_offer"
|
||||
JOIN_HOSTED_SQUAD ReqType = "join_hosted_squad"
|
||||
HOSTED_SQUAD_ACCESS_DENIED ReqType = "hosted_squad_access_denied"
|
||||
HOSTED_SQUAD_STOP_CALL ReqType = "hosted_squad_stop_call"
|
||||
HOSTED_SQUAD_ACCESS_GRANTED ReqType = "hosted_squad_access_granted"
|
||||
LEAVE_HOSTED_SQUAD ReqType = "leave_hosted_squad"
|
||||
INCOMING_MEMBER_HOSTED ReqType = "incoming_member_hosted"
|
||||
LEAVING_MEMBER_HOSTED ReqType = "leaving_member_hosted"
|
||||
HOSTED_SQUAD_WEBRTC_OFFER ReqType = "hosted_squad_offer"
|
||||
HOSTED_SQUAD_WEBRTC_ANSWER ReqType = "hosted_squad_answer"
|
||||
HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER ReqType = "hosted_squad_rennegotiation_offer"
|
||||
HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "hosted_squad_rennegotiation_answer"
|
||||
HOSTED_SQUAD_WEBRTC_COUNTER_OFFER ReqType = "hosted_squad_webrtc_counter_offer"
|
||||
HOSTED_SQUAD_WEBRTC_CANDIDATE ReqType = "hosted_squad_webrtc_candidate"
|
||||
HOSTED_SQUAD_REMOVE_VIDEO ReqType = "hosted_squad_remove_video"
|
||||
GET_HOSTED_SQUAD_TRACKS ReqType = "hosted_squad_get_tracks"
|
||||
NEW_HOSTED_SQUAD = "new_hosted_squad"
|
||||
)
|
||||
// const (
|
||||
// OFFER ReqType = "offer"
|
||||
// ANSWER ReqType = "answer"
|
||||
// COUNTER_OFFER ReqType = "webrtc_counter_offer"
|
||||
// JOIN_HOSTED_SQUAD ReqType = "join_hosted_squad"
|
||||
// HOSTED_SQUAD_ACCESS_DENIED ReqType = "hosted_squad_access_denied"
|
||||
// HOSTED_SQUAD_STOP_CALL ReqType = "hosted_squad_stop_call"
|
||||
// HOSTED_SQUAD_ACCESS_GRANTED ReqType = "hosted_squad_access_granted"
|
||||
// LEAVE_HOSTED_SQUAD ReqType = "leave_hosted_squad"
|
||||
// INCOMING_MEMBER_HOSTED ReqType = "incoming_member_hosted"
|
||||
// LEAVING_MEMBER_HOSTED ReqType = "leaving_member_hosted"
|
||||
// HOSTED_SQUAD_WEBRTC_OFFER ReqType = "hosted_squad_offer"
|
||||
// HOSTED_SQUAD_WEBRTC_ANSWER ReqType = "hosted_squad_answer"
|
||||
// HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER ReqType = "hosted_squad_rennegotiation_offer"
|
||||
// HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "hosted_squad_rennegotiation_answer"
|
||||
// HOSTED_SQUAD_WEBRTC_COUNTER_OFFER ReqType = "hosted_squad_webrtc_counter_offer"
|
||||
// HOSTED_SQUAD_WEBRTC_CANDIDATE ReqType = "hosted_squad_webrtc_candidate"
|
||||
// HOSTED_SQUAD_REMOVE_VIDEO ReqType = "hosted_squad_remove_video"
|
||||
// GET_HOSTED_SQUAD_TRACKS ReqType = "hosted_squad_get_tracks"
|
||||
// NEW_HOSTED_SQUAD = "new_hosted_squad"
|
||||
// )
|
||||
|
||||
type WebRTCGrpcMiddleware struct {
|
||||
Manager *WebRTCCallManager
|
||||
stream SignalingService_LinkClient
|
||||
}
|
||||
// type WebRTCGrpcMiddleware struct {
|
||||
// Manager *WebRTCCallManager
|
||||
// stream SignalingService_LinkClient
|
||||
// }
|
||||
|
||||
func NewWebRTCGrpcMiddleware(manager *WebRTCCallManager) (webrtcGrpcMiddleware *WebRTCGrpcMiddleware) {
|
||||
webrtcGrpcMiddleware = &WebRTCGrpcMiddleware{
|
||||
Manager: manager,
|
||||
}
|
||||
return
|
||||
}
|
||||
// func NewWebRTCGrpcMiddleware(manager *WebRTCCallManager) (webrtcGrpcMiddleware *WebRTCGrpcMiddleware) {
|
||||
// webrtcGrpcMiddleware = &WebRTCGrpcMiddleware{
|
||||
// Manager: manager,
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wgm *WebRTCGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) {
|
||||
bs, err := json.Marshal(map[string]string{
|
||||
"from": wgm.Manager.ID,
|
||||
"to": to,
|
||||
"candidate": candidate.ToJSON().Candidate,
|
||||
"sdpMid": *candidate.ToJSON().SDPMid,
|
||||
"sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = wgm.stream.Send(&SignalingMessage{
|
||||
Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE),
|
||||
From: wgm.Manager.ID,
|
||||
To: to,
|
||||
Payload: bs,
|
||||
})
|
||||
return
|
||||
}
|
||||
// func (wgm *WebRTCGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) {
|
||||
// bs, err := json.Marshal(map[string]string{
|
||||
// "from": wgm.Manager.ID,
|
||||
// "to": to,
|
||||
// "candidate": candidate.ToJSON().Candidate,
|
||||
// "sdpMid": *candidate.ToJSON().SDPMid,
|
||||
// "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
|
||||
// })
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// err = wgm.stream.Send(&SignalingMessage{
|
||||
// Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE),
|
||||
// From: wgm.Manager.ID,
|
||||
// To: to,
|
||||
// Payload: bs,
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
func (wgm *WebRTCGrpcMiddleware) 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); err != nil {
|
||||
errCh <- e
|
||||
return
|
||||
}
|
||||
switch req.Type {
|
||||
case NEW_HOSTED_SQUAD:
|
||||
if err := validateRequest(payload, "ID"); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
logger.Println("new squad incoming")
|
||||
wgm.Manager.SquadMapMux.Lock()
|
||||
wgm.Manager.Squads[payload["ID"]] = &Squad{
|
||||
ID: payload["ID"],
|
||||
Members: []string{},
|
||||
}
|
||||
wgm.Manager.SquadMapMux.Unlock()
|
||||
case string(HOSTED_SQUAD_STOP_CALL):
|
||||
logger.Println("quit squad called")
|
||||
if err := validateRequest(payload, "squadId"); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
wgm.Manager.HandleLeavingMember(req.GetFrom(), payload["squadId"])
|
||||
done <- struct{}{}
|
||||
case string(PEER_CONNECTION_REQUEST):
|
||||
logger.Println("creating offer for peer")
|
||||
if err := wgm.Manager.CreateOffer(ctx, req.GetFrom(), req.GetTo(), wgm.signalCandidate); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
case string(HOSTED_SQUAD_WEBRTC_OFFER):
|
||||
if err := validateRequest(payload, SDP, SQUAD_ID); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if err := wgm.Manager.HandleOffer(ctx, req.GetFrom(), req.GetTo(), payload, wgm.signalCandidate); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
case string(HOSTED_SQUAD_WEBRTC_ANSWER):
|
||||
if err := validateRequest(payload, SDP); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
if err := wgm.Manager.HandleAnswer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
case string(HOSTED_SQUAD_WEBRTC_COUNTER_OFFER):
|
||||
if err := wgm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); 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 := wgm.Manager.HandleRennegotiationAnswer(req.GetFrom(), payload[SDP]); 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 := wgm.Manager.HandleRennegotiationOffer(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 := wgm.Manager.AddCandidate(&webrtc.ICECandidateInit{
|
||||
Candidate: payload["candidate"],
|
||||
SDPMid: &sdpMid,
|
||||
SDPMLineIndex: &SDPMLineIndex,
|
||||
}, req.GetFrom()); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
default:
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
return
|
||||
case <-done:
|
||||
return
|
||||
case err = <-errCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
// func (wgm *WebRTCGrpcMiddleware) 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); err != nil {
|
||||
// errCh <- e
|
||||
// return
|
||||
// }
|
||||
// switch req.Type {
|
||||
// case NEW_HOSTED_SQUAD:
|
||||
// if err := validateRequest(payload, "ID"); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// logger.Println("new squad incoming")
|
||||
// wgm.Manager.SquadMapMux.Lock()
|
||||
// wgm.Manager.Squads[payload["ID"]] = &Squad{
|
||||
// ID: payload["ID"],
|
||||
// Members: []string{},
|
||||
// }
|
||||
// wgm.Manager.SquadMapMux.Unlock()
|
||||
// case string(HOSTED_SQUAD_STOP_CALL):
|
||||
// logger.Println("quit squad called")
|
||||
// if err := validateRequest(payload, "squadId"); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// wgm.Manager.HandleLeavingMember(req.GetFrom(), payload["squadId"])
|
||||
// done <- struct{}{}
|
||||
// case string(PEER_CONNECTION_REQUEST):
|
||||
// logger.Println("creating offer for peer")
|
||||
// if err := wgm.Manager.CreateOffer(ctx, req.GetFrom(), req.GetTo(), wgm.signalCandidate); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// case string(HOSTED_SQUAD_WEBRTC_OFFER):
|
||||
// if err := validateRequest(payload, SDP, SQUAD_ID); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// if err := wgm.Manager.HandleOffer(ctx, req.GetFrom(), req.GetTo(), payload, wgm.signalCandidate); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// case string(HOSTED_SQUAD_WEBRTC_ANSWER):
|
||||
// if err := validateRequest(payload, SDP); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// if err := wgm.Manager.HandleAnswer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// case string(HOSTED_SQUAD_WEBRTC_COUNTER_OFFER):
|
||||
// if err := wgm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); 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 := wgm.Manager.HandleRennegotiationAnswer(req.GetFrom(), payload[SDP]); 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 := wgm.Manager.HandleRennegotiationOffer(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 := wgm.Manager.AddCandidate(&webrtc.ICECandidateInit{
|
||||
// Candidate: payload["candidate"],
|
||||
// SDPMid: &sdpMid,
|
||||
// SDPMLineIndex: &SDPMLineIndex,
|
||||
// }, req.GetFrom()); err != nil {
|
||||
// errCh <- err
|
||||
// return
|
||||
// }
|
||||
// default:
|
||||
// }
|
||||
// }()
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// err = ctx.Err()
|
||||
// return
|
||||
// case <-done:
|
||||
// return
|
||||
// case err = <-errCh:
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -1,66 +1,66 @@
|
||||
package localserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
// import (
|
||||
// "context"
|
||||
// "encoding/json"
|
||||
// "io"
|
||||
// "net/http"
|
||||
// )
|
||||
|
||||
type (
|
||||
WebRTCHttpMiddleware struct {
|
||||
//menuItem *systray.MenuItem
|
||||
}
|
||||
)
|
||||
// type (
|
||||
// WebRTCHttpMiddleware struct {
|
||||
// //menuItem *systray.MenuItem
|
||||
// }
|
||||
// )
|
||||
|
||||
const (
|
||||
CREATE_HOSTED_SQUAD = "create_hosted_squad"
|
||||
)
|
||||
// const (
|
||||
// CREATE_HOSTED_SQUAD = "create_hosted_squad"
|
||||
// )
|
||||
|
||||
// func NewWebRTCHttpMiddleware(menuItem *systray.MenuItem) (webRTCHttpMiddleware *WebRTCHttpMiddleware) {
|
||||
// webRTCHttpMiddleware = &WebRTCHttpMiddleware{
|
||||
// menuItem: menuItem,
|
||||
// // func NewWebRTCHttpMiddleware(menuItem *systray.MenuItem) (webRTCHttpMiddleware *WebRTCHttpMiddleware) {
|
||||
// // webRTCHttpMiddleware = &WebRTCHttpMiddleware{
|
||||
// // 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
|
||||
// }
|
||||
|
||||
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{
|
||||
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,
|
||||
@ -459,10 +459,10 @@ func (ac *AudioChannel) createPeerConnection(target string, from string, peerTyp
|
||||
} else if localTrack.Kind() == webrtc.RTPCodecTypeVideo {
|
||||
logger.Println("track of wrong type")
|
||||
}
|
||||
go func() {
|
||||
go func(id string) {
|
||||
<-time.After(time.Millisecond * 500)
|
||||
connection.negotiate(id, sendDCMessage)
|
||||
}()
|
||||
}(id)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@ -413,7 +413,7 @@ func (fs *ChatFSInstance) FileDownloadFailed(chatId, filename, userId string) (e
|
||||
// config := webrtc.Configuration{
|
||||
// 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 {
|
||||
ID uint64
|
||||
ChatId string `json:"chatId"`
|
||||
Owner string `json:"owner"`
|
||||
Name string `json:"name"`
|
||||
|
||||
@ -937,7 +937,7 @@ func (zch *ZoneChatsHandler[T]) AddChatMessage(userId, chatId, content string, i
|
||||
ResponseOf: nil,
|
||||
File: file,
|
||||
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 chatMembers []string
|
||||
|
||||
@ -1109,8 +1109,8 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq
|
||||
Owner: req.Payload["userId"].(string),
|
||||
Type: PRIVATE,
|
||||
Folder: true,
|
||||
ModTime: time.Now().Format(time.UnixDate),
|
||||
CreationTime: time.Now().Format(time.UnixDate),
|
||||
ModTime: time.Now().Format(time.RFC3339),
|
||||
CreationTime: time.Now().Format(time.RFC3339),
|
||||
Size: 0,
|
||||
Members: map[string]*FSEntityAccessRights{
|
||||
req.Payload["userId"].(string): {
|
||||
@ -1148,8 +1148,8 @@ func (zfh *ZoneFileHandler[T]) handleZoneRequest(c context.Context, req *ZoneReq
|
||||
Owner: req.Payload["userId"].(string),
|
||||
Type: req.Payload["type"].(string),
|
||||
Size: uint64(req.Payload["size"].(float64)),
|
||||
ModTime: time.Now().Format(time.UnixDate),
|
||||
CreationTime: time.Now().Format(time.UnixDate),
|
||||
ModTime: time.Now().Format(time.RFC3339),
|
||||
CreationTime: time.Now().Format(time.RFC3339),
|
||||
Folder: false,
|
||||
Members: map[string]*FSEntityAccessRights{
|
||||
req.Payload["userId"].(string): {
|
||||
|
||||
@ -416,7 +416,7 @@ func (fs *FSInstance) FileDownloadFailed(path, filename, userId string) (err err
|
||||
// config := webrtc.Configuration{
|
||||
// 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,
|
||||
|
||||
@ -3,6 +3,7 @@ package localserver
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"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) {
|
||||
done, errCh := make(chan struct{}), make(chan error)
|
||||
fmt.Println("got req", req.Type, string(req.Payload))
|
||||
go func() {
|
||||
var payload map[string]string
|
||||
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) {
|
||||
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)
|
||||
go func() {
|
||||
defer close(reqChan)
|
||||
reqChan <- &ZoneRequest{
|
||||
ReqType: string(REMOVE_USER),
|
||||
From: payload["userId"],
|
||||
Payload: map[string]interface{}{
|
||||
"userId": payload["userId"],
|
||||
},
|
||||
}
|
||||
// reqChan <- &ZoneRequest{
|
||||
// ReqType: string(REMOVE_USER),
|
||||
// From: payload["userId"],
|
||||
// Payload: map[string]interface{}{
|
||||
// "userId": payload["userId"],
|
||||
// },
|
||||
// }
|
||||
reqChan <- &ZoneRequest{
|
||||
ReqType: string(REMOVED_ZONE_AUTHORIZED_MEMBER),
|
||||
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) {
|
||||
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)
|
||||
go func() {
|
||||
defer close(reqChan)
|
||||
reqChan <- &ZoneRequest{
|
||||
ReqType: string(ADD_USER),
|
||||
From: payload["userId"],
|
||||
Payload: map[string]interface{}{
|
||||
"userId": payload["userId"],
|
||||
},
|
||||
}
|
||||
reqChan <- &ZoneRequest{
|
||||
ReqType: string(NEW_AUTHORIZED_ZONE_MEMBER),
|
||||
From: payload["userId"],
|
||||
@ -142,6 +145,13 @@ func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage
|
||||
"userId": payload["userId"],
|
||||
},
|
||||
}
|
||||
// reqChan <- &ZoneRequest{
|
||||
// ReqType: string(ADD_USER),
|
||||
// From: payload["userId"],
|
||||
// Payload: map[string]interface{}{
|
||||
// "userId": payload["userId"],
|
||||
// },
|
||||
// }
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
|
||||
@ -400,7 +400,7 @@ func (zm *ZoneManager) createPeerConnection(target string, from string, zoneId s
|
||||
config := webrtc.Configuration{
|
||||
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,
|
||||
|
||||
@ -48,44 +48,6 @@ func NewZoneNotificationsHandler(_ string, zoneId string, owner string, authoriz
|
||||
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) {
|
||||
//? initialization code here
|
||||
return
|
||||
@ -169,12 +131,6 @@ func (znh *ZoneNotificationsHandler) handleZoneRequest(ctx context.Context, req
|
||||
err = fmt.Errorf(" field recipient in payload is wrong type")
|
||||
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)...)
|
||||
}
|
||||
|
||||
@ -80,16 +80,16 @@ func NewZoneRequestScheduler(authorizedMembers []string, handlers ...ZoneRequest
|
||||
|
||||
func (zrs *ZoneRequestScheduler) Schedule(reqChan <-chan *ZoneRequest) (done chan struct{}, errCh chan error) {
|
||||
done, errCh = make(chan struct{}), make(chan error)
|
||||
for req := range reqChan {
|
||||
go func(r *ZoneRequest) {
|
||||
for _, publisher := range zrs.handlersPublishers {
|
||||
go func(p chan<- *ZoneRequest) {
|
||||
p <- r
|
||||
}(publisher)
|
||||
}
|
||||
}(req)
|
||||
}
|
||||
done <- struct{}{}
|
||||
go func() {
|
||||
for req := range reqChan {
|
||||
go func(r *ZoneRequest) {
|
||||
for _, publisher := range zrs.handlersPublishers {
|
||||
publisher <- r
|
||||
}
|
||||
}(req)
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
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