add chat tracking

This commit is contained in:
Loïs BIBEHE 2022-08-28 22:17:30 +02:00
parent 5e95736e76
commit a4ee2b4048
31 changed files with 5950 additions and 3472 deletions

View File

@ -6,6 +6,8 @@ import (
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"encoding/pem" "encoding/pem"
"fmt"
"io"
"log" "log"
"net/http" "net/http"
"os" "os"
@ -14,6 +16,8 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
var NodeToken string
type LocalServerConfig struct { type LocalServerConfig struct {
configFilePath string configFilePath string
NodeId string `json:"nodeId"` NodeId string `json:"nodeId"`
@ -29,9 +33,13 @@ func NewLocalServerConfig() (localServerConfig *LocalServerConfig, err error) {
logger.SetOutput(logFile) logger.SetOutput(logFile)
logger.SetFlags(log.Ldate | log.LUTC | log.Lshortfile | log.Ltime | log.LstdFlags | log.Lmsgprefix) logger.SetFlags(log.Ldate | log.LUTC | log.Lshortfile | log.Ltime | log.LstdFlags | log.Lmsgprefix)
localServerConfig = &LocalServerConfig{ localServerConfig = &LocalServerConfig{
configFilePath: filepath.Join("config","node_config.json"), configFilePath: filepath.Join("config", "node_config.json"),
} }
err = localServerConfig.startup() err = localServerConfig.startup()
if err != nil {
return
}
err = localServerConfig.authenticate()
return return
} }
@ -74,6 +82,7 @@ func (l *LocalServerConfig) startup() (err error) {
"from": id, "from": id,
"to": "serv", "to": "serv",
"token": "", "token": "",
"peerType":"node",
"payload": map[string]string{ "payload": map[string]string{
"nodeId": id, "nodeId": id,
"nodeKey": string(key), "nodeKey": string(key),
@ -99,6 +108,70 @@ func (l *LocalServerConfig) startup() (err error) {
if err != nil { if err != nil {
return return
} }
err = json.NewDecoder(file).Decode(&l) var config map[string]string
err = json.NewDecoder(file).Decode(&config)
if err != nil {
return
}
logger.Println(config)
l.NodeId = config["nodeId"]
l.PrivateKeyPath = config["privKeyPath"]
l.Token = config["token"]
return
}
func (l *LocalServerConfig) authenticate() (err error) {
f, err := os.Open(l.PrivateKeyPath)
if err != nil {
return
}
var buff []byte
buff, err = io.ReadAll(f)
if err != nil {
return
}
em := NewEncryptionManager()
sig, err := em.SignRequest(string(buff), l.NodeId)
if err != nil {
return
}
body, err := json.Marshal(map[string]interface{}{
"type": "login_key_node",
"from": l.NodeId,
"peerType":"node",
"payload": map[string]string{
"id": l.NodeId,
"signature": sig,
},
})
if err != nil {
return
}
res, err := http.Post("https://app.zippytal.com/req", "application/json", bytes.NewBuffer(body))
if err != nil {
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
}
err = l.handleLoginResponse(payload)
return
}
func (l *LocalServerConfig) handleLoginResponse(res map[string]any) (err error) {
if _, ok := res["token"]; !ok {
err = fmt.Errorf("no field token in res")
return
}
if _, ok := res["token"].(string); !ok {
err = fmt.Errorf("field token not string in res")
return
}
NodeToken = res["token"].(string)
return return
} }

View File

@ -2,9 +2,13 @@ package localserver
import ( import (
"context" "context"
"crypto"
"crypto/hmac"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/base64"
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"io" "io"
@ -110,3 +114,37 @@ func (em *EncryptionManager) LoadPrivKey(privKeyPath string, password string) (e
em.PrivKey = privKey em.PrivKey = privKey
return return
} }
func (em *EncryptionManager) ParsePrivKey(privateKey string) (privKey *rsa.PrivateKey, err error) {
key := []byte(privateKey)
block, _ := pem.Decode(key)
b := block.Bytes
privKey, err = x509.ParsePKCS1PrivateKey(b)
if err != nil {
return
}
return
}
func (em *EncryptionManager) SignRequest(privKey, id string) (sig string, err error) {
key, err := em.ParsePrivKey(privKey)
if err != nil {
return
}
msg := []byte(id)
hashed := sha256.Sum256(msg)
signature, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, hashed[:])
if err != nil {
return
}
sig = base64.StdEncoding.EncodeToString(signature)
return
}
func (em *EncryptionManager) SignRequestHMAC(id string) string {
h := hmac.New(sha256.New, []byte(NodeToken))
h.Write([]byte(id))
sha := h.Sum(nil)
sig := base64.StdEncoding.EncodeToString(sha)
return sig
}

4
go.mod
View File

@ -1,10 +1,9 @@
module github.com/loisBN/zippytal_node/localserver module github.com/loisBN/zippytal_node/localserver
go 1.17 go 1.18
require ( require (
github.com/dgraph-io/badger/v3 v3.2103.2 github.com/dgraph-io/badger/v3 v3.2103.2
github.com/golang/protobuf v1.5.2
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/pion/rtcp v1.2.9 github.com/pion/rtcp v1.2.9
github.com/pion/webrtc/v3 v3.1.11 github.com/pion/webrtc/v3 v3.1.11
@ -20,6 +19,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.3 // indirect github.com/golang/snappy v0.0.3 // indirect
github.com/google/flatbuffers v1.12.1 // indirect github.com/google/flatbuffers v1.12.1 // indirect
github.com/klauspost/compress v1.12.3 // indirect github.com/klauspost/compress v1.12.3 // indirect

File diff suppressed because it is too large Load Diff

View File

@ -1,422 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package localserver
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// GrpcManagerClient is the client API for GrpcManager service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type GrpcManagerClient interface {
Link(ctx context.Context, opts ...grpc.CallOption) (GrpcManager_LinkClient, error)
RegisterPeer(ctx context.Context, in *PeerRegisterRequest, opts ...grpc.CallOption) (*PeerRegisterResponse, error)
ListPeers(ctx context.Context, in *PeerListRequest, opts ...grpc.CallOption) (*PeerListResponse, error)
CreateSquad(ctx context.Context, in *SquadCreateRequest, opts ...grpc.CallOption) (*SquadCreateResponse, error)
UpdateSquad(ctx context.Context, in *SquadUpdateRequest, opts ...grpc.CallOption) (*SquadUpdateResponse, error)
DeleteSquad(ctx context.Context, in *SquadDeleteRequest, opts ...grpc.CallOption) (*SquadDeleteResponse, error)
ListSquad(ctx context.Context, in *SquadListRequest, opts ...grpc.CallOption) (*SquadListResponse, error)
ConnectSquad(ctx context.Context, in *SquadConnectRequest, opts ...grpc.CallOption) (*SquadConnectResponse, error)
LeaveSquad(ctx context.Context, in *SquadLeaveRequest, opts ...grpc.CallOption) (*SquadLeaveResponse, error)
}
type grpcManagerClient struct {
cc grpc.ClientConnInterface
}
func NewGrpcManagerClient(cc grpc.ClientConnInterface) GrpcManagerClient {
return &grpcManagerClient{cc}
}
func (c *grpcManagerClient) Link(ctx context.Context, opts ...grpc.CallOption) (GrpcManager_LinkClient, error) {
stream, err := c.cc.NewStream(ctx, &GrpcManager_ServiceDesc.Streams[0], "/manager.GrpcManager/Link", opts...)
if err != nil {
return nil, err
}
x := &grpcManagerLinkClient{stream}
return x, nil
}
type GrpcManager_LinkClient interface {
Send(*Request) error
Recv() (*Response, error)
grpc.ClientStream
}
type grpcManagerLinkClient struct {
grpc.ClientStream
}
func (x *grpcManagerLinkClient) Send(m *Request) error {
return x.ClientStream.SendMsg(m)
}
func (x *grpcManagerLinkClient) Recv() (*Response, error) {
m := new(Response)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *grpcManagerClient) RegisterPeer(ctx context.Context, in *PeerRegisterRequest, opts ...grpc.CallOption) (*PeerRegisterResponse, error) {
out := new(PeerRegisterResponse)
err := c.cc.Invoke(ctx, "/manager.GrpcManager/RegisterPeer", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *grpcManagerClient) ListPeers(ctx context.Context, in *PeerListRequest, opts ...grpc.CallOption) (*PeerListResponse, error) {
out := new(PeerListResponse)
err := c.cc.Invoke(ctx, "/manager.GrpcManager/ListPeers", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *grpcManagerClient) CreateSquad(ctx context.Context, in *SquadCreateRequest, opts ...grpc.CallOption) (*SquadCreateResponse, error) {
out := new(SquadCreateResponse)
err := c.cc.Invoke(ctx, "/manager.GrpcManager/CreateSquad", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *grpcManagerClient) UpdateSquad(ctx context.Context, in *SquadUpdateRequest, opts ...grpc.CallOption) (*SquadUpdateResponse, error) {
out := new(SquadUpdateResponse)
err := c.cc.Invoke(ctx, "/manager.GrpcManager/UpdateSquad", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *grpcManagerClient) DeleteSquad(ctx context.Context, in *SquadDeleteRequest, opts ...grpc.CallOption) (*SquadDeleteResponse, error) {
out := new(SquadDeleteResponse)
err := c.cc.Invoke(ctx, "/manager.GrpcManager/DeleteSquad", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *grpcManagerClient) ListSquad(ctx context.Context, in *SquadListRequest, opts ...grpc.CallOption) (*SquadListResponse, error) {
out := new(SquadListResponse)
err := c.cc.Invoke(ctx, "/manager.GrpcManager/ListSquad", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *grpcManagerClient) ConnectSquad(ctx context.Context, in *SquadConnectRequest, opts ...grpc.CallOption) (*SquadConnectResponse, error) {
out := new(SquadConnectResponse)
err := c.cc.Invoke(ctx, "/manager.GrpcManager/ConnectSquad", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *grpcManagerClient) LeaveSquad(ctx context.Context, in *SquadLeaveRequest, opts ...grpc.CallOption) (*SquadLeaveResponse, error) {
out := new(SquadLeaveResponse)
err := c.cc.Invoke(ctx, "/manager.GrpcManager/LeaveSquad", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// GrpcManagerServer is the server API for GrpcManager service.
// All implementations must embed UnimplementedGrpcManagerServer
// for forward compatibility
type GrpcManagerServer interface {
Link(GrpcManager_LinkServer) error
RegisterPeer(context.Context, *PeerRegisterRequest) (*PeerRegisterResponse, error)
ListPeers(context.Context, *PeerListRequest) (*PeerListResponse, error)
CreateSquad(context.Context, *SquadCreateRequest) (*SquadCreateResponse, error)
UpdateSquad(context.Context, *SquadUpdateRequest) (*SquadUpdateResponse, error)
DeleteSquad(context.Context, *SquadDeleteRequest) (*SquadDeleteResponse, error)
ListSquad(context.Context, *SquadListRequest) (*SquadListResponse, error)
ConnectSquad(context.Context, *SquadConnectRequest) (*SquadConnectResponse, error)
LeaveSquad(context.Context, *SquadLeaveRequest) (*SquadLeaveResponse, error)
mustEmbedUnimplementedGrpcManagerServer()
}
// UnimplementedGrpcManagerServer must be embedded to have forward compatible implementations.
type UnimplementedGrpcManagerServer struct {
}
func (UnimplementedGrpcManagerServer) Link(GrpcManager_LinkServer) error {
return status.Errorf(codes.Unimplemented, "method Link not implemented")
}
func (UnimplementedGrpcManagerServer) RegisterPeer(context.Context, *PeerRegisterRequest) (*PeerRegisterResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RegisterPeer not implemented")
}
func (UnimplementedGrpcManagerServer) ListPeers(context.Context, *PeerListRequest) (*PeerListResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListPeers not implemented")
}
func (UnimplementedGrpcManagerServer) CreateSquad(context.Context, *SquadCreateRequest) (*SquadCreateResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateSquad not implemented")
}
func (UnimplementedGrpcManagerServer) UpdateSquad(context.Context, *SquadUpdateRequest) (*SquadUpdateResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateSquad not implemented")
}
func (UnimplementedGrpcManagerServer) DeleteSquad(context.Context, *SquadDeleteRequest) (*SquadDeleteResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteSquad not implemented")
}
func (UnimplementedGrpcManagerServer) ListSquad(context.Context, *SquadListRequest) (*SquadListResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListSquad not implemented")
}
func (UnimplementedGrpcManagerServer) ConnectSquad(context.Context, *SquadConnectRequest) (*SquadConnectResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ConnectSquad not implemented")
}
func (UnimplementedGrpcManagerServer) LeaveSquad(context.Context, *SquadLeaveRequest) (*SquadLeaveResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method LeaveSquad not implemented")
}
func (UnimplementedGrpcManagerServer) mustEmbedUnimplementedGrpcManagerServer() {}
// UnsafeGrpcManagerServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to GrpcManagerServer will
// result in compilation errors.
type UnsafeGrpcManagerServer interface {
mustEmbedUnimplementedGrpcManagerServer()
}
func RegisterGrpcManagerServer(s grpc.ServiceRegistrar, srv GrpcManagerServer) {
s.RegisterService(&GrpcManager_ServiceDesc, srv)
}
func _GrpcManager_Link_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(GrpcManagerServer).Link(&grpcManagerLinkServer{stream})
}
type GrpcManager_LinkServer interface {
Send(*Response) error
Recv() (*Request, error)
grpc.ServerStream
}
type grpcManagerLinkServer struct {
grpc.ServerStream
}
func (x *grpcManagerLinkServer) Send(m *Response) error {
return x.ServerStream.SendMsg(m)
}
func (x *grpcManagerLinkServer) Recv() (*Request, error) {
m := new(Request)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _GrpcManager_RegisterPeer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PeerRegisterRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrpcManagerServer).RegisterPeer(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/manager.GrpcManager/RegisterPeer",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcManagerServer).RegisterPeer(ctx, req.(*PeerRegisterRequest))
}
return interceptor(ctx, in, info, handler)
}
func _GrpcManager_ListPeers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PeerListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrpcManagerServer).ListPeers(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/manager.GrpcManager/ListPeers",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcManagerServer).ListPeers(ctx, req.(*PeerListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _GrpcManager_CreateSquad_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SquadCreateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrpcManagerServer).CreateSquad(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/manager.GrpcManager/CreateSquad",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcManagerServer).CreateSquad(ctx, req.(*SquadCreateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _GrpcManager_UpdateSquad_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SquadUpdateRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrpcManagerServer).UpdateSquad(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/manager.GrpcManager/UpdateSquad",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcManagerServer).UpdateSquad(ctx, req.(*SquadUpdateRequest))
}
return interceptor(ctx, in, info, handler)
}
func _GrpcManager_DeleteSquad_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SquadDeleteRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrpcManagerServer).DeleteSquad(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/manager.GrpcManager/DeleteSquad",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcManagerServer).DeleteSquad(ctx, req.(*SquadDeleteRequest))
}
return interceptor(ctx, in, info, handler)
}
func _GrpcManager_ListSquad_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SquadListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrpcManagerServer).ListSquad(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/manager.GrpcManager/ListSquad",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcManagerServer).ListSquad(ctx, req.(*SquadListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _GrpcManager_ConnectSquad_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SquadConnectRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrpcManagerServer).ConnectSquad(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/manager.GrpcManager/ConnectSquad",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcManagerServer).ConnectSquad(ctx, req.(*SquadConnectRequest))
}
return interceptor(ctx, in, info, handler)
}
func _GrpcManager_LeaveSquad_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SquadLeaveRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrpcManagerServer).LeaveSquad(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/manager.GrpcManager/LeaveSquad",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcManagerServer).LeaveSquad(ctx, req.(*SquadLeaveRequest))
}
return interceptor(ctx, in, info, handler)
}
// GrpcManager_ServiceDesc is the grpc.ServiceDesc for GrpcManager service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var GrpcManager_ServiceDesc = grpc.ServiceDesc{
ServiceName: "manager.GrpcManager",
HandlerType: (*GrpcManagerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "RegisterPeer",
Handler: _GrpcManager_RegisterPeer_Handler,
},
{
MethodName: "ListPeers",
Handler: _GrpcManager_ListPeers_Handler,
},
{
MethodName: "CreateSquad",
Handler: _GrpcManager_CreateSquad_Handler,
},
{
MethodName: "UpdateSquad",
Handler: _GrpcManager_UpdateSquad_Handler,
},
{
MethodName: "DeleteSquad",
Handler: _GrpcManager_DeleteSquad_Handler,
},
{
MethodName: "ListSquad",
Handler: _GrpcManager_ListSquad_Handler,
},
{
MethodName: "ConnectSquad",
Handler: _GrpcManager_ConnectSquad_Handler,
},
{
MethodName: "LeaveSquad",
Handler: _GrpcManager_LeaveSquad_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Link",
Handler: _GrpcManager_Link_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "grpc_manager.proto",
}

1658
node.pb.go Normal file

File diff suppressed because it is too large Load Diff

357
node_grpc.pb.go Normal file
View File

@ -0,0 +1,357 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.14.0
// source: node.proto
package localserver
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// NodeServiceClient is the client API for NodeService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type NodeServiceClient interface {
CreateNode(ctx context.Context, in *CreateNodeRequest, opts ...grpc.CallOption) (*CreateNodeResponse, error)
GetNodes(ctx context.Context, in *GetNodesRequest, opts ...grpc.CallOption) (*GetNodesResponse, error)
GetNode(ctx context.Context, in *GetNodeRequest, opts ...grpc.CallOption) (*GetNodeResponse, error)
DeleteNode(ctx context.Context, in *DeleteNodeRequest, opts ...grpc.CallOption) (*DeleteNodeResponse, error)
NewPeerOperatorRequest(ctx context.Context, in *AddPeerOperatorRequest, opts ...grpc.CallOption) (*AddPeerOperatorResponse, error)
DeletePeerOperatorRequest(ctx context.Context, in *RejectPeerOperatorRequest, opts ...grpc.CallOption) (*RejectPeerOperatorResponse, error)
AcceptNewPeerOperator(ctx context.Context, in *AcceptPeerOperatorRequest, opts ...grpc.CallOption) (*AcceptPeerOperatorResponse, error)
RemovePeerOperator(ctx context.Context, in *RemovePeerOperatorRequest, opts ...grpc.CallOption) (*RemovePeerOperatorResponse, error)
}
type nodeServiceClient struct {
cc grpc.ClientConnInterface
}
func NewNodeServiceClient(cc grpc.ClientConnInterface) NodeServiceClient {
return &nodeServiceClient{cc}
}
func (c *nodeServiceClient) CreateNode(ctx context.Context, in *CreateNodeRequest, opts ...grpc.CallOption) (*CreateNodeResponse, error) {
out := new(CreateNodeResponse)
err := c.cc.Invoke(ctx, "/node.NodeService/CreateNode", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *nodeServiceClient) GetNodes(ctx context.Context, in *GetNodesRequest, opts ...grpc.CallOption) (*GetNodesResponse, error) {
out := new(GetNodesResponse)
err := c.cc.Invoke(ctx, "/node.NodeService/GetNodes", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *nodeServiceClient) GetNode(ctx context.Context, in *GetNodeRequest, opts ...grpc.CallOption) (*GetNodeResponse, error) {
out := new(GetNodeResponse)
err := c.cc.Invoke(ctx, "/node.NodeService/GetNode", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *nodeServiceClient) DeleteNode(ctx context.Context, in *DeleteNodeRequest, opts ...grpc.CallOption) (*DeleteNodeResponse, error) {
out := new(DeleteNodeResponse)
err := c.cc.Invoke(ctx, "/node.NodeService/DeleteNode", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *nodeServiceClient) NewPeerOperatorRequest(ctx context.Context, in *AddPeerOperatorRequest, opts ...grpc.CallOption) (*AddPeerOperatorResponse, error) {
out := new(AddPeerOperatorResponse)
err := c.cc.Invoke(ctx, "/node.NodeService/NewPeerOperatorRequest", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *nodeServiceClient) DeletePeerOperatorRequest(ctx context.Context, in *RejectPeerOperatorRequest, opts ...grpc.CallOption) (*RejectPeerOperatorResponse, error) {
out := new(RejectPeerOperatorResponse)
err := c.cc.Invoke(ctx, "/node.NodeService/DeletePeerOperatorRequest", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *nodeServiceClient) AcceptNewPeerOperator(ctx context.Context, in *AcceptPeerOperatorRequest, opts ...grpc.CallOption) (*AcceptPeerOperatorResponse, error) {
out := new(AcceptPeerOperatorResponse)
err := c.cc.Invoke(ctx, "/node.NodeService/AcceptNewPeerOperator", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *nodeServiceClient) RemovePeerOperator(ctx context.Context, in *RemovePeerOperatorRequest, opts ...grpc.CallOption) (*RemovePeerOperatorResponse, error) {
out := new(RemovePeerOperatorResponse)
err := c.cc.Invoke(ctx, "/node.NodeService/RemovePeerOperator", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// NodeServiceServer is the server API for NodeService service.
// All implementations must embed UnimplementedNodeServiceServer
// for forward compatibility
type NodeServiceServer interface {
CreateNode(context.Context, *CreateNodeRequest) (*CreateNodeResponse, error)
GetNodes(context.Context, *GetNodesRequest) (*GetNodesResponse, error)
GetNode(context.Context, *GetNodeRequest) (*GetNodeResponse, error)
DeleteNode(context.Context, *DeleteNodeRequest) (*DeleteNodeResponse, error)
NewPeerOperatorRequest(context.Context, *AddPeerOperatorRequest) (*AddPeerOperatorResponse, error)
DeletePeerOperatorRequest(context.Context, *RejectPeerOperatorRequest) (*RejectPeerOperatorResponse, error)
AcceptNewPeerOperator(context.Context, *AcceptPeerOperatorRequest) (*AcceptPeerOperatorResponse, error)
RemovePeerOperator(context.Context, *RemovePeerOperatorRequest) (*RemovePeerOperatorResponse, error)
mustEmbedUnimplementedNodeServiceServer()
}
// UnimplementedNodeServiceServer must be embedded to have forward compatible implementations.
type UnimplementedNodeServiceServer struct {
}
func (UnimplementedNodeServiceServer) CreateNode(context.Context, *CreateNodeRequest) (*CreateNodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateNode not implemented")
}
func (UnimplementedNodeServiceServer) GetNodes(context.Context, *GetNodesRequest) (*GetNodesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetNodes not implemented")
}
func (UnimplementedNodeServiceServer) GetNode(context.Context, *GetNodeRequest) (*GetNodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetNode not implemented")
}
func (UnimplementedNodeServiceServer) DeleteNode(context.Context, *DeleteNodeRequest) (*DeleteNodeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteNode not implemented")
}
func (UnimplementedNodeServiceServer) NewPeerOperatorRequest(context.Context, *AddPeerOperatorRequest) (*AddPeerOperatorResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method NewPeerOperatorRequest not implemented")
}
func (UnimplementedNodeServiceServer) DeletePeerOperatorRequest(context.Context, *RejectPeerOperatorRequest) (*RejectPeerOperatorResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeletePeerOperatorRequest not implemented")
}
func (UnimplementedNodeServiceServer) AcceptNewPeerOperator(context.Context, *AcceptPeerOperatorRequest) (*AcceptPeerOperatorResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method AcceptNewPeerOperator not implemented")
}
func (UnimplementedNodeServiceServer) RemovePeerOperator(context.Context, *RemovePeerOperatorRequest) (*RemovePeerOperatorResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RemovePeerOperator not implemented")
}
func (UnimplementedNodeServiceServer) mustEmbedUnimplementedNodeServiceServer() {}
// UnsafeNodeServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to NodeServiceServer will
// result in compilation errors.
type UnsafeNodeServiceServer interface {
mustEmbedUnimplementedNodeServiceServer()
}
func RegisterNodeServiceServer(s grpc.ServiceRegistrar, srv NodeServiceServer) {
s.RegisterService(&NodeService_ServiceDesc, srv)
}
func _NodeService_CreateNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateNodeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NodeServiceServer).CreateNode(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/node.NodeService/CreateNode",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NodeServiceServer).CreateNode(ctx, req.(*CreateNodeRequest))
}
return interceptor(ctx, in, info, handler)
}
func _NodeService_GetNodes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetNodesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NodeServiceServer).GetNodes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/node.NodeService/GetNodes",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NodeServiceServer).GetNodes(ctx, req.(*GetNodesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _NodeService_GetNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetNodeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NodeServiceServer).GetNode(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/node.NodeService/GetNode",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NodeServiceServer).GetNode(ctx, req.(*GetNodeRequest))
}
return interceptor(ctx, in, info, handler)
}
func _NodeService_DeleteNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteNodeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NodeServiceServer).DeleteNode(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/node.NodeService/DeleteNode",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NodeServiceServer).DeleteNode(ctx, req.(*DeleteNodeRequest))
}
return interceptor(ctx, in, info, handler)
}
func _NodeService_NewPeerOperatorRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddPeerOperatorRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NodeServiceServer).NewPeerOperatorRequest(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/node.NodeService/NewPeerOperatorRequest",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NodeServiceServer).NewPeerOperatorRequest(ctx, req.(*AddPeerOperatorRequest))
}
return interceptor(ctx, in, info, handler)
}
func _NodeService_DeletePeerOperatorRequest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RejectPeerOperatorRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NodeServiceServer).DeletePeerOperatorRequest(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/node.NodeService/DeletePeerOperatorRequest",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NodeServiceServer).DeletePeerOperatorRequest(ctx, req.(*RejectPeerOperatorRequest))
}
return interceptor(ctx, in, info, handler)
}
func _NodeService_AcceptNewPeerOperator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AcceptPeerOperatorRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NodeServiceServer).AcceptNewPeerOperator(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/node.NodeService/AcceptNewPeerOperator",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NodeServiceServer).AcceptNewPeerOperator(ctx, req.(*AcceptPeerOperatorRequest))
}
return interceptor(ctx, in, info, handler)
}
func _NodeService_RemovePeerOperator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemovePeerOperatorRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(NodeServiceServer).RemovePeerOperator(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/node.NodeService/RemovePeerOperator",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(NodeServiceServer).RemovePeerOperator(ctx, req.(*RemovePeerOperatorRequest))
}
return interceptor(ctx, in, info, handler)
}
// NodeService_ServiceDesc is the grpc.ServiceDesc for NodeService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var NodeService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "node.NodeService",
HandlerType: (*NodeServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "CreateNode",
Handler: _NodeService_CreateNode_Handler,
},
{
MethodName: "GetNodes",
Handler: _NodeService_GetNodes_Handler,
},
{
MethodName: "GetNode",
Handler: _NodeService_GetNode_Handler,
},
{
MethodName: "DeleteNode",
Handler: _NodeService_DeleteNode_Handler,
},
{
MethodName: "NewPeerOperatorRequest",
Handler: _NodeService_NewPeerOperatorRequest_Handler,
},
{
MethodName: "DeletePeerOperatorRequest",
Handler: _NodeService_DeletePeerOperatorRequest_Handler,
},
{
MethodName: "AcceptNewPeerOperator",
Handler: _NodeService_AcceptNewPeerOperator_Handler,
},
{
MethodName: "RemovePeerOperator",
Handler: _NodeService_RemovePeerOperator_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "node.proto",
}

120
proto/node.proto Normal file
View File

@ -0,0 +1,120 @@
syntax = "proto3";
package node;
option go_package = ".;localserver";
enum NodeAuthType {
token = 0;
key = 1;
mac = 2;
none = 3;
}
message NodeAuthParams {
string nodeId = 1;
string authKey = 2;
NodeAuthType authType = 3;
}
message OperatorAuthParams {
string operatorId = 1;
string authKey = 2;
NodeAuthType authType = 3;
}
message Node {
string id = 1;
string name = 2;
string pubKey = 3;
bool active = 4;
repeated string operators = 5;
repeated string operatorRequests = 6;
}
message CreateNodeRequest {
string nodeId = 1;
string nodeKey = 2;
string nodeUsername = 3;
}
message CreateNodeResponse {
bool success = 1;
string nodeId = 2;
}
message GetNodesRequest {
OperatorAuthParams authParams = 1;
int64 lastIndex = 2;
int64 limit = 3;
}
message GetNodesResponse {
repeated Node nodes = 1;
bool success = 2;
}
message GetNodeRequest {
string nodeId = 1;
}
message GetNodeResponse {
Node node = 1;
bool success = 2;
}
message DeleteNodeRequest {
NodeAuthParams authParams = 1;
string nodeId = 2;
}
message DeleteNodeResponse {
bool success = 1;
}
message AddPeerOperatorRequest {
OperatorAuthParams authParams = 1;
string nodeId = 2;
string operatorId = 3;
}
message AddPeerOperatorResponse {
bool success = 1;
}
message RejectPeerOperatorRequest {
NodeAuthParams authParams = 1;
string nodeId = 2;
string operatorId = 3;
}
message RejectPeerOperatorResponse {
bool success = 1;
}
message AcceptPeerOperatorRequest {
NodeAuthParams authParams = 1;
string nodeId = 2;
string operatorId = 3;
}
message AcceptPeerOperatorResponse {
bool success = 1;
}
message RemovePeerOperatorRequest {
OperatorAuthParams authParams = 1;
string nodeId = 2;
string operatorId = 3;
}
message RemovePeerOperatorResponse {
bool success = 1;
}
service NodeService {
rpc CreateNode (CreateNodeRequest) returns (CreateNodeResponse);
rpc GetNodes (GetNodesRequest) returns (GetNodesResponse);
rpc GetNode (GetNodeRequest) returns (GetNodeResponse);
rpc DeleteNode (DeleteNodeRequest) returns (DeleteNodeResponse);
rpc NewPeerOperatorRequest (AddPeerOperatorRequest) returns (AddPeerOperatorResponse);
rpc DeletePeerOperatorRequest (RejectPeerOperatorRequest) returns (RejectPeerOperatorResponse);
rpc AcceptNewPeerOperator (AcceptPeerOperatorRequest) returns (AcceptPeerOperatorResponse);
rpc RemovePeerOperator (RemovePeerOperatorRequest) returns (RemovePeerOperatorResponse);
}

26
proto/signaling.proto Normal file
View File

@ -0,0 +1,26 @@
syntax = "proto3";
package signaling;
option go_package = ".;localserver";
message NotifyRequest {
string from = 1;
string to = 2;
string type = 3;
bytes payload = 4;
}
message NotifyResponse {
bool success = 1;
}
message SignalingMessage {
string type = 1;
string from = 2;
string to = 3;
bytes payload = 4;
}
service SignalingService {
rpc Notify (NotifyRequest) returns (NotifyResponse);
rpc Link (stream SignalingMessage) returns (stream SignalingMessage);
}

101
server.go
View File

