diff --git a/backend/services/controller/cmd/controller/main.go b/backend/services/controller/cmd/controller/main.go index e4b50c9..85dd7ce 100755 --- a/backend/services/controller/cmd/controller/main.go +++ b/backend/services/controller/cmd/controller/main.go @@ -26,7 +26,7 @@ func main() { db := db.NewDatabase(c.Mongo.Ctx, c.Mongo.Uri) - api := api.NewApi(c.RestApi, js, nc, bridge, db, kv) + api := api.NewApi(c, js, nc, bridge, db, kv) api.StartApi() <-done diff --git a/backend/services/controller/internal/api/api.go b/backend/services/controller/internal/api/api.go index ba19751..cae0316 100644 --- a/backend/services/controller/internal/api/api.go +++ b/backend/services/controller/internal/api/api.go @@ -17,13 +17,14 @@ import ( ) type Api struct { - port string - js jetstream.JetStream - nc *nats.Conn - bridge bridge.Bridge - db db.Database - kv jetstream.KeyValue - ctx context.Context + port string + js jetstream.JetStream + nc *nats.Conn + bridge bridge.Bridge + db db.Database + kv jetstream.KeyValue + ctx context.Context + enterpise bool } const REQUEST_TIMEOUT = time.Second * 30 @@ -33,15 +34,16 @@ const ( AdminUser ) -func NewApi(c config.RestApi, js jetstream.JetStream, nc *nats.Conn, bridge bridge.Bridge, d db.Database, kv jetstream.KeyValue) Api { +func NewApi(c *config.Config, js jetstream.JetStream, nc *nats.Conn, bridge bridge.Bridge, d db.Database, kv jetstream.KeyValue) Api { return Api{ - port: c.Port, - js: js, - nc: nc, - ctx: c.Ctx, - bridge: bridge, - db: d, - kv: kv, + port: c.RestApi.Port, + js: js, + nc: nc, + ctx: c.RestApi.Ctx, + bridge: bridge, + db: d, + kv: kv, + enterpise: c.Enterprise, } } @@ -73,10 +75,8 @@ func (a *Api) StartApi() { iot.HandleFunc("/{sn}/{mtp}/parameters", a.deviceGetSupportedParametersMsg).Methods("PUT") iot.HandleFunc("/{sn}/{mtp}/instances", a.deviceGetParameterInstances).Methods("PUT") iot.HandleFunc("/{sn}/{mtp}/operate", a.deviceOperateMsg).Methods("PUT") - iot.HandleFunc("/{sn}/{mtp}/fw_update", a.deviceFwUpdate).Methods("PUT") + iot.HandleFunc("/{sn}/{mtp}/fw_update", a.deviceFwUpdate).Methods("PUT") //TODO: put it to work and generalize for usp and cwmp iot.HandleFunc("/{sn}/wifi", a.deviceWifi).Methods("PUT", "GET") - // mtp := r.PathPrefix("/api/mtp").Subrouter() - // mtp.HandleFunc("", a.mtpInfo).Methods("GET") dash := r.PathPrefix("/api/info").Subrouter() dash.HandleFunc("/vendors", a.vendorsInfo).Methods("GET") dash.HandleFunc("/status", a.statusInfo).Methods("GET") diff --git a/backend/services/controller/internal/api/cwmp.go b/backend/services/controller/internal/api/cwmp.go index d6ca034..bb9d34c 100644 --- a/backend/services/controller/internal/api/cwmp.go +++ b/backend/services/controller/internal/api/cwmp.go @@ -3,16 +3,20 @@ package api import ( "encoding/json" "encoding/xml" + "errors" "io" "net/http" "github.com/leandrofars/oktopus/internal/bridge" "github.com/leandrofars/oktopus/internal/cwmp" + "github.com/leandrofars/oktopus/internal/entity" n "github.com/leandrofars/oktopus/internal/nats" "github.com/leandrofars/oktopus/internal/utils" "github.com/nats-io/nats.go" ) +var errDeviceModelNotFound = errors.New("device model not found") + func (a *Api) cwmpGetParameterNamesMsg(w http.ResponseWriter, r *http.Request) { sn := getSerialNumberFromRequest(r) @@ -147,3 +151,17 @@ func cwmpInteraction[T cwmp.SetParameterValuesResponse | cwmp.DeleteObjectRespon } return data, response, err } + +func cwmpGetDeviceModel(device *entity.Device, w http.ResponseWriter) (string, error) { + var model string + if device.Model != "" { + model = device.Model + } else if device.ProductClass != "" { + model = device.ProductClass + } else { + w.WriteHeader(http.StatusInternalServerError) + w.Write(utils.Marshall("Couldn't get device model")) + return model, errDeviceModelNotFound + } + return model, nil +} diff --git a/backend/services/controller/internal/api/enterprise.go b/backend/services/controller/internal/api/enterprise.go new file mode 100644 index 0000000..59cfc5a --- /dev/null +++ b/backend/services/controller/internal/api/enterprise.go @@ -0,0 +1,24 @@ +package api + +import ( + "net/http" + + "github.com/leandrofars/oktopus/internal/bridge" + "github.com/leandrofars/oktopus/internal/entity" +) + +func (a *Api) getEnterpriseResource( + resource string, + action string, + device *entity.Device, + sn string, + w http.ResponseWriter, + body []byte, +) error { + model, err := cwmpGetDeviceModel(device, w) + if err != nil { + return err + } + err = bridge.NatsEnterpriseInteraction("enterprise.v1.cwmp."+model+"."+sn+"."+resource+"."+action, body, w, a.nc) + return err +} diff --git a/backend/services/controller/internal/api/wifi.go b/backend/services/controller/internal/api/wifi.go index e1de563..f21539c 100644 --- a/backend/services/controller/internal/api/wifi.go +++ b/backend/services/controller/internal/api/wifi.go @@ -1,6 +1,7 @@ package api import ( + "io" "log" "net/http" "strings" @@ -212,6 +213,11 @@ func (a *Api) deviceWifi(w http.ResponseWriter, r *http.Request) { if device.Cwmp == entity.Online { + if a.enterpise { + a.getEnterpriseResource("wifi", "get", device, sn, w, []byte{}) + return + } + var ( NUMBER_OF_WIFI_PARAMS_TO_GET = 5 ) @@ -341,6 +347,17 @@ func (a *Api) deviceWifi(w http.ResponseWriter, r *http.Request) { if device.Cwmp == entity.Online { + if a.enterpise { + payload, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write(utils.Marshall(err.Error())) + return + } + a.getEnterpriseResource("wifi", "set", device, sn, w, payload) + return + } + var body []WiFi err := utils.MarshallDecoder(&body, r.Body) diff --git a/backend/services/controller/internal/bridge/bridge.go b/backend/services/controller/internal/bridge/bridge.go index 319ef13..0cb9537 100644 --- a/backend/services/controller/internal/bridge/bridge.go +++ b/backend/services/controller/internal/bridge/bridge.go @@ -246,3 +246,50 @@ func NatsCwmpInteraction( return answer.Msg, nil } + +func NatsEnterpriseInteraction( + subj string, + body []byte, + w http.ResponseWriter, + nc *nats.Conn, +) error { + + log.Println("Sending enterprise message") + log.Println("Subject: ", subj) + + var answer entity.MsgAnswer[[]byte] + + msg, err := nc.Request(subj, body, local.NATS_REQUEST_TIMEOUT) + 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")) + return err + } + w.WriteHeader(http.StatusInternalServerError) + w.Write(utils.Marshall("Error to communicate with nats: " + err.Error())) + return err + } + + err = json.Unmarshal(msg.Data, &answer) + if err != nil { + + var errMsg *entity.MsgAnswer[*string] + err = json.Unmarshal(msg.Data, &errMsg) + + if err != nil { + log.Println("Bad answer message formatting: ", err.Error()) + w.WriteHeader(http.StatusInternalServerError) + w.Write(msg.Data) + return err + } + + log.Printf("Error message received, msg: %s, code: %d", *errMsg.Msg, errMsg.Code) + w.WriteHeader(errMsg.Code) + w.Write(utils.Marshall(*errMsg.Msg)) + return errNatsMsgReceivedWithErrorData + } + + w.Write(answer.Msg) + return nil +} diff --git a/backend/services/controller/internal/config/config.go b/backend/services/controller/internal/config/config.go index e3eb06f..e5b9b66 100644 --- a/backend/services/controller/internal/config/config.go +++ b/backend/services/controller/internal/config/config.go @@ -30,9 +30,10 @@ type RestApi struct { } type Config struct { - RestApi RestApi - Nats Nats - Mongo Mongo + RestApi RestApi + Nats Nats + Mongo Mongo + Enterprise bool } func NewConfig() *Config { @@ -45,6 +46,7 @@ func NewConfig() *Config { natsVerifyCertificates := flag.Bool("nats_verify_certificates", lookupEnvOrBool("NATS_VERIFY_CERTIFICATES", false), "verify validity of certificates from nats server") 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") flHelp := flag.Bool("help", false, "Help") /* @@ -78,6 +80,7 @@ func NewConfig() *Config { Uri: *mongoUri, Ctx: ctx, }, + Enterprise: *enterpise, } }