This commit is contained in:
wenyifan 2022-08-03 10:30:59 +08:00
commit 60b889a44b
28 changed files with 5366 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.idea
fastlink
fastlink.exe
session
*.zip
config.conf

32
9d18eed7.0 Normal file
View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFbjCCA1agAwIBAgIII1tVz2zmPdEwDQYJKoZIhvcNAQELBQAwTDELMAkGA1UE
BhMCQ04xDTALBgNVBAoTBEV2YW4xDTALBgNVBAsTBEV2YW4xHzAdBgNVBAMTFkV2
YW4gQXNzdXJhbmNlIFJvb3QgQ0EwIBcNMDgwMTAxMDAwMDAwWhgPMjA1MDEyMzEy
MzU5NTlaMEwxCzAJBgNVBAYTAkNOMQ0wCwYDVQQKEwRFdmFuMQ0wCwYDVQQLEwRF
dmFuMR8wHQYDVQQDExZFdmFuIEFzc3VyYW5jZSBSb290IENBMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEAq4vXbtUk8y6n3AgH7ApVQ7KOSwymwQnRg0HQ
iZJfamIWYsa1uN7n2/W3tJy82ij0JyIqqiUfTZu8dr6yd12Um7RTN1j32x+3hzSF
+2Hag08vd4ksrHAm61MjZVqdQdi5+EMwR5YlaEYFrgBYsKfo+B4vjFYz2PW7IFlI
SSeHBrtJdKf+pIr0+e2Sp7O91NwAp3JiDChqqyMQbKLaXag0G0o422Mj555L2k6r
E1lNm2JzMKaHqP2Ql/GtCaZXmsD4oRtBfMOr5OkNeGA0lA9OXkZxDXYTGrgGfaY+
kq7ig12eBwa2f09x++aPAYxci8I5fZGCpCPY5siAUpw7r9Tui8d2KOCVDGF4FZ3+
vDnLDPXrrN/2FYHt0p/kNqn9WINLOtfQyk8Ko+zicxjTA9DH5tNyKvxs5NncVXeH
Gjgmto3OAJcRNfu/NOo3IEHRmyOHCtMK/U1oR3ByYsRxc9raKrPI/CBcJQ48XIut
OxdQ+5cK+7IumEXqiAxnvKK1SMBVMnSav7jrfAfq66RNOOTvxng1p2W52dTJIIZB
KcEpzrnMGmLLDba9Zp7v/6PsXYLtwzg0oOJZcnnqOCMz2jMt9o06HFlqYke8Z8Nb
35njmwN4PWyHpEZhbC9VttXqVVD7UaAMStsP2yS2L6Wdt3Af7Mr/kurhLBwK6XOp
H403rdsCAwEAAaNSMFAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9GuH/q/
TaECv2bMT75Rrso+Hu4wCwYDVR0PBAQDAgGGMBEGCWCGSAGG+EIBAQQEAwIABzAN
BgkqhkiG9w0BAQsFAAOCAgEAjsWIGSK2ZJOiCBoH4CqMQNJa2nWOAAY99op8BXRi
Sx8tpbid+mZ6IOIBOY4GLT82lkbDbGmAOXhjDD9pMWYMbxko35MNX3j1/9BAIKU/
W4U5NEIEnWogJJirJjtW+3BGSrbtZyTODCGf2nuJQsXz+YnBAVUNjKILmAVR1bCx
KSXo9YJdrfroHTxk+TB6wewiO8cs5/YlMfKQEyUxTdMOEzRdGvl0dkw6t4346BcS
FqY0tpJ6tvbatVjc+ka//ZxBdKTHWJqkcR0f5g91L0AMllRnAUKAyXIzxUlMB8zN
zfKXhSce2Wk+39kBaDrw4YS6SiJTXVX5ID2Myz/NPDY3upbjPVtDABloF35PcKlr
lekQmYUtN+QoHCb+LEkmHn6/AUdUke7J0Vr1gtqEjqC4f5zPVAx81ZLoBU3FQvb/
DN8QLfdI2/qelfubCRv/XDH+ybhd9aQizKacbcEvpCCLCDkkDqhQyToVoJyeWlGY
s+v2oTcP2x+gPs/2uFGsCQE2U8re9B6BOwYaYaAhoPmHSz4hr7oYc45xop1B+70G
GTGHMHbrbKMPld66dKJQEJ9+mtkJbBrsX/ZHbRHCUjU2yMasSV/S9YHYWWqhC8E+
BSCwrlnMnu4TTxF7uHx0nzWOJFRVfmowsfN4ffHDVoAXfuN7NLc56JsJszVyY0ij
YY8=
-----END CERTIFICATE-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFbjCCA1agAwIBAgIII1tVz2zmPdEwDQYJKoZIhvcNAQELBQAwTDELMAkGA1UE
BhMCQ04xDTALBgNVBAoTBEV2YW4xDTALBgNVBAsTBEV2YW4xHzAdBgNVBAMTFkV2
YW4gQXNzdXJhbmNlIFJvb3QgQ0EwIBcNMDgwMTAxMDAwMDAwWhgPMjA1MDEyMzEy
MzU5NTlaMEwxCzAJBgNVBAYTAkNOMQ0wCwYDVQQKEwRFdmFuMQ0wCwYDVQQLEwRF
dmFuMR8wHQYDVQQDExZFdmFuIEFzc3VyYW5jZSBSb290IENBMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEAq4vXbtUk8y6n3AgH7ApVQ7KOSwymwQnRg0HQ
iZJfamIWYsa1uN7n2/W3tJy82ij0JyIqqiUfTZu8dr6yd12Um7RTN1j32x+3hzSF
+2Hag08vd4ksrHAm61MjZVqdQdi5+EMwR5YlaEYFrgBYsKfo+B4vjFYz2PW7IFlI
SSeHBrtJdKf+pIr0+e2Sp7O91NwAp3JiDChqqyMQbKLaXag0G0o422Mj555L2k6r
E1lNm2JzMKaHqP2Ql/GtCaZXmsD4oRtBfMOr5OkNeGA0lA9OXkZxDXYTGrgGfaY+
kq7ig12eBwa2f09x++aPAYxci8I5fZGCpCPY5siAUpw7r9Tui8d2KOCVDGF4FZ3+
vDnLDPXrrN/2FYHt0p/kNqn9WINLOtfQyk8Ko+zicxjTA9DH5tNyKvxs5NncVXeH
Gjgmto3OAJcRNfu/NOo3IEHRmyOHCtMK/U1oR3ByYsRxc9raKrPI/CBcJQ48XIut
OxdQ+5cK+7IumEXqiAxnvKK1SMBVMnSav7jrfAfq66RNOOTvxng1p2W52dTJIIZB
KcEpzrnMGmLLDba9Zp7v/6PsXYLtwzg0oOJZcnnqOCMz2jMt9o06HFlqYke8Z8Nb
35njmwN4PWyHpEZhbC9VttXqVVD7UaAMStsP2yS2L6Wdt3Af7Mr/kurhLBwK6XOp
H403rdsCAwEAAaNSMFAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9GuH/q/
TaECv2bMT75Rrso+Hu4wCwYDVR0PBAQDAgGGMBEGCWCGSAGG+EIBAQQEAwIABzAN
BgkqhkiG9w0BAQsFAAOCAgEAjsWIGSK2ZJOiCBoH4CqMQNJa2nWOAAY99op8BXRi
Sx8tpbid+mZ6IOIBOY4GLT82lkbDbGmAOXhjDD9pMWYMbxko35MNX3j1/9BAIKU/
W4U5NEIEnWogJJirJjtW+3BGSrbtZyTODCGf2nuJQsXz+YnBAVUNjKILmAVR1bCx
KSXo9YJdrfroHTxk+TB6wewiO8cs5/YlMfKQEyUxTdMOEzRdGvl0dkw6t4346BcS
FqY0tpJ6tvbatVjc+ka//ZxBdKTHWJqkcR0f5g91L0AMllRnAUKAyXIzxUlMB8zN
zfKXhSce2Wk+39kBaDrw4YS6SiJTXVX5ID2Myz/NPDY3upbjPVtDABloF35PcKlr
lekQmYUtN+QoHCb+LEkmHn6/AUdUke7J0Vr1gtqEjqC4f5zPVAx81ZLoBU3FQvb/
DN8QLfdI2/qelfubCRv/XDH+ybhd9aQizKacbcEvpCCLCDkkDqhQyToVoJyeWlGY
s+v2oTcP2x+gPs/2uFGsCQE2U8re9B6BOwYaYaAhoPmHSz4hr7oYc45xop1B+70G
GTGHMHbrbKMPld66dKJQEJ9+mtkJbBrsX/ZHbRHCUjU2yMasSV/S9YHYWWqhC8E+
BSCwrlnMnu4TTxF7uHx0nzWOJFRVfmowsfN4ffHDVoAXfuN7NLc56JsJszVyY0ij
YY8=
-----END CERTIFICATE-----

Binary file not shown.

Binary file not shown.

BIN
app/fastlink.apk Normal file

Binary file not shown.

3
build.bat Normal file
View File

@ -0,0 +1,3 @@
packr build -ldflags "-s -w" -trimpath
set GOOS=linux
packr build -ldflags "-s -w" -trimpath

82
cert.crt Normal file
View File

