diff --git a/.gitignore b/.gitignore index 8846f37d175c053007ac8dee5e513c8b7b2b6256..ddc9524e0603833cf17d69606bfcfffb1a0edc83 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ log/ vendor/ public/ +node_modules/ +yarn.lock + # emacs/vim GPATH GRTAGS diff --git a/backend/WebUI/api_webui.go b/backend/WebUI/api_webui.go index 8d1b24bed123ca6f7e695d8998bc94cf4d503957..0141033486b0119b9b7a419ecbad931433a81c4d 100644 --- a/backend/WebUI/api_webui.go +++ b/backend/WebUI/api_webui.go @@ -1,12 +1,14 @@ package WebUI import ( + "context" "crypto/tls" "encoding/json" "fmt" "net/http" "os" "reflect" + "strconv" "strings" "time" @@ -24,15 +26,17 @@ import ( ) const ( - authSubsDataColl = "subscriptionData.authenticationData.authenticationSubscription" - amDataColl = "subscriptionData.provisionedData.amData" - smDataColl = "subscriptionData.provisionedData.smData" - smfSelDataColl = "subscriptionData.provisionedData.smfSelectionSubscriptionData" - amPolicyDataColl = "policyData.ues.amData" - smPolicyDataColl = "policyData.ues.smData" - flowRuleDataColl = "policyData.ues.flowRule" - userDataColl = "userData" - tenantDataColl = "tenantData" + authSubsDataColl = "subscriptionData.authenticationData.authenticationSubscription" + amDataColl = "subscriptionData.provisionedData.amData" + smDataColl = "subscriptionData.provisionedData.smData" + smfSelDataColl = "subscriptionData.provisionedData.smfSelectionSubscriptionData" + amPolicyDataColl = "policyData.ues.amData" + smPolicyDataColl = "policyData.ues.smData" + flowRuleDataColl = "policyData.ues.flowRule" + qosFlowDataColl = "policyData.ues.qosFlow" + userDataColl = "userData" + tenantDataColl = "tenantData" + msisdnSupiMapColl = "subscriptionData.msisdnSupiMap" ) var httpsClient *http.Client @@ -46,24 +50,30 @@ func init() { } func mapToByte(data map[string]interface{}) (ret []byte) { - ret, _ = json.Marshal(data) + ret, err := json.Marshal(data) + if err != nil { + logger.ProcLog.Errorf("mapToByte err: %+v", err) + } return } func sliceToByte(data []map[string]interface{}) (ret []byte) { - ret, _ = json.Marshal(data) + ret, err := json.Marshal(data) + if err != nil { + logger.ProcLog.Errorf("sliceToByte err: %+v", err) + } return } func toBsonM(data interface{}) (ret bson.M) { - tmp, _ := json.Marshal(data) - json.Unmarshal(tmp, &ret) - return -} - -func toBsonA(data interface{}) (ret bson.A) { - tmp, _ := json.Marshal(data) - json.Unmarshal(tmp, &ret) + tmp, err := json.Marshal(data) + if err != nil { + logger.ProcLog.Errorf("toBsonM err: %+v", err) + } + err = json.Unmarshal(tmp, &ret) + if err != nil { + logger.ProcLog.Errorf("toBsonM err: %+v", err) + } return } @@ -78,13 +88,52 @@ func UnescapeDnn(dnnKey string) string { func setCorsHeader(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Origin", "*") c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") - c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") + c.Writer.Header().Set( + "Access-Control-Allow-Headers", + "Content-Type, Content-Length, Accept-Encoding, "+ + "X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With", + ) c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, PATCH, DELETE") } +func getMsisdn(gpsis interface{}) string { + msisdn := "" + gpsisReflected := reflect.ValueOf(gpsis) // use reflect to range over interface{} + for i := 0; i < gpsisReflected.Len(); i++ { + gpsi := gpsisReflected.Index(i).Interface().(string) // transform type reflect.value to string + if strings.HasPrefix(gpsi, "msisdn-") { // check if gpsi contain prefix "msisdn-" + msisdn = gpsi[7:] + } + } + return msisdn +} + +func msisdnToSupi(ueId string) string { + if strings.HasPrefix(ueId, "msisdn-") { + filter := bson.M{"msisdn": ueId[7:]} + dbResult, err := mongoapi.RestfulAPIGetOne(msisdnSupiMapColl, filter) + if err != nil { + logger.ProcLog.Errorf("GetSupibyMsisdn err: %+v", err) + } + if dbResult != nil { + ueId = dbResult["ueId"].(string) + } else { + // db cannot find a supi mapped to msisdn, return null string for error detection + logger.ProcLog.Error("msisdn not found") + return "" + } + } + return ueId +} + func sendResponseToClient(c *gin.Context, response *http.Response) { var jsonData interface{} - json.NewDecoder(response.Body).Decode(&jsonData) + err := json.NewDecoder(response.Body).Decode(&jsonData) + if err != nil { + logger.ProcLog.Errorf("sendResponseToClient err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } c.JSON(response.StatusCode, jsonData) } @@ -93,7 +142,9 @@ func sendResponseToClientFilterTenant(c *gin.Context, response *http.Response, t filterTenantIdOnly := bson.M{"tenantId": tenantId} amDataList, err := mongoapi.RestfulAPIGetMany(amDataColl, filterTenantIdOnly) if err != nil { - logger.WebUILog.Errorf("sendResponseToClientFilterTenant err: %+v", err) + logger.ProcLog.Errorf("sendResponseToClientFilterTenant err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } tenantCheck := func(supi string) bool { @@ -107,7 +158,12 @@ func sendResponseToClientFilterTenant(c *gin.Context, response *http.Response, t // Response data. var jsonData interface{} - json.NewDecoder(response.Body).Decode(&jsonData) + err = json.NewDecoder(response.Body).Decode(&jsonData) + if err != nil { + logger.ProcLog.Errorf("sendResponseToClientFilterTenant err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } s := reflect.ValueOf(jsonData) if s.Kind() != reflect.Slice { @@ -135,7 +191,7 @@ func sendResponseToClientFilterTenant(c *gin.Context, response *http.Response, t func GetSampleJSON(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Get a JSON Example") + logger.ProcLog.Infoln("Get a JSON Example") var subsData SubsData @@ -345,14 +401,22 @@ func JWT(email, userId, tenantId string) string { claims["email"] = email claims["tenantId"] = tenantId - tokenString, _ := token.SignedString([]byte(os.Getenv("SIGNINGKEY"))) + tokenString, err := token.SignedString([]byte(os.Getenv("SIGNINGKEY"))) + if err != nil { + logger.ProcLog.Errorf("JWT err: %+v", err) + } return tokenString } -func generateHash(password string) { - hash, _ := bcrypt.GenerateFromPassword([]byte(password), 12) - logger.WebUILog.Warnln("Password hash:", hash) +func generateHash(password string) error { + hash, err := bcrypt.GenerateFromPassword([]byte(password), 12) + if err != nil { + logger.ProcLog.Errorf("generateHash err: %+v", err) + return err + } + logger.ProcLog.Warnln("Password hash:", hash) + return err } func Login(c *gin.Context) { @@ -361,21 +425,28 @@ func Login(c *gin.Context) { login := LoginRequest{} err := json.NewDecoder(c.Request.Body).Decode(&login) if err != nil { - logger.WebUILog.Warnln("JSON decode error", err) + logger.ProcLog.Warnln("JSON decode error", err) c.JSON(http.StatusInternalServerError, gin.H{}) return } - generateHash(login.Password) + err = generateHash(login.Password) + if err != nil { + logger.ProcLog.Errorf("Login err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } filterEmail := bson.M{"email": login.Username} userData, err := mongoapi.RestfulAPIGetOne(userDataColl, filterEmail) if err != nil { - logger.WebUILog.Errorf("Login err: %+v", err) + logger.ProcLog.Errorf("Login err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(userData) == 0 { - logger.WebUILog.Warnln("Can't find user email", login.Username) + logger.ProcLog.Warnln("Can't find user email", login.Username) c.JSON(http.StatusForbidden, gin.H{}) return } @@ -384,7 +455,7 @@ func Login(c *gin.Context) { err = bcrypt.CompareHashAndPassword([]byte(hash), []byte(login.Password)) if err != nil { - logger.WebUILog.Warnln("Password incorrect", login.Username) + logger.ProcLog.Warnln("Password incorrect", login.Username) c.JSON(http.StatusForbidden, gin.H{}) return } @@ -392,12 +463,12 @@ func Login(c *gin.Context) { userId := userData["userId"].(string) tenantId := userData["tenantId"].(string) - logger.WebUILog.Warnln("Login success", login.Username) - logger.WebUILog.Warnln("userid", userId) - logger.WebUILog.Warnln("tenantid", tenantId) + logger.ProcLog.Warnln("Login success", login.Username) + logger.ProcLog.Warnln("userid", userId) + logger.ProcLog.Warnln("tenantid", tenantId) token := JWT(login.Username, userId, tenantId) - logger.WebUILog.Warnln("token", token) + logger.ProcLog.Warnln("token", token) oauth := OAuth{} oauth.AccessToken = token @@ -421,7 +492,6 @@ func ParseJWT(tokenStr string) (jwt.MapClaims, error) { token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) { return []byte(os.Getenv("SIGNINGKEY")), nil }) - if err != nil { return nil, errors.Wrap(err, "ParseJWT error") } @@ -441,7 +511,7 @@ func CheckAuth(c *gin.Context) bool { } } -// Tenat ID +// Tenant ID func GetTenantId(c *gin.Context) (string, error) { tokenStr := c.GetHeader("Token") if tokenStr == "admin" { @@ -449,6 +519,7 @@ func GetTenantId(c *gin.Context) (string, error) { } claims, err := ParseJWT(tokenStr) if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{}) return "", errors.Wrap(err, "GetTenantId error") } return claims["tenantId"].(string), nil @@ -465,11 +536,17 @@ func GetTenants(c *gin.Context) { tenantDataInterface, err := mongoapi.RestfulAPIGetMany(tenantDataColl, bson.M{}) if err != nil { - logger.WebUILog.Errorf("GetTenants err: %+v", err) + logger.ProcLog.Errorf("GetTenants err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } var tenantData []Tenant - json.Unmarshal(sliceToByte(tenantDataInterface), &tenantData) - + err = json.Unmarshal(sliceToByte(tenantDataInterface), &tenantData) + if err != nil { + logger.ProcLog.Errorf("GetTenants err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } c.JSON(http.StatusOK, tenantData) } @@ -486,7 +563,9 @@ func GetTenantByID(c *gin.Context) { filterTenantIdOnly := bson.M{"tenantId": tenantId} tenantDataInterface, err := mongoapi.RestfulAPIGetOne(tenantDataColl, filterTenantIdOnly) if err != nil { - logger.WebUILog.Errorf("GetTenantByID err: %+v", err) + logger.ProcLog.Errorf("GetTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(tenantDataInterface) == 0 { c.JSON(http.StatusNotFound, bson.M{}) @@ -494,8 +573,12 @@ func GetTenantByID(c *gin.Context) { } var tenantData Tenant - json.Unmarshal(mapToByte(tenantDataInterface), &tenantData) - + err = json.Unmarshal(mapToByte(tenantDataInterface), &tenantData) + if err != nil { + logger.ProcLog.Errorf("GetTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } c.JSON(http.StatusOK, tenantData) } @@ -520,7 +603,9 @@ func PostTenant(c *gin.Context) { tenantBsonM := toBsonM(tenantData) filterTenantIdOnly := bson.M{"tenantId": tenantData.TenantId} if _, err := mongoapi.RestfulAPIPost(tenantDataColl, filterTenantIdOnly, tenantBsonM); err != nil { - logger.WebUILog.Errorf("PostTenant err: %+v", err) + logger.ProcLog.Errorf("PostTenant err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, tenantData) @@ -539,7 +624,9 @@ func PutTenantByID(c *gin.Context) { filterTenantIdOnly := bson.M{"tenantId": tenantId} tenantDataInterface, err := mongoapi.RestfulAPIGetOne(tenantDataColl, filterTenantIdOnly) if err != nil { - logger.WebUILog.Errorf("PutTenantByID err: %+v", err) + logger.ProcLog.Errorf("PutTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(tenantDataInterface) == 0 { c.JSON(http.StatusNotFound, bson.M{}) @@ -556,7 +643,9 @@ func PutTenantByID(c *gin.Context) { tenantBsonM := toBsonM(tenantData) filterTenantIdOnly = bson.M{"tenantId": tenantId} if _, err := mongoapi.RestfulAPIPost(tenantDataColl, filterTenantIdOnly, tenantBsonM); err != nil { - logger.WebUILog.Errorf("PutTenantByID err: %+v", err) + logger.ProcLog.Errorf("PutTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, gin.H{}) @@ -574,13 +663,19 @@ func DeleteTenantByID(c *gin.Context) { filterTenantIdOnly := bson.M{"tenantId": tenantId} if err := mongoapi.RestfulAPIDeleteMany(amDataColl, filterTenantIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteTenantByID err: %+v", err) + logger.ProcLog.Errorf("DeleteTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIDeleteMany(userDataColl, filterTenantIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteTenantByID err: %+v", err) + logger.ProcLog.Errorf("DeleteTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIDeleteOne(tenantDataColl, filterTenantIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteTenantByID err: %+v", err) + logger.ProcLog.Errorf("DeleteTenantByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, gin.H{}) @@ -591,7 +686,7 @@ func GetTenantById(tenantId string) map[string]interface{} { filterTenantIdOnly := bson.M{"tenantId": tenantId} tenantData, err := mongoapi.RestfulAPIGetOne(tenantDataColl, filterTenantIdOnly) if err != nil { - logger.WebUILog.Errorf("GetTenantById err: %+v", err) + logger.ProcLog.Errorf("GetTenantById err: %+v", err) return nil } return tenantData @@ -615,12 +710,20 @@ func GetUsers(c *gin.Context) { filterTenantIdOnly := bson.M{"tenantId": tenantId} userDataInterface, err := mongoapi.RestfulAPIGetMany(userDataColl, filterTenantIdOnly) if err != nil { - logger.WebUILog.Errorf("GetUsers err: %+v", err) + logger.ProcLog.Errorf("GetUsers err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } var userData []User - json.Unmarshal(sliceToByte(userDataInterface), &userData) - for pos, _ := range userData { + err = json.Unmarshal(sliceToByte(userDataInterface), &userData) + if err != nil { + logger.ProcLog.Errorf("GetUsers err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + + for pos := range userData { userData[pos].EncryptedPassword = "" } @@ -645,7 +748,9 @@ func GetUserByID(c *gin.Context) { filterUserIdOnly := bson.M{"tenantId": tenantId, "userId": userId} userDataInterface, err := mongoapi.RestfulAPIGetOne(userDataColl, filterUserIdOnly) if err != nil { - logger.WebUILog.Errorf("GetUserByID err: %+v", err) + logger.ProcLog.Errorf("GetUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(userDataInterface) == 0 { c.JSON(http.StatusNotFound, bson.M{}) @@ -653,7 +758,13 @@ func GetUserByID(c *gin.Context) { } var userData User - json.Unmarshal(mapToByte(userDataInterface), &userData) + err = json.Unmarshal(mapToByte(userDataInterface), &userData) + if err != nil { + logger.ProcLog.Errorf("GetUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + userData.EncryptedPassword = "" c.JSON(http.StatusOK, userData) @@ -682,23 +793,32 @@ func PostUserByID(c *gin.Context) { filterEmail := bson.M{"email": userData.Email} userWithEmailData, err := mongoapi.RestfulAPIGetOne(userDataColl, filterEmail) if err != nil { - logger.WebUILog.Errorf("PostUserByID err: %+v", err) + logger.ProcLog.Errorf("PostUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(userWithEmailData) != 0 { - logger.WebUILog.Warnln("Email already exists", userData.Email) + logger.ProcLog.Warnln("Email already exists", userData.Email) c.JSON(http.StatusForbidden, gin.H{}) return } userData.TenantId = tenantId userData.UserId = uuid.Must(uuid.NewRandom()).String() - hash, _ := bcrypt.GenerateFromPassword([]byte(userData.EncryptedPassword), 12) + hash, err := bcrypt.GenerateFromPassword([]byte(userData.EncryptedPassword), 12) + if err != nil { + logger.ProcLog.Errorf("PostUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } userData.EncryptedPassword = string(hash) userBsonM := toBsonM(userData) filterUserIdOnly := bson.M{"tenantId": userData.TenantId, "userId": userData.UserId} if _, err := mongoapi.RestfulAPIPost(userDataColl, filterUserIdOnly, userBsonM); err != nil { - logger.WebUILog.Errorf("PostUserByID err: %+v", err) + logger.ProcLog.Errorf("PostUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, userData) @@ -728,7 +848,9 @@ func PutUserByID(c *gin.Context) { filterUserIdOnly := bson.M{"tenantId": tenantId, "userId": userId} userDataInterface, err := mongoapi.RestfulAPIGetOne(userDataColl, filterUserIdOnly) if err != nil { - logger.WebUILog.Errorf("PutUserByID err: %+v", err) + logger.ProcLog.Errorf("PutUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(userDataInterface) == 0 { c.JSON(http.StatusNotFound, bson.M{}) @@ -736,13 +858,20 @@ func PutUserByID(c *gin.Context) { } var userData User - json.Unmarshal(mapToByte(userDataInterface), &userData) + err = json.Unmarshal(mapToByte(userDataInterface), &userData) + if err != nil { + logger.ProcLog.Errorf("PutUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } if newUserData.Email != "" && newUserData.Email != userData.Email { filterEmail := bson.M{"email": newUserData.Email} sameEmailInterface, err := mongoapi.RestfulAPIGetOne(userDataColl, filterEmail) if err != nil { - logger.WebUILog.Errorf("PutUserByID err: %+v", err) + logger.ProcLog.Errorf("PutUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if len(sameEmailInterface) != 0 { c.JSON(http.StatusBadRequest, bson.M{}) @@ -752,13 +881,20 @@ func PutUserByID(c *gin.Context) { } if newUserData.EncryptedPassword != "" { - hash, _ := bcrypt.GenerateFromPassword([]byte(newUserData.EncryptedPassword), 12) + hash, err := bcrypt.GenerateFromPassword([]byte(newUserData.EncryptedPassword), 12) + if err != nil { + logger.ProcLog.Errorf("PutUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } userData.EncryptedPassword = string(hash) } userBsonM := toBsonM(userData) if _, err := mongoapi.RestfulAPIPost(userDataColl, filterUserIdOnly, userBsonM); err != nil { - logger.WebUILog.Errorf("PutUserByID err: %+v", err) + logger.ProcLog.Errorf("PutUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, userData) @@ -781,7 +917,9 @@ func DeleteUserByID(c *gin.Context) { filterUserIdOnly := bson.M{"tenantId": tenantId, "userId": userId} if err := mongoapi.RestfulAPIDeleteOne(userDataColl, filterUserIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteUserByID err: %+v", err) + logger.ProcLog.Errorf("DeleteUserByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusOK, gin.H{}) @@ -791,7 +929,7 @@ func DeleteUserByID(c *gin.Context) { func GetSubscribers(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Get All Subscribers List") + logger.ProcLog.Infoln("Get All Subscribers List") tokenStr := c.GetHeader("Token") @@ -801,7 +939,7 @@ func GetSubscribers(c *gin.Context) { claims, err = ParseJWT(tokenStr) } if err != nil { - logger.WebUILog.Errorln(err.Error()) + logger.ProcLog.Errorln(err.Error()) c.JSON(http.StatusBadRequest, gin.H{ "cause": "Illegal Token", }) @@ -811,26 +949,37 @@ func GetSubscribers(c *gin.Context) { var subsList []SubsListIE = make([]SubsListIE, 0) amDataList, err := mongoapi.RestfulAPIGetMany(amDataColl, bson.M{}) if err != nil { - logger.WebUILog.Errorf("GetSubscribers err: %+v", err) + logger.ProcLog.Errorf("GetSubscribers err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } for _, amData := range amDataList { ueId := amData["ueId"] servingPlmnId := amData["servingPlmnId"] + msisdn := getMsisdn(amData["gpsis"]) tenantId := amData["tenantId"] filterUeIdOnly := bson.M{"ueId": ueId} authSubsDataInterface, err := mongoapi.RestfulAPIGetOne(authSubsDataColl, filterUeIdOnly) if err != nil { - logger.WebUILog.Errorf("GetSubscribers err: %+v", err) + logger.ProcLog.Errorf("GetSubscribers err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } var authSubsData AuthSub - json.Unmarshal(mapToByte(authSubsDataInterface), &authSubsData) + err = json.Unmarshal(mapToByte(authSubsDataInterface), &authSubsData) + if err != nil { + logger.ProcLog.Errorf("GetSubscribers err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } if tokenStr == "admin" || tenantId == claims["tenantId"].(string) { tmp := SubsListIE{ PlmnID: servingPlmnId.(string), UeId: ueId.(string), + Msisdn: msisdn, } subsList = append(subsList, tmp) } @@ -842,59 +991,128 @@ func GetSubscribers(c *gin.Context) { func GetSubscriberByID(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Get One Subscriber Data") + logger.ProcLog.Infoln("Get One Subscriber Data") var subsData SubsData ueId := c.Param("ueId") + ueId = msisdnToSupi(ueId) servingPlmnId := c.Param("servingPlmnId") - + // checking whether msisdn is successfully transformed to supi or not + if ueId == "" { + logger.ProcLog.Errorf("GetSubscriberByID err: msisdn does not exists") + c.JSON(http.StatusNotFound, gin.H{ + "cause": "msisdn does not exists", + }) + return + } filterUeIdOnly := bson.M{"ueId": ueId} filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} authSubsDataInterface, err := mongoapi.RestfulAPIGetOne(authSubsDataColl, filterUeIdOnly) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } amDataDataInterface, err := mongoapi.RestfulAPIGetOne(amDataColl, filter) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } smDataDataInterface, err := mongoapi.RestfulAPIGetMany(smDataColl, filter) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } smfSelDataInterface, err := mongoapi.RestfulAPIGetOne(smfSelDataColl, filter) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } amPolicyDataInterface, err := mongoapi.RestfulAPIGetOne(amPolicyDataColl, filterUeIdOnly) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } smPolicyDataInterface, err := mongoapi.RestfulAPIGetOne(smPolicyDataColl, filterUeIdOnly) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } flowRuleDataInterface, err := mongoapi.RestfulAPIGetMany(flowRuleDataColl, filter) if err != nil { - logger.WebUILog.Errorf("GetSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + qosFlowInterface, err := mongoapi.RestfulAPIGetMany(qosFlowDataColl, filter) + if err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } var authSubsData models.AuthenticationSubscription - json.Unmarshal(mapToByte(authSubsDataInterface), &authSubsData) + if err := json.Unmarshal(mapToByte(authSubsDataInterface), &authSubsData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var amDataData models.AccessAndMobilitySubscriptionData - json.Unmarshal(mapToByte(amDataDataInterface), &amDataData) + if err := json.Unmarshal(mapToByte(amDataDataInterface), &amDataData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var smDataData []models.SessionManagementSubscriptionData - json.Unmarshal(sliceToByte(smDataDataInterface), &smDataData) + if err := json.Unmarshal(sliceToByte(smDataDataInterface), &smDataData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var smfSelData models.SmfSelectionSubscriptionData - json.Unmarshal(mapToByte(smfSelDataInterface), &smfSelData) + if err := json.Unmarshal(mapToByte(smfSelDataInterface), &smfSelData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var amPolicyData models.AmPolicyData - json.Unmarshal(mapToByte(amPolicyDataInterface), &amPolicyData) + if err := json.Unmarshal(mapToByte(amPolicyDataInterface), &amPolicyData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var smPolicyData models.SmPolicyData - json.Unmarshal(mapToByte(smPolicyDataInterface), &smPolicyData) + if err := json.Unmarshal(mapToByte(smPolicyDataInterface), &smPolicyData); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } var flowRules []FlowRule - json.Unmarshal(sliceToByte(flowRuleDataInterface), &flowRules) + if err := json.Unmarshal(sliceToByte(flowRuleDataInterface), &flowRules); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + var qosFlows []QosFlow + if err := json.Unmarshal(sliceToByte(qosFlowInterface), &qosFlows); err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + + if flowRules == nil { + flowRules = make([]FlowRule, 0) + } + if qosFlows == nil { + qosFlows = make([]QosFlow, 0) + } for key, SnssaiData := range smPolicyData.SmPolicySnssaiData { tmpSmPolicyDnnData := make(map[string]models.SmPolicyDnnData) @@ -916,15 +1134,27 @@ func GetSubscriberByID(c *gin.Context) { AmPolicyData: amPolicyData, SmPolicyData: smPolicyData, FlowRules: flowRules, + QosFlows: qosFlows, } c.JSON(http.StatusOK, subsData) } // Post subscriber by IMSI(ueId) and PlmnID(servingPlmnId) +// PostSubscriberByID godoc +// @Summary CreateSubscriberByID +// @Description Create subscriber by IMSI(ueId) and PlmnID(servingPlmnId) +// @Accept json +// @Produce json +// @Param ueId path string true "imsi" +// @Param servingPlmnId path string true "servingPlmnId" +// @Param subdata body SubsData true "sub data" +// @Success 201 "Create subscription success" +// @Failure 400 {object} HTTPError "JSON format incorrect" +// @Router /subscriber/{ueId}/{servingPlmnId}/{userNumber} [post] func PostSubscriberByID(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Post One Subscriber Data") + logger.ProcLog.Infoln("Post One Subscriber Data") var claims jwt.MapClaims = nil var err error = nil @@ -934,7 +1164,7 @@ func PostSubscriberByID(c *gin.Context) { claims, err = ParseJWT(tokenStr) } if err != nil { - logger.WebUILog.Errorln(err.Error()) + logger.ProcLog.Errorln(err.Error()) c.JSON(http.StatusBadRequest, gin.H{ "cause": "Illegal Token", }) @@ -942,205 +1172,295 @@ func PostSubscriberByID(c *gin.Context) { } var subsData SubsData - if err := c.ShouldBindJSON(&subsData); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %v", err) + err = c.ShouldBindJSON(&subsData) + if err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %v", err) c.JSON(http.StatusBadRequest, gin.H{ "cause": "JSON format incorrect", }) return } - - ueId := c.Param("ueId") + ueId := strings.Split(c.Param("ueId"), "-")[1] servingPlmnId := c.Param("servingPlmnId") + userNumber := c.Param("userNumber") + if userNumber == "" { + userNumber = "1" + } + userNumberTemp, err := strconv.Atoi(userNumber) + if err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %+v", err) + c.JSON(http.StatusBadRequest, gin.H{ + "cause": "userNumber format incorrect", + }) + return + } + msisdn := getMsisdn(toBsonM(subsData.AccessAndMobilitySubscriptionData)["gpsis"]) + msisdnTemp := 0 + if msisdn != "" { + msisdnTemp, err = strconv.Atoi(msisdn) + if err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %+v", err) + c.JSON(http.StatusBadRequest, gin.H{ + "cause": "msisdn format incorrect", + }) + return + } + } - filterUeIdOnly := bson.M{"ueId": ueId} - filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} + ueIdTemp, err := strconv.Atoi(ueId) + if err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %+v", err) + c.JSON(http.StatusBadRequest, gin.H{ + "cause": "ueId format incorrect", + }) + return + } - // Lookup same UE ID of other tenant's subscription. - if claims != nil { - authSubsDataInterface, err := mongoapi.RestfulAPIGetOne(authSubsDataColl, filterUeIdOnly) - if err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) + for i := 0; i < userNumberTemp; i++ { + ueId = fmt.Sprintf("imsi-%015d", ueIdTemp) + if msisdnTemp != 0 { + if !validate(ueId, msisdn) { + logger.ProcLog.Errorf("duplicate msisdn: %v", msisdn) + c.JSON(http.StatusBadRequest, gin.H{ + "cause": "duplicate msisdn", + }) + return + } + msisdnTemp += 1 } - if len(authSubsDataInterface) > 0 { - if authSubsDataInterface["tenantId"].(string) != claims["tenantId"].(string) { - c.JSON(http.StatusUnprocessableEntity, gin.H{}) + ueIdTemp += 1 + + subsData.AccessAndMobilitySubscriptionData.Gpsis[0] = "msisdn-" + msisdn + // create a msisdn-supi map + logger.ProcLog.Infof("PostSubscriberByID msisdn: %+v", msisdn) + msisdnSupiMapOperation(ueId, msisdn, "post") + filterUeIdOnly := bson.M{"ueId": ueId} + + // Lookup same UE ID of other tenant's subscription. + if claims != nil { + authSubsDataInterface, err := mongoapi.RestfulAPIGetOne(authSubsDataColl, filterUeIdOnly) + if err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) return } + if len(authSubsDataInterface) > 0 { + if authSubsDataInterface["tenantId"].(string) != claims["tenantId"].(string) { + c.JSON(http.StatusUnprocessableEntity, gin.H{}) + return + } + } } + dbOperation(ueId, servingPlmnId, "post", &subsData, claims) } + c.JSON(http.StatusCreated, gin.H{}) +} - authSubsBsonM := toBsonM(subsData.AuthenticationSubscription) - authSubsBsonM["ueId"] = ueId - if claims != nil { - authSubsBsonM["tenantId"] = claims["tenantId"].(string) +func validate(supi string, msisdn string) bool { + filter := bson.M{"msisdn": msisdn} + msisdnSupiMap, err := mongoapi.RestfulAPIGetOne(msisdnSupiMapColl, filter) + if err != nil { + logger.ProcLog.Errorf("GetSubscriberByID err: %+v", err) } - amDataBsonM := toBsonM(subsData.AccessAndMobilitySubscriptionData) - amDataBsonM["ueId"] = ueId - amDataBsonM["servingPlmnId"] = servingPlmnId - if claims != nil { - amDataBsonM["tenantId"] = claims["tenantId"].(string) + if msisdnSupiMap != nil && msisdnSupiMap["ueId"] != supi { + return false + } else { + return true } +} - smDatasBsonA := make([]interface{}, 0, len(subsData.SessionManagementSubscriptionData)) - for _, smSubsData := range subsData.SessionManagementSubscriptionData { - smDataBsonM := toBsonM(smSubsData) - smDataBsonM["ueId"] = ueId - smDataBsonM["servingPlmnId"] = servingPlmnId - smDatasBsonA = append(smDatasBsonA, smDataBsonM) - } +func msisdnSupiMapOperation(supi string, msisdn string, method string) { + filter := bson.M{"ueId": supi} + data := bson.M{"ueId": supi, "msisdn": msisdn} - for key, SnssaiData := range subsData.SmPolicyData.SmPolicySnssaiData { - tmpSmPolicyDnnData := make(map[string]models.SmPolicyDnnData) - for dnnKey, dnn := range SnssaiData.SmPolicyDnnData { - escapedDnn := EscapeDnn(dnnKey) - tmpSmPolicyDnnData[escapedDnn] = dnn + if method == "put" || method == "post" { + if msisdn != "" { + if _, err := mongoapi.RestfulAPIPutOne(msisdnSupiMapColl, filter, data); err != nil { + logger.ProcLog.Errorf("PutMsisdnSupiMap err: %+v", err) + } + } else { + // delete + if err := mongoapi.RestfulAPIDeleteOne(msisdnSupiMapColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteMsisdnSupiMap err: %+v", err) + } } - SnssaiData.SmPolicyDnnData = tmpSmPolicyDnnData - subsData.SmPolicyData.SmPolicySnssaiData[key] = SnssaiData } +} - smfSelSubsBsonM := toBsonM(subsData.SmfSelectionSubscriptionData) - smfSelSubsBsonM["ueId"] = ueId - smfSelSubsBsonM["servingPlmnId"] = servingPlmnId - amPolicyDataBsonM := toBsonM(subsData.AmPolicyData) - amPolicyDataBsonM["ueId"] = ueId - smPolicyDataBsonM := toBsonM(subsData.SmPolicyData) - smPolicyDataBsonM["ueId"] = ueId +func dbOperation(ueId string, servingPlmnId string, method string, subsData *SubsData, claims jwt.MapClaims) { + filterUeIdOnly := bson.M{"ueId": ueId} + filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} - flowRulesBsonA := make([]interface{}, 0, len(subsData.FlowRules)) - for _, flowRule := range subsData.FlowRules { - flowRuleBsonM := toBsonM(flowRule) - flowRuleBsonM["ueId"] = ueId - flowRuleBsonM["servingPlmnId"] = servingPlmnId - flowRulesBsonA = append(flowRulesBsonA, flowRuleBsonM) + // Replace all data with new one + if method == "put" { + if err := mongoapi.RestfulAPIDeleteMany(flowRuleDataColl, filter); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteMany(qosFlowDataColl, filter); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + } else if method == "delete" { + if err := mongoapi.RestfulAPIDeleteOne(authSubsDataColl, filterUeIdOnly); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteOne(amDataColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteMany(smDataColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteMany(flowRuleDataColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteOne(smfSelDataColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteOne(amPolicyDataColl, filterUeIdOnly); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteOne(smPolicyDataColl, filterUeIdOnly); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteMany(qosFlowDataColl, filter); err != nil { + logger.ProcLog.Errorf("DeleteSubscriberByID err: %+v", err) + } + if err := mongoapi.RestfulAPIDeleteOne(msisdnSupiMapColl, filterUeIdOnly); err != nil { + logger.ProcLog.Errorf("DeleteMsisdnSupiMap err: %+v", err) + } } + if method == "post" || method == "put" { + authSubsBsonM := toBsonM(subsData.AuthenticationSubscription) + authSubsBsonM["ueId"] = ueId + if claims != nil { + authSubsBsonM["tenantId"] = claims["tenantId"].(string) + } + amDataBsonM := toBsonM(subsData.AccessAndMobilitySubscriptionData) + amDataBsonM["ueId"] = ueId + amDataBsonM["servingPlmnId"] = servingPlmnId + if claims != nil { + amDataBsonM["tenantId"] = claims["tenantId"].(string) + } - if _, err := mongoapi.RestfulAPIPost(authSubsDataColl, filterUeIdOnly, authSubsBsonM); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPost(amDataColl, filter, amDataBsonM); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIPostMany(smDataColl, filter, smDatasBsonA); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPost(smfSelDataColl, filter, smfSelSubsBsonM); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPost(amPolicyDataColl, filterUeIdOnly, amPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPost(smPolicyDataColl, filterUeIdOnly, smPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIPostMany(flowRuleDataColl, filter, flowRulesBsonA); err != nil { - logger.WebUILog.Errorf("PostSubscriberByID err: %+v", err) - } + // Replace all data with new one + if err := mongoapi.RestfulAPIDeleteMany(smDataColl, filter); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + for _, data := range subsData.SessionManagementSubscriptionData { + smDataBsonM := toBsonM(data) + smDataBsonM["ueId"] = ueId + smDataBsonM["servingPlmnId"] = servingPlmnId + filterSmData := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId, "snssai": data.SingleNssai} + if _, err := mongoapi.RestfulAPIPutOne(smDataColl, filterSmData, smDataBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + } - c.JSON(http.StatusCreated, gin.H{}) + for key, SnssaiData := range subsData.SmPolicyData.SmPolicySnssaiData { + tmpSmPolicyDnnData := make(map[string]models.SmPolicyDnnData) + for dnnKey, dnn := range SnssaiData.SmPolicyDnnData { + escapedDnn := EscapeDnn(dnnKey) + tmpSmPolicyDnnData[escapedDnn] = dnn + } + SnssaiData.SmPolicyDnnData = tmpSmPolicyDnnData + subsData.SmPolicyData.SmPolicySnssaiData[key] = SnssaiData + } + + smfSelSubsBsonM := toBsonM(subsData.SmfSelectionSubscriptionData) + smfSelSubsBsonM["ueId"] = ueId + smfSelSubsBsonM["servingPlmnId"] = servingPlmnId + amPolicyDataBsonM := toBsonM(subsData.AmPolicyData) + amPolicyDataBsonM["ueId"] = ueId + smPolicyDataBsonM := toBsonM(subsData.SmPolicyData) + smPolicyDataBsonM["ueId"] = ueId + + if len(subsData.FlowRules) == 0 { + logger.ProcLog.Infoln("No Flow Rule") + } else { + flowRulesBsonA := make([]interface{}, 0, len(subsData.FlowRules)) + for _, flowRule := range subsData.FlowRules { + flowRuleBsonM := toBsonM(flowRule) + flowRuleBsonM["ueId"] = ueId + flowRuleBsonM["servingPlmnId"] = servingPlmnId + flowRulesBsonA = append(flowRulesBsonA, flowRuleBsonM) + } + if err := mongoapi.RestfulAPIPostMany(flowRuleDataColl, filter, flowRulesBsonA); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + } + + if len(subsData.QosFlows) == 0 { + logger.ProcLog.Infoln("No QoS Flow") + } else { + qosFlowBsonA := make([]interface{}, 0, len(subsData.QosFlows)) + for _, qosFlow := range subsData.QosFlows { + qosFlowBsonM := toBsonM(qosFlow) + qosFlowBsonM["ueId"] = ueId + qosFlowBsonM["servingPlmnId"] = servingPlmnId + qosFlowBsonA = append(qosFlowBsonA, qosFlowBsonM) + } + if err := mongoapi.RestfulAPIPostMany(qosFlowDataColl, filter, qosFlowBsonA); err != nil { + logger.ProcLog.Errorf("PostSubscriberByID err: %+v", err) + } + } + + if _, err := mongoapi.RestfulAPIPutOne(authSubsDataColl, filterUeIdOnly, authSubsBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + if _, err := mongoapi.RestfulAPIPutOne(amDataColl, filter, amDataBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + if _, err := mongoapi.RestfulAPIPutOne(smfSelDataColl, filter, smfSelSubsBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + if _, err := mongoapi.RestfulAPIPutOne(amPolicyDataColl, filterUeIdOnly, amPolicyDataBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + if _, err := mongoapi.RestfulAPIPutOne(smPolicyDataColl, filterUeIdOnly, smPolicyDataBsonM); err != nil { + logger.ProcLog.Errorf("PutSubscriberByID err: %+v", err) + } + } } // Put subscriber by IMSI(ueId) and PlmnID(servingPlmnId) func PutSubscriberByID(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Put One Subscriber Data") - + logger.ProcLog.Infoln("Put One Subscriber Data") var subsData SubsData if err := c.ShouldBindJSON(&subsData); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %v", err) + logger.ProcLog.Errorf("PutSubscriberByID err: %v", err) c.JSON(http.StatusBadRequest, gin.H{ "cause": "JSON format incorrect", }) return } - ueId := c.Param("ueId") servingPlmnId := c.Param("servingPlmnId") - - filterUeIdOnly := bson.M{"ueId": ueId} - filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} - - authSubsBsonM := toBsonM(subsData.AuthenticationSubscription) - authSubsBsonM["ueId"] = ueId - amDataBsonM := toBsonM(subsData.AccessAndMobilitySubscriptionData) - amDataBsonM["ueId"] = ueId - amDataBsonM["servingPlmnId"] = servingPlmnId - - // Replace all data with new one - if err := mongoapi.RestfulAPIDeleteMany(smDataColl, filter); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - for _, data := range subsData.SessionManagementSubscriptionData { - smDataBsonM := toBsonM(data) - smDataBsonM["ueId"] = ueId - smDataBsonM["servingPlmnId"] = servingPlmnId - filterSmData := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId, "snssai": data.SingleNssai} - if _, err := mongoapi.RestfulAPIPutOne(smDataColl, filterSmData, smDataBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - } - - for key, SnssaiData := range subsData.SmPolicyData.SmPolicySnssaiData { - tmpSmPolicyDnnData := make(map[string]models.SmPolicyDnnData) - for dnnKey, dnn := range SnssaiData.SmPolicyDnnData { - escapedDnn := EscapeDnn(dnnKey) - tmpSmPolicyDnnData[escapedDnn] = dnn - } - SnssaiData.SmPolicyDnnData = tmpSmPolicyDnnData - subsData.SmPolicyData.SmPolicySnssaiData[key] = SnssaiData - } - - smfSelSubsBsonM := toBsonM(subsData.SmfSelectionSubscriptionData) - smfSelSubsBsonM["ueId"] = ueId - smfSelSubsBsonM["servingPlmnId"] = servingPlmnId - amPolicyDataBsonM := toBsonM(subsData.AmPolicyData) - amPolicyDataBsonM["ueId"] = ueId - smPolicyDataBsonM := toBsonM(subsData.SmPolicyData) - smPolicyDataBsonM["ueId"] = ueId - - flowRulesBsonA := make([]interface{}, 0, len(subsData.FlowRules)) - for _, flowRule := range subsData.FlowRules { - flowRuleBsonM := toBsonM(flowRule) - flowRuleBsonM["ueId"] = ueId - flowRuleBsonM["servingPlmnId"] = servingPlmnId - flowRulesBsonA = append(flowRulesBsonA, flowRuleBsonM) - } - // Replace all data with new one - if err := mongoapi.RestfulAPIDeleteMany(flowRuleDataColl, filter); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIPostMany(flowRuleDataColl, filter, flowRulesBsonA); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) + // modify a msisdn-supi map + msisdn := getMsisdn(toBsonM(subsData.AccessAndMobilitySubscriptionData)["gpsis"]) + if !validate(ueId, msisdn) { + logger.ProcLog.Errorf("duplicate msisdn: %v", msisdn) + c.JSON(http.StatusBadRequest, gin.H{ + "cause": "duplicate msisdn", + }) + return } - if _, err := mongoapi.RestfulAPIPutOne(authSubsDataColl, filterUeIdOnly, authSubsBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPutOne(amDataColl, filter, amDataBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPutOne(smfSelDataColl, filter, smfSelSubsBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPutOne(amPolicyDataColl, filterUeIdOnly, amPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } - if _, err := mongoapi.RestfulAPIPutOne(smPolicyDataColl, filterUeIdOnly, smPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PutSubscriberByID err: %+v", err) - } + logger.ProcLog.Infof("PutSubscriberByID msisdn: %+v", msisdn) + msisdnSupiMapOperation(ueId, msisdn, "put") + var claims jwt.MapClaims = nil + dbOperation(ueId, servingPlmnId, "put", &subsData, claims) c.JSON(http.StatusNoContent, gin.H{}) } // Patch subscriber by IMSI(ueId) and PlmnID(servingPlmnId) func PatchSubscriberByID(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Patch One Subscriber Data") + logger.ProcLog.Infoln("Patch One Subscriber Data") var subsData SubsData if err := c.ShouldBindJSON(&subsData); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %v", err) c.JSON(http.StatusBadRequest, gin.H{ "cause": "JSON format incorrect", }) @@ -1148,8 +1468,16 @@ func PatchSubscriberByID(c *gin.Context) { } ueId := c.Param("ueId") + ueId = msisdnToSupi(ueId) servingPlmnId := c.Param("servingPlmnId") - + // checking whether msisdn is successfully transformed to supi or not + if ueId == "" { + logger.ProcLog.Errorf("PatchSubscriberByID err: msisdn does not exists") + c.JSON(http.StatusNotFound, gin.H{ + "cause": "msisdn does not exists", + }) + return + } filterUeIdOnly := bson.M{"ueId": ueId} filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} @@ -1161,7 +1489,9 @@ func PatchSubscriberByID(c *gin.Context) { // Replace all data with new one if err := mongoapi.RestfulAPIDeleteMany(smDataColl, filter); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } for _, data := range subsData.SessionManagementSubscriptionData { smDataBsonM := toBsonM(data) @@ -1169,18 +1499,10 @@ func PatchSubscriberByID(c *gin.Context) { smDataBsonM["servingPlmnId"] = servingPlmnId filterSmData := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId, "snssai": data.SingleNssai} if err := mongoapi.RestfulAPIMergePatch(smDataColl, filterSmData, smDataBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) - } - } - - for key, SnssaiData := range subsData.SmPolicyData.SmPolicySnssaiData { - tmpSmPolicyDnnData := make(map[string]models.SmPolicyDnnData) - for dnnKey, dnn := range SnssaiData.SmPolicyDnnData { - escapedDnn := EscapeDnn(dnnKey) - tmpSmPolicyDnnData[escapedDnn] = dnn + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } - SnssaiData.SmPolicyDnnData = tmpSmPolicyDnnData - subsData.SmPolicyData.SmPolicySnssaiData[key] = SnssaiData } smfSelSubsBsonM := toBsonM(subsData.SmfSelectionSubscriptionData) @@ -1192,19 +1514,29 @@ func PatchSubscriberByID(c *gin.Context) { smPolicyDataBsonM["ueId"] = ueId if err := mongoapi.RestfulAPIMergePatch(authSubsDataColl, filterUeIdOnly, authSubsBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIMergePatch(amDataColl, filter, amDataBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIMergePatch(smfSelDataColl, filter, smfSelSubsBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIMergePatch(amPolicyDataColl, filterUeIdOnly, amPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } if err := mongoapi.RestfulAPIMergePatch(smPolicyDataColl, filterUeIdOnly, smPolicyDataBsonM); err != nil { - logger.WebUILog.Errorf("PatchSubscriberByID err: %+v", err) + logger.ProcLog.Errorf("PatchSubscriberByID err: %+v", err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return } c.JSON(http.StatusNoContent, gin.H{}) @@ -1213,45 +1545,29 @@ func PatchSubscriberByID(c *gin.Context) { // Delete subscriber by IMSI(ueId) and PlmnID(servingPlmnId) func DeleteSubscriberByID(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Delete One Subscriber Data") - + logger.ProcLog.Infoln("Delete One Subscriber Data") ueId := c.Param("ueId") + ueId = msisdnToSupi(ueId) servingPlmnId := c.Param("servingPlmnId") - - filterUeIdOnly := bson.M{"ueId": ueId} - filter := bson.M{"ueId": ueId, "servingPlmnId": servingPlmnId} - - if err := mongoapi.RestfulAPIDeleteOne(authSubsDataColl, filterUeIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteOne(amDataColl, filter); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteMany(smDataColl, filter); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteMany(flowRuleDataColl, filter); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteOne(smfSelDataColl, filter); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteOne(amPolicyDataColl, filterUeIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) - } - if err := mongoapi.RestfulAPIDeleteOne(smPolicyDataColl, filterUeIdOnly); err != nil { - logger.WebUILog.Errorf("DeleteSubscriberByID err: %+v", err) + // checking whether msisdn is successfully transformed to supi or not + if ueId == "" { + logger.ProcLog.Errorf("DeleteSubscriberByID err: msisdn does not exists") + c.JSON(http.StatusNotFound, gin.H{ + "cause": "msisdn does not exists", + }) + return } - + var claims jwt.MapClaims = nil + dbOperation(ueId, servingPlmnId, "delete", nil, claims) c.JSON(http.StatusNoContent, gin.H{}) } func GetRegisteredUEContext(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Get Registered UE Context") + logger.ProcLog.Infoln("Get Registered UE Context") - webuiSelf := webui_context.WEBUI_Self() + webuiSelf := webui_context.GetSelf() webuiSelf.UpdateNfProfiles() supi, supiExists := c.Params.Get("supi") @@ -1266,17 +1582,28 @@ func GetRegisteredUEContext(c *gin.Context) { requestUri = fmt.Sprintf("%s/namf-oam/v1/registered-ue-context", amfUris[0]) } - resp, err := httpsClient.Get(requestUri) + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, requestUri, nil) + if err != nil { + logger.ProcLog.Error(err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + resp, err := httpsClient.Do(req) if err != nil { - logger.WebUILog.Error(err) + logger.ProcLog.Error(err) c.JSON(http.StatusInternalServerError, gin.H{}) return } + defer func() { + if closeErr := resp.Body.Close(); closeErr != nil { + logger.ProcLog.Error(closeErr) + } + }() // Filter by tenant. tenantId, err := GetTenantId(c) if err != nil { - logger.WebUILog.Errorln(err.Error()) + logger.ProcLog.Errorln(err.Error()) c.JSON(http.StatusBadRequest, gin.H{ "cause": "Illegal Token", }) @@ -1298,9 +1625,9 @@ func GetRegisteredUEContext(c *gin.Context) { func GetUEPDUSessionInfo(c *gin.Context) { setCorsHeader(c) - logger.WebUILog.Infoln("Get UE PDU Session Info") + logger.ProcLog.Infoln("Get UE PDU Session Info") - webuiSelf := webui_context.WEBUI_Self() + webuiSelf := webui_context.GetSelf() webuiSelf.UpdateNfProfiles() smContextRef, smContextRefExists := c.Params.Get("smContextRef") @@ -1312,12 +1639,23 @@ func GetUEPDUSessionInfo(c *gin.Context) { // TODO: support fetching data from multiple SMF if smfUris := webuiSelf.GetOamUris(models.NfType_SMF); smfUris != nil { requestUri := fmt.Sprintf("%s/nsmf-oam/v1/ue-pdu-session-info/%s", smfUris[0], smContextRef) - resp, err := httpsClient.Get(requestUri) + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, requestUri, nil) if err != nil { - logger.WebUILog.Error(err) + logger.ProcLog.Error(err) c.JSON(http.StatusInternalServerError, gin.H{}) return } + resp, err := httpsClient.Do(req) + if err != nil { + logger.ProcLog.Error(err) + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + defer func() { + if closeErr := resp.Body.Close(); closeErr != nil { + logger.ProcLog.Error(closeErr) + } + }() sendResponseToClient(c, resp) } else { diff --git a/backend/WebUI/model_flow_rule.go b/backend/WebUI/model_flow_rule.go index e16bd44e4f6a75376db752934b4c8778b28300bf..e6fa7d6c2a77051a1661a19a8e658f0615323670 100644 --- a/backend/WebUI/model_flow_rule.go +++ b/backend/WebUI/model_flow_rule.go @@ -1,12 +1,9 @@ package WebUI type FlowRule struct { - Filter string `json:"filter,omitempty" yaml:"filter" bson:"filter" mapstructure:"filter"` - Snssai string `json:"snssai,omitempty" yaml:"snssai" bson:"snssai" mapstructure:"snssai"` - Dnn string `json:"dnn,omitempty" yaml:"v" bson:"dnn" mapstructure:"dnn"` - Var5QI int `json:"5qi,omitempty" yaml:"5qi" bson:"5qi" mapstructure:"5qi"` - MBRUL string `json:"mbrUL,omitempty" yaml:"mbrUL" bson:"mbrUL" mapstructure:"mbrUL"` - MBRDL string `json:"mbrDL,omitempty" yaml:"mbrDL" bson:"mbrDL" mapstructure:"mbrDL"` - GBRUL string `json:"gbrUL,omitempty" yaml:"gbrUL" bson:"gbrUL" mapstructure:"gbrUL"` - GBRDL string `json:"gbrDL,omitempty" yaml:"gbrDL" bson:"gbrDL" mapstructure:"gbrDL"` + Filter string `json:"filter,omitempty" yaml:"filter" bson:"filter" mapstructure:"filter"` + Precedence int `json:"precedence,omitempty" yaml:"precedence" bson:"precedence" mapstructure:"precedence"` + Snssai string `json:"snssai,omitempty" yaml:"snssai" bson:"snssai" mapstructure:"snssai"` + Dnn string `json:"dnn,omitempty" yaml:"dnn" bson:"dnn" mapstructure:"dnn"` + QFI int `json:"qfi,omitempty" yaml:"qfi" bson:"qfi" mapstructure:"qfi"` } diff --git a/backend/WebUI/model_qos_flow.go b/backend/WebUI/model_qos_flow.go new file mode 100644 index 0000000000000000000000000000000000000000..648cc41709608879886e5fdb693e5230b2cfa712 --- /dev/null +++ b/backend/WebUI/model_qos_flow.go @@ -0,0 +1,12 @@ +package WebUI + +type QosFlow struct { + Snssai string `json:"snssai" yaml:"snssai" bson:"snssai" mapstructure:"snssai"` + Dnn string `json:"dnn" yaml:"dnn" bson:"dnn" mapstructure:"dnn"` + QFI uint8 `json:"qfi" yaml:"qfi" bson:"qfi" mapstructure:"qfi"` + Var5QI int `json:"5qi" yaml:"5qi" bson:"5qi" mapstructure:"5qi"` + MBRUL string `json:"mbrUL,omitempty" yaml:"mbrUL" bson:"mbrUL" mapstructure:"mbrUL"` + MBRDL string `json:"mbrDL,omitempty" yaml:"mbrDL" bson:"mbrDL" mapstructure:"mbrDL"` + GBRUL string `json:"gbrUL,omitempty" yaml:"gbrUL" bson:"gbrUL" mapstructure:"gbrUL"` + GBRDL string `json:"gbrDL,omitempty" yaml:"gbrDL" bson:"gbrDL" mapstructure:"gbrDL"` +} diff --git a/backend/WebUI/model_subs_data.go b/backend/WebUI/model_subs_data.go index 0a41309fbb3625797b1446768b66f2f56a96960f..3efde2ad0a4a0944a28f0b7f00d7beda50aef57b 100644 --- a/backend/WebUI/model_subs_data.go +++ b/backend/WebUI/model_subs_data.go @@ -14,4 +14,5 @@ type SubsData struct { AmPolicyData models.AmPolicyData `json:"AmPolicyData"` SmPolicyData models.SmPolicyData `json:"SmPolicyData"` FlowRules []FlowRule `json:"FlowRules"` + QosFlows []QosFlow `json:"QosFlows"` } diff --git a/backend/WebUI/model_subs_list_ie.go b/backend/WebUI/model_subs_list_ie.go index b4f3000ca252aced57945c8db0c1d2a0f1ff867e..5fd51c3bb15e82ae6ab2f7f4e82a39220e204872 100644 --- a/backend/WebUI/model_subs_list_ie.go +++ b/backend/WebUI/model_subs_list_ie.go @@ -3,4 +3,5 @@ package WebUI type SubsListIE struct { PlmnID string `json:"plmnID"` UeId string `json:"ueId"` + Msisdn string `json:"msisdn"` } diff --git a/backend/WebUI/routers.go b/backend/WebUI/routers.go index 226b0a36d755a30676ec0d95964db2a796438f48..a3ab212c515f80cdbe812dfac337d5e263bdbc6c 100644 --- a/backend/WebUI/routers.go +++ b/backend/WebUI/routers.go @@ -165,6 +165,13 @@ var routes = Routes{ PostSubscriberByID, }, + { + "PostMultiSubscriber", + http.MethodPost, + "/subscriber/:ueId/:servingPlmnId/:userNumber", + PostSubscriberByID, + }, + { "PutSubscriberByID", http.MethodPut, diff --git a/backend/factory/config.go b/backend/factory/config.go index 8ddbe2124dc845e8df3eb2bc71b0a1bc7f193149..5de44c58c72380099cbcb343bd233d535c52f649 100644 --- a/backend/factory/config.go +++ b/backend/factory/config.go @@ -7,32 +7,145 @@ package factory import ( - logger_util "github.com/free5gc/util/logger" + "fmt" + "sync" + + "github.com/asaskevich/govalidator" + + "github.com/free5gc/webconsole/backend/logger" +) + +const ( + WebuiDefaultTLSKeyLogPath = "./log/webuisslkey.log" + WebuiDefaultCertPemPath = "./cert/webui.pem" + WebuiDefaultPrivateKeyPath = "./cert/webui.key" + WebuiDefaultConfigPath = "./config/webuicfg.yaml" ) type Config struct { - Info *Info `yaml:"info"` - Configuration *Configuration `yaml:"configuration"` - Logger *logger_util.Logger `yaml:"logger"` + Info *Info `yaml:"info" valid:"required"` + Configuration *Configuration `yaml:"configuration" valid:"required"` + Logger *Logger `yaml:"logger" valid:"required"` + sync.RWMutex +} + +func (c *Config) Validate() (bool, error) { + result, err := govalidator.ValidateStruct(c) + return result, appendInvalid(err) } type Info struct { - Version string `yaml:"version,omitempty"` - Description string `yaml:"description,omitempty"` + Version string `yaml:"version,omitempty" valid:"required,in(1.0.1)"` + Description string `yaml:"description,omitempty" valid:"type(string)"` } type Configuration struct { - WebServer *WebServer `yaml:"WebServer,omitempty"` - Mongodb *Mongodb `yaml:"mongodb"` + WebServer *WebServer `yaml:"webServer,omitempty" valid:"optional"` + Mongodb *Mongodb `yaml:"mongodb" valid:"required"` +} + +type Logger struct { + Enable bool `yaml:"enable" valid:"type(bool)"` + Level string `yaml:"level" valid:"required,in(trace|debug|info|warn|error|fatal|panic)"` + ReportCaller bool `yaml:"reportCaller" valid:"type(bool)"` } type WebServer struct { - Scheme string `yaml:"scheme"` - IP string `yaml:"ipv4Address"` - PORT string `yaml:"port"` + Scheme string `yaml:"scheme" valid:"required"` + IP string `yaml:"ipv4Address" valid:"required"` + PORT string `yaml:"port" valid:"required"` } type Mongodb struct { - Name string `yaml:"name"` - Url string `yaml:"url"` + Name string `yaml:"name" valid:"required"` + Url string `yaml:"url" valid:"required"` +} + +func appendInvalid(err error) error { + var errs govalidator.Errors + + if err == nil { + return nil + } + + es := err.(govalidator.Errors).Errors() + for _, e := range es { + errs = append(errs, fmt.Errorf("invalid %w", e)) + } + + return error(errs) +} + +func (c *Config) SetLogEnable(enable bool) { + c.Lock() + defer c.Unlock() + + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + c.Logger = &Logger{ + Enable: enable, + Level: "info", + } + } else { + c.Logger.Enable = enable + } +} + +func (c *Config) SetLogLevel(level string) { + c.Lock() + defer c.Unlock() + + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + c.Logger = &Logger{ + Level: level, + } + } else { + c.Logger.Level = level + } +} + +func (c *Config) SetLogReportCaller(reportCaller bool) { + c.Lock() + defer c.Unlock() + + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + c.Logger = &Logger{ + Level: "info", + ReportCaller: reportCaller, + } + } else { + c.Logger.ReportCaller = reportCaller + } +} + +func (c *Config) GetLogEnable() bool { + c.RLock() + defer c.RUnlock() + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + return false + } + return c.Logger.Enable +} + +func (c *Config) GetLogLevel() string { + c.RLock() + defer c.RUnlock() + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + return "info" + } + return c.Logger.Level +} + +func (c *Config) GetLogReportCaller() bool { + c.RLock() + defer c.RUnlock() + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + return false + } + return c.Logger.ReportCaller } diff --git a/backend/factory/factory.go b/backend/factory/factory.go index 205206fe6a1f6e44e6a794bee9918b2dee35a48c..57726b37ae83456f701ca4958e8c6bbcb72a396a 100644 --- a/backend/factory/factory.go +++ b/backend/factory/factory.go @@ -8,22 +8,45 @@ import ( "fmt" "io/ioutil" + "github.com/asaskevich/govalidator" "gopkg.in/yaml.v2" + + "github.com/free5gc/webconsole/backend/logger" ) -var WebUIConfig Config +var WebuiConfig *Config // TODO: Support configuration update from REST api -func InitConfigFactory(f string) error { +func InitConfigFactory(f string, cfg *Config) error { + if f == "" { + // Use default config path + f = WebuiDefaultConfigPath + } if content, err := ioutil.ReadFile(f); err != nil { - return fmt.Errorf("[Configuration] %+v", err) + return fmt.Errorf("[Factory] %+v", err) } else { - WebUIConfig = Config{} - - if yamlErr := yaml.Unmarshal(content, &WebUIConfig); yamlErr != nil { - return fmt.Errorf("[Configuration] %+v", yamlErr) + logger.CfgLog.Infof("Read config from [%s]", f) + if yamlErr := yaml.Unmarshal(content, &cfg); yamlErr != nil { + return fmt.Errorf("[Factory] %+v", yamlErr) } } return nil } + +func ReadConfig(cfgPath string) (*Config, error) { + cfg := &Config{} + if err := InitConfigFactory(cfgPath, cfg); err != nil { + return nil, fmt.Errorf("ReadConfig [%s] Error: %+v", cfgPath, err) + } + if _, err := cfg.Validate(); err != nil { + validErrs := err.(govalidator.Errors).Errors() + for _, validErr := range validErrs { + logger.CfgLog.Errorf("%+v", validErr) + } + logger.CfgLog.Errorf("[-- PLEASE REFER TO SAMPLE CONFIG FILE COMMENTS --]") + return nil, fmt.Errorf("Config validate Error") + } + + return cfg, nil +} diff --git a/backend/logger/logger.go b/backend/logger/logger.go index db51f6d14002face17c0216620436584e18c996c..e7b8a08949b3ed6c4255158eede72a3e87cbeb2d 100644 --- a/backend/logger/logger.go +++ b/backend/logger/logger.go @@ -1,73 +1,33 @@ package logger import ( - "os" - "time" - - formatter "github.com/antonfisher/nested-logrus-formatter" "github.com/sirupsen/logrus" logger_util "github.com/free5gc/util/logger" ) var ( - log *logrus.Logger - AppLog *logrus.Entry - InitLog *logrus.Entry - WebUILog *logrus.Entry - ContextLog *logrus.Entry - GinLog *logrus.Entry + Log *logrus.Logger + NfLog *logrus.Entry + MainLog *logrus.Entry + InitLog *logrus.Entry + ProcLog *logrus.Entry + CtxLog *logrus.Entry + CfgLog *logrus.Entry + GinLog *logrus.Entry ) func init() { - log = logrus.New() - log.SetReportCaller(false) - - log.Formatter = &formatter.Formatter{ - TimestampFormat: time.RFC3339Nano, - TrimMessages: true, - NoFieldsSpace: true, - HideKeys: true, - FieldsOrder: []string{"component", "category"}, + fieldsOrder := []string{ + logger_util.FieldNF, + logger_util.FieldCategory, } - - AppLog = log.WithFields(logrus.Fields{"component": "WebUI", "category": "App"}) - InitLog = log.WithFields(logrus.Fields{"component": "WebUI", "category": "Init"}) - WebUILog = log.WithFields(logrus.Fields{"component": "WebUI", "category": "WebUI"}) - ContextLog = log.WithFields(logrus.Fields{"component": "WebUI", "category": "Context"}) - GinLog = log.WithFields(logrus.Fields{"component": "WebUI", "category": "GIN"}) -} - -func LogFileHook(logNfPath string, log5gcPath string) error { - if fullPath, err := logger_util.CreateFree5gcLogFile(log5gcPath); err == nil { - if fullPath != "" { - free5gcLogHook, hookErr := logger_util.NewFileHook(fullPath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0o666) - if hookErr != nil { - return hookErr - } - log.Hooks.Add(free5gcLogHook) - } - } else { - return err - } - - if fullPath, err := logger_util.CreateNfLogFile(logNfPath, "webconsole.log"); err == nil { - selfLogHook, hookErr := logger_util.NewFileHook(fullPath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0o666) - if hookErr != nil { - return hookErr - } - log.Hooks.Add(selfLogHook) - } else { - return err - } - - return nil -} - -func SetLogLevel(level logrus.Level) { - log.SetLevel(level) -} - -func SetReportCaller(enable bool) { - log.SetReportCaller(enable) + Log = logger_util.New(fieldsOrder) + NfLog = Log.WithField(logger_util.FieldNF, "WEBUI") + MainLog = NfLog.WithField(logger_util.FieldCategory, "Main") + InitLog = NfLog.WithField(logger_util.FieldCategory, "Init") + ProcLog = NfLog.WithField(logger_util.FieldCategory, "Proc") + CtxLog = NfLog.WithField(logger_util.FieldCategory, "CTX") + CfgLog = NfLog.WithField(logger_util.FieldCategory, "CFG") + GinLog = NfLog.WithField(logger_util.FieldCategory, "GIN") } diff --git a/backend/webui_context/context.go b/backend/webui_context/context.go index 00036ac7bb97fb2b942c757dc9d62fe13f492f47..37aa6346a6785d0d06797fd3bff9f094620d08f1 100644 --- a/backend/webui_context/context.go +++ b/backend/webui_context/context.go @@ -9,7 +9,7 @@ import ( "github.com/free5gc/webconsole/backend/logger" ) -var webuiContext = WEBUIContext{} +var webuiContext WEBUIContext type WEBUIContext struct { NFProfiles []models.NfProfile @@ -22,18 +22,15 @@ type NfOamInstance struct { Uri string } -func init() { -} - func (context *WEBUIContext) UpdateNfProfiles() { nfProfilesRaw, err := mongoapi.RestfulAPIGetMany("NfProfile", nil) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } var nfProfiles []models.NfProfile if err := timedecode.Decode(nfProfilesRaw, &nfProfiles); err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } @@ -105,7 +102,7 @@ func (context *WEBUIContext) GetOamUris(targetNfType models.NfType) (uris []stri return } -func WEBUI_Self() *WEBUIContext { +func GetSelf() *WEBUIContext { return &webuiContext } diff --git a/backend/webui_service/webui_init.go b/backend/webui_service/webui_init.go index a69527e97f29bb870df94b2b1455f3ec10f1bda8..ce498ed6389e64268c5238f0029e9cc3fe9acc70 100644 --- a/backend/webui_service/webui_init.go +++ b/backend/webui_service/webui_init.go @@ -1,16 +1,11 @@ package webui_service import ( - "bufio" - "fmt" - "os/exec" - "path/filepath" - "runtime/debug" - "sync" + "io/ioutil" + "os" "github.com/gin-contrib/cors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" "github.com/free5gc/util/mongoapi" "github.com/free5gc/webconsole/backend/WebUI" @@ -19,118 +14,70 @@ import ( "github.com/free5gc/webconsole/backend/webui_context" ) -type WEBUI struct{} - -type ( - // Commands information. - Commands struct { - config string - public string - } -) - -var commands Commands - -var cliCmd = []cli.Flag{ - cli.StringFlag{ - Name: "public, p", - Usage: "Load public path from `FOLDER`", - }, - cli.StringFlag{ - Name: "config, c", - Usage: "Load configuration from `FILE`", - }, - cli.StringFlag{ - Name: "log, l", - Usage: "Output NF log to `FILE`", - }, - cli.StringFlag{ - Name: "log5gc, lc", - Usage: "Output free5gc log to `FILE`", - }, +type WebuiApp struct { + cfg *factory.Config + webuiCtx *webui_context.WEBUIContext } -var initLog *logrus.Entry +func NewApp(cfg *factory.Config) (*WebuiApp, error) { + webui := &WebuiApp{cfg: cfg} + webui.SetLogEnable(cfg.GetLogEnable()) + webui.SetLogLevel(cfg.GetLogLevel()) + webui.SetReportCaller(cfg.GetLogReportCaller()) -func (*WEBUI) GetCliCmd() (flags []cli.Flag) { - return cliCmd + webui.webuiCtx = webui_context.GetSelf() + return webui, nil } -func (webui *WEBUI) Initialize(c *cli.Context) error { - commands = Commands{ - config: c.String("config"), - public: c.String("public"), +func (a *WebuiApp) SetLogEnable(enable bool) { + logger.MainLog.Infof("Log enable is set to [%v]", enable) + if enable && logger.Log.Out == os.Stderr { + return + } else if !enable && logger.Log.Out == ioutil.Discard { + return } - - initLog = logger.InitLog - - if commands.config != "" { - if err := factory.InitConfigFactory(commands.config); err != nil { - return err - } + a.cfg.SetLogEnable(enable) + if enable { + logger.Log.SetOutput(os.Stderr) } else { - if err := factory.InitConfigFactory("./config/webuicfg.yaml"); err != nil { - return err - } - } - - if commands.public != "" { - PublicPath = filepath.Clean(commands.public) + logger.Log.SetOutput(ioutil.Discard) } - - webui.setLogLevel() - - return nil } -func (webui *WEBUI) setLogLevel() { - if factory.WebUIConfig.Logger == nil { - initLog.Warnln("Webconsole config without log level setting!!!") +func (a *WebuiApp) SetLogLevel(level string) { + lvl, err := logrus.ParseLevel(level) + if err != nil { + logger.MainLog.Warnf("Log level [%s] is invalid", level) return } - - if factory.WebUIConfig.Logger.WEBUI != nil { - if factory.WebUIConfig.Logger.WEBUI.DebugLevel != "" { - if level, err := logrus.ParseLevel(factory.WebUIConfig.Logger.WEBUI.DebugLevel); err != nil { - initLog.Warnf("WebUI Log level [%s] is invalid, set to [info] level", - factory.WebUIConfig.Logger.WEBUI.DebugLevel) - logger.SetLogLevel(logrus.InfoLevel) - } else { - initLog.Infof("WebUI Log level is set to [%s] level", level) - logger.SetLogLevel(level) - } - } else { - initLog.Warnln("WebUI Log level not set. Default set to [info] level") - logger.SetLogLevel(logrus.InfoLevel) - } - logger.SetReportCaller(factory.WebUIConfig.Logger.WEBUI.ReportCaller) + logger.MainLog.Infof("Log level is set to [%s]", level) + if lvl == logger.Log.GetLevel() { + return } + a.cfg.SetLogLevel(level) + logger.Log.SetLevel(lvl) } -func (webui *WEBUI) FilterCli(c *cli.Context) (args []string) { - for _, flag := range webui.GetCliCmd() { - name := flag.GetName() - value := fmt.Sprint(c.Generic(name)) - if value == "" { - continue - } - - args = append(args, "--"+name, value) +func (a *WebuiApp) SetReportCaller(reportCaller bool) { + logger.MainLog.Infof("Report Caller is set to [%v]", reportCaller) + if reportCaller == logger.Log.ReportCaller { + return } - return args + a.cfg.SetLogReportCaller(reportCaller) + logger.Log.SetReportCaller(reportCaller) } -func (webui *WEBUI) Start() { +func (a *WebuiApp) Start(tlsKeyLogPath string) { // get config file info from WebUIConfig - mongodb := factory.WebUIConfig.Configuration.Mongodb + mongodb := factory.WebuiConfig.Configuration.Mongodb // Connect to MongoDB if err := mongoapi.SetMongoDB(mongodb.Name, mongodb.Url); err != nil { - initLog.Errorf("Server start err: %+v", err) + logger.InitLog.Errorf("Server start err: %+v", err) return } - initLog.Infoln("Server started") + logger.InitLog.Infoln("Server started") router := WebUI.NewRouter() @@ -146,79 +93,10 @@ func (webui *WEBUI) Start() { MaxAge: 86400, })) - self := webui_context.WEBUI_Self() + self := webui_context.GetSelf() self.UpdateNfProfiles() router.NoRoute(ReturnPublic()) - initLog.Infoln(router.Run(":5000")) -} - -func (webui *WEBUI) Exec(c *cli.Context) error { - initLog.Traceln("args:", c.String("webuicfg")) - args := webui.FilterCli(c) - initLog.Traceln("filter: ", args) - command := exec.Command("./webui", args...) - - if err := webui.Initialize(c); err != nil { - return err - } - - stdout, err := command.StdoutPipe() - if err != nil { - initLog.Fatalln(err) - } - wg := sync.WaitGroup{} - wg.Add(3) - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - in := bufio.NewScanner(stdout) - for in.Scan() { - fmt.Println(in.Text()) - } - wg.Done() - }() - - stderr, err := command.StderrPipe() - if err != nil { - initLog.Fatalln(err) - } - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - in := bufio.NewScanner(stderr) - for in.Scan() { - fmt.Println(in.Text()) - } - wg.Done() - }() - - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - if errCmd := command.Start(); errCmd != nil { - fmt.Println("command.Start Fails!") - } - wg.Done() - }() - - wg.Wait() - - return err + logger.InitLog.Infoln(router.Run(":5000")) } diff --git a/config/webuicfg.yaml b/config/webuicfg.yaml index f837ab769f3a6330a93db10cbfb308eddb411efa..ed7aff3453dd8dcbf04cca071e3569062208d1cf 100644 --- a/config/webuicfg.yaml +++ b/config/webuicfg.yaml @@ -1,5 +1,5 @@ info: - version: 1.0.0 + version: 1.0.1 description: WebUI initial local configuration configuration: @@ -7,10 +7,7 @@ configuration: name: free5gc # name of the mongodb url: mongodb://localhost:27017 # a valid URL of the mongodb -# the kind of log output -# debugLevel: how detailed to output, value: trace, debug, info, warn, error, fatal, panic -# ReportCaller: enable the caller report or not, value: true or false -logger: - WEBUI: - debugLevel: info - ReportCaller: false +logger: # log output setting + enable: true # true or false + level: info # how detailed to output, value: trace, debug, info, warn, error, fatal, panic + reportCaller: false # enable the caller report or not, value: true or false diff --git a/frontend/package.json b/frontend/package.json index 451b419e05ddd118cf3c24c59df1c6f661ad5dcf..37f556ae00501f4c445a00d9770bc6c90b0475a1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,6 +4,7 @@ "private": true, "homepage": "", "dependencies": { + "@fortawesome/fontawesome-free": "^5.15.2", "autoprefixer": "7.1.2", "axios": "latest", "babel-core": "6.26.3", @@ -12,6 +13,7 @@ "babel-loader": "7.1.1", "babel-preset-react-app": "^3.0.2", "babel-runtime": "6.26.0", + "bootstrap": "~3.3.6", "case-sensitive-paths-webpack-plugin": "2.4.0", "chalk": "1.1.3", "classnames": "^2.2.5", @@ -60,7 +62,7 @@ "whatwg-fetch": "2.0.3" }, "devDependencies": { - "sass": "^1.50.0", + "sass": "1.51.0", "sass-loader": "^7.0.1", "webpack-dev-server": "2.9.7", "ws": "3.3.2" diff --git a/frontend/public/index.html b/frontend/public/index.html index d038b4d1de3816ac14c7e7f8ad59e0b284135ebd..3182307da6088dd57d268050b61c2a99cab3a874 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -4,10 +4,6 @@ <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="theme-color" content="#000000"> - <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> - <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous"> - <link href="https://fonts.googleapis.com/css?family=Open+Sans:100,400,600,700" rel="stylesheet"> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.0/gh-fork-ribbon.min.css" /> <!-- manifest.json provides metadata used when your web app is added to the @@ -37,7 +33,6 @@ <div style="margin: 14px; font-size: 18px; float: left;"> Loading... </div> - - <script src="https://unpkg.com/react-jsonschema-form/dist/react-jsonschema-form.js"></script> + </body> </html> diff --git a/frontend/src/components/FormInputs/Radio.js b/frontend/src/components/FormInputs/Radio.js index c7b7b70589b0f8c3180a8508a4c9393b022bb582..2fa265196bf081cb16cccc4428007bfa1012887a 100644 --- a/frontend/src/components/FormInputs/Radio.js +++ b/frontend/src/components/FormInputs/Radio.js @@ -6,8 +6,8 @@ import checkImage from 'assets/images/radio-2.svg'; class Radio extends Component { - componentWillReceiveProps(props) { - console.log(props); + static getDerivedStateFromProps(nextProps, prevState){ + console.log(nextProps); } render() { diff --git a/frontend/src/index.js b/frontend/src/index.js index fb721aa90f5fa93d65b69bbd1a2ed8aa89924efa..024a09521611c57af32da10f9016076c7cc05f3e 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -1,11 +1,18 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import registerServiceWorker from './registerServiceWorker'; +import {Provider} from 'react-redux'; import {HashRouter} from 'react-router-dom'; -import './assets/styles/base.scss'; + import App from './pages/App'; import configureStore from './config/configureStore'; -import {Provider} from 'react-redux'; +import registerServiceWorker from './registerServiceWorker'; + +// dependency CSS +import 'bootstrap/dist/css/bootstrap.min.css'; +import '@fortawesome/fontawesome-free/css/all.min.css'; + +import './assets/styles/base.scss'; + export const store = configureStore(); const rootElement = document.getElementById('root'); diff --git a/frontend/src/metadata/index.js b/frontend/src/metadata/index.js index 30f50b4ed0d357504be0f710b7983149b19d5610..55cebc99f793db4761648455765af8d22064f238 100644 --- a/frontend/src/metadata/index.js +++ b/frontend/src/metadata/index.js @@ -5,343 +5,356 @@ // userModalSchema let subModalSchema = { - // title: "A registration form", - // "description": "A simple form example.", - type: "object", - required: [ - "userNumber", - "plmnID", - "ueId", - "authenticationMethod", - "K", - "OPOPcSelect", - "OPOPc", - "SQN", - ], - properties: { - userNumber: { - type: "integer", - title: "Subscriber data number (auto-increased with SUPI)", - default: 1, - maximum: 100000, - minimum: 1 - }, - plmnID: { - type: "string", - title: "PLMN ID", - pattern: "^[0-9]{5,6}$", - default: "20893", - }, - ueId: { - type: "string", - title: "SUPI (IMSI)", - pattern: "^[0-9]{10,15}$", - default: "208930000000003", - }, - authenticationMethod: { - type: "string", - title: "Authentication Method", - default: "5G_AKA", - enum: ["5G_AKA", "EAP_AKA_PRIME"], - }, - K: { - type: "string", - title: "K", - pattern: "^[A-Fa-f0-9]{32}$", - default: "8baf473f2f8fd09487cccbd7097c6862", - }, - OPOPcSelect: { - type: "string", - title: "Operator Code Type", - enum: ["OP", "OPc"], - default: "OPc", - }, - OPOPc: { - type: "string", - title: "Operator Code Value", - pattern: "^[A-Fa-f0-9]{32}$", - default: "8e27b6af0e692e750f32667a3b14605d", - }, - SQN: { - type: "string", - title: "SQN", - pattern: "^[A-Fa-f0-9]{1,12}$", - default: "16f3b3f70fc2", - }, - sliceConfigurations: { - type: "array", - title: "S-NSSAI Configuration", - items: { $ref: "#/definitions/SliceConfiguration" }, - default: [ - { - snssai: { - "sst": 1, - "sd": "010203", - "isDefault": true, - }, - dnnConfigurations: [ - { - dnn: "internet", - uplinkAmbr: "200 Mbps", - downlinkAmbr: "100 Mbps", - "5qi": 9, - }, - { - dnn: "internet2", - uplinkAmbr: "200 Mbps", - downlinkAmbr: "100 Mbps", - "5qi": 9, - } - ] + // title: "A registration form", + // "description": "A simple form example.", + type: "object", + required: [ + "userNumber", + "plmnID", + "ueId", + "authenticationMethod", + "K", + "OPOPcSelect", + "OPOPc", + "AMF", + "SQN", + ], + properties: { + userNumber: { + type: "integer", + title: "Subscriber data number (auto-increased with SUPI)", + default: 1, + maximum: 100000, + minimum: 1 + }, + plmnID: { + type: "string", + title: "PLMN ID", + pattern: "^[0-9]{5,6}$", + default: "20893", + }, + ueId: { + type: "string", + title: "SUPI (IMSI)", + pattern: "^[0-9]{10,15}$", + default: "208930000000003", + }, + msisdn: { + type: "string", + title: "MSISDN", + pattern: "^$|^[0-9]{5,15}$", + default: "" + }, + authenticationMethod: { + type: "string", + title: "Authentication Method", + default: "5G_AKA", + enum: ["5G_AKA", "EAP_AKA_PRIME"], + }, + K: { + type: "string", + title: "K", + pattern: "^[A-Fa-f0-9]{32}$", + default: "8baf473f2f8fd09487cccbd7097c6862", + }, + OPOPcSelect: { + type: "string", + title: "Operator Code Type", + enum: ["OP", "OPc"], + default: "OPc", + }, + OPOPc: { + type: "string", + title: "Operator Code Value", + pattern: "^[A-Fa-f0-9]{32}$", + default: "8e27b6af0e692e750f32667a3b14605d", + }, + AMF: { + type: "string", + title: "Authentication Management Field (AMF)", + pattern: "^[A-Fa-f0-9]{4}$", + default: "8000", + }, + SQN: { + type: "string", + title: "SQN", + pattern: "^[A-Fa-f0-9]{1,12}$", + default: "16f3b3f70fc2", + }, + sliceConfigurations: { + type: "array", + title: "S-NSSAI Configuration", + items: { $ref: "#/definitions/SliceConfiguration" }, + default: [ + { + snssai: { + "sst": 1, + "sd": "010203", + "isDefault": true, }, - { - snssai: { - "sst": 1, - "sd": "112233", - "isDefault": true, + dnnConfigurations: [ + { + dnn: "internet", + uplinkAmbr: "200 Mbps", + downlinkAmbr: "100 Mbps", + "5qi": 9, }, - dnnConfigurations: [ - { - dnn: "internet", - uplinkAmbr: "200 Mbps", - downlinkAmbr: "100 Mbps", - "5qi": 9, - }, - { - dnn: "internet2", - uplinkAmbr: "200 Mbps", - downlinkAmbr: "100 Mbps", - "5qi": 9, - } - ] + { + dnn: "internet2", + uplinkAmbr: "200 Mbps", + downlinkAmbr: "100 Mbps", + "5qi": 9, + } + ] + }, + { + snssai: { + "sst": 1, + "sd": "112233", + "isDefault": true, }, - ], - }, + dnnConfigurations: [ + { + dnn: "internet", + uplinkAmbr: "200 Mbps", + downlinkAmbr: "100 Mbps", + "5qi": 9, + }, + { + dnn: "internet2", + uplinkAmbr: "200 Mbps", + downlinkAmbr: "100 Mbps", + "5qi": 9, + } + ] + }, + ], }, - definitions: { - Snssai: { - type: "object", - required: ["sst", "sd"], - properties: { - sst: { - type: "integer", - title: "SST", - minimum: 0, - maximum: 255, - }, - sd: { - type: "string", - title: "SD", - pattern: "^[A-Fa-f0-9]{6}$", - }, - isDefault: { - type: "boolean", - title: "Default S-NSSAI", - default: false, - }, + }, + definitions: { + Snssai: { + type: "object", + required: ["sst", "sd"], + properties: { + sst: { + type: "integer", + title: "SST", + minimum: 0, + maximum: 255, + }, + sd: { + type: "string", + title: "SD", + pattern: "^[A-Fa-f0-9]{6}$", + }, + isDefault: { + type: "boolean", + title: "Default S-NSSAI", + default: false, }, }, - SliceConfiguration: { - type: "object", - properties: { - snssai: { - $ref: "#/definitions/Snssai" - }, - dnnConfigurations: { - type: "array", - title: "DNN Configurations", - items: { $ref: "#/definitions/DnnConfiguration" }, - } + }, + SliceConfiguration: { + type: "object", + properties: { + snssai: { + $ref: "#/definitions/Snssai" + }, + dnnConfigurations: { + type: "array", + title: "DNN Configurations", + items: { $ref: "#/definitions/DnnConfiguration" }, } - }, - DnnConfiguration: { - type: "object", - required: ["dnn", "uplinkAmbr", "downlinkAmbr"], - properties: { - dnn: { - type: "string", - title: "Data Network Name" - }, - uplinkAmbr: { - $ref: "#/definitions/bitRate", - title: "Uplink AMBR", - default: "1000 Kbps" - }, - downlinkAmbr: { - $ref: "#/definitions/bitRate", - title: "Downlink AMBR", - default: "1000 Kbps" - }, - "5qi": { - type: "integer", - minimum: 0, - maximum: 255, - title: "Default 5QI" - }, - flowRules: { - type: "array", - items: { $ref: "#/definitions/FlowInformation" }, - maxItems: 1, - title: "Flow Rules" - }, - upSecurityChk: { - "type": "boolean", - title: "UP Security", - "default": false - }, + } + }, + DnnConfiguration: { + type: "object", + required: ["dnn", "uplinkAmbr", "downlinkAmbr"], + properties: { + dnn: { + type: "string", + title: "Data Network Name" }, - "dependencies": { - upSecurityChk: { - "oneOf": [ - { - "properties": { - upSecurityChk: { - "enum": [false] - } - }, - }, - { - "properties": { - upSecurityChk: { - "enum": [true] - }, - upIntegrity: { - type: "string", - title: "Integrity of UP Security", - enum: ["NOT_NEEDED", "PREFERRED", "REQUIRED"], - }, - upConfidentiality: { - type: "string", - title: "Confidentiality of UP Security", - enum: ["NOT_NEEDED", "PREFERRED", "REQUIRED"], - }, - }, - "required": [ - "upIntegrity", - "upConfidentiality" - ] - } - ] - } + uplinkAmbr: { + $ref: "#/definitions/bitRate", + title: "Uplink AMBR", + default: "1000 Kbps" + }, + downlinkAmbr: { + $ref: "#/definitions/bitRate", + title: "Downlink AMBR", + default: "1000 Kbps" + }, + "5qi": { + type: "integer", + minimum: 0, + maximum: 255, + title: "Default 5QI" + }, + flowRules: { + type: "array", + items: { $ref: "#/definitions/FlowInformation" }, + maxItems: 1, + title: "Flow Rules" + }, + upSecurityChk: { + "type": "boolean", + title: "UP Security", + "default": false }, }, - FlowInformation: { - type: "object", - properties: { - filter: { - $ref: "#/definitions/IPFilter", - title: "IP Filter" - }, - "5qi": { - type: "integer", - minimum: 0, - maximum: 255, - title: "5QI" - }, - gbrUL: { - $ref: "#/definitions/bitRate", - title: "Uplink GBR", - }, - gbrDL: { - $ref: "#/definitions/bitRate", - title: "Downlink GBR", - }, - mbrUL: { - $ref: "#/definitions/bitRate", - title: "Uplink MBR", - }, - mbrDL: { - $ref: "#/definitions/bitRate", - title: "Downlink MBR", - }, + "dependencies": { + upSecurityChk: { + "oneOf": [ + { + "properties": { + upSecurityChk: { + "enum": [false] + } + }, + }, + { + "properties": { + upSecurityChk: { + "enum": [true] + }, + upIntegrity: { + type: "string", + title: "Integrity of UP Security", + enum: ["NOT_NEEDED", "PREFERRED", "REQUIRED"], + }, + upConfidentiality: { + type: "string", + title: "Confidentiality of UP Security", + enum: ["NOT_NEEDED", "PREFERRED", "REQUIRED"], + }, + }, + "required": [ + "upIntegrity", + "upConfidentiality" + ] + } + ] } }, - IPFilter: { - type: "string", - }, - bitRate: { - type: "string", - pattern: "^[0-9]+(\\.[0-9]+)? (bps|Kbps|Mbps|Gbps|Tbps)$" - }, }, - }; + FlowInformation: { + type: "object", + properties: { + filter: { + $ref: "#/definitions/IPFilter", + title: "IP Filter" + }, + "5qi": { + type: "integer", + minimum: 0, + maximum: 255, + title: "5QI" + }, + gbrUL: { + $ref: "#/definitions/bitRate", + title: "Uplink GBR", + }, + gbrDL: { + $ref: "#/definitions/bitRate", + title: "Downlink GBR", + }, + mbrUL: { + $ref: "#/definitions/bitRate", + title: "Uplink MBR", + }, + mbrDL: { + $ref: "#/definitions/bitRate", + title: "Downlink MBR", + }, + } + }, + IPFilter: { + type: "string", + }, + bitRate: { + type: "string", + pattern: "^[0-9]+(\\.[0-9]+)? (bps|Kbps|Mbps|Gbps|Tbps)$" + }, + }, +}; - let subModaluiSchema = { - OPOPcSelect: { - "ui:widget": "select", +let subModaluiSchema = { + OPOPcSelect: { + "ui:widget": "select", + }, + authenticationMethod: { + "ui:widget": "select", + }, + SliceConfiurations: { + "ui:options": { + "orderable": false }, - authenticationMethod: { - "ui:widget": "select", + "isDefault": { + "ui:widget": "radio", }, - SliceConfiurations: { + "dnnConfigurations": { "ui:options": { "orderable": false }, - "isDefault": { - "ui:widget": "radio", - }, - "dnnConfigurations": { + "flowRules": { "ui:options": { "orderable": false }, - "flowRules": { - "ui:options": { - "orderable": false - }, - } } } - }; + } +}; let tenantSchema = { - // title: "A registration form", - // "description": "A simple form example.", - type: "object", - required: [ - "tenantName", - ], - properties: { - tenantId: { - type: "string", - title: "Tenant ID", - pattern: "^[0-9a-zA-Z-]*$", - default: "", - readOnly: true, - }, - tenantName: { - type: "string", - title: "Tenant Name", - default: "", - }, + // title: "A registration form", + // "description": "A simple form example.", + type: "object", + required: [ + "tenantName", + ], + properties: { + tenantId: { + type: "string", + title: "Tenant ID", + pattern: "^[0-9a-zA-Z-]*$", + default: "", + readOnly: true, + }, + tenantName: { + type: "string", + title: "Tenant Name", + default: "", }, - }; + }, +}; let userModalSchema = { - // title: "A registration form", - // "description": "A simple form example.", - type: "object", - required: [ - "email", - ], - properties: { - userId: { - type: "string", - title: "User ID", - pattern: "^[0-9a-zA-Z-]*$", - default: "", - readOnly: true, - }, - email: { - type: "string", - title: "User Email", - default: "", - }, - password: { - type: "string", - title: "Password", - default: "", - }, + // title: "A registration form", + // "description": "A simple form example.", + type: "object", + required: [ + "email", + ], + properties: { + userId: { + type: "string", + title: "User ID", + pattern: "^[0-9a-zA-Z-]*$", + default: "", + readOnly: true, + }, + email: { + type: "string", + title: "User Email", + default: "", + }, + password: { + type: "string", + title: "Password", + default: "", }, - }; + }, +}; -export { subModalSchema, subModaluiSchema, tenantSchema, userModalSchema}; \ No newline at end of file +export { subModalSchema, subModaluiSchema, tenantSchema, userModalSchema }; diff --git a/frontend/src/models/Subscriber.js b/frontend/src/models/Subscriber.js index 863128ad6ba1260637c94118f587edf99c2c877e..648ecd2ca73d33dd3c4c210e7e8754fd83ebbf67 100644 --- a/frontend/src/models/Subscriber.js +++ b/frontend/src/models/Subscriber.js @@ -2,9 +2,11 @@ export default class Subscriber { id = ''; plmn = ''; + msisdn = ''; - constructor(id, plmn) { + constructor(id, plmn, msisdn) { this.id = id; this.plmn = plmn; - } + this.msisdn = msisdn; + }; } diff --git a/frontend/src/pages/Subscribers/SubscriberOverview.js b/frontend/src/pages/Subscribers/SubscriberOverview.js index d21afa6a3dafc5785e2d5e30b01d60bb7aa2c6bd..826e09043d1bcbb809819b3ab8ee74b0c214c318 100644 --- a/frontend/src/pages/Subscribers/SubscriberOverview.js +++ b/frontend/src/pages/Subscribers/SubscriberOverview.js @@ -29,28 +29,44 @@ class SubscriberOverview extends Component { */ async openEditSubscriber(subscriberId, plmn) { const subscriber = await ApiHelper.fetchSubscriberById(subscriberId, plmn); - + let origiData = subscriber; + if (subscriber.FlowRules !== undefined && subscriber.FlowRules !== null) { + subscriber.FlowRules.forEach(FlowRule => { + let i = 0; + subscriber.QosFlows.forEach(QosFlow => { + if (QosFlow.snssai === FlowRule.snssai && + QosFlow.dnn === FlowRule.dnn && + QosFlow["5qi"] === FlowRule.qfi) { + if (origiData.QosFlows[i].flowRules === undefined) { + origiData.QosFlows[i].flowRules = []; + } + origiData.QosFlows[i].flowRules.push(Object.assign({ precedence: FlowRule.precedence, filter: FlowRule.filter })) + } + i++; + }) + }); + } + delete origiData.FlowRules; this.setState({ subscriberModalOpen: true, - subscriberModalData: subscriber, + subscriberModalData: origiData, }); } async addSubscriber(subscriberData) { this.setState({ subscriberModalOpen: false }); let userNumber = subscriberData["userNumber"]; - delete subscriberData["userNumber"]; - let imsiLength = subscriberData["ueId"].length - 5 - let imsi = subscriberData["ueId"].substr(5, imsiLength); - for(let i = 0; i < userNumber; i++){ - let newImsi = (Number(imsi) + i).toString(); - newImsi = newImsi.padStart(imsiLength, '0') - subscriberData["ueId"] = `imsi-${newImsi}`; - if (!await ApiHelper.createSubscriber(subscriberData)) { + + if (!await ApiHelper.createSubscriber(subscriberData)) { + if (userNumber > 1) { + alert("Error creating new multiple subscribers when create user"); + } + else if (userNumber === 1) { alert("Error creating new subscriber when create user"); } - ApiHelper.fetchSubscribers().then(); } + delete subscriberData["userNumber"]; + ApiHelper.fetchSubscribers().then(); } /** @@ -99,7 +115,8 @@ class SubscriberOverview extends Component { <thead> <tr> <th style={{ width: 80 }}>PLMN</th> - <th colSpan={2}>UE ID</th> + <th>UE ID</th> + <th>MSISDN</th> </tr> </thead> <tbody> @@ -107,10 +124,11 @@ class SubscriberOverview extends Component { <tr key={subscriber.id}> <td>{subscriber.plmn}</td> <td>{subscriber.id}</td> + <td>{subscriber.msisdn}</td> <td style={{ textAlign: 'center' }}> <Button variant="danger" onClick={this.deleteSubscriber.bind(this, subscriber)}>Delete</Button> - - <Button variant="info" onClick={this.openEditSubscriber.bind(this, subscriber.id, subscriber.plmn)}>Modify</Button> + + <Button variant="info" onClick={this.openEditSubscriber.bind(this, subscriber.id, subscriber.plmn)}>Modify</Button> </td> </tr> ))} diff --git a/frontend/src/pages/Subscribers/components/SubscriberModal.js b/frontend/src/pages/Subscribers/components/SubscriberModal.js index 1738f4844ba58c247df1b267e022843037c4f9dc..f74e04284a3709abf46fc4faae37c0b1c9965d8b 100644 --- a/frontend/src/pages/Subscribers/components/SubscriberModal.js +++ b/frontend/src/pages/Subscribers/components/SubscriberModal.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import {subModalSchema, subModaluiSchema} from '../../../metadata/index' +import { subModalSchema, subModaluiSchema } from '../../../metadata/index' import { Modal } from "react-bootstrap"; import Form from "react-jsonschema-form"; import PropTypes from 'prop-types'; @@ -7,35 +7,35 @@ import _ from 'lodash'; let snssaiToString = (snssai) => snssai.sst.toString(16).padStart(2, '0').toUpperCase() + snssai.sd -function dnnConfigurationFromSliceConfiguration(dnnConfig){ - if (dnnConfig.upSecurityChk === true) { - return { - "sscModes": { - "defaultSscMode": "SSC_MODE_1", - "allowedSscModes": ["SSC_MODE_2", "SSC_MODE_3"] - }, - "pduSessionTypes": { - "defaultSessionType": "IPV4", - "allowedSessionTypes": ["IPV4"] - }, - "sessionAmbr": { - "uplink": dnnConfig.uplinkAmbr, - "downlink": dnnConfig.downlinkAmbr - }, - "5gQosProfile": { - "5qi": dnnConfig["5qi"], - "arp": { - "priorityLevel": 8 - }, - "priorityLevel": 8 - }, - "upSecurity": { - "upIntegr": dnnConfig.upIntegrity, - "upConfid": dnnConfig.upConfidentiality +function validate(formData, errors) { + let item = formData["sliceConfigurations"] + item.map(i => { + i.dnnConfigurations.map(d => { + var qfiList = new Set([]); + if (d.qosFlows) { + d.qosFlows.map(q => { + if (qfiList.has(q["5qi"])) { + errors.sliceConfigurations.addError(`The QoS Flow (qfi = ${q["5qi"]}) is already exist.`); + } else { + qfiList.add(q["5qi"]); + } + if (q["5qi"] >= 5 && (q.gbrUL || q.gbrDL)) { + errors.sliceConfigurations.addError(`${q["5qi"]} is non-GBR 5QI.\nthus, gbr related parameters won’t be applied.`); + } + return 0; + }) } - } - } - return { + return 0; + }) + return 0; + }) + return errors; +} + + + +function dnnConfigurationFromSliceConfiguration(dnnConfig) { + let obj = { "sscModes": { "defaultSscMode": "SSC_MODE_1", "allowedSscModes": ["SSC_MODE_2", "SSC_MODE_3"] @@ -56,6 +56,22 @@ function dnnConfigurationFromSliceConfiguration(dnnConfig){ "priorityLevel": 8 } } + + if (dnnConfig.upSecurityChk === true) { + obj["upSecurity"] = { + "upIntegr": dnnConfig.upIntegrity, + "upConfid": dnnConfig.upConfidentiality + } + } + + if (dnnConfig.staticIP !== undefined && dnnConfig.staticIP.length !== 0) { + obj["staticIpAddress"] = [ + { + "ipv4Addr": dnnConfig.staticIP + } + ] + } + return obj } function smDatasFromSliceConfiguration(sliceConfiguration) { @@ -75,13 +91,33 @@ function smDatasFromSliceConfiguration(sliceConfiguration) { }) } +function qosFlowsFromSliceConfiguration(sliceConfigurations) { + var qosFlows = []; + sliceConfigurations.forEach(slice => { + slice.dnnConfigurations.forEach(dnn => { + if (dnn.qosFlows !== undefined) { + dnn.qosFlows.forEach(qosFlow => { + qosFlows.push( + Object.assign({ snssai: snssaiToString(slice.snssai), dnn: dnn.dnn, qfi: qosFlow["5qi"] }, + qosFlow)) + }) + } + }) + }) + return qosFlows +} + function flowRulesFromSliceConfiguration(sliceConfigurations) { var flowRules = [] sliceConfigurations.forEach(slice => { slice.dnnConfigurations.forEach(dnn => { - if (dnn.flowRules !== undefined) { - dnn.flowRules.forEach(flowRule => { - flowRules.push(Object.assign({ snssai: snssaiToString(slice.snssai), dnn: dnn.dnn }, flowRule)) + if (dnn.qosFlows !== undefined) { + dnn.qosFlows.forEach(qosFlow => { + if (qosFlow.flowRules !== undefined) { + qosFlow.flowRules.forEach(flowRule => { + flowRules.push(Object.assign({ snssai: snssaiToString(slice.snssai), dnn: dnn.dnn, qfi: qosFlow["5qi"] }, flowRule)) + }) + } }) } }) @@ -119,29 +155,42 @@ function sliceConfigurationsFromSubscriber(subscriber) { sliceConfigurations.forEach(sliceConf => { const dnnConfigs = sessionManagementSubscriptionData.find(data => data.singleNssai.sst === sliceConf.snssai.sst && data.singleNssai.sd === sliceConf.snssai.sd).dnnConfigurations; sliceConf.dnnConfigurations = Object.keys(dnnConfigs).map(dnn => { - let flowRules = []; - const flowRulesData = subscriber["FlowRules"]; - if(flowRulesData && flowRulesData.length !== 0) { - flowRules = flowRulesData - .filter(rule => rule.snssai === snssaiToString(sliceConf.snssai) && dnn === rule.dnn) - .map(rule => { - return { - filter: rule.filter, - "5qi": rule["5qi"], - gbrUL: rule.gbrUL, - gbrDL: rule.gbrDL, - mbrUL: rule.mbrUL, - mbrDL: rule.mbrDL - } - }) + + let qosFlows = []; + const qosFlowsData = subscriber["QosFlows"]; + if (qosFlowsData && qosFlowsData.length !== 0) { + qosFlows = qosFlowsData + .filter(rule => rule.snssai === snssaiToString(sliceConf.snssai) && dnn === rule.dnn) + .map(rule => { + return { + "5qi": rule["5qi"], + gbrUL: rule.gbrUL, + gbrDL: rule.gbrDL, + mbrUL: rule.mbrUL, + mbrDL: rule.mbrDL, + flowRules: rule.flowRules + } + }) } - if (dnnConfigs[dnn].upSecurity){ + + + + + let staticIps = ""; + const staticIpAddress = dnnConfigs[dnn].staticIpAddress + if (staticIpAddress && staticIpAddress.length !== 0) { + staticIps += staticIpAddress.reduce((total, element) => { + return total + element["ipv4Addr"] + }, "") + } + if (dnnConfigs[dnn].upSecurity) { return { dnn: dnn, + staticIP: staticIps, uplinkAmbr: dnnConfigs[dnn].sessionAmbr.uplink, downlinkAmbr: dnnConfigs[dnn].sessionAmbr.downlink, "5qi": dnnConfigs[dnn]["5gQosProfile"]["5qi"], - flowRules: flowRules, + qosFlows: qosFlows, upSecurityChk: true, upIntegrity: dnnConfigs[dnn].upSecurity.upIntegr, upConfidentiality: dnnConfigs[dnn].upSecurity.upConfid @@ -149,10 +198,11 @@ function sliceConfigurationsFromSubscriber(subscriber) { } return { dnn: dnn, + staticIP: staticIps, uplinkAmbr: dnnConfigs[dnn].sessionAmbr.uplink, downlinkAmbr: dnnConfigs[dnn].sessionAmbr.downlink, "5qi": dnnConfigs[dnn]["5gQosProfile"]["5qi"], - flowRules: flowRules + qosFlows: qosFlows }; }); }); @@ -186,10 +236,12 @@ class SubscriberModal extends Component { if (this.props.subscriber) { const subscriber = this.props.subscriber; const isOp = subscriber['AuthenticationSubscription']["milenage"]["op"]["opValue"] !== ""; - + //get the index of msisdn in gpsis + let msisdnId = subscriber['AccessAndMobilitySubscriptionData']["gpsis"].findIndex(gpsi => gpsi.includes("msisdn-")); let formData = { plmnID: subscriber['plmnID'], ueId: subscriber['ueId'].replace("imsi-", ""), + msisdn: msisdnId === -1 ? null : subscriber['AccessAndMobilitySubscriptionData']["gpsis"][msisdnId].replace("msisdn-", ""), authenticationMethod: subscriber['AuthenticationSubscription']["authenticationMethod"], K: subscriber['AuthenticationSubscription']["permanentKey"]["permanentKeyValue"], OPOPcSelect: isOp ? "OP" : "OPc", @@ -199,6 +251,7 @@ class SubscriberModal extends Component { sliceConfigurations: sliceConfigurationsFromSubscriber(subscriber), }; + this.updateFormData(formData).then(); } } @@ -248,7 +301,7 @@ class SubscriberModal extends Component { "plmnID": formData["plmnID"], // Change required "ueId": "imsi-" + formData["ueId"], // Change required "AuthenticationSubscription": { - "authenticationManagementField": "8000", + "authenticationManagementField": formData["AMF"], "authenticationMethod": formData["authenticationMethod"], // "5G_AKA", "EAP_AKA_PRIME" "milenage": { "op": { @@ -271,7 +324,7 @@ class SubscriberModal extends Component { }, "AccessAndMobilitySubscriptionData": { "gpsis": [ - "msisdn-0900000000" + formData["msisdn"] === undefined ? null : "msisdn-" + formData["msisdn"] ], "nssai": { "defaultSingleNssais": _(formData["sliceConfigurations"]) @@ -292,7 +345,7 @@ class SubscriberModal extends Component { _.map(formData["sliceConfigurations"], slice => [snssaiToString(slice.snssai), { "dnnInfos": _.map(slice.dnnConfigurations, dnnCofig => { - return {"dnn": dnnCofig.dnn} + return { "dnn": dnnCofig.dnn } }) }])) }, @@ -319,10 +372,10 @@ class SubscriberModal extends Component { ) }])) }, - "FlowRules": flowRulesFromSliceConfiguration(formData["sliceConfigurations"]) + "FlowRules": flowRulesFromSliceConfiguration(formData["sliceConfigurations"]), + "QosFlows": qosFlowsFromSliceConfiguration(formData["sliceConfigurations"]), }; - - if(this.state.editMode) { + if (this.state.editMode) { this.props.onModify(subscriberData); } else { this.props.onSubmit(subscriberData); @@ -347,6 +400,7 @@ class SubscriberModal extends Component { <Form schema={this.schema} uiSchema={this.uiSchema} formData={this.state.formData} + validate={validate} onChange={this.onChange.bind(this)} onSubmit={this.onSubmitClick.bind(this)} /> } diff --git a/frontend/src/pages/Tasks/TasksOverview.js b/frontend/src/pages/Tasks/TasksOverview.js index ae4ad388cd58b5f25a391d952c821925eefb8126..abc9040f65e6e38d48a96342c9024843bfeae154 100644 --- a/frontend/src/pages/Tasks/TasksOverview.js +++ b/frontend/src/pages/Tasks/TasksOverview.js @@ -13,7 +13,7 @@ class TasksOverview extends Component { this.targetDownloaded = false; } - componentWillMount() { + componentDidMount() { this.refreshEnabled = true; this.updateTasksTable().then(); } @@ -22,8 +22,8 @@ class TasksOverview extends Component { this.refreshEnabled = false; } - componentWillReceiveProps(nextProps) { - let urlParams = queryString.parse(this.props.location.search); + static getDerivedStateFromProps(nextProps, prevState){ + let urlParams = queryString.parse(nextProps.location.search); if (urlParams['target'] !== undefined) { let dashedUuid = AppUtils.dashUuid(urlParams['target']); diff --git a/frontend/src/util/ApiHelper.js b/frontend/src/util/ApiHelper.js index b1a0a4b43f57ef1c7f2f1a3793ff4cd006502582..44b83d22c3f8a0ee0219e143775d897a1f9f1ab7 100644 --- a/frontend/src/util/ApiHelper.js +++ b/frontend/src/util/ApiHelper.js @@ -1,5 +1,5 @@ import Http from './Http'; -import {store} from '../index'; +import { store } from '../index'; import subscriberActions from "../redux/actions/subscriberActions"; import Subscriber from "../models/Subscriber"; import tenantActions from "../redux/actions/tenantActions"; @@ -17,7 +17,7 @@ class ApiHelper { axios.defaults.headers.common['Token'] = user.accessToken; let response = await Http.get('subscriber'); if (response.status === 200 && response.data) { - const subscribers = response.data.map(val => new Subscriber(val['ueId'], val['plmnID'])); + const subscribers = response.data.map(val => new Subscriber(val['ueId'], val['plmnID'], val['msisdn'])); store.dispatch(subscriberActions.setSubscribers(subscribers)); return true; } @@ -44,7 +44,7 @@ class ApiHelper { let user = LocalStorageHelper.getUserInfo(); axios.defaults.headers.common['Token'] = user.accessToken; let response = await Http.post( - `subscriber/${subscriberData["ueId"]}/${subscriberData["plmnID"]}`, subscriberData); + `subscriber/${subscriberData["ueId"]}/${subscriberData["plmnID"]}/${subscriberData["userNumber"]}`, subscriberData); if (response.status === 201) return true; } catch (error) { diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 600a44c95ed9a80b3ffa91fea92634197bfe0494..628941f381026d4760135fcea36cf8bed71687e9 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -130,6 +130,11 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@fortawesome/fontawesome-free@^5.15.2": + version "5.15.2" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.2.tgz#218cd7276ab4f9ab57cc3d2efa2697e6a579f25d" + integrity sha512-7l/AX41m609L/EXI9EKH3Vs3v0iA8tKlIOGtw+kgcoanI7p+e4I4GYLqW3UXWiTnjSFymKSmTTPKYrivzbxxqA== + "@jridgewell/gen-mapping@^0.3.0": version "0.3.1" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9" @@ -1487,6 +1492,11 @@ boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +bootstrap@~3.3.6: + version "3.3.7" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.3.7.tgz#5a389394549f23330875a3b150656574f8a9eb71" + integrity sha1-WjiTlFSfIzMIdaOxUGVldPip63E= + boxen@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" @@ -5784,12 +5794,12 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -"lodash@>=3.5 <5", lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.3, lodash@^4.2.1, lodash@^4.3.0: +"lodash@>=3.5 <5", lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.1, lodash@^4.3.0: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@^4.17.15, lodash@^4.17.4: +lodash@^4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8191,7 +8201,7 @@ sass-loader@^7.0.1: pify "^4.0.1" semver "^6.3.0" -sass@^1.50.0: +sass@1.51.0: version "1.51.0" resolved "https://registry.yarnpkg.com/sass/-/sass-1.51.0.tgz#25ea36cf819581fe1fe8329e8c3a4eaaf70d2845" integrity sha512-haGdpTgywJTvHC2b91GSq+clTKGbtkkZmVAb82jZQN/wTy6qs8DdFm2lhEQbEwrY0QDRgSQ3xDurqM977C3noA== diff --git a/go.mod b/go.mod index 85a5477d66525b7006f441d4f358a39c1cc1d1ed..9f4553cda911510d40bf6830b87276631f9dac3f 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/free5gc/webconsole -go 1.14 +go 1.17 require ( - github.com/antonfisher/nested-logrus-formatter v1.3.1 - github.com/free5gc/openapi v1.0.4 - github.com/free5gc/util v1.0.3 + github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d + github.com/free5gc/openapi v1.0.6 + github.com/free5gc/util v1.0.5-0.20230306071612-a52909216bd2 github.com/gin-contrib/cors v1.3.1 github.com/gin-gonic/gin v1.7.7 github.com/golang-jwt/jwt v3.2.1+incompatible @@ -17,3 +17,38 @@ require ( golang.org/x/crypto v0.1.0 gopkg.in/yaml.v2 v2.4.0 ) + +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect + github.com/evanphx/json-patch v0.5.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.13.0 // indirect + github.com/go-playground/universal-translator v0.17.0 // indirect + github.com/go-playground/validator/v10 v10.4.1 // indirect + github.com/go-stack/stack v1.8.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.1 // indirect + github.com/google/go-cmp v0.5.6 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.13.6 // indirect + github.com/kr/pretty v0.2.0 // indirect + github.com/leodido/go-urn v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mitchellh/mapstructure v1.4.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/tim-ywliu/nested-logrus-formatter v1.3.2 // indirect + github.com/ugorji/go/codec v1.1.7 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.0.2 // indirect + github.com/xdg-go/stringprep v1.0.2 // indirect + github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index b08c8e558633530e2fd0ddc7ccc6cc7e43f2bc41..bd8cb9244a54f305d31535c7ab4c7d1d10e58715 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,6 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ= -github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -44,8 +42,9 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -55,16 +54,15 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/free5gc/openapi v1.0.4 h1:bC6oqXy8Z+3e532xLMFmrTHvdyv4sNGDPezQSslw5gQ= -github.com/free5gc/openapi v1.0.4/go.mod h1:KRCnnp0GeK0Bl4gnrX79cQAidKXNENf8VRdG0y9R0Fc= -github.com/free5gc/util v1.0.3 h1:or/gqHCAi3j2YKd+nzViRnc/tl1tuuJAYxCao6IbOAU= -github.com/free5gc/util v1.0.3/go.mod h1:DL1Dnryh//Ps5B+hfXbhU1R07fVfrmPs4uuTO4g9yTg= +github.com/free5gc/openapi v1.0.6 h1:ytRjU/YZRI8UhKKyfajXSyGB6s1YDFkJ1weeAGJ8LXw= +github.com/free5gc/openapi v1.0.6/go.mod h1:iw/N0E+FlX44EEx24IBi2EdZW8v+bkj3ETWPGnlK9DI= +github.com/free5gc/util v1.0.5-0.20230306071612-a52909216bd2 h1:FG8KlJ46Epscj3F9XBAKuDGJD9kSKJdstCL9fttjUjE= +github.com/free5gc/util v1.0.5-0.20230306071612-a52909216bd2/go.mod h1:fgV0hXf5arxAWs8D9LfrrfNByZ1IDCWYlgBzncy5GtE= github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA= github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= -github.com/gin-gonic/gin v1.7.3/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -107,8 +105,10 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -120,8 +120,10 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -145,8 +147,9 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -154,8 +157,9 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -163,14 +167,18 @@ github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdA github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -179,9 +187,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -198,8 +206,11 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tim-ywliu/nested-logrus-formatter v1.3.2 h1:jugNJ2/CNCI79SxOJCOhwUHeN3O7/7/bj+ZRGOFlCSw= +github.com/tim-ywliu/nested-logrus-formatter v1.3.2/go.mod h1:oGPmcxZB65j9Wo7mCnQKSrKEJtVDqyjD666SGmyStXI= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= @@ -290,9 +301,9 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -308,6 +319,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -338,26 +351,28 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -481,11 +496,15 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= @@ -494,8 +513,9 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/server.go b/server.go index 51ebabd2861776c0e79a5855ca8f8da388a768a3..4289a98e6d14f783b61d8df61b91585669b09888 100644 --- a/server.go +++ b/server.go @@ -1,50 +1,94 @@ package main import ( - "fmt" "os" + "path/filepath" + "runtime/debug" "github.com/urfave/cli" + logger_util "github.com/free5gc/util/logger" "github.com/free5gc/util/version" + "github.com/free5gc/webconsole/backend/factory" "github.com/free5gc/webconsole/backend/logger" "github.com/free5gc/webconsole/backend/webui_service" ) -var WEBUI = &webui_service.WEBUI{} +var WEBUI *webui_service.WebuiApp func main() { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.MainLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + } + }() app := cli.NewApp() app.Name = "webui" app.Usage = "free5GC Web Console" app.Action = action - app.Flags = WEBUI.GetCliCmd() + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "config, c", + Usage: "Load configuration from `FILE`", + }, + cli.StringSliceFlag{ + Name: "log, l", + Usage: "Output NF log to `FILE`", + }, + } if err := app.Run(os.Args); err != nil { - logger.AppLog.Errorf("Web Console Run error: %v", err) + logger.MainLog.Errorf("WEBUI Run error: %v\n", err) } } -func action(c *cli.Context) error { - if err := initLogFile(c.String("log"), c.String("log5gc")); err != nil { - logger.AppLog.Errorf("%+v", err) +func action(cliCtx *cli.Context) error { + tlsKeyLogPath, err := initLogFile(cliCtx.StringSlice("log")) + if err != nil { return err } - if err := WEBUI.Initialize(c); err != nil { - return fmt.Errorf("Failed to initialize !! %+v", err) + logger.MainLog.Infoln("WEBUI version: ", version.GetVersion()) + + cfg, err := factory.ReadConfig(cliCtx.String("config")) + if err != nil { + return err } + factory.WebuiConfig = cfg - logger.AppLog.Infoln(c.App.Name) - logger.AppLog.Infoln("webconsole version: ", version.GetVersion()) + webui, err := webui_service.NewApp(cfg) + if err != nil { + return err + } + WEBUI = webui - WEBUI.Start() + webui.Start(tlsKeyLogPath) return nil } -func initLogFile(logNfPath, log5gcPath string) error { - if err := logger.LogFileHook(logNfPath, log5gcPath); err != nil { - return err +func initLogFile(logNfPath []string) (string, error) { + logTlsKeyPath := "" + + for _, path := range logNfPath { + if err := logger_util.LogFileHook(logger.Log, path); err != nil { + return "", err + } + if logTlsKeyPath != "" { + continue + } + + nfDir, _ := filepath.Split(path) + tmpDir := filepath.Join(nfDir, "key") + if err := os.MkdirAll(tmpDir, 0o775); err != nil { + logger.InitLog.Errorf("Make directory %s failed: %+v", tmpDir, err) + + return "", err + } + + _, name := filepath.Split(factory.WebuiDefaultTLSKeyLogPath) + logTlsKeyPath = filepath.Join(tmpDir, name) } - return nil + + return logTlsKeyPath, nil }