master
This commit is contained in:
commit
b4a7f4e53f
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.exe
|
||||||
|
.idea
|
58
cert.crt
Normal file
58
cert.crt
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEeDCCAmCgAwIBAgIIEtc4Hk5jNvIwDQYJKoZIhvcNAQELBQAwVDELMAkGA1UE
|
||||||
|
BhMCQ04xDzANBgNVBAcTBlN1emhvdTEQMA4GA1UECgwHRXZhbl9DQTEQMA4GA1UE
|
||||||
|
CwwHRXZhbl9DQTEQMA4GA1UEAwwHRXZhbl9DQTAeFw0yMDA5MDMwMTA1MDBaFw0y
|
||||||
|
MTA5MDMwMTA1MDBaMBkxFzAVBgNVBAMMDioubWlyaWxsaXMuY29tMIIBIjANBgkq
|
||||||
|
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwcf0gqkGW79baJ39995n1YirD4x5KtnC
|
||||||
|
3CsD1gftE/va3GZMToBsP3jdohi5TRvB/CnF6Ngv22kszDWTwLKg/K73D+5dfu8m
|
||||||
|
xVyxx2kU4uzC0NR0Z2AtumWIS05N/S53PWESBbCgPBJLcGKqLULYXf9EvEPtXebh
|
||||||
|
i68C4aGnCmWOcMCLH/VrCcwFT15eR6zsD3whv8Oqpdj5aneItpEaWd8dzBrHO69k
|
||||||
|
byfqK/GL14eLtPClVoY0EQ8z6hNqNTil6eseBdEuwytND5LJnfzarXCBxdnh74CE
|
||||||
|
u4nxZcgEIx3hoSpLJVtHiZfGXPZgtIech3LgcAcAmcaI6wtD94Z5pQIDAQABo4GI
|
||||||
|
MIGFMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFK5Br4x8HWHZOIAYSdYxEj1JW/TF
|
||||||
|
MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
|
||||||
|
JwYDVR0RBCAwHoIOKi5taXJpbGxpcy5jb22CDG1pcmlsbGlzLmNvbTANBgkqhkiG
|
||||||
|
9w0BAQsFAAOCAgEARlaPieyqlQgp9xsMd6Qc59Wx90NJF8BbkXOxxP9A84Pf93D/
|
||||||
|
oYhSWJQepDl6Q9aw4BuXBYMOrBJiOpMwbm1cv8YdCiNZUAS3mOwhgffBUwcJPnGt
|
||||||
|
/YjOulNDYu9sq09MlS6MALMZs7Dqfzh3RHzaWLsSEXZrsFUw2ONCKxjCT33Zv30v
|
||||||
|
1uKpWONRRcfPQV/b72uYrJf+ByX7gYZKSssolj1aq3qtxBitZR05EV4iWCPBp7tz
|
||||||
|
y0wQcz9+vbfE9tdo+VECuD8Rldl770VE6GZnL9/jAwLsv8VB+9z7jlvbcfwdatvv
|
||||||
|
FYE1dSPkw2eQq0/urrFv2thqISKv/qO8SsHYnXfA6gbHuz8WHH6DYc57ArLAO329
|
||||||
|
5bdVEn5hL/rWxUx/QipjCmtlIBG1eqUSfdAKOWK3PVUC3NqwoUSBoNLxJ9xU9rJB
|
||||||
|
7xdOEP0TdXyCOhaiLYCv3FoHRfgTwrtHiSoi3hyHftxjwKGtyrFo1rS3FTdLHret
|
||||||
|
IRWIzIV7NP9gGYNZaCG+V2fQqxOFjH+lR7hvrOHP+oX2M8D6rnIPWvJtZLpnxIyF
|
||||||
|
kDPS2ch0TR5UtohXiGfO5mx/m4CVKcrcRTJGVQSkGv0VZ1cHSvxZayGgTfkOIUBu
|
||||||
|
YPHJeQBt6agjGzAUOxHzRa4xdyYIUMC6/nv8SMAIPTUEBq1GvE4kPmB48MI=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
a2PBtACsbvJx70oiDxZ1yw==
|
||||||
|
-----END CERTIFICATE-----
|
14
common.go
Normal file
14
common.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func formatJson(v interface{}) []byte {
|
||||||
|
marshal, _ := json.Marshal(v)
|
||||||
|
var b bytes.Buffer
|
||||||
|
json.Indent(&b, marshal, "", " ")
|
||||||
|
|
||||||
|
return b.Bytes()
|
||||||
|
}
|
25
login.go
Normal file
25
login.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type LoginRequest struct {
|
||||||
|
Login string `json:"login"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Fingerprint string `json:"fingerprint"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoginResponse struct {
|
||||||
|
CreatedAt int64 `json:"created_at,omitempty"` //1599031125
|
||||||
|
ApiKey string `json:"apikey,omitempty"` //"apikey-af461547-da12-5e33-9a99-c63d9000762b"
|
||||||
|
MacAddress string `json:"mac_address,omitempty"` //"04ea56b3f380"
|
||||||
|
Metadata string `json:"metadata,omitempty"` //"{\"os\":\"1\",\"state\":\"online\",\"streamer\":\"0\",\"type\":\"2\"}"
|
||||||
|
Fingerprint string `json:"fingerprint,omitempty"` //"fingerprint": "45c9dd6c-6925-a994-3fc3-c26cdec786ce",
|
||||||
|
Id string `json:"id,omitempty"` // "id": "device-4d4c9fba-4d11-5b10-b3b3-05346a7bd4d2",
|
||||||
|
Client string `json:"client,omitempty"` //"client": "client-8c220f6e-3432-5f41-80f2-b830f7b60658",
|
||||||
|
Name string `json:"name,omitemptys"` //"name": "DESKTOP-3H0CLPE"
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddDeviceRequest struct {
|
||||||
|
Name string `json:"name"` //DESKTOP-3H0CLPE
|
||||||
|
MacAddress string `json:"mac_address"` //04ea56b3f380
|
||||||
|
Fingerprint string `json:"fingerprint"`
|
||||||
|
Metadata string `json:"metadata"`
|
||||||
|
}
|
251
management.go
Normal file
251
management.go
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
uuid "github.com/satori/go.uuid"
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MonfloManagement struct {
|
||||||
|
clients []*ClientInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientInfo struct {
|
||||||
|
UserName string `json:"user_name"`
|
||||||
|
Metadata string `json:"metadata"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
PrivateAddr string `json:"private_addr"`
|
||||||
|
PublicAddr string `json:"public_addr"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
Bitrate int `json:"bitrate"`
|
||||||
|
Formats []string `json:"formats"` //h264 h265
|
||||||
|
Stream string `json:"stream"`
|
||||||
|
ClientStatus string `json:"client_status"` //online offline
|
||||||
|
StreamStatus string `json:"stream_status"` //ready busy
|
||||||
|
CreatedAt int64 `json:"created_at"` //1599031125
|
||||||
|
ApiKey string `json:"apikey"` //"apikey-af461547-da12-5e33-9a99-c63d9000762b"
|
||||||
|
MacAddress string `json:"mac_address"` //"04ea56b3f380"
|
||||||
|
Fingerprint string `json:"fingerprint"` //"fingerprint": "45c9dd6c-6925-a994-3fc3-c26cdec786ce",
|
||||||
|
Id string `json:"id"` // "id": "device-4d4c9fba-4d11-5b10-b3b3-05346a7bd4d2",
|
||||||
|
Client string `json:"client"` //"client": "client-8c220f6e-3432-5f41-80f2-b830f7b60658",
|
||||||
|
wsConn *websocket.Conn
|
||||||
|
StreamId string `json:"stream_id"` //40832596627
|
||||||
|
ClientType string `json:"client_type"` //client server
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) checkExist(userName string, fingerprint string) bool {
|
||||||
|
if m.clients == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, client := range m.clients {
|
||||||
|
if client.Fingerprint == fingerprint && client.UserName == userName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) getClient(userName string, fingerprint string) *ClientInfo {
|
||||||
|
|
||||||
|
for _, client := range m.clients {
|
||||||
|
if client.Fingerprint == fingerprint && client.UserName == userName {
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) getClientByApiKey(apiKey string) *ClientInfo {
|
||||||
|
for _, client := range m.clients {
|
||||||
|
if client.ApiKey == apiKey {
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) getClientByFingerprint(fingerprint string) *ClientInfo {
|
||||||
|
|
||||||
|
for _, client := range m.clients {
|
||||||
|
if client.Fingerprint == fingerprint {
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) addClient(userName string, fingerprint string) {
|
||||||
|
if !m.checkExist(userName, fingerprint) {
|
||||||
|
client := &ClientInfo{
|
||||||
|
UserName: userName,
|
||||||
|
CreatedAt: time.Now().Unix(),
|
||||||
|
ApiKey: "apikey-" + fingerprint,
|
||||||
|
Fingerprint: fingerprint,
|
||||||
|
Id: "device-" + fingerprint,
|
||||||
|
Client: "client-" + fingerprint,
|
||||||
|
ClientStatus: "offline",
|
||||||
|
}
|
||||||
|
m.clients = append(m.clients, client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) addServer(clientStr string) *ClientInfo {
|
||||||
|
for _, c := range m.clients {
|
||||||
|
if c.Client == clientStr {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client := &ClientInfo{
|
||||||
|
Client: clientStr,
|
||||||
|
CreatedAt: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
m.clients = append(m.clients, client)
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) addDevice(request AddDeviceRequest) {
|
||||||
|
client := m.getClientByFingerprint(request.Fingerprint)
|
||||||
|
client.Name = request.Name
|
||||||
|
client.Fingerprint = request.Fingerprint
|
||||||
|
client.Metadata = request.Metadata
|
||||||
|
client.MacAddress = request.MacAddress
|
||||||
|
if strings.Contains(request.Metadata, "\"streamer\":\"1\",") {
|
||||||
|
client.ClientType = "server"
|
||||||
|
} else {
|
||||||
|
client.ClientType = "client"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) getPeers(userName string) map[string]PeerInfo {
|
||||||
|
peers := make(map[string]PeerInfo)
|
||||||
|
|
||||||
|
if m.clients == nil {
|
||||||
|
return peers
|
||||||
|
}
|
||||||
|
for _, client := range m.clients {
|
||||||
|
if client.UserName == userName {
|
||||||
|
streams := make(map[string]StreamInfo)
|
||||||
|
var formats = []string{"h264", "h265"}
|
||||||
|
if client.Stream != "" {
|
||||||
|
streams[client.Stream] = StreamInfo{
|
||||||
|
Id: client.Stream,
|
||||||
|
Status: client.StreamStatus,
|
||||||
|
OwnerClient: client.Client,
|
||||||
|
Name: client.Name,
|
||||||
|
PrivateAddr: client.PrivateAddr,
|
||||||
|
PublicAddr: client.PublicAddr,
|
||||||
|
Key: client.Key,
|
||||||
|
Bitrate: 500000,
|
||||||
|
Formats: formats,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
peers[client.Client] = PeerInfo{
|
||||||
|
Name: client.Name,
|
||||||
|
Status: client.ClientStatus, //online;offline
|
||||||
|
LastIP: IPInfo{
|
||||||
|
PrivateAddr: client.PrivateAddr,
|
||||||
|
PublicAddr: client.PublicAddr,
|
||||||
|
},
|
||||||
|
Streams: streams,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) getDevices(userName string) []LoginResponse {
|
||||||
|
var devices []LoginResponse
|
||||||
|
|
||||||
|
if m.clients == nil {
|
||||||
|
return devices
|
||||||
|
}
|
||||||
|
for _, client := range m.clients {
|
||||||
|
if client.UserName == userName {
|
||||||
|
device := LoginResponse{
|
||||||
|
CreatedAt: client.CreatedAt,
|
||||||
|
ApiKey: client.ApiKey,
|
||||||
|
MacAddress: client.MacAddress,
|
||||||
|
Metadata: client.Metadata,
|
||||||
|
Fingerprint: client.Fingerprint,
|
||||||
|
Id: client.Id,
|
||||||
|
Client: client.Client,
|
||||||
|
Name: client.Name,
|
||||||
|
}
|
||||||
|
devices = append(devices, device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return devices
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) genId(client *ClientInfo) {
|
||||||
|
id := 10000000009 + 8*rand.Int63n(5)
|
||||||
|
for {
|
||||||
|
if !m.checkIdExist(strconv.FormatInt(id, 10)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
id = 10000000009 + 8*rand.Int63n(10000)
|
||||||
|
}
|
||||||
|
client.StreamId = strconv.FormatInt(id, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) genStream(client *ClientInfo) {
|
||||||
|
uid, _ := uuid.NewV4()
|
||||||
|
client.Stream = "stream-" + uid.String()
|
||||||
|
client.StreamStatus = "ready"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) checkIdExist(id string) bool {
|
||||||
|
for _, c := range m.clients {
|
||||||
|
if c.Id == id {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) setOffline(clientId string) {
|
||||||
|
del := -1
|
||||||
|
for i, c := range m.clients {
|
||||||
|
if c.Client == clientId {
|
||||||
|
if c.ApiKey == "" && c.Id == "" {
|
||||||
|
del = i
|
||||||
|
}
|
||||||
|
c.ClientStatus = "offline"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if del >= 0 {
|
||||||
|
m.clients = append(m.clients[:del], m.clients[del+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) getClientStream(stream string) *ClientInfo {
|
||||||
|
for _, c := range m.clients {
|
||||||
|
if c.Stream == stream {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) getClientStreamId(streamId string) *ClientInfo {
|
||||||
|
for _, c := range m.clients {
|
||||||
|
if c.StreamId == streamId {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonfloManagement) getUserAllClient(userName string) []*ClientInfo {
|
||||||
|
var clients []*ClientInfo
|
||||||
|
for _, c := range m.clients {
|
||||||
|
if c.UserName == userName {
|
||||||
|
clients = append(clients, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clients
|
||||||
|
}
|
583
monflo.go
Normal file
583
monflo.go
Normal file
@ -0,0 +1,583 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
uuid "github.com/satori/go.uuid"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
upgrader = websocket.Upgrader{
|
||||||
|
//允许跨域访问
|
||||||
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
EnableCompression: false,
|
||||||
|
}
|
||||||
|
managementService = MonfloManagement{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
go stun()
|
||||||
|
http.HandleFunc("/2015-10-26/verification", verification)
|
||||||
|
http.HandleFunc("/2015-10-26/devices", device)
|
||||||
|
http.HandleFunc("/2015-10-26", wsHandler)
|
||||||
|
http.HandleFunc("/config", configHandler)
|
||||||
|
http.HandleFunc("/set", configSetHandler)
|
||||||
|
http.HandleFunc("/peer", peerHandler)
|
||||||
|
err := http.ListenAndServeTLS("", "cert.crt", "private.key", nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
marshal, _ := json.Marshal(managementService.clients)
|
||||||
|
w.Write(marshal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func configSetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var clients []*ClientInfo
|
||||||
|
data, _ := ioutil.ReadAll(r.Body)
|
||||||
|
|
||||||
|
json.Unmarshal(data, &clients)
|
||||||
|
managementService.clients = clients
|
||||||
|
}
|
||||||
|
|
||||||
|
func peerHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
peers := managementService.getPeers(r.FormValue("username"))
|
||||||
|
marshal, _ := json.Marshal(peers)
|
||||||
|
w.Write(marshal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stun() {
|
||||||
|
address := "0.0.0.0:3478"
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", address)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := net.ListenUDP("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Here must use make and give the lenth of buffer
|
||||||
|
data := make([]byte, 4096)
|
||||||
|
_, rAddr, err := conn.ReadFromUDP(data)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
//file, _ := ioutil.ReadFile("data.bin")
|
||||||
|
//_, err = conn.WriteToUDP(file, rAddr)
|
||||||
|
_, err = conn.WriteToUDP([]byte{254, 239, 1, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 6, 58, 210, 98, 46, 174, 198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, rAddr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var (
|
||||||
|
wsConn *websocket.Conn
|
||||||
|
err error
|
||||||
|
mutex sync.Mutex
|
||||||
|
client *ClientInfo
|
||||||
|
)
|
||||||
|
//Upgrade websocket(返回给客户端的消息)
|
||||||
|
if wsConn, err = upgrader.Upgrade(w, r, nil); err != nil {
|
||||||
|
//报错了,直接返回底层的websocket链接就会终断掉
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
mutex.Lock()
|
||||||
|
err2 := wsConn.WriteMessage(websocket.PingMessage, []byte{})
|
||||||
|
mutex.Unlock()
|
||||||
|
if err2 != nil {
|
||||||
|
wsConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf("Runtime error caught: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf("Runtime error caught: %v", r)
|
||||||
|
}
|
||||||
|
if nil != client {
|
||||||
|
managementService.setOffline(client.Client)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
mutex.Lock()
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, []byte("{\n \"event\": \"cookie_created\",\n \"data\": \"5f0389ae63c5c5962de0bf1fa7edbb9e7605da388a8ffb2f69af4c47b1fde020\"\n}"))
|
||||||
|
mutex.Unlock()
|
||||||
|
client = new(ClientInfo)
|
||||||
|
for {
|
||||||
|
messageType, p, err := wsConn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("WebSocket异常断开," + wsConn.RemoteAddr().String() + ",异常信息:" + err.Error() + "\n")
|
||||||
|
wsConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if messageType == websocket.CloseMessage {
|
||||||
|
fmt.Printf("WebSocket断开," + wsConn.RemoteAddr().String() + "\n")
|
||||||
|
wsConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf(string(p) + "\n")
|
||||||
|
mutex.Lock()
|
||||||
|
tempClient := wsProcess(wsConn, p, client)
|
||||||
|
mutex.Unlock()
|
||||||
|
if tempClient != nil {
|
||||||
|
client = tempClient
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsProcess(wsConn *websocket.Conn, data []byte, client *ClientInfo) *ClientInfo {
|
||||||
|
|
||||||
|
str := string(data)
|
||||||
|
|
||||||
|
if len(str) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if str[0:1] == "{" {
|
||||||
|
|
||||||
|
var request MonfloRequest
|
||||||
|
json.Unmarshal(data, &request)
|
||||||
|
|
||||||
|
if request.Uri == "/" {
|
||||||
|
if request.Headers.ApiKey != "" {
|
||||||
|
client = managementService.getClientByApiKey(request.Headers.ApiKey)
|
||||||
|
client.wsConn = wsConn
|
||||||
|
client.ClientStatus = "online"
|
||||||
|
response := WelcomeInfo{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Event: "welcome",
|
||||||
|
},
|
||||||
|
Data: WelcomeData{
|
||||||
|
Client: client.Client,
|
||||||
|
Features: Features{
|
||||||
|
MaxUserProfiles: 1,
|
||||||
|
MaxStreams: 128,
|
||||||
|
MaxDevices: 128,
|
||||||
|
MaxP2pResolution: 2160,
|
||||||
|
MaxP2pFrames: 60,
|
||||||
|
MaxRelayResolution: 2160,
|
||||||
|
MaxRelayFrames: 60,
|
||||||
|
Hevc: true,
|
||||||
|
SessionRecording: true,
|
||||||
|
ManagementConsole: true,
|
||||||
|
ConnectAnywhere: false,
|
||||||
|
Support: false,
|
||||||
|
CommercialUse: false,
|
||||||
|
DeviceThumbnails: false,
|
||||||
|
FileTransfer: true,
|
||||||
|
},
|
||||||
|
Peers: managementService.getPeers(client.UserName),
|
||||||
|
Subscription: 3,
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response))
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, []byte("{\"status\":200}"))
|
||||||
|
|
||||||
|
//如果是客户端登陆,需要通知该用户下所有的服务端
|
||||||
|
//if client.ClientType == "client" {
|
||||||
|
// streamClients := managementService.getUserAllClient(client.UserName)
|
||||||
|
// if streamClients != nil {
|
||||||
|
// for _, streamClient := range streamClients {
|
||||||
|
// if streamClient.ClientStatus == "online" && streamClient.ClientType == "server" {
|
||||||
|
// eventResponse := &ClientConnectedEvent{
|
||||||
|
// MonfloResponse: MonfloResponse{
|
||||||
|
// Event: "client_connected",
|
||||||
|
// },
|
||||||
|
// Data: client.Client,
|
||||||
|
// }
|
||||||
|
// streamClient.wsConn.WriteMessage(websocket.TextMessage, formatJson(eventResponse))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
return client
|
||||||
|
} else {
|
||||||
|
//服务端匿名登录
|
||||||
|
uid, _ := uuid.NewV4()
|
||||||
|
|
||||||
|
response := WelcomeInfo{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Event: "welcome",
|
||||||
|
},
|
||||||
|
Data: WelcomeData{
|
||||||
|
Client: "client-" + uid.String(),
|
||||||
|
Features: Features{
|
||||||
|
MaxUserProfiles: 1,
|
||||||
|
MaxStreams: 128,
|
||||||
|
MaxDevices: 128,
|
||||||
|
MaxP2pResolution: 2160,
|
||||||
|
MaxP2pFrames: 60,
|
||||||
|
MaxRelayResolution: 2160,
|
||||||
|
MaxRelayFrames: 60,
|
||||||
|
Hevc: true,
|
||||||
|
SessionRecording: true,
|
||||||
|
ManagementConsole: true,
|
||||||
|
ConnectAnywhere: false,
|
||||||
|
Support: false,
|
||||||
|
CommercialUse: false,
|
||||||
|
DeviceThumbnails: false,
|
||||||
|
FileTransfer: false,
|
||||||
|
},
|
||||||
|
Subscription: 3,
|
||||||
|
Peers: make(map[string]PeerInfo),
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
client = managementService.addServer(response.Data.Client)
|
||||||
|
client.wsConn = wsConn
|
||||||
|
client.ClientType = "server"
|
||||||
|
client.ClientStatus = "online"
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response))
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, []byte("{\"status\":200}"))
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Uri == "/devices/current" && request.Method == "PATCH" {
|
||||||
|
client.Metadata = request.Data.Metadata
|
||||||
|
response := PatchDeviceResponse{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Status: 200,
|
||||||
|
},
|
||||||
|
Data: LoginResponse{
|
||||||
|
Metadata: client.Metadata,
|
||||||
|
Id: client.Id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Method == "PATCH" && strings.Contains(request.Uri, "/streams/stream-") {
|
||||||
|
streamId := request.Uri[9:]
|
||||||
|
client.Key = request.Data.Key
|
||||||
|
client.PrivateAddr = request.Data.PrivateAddr
|
||||||
|
client.PublicAddr = request.Data.PublicAddr
|
||||||
|
client.StreamStatus = request.Data.Status
|
||||||
|
response := MonfloResponse{
|
||||||
|
Status: 200,
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response))
|
||||||
|
|
||||||
|
//通知所有该账号的客户端更新服务端信息
|
||||||
|
|
||||||
|
streamClients := managementService.getUserAllClient(client.UserName)
|
||||||
|
if streamClients != nil {
|
||||||
|
for _, streamClient := range streamClients {
|
||||||
|
if streamClient.ClientStatus == "online" && streamClient.ClientType == "client" {
|
||||||
|
response2 := &StreamResponse{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Event: "stream_updated",
|
||||||
|
},
|
||||||
|
Data: StreamInfo{
|
||||||
|
Id: streamId,
|
||||||
|
Key: request.Data.Key,
|
||||||
|
Status: request.Data.Status,
|
||||||
|
OwnerClient: client.Client,
|
||||||
|
PrivateAddr: request.Data.PrivateAddr,
|
||||||
|
PublicAddr: request.Data.PublicAddr,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if request.Uri == "/devices" && request.Method == "GET" {
|
||||||
|
response := DeviceResponse{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Status: 200,
|
||||||
|
},
|
||||||
|
Data: LoginResponse{
|
||||||
|
Metadata: client.Metadata,
|
||||||
|
Id: client.Id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response))
|
||||||
|
}
|
||||||
|
if request.Uri == "/peers" && request.Method == "GET" {
|
||||||
|
peers := managementService.getPeers(client.UserName)
|
||||||
|
response := PeersResponse{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Status: 200,
|
||||||
|
},
|
||||||
|
Data: peers,
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Method == "POST" && request.Uri == "/incognitos" {
|
||||||
|
if request.Data.Stream != "" {
|
||||||
|
//登录账户模式
|
||||||
|
managementService.genId(client)
|
||||||
|
} else {
|
||||||
|
//服务端匿名模式
|
||||||
|
client.PrivateAddr = request.Data.PrivateAddr
|
||||||
|
client.PublicAddr = request.Data.PublicAddr
|
||||||
|
client.Bitrate = request.Data.Bitrate
|
||||||
|
client.Key = request.Data.Key
|
||||||
|
client.Formats = request.Data.Formats
|
||||||
|
//分配Id
|
||||||
|
managementService.genId(client)
|
||||||
|
client.Id = client.StreamId
|
||||||
|
//分配StreamId
|
||||||
|
managementService.genStream(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &IncognitosResponse{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Status: 200,
|
||||||
|
},
|
||||||
|
Data: IncognitosData{
|
||||||
|
Id: client.StreamId,
|
||||||
|
Stream: client.Stream,
|
||||||
|
Client: client.Client,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Method == "POST" && request.Uri == "/streams" {
|
||||||
|
client.PrivateAddr = request.Data.PrivateAddr
|
||||||
|
client.PublicAddr = request.Data.PublicAddr
|
||||||
|
client.Bitrate = request.Data.Bitrate
|
||||||
|
client.Key = request.Data.Key
|
||||||
|
client.Formats = request.Data.Formats
|
||||||
|
|
||||||
|
managementService.genStream(client)
|
||||||
|
|
||||||
|
response := &StreamResponse{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Status: 200,
|
||||||
|
},
|
||||||
|
Data: StreamInfo{
|
||||||
|
Id: client.Stream,
|
||||||
|
Status: "ready",
|
||||||
|
OwnerClient: client.Client,
|
||||||
|
Name: "0",
|
||||||
|
PrivateAddr: client.PrivateAddr,
|
||||||
|
PublicAddr: client.PublicAddr,
|
||||||
|
Key: client.Key,
|
||||||
|
Bitrate: client.Bitrate,
|
||||||
|
Formats: client.Formats,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Method == "GET" && (request.Uri == "/invitations/logins" || request.Uri == "/invitations" || request.Uri == "/shortcuts") {
|
||||||
|
response := &MonfloResponse{
|
||||||
|
Status: 200,
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Method == "GET" && strings.Contains(request.Uri, "/streams/stream-") {
|
||||||
|
streamCode := request.Uri[9:]
|
||||||
|
stream := managementService.getClientStream(streamCode)
|
||||||
|
if stream != nil {
|
||||||
|
response := &StreamResponse{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Status: 200,
|
||||||
|
},
|
||||||
|
Data: StreamInfo{
|
||||||
|
Id: stream.Stream,
|
||||||
|
Status: "ready",
|
||||||
|
OwnerClient: stream.Client,
|
||||||
|
Name: "0",
|
||||||
|
PrivateAddr: stream.PrivateAddr,
|
||||||
|
PublicAddr: stream.PublicAddr,
|
||||||
|
Key: stream.Key,
|
||||||
|
Bitrate: stream.Bitrate,
|
||||||
|
Formats: stream.Formats,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response))
|
||||||
|
|
||||||
|
response2 := &StreamReadResponse{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Event: "_stream_read",
|
||||||
|
},
|
||||||
|
Data: StreamReadData{
|
||||||
|
Client: client.Client,
|
||||||
|
Format: request.Headers.Format,
|
||||||
|
Id: stream.Stream,
|
||||||
|
PrivateAddr: request.Headers.PrivateAddr,
|
||||||
|
PublicAddr: request.Headers.PublicAddr,
|
||||||
|
Type: request.Headers.Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
stream.wsConn.WriteMessage(websocket.TextMessage, formatJson(response2))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Method == "GET" && strings.Contains(request.Uri, "/incognitos/") {
|
||||||
|
stream := managementService.getClientStreamId(request.Uri[12:])
|
||||||
|
if stream != nil {
|
||||||
|
response := &StreamResponse{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Status: 200,
|
||||||
|
},
|
||||||
|
Data: StreamInfo{
|
||||||
|
Id: stream.Stream,
|
||||||
|
Status: "ready",
|
||||||
|
OwnerClient: stream.Client,
|
||||||
|
Name: stream.StreamId,
|
||||||
|
PrivateAddr: stream.PrivateAddr,
|
||||||
|
PublicAddr: stream.PublicAddr,
|
||||||
|
Key: stream.Key,
|
||||||
|
Bitrate: stream.Bitrate,
|
||||||
|
Formats: stream.Formats,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(response))
|
||||||
|
|
||||||
|
response2 := &StreamReadResponse{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Event: "_stream_read",
|
||||||
|
},
|
||||||
|
Data: StreamReadData{
|
||||||
|
Client: client.Client,
|
||||||
|
Format: request.Headers.Format,
|
||||||
|
Id: stream.Stream,
|
||||||
|
PrivateAddr: request.Headers.PrivateAddr,
|
||||||
|
PublicAddr: request.Headers.PublicAddr,
|
||||||
|
Type: request.Headers.Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.wsConn.WriteMessage(websocket.TextMessage, formatJson(response2))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if str[0:1] == "[" {
|
||||||
|
var requests []MonfloRequest
|
||||||
|
json.Unmarshal(data, &requests)
|
||||||
|
|
||||||
|
var datas []interface{}
|
||||||
|
for _, request := range requests {
|
||||||
|
if request.Uri == "/devices" && request.Method == "GET" {
|
||||||
|
device := managementService.getDevices(client.UserName)
|
||||||
|
response := DevicesResponse{
|
||||||
|
MonfloResponse: MonfloResponse{
|
||||||
|
Status: 200,
|
||||||
|
},
|
||||||
|
Data: device,
|
||||||
|
}
|
||||||
|
datas = append(datas, response)
|
||||||
|
}
|
||||||
|
if request.Uri == "/info" && request.Method == "GET" {
|
||||||
|
|
||||||
|
response := MonfloResponse{
|
||||||
|
Status: 200,
|
||||||
|
}
|
||||||
|
datas = append(datas, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wsConn.WriteMessage(websocket.TextMessage, formatJson(datas))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func verification(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Server", "MonfloHTTPServer")
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print("verification:" + string(data) + "\n")
|
||||||
|
var loginRequest LoginRequest
|
||||||
|
json.Unmarshal(data, &loginRequest)
|
||||||
|
|
||||||
|
if !managementService.checkExist(loginRequest.Login, loginRequest.Fingerprint) {
|
||||||
|
w.WriteHeader(404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := managementService.getClient(loginRequest.Login, loginRequest.Fingerprint)
|
||||||
|
client.UserName = loginRequest.Login
|
||||||
|
response := LoginResponse{
|
||||||
|
CreatedAt: client.CreatedAt,
|
||||||
|
ApiKey: client.ApiKey,
|
||||||
|
MacAddress: client.MacAddress,
|
||||||
|
Metadata: client.Metadata,
|
||||||
|
Fingerprint: client.Fingerprint,
|
||||||
|
Id: client.Id,
|
||||||
|
Client: client.Client,
|
||||||
|
Name: client.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
marshal, _ := json.Marshal(response)
|
||||||
|
w.Write(marshal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func device(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Server", "MonfloHTTPServer")
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Print("Add Device:" + string(data) + "\n")
|
||||||
|
var addDeviceRequest AddDeviceRequest
|
||||||
|
json.Unmarshal(data, &addDeviceRequest)
|
||||||
|
|
||||||
|
userName := r.Header.Get("Monflo-login")
|
||||||
|
managementService.addClient(userName, addDeviceRequest.Fingerprint)
|
||||||
|
managementService.addDevice(addDeviceRequest)
|
||||||
|
|
||||||
|
client := managementService.getClientByFingerprint(addDeviceRequest.Fingerprint)
|
||||||
|
|
||||||
|
response := LoginResponse{
|
||||||
|
CreatedAt: client.CreatedAt,
|
||||||
|
ApiKey: client.ApiKey,
|
||||||
|
MacAddress: client.MacAddress,
|
||||||
|
Metadata: client.Metadata,
|
||||||
|
Fingerprint: client.Fingerprint,
|
||||||
|
Id: client.Id,
|
||||||
|
Client: client.Client,
|
||||||
|
Name: client.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
marshal, _ := json.Marshal(response)
|
||||||
|
w.Write(marshal)
|
||||||
|
}
|
27
private.key
Normal file
27
private.key
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAwcf0gqkGW79baJ39995n1YirD4x5KtnC3CsD1gftE/va3GZM
|
||||||
|
ToBsP3jdohi5TRvB/CnF6Ngv22kszDWTwLKg/K73D+5dfu8mxVyxx2kU4uzC0NR0
|
||||||
|
Z2AtumWIS05N/S53PWESBbCgPBJLcGKqLULYXf9EvEPtXebhi68C4aGnCmWOcMCL
|
||||||
|
H/VrCcwFT15eR6zsD3whv8Oqpdj5aneItpEaWd8dzBrHO69kbyfqK/GL14eLtPCl
|
||||||
|
VoY0EQ8z6hNqNTil6eseBdEuwytND5LJnfzarXCBxdnh74CEu4nxZcgEIx3hoSpL
|
||||||
|
JVtHiZfGXPZgtIech3LgcAcAmcaI6wtD94Z5pQIDAQABAoIBAAJmo9TqmzWPzWYi
|
||||||
|
bv8fNlIi+1uZ9fZd9FgeAFIqjvlsaW3JprBiTvUKXlSf0cvuyByDt/wGkbE6QF/X
|
||||||
|
WhlNHUmEMXN1FJt6AxT27Qz3dFbLcC5+M2MEggyJLYMhWT4F0VxlU3/WjGWyJFUk
|
||||||
|
I8+jwGKJwyRCAzLipXDBnluFUTiDvBbw3SpS+C6zn6wcHCfO0waDUc6Gsn9/Von/
|
||||||
|
4/AXx6tQfGYXhGYsBR/MxNQpbNol0CsAdTpi5wwZ5LUQf2Y/JlUGqQ1UwBouSZPu
|
||||||
|
k/djzrzBqsFrMqxKE4+M1TuKluawlDWPEqKgcMooegFoopYfLavKtX8lLQFDE2xo
|
||||||
|
7pIEvZECgYEA4nOc9W6iwP821p7rkUjnaaLth9jlt05t/sipN8eE8vX70ITHlNda
|
||||||
|
vQVLsAbyY3BWzyEtg+KTsDinEyzdfKuqnBRbxda/7PwZ/VfOwSLVbgkXL2No68eV
|
||||||
|
x1CzigJo7EERk+gx/zjCYlSpDVk3ycIimYkVWkIU9tmqtEXnFFdx1ocCgYEA2xEE
|
||||||
|
Bi9jh3cY2QUCewz3Dz5rMKZkkEj3lBAD7pMTy53ZTbocL1ZTnWGyacW0byiOlnzd
|
||||||
|
iny71iFAx4fdgXrhFmO/3x9duy25HJMlKyvG6U+6lD/Gr7JmX5WXGlgT61d+JfR3
|
||||||
|
mtYwaM0f2ngxWYuwvfVq/WXaB7iESNOwfOHfzXMCgYADdXKbSRJRUlSbGJhOgseO
|
||||||
|
FH/+SDDSCO+jKZt0D6cXMuyitbR6sINhSbhrOt/u5uNcjIwubIKG+YaLw26qndCg
|
||||||
|
S6tPLUWHMB6RgQrWZlrOMHNbNPCAUW8XOUNUw06o9SF4md5RoKNPby2Z15gDi+SN
|
||||||
|
Zcuesk2xq4dw83RhGijR9wKBgBiCGtUmUBhDtr/w04o4tRs7fHqA4xdRUoF6GTaD
|
||||||
|
td891aXggG67VbdxyqgSulEFVI55gb+QnOMj7T9lb96ghLYgisLHm5DpWKBdxfbC
|
||||||
|
ewp3JQSY7f2SE+n1rmYAHJpju3U7mHX2KIxRBpNGhx7hhfB6mHGpB299sS8En+YY
|
||||||
|
zxUJAoGBAKXCJqyEwDkZfov312UNQ5S7PzkuM6eTNa0Lrbo/lrn/srdMN8Iawpqs
|
||||||
|
R27AXbHEMuHjTFVIM77ju735XFn2gYdP3n5nPKWmiyd3GqSso07C3dxdTYjKPFT8
|
||||||
|
wmIf+gNAN/0r/j7G0p+pIQQ6EWOfTGmXJvdYm1ZMyUvEkKZxmO6B
|
||||||
|
-----END RSA PRIVATE KEY-----
|
29
request_struct.go
Normal file
29
request_struct.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type MonfloRequest struct {
|
||||||
|
Method string `json:"method,omitempty"`
|
||||||
|
Uri string `json:"uri,omitempty"`
|
||||||
|
Headers MonfloHeader `json:"headers,omitempty"`
|
||||||
|
Data MonfloRequestData `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MonfloHeader struct {
|
||||||
|
ApiKey string `json:"apikey,omitempty"`
|
||||||
|
PrivateAddr string `json:"private_addr,omitempty"`
|
||||||
|
PublicAddr string `json:"public_addr,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"` //p2p
|
||||||
|
Format string `json:"format,omitempty"` //h264
|
||||||
|
}
|
||||||
|
|
||||||
|
type MonfloRequestData struct {
|
||||||
|
Metadata string `json:"metadata,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
PrivateAddr string `json:"private_addr,omitempty"`
|
||||||
|
PublicAddr string `json:"public_addr,omitempty"`
|
||||||
|
Key string `json:"key,omitempty"`
|
||||||
|
Bitrate int `json:"bitrate,omitempty"`
|
||||||
|
Formats []string `json:"formats,omitempty"` //h264 h265
|
||||||
|
Stream string `json:"stream,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Client string `json:"client,omitempty"`
|
||||||
|
}
|
60
response_struct.go
Normal file
60
response_struct.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type MonfloResponse struct {
|
||||||
|
Event string `json:"event,omitempty"`
|
||||||
|
Status int `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DevicesResponse struct {
|
||||||
|
MonfloResponse
|
||||||
|
Data []LoginResponse `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceResponse struct {
|
||||||
|
MonfloResponse
|
||||||
|
Data LoginResponse `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PatchDeviceResponse struct {
|
||||||
|
MonfloResponse
|
||||||
|
Data LoginResponse `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PeersResponse struct {
|
||||||
|
MonfloResponse
|
||||||
|
Data map[string]PeerInfo `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IncognitosResponse struct {
|
||||||
|
MonfloResponse
|
||||||
|
Data IncognitosData `json:"data"`
|
||||||
|
}
|
||||||
|
type IncognitosData struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Stream string `json:"stream"`
|
||||||
|
Client string `json:"client"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamResponse struct {
|
||||||
|
MonfloResponse
|
||||||
|
Data StreamInfo `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamReadResponse struct {
|
||||||
|
MonfloResponse
|
||||||
|
Data StreamReadData `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamReadData struct {
|
||||||
|
Client string `json:"client"` //client-528591bf-bc07-59c5-8f24-a543bf383680
|
||||||
|
Format string `json:"format"` //h264
|
||||||
|
Id string `json:"id"` //stream-16f25ad7-be2b-5d45-a777-47d8f4c77179
|
||||||
|
PrivateAddr string `json:"private_addr"` //172.19.10.175:58169
|
||||||
|
PublicAddr string `json:"public_addr"` //58.210.98.46:44742
|
||||||
|
Type string `json:"type"` //p2p
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConnectedEvent struct {
|
||||||
|
MonfloResponse
|
||||||
|
Data string `json:"data"`
|
||||||
|
}
|
57
welcome_struct.go
Normal file
57
welcome_struct.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
|
||||||
|
type Features struct {
|
||||||
|
MaxUserProfiles int `json:"max_user_profiles"`
|
||||||
|
MaxStreams int `json:"max_streams"`
|
||||||
|
MaxDevices int `json:"max_devices"`
|
||||||
|
MaxP2pResolution int `json:"max_p2p_resolution"`
|
||||||
|
MaxP2pFrames int `json:"max_p2p_frames"`
|
||||||
|
MaxRelayResolution int `json:"max_relay_resolution"`
|
||||||
|
MaxRelayFrames int `json:"max_relay_frames"`
|
||||||
|
Hevc bool `json:"hevc"`
|
||||||
|
SessionRecording bool `json:"session_recording"`
|
||||||
|
ManagementConsole bool `json:"management_console"`
|
||||||
|
ConnectAnywhere bool `json:"connect_anywhere"`
|
||||||
|
Support bool `json:"support"`
|
||||||
|
CommercialUse bool `json:"commercial_use"`
|
||||||
|
DeviceThumbnails bool `json:"device_thumbnails"`
|
||||||
|
FileTransfer bool `json:"file_transfer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IPInfo struct {
|
||||||
|
PrivateAddr string `json:"private_addr"`
|
||||||
|
PublicAddr string `json:"public_addr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamInfo struct {
|
||||||
|
Id string `json:"id,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"` //ready busy
|
||||||
|
OwnerClient string `json:"owner_client,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
PrivateAddr string `json:"private_addr,omitempty"`
|
||||||
|
PublicAddr string `json:"public_addr,omitempty"`
|
||||||
|
Key string `json:"key,omitempty"`
|
||||||
|
Bitrate int `json:"bitrate,omitempty"`
|
||||||
|
Formats []string `json:"formats,omitempty"` //h264 h265
|
||||||
|
}
|
||||||
|
|
||||||
|
type WelcomeData struct {
|
||||||
|
Client string `json:"client,omitempty"`
|
||||||
|
Features Features `json:"features,omitempty"`
|
||||||
|
Peers map[string]PeerInfo `json:"peers,omitempty"`
|
||||||
|
Subscription int `json:"subscription,omitempty"` //1=免费 2=专业 3=旗舰
|
||||||
|
Timestamp int64 `json:"timestamp,omitempty"` //1599118479
|
||||||
|
}
|
||||||
|
|
||||||
|
type WelcomeInfo struct {
|
||||||
|
MonfloResponse
|
||||||
|
Data WelcomeData `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PeerInfo struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
LastIP IPInfo `json:"last_ip,omitempty"`
|
||||||
|
Streams map[string]StreamInfo `json:"streams"`
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user