@ -0,0 +1,82 @@
-----BEGIN CERTIFICATE-----
MIIDyzCCArOgAwIBAgIIcK3jv9ks97kwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UE
BhMCQ04xDTALBgNVBAoTBEV2YW4xDTALBgNVBAsTBEV2YW4xGDAWBgNVBAMTD0V2
YW4gVExTIFJTQSBDQTAeFw0yMjAzMjQwMDAwMDBaFw0yMzAzMjMyMzU5NTlaMEcx
CzAJBgNVBAYTAkNOMQ0wCwYDVQQKEwRFdmFuMQ0wCwYDVQQLEwRFdmFuMRowGAYD
VQQDExFFdmFuIEZhc3RsaW5rIFRMUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALv0Mo21LKTJjFCZcgzWQX5cffJdb2fTObCBv0fkU+0OlsILJYePSD59
/kn59jG9oSwFgFv0Rsr73hg1NRY2+StNslo1eRh8jd0dyQXXaX1cmm3GxrlS0xY5
WEdGYSNrqTEhloBq1bMEmZCAVc7gKemektvOAE0wtZcWILT8B1mo5bkn/ROdhJzW
DBWlZQQRUt5eqxQRVGGllP9k1BUki/ZonA6RdEKzaRl3baUf3MY8tW4S4Sk5yR7W
X8VRZPjCFUFjrFDoFtFCZIziaB6OeTnDmi0+evkPUoUvW4+xFjoWgC/eGYNe2i/k
65Emy5vCmChPrwn3PMDjl+4/cKdM4T0CAwEAAaOBvDCBuTAMBgNVHRMBAf8EAjAA
MB0GA1UdDgQWBBRkwj6Ebx5tnVn21zfPkq/k+WI8aTAfBgNVHSMEGDAWgBTkzE//
yCDVeD30iijpcqJ4vgc95jALBgNVHQ8EBAMCA+gwHQYDVR0lBBYwFAYIKwYBBQUH
AwEGCCsGAQUFBwMCMCoGA1UdEQQjMCGCEmZhc3RsaW5rLmljbG91ZC5jboILKi5p
Y2xvdWQuY24wEQYJYIZIAYb4QgEBBAQDAgbAMA0GCSqGSIb3DQEBCwUAA4IBAQAd
F1m3sw3Ygf8ISoMeaGvMS2qhDiCqZoICy0YEQWuuMowmPVr+6y+dGPbCPCGx/V/+
aD8TH8ZfmlNYz0QYohtWVzsDUdmb/dZkMijFaqWf77QwzA1/ObUUOL3xvE8+8rLD
TvLx7Wg8vVntPnG/3EocW//F+tvoDDE626Moxx02iFxdt7OKkntr5C/CQCIAXSbj
jCzo4GsByGRmGv4jvez1fZ4/MoRcwH19tpoJLEINU9JDm78E0rt9DNKR3f1vPQBO
AR4NKmlZLxPKbhnEgvKbGgIG5R66tHcL4JF4DI/J/bEFK3vWwVfufRA84wKUK07l
w74dNHVmcL3sq9SvK7At
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEhDCCAmygAwIBAgIIUw9sncwT5HcwDQYJKoZIhvcNAQELBQAwTDELMAkGA1UE
BhMCQ04xDTALBgNVBAoTBEV2YW4xDTALBgNVBAsTBEV2YW4xHzAdBgNVBAMTFkV2
YW4gQXNzdXJhbmNlIFJvb3QgQ0EwHhcNMjIwMTAxMDAwMDAwWhcNMzIxMjMxMjM1
OTU5WjBFMQswCQYDVQQGEwJDTjENMAsGA1UEChMERXZhbjENMAsGA1UECxMERXZh
bjEYMBYGA1UEAxMPRXZhbiBUTFMgUlNBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAox2P/00Y8RimWNxKi/iA3ERywf21imyNy+v8d47Bg2iH8PaZ
RAKYya19OkLQOsWFCS6L8/Bx/c/HPZfMOS62ro9IQ6D5UWP0rK0nSd7gvUU63f6K
6ZJ5LR1owAVx0xZ9b+sL89J+VlNuVR8+SjYx2OlOUonk3IPHkvm9WCtFMA2bkHjd
uG2KSJTn4roXr5nVhIi53RnSaiyBlRJ0OeJ2IPgmA/U5v/0Rr2I3YN7NJ/n+3bCO
kNeX/o3qa+C4PoedXfnlIGJyqcc3l6SCSsZ5IFyvQDgE8kuO9UYXk/jFQnYyHsNb
QA/ZiiR2mzQ12eeDxaqZAYYeC0ys/OL7ZvZt5QIDAQABo3EwbzAPBgNVHRMBAf8E
BTADAQH/MB0GA1UdDgQWBBTkzE//yCDVeD30iijpcqJ4vgc95jALBgNVHQ8EBAMC
AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBEGCWCGSAGG+EIBAQQE
AwIAxzANBgkqhkiG9w0BAQsFAAOCAgEAPcEmb/L749blVCP5+RDFN+lS78MYQyMT
ZikN3AJP6pV5pTM3uc+zD+sproRslJum7DkeuPn2WltRN3bcB4cemvFMZyx0luFc
XVJ3Gy+wx0L0JexBri5B9iFovZaHxKM0uaBmHDyHfGV6NhsFJWlPL9XQTRQD6EQ4
meWRNpEs+W5RCXMMQ7VqyRLovC4OdbnPkyZv+UGKajGzGPzm6hZfG3bA5TxSFney
ZerpxSnLASkzAe1w5G91RU3dPKPiULJq01l5uv2fy7p5KYDWSqxOrB0KvpZtAVRx
6VbmFEogmG66M2OGEm9ptz4FOh08KsTfcuS3KWiyVjBi/IcqzbOMWkkIrw0SM7Bu
tbgIrS4bbYUFfCJqYsRw0Nfmn1Ndll2sLD3iTqL5kqSaAsI9zGyEn5QPnZCGvx3R
ryAHAI+ImvfFMZjvbZ95++IBJ/7CDYlp25oVd/JBzxq2fpNqO+Z5r4Cg+UFve1uU
c6M1KGeKF15sQi69gkbcUPQpYaxT5+zTag88V0NIwhuGgmaMvvo1vN5O+tziL4B4
HxJNaUJbv2rolJ7stspw8C6L6daP+7qfyIaZONJCGEXt46V4mDH9vV5/JWyhhuSE
rL39I9OzjaUhsooodJwcKUuU1BAzg3oaXxANzEPMILDHo7dPDWkY+JoVa/VW5Huc
2kzz4/hrJdU=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFbjCCA1agAwIBAgIII1tVz2zmPdEwDQYJKoZIhvcNAQELBQAwTDELMAkGA1UE
BhMCQ04xDTALBgNVBAoTBEV2YW4xDTALBgNVBAsTBEV2YW4xHzAdBgNVBAMTFkV2
YW4gQXNzdXJhbmNlIFJvb3QgQ0EwIBcNMDgwMTAxMDAwMDAwWhgPMjA1MDEyMzEy
MzU5NTlaMEwxCzAJBgNVBAYTAkNOMQ0wCwYDVQQKEwRFdmFuMQ0wCwYDVQQLEwRF
dmFuMR8wHQYDVQQDExZFdmFuIEFzc3VyYW5jZSBSb290IENBMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEAq4vXbtUk8y6n3AgH7ApVQ7KOSwymwQnRg0HQ
iZJfamIWYsa1uN7n2/W3tJy82ij0JyIqqiUfTZu8dr6yd12Um7RTN1j32x+3hzSF
+2Hag08vd4ksrHAm61MjZVqdQdi5+EMwR5YlaEYFrgBYsKfo+B4vjFYz2PW7IFlI
SSeHBrtJdKf+pIr0+e2Sp7O91NwAp3JiDChqqyMQbKLaXag0G0o422Mj555L2k6r
E1lNm2JzMKaHqP2Ql/GtCaZXmsD4oRtBfMOr5OkNeGA0lA9OXkZxDXYTGrgGfaY+
kq7ig12eBwa2f09x++aPAYxci8I5fZGCpCPY5siAUpw7r9Tui8d2KOCVDGF4FZ3+
vDnLDPXrrN/2FYHt0p/kNqn9WINLOtfQyk8Ko+zicxjTA9DH5tNyKvxs5NncVXeH
Gjgmto3OAJcRNfu/NOo3IEHRmyOHCtMK/U1oR3ByYsRxc9raKrPI/CBcJQ48XIut
OxdQ+5cK+7IumEXqiAxnvKK1SMBVMnSav7jrfAfq66RNOOTvxng1p2W52dTJIIZB
KcEpzrnMGmLLDba9Zp7v/6PsXYLtwzg0oOJZcnnqOCMz2jMt9o06HFlqYke8Z8Nb
35njmwN4PWyHpEZhbC9VttXqVVD7UaAMStsP2yS2L6Wdt3Af7Mr/kurhLBwK6XOp
H403rdsCAwEAAaNSMFAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9GuH/q/
TaECv2bMT75Rrso+Hu4wCwYDVR0PBAQDAgGGMBEGCWCGSAGG+EIBAQQEAwIABzAN
BgkqhkiG9w0BAQsFAAOCAgEAjsWIGSK2ZJOiCBoH4CqMQNJa2nWOAAY99op8BXRi
Sx8tpbid+mZ6IOIBOY4GLT82lkbDbGmAOXhjDD9pMWYMbxko35MNX3j1/9BAIKU/
W4U5NEIEnWogJJirJjtW+3BGSrbtZyTODCGf2nuJQsXz+YnBAVUNjKILmAVR1bCx
KSXo9YJdrfroHTxk+TB6wewiO8cs5/YlMfKQEyUxTdMOEzRdGvl0dkw6t4346BcS
FqY0tpJ6tvbatVjc+ka//ZxBdKTHWJqkcR0f5g91L0AMllRnAUKAyXIzxUlMB8zN
zfKXhSce2Wk+39kBaDrw4YS6SiJTXVX5ID2Myz/NPDY3upbjPVtDABloF35PcKlr
lekQmYUtN+QoHCb+LEkmHn6/AUdUke7J0Vr1gtqEjqC4f5zPVAx81ZLoBU3FQvb/
DN8QLfdI2/qelfubCRv/XDH+ybhd9aQizKacbcEvpCCLCDkkDqhQyToVoJyeWlGY
s+v2oTcP2x+gPs/2uFGsCQE2U8re9B6BOwYaYaAhoPmHSz4hr7oYc45xop1B+70G
GTGHMHbrbKMPld66dKJQEJ9+mtkJbBrsX/ZHbRHCUjU2yMasSV/S9YHYWWqhC8E+
BSCwrlnMnu4TTxF7uHx0nzWOJFRVfmowsfN4ffHDVoAXfuN7NLc56JsJszVyY0ij
YY8=
-----END CERTIFICATE-----

189
fastlink.go Normal file
View File

@ -0,0 +1,189 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"github.com/gobuffalo/packr"
"io"
"log"
"math/rand"
"net/http"
"os"
"os/signal"
"time"
)
func main() {
Logger = log.New(io.Writer(os.Stdout), "", log.Lshortfile|log.LstdFlags)
rand.Seed(time.Now().Unix())
var port string
var address string
flag.StringVar(&port, "port", "1443", "serve port")
flag.StringVar(&address, "listen", "", "listen address")
flag.StringVar(&configPath, "config", "config.conf", "config path")
flag.Parse()
fastLinkService = new(FastLinkService)
ReadConfig()
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, os.Kill)
go func() {
<-c //阻塞直至有信号传入
Logger.Println("Saving config")
SaveConfig()
Logger.Println("Save config done,exit now.")
os.Exit(0)
}()
go func() {
ticker := time.NewTicker(time.Minute)
for range ticker.C {
<-ticker.C
fastLinkService.KillAloneSignalSession()
fastLinkService.KillExpiredToken()
SaveConfig()
Logger.Println("Saving config")
ticker.Reset(time.Minute)
}
}()
http.HandleFunc("/admin/config", configHandler)
http.HandleFunc("/admin/addUser", addUserHandler)
http.HandleFunc("/admin/editUser", editUserHandler)
http.HandleFunc("/admin/removeUser", removeUserHandler)
http.HandleFunc("/admin/kickUser", kickUserHandler)
http.HandleFunc("/admin/removeDevice", removeDeviceHandler)
http.HandleFunc("/admin/removeCustomName", removeCustomNameHandler)
http.HandleFunc("/socket.io/", socketHandler)
http.HandleFunc("/fastLink/shareUrl/connect/check", checkHandler)
http.HandleFunc("/fastLink/device/deviceVerificationType", deviceVerificationTypeHandler)
http.HandleFunc("/fastLink/config/idTransfer", idTransferHandler)
http.HandleFunc("/fastLink/stream/record/close", closeHandler)
http.HandleFunc("/fastLink/user/loginForApp", loginForAppHandler)
http.HandleFunc("/fastLink/device/list", appListDeviceHandler)
http.HandleFunc("/fastLink/user/logout", logoutHandler)
http.HandleFunc("/fastLink/user/register", registerHandler)
http.HandleFunc("/fastLink/code/send", sendCodeHandler)
box := packr.NewBox("web")
http.Handle("/", http.FileServer(box))
err := http.ListenAndServeTLS(address+":"+port, "cert.crt", "private.key", nil)
//套nginx或Caddy可直接使用Http
//err := http.ListenAndServe(address+":"+port, nil)
if err != nil {
fmt.Print(err)
}
}
func socketHandler(w http.ResponseWriter, r *http.Request) {
clientType := r.FormValue("clientType")
if clientType == CLIENT_TYPE_CPP || clientType == CLIENT_TYPE_WEB {
signalHandler(w, r)
} else {
serviceHandler(w, r)
}
}
func configHandler(w http.ResponseWriter, r *http.Request) {
marshal, _ := json.Marshal(fastLinkService)
w.Write(marshal)
}
func addUserHandler(w http.ResponseWriter, r *http.Request) {
user := r.FormValue("user")
pass := r.FormValue("pass")
if user == "" {
w.Write([]byte("user is empty"))
return
}
if pass == "" {
w.Write([]byte("pass is empty"))
return
}
exist := fastLinkService.CheckUserExist(user)
if exist {
w.Write([]byte("user exist"))
return
}
fastLinkService.AddUser(user, pass)
w.Write([]byte("success"))
}
func editUserHandler(w http.ResponseWriter, r *http.Request) {
user := r.FormValue("user")
pass := r.FormValue("pass")
if user == "" {
w.Write([]byte("user is empty"))
return
}
if pass == "" {
w.Write([]byte("pass is empty"))
return
}
exist := fastLinkService.CheckUserExist(user)
if !exist {
w.Write([]byte("user not exist"))
return
}
fastLinkService.EditUser(user, pass)
w.Write([]byte("success"))
}
func removeUserHandler(w http.ResponseWriter, r *http.Request) {
user := r.FormValue("user")
if user == "" {
w.Write([]byte("user is empty"))
return
}
fastLinkService.removeUser(user)
fastLinkService.removeDevicesByUser(user)
fastLinkService.removeTokenByUser(user)
w.Write([]byte("success"))
}
func kickUserHandler(w http.ResponseWriter, r *http.Request) {
user := r.FormValue("user")
if user == "" {
w.Write([]byte("user is empty"))
return
}
fastLinkService.removeTokenByUser(user)
w.Write([]byte("success"))
}
func removeDeviceHandler(w http.ResponseWriter, r *http.Request) {
deviceNo := r.FormValue("deviceNo")
if deviceNo == "" {
w.Write([]byte("deviceNo is empty"))
return
}
device := fastLinkService.GetDeviceByNo(deviceNo)
if device != nil {
user := device.BelongUser
fastLinkService.removeDevice(deviceNo)
if user != "" {
notifyDeviceChange(user)
}
}
w.Write([]byte("success"))
}
func removeCustomNameHandler(w http.ResponseWriter, r *http.Request) {
deviceNo := r.FormValue("deviceNo")
if deviceNo == "" {
w.Write([]byte("deviceNo is empty"))
return
}
device := fastLinkService.GetDeviceByNo(deviceNo)
if device != nil {
device.CustomName = ""
if device.BelongUser != "" {
notifyDeviceChange(device.BelongUser)
}
}
w.Write([]byte("success"))
}

