diff --git a/backend/services/acs/internal/server/handler/cwmp.go b/backend/services/acs/internal/server/handler/cwmp.go index 3f3bc65..aff1024 100644 --- a/backend/services/acs/internal/server/handler/cwmp.go +++ b/backend/services/acs/internal/server/handler/cwmp.go @@ -80,6 +80,8 @@ func (h *Handler) CwmpHandler(w http.ResponseWriter, r *http.Request) { h.pub(NATS_CWMP_SUBJECT_PREFIX+sn+".info", tmp) } + cpe.ConnectionRequestURL = Inform.GetConnectionRequest() // Update connection request URL, in case the CPE changed IP + log.Printf("Received an Inform from device %s withEventCodes %s", addr, Inform.GetEvents()) expiration := time.Now().AddDate(0, 0, 1) diff --git a/backend/services/controller/internal/api/api.go b/backend/services/controller/internal/api/api.go index d0b6898..29e5227 100644 --- a/backend/services/controller/internal/api/api.go +++ b/backend/services/controller/internal/api/api.go @@ -53,6 +53,11 @@ func (a *Api) StartApi() { iot := r.PathPrefix("/api/device").Subrouter() iot.HandleFunc("/alias", a.setDeviceAlias).Methods("PUT") iot.HandleFunc("/auth", a.deviceAuth).Methods("GET", "POST", "DELETE") + iot.HandleFunc("/message/{type}", a.addTemplate).Methods("POST") + iot.HandleFunc("/message", a.updateTemplate).Methods("PUT") + iot.HandleFunc("/message", a.getTemplate).Methods("GET") + iot.HandleFunc("/message", a.deleteTemplate).Methods("DELETE") + iot.HandleFunc("/cwmp/{sn}/generic", a.cwmpGenericMsg).Methods("PUT") iot.HandleFunc("/cwmp/{sn}/getParameterNames", a.cwmpGetParameterNamesMsg).Methods("PUT") iot.HandleFunc("/cwmp/{sn}/getParameterValues", a.cwmpGetParameterValuesMsg).Methods("PUT") iot.HandleFunc("/cwmp/{sn}/getParameterAttributes", a.cwmpGetParameterAttributesMsg).Methods("PUT") @@ -61,6 +66,7 @@ func (a *Api) StartApi() { iot.HandleFunc("/cwmp/{sn}/deleteObject", a.cwmpDeleteObjectMsg).Methods("PUT") iot.HandleFunc("", a.retrieveDevices).Methods("GET") iot.HandleFunc("/filterOptions", a.filterOptions).Methods("GET") + iot.HandleFunc("/{sn}/{mtp}/generic", a.deviceGenericMessage).Methods("PUT") iot.HandleFunc("/{sn}/{mtp}/get", a.deviceGetMsg).Methods("PUT") iot.HandleFunc("/{sn}/{mtp}/add", a.deviceCreateMsg).Methods("PUT") iot.HandleFunc("/{sn}/{mtp}/del", a.deviceDeleteMsg).Methods("PUT") diff --git a/backend/services/controller/internal/api/cwmp.go b/backend/services/controller/internal/api/cwmp.go index bb9d34c..3eb6e6f 100644 --- a/backend/services/controller/internal/api/cwmp.go +++ b/backend/services/controller/internal/api/cwmp.go @@ -17,6 +17,31 @@ import ( var errDeviceModelNotFound = errors.New("device model not found") +func (a *Api) cwmpGenericMsg(w http.ResponseWriter, r *http.Request) { + + sn := getSerialNumberFromRequest(r) + + payload, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write(utils.Marshall(err.Error())) + return + } + + if len(payload) == 0 { + w.WriteHeader(http.StatusBadRequest) + w.Write(utils.Marshall("Empty payload")) + return + } + + data, _, err := cwmpInteraction[cwmp.SoapEnvelope](sn, payload, w, a.nc) + if err != nil { + return + } + + w.Write(data) +} + func (a *Api) cwmpGetParameterNamesMsg(w http.ResponseWriter, r *http.Request) { sn := getSerialNumberFromRequest(r) @@ -125,7 +150,7 @@ func (a *Api) cwmpDeleteObjectMsg(w http.ResponseWriter, r *http.Request) { w.Write(data) } -func cwmpInteraction[T cwmp.SetParameterValuesResponse | cwmp.DeleteObjectResponse | cwmp.GetParameterAttributesResponse | cwmp.GetParameterNamesResponse | cwmp.GetParameterValuesResponse | cwmp.AddObjectResponse]( +func cwmpInteraction[T cwmp.SetParameterValuesResponse | cwmp.SoapEnvelope | cwmp.DeleteObjectResponse | cwmp.GetParameterAttributesResponse | cwmp.GetParameterNamesResponse | cwmp.GetParameterValuesResponse | cwmp.AddObjectResponse]( sn string, payload []byte, w http.ResponseWriter, nc *nats.Conn, ) ([]byte, T, error) { diff --git a/backend/services/controller/internal/api/device.go b/backend/services/controller/internal/api/device.go index a24485f..4270186 100644 --- a/backend/services/controller/internal/api/device.go +++ b/backend/services/controller/internal/api/device.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" + "github.com/gorilla/mux" "github.com/leandrofars/oktopus/internal/bridge" "github.com/leandrofars/oktopus/internal/db" "github.com/leandrofars/oktopus/internal/entity" @@ -367,3 +368,141 @@ func (a *Api) filterOptions(w http.ResponseWriter, r *http.Request) { w.WriteHeader(resp.Code) w.Write(utils.Marshall(resp.Msg)) } + +func (a *Api) updateTemplate(w http.ResponseWriter, r *http.Request) { + + name := r.URL.Query().Get("name") + if name == "" { + w.WriteHeader(http.StatusBadRequest) + utils.MarshallEncoder("No name provided", w) + return + } + + payload, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + utils.MarshallEncoder("Error to decode payload: "+err.Error(), w) + return + } + + payloadLen := len(payload) + if payloadLen == 0 { + w.WriteHeader(http.StatusBadRequest) + utils.MarshallEncoder("No payload provided", w) + return + } + + err = a.db.UpdateTemplate(name, string(payload)) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + json.NewEncoder(w).Encode(err.Error()) + return + } + + w.WriteHeader(http.StatusNoContent) +} + +func (a *Api) addTemplate(w http.ResponseWriter, r *http.Request) { + + name := r.URL.Query().Get("name") + if name == "" { + w.WriteHeader(http.StatusBadRequest) + utils.MarshallEncoder("No name provided", w) + return + } + + payload, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + utils.MarshallEncoder("Error to decode payload: "+err.Error(), w) + return + } + + payloadLen := len(payload) + if payloadLen == 0 { + w.WriteHeader(http.StatusBadRequest) + utils.MarshallEncoder("No payload provided", w) + return + } + + vars := mux.Vars(r) + switch vars["type"] { + case "cwmp": + err = a.db.AddTemplate(name, "cwmp", string(payload)) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + json.NewEncoder(w).Encode(err.Error()) + return + } + w.WriteHeader(http.StatusNoContent) + return + case "usp": + err = a.db.AddTemplate(name, "usp", string(payload)) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + json.NewEncoder(w).Encode(err.Error()) + return + } + w.WriteHeader(http.StatusNoContent) + return + default: + w.WriteHeader(http.StatusBadRequest) + utils.MarshallEncoder("Invalid template type", w) + return + } +} + +func (a *Api) getTemplate(w http.ResponseWriter, r *http.Request) { + + name := r.URL.Query().Get("name") + msgType := r.URL.Query().Get("type") + + if name == "" { + + var filter bson.D + if msgType == "" { + filter = bson.D{} + } else { + filter = bson.D{{"type", msgType}} + } + + result, err := a.db.AllTemplates(filter) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode("Error to get all templates: " + err.Error()) + return + } + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(result) + return + } else { + t, err := a.db.FindTemplate(bson.D{{"name", name}}) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + json.NewEncoder(w).Encode("error to find message: " + err.Error()) + return + } + w.WriteHeader(http.StatusOK) + w.Write([]byte(t.Value)) + return + } + +} + +func (a *Api) deleteTemplate(w http.ResponseWriter, r *http.Request) { + + name := r.URL.Query().Get("name") + if name == "" { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode("needs template name!") + return + } else { + err := a.db.DeleteTemplate(name) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode("error to delete template: " + err.Error()) + return + } + w.WriteHeader(http.StatusNoContent) + } +} diff --git a/backend/services/controller/internal/api/user.go b/backend/services/controller/internal/api/user.go index 3e31726..9103e93 100644 --- a/backend/services/controller/internal/api/user.go +++ b/backend/services/controller/internal/api/user.go @@ -135,16 +135,6 @@ func (a *Api) changePassword(w http.ResponseWriter, r *http.Request) { return } - userToChangePasswd := mux.Vars(r)["user"] - if userToChangePasswd != "" && userToChangePasswd != email { - rUser, _ := a.db.FindUser(email) - if rUser.Level != db.AdminUser { - w.WriteHeader(http.StatusForbidden) - return - } - email = userToChangePasswd - } - var user db.User err = json.NewDecoder(r.Body).Decode(&user) if err != nil { @@ -154,6 +144,12 @@ func (a *Api) changePassword(w http.ResponseWriter, r *http.Request) { } user.Email = email + if len(user.Password) < 8 { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("Password must be at least 8 characters long")) + return + } + if err := user.HashPassword(user.Password); err != nil { w.WriteHeader(http.StatusInternalServerError) return @@ -164,6 +160,7 @@ func (a *Api) changePassword(w http.ResponseWriter, r *http.Request) { return } + w.WriteHeader(http.StatusNoContent) } func (a *Api) registerAdminUser(w http.ResponseWriter, r *http.Request) { diff --git a/backend/services/controller/internal/api/usp.go b/backend/services/controller/internal/api/usp.go index 7a663ff..e552b27 100644 --- a/backend/services/controller/internal/api/usp.go +++ b/backend/services/controller/internal/api/usp.go @@ -1,6 +1,7 @@ package api import ( + "io" "log" "net/http" @@ -12,6 +13,7 @@ import ( "github.com/leandrofars/oktopus/internal/usp/usp_utils" "github.com/leandrofars/oktopus/internal/utils" "github.com/nats-io/nats.go" + "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" ) @@ -59,6 +61,16 @@ func sendUspMsg(msg usp_msg.Msg, sn string, w http.ResponseWriter, nc *nats.Conn } body := receivedMsg.Body.GetResponse() + if body == nil { + errorMsg := receivedMsg.Body.GetError() + if errorMsg == nil { + w.WriteHeader(http.StatusInternalServerError) + log.Println("No response body or error") + return nil + } + w.Write(utils.Marshall(errorMsg)) + return nil + } switch body.RespType.(type) { case *usp_msg.Response_GetResp: @@ -86,6 +98,44 @@ func sendUspMsg(msg usp_msg.Msg, sn string, w http.ResponseWriter, nc *nats.Conn return nil } +func (a *Api) deviceGenericMessage(w http.ResponseWriter, r *http.Request) { + + sn := getSerialNumberFromRequest(r) + mtp, err := getMtpFromRequest(r, w) + if err != nil { + return + } + + if mtp == "" { + var ok bool + mtp, ok = deviceStateOK(w, a.nc, sn) + if !ok { + return + } + } + + payload, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write(utils.Marshall(err.Error())) + return + } + + var msg usp_msg.Msg + + err = protojson.Unmarshal(payload, &msg) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write(utils.Marshall(err.Error())) + return + } + + err = sendUspMsg(msg, sn, w, a.nc, mtp) + if err != nil { + return + } +} + func (a *Api) deviceGetMsg(w http.ResponseWriter, r *http.Request) { sn := getSerialNumberFromRequest(r) diff --git a/backend/services/controller/internal/db/db.go b/backend/services/controller/internal/db/db.go index c3365b2..c5289ab 100644 --- a/backend/services/controller/internal/db/db.go +++ b/backend/services/controller/internal/db/db.go @@ -10,9 +10,10 @@ import ( ) type Database struct { - client *mongo.Client - users *mongo.Collection - ctx context.Context + client *mongo.Client + users *mongo.Collection + template *mongo.Collection + ctx context.Context } func NewDatabase(ctx context.Context, mongoUri string) Database { @@ -43,6 +44,16 @@ func NewDatabase(ctx context.Context, mongoUri string) Database { log.Fatalln(err) } + db.template = client.Database("general").Collection("templates") + indexField = bson.M{"name": 1} + _, err = db.template.Indexes().CreateOne(ctx, mongo.IndexModel{ + Keys: indexField, + Options: options.Index().SetUnique(true), + }) + if err != nil { + log.Fatalln(err) + } + db.ctx = ctx return db diff --git a/backend/services/controller/internal/db/template.go b/backend/services/controller/internal/db/template.go new file mode 100644 index 0000000..efdcfd4 --- /dev/null +++ b/backend/services/controller/internal/db/template.go @@ -0,0 +1,72 @@ +package db + +import ( + "errors" + "log" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type Template struct { + Name string `json:"name" bson:"name"` + Type string `json:"type" bson:"type"` + Value string `json:"value" bson:"value"` +} + +var ErrorTemplateExists = errors.New("message already exists") +var ErrorTemplateNotExists = errors.New("message don't exist") + +func (d *Database) FindTemplate(filter interface{}) (Template, error) { + var result Template + err := d.template.FindOne(d.ctx, filter).Decode(&result) + return result, err +} + +func (d *Database) AllTemplates(filter interface{}) ([]Template, error) { + var results []Template + + cursor, err := d.template.Find(d.ctx, filter) + if err != nil { + return results, err + } + if err = cursor.All(d.ctx, &results); err != nil { + log.Println(err) + } + return results, err +} + +func (d *Database) AddTemplate(name, tr string, t string) error { + opts := options.FindOneAndReplace().SetUpsert(true) + err := d.template.FindOneAndReplace(d.ctx, bson.D{{"name", name}}, Template{Name: name, Type: tr, Value: t}, opts).Err() + if err != nil { + if err == mongo.ErrNoDocuments { + log.Printf("New message %s added to database", name) + return nil + } + return err + } + log.Printf("Message %s already existed, and got replaced for new payload", name) + return err +} + +func (d *Database) UpdateTemplate(name, t string) error { + result, err := d.template.UpdateOne(d.ctx, bson.D{{"name", name}}, bson.D{{"$set", bson.D{{"value", t}}}}) + if err == nil { + if result.MatchedCount == 0 { + return ErrorTemplateNotExists + } + } + return err +} + +func (d *Database) DeleteTemplate(name string) error { + result, err := d.template.DeleteOne(d.ctx, bson.D{{"name", name}}) + if err == nil { + if result.DeletedCount == 0 { + return ErrorTemplateNotExists + } + } + return err +} diff --git a/backend/services/mtp/adapter/internal/db/device.go b/backend/services/mtp/adapter/internal/db/device.go index 50ef7e8..bb6be92 100644 --- a/backend/services/mtp/adapter/internal/db/device.go +++ b/backend/services/mtp/adapter/internal/db/device.go @@ -82,6 +82,13 @@ func (d *Database) CreateDevice(device Device) error { return err } } + + /* ------------------------- Do not overwrite alias ------------------------- */ + if deviceExistent.Alias != "" { + device.Alias = deviceExistent.Alias + } + /* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ callback := func(sessCtx mongo.SessionContext) (interface{}, error) { diff --git a/frontend/src/contexts/auth-context.js b/frontend/src/contexts/auth-context.js index a35e9d4..8400f9e 100644 --- a/frontend/src/contexts/auth-context.js +++ b/frontend/src/contexts/auth-context.js @@ -211,6 +211,8 @@ export const AuthProvider = (props) => { }; const signOut = () => { + router.push("/auth/login") + localStorage.removeItem("token") dispatch({ type: HANDLERS.SIGN_OUT }); diff --git a/frontend/src/contexts/backend-context.js b/frontend/src/contexts/backend-context.js new file mode 100644 index 0000000..4a26070 --- /dev/null +++ b/frontend/src/contexts/backend-context.js @@ -0,0 +1,91 @@ +import { createContext, useContext, } from 'react'; +import { useRouter } from 'next/router'; +import { useAlertContext } from './error-context'; + +export const BackendContext = createContext({ undefined }); + +export const BackendProvider = (props) => { + const { children } = props; + + const { setAlert } = useAlertContext(); + + const router = useRouter(); + + var myHeaders = new Headers(); + myHeaders.append("Content-Type", "application/json"); + myHeaders.append("Authorization", localStorage.getItem("token")); + + const httpRequest = async (path, method, body, headers, encoding) => { + + var requestOptions = { + method: method, + redirect: 'follow', + }; + + if (body) { + requestOptions.body = body + } + + console.log("headers:", headers) + if (headers) { + requestOptions.headers = headers + }else { + requestOptions.headers = myHeaders + } + + const response = await fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}${path}`, requestOptions) + console.log("status:", response.status) + if (response.status != 200) { + if (response.status == 401) { + router.push("/auth/login") + } + if (response.status == 204 || response.status == 201) { + return {status : response.status, result: null} + } + const data = await response.text() + // setAlert({ + // severity: "error", + // title: "Error", + // message: `Status: ${response.status}, Content: ${data}`, + // }) + setAlert({ + severity: "error", + // title: "Error", + message: `${data}`, + }) + return {status : response.status, result: null} + } + if (encoding) { + console.log("encoding:", encoding) + if (encoding == "text") { + const data = await response.text() + return {status: response.status, result: data} + }else if (encoding == "blob") { + const data = await response.blob() + return {status: response.status, result: data} + }else if (encoding == "json") { + const data = await response.json() + return {status: response.status, result: data} + }else{ + return {status: response.status, result: response} + } + } + const data = await response.json() + return {status: response.status, result: data} + } + + return ( + + {children} + + ); +}; + +export const BackendConsumer = BackendContext.Consumer; + +export const useBackendContext = () => useContext(BackendContext); \ No newline at end of file diff --git a/frontend/src/contexts/error-context.js b/frontend/src/contexts/error-context.js new file mode 100644 index 0000000..75651d9 --- /dev/null +++ b/frontend/src/contexts/error-context.js @@ -0,0 +1,32 @@ +import { createContext, useContext, useState } from 'react'; + + +export const AlertContext = createContext({ undefined }); + +export const AlertProvider = (props) => { + const { children } = props; + /* + { + severity: '', // options => error, warning, info, success + message: '', + title: '', + } + */ + // const [alert, setAlert] = useState(null); + const [alert, setAlert] = useState(); + + return ( + + {children} + + ); +}; + +export const AlertConsumer = AlertContext.Consumer; + +export const useAlertContext = () => useContext(AlertContext); \ No newline at end of file diff --git a/frontend/src/layouts/dashboard/config.js b/frontend/src/layouts/dashboard/config.js index e77f4d2..4a836b8 100644 --- a/frontend/src/layouts/dashboard/config.js +++ b/frontend/src/layouts/dashboard/config.js @@ -1,14 +1,16 @@ import ChartBarIcon from '@heroicons/react/24/solid/ChartBarIcon'; import CogIcon from '@heroicons/react/24/solid/CogIcon'; import ChatBubbleLeftRightIcon from '@heroicons/react/24/solid/ChatBubbleLeftRightIcon' -import MapIcon from '@heroicons/react/24/solid/MapIcon' import RectangleGroupIcon from '@heroicons/react/24/solid/RectangleGroupIcon' import ArrowDownOnSquareStackIcon from '@heroicons/react/24/solid/ArrowDownOnSquareStackIcon' import UserGroupIcon from '@heroicons/react/24/solid/UserGroupIcon' import KeyIcon from '@heroicons/react/24/solid/KeyIcon' import CpuChip from '@heroicons/react/24/solid/CpuChipIcon'; -import BriefCaseIcon from '@heroicons/react/24/outline/BriefcaseIcon'; import { SvgIcon } from '@mui/material'; +import FolderIcon from '@heroicons/react/24/solid/FolderIcon'; +import ShieldCheckIcon from '@heroicons/react/24/solid/ShieldCheckIcon'; +import EnvelopeIcon from '@heroicons/react/24/solid/EnvelopeIcon'; +import UserIcon from '@heroicons/react/24/solid/UserIcon'; export const items = [ { @@ -48,19 +50,19 @@ export const items = [ ), disabled: true - } + }, + { + title: 'Message', + tooltip: 'Upgrade to Business Plan', + disabled: true, + icon: ( + + + + ) + }, ] }, - { - title: 'Map', - tooltip: 'Upgrade to Business Plan', - icon: ( - - - - ), - disabled: true - }, { title: 'Credentials', path: '/credentials', @@ -71,11 +73,43 @@ export const items = [ ) }, { - title: 'Users', - path: '/users', + title: 'Access Control', + disabled: true, + tooltip: 'Upgrade to Business Plan', icon: ( - + + + ), + children: [ + { + title: 'Roles', + disabled: true, + tooltip: 'Upgrade to Business Plan', + icon: ( + + + + ) + }, + { + title: 'Users', + path: '/access-control/users', + icon: ( + + + + ) + }, + ] + }, + { + title: 'File Server', + tooltip: 'Upgrade to Business Plan', + disabled: true, + icon: ( + + ) }, diff --git a/frontend/src/layouts/dashboard/layout.js b/frontend/src/layouts/dashboard/layout.js index 104db95..3dddca3 100644 --- a/frontend/src/layouts/dashboard/layout.js +++ b/frontend/src/layouts/dashboard/layout.js @@ -4,6 +4,8 @@ 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 { useAlertContext } from 'src/contexts/error-context'; +import { Alert, AlertTitle, Snackbar } from '@mui/material'; const SIDE_NAV_WIDTH = 280; @@ -28,6 +30,8 @@ export const Layout = withAuthGuard((props) => { const pathname = usePathname(); const [openNav, setOpenNav] = useState(false); + const {alert, setAlert} = useAlertContext(); + const handlePathnameChange = useCallback( () => { if (openNav) { @@ -57,6 +61,21 @@ export const Layout = withAuthGuard((props) => { {children} + {alert && setAlert(null)} + > + + {alert?.title && {alert.title}} + {alert?.message} + + } > ); }); \ No newline at end of file diff --git a/frontend/src/layouts/dashboard/side-nav-item.js b/frontend/src/layouts/dashboard/side-nav-item.js index dee2d2d..e46634c 100644 --- a/frontend/src/layouts/dashboard/side-nav-item.js +++ b/frontend/src/layouts/dashboard/side-nav-item.js @@ -9,7 +9,7 @@ import { usePathname } from 'next/navigation'; export const SideNavItem = (props) => { const { active = false, disabled, external, icon, path, title, children, padleft, tooltip } = props; - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(true); const pathname = usePathname(); const isItemActive = (currentPath, itemPath) => { @@ -64,10 +64,14 @@ export const SideNavItem = (props) => { } }} > - {icon && ( { + if (!path){ + setOpen(!open) + } + }} sx={{ alignItems: 'center', color: 'neutral.400', @@ -85,6 +89,11 @@ export const SideNavItem = (props) => { )} { + if (!path){ + setOpen(!open) + } + }} sx={{ color: 'neutral.400', flexGrow: 1, diff --git a/frontend/src/pages/_app.js b/frontend/src/pages/_app.js index a59cb06..d745110 100644 --- a/frontend/src/pages/_app.js +++ b/frontend/src/pages/_app.js @@ -9,9 +9,10 @@ import { useNProgress } from 'src/hooks/use-nprogress'; 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'; +import { BackendProvider } from 'src/contexts/backend-context'; +import { AlertProvider } from 'src/contexts/error-context'; const clientSideEmotionCache = createEmotionCache(); @@ -33,7 +34,7 @@ const App = (props) => { - Oktopus | TR-369 Controller + Oktopus | Controller { - - - - { - (auth) => auth.isLoading - ? - : getLayout() - } - - + {/* */} + + + + + + { + (auth) => auth.isLoading + ? + : getLayout() + } + + + + + {/* */} diff --git a/frontend/src/pages/users.js b/frontend/src/pages/access-control/users.js similarity index 100% rename from frontend/src/pages/users.js rename to frontend/src/pages/access-control/users.js diff --git a/frontend/src/pages/devices.js b/frontend/src/pages/devices.js index 1f91311..dec5f6e 100644 --- a/frontend/src/pages/devices.js +++ b/frontend/src/pages/devices.js @@ -204,15 +204,10 @@ const Page = () => { useEffect(() => { getColumns() setLoading(true) - if (auth.user.token) { - console.log("auth.user.token =", auth.user.token) - } else { - auth.user.token = localStorage.getItem("token") - } var myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); - myHeaders.append("Authorization", auth.user.token); + myHeaders.append("Authorization", localStorage.getItem("token")); var requestOptions = { method: 'GET', @@ -293,8 +288,9 @@ const Page = () => { console.log("set alias result:", content) setShowSetDeviceAlias(false) setDeviceAlias(null) - orders[deviceToBeChanged].Alias = alias + devices[deviceToBeChanged].Alias = alias setDeviceToBeChanged(null) + setDevices([...devices]) } // .then(response => { // if (response.status === 401) { @@ -415,7 +411,7 @@ const Page = () => { <> - Oktopus | TR-369 + Oktopus | Controller @@ -651,7 +647,7 @@ const Page = () => { } - {/* + { setDeviceToBeChanged(index) @@ -667,7 +663,7 @@ const Page = () => { - + {/* { setDeviceToBeChanged(index) @@ -724,7 +720,7 @@ const Page = () => { { setDeviceAlias(e.target.value) }} onKeyUp={e => { if (e.key === 'Enter') { - setNewDeviceAlias(deviceAlias, orders[deviceToBeChanged].SN) + setNewDeviceAlias(deviceAlias, devices[deviceToBeChanged].SN) } }}> @@ -736,7 +732,7 @@ const Page = () => { setDeviceToBeChanged(null) }}>Cancel { - setNewDeviceAlias(deviceAlias, orders[deviceToBeChanged].SN) + setNewDeviceAlias(deviceAlias, devices[deviceToBeChanged].SN) }}>Save } diff --git a/frontend/src/pages/devices/cwmp/[...id].js b/frontend/src/pages/devices/cwmp/[...id].js index b30ac99..6168ac4 100644 --- a/frontend/src/pages/devices/cwmp/[...id].js +++ b/frontend/src/pages/devices/cwmp/[...id].js @@ -19,6 +19,9 @@ import DevicePhoneMobile from '@heroicons/react/24/solid/DevicePhoneMobileIcon'; import WrenchScrewDriverIcon from '@heroicons/react/24/outline/WrenchScrewdriverIcon'; import CommandLineIcon from '@heroicons/react/24/outline/CommandLineIcon'; import { DevicesWiFi } from 'src/sections/devices/cwmp/devices-wifi'; +import ArrowTrendingUpIcon from '@heroicons/react/24/outline/ArrowTrendingUpIcon'; +import DocumentTextIcon from '@heroicons/react/24/outline/DocumentTextIcon'; +import MapPinIcon from '@heroicons/react/24/outline/MapPinIcon'; const Page = () => { @@ -31,10 +34,10 @@ const Page = () => { switch(section){ case "msg": return - case "wifi": - return + /* case "wifi": + return */ default: - router.push(`/devices/cwmp/${deviceID}/wifi`) + router.replace(`/devices/cwmp/${deviceID}/msg`) } } @@ -42,7 +45,7 @@ const Page = () => { <> - Oktopus | TR-369 + Oktopus | Controller { py: 0, }} > - - - + + + {[ Devices , @@ -71,19 +74,19 @@ const Page = () => { display:'flex', justifyContent:'center', }}> - + + } iconPosition={"end"} label="Wi-Fi" - onClick={()=>{router.push(`/devices/cwmp/${deviceID}/wifi`)}} - value={"wifi"}/> + value={"wifi"} + style={{opacity:"0.5", cursor:"default"}}/> } iconPosition={"end"} label="Site Survey" - onClick={()=>{router.push(`/devices/cwmp/${deviceID}/site-survey`)}} value={"site-survey"} style={{opacity:"0.5", cursor:"default"}}/> @@ -92,7 +95,6 @@ const Page = () => { iconPosition={"end"} label="Connected Devices" style={{opacity:"0.5", cursor:"default"}} - onClick={()=>{router.push(`/devices/cwmp/${deviceID}/connected-devices`)}} value={"connected-devices"} /> @@ -100,7 +102,6 @@ const Page = () => { icon={} iconPosition={"end"} label="Diagnostic" - onClick={()=>{router.push(`/devices/cwmp/${deviceID}/diagnostic`)}} value={"diagnostic"} style={{opacity:"0.5", cursor:"default"}}/> @@ -108,25 +109,48 @@ const Page = () => { icon={} iconPosition={"end"} label="Ports" - onClick={()=>{router.push(`/devices/usp/${deviceID}/ports`)}} style={{opacity:"0.5", cursor:"default"}} value={"ports"} /> } + iconPosition={"end"} + label="Historic" + value={"historic"} + style={{opacity:"0.5", cursor:"default"}}/> + + } iconPosition={"end"} label="Actions" - onClick={()=>{router.push(`/devices/usp/${deviceID}/actions`)}} style={{opacity:"0.5", cursor:"default"}} value={"actions"} /> + + } + iconPosition={"end"} + label="Logs" + style={{opacity:"0.5", cursor:"default"}} + value={"logs"} /> + + } + iconPosition={"end"} + label="Location" + style={{opacity:"0.5", cursor:"default"}} + value={"location"} /> {router.push(`/devices/cwmp/${deviceID}/msg`)}} icon={} iconPosition={"end"} - label="Remote Messages" /> + label="Messages" /> + + + + { sectionHandler() } diff --git a/frontend/src/pages/devices/usp/[...id].js b/frontend/src/pages/devices/usp/[...id].js index 986c10b..a42bd41 100644 --- a/frontend/src/pages/devices/usp/[...id].js +++ b/frontend/src/pages/devices/usp/[...id].js @@ -20,6 +20,9 @@ import SignalIcon from '@heroicons/react/24/solid/SignalIcon'; import DevicePhoneMobile from '@heroicons/react/24/solid/DevicePhoneMobileIcon'; import WrenchScrewDriverIcon from '@heroicons/react/24/outline/WrenchScrewdriverIcon'; import CommandLineIcon from '@heroicons/react/24/outline/CommandLineIcon'; +import CubeTransparentIcon from '@heroicons/react/24/outline/CubeTransparentIcon'; +import MapPin from '@heroicons/react/24/outline/MapPinIcon'; +import ArrowTrendingUp from '@heroicons/react/24/outline/ArrowTrendingUpIcon'; const Page = () => { const router = useRouter() @@ -36,7 +39,7 @@ const Page = () => { case "discovery": return default: - router.push(`/devices/usp/${deviceID}/discovery`) + router.replace(`/devices/usp/${deviceID}/discovery`) } } @@ -44,7 +47,7 @@ const Page = () => { <> - Oktopus | TR-369 + Oktopus | Controller { py: 0, }} > - - - + + + {[ Devices , @@ -72,9 +75,8 @@ const Page = () => { - + }}> + } @@ -117,6 +119,27 @@ const Page = () => { value={"ports"} /> } + iconPosition={"end"} + label="Historic" + style={{cursor:"default", opacity: 0.5}} + value={"historic"} /> + + } + iconPosition={"end"} + label="LCM" + style={{cursor:"default", opacity: 0.5}} + value={"lcm"} /> + + } + iconPosition={"end"} + label="Location" + style={{cursor:"default", opacity: 0.5}} + value={"location"} /> + + } iconPosition={"end"} label="Actions" @@ -127,15 +150,19 @@ const Page = () => { onClick={()=>{router.push(`/devices/usp/${deviceID}/discovery`)}} icon={} iconPosition={"end"} - label="Discover Parameters" /> + label="Parameters" /> {router.push(`/devices/usp/${deviceID}/msg`)}} icon={} iconPosition={"end"} - label="Remote Messages" /> + label="Messages" /> + + + + { sectionHandler() } diff --git a/frontend/src/pages/index.js b/frontend/src/pages/index.js index 260fbfc..e8b759d 100644 --- a/frontend/src/pages/index.js +++ b/frontend/src/pages/index.js @@ -129,7 +129,7 @@ const Page = () => { <> - Oktopus | TR-369 + Oktopus | Controller (width, height) => ({ - x: -(width / 2) + pixelOffset.x, - y: -(height / 2) + pixelOffset.y -}); - -const Popup = props => { - return ( - - - - {props.content} - - - - ); -}; - -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(()=> { - 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( - () => ({ - disableDefaultUI: false, - clickableIcons: true, - zoomControl: true, - controlSize: 23, - styles: mapStyles, - mapTypeControlOptions: { - mapTypeIds: ['roadmap', 'satellite'], - } - }), - [] - ); - - const { isLoaded } = useLoadScript({ - googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_KEY, - libraries: libraries, - }); - - if (!isLoaded) { - return Loading...; - } - - return ( markers && zoom && - <> - - - Maps | Oktopus - - - - { - markers.map((marker, index) => ( - { - 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 } - }); - }} - > - - )) - } - {activeMarker && - - SN: {activeMarker.sn} - - Model: {activeMarkerdata.Model?activeMarkerdata.Model:activeMarkerdata.ProductClass} - Alias: {activeMarkerdata.Alias} - Status: {activeMarkerdata.Status == 2 ? online : offline} - - - : no device info found} - />} - - > - )}; - - Page.getLayout = (page) => ( - - {page} - - ); - - export default Page; \ No newline at end of file diff --git a/frontend/src/sections/devices/cwmp/devices-rpc.js b/frontend/src/sections/devices/cwmp/devices-rpc.js index 62c97b0..13d99bf 100644 --- a/frontend/src/sections/devices/cwmp/devices-rpc.js +++ b/frontend/src/sections/devices/cwmp/devices-rpc.js @@ -19,37 +19,26 @@ import { DialogContentText, DialogActions, Box, - IconButton + IconButton, + Grid } from '@mui/material'; import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon'; import PaperAirplane from '@heroicons/react/24/solid/PaperAirplaneIcon'; import CircularProgress from '@mui/material/CircularProgress'; import Backdrop from '@mui/material/Backdrop'; import { useRouter } from 'next/router'; +import { useBackendContext } from 'src/contexts/backend-context'; +import DocumentArrowDown from '@heroicons/react/24/outline/DocumentArrowDownIcon'; +import TrashIcon from '@heroicons/react/24/outline/TrashIcon'; +import PlusCircleIcon from '@heroicons/react/24/outline/PlusCircleIcon'; +import EnvelopeIcon from '@heroicons/react/24/outline/EnvelopeIcon'; +import CheckIcon from '@heroicons/react/24/outline/CheckIcon'; export const DevicesRPC = () => { const router = useRouter() - -const [open, setOpen] = useState(false); -const [scroll, setScroll] = useState('paper'); -const [answer, setAnswer] = useState(false) -const [content, setContent] = useState('') -const [age, setAge] = useState(2); - -const [value, setValue] = useState(` - - - - - - InternetGatewayDevice.LANDevice.1.WLANConfiguration.1. - InternetGatewayDevice.LANDevice.1.WLANConfiguration.2. - - - -`) +let { httpRequest } = useBackendContext() var prettifyXml = function(sourceXml) { @@ -75,129 +64,219 @@ var prettifyXml = function(sourceXml) return resultXml; }; +const [open, setOpen] = useState(false); +const [scroll, setScroll] = useState('paper'); +const [answer, setAnswer] = useState(false) +const [content, setContent] = useState('') +const [age, setAge] = useState(6); +const [newMessage, setNewMessage] = useState(false) +const [message, setMessage] = useState(null) +const [currentMsg, setCurrentMsg] = useState(0) +const [newMsgName, setNewMsgName] = useState("") +const [value, setValue] = useState() +const [saveChanges, setSaveChanges] = useState(false) +const [loadingSaveMsg, setLoadingSaveMsg] = useState(false) +const possibleMsgs = [ + ` + + + + + + + InternetGatewayDevice.TraceRouteDiagnostics.Host + 192.168.60.4 + + + + + +`, +` + + + + + InternetGatewayDevice.LANDevice.1.WLANConfiguration.2. + + + +`, +` + + + + + InternetGatewayDevice.LANDevice.1.WLANConfiguration. + + + +`, +` + + + + + + 007 + + + + `, +` + + + + + + InternetGatewayDevice.LANDevice.1.WLANConfiguration.1. + + + +`,` + + + + + InternetGatewayDevice. + 1 + + +`, +` + + + + + + InternetGatewayDevice.LANDevice.1.WLANConfiguration. + + + +`] +const [newMsgValue, setNewMsgValue] = useState(possibleMsgs[age-1]) +const [loading, setLoading] = useState(false); + +const handleNewMessageValue = (event) => { + setNewMsgValue(event.target.value) +} + const handleClose = () => { setOpen(false); }; -const handleOpen = () => { - setOpen(true); - var myHeaders = new Headers(); - myHeaders.append("Authorization", localStorage.getItem("token")); - var raw = value +const handleCancelNewMsgTemplate = () => { + setNewMessage(false) + setNewMsgName("") + setNewMsgValue(possibleMsgs[age-1]) + // setValue(possibleMsgs[age-1]) +} - var requestOptions = { - method: 'PUT', - headers: myHeaders, - body: raw, - redirect: 'follow' - }; - - var method; - - switch(age) { - case 1: - method="addObject" - break; - case 2: - method="getParameterValues" - break; - case 3: - method="setParameterValues" - break; - case 4: - method="deleteObject" - break; - } - - - fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device/cwmp/${router.query.id[0]}/${method}`, requestOptions) - .then(response => response.text()) - .then(result => { - if (result.status === 401){ - router.push("/auth/login") +const saveMsg = async () => { + let {status} = await httpRequest( + `/api/device/message?name=`+message[currentMsg].name, + "PUT", + value, + null, + ) + if ( status === 204){ + setSaveChanges(false) + setMessage(message.map((msg, index) => { + if (index === currentMsg) { + return {...msg, value: value} + }else{ + return msg } - setOpen(false) - setAnswer(true) - let teste = prettifyXml(result) - console.log(teste) - setContent(teste) - }) - .catch(error => console.log('error', error)); - }; + })) + } +} + +const createNewMsg = async () => { + setLoading(true) + let {status} = await httpRequest( + `/api/device/message/cwmp?name=`+newMsgName, + "POST", + newMsgValue, + null, + ) + if ( status === 204){ + setNewMessage(false) + setNewMsgName("") + let result = await fetchMessages() + if (result) { + setCurrentMsg(result.length-1) + } + setValue(newMsgValue) + setNewMsgValue(possibleMsgs[age-1]) + } + setLoading(false) +} + +const handleChangeMessage = (event) => { + setSaveChanges(false) + setCurrentMsg(event.target.value) + setValue(message[event.target.value].value) +} + +const handleDeleteMessage = async () => { + let {status} = await httpRequest( + `/api/device/message?name=`+message[currentMsg].name.replace(" ", '+'), + "DELETE", + ) + if ( status === 204){ + fetchMessages() + setCurrentMsg(0) + setValue("") + } +} + +const handleOpen = async () => { + setOpen(true); + + let {result, status} = await httpRequest( + `/api/device/cwmp/${router.query.id[0]}/generic`, + "PUT", + value, + null, + "text", + ) + if (status === 200){ + setAnswer(true) + console.log("result:",result) + let answer = prettifyXml(result) + if (answer == "null"){ + answer = result + } + console.log(answer) + setContent(answer) + } + setOpen(false) + +} + +const fetchMessages = async () => { + let {result, status} = await httpRequest( + `/api/device/message?type=cwmp`, + "GET", + null, + null, + ) + if ( status === 200){ + setMessage(result) + setValue(result ? result[0].value : "") + return result + } +} const handleChangeRPC = (event) => { setAge(event.target.value); - switch(event.target.value) { - case 1: - setValue(` - - - - - InternetGatewayDevice.LANDevice. - - - - `) - break; - case 2: - setValue(` - - - - - - InternetGatewayDevice.LANDevice.1.WLANConfiguration.1. - InternetGatewayDevice.LANDevice.1.WLANConfiguration.2. - InternetGatewayDevice.LANDevice.2.WLANConfiguration.2. - InternetGatewayDevice.LANDevice.2.WLANConfiguration.1. - - - - `) - break; - case 3: - setValue(` - - - - - - - - InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.Enable - 0 - - - InternetGatewayDevice.LANDevice.1.WLANConfiguration.2.SSID - HUAWEI_TEST-2 - - - LC1309123 - - - `) - break; - case 4: - setValue(` - - - - - InternetGatewayDevice.LANDevice.3. - - - - `) - break; - default: - // code block - } + setNewMsgValue(possibleMsgs[event.target.value-1]) }; - const handleChange = (event) => { - setValue(event.target.value); - }; + const handleEditMessage = (event) => { + setSaveChanges(true) + setValue(event.target.value) + } const handleSubmit = useCallback( (event) => { @@ -206,54 +285,91 @@ const handleOpen = () => { [] ); + useEffect(() => { + fetchMessages(); + },[]); + return ( - - - {handleChangeRPC(event)}} - variant='standard' - > - Create - Read - Update - Delete - - - + < EnvelopeIcon/>} + title="Custom Message" + action={ + + } + onClick={()=>{setNewMessage(true)}} + > + + New Message + + + } + > + + + + Message + {handleChangeMessage(event)}} + > + {message && message.map((msg, index) => { + return {msg.name} + })} + + + - + />:} - - - } - onClick={handleOpen} - > - Send - + {/* */} + + + } + onClick={handleDeleteMessage} + disabled={!message} + > + Delete + + {!loadingSaveMsg ? } + onClick={saveMsg} + disabled={!saveChanges} + > + Save + : } + + + } + onClick={handleOpen} + > + Send + + theme.zIndex.drawer + 1 }} @@ -307,6 +423,83 @@ const handleOpen = () => { }}>Ok + + + + + + + {setNewMsgName(event.target.value)}} + label="Name" + sx={{maxWidth: "30%", justifyContent:"center"}} + /> + + Template + {handleChangeRPC(event)}} + > + SetParameterValues + DeleteObject + AddObject + Reboot + GetParameterValues + GetParameterNames + GetParameterAttributes + + + + + + + + {/* */} + + + Cancel + + {!loading ? + } + onClick={createNewMsg} + > + Save + :} + + ); diff --git a/frontend/src/sections/devices/usp/devices-rpc.js b/frontend/src/sections/devices/usp/devices-rpc.js index 521bd9e..4f54a41 100644 --- a/frontend/src/sections/devices/usp/devices-rpc.js +++ b/frontend/src/sections/devices/usp/devices-rpc.js @@ -19,151 +19,296 @@ import { DialogContentText, DialogActions, Box, - IconButton + IconButton, + Grid } from '@mui/material'; import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon'; import PaperAirplane from '@heroicons/react/24/solid/PaperAirplaneIcon'; import CircularProgress from '@mui/material/CircularProgress'; import Backdrop from '@mui/material/Backdrop'; import { useRouter } from 'next/router'; +import { useBackendContext } from 'src/contexts/backend-context'; +import DocumentArrowDown from '@heroicons/react/24/outline/DocumentArrowDownIcon'; +import TrashIcon from '@heroicons/react/24/outline/TrashIcon'; +import PlusCircleIcon from '@heroicons/react/24/outline/PlusCircleIcon'; +import EnvelopeIcon from '@heroicons/react/24/outline/EnvelopeIcon'; +import CheckIcon from '@heroicons/react/24/outline/CheckIcon'; export const DevicesRPC = () => { const router = useRouter() +let { httpRequest } = useBackendContext() const [open, setOpen] = useState(false); const [scroll, setScroll] = useState('paper'); const [answer, setAnswer] = useState(false) const [content, setContent] = useState('') -const [age, setAge] = useState(2); +const [age, setAge] = useState(6); +const [newMessage, setNewMessage] = useState(false) +const [message, setMessage] = useState(null) +const [currentMsg, setCurrentMsg] = useState(0) +const [newMsgName, setNewMsgName] = useState("") +const [value, setValue] = useState() +const [saveChanges, setSaveChanges] = useState(false) +const [loadingSaveMsg, setLoadingSaveMsg] = useState(false) +const possibleMsgs = [ + `{ + "header": { + "msg_id": "b7dc38ea-aefb-4761-aa55-edaa97adb2f0", + "msg_type": 4 + }, + "body": { + "request": { + "set": { + "allow_partial":true, + "update_objs":[ + { + "obj_path":"Device.IP.Interface.1.", + "param_settings":[ + { + "param":"Alias", + "value":"test", + "required":true + } + ] + } + ] + } + } + } +}`, +`{ + "header": { + "msg_id": "b7dc38ea-aefb-4761-aa55-edaa97adb2f0", + "msg_type": 10 + }, + "body": { + "request": { + "delete": { + "allow_partial": true, + "obj_paths": [ + "Device.IP.Interface.[Alias==test]." + ] + } + } + } +}`, +` +{ + "header": { + "msg_id": "b7dc38ea-aefb-4761-aa55-edaa97adb2f0", + "msg_type": 8 + }, + "body": { + "request": { + "add": { + "allow_partial": true, + "create_objs": [ + { + "obj_path": "Device.IP.Interface.", + "param_settings": [ + { + "param": "Alias", + "value": "test", + "required": true + } + ] + } + ] + } + } + } +}`, +`{ + "header": { + "msg_id": "b7dc38ea-aefb-4761-aa55-edaa97adb2f0", + "msg_type": 6 + }, + "body": { + "request": { + "operate": { + "command": "Device.Reboot()", + "send_resp": true + } + } + } +}`, +`{ + "header": { + "msg_id": "b7dc38ea-aefb-4761-aa55-edaa97adb2f0", + "msg_type": 1 + }, + "body": { + "request": { + "get": { + "paramPaths": [ + "Device.WiFi.SSID.[Name==wlan0].", + "Device.IP.Interface.*.Alias", + "Device.DeviceInfo.FirmwareImage.*.Alias", + "Device.IP.Interface.1.IPv4Address.1.IPAddress" + ], + "maxDepth": 2 + } + } + } +}`,`{ + "header": { + "msg_id": "b7dc38ea-aefb-4761-aa55-edaa97adb2f0", + "msg_type": 12 + }, + "body": { + "request": { + "get_supported_dm": { + "obj_paths" : [ + "Device." + ], + "first_level_only" : false, + "return_commands" : false, + "return_events" : false, + "return_params" : true + } + } + } +}`, +`{ + "header": { + "msg_id": "b7dc38ea-aefb-4761-aa55-edaa97adb2f0", + "msg_type": 14 + }, + "body": { + "request": { + "get_instances": { + "obj_paths" : ["Device.DeviceInfo."], + "first_level_only" : false + } + } + } +}`] +const [newMsgValue, setNewMsgValue] = useState(possibleMsgs[age-1]) +const [loading, setLoading] = useState(false); -const [value, setValue] = useState(`{ - "param_paths": [ - "Device.WiFi.SSID.[Name==wlan0].", - "Device.IP.Interface.*.Alias", - "Device.DeviceInfo.FirmwareImage.*.Alias", - "Device.IP.Interface.1.IPv4Address.1.IPAddress" - ], - "max_depth": 2 -}`) +const handleNewMessageValue = (event) => { + setNewMsgValue(event.target.value) +} const handleClose = () => { setOpen(false); }; -const handleOpen = () => { - setOpen(true); - var myHeaders = new Headers(); - myHeaders.append("Content-Type", "application/json"); - myHeaders.append("Authorization", localStorage.getItem("token")); - var raw = value +const handleCancelNewMsgTemplate = () => { + setNewMessage(false) + setNewMsgName("") + setNewMsgValue(possibleMsgs[age-1]) + // setValue(possibleMsgs[age-1]) +} - var requestOptions = { - method: 'PUT', - headers: myHeaders, - body: raw, - redirect: 'follow' - }; - - var method; - - switch(age) { - case 1: - method="add" - break; - case 2: - method="get" - break; - case 3: - method="set" - break; - case 4: - method="del" - break; - } - - - fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device/${router.query.id[0]}/any/${method}`, requestOptions) - .then(response => response.text()) - .then(result => { - if (result.status === 401){ - router.push("/auth/login") +const saveMsg = async () => { + let {status} = await httpRequest( + `/api/device/message?name=`+message[currentMsg].name, + "PUT", + value, + null, + ) + if ( status === 204){ + setSaveChanges(false) + setMessage(message.map((msg, index) => { + if (index === currentMsg) { + return {...msg, value: value} + }else{ + return msg } - setOpen(false) - setAnswer(true) - let teste = JSON.stringify(JSON.parse(result), null, 2) - console.log(teste) - setContent(teste) - }) - .catch(error => console.log('error', error)); - }; + })) + } +} + +const createNewMsg = async () => { + setLoading(true) + let {status} = await httpRequest( + `/api/device/message/usp?name=`+newMsgName, + "POST", + newMsgValue, + null, + ) + if ( status === 204){ + setNewMessage(false) + setNewMsgName("") + let result = await fetchMessages() + if (result) { + setCurrentMsg(result.length-1) + } + setValue(newMsgValue) + setNewMsgValue(possibleMsgs[age-1]) + } + setLoading(false) +} + +const handleChangeMessage = (event) => { + setSaveChanges(false) + setCurrentMsg(event.target.value) + setValue(message[event.target.value].value) +} + +const handleDeleteMessage = async () => { + let {status} = await httpRequest( + `/api/device/message?name=`+message[currentMsg].name.replace(" ", '+'), + "DELETE", + ) + if ( status === 204){ + fetchMessages() + setCurrentMsg(0) + setValue("") + } +} + +const handleOpen = async () => { + setOpen(true); + + let {result, status} = await httpRequest( + `/api/device/${router.query.id[0]}/any/generic`, + "PUT", + value, + null, + ) + if (status === 200){ + setAnswer(true) + console.log("result:",result) + let answer = JSON.stringify(result, null, 2) + if (answer == "null"){ + answer = result + } + console.log(answer) + setContent(answer) + } + setOpen(false) + +} + +const fetchMessages = async () => { + let {result, status} = await httpRequest( + `/api/device/message?type=usp`, + "GET", + null, + null, + ) + if ( status === 200){ + setMessage(result) + setValue(result ? result[0].value : "") + return result + } +} const handleChangeRPC = (event) => { setAge(event.target.value); - switch(event.target.value) { - case 1: - setValue(`{ - "allow_partial": true, - "create_objs": [ - { - "obj_path": "Device.IP.Interface.", - "param_settings": [ - { - "param": "Alias", - "value": "test", - "required": true - } - ] - } - ] - }`) - break; - case 2: - setValue(`{ - "param_paths": [ - "Device.WiFi.SSID.[Name==wlan0].", - "Device.IP.Interface.*.Alias", - "Device.DeviceInfo.FirmwareImage.*.Alias", - "Device.IP.Interface.1.IPv4Address.1.IPAddress" - ], - "max_depth": 2 - }`) - break; - case 3: - setValue(` - { - "allow_partial":true, - "update_objs":[ - { - "obj_path":"Device.IP.Interface.[Alias==pamonha].", - "param_settings":[ - { - "param":"Alias", - "value":"goiaba", - "required":true - } - ] - } - ] - }`) - break; - case 4: - setValue(`{ - "allow_partial": true, - "obj_paths": [ - "Device.IP.Interface.3." - ] - }`) - break; - default: - // code block - } + setNewMsgValue(possibleMsgs[event.target.value-1]) }; - const handleChange = (event) => { - setValue(event.target.value); - }; + const handleEditMessage = (event) => { + if (message) { + setSaveChanges(true) + } + setValue(event.target.value) + } const handleSubmit = useCallback( (event) => { @@ -172,54 +317,91 @@ const handleOpen = () => { [] ); + useEffect(() => { + fetchMessages(); + },[]); + return ( - - - {handleChangeRPC(event)}} - variant='standard' - > - Create - Read - Update - Delete - - - + < EnvelopeIcon/>} + title="Custom Message" + action={ + + } + onClick={()=>{setNewMessage(true)}} + > + + New Message + + + } + > + + + + Message + {handleChangeMessage(event)}} + > + {message && message.map((msg, index) => { + return {msg.name} + })} + + + - + />:} - - - } - onClick={handleOpen} - > - Send - + {/* */} + + + } + onClick={handleDeleteMessage} + disabled={!message} + > + Delete + + {!loadingSaveMsg ? } + onClick={saveMsg} + disabled={!saveChanges} + > + Save + : } + + + } + onClick={handleOpen} + > + Send + + theme.zIndex.drawer + 1 }} @@ -273,6 +455,83 @@ const handleOpen = () => { }}>Ok + + + + + + + {setNewMsgName(event.target.value)}} + label="Name" + sx={{maxWidth: "30%", justifyContent:"center"}} + /> + + Template + {handleChangeRPC(event)}} + > + Set + Delete + Add + Operate + Get + Get Supported DM + Get Instances + + + + + + + + {/* */} + + + Cancel + + {!loading ? + } + onClick={createNewMsg} + > + Save + :} + + ); diff --git a/frontend/src/sections/settings/settings-password.js b/frontend/src/sections/settings/settings-password.js index 8899d98..e022fde 100644 --- a/frontend/src/sections/settings/settings-password.js +++ b/frontend/src/sections/settings/settings-password.js @@ -9,8 +9,14 @@ import { Stack, TextField } from '@mui/material'; +import { useBackendContext } from 'src/contexts/backend-context'; +import { useAlertContext } from 'src/contexts/error-context'; export const SettingsPassword = () => { + + let {httpRequest} = useBackendContext(); + let {setAlert} = useAlertContext(); + const [values, setValues] = useState({ password: '', confirm: '' @@ -26,15 +32,8 @@ export const SettingsPassword = () => { [] ); - const handleSubmit = useCallback( - (event) => { - event.preventDefault(); - }, - [] - ); - return ( - + { - + { + if (values.password !== values.confirm) { + console.log("Passwords do not match") + setAlert({ + severity: 'error', + message: 'Passwords do not match' + }); + return + } + let {status} = await httpRequest('/api/auth/password', 'PUT', JSON.stringify({"password": values.password})) + if (status === 204) { + console.log("Password updated") + setAlert({ + severity: 'success', + message: 'Password updated' + }); + } + }} + > Update
Loading...
no device info found