Merge pull request #313 from OktopUSP/dev

Dev
This commit is contained in:
Leandro Antônio Farias Machado 2024-07-10 16:06:38 -03:00 committed by GitHub
commit 88027ae773
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
69 changed files with 1664 additions and 690 deletions

View File

@ -14,7 +14,7 @@ Oktopus is a multi-vendor management platform for CPEs and IoTs. <b>Any device t
<a href="https://www.inango.com/" target="_blank"><img src="https://github.com/OktopUSP/oktopus/assets/83298718/3b3e65d9-33fa-46c4-8b24-f9e2a84a04a6" width="100px"/></a>
<p>If you'd like to become a sponsor, start a partnership or somehow to contribute to the project, email <a href="">leandro@oktopus.app.br</a>, every contribution is welcome, and the resources will help the project to move on. Also, if your company uses this project and you'd like your logo to appear up here, contact us.
<p>If you'd like to become a sponsor, start a partnership or somehow to contribute to the project, email <a href="">contact@oktopus.app.br</a>, every contribution is welcome, and the resources will help the project to move on. Also, if your company uses this project and you'd like your logo to appear up here, contact us.
<ul>
<li>
@ -23,7 +23,7 @@ Oktopus is a multi-vendor management platform for CPEs and IoTs. <b>Any device t
<p>
Our solution has an open-source software license, meaning you can modify/study the code and use it for free. You can perform all the configurations, allocate servers, and set it up on your network with the classic "do it yourself" approach, or save time and money: contact us for a quote and get commercial support.
</p>
<p>Contact <a href="">leandro@oktopus.app.br</a> via email and get a quote.</p>
<p>Contact <a href="">sales@oktopus.app.br</a> via email and get a quote.</p>
</li>
</ul>

View File