627
fastlink_manager.go Normal file
View File

@ -0,0 +1,627 @@
package main
import (
"github.com/beevik/guid"
"github.com/gorilla/websocket"
"math/rand"
"strconv"
"strings"
"sync"
"time"
)
type SessionInfo struct {
SessionId string `json:"sessionId"`
DeviceNo string `json:"deviceNo"`
ClientType string `json:"clientType"`
Guid string `json:"guid"`
WsConn *websocket.Conn `json:"-"`
}
type SignalSessionInfo struct {
Token string `json:"token"`
RelateDeviceId int `json:"relateDeviceId"`
RelateDeviceNo string `json:"relateDeviceNo"`
StreamUser string `json:"streamUser"`
StreamToken string `json:"streamToken"`
CreateTime time.Time `json:"createTime"`
Active bool `json:"active"`
Guid string `json:"guid"`
WsConn *websocket.Conn `json:"-"`
}
type UserInfo struct {
Name string `json:"name"`
Pass string `json:"pass"`
}
type TokenInfo struct {
Token string `json:"token"`
Type string `json:"type"`
User string `json:"user"`
SessionId string `json:"sessionId"`
LastActive time.Time `json:"lastActive"`
}
type DeviceInfo struct {
DeviceNo string `json:"deviceNo"`
DeviceId int `json:"deviceId"`
DeviceName string `json:"deviceName"`
IdentificationCode string `json:"identificationCode"`
VerificationCode string `json:"verificationCode"`
ShareRand string `json:"shareRand"`
BelongUser string `json:"belongUser"`
Visible bool `json:"visible"`
TwoStepPass string `json:"twoStepPass"`
VerificationType int `json:"verificationType"`
CustomName string `json:"customName"`
Online bool `json:"-"`
}
type FastLinkService struct {
sessionLock sync.Mutex
deviceLock sync.Mutex
userLock sync.Mutex
tokenLock sync.Mutex
signalLock sync.Mutex
Sessions []*SessionInfo `json:"sessions"`
Users []*UserInfo `json:"users"`
Devices []*DeviceInfo `json:"devices"`
Tokens []*TokenInfo `json:"tokens"`
SignalSessions []*SignalSessionInfo `json:"signalSessions"`
}
func (f *FastLinkService) FillUsers(users []*UserInfo) {
f.userLock.Lock()
defer f.userLock.Unlock()
f.Users = users
}
func (f *FastLinkService) FillDevices(devices []*DeviceInfo) {
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
f.Devices = devices
}
func (f *FastLinkService) FillTokens(tokens []*TokenInfo) {
f.tokenLock.Lock()
defer f.tokenLock.Unlock()
f.Tokens = tokens
}
func (f *FastLinkService) NewSession(deviceNo string, sessionId string, sessionGuid string, clientType string, wsConn *websocket.Conn) {
f.sessionLock.Lock()
defer f.sessionLock.Unlock()
for _, session := range f.Sessions {
if session.SessionId == sessionId {
session.DeviceNo = deviceNo
session.WsConn = wsConn
session.ClientType = clientType
session.Guid = sessionGuid
return
}
}
f.Sessions = append(f.Sessions, &SessionInfo{
SessionId: sessionId,
DeviceNo: deviceNo,
WsConn: wsConn,
ClientType: clientType,
Guid: sessionGuid,
})
}
func (f *FastLinkService) SetOnlineStatus(deviceNo string) {
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
session := f.GetSession(deviceNo, CLIENT_TYPE_FASTLINK_SERVICE)
for _, device := range f.Devices {
if device.DeviceNo == deviceNo {
if session == nil {
device.Online = false
} else {
device.Online = true
}
return
}
}
}
func (f *FastLinkService) GetSession(deviceNo string, clientType string) *SessionInfo {
f.sessionLock.Lock()
defer f.sessionLock.Unlock()
for _, session := range f.Sessions {
if session.DeviceNo == deviceNo && session.ClientType == clientType {
return session
}
}
return nil
}
func (f *FastLinkService) GetSessionsByUser(user string, clientType string) []*SessionInfo {
f.sessionLock.Lock()
defer f.sessionLock.Unlock()
var result []*SessionInfo
for _, session := range f.Sessions {
device := f.GetDeviceByNo(session.DeviceNo)
if device != nil && device.BelongUser == user && session.ClientType == clientType {
result = append(result, session)
}
}
return result
}
func (f *FastLinkService) PrepareSignalSession(token string, relateDeviceId int, relateDeviceNo string, streamToken string, streamUser string) {
f.signalLock.Lock()
defer f.signalLock.Unlock()
f.SignalSessions = append(f.SignalSessions, &SignalSessionInfo{
Token: token,
RelateDeviceId: relateDeviceId,
RelateDeviceNo: relateDeviceNo,
StreamToken: streamToken,
StreamUser: streamUser,
CreateTime: time.Now().In(TimeLocation),
})
}
func (f *FastLinkService) NewSignalSession(token string, sessionGuid string, wsConn *websocket.Conn) {
f.signalLock.Lock()
defer f.signalLock.Unlock()
for _, session := range f.SignalSessions {
if session.Token == token {
session.WsConn = wsConn
session.Active = true
session.Guid = sessionGuid
return
}
}
f.SignalSessions = append(f.SignalSessions, &SignalSessionInfo{
Token: token,
Active: true,
WsConn: wsConn,
Guid: sessionGuid,
})
}
func (f *FastLinkService) KillAloneSignalSession() {
f.signalLock.Lock()
defer f.signalLock.Unlock()
var tmp []*SignalSessionInfo
for _, session := range f.SignalSessions {
if session.Active == false && session.RelateDeviceNo != "" {
if time.Now().In(TimeLocation).Sub(session.CreateTime.In(TimeLocation)).Seconds() > 120 {
continue
}
}
tmp = append(tmp, session)
}
f.SignalSessions = tmp
}
func (f *FastLinkService) RemoveSession(sessionId string, sessionGuid string) {
f.sessionLock.Lock()
defer f.sessionLock.Unlock()
var tmp []*SessionInfo
needRemove := false
for _, session := range f.Sessions {
if session.SessionId == sessionId && session.Guid == sessionGuid {
needRemove = true
break
}
}
if needRemove {
for _, session := range f.Sessions {
if session.SessionId != sessionId {
tmp = append(tmp, session)
}
}
f.Sessions = tmp
}
}
func (f *FastLinkService) RemoveSignalSession(token string, sessionGuid string) {
f.signalLock.Lock()
defer f.signalLock.Unlock()
var tmp []*SignalSessionInfo
needRemove := false
for _, session := range f.SignalSessions {
if session.Token == token && session.Guid == sessionGuid {
needRemove = true
break
}
}
if needRemove {
for _, session := range f.SignalSessions {
if session.Token != token {
tmp = append(tmp, session)
}
}
f.SignalSessions = tmp
}
}
func (f *FastLinkService) GetSignalSession(token string) *SignalSessionInfo {
f.signalLock.Lock()
defer f.signalLock.Unlock()
for _, session := range f.SignalSessions {
if session.Token == token {
return session
}
}
return nil
}
func (f *FastLinkService) GetSignalSessionByDeviceId(deviceId int) []*SignalSessionInfo {
f.signalLock.Lock()
defer f.signalLock.Unlock()
var result []*SignalSessionInfo
for _, session := range f.SignalSessions {
if session.RelateDeviceId == deviceId {
result = append(result, session)
}
}
return result
}
func (f *FastLinkService) AddDevice(deviceNo string, deviceName string) *DeviceInfo {
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
for _, device := range f.Devices {
if device.DeviceNo == deviceNo {
device.DeviceName = deviceName
return device
}
}
newDeviceId := 0
for i := LastDeviceId + 1; i < 10000; i++ {
exist := false
for _, device := range f.Devices {
if device.DeviceId == i {
exist = true
break
}
}
if !exist {
newDeviceId = i
LastDeviceId = i
break
}
}
identificationCode := RandNum(9)
for {
exist := false
for _, device := range f.Devices {
if device.IdentificationCode == identificationCode {
exist = true
break
}
}
if exist {
identificationCode = RandNum(9)
} else {
break
}
}
device := &DeviceInfo{
DeviceNo: deviceNo,
DeviceId: newDeviceId,
DeviceName: deviceName,
IdentificationCode: identificationCode,
VerificationCode: RandStr(6),
ShareRand: "1" + RandNum(9),
BelongUser: "",
Online: true,
Visible: true,
}
f.Devices = append(f.Devices, device)
return device
}
func (f *FastLinkService) RefreshDeviceVerificationCode(deviceNo string) *DeviceInfo {
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
for _, device := range f.Devices {
if device.DeviceNo == deviceNo {
device.VerificationCode = RandStr(6)
device.ShareRand = "1" + RandNum(9)
return device
}
}
return nil
}
func (f *FastLinkService) GetDeviceByNo(deviceNo string) *DeviceInfo {
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
for _, device := range f.Devices {
if device.DeviceNo == deviceNo {
return device
}
}
return nil
}
func (f *FastLinkService) removeDevice(deviceNo string) {
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
var tmp []*DeviceInfo
for _, device := range f.Devices {
if device.DeviceNo != deviceNo {
tmp = append(tmp, device)
}
}
f.Devices = tmp
}
func (f *FastLinkService) GetDeviceByIdentificationCode(identificationCode string) *DeviceInfo {
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
for _, device := range f.Devices {
if device.IdentificationCode == identificationCode {
return device
}
}
return nil
}
func (f *FastLinkService) GetDeviceById(deviceId int) *DeviceInfo {
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
for _, device := range f.Devices {
if device.DeviceId == deviceId {
return device
}
}
return nil
}
func (f *FastLinkService) AddUser(username string, password string) *UserInfo {
f.userLock.Lock()
defer f.userLock.Unlock()
for _, user := range f.Users {
if user.Name == username {
return user
}
}
u := &UserInfo{
Name: username,
Pass: password,
}
f.Users = append(f.Users, u)
return u
}
func (f *FastLinkService) Login(username string, password string, deviceId int) string {
f.userLock.Lock()
defer f.userLock.Unlock()
succ := false
for _, user := range f.Users {
if user.Name == username && user.Pass == password {
succ = true
break
}
}
if !succ {
return ""
}
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
for _, device := range f.Devices {
if device.DeviceId == deviceId {
device.BelongUser = username
}
}
return f.NewToken(username, TOKEN_TYPE_PC)
}
func (f *FastLinkService) CheckUser(username string, password string) bool {
f.userLock.Lock()
defer f.userLock.Unlock()
for _, user := range f.Users {
if user.Name == username && user.Pass == password {
return true
}
}
return false
}
func (f *FastLinkService) CheckUserExist(username string) bool {
f.userLock.Lock()
defer f.userLock.Unlock()
for _, user := range f.Users {
if user.Name == username {
return true
}
}
return false
}
func (f *FastLinkService) EditUser(username string, password string) {
f.userLock.Lock()
defer f.userLock.Unlock()
for _, user := range f.Users {
if user.Name == username {
user.Pass = password
}
}
}
func (f *FastLinkService) removeUser(username string) {
f.userLock.Lock()
defer f.userLock.Unlock()
var tmp []*UserInfo
for _, user := range f.Users {
if user.Name != username {
tmp = append(tmp, user)
}
}
f.Users = tmp
}
func (f *FastLinkService) removeDevicesByUser(username string) {
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
var tmp []*DeviceInfo
for _, device := range f.Devices {
if device.BelongUser != username {
tmp = append(tmp, device)
}
}
f.Devices = tmp
}
func (f *FastLinkService) removeTokenByUser(username string) {
f.tokenLock.Lock()
defer f.tokenLock.Unlock()
var tmp []*TokenInfo
for _, token := range f.Tokens {
if token.User != username {
tmp = append(tmp, token)
}
}
f.Tokens = tmp
}
func (f *FastLinkService) GetUserByToken(token string) string {
f.tokenLock.Lock()
defer f.tokenLock.Unlock()
for _, info := range f.Tokens {
if info.Token == token {
return info.User
}
}
return ""
}
func (f *FastLinkService) GetDeviceByToken(token string) []*DeviceInfo {
user := f.GetUserByToken(token)
if user == "" {
return []*DeviceInfo{}
}
f.RenewToken(token)
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
var dList []*DeviceInfo
for _, device := range f.Devices {
if device.BelongUser == user && device.Visible {
dList = append(dList, device)
}
}
return dList
}
func (f *FastLinkService) NewToken(username string, tokenType string) string {
f.tokenLock.Lock()
defer f.tokenLock.Unlock()
token := guid.New().String()
f.Tokens = append(f.Tokens, &TokenInfo{
Token: token,
User: username,
Type: tokenType,
LastActive: time.Now().In(TimeLocation),
})
return token
}
func (f *FastLinkService) BindSessionToToken(sessionId string, token string) {
f.tokenLock.Lock()
defer f.tokenLock.Unlock()
for _, info := range f.Tokens {
if info.Token == token {
info.SessionId = sessionId
return
}
}
}
func (f *FastLinkService) GetTokenBySession(sessionId string) string {
f.tokenLock.Lock()
defer f.tokenLock.Unlock()
for _, info := range f.Tokens {
if info.SessionId == sessionId {
return info.Token
}
}
return ""
}
func (f *FastLinkService) RemoveToken(token string) {
f.tokenLock.Lock()
defer f.tokenLock.Unlock()
var tmp []*TokenInfo
for _, info := range f.Tokens {
if info.Token != token {
tmp = append(tmp, info)
}
}
f.Tokens = tmp
}
func (f *FastLinkService) RenewToken(token string) {
f.tokenLock.Lock()
defer f.tokenLock.Unlock()
for _, info := range f.Tokens {
if info.Token == token {
info.LastActive = time.Now().In(TimeLocation)
}
}
}
func (f *FastLinkService) KillExpiredToken() {
f.tokenLock.Lock()
defer f.tokenLock.Unlock()
var tmp []*TokenInfo
for _, info := range f.Tokens {
life := TOKEN_APP_MAX_LIFE
if info.Type == "pc" {
life = TOKEN_PC_MAX_LIFE
}
if time.Now().In(TimeLocation).Sub(info.LastActive.In(TimeLocation)).Seconds() <= float64(life) {
tmp = append(tmp, info)
} else {
Logger.Println("Remove expired token:" + info.Token + ", LastActive time=" + info.LastActive.In(TimeLocation).Format("2006-01-02 15:04:05"))
}
}
f.Tokens = tmp
}
func (f *FastLinkService) CheckWebLogin(deviceNo string, random string) int {
f.deviceLock.Lock()
defer f.deviceLock.Unlock()
for _, device := range f.Devices {
if device.DeviceNo == deviceNo {
if device.ShareRand == random {
return 0
} else {
return 1
}
}
}
return 2
}
func RandNum(length int) string {
var result string
for i := 0; i < length; i++ {
result += strconv.Itoa(rand.Intn(9))
}
return result
}
func RandStr(length int) string {
str := "0123456789abcdefghijklmnopqrstuvwxyz"
bytes := []byte(str)
result := []byte{}
rand.Seed(time.Now().UnixNano() + int64(rand.Intn(100)))
for i := 0; i < length; i++ {
result = append(result, bytes[rand.Intn(len(bytes))])
}
return string(result)
}
func GenShortGUID() string {
return strings.ReplaceAll(guid.New().String(), "-", "")
}

