diff --git a/README.md b/README.md index dc6e229..2c8f3ae 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Oktopus is a multi-vendor management platform for CPEs and IoTs. Any device t -

If you'd like to become a sponsor, start a partnership or somehow to contribute to the project, email leandro@oktopus.app.br, 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. +

If you'd like to become a sponsor, start a partnership or somehow to contribute to the project, email contact@oktopus.app.br, 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.

diff --git a/backend/services/acs/internal/config/config.go b/backend/services/acs/internal/config/config.go index e6e6325..1f5fd5e 100644 --- a/backend/services/acs/internal/config/config.go +++ b/backend/services/acs/internal/config/config.go @@ -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, diff --git a/backend/services/acs/internal/nats/nats.go b/backend/services/acs/internal/nats/nats.go index 0b1e803..d4d8a6e 100644 --- a/backend/services/acs/internal/nats/nats.go +++ b/backend/services/acs/internal/nats/nats.go @@ -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 diff --git a/backend/services/bulkdata/http/internal/config/config.go b/backend/services/bulkdata/http/internal/config/config.go index 8375aca..fff17d3 100644 --- a/backend/services/bulkdata/http/internal/config/config.go +++ b/backend/services/bulkdata/http/internal/config/config.go @@ -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, + }, }, } } diff --git a/backend/services/bulkdata/http/internal/nats/nats.go b/backend/services/bulkdata/http/internal/nats/nats.go index 0c77ac4..dd58bf8 100644 --- a/backend/services/bulkdata/http/internal/nats/nats.go +++ b/backend/services/bulkdata/http/internal/nats/nats.go @@ -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 diff --git a/backend/services/controller/internal/api/api.go b/backend/services/controller/internal/api/api.go index 4046436..404e900 100644 --- a/backend/services/controller/internal/api/api.go +++ b/backend/services/controller/internal/api/api.go @@ -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") diff --git a/backend/services/controller/internal/api/enterprise.go b/backend/services/controller/internal/api/enterprise.go index f7ce633..1e09d44 100644 --- a/backend/services/controller/internal/api/enterprise.go +++ b/backend/services/controller/internal/api/enterprise.go @@ -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"] diff --git a/backend/services/controller/internal/api/user.go b/backend/services/controller/internal/api/user.go index 5484a06..b67d4b5 100644 --- a/backend/services/controller/internal/api/user.go +++ b/backend/services/controller/internal/api/user.go @@ -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 } } diff --git a/backend/services/controller/internal/bridge/bridge.go b/backend/services/controller/internal/bridge/bridge.go index f9697ed..16f1b43 100644 --- a/backend/services/controller/internal/bridge/bridge.go +++ b/backend/services/controller/internal/bridge/bridge.go @@ -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) diff --git a/backend/services/controller/internal/config/config.go b/backend/services/controller/internal/config/config.go index ff97dd6..e90dc5e 100644 --- a/backend/services/controller/internal/config/config.go +++ b/backend/services/controller/internal/config/config.go @@ -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, diff --git a/backend/services/controller/internal/nats/nats.go b/backend/services/controller/internal/nats/nats.go index 3786604..394a1f2 100644 --- a/backend/services/controller/internal/nats/nats.go +++ b/backend/services/controller/internal/nats/nats.go @@ -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 diff --git a/backend/services/mtp/adapter/internal/config/config.go b/backend/services/mtp/adapter/internal/config/config.go index 7562b36..25b0840 100644 --- a/backend/services/mtp/adapter/internal/config/config.go +++ b/backend/services/mtp/adapter/internal/config/config.go @@ -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, diff --git a/backend/services/mtp/adapter/internal/events/cwmp_handler/info.go b/backend/services/mtp/adapter/internal/events/cwmp_handler/info.go index d46a895..8ce31f1 100644 --- a/backend/services/mtp/adapter/internal/events/cwmp_handler/info.go +++ b/backend/services/mtp/adapter/internal/events/cwmp_handler/info.go @@ -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) diff --git a/backend/services/mtp/adapter/internal/events/usp_handler/info.go b/backend/services/mtp/adapter/internal/events/usp_handler/info.go index 07acca7..ce2bd66 100644 --- a/backend/services/mtp/adapter/internal/events/usp_handler/info.go +++ b/backend/services/mtp/adapter/internal/events/usp_handler/info.go @@ -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) diff --git a/backend/services/mtp/adapter/internal/nats/nats.go b/backend/services/mtp/adapter/internal/nats/nats.go index 7985275..9396d7f 100644 --- a/backend/services/mtp/adapter/internal/nats/nats.go +++ b/backend/services/mtp/adapter/internal/nats/nats.go @@ -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 diff --git a/backend/services/mtp/mqtt-adapter/internal/config/config.go b/backend/services/mtp/mqtt-adapter/internal/config/config.go index 9bac4a7..0cfd6c6 100644 --- a/backend/services/mtp/mqtt-adapter/internal/config/config.go +++ b/backend/services/mtp/mqtt-adapter/internal/config/config.go @@ -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, diff --git a/backend/services/mtp/mqtt-adapter/internal/nats/nats.go b/backend/services/mtp/mqtt-adapter/internal/nats/nats.go index 7698d76..4e96605 100644 --- a/backend/services/mtp/mqtt-adapter/internal/nats/nats.go +++ b/backend/services/mtp/mqtt-adapter/internal/nats/nats.go @@ -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 diff --git a/backend/services/mtp/mqtt/internal/config/config.go b/backend/services/mtp/mqtt/internal/config/config.go index 805d4ae..e701117 100644 --- a/backend/services/mtp/mqtt/internal/config/config.go +++ b/backend/services/mtp/mqtt/internal/config/config.go @@ -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, + }, }, } diff --git a/backend/services/mtp/mqtt/internal/nats/nats.go b/backend/services/mtp/mqtt/internal/nats/nats.go index 1eb51a4..d1548a3 100644 --- a/backend/services/mtp/mqtt/internal/nats/nats.go +++ b/backend/services/mtp/mqtt/internal/nats/nats.go @@ -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 diff --git a/backend/services/mtp/stomp-adapter/internal/config/config.go b/backend/services/mtp/stomp-adapter/internal/config/config.go index f79de27..2b54a40 100644 --- a/backend/services/mtp/stomp-adapter/internal/config/config.go +++ b/backend/services/mtp/stomp-adapter/internal/config/config.go @@ -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, diff --git a/backend/services/mtp/stomp-adapter/internal/nats/nats.go b/backend/services/mtp/stomp-adapter/internal/nats/nats.go index ad739fd..774ba86 100644 --- a/backend/services/mtp/stomp-adapter/internal/nats/nats.go +++ b/backend/services/mtp/stomp-adapter/internal/nats/nats.go @@ -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 } diff --git a/backend/services/mtp/ws-adapter/internal/config/config.go b/backend/services/mtp/ws-adapter/internal/config/config.go index c78e4dd..cf4231b 100644 --- a/backend/services/mtp/ws-adapter/internal/config/config.go +++ b/backend/services/mtp/ws-adapter/internal/config/config.go @@ -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, diff --git a/backend/services/mtp/ws-adapter/internal/nats/nats.go b/backend/services/mtp/ws-adapter/internal/nats/nats.go index c5a1de4..729129c 100644 --- a/backend/services/mtp/ws-adapter/internal/nats/nats.go +++ b/backend/services/mtp/ws-adapter/internal/nats/nats.go @@ -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 diff --git a/backend/services/mtp/ws/internal/config/config.go b/backend/services/mtp/ws/internal/config/config.go index f79f176..3080571 100644 --- a/backend/services/mtp/ws/internal/config/config.go +++ b/backend/services/mtp/ws/internal/config/config.go @@ -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, + }, }, } } diff --git a/backend/services/mtp/ws/internal/nats/nats.go b/backend/services/mtp/ws/internal/nats/nats.go index a5a557d..2afc947 100644 --- a/backend/services/mtp/ws/internal/nats/nats.go +++ b/backend/services/mtp/ws/internal/nats/nats.go @@ -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 diff --git a/backend/services/utils/file-server/.env b/backend/services/utils/file-server/.env index 8eb288c..10e120d 100644 --- a/backend/services/utils/file-server/.env +++ b/backend/services/utils/file-server/.env @@ -1,2 +1,2 @@ -DIRECTORY_PATH="./firmwares" +DIRECTORY_PATH="." SERVER_PORT=":8004" \ No newline at end of file diff --git a/backend/services/utils/file-server/.gitignore b/backend/services/utils/file-server/.gitignore index 0835f35..943b9ca 100644 --- a/backend/services/utils/file-server/.gitignore +++ b/backend/services/utils/file-server/.gitignore @@ -1,2 +1,3 @@ firmwares/ -.env.local \ No newline at end of file +.env.local +images/ \ No newline at end of file diff --git a/backend/services/utils/file-server/build/Dockerfile b/backend/services/utils/file-server/build/Dockerfile new file mode 100644 index 0000000..25700be --- /dev/null +++ b/backend/services/utils/file-server/build/Dockerfile @@ -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"] \ No newline at end of file diff --git a/backend/services/utils/file-server/build/Makefile b/backend/services/utils/file-server/build/Makefile new file mode 100644 index 0000000..a35adde --- /dev/null +++ b/backend/services/utils/file-server/build/Makefile @@ -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 \ No newline at end of file diff --git a/backend/services/utils/file-server/main.go b/backend/services/utils/file-server/main.go index 82e7705..e3dd389 100644 --- a/backend/services/utils/file-server/main.go +++ b/backend/services/utils/file-server/main.go @@ -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) diff --git a/build/Makefile b/build/Makefile index c87bf00..3e41492 100644 --- a/build/Makefile +++ b/build/Makefile @@ -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} diff --git a/deploy/compose/.env.acs b/deploy/compose/.env.acs index 9af7128..472de27 100644 --- a/deploy/compose/.env.acs +++ b/deploy/compose/.env.acs @@ -1 +1,5 @@ -NATS_URL=nats://msg_broker:4222 \ No newline at end of file +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 diff --git a/deploy/compose/.env.adapter b/deploy/compose/.env.adapter index 70fd6fe..d3dcd0d 100644 --- a/deploy/compose/.env.adapter +++ b/deploy/compose/.env.adapter @@ -1,2 +1,6 @@ -NATS_URL=nats://msg_broker:4222 -MONGO_URI=mongodb://mongo_usp:27017 \ No newline at end of file +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 diff --git a/deploy/compose/.env.controller b/deploy/compose/.env.controller index 70fd6fe..d3dcd0d 100644 --- a/deploy/compose/.env.controller +++ b/deploy/compose/.env.controller @@ -1,2 +1,6 @@ -NATS_URL=nats://msg_broker:4222 -MONGO_URI=mongodb://mongo_usp:27017 \ No newline at end of file +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 diff --git a/deploy/compose/.env.file-server b/deploy/compose/.env.file-server new file mode 100644 index 0000000..10e120d --- /dev/null +++ b/deploy/compose/.env.file-server @@ -0,0 +1,2 @@ +DIRECTORY_PATH="." +SERVER_PORT=":8004" \ No newline at end of file diff --git a/deploy/compose/.env.mqtt b/deploy/compose/.env.mqtt index e0c2d35..4f467a1 100644 --- a/deploy/compose/.env.mqtt +++ b/deploy/compose/.env.mqtt @@ -1,3 +1,7 @@ REDIS_ENABLE=false REDIS_ADDR=redis_usp:6379 -NATS_URL=nats://msg_broker:4222 \ No newline at end of file +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 diff --git a/deploy/compose/.env.mqtt-adapter b/deploy/compose/.env.mqtt-adapter index e567b54..569032b 100644 --- a/deploy/compose/.env.mqtt-adapter +++ b/deploy/compose/.env.mqtt-adapter @@ -1,2 +1,6 @@ -NATS_URL=nats://msg_broker:4222 -MQTT_URL=tcp://mqtt:1883 \ No newline at end of file +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 diff --git a/deploy/compose/.env.nats b/deploy/compose/.env.nats new file mode 100644 index 0000000..95fa9bc --- /dev/null +++ b/deploy/compose/.env.nats @@ -0,0 +1,3 @@ +NATS_NAME=oktopus +NATS_USER=oktopususer +NATS_PW=oktopuspw diff --git a/deploy/compose/.env.socketio b/deploy/compose/.env.socketio index 9af7128..472de27 100644 --- a/deploy/compose/.env.socketio +++ b/deploy/compose/.env.socketio @@ -1 +1,5 @@ -NATS_URL=nats://msg_broker:4222 \ No newline at end of file +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 diff --git a/deploy/compose/.env.stomp-adapter b/deploy/compose/.env.stomp-adapter index cdd100a..8754e23 100644 --- a/deploy/compose/.env.stomp-adapter +++ b/deploy/compose/.env.stomp-adapter @@ -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 diff --git a/deploy/compose/.env.ws b/deploy/compose/.env.ws index 9af7128..472de27 100644 --- a/deploy/compose/.env.ws +++ b/deploy/compose/.env.ws @@ -1 +1,5 @@ -NATS_URL=nats://msg_broker:4222 \ No newline at end of file +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 diff --git a/deploy/compose/.env.ws-adapter b/deploy/compose/.env.ws-adapter index 13fcdca..8454cce 100644 --- a/deploy/compose/.env.ws-adapter +++ b/deploy/compose/.env.ws-adapter @@ -1,2 +1,6 @@ -NATS_URL=nats://msg_broker:4222 -WS_ADDR=ws \ No newline at end of file +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 diff --git a/deploy/compose/.gitignore b/deploy/compose/.gitignore index af5be97..93a4138 100644 --- a/deploy/compose/.gitignore +++ b/deploy/compose/.gitignore @@ -3,4 +3,8 @@ portainer_data/* mongo_data/* !mongo_data/.gitkeep nats_data/* -!nats_data/.gitkeep \ No newline at end of file +!nats_data/.gitkeep +firmwares/* +!firmwares/.gitkeep +images/* +!images/logo.png \ No newline at end of file diff --git a/deploy/compose/docker-compose.yaml b/deploy/compose/docker-compose.yaml index 5fdbddd..410fe46 100644 --- a/deploy/compose/docker-compose.yaml +++ b/deploy/compose/docker-compose.yaml @@ -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 @@ -173,38 +190,52 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock - ./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 profiles: [cwmp] - + nginx: image: nginx:latest 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: + ipam: driver: default config: - subnet: 172.16.235.0/24 diff --git a/deploy/compose/firmwares/.gitkeep b/deploy/compose/firmwares/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/public/assets/oktopus.png b/deploy/compose/images/logo.png similarity index 100% rename from frontend/public/assets/oktopus.png rename to deploy/compose/images/logo.png diff --git a/deploy/compose/nats_config/cert.pem b/deploy/compose/nats_config/cert.pem new file mode 100644 index 0000000..0cf14cb --- /dev/null +++ b/deploy/compose/nats_config/cert.pem @@ -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----- diff --git a/deploy/compose/nats_config/key.pem b/deploy/compose/nats_config/key.pem new file mode 100644 index 0000000..83486a9 --- /dev/null +++ b/deploy/compose/nats_config/key.pem @@ -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----- diff --git a/deploy/compose/nats_config/nats.cfg b/deploy/compose/nats_config/nats.cfg new file mode 100644 index 0000000..ae6618f --- /dev/null +++ b/deploy/compose/nats_config/nats.cfg @@ -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" +} diff --git a/deploy/compose/nats_config/rootCA.pem b/deploy/compose/nats_config/rootCA.pem new file mode 100644 index 0000000..30288b2 --- /dev/null +++ b/deploy/compose/nats_config/rootCA.pem @@ -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----- diff --git a/deploy/compose/nginx.conf b/deploy/compose/nginx.conf index 1bba7d8..64e9648 100644 --- a/deploy/compose/nginx.conf +++ b/deploy/compose/nginx.conf @@ -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 { diff --git a/frontend/.env b/frontend/.env index 2116eeb..293b49b 100644 --- a/frontend/.env +++ b/frontend/.env @@ -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="" diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cf2561d..12297bb 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -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": { diff --git a/frontend/package.json b/frontend/package.json index 7347046..02747e2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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", diff --git a/frontend/public/assets/logo.png b/frontend/public/assets/logo.png new file mode 100644 index 0000000..5352dba Binary files /dev/null and b/frontend/public/assets/logo.png differ diff --git a/frontend/src/layouts/auth/layout.js b/frontend/src/layouts/auth/layout.js index 203d38d..b8d2872 100644 --- a/frontend/src/layouts/auth/layout.js +++ b/frontend/src/layouts/auth/layout.js @@ -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 ( { 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) => { }} > - - - - - + + + + + + Powered by + + + + Oktopus logo image + ); }; diff --git a/frontend/src/layouts/dashboard/config.js b/frontend/src/layouts/dashboard/config.js index 171482a..f2e7fff 100644 --- a/frontend/src/layouts/dashboard/config.js +++ b/frontend/src/layouts/dashboard/config.js @@ -35,15 +35,15 @@ export const items = [ // // ) // }, - // { - // title: 'Map', - // path: '/map', - // icon: ( - // - // - // - // ), - // }, + { + title: 'Map', + path: '/map', + icon: ( + + + + ), + }, { title: 'Credentials', path: '/credentials', diff --git a/frontend/src/layouts/dashboard/layout.js b/frontend/src/layouts/dashboard/layout.js index 2753fa6..104db95 100644 --- a/frontend/src/layouts/dashboard/layout.js +++ b/frontend/src/layouts/dashboard/layout.js @@ -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; diff --git a/frontend/src/layouts/dashboard/side-nav.js b/frontend/src/layouts/dashboard/side-nav.js index d67d4d5..d09aed5 100644 --- a/frontend/src/layouts/dashboard/side-nav.js +++ b/frontend/src/layouts/dashboard/side-nav.js @@ -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' }} > - +
-
@@ -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) => { })} - + + + Powered by + + + + Oktopus logo image + ); @@ -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 } diff --git a/frontend/src/pages/_app.js b/frontend/src/pages/_app.js index 7bc21fb..06c636d 100644 --- a/frontend/src/pages/_app.js +++ b/frontend/src/pages/_app.js @@ -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 && ( diff --git a/frontend/src/pages/map.js b/frontend/src/pages/map.js index 24a111c..6ba92ea 100644 --- a/frontend/src/pages/map.js +++ b/frontend/src/pages/map.js @@ -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> </> )}; diff --git a/frontend/src/sections/devices/cwmp/connecteddevices.js b/frontend/src/sections/devices/cwmp/connecteddevices.js index 50e86a2..6d5d859 100644 --- a/frontend/src/sections/devices/cwmp/connecteddevices.js +++ b/frontend/src/sections/devices/cwmp/connecteddevices.js @@ -16,102 +16,133 @@ 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(); myHeaders.append("Content-Type", "application/json"); 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"} + return ( + <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> ) } \ No newline at end of file diff --git a/frontend/src/sections/devices/cwmp/devices-wifi.js b/frontend/src/sections/devices/cwmp/devices-wifi.js index e9047f2..f0ed7e5 100644 --- a/frontend/src/sections/devices/cwmp/devices-wifi.js +++ b/frontend/src/sections/devices/cwmp/devices-wifi.js @@ -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'; @@ -52,44 +52,44 @@ export const DevicesWiFi = () => { var myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); 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" + <Stack + 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,67 +126,91 @@ 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"}}> - <Button - variant="contained" + <CardActions sx={{ display: "flex", justifyContent: "flex-end" }}> + <Button + variant="contained" disabled={!applyContent[index].hasChanges} - endIcon={<SvgIcon><Check /></SvgIcon>} + endIcon={<SvgIcon><Check /></SvgIcon>} onClick={ - ()=>{ + () => { setApply(true) var myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Authorization", localStorage.getItem("token")); - + delete applyContent[index].hasChanges let contentToApply = [applyContent[index]] console.log("contentToApply: ", contentToApply) @@ -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> @@ -239,44 +263,44 @@ 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"> + <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"> <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> + ); }; diff --git a/frontend/src/sections/devices/cwmp/site-survey.js b/frontend/src/sections/devices/cwmp/site-survey.js index 1a97cfe..dcfb1bb 100644 --- a/frontend/src/sections/devices/cwmp/site-survey.js +++ b/frontend/src/sections/devices/cwmp/site-survey.js @@ -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> ); diff --git a/frontend/src/sections/overview/overview-tasks-progress.js b/frontend/src/sections/overview/overview-tasks-progress.js index da8beaf..bfb3c0f 100644 --- a/frontend/src/sections/overview/overview-tasks-progress.js +++ b/frontend/src/sections/overview/overview-tasks-progress.js @@ -97,7 +97,7 @@ const showIcon = (mtpType) => { </Stack> <Avatar sx={{ - backgroundColor: '#f28950', + backgroundColor: 'primary.darkest', height: 56, width: 56 }} diff --git a/frontend/src/sections/overview/overview-traffic.js b/frontend/src/sections/overview/overview-traffic.js index 19b05a3..6fc4bbf 100644 --- a/frontend/src/sections/overview/overview-traffic.js +++ b/frontend/src/sections/overview/overview-traffic.js @@ -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, diff --git a/frontend/src/theme/colors.js b/frontend/src/theme/colors.js index 74ef40e..7d6589e 100644 --- a/frontend/src/theme/colors.js +++ b/frontend/src/theme/colors.js @@ -1,5 +1,5 @@ import { alpha } from '@mui/material/styles'; - + const withAlphas = (color) => { return { ...color, @@ -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', diff --git a/frontend/src/theme/create-palette.js b/frontend/src/theme/create-palette.js index 43e67f4..7768322 100644 --- a/frontend/src/theme/create-palette.js +++ b/frontend/src/theme/create-palette.js @@ -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() { - return { + + 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 }; diff --git a/frontend/src/utils/map.css b/frontend/src/utils/map.css new file mode 100644 index 0000000..55a1725 --- /dev/null +++ b/frontend/src/utils/map.css @@ -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); + } + \ No newline at end of file