@ -2,6 +2,8 @@ package localserver
import ( import (
"context" "context"
"encoding/json"
"fmt"
"log" "log"
"net/http" "net/http"
"path/filepath" "path/filepath"
@ -41,8 +43,8 @@ type (
Process(ctx context.Context, req *http.Request, w http.ResponseWriter) (err error) Process(ctx context.Context, req *http.Request, w http.ResponseWriter) (err error)
} }
GrpcClientManagerMiddleware interface { SignalingClientManagerMiddleware interface {
Process(ctx context.Context, req *Response, stream GrpcManager_LinkClient) (err error) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error)
} }
LocalServerHandler struct { LocalServerHandler struct {
@ -51,14 +53,14 @@ type (
LocalServer struct { LocalServer struct {
ID string ID string
GrpcClientManager *GrpcClientManager GrpcClientManager *SignalingClientManager
} }
GrpcClientManager struct { SignalingClientManager struct {
GrpcConn grpc.ClientConnInterface GrpcConn grpc.ClientConnInterface
GrpcManagerClient GrpcManagerClient SignalingManagerClient SignalingServiceClient
GrpcLinkClient GrpcManager_LinkClient GrpcLinkClient SignalingService_LinkClient
middlewares []GrpcClientManagerMiddleware middlewares []SignalingClientManagerMiddleware
} }
CustomMenuItem struct { CustomMenuItem struct {
@ -77,72 +79,89 @@ type (
} }
) )
func NewLocalServer(addr string, grpcAddr string, id string, token string) (localServer *LocalServer, err error) { func NewLocalServer(_ string, grpcAddr string, id string, token string) (localServer *LocalServer, err error) {
webRTCCallManager, err := NewWebRTCCallManager(id, token, NewWebrtcCallSoundManager(), NewWebrtcCallChatManager(), NewWebrtcCallVideoManager(), NewWebrtcCallFileManager()) webRTCCallManager, err := NewWebRTCCallManager(id, token, NewWebrtcCallSoundManager(), NewWebrtcCallChatManager(), NewWebrtcCallVideoManager(), NewWebrtcCallFileManager())
if err != nil { if err != nil {
err = fmt.Errorf("error from call manager")
return return
} }
zoneManager, err := NewZoneManager(id, token) zoneManager, err := NewZoneManager(id, token)
if err != nil { if err != nil {
err = fmt.Errorf("error from zone manager")
return return
} }
webrtcFsManager, err := NewWebrtcFsManager(NewP2PFSDatachannelManager()) webrtcFsManager, err := NewWebrtcFsManager(NewP2PFSDatachannelManager())
if err != nil { if err != nil {
err = fmt.Errorf("error from fs manager")
return return
} }
webrtcGrpcMiddleware := NewWebRTCGrpcMiddleware(webRTCCallManager) webrtcGrpcMiddleware := NewWebRTCGrpcMiddleware(webRTCCallManager)
ZoneGrpcMiddleware := NewZoneGrpcMiddleware(zoneManager) ZoneGrpcMiddleware := NewZoneGrpcMiddleware(zoneManager)
webrtcFsMiddleware := NewWebRTCFsMiddleware(webrtcFsManager) webrtcFsMiddleware := NewWebRTCFsMiddleware(webrtcFsManager)
grpcClientManager, err := NewGrpcClientManager(grpcAddr, id, webrtcGrpcMiddleware, ZoneGrpcMiddleware) signalingClientManager, err := NewGrpcClientManager(grpcAddr, id, webrtcGrpcMiddleware, ZoneGrpcMiddleware)
webrtcGrpcMiddleware.stream = grpcClientManager.GrpcLinkClient
webRTCCallManager.stream = grpcClientManager.GrpcLinkClient
zoneManager.stream = grpcClientManager.GrpcLinkClient
webrtcFsMiddleware.stream = grpcClientManager.GrpcLinkClient
webrtcFsManager.stream = grpcClientManager.GrpcLinkClient
ZoneGrpcMiddleware.stream = grpcClientManager.GrpcLinkClient
localServer = &LocalServer{
ID: id,
GrpcClientManager: grpcClientManager,
}
return
}
func NewGrpcClientManager(addr string, id string, middleware ...GrpcClientManagerMiddleware) (grpcClientManager *GrpcClientManager, err error) {
conn, grpcClient, grpcLinkClient, err := NewGrpcConn(addr, id)
if err != nil { if err != nil {
return return
} }
grpcClientManager = &GrpcClientManager{ webrtcGrpcMiddleware.stream = signalingClientManager.GrpcLinkClient
GrpcConn: conn, webRTCCallManager.stream = signalingClientManager.GrpcLinkClient
GrpcManagerClient: grpcClient, zoneManager.stream = signalingClientManager.GrpcLinkClient
GrpcLinkClient: grpcLinkClient, webrtcFsMiddleware.stream = signalingClientManager.GrpcLinkClient
middlewares: middleware, webrtcFsManager.stream = signalingClientManager.GrpcLinkClient
ZoneGrpcMiddleware.stream = signalingClientManager.GrpcLinkClient
localServer = &LocalServer{
ID: id,
GrpcClientManager: signalingClientManager,
} }
return return
} }
func NewGrpcConn(addr string, id string) (conn grpc.ClientConnInterface, grpcClient GrpcManagerClient, grpcLinkClient GrpcManager_LinkClient, err error) { func NewGrpcClientManager(addr string, id string, middleware ...SignalingClientManagerMiddleware) (signalingClientManager *SignalingClientManager, err error) {
var cert = filepath.Join("config", "cert.pem") conn, signalingClient, signalingLinkClient, err := NewGrpcConn(addr, id)
if err != nil {
return
}
signalingClientManager = &SignalingClientManager{
GrpcConn: conn,
SignalingManagerClient: signalingClient,
GrpcLinkClient: signalingLinkClient,
middlewares: middleware,
}
return
}
func NewGrpcConn(addr string, id string) (conn grpc.ClientConnInterface, signalingClient SignalingServiceClient, signalingLinkClient SignalingService_LinkClient, err error) {
var cert = filepath.Join("config", "fullchain.pem")
creds, err := credentials.NewClientTLSFromFile(cert, "dev.zippytal.com") creds, err := credentials.NewClientTLSFromFile(cert, "dev.zippytal.com")
if err != nil { if err != nil {
fmt.Println("there")
return return
} }
var opts []grpc.DialOption = []grpc.DialOption{grpc.WithTransportCredentials(creds)} var opts []grpc.DialOption = []grpc.DialOption{grpc.WithTransportCredentials(creds)}
conn, err = grpc.Dial(addr, opts...) conn, err = grpc.Dial(addr, opts...)
if err != nil { if err != nil {
fmt.Println("over there")
return return
} }
grpcClient = NewGrpcManagerClient(conn) signalingClient = NewSignalingServiceClient(conn)
grpcLinkClient, err = grpcClient.Link(context.Background()) signalingLinkClient, err = signalingClient.Link(context.Background())
if err != nil {
fmt.Println("really there")
logger.Println(err)
return
}
payload := map[string]any{
"init": "init",
}
bs, err := json.Marshal(payload)
if err != nil { if err != nil {
logger.Println(err) logger.Println(err)
return return
} }
if err = grpcLinkClient.Send(&Request{ if err = signalingLinkClient.Send(&SignalingMessage{
Type: "init", Type: "init",
From: id, From: id,
Payload: map[string]string{}, To: "server",
Token: "none", Payload: bs,
}); err != nil { }); err != nil {
return return
} }
@ -180,7 +199,7 @@ func (lsh *LocalServerHandler) ServeHTTP(w http.ResponseWriter, req *http.Reques
} }
} }
func (gcm *GrpcClientManager) Handle(ctx context.Context) (err error) { func (gcm *SignalingClientManager) Handle(ctx context.Context) (err error) {
done, errCh := make(chan struct{}), make(chan error) done, errCh := make(chan struct{}), make(chan error)
go func() { go func() {
wg := new(sync.WaitGroup) wg := new(sync.WaitGroup)
@ -192,11 +211,11 @@ func (gcm *GrpcClientManager) Handle(ctx context.Context) (err error) {
} }
for _, middleware := range gcm.middlewares { for _, middleware := range gcm.middlewares {
wg.Add(1) wg.Add(1)
go func(m GrpcClientManagerMiddleware) { go func(m SignalingClientManagerMiddleware) {
defer wg.Done()
if err := m.Process(ctx, res, gcm.GrpcLinkClient); err != nil { if err := m.Process(ctx, res, gcm.GrpcLinkClient); err != nil {
logger.Println(err) logger.Println(err)
} }
wg.Done()
}(middleware) }(middleware)
} }
wg.Wait() wg.Wait()
@ -212,8 +231,10 @@ func (gcm *GrpcClientManager) Handle(ctx context.Context) (err error) {
err = ctx.Err() err = ctx.Err()
return return
case <-done: case <-done:
logger.Println("done")
return return
case err = <-errCh: case err = <-errCh:
logger.Println("done with error")
if closeErr := gcm.GrpcLinkClient.CloseSend(); closeErr != nil { if closeErr := gcm.GrpcLinkClient.CloseSend(); closeErr != nil {
return closeErr return closeErr
} }

337
signaling.pb.go Normal file
View File

@ -0,0 +1,337 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.0
// protoc v3.14.0
// source: signaling.proto
package localserver
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type NotifyRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"`
To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"`
Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
Payload []byte `protobuf:"bytes,4,opt,name=payload,proto3" json:"payload,omitempty"`
}
func (x *NotifyRequest) Reset() {
*x = NotifyRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_signaling_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *NotifyRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NotifyRequest) ProtoMessage() {}
func (x *NotifyRequest) ProtoReflect() protoreflect.Message {
mi := &file_signaling_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use NotifyRequest.ProtoReflect.Descriptor instead.
func (*NotifyRequest) Descriptor() ([]byte, []int) {
return file_signaling_proto_rawDescGZIP(), []int{0}
}
func (x *NotifyRequest) GetFrom() string {
if x != nil {
return x.From
}
return ""
}
func (x *NotifyRequest) GetTo() string {
if x != nil {
return x.To
}
return ""
}
func (x *NotifyRequest) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *NotifyRequest) GetPayload() []byte {
if x != nil {
return x.Payload
}
return nil
}
type NotifyResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
}
func (x *NotifyResponse) Reset() {
*x = NotifyResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_signaling_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *NotifyResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NotifyResponse) ProtoMessage() {}
func (x *NotifyResponse) ProtoReflect() protoreflect.Message {
mi := &file_signaling_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use NotifyResponse.ProtoReflect.Descriptor instead.
func (*NotifyResponse) Descriptor() ([]byte, []int) {
return file_signaling_proto_rawDescGZIP(), []int{1}
}
func (x *NotifyResponse) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
type SignalingMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
From string `protobuf:"bytes,2,opt,name=from,proto3" json:"from,omitempty"`
To string `protobuf:"bytes,3,opt,name=to,proto3" json:"to,omitempty"`
Payload []byte `protobuf:"bytes,4,opt,name=payload,proto3" json:"payload,omitempty"`
}
func (x *SignalingMessage) Reset() {
*x = SignalingMessage{}
if protoimpl.UnsafeEnabled {
mi := &file_signaling_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SignalingMessage) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SignalingMessage) ProtoMessage() {}
func (x *SignalingMessage) ProtoReflect() protoreflect.Message {
mi := &file_signaling_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SignalingMessage.ProtoReflect.Descriptor instead.
func (*SignalingMessage) Descriptor() ([]byte, []int) {
return file_signaling_proto_rawDescGZIP(), []int{2}
}
func (x *SignalingMessage) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *SignalingMessage) GetFrom() string {
if x != nil {
return x.From
}
return ""
}
func (x *SignalingMessage) GetTo() string {
if x != nil {
return x.To
}
return ""
}
func (x *SignalingMessage) GetPayload() []byte {
if x != nil {
return x.Payload
}
return nil
}
var File_signaling_proto protoreflect.FileDescriptor
var file_signaling_proto_rawDesc = []byte{
0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x22, 0x61, 0x0a, 0x0d,
0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a,
0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f,
0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74,
0x6f, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22,
0x2a, 0x0a, 0x0e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01,
0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x64, 0x0a, 0x10, 0x53,
0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74,
0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f,
0x61, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61,
0x64, 0x32, 0x97, 0x01, 0x0a, 0x10, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x53,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3d, 0x0a, 0x06, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79,
0x12, 0x18, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x4e, 0x6f, 0x74,
0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x73, 0x69, 0x67,
0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x1b, 0x2e,
0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c,
0x69, 0x6e, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1b, 0x2e, 0x73, 0x69, 0x67,
0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x69, 0x6e, 0x67,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x0f, 0x5a, 0x0d, 0x2e,
0x3b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
file_signaling_proto_rawDescOnce sync.Once
file_signaling_proto_rawDescData = file_signaling_proto_rawDesc
)
func file_signaling_proto_rawDescGZIP() []byte {
file_signaling_proto_rawDescOnce.Do(func() {
file_signaling_proto_rawDescData = protoimpl.X.CompressGZIP(file_signaling_proto_rawDescData)
})
return file_signaling_proto_rawDescData
}
var file_signaling_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_signaling_proto_goTypes = []interface{}{
(*NotifyRequest)(nil), // 0: signaling.NotifyRequest
(*NotifyResponse)(nil), // 1: signaling.NotifyResponse
(*SignalingMessage)(nil), // 2: signaling.SignalingMessage
}
var file_signaling_proto_depIdxs = []int32{
0, // 0: signaling.SignalingService.Notify:input_type -> signaling.NotifyRequest
2, // 1: signaling.SignalingService.Link:input_type -> signaling.SignalingMessage
1, // 2: signaling.SignalingService.Notify:output_type -> signaling.NotifyResponse
2, // 3: signaling.SignalingService.Link:output_type -> signaling.SignalingMessage
2, // [2:4] is the sub-list for method output_type
0, // [0:2] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_signaling_proto_init() }
func file_signaling_proto_init() {
if File_signaling_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_signaling_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*NotifyRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_signaling_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*NotifyResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_signaling_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SignalingMessage); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_signaling_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_signaling_proto_goTypes,
DependencyIndexes: file_signaling_proto_depIdxs,
MessageInfos: file_signaling_proto_msgTypes,
}.Build()
File_signaling_proto = out.File
file_signaling_proto_rawDesc = nil
file_signaling_proto_goTypes = nil
file_signaling_proto_depIdxs = nil
}

174
signaling_grpc.pb.go Normal file
View File

@ -0,0 +1,174 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.14.0
// source: signaling.proto
package localserver
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// SignalingServiceClient is the client API for SignalingService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type SignalingServiceClient interface {
Notify(ctx context.Context, in *NotifyRequest, opts ...grpc.CallOption) (*NotifyResponse, error)
Link(ctx context.Context, opts ...grpc.CallOption) (SignalingService_LinkClient, error)
}
type signalingServiceClient struct {
cc grpc.ClientConnInterface
}
func NewSignalingServiceClient(cc grpc.ClientConnInterface) SignalingServiceClient {
return &signalingServiceClient{cc}
}
func (c *signalingServiceClient) Notify(ctx context.Context, in *NotifyRequest, opts ...grpc.CallOption) (*NotifyResponse, error) {
out := new(NotifyResponse)
err := c.cc.Invoke(ctx, "/signaling.SignalingService/Notify", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *signalingServiceClient) Link(ctx context.Context, opts ...grpc.CallOption) (SignalingService_LinkClient, error) {
stream, err := c.cc.NewStream(ctx, &SignalingService_ServiceDesc.Streams[0], "/signaling.SignalingService/Link", opts...)
if err != nil {
return nil, err
}
x := &signalingServiceLinkClient{stream}
return x, nil
}
type SignalingService_LinkClient interface {
Send(*SignalingMessage) error
Recv() (*SignalingMessage, error)
grpc.ClientStream
}
type signalingServiceLinkClient struct {
grpc.ClientStream
}
func (x *signalingServiceLinkClient) Send(m *SignalingMessage) error {
return x.ClientStream.SendMsg(m)
}
func (x *signalingServiceLinkClient) Recv() (*SignalingMessage, error) {
m := new(SignalingMessage)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// SignalingServiceServer is the server API for SignalingService service.
// All implementations must embed UnimplementedSignalingServiceServer
// for forward compatibility
type SignalingServiceServer interface {
Notify(context.Context, *NotifyRequest) (*NotifyResponse, error)
Link(SignalingService_LinkServer) error
mustEmbedUnimplementedSignalingServiceServer()
}
// UnimplementedSignalingServiceServer must be embedded to have forward compatible implementations.
type UnimplementedSignalingServiceServer struct {
}
func (UnimplementedSignalingServiceServer) Notify(context.Context, *NotifyRequest) (*NotifyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Notify not implemented")
}
func (UnimplementedSignalingServiceServer) Link(SignalingService_LinkServer) error {
return status.Errorf(codes.Unimplemented, "method Link not implemented")
}
func (UnimplementedSignalingServiceServer) mustEmbedUnimplementedSignalingServiceServer() {}
// UnsafeSignalingServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to SignalingServiceServer will
// result in compilation errors.
type UnsafeSignalingServiceServer interface {
mustEmbedUnimplementedSignalingServiceServer()
}
func RegisterSignalingServiceServer(s grpc.ServiceRegistrar, srv SignalingServiceServer) {
s.RegisterService(&SignalingService_ServiceDesc, srv)
}
func _SignalingService_Notify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(NotifyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SignalingServiceServer).Notify(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/signaling.SignalingService/Notify",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SignalingServiceServer).Notify(ctx, req.(*NotifyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _SignalingService_Link_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(SignalingServiceServer).Link(&signalingServiceLinkServer{stream})
}
type SignalingService_LinkServer interface {
Send(*SignalingMessage) error
Recv() (*SignalingMessage, error)
grpc.ServerStream
}
type signalingServiceLinkServer struct {
grpc.ServerStream
}
func (x *signalingServiceLinkServer) Send(m *SignalingMessage) error {
return x.ServerStream.SendMsg(m)
}
func (x *signalingServiceLinkServer) Recv() (*SignalingMessage, error) {
m := new(SignalingMessage)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// SignalingService_ServiceDesc is the grpc.ServiceDesc for SignalingService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var SignalingService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "signaling.SignalingService",
HandlerType: (*SignalingServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Notify",
Handler: _SignalingService_Notify_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Link",
Handler: _SignalingService_Link_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "signaling.proto",
}

View File

@ -36,7 +36,7 @@ type Squad struct {
} }
type WebRTCCallManager struct { type WebRTCCallManager struct {
stream GrpcManager_LinkClient stream SignalingService_LinkClient
middlewares []WebrtcCallEventManager middlewares []WebrtcCallEventManager
ID string ID string
LocalSD map[string]*webrtc.SessionDescription LocalSD map[string]*webrtc.SessionDescription
@ -65,6 +65,7 @@ type IncomingCandidate struct {
type RTCPeerConnection struct { type RTCPeerConnection struct {
*webrtc.PeerConnection *webrtc.PeerConnection
id string
makingOffer bool makingOffer bool
negotiate func(string, string) negotiate func(string, string)
makingOfferLock *sync.Mutex makingOfferLock *sync.Mutex
@ -131,10 +132,13 @@ func NewWebRTCCallManager(id string, token string, eventHandlers ...WebrtcCallEv
} }
func loadHostedSquads(token string, hostId string) (squads []*Squad, err error) { func loadHostedSquads(token string, hostId string) (squads []*Squad, err error) {
em := NewEncryptionManager()
sig := em.SignRequestHMAC(hostId)
body, err := json.Marshal(map[string]interface{}{ body, err := json.Marshal(map[string]interface{}{
"type": LIST_HOSTED_SQUADS_BY_HOST, "type": LIST_HOSTED_SQUADS_BY_HOST,
"token": token, "mac": sig,
"from": hostId, "from": hostId,
"peerType":"node",
"payload": map[string]string{ "payload": map[string]string{
"host": hostId, "host": hostId,
"lastIndex": "0", "lastIndex": "0",
@ -143,7 +147,6 @@ func loadHostedSquads(token string, hostId string) (squads []*Squad, err error)
if err != nil { if err != nil {
return return
} }
res, err := http.Post("https://app.zippytal.com/req", "application/json", bytes.NewBuffer(body)) res, err := http.Post("https://app.zippytal.com/req", "application/json", bytes.NewBuffer(body))
if err != nil { if err != nil {
logger.Println("error come from there in webrtc call manager") logger.Println("error come from there in webrtc call manager")
@ -153,7 +156,29 @@ func loadHostedSquads(token string, hostId string) (squads []*Squad, err error)
if err != nil { if err != nil {
return return
} }
err = json.Unmarshal(bs, &squads) 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 (wm *WebRTCCallManager) sendSignalingMessage(messageType, from, to string, payload map[string]interface{}) (err error) {
bs, err := json.Marshal(payload)
if err != nil {
return
}
err = wm.stream.Send(&SignalingMessage{
Type: messageType,
From: from,
To: to,
Payload: bs,
})
return return
} }
@ -179,20 +204,15 @@ func (wm *WebRTCCallManager) CreateOffer(ctx context.Context, target string, fro
negotiate: wm.negotiate, negotiate: wm.negotiate,
} }
wm.RTCPeerConnectionMapMux.Unlock() wm.RTCPeerConnectionMapMux.Unlock()
err = wm.stream.Send(&Request{ err = wm.sendSignalingMessage(string(HOSTED_SQUAD_WEBRTC_OFFER), wm.ID, target, map[string]interface{}{
Type: string(HOSTED_SQUAD_WEBRTC_OFFER), "to": target,
From: wm.ID, "from": wm.ID,
Token: "none", "sdp": rawOffer.SDP,
Payload: map[string]string{
"to": target,
"from": wm.ID,
"sdp": rawOffer.SDP,
},
}) })
return return
} }
func (wm *WebRTCCallManager) HandleOffer(ctx context.Context, req map[string]string, cb OnICECandidateFunc) (err error) { func (wm *WebRTCCallManager) HandleOffer(ctx context.Context, from, to string, req map[string]string, cb OnICECandidateFunc) (err error) {
done, errCh := make(chan struct{}), make(chan error) done, errCh := make(chan struct{}), make(chan error)
go func() { go func() {
if _, ok := wm.Squads[req[SQUAD_ID]]; !ok { if _, ok := wm.Squads[req[SQUAD_ID]]; !ok {
@ -200,13 +220,13 @@ func (wm *WebRTCCallManager) HandleOffer(ctx context.Context, req map[string]str
errCh <- err errCh <- err
return return
} }
peerConnection, err := wm.createPeerConnection(req[FROM], req[TO], req[SQUAD_ID], webrtc.SDPTypeAnswer, cb) peerConnection, err := wm.createPeerConnection(from, to, req[SQUAD_ID], webrtc.SDPTypeAnswer, cb)
if err != nil { if err != nil {
errCh <- err errCh <- err
return return
} }
wm.RTCPeerConnectionMapMux.Lock() wm.RTCPeerConnectionMapMux.Lock()
wm.RTCPeerConnections[req[FROM]] = &RTCPeerConnection{ wm.RTCPeerConnections[from] = &RTCPeerConnection{
PeerConnection: peerConnection, PeerConnection: peerConnection,
makingOffer: false, makingOffer: false,
makingOfferLock: &sync.Mutex{}, makingOfferLock: &sync.Mutex{},
@ -227,20 +247,19 @@ func (wm *WebRTCCallManager) HandleOffer(ctx context.Context, req map[string]str
return return
} }
wm.LocalSDMapMux.Lock() wm.LocalSDMapMux.Lock()
wm.LocalSD[req[FROM]] = &rawAnswer wm.LocalSD[from] = &rawAnswer
wm.LocalSDMapMux.Unlock() wm.LocalSDMapMux.Unlock()
if err = peerConnection.SetLocalDescription(rawAnswer); err != nil {
errCh <- err
return
}
wm.SquadMapMux.Lock() wm.SquadMapMux.Lock()
wm.Squads[req[SQUAD_ID]].Members = append(wm.Squads[req[SQUAD_ID]].Members, req[FROM]) wm.Squads[req[SQUAD_ID]].Members = append(wm.Squads[req[SQUAD_ID]].Members, from)
wm.SquadMapMux.Unlock() wm.SquadMapMux.Unlock()
if err = wm.stream.Send(&Request{ if err = wm.sendSignalingMessage(string(HOSTED_SQUAD_WEBRTC_ANSWER), wm.ID, from, map[string]interface{}{
Type: string(HOSTED_SQUAD_WEBRTC_ANSWER), "to": from,
From: wm.ID, "from": wm.ID,
Token: "none", "sdp": rawAnswer.SDP,
Payload: map[string]string{
"to": req[FROM],
"from": wm.ID,
"sdp": rawAnswer.SDP,
},
}); err != nil { }); err != nil {
errCh <- err errCh <- err
return return
@ -258,7 +277,7 @@ func (wm *WebRTCCallManager) HandleOffer(ctx context.Context, req map[string]str
} }
} }
func (wm *WebRTCCallManager) HandleAnswer(ctx context.Context, req map[string]string) (err error) { func (wm *WebRTCCallManager) HandleAnswer(ctx context.Context, from, to string, req map[string]string) (err error) {
wm.RTCPeerConnectionMapMux.Lock() wm.RTCPeerConnectionMapMux.Lock()
defer wm.RTCPeerConnectionMapMux.Unlock() defer wm.RTCPeerConnectionMapMux.Unlock()
defer func() { defer func() {
@ -266,11 +285,11 @@ func (wm *WebRTCCallManager) HandleAnswer(ctx context.Context, req map[string]st
logger.Printf("recover from panic in handle answer : %v\n", r) logger.Printf("recover from panic in handle answer : %v\n", r)
} }
}() }()
if _, ok := wm.RTCPeerConnections[req[FROM]]; !ok { if _, ok := wm.RTCPeerConnections[from]; !ok {
err = fmt.Errorf("no corresponding peer connection for id : %s", req[FROM]) err = fmt.Errorf("no corresponding peer connection for id : %s", from)
return return
} }
peerConnnection := wm.RTCPeerConnections[req[FROM]] peerConnnection := wm.RTCPeerConnections[from]
logger.Println("---------------------") logger.Println("---------------------")
logger.Println(req[SDP]) logger.Println(req[SDP])
logger.Println("---------------------") logger.Println("---------------------")
@ -281,31 +300,21 @@ func (wm *WebRTCCallManager) HandleAnswer(ctx context.Context, req map[string]st
logger.Println("error occured while setting remote description in handle answer") logger.Println("error occured while setting remote description in handle answer")
return return
} }
if err = wm.stream.Send(&Request{ if err = wm.sendSignalingMessage(string(HOSTED_SQUAD_WEBRTC_COUNTER_OFFER), wm.ID, from, map[string]interface{}{
Type: string(HOSTED_SQUAD_WEBRTC_COUNTER_OFFER), "from": wm.ID,
From: wm.ID, "to": from,
Token: "none",
Payload: map[string]string{
"from": wm.ID,
"to": req[FROM],
},
}); err != nil { }); err != nil {
return return
} }
wm.CandidateMux.Lock() wm.CandidateMux.Lock()
for _, candidate := range wm.PendingCandidates[req[FROM]] { for _, candidate := range wm.PendingCandidates[from] {
logger.Println("sending candidate from answer to", req[FROM]) logger.Println("sending candidate from answer to", from)
if err = wm.stream.Send(&Request{ if err = wm.sendSignalingMessage(string(HOSTED_SQUAD_WEBRTC_CANDIDATE), wm.ID, from, map[string]interface{}{
Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE), "from": wm.ID,
From: wm.ID, "to": from,
Token: "none", "candidate": candidate.ToJSON().Candidate,
Payload: map[string]string{ "sdpMid": *candidate.ToJSON().SDPMid,
"from": wm.ID, "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
"to": req[FROM],
"candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
},
}); err != nil { }); err != nil {
wm.CandidateMux.Unlock() wm.CandidateMux.Unlock()
return return
@ -313,53 +322,48 @@ func (wm *WebRTCCallManager) HandleAnswer(ctx context.Context, req map[string]st
} }
wm.CandidateMux.Unlock() wm.CandidateMux.Unlock()
wm.CandidateMux.Lock() wm.CandidateMux.Lock()
delete(wm.PendingCandidates, req[FROM]) delete(wm.PendingCandidates, from)
wm.CandidateMux.Unlock() wm.CandidateMux.Unlock()
wm.LocalSDMapMux.Lock() wm.LocalSDMapMux.Lock()
delete(wm.LocalSD, req[FROM]) delete(wm.LocalSD, from)
wm.LocalSDMapMux.Unlock() wm.LocalSDMapMux.Unlock()
return return
} }
func (wm *WebRTCCallManager) HandleCounterOffer(ctx context.Context, req map[string]string) (err error) { func (wm *WebRTCCallManager) HandleCounterOffer(ctx context.Context, from, to string, req map[string]string) (err error) {
wm.RTCPeerConnectionMapMux.Lock() wm.RTCPeerConnectionMapMux.Lock()
if _, ok := wm.RTCPeerConnections[req[FROM]]; !ok { if _, ok := wm.RTCPeerConnections[from]; !ok {
err = fmt.Errorf("no field corresponding peer connection for id %s", req[FROM]) err = fmt.Errorf("no field corresponding peer connection for id %s", from)
wm.RTCPeerConnectionMapMux.Unlock() wm.RTCPeerConnectionMapMux.Unlock()
return return
} }
logger.Println("handling counter offer") logger.Println("handling counter offer")
connection := wm.RTCPeerConnections[req[FROM]] //connection := wm.RTCPeerConnections[from]
wm.RTCPeerConnectionMapMux.Unlock() wm.RTCPeerConnectionMapMux.Unlock()
wm.LocalSDMapMux.Lock() // wm.LocalSDMapMux.Lock()
if err = connection.SetLocalDescription(*wm.LocalSD[req[FROM]]); err != nil { // if err = connection.SetLocalDescription(*wm.LocalSD[from]); err != nil {
wm.LocalSDMapMux.Unlock() // wm.LocalSDMapMux.Unlock()
return // return
} // }
wm.LocalSDMapMux.Unlock() // wm.LocalSDMapMux.Unlock()
wm.CandidateMux.Lock() wm.CandidateMux.Lock()
for _, candidate := range wm.PendingCandidates[req[FROM]] { for _, candidate := range wm.PendingCandidates[from] {
logger.Println("sending candidate to", req[FROM]) logger.Println("sending candidate to", from)
if err = wm.stream.Send(&Request{ if err = wm.sendSignalingMessage(string(HOSTED_SQUAD_WEBRTC_CANDIDATE), wm.ID, from, map[string]interface{}{
Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE), "from": wm.ID,
From: wm.ID, "to": from,
Token: "none", "candidate": candidate.ToJSON().Candidate,
Payload: map[string]string{ "sdpMid": *candidate.ToJSON().SDPMid,
"from": wm.ID, "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
"to": req[FROM],
"candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
},
}); err != nil { }); err != nil {
wm.CandidateMux.Unlock() wm.CandidateMux.Unlock()
return return
} }
} }
delete(wm.PendingCandidates, req[FROM]) delete(wm.PendingCandidates, from)
wm.CandidateMux.Unlock() wm.CandidateMux.Unlock()
wm.LocalSDMapMux.Lock() wm.LocalSDMapMux.Lock()
delete(wm.LocalSD, req[FROM]) delete(wm.LocalSD, from)
wm.LocalSDMapMux.Unlock() wm.LocalSDMapMux.Unlock()
return return
} }
@ -645,14 +649,9 @@ func (wm *WebRTCCallManager) HandleRennegotiationOffer(from string, sdp string)
if err = wm.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil { if err = wm.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
return return
} }
if err = wm.stream.Send(&Request{ if err = wm.sendSignalingMessage(string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER), wm.ID, from, map[string]interface{}{
Type: string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER), "to": from,
From: wm.ID, "sdp": localSd.SDP,
Token: "",
Payload: map[string]string{
"to": from,
"sdp": localSd.SDP,
},
}); err != nil { }); err != nil {
logger.Println(err) logger.Println(err)
return return
@ -828,14 +827,9 @@ func (wm *WebRTCCallManager) negotiate(target string, squadId string) {
logger.Println(err) logger.Println(err)
return return
} }
if err = wm.stream.Send(&Request{ if err = wm.sendSignalingMessage(string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER), wm.ID, id, map[string]interface{}{
Type: string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER), "to": id,
From: wm.ID, "sdp": localSd.SDP,
Token: "",
Payload: map[string]string{
"to": id,
"sdp": localSd.SDP,
},
}); err != nil { }); err != nil {
logger.Println(err) logger.Println(err)
return return

View File

@ -5,14 +5,13 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"strconv"
sync "sync" sync "sync"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
) )
type WebrtcFsManager struct { type WebrtcFsManager struct {
stream GrpcManager_LinkClient stream SignalingService_LinkClient
DatachannelManager DataChannelManager DatachannelManager DataChannelManager
LocalSD map[string]*webrtc.SessionDescription LocalSD map[string]*webrtc.SessionDescription
RTCPeerConnections map[string]*webrtc.PeerConnection RTCPeerConnections map[string]*webrtc.PeerConnection
@ -40,6 +39,20 @@ func NewWebrtcFsManager(dataChannelManager DataChannelManager) (webrtcFsManager
return 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) { func (wf *WebrtcFsManager) CreateOffer(ctx context.Context, target string, from string, cb OnICECandidateFunc) (err error) {
peerConnection, err := wf.createPeerConnection(target, from, webrtc.SDPTypeOffer, cb) peerConnection, err := wf.createPeerConnection(target, from, webrtc.SDPTypeOffer, cb)
if err != nil { if err != nil {
@ -56,15 +69,10 @@ func (wf *WebrtcFsManager) CreateOffer(ctx context.Context, target string, from
logger.Println("adding for target", target) logger.Println("adding for target", target)
wf.RTCPeerConnections[target] = peerConnection wf.RTCPeerConnections[target] = peerConnection
wf.RTCPeerConnectionMapMux.Unlock() wf.RTCPeerConnectionMapMux.Unlock()
err = wf.stream.Send(&Request{ err = wf.sendSignalingMessage(string(WEBRTC_OFFER_FS), "lolo_local_serv", target, map[string]any{
Type: string(WEBRTC_OFFER_FS), "to": target,
From: "lolo_local_serv", "from": "lolo_local_serv",
Token: "none", "sdp": rawOffer.SDP,
Payload: map[string]string{
"to": target,
"from": "lolo_local_serv",
"sdp": rawOffer.SDP,
},
}) })
return return
} }
@ -96,19 +104,19 @@ func (wf *WebrtcFsManager) HandleOffer(ctx context.Context, req map[string]strin
wf.LocalSDMapMux.Lock() wf.LocalSDMapMux.Lock()
wf.LocalSD[req[FROM]] = &rawAnswer wf.LocalSD[req[FROM]] = &rawAnswer
wf.LocalSDMapMux.Unlock() wf.LocalSDMapMux.Unlock()
if err = wf.stream.Send(&Request{ // if err = wf.stream.Send(&Request{
Type: string(WEBRTC_ANSWER_FS), // Type: string(WEBRTC_ANSWER_FS),
From: "lolo_local_serv", // From: "lolo_local_serv",
Token: "none", // Token: "none",
Payload: map[string]string{ // Payload: map[string]string{
"to": req[FROM], // "to": req[FROM],
"from": "lolo_local_serv", // "from": "lolo_local_serv",
"sdp": rawAnswer.SDP, // "sdp": rawAnswer.SDP,
}, // },
}); err != nil { // }); err != nil {
errCh <- err // errCh <- err
return // return
} // }
done <- struct{}{} done <- struct{}{}
}() }()
select { select {
@ -141,34 +149,34 @@ func (wf *WebrtcFsManager) HandleAnswer(ctx context.Context, req map[string]stri
}); err != nil { }); err != nil {
return return
} }
if err = wf.stream.Send(&Request{ // if err = wf.stream.Send(&Request{
Type: string(WEBRTC_COUNTER_OFFER_FS), // Type: string(WEBRTC_COUNTER_OFFER_FS),
From: "lolo_local_serv", // From: "lolo_local_serv",
Token: "none", // Token: "none",
Payload: map[string]string{ // Payload: map[string]string{
"from": "lolo_local_serv", // "from": "lolo_local_serv",
"to": req[FROM], // "to": req[FROM],
}, // },
}); err != nil { // }); err != nil {
return // return
} // }
wf.CandidateMux.RLock() wf.CandidateMux.RLock()
for _, candidate := range wf.PendingCandidates[req[FROM]] { for range wf.PendingCandidates[req[FROM]] {
logger.Println("sending candidate to", req[FROM]) logger.Println("sending candidate to", req[FROM])
if err = wf.stream.Send(&Request{ // if err = wf.stream.Send(&Request{
Type: string(WEBRTC_CANDIDATE_FS), // Type: string(WEBRTC_CANDIDATE_FS),
From: "lolo_local_serv", // From: "lolo_local_serv",
Token: "none", // Token: "none",
Payload: map[string]string{ // Payload: map[string]string{
"from": "lolo_local_serv", // "from": "lolo_local_serv",
"to": req[FROM], // "to": req[FROM],
"candidate": candidate.ToJSON().Candidate, // "candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid, // "sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
}, // },
}); err != nil { // }); err != nil {
return // return
} // }
} }
wf.CandidateMux.RUnlock() wf.CandidateMux.RUnlock()
wf.CandidateMux.Lock() wf.CandidateMux.Lock()
@ -194,22 +202,22 @@ func (wf *WebrtcFsManager) HandleCounterOffer(ctx context.Context, req map[strin
} }
wf.LocalSDMapMux.RUnlock() wf.LocalSDMapMux.RUnlock()
wf.CandidateMux.RLock() wf.CandidateMux.RLock()
for _, candidate := range wf.PendingCandidates[req[FROM]] { for range wf.PendingCandidates[req[FROM]] {
logger.Println("sending candidate to", req[FROM]) // logger.Println("sending candidate to", req[FROM])
if err = wf.stream.Send(&Request{ // if err = wf.stream.Send(&Request{
Type: string(WEBRTC_CANDIDATE_FS), // Type: string(WEBRTC_CANDIDATE_FS),
From: "lolo_local_serv", // From: "lolo_local_serv",
Token: "none", // Token: "none",
Payload: map[string]string{ // Payload: map[string]string{
"from": "lolo_local_serv", // "from": "lolo_local_serv",
"to": req[FROM], // "to": req[FROM],
"candidate": candidate.ToJSON().Candidate, // "candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid, // "sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), // "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
}, // },
}); err != nil { // }); err != nil {
return // return
} // }
} }
wf.CandidateMux.RUnlock() wf.CandidateMux.RUnlock()
return return
@ -356,7 +364,7 @@ func (wf *WebrtcFsManager) createPeerConnection(target string, from string, peer
if peerConnection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && peerConnection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer { if peerConnection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && peerConnection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
wf.RTCPeerConnectionMapMux.Lock() wf.RTCPeerConnectionMapMux.Lock()
defer wf.RTCPeerConnectionMapMux.Unlock() defer wf.RTCPeerConnectionMapMux.Unlock()
for id, connection := range wf.RTCPeerConnections { for _, connection := range wf.RTCPeerConnections {
if connection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && connection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer { if connection.SignalingState() != webrtc.SignalingStateHaveLocalOffer && connection.SignalingState() != webrtc.SignalingStateHaveRemoteOffer {
localSd, err := connection.CreateOffer(nil) localSd, err := connection.CreateOffer(nil)
if err != nil { if err != nil {
@ -367,18 +375,18 @@ func (wf *WebrtcFsManager) createPeerConnection(target string, from string, peer
logger.Println(err) logger.Println(err)
return return
} }
if err = wf.stream.Send(&Request{ // if err = wf.stream.Send(&Request{
Type: string(WEBRTC_RENNEGOTIATION_OFFER_FS), // Type: string(WEBRTC_RENNEGOTIATION_OFFER_FS),
From: "lolo_local_serv", // From: "lolo_local_serv",
Token: "", // Token: "",
Payload: map[string]string{ // Payload: map[string]string{
"to": id, // "to": id,
"sdp": localSd.SDP, // "sdp": localSd.SDP,
}, // },
}); err != nil { // }); err != nil {
logger.Println(err) // logger.Println(err)
return // return
} // }
} }
} }
} }
@ -407,18 +415,18 @@ func (wf *WebrtcFsManager) HandleRennegotiationOffer(from string, dst string, sd
if err = wf.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil { if err = wf.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
return return
} }
if err = wf.stream.Send(&Request{ // if err = wf.stream.Send(&Request{
Type: string(WEBRTC_RENNEGOTIATION_ANSWER_FS), // Type: string(WEBRTC_RENNEGOTIATION_ANSWER_FS),
From: "lolo_local_serv", // From: "lolo_local_serv",
Token: "", // Token: "",
Payload: map[string]string{ // Payload: map[string]string{
"to": from, // "to": from,
"sdp": localSd.SDP, // "sdp": localSd.SDP,
}, // },
}); err != nil { // }); err != nil {
logger.Println(err) // logger.Println(err)
return // return
} // }
return return
} }

View File

@ -2,6 +2,7 @@ package localserver
import ( import (
context "context" context "context"
"encoding/json"
"strconv" "strconv"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
@ -20,7 +21,7 @@ const (
type WebRTCFsMiddleware struct { type WebRTCFsMiddleware struct {
Manager *WebrtcFsManager Manager *WebrtcFsManager
stream GrpcManager_LinkClient stream SignalingService_LinkClient
} }
func NewWebRTCFsMiddleware(manager *WebrtcFsManager) (webrtcFsMiddleware *WebRTCFsMiddleware) { func NewWebRTCFsMiddleware(manager *WebrtcFsManager) (webrtcFsMiddleware *WebRTCFsMiddleware) {
@ -31,111 +32,119 @@ func NewWebRTCFsMiddleware(manager *WebrtcFsManager) (webrtcFsMiddleware *WebRTC
} }
func (wfm *WebRTCFsMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) { func (wfm *WebRTCFsMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) {
err = wfm.stream.Send(&Request{ bs, err := json.Marshal(map[string]string{
Type: string(WEBRTC_CANDIDATE_FS), "from": "lolo_local_serv",
From: "lolo_local_serv", "to": to,
Token: "none", "candidate": candidate.ToJSON().Candidate,
Payload: map[string]string{ "sdpMid": *candidate.ToJSON().SDPMid,
"from": "lolo_local_serv", "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
"to": to, })
"candidate": candidate.ToJSON().Candidate, if err != nil {
"sdpMid": *candidate.ToJSON().SDPMid, return
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), }
}, err = wfm.stream.Send(&SignalingMessage{
Type: string(WEBRTC_CANDIDATE_FS),
From: "lolo_local_serv",
Payload: bs,
}) })
return return
} }
func (wfm *WebRTCFsMiddleware) Process(ctx context.Context, req *Response, stream GrpcManager_LinkClient) (err error) { func (wfm *WebRTCFsMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) {
done, errCh := make(chan struct{}), make(chan error) done, errCh := make(chan struct{}), make(chan error)
go func() { go func() {
var payload map[string]string
if e := json.Unmarshal(req.Payload, &payload); err != nil {
errCh <- e
return
}
switch req.Type { switch req.Type {
case string(INCOMING_PEER_FS): case string(INCOMING_PEER_FS):
logger.Println("quit squad called") logger.Println("quit squad called")
if from, ok := req.Payload[FROM]; ok { if from, ok := payload[FROM]; ok {
logger.Println(from) logger.Println(from)
//wfm.Manager.HandleLeavingMember(from) //wfm.Manager.HandleLeavingMember(from)
done <- struct{}{} done <- struct{}{}
} }
case string(PEER_CONNECTION_REQUEST): case string(PEER_CONNECTION_REQUEST):
if err := validateRequest(req.Payload, FROM, TO); err != nil { if err := validateRequest(payload, FROM, TO); err != nil {
errCh <- err errCh <- err
return return
} }
// if err := wfm.Manager.CreateOffer(ctx, req.Payload[FROM], req.Payload[TO], wfm.signalCandidate); err != nil { // if err := wfm.Manager.CreateOffer(ctx, payload[FROM], payload[TO], wfm.signalCandidate); err != nil {
// errCh <- err // errCh <- err
// return // return
// } // }
done <- struct{}{} done <- struct{}{}
case string(WEBRTC_OFFER_FS): case string(WEBRTC_OFFER_FS):
if err := validateRequest(req.GetPayload(), FROM, TO, SDP); err != nil { if err := validateRequest(payload, FROM, TO, SDP); err != nil {
errCh <- err errCh <- err
return return
} }
if err := wfm.Manager.HandleOffer(ctx, req.GetPayload(), wfm.signalCandidate); err != nil { if err := wfm.Manager.HandleOffer(ctx, payload, wfm.signalCandidate); err != nil {
errCh <- err errCh <- err
return return
} }
done <- struct{}{} done <- struct{}{}
case string(WEBRTC_ANSWER_FS): case string(WEBRTC_ANSWER_FS):
if err := validateRequest(req.GetPayload(), FROM, TO, SDP); err != nil { if err := validateRequest(payload, FROM, TO, SDP); err != nil {
errCh <- err errCh <- err
return return
} }
if err := wfm.Manager.HandleAnswer(ctx, req.GetPayload()); err != nil { if err := wfm.Manager.HandleAnswer(ctx, payload); err != nil {
errCh <- err errCh <- err
return return
} }
done <- struct{}{} done <- struct{}{}
case string(WEBRTC_COUNTER_OFFER_FS): case string(WEBRTC_COUNTER_OFFER_FS):
if err := validateRequest(req.GetPayload(), FROM); err != nil { if err := validateRequest(payload, FROM); err != nil {
errCh <- err errCh <- err
return return
} }
if err := wfm.Manager.HandleCounterOffer(ctx, req.Payload); err != nil { if err := wfm.Manager.HandleCounterOffer(ctx, payload); err != nil {
errCh <- err errCh <- err
return return
} }
done <- struct{}{} done <- struct{}{}
case string(WEBRTC_RENNEGOTIATION_ANSWER_FS): case string(WEBRTC_RENNEGOTIATION_ANSWER_FS):
if err := validateRequest(req.GetPayload(), FROM, SDP); err != nil { if err := validateRequest(payload, FROM, SDP); err != nil {
errCh <- err errCh <- err
return return
} }
if err := wfm.Manager.HandleRennegotiationAnswer(req.Payload[FROM], "lolo_local_serv", req.Payload[SDP]); err != nil { if err := wfm.Manager.HandleRennegotiationAnswer(payload[FROM], "lolo_local_serv", payload[SDP]); err != nil {
errCh <- err errCh <- err
return return
} }
done <- struct{}{} done <- struct{}{}
case string(WEBRTC_RENNEGOTIATION_OFFER_FS): case string(WEBRTC_RENNEGOTIATION_OFFER_FS):
if err := validateRequest(req.GetPayload(), FROM, SDP); err != nil { if err := validateRequest(payload, FROM, SDP); err != nil {
errCh <- err errCh <- err
return return
} }
if err := wfm.Manager.HandleRennegotiationOffer(req.Payload[FROM], "", req.Payload[SDP]); err != nil { if err := wfm.Manager.HandleRennegotiationOffer(payload[FROM], "", payload[SDP]); err != nil {
errCh <- err errCh <- err
return return
} }
done <- struct{}{} done <- struct{}{}
case string(WEBRTC_CANDIDATE_FS): case string(WEBRTC_CANDIDATE_FS):
if err := validateRequest(req.GetPayload(), FROM, "candidate", "sdpMlineIndex", "sdpMid"); err != nil { if err := validateRequest(payload, FROM, "candidate", "sdpMLineIndex", "sdpMid"); err != nil {
errCh <- err errCh <- err
return return
} }
logger.Println(req.Payload) logger.Println(payload)
i, err := strconv.Atoi(req.Payload["sdpMlineIndex"]) i, err := strconv.Atoi(payload["sdpMLineIndex"])
if err != nil { if err != nil {
errCh <- err errCh <- err
return return
} }
sdpMlineIndex := uint16(i) SDPMLineIndex := uint16(i)
sdpMid := req.Payload["sdpMid"] sdpMid := payload["sdpMid"]
logger.Println(sdpMid, sdpMlineIndex) logger.Println(sdpMid, SDPMLineIndex)
if err := wfm.Manager.AddCandidate(&webrtc.ICECandidateInit{ if err := wfm.Manager.AddCandidate(&webrtc.ICECandidateInit{
Candidate: req.Payload["candidate"], Candidate: payload["candidate"],
SDPMid: &sdpMid, SDPMid: &sdpMid,
SDPMLineIndex: &sdpMlineIndex, SDPMLineIndex: &SDPMLineIndex,
}, req.Payload[FROM]); err != nil { }, payload[FROM]); err != nil {
errCh <- err errCh <- err
return return
} }

View File

@ -2,6 +2,7 @@ package localserver
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"strconv" "strconv"
@ -38,7 +39,7 @@ const (
type WebRTCGrpcMiddleware struct { type WebRTCGrpcMiddleware struct {
Manager *WebRTCCallManager Manager *WebRTCCallManager
stream GrpcManager_LinkClient stream SignalingService_LinkClient
} }
func NewWebRTCGrpcMiddleware(manager *WebRTCCallManager) (webrtcGrpcMiddleware *WebRTCGrpcMiddleware) { func NewWebRTCGrpcMiddleware(manager *WebRTCCallManager) (webrtcGrpcMiddleware *WebRTCGrpcMiddleware) {
@ -59,124 +60,125 @@ func validateRequest(req map[string]string, entries ...string) (err error) {
} }
func (wgm *WebRTCGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) { func (wgm *WebRTCGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) {
err = wgm.stream.Send(&Request{ bs, err := json.Marshal(map[string]string{
Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE), "from": wgm.Manager.ID,
From: wgm.Manager.ID, "to": to,
Token: "none", "candidate": candidate.ToJSON().Candidate,
Payload: map[string]string{ "sdpMid": *candidate.ToJSON().SDPMid,
"from": wgm.Manager.ID, "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
"to": to, })
"candidate": candidate.ToJSON().Candidate, if err != nil {
"sdpMid": *candidate.ToJSON().SDPMid, return
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), }
}, err = wgm.stream.Send(&SignalingMessage{
Type: string(HOSTED_SQUAD_WEBRTC_CANDIDATE),
From: wgm.Manager.ID,
To: to,
Payload: bs,
}) })
return return
} }
func (wgm *WebRTCGrpcMiddleware) Process(ctx context.Context, req *Response, stream GrpcManager_LinkClient) (err error) { func (wgm *WebRTCGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) {
done, errCh := make(chan struct{}), make(chan error) done, errCh := make(chan struct{}), make(chan error)
go func() { go func() {
defer func() { defer func() {
done <- struct{}{} done <- struct{}{}
}() }()
var payload map[string]string
if e := json.Unmarshal(req.Payload, &payload); err != nil {
errCh <- e
return
}
switch req.Type { switch req.Type {
case NEW_HOSTED_SQUAD: case NEW_HOSTED_SQUAD:
if err := validateRequest(req.Payload, "ID"); err != nil { if err := validateRequest(payload, "ID"); err != nil {
errCh <- err errCh <- err
return return
} }
logger.Println("new squad incoming") logger.Println("new squad incoming")
wgm.Manager.SquadMapMux.Lock() wgm.Manager.SquadMapMux.Lock()
wgm.Manager.Squads[req.Payload["ID"]] = &Squad{ wgm.Manager.Squads[payload["ID"]] = &Squad{
ID: req.Payload["ID"], ID: payload["ID"],
Members: []string{}, Members: []string{},
} }
wgm.Manager.SquadMapMux.Unlock() wgm.Manager.SquadMapMux.Unlock()
case string(HOSTED_SQUAD_STOP_CALL): case string(HOSTED_SQUAD_STOP_CALL):
logger.Println("quit squad called") logger.Println("quit squad called")
if err := validateRequest(req.Payload, FROM, "squadId"); err != nil { if err := validateRequest(payload, "squadId"); err != nil {
errCh <- err errCh <- err
return return
} }
wgm.Manager.HandleLeavingMember(req.Payload[FROM], req.Payload["squadId"]) wgm.Manager.HandleLeavingMember(req.GetFrom(), payload["squadId"])
done <- struct{}{} done <- struct{}{}
case string(PEER_CONNECTION_REQUEST): case string(PEER_CONNECTION_REQUEST):
if err := validateRequest(req.Payload, FROM, TO); err != nil {
errCh <- err
return
}
logger.Println("creating offer for peer") logger.Println("creating offer for peer")
if err := wgm.Manager.CreateOffer(ctx, req.Payload[FROM], req.Payload[TO], wgm.signalCandidate); err != nil { if err := wgm.Manager.CreateOffer(ctx, req.GetFrom(), req.GetTo(), wgm.signalCandidate); err != nil {
errCh <- err errCh <- err
return return
} }
case string(HOSTED_SQUAD_WEBRTC_OFFER): case string(HOSTED_SQUAD_WEBRTC_OFFER):
if err := validateRequest(req.GetPayload(), FROM, TO, SDP, SQUAD_ID); err != nil { if err := validateRequest(payload, SDP, SQUAD_ID); err != nil {
errCh <- err errCh <- err
return return
} }
if err := wgm.Manager.HandleOffer(ctx, req.GetPayload(), wgm.signalCandidate); err != nil { if err := wgm.Manager.HandleOffer(ctx, req.GetFrom(), req.GetTo(), payload, wgm.signalCandidate); err != nil {
errCh <- err errCh <- err
return return
} }
case string(HOSTED_SQUAD_WEBRTC_ANSWER): case string(HOSTED_SQUAD_WEBRTC_ANSWER):
if err := validateRequest(req.GetPayload(), FROM, TO, SDP); err != nil { if err := validateRequest(payload, SDP); err != nil {
errCh <- err errCh <- err
return return
} }
if err := wgm.Manager.HandleAnswer(ctx, req.GetPayload()); err != nil { if err := wgm.Manager.HandleAnswer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
errCh <- err errCh <- err
return return
} }
case string(HOSTED_SQUAD_WEBRTC_COUNTER_OFFER): case string(HOSTED_SQUAD_WEBRTC_COUNTER_OFFER):
if err := validateRequest(req.GetPayload(), FROM); err != nil { if err := wgm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
errCh <- err
return
}
if err := wgm.Manager.HandleCounterOffer(ctx, req.Payload); err != nil {
errCh <- err errCh <- err
return return
} }
case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER): case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER):
logger.Println("received negotiation answer") logger.Println("received negotiation answer")
if err := validateRequest(req.GetPayload(), FROM, SDP); err != nil { if err := validateRequest(payload, SDP); err != nil {
errCh <- err errCh <- err
return return
} }
if err := wgm.Manager.HandleRennegotiationAnswer(req.Payload[FROM], req.Payload[SDP]); err != nil { if err := wgm.Manager.HandleRennegotiationAnswer(req.GetFrom(), payload[SDP]); err != nil {
errCh <- err errCh <- err
return return
} }
case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER): case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER):
logger.Println("received negotiation offer") logger.Println("received negotiation offer")
if err := validateRequest(req.GetPayload(), FROM, SDP); err != nil { if err := validateRequest(payload, SDP); err != nil {
errCh <- err errCh <- err
return return
} }
if err := wgm.Manager.HandleRennegotiationOffer(req.Payload[FROM], req.Payload[SDP]); err != nil { if err := wgm.Manager.HandleRennegotiationOffer(req.GetFrom(), payload[SDP]); err != nil {
errCh <- err errCh <- err
return return
} }
case string(HOSTED_SQUAD_WEBRTC_CANDIDATE): case string(HOSTED_SQUAD_WEBRTC_CANDIDATE):
if err := validateRequest(req.GetPayload(), FROM, "candidate", "sdpMlineIndex", "sdpMid"); err != nil { if err := validateRequest(payload, "candidate", "sdpMLineIndex", "sdpMid"); err != nil {
errCh <- err errCh <- err
return return
} }
logger.Println(req.Payload) logger.Println(payload)
i, err := strconv.Atoi(req.Payload["sdpMlineIndex"]) i, err := strconv.Atoi(payload["sdpMLineIndex"])
if err != nil { if err != nil {
errCh <- err errCh <- err
return return
} }
sdpMlineIndex := uint16(i) SDPMLineIndex := uint16(i)
sdpMid := req.Payload["sdpMid"] sdpMid := payload["sdpMid"]
logger.Println(sdpMid, sdpMlineIndex) logger.Println(sdpMid, SDPMLineIndex)
if err := wgm.Manager.AddCandidate(&webrtc.ICECandidateInit{ if err := wgm.Manager.AddCandidate(&webrtc.ICECandidateInit{
Candidate: req.Payload["candidate"], Candidate: payload["candidate"],
SDPMid: &sdpMid, SDPMid: &sdpMid,
SDPMLineIndex: &sdpMlineIndex, SDPMLineIndex: &SDPMLineIndex,
}, req.Payload[FROM]); err != nil { }, req.GetFrom()); err != nil {
errCh <- err errCh <- err
return return
} }

View File

@ -129,10 +129,10 @@ func (ac *AudioChannel) HandleOffer(ctx context.Context, channelId string, userI
errCh <- err errCh <- err
return return
} }
_ = atomicallyExecute(ac.localSDMapFlag, func() (err error) { if err = peerConnection.SetLocalDescription(rawAnswer); err != nil {
ac.localSD[userId] = &rawAnswer errCh <- err
return return
}) }
_, _ = sendDCMessage(string(AUDIO_CHANNEL_WEBRTC_ANSWER), hostId, userId, map[string]interface{}{ _, _ = sendDCMessage(string(AUDIO_CHANNEL_WEBRTC_ANSWER), hostId, userId, map[string]interface{}{
"to": userId, "to": userId,
"from": ac.ID, "from": ac.ID,
@ -146,25 +146,25 @@ func (ac *AudioChannel) HandleOffer(ctx context.Context, channelId string, userI
} }
func (ac *AudioChannel) HandleCounterOffer(ctx context.Context, userId string, sendDCMessage SendDCMessageFunc) (err error) { func (ac *AudioChannel) HandleCounterOffer(ctx context.Context, userId string, sendDCMessage SendDCMessageFunc) (err error) {
if err = atomicallyExecute(ac.rtcPeerConnectionMapFlag, func() (err error) { // if err = atomicallyExecute(ac.rtcPeerConnectionMapFlag, func() (err error) {
if _, ok := ac.rtcPeerConnections[userId]; !ok { // if _, ok := ac.rtcPeerConnections[userId]; !ok {
err = fmt.Errorf("no field corresponding peer connection for id %s", userId) // err = fmt.Errorf("no field corresponding peer connection for id %s", userId)
return // return
} // }
logger.Println("handling counter offer") // logger.Println("handling counter offer")
connection := ac.rtcPeerConnections[userId] // connection := ac.rtcPeerConnections[userId]
err = atomicallyExecute(ac.localSDMapFlag, func() (err error) { // err = atomicallyExecute(ac.localSDMapFlag, func() (err error) {
err = connection.SetLocalDescription(*ac.localSD[userId]) // err = connection.SetLocalDescription(*ac.localSD[userId])
return // return
}) // })
return // return
}); err != nil { // }); err != nil {
return // return
} // }
_ = atomicallyExecute(ac.localSDMapFlag, func() (err error) { // _ = atomicallyExecute(ac.localSDMapFlag, func() (err error) {
delete(ac.localSD, userId) // delete(ac.localSD, userId)
return // return
}) // })
if err = atomicallyExecute(ac.candidateFlag, func() (err error) { if err = atomicallyExecute(ac.candidateFlag, func() (err error) {
for _, candidate := range ac.pendingCandidates[userId] { for _, candidate := range ac.pendingCandidates[userId] {
logger.Println("sending candidate to", userId, candidate) logger.Println("sending candidate to", userId, candidate)
@ -173,7 +173,7 @@ func (ac *AudioChannel) HandleCounterOffer(ctx context.Context, userId string, s
"to": userId, "to": userId,
"candidate": candidate.ToJSON().Candidate, "candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid, "sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
}) })
select { select {
case <-d: case <-d:
@ -508,39 +508,39 @@ func (ac *AudioChannel) createPeerConnection(target string, from string, peerTyp
return return
}) })
}) })
peerConnection.OnNegotiationNeeded(func() { //peerConnection.OnNegotiationNeeded(func() {
logger.Println("---------------- rennego is needed -----------") //logger.Println("---------------- rennego is needed -----------")
// _ = atomicallyExecute(ac.rtcPeerConnectionMapFlag, func() (err error) { // _ = atomicallyExecute(ac.rtcPeerConnectionMapFlag, func() (err error) {
// for _, id := range ac.CurrentMembersId { // for _, id := range ac.CurrentMembersId {
// logger.Println("----------------- sending renego to peer with id", id) // logger.Println("----------------- sending renego to peer with id", id)
// if _, ok := ac.rtcPeerConnections[id]; !ok { // if _, ok := ac.rtcPeerConnections[id]; !ok {
// continue // continue
// } // }
// if peerConnection.SignalingState() == webrtc.SignalingStateStable { // if peerConnection.SignalingState() == webrtc.SignalingStateStable {
// localSd, localSdErr := peerConnection.CreateOffer(nil) // localSd, localSdErr := peerConnection.CreateOffer(nil)
// if localSdErr != nil { // if localSdErr != nil {
// logger.Println(localSdErr) // logger.Println(localSdErr)
// return localSdErr // return localSdErr
// } // }
// if err = peerConnection.SetLocalDescription(localSd); err != nil { // if err = peerConnection.SetLocalDescription(localSd); err != nil {
// logger.Println(err) // logger.Println(err)
// return // return
// } // }
// d, e := sendDCMessage(string(AUDIO_CHANNEL_WEBRTC_RENNEGOTIATION_OFFER), ac.ID, id, map[string]interface{}{ // d, e := sendDCMessage(string(AUDIO_CHANNEL_WEBRTC_RENNEGOTIATION_OFFER), ac.ID, id, map[string]interface{}{
// "from": ac.ID, // "from": ac.ID,
// "to": id, // "to": id,
// "sdp": localSd.SDP, // "sdp": localSd.SDP,
// }) // })
// select { // select {
// case <-d: // case <-d:
// case err = <-e: // case err = <-e:
// logger.Println(err) // logger.Println(err)
// } // }
// } // }
// } // }
// return // return
// }) // })
}) //})
return return
} }

View File

@ -27,8 +27,14 @@ const (
) )
const ( const (
USER_JOINED_AUDIO_CHANNEL = "user_joined_audio_channel" USER_JOINED_AUDIO_CHANNEL = "user_joined_audio_channel"
USER_LEFT_AUDIO_CHANNEL = "user_left_audio_channel" USER_LEFT_AUDIO_CHANNEL = "user_left_audio_channel"
AUDIO_CHANNNEL_NAME_EDITED = "audio_channel_name_edited"
AUDIO_CHANNNEL_TYPE_EDITED = "audio_channel_type_edited"
AUDIO_CHANNEL_MEMBER_REMOVED = "audio_channel_member_removed"
AUDIO_CHANNEL_MEMBER_ADDED = "audio_channel_member_added"
ADDED_IN_AUDIO_CHANNEL = "added_in_audio_channel"
REMOVED_FROM_AUDIO_CHANNEL = "removed_from_audio_channel"
) )
type AudioChannelMember struct { type AudioChannelMember struct {
@ -195,7 +201,7 @@ func (zach *ZoneAudioChannelsHandler) signalCandidate(from string, to string, ca
"to": to, "to": to,
"candidate": candidate.ToJSON().Candidate, "candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid, "sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
}) })
select { select {
case <-d: case <-d:
@ -270,15 +276,15 @@ func (zach *ZoneAudioChannelsHandler) AddNewAudioChannel(channelName string, own
} }
m := make([]string, 0, len(members)) m := make([]string, 0, len(members))
for _, member := range members { for _, member := range members {
if _, ok := member.(string); ok { if mbr, ok := member.(string); ok && mbr != owner {
m = append(m, member.(string)) m = append(m, mbr)
} }
} }
baseConfig := &AudioChannelConfig{ baseConfig := &AudioChannelConfig{
ID: channelName, ID: channelName,
Owner: owner, Owner: owner,
ChannelType: channelType, ChannelType: channelType,
Members: m, Members: append(m,owner),
} }
bs, jsonErr := json.Marshal(baseConfig) bs, jsonErr := json.Marshal(baseConfig)
if jsonErr != nil { if jsonErr != nil {
@ -344,7 +350,7 @@ func (zach *ZoneAudioChannelsHandler) DeleteAudioChannel(channelId string) (err
"userId": member, "userId": member,
"channelId": channelId, "channelId": channelId,
}) })
done, e := zach.sendDataChannelMessage(REMOVED_FROM_CHAT, "node", member, map[string]interface{}{ done, e := zach.sendDataChannelMessage(REMOVED_FROM_AUDIO_CHANNEL, "node", member, map[string]interface{}{
"channelId": channelId, "channelId": channelId,
"userId": member, "userId": member,
}) })
@ -419,7 +425,7 @@ func (zach *ZoneAudioChannelsHandler) EditAudioChannelName(channelId string, new
"userId": member, "userId": member,
"channelId": channelId, "channelId": channelId,
}) })
done, e := zach.sendDataChannelMessage(CHAT_NAME_EDITED, "node", member, map[string]interface{}{ done, e := zach.sendDataChannelMessage(AUDIO_CHANNNEL_NAME_EDITED, "node", member, map[string]interface{}{
"formerAudioChannelId": channelId, "formerAudioChannelId": channelId,
"newAudioChannelId": newAudioChannelId, "newAudioChannelId": newAudioChannelId,
}) })
@ -449,7 +455,7 @@ func (zach *ZoneAudioChannelsHandler) EditAudioChannelType(channelId string, cha
err = fmt.Errorf("no coresponding audioChannel") err = fmt.Errorf("no coresponding audioChannel")
return return
} }
bs, err := os.ReadFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId)) bs, err := os.ReadFile(filepath.Join("data", "zones", zach.ZoneId, "audioChannels", channelId, "audioChannelConfig.json"))
if err != nil { if err != nil {
return return
} }
@ -483,6 +489,10 @@ func (zach *ZoneAudioChannelsHandler) EditAudioChannelType(channelId string, cha
if pubErr := zach.SetAudioChannelPublicForUser(channelId, member); pubErr != nil { if pubErr := zach.SetAudioChannelPublicForUser(channelId, member); pubErr != nil {
logger.Println(pubErr) logger.Println(pubErr)
} }
zach.sendDataChannelMessage(AUDIO_CHANNNEL_TYPE_EDITED, "node", member, map[string]interface{}{
"channelId": channelId,
"channelType": channelType,
})
} }
case PRIVATE: case PRIVATE:
var members = []string{} var members = []string{}
@ -494,6 +504,10 @@ func (zach *ZoneAudioChannelsHandler) EditAudioChannelType(channelId string, cha
if pubErr := zach.SetAudioChannelPrivateForUser(channelId, member); pubErr != nil { if pubErr := zach.SetAudioChannelPrivateForUser(channelId, member); pubErr != nil {
logger.Println(pubErr) logger.Println(pubErr)
} }
zach.sendDataChannelMessage(AUDIO_CHANNNEL_TYPE_EDITED, "node", member, map[string]interface{}{
"channelId": channelId,
"channelType": channelType,
})
} }
} }
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { _ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
@ -549,38 +563,38 @@ memberLoop:
if _, err = f.Write(bs); err != nil { if _, err = f.Write(bs); err != nil {
return err return err
} }
ac := NewAudioChannel(audioChannelConfig.ID, audioChannelConfig.Owner, audioChannelConfig.ChannelType, audioChannelConfig.Members, make([]string, 0), make(map[string]*AudioChannelMember))
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { _ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
zach.AudioChannels[channelId] = ac zach.AudioChannels[channelId].Members = audioChannelConfig.Members
return ac := zach.AudioChannels[channelId]
}) broadcastLoop:
broadcastLoop: for _, member := range ac.Members {
for _, member := range ac.Members { for _, m := range addedMembers {
for _, m := range addedMembers { if member == m {
if member == m { done, e := zach.sendDataChannelMessage(ADDED_IN_AUDIO_CHANNEL, "node", member, map[string]interface{}{
done, e := zach.sendDataChannelMessage(ADDED_IN_CHAT, "node", member, map[string]interface{}{ "audioChannel": ac,
"audioChannel": ac, })
}) select {
select { case <-done:
case <-done: case err = <-e:
case err = <-e: logger.Println(err)
logger.Println(err) }
continue broadcastLoop
} }
continue broadcastLoop if _, ok := zach.DataChannels[member]; ok {
} done, e := zach.sendDataChannelMessage(AUDIO_CHANNEL_MEMBER_ADDED, "node", member, map[string]interface{}{
if _, ok := zach.DataChannels[member]; ok { "userId": m,
done, e := zach.sendDataChannelMessage(CHAT_MEMBER_ADDED, "node", member, map[string]interface{}{ "channelId": ac.ID,
"userId": m, })
"channelId": ac.ID, select {
}) case <-done:
select { case err = <-e:
case <-done: logger.Println(err)
case err = <-e: }
logger.Println(err)
} }
} }
} }
} return
})
return return
} }
@ -633,17 +647,31 @@ func (zach *ZoneAudioChannelsHandler) RemoveAudioChannelMember(channelId string,
if _, err = f.Write(bs); err != nil { if _, err = f.Write(bs); err != nil {
return return
} }
var ac *AudioChannel
_ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { _ = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
ac := NewAudioChannel(audioChannelConfig.ID, audioChannelConfig.Owner, audioChannelConfig.ChannelType, audioChannelConfig.Members, make([]string, 0), make(map[string]*AudioChannelMember)) zach.AudioChannels[channelId].Members = audioChannelConfig.Members
zach.AudioChannels[channelId] = ac ac := zach.AudioChannels[channelId]
broadcastLoop:
for _, member := range ac.Members {
if member == channelMember {
continue broadcastLoop
}
done, e := zach.sendDataChannelMessage(AUDIO_CHANNEL_MEMBER_REMOVED, "node", member, map[string]interface{}{
"userId": channelMember,
"channelId": ac.ID,
})
select {
case <-done:
case err = <-e:
logger.Println(err)
}
}
return return
}) })
zach.sendZoneRequest(REMOVE_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{ zach.sendZoneRequest(REMOVE_KNOWN_AUDIO_CHANNEL, "node", map[string]interface{}{
"userId": channelMember, "userId": channelMember,
"channelId": channelId, "channelId": channelId,
}) })
done, e := zach.sendDataChannelMessage(REMOVED_FROM_CHAT, "node", channelMember, map[string]interface{}{ done, e := zach.sendDataChannelMessage(REMOVED_FROM_AUDIO_CHANNEL, "node", channelMember, map[string]interface{}{
"channelId": channelId, "channelId": channelId,
"userId": channelMember, "userId": channelMember,
}) })
@ -652,21 +680,6 @@ func (zach *ZoneAudioChannelsHandler) RemoveAudioChannelMember(channelId string,
case err = <-e: case err = <-e:
logger.Println(err) logger.Println(err)
} }
broadcastLoop:
for _, member := range ac.Members {
if member == channelMember {
continue broadcastLoop
}
done, e := zach.sendDataChannelMessage(CHAT_MEMBER_REMOVED, "node", member, map[string]interface{}{
"userId": channelMember,
"channelId": ac.ID,
})
select {
case <-done:
case err = <-e:
logger.Println(err)
}
}
return return
} }
@ -685,7 +698,7 @@ func (zach *ZoneAudioChannelsHandler) SetAudioChannelPrivateForUser(channelId st
"userId": member, "userId": member,
"channelId": channelId, "channelId": channelId,
}) })
done, e := zach.sendDataChannelMessage(REMOVED_FROM_CHAT, "node", member, map[string]interface{}{ done, e := zach.sendDataChannelMessage(REMOVED_FROM_AUDIO_CHANNEL, "node", member, map[string]interface{}{
"channelId": channelId, "channelId": channelId,
"userId": member, "userId": member,
}) })
@ -715,7 +728,7 @@ func (zach *ZoneAudioChannelsHandler) SetAudioChannelPublicForUser(channelId str
"userId": member, "userId": member,
"channelId": channelId, "channelId": channelId,
}) })
done, e := zach.sendDataChannelMessage(ADDED_IN_CHAT, "node", member, map[string]interface{}{ done, e := zach.sendDataChannelMessage(ADDED_IN_AUDIO_CHANNEL, "node", member, map[string]interface{}{
"audioChannel": audioChannel, "audioChannel": audioChannel,
}) })
select { select {
@ -788,7 +801,7 @@ func (zach *ZoneAudioChannelsHandler) JoinAudioChannel(channelId string, userId
audioChannel.CurrentMembersId = append(audioChannel.CurrentMembersId, userId) audioChannel.CurrentMembersId = append(audioChannel.CurrentMembersId, userId)
audioChannel.CurrentMembers[userId] = &AudioChannelMember{} audioChannel.CurrentMembers[userId] = &AudioChannelMember{}
signalMembers := func(members []string) { signalMembers := func(members []string) {
for _, u := range audioChannel.Members { for _, u := range members {
done, e := zach.sendDataChannelMessage(USER_JOINED_AUDIO_CHANNEL, "node", u, map[string]interface{}{ done, e := zach.sendDataChannelMessage(USER_JOINED_AUDIO_CHANNEL, "node", u, map[string]interface{}{
"userId": userId, "userId": userId,
"channelId": channelId, "channelId": channelId,
@ -842,7 +855,7 @@ func (zach *ZoneAudioChannelsHandler) LeaveAudioChannel(channelId string, userId
} }
delete(audioChannel.CurrentMembers, userId) delete(audioChannel.CurrentMembers, userId)
signalMembers := func(members []string) { signalMembers := func(members []string) {
for _, u := range audioChannel.Members { for _, u := range members {
done, e := zach.sendDataChannelMessage(USER_LEFT_AUDIO_CHANNEL, "node", u, map[string]interface{}{ done, e := zach.sendDataChannelMessage(USER_LEFT_AUDIO_CHANNEL, "node", u, map[string]interface{}{
"userId": userId, "userId": userId,
"channelId": channelId, "channelId": channelId,
@ -1008,17 +1021,17 @@ func (zach *ZoneAudioChannelsHandler) handleZoneRequest(ctx context.Context, req
case string(AUDIO_CHANNEL_WEBRTC_CANDIDATE): case string(AUDIO_CHANNEL_WEBRTC_CANDIDATE):
logger.Println("handling audio channel webrtc candidate") logger.Println("handling audio channel webrtc candidate")
logger.Println(req.Payload) logger.Println(req.Payload)
if err = verifyFieldsString(req.Payload, FROM, "candidate", "sdpMlineIndex", "sdpMid", "channelId", "userId"); err != nil { if err = verifyFieldsString(req.Payload, FROM, "candidate", "sdpMLineIndex", "sdpMid", "channelId", "userId"); err != nil {
return return
} }
logger.Println(req.Payload) logger.Println(req.Payload)
i, convErr := strconv.Atoi(req.Payload["sdpMlineIndex"].(string)) i, convErr := strconv.Atoi(req.Payload["sdpMLineIndex"].(string))
if convErr != nil { if convErr != nil {
return convErr return convErr
} }
sdpMlineIndex := uint16(i) SDPMLineIndex := uint16(i)
sdpMid := req.Payload["sdpMid"].(string) sdpMid := req.Payload["sdpMid"].(string)
logger.Println(sdpMid, sdpMlineIndex) logger.Println(sdpMid, SDPMLineIndex)
err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) { err = atomicallyExecute(zach.AudioChannelsFlag, func() (err error) {
if _, ok := zach.AudioChannels[req.Payload["channelId"].(string)]; !ok { if _, ok := zach.AudioChannels[req.Payload["channelId"].(string)]; !ok {
err = fmt.Errorf("no channel corresponging the one requested") err = fmt.Errorf("no channel corresponging the one requested")
@ -1027,7 +1040,7 @@ func (zach *ZoneAudioChannelsHandler) handleZoneRequest(ctx context.Context, req
err = zach.AudioChannels[req.Payload["channelId"].(string)].AddCandidate(&webrtc.ICECandidateInit{ err = zach.AudioChannels[req.Payload["channelId"].(string)].AddCandidate(&webrtc.ICECandidateInit{
Candidate: req.Payload["candidate"].(string), Candidate: req.Payload["candidate"].(string),
SDPMid: &sdpMid, SDPMid: &sdpMid,
SDPMLineIndex: &sdpMlineIndex, SDPMLineIndex: &SDPMLineIndex,
}, req.Payload["userId"].(string)) }, req.Payload["userId"].(string))
return return
}) })

707
zoneChatFileInstance.go Normal file
View File

@ -0,0 +1,707 @@
package localserver
import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
sync "sync"
"time"
"github.com/pion/webrtc/v3"
)
const (
ZONE_CHAT_WEBRTC_OFFER ReqType = "zone_chat_offer"
ZONE_CHAT_WEBRTC_ANSWER ReqType = "zone_chat_answer"
ZONE_CHAT_WEBRTC_RENNEGOTIATION_OFFER ReqType = "zone_chat_rennegotiation_offer"
ZONE_CHAT_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "zone_chat_rennegotiation_answer"
ZONE_CHAT_WEBRTC_COUNTER_OFFER ReqType = "zone_chat_webrtc_counter_offer"
ZONE_CHAT_WEBRTC_CANDIDATE ReqType = "zone_chat_webrtc_candidate"
)
type ChatFSInstance struct {
ZoneID string `json:"id"`
Owner string `json:"owner"`
Members []string `json:"members"`
OpenFiles map[string]*os.File `json:"-"`
OpenFilesForUser map[string][]string `json:"-"`
localSD map[string]*webrtc.SessionDescription `json:"-"`
rtcPeerConnections map[string]*ZoneRTCPeerConnection `json:"-"`
zoneFSDataChannels map[string]map[string]*DataChannel `json:"-"`
pendingCandidates map[string][]*webrtc.ICECandidate `json:"-"`
middlewares []interface{} `json:"-"`
candidateFlag *uint32 `json:"-"`
filesFlag *uint32 `json:"-"`
rtcPeerConnectionMapFlag *uint32 `json:"-"`
dataChannelMapFlag *uint32 `json:"-"`
localSDMapFlag *uint32 `json:"-"`
}
type ChatFSInstanceOnICECandidateFunc = func(string, string, *webrtc.ICECandidate) error
func NewChatFSInstance(id, owner string, members []string) (audioChannel *ChatFSInstance) {
candidateFlag := uint32(0)
rtcPeerConnectionMapFlag := uint32(0)
dataChannelMapFlag := uint32(0)
filesFlag := uint32(0)
localSDMapFlag := uint32(0)
audioChannel = &ChatFSInstance{
ZoneID: id,
Owner: owner,
Members: members,
OpenFiles: make(map[string]*os.File),
OpenFilesForUser: make(map[string][]string),
localSD: make(map[string]*webrtc.SessionDescription),
rtcPeerConnections: make(map[string]*ZoneRTCPeerConnection),
zoneFSDataChannels: make(map[string]map[string]*DataChannel),
pendingCandidates: make(map[string][]*webrtc.ICECandidate),
middlewares: make([]interface{}, 0),
candidateFlag: &candidateFlag,
filesFlag: &filesFlag,
rtcPeerConnectionMapFlag: &rtcPeerConnectionMapFlag,
dataChannelMapFlag: &dataChannelMapFlag,
localSDMapFlag: &localSDMapFlag,
}
return
}
func (fs *ChatFSInstance) SetupFileUpload(chatId, filename, userId string) (err error) {
concretePath := filepath.Join("data", "zones", fs.ZoneID, "chats", chatId, "__files__", filename)
if _, rErr := os.ReadDir(filepath.Join("data", "zones", fs.ZoneID, "chats", chatId, "__files__")); os.IsNotExist(rErr) {
if err = os.MkdirAll(filepath.Join("data", "zones", fs.ZoneID, "chats", chatId, "__files__"), 0700); err != nil {
return
}
} else if rErr != nil {
return 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
}
_ = atomicallyExecute(fs.filesFlag, func() (err error) {
fs.OpenFiles[fmt.Sprintf("%s/%s", filename, userId)] = file
if _, ok := fs.OpenFilesForUser[userId]; !ok {
fs.OpenFilesForUser[userId] = []string{}
}
fs.OpenFilesForUser[userId] = append(fs.OpenFilesForUser[userId], fmt.Sprintf("%s/%s", filename, userId))
return
})
var dc *webrtc.DataChannel
if err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
if pc, ok := fs.rtcPeerConnections[userId]; ok {
maxRetransmits := uint16(100)
dc, err = pc.CreateDataChannel(filename, &webrtc.DataChannelInit{
MaxRetransmits: &maxRetransmits,
})
if err != nil {
return
}
} else {
err = fmt.Errorf("no peerconnection for id %s", userId)
}
return
}); err != nil {
return
}
go func() {
dc.SetBufferedAmountLowThreshold(15000000)
dc.OnMessage(func(msg webrtc.DataChannelMessage) {
logger.Println(msg.Data)
_, _ = file.Write(msg.Data)
})
dc.OnOpen(func() {
logger.Println("updated")
err = dc.SendText("yooooo man this channel is open you should be able to send stuff")
logger.Println("!-----------------------------!")
logger.Printf("datachannel with id %s is now open\n", dc.Label())
logger.Println(*dc.ID())
logger.Println(err)
logger.Println("!-----------------------------!")
})
dc.OnClose(func() {
logger.Println("channel closed")
_ = atomicallyExecute(fs.filesFlag, func() (err error) {
if f, ok := fs.OpenFiles[fmt.Sprintf("%s/%s", filename, userId)]; ok {
err = f.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
})
_ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
delete(fs.zoneFSDataChannels[userId], fmt.Sprintf("%s/%s", filename, userId))
return
})
})
}()
err = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[userId]; !ok {
fs.zoneFSDataChannels[userId] = make(map[string]*DataChannel)
}
l := int32(0)
fs.zoneFSDataChannels[userId][dc.Label()] = &DataChannel{
DataChannel: dc,
l: &l,
}
return
})
return
}
func (fs *ChatFSInstance) FileUploadDone(chatId, filename, userId string) (err error) {
err = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[userId]; ok {
if dc, ok := fs.zoneFSDataChannels[userId][filename]; ok {
err = dc.DataChannel.Close()
return
}
}
return
})
return
}
func (fs *ChatFSInstance) FileUploadFailed(chatId, filename, userId string) (err error) {
concretePath := filepath.Join("data", "zones", fs.ZoneID, "chats", chatId, "__files__", filename)
if err = os.Remove(concretePath); err != nil {
return
}
err = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[userId]; ok {
if dc, ok := fs.zoneFSDataChannels[userId][filename]; ok {
err = dc.DataChannel.Close()
return
}
}
return
})
return
}
func (fs *ChatFSInstance) SetupFileDownload(chatId, filename, userId string) (err error) {
concretePath := filepath.Join("data", "zones", fs.ZoneID, "chats", chatId, "__files__", filename)
file, err := os.OpenFile(concretePath, os.O_RDONLY, 0755)
if err != nil {
return
}
_ = atomicallyExecute(fs.filesFlag, func() (err error) {
fs.OpenFiles[fmt.Sprintf("%s/%s", filename, userId)] = file
if _, ok := fs.OpenFilesForUser[userId]; !ok {
fs.OpenFilesForUser[userId] = []string{}
}
fs.OpenFilesForUser[userId] = append(fs.OpenFilesForUser[userId], fmt.Sprintf("%s/%s", filename, userId))
return
})
var dc *webrtc.DataChannel
if err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
if pc, ok := fs.rtcPeerConnections[userId]; ok {
maxRetransmits := uint16(100)
dc, err = pc.CreateDataChannel(filename, &webrtc.DataChannelInit{
MaxRetransmits: &maxRetransmits,
})
if err != nil {
return
}
} else {
err = fmt.Errorf("no peerconnection for id %s", userId)
}
return
}); err != nil {
return
}
if dc != nil {
dc.SetBufferedAmountLowThreshold(15000000)
bufferedAmountLock := make(chan struct{})
dc.OnOpen(func() {
go func() {
defer func() {
close(bufferedAmountLock)
bufferedAmountLock = nil
_ = atomicallyExecute(fs.filesFlag, func() (err error) {
if f, ok := fs.OpenFiles[fmt.Sprintf("%s/%s", filename, userId)]; ok {
err = f.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
})
}()
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")
<-time.After(time.Second * 4)
if err = dc.SendText("done"); err != nil {
logger.Println(err)
}
dc.Close()
_ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[userId]; ok {
delete(fs.zoneFSDataChannels[userId], dc.Label())
}
return
})
logger.Println("done 2")
}()
})
dc.OnBufferedAmountLow(func() {
if bufferedAmountLock != nil {
bufferedAmountLock <- struct{}{}
}
})
dc.OnClose(func() {
_ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[userId]; ok {
delete(fs.zoneFSDataChannels[userId], dc.Label())
//fs.HandleLeavingMember(target)
}
return
})
})
err = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[userId]; !ok {
fs.zoneFSDataChannels[userId] = make(map[string]*DataChannel)
}
l := int32(0)
fs.zoneFSDataChannels[userId][dc.Label()] = &DataChannel{
DataChannel: dc,
l: &l,
}
return
})
} 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 *ChatFSInstance) HandleOffer(ctx context.Context, channelId, userId, sdp, hostId string, sendDCMessage SendDCMessageFunc, cb ChatFSInstanceOnICECandidateFunc) (done chan struct{}, errCh chan error) {
done, errCh = make(chan struct{}), make(chan error)
go func() {
peerConnection, err := fs.createPeerConnection(userId, fs.ZoneID, webrtc.SDPTypeAnswer, cb, sendDCMessage)
if err != nil {
errCh <- err
return
}
_ = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
fs.rtcPeerConnections[userId] = &ZoneRTCPeerConnection{
PeerConnection: peerConnection,
makingOffer: false,
makingOfferLock: &sync.Mutex{},
}
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(ZONE_CHAT_WEBRTC_ANSWER), hostId, userId, map[string]interface{}{
"to": userId,
"from": fs.ZoneID,
"channelId": channelId,
"sdp": rawAnswer.SDP,
})
done <- struct{}{}
logger.Println("handle offer done")
}()
return
}
func (fs *ChatFSInstance) HandleCounterOffer(ctx context.Context, userId string, sendDCMessage SendDCMessageFunc) (err error) {
// if err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
// if _, ok := fs.rtcPeerConnections[userId]; !ok {
// err = fmt.Errorf("no field corresponding peer connection for id %s", userId)
// return
// }
// logger.Println("handling counter offer")
// connection := fs.rtcPeerConnections[userId]
// err = atomicallyExecute(fs.localSDMapFlag, func() (err error) {
// err = connection.SetLocalDescription(*fs.localSD[userId])
// return
// })
// return
// }); err != nil {
// return
// }
// _ = atomicallyExecute(fs.localSDMapFlag, func() (err error) {
// delete(fs.localSD, userId)
// return
// })
if err = atomicallyExecute(fs.candidateFlag, func() (err error) {
for _, candidate := range fs.pendingCandidates[userId] {
logger.Println("sending candidate to", userId, candidate)
d, e := sendDCMessage(string(ZONE_CHAT_WEBRTC_CANDIDATE), "", userId, map[string]interface{}{
"from": fs.ZoneID,
"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(fs.pendingCandidates, userId)
return
}); err != nil {
return
}
return
}
func (fs *ChatFSInstance) HandleRennegotiationOffer(from, sdp string, sendDCMessage SendDCMessageFunc) (err error) {
err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
if _, ok := fs.rtcPeerConnections[from]; !ok {
err = fmt.Errorf("no corresponding peer connection for id %s", from)
return
}
//fs.rtcPeerConnections[from].makingOfferLock.Lock()
if fs.rtcPeerConnections[from].makingOffer {
//fs.rtcPeerConnections[from].makingOfferLock.Unlock()
return fmt.Errorf("already making an offer or state is stable")
}
//fs.rtcPeerConnections[from].makingOfferLock.Unlock()
if err = fs.rtcPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
return
}
localSd, err := fs.rtcPeerConnections[from].CreateAnswer(nil)
if err != nil {
return
}
if err = fs.rtcPeerConnections[from].SetLocalDescription(localSd); err != nil {
return
}
d, e := sendDCMessage(string(ZONE_CHAT_WEBRTC_RENNEGOTIATION_ANSWER), fs.ZoneID, from, map[string]interface{}{
"from": fs.ZoneID,
"to": from,
"sdp": localSd.SDP,
})
select {
case <-d:
case err = <-e:
}
return
})
return
}
func (fs *ChatFSInstance) HandleRennegotiationAnswer(from, sdp string) (err error) {
logger.Println("---------------------handling rennego answer")
err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
err = fs.rtcPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeAnswer})
return
})
return
}
func (fs *ChatFSInstance) AddCandidate(candidate *webrtc.ICECandidateInit, from string) (err error) {
logger.Println("adding ice candidate", candidate)
err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
if _, ok := fs.rtcPeerConnections[from]; ok && candidate != nil {
err = fs.rtcPeerConnections[from].AddICECandidate(*candidate)
}
return
})
return
}
func (fs *ChatFSInstance) createPeerConnection(target, from string, peerType webrtc.SDPType, cb ChatFSInstanceOnICECandidateFunc, 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:stunserver.org:3478"},
},
},
}
peerConnection, err = webrtc.NewPeerConnection(config)
if err != nil {
return
}
logger.Println("---------------------------------------------------")
if peerType == webrtc.SDPTypeAnswer {
maxRetransmits := uint16(1)
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) {
var event CallEvent
if err := json.Unmarshal(msg.Data, &event); err != nil {
logger.Println(err)
return
}
if e := fs.HandleDataChannelEvents(event.From, event.EventId, event.Payload); e != nil {
logger.Println("*-------------- datachannel error: ", e)
}
})
logger.Println("new channel for target : ", target)
channel.SetBufferedAmountLowThreshold(bufferedAmountLowThreshold)
channel.OnBufferedAmountLow(func() {
})
channel.OnClose(func() {
})
_ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
logger.Println(target)
l := int32(0)
if _, ok := fs.zoneFSDataChannels[target]; !ok {
fs.zoneFSDataChannels[target] = make(map[string]*DataChannel)
}
fs.zoneFSDataChannels[target][channel.Label()] = &DataChannel{
DataChannel: channel,
l: &l,
}
return
})
} else {
peerConnection.OnDataChannel(func(dc *webrtc.DataChannel) {
_ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
l := int32(0)
if _, ok := fs.zoneFSDataChannels[target]; !ok {
fs.zoneFSDataChannels[target] = make(map[string]*DataChannel)
}
fs.zoneFSDataChannels[target][dc.Label()] = &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 := fs.HandleDataChannelEvents(event.From, event.EventId, event.Payload); e != nil {
logger.Println("*-------------- datachannel error: ", e)
}
})
dc.OnClose(func() {
_ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[target]; ok {
delete(fs.zoneFSDataChannels[target], dc.Label())
fs.HandleLeavingMember(target)
}
return
})
})
_ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
logger.Println(target)
l := int32(0)
if _, ok := fs.zoneFSDataChannels[target]; !ok {
fs.zoneFSDataChannels[target] = make(map[string]*DataChannel)
}
fs.zoneFSDataChannels[target][dc.Label()] = &DataChannel{
DataChannel: dc,
l: &l,
}
return
})
})
}
peerConnection.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) {
if pcs == webrtc.PeerConnectionStateClosed || pcs == webrtc.PeerConnectionStateDisconnected || pcs == webrtc.PeerConnectionStateFailed {
logger.Println(pcs)
fs.HandleLeavingMember(target)
}
})
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)
fs.HandleLeavingMember(target)
}
})
peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
if i == nil {
return
}
_ = atomicallyExecute(fs.candidateFlag, func() (err error) {
desc := peerConnection.RemoteDescription()
if desc == nil {
logger.Println("generated candidate appended to list : ", i)
fs.pendingCandidates[target] = append(fs.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(fs.rtcPeerConnectionMapFlag, func() (err error) {
// logger.Println("----------------- sending renego to peer with id", target)
// 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(ZONE_CHAT_WEBRTC_RENNEGOTIATION_OFFER), fs.ZoneID, target, map[string]interface{}{
// "from": fs.ZoneID,
// "to": target,
// "sdp": localSd.SDP,
// })
// select {
// case <-d:
// case err = <-e:
// logger.Println(err)
// }
// }
// return
// })
// })
return
}
func (fs *ChatFSInstance) HandleLeavingMember(id string) {
_ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[id]; ok {
for _, dc := range fs.zoneFSDataChannels[id] {
dc.DataChannel.Close()
}
}
delete(fs.zoneFSDataChannels, id)
return
})
logger.Println("chatfs datachannels cleaned")
_ = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
if pc, ok := fs.rtcPeerConnections[id]; ok {
if closeErr := pc.Close(); closeErr != nil {
err = closeErr
logger.Println("peer connection close error", closeErr)
}
}
delete(fs.rtcPeerConnections, id)
return
})
logger.Println("chats perrconnections cleaned")
_ = atomicallyExecute(fs.candidateFlag, func() (err error) {
delete(fs.pendingCandidates, id)
return
})
_ = atomicallyExecute(fs.filesFlag, func() (err error) {
for _, openFile := range fs.OpenFilesForUser[id] {
if f, ok := fs.OpenFiles[openFile]; ok {
_ = f.Close()
}
delete(fs.OpenFiles, openFile)
}
delete(fs.OpenFilesForUser, id)
return
})
}
func (fs *ChatFSInstance) HandleDataChannelEvents(from, eventId string, payload map[string]interface{}) (err error) {
switch eventId {
}
return
}

150
zoneChatTracking.go Normal file
View File

@ -0,0 +1,150 @@
package localserver
import (
"encoding/binary"
"os"
"path/filepath"
"sync"
"github.com/dgraph-io/badger/v3"
)
type ZoneChatTrackingDB struct {
db func(cb func(*badger.DB) (err error)) (err error)
lock *sync.RWMutex
}
func NewZoneChatTracking(zoneId,chatId string,members ...string) (*ZoneChatTrackingDB,error) {
if _,dirErr := os.ReadDir(filepath.Join("data", "zones", zoneId, "chats", chatId,"__tracking__")); os.IsNotExist(dirErr) {
_ = os.MkdirAll(filepath.Join("data", "zones", zoneId, "chats", chatId,"__tracking__"),0700)
}
db := func(f func(*badger.DB) (err error)) (err error) {
db, err := badger.Open(badger.DefaultOptions(filepath.Join("data", "zones", zoneId, "chats", chatId,"__tracking__")).WithLogger(dbLogger))
if err != nil {
return
}
defer db.Close()
err = f(db)
return
}
lock := new(sync.RWMutex)
if err := db(func(d *badger.DB) (err error) {
err = d.Update(func(txn *badger.Txn) error {
b := make([]byte,bufferSize)
binary.BigEndian.PutUint64(b,0)
for _, member := range 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 &ZoneChatTrackingDB{db,lock},nil
}
func (zctdb *ZoneChatTrackingDB) Initialize(lastIndex uint64,users ...string) (err error) {
for _, user := range users {
if err = zctdb.SetUserLastIndex(user,lastIndex);err != nil {
return
}
}
return
}
func (zctdb *ZoneChatTrackingDB) 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 *ZoneChatTrackingDB) 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 *ZoneChatTrackingDB) 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 *ZoneChatTrackingDB) 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
}

View File

@ -3,22 +3,26 @@ package localserver
import ( import (
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"os"
"path/filepath" "path/filepath"
"sync"
"github.com/dgraph-io/badger/v3" "github.com/dgraph-io/badger/v3"
) )
type ChatFile struct { type ChatFile struct {
Path string `json:"path"` ChatId string `json:"chatId"`
Owner string `json:"owner"`
Name string `json:"name"` Name string `json:"name"`
Size int `json:"size"` Size int `json:"size"`
UploadTime string `json:"uploadTime` UploadTime string `json:"uploadTime"`
} }
type ChatMessage struct { type ChatMessage struct {
ID uint64 `json:"id"` ID uint64 `json:"id"`
From string `json:"from"` From string `json:"from"`
ResponseOf *ChatMessage `json:"ResponseOf"` IsResponse bool `json:"isResponse"`
ResponseOf *ChatMessage `json:"responseOf"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
Content string `json:"content"` Content string `json:"content"`
Date string `json:"date"` Date string `json:"date"`
@ -29,9 +33,13 @@ type ZoneChatDBHandler struct {
ChatID string ChatID string
ZoneID string ZoneID string
PreviousId uint64 PreviousId uint64
ItemCount uint64
db func(func(*badger.DB) (err error)) (err error) db func(func(*badger.DB) (err error)) (err error)
lock *sync.RWMutex
} }
const bufferSize = 8
func NewZoneChatDBHandler(zoneId string, chatID string) (zoneChatDBHandler *ZoneChatDBHandler, err error) { func NewZoneChatDBHandler(zoneId string, chatID string) (zoneChatDBHandler *ZoneChatDBHandler, err error) {
zoneChatDBHandler = &ZoneChatDBHandler{ zoneChatDBHandler = &ZoneChatDBHandler{
db: func(f func(*badger.DB) (err error)) (err error) { db: func(f func(*badger.DB) (err error)) (err error) {
@ -45,18 +53,95 @@ func NewZoneChatDBHandler(zoneId string, chatID string) (zoneChatDBHandler *Zone
}, },
ChatID: chatID, ChatID: chatID,
ZoneID: zoneId, ZoneID: zoneId,
lock: new(sync.RWMutex),
} }
err = zoneChatDBHandler.db(func(d *badger.DB) (err error) { err = zoneChatDBHandler.db(func(d *badger.DB) (err error) {
err = d.View(func(txn *badger.Txn) error { err = d.View(func(txn *badger.Txn) error {
opt := badger.DefaultIteratorOptions opt := badger.DefaultIteratorOptions
it := txn.NewIterator(opt) it := txn.NewIterator(opt)
defer it.Close() defer it.Close()
counter := 0 var counter uint64 = 1
var itemCounter uint64 = 0
for it.Rewind(); it.Valid(); it.Next() { for it.Rewind(); it.Valid(); it.Next() {
counter++ 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
}
}
zoneChatDBHandler.PreviousId = counter
zoneChatDBHandler.ItemCount = itemCounter
return nil
})
return
})
return
}
func (zcdbh *ZoneChatDBHandler) 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)
for it.Seek(b); 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
}
count++
return
}); err != nil {
return err
}
}
if count > 0 {
count--
}
return nil
})
return
})
return
}
func (zcdbh *ZoneChatDBHandler) revertPreviousId() (err error) {
zcdbh.lock.Lock()
defer zcdbh.lock.Unlock()
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
} }
logger.Println(counter)
zoneChatDBHandler.PreviousId = uint64(counter)
return nil return nil
}) })
return return
@ -77,10 +162,12 @@ func (zcdbh *ZoneChatDBHandler) updateDbCallbackFolder(newChatId string) {
} }
func (zcdbh *ZoneChatDBHandler) AddNewChatMessage(chatMessage *ChatMessage) (err error) { func (zcdbh *ZoneChatDBHandler) AddNewChatMessage(chatMessage *ChatMessage) (err error) {
b := make([]byte, 100) zcdbh.lock.Lock()
binary.LittleEndian.PutUint64(b, zcdbh.PreviousId+1) defer zcdbh.lock.Unlock()
chatMessage.ID = zcdbh.PreviousId b := make([]byte, bufferSize)
zcdbh.PreviousId++ zcdbh.PreviousId++
binary.BigEndian.PutUint64(b, zcdbh.PreviousId)
chatMessage.ID = zcdbh.PreviousId
bs, err := json.Marshal(chatMessage) bs, err := json.Marshal(chatMessage)
if err != nil { if err != nil {
return return
@ -100,34 +187,54 @@ func (zcdbh *ZoneChatDBHandler) AddNewChatMessage(chatMessage *ChatMessage) (err
} }
func (zcdbh *ZoneChatDBHandler) DeleteChatMessage(key uint64) (err error) { func (zcdbh *ZoneChatDBHandler) DeleteChatMessage(key uint64) (err error) {
if err = zcdbh.db(func(d *badger.DB) (err error) {
zcdbh.lock.Lock()
defer zcdbh.lock.Unlock()
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 return
} }
func (zcdbh *ZoneChatDBHandler) ListChatMessages(lastIndex int, limit int) (chatMessages []*ChatMessage, l int, err error) { func (zcdbh *ZoneChatDBHandler) DeleteChatFile(filename string, key uint64) (err error) {
zcdbh.lock.Lock()
defer zcdbh.lock.Unlock()
if err = zcdbh.DeleteChatMessage(key); err != nil {
return
}
err = os.Remove(filepath.Join("data", "zones", zcdbh.ZoneID, "chats", zcdbh.ChatID, "__files__", filename))
return
}
func (zcdbh *ZoneChatDBHandler) ListChatMessages(lastIndex int, limit int) (chatMessages []*ChatMessage, l int, done bool, err error) {
zcdbh.lock.RLock()
defer zcdbh.lock.RUnlock()
err = zcdbh.db(func(d *badger.DB) (err error) { err = zcdbh.db(func(d *badger.DB) (err error) {
err = d.View(func(txn *badger.Txn) (err error) { err = d.View(func(txn *badger.Txn) (err error) {
opt := badger.DefaultIteratorOptions opt := badger.DefaultIteratorOptions
opt.Reverse = true opt.Reverse = true
it := txn.NewIterator(opt) it := txn.NewIterator(opt)
b := make([]byte, 100) b := make([]byte, bufferSize)
if lastIndex <= 0 { if lastIndex <= 0 {
binary.LittleEndian.PutUint64(b, uint64(zcdbh.PreviousId)) binary.BigEndian.PutUint64(b, uint64(zcdbh.PreviousId))
} else { } else {
binary.LittleEndian.PutUint64(b, uint64(lastIndex)) binary.BigEndian.PutUint64(b, uint64(lastIndex))
} }
x := 0 x := 0
defer it.Close() defer it.Close()
defer func() { defer func() {
if lastIndex > limit { if x < limit {
l = lastIndex - limit - 1 done = true
} else if lastIndex == 0 {
if zcdbh.PreviousId > uint64(limit) {
l = int(zcdbh.PreviousId) - limit - 1
} else {
l = 0
}
} else {
l = 0
} }
}() }()
chatMessages = make([]*ChatMessage, 0) chatMessages = make([]*ChatMessage, 0)
@ -141,6 +248,7 @@ func (zcdbh *ZoneChatDBHandler) ListChatMessages(lastIndex int, limit int) (chat
if err = json.Unmarshal(val, &chatMessage); err != nil { if err = json.Unmarshal(val, &chatMessage); err != nil {
return err return err
} }
l = int(chatMessage.ID)
chatMessages = append(chatMessages, chatMessage) chatMessages = append(chatMessages, chatMessage)
return return
}); err != nil { }); err != nil {
@ -155,11 +263,75 @@ func (zcdbh *ZoneChatDBHandler) ListChatMessages(lastIndex int, limit int) (chat
return return
} }
func (zcdbh *ZoneChatDBHandler) GetChatMessage(index uint64) (chatMessage *ChatMessage, err error) { func (zcdbh *ZoneChatDBHandler) ListChatFiles(lastIndex int, limit int) (chatMessages []*ChatFile, l int, err error) {
zcdbh.lock.RLock()
defer zcdbh.lock.RUnlock()
err = zcdbh.db(func(d *badger.DB) (err error) { err = zcdbh.db(func(d *badger.DB) (err error) {
err = d.View(func(txn *badger.Txn) (err error) { err = d.View(func(txn *badger.Txn) (err error) {
b := make([]byte, 100) opt := badger.DefaultIteratorOptions
binary.LittleEndian.PutUint64(b, uint64(zcdbh.PreviousId)) opt.Reverse = true
it := txn.NewIterator(opt)
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 *ZoneChatDBHandler) GetChatMessage(index uint64) (chatMessage *ChatMessage, err error) {
zcdbh.lock.RLock()
defer zcdbh.lock.RUnlock()
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) item, err := txn.Get(b)
if err != nil { if err != nil {
return return
@ -174,6 +346,30 @@ func (zcdbh *ZoneChatDBHandler) GetChatMessage(index uint64) (chatMessage *ChatM
return return
} }
func (zcdbh *ZoneChatDBHandler) ModifyChatMessage(id string, chatMessage *ChatMessage) (err error) { func (zcdbh *ZoneChatDBHandler) ModifyChatMessage(key uint64, newContent string) (err error) {
zcdbh.lock.Lock()
defer zcdbh.lock.Unlock()
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 return
} }

View File

@ -7,34 +7,50 @@ import (
"io/fs" "io/fs"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"time" "time"
"github.com/dgraph-io/badger/v3" "github.com/dgraph-io/badger/v3"
"github.com/pion/webrtc/v3"
) )
const ( const (
LIST_LATEST_CHATS = "list_latest_chats" LIST_LATEST_CHATS = "list_latest_chats"
LIST_CHATS = "list_chats" READ_LATEST_MESSAGE = "read_latest_message"
GET_CHATS = "get_chats" LIST_LATEST_FILES = "list_latest_files"
GET_CHAT = "get_chat" LIST_CHATS = "list_chats"
CREATE_CHAT = "create_chat" GET_CHATS = "get_chats"
DELETE_CHAT = "delete_chat" GET_CHAT = "get_chat"
EDIT_CHAT_TYPE = "edit_chat_type" CREATE_CHAT = "create_chat"
EDIT_CHAT_NAME = "edit_chat_name" DELETE_CHAT = "delete_chat"
ADD_CHAT_MEMBERS = "add_chat_members" EDIT_CHAT_TYPE = "edit_chat_type"
REMOVE_CHAT_MEMBER = "remove_chat_member" EDIT_CHAT_NAME = "edit_chat_name"
ADD_CHAT_MESSAGE = "add_chat_message" ADD_CHAT_MEMBERS = "add_chat_members"
REMOVE_CHAT_MEMBER = "remove_chat_member"
ADD_CHAT_MESSAGE = "add_chat_message"
ZONE_UPLOAD_CHAT_FILE = "zone_upload_chat_file"
ZONE_DOWNLOAD_CHAT_FILE = "zone_download_chat_file"
DELETE_CHAT_FILE = "delete_chat_file"
DELETE_CHAT_MESSAGE = "delete_chat_message"
EDIT_CHAT_MESSAGE = "edit_chat_message"
ZONE_UPLOAD_CHAT_FILE_DONE = "zone_upload_chat_file_done"
) )
const ( const (
CHAT_NAME_EDITED = "chat_name_edited" CHAT_NAME_EDITED = "chat_name_edited"
CHAT_MEMBER_LIST = "chat_messages_list" CHAT_TYPE_EDITED = "chat_type_edited"
CHAT_MEMBER_ADDED = "chat_member_added" CHAT_MESSAGE_LIST = "chat_messages_list"
CHAT_MEMBER_REMOVED = "chat_member_removed" CHAT_FILES_LIST = "chat_files_list"
REMOVED_FROM_CHAT = "removed_from_chat" CHAT_MEMBER_ADDED = "chat_member_added"
ADDED_IN_CHAT = "added_in_chat" CHAT_MEMBER_REMOVED = "chat_member_removed"
GET_CHATS_RESPONSE = "get_chats_response" REMOVED_FROM_CHAT = "removed_from_chat"
NEW_CHAT_MESSAGE = "new_chat_message" ADDED_IN_CHAT = "added_in_chat"
GET_CHATS_RESPONSE = "get_chats_response"
NEW_CHAT_MESSAGE = "new_chat_message"
CHAT_MESSAGE_DELETED = "chat_message_deleted"
CHAT_MESSAGE_EDITED = "chat_message_edited"
CHAT_FILE_DELETED = "chat_file_deleted"
NEW_CHAT_FILE = "new_chat_file"
) )
const ( const (
@ -56,21 +72,27 @@ type Chat struct {
ChatType string `json:"chatType"` ChatType string `json:"chatType"`
Owner string `json:"owner"` Owner string `json:"owner"`
Members []string `json:"members"` Members []string `json:"members"`
LastReadIndex uint `json:"lastReadIndex"`
Unread uint `json:"unread"`
DB *ZoneChatDBHandler `json:"-"` DB *ZoneChatDBHandler `json:"-"`
Tracking *ZoneChatTrackingDB `json:"-"`
} }
type ZoneChatsHandler struct { type ZoneChatsHandler struct {
ZoneId string ZoneId string
ZoneMembersId []string HostId string
DataChannels map[string]*DataChannel ChatFSInstance *ChatFSInstance
Flag *uint32 ZoneMembersId []string
ChatFlag *uint32 DataChannels map[string]*DataChannel
Chats map[string]*Chat Flag *uint32
reqChans []chan<- *ZoneRequest ChatFSInstanceFlag *uint32
init bool ChatFlag *uint32
Chats map[string]*Chat
reqChans []chan<- *ZoneRequest
init bool
} }
func NewZoneChatsHandler(zoneId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, flag *uint32) (zoneChatsHandler *ZoneChatsHandler, err error) { func NewZoneChatsHandler(hostId, zoneId, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, flag *uint32) (zoneChatsHandler *ZoneChatsHandler, err error) {
var dirs []fs.DirEntry var dirs []fs.DirEntry
dirs, err = os.ReadDir(filepath.Join("data", "zones", zoneId, "chats")) dirs, err = os.ReadDir(filepath.Join("data", "zones", zoneId, "chats"))
if err != nil { if err != nil {
@ -112,6 +134,10 @@ func NewZoneChatsHandler(zoneId string, owner string, authorizedMembers []string
if err != nil { if err != nil {
return nil, err return nil, err
} }
zoneChatTracking,err := NewZoneChatTracking(zoneId, chat.Name())
if err != nil {
return nil, err
}
var bs []byte var bs []byte
bs, err = os.ReadFile(filepath.Join("data", "zones", zoneId, "chats", chat.Name(), "chatConfig.json")) bs, err = os.ReadFile(filepath.Join("data", "zones", zoneId, "chats", chat.Name(), "chatConfig.json"))
if err != nil { if err != nil {
@ -122,19 +148,27 @@ func NewZoneChatsHandler(zoneId string, owner string, authorizedMembers []string
if err = json.Unmarshal(bs, &c); err != nil { if err = json.Unmarshal(bs, &c); err != nil {
return nil, err return nil, err
} }
if err = zoneChatTracking.Initialize(0,c.Members...); err != nil {
return nil,err
}
logger.Println("chats data :", c.ChatId, c.ChatType, c.Owner, c.Members) logger.Println("chats data :", c.ChatId, c.ChatType, c.Owner, c.Members)
c.DB = zoneChatDBHandler c.DB = zoneChatDBHandler
c.Tracking = zoneChatTracking
chats[c.ChatId] = &c chats[c.ChatId] = &c
} }
chatFlag := uint32(0) chatFlag := uint32(0)
chatFSFlag := uint32(0)
zoneChatsHandler = &ZoneChatsHandler{ zoneChatsHandler = &ZoneChatsHandler{
ZoneId: zoneId, HostId: hostId,
ZoneMembersId: authorizedMembers, ChatFSInstance: NewChatFSInstance(zoneId, owner, authorizedMembers),
DataChannels: dataChannels, ChatFSInstanceFlag: &chatFSFlag,
Flag: flag, ZoneId: zoneId,
Chats: chats, ZoneMembersId: authorizedMembers,
ChatFlag: &chatFlag, DataChannels: dataChannels,
init: false, Flag: flag,
Chats: chats,
ChatFlag: &chatFlag,
init: false,
} }
return return
} }
@ -206,6 +240,21 @@ func (zch *ZoneChatsHandler) Subscribe(ctx context.Context, publisher <-chan *Zo
return return
} }
func (zch *ZoneChatsHandler) signalCandidate(from string, to string, candidate *webrtc.ICECandidate) (err error) {
d, e := zch.sendDataChannelMessage(string(ZONE_CHAT_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 (zch *ZoneChatsHandler) GetChats(userId string, chatsId ...interface{}) (err error) { func (zch *ZoneChatsHandler) GetChats(userId string, chatsId ...interface{}) (err error) {
chats := make([]*Chat, 0, len(chatsId)) chats := make([]*Chat, 0, len(chatsId))
for _, id := range chatsId { for _, id := range chatsId {
@ -213,7 +262,7 @@ func (zch *ZoneChatsHandler) GetChats(userId string, chatsId ...interface{}) (er
err = fmt.Errorf("id of wrong type") err = fmt.Errorf("id of wrong type")
return return
} }
logger.Println("chat from get chats", id.(string)) fmt.Println("chat from get chats", id.(string))
_ = atomicallyExecute(zch.ChatFlag, func() (err error) { _ = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[id.(string)]; ok { if _, ok := zch.Chats[id.(string)]; ok {
logger.Println(zch.Chats[id.(string)]) logger.Println(zch.Chats[id.(string)])
@ -222,27 +271,36 @@ func (zch *ZoneChatsHandler) GetChats(userId string, chatsId ...interface{}) (er
return return
}) })
} }
answer := &ZoneResponse{ fmt.Println("first loop done")
Type: GET_CHATS_RESPONSE, for _, chat := range chats {
From: "", fmt.Println(chat.ChatId)
To: "", index,err := chat.Tracking.GetUserLastIndex(userId)
Payload: map[string]interface{}{ if err != nil {
"chats": chats, fmt.Println("there")
}, fmt.Println(err)
} return err
bs, jsonErr := json.Marshal(answer) }
if jsonErr != nil { chat.DB.lock.RLock()
return jsonErr defer chat.DB.lock.RUnlock()
} unread,err := chat.DB.calculateNewChatCount(uint64(index))
_ = atomicallyExecute(zch.Flag, func() (err error) { if err != nil {
if _, ok := zch.DataChannels[userId]; ok { fmt.Println("over there")
err = zch.DataChannels[userId].DataChannel.SendText(string(bs)) fmt.Println(err)
if err != nil { return err
return }
} chat.LastReadIndex = index
chat.Unread = unread
}
fmt.Println(chats)
done,e := zch.sendDataChannelMessage(GET_CHATS_RESPONSE,"node",userId,map[string]interface{}{
"chats": chats,
})
select {
case <-done:
fmt.Println("done")
case terr :=<-e:
fmt.Println(terr)
} }
return
})
return return
} }
@ -266,21 +324,25 @@ func (zch *ZoneChatsHandler) AddNewChat(chatName string, owner string, chatType
if mkdirErr != nil { if mkdirErr != nil {
return mkdirErr return mkdirErr
} }
mkdirErr = os.Mkdir(filepath.Join("data", "zones", zch.ZoneId, "chats", chatName,"__tracking__"), 0700)
if mkdirErr != nil {
return mkdirErr
}
file, ferr := os.Create(filepath.Join("data", "zones", zch.ZoneId, "chats", chatName, "chatConfig.json")) file, ferr := os.Create(filepath.Join("data", "zones", zch.ZoneId, "chats", chatName, "chatConfig.json"))
if ferr != nil { if ferr != nil {
return ferr return ferr
} }
m := make([]string, 0, len(members)) m := make([]string, 0, len(members))
for _, member := range members { for _, member := range members {
if _, ok := member.(string); ok { if mbr, ok := member.(string); ok && mbr != owner {
m = append(m, member.(string)) m = append(m, mbr)
} }
} }
baseConfig := &ChatConfig{ baseConfig := &ChatConfig{
ChatId: chatName, ChatId: chatName,
Owner: owner, Owner: owner,
ChatType: chatType, ChatType: chatType,
Members: m, Members: append(m,owner),
} }
bs, jsonErr := json.Marshal(baseConfig) bs, jsonErr := json.Marshal(baseConfig)
if jsonErr != nil { if jsonErr != nil {
@ -297,11 +359,19 @@ func (zch *ZoneChatsHandler) AddNewChat(chatName string, owner string, chatType
if err != nil { if err != nil {
return err return err
} }
zoneChatTrackingDB,err := NewZoneChatTracking(zch.ZoneId,chatName)
if err != nil {
return err
}
if err = zoneChatTrackingDB.Initialize(1,baseConfig.Members...); err != nil {
return err
}
var c Chat var c Chat
if err = json.Unmarshal(bs, &c); err != nil { if err = json.Unmarshal(bs, &c); err != nil {
return err return err
} }
c.DB = zoneChatDBHandler c.DB = zoneChatDBHandler
c.Tracking = zoneChatTrackingDB
_ = atomicallyExecute(zch.ChatFlag, func() (err error) { _ = atomicallyExecute(zch.ChatFlag, func() (err error) {
zch.Chats[c.ChatId] = &c zch.Chats[c.ChatId] = &c
return return
@ -396,10 +466,10 @@ func (zch *ZoneChatsHandler) EditChatName(chatId string, newChatId string) (err
} }
chatConfig.ChatId = newChatId chatConfig.ChatId = newChatId
bs, err = json.Marshal(&chatConfig) bs, err = json.Marshal(&chatConfig)
if err = os.Rename(filepath.Join("data", "zones", zch.ZoneId, "chats", chatId), filepath.Join("data", "zones", zch.ZoneId, "chats", chatId)); err != nil { if err = os.Rename(filepath.Join("data", "zones", zch.ZoneId, "chats", chatId), filepath.Join("data", "zones", zch.ZoneId, "chats", newChatId)); err != nil {
return return
} }
f, err := os.OpenFile(filepath.Join("data", "zones", zch.ZoneId, "chats", chatId, "chatConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) f, err := os.OpenFile(filepath.Join("data", "zones", zch.ZoneId, "chats", newChatId, "chatConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
defer func() { defer func() {
_ = f.Close() _ = f.Close()
}() }()
@ -415,6 +485,7 @@ func (zch *ZoneChatsHandler) EditChatName(chatId string, newChatId string) (err
ChatType: chatConfig.ChatType, ChatType: chatConfig.ChatType,
Members: chatConfig.Members, Members: chatConfig.Members,
DB: zch.Chats[chatId].DB, DB: zch.Chats[chatId].DB,
Tracking:zch.Chats[chatId].Tracking,
} }
chat.DB.updateDbCallbackFolder(newChatId) chat.DB.updateDbCallbackFolder(newChatId)
_ = atomicallyExecute(zch.ChatFlag, func() (err error) { _ = atomicallyExecute(zch.ChatFlag, func() (err error) {
@ -483,9 +554,10 @@ func (zch *ZoneChatsHandler) EditChatType(chatId string, chatType string) (err e
chat := &Chat{ chat := &Chat{
Owner: chatConfig.Owner, Owner: chatConfig.Owner,
ChatId: chatConfig.ChatId, ChatId: chatConfig.ChatId,
ChatType: chatConfig.ChatType, ChatType: chatType,
Members: chatConfig.Members, Members: chatConfig.Members,
DB: zch.Chats[chatId].DB, DB: zch.Chats[chatId].DB,
Tracking:zch.Chats[chatId].Tracking,
} }
switch chatType { switch chatType {
case BROADCAST: case BROADCAST:
@ -500,6 +572,10 @@ func (zch *ZoneChatsHandler) EditChatType(chatId string, chatType string) (err e
if pubErr := zch.SetChatPublicForUser(chatId, member); pubErr != nil { if pubErr := zch.SetChatPublicForUser(chatId, member); pubErr != nil {
logger.Println(pubErr) logger.Println(pubErr)
} }
zch.sendDataChannelMessage(CHAT_TYPE_EDITED, "node", member, map[string]interface{}{
"chatId": chatId,
"chatType": chatType,
})
} }
case PRIVATE: case PRIVATE:
var members = []string{} var members = []string{}
@ -511,6 +587,10 @@ func (zch *ZoneChatsHandler) EditChatType(chatId string, chatType string) (err e
if pubErr := zch.SetChatPrivateForUser(chatId, member); pubErr != nil { if pubErr := zch.SetChatPrivateForUser(chatId, member); pubErr != nil {
logger.Println(pubErr) logger.Println(pubErr)
} }
zch.sendDataChannelMessage(CHAT_TYPE_EDITED, "node", member, map[string]interface{}{
"chatId": chatId,
"chatType": chatType,
})
} }
} }
_ = atomicallyExecute(zch.ChatFlag, func() (err error) { _ = atomicallyExecute(zch.ChatFlag, func() (err error) {
@ -573,14 +653,27 @@ memberLoop:
ChatType: chatConfig.ChatType, ChatType: chatConfig.ChatType,
Members: chatConfig.Members, Members: chatConfig.Members,
DB: zch.Chats[chatId].DB, DB: zch.Chats[chatId].DB,
Tracking: zch.Chats[chatId].Tracking,
} }
chat.DB.lock.Lock()
lastIndex := chat.DB.PreviousId
chat.DB.lock.Unlock()
_ = atomicallyExecute(zch.ChatFlag, func() (err error) { _ = atomicallyExecute(zch.ChatFlag, func() (err error) {
zch.Chats[chatId] = chat zch.Chats[chatId] = chat
return return
}) })
var chmb []string
if chat.ChatType == PRIVATE {
chmb = chat.Members
} else {
chmb = zch.ZoneMembersId
}
broadcastLoop: broadcastLoop:
for _, member := range chat.Members { for _, member := range chmb {
for _, m := range addedMembers { for _, m := range addedMembers {
if err = chat.Tracking.SetUserLastIndex(member,lastIndex); err != nil {
return
}
if member == m { if member == m {
done, e := zch.sendDataChannelMessage(ADDED_IN_CHAT, "node", member, map[string]interface{}{ done, e := zch.sendDataChannelMessage(ADDED_IN_CHAT, "node", member, map[string]interface{}{
"chat": chat, "chat": chat,
@ -665,10 +758,14 @@ func (zch *ZoneChatsHandler) RemoveChatMember(chatId string, chatMember string)
ChatType: chatConfig.ChatType, ChatType: chatConfig.ChatType,
Members: chatConfig.Members, Members: chatConfig.Members,
DB: zch.Chats[chatId].DB, DB: zch.Chats[chatId].DB,
Tracking: zch.Chats[chatId].Tracking,
} }
zch.Chats[chatId] = chat zch.Chats[chatId] = chat
return return
}) })
if err = chat.Tracking.DeleteUserTracking(chatMember); err != nil {
return
}
zch.sendZoneRequest(REMOVE_KNOWN_CHAT, "node", map[string]interface{}{ zch.sendZoneRequest(REMOVE_KNOWN_CHAT, "node", map[string]interface{}{
"userId": chatMember, "userId": chatMember,
"chatId": chatId, "chatId": chatId,
@ -682,8 +779,14 @@ func (zch *ZoneChatsHandler) RemoveChatMember(chatId string, chatMember string)
case err = <-e: case err = <-e:
logger.Println(err) logger.Println(err)
} }
var chmb []string
if chat.ChatType == PRIVATE {
chmb = chat.Members
} else {
chmb = zch.ZoneMembersId
}
broadcastLoop: broadcastLoop:
for _, member := range chat.Members { for _, member := range chmb {
if member == chatMember { if member == chatMember {
continue broadcastLoop continue broadcastLoop
} }
@ -700,19 +803,58 @@ broadcastLoop:
return return
} }
func (zch *ZoneChatsHandler) ListLatestChatMessages(userId string, chatId string, lastIndex float64, limit float64) (err error) { func(zch *ZoneChatsHandler) ReadLastMessage(userId,chatId string) (err error) {
err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if chat, ok := zch.Chats[chatId]; ok {
err = chat.Tracking.SetUserLastIndex(userId,chat.DB.PreviousId)
}
return
})
return
}
func (zch *ZoneChatsHandler) ListLatestChatMessages(userId, chatId string, lastIndex, limit float64) (err error) {
var list []*ChatMessage var list []*ChatMessage
var i int var i int
var done bool
if err = atomicallyExecute(zch.ChatFlag, func() (err error) { if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[chatId]; ok { if chat, ok := zch.Chats[chatId]; ok {
list, i, err = zch.Chats[chatId].DB.ListChatMessages(int(lastIndex), int(limit)) list, i, done, err = zch.Chats[chatId].DB.ListChatMessages(int(lastIndex), int(limit))
if err != nil {
return
}
err = chat.Tracking.SetUserLastIndex(userId,chat.DB.PreviousId)
} }
return return
}); err != nil { }); err != nil {
return return
} }
done, e := zch.sendDataChannelMessage(CHAT_MEMBER_LIST, "node", userId, map[string]interface{}{ success, e := zch.sendDataChannelMessage(CHAT_MESSAGE_LIST, "node", userId, map[string]interface{}{
"done": i == 0, "done": done || i <= 0,
"lastIndex": i - 1,
"chatId": chatId,
"chatMessages": list,
})
select {
case <-success:
case err = <-e:
}
return
}
func (zch *ZoneChatsHandler) ListLatestChatFiles(userId, chatId string, lastIndex, limit float64) (err error) {
var list []*ChatFile
var i int
if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[chatId]; ok {
list, i, err = zch.Chats[chatId].DB.ListChatFiles(int(lastIndex), int(limit))
}
return
}); err != nil {
return
}
done, e := zch.sendDataChannelMessage(CHAT_FILES_LIST, "node", userId, map[string]interface{}{
"done": i <= 0,
"lastIndex": i, "lastIndex": i,
"chatId": chatId, "chatId": chatId,
"chatMessages": list, "chatMessages": list,
@ -724,27 +866,33 @@ func (zch *ZoneChatsHandler) ListLatestChatMessages(userId string, chatId string
return return
} }
func (zch *ZoneChatsHandler) AddChatMessage(userId string, chatId string, content string, chatResponseId uint64, file *ChatFile) (err error) { func (zch *ZoneChatsHandler) AddChatMessage(userId, chatId, content string, isResponse bool, chatResponseId uint64, file *ChatFile) (err error) {
chatMessage := &ChatMessage{ chatMessage := &ChatMessage{
Content: content, Content: content,
From: userId, From: userId,
IsResponse: isResponse,
ResponseOf: nil, ResponseOf: nil,
File: file, File: file,
Tags: make([]string, 0), Tags: make([]string, 0),
Date: time.Now().Format("Mon, 02 Jan 2006 15:04:05 MST"), Date: time.Now().Format("Mon, 02 Jan 2006 15:04:05 MST"),
} }
if err = atomicallyExecute(zch.ChatFlag, func() (err error) { if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[chatId]; ok { if chat, ok := zch.Chats[chatId]; ok {
parentMessage, getErr := zch.Chats[chatId].DB.GetChatMessage(chatResponseId) if isResponse {
if err != nil { parentMessage, getErr := chat.DB.GetChatMessage(chatResponseId)
if getErr != badger.ErrKeyNotFound { if err != nil {
return getErr if getErr != badger.ErrKeyNotFound {
return getErr
}
} }
chatMessage.ResponseOf = parentMessage
} }
chatMessage.ResponseOf = parentMessage
if err = zch.Chats[chatId].DB.AddNewChatMessage(chatMessage); err != nil { if err = zch.Chats[chatId].DB.AddNewChatMessage(chatMessage); err != nil {
return return
} }
chatMessage.ID = zch.Chats[chatId].DB.PreviousId
} else { } else {
err = fmt.Errorf("no corresponding chats") err = fmt.Errorf("no corresponding chats")
} }
@ -752,7 +900,6 @@ func (zch *ZoneChatsHandler) AddChatMessage(userId string, chatId string, conten
}); err != nil { }); err != nil {
return return
} }
chatMessage.ID = zch.Chats[chatId].DB.PreviousId
_ = atomicallyExecute(zch.ChatFlag, func() (err error) { _ = atomicallyExecute(zch.ChatFlag, func() (err error) {
chat := zch.Chats[chatId] chat := zch.Chats[chatId]
logger.Println(chat.ChatType) logger.Println(chat.ChatType)
@ -896,9 +1043,166 @@ func (zch *ZoneChatsHandler) RemoveAllUserChat(userId string) (err error) {
return return
} }
func (zch *ZoneChatsHandler) ConnectToChatFSInstance(channelId string, userId string, sdp string) (err error) {
err = atomicallyExecute(zch.ChatFSInstanceFlag, func() (err error) {
d, e := zch.ChatFSInstance.HandleOffer(context.Background(), channelId, userId, sdp, zch.HostId, zch.sendDataChannelMessage, zch.signalCandidate)
select {
case <-d:
case err = <-e:
}
return
})
return
}
func (zch *ZoneChatsHandler) LeaveChatFSInstance(
userId string) (err error) {
zch.ChatFSInstance.HandleLeavingMember(userId)
return
}
func (zch *ZoneChatsHandler) DeleteChatMessage(key uint64, chatId string) (err error) {
err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[chatId]; !ok {
err = fmt.Errorf("no file corresponding to id %s", chatId)
return
}
if err = zch.Chats[chatId].DB.DeleteChatMessage(key); err != nil {
return
}
if zch.Chats[chatId].ChatType == PRIVATE {
for _, member := range zch.Chats[chatId].Members {
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{
"chatId": chatId,
"messageId": key,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
} else {
for _, member := range zch.ZoneMembersId {
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{
"chatId": chatId,
"messageId": key,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
}
previousId := zch.Chats[chatId].DB.PreviousId
if previousId == key {
if err = zch.Chats[chatId].DB.revertPreviousId(); err != nil {
return
}
previousId = zch.Chats[chatId].DB.PreviousId
if previousId == 1 {
previousId = 0
}
if err = zch.Chats[chatId].Tracking.RevertTrackingLastIndex(previousId); err != nil {
return
}
}
return
})
return
}
func (zch *ZoneChatsHandler) UpdateChatMessage(key uint64, chatId, newContent string) (err error) {
err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[chatId]; !ok {
err = fmt.Errorf("no file corresponding to id %s", chatId)
return
}
if err = zch.Chats[chatId].DB.ModifyChatMessage(key, newContent); err != nil {
return
}
if zch.Chats[chatId].ChatType == PRIVATE {
for _, member := range zch.Chats[chatId].Members {
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_EDITED, "node", member, map[string]interface{}{
"chatId": chatId,
"messageId": key,
"newContent": newContent,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
} else {
for _, member := range zch.ZoneMembersId {
d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_EDITED, "node", member, map[string]interface{}{
"chatId": chatId,
"messageId": key,
"newContent": newContent,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
}
return
})
return
}
func (zch *ZoneChatsHandler) DeleteChatFile(key uint64, fileName, chatId string) (err error) {
err = atomicallyExecute(zch.ChatFlag, func() (err error) {
if _, ok := zch.Chats[chatId]; !ok {
err = fmt.Errorf("no file corresponding to id %s", chatId)
return
}
if err = zch.Chats[chatId].DB.DeleteChatFile(fileName, key); err != nil {
return
}
if zch.Chats[chatId].ChatType == PRIVATE {
for _, member := range zch.Chats[chatId].Members {
d, e := zch.sendDataChannelMessage(CHAT_FILE_DELETED, "node", member, map[string]interface{}{
"chatId": chatId,
"fileId": key,
"fileName": fileName,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
} else {
for _, member := range zch.ZoneMembersId {
d, e := zch.sendDataChannelMessage(CHAT_FILE_DELETED, "node", member, map[string]interface{}{
"chatId": chatId,
"fileId": key,
"fileName": fileName,
})
select {
case <-d:
case tempErr := <-e:
logger.Println(tempErr)
}
}
}
return
})
return
}
func (zch *ZoneChatsHandler) handleZoneRequest(ctx context.Context, req *ZoneRequest) (err error) { func (zch *ZoneChatsHandler) handleZoneRequest(ctx context.Context, req *ZoneRequest) (err error) {
logger.Println("got request in zone chat handler", req) logger.Println("got request in zone chat handler", req)
switch req.ReqType { switch req.ReqType {
case LEAVE_ZONE:
if err = verifyFieldsString(req.Payload, "userId"); err != nil {
return
}
err = zch.LeaveChatFSInstance(req.Payload["userId"].(string))
case string(REMOVED_ZONE_AUTHORIZED_MEMBER): case string(REMOVED_ZONE_AUTHORIZED_MEMBER):
if err = verifyFieldsString(req.Payload, "userId"); err != nil { if err = verifyFieldsString(req.Payload, "userId"); err != nil {
return return
@ -975,10 +1279,13 @@ func (zch *ZoneChatsHandler) handleZoneRequest(ctx context.Context, req *ZoneReq
} }
err = zch.AddNewChat(req.Payload["chatId"].(string), req.Payload["owner"].(string), req.Payload["chatType"].(string), req.Payload["members"].([]interface{})) err = zch.AddNewChat(req.Payload["chatId"].(string), req.Payload["owner"].(string), req.Payload["chatType"].(string), req.Payload["members"].([]interface{}))
case GET_CHATS: case GET_CHATS:
fmt.Println("got a get chat req")
if err = verifyFieldsSliceInterface(req.Payload, "chatsId"); err != nil { if err = verifyFieldsSliceInterface(req.Payload, "chatsId"); err != nil {
return return
} }
fmt.Println("calling get chat")
err = zch.GetChats(req.From, req.Payload["chatsId"].([]interface{})...) err = zch.GetChats(req.From, req.Payload["chatsId"].([]interface{})...)
fmt.Println("get chat done")
case LIST_LATEST_CHATS: case LIST_LATEST_CHATS:
if err = verifyFieldsString(req.Payload, "chatId"); err != nil { if err = verifyFieldsString(req.Payload, "chatId"); err != nil {
return return
@ -988,16 +1295,37 @@ func (zch *ZoneChatsHandler) handleZoneRequest(ctx context.Context, req *ZoneReq
} }
err = zch.ListLatestChatMessages(req.From, req.Payload["chatId"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64)) err = zch.ListLatestChatMessages(req.From, req.Payload["chatId"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64))
return return
case LIST_LATEST_FILES:
if err = verifyFieldsString(req.Payload, "chatId"); err != nil {
return
}
if err = verifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil {
return
}
err = zch.ListLatestChatFiles(req.From, req.Payload["chatId"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64))
return
case READ_LATEST_MESSAGE:
if err = verifyFieldsString(req.Payload, "chatId"); err != nil {
return
}
err = zch.ReadLastMessage(req.From,req.Payload["chatId"].(string))
case ADD_CHAT_MESSAGE: case ADD_CHAT_MESSAGE:
logger.Println("got request in zone chat handler", req) logger.Println("got request in zone chat handler", req)
if err = verifyFieldsString(req.Payload, "chatId", "content"); err != nil { if err = verifyFieldsString(req.Payload, "chatId", "content"); err != nil {
return return
} }
if err = verifyFieldsFloat64(req.Payload, "parentChatId"); err != nil { if err = verifyFieldsBool(req.Payload, "isResponse"); err != nil {
return 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 var file *ChatFile = nil
if _,ok := req.Payload["file"]; ok { if _, ok := req.Payload["file"]; ok {
bs, jsonErr := json.Marshal(req.Payload["file"]) bs, jsonErr := json.Marshal(req.Payload["file"])
if jsonErr != nil { if jsonErr != nil {
return jsonErr return jsonErr
@ -1009,7 +1337,114 @@ func (zch *ZoneChatsHandler) handleZoneRequest(ctx context.Context, req *ZoneReq
} }
file = &f file = &f
} }
err = zch.AddChatMessage(req.From, req.Payload["chatId"].(string), req.Payload["content"].(string),uint64(req.Payload["parentChatId"].(float64)),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 {
return
}
if err = verifyFieldsFloat64(req.Payload, "messageId"); err != nil {
return
}
err = zch.DeleteChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatId"].(string))
case EDIT_CHAT_MESSAGE:
if err = verifyFieldsString(req.Payload, "chatId", "newContent"); err != nil {
return
}
if err = verifyFieldsFloat64(req.Payload, "messageId"); err != nil {
return
}
err = zch.UpdateChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatId"].(string), req.Payload["newContent"].(string))
case DELETE_CHAT_FILE:
if err = verifyFieldsString(req.Payload, "chatId", "fileName"); err != nil {
return
}
if err = verifyFieldsFloat64(req.Payload, "fileId"); err != nil {
return
}
err = zch.DeleteChatFile(uint64(req.Payload["fileId"].(float64)), req.Payload["fileName"].(string), req.Payload["chatId"].(string))
case ZONE_UPLOAD_CHAT_FILE:
if err = verifyFieldsString(req.Payload, "chatId", "userId", "fileName"); err != nil {
return
}
err = zch.ChatFSInstance.SetupFileUpload(req.Payload["chatId"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string))
case ZONE_DOWNLOAD_CHAT_FILE:
if err = verifyFieldsString(req.Payload, "chatId", "userId", "fileName"); err != nil {
return
}
err = zch.ChatFSInstance.SetupFileDownload(req.Payload["chatId"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string))
case ZONE_UPLOAD_CHAT_FILE_DONE:
if err = verifyFieldsBool(req.Payload, "failed"); err != nil {
return
}
if err = verifyFieldsString(req.Payload, "path", "userId", "fileName", "type"); err != nil {
return
}
if err = verifyFieldsFloat64(req.Payload, "size"); err != nil {
return
}
if _, ok := req.Payload["chatFile"]; !ok {
err = fmt.Errorf("no field chatFile in request payload")
return
}
if req.Payload["failed"].(bool) {
logger.Println(zch.ChatFSInstance.FileUploadFailed(req.Payload["path"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string)))
} else {
defer func() {
logger.Println(zch.ChatFSInstance.FileUploadDone(req.Payload["path"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string)))
}()
}
case string(ZONE_CHAT_WEBRTC_OFFER):
if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
return
}
err = zch.ConnectToChatFSInstance(req.Payload["channelId"].(string), req.Payload["userId"].(string), req.Payload["sdp"].(string))
case string(ZONE_CHAT_WEBRTC_COUNTER_OFFER):
logger.Println("handling fs instance counter offer")
if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil {
return
}
err = atomicallyExecute(zch.ChatFSInstanceFlag, func() (err error) {
err = zch.ChatFSInstance.HandleCounterOffer(context.Background(), req.Payload["userId"].(string), zch.sendDataChannelMessage)
return
})
case string(ZONE_CHAT_WEBRTC_RENNEGOTIATION_OFFER):
if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
return
}
err = atomicallyExecute(zch.ChatFSInstanceFlag, func() (err error) {
err = zch.ChatFSInstance.HandleRennegotiationOffer(req.Payload["userId"].(string), req.Payload["sdp"].(string), zch.sendDataChannelMessage)
return
})
case string(ZONE_CHAT_WEBRTC_RENNEGOTIATION_ANSWER):
if err = verifyFieldsString(req.Payload, "channelId", "userId", "sdp"); err != nil {
return
}
err = atomicallyExecute(zch.ChatFSInstanceFlag, func() (err error) {
err = zch.ChatFSInstance.HandleRennegotiationAnswer(req.Payload["userId"].(string), req.Payload["sdp"].(string))
return
})
case string(ZONE_CHAT_WEBRTC_CANDIDATE):
logger.Println("handling fs instance 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(zch.ChatFSInstanceFlag, func() (err error) {
err = zch.ChatFSInstance.AddCandidate(&webrtc.ICECandidateInit{
Candidate: req.Payload["candidate"].(string),
SDPMid: &sdpMid,
SDPMLineIndex: &SDPMLineIndex,
}, req.Payload["userId"].(string))
return
})
} }
return return
} }

View File

@ -96,7 +96,7 @@ type ZoneFSEntity struct {
} }
// NewZoneFileHandler: factory function to create a zone file handler the name of the parameters are explicit, this is the only way to safely create a ZoneFSEntity // NewZoneFileHandler: factory function to create a zone file handler the name of the parameters are explicit, this is the only way to safely create a ZoneFSEntity
func NewZoneFileHandler(hostId string, zoneId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, flag *uint32) (zoneFileHandler *ZoneFileHandler, err error) { func NewZoneFileHandler(_ string, zoneId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, flag *uint32) (zoneFileHandler *ZoneFileHandler, err error) {
db, err := NewZoneFilesDBHandler(zoneId) db, err := NewZoneFilesDBHandler(zoneId)
if err != nil { if err != nil {
return return
@ -219,7 +219,7 @@ func (zfh *ZoneFileHandler) signalCandidate(from string, to string, candidate *w
"to": to, "to": to,
"candidate": candidate.ToJSON().Candidate, "candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid, "sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
}) })
select { select {
case <-d: case <-d:
@ -560,6 +560,18 @@ func (zfh *ZoneFileHandler) RemoveFolderMember(userId string, path string, folde
logger.Println(sendErr) logger.Println(sendErr)
} }
} }
for _, member := range members {
if _, ok := member.(string); ok {
done, errCh := zfh.sendDataChannelMessage(REMOVE_FS_ENTITY_MEMBERS_DONE, "node", member.(string), map[string]interface{}{
"path": filepath.Join(path, folderName),
})
select {
case <-done:
case sendErr := <-errCh:
logger.Println(sendErr)
}
}
}
return return
} }
@ -789,14 +801,16 @@ func (zfh *ZoneFileHandler) CopyDir(src, dst string, init bool, fsEntity *ZoneFS
} }
} }
} }
for member := range fsEntity.Members { if fsEntity != nil {
done, errCh := zfh.sendDataChannelMessage(COPY_FOLDER_DONE, "node", member, map[string]interface{}{ for member := range fsEntity.Members {
"path": dst, done, errCh := zfh.sendDataChannelMessage(COPY_FOLDER_DONE, "node", member, map[string]interface{}{
}) "path": dst,
select { })
case <-done: select {
case sendErr := <-errCh: case <-done:
logger.Println(sendErr) case sendErr := <-errCh:
logger.Println(sendErr)
}
} }
} }
return return
@ -845,27 +859,41 @@ func (zch *ZoneFileHandler) SetPublicRootFolders(user string) (err error) {
func (zfh *ZoneFileHandler) handleZoneRequest(c context.Context, req *ZoneRequest) (err error) { func (zfh *ZoneFileHandler) handleZoneRequest(c context.Context, req *ZoneRequest) (err error) {
switch req.ReqType { switch req.ReqType {
case LEAVE_ZONE: case string(REMOVED_ZONE_AUTHORIZED_MEMBER):
logger.Println("*-----------------handling leaving zone---------------*")
if err = verifyFieldsString(req.Payload, "userId"); err != nil { if err = verifyFieldsString(req.Payload, "userId"); err != nil {
return return
} }
err = zfh.LeaveFSInstance(req.Payload["userId"].(string)) var index int
case ZONE_UPLOAD_FILE_DONE: for i, m := range zfh.ZoneMembersId {
if err = verifyFieldsString(req.Payload, "path", "userId", "fileName", "type"); err != nil { if m == req.Payload["userId"].(string) {
index = i
break
}
}
zfh.ZoneMembersId = append(zfh.ZoneMembersId[:index], zfh.ZoneMembersId[index+1:]...)
err = zfh.DeleteFolder("", req.Payload["userId"].(string))
case string(NEW_AUTHORIZED_ZONE_MEMBER):
if err = verifyFieldsString(req.Payload, "userId"); err != nil {
return return
} }
if err = verifyFieldsFloat64(req.Payload, "size"); err != nil { var contain bool
return for _, m := range zfh.ZoneMembersId {
if m == req.Payload["userId"].(string) {
contain = true
break
}
} }
err = zfh.CreateFile(req.Payload["path"].(string), req.Payload["fileName"].(string), &ZoneFSEntity{ if !contain {
Name: req.Payload["fileName"].(string), zfh.ZoneMembersId = append(zfh.ZoneMembersId, req.Payload["userId"].(string))
}
err = zfh.DB.AddNewFSEntity("", &ZoneFSEntity{
Name: req.Payload["userId"].(string),
Owner: req.Payload["userId"].(string), Owner: req.Payload["userId"].(string),
Type: req.Payload["type"].(string), Type: PRIVATE,
Size: uint64(req.Payload["size"].(float64)), Folder: true,
ModTime: time.Now().Format(time.UnixDate), ModTime: time.Now().Format(time.UnixDate),
CreationTime: time.Now().Format(time.UnixDate), CreationTime: time.Now().Format(time.UnixDate),
Folder: false, Size: 0,
Members: map[string]*FSEntityAccessRights{ Members: map[string]*FSEntityAccessRights{
req.Payload["userId"].(string): { req.Payload["userId"].(string): {
Read: true, Read: true,
@ -875,6 +903,46 @@ func (zfh *ZoneFileHandler) handleZoneRequest(c context.Context, req *ZoneReques
}, },
}, },
}) })
case LEAVE_ZONE:
logger.Println("*-----------------handling leaving zone---------------*")
if err = verifyFieldsString(req.Payload, "userId"); err != nil {
return
}
err = zfh.LeaveFSInstance(req.Payload["userId"].(string))
case ZONE_UPLOAD_FILE_DONE:
if err = verifyFieldsBool(req.Payload, "failed"); err != nil {
return
}
if err = verifyFieldsString(req.Payload, "path", "userId", "fileName", "type"); err != nil {
return
}
if err = verifyFieldsFloat64(req.Payload, "size"); err != nil {
return
}
if req.Payload["failed"].(bool) {
logger.Println(zfh.FSInstance.FileUploadFailed(req.Payload["path"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string)))
} else {
defer func() {
logger.Println(zfh.FSInstance.FileUploadDone(req.Payload["path"].(string), req.Payload["fileName"].(string), req.Payload["userId"].(string)))
}()
err = zfh.CreateFile(req.Payload["path"].(string), req.Payload["fileName"].(string), &ZoneFSEntity{
Name: req.Payload["fileName"].(string),
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),
Folder: false,
Members: map[string]*FSEntityAccessRights{
req.Payload["userId"].(string): {
Read: true,
Write: true,
Download: true,
Rename: true,
},
},
})
}
case ZONE_UPLOAD_FILE: case ZONE_UPLOAD_FILE:
if err = verifyFieldsString(req.Payload, "path", "userId", "fileName"); err != nil { if err = verifyFieldsString(req.Payload, "path", "userId", "fileName"); err != nil {
return return
@ -1087,22 +1155,22 @@ func (zfh *ZoneFileHandler) handleZoneRequest(c context.Context, req *ZoneReques
case string(ZONE_FS_WEBRTC_CANDIDATE): case string(ZONE_FS_WEBRTC_CANDIDATE):
logger.Println("handling fs instance webrtc candidate") logger.Println("handling fs instance webrtc candidate")
logger.Println(req.Payload) logger.Println(req.Payload)
if err = verifyFieldsString(req.Payload, FROM, "candidate", "sdpMlineIndex", "sdpMid", "channelId", "userId"); err != nil { if err = verifyFieldsString(req.Payload, FROM, "candidate", "sdpMLineIndex", "sdpMid", "channelId", "userId"); err != nil {
return return
} }
logger.Println(req.Payload) logger.Println(req.Payload)
i, convErr := strconv.Atoi(req.Payload["sdpMlineIndex"].(string)) i, convErr := strconv.Atoi(req.Payload["sdpMLineIndex"].(string))
if convErr != nil { if convErr != nil {
return convErr return convErr
} }
sdpMlineIndex := uint16(i) SDPMLineIndex := uint16(i)
sdpMid := req.Payload["sdpMid"].(string) sdpMid := req.Payload["sdpMid"].(string)
logger.Println(sdpMid, sdpMlineIndex) logger.Println(sdpMid, SDPMLineIndex)
err = atomicallyExecute(zfh.FSInstanceFlag, func() (err error) { err = atomicallyExecute(zfh.FSInstanceFlag, func() (err error) {
err = zfh.FSInstance.AddCandidate(&webrtc.ICECandidateInit{ err = zfh.FSInstance.AddCandidate(&webrtc.ICECandidateInit{
Candidate: req.Payload["candidate"].(string), Candidate: req.Payload["candidate"].(string),
SDPMid: &sdpMid, SDPMid: &sdpMid,
SDPMLineIndex: &sdpMlineIndex, SDPMLineIndex: &SDPMLineIndex,
}, req.Payload["userId"].(string)) }, req.Payload["userId"].(string))
return return
}) })

View File

@ -24,13 +24,12 @@ const (
ZONE_FS_WEBRTC_CANDIDATE ReqType = "zone_fs_webrtc_candidate" ZONE_FS_WEBRTC_CANDIDATE ReqType = "zone_fs_webrtc_candidate"
) )
const ()
type FSInstance struct { type FSInstance struct {
ZoneID string `json:"id"` ZoneID string `json:"id"`
Owner string `json:"owner"` Owner string `json:"owner"`
Members []string `json:"members"` Members []string `json:"members"`
OpenFiles map[string]*os.File `json:"-"` OpenFiles map[string]*os.File `json:"-"`
OpenFilesForUser map[string][]string `json:"-"`
localSD map[string]*webrtc.SessionDescription `json:"-"` localSD map[string]*webrtc.SessionDescription `json:"-"`
rtcPeerConnections map[string]*ZoneRTCPeerConnection `json:"-"` rtcPeerConnections map[string]*ZoneRTCPeerConnection `json:"-"`
zoneFSDataChannels map[string]map[string]*DataChannel `json:"-"` zoneFSDataChannels map[string]map[string]*DataChannel `json:"-"`
@ -56,6 +55,7 @@ func NewFSInstance(id, owner string, members []string) (audioChannel *FSInstance
Owner: owner, Owner: owner,
Members: members, Members: members,
OpenFiles: make(map[string]*os.File), OpenFiles: make(map[string]*os.File),
OpenFilesForUser: make(map[string][]string),
localSD: make(map[string]*webrtc.SessionDescription), localSD: make(map[string]*webrtc.SessionDescription),
rtcPeerConnections: make(map[string]*ZoneRTCPeerConnection), rtcPeerConnections: make(map[string]*ZoneRTCPeerConnection),
zoneFSDataChannels: make(map[string]map[string]*DataChannel), zoneFSDataChannels: make(map[string]map[string]*DataChannel),
@ -79,6 +79,9 @@ func (fs *FSInstance) SetupFileUpload(path, filename, userId string) (err error)
} else if rErr != nil { } else if rErr != nil {
return rErr return 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) file, err := os.OpenFile(concretePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0755)
if err != nil { if err != nil {
return return
@ -87,11 +90,11 @@ func (fs *FSInstance) SetupFileUpload(path, filename, userId string) (err error)
fs.OpenFiles[filename] = file fs.OpenFiles[filename] = file
return return
}) })
err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) { setupDataChan := func(id, fileName string) (err error) {
if pc, ok := fs.rtcPeerConnections[userId]; ok { if pc, ok := fs.rtcPeerConnections[id]; ok {
maxRetransmits := uint16(100) maxRetransmits := uint16(100)
var dc *webrtc.DataChannel var dc *webrtc.DataChannel
dc, err = pc.CreateDataChannel(filename, &webrtc.DataChannelInit{ dc, err = pc.CreateDataChannel(fileName, &webrtc.DataChannelInit{
MaxRetransmits: &maxRetransmits, MaxRetransmits: &maxRetransmits,
}) })
if err != nil { if err != nil {
@ -100,34 +103,75 @@ func (fs *FSInstance) SetupFileUpload(path, filename, userId string) (err error)
dc.OnOpen(func() { dc.OnOpen(func() {
logger.Println("!-----------------------------!") logger.Println("!-----------------------------!")
logger.Printf("datachannel with id %s is now open\n", dc.Label()) logger.Printf("datachannel with id %s is now open\n", dc.Label())
logger.Println("updated")
logger.Println("!-----------------------------!") logger.Println("!-----------------------------!")
}) })
dc.OnMessage(func(msg webrtc.DataChannelMessage) { dc.OnMessage(func(msg webrtc.DataChannelMessage) {
logger.Println(msg)
_, _ = file.Write(msg.Data) _, _ = file.Write(msg.Data)
}) })
dc.OnClose(func() { dc.OnClose(func() {
//fmt.Println("closing datachannel with id", dc.Label())
_ = atomicallyExecute(fs.filesFlag, func() (err error) { _ = atomicallyExecute(fs.filesFlag, func() (err error) {
if f, ok := fs.OpenFiles[filename]; ok { if f, ok := fs.OpenFiles[fileName]; ok {
err = f.Close() err = f.Close()
} }
delete(fs.OpenFiles, filename) delete(fs.OpenFiles, fileName)
return return
}) })
_ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
delete(fs.zoneFSDataChannels[userId], fileName)
return
})
dc.Close()
}) })
err = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) { err = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[userId]; !ok { if _, ok := fs.zoneFSDataChannels[id]; !ok {
err = fmt.Errorf("no corresponding map entry in zoneFSDataChannels for id %s", userId) fs.zoneFSDataChannels[id] = make(map[string]*DataChannel)
return
} }
l := int32(0) l := int32(0)
fs.zoneFSDataChannels[userId][dc.Label()] = &DataChannel{ fs.zoneFSDataChannels[id][dc.Label()] = &DataChannel{
DataChannel: dc, DataChannel: dc,
l: &l, l: &l,
} }
return return
}) })
} else { } else {
err = fmt.Errorf("no peerconnection for id %s", userId) err = fmt.Errorf("no peerconnection for id %s", id)
}
return
}
err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
err = setupDataChan(userId, filename)
return
})
return
}
func (fs *FSInstance) FileUploadDone(path, filename, userId string) (err error) {
err = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[userId]; ok {
if dc, ok := fs.zoneFSDataChannels[userId][filename]; ok {
err = dc.DataChannel.Close()
return
}
}
return
})
return
}
func (fs *FSInstance) FileUploadFailed(path, filename, userId string) (err error) {
concretePath := filepath.Join("data", "zones", fs.ZoneID, "fs", path, "__files__", filename)
if err = os.Remove(concretePath); err != nil {
return
}
err = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[userId]; ok {
if dc, ok := fs.zoneFSDataChannels[userId][filename]; ok {
err = dc.DataChannel.Close()
return
}
} }
return return
}) })
@ -142,76 +186,133 @@ func (fs *FSInstance) SetupFileDownload(path, filename, userId string) (err erro
} }
_ = atomicallyExecute(fs.filesFlag, func() (err error) { _ = atomicallyExecute(fs.filesFlag, func() (err error) {
fs.OpenFiles[filename] = file fs.OpenFiles[filename] = file
if _, ok := fs.OpenFilesForUser[userId]; !ok {
fs.OpenFilesForUser[userId] = []string{}
}
fs.OpenFilesForUser[userId] = append(fs.OpenFilesForUser[userId], filename)
return return
}) })
var dc *webrtc.DataChannel
err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) { err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
if pc, ok := fs.rtcPeerConnections[userId]; ok { if pc, ok := fs.rtcPeerConnections[userId]; ok {
maxRetransmits := uint16(100) maxRetransmits := uint16(100)
var dc *webrtc.DataChannel
dc, err = pc.CreateDataChannel(filename, &webrtc.DataChannelInit{ dc, err = pc.CreateDataChannel(filename, &webrtc.DataChannelInit{
MaxRetransmits: &maxRetransmits, MaxRetransmits: &maxRetransmits,
}) })
if err != nil { if err != nil {
return return
} }
dc.SetBufferedAmountLowThreshold(16000000)
bufferedAmountLock := make(chan struct{})
done := make(chan struct{})
dc.OnOpen(func() {
go func() {
defer func() {
bufferedAmountLock = nil
}()
r := bufio.NewReader(file)
buf := make([]byte, 0, 60000)
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
}
logger.Println(readErr)
return
}
if err = dc.Send(buf); err != nil {
logger.Println(err)
}
if dc.BufferedAmount() > dc.
BufferedAmountLowThreshold() {
<-bufferedAmountLock
}
}
logger.Println("done")
_ = dc.SendText("done")
<-time.After(time.Second * 5)
_ = dc.Close()
}()
})
dc.OnBufferedAmountLow(func() {
bufferedAmountLock <- struct{}{}
})
dc.OnClose(func() {
done <- struct{}{}
defer close(done)
_ = atomicallyExecute(fs.filesFlag, func() (err error) {
if f, ok := fs.OpenFiles[filename]; ok {
err = f.Close()
}
delete(fs.OpenFiles, filename)
return
})
})
} else {
err = fmt.Errorf("no peerconnection for id %s", userId)
} }
return return
}) })
if dc != nil {
dc.SetBufferedAmountLowThreshold(15000000)
bufferedAmountLock := make(chan struct{})
done := make(chan struct{})
dc.OnOpen(func() {
go func() {
defer func() {
close(bufferedAmountLock)
bufferedAmountLock = nil
_ = atomicallyExecute(fs.filesFlag, func() (err error) {
if f, ok := fs.OpenFiles[filename]; ok {
err = f.Close()
}
delete(fs.OpenFiles, filename)
if _, ok := fs.OpenFilesForUser[userId]; ok {
var index int
for i, v := range fs.OpenFilesForUser[userId] {
if v == filename {
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
})
}()
r := bufio.NewReader(file)
buf := make([]byte, 0, 15000)
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
}
if err == io.EOF {
break
}
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")
<-time.After(4*time.Second)
_ = dc.SendText("done")
<-time.After(time.Second)
_ = dc.Close()
_ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[userId]; ok {
delete(fs.zoneFSDataChannels[userId], dc.Label())
}
return
})
}()
})
dc.OnBufferedAmountLow(func() {
if bufferedAmountLock != nil {
bufferedAmountLock <- struct{}{}
}
})
dc.OnClose(func() {
done <- struct{}{}
defer close(done)
_ = atomicallyExecute(fs.filesFlag, func() (err error) {
if f, ok := fs.OpenFiles[filename]; ok {
err = f.Close()
}
delete(fs.OpenFiles, filename)
return
})
})
err = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[userId]; !ok {
fs.zoneFSDataChannels[userId] = make(map[string]*DataChannel)
}
l := int32(0)
fs.zoneFSDataChannels[userId][dc.Label()] = &DataChannel{
DataChannel: dc,
l: &l,
}
return
})
} else {
err = fmt.Errorf("datachannel not created")
_ = atomicallyExecute(fs.filesFlag, func() (err error) {
if file, ok := fs.OpenFiles[filename]; ok {
file.Close()
}
delete(fs.OpenFiles, filename)
return
})
}
return return
} }
@ -244,10 +345,10 @@ func (fs *FSInstance) HandleOffer(ctx context.Context, channelId, userId, sdp, h
errCh <- err errCh <- err
return return
} }
_ = atomicallyExecute(fs.localSDMapFlag, func() (err error) { if err = peerConnection.SetLocalDescription(rawAnswer); err != nil {
fs.localSD[userId] = &rawAnswer errCh <- err
return return
}) }
_, _ = sendDCMessage(string(ZONE_FS_WEBRTC_ANSWER), hostId, userId, map[string]interface{}{ _, _ = sendDCMessage(string(ZONE_FS_WEBRTC_ANSWER), hostId, userId, map[string]interface{}{
"to": userId, "to": userId,
"from": fs.ZoneID, "from": fs.ZoneID,
@ -261,25 +362,25 @@ func (fs *FSInstance) HandleOffer(ctx context.Context, channelId, userId, sdp, h
} }
func (fs *FSInstance) HandleCounterOffer(ctx context.Context, userId string, sendDCMessage SendDCMessageFunc) (err error) { func (fs *FSInstance) HandleCounterOffer(ctx context.Context, userId string, sendDCMessage SendDCMessageFunc) (err error) {
if err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) { // if err = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
if _, ok := fs.rtcPeerConnections[userId]; !ok { // if _, ok := fs.rtcPeerConnections[userId]; !ok {
err = fmt.Errorf("no field corresponding peer connection for id %s", userId) // err = fmt.Errorf("no field corresponding peer connection for id %s", userId)
return // return
} // }
logger.Println("handling counter offer") // logger.Println("handling counter offer")
connection := fs.rtcPeerConnections[userId] // connection := fs.rtcPeerConnections[userId]
err = atomicallyExecute(fs.localSDMapFlag, func() (err error) { // err = atomicallyExecute(fs.localSDMapFlag, func() (err error) {
err = connection.SetLocalDescription(*fs.localSD[userId]) // err = connection.SetLocalDescription(*fs.localSD[userId])
return // return
}) // })
return // return
}); err != nil { // }); err != nil {
return // return
} // }
_ = atomicallyExecute(fs.localSDMapFlag, func() (err error) { // _ = atomicallyExecute(fs.localSDMapFlag, func() (err error) {
delete(fs.localSD, userId) // delete(fs.localSD, userId)
return // return
}) // })
if err = atomicallyExecute(fs.candidateFlag, func() (err error) { if err = atomicallyExecute(fs.candidateFlag, func() (err error) {
for _, candidate := range fs.pendingCandidates[userId] { for _, candidate := range fs.pendingCandidates[userId] {
logger.Println("sending candidate to", userId, candidate) logger.Println("sending candidate to", userId, candidate)
@ -288,7 +389,7 @@ func (fs *FSInstance) HandleCounterOffer(ctx context.Context, userId string, sen
"to": userId, "to": userId,
"candidate": candidate.ToJSON().Candidate, "candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid, "sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
}) })
select { select {
case <-d: case <-d:
@ -310,12 +411,12 @@ func (fs *FSInstance) HandleRennegotiationOffer(from, sdp string, sendDCMessage
err = fmt.Errorf("no corresponding peer connection for id %s", from) err = fmt.Errorf("no corresponding peer connection for id %s", from)
return return
} }
fs.rtcPeerConnections[from].makingOfferLock.Lock() // fs.rtcPeerConnections[from].makingOfferLock.Lock()
if fs.rtcPeerConnections[from].makingOffer { // if fs.rtcPeerConnections[from].makingOffer {
fs.rtcPeerConnections[from].makingOfferLock.Unlock() // fs.rtcPeerConnections[from].makingOfferLock.Unlock()
return fmt.Errorf("already making an offer or state is stable") // return fmt.Errorf("already making an offer or state is stable")
} // }
fs.rtcPeerConnections[from].makingOfferLock.Unlock() // fs.rtcPeerConnections[from].makingOfferLock.Unlock()
if err = fs.rtcPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil { if err = fs.rtcPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
return return
} }
@ -452,7 +553,7 @@ func (fs *FSInstance) createPeerConnection(target, from string, peerType webrtc.
peerConnection.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) { peerConnection.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) {
if pcs == webrtc.PeerConnectionStateClosed || pcs == webrtc.PeerConnectionStateDisconnected || pcs == webrtc.PeerConnectionStateFailed { if pcs == webrtc.PeerConnectionStateClosed || pcs == webrtc.PeerConnectionStateDisconnected || pcs == webrtc.PeerConnectionStateFailed {
logger.Println(pcs) logger.Println(pcs)
fs.HandleLeavingMember(target) //fs.HandleLeavingMember(target)
} }
}) })
peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) { peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) {
@ -479,60 +580,38 @@ func (fs *FSInstance) createPeerConnection(target, from string, peerType webrtc.
return return
}) })
}) })
peerConnection.OnNegotiationNeeded(func() { // peerConnection.OnNegotiationNeeded(func() {
logger.Println("---------------- rennego is needed -----------") // logger.Println("---------------- rennego is needed -----------")
_ = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) { // _ = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
logger.Println("----------------- sending renego to peer with id", target) // logger.Println("----------------- sending renego to peer with id", target)
if peerConnection.SignalingState() == webrtc.SignalingStateStable { // if peerConnection.SignalingState() == webrtc.SignalingStateStable {
localSd, localSdErr := peerConnection.CreateOffer(nil) // localSd, localSdErr := peerConnection.CreateOffer(nil)
if localSdErr != nil { // if localSdErr != nil {
logger.Println(localSdErr) // logger.Println(localSdErr)
return localSdErr // return localSdErr
} // }
if err = peerConnection.SetLocalDescription(localSd); err != nil { // if err = peerConnection.SetLocalDescription(localSd); err != nil {
logger.Println(err) // logger.Println(err)
return // return
} // }
d, e := sendDCMessage(string(ZONE_FS_WEBRTC_RENNEGOTIATION_OFFER), fs.ZoneID, target, map[string]interface{}{ // d, e := sendDCMessage(string(ZONE_FS_WEBRTC_RENNEGOTIATION_OFFER), fs.ZoneID, target, map[string]interface{}{
"from": fs.ZoneID, // "from": fs.ZoneID,
"to": target, // "to": target,
"sdp": localSd.SDP, // "sdp": localSd.SDP,
}) // })
select { // select {
case <-d: // case <-d:
case err = <-e: // case err = <-e:
logger.Println(err) // logger.Println(err)
} // }
} // }
return // return
}) // })
}) // })
return return
} }
func (fs *FSInstance) HandleLeavingMember(id string) { func (fs *FSInstance) HandleLeavingMember(id string) {
if err := atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
if _, ok := fs.rtcPeerConnections[id]; !ok {
err = fmt.Errorf("no corresponding peerconnection for audio channel leaving member")
}
return
}); err != nil {
logger.Println(err)
} else {
defer func() {
_ = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
if _, ok := fs.rtcPeerConnections[id]; ok {
if closeErr := fs.rtcPeerConnections[id].Close(); closeErr != nil {
err = closeErr
logger.Println("peer connection close error", closeErr)
}
}
delete(fs.rtcPeerConnections, id)
return
})
}()
}
logger.Printf("peer %s is leaving the squad\n", id)
_ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) { _ = atomicallyExecute(fs.dataChannelMapFlag, func() (err error) {
if _, ok := fs.zoneFSDataChannels[id]; ok { if _, ok := fs.zoneFSDataChannels[id]; ok {
for _, dc := range fs.zoneFSDataChannels[id] { for _, dc := range fs.zoneFSDataChannels[id] {
@ -542,14 +621,32 @@ func (fs *FSInstance) HandleLeavingMember(id string) {
delete(fs.zoneFSDataChannels, id) delete(fs.zoneFSDataChannels, id)
return return
}) })
_ = atomicallyExecute(fs.localSDMapFlag, func() (err error) { logger.Println("chatfs datachannels cleaned")
delete(fs.localSD, id) _ = atomicallyExecute(fs.rtcPeerConnectionMapFlag, func() (err error) {
if pc, ok := fs.rtcPeerConnections[id]; ok {
if closeErr := pc.Close(); closeErr != nil {
err = closeErr
logger.Println("peer connection close error", closeErr)
}
}
delete(fs.rtcPeerConnections, id)
return return
}) })
logger.Println("chats perrconnections cleaned")
_ = atomicallyExecute(fs.candidateFlag, func() (err error) { _ = atomicallyExecute(fs.candidateFlag, func() (err error) {
delete(fs.pendingCandidates, id) delete(fs.pendingCandidates, id)
return return
}) })
_ = atomicallyExecute(fs.filesFlag, func() (err error) {
for _, openFile := range fs.OpenFilesForUser[id] {
if f, ok := fs.OpenFiles[openFile]; ok {
_ = f.Close()
}
delete(fs.OpenFiles, openFile)
}
delete(fs.OpenFilesForUser, id)
return
})
} }
func (fs *FSInstance) HandleDataChannelEvents(from, eventId string, payload map[string]interface{}) (err error) { func (fs *FSInstance) HandleDataChannelEvents(from, eventId string, payload map[string]interface{}) (err error) {

View File

@ -2,6 +2,7 @@ package localserver
import ( import (
"context" "context"
"encoding/json"
"strconv" "strconv"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
@ -23,11 +24,13 @@ const (
NEW_ZONE ReqType = "new_zone" NEW_ZONE ReqType = "new_zone"
NEW_AUTHORIZED_ZONE_MEMBER ReqType = "new_authorized_zone_member" NEW_AUTHORIZED_ZONE_MEMBER ReqType = "new_authorized_zone_member"
REMOVED_ZONE_AUTHORIZED_MEMBER ReqType = "removed_zone_authorized_member" REMOVED_ZONE_AUTHORIZED_MEMBER ReqType = "removed_zone_authorized_member"
DELETE_ZONE = "delete_zone"
DISCONNECT_ZONE_MEMBER = "disconnect_zone_member"
) )
type ZoneGrpcMiddleware struct { type ZoneGrpcMiddleware struct {
Manager *ZoneManager Manager *ZoneManager
stream GrpcManager_LinkClient stream SignalingService_LinkClient
} }
func NewZoneGrpcMiddleware(manager *ZoneManager) (zoneGrpcMiddleware *ZoneGrpcMiddleware) { func NewZoneGrpcMiddleware(manager *ZoneManager) (zoneGrpcMiddleware *ZoneGrpcMiddleware) {
@ -38,55 +41,72 @@ func NewZoneGrpcMiddleware(manager *ZoneManager) (zoneGrpcMiddleware *ZoneGrpcMi
} }
func (zm *ZoneGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) { func (zm *ZoneGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) {
err = zm.stream.Send(&Request{ bs, err := json.Marshal(map[string]string{
Type: string(ZONE_WEBRTC_CANDIDATE), "from": zm.Manager.ID,
From: zm.Manager.ID, "to": to,
Token: "none", "candidate": candidate.ToJSON().Candidate,
Payload: map[string]string{ "sdpMid": *candidate.ToJSON().SDPMid,
"from": zm.Manager.ID, "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
"to": to, })
"candidate": candidate.ToJSON().Candidate, if err != nil {
"sdpMid": *candidate.ToJSON().SDPMid, return
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), }
}, err = zm.stream.Send(&SignalingMessage{
Type: string(ZONE_WEBRTC_CANDIDATE),
From: zm.Manager.ID,
To: to,
Payload: bs,
}) })
return return
} }
func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *Response, stream GrpcManager_LinkClient) (err error) { func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) {
done, errCh := make(chan struct{}), make(chan error) done, errCh := make(chan struct{}), make(chan error)
go func() { go func() {
var payload map[string]string
if e := json.Unmarshal(req.Payload, &payload); err != nil {
errCh <- e
return
}
switch req.Type { switch req.Type {
case string(INCOMING_ZONE_MEMBER): case string(INCOMING_ZONE_MEMBER):
case string(LEAVING_ZONE_MEMBER): case string(DELETE_ZONE):
if err := validateRequest(req.GetPayload(), "zoneId", "userId"); err != nil { if err := validateRequest(payload, "zoneId"); err != nil {
return return
} }
if err := zm.Manager.HandleLeavingMember(req.Payload["userId"], req.Payload["zoneId"]); err != nil { if err := zm.Manager.DeleteZone(payload["zoneId"]); err != nil {
errCh <- err
return
}
case string(LEAVING_ZONE_MEMBER):
if err := validateRequest(payload, "zoneId"); err != nil {
return
}
if err := zm.Manager.HandleLeavingMember(req.GetFrom(), payload["zoneId"]); err != nil {
errCh <- err errCh <- err
return return
} }
case string(REMOVED_ZONE_AUTHORIZED_MEMBER): case string(REMOVED_ZONE_AUTHORIZED_MEMBER):
if err := validateRequest(req.GetPayload(), "zoneId", "userId"); err != nil { if err := validateRequest(payload, "zoneId", "userId"); err != nil {
return return
} }
if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) { if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) {
reqChan := make(chan *ZoneRequest) reqChan := make(chan *ZoneRequest)
done, e := zm.Manager.Zones[req.Payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan) done, e := zm.Manager.Zones[payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan)
go func() { go func() {
defer close(reqChan) defer close(reqChan)
reqChan <- &ZoneRequest{ reqChan <- &ZoneRequest{
ReqType: string(REMOVE_USER), ReqType: string(REMOVE_USER),
From: req.Payload["userId"], From: payload["userId"],
Payload: map[string]interface{}{ Payload: map[string]interface{}{
"userId": req.Payload["userId"], "userId": payload["userId"],
}, },
} }
reqChan <- &ZoneRequest{ reqChan <- &ZoneRequest{
ReqType: string(REMOVED_ZONE_AUTHORIZED_MEMBER), ReqType: string(REMOVED_ZONE_AUTHORIZED_MEMBER),
From: req.Payload["userId"], From: payload["userId"],
Payload: map[string]interface{}{ Payload: map[string]interface{}{
"userId": req.Payload["userId"], "userId": payload["userId"],
}, },
} }
}() }()
@ -100,26 +120,26 @@ func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *Response, stream
return return
} }
case string(NEW_AUTHORIZED_ZONE_MEMBER): case string(NEW_AUTHORIZED_ZONE_MEMBER):
if err := validateRequest(req.GetPayload(), "zoneId", "userId"); err != nil { if err := validateRequest(payload, "zoneId", "userId"); err != nil {
return return
} }
if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) { if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) {
reqChan := make(chan *ZoneRequest) reqChan := make(chan *ZoneRequest)
done, e := zm.Manager.Zones[req.Payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan) done, e := zm.Manager.Zones[payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan)
go func() { go func() {
defer close(reqChan) defer close(reqChan)
reqChan <- &ZoneRequest{ reqChan <- &ZoneRequest{
ReqType: string(ADD_USER), ReqType: string(ADD_USER),
From: req.Payload["userId"], From: payload["userId"],
Payload: map[string]interface{}{ Payload: map[string]interface{}{
"userId": req.Payload["userId"], "userId": payload["userId"],
}, },
} }
reqChan <- &ZoneRequest{ reqChan <- &ZoneRequest{
ReqType: string(NEW_AUTHORIZED_ZONE_MEMBER), ReqType: string(NEW_AUTHORIZED_ZONE_MEMBER),
From: req.Payload["userId"], From: payload["userId"],
Payload: map[string]interface{}{ Payload: map[string]interface{}{
"userId": req.Payload["userId"], "userId": payload["userId"],
}, },
} }
}() }()
@ -133,12 +153,12 @@ func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *Response, stream
return return
} }
case string(NEW_ZONE): case string(NEW_ZONE):
logger.Println(req.Payload) logger.Println(payload)
if err := validateRequest(req.GetPayload(), "zoneId", "zoneName", "zoneImageURL", "zoneOwner", "zoneCreationDate"); err != nil { if err := validateRequest(payload, "zoneId", "zoneName", "zoneImageURL", "zoneOwner", "zoneCreationDate"); err != nil {
errCh <- err errCh <- err
return return
} }
zone, err := NewZone(zm.Manager.ID, req.Payload["zoneId"], req.Payload["zoneName"], req.Payload["zoneImageURL"], req.Payload["zoneOwner"], req.Payload["zoneCreationDate"], true, []string{req.Payload["zoneOwner"]}) zone, err := NewZone(zm.Manager.ID, payload["zoneId"], payload["zoneName"], payload["zoneImageURL"], payload["zoneOwner"], payload["zoneCreationDate"], true, []string{payload["zoneOwner"]})
if err != nil { if err != nil {
errCh <- err errCh <- err
return return
@ -148,77 +168,73 @@ func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *Response, stream
return return
}) })
case string(ZONE_OFFER): case string(ZONE_OFFER):
if err := validateRequest(req.GetPayload(), FROM, TO, SDP); err != nil { if err := validateRequest(payload, SDP); err != nil {
errCh <- err errCh <- err
return return
} }
if err := zm.Manager.HandleOffer(ctx, req.GetPayload(), zm.signalCandidate); err != nil { if err := zm.Manager.HandleOffer(ctx, req.GetFrom(), req.GetTo(), payload, zm.signalCandidate); err != nil {
errCh <- err errCh <- err
return return
} }
case string(ZONE_ANSWER): case string(ZONE_ANSWER):
if err := validateRequest(req.GetPayload(), FROM, TO, SDP); err != nil { if err := validateRequest(payload, SDP); err != nil {
errCh <- err errCh <- err
return return
} }
if err := zm.Manager.HandleAnswer(ctx, req.GetPayload()); err != nil { if err := zm.Manager.HandleAnswer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
errCh <- err errCh <- err
return return
} }
case string(ZONE_COUNTER_OFFER): case string(ZONE_COUNTER_OFFER):
if err := validateRequest(req.GetPayload(), FROM); err != nil { if err := zm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
errCh <- err
return
}
if err := zm.Manager.HandleCounterOffer(ctx, req.Payload); err != nil {
errCh <- err errCh <- err
return return
} }
case string(ZONE_WEBRTC_RENNEGOTIATION_OFFER): case string(ZONE_WEBRTC_RENNEGOTIATION_OFFER):
logger.Println("received negotiation offer") logger.Println("received negotiation offer")
if err := validateRequest(req.GetPayload(), FROM, SDP); err != nil { if err := validateRequest(payload, SDP); err != nil {
errCh <- err errCh <- err
return return
} }
if err := zm.Manager.HandleRennegotiationOffer(req.Payload[FROM], req.Payload[SDP]); err != nil { if err := zm.Manager.HandleRennegotiationOffer(req.GetFrom(), payload[SDP]); err != nil {
errCh <- err errCh <- err
return return
} }
case string(ZONE_WEBRTC_RENNEGOTIATION_ANSWER): case string(ZONE_WEBRTC_RENNEGOTIATION_ANSWER):
logger.Println("received negotiation answer") logger.Println("received negotiation answer")
if err := validateRequest(req.GetPayload(), FROM, SDP); err != nil { if err := validateRequest(payload, SDP); err != nil {
errCh <- err errCh <- err
return return
} }
if err := zm.Manager.HandleRennegotiationAnswer(req.Payload[FROM], req.Payload[SDP]); err != nil { if err := zm.Manager.HandleRennegotiationAnswer(req.GetFrom(), payload[SDP]); err != nil {
errCh <- err errCh <- err
return return
} }
case string(ZONE_WEBRTC_CANDIDATE): case string(ZONE_WEBRTC_CANDIDATE):
if err := validateRequest(req.GetPayload(), FROM, "candidate", "sdpMlineIndex", "sdpMid"); err != nil { if err := validateRequest(payload, "candidate", "sdpMLineIndex", "sdpMid"); err != nil {
errCh <- err errCh <- err
return return
} }
logger.Println(req.Payload) logger.Println(payload)
i, err := strconv.Atoi(req.Payload["sdpMlineIndex"]) i, err := strconv.Atoi(payload["sdpMLineIndex"])
if err != nil { if err != nil {
errCh <- err errCh <- err
return return
} }
sdpMlineIndex := uint16(i) SDPMLineIndex := uint16(i)
sdpMid := req.Payload["sdpMid"] sdpMid := payload["sdpMid"]
logger.Println(sdpMid, sdpMlineIndex) logger.Println(sdpMid, SDPMLineIndex)
if err := zm.Manager.AddCandidate(&webrtc.ICECandidateInit{ if err := zm.Manager.AddCandidate(&webrtc.ICECandidateInit{
Candidate: req.Payload["candidate"], Candidate: payload["candidate"],
SDPMid: &sdpMid, SDPMid: &sdpMid,
SDPMLineIndex: &sdpMlineIndex, SDPMLineIndex: &SDPMLineIndex,
}, req.Payload[FROM]); err != nil { }, req.GetFrom()); err != nil {
errCh <- err errCh <- err
return return
} }
default: default:
logger.Println("no request for zon grpc middleware") logger.Println("no request for zon grpc middleware")
logger.Println(req.GetPayload()) logger.Println(payload)
logger.Println(req.Type) logger.Println(req.Type)
} }
done <- struct{}{} done <- struct{}{}

View File

@ -7,10 +7,13 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"os"
"path/filepath"
"strconv" "strconv"
sync "sync" sync "sync"
"sync/atomic" "sync/atomic"
"github.com/google/uuid"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
) )
@ -26,7 +29,7 @@ type ZoneManager struct {
RTCPeerConnections map[string]*RTCPeerConnection RTCPeerConnections map[string]*RTCPeerConnection
DataChannels map[string]*DataChannel DataChannels map[string]*DataChannel
PendingCandidates map[string][]*webrtc.ICECandidate PendingCandidates map[string][]*webrtc.ICECandidate
stream GrpcManager_LinkClient stream SignalingService_LinkClient
zoneFlag *uint32 zoneFlag *uint32
peerConnectionFlag *uint32 peerConnectionFlag *uint32
localSDFlag *uint32 localSDFlag *uint32
@ -49,7 +52,7 @@ type Zone struct {
func NewZone(hostId string, zoneId string, zoneName string, imageUrl string, owner string, creationDate string, initialized bool, authorizedMembers []string) (zone *Zone, err error) { func NewZone(hostId string, zoneId string, zoneName string, imageUrl string, owner string, creationDate string, initialized bool, authorizedMembers []string) (zone *Zone, err error) {
dataChannels, dataChannelFlag := make(map[string]*DataChannel), uint32(0) dataChannels, dataChannelFlag := make(map[string]*DataChannel), uint32(0)
zoneChatHandler, err := NewZoneChatsHandler(zoneId, owner, authorizedMembers, dataChannels, &dataChannelFlag) zoneChatHandler, err := NewZoneChatsHandler(hostId, zoneId, owner, authorizedMembers, dataChannels, &dataChannelFlag)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -69,7 +72,7 @@ func NewZone(hostId string, zoneId string, zoneName string, imageUrl string, own
if err != nil { if err != nil {
return return
} }
zoneScheduler, e := NewZoneRequestScheduler(authorizedMembers, zoneUsersHandler, zoneChatHandler, zoneAudioChannelsHandler, zoneVideoChannelsHandler, zoneFileHandler) zoneScheduler, e := NewZoneRequestScheduler(authorizedMembers, zoneUsersHandler, zoneAudioChannelsHandler, zoneVideoChannelsHandler, zoneFileHandler, zoneChatHandler)
go func() { go func() {
for schedErr := range e { for schedErr := range e {
logger.Println("from scheduler :", schedErr) logger.Println("from scheduler :", schedErr)
@ -108,6 +111,10 @@ func NewZoneManager(id string, token string) (zoneManager *ZoneManager, err erro
} }
zoneMap[zone.ID] = z zoneMap[zone.ID] = z
} }
zonesFolder,err := os.ReadDir(filepath.Join("data", "zones"))
if err != nil {
return nil, err
}
logger.Println(zoneMap) logger.Println(zoneMap)
zoneManager = &ZoneManager{ zoneManager = &ZoneManager{
ID: id, ID: id,
@ -122,6 +129,11 @@ func NewZoneManager(id string, token string) (zoneManager *ZoneManager, err erro
dataChannelFlag: &dataChannelFlag, dataChannelFlag: &dataChannelFlag,
candidateFlag: &candidateFlag, candidateFlag: &candidateFlag,
} }
for _, z := range zonesFolder {
if _,ok := zoneMap[z.Name()]; !ok {
logger.Println(zoneManager.DeleteZone(z.Name()))
}
}
return return
} }
@ -136,13 +148,32 @@ func atomicallyExecute(flag *uint32, job func() (err error)) (err error) {
return return
} }
func (zm *ZoneManager) DeleteZone(zoneId string) {} func (zm *ZoneManager) 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 *ZoneManager) DeleteZone(zoneId string) error {
return os.RemoveAll(filepath.Join("data", "zones",zoneId))
}
func (zm *ZoneManager) fetchZones(nodeId string, token string) (zones []*Zone, err error) { func (zm *ZoneManager) fetchZones(nodeId string, token string) (zones []*Zone, err error) {
em := NewEncryptionManager()
sig := em.SignRequestHMAC(nodeId)
body, err := json.Marshal(map[string]interface{}{ body, err := json.Marshal(map[string]interface{}{
"type": LIST_ZONES_BY_HOST, "type": LIST_ZONES_BY_HOST,
"token": token, "mac": sig,
"from": nodeId, "from": nodeId,
"peerType":"node",
"payload": map[string]string{ "payload": map[string]string{
"host": nodeId, "host": nodeId,
"lastIndex": "0", "lastIndex": "0",
@ -160,7 +191,15 @@ func (zm *ZoneManager) fetchZones(nodeId string, token string) (zones []*Zone, e
if err != nil { if err != nil {
return return
} }
err = json.Unmarshal(bs, &zones) var payload map[string]any
if err = json.Unmarshal(bs, &payload); err != nil {
return
}
b, err := json.Marshal(payload["zones"])
if err != nil {
return
}
err = json.Unmarshal(b, &zones)
return return
} }
@ -178,8 +217,10 @@ func (zm *ZoneManager) CreateOffer(ctx context.Context, target string, from stri
return return
} }
_ = atomicallyExecute(zm.peerConnectionFlag, func() (err error) { _ = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
id := uuid.New().String()
logger.Println("adding for target", target) logger.Println("adding for target", target)
zm.RTCPeerConnections[target] = &RTCPeerConnection{ zm.RTCPeerConnections[target] = &RTCPeerConnection{
id: id,
PeerConnection: peerConnection, PeerConnection: peerConnection,
makingOffer: true, makingOffer: true,
makingOfferLock: &sync.Mutex{}, makingOfferLock: &sync.Mutex{},
@ -187,20 +228,15 @@ func (zm *ZoneManager) CreateOffer(ctx context.Context, target string, from stri
} }
return return
}) })
err = zm.stream.Send(&Request{ err = zm.sendSignalingMessage(string(ZONE_OFFER), zm.ID, target, map[string]any{
Type: string(ZONE_OFFER), "to": target,
From: zm.ID, "from": zm.ID,
Token: "none", "sdp": rawOffer.SDP,
Payload: map[string]string{
"to": target,
"from": zm.ID,
"sdp": rawOffer.SDP,
},
}) })
return return
} }
func (zm *ZoneManager) HandleOffer(ctx context.Context, req map[string]string, cb OnICECandidateFunc) (err error) { func (zm *ZoneManager) HandleOffer(ctx context.Context, from string, to string, req map[string]string, cb OnICECandidateFunc) (err error) {
done, errCh := make(chan struct{}), make(chan error) done, errCh := make(chan struct{}), make(chan error)
go func() { go func() {
if _, ok := zm.Zones[req["zoneId"]]; !ok { if _, ok := zm.Zones[req["zoneId"]]; !ok {
@ -209,21 +245,31 @@ func (zm *ZoneManager) HandleOffer(ctx context.Context, req map[string]string, c
return return
} }
logger.Println("handling zone offer") logger.Println("handling zone offer")
peerConnection, err := zm.createPeerConnection(req[FROM], req[TO], req["zoneId"], webrtc.SDPTypeAnswer, cb)
if _,ok := zm.RTCPeerConnections[from]; ok {
if e := zm.HandleLeavingMember(from,req["zoneId"]); e != nil {
logger.Println(e)
}
}
peerConnection, err := zm.createPeerConnection(from, to, req["zoneId"], webrtc.SDPTypeAnswer, cb)
if err != nil { if err != nil {
errCh <- err errCh <- err
return return
} }
logger.Println("peer connection created") logger.Println("peer connection created")
_ = atomicallyExecute(zm.peerConnectionFlag, func() (err error) { _ = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
zm.RTCPeerConnections[req[FROM]] = &RTCPeerConnection{ id := uuid.New().String()
zm.RTCPeerConnections[from] = &RTCPeerConnection{
PeerConnection: peerConnection, PeerConnection: peerConnection,
id: id,
makingOffer: false, makingOffer: false,
makingOfferLock: &sync.Mutex{}, makingOfferLock: &sync.Mutex{},
negotiate: zm.negotiate, negotiate: zm.negotiate,
} }
return return
}) })
logger.Println("peer connection added to map")
offer := webrtc.SessionDescription{ offer := webrtc.SessionDescription{
Type: webrtc.SDPTypeOffer, Type: webrtc.SDPTypeOffer,
SDP: req[SDP], SDP: req[SDP],
@ -238,23 +284,17 @@ func (zm *ZoneManager) HandleOffer(ctx context.Context, req map[string]string, c
return return
} }
_ = atomicallyExecute(zm.localSDFlag, func() (err error) { _ = atomicallyExecute(zm.localSDFlag, func() (err error) {
zm.LocalSD[req[FROM]] = &rawAnswer zm.LocalSD[from] = &rawAnswer
return return
}) })
if err = peerConnection.SetLocalDescription(rawAnswer); err != nil {
_ = atomicallyExecute(zm.zoneFlag, func() (err error) { errCh <- err
//zm.Zones[req[SQUAD_ID]].Members = append(zm.Squads[req[SQUAD_ID]].Members, req[FROM])
return return
}) }
if err = zm.stream.Send(&Request{ if err = zm.sendSignalingMessage(string(ZONE_ANSWER), zm.ID, from, map[string]any{
Type: string(ZONE_ANSWER), "to": from,
From: zm.ID, "from": zm.ID,
Token: "none", "sdp": rawAnswer.SDP,
Payload: map[string]string{
"to": req[FROM],
"from": zm.ID,
"sdp": rawAnswer.SDP,
},
}); err != nil { }); err != nil {
errCh <- err errCh <- err
return return
@ -272,18 +312,18 @@ func (zm *ZoneManager) HandleOffer(ctx context.Context, req map[string]string, c
} }
} }
func (zm *ZoneManager) HandleAnswer(ctx context.Context, req map[string]string) (err error) { func (zm *ZoneManager) HandleAnswer(ctx context.Context, from string, to string, req map[string]string) (err error) {
defer func() { defer func() {
if r := recover(); err != nil { if r := recover(); err != nil {
logger.Printf("recover from panic in handle answer : %v\n", r) logger.Printf("recover from panic in handle answer : %v\n", r)
} }
}() }()
if err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) { if err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
if _, ok := zm.RTCPeerConnections[req[FROM]]; !ok { if _, ok := zm.RTCPeerConnections[from]; !ok {
err = fmt.Errorf("no corresponding peer connection for id : %s", req[FROM]) err = fmt.Errorf("no corresponding peer connection for id : %s", from)
return return
} }
peerConnnection := zm.RTCPeerConnections[req[FROM]] peerConnnection := zm.RTCPeerConnections[from]
logger.Println("---------------------") logger.Println("---------------------")
logger.Println(req[SDP]) logger.Println(req[SDP])
logger.Println("---------------------") logger.Println("---------------------")
@ -298,92 +338,77 @@ func (zm *ZoneManager) HandleAnswer(ctx context.Context, req map[string]string)
}); err != nil { }); err != nil {
return return
} }
if err = zm.stream.Send(&Request{ if err = zm.sendSignalingMessage(string(ZONE_COUNTER_OFFER), zm.ID, from, map[string]any{
Type: string(ZONE_COUNTER_OFFER), "from": zm.ID,
From: zm.ID, "to": from,
Token: "none",
Payload: map[string]string{
"from": zm.ID,
"to": req[FROM],
},
}); err != nil { }); err != nil {
return return
} }
_ = atomicallyExecute(zm.candidateFlag, func() (err error) { _ = atomicallyExecute(zm.candidateFlag, func() (err error) {
for _, candidate := range zm.PendingCandidates[req[FROM]] { for _, candidate := range zm.PendingCandidates[from] {
logger.Println("sending candidate from answer to", req[FROM]) logger.Println("sending candidate from answer to", from)
if err = zm.stream.Send(&Request{ if err = zm.sendSignalingMessage(string(ZONE_WEBRTC_CANDIDATE), zm.ID, from, map[string]any{
Type: string(ZONE_WEBRTC_CANDIDATE), "from": zm.ID,
From: zm.ID, "to": from,
Token: "none", "candidate": candidate.ToJSON().Candidate,
Payload: map[string]string{ "sdpMid": *candidate.ToJSON().SDPMid,
"from": zm.ID, "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
"to": req[FROM],
"candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
},
}); err != nil { }); err != nil {
logger.Println(err) logger.Println(err)
continue continue
} }
} }
delete(zm.PendingCandidates, req[FROM]) delete(zm.PendingCandidates, from)
return return
}) })
_ = atomicallyExecute(zm.localSDFlag, func() (err error) { _ = atomicallyExecute(zm.localSDFlag, func() (err error) {
delete(zm.LocalSD, req[FROM]) delete(zm.LocalSD, from)
return return
}) })
return return
} }
func (zm *ZoneManager) HandleCounterOffer(ctx context.Context, req map[string]string) (err error) { func (zm *ZoneManager) HandleCounterOffer(ctx context.Context, from string, to string, req map[string]string) (err error) {
logger.Println("handling counter offer 1") logger.Println("handling counter offer 1")
if err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) { // if err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
logger.Println("start job") // logger.Println("start job")
if _, ok := zm.RTCPeerConnections[req[FROM]]; !ok { // if _, ok := zm.RTCPeerConnections[from]; !ok {
logger.Println("error here") // logger.Println("error here")
err = fmt.Errorf("no field corresponding peer connection for id %s", req[FROM]) // err = fmt.Errorf("no field corresponding peer connection for id %s", from)
return // return
} // }
logger.Println("handling counter offer") // logger.Println("handling counter offer")
connection := zm.RTCPeerConnections[req[FROM]] // connection := zm.RTCPeerConnections[from]
err = atomicallyExecute(zm.localSDFlag, func() (err error) { // err = atomicallyExecute(zm.localSDFlag, func() (err error) {
if err = connection.SetLocalDescription(*zm.LocalSD[req[FROM]]); err != nil { // if err = connection.SetLocalDescription(*zm.LocalSD[from]); err != nil {
logger.Println(err) // logger.Println(err)
return // return
} // }
return // return
}) // })
return // return
}); err != nil { // }); err != nil {
return // return
} // }
logger.Println("handling counter offer 2") logger.Println("handling counter offer 2")
_ = atomicallyExecute(zm.candidateFlag, func() (err error) { _ = atomicallyExecute(zm.candidateFlag, func() (err error) {
for _, candidate := range zm.PendingCandidates[req[FROM]] { for _, candidate := range zm.PendingCandidates[from] {
logger.Println("sending candidate to", req[FROM]) logger.Println("sending candidate to", from)
if err = zm.stream.Send(&Request{ if err = zm.sendSignalingMessage(string(ZONE_WEBRTC_CANDIDATE), zm.ID, from, map[string]any{
Type: string(ZONE_WEBRTC_CANDIDATE), "from": zm.ID,
From: zm.ID, "to": from,
Token: "none", "candidate": candidate.ToJSON().Candidate,
Payload: map[string]string{ "sdpMid": *candidate.ToJSON().SDPMid,
"from": zm.ID, "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
"to": req[FROM],
"candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
},
}); err != nil { }); err != nil {
return return
} }
} }
delete(zm.PendingCandidates, req[FROM]) delete(zm.PendingCandidates, from)
return return
}) })
_ = atomicallyExecute(zm.localSDFlag, func() (err error) { _ = atomicallyExecute(zm.localSDFlag, func() (err error) {
delete(zm.LocalSD, req[FROM]) delete(zm.LocalSD, from)
return return
}) })
return return
@ -409,7 +434,7 @@ func (zm *ZoneManager) createPeerConnection(target string, from string, zoneId s
return return
} }
logger.Println("---------------------------------------------------") logger.Println("---------------------------------------------------")
if peerType == webrtc.SDPTypeAnswer { if peerType == webrtc.SDPTypeOffer {
maxRetransmits := uint16(100) maxRetransmits := uint16(100)
channel, err := peerConnection.CreateDataChannel("data", &webrtc.DataChannelInit{ channel, err := peerConnection.CreateDataChannel("data", &webrtc.DataChannelInit{
MaxRetransmits: &maxRetransmits, MaxRetransmits: &maxRetransmits,
@ -446,13 +471,16 @@ func (zm *ZoneManager) createPeerConnection(target string, from string, zoneId s
logger.Println("error in open channel send", sendErr) logger.Println("error in open channel send", sendErr)
return return
} }
for { go func() {
select { for {
case <-done: select {
return case <-done:
case <-err: return
case e := <-err:
logger.Println("----- error from scheduler:", e)
}
} }
} }()
} }
}) })
channel.OnClose(func() { channel.OnClose(func() {
@ -494,11 +522,17 @@ func (zm *ZoneManager) createPeerConnection(target string, from string, zoneId s
if _, ok := zm.Zones[zoneId]; ok { if _, ok := zm.Zones[zoneId]; ok {
logger.Println("this zone exist") logger.Println("this zone exist")
_ = atomicallyExecute(zm.Zones[zoneId].DataChannelsFlag, func() (err error) { _ = atomicallyExecute(zm.Zones[zoneId].DataChannelsFlag, func() (err error) {
logger.Println("adding dc to dc map")
x := int32(0) x := int32(0)
zm.Zones[zoneId].DataChannels[target] = &DataChannel{DataChannel: dc, bufferedAmountLowThresholdReached: make(<-chan struct{}), l: &x} zm.Zones[zoneId].DataChannels[target] = &DataChannel{DataChannel: dc, bufferedAmountLowThresholdReached: make(<-chan struct{}), l: &x}
return return
}) })
if _, ok := zm.Zones[zoneId]; !ok {
err = fmt.Errorf("no corresponding zones")
return
}
done, err := zm.Zones[zoneId].ZoneRequestScheduler.Schedule(reqChan) done, err := zm.Zones[zoneId].ZoneRequestScheduler.Schedule(reqChan)
bs, jsonErr := json.Marshal(&ZoneResponse{ bs, jsonErr := json.Marshal(&ZoneResponse{
Type: "user_zone_init", Type: "user_zone_init",
From: zoneId, From: zoneId,
@ -513,18 +547,24 @@ func (zm *ZoneManager) createPeerConnection(target string, from string, zoneId s
logger.Println("error in open channel send", sendErr) logger.Println("error in open channel send", sendErr)
return return
} }
for { go func() {
select { for {
case <-done: select {
return case <-done:
case <-err: return
case <-err:
}
} }
} }()
} }
}) })
dc.OnClose(func() { dc.OnClose(func() {
close(reqChan) close(reqChan)
}) })
dc.OnError(func(err error) {
logger.Println("--------------- error in dc:", err)
close(reqChan)
})
dc.OnMessage(func(msg webrtc.DataChannelMessage) { dc.OnMessage(func(msg webrtc.DataChannelMessage) {
var req ZoneRequest var req ZoneRequest
if err := json.Unmarshal(msg.Data, &req); err != nil { if err := json.Unmarshal(msg.Data, &req); err != nil {
@ -534,44 +574,53 @@ func (zm *ZoneManager) createPeerConnection(target string, from string, zoneId s
logger.Println("incoming request", req) logger.Println("incoming request", req)
reqChan <- &req reqChan <- &req
}) })
_ = atomicallyExecute(zm.dataChannelFlag, func() (err error) {
l := int32(0)
zm.DataChannels[target] = &DataChannel{
DataChannel: dc,
bufferedAmountLowThresholdReached: make(<-chan struct{}),
l: &l,
}
return
})
}) })
} }
peerConnection.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) { peerConnection.OnConnectionStateChange(func(pcs webrtc.PeerConnectionState) {
if pcs == webrtc.PeerConnectionStateClosed || pcs == webrtc.PeerConnectionStateDisconnected || pcs == webrtc.PeerConnectionStateFailed { if pcs == webrtc.PeerConnectionStateDisconnected || pcs == webrtc.PeerConnectionStateFailed {
logger.Println(pcs) logger.Println(pcs)
if err = zm.HandleLeavingMember(target, zoneId); err != nil { if err = zm.HandleLeavingMember(target, zoneId); err != nil {
logger.Println(err) logger.Println(err)
} }
} }
}) })
peerConnection.OnNegotiationNeeded(func() { // peerConnection.OnNegotiationNeeded(func() {
logger.Println("------------------- negotiation is needed --------------------") // logger.Println("------------------- negotiation is needed --------------------")
if pc, ok := zm.RTCPeerConnections[target]; ok { // if pc, ok := zm.RTCPeerConnections[target]; ok {
if pc.SignalingState() == webrtc.ICETransportStateConnected { // if pc.SignalingState() == webrtc.ICETransportStateConnected {
localSd, err := pc.CreateOffer(nil) // localSd, err := pc.CreateOffer(nil)
if err != nil { // if err != nil {
logger.Println(err) // logger.Println(err)
return // return
} // }
if err = pc.SetLocalDescription(localSd); err != nil { // if err = pc.SetLocalDescription(localSd); err != nil {
logger.Println(err) // logger.Println(err)
return // return
} // }
if err = zm.stream.Send(&Request{ // if err = zm.stream.Send(&Request{
Type: string(ZONE_WEBRTC_RENNEGOTIATION_OFFER), // Type: string(ZONE_WEBRTC_RENNEGOTIATION_OFFER),
From: zm.ID, // From: zm.ID,
Token: "", // Token: "",
Payload: map[string]string{ // Payload: map[string]string{
"to": target, // "to": target,
"sdp": localSd.SDP, // "sdp": localSd.SDP,
}, // },
}); err != nil { // }); err != nil {
logger.Println(err) // logger.Println(err)
return // return
} // }
} // }
} // }
}) // })
peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) { peerConnection.OnICEConnectionStateChange(func(is webrtc.ICEConnectionState) {
logger.Printf("ICE connection state has changed %s\n", is.String()) logger.Printf("ICE connection state has changed %s\n", is.String())
if is == webrtc.ICEConnectionStateDisconnected || is == webrtc.ICEConnectionStateFailed { if is == webrtc.ICEConnectionStateDisconnected || is == webrtc.ICEConnectionStateFailed {
@ -605,13 +654,13 @@ func (zm *ZoneManager) HandleRennegotiationOffer(from string, sdp string) (err e
err = fmt.Errorf("no corresponding peer connection for id %s", from) err = fmt.Errorf("no corresponding peer connection for id %s", from)
return return
} }
zm.RTCPeerConnections[from].makingOfferLock.Lock() //zm.RTCPeerConnections[from].makingOfferLock.Lock()
if zm.RTCPeerConnections[from].makingOffer { // if zm.RTCPeerConnections[from].makingOffer {
zm.RTCPeerConnections[from].makingOfferLock.Unlock() // //zm.RTCPeerConnections[from].makingOfferLock.Unlock()
err = fmt.Errorf("already making an offer or state is stable") // err = fmt.Errorf("already making an offer or state is stable")
return // return
} // }
zm.RTCPeerConnections[from].makingOfferLock.Unlock() //zm.RTCPeerConnections[from].makingOfferLock.Unlock()
if err = zm.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil { if err = zm.RTCPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
return return
} }
@ -622,14 +671,9 @@ func (zm *ZoneManager) HandleRennegotiationOffer(from string, sdp string) (err e
if err = zm.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil { if err = zm.RTCPeerConnections[from].SetLocalDescription(localSd); err != nil {
return return
} }
if err = zm.stream.Send(&Request{ if err = zm.sendSignalingMessage(string(ZONE_WEBRTC_RENNEGOTIATION_ANSWER), zm.ID, from, map[string]any{
Type: string(ZONE_WEBRTC_RENNEGOTIATION_ANSWER), "to": from,
From: zm.ID, "sdp": localSd.SDP,
Token: "",
Payload: map[string]string{
"to": from,
"sdp": localSd.SDP,
},
}); err != nil { }); err != nil {
logger.Println(err) logger.Println(err)
return return
@ -664,6 +708,10 @@ func (zm *ZoneManager) AddCandidate(candidate *webrtc.ICECandidateInit, from str
} }
func (zm *ZoneManager) HandleLeavingMember(id string, zoneId string) (err error) { func (zm *ZoneManager) HandleLeavingMember(id string, zoneId string) (err error) {
defer func () {
logger.Println(zm.notifyLeavingMember(id,zoneId,zm.ID))
}()
logger.Println("---------------- handling leaving member", id)
if err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) { if err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
if _, ok := zm.RTCPeerConnections[id]; !ok { if _, ok := zm.RTCPeerConnections[id]; !ok {
err = fmt.Errorf("no correponding peerconnection for id %s", id) err = fmt.Errorf("no correponding peerconnection for id %s", id)
@ -673,17 +721,21 @@ func (zm *ZoneManager) HandleLeavingMember(id string, zoneId string) (err error)
}); err != nil { }); err != nil {
return return
} }
if err = atomicallyExecute(zm.zoneFlag, func() (err error) { logger.Println(err)
err = atomicallyExecute(zm.zoneFlag, func() (err error) {
logger.Println("---------------- cleaning zone handlers", id)
if zone, ok := zm.Zones[zoneId]; ok { if zone, ok := zm.Zones[zoneId]; ok {
for _, handlersPublishers := range zone.ZoneRequestScheduler.handlersPublishers { go func() {
handlersPublishers <- &ZoneRequest{ for _, handlersPublishers := range zone.ZoneRequestScheduler.handlersPublishers {
ReqType: LEAVE_ZONE, handlersPublishers <- &ZoneRequest{
From: "node", ReqType: LEAVE_ZONE,
Payload: map[string]interface{}{ From: id,
"userId": id, Payload: map[string]interface{}{
}, "userId": id,
},
}
} }
} }()
if err = atomicallyExecute(zone.DataChannelsFlag, func() (err error) { if err = atomicallyExecute(zone.DataChannelsFlag, func() (err error) {
defer delete(zone.DataChannels, id) defer delete(zone.DataChannels, id)
if dataChannel, ok := zone.DataChannels[id]; ok { if dataChannel, ok := zone.DataChannels[id]; ok {
@ -695,14 +747,14 @@ func (zm *ZoneManager) HandleLeavingMember(id string, zoneId string) (err error)
}); err != nil { }); err != nil {
return return
} }
logger.Println("datachannels cleaned", id)
} else { } else {
err = fmt.Errorf("no corresponding zone for zoneId %s", zoneId) err = fmt.Errorf("no corresponding zone for zoneId %s", zoneId)
} }
return return
}); err != nil { })
return logger.Println(err)
} err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
if err = atomicallyExecute(zm.peerConnectionFlag, func() (err error) {
if _, ok := zm.RTCPeerConnections[id]; ok { if _, ok := zm.RTCPeerConnections[id]; ok {
defer delete(zm.RTCPeerConnections, id) defer delete(zm.RTCPeerConnections, id)
if err = zm.RTCPeerConnections[id].Close(); err != nil { if err = zm.RTCPeerConnections[id].Close(); err != nil {
@ -710,7 +762,34 @@ func (zm *ZoneManager) HandleLeavingMember(id string, zoneId string) (err error)
} }
} }
return return
}); err != nil { })
logger.Println(err)
_ = atomicallyExecute(zm.candidateFlag, func() (err error) {
delete(zm.PendingCandidates, id)
return
})
return
}
func (zm *ZoneManager) notifyLeavingMember(userId,zoneId,hostId string) (err error) {
em := NewEncryptionManager()
sig := em.SignRequestHMAC(hostId)
body, err := json.Marshal(map[string]interface{}{
"type": DISCONNECT_ZONE_MEMBER,
"mac": sig,
"from": hostId,
"peerType":"node",
"payload": map[string]string{
"zoneId": zoneId,
"userId": userId,
},
})
if err != nil {
return
}
_, err = http.Post("https://app.zippytal.com/req", "application/json", bytes.NewBuffer(body))
if err != nil {
logger.Println("error come from there in zone manager")
return return
} }
return return
@ -721,13 +800,13 @@ func (zm *ZoneManager) negotiate(target string, zoneId string) {
if _, ok := zm.RTCPeerConnections[target]; !ok { if _, ok := zm.RTCPeerConnections[target]; !ok {
return return
} }
zm.RTCPeerConnections[target].makingOfferLock.Lock() //zm.RTCPeerConnections[target].makingOfferLock.Lock()
zm.RTCPeerConnections[target].makingOffer = true //zm.RTCPeerConnections[target].makingOffer = true
zm.RTCPeerConnections[target].makingOfferLock.Unlock() //zm.RTCPeerConnections[target].makingOfferLock.Unlock()
defer func() { defer func() {
zm.RTCPeerConnections[target].makingOfferLock.Lock() //zm.RTCPeerConnections[target].makingOfferLock.Lock()
zm.RTCPeerConnections[target].makingOffer = false //zm.RTCPeerConnections[target].makingOffer = false
zm.RTCPeerConnections[target].makingOfferLock.Unlock() //zm.RTCPeerConnections[target].makingOfferLock.Unlock()
}() }()
return return
}) })

View File

@ -0,0 +1,10 @@
package localserver
type ZoneNotificationsHandler struct {
ZoneId string
ZoneMembersId []string
DataChannels map[string]*DataChannel
Flag *uint32
Publishers []<-chan *ZoneRequest
reqChans []chan<- *ZoneRequest
}

View File

@ -2,26 +2,52 @@ package localserver
import ( import (
"encoding/json" "encoding/json"
"os"
"path/filepath" "path/filepath"
"github.com/dgraph-io/badger/v3" "github.com/dgraph-io/badger/v3"
) )
type BasicRights struct {
Create bool `json:"create"`
}
type ChatRights struct {
*BasicRights
}
type AudioChannelRights struct {
*BasicRights
}
type VideoChannelRights struct {
*BasicRights
}
type MediaRights struct {
*BasicRights
}
type FileRights struct {
*BasicRights
CreateAtRoot bool `json:"createAtRoot"`
}
type User struct { type User struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
ChatRights string `json:"chatRights"` ChatRights *ChatRights `json:"chatRights"`
AudioChannelRights string `json:"audioChannelRights"` AudioChannelRights *AudioChannelRights `json:"audioChannelRights"`
VideoChannelRights string `json:"videoChannelRights"` VideoChannelRights *VideoChannelRights `json:"videoChannelRights"`
MediaRights string `json:"mediaRights"` MediaRights *MediaRights `json:"mediaRights"`
FileRights string `json:"fileRights"` FileRights *FileRights `json:"fileRights"`
KnownChatsId []string `json:"knownChatsId"` KnownChatsId []string `json:"knownChatsId"`
KnownAudioChannelsId []string `json:"knownAudioChannelsId"` KnownAudioChannelsId []string `json:"knownAudioChannelsId"`
KnownVideoChannelsId []string `json:"knownVideoChannelsId"` KnownVideoChannelsId []string `json:"knownVideoChannelsId"`
KnownMediaFolderId []string `json:"knownMediaFolderId"` KnownMediaFolderId []string `json:"knownMediaFolderId"`
KnownFileFolderId []string `json:"knownFileFolderId"` KnownFileFolderId []string `json:"knownFileFolderId"`
Admin bool `json:"admin"` Admin bool `json:"admin"`
Owner bool `json:"owner"` Owner bool `json:"owner"`
} }
type ZoneUsersDBHandler struct { type ZoneUsersDBHandler struct {
@ -29,6 +55,46 @@ type ZoneUsersDBHandler struct {
db func(func(*badger.DB) (err error)) (err error) db func(func(*badger.DB) (err error)) (err error)
} }
func NewChatRights(create bool) *ChatRights {
return &ChatRights{
BasicRights: &BasicRights{
Create: create,
},
}
}
func NewAudioChannelRights(create bool) *AudioChannelRights {
return &AudioChannelRights{
BasicRights: &BasicRights{
Create: create,
},
}
}
func NewVideoChannelRights(create bool) *VideoChannelRights {
return &VideoChannelRights{
BasicRights: &BasicRights{
Create: create,
},
}
}
func NewMediaRights(create bool) *MediaRights {
return &MediaRights{
BasicRights: &BasicRights{
Create: create,
},
}
}
func NewFileRights(create bool) *FileRights {
return &FileRights{
BasicRights: &BasicRights{
Create: create,
},
}
}
func NewZoneUsersDBHandler(zoneId string, owner string, authorizedMembers []string, init bool) (zoneChatDBHandler *ZoneUsersDBHandler, err error) { func NewZoneUsersDBHandler(zoneId string, owner string, authorizedMembers []string, init bool) (zoneChatDBHandler *ZoneUsersDBHandler, err error) {
db := func(f func(*badger.DB) (err error)) (err error) { db := func(f func(*badger.DB) (err error)) (err error) {
db, err := badger.Open(badger.DefaultOptions(filepath.Join("data", "zones", zoneId, "users")).WithLogger(dbLogger)) db, err := badger.Open(badger.DefaultOptions(filepath.Join("data", "zones", zoneId, "users")).WithLogger(dbLogger))
@ -43,11 +109,11 @@ func NewZoneUsersDBHandler(zoneId string, owner string, authorizedMembers []stri
admin := &User{ admin := &User{
ID: owner, ID: owner,
Name: owner, Name: owner,
ChatRights: "", ChatRights: NewChatRights(true),
AudioChannelRights: "", AudioChannelRights: NewAudioChannelRights(true),
VideoChannelRights: "", VideoChannelRights: NewVideoChannelRights(true),
MediaRights: "", MediaRights: NewMediaRights(true),
FileRights: "", FileRights: NewFileRights(true),
KnownChatsId: []string{}, KnownChatsId: []string{},
KnownAudioChannelsId: []string{}, KnownAudioChannelsId: []string{},
KnownVideoChannelsId: []string{}, KnownVideoChannelsId: []string{},
@ -70,6 +136,14 @@ func NewZoneUsersDBHandler(zoneId string, owner string, authorizedMembers []stri
return return
} }
} }
bs, err := os.ReadFile(filepath.Join("data", "zones", zoneId, "users", "usersConfig.json"))
if err != nil {
return
}
var baseUserConfig ZoneUserConfig
if err = json.Unmarshal(bs, &baseUserConfig); err != nil {
return
}
if err = db(func(d *badger.DB) (err error) { if err = db(func(d *badger.DB) (err error) {
err = d.Update(func(txn *badger.Txn) (err error) { err = d.Update(func(txn *badger.Txn) (err error) {
for _, member := range authorizedMembers { for _, member := range authorizedMembers {
@ -79,11 +153,11 @@ func NewZoneUsersDBHandler(zoneId string, owner string, authorizedMembers []stri
user := &User{ user := &User{
ID: member, ID: member,
Name: member, Name: member,
ChatRights: "", ChatRights: baseUserConfig.DefaultChatRights,
AudioChannelRights: "", AudioChannelRights: baseUserConfig.DefaultAudioChannelRights,
VideoChannelRights: "", VideoChannelRights: baseUserConfig.DefaultVideoChannelRights,
MediaRights: "", MediaRights: baseUserConfig.DefaultMediaRights,
FileRights: "", FileRights: baseUserConfig.DefaultFileRights,
KnownChatsId: []string{}, KnownChatsId: []string{},
KnownAudioChannelsId: []string{}, KnownAudioChannelsId: []string{},
KnownVideoChannelsId: []string{}, KnownVideoChannelsId: []string{},
@ -154,7 +228,7 @@ func (zcdbh *ZoneUsersDBHandler) ListUsers(lastIndex int, limit int) (users []*U
opt.Reverse = true opt.Reverse = true
it := txn.NewIterator(opt) it := txn.NewIterator(opt)
defer it.Close() defer it.Close()
users = make([]*User, 0) users = []*User{}
for it.Rewind(); it.Valid(); it.Next() { for it.Rewind(); it.Valid(); it.Next() {
item := it.Item() item := it.Item()
if err = item.Value(func(val []byte) error { if err = item.Value(func(val []byte) error {

View File

@ -9,9 +9,12 @@ import (
) )
const ( const (
EDIT_DEFAULT_RIGHTS = "edit_default_rights"
EDIT_USERS_RIGHTS = "edit_users_rights"
LIST_ZONE_MEMBERS = "list_zone_members" LIST_ZONE_MEMBERS = "list_zone_members"
LIST_ZONE_MEMBERS_RESPONSE = "list_zone_members_response" LIST_ZONE_MEMBERS_RESPONSE = "list_zone_members_response"
GET_USER = "get_user" GET_USER = "get_user"
GET_DEFAULT_RIGHTS = "get_default_rights"
MODIFY_USER_CHAT_RIGHTS = "modify_user_chat_rights" MODIFY_USER_CHAT_RIGHTS = "modify_user_chat_rights"
ADD_KNOWN_CHAT = "add_known_chat" ADD_KNOWN_CHAT = "add_known_chat"
REMOVE_KNOWN_CHAT = "remove_known_chat" REMOVE_KNOWN_CHAT = "remove_known_chat"
@ -24,8 +27,12 @@ const (
) )
const ( const (
NEW_ZONE_USER = "new_zone_user" NEW_ZONE_USER = "new_zone_user"
REMOVED_ZONE_USER = "removed_zone_user" GET_CURRENT_USER_RESPONSE = "get_current_user_response"
GET_USER_RESPONSE = "get_user_response"
REMOVED_ZONE_USER = "removed_zone_user"
DEFAULT_RIGHTS_EDITED = "default_rights_edited"
USER_RIGHTS_EDITED = "user_rights_edited"
) )
type ZoneUsersHandler struct { type ZoneUsersHandler struct {
@ -38,13 +45,19 @@ type ZoneUsersHandler struct {
reqChans []chan<- *ZoneRequest reqChans []chan<- *ZoneRequest
} }
type ZoneMember struct {
ID string `json:"id"`
Admin bool `json:"admin"`
Owner bool `json:"owner"`
Config *ZoneUserConfig `json:"config"`
}
type ZoneUserConfig struct { type ZoneUserConfig struct {
DefaultChatsRights string `json:"defaultChatsRights"` DefaultChatRights *ChatRights `json:"defaultChatsRights"`
DefaultAudioChannelsRights string `json:"defaultAudioChannelsRights"` DefaultAudioChannelRights *AudioChannelRights `json:"defaultAudioChannelsRights"`
DefaultVideoChannelsRights string `json:"defaultVideoChannelsRights"` DefaultVideoChannelRights *VideoChannelRights `json:"defaultVideoChannelsRights"`
DefaultMediaRights string `json:"defaultMediaRights"` DefaultMediaRights *MediaRights `json:"defaultMediaRights"`
DefaultFileRights string `json:"defaultFileRights"` DefaultFileRights *FileRights `json:"defaultFileRights"`
AdminRights string `json:"adminRights"`
} }
func NewZoneUsersHandler(zoneId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, flag *uint32) (zoneUsersHandler *ZoneUsersHandler, err error) { func NewZoneUsersHandler(zoneId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, flag *uint32) (zoneUsersHandler *ZoneUsersHandler, err error) {
@ -57,21 +70,16 @@ func NewZoneUsersHandler(zoneId string, owner string, authorizedMembers []string
if mkdirErr != nil { if mkdirErr != nil {
return nil, mkdirErr return nil, mkdirErr
} }
zoneUsersDBHandler, err = NewZoneUsersDBHandler(zoneId, owner, authorizedMembers, true)
if err != nil {
return
}
file, ferr := os.Create(filepath.Join("data", "zones", zoneId, "users", "usersConfig.json")) file, ferr := os.Create(filepath.Join("data", "zones", zoneId, "users", "usersConfig.json"))
if ferr != nil { if ferr != nil {
return nil, ferr return nil, ferr
} }
baseConfig := ZoneUserConfig{ baseConfig := ZoneUserConfig{
DefaultChatsRights: "", DefaultChatRights: NewChatRights(true),
DefaultAudioChannelsRights: "", DefaultAudioChannelRights: NewAudioChannelRights(true),
DefaultVideoChannelsRights: "", DefaultVideoChannelRights: NewVideoChannelRights(true),
DefaultMediaRights: "", DefaultMediaRights: NewMediaRights(true),
DefaultFileRights: "", DefaultFileRights: NewFileRights(true),
AdminRights: "",
} }
bs, jsonErr := json.Marshal(baseConfig) bs, jsonErr := json.Marshal(baseConfig)
if jsonErr != nil { if jsonErr != nil {
@ -80,7 +88,11 @@ func NewZoneUsersHandler(zoneId string, owner string, authorizedMembers []string
if _, writeErr := file.WriteString(string(bs)); writeErr != nil { if _, writeErr := file.WriteString(string(bs)); writeErr != nil {
return nil, writeErr return nil, writeErr
} }
_ = file.Close() err = file.Close()
if err != nil {
return
}
zoneUsersDBHandler, err = NewZoneUsersDBHandler(zoneId, owner, authorizedMembers, true)
if err != nil { if err != nil {
return return
} }
@ -139,6 +151,34 @@ func (zuh *ZoneUsersHandler) Init(ctx context.Context, authorizedMembers []strin
return return
} }
func (zuh *ZoneUsersHandler) sendDataChannelMessage(reqType string, from string, to string, payload map[string]interface{}) (<-chan struct{}, <-chan error) {
done, errCh := make(chan struct{}, 10), make(chan error, 10)
go func() {
if err := atomicallyExecute(zuh.Flag, func() (err error) {
if _, ok := zuh.DataChannels[to]; ok {
bs, jsonErr := json.Marshal(&ZoneResponse{
Type: reqType,
From: from,
To: to,
Payload: payload,
})
if jsonErr != nil {
return jsonErr
}
logger.Println(string(bs))
err = zuh.DataChannels[to].DataChannel.SendText(string(bs))
}
return
}); err != nil {
errCh <- err
return
}
done <- struct{}{}
logger.Printf("sending %v done\n", payload)
}()
return done, errCh
}
func (zuh *ZoneUsersHandler) Subscribe(ctx context.Context, publisher <-chan *ZoneRequest) (reqChan chan *ZoneRequest, done chan struct{}, errCh chan error) { func (zuh *ZoneUsersHandler) Subscribe(ctx context.Context, publisher <-chan *ZoneRequest) (reqChan chan *ZoneRequest, done chan struct{}, errCh chan error) {
reqChan, done, errCh = make(chan *ZoneRequest), make(chan struct{}), make(chan error) reqChan, done, errCh = make(chan *ZoneRequest), make(chan struct{}), make(chan error)
zuh.reqChans = append(zuh.reqChans, reqChan) zuh.reqChans = append(zuh.reqChans, reqChan)
@ -159,14 +199,22 @@ func (zuh *ZoneUsersHandler) Subscribe(ctx context.Context, publisher <-chan *Zo
} }
func (zuh *ZoneUsersHandler) AddUser(userId string) (err error) { func (zuh *ZoneUsersHandler) AddUser(userId string) (err error) {
bs, err := os.ReadFile(filepath.Join("data", "zones", zuh.ZoneId, "users", "usersConfig.json"))
if err != nil {
return
}
var baseUserConfig ZoneUserConfig
if err = json.Unmarshal(bs, &baseUserConfig); err != nil {
return
}
newUser := &User{ newUser := &User{
ID: userId, ID: userId,
Name: userId, Name: userId,
ChatRights: "", ChatRights: baseUserConfig.DefaultChatRights,
AudioChannelRights: "", AudioChannelRights: baseUserConfig.DefaultAudioChannelRights,
VideoChannelRights: "", VideoChannelRights: baseUserConfig.DefaultVideoChannelRights,
MediaRights: "", MediaRights: baseUserConfig.DefaultMediaRights,
FileRights: "", FileRights: baseUserConfig.DefaultFileRights,
KnownChatsId: []string{}, KnownChatsId: []string{},
KnownAudioChannelsId: []string{}, KnownAudioChannelsId: []string{},
KnownVideoChannelsId: make([]string, 0), KnownVideoChannelsId: make([]string, 0),
@ -418,9 +466,137 @@ func (zuh *ZoneUsersHandler) RemoveKnownFolder(folderId string, userId string) (
return return
} }
func (zuh *ZoneUsersHandler) GetDefaultRights(userId string) (err error) {
bs, err := os.ReadFile(filepath.Join("data", "zones", zuh.ZoneId, "users", "usersConfig.json"))
if err != nil {
return
}
var defaultRights ZoneUserConfig
if err = json.Unmarshal(bs, &defaultRights); err != nil {
return
}
d, e := zuh.sendDataChannelMessage(GET_DEFAULT_RIGHTS, "node", userId, map[string]interface{}{
"defaultRights": defaultRights,
})
select {
case <-d:
case err = <-e:
}
return
}
func (zuh *ZoneUsersHandler) EditDefaultRights(defaultChatRights *ChatRights, defaultAudioChannelRights *AudioChannelRights, defaultVideoChannelRights *VideoChannelRights, defaultMediaRights *MediaRights, defaultFileRights *FileRights) (err error) {
bs, err := os.ReadFile(filepath.Join("data", "zones", zuh.ZoneId, "users", "usersConfig.json"))
if err != nil {
return
}
var baseUserConfig ZoneUserConfig
if err = json.Unmarshal(bs, &baseUserConfig); err != nil {
return
}
baseUserConfig.DefaultChatRights = defaultChatRights
baseUserConfig.DefaultAudioChannelRights = defaultAudioChannelRights
baseUserConfig.DefaultVideoChannelRights = defaultVideoChannelRights
baseUserConfig.DefaultMediaRights = defaultMediaRights
baseUserConfig.DefaultFileRights = defaultFileRights
bs, err = json.Marshal(baseUserConfig)
if err != nil {
return
}
file, err := os.OpenFile(filepath.Join("data", "zones", zuh.ZoneId, "users", "usersConfig.json"), os.O_WRONLY|os.O_TRUNC, os.ModeAppend)
if err != nil {
return
}
if _, err = file.WriteString(string(bs)); err != nil {
return
}
err = file.Close()
for _, member := range zuh.ZoneMembersId {
done, err := zuh.sendDataChannelMessage(DEFAULT_RIGHTS_EDITED, "node", member, map[string]interface{}{
"defaultRights": baseUserConfig,
})
select {
case <-done:
case e := <-err:
logger.Println(e)
}
}
return
}
func (zuh *ZoneUsersHandler) EditUsersRights(users []interface{}, chatRights *ChatRights, audioChannelRights *AudioChannelRights, videoChannelRights *VideoChannelRights, mediaRights *MediaRights, fileRights *FileRights) (err error) {
for _, u := range users {
if user, ok := u.(string); ok {
if err = zuh.editUserRights(user, chatRights, audioChannelRights, videoChannelRights, mediaRights, fileRights); err != nil {
logger.Println(err)
}
}
}
return
}
func (zuh *ZoneUsersHandler) editUserRights(userId string, chatRights *ChatRights, audioChannelRights *AudioChannelRights, videoChannelRights *VideoChannelRights, mediaRights *MediaRights, fileRights *FileRights) (err error) {
user, err := zuh.DB.GetUser(userId)
if err != nil {
return
}
user.ChatRights = chatRights
user.AudioChannelRights = audioChannelRights
user.VideoChannelRights = videoChannelRights
user.MediaRights = mediaRights
user.FileRights = fileRights
if err = zuh.DB.ModifyUser(userId, user); err != nil {
return
}
for _, id := range zuh.ZoneMembersId {
d, e := zuh.sendDataChannelMessage(USER_RIGHTS_EDITED, "node", userId, map[string]interface{}{
"userId": id,
"user": user,
})
select {
case <-d:
case err = <-e:
}
}
return
}
func (zuh *ZoneUsersHandler) handleZoneRequest(ctx context.Context, req *ZoneRequest) (err error) { func (zuh *ZoneUsersHandler) handleZoneRequest(ctx context.Context, req *ZoneRequest) (err error) {
logger.Println("got request in zone users handler", req) logger.Println("got request in zone users handler", req)
switch req.ReqType { switch req.ReqType {
case EDIT_DEFAULT_RIGHTS:
if _, ok := req.Payload["defaultRights"]; !ok {
err = fmt.Errorf("no field defaultRights in request payload")
return
}
bs, jsonErr := json.Marshal(req.Payload["defaultRights"])
if jsonErr != nil {
return jsonErr
}
var config ZoneUserConfig
if err = json.Unmarshal(bs, &config); err != nil {
err = fmt.Errorf("the defaultRights payload dont match ZoneFSEntity struct pattern : %v", err)
return
}
err = zuh.EditDefaultRights(config.DefaultChatRights, config.DefaultAudioChannelRights, config.DefaultVideoChannelRights, config.DefaultMediaRights, config.DefaultFileRights)
case EDIT_USERS_RIGHTS:
if err = verifyFieldsSliceInterface(req.Payload, "users"); err != nil {
return
}
if _, ok := req.Payload["defaultRights"]; !ok {
err = fmt.Errorf("no field defaultRights in request payload")
return
}
bs, jsonErr := json.Marshal(req.Payload["defaultRights"])
if jsonErr != nil {
return jsonErr
}
var config ZoneUserConfig
if err = json.Unmarshal(bs, &config); err != nil {
err = fmt.Errorf("the defaultRights payload dont match ZoneFSEntity struct pattern : %v", err)
return
}
err = zuh.EditUsersRights(req.Payload["users"].([]interface{}), config.DefaultChatRights, config.DefaultAudioChannelRights, config.DefaultVideoChannelRights, config.DefaultMediaRights, config.DefaultFileRights)
case REMOVE_USER: case REMOVE_USER:
if err = verifyFieldsString(req.Payload, "userId"); err != nil { if err = verifyFieldsString(req.Payload, "userId"); err != nil {
return return
@ -461,6 +637,11 @@ func (zuh *ZoneUsersHandler) handleZoneRequest(ctx context.Context, req *ZoneReq
return return
} }
err = zuh.AddKnownVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) err = zuh.AddKnownVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string))
case GET_DEFAULT_RIGHTS:
if err = verifyFieldsString(req.Payload, "userId"); err != nil {
return
}
err = zuh.GetDefaultRights(req.Payload["userId"].(string))
case GET_USER: case GET_USER:
if err = verifyFieldsString(req.Payload, "userId"); err != nil { if err = verifyFieldsString(req.Payload, "userId"); err != nil {
return return
@ -477,57 +658,58 @@ func (zuh *ZoneUsersHandler) handleZoneRequest(ctx context.Context, req *ZoneReq
if err != nil { if err != nil {
return err return err
} }
var answer *ZoneResponse logger.Println("get user done")
if req.Payload["init"].(bool) { if req.Payload["init"].(bool) {
answer = &ZoneResponse{ done, errCh := zuh.sendDataChannelMessage(GET_CURRENT_USER_RESPONSE, "node", req.Payload["userId"].(string), map[string]interface{}{
Type: "get_current_user_response", "user": user,
To: "", })
From: "", select {
Payload: map[string]interface{}{ case <-done:
"user": user, case err = <-errCh:
}, return err
} }
} else { } else {
answer = &ZoneResponse{ done, errCh := zuh.sendDataChannelMessage(GET_USER_RESPONSE, "node", req.Payload["userId"].(string), map[string]interface{}{
Type: "get_user_response", "user": user,
To: "", })
From: "", select {
Payload: map[string]interface{}{ case <-done:
"user": user, case err = <-errCh:
},
}
}
bs, err := json.Marshal(answer)
if err != nil {
return err
}
logger.Println(string(bs))
if _, ok := zuh.DataChannels[req.From]; ok {
if err = zuh.DataChannels[req.From].DataChannel.SendText(string(bs)); err != nil {
return err return err
} }
} }
case LIST_ZONE_MEMBERS: case LIST_ZONE_MEMBERS:
users, err := zuh.DB.ListUsers(0, 0) users, err := zuh.DB.ListUsers(0, 1000)
if err != nil { if err != nil {
return err return err
} }
bs, err := json.Marshal(&ZoneResponse{ usersIds := []*ZoneMember{}
Type: LIST_ZONE_MEMBERS_RESPONSE, for _, user := range users {
To: "", usersIds = append(usersIds, &ZoneMember{
From: "", ID: user.ID,
Payload: map[string]interface{}{ Admin: user.Admin,
"users": users, Owner: user.Owner,
}, Config: &ZoneUserConfig{
DefaultChatRights: user.ChatRights,
DefaultAudioChannelRights: user.AudioChannelRights,
DefaultVideoChannelRights: user.VideoChannelRights,
DefaultMediaRights: user.MediaRights,
DefaultFileRights: user.FileRights,
},
})
}
logger.Println("get user done")
done, errCh := zuh.sendDataChannelMessage(LIST_ZONE_MEMBERS_RESPONSE, zuh.ZoneId, req.From, map[string]interface{}{
"users": usersIds,
}) })
if err != nil { select {
case <-done:
logger.Println("send get user done")
case err = <-errCh:
return err return err
} }
if _, ok := zuh.DataChannels[req.From]; ok {
if err = zuh.DataChannels[req.From].DataChannel.SendText(string(bs)); err != nil {
return err
}
}
case MODIFY_USER_CHAT_RIGHTS: case MODIFY_USER_CHAT_RIGHTS:
} }
return return

View File

@ -129,10 +129,10 @@ func (vc *VideoChannel) HandleOffer(ctx context.Context, channelId string, userI
errCh <- err errCh <- err
return return
} }
_ = atomicallyExecute(vc.localSDMapFlag, func() (err error) { if err = peerConnection.SetLocalDescription(rawAnswer); err != nil {
vc.localSD[userId] = &rawAnswer errCh <- err
return return
}) }
_, _ = sendDCMessage(string(VIDEO_CHANNEL_WEBRTC_ANSWER), hostId, userId, map[string]interface{}{ _, _ = sendDCMessage(string(VIDEO_CHANNEL_WEBRTC_ANSWER), hostId, userId, map[string]interface{}{
"to": userId, "to": userId,
"from": vc.ID, "from": vc.ID,
@ -146,25 +146,25 @@ func (vc *VideoChannel) HandleOffer(ctx context.Context, channelId string, userI
} }
func (vc *VideoChannel) HandleCounterOffer(ctx context.Context, userId string, sendDCMessage SendDCMessageFunc) (err error) { func (vc *VideoChannel) HandleCounterOffer(ctx context.Context, userId string, sendDCMessage SendDCMessageFunc) (err error) {
if err = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) { // if err = atomicallyExecute(vc.rtcPeerConnectionMapFlag, func() (err error) {
if _, ok := vc.rtcPeerConnections[userId]; !ok { // if _, ok := vc.rtcPeerConnections[userId]; !ok {
err = fmt.Errorf("no field corresponding peer connection for id %s", userId) // err = fmt.Errorf("no field corresponding peer connection for id %s", userId)
return // return
} // }
logger.Println("handling counter offer") // logger.Println("handling counter offer")
connection := vc.rtcPeerConnections[userId] // connection := vc.rtcPeerConnections[userId]
err = atomicallyExecute(vc.localSDMapFlag, func() (err error) { // err = atomicallyExecute(vc.localSDMapFlag, func() (err error) {
err = connection.SetLocalDescription(*vc.localSD[userId]) // err = connection.SetLocalDescription(*vc.localSD[userId])
return // return
}) // })
return // return
}); err != nil { // }); err != nil {
return // return
} // }
_ = atomicallyExecute(vc.localSDMapFlag, func() (err error) { // _ = atomicallyExecute(vc.localSDMapFlag, func() (err error) {
delete(vc.localSD, userId) // delete(vc.localSD, userId)
return // return
}) // })
if err = atomicallyExecute(vc.candidateFlag, func() (err error) { if err = atomicallyExecute(vc.candidateFlag, func() (err error) {
for _, candidate := range vc.pendingCandidates[userId] { for _, candidate := range vc.pendingCandidates[userId] {
logger.Println("sending candidate to", userId, candidate) logger.Println("sending candidate to", userId, candidate)
@ -173,7 +173,7 @@ func (vc *VideoChannel) HandleCounterOffer(ctx context.Context, userId string, s
"to": userId, "to": userId,
"candidate": candidate.ToJSON().Candidate, "candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid, "sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
}) })
select { select {
case <-d: case <-d:
@ -195,12 +195,12 @@ func (vc *VideoChannel) HandleRennegotiationOffer(from string, sdp string, sendD
err = fmt.Errorf("no corresponding peer connection for id %s", from) err = fmt.Errorf("no corresponding peer connection for id %s", from)
return return
} }
vc.rtcPeerConnections[from].makingOfferLock.Lock() // vc.rtcPeerConnections[from].makingOfferLock.Lock()
if vc.rtcPeerConnections[from].makingOffer { // if vc.rtcPeerConnections[from].makingOffer {
vc.rtcPeerConnections[from].makingOfferLock.Unlock() // vc.rtcPeerConnections[from].makingOfferLock.Unlock()
return fmt.Errorf("already making an offer or state is stable") // return fmt.Errorf("already making an offer or state is stable")
} // }
vc.rtcPeerConnections[from].makingOfferLock.Unlock() // vc.rtcPeerConnections[from].makingOfferLock.Unlock()
if err = vc.rtcPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil { if err = vc.rtcPeerConnections[from].SetRemoteDescription(webrtc.SessionDescription{SDP: sdp, Type: webrtc.SDPTypeOffer}); err != nil {
return return
} }
@ -670,13 +670,13 @@ func (vc *VideoChannel) negotiate(target string, sendDCMessage SendDCMessageFunc
if _, ok := vc.rtcPeerConnections[target]; !ok { if _, ok := vc.rtcPeerConnections[target]; !ok {
return return
} }
vc.rtcPeerConnections[target].makingOfferLock.Lock() // vc.rtcPeerConnections[target].makingOfferLock.Lock()
vc.rtcPeerConnections[target].makingOffer = true // vc.rtcPeerConnections[target].makingOffer = true
vc.rtcPeerConnections[target].makingOfferLock.Unlock() // vc.rtcPeerConnections[target].makingOfferLock.Unlock()
defer func() { defer func() {
vc.rtcPeerConnections[target].makingOfferLock.Lock() // vc.rtcPeerConnections[target].makingOfferLock.Lock()
vc.rtcPeerConnections[target].makingOffer = false // vc.rtcPeerConnections[target].makingOffer = false
vc.rtcPeerConnections[target].makingOfferLock.Unlock() // vc.rtcPeerConnections[target].makingOfferLock.Unlock()
}() }()
for _, id := range vc.CurrentMembersId { for _, id := range vc.CurrentMembersId {

View File

@ -27,8 +27,14 @@ const (
) )
const ( const (
USER_JOINED_VIDEO_CHANNEL = "user_joined_video_channel" USER_JOINED_VIDEO_CHANNEL = "user_joined_video_channel"
USER_LEFT_VIDEO_CHANNEL = "user_left_video_channel" USER_LEFT_VIDEO_CHANNEL = "user_left_video_channel"
VIDEO_CHANNNEL_NAME_EDITED = "video_channel_name_edited"
VIDEO_CHANNNEL_TYPE_EDITED = "video_channel_type_edited"
VIDEO_CHANNEL_MEMBER_REMOVED = "video_channel_member_removed"
VIDEO_CHANNEL_MEMBER_ADDED = "video_channel_member_added"
ADDED_IN_VIDEO_CHANNEL = "added_in_video_channel"
REMOVED_FROM_VIDEO_CHANNEL = "removed_from_video_channel"
) )
type VideoChannelMember struct { type VideoChannelMember struct {
@ -193,7 +199,7 @@ func (zvch *ZoneVideoChannelsHandler) signalCandidate(from string, to string, ca
"to": to, "to": to,
"candidate": candidate.ToJSON().Candidate, "candidate": candidate.ToJSON().Candidate,
"sdpMid": *candidate.ToJSON().SDPMid, "sdpMid": *candidate.ToJSON().SDPMid,
"sdpMlineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)), "sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
}) })
select { select {
case <-d: case <-d:
@ -268,15 +274,15 @@ func (zvch *ZoneVideoChannelsHandler) AddNewVideoChannel(channelName string, own
} }
m := make([]string, 0, len(members)) m := make([]string, 0, len(members))
for _, member := range members { for _, member := range members {
if _, ok := member.(string); ok { if mbr, ok := member.(string); ok && mbr != owner {
m = append(m, member.(string)) m = append(m, mbr)
} }
} }
baseConfig := &VideoChannelConfig{ baseConfig := &VideoChannelConfig{
ID: channelName, ID: channelName,
Owner: owner, Owner: owner,
ChannelType: channelType, ChannelType: channelType,
Members: m, Members: append(m,owner),
} }
bs, jsonErr := json.Marshal(baseConfig) bs, jsonErr := json.Marshal(baseConfig)
if jsonErr != nil { if jsonErr != nil {
@ -343,7 +349,7 @@ func (zvch *ZoneVideoChannelsHandler) DeleteVideoChannel(channelId string) (err
"userId": member, "userId": member,
"channelId": channelId, "channelId": channelId,
}) })
done, e := zvch.sendDataChannelMessage(REMOVED_FROM_CHAT, "node", member, map[string]interface{}{ done, e := zvch.sendDataChannelMessage(REMOVED_FROM_VIDEO_CHANNEL, "node", member, map[string]interface{}{
"channelId": channelId, "channelId": channelId,
"userId": member, "userId": member,
}) })
@ -379,7 +385,7 @@ func (zvch *ZoneVideoChannelsHandler) EditVideoChannelName(channelId string, new
err = fmt.Errorf("no coresponding videoChannel") err = fmt.Errorf("no coresponding videoChannel")
return return
} }
bs, err := os.ReadFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId)) bs, err := os.ReadFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId, "videoChannelConfig.json"))
if err != nil { if err != nil {
return return
} }
@ -392,7 +398,7 @@ func (zvch *ZoneVideoChannelsHandler) EditVideoChannelName(channelId string, new
if err = os.Rename(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId), filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", newVideoChannelId)); err != nil { if err = os.Rename(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", channelId), filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", newVideoChannelId)); err != nil {
return return
} }
f, err := os.OpenFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", newVideoChannelId), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) f, err := os.OpenFile(filepath.Join("data", "zones", zvch.ZoneId, "videoChannels", newVideoChannelId, "videoChannelConfig.json"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
defer func() { defer func() {
_ = f.Close() _ = f.Close()
}() }()
@ -416,7 +422,7 @@ func (zvch *ZoneVideoChannelsHandler) EditVideoChannelName(channelId string, new
"userId": member, "userId": member,
"channelId": channelId, "channelId": channelId,
}) })
done, e := zvch.sendDataChannelMessage(CHAT_NAME_EDITED, "node", member, map[string]interface{}{ done, e := zvch.sendDataChannelMessage(VIDEO_CHANNNEL_NAME_EDITED, "node", member, map[string]interface{}{
"formerVideoChannelId": channelId, "formerVideoChannelId": channelId,
"newVideoChannelId": newVideoChannelId, "newVideoChannelId": newVideoChannelId,
}) })
@ -480,6 +486,10 @@ func (zvch *ZoneVideoChannelsHandler) EditVideoChannelType(channelId string, cha
if pubErr := zvch.SetVideoChannelPublicForUser(channelId, member); pubErr != nil { if pubErr := zvch.SetVideoChannelPublicForUser(channelId, member); pubErr != nil {
logger.Println(pubErr) logger.Println(pubErr)
} }
zvch.sendDataChannelMessage(VIDEO_CHANNNEL_TYPE_EDITED, "node", member, map[string]interface{}{
"channelId": channelId,
"channelType": channelType,
})
} }
case PRIVATE: case PRIVATE:
var members = []string{} var members = []string{}
@ -491,6 +501,10 @@ func (zvch *ZoneVideoChannelsHandler) EditVideoChannelType(channelId string, cha
if pubErr := zvch.SetVideoChannelPrivateForUser(channelId, member); pubErr != nil { if pubErr := zvch.SetVideoChannelPrivateForUser(channelId, member); pubErr != nil {
logger.Println(pubErr) logger.Println(pubErr)
} }
zvch.sendDataChannelMessage(VIDEO_CHANNNEL_TYPE_EDITED, "node", member, map[string]interface{}{
"channelId": channelId,
"channelType": channelType,
})
} }
} }
_ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) { _ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
@ -558,7 +572,7 @@ broadcastLoop:
for _, member := range vc.Members { for _, member := range vc.Members {
for _, m := range addedMembers { for _, m := range addedMembers {
if member == m { if member == m {
done, e := zvch.sendDataChannelMessage(ADDED_IN_CHAT, "node", member, map[string]interface{}{ done, e := zvch.sendDataChannelMessage(ADDED_IN_VIDEO_CHANNEL, "node", member, map[string]interface{}{
"videoChannel": vc, "videoChannel": vc,
}) })
select { select {
@ -569,7 +583,7 @@ broadcastLoop:
continue broadcastLoop continue broadcastLoop
} }
if _, ok := zvch.DataChannels[member]; ok { if _, ok := zvch.DataChannels[member]; ok {
done, e := zvch.sendDataChannelMessage(CHAT_MEMBER_ADDED, "node", member, map[string]interface{}{ done, e := zvch.sendDataChannelMessage(VIDEO_CHANNEL_MEMBER_ADDED, "node", member, map[string]interface{}{
"userId": m, "userId": m,
"channelId": vc.ID, "channelId": vc.ID,
}) })
@ -635,7 +649,7 @@ func (zvch *ZoneVideoChannelsHandler) RemoveVideoChannelMember(channelId string,
} }
var vc *VideoChannel var vc *VideoChannel
_ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) { _ = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
vc := NewVideoChannel(videoChannelConfig.ID, videoChannelConfig.Owner, videoChannelConfig.ChannelType, videoChannelConfig.Members, make([]string, 0), make(map[string]*VideoChannelMember)) vc = NewVideoChannel(videoChannelConfig.ID, videoChannelConfig.Owner, videoChannelConfig.ChannelType, videoChannelConfig.Members, make([]string, 0), make(map[string]*VideoChannelMember))
zvch.VideoChannels[channelId] = vc zvch.VideoChannels[channelId] = vc
return return
}) })
@ -643,7 +657,7 @@ func (zvch *ZoneVideoChannelsHandler) RemoveVideoChannelMember(channelId string,
"userId": channelMember, "userId": channelMember,
"channelId": channelId, "channelId": channelId,
}) })
done, e := zvch.sendDataChannelMessage(REMOVED_FROM_CHAT, "node", channelMember, map[string]interface{}{ done, e := zvch.sendDataChannelMessage(REMOVED_FROM_VIDEO_CHANNEL, "node", channelMember, map[string]interface{}{
"channelId": channelId, "channelId": channelId,
"userId": channelMember, "userId": channelMember,
}) })
@ -657,7 +671,7 @@ broadcastLoop:
if member == channelMember { if member == channelMember {
continue broadcastLoop continue broadcastLoop
} }
done, e := zvch.sendDataChannelMessage(CHAT_MEMBER_REMOVED, "node", member, map[string]interface{}{ done, e := zvch.sendDataChannelMessage(VIDEO_CHANNEL_MEMBER_REMOVED, "node", member, map[string]interface{}{
"userId": channelMember, "userId": channelMember,
"channelId": vc.ID, "channelId": vc.ID,
}) })
@ -685,7 +699,7 @@ func (zvch *ZoneVideoChannelsHandler) SetVideoChannelPrivateForUser(channelId st
"userId": member, "userId": member,
"channelId": channelId, "channelId": channelId,
}) })
done, e := zvch.sendDataChannelMessage(REMOVED_FROM_CHAT, "node", member, map[string]interface{}{ done, e := zvch.sendDataChannelMessage(REMOVED_FROM_VIDEO_CHANNEL, "node", member, map[string]interface{}{
"channelId": channelId, "channelId": channelId,
"userId": member, "userId": member,
}) })
@ -715,7 +729,7 @@ func (zvch *ZoneVideoChannelsHandler) SetVideoChannelPublicForUser(channelId str
"userId": member, "userId": member,
"channelId": channelId, "channelId": channelId,
}) })
done, e := zvch.sendDataChannelMessage(ADDED_IN_CHAT, "node", member, map[string]interface{}{ done, e := zvch.sendDataChannelMessage(ADDED_IN_VIDEO_CHANNEL, "node", member, map[string]interface{}{
"videoChannel": videoChannel, "videoChannel": videoChannel,
}) })
select { select {
@ -788,7 +802,7 @@ func (zvch *ZoneVideoChannelsHandler) JoinVideoChannel(channelId string, userId
videoChannel.CurrentMembersId = append(videoChannel.CurrentMembersId, userId) videoChannel.CurrentMembersId = append(videoChannel.CurrentMembersId, userId)
videoChannel.CurrentMembers[userId] = &VideoChannelMember{} videoChannel.CurrentMembers[userId] = &VideoChannelMember{}
signalMembers := func(members []string) { signalMembers := func(members []string) {
for _, u := range videoChannel.Members { for _, u := range members {
done, e := zvch.sendDataChannelMessage(USER_JOINED_VIDEO_CHANNEL, "node", u, map[string]interface{}{ done, e := zvch.sendDataChannelMessage(USER_JOINED_VIDEO_CHANNEL, "node", u, map[string]interface{}{
"userId": userId, "userId": userId,
"channelId": channelId, "channelId": channelId,
@ -842,7 +856,7 @@ func (zvch *ZoneVideoChannelsHandler) LeaveVideoChannel(channelId string, userId
} }
delete(videoChannel.CurrentMembers, userId) delete(videoChannel.CurrentMembers, userId)
signalMembers := func(members []string) { signalMembers := func(members []string) {
for _, u := range videoChannel.Members { for _, u := range members {
done, e := zvch.sendDataChannelMessage(USER_LEFT_VIDEO_CHANNEL, "node", u, map[string]interface{}{ done, e := zvch.sendDataChannelMessage(USER_LEFT_VIDEO_CHANNEL, "node", u, map[string]interface{}{
"userId": userId, "userId": userId,
"channelId": channelId, "channelId": channelId,
@ -1008,17 +1022,17 @@ func (zvch *ZoneVideoChannelsHandler) handleZoneRequest(ctx context.Context, req
case string(VIDEO_CHANNEL_WEBRTC_CANDIDATE): case string(VIDEO_CHANNEL_WEBRTC_CANDIDATE):
logger.Println("handling video channel webrtc candidate") logger.Println("handling video channel webrtc candidate")
logger.Println(req.Payload) logger.Println(req.Payload)
if err = verifyFieldsString(req.Payload, FROM, "candidate", "sdpMlineIndex", "sdpMid", "channelId", "userId"); err != nil { if err = verifyFieldsString(req.Payload, FROM, "candidate", "sdpMLineIndex", "sdpMid", "channelId", "userId"); err != nil {
return return
} }
logger.Println(req.Payload) logger.Println(req.Payload)
i, convErr := strconv.Atoi(req.Payload["sdpMlineIndex"].(string)) i, convErr := strconv.Atoi(req.Payload["sdpMLineIndex"].(string))
if convErr != nil { if convErr != nil {
return convErr return convErr
} }
sdpMlineIndex := uint16(i) SDPMLineIndex := uint16(i)
sdpMid := req.Payload["sdpMid"].(string) sdpMid := req.Payload["sdpMid"].(string)
logger.Println(sdpMid, sdpMlineIndex) logger.Println(sdpMid, SDPMLineIndex)
err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) { err = atomicallyExecute(zvch.VideoChannelsFlag, func() (err error) {
if _, ok := zvch.VideoChannels[req.Payload["channelId"].(string)]; !ok { if _, ok := zvch.VideoChannels[req.Payload["channelId"].(string)]; !ok {
err = fmt.Errorf("no channel corresponging the one requested") err = fmt.Errorf("no channel corresponging the one requested")
@ -1027,7 +1041,7 @@ func (zvch *ZoneVideoChannelsHandler) handleZoneRequest(ctx context.Context, req
err = zvch.VideoChannels[req.Payload["channelId"].(string)].AddCandidate(&webrtc.ICECandidateInit{ err = zvch.VideoChannels[req.Payload["channelId"].(string)].AddCandidate(&webrtc.ICECandidateInit{
Candidate: req.Payload["candidate"].(string), Candidate: req.Payload["candidate"].(string),
SDPMid: &sdpMid, SDPMid: &sdpMid,
SDPMLineIndex: &sdpMlineIndex, SDPMLineIndex: &SDPMLineIndex,
}, req.Payload["userId"].(string)) }, req.Payload["userId"].(string))
return return
}) })