54
fastlink_test.go Normal file
View File

@ -0,0 +1,54 @@
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/beevik/guid"
"testing"
)
func TestName(t *testing.T) {
var params *MessageResponse
params = &MessageResponse{
Id: "fsdgsdgsdg",
Method: "Add",
Result: &AddDeviceResponse{
Code: 0,
DeviceId: 453565,
},
}
response := makeMessageResponse("fff", params)
fmt.Println(string(response))
}
func TestName2(t *testing.T) {
fmt.Println(guid.New().String())
}
func TestName3(t *testing.T) {
var items []interface{}
json.Unmarshal([]byte("[\"owt-message\",{\"data\":\"{\\\"type\\\":\\\"chat-reset\\\"}\",\"from\":\"5ff5fce328f74b8f964fd282f0c4e1b8\",\"to\":\"b5f5ceab-d964-5f41-a7b7-e16f197f0692\"}]"), &items)
s := items[1].(map[string]interface{})["data"].(string)
fmt.Println(s)
}
func TestName4(t *testing.T) {
toString := base64.StdEncoding.EncodeToString([]byte("b5f5ceab-d964-5f41-a7b7-e16f197f0692"))
fmt.Println(toString)
decodeString, _ := base64.StdEncoding.DecodeString("YjVmNWNlYWItZDk2NC01ZjQxLWE3YjctZTE2ZjE5N2YwNjky")
fmt.Println(string(decodeString))
}
func TestAAA(t *testing.T) {
str := "{\"id\":\"182ade4f-b515-8e4f-bf19-4d2b10f651a5\",\"method\":\"NotifyClientOffline\",\"params\":{\"clientIds\":[\"7bc127b80aea4ab598dd35ac589752e1\"],\"reason\":3}}"
var item MessageInfo
json.Unmarshal([]byte(str), &item)
uuu := item.Params.(map[string]interface{})["clientIds"].([]interface{})
for _, i2 := range uuu {
fmt.Println(i2)
}
}

94
global.go Normal file
View File

@ -0,0 +1,94 @@
package main
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"time"
)
const CLIENT_TYPE_CPP = "cpp"
const CLIENT_TYPE_WEB = "Web"
const CLIENT_TYPE_FASTLINK_UI = "fastlink_ui"
const CLIENT_TYPE_FASTLINK_SERVICE = "fastlink_service"
const TOKEN_TYPE_APP = "app"
const TOKEN_TYPE_PC = "pc"
const TOKEN_PC_MAX_LIFE = 3600
const TOKEN_APP_MAX_LIFE = 86400
const WEB_URL = "https://fastlink.evan.run/"
var (
Logger *log.Logger
fastLinkService *FastLinkService
globalConfig = GlobalConfig{
SignalServer: "https://fastlink.evan.run",
StunServers: []string{"stun:106.54.197.45:3478"},
TurnServers: []TurnServer{{
Addr: "turn:106.54.197.45:3478",
Password: "evanturn",
UserName: "evan",
}, {
Addr: "turn:221.229.220.67:3478",
Password: "evanturn",
UserName: "evan",
}},
}
configPath string
LastDeviceId = 0
TimeLocation *time.Location = time.FixedZone("CST", 8*3600)
)
type GlobalConfig struct {
SignalServer string `json:"signalServer"`
StunServers []string `json:"stunServers"`
TurnServers []TurnServer `json:"turnServers"`
}
type TurnServer struct {
Addr string `json:"addr"`
Password string `json:"password"`
UserName string `json:"userName"`
}
type ConfigData struct {
LastDeviceId int `json:"lastDeviceId"`
Users []*UserInfo `json:"users"`
Devices []*DeviceInfo `json:"devices"`
Tokens []*TokenInfo `json:"tokens"`
}
func SaveConfig() {
config := &ConfigData{
Users: fastLinkService.Users,
Devices: fastLinkService.Devices,
Tokens: fastLinkService.Tokens,
LastDeviceId: LastDeviceId,
}
marshal, _ := json.Marshal(config)
ioutil.WriteFile(configPath, marshal, 0777)
}
func ReadConfig() {
b, _ := exists(configPath)
if b {
configData, _ := ioutil.ReadFile(configPath)
var config *ConfigData
json.Unmarshal(configData, &config)
fastLinkService.FillUsers(config.Users)
fastLinkService.FillDevices(config.Devices)
fastLinkService.FillTokens(config.Tokens)
LastDeviceId = config.LastDeviceId
}
}
func exists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return true, err
}

16
go.mod Normal file
View File

@ -0,0 +1,16 @@
module fastlink
go 1.18
require (
github.com/beevik/guid v0.0.0-20170504223318-d0ea8faecee0
github.com/gobuffalo/packr v1.30.1
github.com/gorilla/websocket v1.5.0
)
require (
github.com/gobuffalo/envy v1.7.0 // indirect
github.com/gobuffalo/packd v0.3.0 // indirect
github.com/joho/godotenv v1.3.0 // indirect
github.com/rogpeppe/go-internal v1.3.0 // indirect
)

73
go.sum Normal file
View File

@ -0,0 +1,73 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beevik/guid v0.0.0-20170504223318-d0ea8faecee0 h1:oLd/YLOTOgA4D4aAUhIE8vhl/LAP1ZJrj0mDQpl7GB8=
github.com/beevik/guid v0.0.0-20170504223318-d0ea8faecee0/go.mod h1:XzXWuOd1wJ63MtICHh5+PnvCuxsB/d58T8TswEhI/9I=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

3
host.txt Normal file
View File

@ -0,0 +1,3 @@
106.54.197.45 fastlink.icloud.cn
106.54.197.45 signalserver.icloud.cn
127.0.0.1 datapolaris.shunwang.com

