diff options
Diffstat (limited to 'vendor/github.com/labstack/echo/cookbook/twitter')
6 files changed, 261 insertions, 0 deletions
diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/handler/handler.go b/vendor/github.com/labstack/echo/cookbook/twitter/handler/handler.go new file mode 100644 index 00000000..263c5e21 --- /dev/null +++ b/vendor/github.com/labstack/echo/cookbook/twitter/handler/handler.go @@ -0,0 +1,14 @@ +package handler + +import mgo "gopkg.in/mgo.v2" + +type ( + Handler struct { + DB *mgo.Session + } +) + +const ( + // Key (Should come from somewhere else). + Key = "secret" +) diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/handler/post.go b/vendor/github.com/labstack/echo/cookbook/twitter/handler/post.go new file mode 100644 index 00000000..b1428a30 --- /dev/null +++ b/vendor/github.com/labstack/echo/cookbook/twitter/handler/post.go @@ -0,0 +1,73 @@ +package handler + +import ( + "net/http" + "strconv" + + "github.com/labstack/echo" + "github.com/labstack/echo/cookbook/twitter/model" + mgo "gopkg.in/mgo.v2" + "gopkg.in/mgo.v2/bson" +) + +func (h *Handler) CreatePost(c echo.Context) (err error) { + u := &model.User{ + ID: bson.ObjectIdHex(userIDFromToken(c)), + } + p := &model.Post{ + ID: bson.NewObjectId(), + From: u.ID.Hex(), + } + if err = c.Bind(p); err != nil { + return + } + + // Validation + if p.To == "" || p.Message == "" { + return &echo.HTTPError{Code: http.StatusBadRequest, Message: "invalid to or message fields"} + } + + // Find user from database + db := h.DB.Clone() + defer db.Close() + if err = db.DB("twitter").C("users").FindId(u.ID).One(u); err != nil { + if err == mgo.ErrNotFound { + return echo.ErrNotFound + } + return + } + + // Save post in database + if err = db.DB("twitter").C("posts").Insert(p); err != nil { + return + } + return c.JSON(http.StatusCreated, p) +} + +func (h *Handler) FetchPost(c echo.Context) (err error) { + userID := userIDFromToken(c) + page, _ := strconv.Atoi(c.QueryParam("page")) + limit, _ := strconv.Atoi(c.QueryParam("limit")) + + // Defaults + if page == 0 { + page = 1 + } + if limit == 0 { + limit = 100 + } + + // Retrieve posts from database + posts := []*model.Post{} + db := h.DB.Clone() + if err = db.DB("twitter").C("posts"). + Find(bson.M{"to": userID}). + Skip((page - 1) * limit). + Limit(limit). + All(&posts); err != nil { + return + } + defer db.Close() + + return c.JSON(http.StatusOK, posts) +} diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/handler/user.go b/vendor/github.com/labstack/echo/cookbook/twitter/handler/user.go new file mode 100644 index 00000000..a34d2f4e --- /dev/null +++ b/vendor/github.com/labstack/echo/cookbook/twitter/handler/user.go @@ -0,0 +1,97 @@ +package handler + +import ( + "net/http" + "time" + + jwt "github.com/dgrijalva/jwt-go" + "github.com/labstack/echo" + "github.com/labstack/echo/cookbook/twitter/model" + mgo "gopkg.in/mgo.v2" + "gopkg.in/mgo.v2/bson" +) + +func (h *Handler) Signup(c echo.Context) (err error) { + // Bind + u := &model.User{ID: bson.NewObjectId()} + if err = c.Bind(u); err != nil { + return + } + + // Validate + if u.Email == "" || u.Password == "" { + return &echo.HTTPError{Code: http.StatusBadRequest, Message: "invalid email or password"} + } + + // Save user + db := h.DB.Clone() + defer db.Close() + if err = db.DB("twitter").C("users").Insert(u); err != nil { + return + } + + return c.JSON(http.StatusCreated, u) +} + +func (h *Handler) Login(c echo.Context) (err error) { + // Bind + u := new(model.User) + if err = c.Bind(u); err != nil { + return + } + + // Find user + db := h.DB.Clone() + defer db.Close() + if err = db.DB("twitter").C("users"). + Find(bson.M{"email": u.Email, "password": u.Password}).One(u); err != nil { + if err == mgo.ErrNotFound { + return &echo.HTTPError{Code: http.StatusUnauthorized, Message: "invalid email or password"} + } + return + } + + //----- + // JWT + //----- + + // Create token + token := jwt.New(jwt.SigningMethodHS256) + + // Set claims + claims := token.Claims.(jwt.MapClaims) + claims["id"] = u.ID + claims["exp"] = time.Now().Add(time.Hour * 72).Unix() + + // Generate encoded token and send it as response + u.Token, err = token.SignedString([]byte(Key)) + if err != nil { + return err + } + + u.Password = "" // Don't send password + return c.JSON(http.StatusOK, u) +} + +func (h *Handler) Follow(c echo.Context) (err error) { + userID := userIDFromToken(c) + id := c.Param("id") + + // Add a follower to user + db := h.DB.Clone() + defer db.Close() + if err = db.DB("twitter").C("users"). + UpdateId(bson.ObjectIdHex(id), bson.M{"$addToSet": bson.M{"followers": userID}}); err != nil { + if err == mgo.ErrNotFound { + return echo.ErrNotFound + } + } + + return +} + +func userIDFromToken(c echo.Context) string { + user := c.Get("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + return claims["id"].(string) +} diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/model/post.go b/vendor/github.com/labstack/echo/cookbook/twitter/model/post.go new file mode 100644 index 00000000..7344296e --- /dev/null +++ b/vendor/github.com/labstack/echo/cookbook/twitter/model/post.go @@ -0,0 +1,12 @@ +package model + +import "gopkg.in/mgo.v2/bson" + +type ( + Post struct { + ID bson.ObjectId `json:"id" bson:"_id,omitempty"` + To string `json:"to" bson:"to"` + From string `json:"from" bson:"from"` + Message string `json:"message" bson:"message"` + } +) diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/model/user.go b/vendor/github.com/labstack/echo/cookbook/twitter/model/user.go new file mode 100644 index 00000000..e063c89b --- /dev/null +++ b/vendor/github.com/labstack/echo/cookbook/twitter/model/user.go @@ -0,0 +1,13 @@ +package model + +import "gopkg.in/mgo.v2/bson" + +type ( + User struct { + ID bson.ObjectId `json:"id" bson:"_id,omitempty"` + Email string `json:"email" bson:"email"` + Password string `json:"password,omitempty" bson:"password"` + Token string `json:"token,omitempty" bson:"-"` + Followers []string `json:"followers,omitempty" bson:"followers,omitempty"` + } +) diff --git a/vendor/github.com/labstack/echo/cookbook/twitter/server.go b/vendor/github.com/labstack/echo/cookbook/twitter/server.go new file mode 100644 index 00000000..22db7aa0 --- /dev/null +++ b/vendor/github.com/labstack/echo/cookbook/twitter/server.go @@ -0,0 +1,52 @@ +package main + +import ( + "github.com/labstack/echo" + "github.com/labstack/echo/cookbook/twitter/handler" + "github.com/labstack/echo/middleware" + "github.com/labstack/gommon/log" + mgo "gopkg.in/mgo.v2" +) + +func main() { + e := echo.New() + e.Logger.SetLevel(log.ERROR) + e.Use(middleware.Logger()) + e.Use(middleware.JWTWithConfig(middleware.JWTConfig{ + SigningKey: []byte(handler.Key), + Skipper: func(c echo.Context) bool { + // Skip authentication for and signup login requests + if c.Path() == "/login" || c.Path() == "/signup" { + return true + } + return false + }, + })) + + // Database connection + db, err := mgo.Dial("localhost") + if err != nil { + e.Logger.Fatal(err) + } + + // Create indices + if err = db.Copy().DB("twitter").C("users").EnsureIndex(mgo.Index{ + Key: []string{"email"}, + Unique: true, + }); err != nil { + log.Fatal(err) + } + + // Initialize handler + h := &handler.Handler{DB: db} + + // Routes + e.POST("/signup", h.Signup) + e.POST("/login", h.Login) + e.POST("/follow/:id", h.Follow) + e.POST("/posts", h.CreatePost) + e.GET("/feed", h.FetchPost) + + // Start server + e.Logger.Fatal(e.Start(":1323")) +} |