@ -14,10 +14,11 @@ import (
const LOCAL_ENV = ".env.local"
type Nats struct {
Url string
Name string
VerifyCertificates bool
Ctx context.Context
Url string
Name string
EnableTls bool
Cert Tls
Ctx context.Context
}
type Acs struct {
@ -40,6 +41,12 @@ type Config struct {
Nats Nats
}
type Tls struct {
CertFile string
KeyFile string
CaFile string
}
func NewConfig() *Config {
loadEnvVariables()
@ -47,7 +54,10 @@ func NewConfig() *Config {
natsUrl := flag.String("nats_url", lookupEnvOrString("NATS_URL", "nats://localhost:4222"), "url for nats server")
natsName := flag.String("nats_name", lookupEnvOrString("NATS_NAME", "adapter"), "name for nats client")
natsVerifyCertificates := flag.Bool("nats_verify_certificates", lookupEnvOrBool("NATS_VERIFY_CERTIFICATES", false), "verify validity of certificates from nats server")
natsEnableTls := flag.Bool("nats_enable_tls", lookupEnvOrBool("NATS_ENABLE_TLS", false), "enbale TLS to nats server")
clientCrt := flag.String("client_crt", lookupEnvOrString("CLIENT_CRT", "cert.pem"), "client certificate file to TLS connection")
clientKey := flag.String("client_key", lookupEnvOrString("CLIENT_KEY", "key.pem"), "client key file to TLS connection")
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
acsPort := flag.String("acs_port", lookupEnvOrString("ACS_PORT", ":9292"), "port for acs server")
acsRoute := flag.String("acs_route", lookupEnvOrString("ACS_ROUTE", "/acs"), "route for acs server")
connReqUser := flag.String("connrq_user", lookupEnvOrString("CONN_RQ_USER", ""), "Connection Request Username")
@ -77,10 +87,15 @@ func NewConfig() *Config {
return &Config{
Nats: Nats{
Url: *natsUrl,
Name: *natsName,
VerifyCertificates: *natsVerifyCertificates,
Ctx: ctx,
Url: *natsUrl,
Name: *natsName,
EnableTls: *natsEnableTls,
Ctx: ctx,
Cert: Tls{
CertFile: *clientCrt,
KeyFile: *clientKey,
CaFile: *serverCA,
},
},
Acs: Acs{
Port: *acsPort,

View File

@ -141,8 +141,10 @@ func defineOptions(c config.Nats) []nats.Option {
opts = append(opts, nats.ClosedHandler(func(nc *nats.Conn) {
log.Printf("Connection closed. Reason: %q\n", nc.LastError())
}))
if c.VerifyCertificates {
opts = append(opts, nats.RootCAs())
if c.EnableTls {
log.Printf("Load certificates: %s and %s\n", c.Cert.CertFile, c.Cert.KeyFile)
opts = append(opts, nats.RootCAs(c.Cert.CaFile))
opts = append(opts, nats.ClientCert(c.Cert.CertFile, c.Cert.KeyFile))
}
return opts

View File

@ -13,10 +13,11 @@ import (
const LOCAL_ENV = ".env.local"
type Nats struct {
Url string
Name string
VerifyCertificates bool
Ctx context.Context
Url string
Name string
EnableTls bool
Cert Tls
Ctx context.Context
}
type RestApi struct {
@ -29,6 +30,12 @@ type Config struct {
Nats Nats
}
type Tls struct {
CertFile string
KeyFile string
CaFile string
}
func NewConfig() *Config {
loadEnvVariables()
@ -36,7 +43,10 @@ func NewConfig() *Config {
natsUrl := flag.String("nats_url", lookupEnvOrString("NATS_URL", "nats://localhost:4222"), "url for nats server")
natsName := flag.String("nats_name", lookupEnvOrString("NATS_NAME", "adapter"), "name for nats client")
natsVerifyCertificates := flag.Bool("nats_verify_certificates", lookupEnvOrBool("NATS_VERIFY_CERTIFICATES", false), "verify validity of certificates from nats server")
natsEnableTls := flag.Bool("nats_enable_tls", lookupEnvOrBool("NATS_ENABLE_TLS", false), "enbale TLS to nats server")
clientCrt := flag.String("client_crt", lookupEnvOrString("CLIENT_CRT", "cert.pem"), "client certificate file to TLS connection")
clientKey := flag.String("client_key", lookupEnvOrString("CLIENT_KEY", "key.pem"), "client key file to TLS connection")
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
flApiPort := flag.String("api_port", lookupEnvOrString("REST_API_PORT", "4000"), "Rest api port")
flHelp := flag.Bool("help", false, "Help")
@ -62,10 +72,15 @@ func NewConfig() *Config {
Ctx: ctx,
},
Nats: Nats{
Url: *natsUrl,
Name: *natsName,
VerifyCertificates: *natsVerifyCertificates,
Ctx: ctx,
Url: *natsUrl,
Name: *natsName,
EnableTls: *natsEnableTls,
Ctx: ctx,
Cert: Tls{
CertFile: *clientCrt,
KeyFile: *clientKey,
CaFile: *serverCA,
},
},
}
}

View File

@ -70,8 +70,10 @@ func defineOptions(c config.Nats) []nats.Option {
opts = append(opts, nats.ClosedHandler(func(nc *nats.Conn) {
log.Printf("Connection closed. Reason: %q\n", nc.LastError())
}))
if c.VerifyCertificates {
opts = append(opts, nats.RootCAs())
if c.EnableTls {
log.Printf("Load certificates: %s and %s\n", c.Cert.CertFile, c.Cert.KeyFile)
opts = append(opts, nats.RootCAs(c.Cert.CaFile))
opts = append(opts, nats.ClientCert(c.Cert.CertFile, c.Cert.KeyFile))
}
return opts

View File

@ -57,6 +57,13 @@ func (a *Api) StartApi() {
authentication.HandleFunc("/password", a.changePassword).Methods("PUT")
authentication.HandleFunc("/admin/register", a.registerAdminUser).Methods("POST")
authentication.HandleFunc("/admin/exists", a.adminUserExists).Methods("GET")
if a.enterpise.Enable {
mapRoutes := r.PathPrefix("/api/map").Subrouter()
mapRoutes.HandleFunc("", a.devicesLocation).Methods("GET")
mapRoutes.Use(func(handler http.Handler) http.Handler {
return middleware.Middleware(handler)
})
}
iot := r.PathPrefix("/api/device").Subrouter()
iot.HandleFunc("/alias", a.setDeviceAlias).Methods("PUT")
iot.HandleFunc("/auth", a.deviceAuth).Methods("GET", "POST", "DELETE")

View File

@ -28,6 +28,22 @@ func (a *Api) getEnterpriseResource(
return err
}
func (a *Api) getMapsResource(
action string,
w http.ResponseWriter,
body []byte,
) error {
err := bridge.NatsEnterpriseInteraction("geolocation.v1."+action, body, w, a.nc)
return err
}
func (a *Api) devicesLocation(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
a.getMapsResource("get", w, []byte{})
}
}
func (a *Api) deviceSiteSurvey(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
sn := vars["sn"]

View File

@ -243,7 +243,6 @@ func adminUserExists(users []map[string]interface{}, supportEmail string) bool {
for _, x := range users {
if db.UserLevels(x["level"].(int32)) == db.AdminUser && x["email"].(string) != supportEmail {
log.Println("Admin exists")
return true
}
}

View File

@ -263,7 +263,7 @@ func NatsEnterpriseInteraction(
if err != nil {
if err == nats.ErrNoResponders {
w.WriteHeader(http.StatusInternalServerError)
w.Write(utils.Marshall("You have no enterprise license, to get one contact: leandro@oktopus.app.br"))
w.Write(utils.Marshall("You have no enterprise license, to get one contact: sales@oktopus.app.br"))
return err
}
w.WriteHeader(http.StatusInternalServerError)

View File

@ -13,10 +13,11 @@ import (
const LOCAL_ENV = ".env.local"
type Nats struct {
Url string
Name string
VerifyCertificates bool
Ctx context.Context
Url string
Name string
EnableTls bool
Cert Tls
Ctx context.Context
}
type Mongo struct {
@ -42,6 +43,12 @@ type Config struct {
Enterprise Enterprise
}
type Tls struct {
CertFile string
KeyFile string
CaFile string
}
func NewConfig() *Config {
loadEnvVariables()
@ -49,7 +56,10 @@ func NewConfig() *Config {
natsUrl := flag.String("nats_url", lookupEnvOrString("NATS_URL", "nats://localhost:4222"), "url for nats server")
natsName := flag.String("nats_name", lookupEnvOrString("NATS_NAME", "controller"), "name for nats client")
natsVerifyCertificates := flag.Bool("nats_verify_certificates", lookupEnvOrBool("NATS_VERIFY_CERTIFICATES", false), "verify validity of certificates from nats server")
natsEnableTls := flag.Bool("nats_enable_tls", lookupEnvOrBool("NATS_ENABLE_TLS", false), "enbale TLS to nats server")
clientCrt := flag.String("client_crt", lookupEnvOrString("CLIENT_CRT", "cert.pem"), "client certificate file to TLS connection")
clientKey := flag.String("client_key", lookupEnvOrString("CLIENT_KEY", "key.pem"), "client key file to TLS connection")
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
flApiPort := flag.String("api_port", lookupEnvOrString("REST_API_PORT", "8000"), "Rest api port")
mongoUri := flag.String("mongo_uri", lookupEnvOrString("MONGO_URI", "mongodb://localhost:27017"), "uri for mongodb server")
enterpise := flag.Bool("enterprise", lookupEnvOrBool("ENTERPRISE", false), "enterprise version enable")
@ -79,10 +89,15 @@ func NewConfig() *Config {
Ctx: ctx,
},
Nats: Nats{
Url: *natsUrl,
Name: *natsName,
VerifyCertificates: *natsVerifyCertificates,
Ctx: ctx,
Url: *natsUrl,
Name: *natsName,
EnableTls: *natsEnableTls,
Ctx: ctx,
Cert: Tls{
CertFile: *clientCrt,
KeyFile: *clientKey,
CaFile: *serverCA,
},
},
Mongo: Mongo{
Uri: *mongoUri,

View File

@ -77,8 +77,10 @@ func defineOptions(c config.Nats) []nats.Option {
opts = append(opts, nats.ClosedHandler(func(nc *nats.Conn) {
log.Printf("Connection closed. Reason: %q\n", nc.LastError())
}))
if c.VerifyCertificates {
opts = append(opts, nats.RootCAs())
if c.EnableTls {
log.Printf("Load certificates: %s and %s\n", c.Cert.CertFile, c.Cert.KeyFile)
opts = append(opts, nats.RootCAs(c.Cert.CaFile))
opts = append(opts, nats.ClientCert(c.Cert.CertFile, c.Cert.KeyFile))
}
return opts

View File

@ -13,10 +13,17 @@ import (
const LOCAL_ENV = ".env.local"
type Nats struct {
Url string
Name string
VerifyCertificates bool
Ctx context.Context
Url string
Name string
EnableTls bool
Cert Tls
Ctx context.Context
}
type Tls struct {
CertFile string
KeyFile string
CaFile string
}
type Mongo struct {
@ -42,7 +49,10 @@ func NewConfig() *Config {
natsUrl := flag.String("nats_url", lookupEnvOrString("NATS_URL", "nats://localhost:4222"), "url for nats server")
natsName := flag.String("nats_name", lookupEnvOrString("NATS_NAME", "adapter"), "name for nats client")
natsVerifyCertificates := flag.Bool("nats_verify_certificates", lookupEnvOrBool("NATS_VERIFY_CERTIFICATES", false), "verify validity of certificates from nats server")
natsEnableTls := flag.Bool("nats_enable_tls", lookupEnvOrBool("NATS_ENABLE_TLS", false), "enbale TLS to nats server")
clientCrt := flag.String("client_crt", lookupEnvOrString("CLIENT_CRT", "cert.pem"), "client certificate file to TLS connection")
clientKey := flag.String("client_key", lookupEnvOrString("CLIENT_KEY", "key.pem"), "client key file to TLS connection")
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
mongoUri := flag.String("mongo_uri", lookupEnvOrString("MONGO_URI", "mongodb://localhost:27017"), "uri for mongodb server")
controllerId := flag.String("controller_id", lookupEnvOrString("CONTROLLER_ID", "oktopusController"), "usp controller endpoint id")
controllerPassword := flag.String("controller_passwd", lookupEnvOrString("CONTROLLER_PASSWORD", ""), "usp controller endpoint password to connect to")
@ -66,10 +76,15 @@ func NewConfig() *Config {
return &Config{
Nats: Nats{
Url: *natsUrl,
Name: *natsName,
VerifyCertificates: *natsVerifyCertificates,
Ctx: ctx,
Url: *natsUrl,
Name: *natsName,
EnableTls: *natsEnableTls,
Ctx: ctx,
Cert: Tls{
CertFile: *clientCrt,
KeyFile: *clientKey,
CaFile: *serverCA,
},
},
Mongo: Mongo{
Uri: *mongoUri,

View File

@ -1,6 +1,7 @@
package cwmp_handler
import (
"encoding/json"
"encoding/xml"
"log"
@ -12,6 +13,10 @@ func (h *Handler) HandleDeviceInfo(device string, data []byte, ack func()) {
defer ack()
log.Printf("Device %s info", device)
deviceInfo := parseDeviceInfoMsg(data)
if deviceExists, _ := h.db.DeviceExists(deviceInfo.SN); !deviceExists {
fmtDeviceInfo, _ := json.Marshal(deviceInfo)
h.nc.Publish("device.v1.new", fmtDeviceInfo)
}
err := h.db.CreateDevice(deviceInfo)
if err != nil {
log.Printf("Failed to create device: %v", err)

View File

@ -1,6 +1,7 @@
package usp_handler
import (
"encoding/json"
"log"
"github.com/OktopUSP/oktopus/backend/services/mtp/adapter/internal/db"
@ -14,6 +15,10 @@ func (h *Handler) HandleDeviceInfo(device, subject string, data []byte, mtp stri
defer ack()
log.Printf("Device %s info, mtp: %s", device, mtp)
deviceInfo := parseDeviceInfoMsg(device, subject, data, getMtp(mtp))
if deviceExists, _ := h.db.DeviceExists(deviceInfo.SN); !deviceExists {
fmtDeviceInfo, _ := json.Marshal(deviceInfo)
h.nc.Publish("device.v1.new", fmtDeviceInfo)
}
err := h.db.CreateDevice(deviceInfo)
if err != nil {
log.Printf("Failed to create device: %v", err)

View File

@ -150,8 +150,10 @@ func defineOptions(c config.Nats) []nats.Option {
opts = append(opts, nats.ClosedHandler(func(nc *nats.Conn) {
log.Printf("Connection closed. Reason: %q\n", nc.LastError())
}))
if c.VerifyCertificates {
opts = append(opts, nats.RootCAs())
if c.EnableTls {
log.Printf("Load certificates: %s and %s\n", c.Cert.CertFile, c.Cert.KeyFile)
opts = append(opts, nats.RootCAs(c.Cert.CaFile))
opts = append(opts, nats.ClientCert(c.Cert.CertFile, c.Cert.KeyFile))
}
return opts

View File

@ -13,10 +13,17 @@ import (
const LOCAL_ENV = ".env.local"
type Nats struct {
Url string
Name string
VerifyCertificates bool
Ctx context.Context
Url string
Name string
EnableTls bool
Cert Tls
Ctx context.Context
}
type Tls struct {
CertFile string
KeyFile string
CaFile string
}
type Mqtt struct {
@ -42,7 +49,10 @@ func NewConfig() *Config {
natsUrl := flag.String("nats_url", lookupEnvOrString("NATS_URL", "nats://localhost:4222"), "url for nats server")
natsName := flag.String("nats_name", lookupEnvOrString("NATS_NAME", "mqtt-adapter"), "name for nats client")
natsVerifyCertificates := flag.Bool("nats_verify_certificates", lookupEnvOrBool("NATS_VERIFY_CERTIFICATES", false), "verify validity of certificates from nats server")
natsEnableTls := flag.Bool("nats_enable_tls", lookupEnvOrBool("NATS_ENABLE_TLS", false), "enbale TLS to nats server")
clientCrt := flag.String("client_crt", lookupEnvOrString("CLIENT_CRT", "cert.pem"), "client certificate file to TLS connection")
clientKey := flag.String("client_key", lookupEnvOrString("CLIENT_KEY", "key.pem"), "client key file to TLS connection")
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
mqttUrl := flag.String("mqtt_url", lookupEnvOrString("MQTT_URL", "tcp://localhost:1883"), "url for mqtt server")
mqttsUrl := flag.String("mqtts_url", lookupEnvOrString("MQTTS_URL", ""), "url for mqtts server")
mqttsSkipVerify := flag.Bool("mqtts_skip_verify", lookupEnvOrBool("MQTTS_SKIP_VERIFY", false), "skip verification of server certificate for mqtts")
@ -69,10 +79,15 @@ func NewConfig() *Config {
return &Config{
Nats: Nats{
Url: *natsUrl,
Name: *natsName,
VerifyCertificates: *natsVerifyCertificates,
Ctx: ctx,
Url: *natsUrl,
Name: *natsName,
EnableTls: *natsEnableTls,
Ctx: ctx,
Cert: Tls{
CertFile: *clientCrt,
KeyFile: *clientKey,
CaFile: *serverCA,
},
},
Mqtt: Mqtt{
Url: *mqttUrl,

View File

@ -92,8 +92,10 @@ func defineOptions(c config.Nats) []nats.Option {
opts = append(opts, nats.ClosedHandler(func(nc *nats.Conn) {
log.Printf("Connection closed. Reason: %q\n", nc.LastError())
}))
if c.VerifyCertificates {
opts = append(opts, nats.RootCAs())
if c.EnableTls {
log.Printf("Load certificates: %s and %s\n", c.Cert.CertFile, c.Cert.KeyFile)
opts = append(opts, nats.RootCAs(c.Cert.CaFile))
opts = append(opts, nats.ClientCert(c.Cert.CertFile, c.Cert.KeyFile))
}
return opts

View File

@ -32,10 +32,17 @@ type Config struct {
}
type Nats struct {
Url string
Name string
VerifyCertificates bool
Ctx context.Context
Url string
Name string
EnableTls bool
Cert Tls
Ctx context.Context
}
type Tls struct {
CertFile string
KeyFile string
CaFile string
}
func NewConfig() Config {
@ -66,7 +73,10 @@ func NewConfig() Config {
logLevel := flag.Int("log_level", lookupEnvOrInt("LOG_LEVEL", 1), "0=DEBUG, 1=INFO, 2=WARNING, 3=ERROR")
natsUrl := flag.String("nats_url", lookupEnvOrString("NATS_URL", "nats://localhost:4222"), "url for nats server")
natsName := flag.String("nats_name", lookupEnvOrString("NATS_NAME", "adapter"), "name for nats client")
natsVerifyCertificates := flag.Bool("nats_verify_certificates", lookupEnvOrBool("NATS_VERIFY_CERTIFICATES", false), "verify validity of certificates from nats server")
natsEnableTls := flag.Bool("nats_enable_tls", lookupEnvOrBool("NATS_ENABLE_TLS", false), "enbale TLS to nats server")
clientCrt := flag.String("client_crt", lookupEnvOrString("CLIENT_CRT", "cert.pem"), "client certificate file to TLS connection")
clientKey := flag.String("client_key", lookupEnvOrString("CLIENT_KEY", "key.pem"), "client key file to TLS connection")
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
flag.Parse()
flHelp := flag.Bool("help", false, "Help")
@ -99,10 +109,15 @@ func NewConfig() Config {
HttpPort: *httpPort,
LogLevel: *logLevel,
Nats: Nats{
Url: *natsUrl,
Name: *natsName,
VerifyCertificates: *natsVerifyCertificates,
Ctx: ctx,
Url: *natsUrl,
Name: *natsName,
EnableTls: *natsEnableTls,
Ctx: ctx,
Cert: Tls{
CertFile: *clientCrt,
KeyFile: *clientKey,
CaFile: *serverCA,
},
},
}

View File

@ -74,8 +74,10 @@ func defineOptions(c config.Nats) []nats.Option {
opts = append(opts, nats.ClosedHandler(func(nc *nats.Conn) {
log.Printf("Connection closed. Reason: %q\n", nc.LastError())
}))
if c.VerifyCertificates {
opts = append(opts, nats.RootCAs())
if c.EnableTls {
log.Printf("Load certificates: %s and %s\n", c.Cert.CertFile, c.Cert.KeyFile)
opts = append(opts, nats.RootCAs(c.Cert.CaFile))
opts = append(opts, nats.ClientCert(c.Cert.CertFile, c.Cert.KeyFile))
}
return opts

View File

@ -13,10 +13,17 @@ import (
const LOCAL_ENV = ".env.local"
type Nats struct {
Url string
Name string
VerifyCertificates bool
Ctx context.Context
Url string
Name string
EnableTls bool
Cert Tls
Ctx context.Context
}
type Tls struct {
CertFile string
KeyFile string
CaFile string
}
type Stomp struct {
@ -37,7 +44,10 @@ func NewConfig() *Config {
natsUrl := flag.String("nats_url", lookupEnvOrString("NATS_URL", "nats://localhost:4222"), "url for nats server")
natsName := flag.String("nats_name", lookupEnvOrString("NATS_NAME", "mqtt-adapter"), "name for nats client")
natsVerifyCertificates := flag.Bool("nats_verify_certificates", lookupEnvOrBool("NATS_VERIFY_CERTIFICATES", false), "verify validity of certificates from nats server")
natsEnableTls := flag.Bool("nats_enable_tls", lookupEnvOrBool("NATS_ENABLE_TLS", false), "enbale TLS to nats server")
clientCrt := flag.String("client_crt", lookupEnvOrString("CLIENT_CRT", "cert.pem"), "client certificate file to TLS connection")
clientKey := flag.String("client_key", lookupEnvOrString("CLIENT_KEY", "key.pem"), "client key file to TLS connection")
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
stompAddr := flag.String("stomp_server", lookupEnvOrString("STOMP_SERVER", "localhost:61613"), "STOMP server endpoint")
stompUser := flag.String("stomp_user", lookupEnvOrString("STOMP_USER", ""), "stomp server user")
stompPassword := flag.String("stomp_passsword", lookupEnvOrString("STOMP_PASSWD", ""), "stomp server password")
@ -61,10 +71,15 @@ func NewConfig() *Config {
return &Config{
Nats: Nats{
Url: *natsUrl,
Name: *natsName,
VerifyCertificates: *natsVerifyCertificates,
Ctx: ctx,
Url: *natsUrl,
Name: *natsName,
EnableTls: *natsEnableTls,
Ctx: ctx,
Cert: Tls{
CertFile: *clientCrt,
KeyFile: *clientKey,
CaFile: *serverCA,
},
},
Stomp: Stomp{
Url: *stompAddr,

View File

@ -81,9 +81,10 @@ func defineOptions(c config.Nats) []nats.Option {
opts = append(opts, nats.ClosedHandler(func(nc *nats.Conn) {
log.Printf("Connection closed. Reason: %q\n", nc.LastError())
}))
if c.VerifyCertificates {
opts = append(opts, nats.RootCAs())
if c.EnableTls {
log.Printf("Load certificates: %s and %s\n", c.Cert.CertFile, c.Cert.KeyFile)
opts = append(opts, nats.RootCAs(c.Cert.CaFile))
opts = append(opts, nats.ClientCert(c.Cert.CertFile, c.Cert.KeyFile))
}
return opts
}

View File

@ -13,10 +13,17 @@ import (
const LOCAL_ENV = ".env.local"
type Nats struct {
Url string
Name string
VerifyCertificates bool
Ctx context.Context
Url string
Name string
EnableTls bool
Cert Tls
Ctx context.Context
}
type Tls struct {
CertFile string
KeyFile string
CaFile string
}
type Ws struct {
@ -42,7 +49,10 @@ func NewConfig() *Config {
natsUrl := flag.String("nats_url", lookupEnvOrString("NATS_URL", "nats://localhost:4222"), "url for nats server")
natsName := flag.String("nats_name", lookupEnvOrString("NATS_NAME", "ws-adapter"), "name for nats client")
natsVerifyCertificates := flag.Bool("nats_verify_certificates", lookupEnvOrBool("NATS_VERIFY_CERTIFICATES", false), "verify validity of certificates from nats server")
natsEnableTls := flag.Bool("nats_enable_tls", lookupEnvOrBool("NATS_ENABLE_TLS", false), "enbale TLS to nats server")
clientCrt := flag.String("client_crt", lookupEnvOrString("CLIENT_CRT", "cert.pem"), "client certificate file to TLS connection")
clientKey := flag.String("client_key", lookupEnvOrString("CLIENT_KEY", "key.pem"), "client key file to TLS connection")
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
wsAuthEnable := flag.Bool("ws_auth_enable", lookupEnvOrBool("WS_AUTH_ENABLE", false), "enable authentication for websocket server")
wsAddr := flag.String("ws_addr", lookupEnvOrString("WS_ADDR", "localhost"), "websocket server address (domain or ip)")
wsPort := flag.String("ws_port", lookupEnvOrString("WS_PORT", ":8080"), "websocket server port")
@ -68,10 +78,15 @@ func NewConfig() *Config {
return &Config{
Nats: Nats{
Url: *natsUrl,
Name: *natsName,
VerifyCertificates: *natsVerifyCertificates,
Ctx: ctx,
Url: *natsUrl,
Name: *natsName,
EnableTls: *natsEnableTls,
Ctx: ctx,
Cert: Tls{
CertFile: *clientCrt,
KeyFile: *clientKey,
CaFile: *serverCA,
},
},
Ws: Ws{
AuthEnable: *wsAuthEnable,

View File

@ -91,8 +91,10 @@ func defineOptions(c config.Nats) []nats.Option {
opts = append(opts, nats.ClosedHandler(func(nc *nats.Conn) {
log.Printf("Connection closed. Reason: %q\n", nc.LastError())
}))
if c.VerifyCertificates {
opts = append(opts, nats.RootCAs())
if c.EnableTls {
log.Printf("Load certificates: %s and %s\n", c.Cert.CertFile, c.Cert.KeyFile)
opts = append(opts, nats.RootCAs(c.Cert.CaFile))
opts = append(opts, nats.ClientCert(c.Cert.CertFile, c.Cert.KeyFile))
}
return opts

View File

@ -24,10 +24,17 @@ type Config struct {
}
type Nats struct {
Url string
Name string
VerifyCertificates bool
Ctx context.Context
Url string
Name string
EnableTls bool
Cert Tls
Ctx context.Context
}
type Tls struct {
CertFile string
KeyFile string
CaFile string
}
func NewConfig() Config {
@ -47,7 +54,10 @@ func NewConfig() Config {
/* ------------------------------ define flags ------------------------------ */
natsUrl := flag.String("nats_url", lookupEnvOrString("NATS_URL", "nats://localhost:4222"), "url for nats server")
natsName := flag.String("nats_name", lookupEnvOrString("NATS_NAME", "ws-adapter"), "name for nats client")
natsVerifyCertificates := flag.Bool("nats_verify_certificates", lookupEnvOrBool("NATS_VERIFY_CERTIFICATES", false), "verify validity of certificates from nats server")
natsEnableTls := flag.Bool("nats_enable_tls", lookupEnvOrBool("NATS_ENABLE_TLS", false), "enbale TLS to nats server")
clientCrt := flag.String("client_crt", lookupEnvOrString("CLIENT_CRT", "cert.pem"), "client certificate file to TLS connection")
clientKey := flag.String("client_key", lookupEnvOrString("CLIENT_KEY", "key.pem"), "client key file to TLS connection")
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
flPort := flag.String("port", lookupEnvOrString("SERVER_PORT", ":8080"), "Server port")
flAuth := flag.Bool("auth", lookupEnvOrBool("SERVER_AUTH_ENABLE", false), "Server auth enable/disable")
flControllerEid := flag.String("controller-eid", lookupEnvOrString("CONTROLLER_EID", "oktopusController"), "Controller eid")
@ -81,10 +91,15 @@ func NewConfig() Config {
FullChain: *flFullchain,
PrivateKey: *flPrivKey,
Nats: Nats{
Url: *natsUrl,
Name: *natsName,
VerifyCertificates: *natsVerifyCertificates,
Ctx: ctx,
Url: *natsUrl,
Name: *natsName,
EnableTls: *natsEnableTls,
Ctx: ctx,
Cert: Tls{
CertFile: *clientCrt,
KeyFile: *clientKey,
CaFile: *serverCA,
},
},
}
}

View File

@ -66,8 +66,10 @@ func defineOptions(c config.Nats) []nats.Option {
opts = append(opts, nats.ClosedHandler(func(nc *nats.Conn) {
log.Printf("Connection closed. Reason: %q\n", nc.LastError())
}))
if c.VerifyCertificates {
opts = append(opts, nats.RootCAs())
if c.EnableTls {
log.Printf("Load certificates: %s and %s\n", c.Cert.CertFile, c.Cert.KeyFile)
opts = append(opts, nats.RootCAs(c.Cert.CaFile))
opts = append(opts, nats.ClientCert(c.Cert.CertFile, c.Cert.KeyFile))
}
return opts

View File

@ -1,2 +1,2 @@
DIRECTORY_PATH="./firmwares"
DIRECTORY_PATH="."
SERVER_PORT=":8004"

View File

@ -1,2 +1,3 @@
firmwares/
.env.local
images/

View File

@ -0,0 +1,8 @@
FROM golang:1.22.2@sha256:450e3822c7a135e1463cd83e51c8e2eb03b86a02113c89424e6f0f8344bb4168 as builder
WORKDIR /app
COPY ../ .
RUN CGO_ENABLED=0 GOOS=linux go build -o file-server main.go
FROM alpine:3.14@sha256:0f2d5c38dd7a4f4f733e688e3a6733cb5ab1ac6e3cb4603a5dd564e5bfb80eed
COPY --from=builder /app/file-server /
ENTRYPOINT ["/file-server"]

View File

@ -0,0 +1,61 @@
.PHONY: help build push start stop release remove delete run logs bash
DOCKER_USER ?= oktopusp
DOCKER_APP ?= file-server
DOCKER_TAG ?= $(shell git log --format="%h" -n 1)
CONTAINER_SHELL ?= /bin/sh
.DEFAULT_GOAL := help
help:
@echo "Makefile arguments:"
@echo ""
@echo "DOCKER_USER - docker user to build image"
@echo "DOCKER_APP - docker image name"
@echo "DOCKER_TAG - docker image tag"
@echo "CONTAINER_SHELL - container shell e.g:'/bin/bash'"
@echo ""
@echo "Makefile commands:"
@echo ""
@echo "build - docker image build"
@echo "push - push docker iamge to registry"
@echo "run - create and start docker container with the image"
@echo "start - start existent docker container with the image"
@echo "stop - stop docker container running the image"
@echo "remove - remove docker container running the image"
@echo "delete - delete docker image"
@echo "logs - show logs of docker container"
@echo "bash - access container shell"
@echo "release - tag image as latest and push to registry"
build:
@docker build -t ${DOCKER_USER}/${DOCKER_APP}:${DOCKER_TAG} -f Dockerfile ../
run:
@docker run -d --name ${DOCKER_USER}-${DOCKER_APP} ${DOCKER_USER}/${DOCKER_APP}:${DOCKER_TAG}
stop:
@docker stop ${DOCKER_USER}-${DOCKER_APP}
remove: stop
@docker rm ${DOCKER_USER}-${DOCKER_APP}
delete:
@docker rmi ${DOCKER_USER}/${DOCKER_APP}:${DOCKER_TAG}
start:
@docker start ${DOCKER_USER}-${DOCKER_APP}
push:
@docker push ${DOCKER_USER}/${DOCKER_APP}:${DOCKER_TAG}
logs:
@docker logs -f ${DOCKER_USER}-${DOCKER_APP}
bash:
@docker exec -it ${DOCKER_USER}-${DOCKER_APP} ${CONTAINER_SHELL}
release: build
@docker push ${DOCKER_USER}/${DOCKER_APP}:${DOCKER_TAG}
@docker tag ${DOCKER_USER}/${DOCKER_APP}:${DOCKER_TAG} ${DOCKER_USER}/${DOCKER_APP}:latest
@docker push ${DOCKER_USER}/${DOCKER_APP}:latest

View File

@ -42,7 +42,7 @@ func main() {
http.Handle("/", fileServer)
port := os.Getenv("SERVER_PORT")
fmt.Printf("Server started at http://localhost:%s\n", port)
fmt.Printf("Server started at http://localhost%s\n", port)
err = http.ListenAndServe(port, nil)
if err != nil {
fmt.Printf("Error starting server: %s\n", err)

View File

@ -26,6 +26,7 @@ build-backend:
@make build -C ../backend/services/controller/build/ DOCKER_USER=${DOCKER_USER}
@make build -C ../backend/services/acs/build/ DOCKER_USER=${DOCKER_USER}
@make build -C ../backend/services/utils/socketio/build/ DOCKER_USER=${DOCKER_USER}
@make build -C ../backend/services/utils/file-server/build/ DOCKER_USER=${DOCKER_USER}
@make build -C ../backend/services/mtp/adapter/build/ DOCKER_USER=${DOCKER_USER}
@make build -C ../backend/services/mtp/ws-adapter/build/ DOCKER_USER=${DOCKER_USER}
@make build -C ../backend/services/mtp/ws/build/ DOCKER_USER=${DOCKER_USER}
@ -43,6 +44,7 @@ release-backend:
@make release -C ../backend/services/controller/build/ DOCKER_USER=${DOCKER_USER}
@make release -C ../backend/services/acs/build/ DOCKER_USER=${DOCKER_USER}
@make release -C ../backend/services/utils/socketio/build/ DOCKER_USER=${DOCKER_USER}
@make release -C ../backend/services/utils/file-server/build/ DOCKER_USER=${DOCKER_USER}
@make release -C ../backend/services/mtp/adapter/build/ DOCKER_USER=${DOCKER_USER}
@make release -C ../backend/services/mtp/ws-adapter/build/ DOCKER_USER=${DOCKER_USER}
@make release -C ../backend/services/mtp/ws/build/ DOCKER_USER=${DOCKER_USER}

View File

@ -1 +1,5 @@
NATS_URL=nats://msg_broker:4222
NATS_URL=nats://oktopususer:oktopuspw@msg_broker:4222
NATS_ENABLE_TLS="true"
CLIENT_CRT=/tmp/nats/config/cert.pem
CLIENT_KEY=/tmp/nats/config/key.pem
SERVER_CA=/tmp/nats/config/rootCA.pem

View File

@ -1,2 +1,6 @@
NATS_URL=nats://msg_broker:4222
MONGO_URI=mongodb://mongo_usp:27017
NATS_URL=nats://oktopususer:oktopuspw@msg_broker:4222
NATS_ENABLE_TLS="true"
CLIENT_CRT=/tmp/nats/config/cert.pem
CLIENT_KEY=/tmp/nats/config/key.pem
SERVER_CA=/tmp/nats/config/rootCA.pem

View File

@ -1,2 +1,6 @@
NATS_URL=nats://msg_broker:4222
MONGO_URI=mongodb://mongo_usp:27017
NATS_URL=nats://oktopususer:oktopuspw@msg_broker:4222
NATS_ENABLE_TLS="true"
CLIENT_CRT=/tmp/nats/config/cert.pem
CLIENT_KEY=/tmp/nats/config/key.pem
SERVER_CA=/tmp/nats/config/rootCA.pem

View File

@ -0,0 +1,2 @@
DIRECTORY_PATH="."
SERVER_PORT=":8004"

View File

@ -1,3 +1,7 @@
REDIS_ENABLE=false
REDIS_ADDR=redis_usp:6379
NATS_URL=nats://msg_broker:4222
NATS_URL=nats://oktopususer:oktopuspw@msg_broker:4222
NATS_ENABLE_TLS="true"
CLIENT_CRT=/tmp/nats/config/cert.pem
CLIENT_KEY=/tmp/nats/config/key.pem
SERVER_CA=/tmp/nats/config/rootCA.pem

View File

@ -1,2 +1,6 @@
NATS_URL=nats://msg_broker:4222
MQTT_URL=tcp://mqtt:1883
NATS_URL=nats://oktopususer:oktopuspw@msg_broker:4222
NATS_ENABLE_TLS="true"
CLIENT_CRT=/tmp/nats/config/cert.pem
CLIENT_KEY=/tmp/nats/config/key.pem
SERVER_CA=/tmp/nats/config/rootCA.pem

3
deploy/compose/.env.nats Normal file
View File

@ -0,0 +1,3 @@
NATS_NAME=oktopus
NATS_USER=oktopususer
NATS_PW=oktopuspw

View File

@ -1 +1,5 @@
NATS_URL=nats://msg_broker:4222
NATS_URL=nats://oktopususer:oktopuspw@msg_broker:4222
NATS_ENABLE_TLS="true"
CLIENT_CRT=/tmp/nats/config/cert.pem
CLIENT_KEY=/tmp/nats/config/key.pem
SERVER_CA=/tmp/nats/config/rootCA.pem

View File

@ -1,2 +1,6 @@
NATS_URL=nats://msg_broker:4222
STOMP_SERVER=stomp:61613
NATS_URL=nats://oktopususer:oktopuspw@msg_broker:4222
NATS_ENABLE_TLS="true"
CLIENT_CRT=/tmp/nats/config/cert.pem
CLIENT_KEY=/tmp/nats/config/key.pem
SERVER_CA=/tmp/nats/config/rootCA.pem

View File

@ -1 +1,5 @@
NATS_URL=nats://msg_broker:4222
NATS_URL=nats://oktopususer:oktopuspw@msg_broker:4222
NATS_ENABLE_TLS="true"
CLIENT_CRT=/tmp/nats/config/cert.pem
CLIENT_KEY=/tmp/nats/config/key.pem
SERVER_CA=/tmp/nats/config/rootCA.pem

View File

@ -1,2 +1,6 @@
NATS_URL=nats://msg_broker:4222
WS_ADDR=ws
NATS_URL=nats://oktopususer:oktopuspw@msg_broker:4222
NATS_ENABLE_TLS="true"
CLIENT_CRT=/tmp/nats/config/cert.pem
CLIENT_KEY=/tmp/nats/config/key.pem
SERVER_CA=/tmp/nats/config/rootCA.pem

View File

@ -4,3 +4,7 @@ mongo_data/*
!mongo_data/.gitkeep
nats_data/*
!nats_data/.gitkeep
firmwares/*
!firmwares/.gitkeep
images/*
!images/logo.png

View File

@ -1,38 +1,42 @@
services:
#/* ----------------------------- Message Broker ----------------------------- */
#/* ----------------------------- Message Broker ----------------------------- */
msg_broker:
image: 'nats:latest'
image: "nats:latest"
container_name: nats
ports:
- 4222:4222
- 8222:8222
command: -n oktopus -m 8222 -js
command: -c /tmp/nats/config/nats.cfg
env_file:
- .env.nats
volumes:
- ./nats_data:/tmp/nats/jetstream
- ./nats_config:/tmp/nats/config
networks:
usp_network:
ipv4_address: 172.16.235.2
profiles: [nats]
#/* -------------------------------------------------------------------------- */
#/* -------------------------------------------------------------------------- */
#/* ------------------------ API REST / USP Controller ----------------------- */
#/* ------------------------ API REST / USP Controller ----------------------- */
controller:
image: 'oktopusp/controller'
image: "oktopusp/controller"
container_name: controller
ports:
- 8000:8000
- 8000:8000
depends_on:
- mongo_usp
- mongo_usp
env_file:
- .env.controller
volumes:
- ./nats_config:/tmp/nats/config
networks:
usp_network:
ipv4_address: 172.16.235.3
profiles: [controller]
#/* -------------------------------------------------------------------------- */
#/* -------------------------------------------------------------------------- */
#/* ---------------------------- Databases / Cache --------------------------- */
#/* ---------------------------- Databases / Cache --------------------------- */
mongo_usp:
image: mongo
container_name: mongo_usp
@ -43,115 +47,128 @@ services:
ipv4_address: 172.16.235.4
volumes:
- ./mongo_data:/data/db
profiles: [controller,adapter]
profiles: [controller, adapter]
#/* -------------------------------------------------------------------------- */
#/* -------------------------------------------------------------------------- */
#/* ----------------------- Message Transfer Protocols ----------------------- */
#/* ----------------------- Message Transfer Protocols ----------------------- */
mqtt:
image: 'oktopusp/mqtt'
image: "oktopusp/mqtt"
container_name: mqtt
ports:
- 1883:1883
- 8883:8883
- 1883:1883
- 8883:8883
env_file:
- .env.mqtt
volumes:
- ./nats_config:/tmp/nats/config
networks:
usp_network:
ipv4_address: 172.16.235.6
profiles: [mqtt]
ws:
image: 'oktopusp/ws'
image: "oktopusp/ws"
container_name: websockets
ports:
- 8080:8080
- 8080:8080
env_file:
- .env.ws
volumes:
- ./nats_config:/tmp/nats/config
networks:
usp_network:
ipv4_address: 172.16.235.7
profiles: [ws]
stomp:
image: 'oktopusp/stomp'
image: "oktopusp/stomp"
container_name: stomp
ports:
- 61613:61613
- 61613:61613
networks:
usp_network:
ipv4_address: 172.16.235.8
profiles: [stomp]
#/* -------------------------------------------------------------------------- */
#/* -------------------------------------------------------------------------- */
#/* --------------- Message transfer Protocols Adapters to NATS -------------- */
#/* --------------- Message transfer Protocols Adapters to NATS -------------- */
mqtt-adapter:
image: 'oktopusp/mqtt-adapter'
image: "oktopusp/mqtt-adapter"
container_name: mqtt-adapter
depends_on:
- mqtt
env_file:
- .env.mqtt-adapter
volumes:
- ./nats_config:/tmp/nats/config
networks:
usp_network:
ipv4_address: 172.16.235.9
profiles: [mqtt]
ws-adapter:
image: 'oktopusp/ws-adapter'
image: "oktopusp/ws-adapter"
container_name: ws-adapter
depends_on:
- ws
env_file:
- .env.ws-adapter
volumes:
- ./nats_config:/tmp/nats/config
networks:
usp_network:
ipv4_address: 172.16.235.10
profiles: [ws]
stomp-adapter:
image: 'oktopusp/stomp-adapter'
image: "oktopusp/stomp-adapter"
container_name: stomp-adapter
depends_on:
- stomp
env_file:
- .env.stomp-adapter
volumes:
- ./nats_config:/tmp/nats/config
networks:
usp_network:
ipv4_address: 172.16.235.11
profiles: [stomp]
adapter:
image: 'oktopusp/adapter'
image: "oktopusp/adapter"
container_name: adapter
depends_on:
- mongo_usp
env_file:
- .env.adapter
volumes:
- ./nats_config:/tmp/nats/config
networks:
usp_network:
ipv4_address: 172.16.235.12
profiles: [adapter]
#/* -------------------------------------------------------------------------- */
#/* -------------------------------------------------------------------------- */
#/* ------------- SocketIO Real Time Communication With Frontend ------------- */
#/* ------------- SocketIO Real Time Communication With Frontend ------------- */
socketio:
image: 'oktopusp/socketio'
image: "oktopusp/socketio"
container_name: socketio
ports:
- 5000:5000
- 5000:5000
env_file:
- .env.socketio
volumes:
- ./nats_config:/tmp/nats/config
networks:
usp_network:
ipv4_address: 172.16.235.13
profiles: [frontend]
#/* -------------------------------------------------------------------------- */
#/* -------------------------------------------------------------------------- */
#/* -------------------------------- Frontend -------------------------------- */
#/* -------------------------------- Frontend -------------------------------- */
frontend:
image: 'oktopusp/frontend-ce'
image: "oktopusp/frontend-ce"
container_name: frontend
ports:
- 3000:3000
@ -159,7 +176,7 @@ services:
usp_network:
ipv4_address: 172.16.235.14
profiles: [frontend]
#/* -------------------------------------------------------------------------- */
#/* -------------------------------------------------------------------------- */
portainer:
image: portainer/portainer-ce:latest
@ -175,12 +192,14 @@ services:
- ./portainer_data:/data
acs:
image: oktopusp/acs
image: "oktopusp/acs"
container_name: acs
ports:
- 9292:9292
env_file:
- .env.acs
volumes:
- ./nats_config:/tmp/nats/config
networks:
usp_network:
ipv4_address: 172.16.235.16
@ -191,18 +210,30 @@ services:
container_name: nginx
ports:
- 80:80
depends_on:
- frontend
- controller
- socketio
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
usp_network:
ipv4_address: 172.16.235.17
file-server:
image: oktopusp/file-server
container_name: file-server
ports:
- 8004:8004
volumes:
- ./firmwares:/app/firmwares
- ./images:/app/images
env_file:
- .env.file-server
networks:
usp_network:
ipv4_address: 172.16.235.18
networks:
usp_network:
usp_network:
driver: bridge
ipam:
driver: default

View File

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEeTCCAuGgAwIBAgIRAJenDys60PD3Cjh9VXeAzsowDQYJKoZIhvcNAQELBQAw
gY0xHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTExMC8GA1UECwwoY2hp
ZXNhQEFkcmlhbm9NYWMubG9jYWwgKEFkcmlhbm8gQ2hpZXNhKTE4MDYGA1UEAwwv
bWtjZXJ0IGNoaWVzYUBBZHJpYW5vTWFjLmxvY2FsIChBZHJpYW5vIENoaWVzYSkw
HhcNMjQwNzA2MTQzODQ2WhcNMjYxMDA2MTQzODQ2WjBcMScwJQYDVQQKEx5ta2Nl
cnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxMTAvBgNVBAsMKGNoaWVzYUBBZHJp
YW5vTWFjLmxvY2FsIChBZHJpYW5vIENoaWVzYSkwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQCdQCgwn7M5jtkNzYv/yUelUV84ehsYjPTQspCXCjDdUltO
E6jafRM/2SopUWwzPTorIZkerIpqq/avi77e6wSdCKJZBA5DsrMSOmFjjMqG4H2b
euzr5b+ZKzKnw4nF/CuB2cmWkz/qAiluqVdRl+Gef/Ouyuer/NuhJhK6Mgy1VuAd
ynwizGsAgQ/oAGDuHWmcMhGw/2NVTnXqHei5ntr+F4r08YOgXnRsGId+16nJU5xd
Xs80sw2dFl+1zQiFJxWjVQk65CyZfLDcwC7e3PAMsT+8obzuBKCn6fW3JiIZ68tc
o8GZClHSkeS7IMp+/wtjMXf1iipE0bRWzzdl1xQnAgMBAAGjgYMwgYAwDgYDVR0P
AQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFH2KEsMw
S7/Qu1eE1DxRhZTI0Z7RMDgGA1UdEQQxMC+CCm1zZ19icm9rZXKCCWxvY2FsaG9z
dIcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAYEAVr06
Kg/FaWxUbD6Q+e5OLzckeeFZOKcb/2mmEH+e99HebnCuydiiGebZVWcLsoOw+gmb
WG/4rFyp9D/ZYFiQxWNRm/nCcxiHIV+JHDxhBn3vKENxYNNbr+WTMBTqmaYqPARK
SelnX+XDRTZUnoRBo03wZFYwOYwHUFWrlMFz+o6jOo2KZa4J+pcEDbttkJEoVhiV
C8EKIxSsG9TuW4lh5UySfB62ZfQBNd8SyhD4LF5JHFbBV9ysnylg/8gVvavjHWtM
FleOk/lNi9oj2n8uQEGbBT6FpTaKKJDiF85E3J4fuv8iNiFy2nWhCpJFF9K5/hu6
s41Y2ZRb7luBoUj+CQWlTpGjCJPzzWmSkZUjf5kRP6b01bAXoLBzBBn50KWf8DWw
Rx7VaHtzbBRxfzESQxslGRgZvz2KxjY/x2jH6xdW+bzt93aHkgeGkDrjpY9q/uQF
SyEJYHIDt+FZupcc3KjvTY2p+icbckOrwQAtVWXzEjCsnDjf33+O7VGdACDm
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCdQCgwn7M5jtkN
zYv/yUelUV84ehsYjPTQspCXCjDdUltOE6jafRM/2SopUWwzPTorIZkerIpqq/av
i77e6wSdCKJZBA5DsrMSOmFjjMqG4H2beuzr5b+ZKzKnw4nF/CuB2cmWkz/qAilu
qVdRl+Gef/Ouyuer/NuhJhK6Mgy1VuAdynwizGsAgQ/oAGDuHWmcMhGw/2NVTnXq
Hei5ntr+F4r08YOgXnRsGId+16nJU5xdXs80sw2dFl+1zQiFJxWjVQk65CyZfLDc
wC7e3PAMsT+8obzuBKCn6fW3JiIZ68tco8GZClHSkeS7IMp+/wtjMXf1iipE0bRW
zzdl1xQnAgMBAAECggEAMRDQuYNLJ/2DioQFV/WVDmdaf8PR6pIo3WmqJga/An/t
D2qg+DOoqvZ26leGnGJRYR3lqiWKNwibO2EuWF4anWkRRxc14DfFGj3vH2HR2832
Q2pSvLR+WSuabbBcr9MkPCsZdItTmQ+9n9Lk9QegFZW1Emgra4XFff3kQAbX4kjR
ATyEsvZsgnkK5jHi5bzVHEKY730MKyhYzmHmUgxH1ibD5x+tnFtRiBGK+qY24erW
rw+wu0YKu1ESp62orYDnaA8MH7aWlFZLniPB4liA0h0bElrgHA8Zndx+cEtXqF4m
5+igR3MjXbg2at80ah++z6EGUTtEPccRSpcA7qFqMQKBgQDFVp1msdn45Xwquw+8
CSZ45KAurvQEPNxtK/GMHtR5LjRZlKQEpNy3AVjz56azv72kqUfhNsP+ZNokMQTH
NTMvdkLzcwKe3qCnsfBRlSkRBVCwan266oCJW8SSX8pdaoME9lzc2BfMVAxJ1n+Y
289ZHXyyPYdNd+L4FxjreJDkKQKBgQDL/uD+kCSKZ/dYVZqwF06DIzYiBv1Xjne3
3H90J0h/2iU9/yX9UsCS7D5RWbVROUyMh64fpH0f2lVFT8Xo3SjBWISrMovOxEzl
C2wZwM2VQmrfhiUg9h46drJSl/JL9/V/QOnpuQe35bVWjROtFStaZagUK/3vx1hh
xCV4iVS/zwKBgQC2Ea3zvA/x9jlTa3ee84pNbBLmP4DgEA8Hos2fjCpZC+o85ElY
B4ukRVf+4TILEdM1AwJQpii6o+4oChnwegMZvTEUUH6QebMcRa4Gd2qGS7MgsYAD
XqztDoAU1NBu1ADCKVOQZse+O6WC0qazL8rk27Ha+a3GKeB9KUJSrtBv0QKBgCi5
IftPlSvYI2WL+Uxr6q19KwJR+OMwuq+Goh7y9KMpTkP5GoFesrjh1nLxAKRNVv26
3ETO1ne0Y09p5G1fMRKf9CQk/Anz4BHdXOArQB8q2iDzK5hP6arsJR8d3C3UOzsD
H28cE/FfNvsnQKVN05DBOHOGcLQcTIV/3acZa0S7AoGBAJqSujzhVkewH7wJWOOh
92F66vMnCsE4Qn10h84GQQWORgjr8z8d8UbW7VrobVfZP74IBqo3ZUBYm85+lUFu
FWHjTkXoZzwP8IvMW0gE2iC3W4QSr2Z96a6RlRJdxC70LB+Pm6FFwLTloKBGK8Bf
Kp53gUDjsvJcQGv6nOty0uHQ
-----END PRIVATE KEY-----

View File

@ -0,0 +1,18 @@
server_name: $NATS_NAME
port: 4222
http_port: 8222
authorization: {
users: [
{user: $NATS_USER, password: $NATS_PW}
]
}
tls: {
cert_file: "/tmp/nats/config/cert.pem"
key_file: "/tmp/nats/config/key.pem"
ca_file: "/tmp/nats/config/rootCA.pem"
}
// enables jetstream, an empty block will enable and use defaults
jetstream {
// jetstream data will be in /data/nats-server/jetstream
store_dir: "/tmp/nats/jetstream"
}

View File

@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE6zCCA1OgAwIBAgIQcw3hWvi1nfQFSkUzrcdrhTANBgkqhkiG9w0BAQsFADCB
jTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTEwLwYDVQQLDChjaGll
c2FAQWRyaWFub01hYy5sb2NhbCAoQWRyaWFubyBDaGllc2EpMTgwNgYDVQQDDC9t
a2NlcnQgY2hpZXNhQEFkcmlhbm9NYWMubG9jYWwgKEFkcmlhbm8gQ2hpZXNhKTAe
Fw0yMjA4MTIxNTM1NDdaFw0zMjA4MTIxNTM1NDdaMIGNMR4wHAYDVQQKExVta2Nl
cnQgZGV2ZWxvcG1lbnQgQ0ExMTAvBgNVBAsMKGNoaWVzYUBBZHJpYW5vTWFjLmxv
Y2FsIChBZHJpYW5vIENoaWVzYSkxODA2BgNVBAMML21rY2VydCBjaGllc2FAQWRy
aWFub01hYy5sb2NhbCAoQWRyaWFubyBDaGllc2EpMIIBojANBgkqhkiG9w0BAQEF
AAOCAY8AMIIBigKCAYEA4HBEZqDbKPB0StZX/ILPU1q2Tm/U4j32/HvaWOGt528q
15y5eqa4OqpYQ7waVhkSR+M6258yAk9BCCOGrI+oXcllO7LKv4bg/y+HVhfEg4E0
fnZS583+VWrIcIKOENQQX1cbFnIqLldBL/ph6YNMDT8wOoLqtQ2vp1EiXFA3ghQs
fK3iqDL31SQ7cZhdw7hnNACgvIM6MipwFLYbMsm27eMa9fCL4ZB3xokhbYVBmEcT
RTdhGvcRBs+QF8fcP/Y6uC9y7q2ddeIYEAf5xCVc15QepPqVskuAM3rktzVpBKEl
lzJGtW3EWu3kojrrW8dYZkZFJkHy9ymZVNUPjpAGiIIylp1Rik/o9VttXi+BQmrV
l1tpkrU1JfhLcktjBtRxtvRJTARpSkMM8gVc1rA2KgNsULzF4Zaas12wUh7r/VzW
4lwKiV3vgAM0wE5Fo4NLmv53/j3w7yX9GtSq0Ck/cKu2k0cOe+PTlG9aQ5MdhLCx
+BwMij/ohBfh0qtgPVPdAgMBAAGjRTBDMA4GA1UdDwEB/wQEAwICBDASBgNVHRMB
Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBR9ihLDMEu/0LtXhNQ8UYWUyNGe0TANBgkq
hkiG9w0BAQsFAAOCAYEAyGNw197jniIGfCpcArC2lHbzNIQCq97BXsOj4h+CaUB+
ZfTpTOdtS5DK2fSQ4jwH6nzm7VQvrnHcbP2pCgVDWW9da+Acnh1U6dYXI1VPDDVA
KZGDiK63M6PneX3IDl2N2fubRDP1ALkZQ6zwjtJLC2nT6IVjAJdRNt8egbCUAyMb
nOb4F+Zrr0L2SILM0jHq9diJVpX4Lxqggldmd+5HdVGHoR8BGzw4MReuoRWOZtf/
H+opFtJ2f8GNTWsWtmN7FsjrGOOBgREB/Tz7WCWgGEeXQveJEfkOIddVVgb/n9Ly
mEOTbtJj36o4JJzv0v42SdT1WUkdvc6AmLVmYFn0uaLohNLVelJmU+aGavuVFpFb
08uVsSl8JMBWwVO23CpuvZFbI/1BFVix85UZgDj8BnpAWlsJxPYV1HqxS2oJJJbn
zRf8/0xzFPrLirXzebIr4jiCRVBRamacQTxlvPUz6c6FLTQLaqQdUV/IZaXsXX38
byH1ABmJPGjXLm1r2RWD
-----END CERTIFICATE-----

View File

@ -11,19 +11,7 @@ server {
}
location / {
proxy_pass http://frontend:3000;
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
}
location /api {
proxy_pass http://controller:8000;
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
}
location /socket.io {
proxy_pass http://socketio:5000;
proxy_pass http://host.docker.internal:3000;
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
@ -34,6 +22,33 @@ server {
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /api {
proxy_pass http://host.docker.internal:8000;
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
}
location /custom-frontend {
proxy_pass http://host.docker.internal:8005;
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
}
location /socket.io {
proxy_pass http://host.docker.internal:5000;
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
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;
}
location /companylink {
return 301 https://oktopus.app.br/controller;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {

View File

@ -1,6 +1,5 @@
# ----------------------------- Local Environment ---------------------------- #
NEXT_PUBLIC_REST_ENDPOINT="http://localhost:8000"
NEXT_PUBLIC_WS_ENDPOINT="http://localhost:5000/"
NEXT_PUBLIC_ENTERPRISE_VERSION="false"
NEXT_PUBLIC_GOOGLE_MAPS_KEY=""

View File

@ -18,14 +18,14 @@
"@mui/system": "5.11.9",
"@mui/x-date-pickers": "5.0.19",
"@react-google-maps/api": "^2.19.3",
"apexcharts": "3.37.0",
"apexcharts": "^3.37.0",
"date-fns": "2.29.3",
"formik": "2.2.9",
"next": "^14.2.4",
"nprogress": "0.2.0",
"prop-types": "15.8.1",
"react": "^18.3.1",
"react-apexcharts": "1.4.0",
"react-apexcharts": "^1.4.0",
"react-dom": "^18.3.1",
"simple-peer": "^9.11.1",
"simplebar-react": "^3.2.1",
@ -1480,6 +1480,11 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@yr/monotone-cubic-spline": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA=="
},
"node_modules/acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
@ -1538,10 +1543,11 @@
}
},
"node_modules/apexcharts": {
"version": "3.37.0",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.37.0.tgz",
"integrity": "sha512-0mg1gDKUo3JG00Q//LK0jEXBS6OLjpuglqZ8ec9cqfA5oP8owopD9n5EhfARbWROb5o8GSPzFuohTJiCm2ecWw==",
"version": "3.50.0",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.50.0.tgz",
"integrity": "sha512-LJT1PNAm+NoIU3aogL2P+ViC0y/Cjik54FdzzGV54UNnGQLBoLe5ok3fxsJDTgyez45BGYT8gqNpYKqhdfy5sg==",
"dependencies": {
"@yr/monotone-cubic-spline": "^1.0.3",
"svg.draggable.js": "^2.2.2",
"svg.easing.js": "^2.0.0",
"svg.filter.js": "^2.0.2",
@ -4722,14 +4728,14 @@
}
},
"node_modules/react-apexcharts": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.4.0.tgz",
"integrity": "sha512-DrcMV4aAMrUG+n6412yzyATWEyCDWlpPBBhVbpzBC4PDeuYU6iF84SmExbck+jx5MUm4U5PM3/T307Mc3kzc9Q==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.4.1.tgz",
"integrity": "sha512-G14nVaD64Bnbgy8tYxkjuXEUp/7h30Q0U33xc3AwtGFijJB9nHqOt1a6eG0WBn055RgRg+NwqbKGtqPxy15d0Q==",
"dependencies": {
"prop-types": "^15.5.7"
"prop-types": "^15.8.1"
},
"peerDependencies": {
"apexcharts": "^3.18.0",
"apexcharts": "^3.41.0",
"react": ">=0.13"
}
},
@ -7102,6 +7108,11 @@
"eslint-visitor-keys": "^3.3.0"
}
},
"@yr/monotone-cubic-spline": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA=="
},
"acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
@ -7142,10 +7153,11 @@
}
},
"apexcharts": {
"version": "3.37.0",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.37.0.tgz",
"integrity": "sha512-0mg1gDKUo3JG00Q//LK0jEXBS6OLjpuglqZ8ec9cqfA5oP8owopD9n5EhfARbWROb5o8GSPzFuohTJiCm2ecWw==",
"version": "3.50.0",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.50.0.tgz",
"integrity": "sha512-LJT1PNAm+NoIU3aogL2P+ViC0y/Cjik54FdzzGV54UNnGQLBoLe5ok3fxsJDTgyez45BGYT8gqNpYKqhdfy5sg==",
"requires": {
"@yr/monotone-cubic-spline": "^1.0.3",
"svg.draggable.js": "^2.2.2",
"svg.easing.js": "^2.0.0",
"svg.filter.js": "^2.0.2",
@ -9444,11 +9456,11 @@
}
},
"react-apexcharts": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.4.0.tgz",
"integrity": "sha512-DrcMV4aAMrUG+n6412yzyATWEyCDWlpPBBhVbpzBC4PDeuYU6iF84SmExbck+jx5MUm4U5PM3/T307Mc3kzc9Q==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.4.1.tgz",
"integrity": "sha512-G14nVaD64Bnbgy8tYxkjuXEUp/7h30Q0U33xc3AwtGFijJB9nHqOt1a6eG0WBn055RgRg+NwqbKGtqPxy15d0Q==",
"requires": {
"prop-types": "^15.5.7"
"prop-types": "^15.8.1"
}
},
"react-dom": {

View File

@ -24,14 +24,14 @@
"@mui/system": "5.11.9",
"@mui/x-date-pickers": "5.0.19",
"@react-google-maps/api": "^2.19.3",
"apexcharts": "3.37.0",
"apexcharts": "^3.37.0",
"date-fns": "2.29.3",
"formik": "2.2.9",
"next": "^14.2.4",
"nprogress": "0.2.0",
"prop-types": "15.8.1",
"react": "^18.3.1",
"react-apexcharts": "1.4.0",
"react-apexcharts": "^1.4.0",
"react-dom": "^18.3.1",
"simple-peer": "^9.11.1",
"simplebar-react": "^3.2.1",

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,13 +1,16 @@
import PropTypes from 'prop-types';
import NextLink from 'next/link';
import Link from 'next/link'
import { Box, Typography, Unstable_Grid2 as Grid } from '@mui/material';
import { Box, Typography, Unstable_Grid2 as Grid, Stack } from '@mui/material';
import { Logo } from 'src/components/logo';
// TODO: Change subtitle text
import { useTheme, useMediaQuery } from '@mui/material'
export const Layout = (props) => {
const { children } = props;
const lgUp = useMediaQuery((theme) => theme.breakpoints.up('lg'));
const theme = useTheme();
console.log("logUp", lgUp)
return (
<Box
@ -60,7 +63,7 @@ export const Layout = (props) => {
lg={6}
sx={{
alignItems: 'center',
background: 'radial-gradient(50% 50% at 50% 50%, #306D6F 0%, #255355 100%)',
background: `radial-gradient(50% 50% at 50% 50%, ${theme.palette.primary.main} 0%, ${theme.palette.primary.dark } 100%)`,
color: 'white',
display: 'flex',
justifyContent: 'center',
@ -70,20 +73,32 @@ export const Layout = (props) => {
}}
>
<Box sx={{ p: 3 }}>
<Link href="https://oktopus.app.br" target="_blank">
<img
alt=""
src="/assets/oktopus.png"
/>
</Link>
<Typography
align="center"
sx={{ mb: 3 }}
>
</Typography>
<Link href="http://localhost/companylink" target="_blank">
<img
alt=""
src="http://localhost:8004/images/logo.png"
/>
</Link>
</Box>
</Grid>
</Grid>
<Stack style={{position:"absolute", bottom:"2px", left:"2px"}} direction={"row"} spacing={"1"}>
<Typography
align="center"
color={lgUp ? 'neutral[900]' : 'primary.contrastText'}
component="footer"
variant="body2"
sx={{ p: 2 }}
>
Powered by
</Typography>
</Stack>
<a href='https://oktopus.app.br' style={{position:"absolute", bottom:"10px", left:"100px"}} target='_blank'>
<img
src="/assets/logo.png"
alt="Oktopus logo image"
width={80}/>
</a>
</Box>
);
};

View File

@ -35,15 +35,15 @@ export const items = [
// </SvgIcon>
// )
// },
// {
// title: 'Map',
// path: '/map',
// icon: (
// <SvgIcon fontSize="small">
// <MapIcon/>
// </SvgIcon>
// ),
// },
{
title: 'Map',
path: '/map',
icon: (
<SvgIcon fontSize="small">
<MapIcon/>
</SvgIcon>
),
},
{
title: 'Credentials',
path: '/credentials',

View File

@ -4,8 +4,6 @@ import { styled } from '@mui/material/styles';
import { withAuthGuard } from 'src/hocs/with-auth-guard';
import { SideNav } from './side-nav';
import { TopNav } from './top-nav';
import Image from 'next/image'
import { Link } from '@mui/material';
const SIDE_NAV_WIDTH = 280;

View File

@ -2,8 +2,6 @@ import NextLink from 'next/link';
import { usePathname } from 'next/navigation';
import PropTypes from 'prop-types';
import Link from 'next/link'
import ArrowTopRightOnSquareIcon from '@heroicons/react/24/solid/ArrowTopRightOnSquareIcon';
import ChevronUpDownIcon from '@heroicons/react/24/solid/ChevronUpDownIcon';
import {
Box,
Button,
@ -18,12 +16,15 @@ import { Logo } from 'src/components/logo';
import { Scrollbar } from 'src/components/scrollbar';
import { items } from './config';
import { SideNavItem } from './side-nav-item';
import { useTheme } from '@mui/material';
export const SideNav = (props) => {
const { open, onClose } = props;
const pathname = usePathname();
const lgUp = useMediaQuery((theme) => theme.breakpoints.up('lg'));
const theme = useTheme();
const isItemActive = (currentPath, itemPath) => {
if (currentPath === itemPath) {
return true;
@ -34,7 +35,6 @@ export const SideNav = (props) => {
}
return false;
//TODO: test frontend with color of the landing page
}
const content = (
@ -80,9 +80,9 @@ export const SideNav = (props) => {
p: '12px'
}}
>
<Link href="https://oktopus.app.br" target="_blank">
<Link href="http://localhost/companylink" target="_blank">
<div style={{display:'flex',justifyContent:'center'}}>
<img src="/assets/oktopus.png"
<img src="http://localhost:8004/images/logo.png"
width={'60%'}
/>
</div>
@ -113,6 +113,9 @@ export const SideNav = (props) => {
}}
>
{items.map((item) => {
if (item.title == "Map" && process.env.NEXT_PUBLIC_ENTERPRISE_VERSION != "true"){
return
}
const active = isItemActive(pathname, item.path);
return (
@ -129,7 +132,24 @@ export const SideNav = (props) => {
})}
</Stack>
</Box>
<Divider sx={{ borderColor: 'neutral.700' }} />
<Stack style={{position:"absolute", bottom:"2px", left:"2px"}} direction={"row"} spacing={"1"} zIndex={9999}>
<Typography
align="center"
color="primary.contrastText"
component="footer"
variant="body2"
sx={{ p: 2 }}
>
Powered by
</Typography>
</Stack>
<a href='https://oktopus.app.br' style={{position:"absolute", bottom:"10px", left:"100px"}} target='_blank'>
<img
src="/assets/logo.png"
alt="Oktopus logo image"
width={80}
/>
</a>
</Box>
</Scrollbar>
);
@ -141,7 +161,7 @@ export const SideNav = (props) => {
open
PaperProps={{
sx: {
backgroundColor: 'neutral.800',
background: `linear-gradient(0deg, ${theme.palette.neutral["800"]} 0%, ${theme.palette.primary.dark} 90%);`,
color: 'common.white',
width: 280
}
@ -160,7 +180,7 @@ export const SideNav = (props) => {
open={open}
PaperProps={{
sx: {
backgroundColor: 'neutral.800',
background: `linear-gradient(0deg, ${theme.palette.primary.main} 0%, ${theme.palette.primary.dark} 90%);`,
color: 'common.white',
width: 280
}

View File

@ -10,21 +10,26 @@ import { createTheme } from 'src/theme';
import { createEmotionCache } from 'src/utils/create-emotion-cache';
import 'simplebar-react/dist/simplebar.min.css';
import { WsProvider } from 'src/contexts/socketio-context';
import '../utils/map.css';
import { useEffect, useState } from 'react';
const clientSideEmotionCache = createEmotionCache();
const SplashScreen = () => null;
const App = (props) => {
const [theme, setTheme] = useState(null);
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
useNProgress();
const getLayout = Component.getLayout ?? ((page) => page);
const theme = createTheme();
useEffect(() => {
setTheme(createTheme());
}, []);
return (
return theme && (
<CacheProvider value={emotionCache}>
<Head>
<title>

View File

@ -1,39 +1,163 @@
import Head from 'next/head';
import { GoogleMap, useLoadScript } from "@react-google-maps/api"
import { GoogleMap, useLoadScript, Marker, OverlayView } from "@react-google-maps/api"
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
import { useEffect, useMemo, useState } from 'react';
import mapStyles from '../utils/mapStyles.json';
import { useRouter } from 'next/router';
const getPixelPositionOffset = pixelOffset => (width, height) => ({
x: -(width / 2) + pixelOffset.x,
y: -(height / 2) + pixelOffset.y
});
const Popup = props => {
return (
<OverlayView
position={props.anchorPosition}
mapPaneName={OverlayView.OVERLAY_MOUSE_TARGET}
getPixelPositionOffset={getPixelPositionOffset(props.markerPixelOffset)}
>
<div className="popup-tip-anchor">
<div className="popup-bubble-anchor">
<div className="popup-bubble-content">{props.content}</div>
</div>
</div>
</OverlayView>
);
};
const Page = () => {
const libraries = useMemo(() => ['places'], []);
const router = useRouter();
const [mapRef, setMapRef] = useState(null);
const [mapCenter, setMapCenter] = useState(null);
const [markers, setMarkers] = useState([]);
const [activeMarker, setActiveMarker] = useState(null);
const [activeMarkerdata, setActiveMarkerdata] = useState(null);
const [zoom, setZoom] = useState(null)
const handleDragEnd = () => {
if (mapRef) {
const newCenter = mapRef.getCenter();
console.log("newCenter:",newCenter.lat(), newCenter.lng());
localStorage.setItem("mapCenter", JSON.stringify({"lat":newCenter.lat(),"lng":newCenter.lng()}))
}
}
const handleZoomChange = () => {
if (mapRef) {
const newZoom = mapRef.getZoom();
console.log("new zoom", newZoom)
localStorage.setItem("zoom", newZoom)
}
}
const handleOnLoad = map => {
setMapRef(map);
};
const fetchMarkers = async () => {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", localStorage.getItem("token"));
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
let result = await fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/map`, requestOptions)
if (result.status == 200) {
const content = await result.json()
setMarkers(content)
}else if (result.status == 403) {
console.log("num tenx permissão, seu boca de sandália")
return router.push("/403")
}else if (result.status == 401){
console.log("taix nem autenticado, sai fora oh")
return router.push("/auth/login")
} else {
setMarkers([])
console.log("error to get map markers")
}
}
const fetchActiveMarkerData = async (id) => {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", localStorage.getItem("token"));
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
let result = await fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device?id=`+id, requestOptions)
if (result.status == 200) {
const content = await result.json()
setActiveMarkerdata(content)
}else if (result.status == 403) {
return router.push("/403")
}else if (result.status == 401){
return router.push("/auth/login")
} else {
console.log("no device info found")
const content = await result.json()
}
}
useEffect(()=> {
// Check if geolocation is supported by the browser
if ("geolocation" in navigator) {
// Prompt user for permission to access their location
navigator.geolocation.getCurrentPosition(
// Get the user's latitude and longitude coordinates
// Success callback function
function(position) {
// Update the map with the user's new location
setMapCenter({
lat: position.coords.latitude,
lng: position.coords.longitude,
})
},
// Error callback function
function(error) {
// Handle errors, e.g. user denied location sharing permissions
console.error("Error getting user location:", error);
}
);
} else {
// Geolocation is not supported by the browser
console.error("Geolocation is not supported by this browser.");
}
fetchMarkers();
let zoomFromLocalStorage = localStorage.getItem("zoom")
if (zoomFromLocalStorage) {
setZoom(Number(zoomFromLocalStorage))
}else{
setZoom(25)
}
let mapCenterFromLocalStorage = localStorage.getItem("mapCenter")
if (mapCenterFromLocalStorage){
let fmtMapCenter = JSON.parse(localStorage.getItem("mapCenter"))
console.log("mapCenterFromLocalStorage:", fmtMapCenter)
setMapCenter({
lat: Number(fmtMapCenter.lat),
lng: Number(fmtMapCenter.lng),
})
return
}
// Check if geolocation is supported by the browser
if ("geolocation" in navigator) {
// Prompt user for permission to access their location
navigator.geolocation.getCurrentPosition(
// Get the user's latitude and longitude coordinates
// Success callback function
function(position) {
// Update the map with the user's new location
setMapCenter({
lat: position.coords.latitude,
lng: position.coords.longitude,
})
},
// Error callback function
function(error) {
// Handle errors, e.g. user denied location sharing permissions
console.log("Error getting user location:", error);
}
);
} else {
// Geolocation is not supported by the browser
console.log("Geolocation is not supported by this browser, or the user denied access");
}
},[])
const mapOptions = useMemo(
@ -59,7 +183,7 @@ const Page = () => {
return <p>Loading...</p>;
}
return ( mapCenter &&
return ( markers && zoom &&
<>
<Head>
<title>
@ -68,12 +192,61 @@ const Page = () => {
</Head>
<GoogleMap
options={mapOptions}
zoom={14}
center={mapCenter}
zoom={zoom}
center={mapCenter ? mapCenter : {
lat: 0.0,
lng: 0.0,
}}
mapContainerStyle={{ width: '100%', height: '100%' }}
onLoad={() => console.log('Map Component Loaded...')}
onLoad={handleOnLoad}
clickableIcons={false}
/>
onDragEnd={handleDragEnd}
onZoomChanged={handleZoomChange}
>
{
markers.map((marker, index) => (
<Marker
key={index}
position={{ lat: marker.coordinates.lat, lng: marker.coordinates.lng }}
icon={{
url: marker.img,
scaledSize: new window.google.maps.Size(50, 50),
anchor: new window.google.maps.Point(25, 25),
}}
draggable={false}
clickable={true}
onClick={() => {
setActiveMarkerdata(null);
if (activeMarker?.sn === marker.sn) {
setActiveMarker(null);
return;
}
fetchActiveMarkerData(marker.sn);
setActiveMarker({
sn: marker.sn,
position: { lat: marker.coordinates.lat, lng: marker.coordinates.lng }
});
}}
>
</Marker>
))
}
{activeMarker &&
<Popup
anchorPosition={activeMarker.position}
markerPixelOffset={{ x: 0, y: -32 }}
content={activeMarkerdata ?
<div>
<div>SN: {activeMarker.sn}</div>
<div>
<div>Model: {activeMarkerdata.Model?activeMarkerdata.Model:activeMarkerdata.ProductClass}</div>
<div>Alias: {activeMarkerdata.Alias}</div>
<div>Status: {activeMarkerdata.Status == 2 ? <span style={{color:"green"}}>online</span> : <span style={{color:"red"}}>offline</span>}</div>
</div>
</div>
: <p>no device info found</p>}
/>}
</GoogleMap>
</>
)};

View File

@ -16,6 +16,19 @@ export const ConnectedDevices = () => {
const [interfaces, setInterfaces] = useState([]);
const [interfaceValue, setInterfaceValue] = useState(null);
const getConnectionState = (rssi) => {
let connectionStatus = "Signal "
if (rssi > -30) {
return connectionStatus + "Excellent"
} else if (rssi > -60) {
return connectionStatus + "Good"
} else if (rssi > -70) {
return connectionStatus + "Bad"
} else {
return connectionStatus + "Awful"
}
}
const fetchConnectedDevicesData = async () => {
var myHeaders = new Headers();
@ -23,95 +36,113 @@ export const ConnectedDevices = () => {
myHeaders.append("Authorization", localStorage.getItem("token"));
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device/${router.query.id[0]}/connecteddevices`, requestOptions)
.then(response => {
if (response.status === 401) {
router.push("/auth/login")
}
return response.json()
})
.then(result => {
console.log("connecteddevices content", result)
let interfaces = Object.keys(result)
setInterfaces(interfaces)
setInterfaceValue(interfaces[0])
setContent(result)
})
.catch(error => console.log('error', error));
.then(response => {
if (response.status === 401) {
router.push("/auth/login")
}
return response.json()
})
.then(result => {
console.log("connecteddevices content", result)
let interfaces = Object.keys(result)
setInterfaces(interfaces)
setInterfaceValue(interfaces[0])
setContent(result)
})
.catch(error => console.log('error', error));
};
useEffect(() => {
fetchConnectedDevicesData();
},[])
}, [])
return (
<Stack
justifyContent="center"
alignItems={(!content || interfaces.length == 0) &&"center"}
<Stack
justifyContent="center"
alignItems={(!content || interfaces.length == 0) && "center"}
>
{content && interfaces.length > 0 ?
<Card>
<CardContent>
<Grid mb={3}>
<InputLabel> Interface </InputLabel>
<Select label="interface" variant="standard" value={interfaceValue} onChange={(e)=> setInterfaceValue(e.target.value)}>
{(
interfaces.map((item, index) => (
<MenuItem key={index} value={item}>
{item}
</MenuItem>
{content && interfaces.length > 0 ?
<Card>
<CardContent>
<Grid mb={3}>
<InputLabel> Interface </InputLabel>
<Select label="interface" variant="standard" value={interfaceValue} onChange={(e) => setInterfaceValue(e.target.value)}>
{(
interfaces.map((item, index) => (
<MenuItem key={index} value={item}>
{item}
</MenuItem>
))
)}
</Select>
</Grid>
{
content[interfaceValue].map((property, index) => (
<Card key={index}>
<CardContent>
<Grid container justifyContent={"center"}>
<Stack direction="row" spacing={5}>
<Stack justifyItems={"center"} direction={"row"} mt={2}>
<Tooltip title={property.active ? "Online" : "Offline"}>
<SvgIcon>
<CpuChipIcon color={property.active ? theme.palette.success.main : theme.palette.error.main}></CpuChipIcon>
</SvgIcon>
</Tooltip>
<Typography ml={"10px"}>
{property.hostname}
</Typography>
</Stack>
<Divider orientation="vertical" />
<Stack spacing={2}>
<Typography>
IP address: {property.ip_adress}
</Typography>
<Typography>
MAC: {property.mac}
</Typography>
</Stack>
<Stack spacing={2}>
<Typography>
Source: {property.adress_source}
</Typography>
<Tooltip title={getConnectionState(property.rssi)}>
<Typography display={"flex"} color={() => {
let rssi = property.rssi
if(rssi == 0){
return theme.palette.neutral[900]
} else if (rssi > -30) {
return theme.palette.success.main
} else if (rssi > -60) {
return theme.palette.success.main
} else if (rssi > -70) {
return theme.palette.warning.main
} else {
return theme.palette.error.main
}
}}>
<Typography color={theme.palette.neutral[900]} sx={{pr:"5px"}}>
RSSI:
</Typography>
{property.rssi} dbm
</Typography>
</Tooltip>
</Stack>
</Stack>
</Grid>
</CardContent>
</Card>
))
)}
</Select>
</Grid>
{
content[interfaceValue].map((property,index) => (
<Card key={index}>
<CardContent>
<Grid container justifyContent={"center"}>
<Stack direction="row" spacing={5}>
<Stack justifyItems={"center"} direction={"row"} mt={2}>
<Tooltip title={property.active ? "Online": "Offline"}>
<SvgIcon>
<CpuChipIcon color={property.active ? theme.palette.success.main : theme.palette.error.main}></CpuChipIcon>
</SvgIcon>
</Tooltip>
<Typography ml={"10px"}>
{property.hostname}
</Typography>
</Stack>
<Divider orientation="vertical"/>
<Stack spacing={2}>
<Typography>
IP address: {property.ip_adress}
</Typography>
<Typography>
MAC: {property.mac}
</Typography>
</Stack>
<Stack spacing={2}>
<Typography>
RSSI: {property.rssi} dbm
</Typography>
<Typography>
Source: {property.adress_source}
</Typography>
</Stack>
</Stack>
</Grid>
</CardContent>
</Card>
))
}
</CardContent>
</Card>: (
content ? <Typography> No connected devices found </Typography> : <CircularProgress/>
)}
}
</CardContent>
</Card> : (
content ? <Typography> No connected devices found </Typography> : <CircularProgress />
)}
</Stack>
)
}

View File

@ -1,31 +1,31 @@
import { useCallback, useEffect, useState } from 'react';
import {
Button,
Card,
CardActions,
CardContent,
CardHeader,
Divider,
Stack,
TextField,
InputLabel,
MenuItem,
Select,
FormControl,
SvgIcon,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
Box,
IconButton,
Icon,
SnackbarContent,
Snackbar,
Checkbox,
FormControlLabel,
useTheme,
Button,
Card,
CardActions,
CardContent,
CardHeader,
Divider,
Stack,
TextField,
InputLabel,
MenuItem,
Select,
FormControl,
SvgIcon,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
Box,
IconButton,
Icon,
SnackbarContent,
Snackbar,
Checkbox,
FormControlLabel,
useTheme,
} from '@mui/material';
import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
import Check from '@heroicons/react/24/outline/CheckIcon';
@ -54,42 +54,42 @@ export const DevicesWiFi = () => {
myHeaders.append("Authorization", localStorage.getItem("token"));
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device/${router.query.id[0]}/wifi`, requestOptions)
.then(response => {
if (response.status === 401) {
router.push("/auth/login")
}
return response.json()
})
.then(result => {
console.log("wifi content", result)
result.map((item) => {
let contentToApply = {
hasChanges: false,
path: item.path,
.then(response => {
if (response.status === 401) {
router.push("/auth/login")
}
setApplyContent(oldValue => [...oldValue, contentToApply])
return response.json()
})
setContent(result)
})
.catch(error => console.log('error', error));
.then(result => {
console.log("wifi content", result)
result.map((item) => {
let contentToApply = {
hasChanges: false,
path: item.path,
}
setApplyContent(oldValue => [...oldValue, contentToApply])
})
setContent(result)
})
.catch(error => console.log('error', error));
};
useEffect(()=>{
useEffect(() => {
fetchWifiData()
},[])
}, [])
return (<div>
<Stack
direction="row"
spacing={2}
justifyContent="center"
alignItems="center"
direction="row"
spacing={2}
justifyContent="center"
alignItems="center"
>
{content.length > 1 ?
(content.map((item, index) => {
@ -99,25 +99,25 @@ export const DevicesWiFi = () => {
title={item.name.value}
avatar={
<SvgIcon>
<GlobeAltIcon/>
<GlobeAltIcon />
</SvgIcon>
}
/>
<CardContent>
<Stack spacing={3}>
{ item.enable.value != null &&
<FormControlLabel control={<Checkbox defaultChecked={item.enable.value == 1 ? true : false}
onChange={(e) => {
let enable = item.enable.value == 1 ? "0" : "1"
console.log(enable)
applyContent[index].hasChanges = true
applyContent[index].enable = {
value : enable
}
setApplyContent([...applyContent])
item.enable.value = enable
}}/>}
label="Enabled" />}
{item.enable.value != null &&
<FormControlLabel control={<Checkbox defaultChecked={item.enable.value == 1 ? true : false}
onChange={(e) => {
let enable = item.enable.value == 1 ? "0" : "1"
console.log(enable)
applyContent[index].hasChanges = true
applyContent[index].enable = {
value: enable
}
setApplyContent([...applyContent])
item.enable.value = enable
}} />}
label="Enabled" />}
{item.ssid.value != null && <TextField
fullWidth
label="SSID"
@ -126,62 +126,86 @@ export const DevicesWiFi = () => {
onChange={(e) => {
applyContent[index].hasChanges = true
applyContent[index].ssid = {
value : e.target.value
value: e.target.value
}
setApplyContent([...applyContent])
item.ssid.value = e.target.value
}}
/>}
{item.securityCapabilities &&
<TextField
fullWidth
label="Encryption"
value={""}
/>}
<TextField
fullWidth
label="Encryption"
value={""}
/>}
{item.password.value != null &&
<TextField
fullWidth
type="password"
label="Password"
disabled={!item.password.writable}
value={item.password.value}
onChange={(e) => {
if (e.target.value.length >= 8) {
applyContent[index].hasChanges = true
}else{
applyContent[index].hasChanges = false
}
applyContent[index].password = {
value : e.target.value
}
setApplyContent([...applyContent])
item.password.value = e.target.value
console.log("applyContent: ", applyContent)
}}
/>}
<TextField
fullWidth
type="password"
label="Password"
disabled={!item.password.writable}
value={item.password.value}
onChange={(e) => {
if (e.target.value.length >= 8) {
applyContent[index].hasChanges = true
} else {
applyContent[index].hasChanges = false
}
applyContent[index].password = {
value: e.target.value
}
setApplyContent([...applyContent])
item.password.value = e.target.value
console.log("applyContent: ", applyContent)
}}
/>}
{item.channel?.value != null && item.possibleChannels?.value != null &&
<FormControl variant='filled'>
<InputLabel id="channel">Channel</InputLabel>
<Select
fullWidth
defaultValue={item.channel.value}
value={item.channel.value}
onChange={(e) => {
applyContent[index].hasChanges = true
applyContent[index].channel = {
value: e.target.value
}
setApplyContent([...applyContent])
item.channel.value = e.target.value
}}
label="Channel"
>
{item.possibleChannels.value.map((channel, index) => {
return (
<MenuItem key={index} value={channel}>{channel}</MenuItem>)
})}
</Select>
</FormControl>
}
{item.standard.value != null &&
<TextField
fullWidth
label="Standard"
disabled={!item.standard.writable}
value={item.standard.value}
onChange={(e) => {
applyContent[index].hasChanges = true
applyContent[index].standard = {
value : e.target.value
}
setApplyContent([...applyContent])
item.standard.value = e.target.value
}}
/>}
<TextField
fullWidth
label="Standard"
disabled={!item.standard.writable}
value={item.standard.value}
onChange={(e) => {
applyContent[index].hasChanges = true
applyContent[index].standard = {
value: e.target.value
}
setApplyContent([...applyContent])
item.standard.value = e.target.value
}}
/>}
</Stack>
<CardActions sx={{display:"flex", justifyContent:"flex-end"}}>
<CardActions sx={{ display: "flex", justifyContent: "flex-end" }}>
<Button
variant="contained"
disabled={!applyContent[index].hasChanges}
endIcon={<SvgIcon><Check /></SvgIcon>}
onClick={
()=>{
() => {
setApply(true)
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
@ -198,38 +222,38 @@ export const DevicesWiFi = () => {
redirect: 'follow'
};
fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device/${router.query.id[0]}/wifi`, requestOptions)
.then(response => {
if (response.status === 401) {
router.push("/auth/login")
}
if (response.status == 500) {
setErrorModal(true)
}
return response.json()
})
.then(result => {
if (errorModal) {
setErrorModalText(result)
}
setApply(false)
if (result == 1) {
setErrorModalText("This change could not be applied, or It's gonna be applied later on")
setErrorModal(true)
//TODO: fetch wifi data again
}
})
.catch(error => console.log('error', error));
.then(response => {
if (response.status === 401) {
router.push("/auth/login")
}
if (response.status == 500) {
setErrorModal(true)
}
return response.json()
})
.then(result => {
if (errorModal) {
setErrorModalText(result)
}
setApply(false)
if (result == 1) {
setErrorModalText("This change could not be applied, or It's gonna be applied later on")
setErrorModal(true)
//TODO: fetch wifi data again
}
})
.catch(error => console.log('error', error));
}
}
sx={{mt:'25px', mb:'-15px'}}
>
sx={{ mt: '25px', mb: '-15px' }}
>
Apply
</Button>
</CardActions>
</CardContent>
</Card>
)
})):
})) :
<CircularProgress />
}
</Stack>
@ -240,43 +264,43 @@ export const DevicesWiFi = () => {
<CircularProgress color="inherit" />
</Backdrop>
<Dialog open={errorModal && errorModalText != ""}
slotProps={{ backdrop: { style: { backgroundColor: 'rgba(255,255,255,0.5)' } } }}
fullWidth={ true }
maxWidth={"md"}
scroll={"paper"}
aria-labelledby="scroll-dialog-title"
aria-describedby="scroll-dialog-description"
>
<DialogTitle id="scroll-dialog-title">
slotProps={{ backdrop: { style: { backgroundColor: 'rgba(255,255,255,0.5)' } } }}
fullWidth={true}
maxWidth={"md"}
scroll={"paper"}
aria-labelledby="scroll-dialog-title"
aria-describedby="scroll-dialog-description"
>
<DialogTitle id="scroll-dialog-title">
<Box display="flex" alignItems="center">
<Box flexGrow={1} >Response</Box>
<Box>
<IconButton onClick={()=>{
setErrorModalText("")
setErrorModal(false)
}}>
<SvgIcon
>
< XMarkIcon/>
<IconButton onClick={() => {
setErrorModalText("")
setErrorModal(false)
}}>
<SvgIcon
>
< XMarkIcon />
</SvgIcon>
</IconButton>
</Box>
</Box>
</DialogTitle>
<DialogContent dividers={scroll === 'paper'}>
<DialogContentText id="scroll-dialog-description"tabIndex={-1}>
<pre style={{color: 'black'}}>
</DialogTitle>
<DialogContent dividers={scroll === 'paper'}>
<DialogContentText id="scroll-dialog-description" tabIndex={-1}>
<pre style={{ color: 'black' }}>
{errorModalText}
</pre>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={()=>{
setErrorModalText("")
setErrorModal(false)
}}>OK</Button>
</DialogActions>
</Dialog>
</div>
);
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => {
setErrorModalText("")
setErrorModal(false)
}}>OK</Button>
</DialogActions>
</Dialog>
</div>
);
};

View File

@ -1,6 +1,3 @@
import PropTypes from 'prop-types';
import ArrowPathIcon from '@heroicons/react/24/solid/ArrowPathIcon';
import ArrowRightIcon from '@heroicons/react/24/solid/ArrowRightIcon';
import {
Button,
Card,
@ -22,12 +19,11 @@ import {
TableHead,
TableRow,
TableContainer,
Paper,
Container,
CircularProgress
CircularProgress,
ToggleButton,
ToggleButtonGroup
} from '@mui/material';
import { Scrollbar } from 'src/components/scrollbar';
import { alpha, useTheme } from '@mui/material/styles';
import { useTheme } from '@mui/material/styles';
import { Chart } from 'src/components/chart';
import ChartBarSquareIcon from '@heroicons/react/24/outline/ChartBarSquareIcon';
import ListBulletIcon from '@heroicons/react/24/outline/ListBulletIcon';
@ -35,109 +31,256 @@ import { useRouter } from 'next/router';
import { Stack } from '@mui/system';
import { useEffect, useState } from 'react';
const useChartOptions = () => {
const theme = useTheme();
return {
chart: {
background: 'transparent',
stacked: false,
toolbar: {
show: true
}
},
colors: [
theme.palette.graphics.dark,
theme.palette.graphics.darkest,
theme.palette.graphics.light,
theme.palette.graphics.main,
theme.palette.graphics.lightest,
],
dataLabels: {
enabled: false
},
fill: {
opacity: 1,
type: 'solid'
},
grid: {
borderColor: theme.palette.divider,
strokeDashArray: 2,
xaxis: {
lines: {
show: false
}
},
yaxis: {
lines: {
show: true
}
}
},
legend: {
show: true
},
plotOptions: {
bar: {
columnWidth: '40px'
}
},
stroke: {
colors: ['transparent'],
show: true,
width: 2
},
theme: {
mode: theme.palette.mode
},
xaxis: {
axisBorder: {
color: theme.palette.divider,
show: true
},
axisTicks: {
color: theme.palette.divider,
show: true
},
categories: [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
],
labels: {
offsetY: 5,
style: {
colors: theme.palette.text.secondary
}
}
},
yaxis: {
labels: {
formatter: (value) => (value > 0 ? `${value}K` : `${value}`),
offsetX: -10,
style: {
colors: theme.palette.text.secondary
}
}
}
};
};
export const SiteSurvey = (props) => {
const chartSeries = [{ name: 'This year', data: [18, 16, 5, 8, 3, 14, 14, 16, 17, 19, 18, 20] }, { name: 'Last year', data: [12, 11, 4, 6, 2, 9, 9, 10, 11, 12, 13, 13] }]
// const getMaxChannel = () => {
// if (frequency == "2.4GHz") {
// return 13;
// } else {
// return 128;
// }
// }
// const geMinChannel = () => {
// if (frequency == "2.4GHz") {
// return 0;
// } else {
// return 36;
// }
// }
// const getChannelSpacing = () => {
// if (frequency == "2.4GHz") {
// return 1;
// } else {
// return 4;
// }
// }
// const getChannelAmount = () => {
// if (frequency == "2.4GHz") {
// return 13;
// } else {
// return 20;
// }
// }
const getCategories = () => {
}
const router = useRouter();
const [content, setContent] = useState(null);
const [frequency, setFrequency] = useState("2.4GHz");
const [view, setView] = useState("chart");
const [content, setContent] = useState(null);
const getSeries = () => {
let series = []
content[frequency].map((network) => {
let data = []
if (frequency == "2.4GHz") {
if (Number(network.bandwidth) == 20) {
data.push({"x": Number(network.channel) -2, "y": -100})
data.push({"x": Number(network.channel), "y": Number(network.signal_level)})
data.push({"x": Number(network.channel) +2, "y": -100})
}
if (Number(network.bandwidth) == 40) {
data.push({"x": Number(network.channel) -4, "y": -100})
data.push({"x": Number(network.channel), "y": Number(network.signal_level)})
data.push({"x": Number(network.channel) +4, "y": -100})
}
}else {
if (Number(network.bandwidth) == 20) {
data.push({"x": Number(network.channel) -4, "y": -100})
data.push({"x": Number(network.channel), "y": Number(network.signal_level)})
data.push({"x": Number(network.channel) +4, "y": -100})
}
if (Number(network.bandwidth) == 40) {
data.push({"x": Number(network.channel) -8, "y": -100})
data.push({"x": Number(network.channel), "y": Number(network.signal_level)})
data.push({"x": Number(network.channel) +8, "y": -100})
}
if (Number(network.bandwidth) == 80) {
data.push({"x": Number(network.channel) -16, "y": -100})
data.push({"x": Number(network.channel), "y": Number(network.signal_level)})
data.push({"x": Number(network.channel) +16, "y": -100})
}
if (Number(network.bandwidth) == 160) {
data.push({"x": Number(network.channel) -32, "y": -100})
data.push({"x": Number(network.channel), "y": Number(network.signal_level)})
data.push({"x": Number(network.channel) +32, "y": -100})
}
}
let ssid = network.ssid
if ( ssid == "") {
ssid = " "
}
return series.push({
name: ssid,
data: data
})
})
return series;
}
const useChartOptions = () => {
const theme = useTheme();
return {
chart: {
background: 'transparent',
stacked: false,
toolbar: {
show: true
},
zoom: {
enabled: false
},
},
title: {
text: 'Site Survey Results',
},
// markers: {
// size: 5,
// hover: {
// size: 9
// }
// },
colors: [
theme.palette.graphics.dark,
theme.palette.warning.main,
theme.palette.graphics.darkest,
theme.palette.graphics.main,
theme.palette.info.light,
theme.palette.graphics.lightest,
theme.palette.primary.main,
theme.palette.graphics.light,
theme.palette.error.light,
theme.palette.error.dark
],
dataLabels: {
enabled: false
},
grid: {
//borderColor: theme.palette.divider,
strokeDashArray: 2,
xaxis: {
lines: {
show: true
}
},
yaxis: {
lines: {
show: true
},
},
},
legend: {
show: true,
showForSingleSeries: true,
},
plotOptions: {
area: {
fillTo: 'end',
}
},
stroke: {
show: true,
curve: 'smooth',
lineCap: 'round',
},
theme: {
mode: theme.palette.mode
},
yaxis: {
min: -100,
max: 0,
labels: {
formatter: function (value) {
return value + ' dBm';
},
//offsetY: -10,
style: {
//colors: theme.palette.text.secondary
}
},
},
// annotations: {
// xaxis: [
// {
// x: 9,
// x2: 10,
// borderColor: '#0b54ff',
// label: {
// style: {
// color: 'black',
// },
// text: 'Channel 10',
// offsetX: 45,
// borderColor: 'transparent',
// style: {
// background: 'transparent',
// color: theme.palette.text.secondary,
// fontSize: '17px',
// },
// }
// }
// ]
// },
// annotations: {
// points: [{
// x: 9,
// y: -5,
// label: {
// borderColor: '#775DD0',
// offsetY: 0,
// style: {
// color: '#fff',
// background: '#775DD0',
// },
// rotate: -45,
// text: 'Bananas are good',
// }
// }]
// },
xaxis: {
// tickPlacement: 'on',
// tickAmount: getChannelAmount(),
tickPlacement: 'on',
labels: {
show: true,
style: {
//colors: theme.palette.text.secondary
},
trim: true,
},
// max: getMaxChannel(),
// min: geMinChannel(),
// stepSize: getChannelSpacing(),
//type: 'category',
//categories: [getCategories()],
type: 'numeric',
decimalsInFloat: 0,
},
tooltip: {
x: {
show: true,
formatter: (seriesName) => "Channel "+ seriesName,
},
followCursor: false,
intersect: false,
shared: true,
enabled: true,
onDatasetHover: {
highlightDataSeries: true,
}
}
};
};
const chartOptions = useChartOptions();
@ -181,23 +324,30 @@ export const SiteSurvey = (props) => {
<Grid spacing={1}>
<Grid container>
<Card>
<Button
color="inherit"
size="small"
disabled="true"
<ToggleButtonGroup
value={view}
exclusive
onChange={(e, value) => {
setView(value)
}}
>
<SvgIcon>
<ChartBarSquareIcon />
</SvgIcon>
</Button>
<Button
color="inherit"
size="small"
>
<SvgIcon>
<ListBulletIcon />
</SvgIcon>
</Button>
<ToggleButton
size="small"
value="chart"
>
<SvgIcon>
<ChartBarSquareIcon />
</SvgIcon>
</ToggleButton>
<ToggleButton
size="small"
value="list"
>
<SvgIcon>
<ListBulletIcon />
</SvgIcon>
</ToggleButton>
</ToggleButtonGroup>
</Card>
</Grid>
<Box display="flex"
@ -221,7 +371,7 @@ export const SiteSurvey = (props) => {
</RadioGroup>
</FormControl>
</Box>
<Card sx={{ height: '100%' }}>
{view == "list" && <Card sx={{ height: '100%' }}>
<Box sx={{ minWidth: 800, }}>
<TableContainer sx={{ maxHeight: 600 }}>
<Table exportButton={true}>
@ -273,31 +423,18 @@ export const SiteSurvey = (props) => {
</Table>
</TableContainer>
</Box>
{/* <CardContent>
<Chart
height={500}
options={chartOptions}
series={chartSeries}
type="bar"
width="100%"
/>
</CardContent>
<Divider />
<CardActions sx={{ justifyContent: 'center' }}>
<FormControl xs={2}>
<RadioGroup
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={"2.4GHz"}
>
<Grid container>
<FormControlLabel value="2.4GHz" control={<Radio />} label="2.4GHz" />
<FormControlLabel value="5GHz" control={<Radio />} label="5GHz" />
</Grid>
</RadioGroup>
</FormControl>
</CardActions> */}
</Card>
</Card>}
{view == "chart" && <Card>
<CardContent>
<Chart
height={500}
options={chartOptions}
series={getSeries()}
type="area"
width="100%"
/>
</CardContent>
</Card>}
</Grid>: <CircularProgress></CircularProgress>}
</Stack>
);

View File

@ -97,7 +97,7 @@ const showIcon = (mtpType) => {
</Stack>
<Avatar
sx={{
backgroundColor: '#f28950',
backgroundColor: 'primary.darkest',
height: 56,
width: 56
}}

View File

@ -70,7 +70,6 @@ const useChartOptions = (labels,title) => {
}
else{
options.colors = [
theme.palette.primary.main,
theme.palette.info.main,
theme.palette.warning.main,
theme.palette.graphics.lightest,

View File

@ -11,8 +11,16 @@ const withAlphas = (color) => {
};
};
export const neutral = {
50: '#306d6f',
export const neutral = (colors) => {
console.log("neutral colors:", colors);
let parsedColors = JSON.parse(colors);
let tableColor = parsedColors["tables"]
let sidebarColorInitial = parsedColors["sidebar_initial"]
let wordsOutsideSidebarColor = parsedColors["words_outside_sidebar"]
return {
50: tableColor,
100: '#F3F4F6',
200: '#E5E7EB',
300: '#D2D6DB',
@ -20,18 +28,29 @@ export const neutral = {
500: '#6C737F',
600: '#4D5761',
700: '#FFFFFF',
800: '#306d6f',
900: '#30596f'
800: sidebarColorInitial,
900: wordsOutsideSidebarColor,
}
};
export const indigo = withAlphas({
lightest: '#FFFFFF',
light: '#306D6F',
main: '#306D6F',
dark: '#255355',
darkest: '#00a0b8',
contrastText: '#FFFFFF'
});
export const indigo = (colors) => {
console.log("indigo colors:", colors);
let parsedColors = JSON.parse(colors);
let buttonColor = parsedColors["buttons"]
let sidebarColorEnd = parsedColors["sidebar_end"]
let mtpsColor = parsedColors["connected_mtps_color"]
return withAlphas({
lightest: '#FFFFFF',
light: '#ff3383',
main: buttonColor,
dark: sidebarColorEnd,
darkest: mtpsColor,
contrastText: '#FFFFFF'
});
}
export const success = withAlphas({
lightest: '#F0FDF9',

View File

@ -2,15 +2,62 @@ import { common } from '@mui/material/colors';
import { alpha } from '@mui/material/styles';
import { error, indigo, info, neutral, success, warning, graphics } from './colors';
const getColorScheme = async () => {
let result = await fetch('http://localhost/custom-frontend/colors').catch((error) => {
console.log('Error fetching colors');
sessionStorage.setItem('colors', JSON.stringify({
"buttons": "#306d6f",
"sidebar_end": "#306d6f",
"sidebar_initial": "#306d6f",
"tables": "#306d6f",
"words_outside_sidebar": "#30596f",
"connected_mtps_color": "#f28950"
}));
location.reload();
return
});
if (result.status!=200) {
console.log('Error fetching colors');
sessionStorage.setItem('colors', JSON.stringify({
"buttons": "#306d6f",
"sidebar_end": "#306d6f",
"sidebar_initial": "#306d6f",
"tables": "#306d6f",
"words_outside_sidebar": "#30596f",
"connected_mtps_color": "#f28950"
}));
location.reload();
return
}
let response = await result.json();
let fmtresponse = JSON.stringify(response);
sessionStorage.setItem('colors', fmtresponse);
location.reload();
}
export function createPalette() {
let colors = sessionStorage.getItem('colors');
if (colors !== null) {
console.log('colors already fetched');
} else {
getColorScheme();
}
console.log("colors scheme:", colors);
let neutralColors = neutral(colors);
return {
action: {
active: neutral[500],
disabled: alpha(neutral[900], 0.38),
disabledBackground: alpha(neutral[900], 0.12),
focus: alpha(neutral[900], 0.16),
hover: alpha(neutral[900], 0.04),
selected: alpha(neutral[900], 0.12)
active: neutralColors[500],
disabled: alpha(neutralColors[900], 0.38),
disabledBackground: alpha(neutralColors[900], 0.12),
focus: alpha(neutralColors[900], 0.16),
hover: alpha(neutralColors[900], 0.04),
selected: alpha(neutralColors[900], 0.12)
},
background: {
default: common.white,
@ -21,13 +68,13 @@ export function createPalette() {
graphics,
info,
mode: 'light',
neutral,
primary: indigo,
neutral: neutralColors,
primary: indigo(colors),
success,
text: {
primary: neutral[900],
secondary: neutral[500],
disabled: alpha(neutral[900], 0.38)
primary: neutralColors[900],
secondary: neutralColors[500],
disabled: alpha(neutralColors[900], 0.38)
},
warning
};

View File

@ -0,0 +1,47 @@
/* The location pointed to by the popup tip. */
.popup-tip-anchor {
height: 0;
position: absolute;
/* The max width of the info window. */
width: 200px;
}
/* The bubble is anchored above the tip. */
.popup-bubble-anchor {
position: absolute;
width: 100%;
bottom: /* TIP_HEIGHT= */ 8px;
left: 0;
}
/* Draw the tip. */
.popup-bubble-anchor::after {
content: "";
position: absolute;
top: 0;
left: 0;
/* Center the tip horizontally. */
transform: translate(-50%, 0);
/* The tip is a https://css-tricks.com/snippets/css/css-triangle/ */
width: 0;
height: 0;
/* The tip is 8px high, and 12px wide. */
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: /* TIP_HEIGHT= */ 8px solid white;
}
/* The popup bubble itself. */
.popup-bubble-content {
position: absolute;
top: 0;
left: 0;
transform: translate(-50%, -100%);
/* Style the info window. */
background-color: white;
padding: 5px;
border-radius: 5px;
font-family: sans-serif;
font-size: 14px ;
overflow-y: auto;
max-height: 80px;
box-shadow: 0px 2px 10px 1px rgba(0, 0, 0, 0.5);
}