27
private.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAu/QyjbUspMmMUJlyDNZBflx98l1vZ9M5sIG/R+RT7Q6Wwgsl
h49IPn3+Sfn2Mb2hLAWAW/RGyvveGDU1Fjb5K02yWjV5GHyN3R3JBddpfVyabcbG
uVLTFjlYR0ZhI2upMSGWgGrVswSZkIBVzuAp6Z6S284ATTC1lxYgtPwHWajluSf9
E52EnNYMFaVlBBFS3l6rFBFUYaWU/2TUFSSL9micDpF0QrNpGXdtpR/cxjy1bhLh
KTnJHtZfxVFk+MIVQWOsUOgW0UJkjOJoHo55OcOaLT56+Q9ShS9bj7EWOhaAL94Z
g17aL+TrkSbLm8KYKE+vCfc8wOOX7j9wp0zhPQIDAQABAoIBAQCG2JVoe8Jy27MS
pHRh51oHuX4W45gpCi9KhlvgzVy1KBFbWDntvwua4jQNiQtr9lvqxGuqDWC5Auj9
vza+IGS1/8Fk3RZrTflN2a++xCSH72dQORucHGMbf7rpMAJDMlupxyo+Kef1zugJ
9zkY+lS3qrA8iNhIiRjPM+31SxhO5xW1XLP8lRi8N5eC3wzp0J0UMWfd+JN+H2Ew
rgWNuJ5nmsNq59ZQ9oXNzLvyLmHZIFMrU9hrmSj5qZF8MSsLuzd61Z0Pvs3hHS6u
70jjREn9X21UdKezU5c3pOFBG95HR/6t/MHXJzYQ3zT4eV8RwesPn7Hsn2PzNtqM
oq7k4T+BAoGBAOomjDmfU0O/bWzbNaFkUdgdF2hyu72Q15icMxiytSYjramjiISD
LVKgxG5Ryk5N+42zV6iEa0AIf7PI9YhsHZphJ5xCaayVjeIpXetUrFONMBrJk04j
6HJ/7sZQG0EQnC43tcxCfgScnMiC7rNg2cBGdcA3H7AXHs0p1jN95HldAoGBAM1+
F0coYuY+PT8L+CAiUrf23fNKEjnVkyZWfaffWCo4RtcvnG9nS3eKqHU+y5yeFSwL
HvIxjr+xM7d1eHdYdyQ/GdgLxbE/aDh43E3zeq6QdTuPoOUVbII2zv4iI+LEbKYk
oc34SlxHp5qBq4+Y1D9Q4QpjTrfJXstIJGDJxylhAoGAOu4EsKPmotBUy+bHspy6
9Hct4v2tPmJrwF7fhUHE0Zp9JIqh2yfgikEn7tPq5JY/z0PpteHN1EnPSgIzuWfs
c2QsQel9CxCAqwDaQFYJIOYRy0wcJLho0hhckdCra06w3/jpakT5zlNUKiq5xK+F
XhzdmcjF0t2exOfYnxD6JYkCgYEAlWHmVio7yxjPuTdgRyvsaiOiMaxVF4n1ENer
PRAVrYt/pArOdAXpTJuV3rWpms8ooP3sny/7j8qnx+QzkL7UzZq5lN7p9fIcN4FU
1a38L06Aiq3czuwGF5r0WrZC1CTqvtS+c1bd57uHUcCWESuOkACht8V9ggl3SdvF
5j6I88ECgYAkmL0vnZyQyt3oGvr0bGdSfrTqtkx0e2XZR9ysu3KnMEkjWQOndnag
Vo9bG2zzBnY8D3tXFWJIpt8M9PBWW7EZiWLac/N8JwtcktcfcQg40wKSCUvQpwKx
LWh0V+LWMdMgGrRU2NeYFENoqyiMHeh8z79eBl0SJJMHITrnGch6kg==
-----END RSA PRIVATE KEY-----

168
socket_model.go Normal file
View File

@ -0,0 +1,168 @@
package main
type MessageInfo struct {
Id string `json:"id"`
Method string `json:"method"`
Params interface{}
}
type AddDeviceParam struct {
DeviceNo string `json:"deviceNo"`
DeviceName string `json:"deviceName"`
}
type MessageResponse struct {
Id string `json:"id"`
Method string `json:"method"`
Result interface{} `json:"result"`
}
type MessageErrorResponse struct {
Error *ErrorResponse `json:"error"`
Id string `json:"id"`
Method string `json:"method"`
Result interface{} `json:"result"`
}
type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
}
type AddDeviceResponse struct {
Code int `json:"code"`
DeviceId int `json:"deviceId"`
}
type CheckVersionResponse struct {
Code int `json:"code"`
DownloadUrl string `json:"downloadUrl"`
IsUpdate bool `json:"isUpdate"`
NewVersion string `json:"newVersion"`
}
type GetDeviceRemoteControlInfoParam struct {
DeviceId int `json:"deviceId"`
DeviceNo string `json:"deviceNo"`
}
type GetDeviceRemoteControlInfoResponse struct {
Code int `json:"code"`
DeviceId int `json:"deviceId"`
IdentificationCode string `json:"identificationCode"`
ShareUrl string `json:"shareUrl"`
VerificationCode string `json:"verificationCode"`
}
type GetFileTransferConfigResponse struct {
Code int `json:"code"`
DownloadSpeedRatio int `json:"downloadSpeedRatio"`
UploadSpeedRatio int `json:"uploadSpeedRatio"`
}
type GetTurnTransferConfigResponse struct {
Code int `json:"code"`
VideoBitrate int `json:"videoBitrate"`
}
type LoginParam struct {
PhoneNo string `json:"phoneNo"`
Password string `json:"password"`
DeviceId int `json:"deviceId"`
}
type LoginResponse struct {
Code int `json:"code"`
Token string `json:"token"`
}
type GetDeviceResponse struct {
Code int `json:"code"`
Devices []*DeviceResponse `json:"devices"`
}
type DeviceResponse struct {
DeviceId int `json:"deviceId"`
DeviceName string `json:"deviceName"`
DeviceNo string `json:"deviceNo"`
OnlineState int `json:"onlineState"`
}
type StartRemoteControlParam struct {
Type int `json:"type"`
Token string `json:"token"`
RemoteDeviceId int `json:"remoteDeviceId"`
RemoteIdentificationCode string `json:"remoteIdentificationCode"`
RemoteVerificationCode string `json:"remoteVerificationCode"`
VerificationType int `json:"verificationType"`
UserName string `json:"userName"`
Password string `json:"password"`
}
type StartRemoteControlResponse struct {
Code int `json:"code"`
StreamingConfig StreamingConfig `json:"streamingConfig"`
}
type RemoteStartRemoteControlResponse struct {
Id string `json:"id"`
Method string `json:"method"`
Params RemoteStreamingConfig `json:"params"`
}
type NotifyResponse struct {
Id string `json:"id"`
Method string `json:"method"`
Params interface{} `json:"params"`
}
type EmptyObj struct {
}
type StreamingConfig struct {
ClientId string `json:"clientId"`
ClientIds []string `json:"clientIds"`
ServiceId string `json:"serviceId"`
SignalServer string `json:"signalServer"`
StunAddrs []string `json:"stunAddrs"`
Token string `json:"token"`
TurnAddrs []TurnServer `json:"turnAddrs"`
}
type RemoteStreamingConfig struct {
ClientId string `json:"clientId"`
SignalServer string `json:"signalServer"`
StunAddrs []string `json:"stunAddrs"`
Token string `json:"token"`
TurnAddrs []TurnServer `json:"turnAddrs"`
}
type GetDeviceVerificationTypeResponse struct {
Code int `json:"code"`
VerificationType int `json:"verificationType"`
}
type GetOnlineUsersResponse struct {
Code int `json:"code"`
DeviceId int `json:"deviceId"`
Users []OnlineUser `json:"users"`
}
type OnlineUser struct {
ClientId string `json:"clientId"`
ClientOS string `json:"clientOS"`
UserName string `json:"userName"`
VisitTime string `json:"visitTime"`
}
type SimpleResponse struct {
Code int `json:"code"`
}
type DeleteDeviceResponse struct {
Code int `json:"code"`
DeviceId int `json:"deviceId"`
}
type ForceClientOfflineParam struct {
ClientIds []string `json:"clientIds"`
Reason int `json:"reason"`
}

660
socket_service.go Normal file
View File

