//Adds a new account to the db. Passwords are generated using bcrypt func newAccountHandler(req *http.Request, db *gorm.DB, rendr render.Render) { username := req.FormValue("username") password := req.FormValue("password") if username == "" { log.Println(http.StatusBadRequest, "Username blank") rendr.Text(http.StatusBadRequest, "Supply valid username") return } if password == "" { log.Println(http.StatusBadRequest, "Password blank") rendr.Text(http.StatusBadRequest, "Supply valid password") return } hash, err := bcrypt.GenerateFromPassword([]byte(password), 0) if err != nil { log.Println(err.Error()) rendr.Text(http.StatusBadRequest, err.Error()) return } acc := &account{Username: username, Password: hash, Funds: 0} err = db.Create(acc).Error if err != nil { log.Println(err.Error()) rendr.Text(http.StatusBadRequest, err.Error()) return } rendr.Text(http.StatusOK, "Account created") }
//Deletes a certain account from the db func deleteAccountHandler(db *gorm.DB, acc account, rendr render.Render) { if err := db.Delete(&acc).Error; err != nil { rendr.Text(http.StatusInternalServerError, err.Error()) } else { rendr.Text(http.StatusOK, "Account deleted") } }
// Returns a json list of all the users func getAllUsersHandler(db *gorm.DB, ren render.Render) { users := make([]User, 0, 0) if err := db.Find(&users).Error; err != nil { ren.Text(http.StatusInternalServerError, err.Error()) return } ren.JSON(http.StatusOK, users) }
//Gets a single user as specified in the `id` path param func getUserHandler(db *gorm.DB, params martini.Params, ren render.Render) { u, err := findUserFromIDParam(db, params["id"]) if err != nil { ren.Text(err.toMartiniRender()) return } ren.JSON(http.StatusOK, u) }
//Simple auth handler based on a global key. This may have to be rewritten... func authHandler(r *http.Request, ctx martini.Context, ren render.Render) { log.Println(r.Header) if r.Header.Get("Authorization") != APIKey { ren.Text(http.StatusUnauthorized, "Invalid authorization") return } //Call the next handler ctx.Next() }
//Get /users func (u *UserHandler) GetUsers(request *http.Request, rd render.Render, params martini.Params) { users, err := u.userService.GetUsers() if err != nil { rd.Text(500, err.Error()) return } rd.JSON(200, users) }
//Transfer funds between two accounts. `from account` is retrieved from the context of the //authorization handler. `to account` is retrieved from the request query parameters func transferHandler(rendr render.Render, fromAcc account, req *http.Request, db *gorm.DB) { toAccountName := req.FormValue("to_account") amount, err := strconv.ParseFloat(req.FormValue("amount"), 64) if err != nil { log.Println(http.StatusBadRequest, err.Error()) rendr.Text(http.StatusBadRequest, err.Error()) return } toAcc, err := findAccount(db, &account{Username: toAccountName}) if err != nil { log.Println(http.StatusNotFound, "Other account not found") rendr.Text(http.StatusNotFound, "Other account not found") return } err = transferFunds(db, fromAcc.ID, toAcc.ID, amount) if err != nil { log.Println(err.Error()) rendr.Text(http.StatusBadRequest, err.Error()) return } rendr.Text(http.StatusOK, "Transaction successful") }
//Get /users/:user_id/relationships func (u *UserHandler) GetAllRelations(request *http.Request, rd render.Render, params martini.Params) { // 获取 url 参数 userid, err := strconv.ParseInt(params["user_id"], 10, 64) if err != nil { rd.Text(400, "The request cannot be fulfilled due to bad syntax.") return } //验证 user 是否存在 _, err = u.userService.GetUser(userid) if err != nil { if err == pg.ErrNoRows { rd.Text(400, fmt.Sprintln("user", userid, "not exist")) } else { rd.Text(500, err.Error()) } return } rs, err := u.userService.GetUserRelations(userid) if err != nil { rd.Text(500, err.Error()) return } relations := make([]interface{}, 0, 0) for _, r := range rs { relations = append(relations, fmtRelationShip(r)) } rd.JSON(200, relations) }
func deleteTodoHandler(params martini.Params, rendr render.Render, db *gorm.DB) { id, err := strconv.Atoi(params["id"]) if err != nil { panic(err) } t, err := findTodo(db, &todo{ID: id}) if err != nil { rendr.Text(http.StatusBadRequest, err.Error()) return } db.Delete(&t) rendr.JSON(http.StatusOK, t) }
//Adds a user using POST data. Expects `Content-Type` to be `application/x-www-form-urlencoded` func addUserHandler(db *gorm.DB, r *http.Request, ren render.Render) { name := r.FormValue("name") dob := r.FormValue("dob") address := r.FormValue("address") description := r.FormValue("description") toCheck := []string{name, dob, address, description} for _, key := range toCheck { if key == "" { ren.Text(http.StatusBadRequest, "Provide 'name', 'dob', 'address' and 'description' when creating a new user") return } } u := User{Name: name, Dob: dob, Address: address, Description: description, CreatedAt: time.Now()} db.Create(&u) }
func newTodoHandler(r *http.Request, rendr render.Render, db *gorm.DB) { todoText := r.FormValue("text") if todoText == "" { rendr.Text(http.StatusBadRequest, "Provide a `text` parameter") return } completedBool, err := strconv.ParseBool(r.FormValue("completed")) if err != nil { rendr.Text(http.StatusBadRequest, "Provide a `completed` paramter as a boolean") return } t := todo{Text: todoText, Completed: completedBool} db.Create(&t) rendr.JSON(http.StatusOK, t) }
func oneTodoHandler(params martini.Params, rendr render.Render, db *gorm.DB, r *http.Request) { id, err := strconv.Atoi(params["id"]) if err != nil { panic(err) } t, err := findTodo(db, &todo{ID: id}) if err == gorm.RecordNotFound { rendr.Text(http.StatusNotFound, "Resource not found") return } if err != nil { rendr.Text(http.StatusInternalServerError, err.Error()) return } rendr.JSON(http.StatusOK, t) }
//Post /users func (u *UserHandler) CreateUser(request *http.Request, rd render.Render, params martini.Params) { // 获取 json body 参数 decoder := json.NewDecoder(request.Body) p := struct { Name string `json:"name"` }{} err := decoder.Decode(&p) if err != nil || len(p.Name) == 0 { rd.Text(400, "The request cannot be fulfilled due to bad syntax.") return } user, err := u.userService.CreateUser(p.Name) if err != nil { rd.Text(500, err.Error()) return } rd.JSON(200, user) }
func updateTodoHandler(r *http.Request, params martini.Params, rendr render.Render, db *gorm.DB) { id, err := strconv.Atoi(params["id"]) if err != nil { rendr.Text(http.StatusBadRequest, "Provide a `completed` paramter as a boolean") return } t, err := findTodo(db, &todo{ID: id}) if err != nil { rendr.Text(http.StatusBadRequest, err.Error()) return } text := r.FormValue("text") if text != "" { t.Text = text } completed := r.FormValue("completed") if completed != "" { completedBool, err := strconv.ParseBool(completed) if err != nil { rendr.Text(http.StatusBadRequest, err.Error()) return } t.Completed = completedBool } db.Save(t) rendr.JSON(http.StatusOK, t) }
//Logs a user in. If successful returns the authorization token //Note that since we don't have a logout handler, a token for a deleted account can be valid. This //is partially migitated by the 5 minute exp mark. func loginHandler(req *http.Request, db *gorm.DB, rendr render.Render) { username := req.FormValue("username") password := req.FormValue("password") acc, err := findAccount(db, &account{Username: username}) if err != nil { rendr.Text(http.StatusNotFound, "Username not found") return } err = bcrypt.CompareHashAndPassword(acc.Password, []byte(password)) if err != nil { rendr.Text(http.StatusUnauthorized, "Invalid password") return } token := jwt.New(signingMethod) token.Claims["exp"] = time.Now().Add(tokenExpires).Unix() token.Claims["id"] = acc.ID tokenStr, err := token.SignedString([]byte(secretKey)) if err != nil { rendr.Text(http.StatusInternalServerError, err.Error()) return } tokenJSON := make(map[string]string) tokenJSON["token"] = tokenStr rendr.JSON(http.StatusOK, tokenJSON) }
//Deletes a user based on a given path `id` param func deleteUserHandler(db *gorm.DB, params martini.Params, ren render.Render) { u, err := findUserFromIDParam(db, params["id"]) if err != nil { ren.Text(err.toMartiniRender()) return } if db.Delete(&u).Error != nil { ren.Text(http.StatusInternalServerError, err.Error()) return } ren.Text(http.StatusOK, fmt.Sprintf("User with id: %d was deleted", u.ID)) }
//If we don't write a response here, the next handler will execute func auth(req *http.Request, rendr render.Render, ctx martini.Context, db *gorm.DB) { tokenStr := req.Header.Get("Authorization") token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) { if token.Method != signingMethod { log.Println("token.Method != signingMethod") return nil, fmt.Errorf("Invalid signingMethod") } return secretKey, nil }) if err != nil { log.Println(err.Error()) log.Println("token was:", tokenStr) rendr.Text(http.StatusUnauthorized, err.Error()) return } if !token.Valid { rendr.Text(http.StatusUnauthorized, "Invalid token") return } userID, ok := token.Claims["id"].(float64) if !ok { rendr.Text(http.StatusInternalServerError, "Bad token") return } acc, err := findAccount(db, &account{ID: int(userID)}) if err != nil { rendr.Text(http.StatusNotFound, "User not found") return } //Attach the current user to the context for other handlers //Pass by value because we don't want to manipulate the object for each request, causes race //conditions. Instead, let's do everything in transactions wher we `select` the account first ctx.Map(acc) ctx.Next() }
func withdrawHandler(rendr render.Render, acc account, req *http.Request, db *gorm.DB) { amount, err := strconv.ParseFloat(req.FormValue("amount"), 64) if err != nil { rendr.Text(http.StatusBadRequest, err.Error()) return } if err = withdrawFunds(db, acc.ID, amount); err != nil { rendr.Text(http.StatusBadRequest, err.Error()) return } rendr.Text(http.StatusOK, "Withdrawal successful") }
//Deposit funds to an account. The account is retrieved from the context of the previous //authorization handler func depositHandler(rendr render.Render, acc account, req *http.Request, db *gorm.DB) { amount, err := strconv.ParseFloat(req.FormValue("amount"), 64) if err != nil { log.Println(err.Error()) rendr.Text(http.StatusBadRequest, err.Error()) return } err = depositFunds(db, acc.ID, amount) if err != nil { log.Println(err.Error()) rendr.Text(http.StatusBadRequest, err.Error()) return } rendr.Text(http.StatusOK, "Deposit successful") }
//Updates a user based on a given path parameter `id`. Beware: if there are no supplied parameters to //updated it returns a 400 notifying that no update was made. We might need to change the status //code, but I'd rather prefer the failure to be visible. //Expects `Content-Type` to be `application/x-www-form-urlencoded` func updateUserHandler(db *gorm.DB, params martini.Params, r *http.Request, ren render.Render) { u, err := findUserFromIDParam(db, params["id"]) if err != nil { ren.Text(err.toMartiniRender()) return } anyUpdates := false name := r.FormValue("name") if name != "" { anyUpdates = true u.Name = name } dob := r.FormValue("dob") if dob != "" { anyUpdates = true u.Dob = dob } address := r.FormValue("address") if address != "" { anyUpdates = true u.Address = address } description := r.FormValue("description") if description != "" { anyUpdates = true u.Description = description } if !anyUpdates { ren.Text(http.StatusBadRequest, "At least provide one valid parameter to update") return } if err := db.Save(u).Error; err != nil { ren.Text(http.StatusInternalServerError, err.Error()) return } ren.JSON(http.StatusOK, u) }
//Put /users/:user_id/relationships/:other_user_id func (u *UserHandler) ChangeUserRelation(request *http.Request, rd render.Render, params martini.Params) { // 获取 url 参数 userid, err1 := strconv.ParseInt(params["user_id"], 10, 64) otherid, err2 := strconv.ParseInt(params["other_user_id"], 10, 64) if err1 != nil || err2 != nil { rd.Text(400, "The request cannot be fulfilled due to bad syntax.") return } // 获取 json body 参数 decoder := json.NewDecoder(request.Body) p := struct { State string `json:"state"` }{} err := decoder.Decode(&p) //验证 user 是否存在 _, err = u.userService.GetUser(userid) if err != nil { if err == pg.ErrNoRows { rd.Text(400, fmt.Sprintln("user", userid, "not exist")) } else { rd.Text(500, err.Error()) } return } _, err = u.userService.GetUser(otherid) if err != nil { if err == pg.ErrNoRows { rd.Text(400, fmt.Sprintln("user", otherid, "not exist")) } else { rd.Text(500, err.Error()) } return } if _, ok := Model.AllowedRelation[p.State]; err != nil || !ok { rd.Text(400, "The request cannot be fulfilled due to bad syntax.") return } r, err := u.userService.ChangeUserRelation(userid, otherid, Model.AllowedRelation[p.State]) if err != nil { rd.Text(500, err.Error()) return } rd.JSON(200, fmtRelationShip(r)) }
func (this *Request) showError(err error, r render.Render) { r.Text(500, fmt.Sprintf("%v", err)) }