commit
0d166f2d13
1
backend/services/controller/.gitignore
vendored
1
backend/services/controller/.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
/.env.local
|
/.env.local
|
||||||
run.prod.sh
|
run.prod.sh
|
||||||
|
/tmp
|
||||||
|
|
@ -11,9 +11,13 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
||||||
github.com/golang/snappy v0.0.1 // indirect
|
github.com/golang/snappy v0.0.1 // indirect
|
||||||
|
github.com/gomodule/redigo v1.8.4 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/googollee/go-socket.io v1.7.0 // indirect
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
github.com/joho/godotenv v1.5.1 // indirect
|
github.com/joho/godotenv v1.5.1 // indirect
|
||||||
github.com/klauspost/compress v1.13.6 // indirect
|
github.com/klauspost/compress v1.13.6 // indirect
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,23 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/eclipse/paho.golang v0.10.0 h1:oUGPjRwWcZQRgDD9wVDV7y7i7yBSxts3vcvcNJo8B4Q=
|
github.com/eclipse/paho.golang v0.10.0 h1:oUGPjRwWcZQRgDD9wVDV7y7i7yBSxts3vcvcNJo8B4Q=
|
||||||
github.com/eclipse/paho.golang v0.10.0/go.mod h1:rhrV37IEwauUyx8FHrvmXOKo+QRKng5ncoN1vJiJMcs=
|
github.com/eclipse/paho.golang v0.10.0/go.mod h1:rhrV37IEwauUyx8FHrvmXOKo+QRKng5ncoN1vJiJMcs=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg=
|
||||||
|
github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googollee/go-socket.io v1.7.0 h1:ODcQSAvVIPvKozXtUGuJDV3pLwdpBLDs1Uoq/QHIlY8=
|
||||||
|
github.com/googollee/go-socket.io v1.7.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg=
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
|
@ -34,6 +41,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||||
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
|
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
|
||||||
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
|
@ -71,6 +79,8 @@ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175
|
||||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,11 @@ type Api struct {
|
||||||
QMutex *sync.Mutex
|
QMutex *sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
NormalUser = iota
|
||||||
|
AdminUser
|
||||||
|
)
|
||||||
|
|
||||||
func NewApi(port string, db db.Database, b mtp.Broker, msgQueue map[string](chan usp_msg.Msg), m *sync.Mutex) Api {
|
func NewApi(port string, db db.Database, b mtp.Broker, msgQueue map[string](chan usp_msg.Msg), m *sync.Mutex) Api {
|
||||||
return Api{
|
return Api{
|
||||||
Port: port,
|
Port: port,
|
||||||
|
|
@ -36,25 +41,40 @@ func NewApi(port string, db db.Database, b mtp.Broker, msgQueue map[string](chan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: restructure http api calls for mqtt, to use golang generics and avoid code repetition
|
||||||
|
//TODO: standardize timeouts through code
|
||||||
|
//TODO: fix api methods
|
||||||
|
|
||||||
func StartApi(a Api) {
|
func StartApi(a Api) {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
authentication := r.PathPrefix("/api/auth").Subrouter()
|
authentication := r.PathPrefix("/api/auth").Subrouter()
|
||||||
authentication.HandleFunc("/login", a.generateToken).Methods("PUT")
|
authentication.HandleFunc("/login", a.generateToken).Methods("PUT")
|
||||||
//authentication.HandleFunc("/register", a.registerUser).Methods("POST")
|
authentication.HandleFunc("/register", a.registerUser).Methods("POST")
|
||||||
|
// Keep the line above commented to avoid people get unintended admin privileges.
|
||||||
|
// Uncomment it only once for you to get admin privileges and create new users.
|
||||||
|
//authentication.HandleFunc("/admin/register", a.registerAdminUser).Methods("POST")
|
||||||
iot := r.PathPrefix("/api/device").Subrouter()
|
iot := r.PathPrefix("/api/device").Subrouter()
|
||||||
iot.HandleFunc("", a.retrieveDevices).Methods("GET")
|
iot.HandleFunc("", a.retrieveDevices).Methods("GET")
|
||||||
iot.HandleFunc("/{sn}/get", a.deviceGetMsg).Methods("PUT")
|
iot.HandleFunc("/{sn}/get", a.deviceGetMsg).Methods("PUT")
|
||||||
iot.HandleFunc("/{sn}/add", a.deviceCreateMsg).Methods("PUT")
|
iot.HandleFunc("/{sn}/add", a.deviceCreateMsg).Methods("PUT")
|
||||||
iot.HandleFunc("/{sn}/del", a.deviceDeleteMsg).Methods("PUT")
|
iot.HandleFunc("/{sn}/del", a.deviceDeleteMsg).Methods("PUT")
|
||||||
iot.HandleFunc("/{sn}/set", a.deviceUpdateMsg).Methods("PUT")
|
iot.HandleFunc("/{sn}/set", a.deviceUpdateMsg).Methods("PUT")
|
||||||
//TODO: Create operation action handler
|
iot.HandleFunc("/{sn}/parameters", a.deviceGetSupportedParametersMsg).Methods("PUT")
|
||||||
iot.HandleFunc("/device/{sn}/act", a.deviceUpdateMsg).Methods("PUT")
|
iot.HandleFunc("/{sn}/instances", a.deviceGetParameterInstances).Methods("PUT")
|
||||||
|
iot.HandleFunc("/{sn}/update", a.deviceFwUpdate).Methods("PUT")
|
||||||
|
|
||||||
// Middleware for requests which requires user to be authenticated
|
// Middleware for requests which requires user to be authenticated
|
||||||
iot.Use(func(handler http.Handler) http.Handler {
|
iot.Use(func(handler http.Handler) http.Handler {
|
||||||
return middleware.Middleware(handler)
|
return middleware.Middleware(handler)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
users := r.PathPrefix("/api/users").Subrouter()
|
||||||
|
users.HandleFunc("", a.retrieveUsers).Methods("GET")
|
||||||
|
|
||||||
|
users.Use(func(handler http.Handler) http.Handler {
|
||||||
|
return middleware.Middleware(handler)
|
||||||
|
})
|
||||||
|
|
||||||
// Verifies CORS configs for requests
|
// Verifies CORS configs for requests
|
||||||
corsOpts := cors.GetCorsConfig()
|
corsOpts := cors.GetCorsConfig()
|
||||||
|
|
||||||
|
|
@ -91,6 +111,225 @@ func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Api) retrieveUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
|
users, err := a.Db.FindAllUsers()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, x := range users {
|
||||||
|
delete(x, "password")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.NewEncoder(w).Encode(users)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check which fw image is activated
|
||||||
|
func checkAvaiableFwPartition(reqPathResult []*usp_msg.GetResp_RequestedPathResult) int {
|
||||||
|
for _, x := range reqPathResult {
|
||||||
|
partitionsNumber := len(x.ResolvedPathResults)
|
||||||
|
if partitionsNumber > 1 {
|
||||||
|
log.Printf("Device has %d firmware partitions", partitionsNumber)
|
||||||
|
}
|
||||||
|
for i, y := range x.ResolvedPathResults {
|
||||||
|
if y.ResultParams["Status"] == "Available" {
|
||||||
|
log.Printf("Partition %d is avaiable", i)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Api) deviceFwUpdate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
sn := vars["sn"]
|
||||||
|
a.deviceExists(sn, w)
|
||||||
|
|
||||||
|
msg := utils.NewGetMsg(usp_msg.Get{
|
||||||
|
ParamPaths: []string{"Device.DeviceInfo.FirmwareImage.*.Status"},
|
||||||
|
MaxDepth: 1,
|
||||||
|
})
|
||||||
|
encodedMsg, err := proto.Marshal(&msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
record := utils.NewUspRecord(encodedMsg, sn)
|
||||||
|
tr369Message, err := proto.Marshal(&record)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to encode tr369 record:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg)
|
||||||
|
a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn)
|
||||||
|
|
||||||
|
var getMsgAnswer *usp_msg.GetResp
|
||||||
|
|
||||||
|
select {
|
||||||
|
case msg := <-a.MsgQueue[msg.Header.MsgId]:
|
||||||
|
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
||||||
|
getMsgAnswer = msg.Body.GetResponse().GetGetResp()
|
||||||
|
case <-time.After(time.Second * 30):
|
||||||
|
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
||||||
|
w.WriteHeader(http.StatusGatewayTimeout)
|
||||||
|
json.NewEncoder(w).Encode("Request Timed Out")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check which fw image is activated
|
||||||
|
partition := checkAvaiableFwPartition(getMsgAnswer.ReqPathResults)
|
||||||
|
if partition < 0 {
|
||||||
|
log.Println("Error to get device available firmware partition, probably it has only one partition")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
json.NewEncoder(w).Encode("Server don't have the hability to update device with only one partition")
|
||||||
|
return
|
||||||
|
//TODO: update device with only one partition
|
||||||
|
}
|
||||||
|
|
||||||
|
var receiver = usp_msg.Operate{
|
||||||
|
Command: "Device.DeviceInfo.FirmwareImage.1.Download()",
|
||||||
|
CommandKey: "Download()",
|
||||||
|
SendResp: true,
|
||||||
|
InputArgs: map[string]string{
|
||||||
|
"URL": "http://cronos.intelbras.com.br/download/PON/121AC/beta/121AC-2.3-230620-77753201df4f1e2c607a7236746c8491.tar", //TODO: use dynamic url
|
||||||
|
"AutoActivate": "true",
|
||||||
|
//"Username": "",
|
||||||
|
//"Password": "",
|
||||||
|
"FileSize": "0", //TODO: send firmware length
|
||||||
|
//"CheckSumAlgorithm": "",
|
||||||
|
//"CheckSum": "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = utils.NewOperateMsg(receiver)
|
||||||
|
encodedMsg, err = proto.Marshal(&msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
record = utils.NewUspRecord(encodedMsg, sn)
|
||||||
|
tr369Message, err = proto.Marshal(&record)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to encode tr369 record:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg)
|
||||||
|
a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case msg := <-a.MsgQueue[msg.Header.MsgId]:
|
||||||
|
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
||||||
|
json.NewEncoder(w).Encode(msg.Body.GetResponse().GetSetResp())
|
||||||
|
return
|
||||||
|
case <-time.After(time.Second * 28):
|
||||||
|
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
||||||
|
w.WriteHeader(http.StatusGatewayTimeout)
|
||||||
|
json.NewEncoder(w).Encode("Request Timed Out")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Api) deviceGetParameterInstances(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
sn := vars["sn"]
|
||||||
|
a.deviceExists(sn, w)
|
||||||
|
|
||||||
|
var receiver usp_msg.GetInstances
|
||||||
|
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&receiver)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := utils.NewGetParametersInstancesMsg(receiver)
|
||||||
|
encodedMsg, err := proto.Marshal(&msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
record := utils.NewUspRecord(encodedMsg, sn)
|
||||||
|
tr369Message, err := proto.Marshal(&record)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to encode tr369 record:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn)
|
||||||
|
a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg)
|
||||||
|
a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case msg := <-a.MsgQueue[msg.Header.MsgId]:
|
||||||
|
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
||||||
|
json.NewEncoder(w).Encode(msg.Body.GetResponse().GetGetInstancesResp())
|
||||||
|
return
|
||||||
|
case <-time.After(time.Second * 28):
|
||||||
|
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
||||||
|
w.WriteHeader(http.StatusGatewayTimeout)
|
||||||
|
json.NewEncoder(w).Encode("Request Timed Out")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Api) deviceGetSupportedParametersMsg(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
sn := vars["sn"]
|
||||||
|
a.deviceExists(sn, w)
|
||||||
|
|
||||||
|
var receiver usp_msg.GetSupportedDM
|
||||||
|
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&receiver)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := utils.NewGetSupportedParametersMsg(receiver)
|
||||||
|
encodedMsg, err := proto.Marshal(&msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
record := utils.NewUspRecord(encodedMsg, sn)
|
||||||
|
tr369Message, err := proto.Marshal(&record)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to encode tr369 record:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//a.Broker.Request(tr369Message, usp_msg.Header_GET, "oktopus/v1/agent/"+sn, "oktopus/v1/get/"+sn)
|
||||||
|
a.MsgQueue[msg.Header.MsgId] = make(chan usp_msg.Msg)
|
||||||
|
a.Broker.Publish(tr369Message, "oktopus/v1/agent/"+sn, "oktopus/v1/api/"+sn)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case msg := <-a.MsgQueue[msg.Header.MsgId]:
|
||||||
|
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
||||||
|
json.NewEncoder(w).Encode(msg.Body.GetResponse().GetGetSupportedDmResp())
|
||||||
|
return
|
||||||
|
case <-time.After(time.Second * 28):
|
||||||
|
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
||||||
|
w.WriteHeader(http.StatusGatewayTimeout)
|
||||||
|
json.NewEncoder(w).Encode("Request Timed Out")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Api) deviceCreateMsg(w http.ResponseWriter, r *http.Request) {
|
func (a *Api) deviceCreateMsg(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
sn := vars["sn"]
|
sn := vars["sn"]
|
||||||
|
|
@ -128,7 +367,7 @@ func (a *Api) deviceCreateMsg(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
||||||
json.NewEncoder(w).Encode(msg.Body.GetResponse().GetAddResp())
|
json.NewEncoder(w).Encode(msg.Body.GetResponse().GetAddResp())
|
||||||
return
|
return
|
||||||
case <-time.After(time.Second * 5):
|
case <-time.After(time.Second * 28):
|
||||||
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
||||||
w.WriteHeader(http.StatusGatewayTimeout)
|
w.WriteHeader(http.StatusGatewayTimeout)
|
||||||
json.NewEncoder(w).Encode("Request Timed Out")
|
json.NewEncoder(w).Encode("Request Timed Out")
|
||||||
|
|
@ -173,7 +412,7 @@ func (a *Api) deviceGetMsg(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
||||||
json.NewEncoder(w).Encode(msg.Body.GetResponse().GetGetResp())
|
json.NewEncoder(w).Encode(msg.Body.GetResponse().GetGetResp())
|
||||||
return
|
return
|
||||||
case <-time.After(time.Second * 5):
|
case <-time.After(time.Second * 30):
|
||||||
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
||||||
w.WriteHeader(http.StatusGatewayTimeout)
|
w.WriteHeader(http.StatusGatewayTimeout)
|
||||||
json.NewEncoder(w).Encode("Request Timed Out")
|
json.NewEncoder(w).Encode("Request Timed Out")
|
||||||
|
|
@ -218,7 +457,7 @@ func (a *Api) deviceDeleteMsg(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
||||||
json.NewEncoder(w).Encode(msg.Body.GetResponse().GetDeleteResp())
|
json.NewEncoder(w).Encode(msg.Body.GetResponse().GetDeleteResp())
|
||||||
return
|
return
|
||||||
case <-time.After(time.Second * 5):
|
case <-time.After(time.Second * 28):
|
||||||
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
||||||
w.WriteHeader(http.StatusGatewayTimeout)
|
w.WriteHeader(http.StatusGatewayTimeout)
|
||||||
json.NewEncoder(w).Encode("Request Timed Out")
|
json.NewEncoder(w).Encode("Request Timed Out")
|
||||||
|
|
@ -263,7 +502,7 @@ func (a *Api) deviceUpdateMsg(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
log.Printf("Received Msg: %s", msg.Header.MsgId)
|
||||||
json.NewEncoder(w).Encode(msg.Body.GetResponse().GetSetResp())
|
json.NewEncoder(w).Encode(msg.Body.GetResponse().GetSetResp())
|
||||||
return
|
return
|
||||||
case <-time.After(time.Second * 5):
|
case <-time.After(time.Second * 28):
|
||||||
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
log.Printf("Request %s Timed Out", msg.Header.MsgId)
|
||||||
w.WriteHeader(http.StatusGatewayTimeout)
|
w.WriteHeader(http.StatusGatewayTimeout)
|
||||||
json.NewEncoder(w).Encode("Request Timed Out")
|
json.NewEncoder(w).Encode("Request Timed Out")
|
||||||
|
|
@ -285,6 +524,47 @@ func (a *Api) deviceExists(sn string, w http.ResponseWriter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Api) registerUser(w http.ResponseWriter, r *http.Request) {
|
func (a *Api) registerUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
tokenString := r.Header.Get("Authorization")
|
||||||
|
if tokenString == "" {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
email, err := auth.ValidateToken(tokenString)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if user which is requesting creation has the necessary privileges
|
||||||
|
rUser, err := a.Db.FindUser(email)
|
||||||
|
if rUser.Level != AdminUser {
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var user db.User
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&user)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Level = NormalUser
|
||||||
|
|
||||||
|
if err := user.HashPassword(user.Password); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.Db.RegisterUser(user); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Api) registerAdminUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
var user db.User
|
var user db.User
|
||||||
err := json.NewDecoder(r.Body).Decode(&user)
|
err := json.NewDecoder(r.Body).Decode(&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -292,6 +572,8 @@ func (a *Api) registerUser(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user.Level = AdminUser
|
||||||
|
|
||||||
if err := user.HashPassword(user.Password); err != nil {
|
if err := user.HashPassword(user.Password); err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ import (
|
||||||
type User struct {
|
type User struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password,omitempty"`
|
||||||
|
Level int `json:"level"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Database) RegisterUser(user User) error {
|
func (d *Database) RegisterUser(user User) error {
|
||||||
|
|
@ -25,6 +26,18 @@ func (d *Database) RegisterUser(user User) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Database) FindAllUsers() ([]map[string]interface{}, error) {
|
||||||
|
var result []map[string]interface{}
|
||||||
|
cursor, err := d.users.Find(d.ctx, bson.D{{}})
|
||||||
|
if err != nil {
|
||||||
|
return []map[string]interface{}{}, err
|
||||||
|
}
|
||||||
|
if err = cursor.All(d.ctx, &result); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Database) FindUser(email string) (User, error) {
|
func (d *Database) FindUser(email string) (User, error) {
|
||||||
var result User
|
var result User
|
||||||
err := d.users.FindOne(d.ctx, bson.D{{"email", email}}).Decode(&result)
|
err := d.users.FindOne(d.ctx, bson.D{{"email", email}}).Decode(&result)
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,12 @@ func (m *Mqtt) startClient(devices, controller, disconnect, apiMsg chan *paho.Pu
|
||||||
clientConfig := paho.ClientConfig{
|
clientConfig := paho.ClientConfig{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
Router: singleHandler,
|
Router: singleHandler,
|
||||||
|
OnServerDisconnect: func(disconnect *paho.Disconnect) {
|
||||||
|
log.Println("disconnected from mqtt server, reason code: ", disconnect.ReasonCode)
|
||||||
|
},
|
||||||
|
OnClientError: func(err error) {
|
||||||
|
log.Println(err)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return paho.NewClient(clientConfig)
|
return paho.NewClient(clientConfig)
|
||||||
|
|
|
||||||
|
|
@ -115,3 +115,57 @@ func NewSetMsg(updateStuff usp_msg.Set) usp_msg.Msg {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewGetSupportedParametersMsg(getStuff usp_msg.GetSupportedDM) usp_msg.Msg {
|
||||||
|
return usp_msg.Msg{
|
||||||
|
Header: &usp_msg.Header{
|
||||||
|
MsgId: uuid.NewString(),
|
||||||
|
MsgType: usp_msg.Header_GET_SUPPORTED_DM,
|
||||||
|
},
|
||||||
|
Body: &usp_msg.Body{
|
||||||
|
MsgBody: &usp_msg.Body_Request{
|
||||||
|
Request: &usp_msg.Request{
|
||||||
|
ReqType: &usp_msg.Request_GetSupportedDm{
|
||||||
|
GetSupportedDm: &getStuff,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGetParametersInstancesMsg(getStuff usp_msg.GetInstances) usp_msg.Msg {
|
||||||
|
return usp_msg.Msg{
|
||||||
|
Header: &usp_msg.Header{
|
||||||
|
MsgId: uuid.NewString(),
|
||||||
|
MsgType: usp_msg.Header_GET_INSTANCES,
|
||||||
|
},
|
||||||
|
Body: &usp_msg.Body{
|
||||||
|
MsgBody: &usp_msg.Body_Request{
|
||||||
|
Request: &usp_msg.Request{
|
||||||
|
ReqType: &usp_msg.Request_GetInstances{
|
||||||
|
GetInstances: &getStuff,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOperateMsg(getStuff usp_msg.Operate) usp_msg.Msg {
|
||||||
|
return usp_msg.Msg{
|
||||||
|
Header: &usp_msg.Header{
|
||||||
|
MsgId: uuid.NewString(),
|
||||||
|
MsgType: usp_msg.Header_OPERATE,
|
||||||
|
},
|
||||||
|
Body: &usp_msg.Body{
|
||||||
|
MsgBody: &usp_msg.Body_Request{
|
||||||
|
Request: &usp_msg.Request{
|
||||||
|
ReqType: &usp_msg.Request_Operate{
|
||||||
|
Operate: &getStuff,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
86
backend/services/controller/internal/ws/ws.go
Normal file
86
backend/services/controller/internal/ws/ws.go
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
package ws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
socketio "github.com/googollee/go-socket.io"
|
||||||
|
"github.com/googollee/go-socket.io/engineio"
|
||||||
|
"github.com/googollee/go-socket.io/engineio/transport"
|
||||||
|
"github.com/googollee/go-socket.io/engineio/transport/polling"
|
||||||
|
"github.com/googollee/go-socket.io/engineio/transport/websocket"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/leandrofars/oktopus/internal/api/cors"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Ws() {
|
||||||
|
server := socketio.NewServer(&engineio.Options{
|
||||||
|
PingTimeout: 5 * time.Second,
|
||||||
|
PingInterval: 10 * time.Second,
|
||||||
|
Transports: []transport.Transport{
|
||||||
|
&polling.Transport{
|
||||||
|
Client: &http.Client{
|
||||||
|
Timeout: time.Minute,
|
||||||
|
},
|
||||||
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&websocket.Transport{
|
||||||
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
server.OnConnect("/", func(s socketio.Conn) error {
|
||||||
|
s.SetContext("")
|
||||||
|
fmt.Println("connected:", s.ID())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
server.OnEvent("/", "offer", func(s socketio.Conn, msg string) string {
|
||||||
|
log.Printf("offer: %s", msg)
|
||||||
|
|
||||||
|
return "test"
|
||||||
|
})
|
||||||
|
|
||||||
|
server.OnError("/", func(s socketio.Conn, e error) {
|
||||||
|
fmt.Println("error:", e)
|
||||||
|
})
|
||||||
|
|
||||||
|
server.OnDisconnect("/", func(s socketio.Conn, reason string) {
|
||||||
|
fmt.Println("closed", reason)
|
||||||
|
})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := server.Serve(); err != nil {
|
||||||
|
log.Fatalf("socketio listen error: %s\n", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
const wsPort = "5000"
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
r.Handle("/socket.io/", server)
|
||||||
|
|
||||||
|
//r.Use(func(handler http.Handler) http.Handler {
|
||||||
|
// return middleware.Middleware(handler)
|
||||||
|
//})
|
||||||
|
|
||||||
|
corsOpts := cors.GetCorsConfig()
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: "0.0.0.0:" + wsPort,
|
||||||
|
Handler: corsOpts.Handler(r), // Pass our instance of gorilla/mux in.
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := srv.ListenAndServe(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
log.Printf("Running websocket at port %s", wsPort)
|
||||||
|
}
|
||||||
1
backend/services/socketio/.gitignore
vendored
Normal file
1
backend/services/socketio/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
node_modules/
|
||||||
783
backend/services/socketio/package-lock.json
generated
Normal file
783
backend/services/socketio/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,783 @@
|
||||||
|
{
|
||||||
|
"name": "socketio",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@koa/cors": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@koa/cors/-/cors-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-Y4RrbvGTlAaa04DBoPBWJqDR5gPj32OOz827ULXfgB1F7piD1MB/zwn8JR2LAnvdILhxUbXbkXGWuNVsFuVFCQ==",
|
||||||
|
"requires": {
|
||||||
|
"vary": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@socket.io/component-emitter": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||||
|
},
|
||||||
|
"@types/cookie": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
|
||||||
|
},
|
||||||
|
"@types/cors": {
|
||||||
|
"version": "2.8.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
|
||||||
|
"integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "20.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
|
||||||
|
"integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg=="
|
||||||
|
},
|
||||||
|
"accepts": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||||
|
"requires": {
|
||||||
|
"mime-types": "~2.1.34",
|
||||||
|
"negotiator": "0.6.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"array-flatten": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||||
|
},
|
||||||
|
"base64id": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
|
||||||
|
},
|
||||||
|
"body-parser": {
|
||||||
|
"version": "1.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||||
|
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.1.2",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "2.0.0",
|
||||||
|
"destroy": "1.2.0",
|
||||||
|
"http-errors": "2.0.0",
|
||||||
|
"iconv-lite": "0.4.24",
|
||||||
|
"on-finished": "2.4.1",
|
||||||
|
"qs": "6.11.0",
|
||||||
|
"raw-body": "2.5.1",
|
||||||
|
"type-is": "~1.6.18",
|
||||||
|
"unpipe": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bytes": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
|
||||||
|
},
|
||||||
|
"cache-content-type": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==",
|
||||||
|
"requires": {
|
||||||
|
"mime-types": "^2.1.18",
|
||||||
|
"ylru": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"call-bind": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||||
|
"requires": {
|
||||||
|
"function-bind": "^1.1.1",
|
||||||
|
"get-intrinsic": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"co": {
|
||||||
|
"version": "4.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||||
|
"integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ=="
|
||||||
|
},
|
||||||
|
"content-disposition": {
|
||||||
|
"version": "0.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
|
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content-type": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
|
||||||
|
},
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
|
||||||
|
},
|
||||||
|
"cookie-signature": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||||
|
},
|
||||||
|
"cookies": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~2.0.0",
|
||||||
|
"keygrip": "~1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cors": {
|
||||||
|
"version": "2.8.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||||
|
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||||
|
"requires": {
|
||||||
|
"object-assign": "^4",
|
||||||
|
"vary": "^1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deep-equal": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw=="
|
||||||
|
},
|
||||||
|
"delegates": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
|
||||||
|
},
|
||||||
|
"depd": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
|
||||||
|
},
|
||||||
|
"destroy": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
|
||||||
|
},
|
||||||
|
"ee-first": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||||
|
},
|
||||||
|
"encodeurl": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
|
||||||
|
},
|
||||||
|
"engine.io": {
|
||||||
|
"version": "6.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz",
|
||||||
|
"integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==",
|
||||||
|
"requires": {
|
||||||
|
"@types/cookie": "^0.4.1",
|
||||||
|
"@types/cors": "^2.8.12",
|
||||||
|
"@types/node": ">=10.0.0",
|
||||||
|
"accepts": "~1.3.4",
|
||||||
|
"base64id": "2.0.0",
|
||||||
|
"cookie": "~0.4.1",
|
||||||
|
"cors": "~2.8.5",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.0.3",
|
||||||
|
"ws": "~8.11.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cookie": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
|
||||||
|
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "4.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"engine.io-parser": {
|
||||||
|
"version": "5.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.7.tgz",
|
||||||
|
"integrity": "sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ=="
|
||||||
|
},
|
||||||
|
"escape-html": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||||
|
},
|
||||||
|
"etag": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
|
||||||
|
},
|
||||||
|
"express": {
|
||||||
|
"version": "4.18.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||||
|
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||||
|
"requires": {
|
||||||
|
"accepts": "~1.3.8",
|
||||||
|
"array-flatten": "1.1.1",
|
||||||
|
"body-parser": "1.20.1",
|
||||||
|
"content-disposition": "0.5.4",
|
||||||
|
"content-type": "~1.0.4",
|
||||||
|
"cookie": "0.5.0",
|
||||||
|
"cookie-signature": "1.0.6",
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "2.0.0",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"finalhandler": "1.2.0",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"http-errors": "2.0.0",
|
||||||
|
"merge-descriptors": "1.0.1",
|
||||||
|
"methods": "~1.1.2",
|
||||||
|
"on-finished": "2.4.1",
|
||||||
|
"parseurl": "~1.3.3",
|
||||||
|
"path-to-regexp": "0.1.7",
|
||||||
|
"proxy-addr": "~2.0.7",
|
||||||
|
"qs": "6.11.0",
|
||||||
|
"range-parser": "~1.2.1",
|
||||||
|
"safe-buffer": "5.2.1",
|
||||||
|
"send": "0.18.0",
|
||||||
|
"serve-static": "1.15.0",
|
||||||
|
"setprototypeof": "1.2.0",
|
||||||
|
"statuses": "2.0.1",
|
||||||
|
"type-is": "~1.6.18",
|
||||||
|
"utils-merge": "1.0.1",
|
||||||
|
"vary": "~1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"finalhandler": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"on-finished": "2.4.1",
|
||||||
|
"parseurl": "~1.3.3",
|
||||||
|
"statuses": "2.0.1",
|
||||||
|
"unpipe": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forwarded": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
|
||||||
|
},
|
||||||
|
"fresh": {
|
||||||
|
"version": "0.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||||
|
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
|
||||||
|
},
|
||||||
|
"function-bind": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||||
|
},
|
||||||
|
"get-intrinsic": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
|
||||||
|
"requires": {
|
||||||
|
"function-bind": "^1.1.1",
|
||||||
|
"has": "^1.0.3",
|
||||||
|
"has-proto": "^1.0.1",
|
||||||
|
"has-symbols": "^1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
|
"requires": {
|
||||||
|
"function-bind": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has-proto": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="
|
||||||
|
},
|
||||||
|
"has-symbols": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
|
||||||
|
},
|
||||||
|
"has-tostringtag": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
|
||||||
|
"requires": {
|
||||||
|
"has-symbols": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"http-assert": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==",
|
||||||
|
"requires": {
|
||||||
|
"deep-equal": "~1.0.1",
|
||||||
|
"http-errors": "~1.8.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"depd": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="
|
||||||
|
},
|
||||||
|
"http-errors": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.4",
|
||||||
|
"setprototypeof": "1.2.0",
|
||||||
|
"statuses": ">= 1.5.0 < 2",
|
||||||
|
"toidentifier": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"http-errors": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "2.0.0",
|
||||||
|
"inherits": "2.0.4",
|
||||||
|
"setprototypeof": "1.2.0",
|
||||||
|
"statuses": "2.0.1",
|
||||||
|
"toidentifier": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iconv-lite": {
|
||||||
|
"version": "0.4.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"ipaddr.js": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
||||||
|
},
|
||||||
|
"is-generator-function": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
|
||||||
|
"requires": {
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keygrip": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==",
|
||||||
|
"requires": {
|
||||||
|
"tsscmp": "1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"koa": {
|
||||||
|
"version": "2.14.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/koa/-/koa-2.14.2.tgz",
|
||||||
|
"integrity": "sha512-VFI2bpJaodz6P7x2uyLiX6RLYpZmOJqNmoCst/Yyd7hQlszyPwG/I9CQJ63nOtKSxpt5M7NH67V6nJL2BwCl7g==",
|
||||||
|
"requires": {
|
||||||
|
"accepts": "^1.3.5",
|
||||||
|
"cache-content-type": "^1.0.0",
|
||||||
|
"content-disposition": "~0.5.2",
|
||||||
|
"content-type": "^1.0.4",
|
||||||
|
"cookies": "~0.8.0",
|
||||||
|
"debug": "^4.3.2",
|
||||||
|
"delegates": "^1.0.0",
|
||||||
|
"depd": "^2.0.0",
|
||||||
|
"destroy": "^1.0.4",
|
||||||
|
"encodeurl": "^1.0.2",
|
||||||
|
"escape-html": "^1.0.3",
|
||||||
|
"fresh": "~0.5.2",
|
||||||
|
"http-assert": "^1.3.0",
|
||||||
|
"http-errors": "^1.6.3",
|
||||||
|
"is-generator-function": "^1.0.7",
|
||||||
|
"koa-compose": "^4.1.0",
|
||||||
|
"koa-convert": "^2.0.0",
|
||||||
|
"on-finished": "^2.3.0",
|
||||||
|
"only": "~0.0.2",
|
||||||
|
"parseurl": "^1.3.2",
|
||||||
|
"statuses": "^1.5.0",
|
||||||
|
"type-is": "^1.6.16",
|
||||||
|
"vary": "^1.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "4.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"http-errors": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
|
||||||
|
"requires": {
|
||||||
|
"depd": "~1.1.2",
|
||||||
|
"inherits": "2.0.4",
|
||||||
|
"setprototypeof": "1.2.0",
|
||||||
|
"statuses": ">= 1.5.0 < 2",
|
||||||
|
"toidentifier": "1.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"depd": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"koa-compose": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw=="
|
||||||
|
},
|
||||||
|
"koa-convert": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==",
|
||||||
|
"requires": {
|
||||||
|
"co": "^4.6.0",
|
||||||
|
"koa-compose": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"media-typer": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
|
||||||
|
},
|
||||||
|
"merge-descriptors": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
|
||||||
|
},
|
||||||
|
"mime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||||
|
},
|
||||||
|
"mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
|
||||||
|
},
|
||||||
|
"mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"requires": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||||
|
},
|
||||||
|
"negotiator": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
|
||||||
|
},
|
||||||
|
"object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
|
||||||
|
},
|
||||||
|
"object-inspect": {
|
||||||
|
"version": "1.12.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||||
|
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g=="
|
||||||
|
},
|
||||||
|
"on-finished": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||||
|
"requires": {
|
||||||
|
"ee-first": "1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"only": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz",
|
||||||
|
"integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ=="
|
||||||
|
},
|
||||||
|
"parseurl": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
||||||
|
},
|
||||||
|
"path-to-regexp": {
|
||||||
|
"version": "0.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||||
|
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||||
|
},
|
||||||
|
"proxy-addr": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||||
|
"requires": {
|
||||||
|
"forwarded": "0.2.0",
|
||||||
|
"ipaddr.js": "1.9.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||||
|
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||||
|
"requires": {
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"range-parser": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
||||||
|
},
|
||||||
|
"raw-body": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||||
|
"requires": {
|
||||||
|
"bytes": "3.1.2",
|
||||||
|
"http-errors": "2.0.0",
|
||||||
|
"iconv-lite": "0.4.24",
|
||||||
|
"unpipe": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||||
|
},
|
||||||
|
"safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
|
},
|
||||||
|
"send": {
|
||||||
|
"version": "0.18.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
||||||
|
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "2.6.9",
|
||||||
|
"depd": "2.0.0",
|
||||||
|
"destroy": "1.2.0",
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"etag": "~1.8.1",
|
||||||
|
"fresh": "0.5.2",
|
||||||
|
"http-errors": "2.0.0",
|
||||||
|
"mime": "1.6.0",
|
||||||
|
"ms": "2.1.3",
|
||||||
|
"on-finished": "2.4.1",
|
||||||
|
"range-parser": "~1.2.1",
|
||||||
|
"statuses": "2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serve-static": {
|
||||||
|
"version": "1.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
||||||
|
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
|
||||||
|
"requires": {
|
||||||
|
"encodeurl": "~1.0.2",
|
||||||
|
"escape-html": "~1.0.3",
|
||||||
|
"parseurl": "~1.3.3",
|
||||||
|
"send": "0.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"setprototypeof": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||||
|
},
|
||||||
|
"side-channel": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||||
|
"requires": {
|
||||||
|
"call-bind": "^1.0.0",
|
||||||
|
"get-intrinsic": "^1.0.2",
|
||||||
|
"object-inspect": "^1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"socket.io": {
|
||||||
|
"version": "4.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.2.tgz",
|
||||||
|
"integrity": "sha512-Vp+lSks5k0dewYTfwgPT9UeGGd+ht7sCpB7p0e83VgO4X/AHYWhXITMrNk/pg8syY2bpx23ptClCQuHhqi2BgQ==",
|
||||||
|
"requires": {
|
||||||
|
"accepts": "~1.3.4",
|
||||||
|
"base64id": "~2.0.0",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io": "~6.4.2",
|
||||||
|
"socket.io-adapter": "~2.5.2",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "4.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"socket.io-adapter": {
|
||||||
|
"version": "2.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
|
||||||
|
"integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
|
||||||
|
"requires": {
|
||||||
|
"ws": "~8.11.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"socket.io-parser": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
|
"requires": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "4.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
|
||||||
|
},
|
||||||
|
"toidentifier": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
|
||||||
|
},
|
||||||
|
"tsscmp": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="
|
||||||
|
},
|
||||||
|
"type-is": {
|
||||||
|
"version": "1.6.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"requires": {
|
||||||
|
"media-typer": "0.3.0",
|
||||||
|
"mime-types": "~2.1.24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unpipe": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
|
||||||
|
},
|
||||||
|
"utils-merge": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
|
||||||
|
},
|
||||||
|
"vary": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "8.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||||
|
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg=="
|
||||||
|
},
|
||||||
|
"ylru": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ylru/-/ylru-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
backend/services/socketio/package.json
Normal file
20
backend/services/socketio/package.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "socketio",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "node server.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@koa/cors": "^4.0.0",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"koa": "^2.14.2",
|
||||||
|
"socket.io": "^4.6.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
67
backend/services/socketio/server.js
Normal file
67
backend/services/socketio/server.js
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
const PORT = 5000;
|
||||||
|
|
||||||
|
const http = require('http').Server(app);
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
|
const io = require('socket.io')(http, {
|
||||||
|
cors: {
|
||||||
|
origin: "http://localhost:3000"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
let users = []
|
||||||
|
|
||||||
|
io.on('connection', (socket) => {
|
||||||
|
console.log(`🚀: ${socket.id} user just connected!`);
|
||||||
|
|
||||||
|
socket.on("callUser", ({ userToCall, signalData, from }) => {
|
||||||
|
console.log("user to call:",userToCall)
|
||||||
|
let index = users.findIndex(x =>{
|
||||||
|
return x.name === userToCall
|
||||||
|
})
|
||||||
|
console.log(index)
|
||||||
|
if (index >= 0){
|
||||||
|
console.log("calling user named "+ users[index].name+" and id "+users[index].id)
|
||||||
|
io.to(users[index].id).emit("callUser", { signal: signalData, from });
|
||||||
|
}else{
|
||||||
|
console.log("There is no user named "+userToCall+" or he/she is offline")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("answerCall", (data) => {
|
||||||
|
io.to(data.to).emit("callAccepted", data.signal);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("newuser", (data) => {
|
||||||
|
let index = users.findIndex(x =>{ x.name === data.name})
|
||||||
|
if (index >=0){
|
||||||
|
console.log("user already exists, but got connected with other id")
|
||||||
|
}else{
|
||||||
|
users.push(data)
|
||||||
|
}
|
||||||
|
console.log(data)
|
||||||
|
console.log("total users: ", users)
|
||||||
|
io.emit('users', users)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
console.log('🔥: A user disconnected');
|
||||||
|
let index = users.findIndex(x => x.id === socket.id)
|
||||||
|
if (index >= 0){
|
||||||
|
let deletedEl = users.splice(index, 1)
|
||||||
|
console.log("users deleted", deletedEl)
|
||||||
|
console.log("users after disconection: ", users)
|
||||||
|
io.emit('users', users)
|
||||||
|
}else{
|
||||||
|
console.log("couldn't find user with socket id:", socket.id)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
http.listen(PORT, () => {
|
||||||
|
console.log(`Server listening on ${PORT}`);
|
||||||
|
});
|
||||||
|
|
@ -90,6 +90,19 @@ http {
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_cache_bypass $http_upgrade;
|
proxy_cache_bypass $http_upgrade;
|
||||||
}
|
}
|
||||||
|
location /socket.io {
|
||||||
|
proxy_pass http://127.0.0.1:5000;
|
||||||
|
proxy_read_timeout 60;
|
||||||
|
proxy_connect_timeout 60;
|
||||||
|
proxy_redirect off;
|
||||||
|
|
||||||
|
# Allow the use of websockets
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
|
||||||
error_page 500 502 503 504 /50x.html;
|
error_page 500 502 503 504 /50x.html;
|
||||||
location = /50x.html {
|
location = /50x.html {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# ----------------------------- Local Environment ---------------------------- #
|
# ----------------------------- Local Environment ---------------------------- #
|
||||||
|
|
||||||
NEXT_PUBLIC_REST_ENPOINT="http://localhost:8000"
|
NEXT_PUBLIC_REST_ENPOINT="http://localhost:8000/api"
|
||||||
|
NEXT_PUBLIC_WS_ENPOINT="http://localhost:5000/"
|
||||||
# ---------------------------------------------------------------------------- #
|
# ---------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
2477
frontend/package-lock.json
generated
2477
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
|
@ -32,7 +32,10 @@
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-apexcharts": "1.4.0",
|
"react-apexcharts": "1.4.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"simple-peer": "^9.11.1",
|
||||||
"simplebar-react": "^3.2.1",
|
"simplebar-react": "^3.2.1",
|
||||||
|
"socket.io-client": "^4.6.2",
|
||||||
|
"styled-components": "^6.0.0-rc.3",
|
||||||
"yup": "1.0.0"
|
"yup": "1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"short_name": "Devias Kit",
|
"short_name": "Oktopus TR-369",
|
||||||
"name": "Devias Kit",
|
"name": "Oktopus",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "favicon.ico",
|
"src": "favicon.ico",
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,7 @@ export const AuthProvider = (props) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
window.sessionStorage.setItem('authenticated', 'true');
|
window.sessionStorage.setItem('authenticated', 'true');
|
||||||
|
window.sessionStorage.setItem('email',email)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +164,7 @@ export const AuthProvider = (props) => {
|
||||||
id: '5e86809283e28b96d2d38537',
|
id: '5e86809283e28b96d2d38537',
|
||||||
avatar: '/assets/avatars/default-avatar.png',
|
avatar: '/assets/avatars/default-avatar.png',
|
||||||
name: 'Oktopus',
|
name: 'Oktopus',
|
||||||
email: 'anika.visser@devias.io',
|
email: email,
|
||||||
token: token
|
token: token
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
162
frontend/src/contexts/socketio-context.js
Normal file
162
frontend/src/contexts/socketio-context.js
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
import { createContext, useContext, useEffect, useState, useRef } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import io from 'socket.io-client';
|
||||||
|
import { useAuth } from 'src/hooks/use-auth';
|
||||||
|
import Peer from "simple-peer";
|
||||||
|
|
||||||
|
|
||||||
|
// The role of this context is to propagate socketio io state through app tree
|
||||||
|
export const WsContext = createContext({ undefined });
|
||||||
|
|
||||||
|
export const WsProvider = (props) => {
|
||||||
|
const { children } = props;
|
||||||
|
const [users, setUsers] = useState(null)
|
||||||
|
const [callAccepted, setCallAccepted] = useState(false);
|
||||||
|
const [callEnded, setCallEnded] = useState(false);
|
||||||
|
const [stream, setStream] = useState();
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [call, setCall] = useState({});
|
||||||
|
|
||||||
|
const myVideo = useRef();
|
||||||
|
const userVideo = useRef();
|
||||||
|
const connectionRef = useRef();
|
||||||
|
const auth = useAuth()
|
||||||
|
const socket = io(process.env.NEXT_PUBLIC_WS_ENPOINT)
|
||||||
|
|
||||||
|
const initialize = async () => {
|
||||||
|
// Prevent from calling twice in development mode with React.StrictMode enable
|
||||||
|
|
||||||
|
socket.on('connect', () => {
|
||||||
|
console.log('[IO] Connect => A new connection has been established')
|
||||||
|
|
||||||
|
socket.on("users", (data) => {
|
||||||
|
setUsers(data)
|
||||||
|
console.log("data received from users event: ", data)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.emit("newuser",{
|
||||||
|
id: socket.id,
|
||||||
|
name: window.sessionStorage.getItem("email")
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on("callUser", ({ from, name: callerName, signal }) => {
|
||||||
|
console.log("you're receiving call brow")
|
||||||
|
setCall({ isReceivingCall: true, from, name: callerName, signal });
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('disconnect', function(){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const answerCall = () => {
|
||||||
|
setCallAccepted(true);
|
||||||
|
|
||||||
|
const peer = new Peer({
|
||||||
|
initiator: false,
|
||||||
|
trickle: false,
|
||||||
|
stream: stream,
|
||||||
|
config: {
|
||||||
|
iceServers: [
|
||||||
|
{url:'stun:stun.l.google.com:19302'},
|
||||||
|
{url:'stun:stun1.l.google.com:19302'},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.on("signal", (data) => {
|
||||||
|
socket.emit("answerCall", { signal: data, to: call.from });
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.on("stream", (currentStream) => {
|
||||||
|
userVideo.current.srcObject = currentStream;
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.signal(call.signal);
|
||||||
|
|
||||||
|
connectionRef.current = peer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const callUser = (id) => {
|
||||||
|
|
||||||
|
console.log("calling user ",id)
|
||||||
|
const peer = new Peer({ initiator: true, trickle: false, stream:stream, config: {
|
||||||
|
iceServers: [
|
||||||
|
{url:'stun:stun.l.google.com:19302'},
|
||||||
|
{url:'stun:stun1.l.google.com:19302'},
|
||||||
|
{url:'stun:stun2.l.google.com:19302'},
|
||||||
|
{url:'stun:stun3.l.google.com:19302'},
|
||||||
|
{url:'stun:stun4.l.google.com:19302'},
|
||||||
|
]
|
||||||
|
}, });
|
||||||
|
|
||||||
|
peer.on("signal", (data) => {
|
||||||
|
socket.emit("callUser", {
|
||||||
|
userToCall: id,
|
||||||
|
signalData: data,
|
||||||
|
from: window.sessionStorage.getItem("email"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.on("stream", (currentStream) => {
|
||||||
|
userVideo.current.srcObject = currentStream;
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("callAccepted", (signal) => {
|
||||||
|
setCallAccepted(true);
|
||||||
|
|
||||||
|
peer.signal(signal);
|
||||||
|
});
|
||||||
|
|
||||||
|
connectionRef.current = peer;
|
||||||
|
};
|
||||||
|
|
||||||
|
const leaveCall = () => {
|
||||||
|
setCallEnded(true);
|
||||||
|
|
||||||
|
connectionRef.current.destroy();
|
||||||
|
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
if(auth.isAuthenticated){
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[auth.isAuthenticated]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WsContext.Provider
|
||||||
|
value={{
|
||||||
|
users,
|
||||||
|
call,
|
||||||
|
callAccepted,
|
||||||
|
myVideo,
|
||||||
|
userVideo,
|
||||||
|
stream,
|
||||||
|
callEnded,
|
||||||
|
callUser,
|
||||||
|
leaveCall,
|
||||||
|
answerCall,
|
||||||
|
setStream
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</WsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
WsProvider.propTypes = {
|
||||||
|
children: PropTypes.node
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WsConsumer = WsContext.Consumer;
|
||||||
|
|
||||||
|
export const useWsContext = () => useContext(WsContext);
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
import ChartBarIcon from '@heroicons/react/24/solid/ChartBarIcon';
|
import ChartBarIcon from '@heroicons/react/24/solid/ChartBarIcon';
|
||||||
import CogIcon from '@heroicons/react/24/solid/CogIcon';
|
import CogIcon from '@heroicons/react/24/solid/CogIcon';
|
||||||
import LockClosedIcon from '@heroicons/react/24/solid/LockClosedIcon';
|
import ChatBubbleLeftRightIcon from '@heroicons/react/24/solid/ChatBubbleLeftRightIcon'
|
||||||
import ShoppingBagIcon from '@heroicons/react/24/solid/ShoppingBagIcon';
|
import MapIcon from '@heroicons/react/24/solid/MapIcon'
|
||||||
import UserIcon from '@heroicons/react/24/solid/UserIcon';
|
|
||||||
import UserPlusIcon from '@heroicons/react/24/solid/UserPlusIcon';
|
|
||||||
import UsersIcon from '@heroicons/react/24/solid/UsersIcon';
|
|
||||||
import CpuChip from '@heroicons/react/24/solid/CpuChipIcon';
|
import CpuChip from '@heroicons/react/24/solid/CpuChipIcon';
|
||||||
import XCircleIcon from '@heroicons/react/24/solid/XCircleIcon';
|
|
||||||
import { SvgIcon } from '@mui/material';
|
import { SvgIcon } from '@mui/material';
|
||||||
|
|
||||||
export const items = [
|
export const items = [
|
||||||
|
|
@ -28,6 +24,24 @@ export const items = [
|
||||||
</SvgIcon>
|
</SvgIcon>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Chat (beta)',
|
||||||
|
path: '/chat',
|
||||||
|
icon: (
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ChatBubbleLeftRightIcon/>
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: 'Map',
|
||||||
|
// path: '/',
|
||||||
|
// icon: (
|
||||||
|
// <SvgIcon fontSize="small">
|
||||||
|
// <MapIcon/>
|
||||||
|
// </SvgIcon>
|
||||||
|
// )
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
title: 'Settings',
|
title: 'Settings',
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import BellIcon from '@heroicons/react/24/solid/BellIcon';
|
import BellIcon from '@heroicons/react/24/solid/BellIcon';
|
||||||
import UsersIcon from '@heroicons/react/24/solid/UsersIcon';
|
import UsersIcon from '@heroicons/react/24/solid/UsersIcon';
|
||||||
|
import PhoneIcon from '@heroicons/react/24/solid/PhoneIcon';
|
||||||
import Bars3Icon from '@heroicons/react/24/solid/Bars3Icon';
|
import Bars3Icon from '@heroicons/react/24/solid/Bars3Icon';
|
||||||
import MagnifyingGlassIcon from '@heroicons/react/24/solid/MagnifyingGlassIcon';
|
import MagnifyingGlassIcon from '@heroicons/react/24/solid/MagnifyingGlassIcon';
|
||||||
import {
|
import {
|
||||||
|
|
@ -11,12 +12,21 @@ import {
|
||||||
Stack,
|
Stack,
|
||||||
SvgIcon,
|
SvgIcon,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
useMediaQuery
|
useMediaQuery,
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
Button
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
|
||||||
import { alpha } from '@mui/material/styles';
|
import { alpha } from '@mui/material/styles';
|
||||||
import { usePopover } from 'src/hooks/use-popover';
|
import { usePopover } from 'src/hooks/use-popover';
|
||||||
import { AccountPopover } from './account-popover';
|
import { AccountPopover } from './account-popover';
|
||||||
import { useAuth } from 'src/hooks/use-auth';
|
import { useAuth } from 'src/hooks/use-auth';
|
||||||
|
import { WsContext } from 'src/contexts/socketio-context';
|
||||||
|
import { useContext, useEffect } from 'react';
|
||||||
|
|
||||||
const SIDE_NAV_WIDTH = 280;
|
const SIDE_NAV_WIDTH = 280;
|
||||||
const TOP_NAV_HEIGHT = 64;
|
const TOP_NAV_HEIGHT = 64;
|
||||||
|
|
@ -26,6 +36,7 @@ export const TopNav = (props) => {
|
||||||
const lgUp = useMediaQuery((theme) => theme.breakpoints.up('lg'));
|
const lgUp = useMediaQuery((theme) => theme.breakpoints.up('lg'));
|
||||||
const accountPopover = usePopover();
|
const accountPopover = usePopover();
|
||||||
const auth = useAuth();
|
const auth = useAuth();
|
||||||
|
const { answerCall, call, callAccepted } = useContext(WsContext);
|
||||||
|
|
||||||
return ( auth.user &&
|
return ( auth.user &&
|
||||||
<>
|
<>
|
||||||
|
|
@ -67,13 +78,13 @@ export const TopNav = (props) => {
|
||||||
</SvgIcon>
|
</SvgIcon>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
<Tooltip title="Search">
|
{/* <Tooltip title="Search">
|
||||||
<IconButton>
|
<IconButton>
|
||||||
<SvgIcon fontSize="small">
|
<SvgIcon fontSize="small">
|
||||||
<MagnifyingGlassIcon />
|
<MagnifyingGlassIcon />
|
||||||
</SvgIcon>
|
</SvgIcon>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip> */}
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack
|
<Stack
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
|
|
@ -113,6 +124,57 @@ export const TopNav = (props) => {
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
{call.isReceivingCall && !callAccepted &&
|
||||||
|
<Dialog
|
||||||
|
fullWidth={ true }
|
||||||
|
maxWidth={"sm"}
|
||||||
|
open={true}
|
||||||
|
//scroll={scroll}
|
||||||
|
aria-labelledby="scroll-dialog-title"
|
||||||
|
aria-describedby="scroll-dialog-description"
|
||||||
|
>
|
||||||
|
<DialogContent dividers={scroll === 'paper'}>
|
||||||
|
<Box display="flex" alignItems="center" justifyContent={'center'}>
|
||||||
|
<Box sx={{margin:"30px",textAlign:'center'}}>
|
||||||
|
<Avatar
|
||||||
|
sx={{
|
||||||
|
height: 150,
|
||||||
|
width: 150,
|
||||||
|
}}
|
||||||
|
src={"/assets/avatars/default-avatar.png"}
|
||||||
|
/>
|
||||||
|
<Box flexGrow={1} >{call.from}</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box display="flex" alignItems="center" justifyContent={'center'}>
|
||||||
|
<IconButton>
|
||||||
|
<Tooltip title="Refuse" placement="left" onClick={()=>{}}>
|
||||||
|
<SvgIcon
|
||||||
|
sx={{cursor:'pointer'}}
|
||||||
|
style={{transform: "scale(1.5,1.5)"}}
|
||||||
|
>
|
||||||
|
<PhoneIcon
|
||||||
|
color={"#CB1E02"}
|
||||||
|
/>
|
||||||
|
</SvgIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</IconButton>
|
||||||
|
<div style={{width:'15%'}}></div>
|
||||||
|
<IconButton>
|
||||||
|
<Tooltip title="Accept" placement="right" onClick={()=>{}}>
|
||||||
|
<SvgIcon
|
||||||
|
sx={{cursor:'pointer'}}
|
||||||
|
style={{transform: "scale(1.5,1.5) scale(-1,1)"}}
|
||||||
|
>
|
||||||
|
<PhoneIcon
|
||||||
|
color={"#17A000"}
|
||||||
|
/>
|
||||||
|
</SvgIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>}
|
||||||
<AccountPopover
|
<AccountPopover
|
||||||
anchorEl={accountPopover.anchorRef.current}
|
anchorEl={accountPopover.anchorRef.current}
|
||||||
open={accountPopover.open}
|
open={accountPopover.open}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ const Page = () => (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>
|
<title>
|
||||||
404 | Devias Kit
|
404 | Oktopus TR-369
|
||||||
</title>
|
</title>
|
||||||
</Head>
|
</Head>
|
||||||
<Box
|
<Box
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { useNProgress } from 'src/hooks/use-nprogress';
|
||||||
import { createTheme } from 'src/theme';
|
import { createTheme } from 'src/theme';
|
||||||
import { createEmotionCache } from 'src/utils/create-emotion-cache';
|
import { createEmotionCache } from 'src/utils/create-emotion-cache';
|
||||||
import 'simplebar-react/dist/simplebar.min.css';
|
import 'simplebar-react/dist/simplebar.min.css';
|
||||||
|
import { WsProvider } from 'src/contexts/socketio-context';
|
||||||
|
|
||||||
const clientSideEmotionCache = createEmotionCache();
|
const clientSideEmotionCache = createEmotionCache();
|
||||||
|
|
||||||
|
|
@ -27,7 +28,7 @@ const App = (props) => {
|
||||||
<CacheProvider value={emotionCache}>
|
<CacheProvider value={emotionCache}>
|
||||||
<Head>
|
<Head>
|
||||||
<title>
|
<title>
|
||||||
Devias Kit
|
Oktopus
|
||||||
</title>
|
</title>
|
||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
|
|
@ -36,6 +37,7 @@ const App = (props) => {
|
||||||
</Head>
|
</Head>
|
||||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
|
<WsProvider>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<AuthConsumer>
|
<AuthConsumer>
|
||||||
|
|
@ -46,6 +48,7 @@ const App = (props) => {
|
||||||
}
|
}
|
||||||
</AuthConsumer>
|
</AuthConsumer>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
</WsProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</LocalizationProvider>
|
</LocalizationProvider>
|
||||||
</CacheProvider>
|
</CacheProvider>
|
||||||
|
|
|
||||||
154
frontend/src/pages/chat.js
Normal file
154
frontend/src/pages/chat.js
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
import React, { useEffect, useState, useContext } from "react";
|
||||||
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
|
import PhoneIcon from "@heroicons/react/24/solid/PhoneIcon";
|
||||||
|
import PhoneXMarkIcon from "@heroicons/react/24/solid/PhoneXMarkIcon"
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Box,
|
||||||
|
CardContent,
|
||||||
|
Container,
|
||||||
|
SvgIcon,
|
||||||
|
CircularProgress,
|
||||||
|
Avatar,
|
||||||
|
Tooltip
|
||||||
|
} from "@mui/material";
|
||||||
|
import { WsContext } from "src/contexts/socketio-context";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
const Page = () => {
|
||||||
|
|
||||||
|
//const [isConnected, setIsConnected] = useState(socket.connected);
|
||||||
|
const [users, setUsers] = useState([])
|
||||||
|
//const [onlineUsers, setOnlineUsers] = useState([])
|
||||||
|
|
||||||
|
const ws = useContext(WsContext)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
var myHeaders = new Headers();
|
||||||
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
myHeaders.append("Authorization", localStorage.getItem("token"));
|
||||||
|
|
||||||
|
var requestOptions = {
|
||||||
|
method: 'GET',
|
||||||
|
headers: myHeaders,
|
||||||
|
redirect: 'follow'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(`${process.env.NEXT_PUBLIC_REST_ENPOINT}/users`,requestOptions)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => {
|
||||||
|
// let teste = JSON.stringify(JSON.parse(result), null, 2)
|
||||||
|
setUsers(result)
|
||||||
|
})
|
||||||
|
.catch(error => console.log('error', error));
|
||||||
|
},[])
|
||||||
|
|
||||||
|
const renderUsers = () => {
|
||||||
|
console.log("users: ", users)
|
||||||
|
console.log("wsUsers: ", ws.users)
|
||||||
|
if(users.length == 0){
|
||||||
|
console.log("users is empty")
|
||||||
|
return (
|
||||||
|
<div style={{display:'flex', justifyContent:'center'}} height={'100%'} >
|
||||||
|
<CircularProgress color="inherit" width='100%'/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}else {
|
||||||
|
return (
|
||||||
|
<Card sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent:'center',
|
||||||
|
}}>
|
||||||
|
<CardContent>
|
||||||
|
|
||||||
|
<Container sx={{display:'flex',justifyContent:'center'}}>
|
||||||
|
{users.map((x)=> {
|
||||||
|
|
||||||
|
let color = "#CB1E02"
|
||||||
|
let status = "offline"
|
||||||
|
|
||||||
|
if (ws.users.findIndex(y => y.name === x.email) >= 0){
|
||||||
|
console.log("user: "+x.email+" is online")
|
||||||
|
//color = "#11ADFB"
|
||||||
|
color = "#17A000"
|
||||||
|
status = "online"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.email !== window.sessionStorage.getItem("email")){
|
||||||
|
return (
|
||||||
|
<Box sx={{margin:"30px",textAlign:'center'}} key={x.email}>
|
||||||
|
<Avatar
|
||||||
|
sx={{
|
||||||
|
height: 150,
|
||||||
|
width: 150,
|
||||||
|
border: '3px solid '+color
|
||||||
|
}}
|
||||||
|
src={"/assets/avatars/default-avatar.png"}
|
||||||
|
/>
|
||||||
|
<div style={{marginTop:'10px'}}>
|
||||||
|
</div>
|
||||||
|
{status === "online" ?
|
||||||
|
<Tooltip title="Call" placement="right" onClick={()=>{
|
||||||
|
router.push({
|
||||||
|
pathname:"chat/room",
|
||||||
|
query: {user: x.email}
|
||||||
|
})
|
||||||
|
}}>
|
||||||
|
<SvgIcon
|
||||||
|
sx={{cursor:'pointer'}}
|
||||||
|
>
|
||||||
|
<PhoneIcon
|
||||||
|
color={color}
|
||||||
|
/>
|
||||||
|
</SvgIcon>
|
||||||
|
</Tooltip>
|
||||||
|
:
|
||||||
|
<Tooltip title="Offline" placement="right">
|
||||||
|
<SvgIcon
|
||||||
|
sx={{cursor:'default'}}
|
||||||
|
>
|
||||||
|
<PhoneXMarkIcon
|
||||||
|
color={color}
|
||||||
|
/>
|
||||||
|
</SvgIcon>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
<p style={{marginTop:'-2.5px'}}>{x.email}</p>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Container>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(ws.users ?
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
py: 10,
|
||||||
|
alignItems: 'center',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="md">
|
||||||
|
{renderUsers()}
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
:
|
||||||
|
<CircularProgress color="inherit" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Page.getLayout = (page) => (
|
||||||
|
<DashboardLayout>
|
||||||
|
{page}
|
||||||
|
</DashboardLayout>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
70
frontend/src/pages/chat/room.js
Normal file
70
frontend/src/pages/chat/room.js
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
import React, { useEffect, useState, useContext } from "react";
|
||||||
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Box,
|
||||||
|
CardContent,
|
||||||
|
Container,
|
||||||
|
SvgIcon,
|
||||||
|
CircularProgress,
|
||||||
|
Avatar,
|
||||||
|
Tooltip
|
||||||
|
} from "@mui/material";
|
||||||
|
import { WsContext } from "src/contexts/socketio-context";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
const Page = (props) => {
|
||||||
|
|
||||||
|
const { callUser, callAccepted, myVideo, userVideo, callEnded, stream, call, setStream } =
|
||||||
|
useContext(WsContext);
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const stopCamera = () => {
|
||||||
|
// stream.getTracks().forEach(function(track) {
|
||||||
|
// track.stop();
|
||||||
|
// });
|
||||||
|
//console.log(stream)
|
||||||
|
window.location.reload() //TODO: find better way to stop recording user
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
callUser(router.query.user)
|
||||||
|
navigator.mediaDevices
|
||||||
|
.getUserMedia({ video: true, audio: true })
|
||||||
|
.then((currentStream) => {
|
||||||
|
setStream(currentStream);
|
||||||
|
if (myVideo.current) {
|
||||||
|
myVideo.current.srcObject = currentStream;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('You cannot place/ receive a call without granting video and audio permissions! Please change your settings to use Oktopus calls.')
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
return(stopCamera)
|
||||||
|
},[])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
{ myVideo &&
|
||||||
|
<video
|
||||||
|
className="userVideo"
|
||||||
|
playsInline
|
||||||
|
muted
|
||||||
|
ref={myVideo}
|
||||||
|
autoPlay />
|
||||||
|
}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Page.getLayout = (page) => (
|
||||||
|
<DashboardLayout>
|
||||||
|
{page}
|
||||||
|
</DashboardLayout>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
80
frontend/src/pages/devices/[...id].js
Normal file
80
frontend/src/pages/devices/[...id].js
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
import Head from 'next/head';
|
||||||
|
import { Box, Stack, Typography, Container, Unstable_Grid2 as Grid,
|
||||||
|
Tab,
|
||||||
|
Tabs,
|
||||||
|
SvgIcon } from '@mui/material';
|
||||||
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import { DevicesRPC } from 'src/sections/devices/devices-rpc';
|
||||||
|
import { DevicesDiscovery } from 'src/sections/devices/devices-discovery';
|
||||||
|
import EnvelopeIcon from '@heroicons/react/24/outline/EnvelopeIcon';
|
||||||
|
import MagnifyingGlassIcon from '@heroicons/react/24/solid/MagnifyingGlassIcon';
|
||||||
|
import WifiIcon from '@heroicons/react/24/solid/WifiIcon';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const Page = () => {
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const deviceID = router.query.id[0]
|
||||||
|
const section = router.query.id[1]
|
||||||
|
|
||||||
|
const sectionHandler = () => {
|
||||||
|
switch(section){
|
||||||
|
case "msg":
|
||||||
|
return <DevicesRPC/>
|
||||||
|
case "discovery":
|
||||||
|
return <DevicesDiscovery/>
|
||||||
|
default:
|
||||||
|
return <p>Hello World</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
console.log("deviceid:",deviceID)
|
||||||
|
})
|
||||||
|
|
||||||
|
return(
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>
|
||||||
|
Oktopus | TR-369
|
||||||
|
</title>
|
||||||
|
</Head>
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
py: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="lg" >
|
||||||
|
<Stack spacing={3} >
|
||||||
|
<Box sx={{
|
||||||
|
display:'flex',
|
||||||
|
justifyContent:'center'
|
||||||
|
}}
|
||||||
|
mb={3}>
|
||||||
|
<Tabs value={router.query.id[1]} aria-label="icon label tabs example">
|
||||||
|
<Tab icon={<SvgIcon><WifiIcon/></SvgIcon>} iconPosition={"end"} label="Wi-Fi" />
|
||||||
|
<Tab value={"discovery"} onClick={()=>{router.push(`/devices/${deviceID}/discovery`)}} icon={<SvgIcon><MagnifyingGlassIcon/></SvgIcon>} iconPosition={"end"} label="Discover Parameters" />
|
||||||
|
<Tab value={"msg"} onClick={()=>{router.push(`/devices/${deviceID}/msg`)}} icon={<SvgIcon><EnvelopeIcon/></SvgIcon>} iconPosition={"end"} label="Remote Messages" />
|
||||||
|
</Tabs>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{
|
||||||
|
sectionHandler()
|
||||||
|
}
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Page.getLayout = (page) => (
|
||||||
|
<DashboardLayout>
|
||||||
|
{page}
|
||||||
|
</DashboardLayout>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
import Head from 'next/head';
|
|
||||||
import { Box, Stack, Typography, Container, Unstable_Grid2 as Grid } from '@mui/material';
|
|
||||||
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import { DevicesRPC } from 'src/sections/devices/devices-rpc';
|
|
||||||
|
|
||||||
const Page = () => {
|
|
||||||
const router = useRouter()
|
|
||||||
const { id } = router.query
|
|
||||||
|
|
||||||
|
|
||||||
return(
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>
|
|
||||||
Oktopus | TR-369
|
|
||||||
</title>
|
|
||||||
</Head>
|
|
||||||
<Box
|
|
||||||
component="main"
|
|
||||||
sx={{
|
|
||||||
flexGrow: 1,
|
|
||||||
py: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Container maxWidth="lg" >
|
|
||||||
<Stack spacing={3} >
|
|
||||||
<Typography variant="h4">
|
|
||||||
RPC
|
|
||||||
</Typography>
|
|
||||||
{/*<SettingsNotifications />*/}
|
|
||||||
< DevicesRPC />
|
|
||||||
</Stack>
|
|
||||||
</Container>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
Page.getLayout = (page) => (
|
|
||||||
<DashboardLayout>
|
|
||||||
{page}
|
|
||||||
</DashboardLayout>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Page;
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
|
import React, { useState } from 'react';
|
||||||
import { subDays, subHours } from 'date-fns';
|
import { subDays, subHours } from 'date-fns';
|
||||||
import { Box, Container, Unstable_Grid2 as Grid } from '@mui/material';
|
import { Box, Container, Unstable_Grid2 as Grid } from '@mui/material';
|
||||||
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
|
|
@ -13,7 +14,9 @@ import { OverviewTraffic } from 'src/sections/overview/overview-traffic';
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
const Page = () => (
|
const Page = () => {
|
||||||
|
|
||||||
|
return(
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>
|
<title>
|
||||||
|
|
@ -109,8 +112,8 @@ const Page = () => (
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
</Container>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>)
|
||||||
);
|
};
|
||||||
|
|
||||||
Page.getLayout = (page) => (
|
Page.getLayout = (page) => (
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
|
|
|
||||||
169
frontend/src/sections/devices/devices-discovery.js
Normal file
169
frontend/src/sections/devices/devices-discovery.js
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
SvgIcon,
|
||||||
|
IconButton,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemText,
|
||||||
|
Collapse,
|
||||||
|
Box,
|
||||||
|
Tabs,
|
||||||
|
Tab
|
||||||
|
} from '@mui/material';
|
||||||
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
|
|
||||||
|
export const DevicesDiscovery = () => {
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const [deviceParameters, setDeviceParameters] = useState(null)
|
||||||
|
|
||||||
|
const initialize = async (raw) => {
|
||||||
|
let content = await getDeviceParameters(raw)
|
||||||
|
setDeviceParameters(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDeviceParameters = async (raw) =>{
|
||||||
|
var myHeaders = new Headers();
|
||||||
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
myHeaders.append("Authorization", localStorage.getItem("token"));
|
||||||
|
|
||||||
|
var requestOptions = {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: myHeaders,
|
||||||
|
redirect: 'follow',
|
||||||
|
body: raw
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = await (await fetch(`${process.env.NEXT_PUBLIC_REST_ENPOINT}/device/${router.query.id[0]}/parameters`, requestOptions))
|
||||||
|
if (result.status != 200) {
|
||||||
|
throw new Error('Please check your email and password');
|
||||||
|
}else{
|
||||||
|
return result.json()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(()=> {
|
||||||
|
|
||||||
|
initialize(
|
||||||
|
JSON.stringify({
|
||||||
|
"obj_paths": ["Device."],
|
||||||
|
"first_level_only" : false,
|
||||||
|
"return_commands" : false,
|
||||||
|
"return_events" : false,
|
||||||
|
"return_params" : true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},[])
|
||||||
|
|
||||||
|
//Together with showParameters, this function renders all the device parameters the device supports
|
||||||
|
//but you must set req with first_level_only property to false
|
||||||
|
// const showPathParameters = (pathParamsList) => {
|
||||||
|
// return pathParamsList.map((x,i)=>{
|
||||||
|
// return(
|
||||||
|
// <List component="div" disablePadding dense={true}>
|
||||||
|
// <ListItem
|
||||||
|
// key={i}
|
||||||
|
// divider={true}
|
||||||
|
// sx={{
|
||||||
|
// boxShadow: 'rgba(149, 157, 165, 0.2) 0px 0px 5px;',
|
||||||
|
// pl: 4
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <ListItemText
|
||||||
|
// primary={x.param_name}
|
||||||
|
// />
|
||||||
|
// </ListItem>
|
||||||
|
// </List>
|
||||||
|
// )
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const updateDeviceParameters = async (param,) => {
|
||||||
|
|
||||||
|
// let raw = JSON.stringify({
|
||||||
|
// "obj_paths": [param],
|
||||||
|
// "first_level_only" : true,
|
||||||
|
// "return_commands" : false,
|
||||||
|
// "return_events" : false,
|
||||||
|
// "return_params" : true
|
||||||
|
// })
|
||||||
|
|
||||||
|
// let content = await getDeviceParameters(raw)
|
||||||
|
|
||||||
|
// console.log(content)
|
||||||
|
|
||||||
|
// setDeviceParameters(prevState => {
|
||||||
|
// return {...prevState, req_obj_results: [
|
||||||
|
// {
|
||||||
|
// supported_objs:[ ...prevState.req_obj_results[0].
|
||||||
|
// supported_objs][index]
|
||||||
|
// .supported_params:[]
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
const showParameters = () => {
|
||||||
|
console.log(deviceParameters)
|
||||||
|
return deviceParameters.req_obj_results[0].supported_objs.map((x,i)=> {
|
||||||
|
return (
|
||||||
|
<List dense={true}>
|
||||||
|
<ListItem
|
||||||
|
key={x.supported_obj_path}
|
||||||
|
divider={true}
|
||||||
|
// secondaryAction={
|
||||||
|
// <IconButton /*onClick={()=>updateDeviceParameters(x.supported_obj_path, index)}*/>
|
||||||
|
// <SvgIcon>
|
||||||
|
// <PlusCircleIcon></PlusCircleIcon>
|
||||||
|
// </SvgIcon>
|
||||||
|
// </IconButton>
|
||||||
|
// }
|
||||||
|
sx={{
|
||||||
|
boxShadow: 'rgba(149, 157, 165, 0.2) 0px 0px 5px;'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListItemText
|
||||||
|
primary={<b>{x.supported_obj_path}</b>}
|
||||||
|
sx={{fontWeight:'bold'}}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
{ x.supported_params &&
|
||||||
|
x.supported_params.map((y)=>{
|
||||||
|
return <List component="div" disablePadding dense={true}>
|
||||||
|
<ListItem
|
||||||
|
key={i}
|
||||||
|
divider={true}
|
||||||
|
sx={{
|
||||||
|
boxShadow: 'rgba(149, 157, 165, 0.2) 0px 0px 5px;',
|
||||||
|
pl: 4
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListItemText
|
||||||
|
primary={y.param_name}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</List>)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ( deviceParameters ?
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
{showParameters()}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
:
|
||||||
|
<Box sx={{display:'flex',justifyContent:'center'}}>
|
||||||
|
<CircularProgress color="inherit" />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
@ -84,7 +84,7 @@ const handleOpen = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fetch(`${process.env.NEXT_PUBLIC_REST_ENPOINT}/device/${router.query.id}/${method}`, requestOptions)
|
fetch(`${process.env.NEXT_PUBLIC_REST_ENPOINT}/device/${router.query.id[0]}/${method}`, requestOptions)
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(result => {
|
.then(result => {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
|
|
@ -257,7 +257,7 @@ const handleOpen = () => {
|
||||||
//ref={descriptionElementRef}
|
//ref={descriptionElementRef}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<pre>
|
<pre style={{color: 'black'}}>
|
||||||
{content}
|
{content}
|
||||||
</pre>
|
</pre>
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ export const OverviewLatestOrders = (props) => {
|
||||||
sx={{cursor:'pointer'}}
|
sx={{cursor:'pointer'}}
|
||||||
onClick={()=>{
|
onClick={()=>{
|
||||||
if (order.Status !== 2){
|
if (order.Status !== 2){
|
||||||
router.push("devices/"+order.SN)
|
router.push("devices/"+order.SN+"/discovery")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user