@ -0,0 +1,660 @@
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/beevik/guid"
"github.com/gorilla/websocket"
"net/http"
"strings"
"time"
)
var (
upgrader = websocket.Upgrader{
//允许跨域访问
CheckOrigin: func(r *http.Request) bool {
return true
},
EnableCompression: false,
}
)
func serviceHandler(w http.ResponseWriter, r *http.Request) {
deviceNo := r.FormValue("deviceNo")
sessionId := r.FormValue("session_id")
clientType := r.FormValue("client_type")
wsConn, upgradeErr := upgrader.Upgrade(w, r, w.Header())
if upgradeErr != nil {
return
}
defer func() {
if reco := recover(); reco != any(nil) {
fmt.Printf("Service Runtime error caught: %v", reco)
}
}()
tokenOfSession := fastLinkService.GetTokenBySession(sessionId)
sessionGuid := guid.New().String()
fastLinkService.NewSession(deviceNo, sessionId, sessionGuid, clientType, wsConn)
wsConn.WriteMessage(websocket.TextMessage, []byte("0{\"sid\":\""+guid.New().String()+"\",\"upgrades\":[\"websocket\"],\"pingInterval\":25000,\"pingTimeout\":60000}"))
wsConn.WriteMessage(websocket.TextMessage, []byte("40"))
if clientType == CLIENT_TYPE_FASTLINK_SERVICE {
Logger.Println("Online DeviceNo:" + deviceNo + " Session:" + sessionId)
fastLinkService.SetOnlineStatus(deviceNo)
device := fastLinkService.GetDeviceByNo(deviceNo)
if device != nil && device.BelongUser != "" {
notifyDeviceChange(device.BelongUser)
}
}
for {
wsConn.SetReadDeadline(time.Now().In(TimeLocation).Add(time.Second * 60))
messageType, p, readError := wsConn.ReadMessage()
if readError != nil {
Logger.Println("Service WebSocket异常断开," + wsConn.RemoteAddr().String() + ",异常信息:" + readError.Error())
fastLinkService.RemoveSession(sessionId, sessionGuid)
if clientType == CLIENT_TYPE_FASTLINK_SERVICE {
Logger.Println("Offline DeviceNo:" + deviceNo + " Session:" + sessionId)
fastLinkService.SetOnlineStatus(deviceNo)
device := fastLinkService.GetDeviceByNo(deviceNo)
if device != nil {
//推送通知
notifyDeviceChange(device.BelongUser)
}
}
wsConn.Close()
return
}
if messageType == websocket.CloseMessage {
Logger.Println("Service WebSocket断开," + wsConn.RemoteAddr().String())
fastLinkService.RemoveSession(sessionId, sessionGuid)
if clientType == CLIENT_TYPE_FASTLINK_SERVICE {
Logger.Println("Offline DeviceNo:" + deviceNo + " Session:" + sessionId)
fastLinkService.SetOnlineStatus(deviceNo)
device := fastLinkService.GetDeviceByNo(deviceNo)
if device != nil {
//推送通知
notifyDeviceChange(device.BelongUser)
}
}
wsConn.Close()
return
}
strData := string(p)
//fmt.Printf("DATA:" + strData + "\n")
index := strings.Index(strData, "[")
cmd := ""
var items []interface{}
if index != -1 {
cmd = strData[0:index]
json.Unmarshal([]byte(strData[index:]), &items)
} else {
cmd = strData
}
switch cmd {
case "2":
if tokenOfSession != "" {
fastLinkService.RenewToken(tokenOfSession)
}
wsConn.WriteMessage(websocket.TextMessage, []byte("3"))
case "42":
if items[0] == "KeepAlive" {
wsConn.WriteMessage(websocket.TextMessage, []byte("42[\"KeepAliveAck\",{\"code\":200,\"message\":\"\",\"success\":true,\"error\":false}]"))
} else if items[0] == "message" {
msg := items[1].(map[string]interface{})["message"].(string)
var messageInfo *MessageInfo
json.Unmarshal([]byte(msg), &messageInfo)
if messageInfo != nil {
switch messageInfo.Method {
case "AddDevice":
var param AddDeviceParam
param.DeviceName = messageInfo.Params.(map[string]interface{})["deviceName"].(string)
param.DeviceNo = messageInfo.Params.(map[string]interface{})["deviceNo"].(string)
device := fastLinkService.AddDevice(param.DeviceNo, param.DeviceName)
addDeviceResp := &MessageResponse{
Id: messageInfo.Id,
Method: "AddDeviceAck",
Result: &AddDeviceResponse{
Code: 200,
DeviceId: device.DeviceId,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, addDeviceResp))
case "CheckVersion":
checkVersionResp := &MessageResponse{
Id: messageInfo.Id,
Method: "CheckVersionAck",
Result: &CheckVersionResponse{
Code: 200,
DownloadUrl: "",
IsUpdate: false,
NewVersion: "1.4.2.0",
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, checkVersionResp))
case "GetDeviceRemoteControlInfo":
var param GetDeviceRemoteControlInfoParam
param.DeviceNo = messageInfo.Params.(map[string]interface{})["deviceNo"].(string)
param.DeviceId = int(messageInfo.Params.(map[string]interface{})["deviceId"].(float64))
device := fastLinkService.GetDeviceByNo(param.DeviceNo)
if device != nil {
getDeviceRemoteControlInfoResp := &MessageResponse{
Id: messageInfo.Id,
Method: "GetDeviceRemoteControlInfoAck",
Result: &GetDeviceRemoteControlInfoResponse{
Code: 200,
DeviceId: device.DeviceId,
IdentificationCode: device.IdentificationCode,
ShareUrl: WEB_URL + "?d=" + base64.StdEncoding.EncodeToString([]byte(device.DeviceNo)) + "&r=" + device.ShareRand,
VerificationCode: device.VerificationCode,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, getDeviceRemoteControlInfoResp))
}
case "GetFileTransferConfig":
getFileTransferConfigResp := &MessageResponse{
Id: messageInfo.Id,
Method: "GetFileTransferConfigAck",
Result: &GetFileTransferConfigResponse{
Code: 200,
DownloadSpeedRatio: 80,
UploadSpeedRatio: 80,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo+"_service", getFileTransferConfigResp))
case "GetTurnTransferConfig":
getTurnTransferConfigResp := &MessageResponse{
Id: messageInfo.Id,
Method: "GetTurnTransferConfigAck",
Result: &GetTurnTransferConfigResponse{
Code: 200,
VideoBitrate: 4194377,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo+"_service", getTurnTransferConfigResp))
case "Login":
var param LoginParam
param.PhoneNo = messageInfo.Params.(map[string]interface{})["phoneNo"].(string)
param.Password = messageInfo.Params.(map[string]interface{})["password"].(string)
param.DeviceId = int(messageInfo.Params.(map[string]interface{})["deviceId"].(float64))
token := fastLinkService.Login(param.PhoneNo, param.Password, param.DeviceId)
loginResp := &MessageResponse{
Id: messageInfo.Id,
Method: "LoginAck",
}
result := &LoginResponse{}
if token == "" {
result.Code = 1009
} else {
result.Code = 200
result.Token = token
notifyDeviceChange(param.PhoneNo)
}
loginResp.Result = result
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, loginResp))
tokenOfSession = token
fastLinkService.BindSessionToToken(sessionId, token)
case "Logout":
token := messageInfo.Params.(map[string]interface{})["token"].(string)
fastLinkService.RemoveToken(token)
logoutResp := &MessageResponse{
Id: messageInfo.Id,
Method: "LogoutAck",
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, logoutResp))
case "GetDevices":
token := messageInfo.Params.(map[string]interface{})["token"].(string)
deviceList := fastLinkService.GetDeviceByToken(token)
var dList []*DeviceResponse
for _, info := range deviceList {
d := &DeviceResponse{
DeviceId: info.DeviceId,
DeviceName: info.DeviceName,
DeviceNo: info.DeviceNo,
}
if info.CustomName != "" {
d.DeviceName = info.CustomName
}
if info.Online {
d.OnlineState = 1
} else {
d.OnlineState = 0
}
dList = append(dList, d)
}
getDevicesResp := &MessageResponse{
Id: messageInfo.Id,
Method: "GetDevicesAck",
Result: &GetDeviceResponse{
Code: 200,
Devices: dList,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, getDevicesResp))
case "UpdateDeviceVerificationCode":
paramDeviceNo := messageInfo.Params.(map[string]interface{})["deviceNo"].(string)
device := fastLinkService.RefreshDeviceVerificationCode(paramDeviceNo)
if device != nil {
updateDeviceVerificationCodeResp := &MessageResponse{
Id: messageInfo.Id,
Method: "UpdateDeviceVerificationCodeAck",
Result: &GetDeviceRemoteControlInfoResponse{
Code: 200,
DeviceId: device.DeviceId,
IdentificationCode: device.IdentificationCode,
ShareUrl: WEB_URL + "?d=" + device.DeviceNo + "&r=" + device.ShareRand,
VerificationCode: device.VerificationCode,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, updateDeviceVerificationCodeResp))
}
case "GetDeviceVerificationType":
paramDeviceId := int(messageInfo.Params.(map[string]interface{})["deviceId"].(float64))
device := fastLinkService.GetDeviceById(paramDeviceId)
verificationType := 0
if device != nil {
verificationType = device.VerificationType
}
getDeviceVerificationTypeResp := &MessageResponse{
Id: messageInfo.Id,
Method: "GetDeviceVerificationTypeAck",
Result: &GetDeviceVerificationTypeResponse{
Code: 200,
VerificationType: verificationType,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, getDeviceVerificationTypeResp))
case "UpdateDeviceVerificationType":
verifyDeviceNo := messageInfo.Params.(map[string]interface{})["deviceNo"].(string)
verifyPass := messageInfo.Params.(map[string]interface{})["password"].(string)
verifyType := int(messageInfo.Params.(map[string]interface{})["verificationType"].(float64))
deviceId := int(messageInfo.Params.(map[string]interface{})["deviceId"].(float64))
device := fastLinkService.GetDeviceByNo(verifyDeviceNo)
if device != nil {
switch verifyType {
case 0:
device.VerificationType = 0
device.TwoStepPass = ""
case 1:
//未实现
device.VerificationType = 0
device.TwoStepPass = ""
case 2:
device.VerificationType = 2
device.TwoStepPass = verifyPass
}
}
updateDeviceVerificationTypeResp := &MessageResponse{
Id: messageInfo.Id,
Method: "UpdateDeviceVerificationTypeAck",
Result: &DeleteDeviceResponse{
Code: 200,
DeviceId: deviceId,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, updateDeviceVerificationTypeResp))
case "StartRemoteControl":
remoteControlParam := StartRemoteControlParam{
Type: int(messageInfo.Params.(map[string]interface{})["type"].(float64)),
Token: messageInfo.Params.(map[string]interface{})["token"].(string),
RemoteDeviceId: int(messageInfo.Params.(map[string]interface{})["remoteDeviceId"].(float64)),
RemoteIdentificationCode: messageInfo.Params.(map[string]interface{})["remoteIdentificationCode"].(string),
RemoteVerificationCode: messageInfo.Params.(map[string]interface{})["remoteVerificationCode"].(string),
VerificationType: int(messageInfo.Params.(map[string]interface{})["verificationType"].(float64)),
UserName: messageInfo.Params.(map[string]interface{})["userName"].(string),
Password: messageInfo.Params.(map[string]interface{})["password"].(string),
}
var device *DeviceInfo
if remoteControlParam.RemoteDeviceId != 0 {
device = fastLinkService.GetDeviceById(remoteControlParam.RemoteDeviceId)
} else {
device = fastLinkService.GetDeviceByIdentificationCode(remoteControlParam.RemoteIdentificationCode)
}
if device.Online && device.Visible {
verifyResult := true
userName := "游客"
if remoteControlParam.Token != "" {
userName = fastLinkService.GetUserByToken(remoteControlParam.Token)
if device.BelongUser != userName {
verifyResult = false
}
} else {
if device.VerificationCode != remoteControlParam.RemoteVerificationCode || device.IdentificationCode != remoteControlParam.RemoteIdentificationCode {
verifyResult = false
}
}
if device.VerificationType == 2 && device.TwoStepPass != "" {
if device.TwoStepPass != remoteControlParam.Password {
verifyResult = false
}
}
if !verifyResult {
errorResp := &MessageResponse{
Id: messageInfo.Id,
Method: "StartRemoteControlAck",
Result: &ErrorResponse{
Code: 5004,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, errorResp))
} else {
fastLinkService.RenewToken(remoteControlParam.Token)
clientId := GenShortGUID()
streamToken := GenShortGUID()
fastLinkService.PrepareSignalSession(clientId, device.DeviceId, device.DeviceNo, streamToken, userName)
targetRemoteControlResp := &RemoteStartRemoteControlResponse{
Id: guid.New().String(),
Method: "SetStreamingConfig",
Params: RemoteStreamingConfig{
ClientId: clientId,
SignalServer: globalConfig.SignalServer,
StunAddrs: globalConfig.StunServers,
Token: streamToken,
TurnAddrs: globalConfig.TurnServers,
},
}
remoteSession := fastLinkService.GetSession(device.DeviceNo, CLIENT_TYPE_FASTLINK_SERVICE)
remoteSession.WsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(device.DeviceNo+"_service", targetRemoteControlResp))
//推送UI提示
notifyOnlineUserChange(device.DeviceNo)
startRemoteControlResp := &MessageResponse{
Id: messageInfo.Id,
Method: "StartRemoteControlAck",
Result: &StartRemoteControlResponse{
Code: 200,
StreamingConfig: StreamingConfig{
ClientId: clientId,
ClientIds: []string{},
ServiceId: device.DeviceNo,
SignalServer: globalConfig.SignalServer,
StunAddrs: globalConfig.StunServers,
Token: streamToken,
TurnAddrs: globalConfig.TurnServers,
},
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, startRemoteControlResp))
}
} else {
errorResp := &MessageResponse{
Id: messageInfo.Id,
Method: "StartRemoteControlAck",
Result: &ErrorResponse{
Code: 5007,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, errorResp))
}
case "GetOnlineUsers":
paramDeviceId := int(messageInfo.Params.(map[string]interface{})["deviceId"].(float64))
clients := fastLinkService.GetSignalSessionByDeviceId(paramDeviceId)
var onlineUsers []OnlineUser
onlineUsers = []OnlineUser{}
for _, client := range clients {
onlineUsers = append(onlineUsers, OnlineUser{
ClientId: client.Token,
ClientOS: "Windows 8(OS build number: 9200)",
UserName: client.StreamUser,
VisitTime: client.CreateTime.Format("2006-01-02 15:04"),
})
}
getOnlineUsersResp := &MessageResponse{
Id: messageInfo.Id,
Method: "GetOnlineUsersAck",
Result: &GetOnlineUsersResponse{
Code: 200,
DeviceId: paramDeviceId,
Users: onlineUsers,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, getOnlineUsersResp))
case "VerifyStreamingToken":
streamToken := messageInfo.Params.(map[string]interface{})["token"].(string)
clientId := messageInfo.Params.(map[string]interface{})["clientId"].(string)
signalSession := fastLinkService.GetSignalSession(clientId)
if streamToken == signalSession.StreamToken {
verifyStreamingTokenResp := &MessageResponse{
Id: messageInfo.Id,
Method: "VerifyStreamingTokenAck",
Result: &SimpleResponse{
Code: 200,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo+"_service", verifyStreamingTokenResp))
}
case "DeleteDevice":
token := messageInfo.Params.(map[string]interface{})["token"].(string)
deviceId := int(messageInfo.Params.(map[string]interface{})["deviceId"].(float64))
userName := fastLinkService.GetUserByToken(token)
device := fastLinkService.GetDeviceById(deviceId)
if device != nil && userName != "" && device.BelongUser == userName {
fastLinkService.RenewToken(token)
device.Visible = false
//推送通知
notifyDeviceChange(userName)
}
deleteDeviceResp := &MessageResponse{
Id: messageInfo.Id,
Method: "DeleteDeviceAck",
Result: &DeleteDeviceResponse{
Code: 200,
DeviceId: deviceId,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, deleteDeviceResp))
case "BindDevice":
token := messageInfo.Params.(map[string]interface{})["token"].(string)
bindDeviceNo := messageInfo.Params.(map[string]interface{})["deviceNo"].(string)
deviceId := int(messageInfo.Params.(map[string]interface{})["deviceId"].(float64))
userName := fastLinkService.GetUserByToken(token)
device := fastLinkService.GetDeviceByNo(bindDeviceNo)
if device != nil && userName != "" {
fastLinkService.RenewToken(token)
device.BelongUser = userName
device.Visible = true
//推送通知
notifyDeviceChange(userName)
}
deleteDeviceResp := &MessageResponse{
Id: messageInfo.Id,
Method: "BindDeviceAck",
Result: &DeleteDeviceResponse{
Code: 200,
DeviceId: deviceId,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, deleteDeviceResp))
case "UpdateDeviceVerificationUpdateFrequency":
//未实现,直接返回
deleteDeviceResp := &MessageResponse{
Id: messageInfo.Id,
Method: "UpdateDeviceVerificationUpdateFrequencyAck",
Result: &SimpleResponse{
Code: 200,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, deleteDeviceResp))
case "RebootDevice":
token := messageInfo.Params.(map[string]interface{})["token"].(string)
deviceId := int(messageInfo.Params.(map[string]interface{})["deviceId"].(float64))
device := fastLinkService.GetDeviceById(deviceId)
user := fastLinkService.GetUserByToken(token)
if device != nil && user != "" && device.BelongUser == user {
fastLinkService.RenewToken(token)
session := fastLinkService.GetSession(device.DeviceNo, CLIENT_TYPE_FASTLINK_SERVICE)
if session != nil {
notifyResp := &NotifyResponse{
Id: guid.New().String(),
Method: "NotifyReboot",
Params: EmptyObj{},
}
session.WsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(session.DeviceNo+"_service", notifyResp))
}
}
rebootDeviceResp := &MessageResponse{
Id: messageInfo.Id,
Method: "RebootDeviceAck",
Result: &AddDeviceResponse{
Code: 200,
DeviceId: deviceId,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, rebootDeviceResp))
case "ShutdownDevice":
token := messageInfo.Params.(map[string]interface{})["token"].(string)
deviceId := int(messageInfo.Params.(map[string]interface{})["deviceId"].(float64))
device := fastLinkService.GetDeviceById(deviceId)
user := fastLinkService.GetUserByToken(token)
if device != nil && user != "" && device.BelongUser == user {
fastLinkService.RenewToken(token)
session := fastLinkService.GetSession(device.DeviceNo, CLIENT_TYPE_FASTLINK_SERVICE)
if session != nil {
notifyResp := &NotifyResponse{
Id: guid.New().String(),
Method: "NotifyShutdown",
Params: EmptyObj{},
}
session.WsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(session.DeviceNo+"_service", notifyResp))
}
}
shutdownDeviceResp := &MessageResponse{
Id: messageInfo.Id,
Method: "ShutdownDeviceAck",
Result: &AddDeviceResponse{
Code: 200,
DeviceId: deviceId,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, shutdownDeviceResp))
case "UpdateDeviceName":
token := messageInfo.Params.(map[string]interface{})["token"].(string)
deviceId := int(messageInfo.Params.(map[string]interface{})["deviceId"].(float64))
newDeviceName := messageInfo.Params.(map[string]interface{})["newDeviceName"].(string)
user := fastLinkService.GetUserByToken(token)
device := fastLinkService.GetDeviceById(deviceId)
if user != "" && device != nil && device.BelongUser == user {
fastLinkService.RenewToken(token)
device.CustomName = newDeviceName
notifyDeviceChange(user)
}
updateDeviceNameResp := &MessageResponse{
Id: messageInfo.Id,
Method: "UpdateDeviceNameAck",
Result: &AddDeviceResponse{
Code: 200,
DeviceId: deviceId,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, updateDeviceNameResp))
case "ForceClientOffline":
deviceId := int(messageInfo.Params.(map[string]interface{})["deviceId"].(float64))
offlineDeviceNo := messageInfo.Params.(map[string]interface{})["deviceNo"].(string)
device := fastLinkService.GetDeviceByNo(offlineDeviceNo)
if device != nil && device.DeviceId == deviceId {
session := fastLinkService.GetSession(offlineDeviceNo, CLIENT_TYPE_FASTLINK_SERVICE)
if session != nil {
tClientIds := messageInfo.Params.(map[string]interface{})["clientIds"].([]interface{})
var clientIds []string
clientIds = []string{}
for _, id := range tClientIds {
clientIds = append(clientIds, id.(string))
}
notifyResp := &NotifyResponse{
Id: guid.New().String(),
Method: "NotifyClientOffline",
Params: ForceClientOfflineParam{
ClientIds: clientIds,
Reason: 3,
},
}
session.WsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(session.DeviceNo+"_service", notifyResp))
}
}
forceClientOfflineResp := &MessageResponse{
Id: messageInfo.Id,
Method: "ForceClientOfflineAck",
Result: &SimpleResponse{
Code: 200,
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, forceClientOfflineResp))
case "SetStreamingConfigAck":
default:
errorResp := &MessageErrorResponse{
Method: messageInfo.Method,
Error: &ErrorResponse{
Code: 2004,
Message: "请求调用的方法不存在",
},
}
wsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, errorResp))
}
}
}
}
}
}
func notifyDeviceChange(user string) {
userUISessions := fastLinkService.GetSessionsByUser(user, CLIENT_TYPE_FASTLINK_UI)
for _, session := range userUISessions {
notifyResp := &NotifyResponse{
Id: guid.New().String(),
Method: "NotifyDevicesChange",
Params: EmptyObj{},
}
Logger.Println("notifyDeviceChange: DeviceNo:" + session.DeviceNo + " User:" + user)
session.WsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(session.DeviceNo, notifyResp))
}
}
func notifyOnlineUserChange(deviceNo string) {
remoteUISession := fastLinkService.GetSession(deviceNo, CLIENT_TYPE_FASTLINK_UI)
if remoteUISession != nil {
notifyResp := &NotifyResponse{
Id: guid.New().String(),
Method: "NotifyOnlineUsersChange",
Params: EmptyObj{},
}
Logger.Println("notifyOnlineUserChange: DeviceNo:" + deviceNo)
remoteUISession.WsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(deviceNo, notifyResp))
}
}
func makeMessageResponse(deviceNo string, params interface{}) []byte {
var result []interface{}
result = append(result, "message")
var msg map[string]interface{}
msg = make(map[string]interface{})
msg["deviceNo"] = deviceNo
marshal, _ := json.Marshal(params)
msg["message"] = string(marshal)
result = append(result, msg)
bytes, _ := json.Marshal(result)
return append([]byte("42"), bytes...)
}

