func (tr *TodoResource) UpdateTodo(c *gin.Context) { id, err := tr.getId(c) if err != nil { c.JSON(400, api.NewError("problem decoding id sent")) return } var todo api.Todo if !c.Bind(&todo) { c.JSON(400, api.NewError("problem decoding body")) return } todo.Id = int32(id) var existing api.Todo if tr.db.First(&existing, id).RecordNotFound() { c.JSON(404, api.NewError("not found")) } else { tr.db.Save(&todo) c.JSON(200, todo) } }
func GetLoginToken(c *gin.Context) { remote := remote.FromContext(c) in := &tokenPayload{} err := c.Bind(in) if err != nil { c.AbortWithError(http.StatusBadRequest, err) return } login, err := remote.Auth(in.Access, in.Refresh) if err != nil { c.AbortWithError(http.StatusUnauthorized, err) return } user, err := store.GetUserLogin(c, login) if err != nil { c.AbortWithError(http.StatusNotFound, err) return } exp := time.Now().Add(time.Hour * 72).Unix() token := token.New(token.SessToken, user.Login) tokenstr, err := token.SignExpires(user.Hash, exp) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } c.IndentedJSON(http.StatusOK, &tokenPayload{ Access: tokenstr, Expires: exp - time.Now().Unix(), }) }
func loginHandler(c *gin.Context) { var ( login LoginData ) // bind POST data to struct err := c.Bind(&login) if err != nil { c.JSON(401, "Invalid Credentials Provided") } else { // return 401 if empty if login.Username == "" || login.Password == "" { c.JSON(401, "Invalid Credentials Provided") return } // get user from database and fill our struct dbUserObject, err := validateUser(login.Username, login.Password) if err != nil { // return 401 if incorect user or password c.JSON(401, "Invalid Credentials") return } // generate token token := genToken(dbUserObject) // return token to user c.JSON(200, token) return } // return 400 if any other error is encountered c.JSON(400, "Error encountered") }
func saveCurrentChannelEndPoint(c *gin.Context) { var json = &struct { ID int `json:"ID"` Type string `json:"Type"` }{} if c.Bind(json) != nil { c.JSON(http.StatusBadRequest, gin.H{"ERR": "WRONG_INPUT"}) return } userID64, _ := c.Get("userID") userID := userID64.(int) cc := &CurrentChannel{ Type: json.Type, ID: json.ID, } if peer, ok := peers.get(userID); ok { maxMsgId, _ := maxMsgIDofRoom(json.ID) cc.LastMsgID = maxMsgId peer.ccUpdate(cc) } if _, err := saveCurrentChannel(userID, cc); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "ERR": "INTERNAL_SERVER_ERROR", }) } else { c.JSON(http.StatusOK, gin.H{"SUC": "OK"}) } }
func configure(c *gin.Context) { var form conf.Conf var dat []byte var err error if err = c.Bind(&form); err == nil { errors := form.Validate() if len(errors) > 0 { c.JSON(http.StatusBadRequest, errors) return } if err = form.FillDefaults(); err != nil { fmt.Println("An error occured while filling default values :", err) c.AbortWithError(http.StatusInternalServerError, err) return } if dat, err = yaml.Marshal(&form); err != nil { fmt.Println("An error occured while marshalling the yaml data :", err) c.AbortWithError(http.StatusInternalServerError, err) return } if err = ioutil.WriteFile("conf.yml", dat, 0644); err != nil { fmt.Println("An error occured while writing the conf.yml file :", err) c.AbortWithError(http.StatusInternalServerError, err) return } } else { fmt.Println("An error occured while reading the form data :", err) c.JSON(http.StatusBadRequest, gin.H{}) return } c.JSON(http.StatusCreated, gin.H{}) }
func AddTemplate(c *gin.Context) { project := c.MustGet("project").(models.Project) var template models.Template if err := c.Bind(&template); err != nil { return } res, err := database.Mysql.Exec("insert into project__template set ssh_key_id=?, project_id=?, inventory_id=?, repository_id=?, environment_id=?, playbook=?, arguments=?, override_args=?", template.SshKeyID, project.ID, template.InventoryID, template.RepositoryID, template.EnvironmentID, template.Playbook, template.Arguments, template.OverrideArguments) if err != nil { panic(err) } insertID, err := res.LastInsertId() if err != nil { panic(err) } template.ID = int(insertID) objType := "template" desc := "Template ID " + strconv.Itoa(template.ID) + " created" if err := (models.Event{ ProjectID: &project.ID, ObjectType: &objType, ObjectID: &template.ID, Description: &desc, }.Insert()); err != nil { panic(err) } c.JSON(201, template) }
// POST /api/repos func apiReposCreate(c *gin.Context) { var b struct { Name string `binding:"required"` Comment string DefaultDistribution string DefaultComponent string } if !c.Bind(&b) { return } repo := deb.NewLocalRepo(b.Name, b.Comment) repo.DefaultComponent = b.DefaultComponent repo.DefaultDistribution = b.DefaultDistribution collection := context.CollectionFactory().LocalRepoCollection() collection.Lock() defer collection.Unlock() err := context.CollectionFactory().LocalRepoCollection().Add(repo) if err != nil { c.Fail(400, err) return } c.JSON(201, repo) }
// generate jwt token and return to client func JwtGetToken(c *gin.Context) { var login Login val := c.Bind(&login) if !val { c.JSON(200, gin.H{"code": 401, "msg": "Both name & password are required"}) return } // if login.Name == validUser.Name && login.Pass == validUser.Pass { token := jwt.New(jwt.SigningMethodHS256) // Headers token.Header["alg"] = "HS256" token.Header["typ"] = "JWT" // Claims token.Claims["name"] = validUser.Name token.Claims["mail"] = validUser.Mail token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix() tokenString, err := token.SignedString([]byte(mySigningKey)) fmt.Println("jwt token raw: ", tokenString) if err != nil { c.JSON(200, gin.H{"code": 500, "msg": "Server error!"}) return } c.JSON(200, gin.H{"code": 200, "msg": "OK", "jwt": tokenString}) // } else { // c.JSON(200, gin.H{"code": 400, "msg": "Error username or password!"}) // } }
// Create Training Job func (e EndpointContext) CreateTrainingJob(c *gin.Context) { // bind to json user := c.MustGet(MIDDLEWARE_KEY_USER).(User) db := c.MustGet(MIDDLEWARE_KEY_DB).(couch.Database) trainingJob := NewTrainingJob(e.Configuration) trainingJob.UserID = user.Id // bind the input struct to the JSON request if ok := c.Bind(trainingJob); !ok { errMsg := fmt.Sprintf("Invalid input") c.String(400, errMsg) return } logg.LogTo("REST", "Create new TrainingJob: %+v", trainingJob) // save training job in db trainingJob, err := trainingJob.Insert(db) if err != nil { c.String(500, err.Error()) return } // job will get kicked off by changes listener // return solver object c.JSON(201, *trainingJob) }
//SignInPost handles POST /signin route, authenticates user func SignInPost(c *gin.Context) { session := sessions.Default(c) user := &models.User{} if err := c.Bind(user); err != nil { session.AddFlash("Please, fill out form correctly.") session.Save() c.Redirect(http.StatusFound, "/signin") return } userDB, _ := models.GetUserByEmail(user.Email) if userDB.ID == 0 { logrus.Errorf("Login error, IP: %s, Email: %s", c.ClientIP(), user.Email) session.AddFlash("Email or password incorrect") session.Save() c.Redirect(http.StatusFound, "/signin") return } if err := bcrypt.CompareHashAndPassword([]byte(userDB.Password), []byte(user.Password)); err != nil { logrus.Errorf("Login error, IP: %s, Email: %s", c.ClientIP(), user.Email) session.AddFlash("Email or password incorrect") session.Save() c.Redirect(http.StatusFound, "/signin") return } session.Set("UserID", userDB.ID) session.Save() c.Redirect(http.StatusFound, "/") }
func registerUser(c *gin.Context, d db.DbService) { var u JSON if err := c.Bind(&u); err != nil { RestErrorInvalidBody(c) return } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u["password"].(string)), 10) if err != nil { RestError(c, err) return } doc := JSON{ "_id": u["email"].(string), "password": hashedPassword, "createdAt": time.Now(), } if err := d.Insert(doc); err != nil { RestError(c, err) return } utils.OK(c) }
func AddUser(c *gin.Context) { project := c.MustGet("project").(models.Project) var user struct { UserID int `json:"user_id" binding:"required"` Admin bool `json:"admin"` } if err := c.Bind(&user); err != nil { return } if _, err := database.Mysql.Exec("insert into project__user set user_id=?, project_id=?, admin=?", user.UserID, project.ID, user.Admin); err != nil { panic(err) } objType := "user" desc := "User ID " + strconv.Itoa(user.UserID) + " added to team" if err := (models.Event{ ProjectID: &project.ID, ObjectType: &objType, ObjectID: &user.UserID, Description: &desc, }.Insert()); err != nil { panic(err) } c.AbortWithStatus(204) }
func (tr *TodoResource) Create(c *gin.Context) { // Parse the form data var todo Todo c.Bind(&todo) // Write to the database err := tr.db.Update(func(tx *bolt.Tx) error { b := tx.Bucket(bucket) if todo.Id == 0 { id, _ := b.NextSequence() todo.Id = id todo.Created = int32(time.Now().Unix()) } return b.Put(todo.Key(), todo.Value()) }) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Respond with the results c.JSON(http.StatusCreated, todo) }
func (tr *TodoResource) PatchTodo(c *gin.Context) { id, err := tr.getId(c) if err != nil { c.JSON(400, api.NewError("problem decoding id sent")) return } // this is a hack because Gin falsely claims my unmarshalled obj is invalid. // recovering from the panic and using my object that already has the json body bound to it. var json []api.Patch defer func() { if r := recover(); r != nil { if json[0].Op != "replace" && json[0].Path != "/status" { c.JSON(400, api.NewError("PATCH support is limited and can only replace the /status path")) return } var todo api.Todo if tr.db.First(&todo, id).RecordNotFound() { c.JSON(404, api.NewError("not found")) } else { todo.Status = json[0].Value tr.db.Save(&todo) c.JSON(200, todo) } } }() c.Bind(&json) }
func PostUser(c *gin.Context) { var user User c.Bind(&user) if user.Firstname != "" && user.Lastname != "" { db, _ := sql.Open("postgres", "user=cshutchinson dbname=godb sslmode=disable") // checkErr(err) var lastInsertId int64 _ = db.QueryRow("INSERT INTO users(firstname,lastname, id) VALUES($1,$2, Default) returning id;", user.Firstname, user.Lastname).Scan(&lastInsertId) content := &User{ Id: lastInsertId, Firstname: user.Firstname, Lastname: user.Lastname, } c.JSON(201, content) // if insert, _ := dbmap.Exec(`INSERT INTO users (firstname, lastname) VALUES (?, ?) returning id;`, user.Firstname, user.Lastname).Scan(&lastInsertId); insert != nil { // user_id, err := insert.LastInsertId() // if err == nil { // content := &User{ // Id: user_id, // Firstname: user.Firstname, // Lastname: user.Lastname, // } // c.JSON(201, content) // } else { // checkErr(err, "Insert failed") // } // } } else { c.JSON(422, gin.H{"error": "fields are empty"}) } // curl -i -X POST -H "Content-Type: application/json" -d "{ \"firstname\": \"Thea\", \"lastname\": \"Queen\" }" http://localhost:8080/api/v1/users }
// Creates a datafile func (e EndpointContext) CreateDataFileEndpoint(c *gin.Context) { user := c.MustGet(MIDDLEWARE_KEY_USER).(User) db := c.MustGet(MIDDLEWARE_KEY_DB).(couch.Database) datafile := NewDatafile(e.Configuration) datafile.UserID = user.DocId() // bind the Datafile to the JSON request, which will bind the // url field or throw an error. if ok := c.Bind(&datafile); !ok { errMsg := fmt.Sprintf("Invalid datafile") c.String(400, errMsg) return } logg.LogTo("REST", "datafile: %+v", datafile) // create a new Datafile object in db datafile, err := datafile.Save(db) if err != nil { errMsg := fmt.Sprintf("Error creating new datafile: %v", err) c.String(500, errMsg) return } c.JSON(201, gin.H{"id": datafile.Id}) }
func UpdateUser(c *gin.Context) { id := c.Params.ByName("id") var user User err := dbmap.SelectOne(&user, "SELECT * FROM user WHERE id=?", id) if err == nil { var json User c.Bind(&json) user_id, _ := strconv.ParseInt(id, 0, 64) user := User{ Id: user_id, Firstname: json.Firstname, Lastname: json.Lastname, } if user.Firstname != "" && user.Lastname != "" { _, err = dbmap.Update(&user) if err == nil { c.JSON(200, user) } else { checkErr(err, "Updated failed") } } else { c.JSON(422, gin.H{"error": "fields are empty"}) } } else { c.JSON(404, gin.H{"error": "user not found"}) } // curl -i -X PUT -H "Content-Type: application/json" -d "{ \"firstname\": \"Thea\", \"lastname\": \"Merlyn\" }" http://localhost:8080/api/v1/users/1 }
// Creates datasets from a datafile func (e EndpointContext) CreateDataSetsEndpoint(c *gin.Context) { user := c.MustGet(MIDDLEWARE_KEY_USER).(User) db := c.MustGet(MIDDLEWARE_KEY_DB).(couch.Database) logg.LogTo("REST", "user: %v db: %v", user, db) dataset := NewDataset(e.Configuration) // bind the input struct to the JSON request if ok := c.Bind(dataset); !ok { errMsg := fmt.Sprintf("Invalid input") c.String(400, errMsg) return } logg.LogTo("REST", "dataset: %+v", dataset) // save dataset in db if err := dataset.Insert(); err != nil { c.String(500, err.Error()) return } // the changes listener will see new datafile and download to cbfs // update with urls of training/testing artifacts (which don't exist yet) if err := dataset.AddArtifactUrls(); err != nil { errMsg := fmt.Sprintf("Error updating dataset: %+v. Err: %v", dataset, err) c.String(500, errMsg) return } c.JSON(201, dataset) }
func UpdateTemplate(c *gin.Context) { oldTemplate := c.MustGet("template").(models.Template) var template models.Template if err := c.Bind(&template); err != nil { return } if _, err := database.Mysql.Exec("update project__template set ssh_key_id=?, inventory_id=?, repository_id=?, environment_id=?, playbook=?, arguments=?, override_args=? where id=?", template.SshKeyID, template.InventoryID, template.RepositoryID, template.EnvironmentID, template.Playbook, template.Arguments, template.OverrideArguments, oldTemplate.ID); err != nil { panic(err) } desc := "Template ID " + strconv.Itoa(template.ID) + " updated" objType := "template" if err := (models.Event{ ProjectID: &oldTemplate.ProjectID, Description: &desc, ObjectID: &oldTemplate.ID, ObjectType: &objType, }.Insert()); err != nil { panic(err) } c.AbortWithStatus(204) }
// Convert a "normal" user to a "system" user func (*UsersController) Convert(ctx *gin.Context) { var convertJSON convertUserJSON ctx.Bind(&convertJSON) if !strings.HasPrefix(convertJSON.Username, "tat.system") { AbortWithReturnError(ctx, http.StatusBadRequest, fmt.Errorf("Username does not begin with tat.system (%s), it's not possible to convert this user", convertJSON.Username)) return } var userToConvert = models.User{} err := userToConvert.FindByUsername(convertJSON.Username) if err != nil { AbortWithReturnError(ctx, http.StatusBadRequest, fmt.Errorf("user with username %s does not exist", convertJSON.Username)) return } if userToConvert.IsSystem { AbortWithReturnError(ctx, http.StatusBadRequest, fmt.Errorf("user with username %s is already a system user", convertJSON.Username)) return } newPassword, err := userToConvert.ConvertToSystem(utils.GetCtxUsername(ctx), convertJSON.CanWriteNotifications) if err != nil { AbortWithReturnError(ctx, http.StatusBadRequest, fmt.Errorf("Convert %s to system user failed", convertJSON.Username)) return } ctx.JSON(http.StatusOK, gin.H{ "message": "Verification successfull", "username": userToConvert.Username, "password": newPassword, "url": fmt.Sprintf("%s://%s:%s%s", viper.GetString("exposed_scheme"), viper.GetString("exposed_host"), viper.GetString("exposed_port"), viper.GetString("exposed_path")), }) }
// AddLog adds a new log entry for the current user. func AddLog(c *gin.Context) { var log models.Log err := c.Bind(&log) if err == nil { if log.Type != "" && log.Description != "" && log.Origin != "" && log.Object != "" { uid, err := authentication.GetUserID(c) if err == nil { log.UserID = uid err := configuration.Dbmap.Insert(&log) if err == nil { showResult(c, 201, log) return } } showError(c, 400, fmt.Errorf("adding new log entry failed")) return } showError(c, 422, fmt.Errorf("field(s) are empty")) return } showError(c, 400, fmt.Errorf("adding new log entry failed")) }
// ResetSystemUser reset password for a system user func (*UsersController) ResetSystemUser(ctx *gin.Context) { var systemUserJSON resetSystemUserJSON ctx.Bind(&systemUserJSON) if !strings.HasPrefix(systemUserJSON.Username, "tat.system") { AbortWithReturnError(ctx, http.StatusBadRequest, fmt.Errorf("Username does not begin with tat.system (%s), it's not possible to reset password for this user", systemUserJSON.Username)) return } var systemUserToReset = models.User{} err := systemUserToReset.FindByUsername(systemUserJSON.Username) if err != nil { AbortWithReturnError(ctx, http.StatusBadRequest, fmt.Errorf("user with username %s does not exist", systemUserJSON.Username)) return } if !systemUserToReset.IsSystem { AbortWithReturnError(ctx, http.StatusBadRequest, fmt.Errorf("user with username %s is not a system user", systemUserJSON.Username)) return } newPassword, err := systemUserToReset.ResetSystemUserPassword() if err != nil { AbortWithReturnError(ctx, http.StatusBadRequest, fmt.Errorf("Reset password for %s (system user) failed", systemUserJSON.Username)) return } ctx.JSON(http.StatusOK, gin.H{ "message": "Reset password successfull", "username": systemUserToReset.Username, "password": newPassword, "url": fmt.Sprintf("%s://%s:%s%s", viper.GetString("exposed_scheme"), viper.GetString("exposed_host"), viper.GetString("exposed_port"), viper.GetString("exposed_path")), }) }
func createRoomEndPoint(c *gin.Context) { var json = &struct { Name string `form:"name" json:"name" binding:"required"` Description string `form:"description" json:"description"` }{} if c.Bind(json) != nil { c.JSON(http.StatusBadRequest, gin.H{"ERR": "WRONG_INPUT"}) return } userID64, _ := c.Get("userID") roomRaw := &RoomData{ Name: json.Name, Description: json.Description, OwnerID: userID64.(int), } roomID, err := saveRoomRaw(roomRaw) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"ERR": "INTERNAL_SERVER_ERR"}) return } if peer, ok := peers.get(userID64.(int)); ok { peer.joinRoom(roomID) } // return roomid and name c.JSON(http.StatusOK, gin.H{ "ID": roomID, "Name": json.Name, }) }
func UpdateHeartbeat(c *gin.Context) { id := c.Params.ByName("id") var heartbeat Heartbeat err := dbmap.SelectOne(&heartbeat, "SELECT * FROM heartbeat WHERE id=?", id) if err == nil { var json Heartbeat c.Bind(&json) heartbeat_id, _ := strconv.ParseInt(id, 0, 64) heartbeat := Heartbeat{ Id: heartbeat_id, BatteryStatus: json.BatteryStatus, WifiStrength: json.WifiStrength, } if heartbeat.BatteryStatus != "" && heartbeat.WifiStrength != "" { _, err = dbmap.Update(&heartbeat) if err == nil { c.JSON(200, heartbeat) } else { checkErr(err, "Updated failed") } } else { c.JSON(422, gin.H{"error": "fields are empty"}) } } else { c.JSON(404, gin.H{"error": "heartbeat not found"}) } // curl -i -X PUT -H "Content-Type: application/json" -d "{ \"battery_status\": \"83\", \"wifi_strength\": \"100\" }" http://localhost:8080/api/v1/heartbeats/1 }
func AddEnvironment(c *gin.Context) { project := c.MustGet("project").(models.Project) var env models.Environment if err := c.Bind(&env); err != nil { return } res, err := database.Mysql.Exec("insert into project__environment set project_id=?, name=?, json=?, password=?", project.ID, env.Name, env.JSON, env.Password) if err != nil { panic(err) } insertID, _ := res.LastInsertId() insertIDInt := int(insertID) objType := "environment" desc := "Environment " + env.Name + " created" if err := (models.Event{ ProjectID: &project.ID, ObjectType: &objType, ObjectID: &insertIDInt, Description: &desc, }.Insert()); err != nil { panic(err) } c.AbortWithStatus(204) }
func LoginPostHandler(c *gin.Context) { redirect := c.DefaultQuery(auth.RedirectParam, "/") a := auth.Default(c) if a.User.IsAuthenticated() { c.Redirect(http.StatusMovedPermanently, redirect) return } loginURL := fmt.Sprintf("/login?%s=%s", auth.RedirectParam, redirect) var form LoginForm if c.Bind(&form) == nil { model := models.Default(c) u := model.GetUserByNicknamePwd(form.Nickname, form.Password) if u != nil { session := sessions.Default(c) err := auth.AuthenticateSession(session, u) if err != nil { c.JSON(http.StatusBadRequest, err) } c.Redirect(http.StatusMovedPermanently, redirect) return } else { c.Redirect(http.StatusMovedPermanently, loginURL) return } } else { c.Redirect(http.StatusMovedPermanently, loginURL) return } }
// DeleteController deletes links func DeleteController(c *gin.Context) { var err error var df deleteForm err = c.Bind(&df) if err != nil { c.Error(err).SetMeta("link.DeleteController.Bind") c.HTML(http.StatusInternalServerError, "error.tmpl", nil) return } var link m.LinkType // get the gallery from bolt err = u.Storm.One("ID", df.Link, &link) if err != nil { c.Error(err).SetMeta("link.DeleteController.One") c.HTML(http.StatusInternalServerError, "error.tmpl", nil) return } // delete it err = u.Storm.Remove(&link) if err != nil { c.Error(err).SetMeta("link.DeleteController.Remove") c.HTML(http.StatusInternalServerError, "error.tmpl", nil) return } c.Redirect(http.StatusFound, c.Request.Referer()) return }
func RegisterPostHandler(c *gin.Context) { redirect := c.DefaultQuery(auth.RedirectParam, "/") a := auth.Default(c) if a.User.IsAuthenticated() { c.Redirect(http.StatusMovedPermanently, redirect) return } registerURL := fmt.Sprintf("/register?%s=%s", auth.RedirectParam, redirect) var form LoginForm if c.Bind(&form) == nil { model := models.Default(c) u := model.AddUserWithNicknamePwd(form.Nickname, form.Password) if u != nil { session := sessions.Default(c) err := auth.AuthenticateSession(session, u) if err != nil { c.JSON(http.StatusBadRequest, err) } c.Redirect(http.StatusMovedPermanently, redirect) return } else { log.Print("Register user add error") c.Redirect(http.StatusMovedPermanently, registerURL) return } } else { log.Print("Register form bind error") c.Redirect(http.StatusMovedPermanently, registerURL) return } }
func (pc *NodeController) postNodeAction(c *gin.Context) { id := c.Param("id") var form models.NodeUpdateForm if err := c.Bind(&form); err != nil { c.AbortWithStatus(http.StatusBadRequest) return } node, err := models.NodeMapper.FetchOneById(id) if err != nil { c.HTML(http.StatusInternalServerError, "error_500.html", map[string]interface{}{ "error": err, }) return } if node == nil { c.HTML(http.StatusNotFound, "error_404.html", map[string]interface{}{ "text": "Node not found", }) return } node.Tags = form.Tags node.Description = form.Description models.NodeMapper.Update(node) c.Redirect(http.StatusFound, "/nodes") }
func update(c *gin.Context) { var input Dog c.Bind(&input) log.Print(c) id := c.Param("id") log.Print(id) //check if valid id if bson.IsObjectIdHex(id) == false { c.JSON(400, gin.H{"error": "ID not valid"}) return } bId := bson.ObjectIdHex(id) err := Collection.UpdateId(bId, &Dog{bId, input.Name, input.Owner}) if err != nil { c.JSON(400, "id not found") } else { c.JSON(200, "dog updated") } }