117
socket_signal.go Normal file
View File

@ -0,0 +1,117 @@
package main
import (
"encoding/json"
"fmt"
"github.com/beevik/guid"
"github.com/gorilla/websocket"
"net/http"
"strings"
"time"
)
func signalHandler(w http.ResponseWriter, r *http.Request) {
token := r.FormValue("token")
if token == "" {
fmt.Println("signalHandler Error : token is empty")
}
wsConn, upgradeErr := upgrader.Upgrade(w, r, w.Header())
if upgradeErr != nil {
return
}
defer func() {
if reco := recover(); reco != any(nil) {
fmt.Printf("Signal Runtime error caught: %v", reco)
}
}()
sessionGuid := guid.New().String()
fastLinkService.NewSignalSession(token, sessionGuid, wsConn)
wsConn.WriteMessage(websocket.TextMessage, []byte("0{\"sid\":\""+guid.New().String()+"\",\"upgrades\":[\"websocket\"],\"pingInterval\":25000,\"pingTimeout\":60000}"))
wsConn.WriteMessage(websocket.TextMessage, []byte("40"))
wsConn.WriteMessage(websocket.TextMessage, []byte("42[\"server-authenticated\",{\"uid\":\""+token+"\",\"authenticated\":true}]"))
for {
wsConn.SetReadDeadline(time.Now().In(TimeLocation).Add(time.Second * 60))
messageType, p, readError := wsConn.ReadMessage()
if readError != nil {
Logger.Println("Signal WebSocket异常断开," + wsConn.RemoteAddr().String() + ",异常信息:" + readError.Error())
signalSession := fastLinkService.GetSignalSession(token)
fastLinkService.RemoveSignalSession(token, sessionGuid)
if signalSession != nil {
//推送UI提示
notifyOnlineUserChange(signalSession.RelateDeviceNo)
}
wsConn.Close()
return
}
if messageType == websocket.CloseMessage {
Logger.Println("Signal WebSocket断开," + wsConn.RemoteAddr().String())
signalSession := fastLinkService.GetSignalSession(token)
fastLinkService.RemoveSignalSession(token, sessionGuid)
if signalSession != nil {
//推送UI提示
notifyOnlineUserChange(signalSession.RelateDeviceNo)
}
wsConn.Close()
return
}
strData := string(p)
//fmt.Printf("SIGNAL:" + strData + "\n")
index := strings.Index(strData, "[")
cmd := ""
var items []interface{}
if index != -1 {
cmd = strData[0:index]
json.Unmarshal([]byte(strData[index:]), &items)
} else {
cmd = strData
}
if len(cmd) > 1 && cmd[:2] == "42" {
cmd = "42"
}
switch cmd {
case "2":
wsConn.WriteMessage(websocket.TextMessage, []byte("3"))
case "42":
if items[0] == "KeepAlive" {
wsConn.WriteMessage(websocket.TextMessage, []byte("42[\"KeepAliveAck\",{}]"))
} else if items[0] == "owt-message" {
from := ""
if _, ok := items[1].(map[string]interface{})["from"]; ok {
from = items[1].(map[string]interface{})["from"].(string)
}
if from == "" {
from = token
}
to := items[1].(map[string]interface{})["to"].(string)
data := items[1].(map[string]interface{})["data"].(string)
target := fastLinkService.GetSignalSession(to)
if target != nil {
target.WsConn.WriteMessage(websocket.TextMessage, makeOwtMessage(from, to, data))
}
}
}
}
}
func makeOwtMessage(from string, to string, data string) []byte {
var message []interface{}
message = append(message, "owt-message")
var m map[string]string
m = make(map[string]string)
m["from"] = from
m["to"] = to
m["data"] = data
message = append(message, m)
marshal, _ := json.Marshal(message)
return append([]byte("42"), marshal...)
}

BIN
web/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

24
web/index.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="keywords" content="fastLink,FASTLINK,顺网云游戏,云游戏,顺网云玩,顺网">
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,minimal-ui">
<meta http-equiv="X-UA-Compatible" content="ie=edge,chrome=1">
<meta name="msapplication-tap-highlight" content="no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="browsermode" content="application">
<meta name="full-screen" content="yes">
<meta name="x5-fullscreen" content="true">
<meta name="x5-page-mode" content="app">
<meta name="360-fullscreen" content="true">
<meta name="wap-font-scale" content="no">
<meta name="format-detection" content="telphone=no, email=no">
<title>FASTLINK</title>
<link rel="shortcut icon" href="favicon.png"><link href="static1/css/tool.e82ab3594a815ab433dc.css" rel="stylesheet"></head>
<body>
<div id="video-and-stats"></div>
<script src="./static1/js/hammer.js"></script>
<script src="static1/js/runtimechunk~tool.js"></script><script src="static1/js/2.7473f57.js"></script><script src="static1/js/1.bdd544c.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

BIN
web/static1/img/eye.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2643
web/static1/js/hammer.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

458
web_api.go Normal file
View File

@ -0,0 +1,458 @@
package main
import (
"encoding/base64"
"encoding/json"
"github.com/beevik/guid"
"github.com/gorilla/websocket"
"io/ioutil"
"net/http"
"strings"
)
type WebApiRequest struct {
DeviceNo string `json:"deviceNo"`
Random string `json:"random"`
ClientNo string `json:"clientNo"`
UserName string `json:"userName"`
VerificationPassword string `json:"verificationPassword"`
}
type WebApiResponse struct {
TraceId interface{} `json:"traceId"`
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
Success bool `json:"success"`
Error bool `json:"error"`
}
type WebCheckData struct {
Pass bool `json:"pass"`
}
type DeviceVerificationTypeData struct {
DeviceId interface{} `json:"deviceId"`
VerificationType interface{} `json:"verificationType"`
}
type IdTransferData struct {
StreamingConfig StreamingConfig `json:"streamingConfig"`
StreamRecordId int `json:"streamRecordId"` //记录分析,没啥用,未实现相关功能
}
type LoginRequest struct {
Password string `json:"password"`
Phone string `json:"phone"`
}
type AppDeviceListData struct {
DeviceId int `json:"deviceId"`
DeviceNo string `json:"deviceNo"`
DeviceName string `json:"deviceName"`
ExpireTime interface{} `json:"expireTime"`
ExpireTimeStr interface{} `json:"expireTimeStr"`
OnlineState int `json:"onlineState"`
EnableConnect int `json:"enableConnect"`
ClientVersion interface{} `json:"clientVersion"`
}
type RegisterModel struct {
Code string `json:"code"`
Password string `json:"password"`
Password2 string `json:"password2"`
Phone string `json:"phone"`
UserName string `json:"userName"`
}
func checkHandler(w http.ResponseWriter, r *http.Request) {
data, _ := ioutil.ReadAll(r.Body)
defer r.Body.Close()
var model *WebApiRequest
json.Unmarshal(data, &model)
decodeString, _ := base64.StdEncoding.DecodeString(model.DeviceNo)
model.DeviceNo = string(decodeString)
result := fastLinkService.CheckWebLogin(model.DeviceNo, model.Random)
resp := WebApiResponse{
TraceId: nil,
Code: 200,
Message: "",
Data: &WebCheckData{
Pass: true,
},
Success: true,
Error: false,
}
switch result {
case 1:
resp.Data = nil
resp.Error = true
resp.Success = false
resp.Message = "远程主机没有分享"
resp.Code = 500
case 2:
resp.Data = nil
resp.Error = true
resp.Success = false
resp.Message = "要连接的主机不存在"
resp.Code = 500
}
marshal, _ := json.Marshal(resp)
w.Write(marshal)
}
func deviceVerificationTypeHandler(w http.ResponseWriter, r *http.Request) {
data, _ := ioutil.ReadAll(r.Body)
defer r.Body.Close()
var model *WebApiRequest
json.Unmarshal(data, &model)
device := fastLinkService.GetDeviceByNo(model.DeviceNo)
vData := &DeviceVerificationTypeData{
DeviceId: nil,
VerificationType: nil,
}
resp := WebApiResponse{
TraceId: nil,
Code: 200,
Message: "",
Data: vData,
Success: true,
Error: false,
}
if device != nil {
vData.DeviceId = device.DeviceId
vData.VerificationType = device.VerificationType
}
marshal, _ := json.Marshal(resp)
w.Write(marshal)
}
func idTransferHandler(w http.ResponseWriter, r *http.Request) {
data, _ := ioutil.ReadAll(r.Body)
defer r.Body.Close()
var model *WebApiRequest
json.Unmarshal(data, &model)
device := fastLinkService.GetDeviceByNo(model.DeviceNo)
if device == nil {
resp := WebApiResponse{
TraceId: nil,
Code: 500,
Message: "设备不存在",
Data: nil,
Success: false,
Error: true,
}
marshal, _ := json.Marshal(resp)
w.Write(marshal)
} else {
if device.Online && device.Visible {
if device.VerificationType == 2 && device.TwoStepPass != "" && device.TwoStepPass != model.VerificationPassword {
resp := WebApiResponse{
TraceId: nil,
Code: 500,
Message: "访问密码不正确",
Data: nil,
Success: false,
Error: true,
}
marshal, _ := json.Marshal(resp)
w.Write(marshal)
return
}
streamToken := GenShortGUID()
if model.UserName == "" {
model.UserName = "WEB客户端"
}
fastLinkService.PrepareSignalSession(model.ClientNo, device.DeviceId, device.DeviceNo, streamToken, model.UserName)
targetRemoteControlResp := &RemoteStartRemoteControlResponse{
Id: guid.New().String(),
Method: "SetStreamingConfig",
Params: RemoteStreamingConfig{
ClientId: model.ClientNo,
SignalServer: globalConfig.SignalServer,
StunAddrs: globalConfig.StunServers,
Token: streamToken,
TurnAddrs: globalConfig.TurnServers,
},
}
remoteSession := fastLinkService.GetSession(device.DeviceNo, CLIENT_TYPE_FASTLINK_SERVICE)
remoteSession.WsConn.WriteMessage(websocket.TextMessage, makeMessageResponse(device.DeviceNo+"_service", targetRemoteControlResp))
//推送UI提示
notifyOnlineUserChange(device.DeviceNo)
idTransferData := IdTransferData{
StreamingConfig: StreamingConfig{
ClientId: model.ClientNo,
ClientIds: []string{model.ClientNo},
ServiceId: model.DeviceNo,
SignalServer: globalConfig.SignalServer,
StunAddrs: globalConfig.StunServers,
Token: streamToken,
TurnAddrs: globalConfig.TurnServers,
},
StreamRecordId: 0,
}
resp := WebApiResponse{
TraceId: nil,
Code: 200,
Message: "",
Data: idTransferData,
Success: true,
Error: false,
}
marshal, _ := json.Marshal(resp)
w.Write(marshal)
} else {
resp := WebApiResponse{
TraceId: nil,
Code: 500,
Message: "远程主机不在线",
Data: nil,
Success: false,
Error: true,
}
marshal, _ := json.Marshal(resp)
w.Write(marshal)
}
}
}
func closeHandler(w http.ResponseWriter, r *http.Request) {
//记录埋点用,未实现,直接返回
resp := WebApiResponse{
TraceId: nil,
Code: 200,
Message: "",
Data: nil,
Success: true,
Error: false,
}
marshal, _ := json.Marshal(resp)
w.Write(marshal)
}
func loginForAppHandler(w http.ResponseWriter, r *http.Request) {
data, _ := ioutil.ReadAll(r.Body)
defer r.Body.Close()
var model *LoginRequest
json.Unmarshal(data, &model)
success := fastLinkService.CheckUser(model.Phone, model.Password)
resp := WebApiResponse{
TraceId: nil,
Code: 200,
Message: "",
Data: nil,
Success: true,
Error: false,
}
if !success {
resp.Success = false
resp.Error = true
resp.Code = 500
resp.Message = "用户名或密码不正确!"
}
token := fastLinkService.NewToken(model.Phone, TOKEN_TYPE_APP)
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: token,
})
marshal, _ := json.Marshal(resp)
w.Write(marshal)
}
func appListDeviceHandler(w http.ResponseWriter, r *http.Request) {
cookie := r.Header.Get("Cookie")
user := ""
token := ""
if len(cookie) > 7 {
index := strings.Index(cookie, "token=")
token = cookie[index+6:]
user = fastLinkService.GetUserByToken(token)
}
if user == "" {
errResp := WebApiResponse{
TraceId: nil,
Code: 500,
Message: "用户未登录",
Data: nil,
Success: false,
Error: true,
}
marshal, _ := json.Marshal(errResp)
w.Write(marshal)
return
}
fastLinkService.RenewToken(token)
devices := fastLinkService.GetDeviceByToken(token)
var data []*AppDeviceListData
for _, device := range devices {
t := &AppDeviceListData{
DeviceId: device.DeviceId,
DeviceNo: device.DeviceNo,
DeviceName: device.DeviceName,
ExpireTime: nil,
ExpireTimeStr: nil,
ClientVersion: nil,
}
if device.CustomName != "" {
t.DeviceName = device.CustomName
}
if device.Online {
t.OnlineState = 1
} else {
t.OnlineState = 0
}
if device.Visible {
t.EnableConnect = 1
} else {
t.EnableConnect = 0
}
data = append(data, t)
}
resp := WebApiResponse{
TraceId: nil,
Code: 200,
Message: "",
Data: data,
Success: true,
Error: false,
}
marshal, _ := json.Marshal(resp)
w.Write(marshal)
}
func logoutHandler(w http.ResponseWriter, r *http.Request) {
cookie := r.Header.Get("Cookie")
user := ""
token := ""
if len(cookie) > 7 {
index := strings.Index(cookie, "token=")
token = cookie[index+6:]
user = fastLinkService.GetUserByToken(token)
}
if user == "" {
errResp := WebApiResponse{
TraceId: nil,
Code: 500,
Message: "用户未登录",
Data: nil,
Success: false,
Error: true,
}
marshal, _ := json.Marshal(errResp)
w.Write(marshal)
return
}
fastLinkService.RemoveToken(token)
resp := WebApiResponse{
TraceId: nil,
Code: 200,
Message: "",
Data: nil,
Success: true,
Error: false,
}
marshal, _ := json.Marshal(resp)
w.Write(marshal)
}
func registerHandler(w http.ResponseWriter, r *http.Request) {
data, _ := ioutil.ReadAll(r.Body)
defer r.Body.Close()
var model *RegisterModel
json.Unmarshal(data, &model)
if model.UserName == "" || model.Phone == "" {
errResp := WebApiResponse{
TraceId: nil,
Code: 500,
Message: "用户名不能为空",
Data: nil,
Success: false,
Error: true,
}
marshal, _ := json.Marshal(errResp)
w.Write(marshal)
return
}
if model.Password == "" {
errResp := WebApiResponse{
TraceId: nil,
Code: 500,
Message: "密码不能为空",
Data: nil,
Success: false,
Error: true,
}
marshal, _ := json.Marshal(errResp)
w.Write(marshal)
return
}
if model.Password != model.Password2 {
errResp := WebApiResponse{
TraceId: nil,
Code: 500,
Message: "两次密码输入不一致",
Data: nil,
Success: false,
Error: true,
}
marshal, _ := json.Marshal(errResp)
w.Write(marshal)
return
}
if model.Password != model.Password2 {
errResp := WebApiResponse{
TraceId: nil,
Code: 500,
Message: "两次密码输入不一致",
Data: nil,
Success: false,
Error: true,
}
marshal, _ := json.Marshal(errResp)
w.Write(marshal)
return
}
exist := fastLinkService.CheckUserExist(model.UserName)
if exist {
errResp := WebApiResponse{
TraceId: nil,
Code: 500,
Message: "此手机号已被其他用户绑定!",
Data: nil,
Success: false,
Error: true,
}
marshal, _ := json.Marshal(errResp)
w.Write(marshal)
return
}
fastLinkService.AddUser(model.UserName, model.Password)
resp := WebApiResponse{
TraceId: nil,
Code: 200,
Message: "",
Data: nil,
Success: true,
Error: false,
}
marshal, _ := json.Marshal(resp)
w.Write(marshal)
}
func sendCodeHandler(w http.ResponseWriter, r *http.Request) {
resp := WebApiResponse{
TraceId: nil,
Code: 500,
Message: "私服未实现发送验证码,输入任意验证码即可",
Data: nil,
Success: false,
Error: true,
}
marshal, _ := json.Marshal(resp)
w.Write(marshal)
}