diff --git a/server/api.go b/server/api.go index bff3918..26e1648 100644 --- a/server/api.go +++ b/server/api.go @@ -6,6 +6,8 @@ import ( "log" "net/http" "runtime/debug" + + "github.com/gorilla/mux" ) type ApiError struct { @@ -41,6 +43,15 @@ type Context struct { R *http.Request } +func (c Context) GetVar(name string) string { + vars := mux.Vars(c.R) + val, ok := vars[name] + if ok { + return val + } + return "" +} + func (c Context) Header(header string, value string) *Context { c.W.Header().Add(header, value) return &c diff --git a/server/handlersBookmark.go b/server/handlersBookmark.go index 03c0887..f2dc97b 100644 --- a/server/handlersBookmark.go +++ b/server/handlersBookmark.go @@ -2,7 +2,7 @@ package server import ( "log" - "strings" + "strconv" "time" "git.nefrace.ru/nefrace/nashboard/storage" @@ -45,19 +45,9 @@ func (s Server) handleGetBookmarks(c Context) error { ctx := c.R.Context() user := ctx.Value(CtxValue("user")).(*storage.User) // Getting currently logged in user query := c.R.URL.Query() - filter := bson.D{{Key: "userid", Value: user.Id}} // Filter only bookmarks owned by User - for k, v := range query { - var f bson.E - q := strings.Split(k, "_") - if len(q) == 1 { // If param is like "name" or "url" we're using exact matching - f = bson.E{Key: k, Value: v[0]} - } else { - if q[1] == "like" { // If it's like "name_like", we're using regex - f = bson.E{Key: q[0], Value: bson.D{{Key: "$regex", Value: v[0]}}} - } - } - filter = append(filter, f) // Applying query filters to user filter - } + filter := storage.QueryFilter(&query) + filter = append(filter, bson.E{Key: "userid", Value: user.Id}) + log.Println(filter) bookmarks, err := s.Db.GetBookmarks(&filter) if err != nil { log.Println("Error getting bookmarks: ", err) @@ -65,3 +55,18 @@ func (s Server) handleGetBookmarks(c Context) error { } return c.WriteJSON(200, bookmarks) } + +func (s Server) handleGetRandomBookmarks(c Context) error { + ctx := c.R.Context() + user := ctx.Value(CtxValue("user")).(*storage.User) + countVar := c.GetVar("count") + count, err := strconv.Atoi(countVar) + if err != nil { + count = 1 + } + bookmarks, err := s.Db.GetRandomBookmarks(count, &bson.D{{Key: "userid", Value: user.Id}}) + if err != nil { + return ApiError{Err: "can't get bookmarks: " + err.Error(), Status: 400} + } + return c.WriteJSON(200, bookmarks) +} diff --git a/server/server.go b/server/server.go index 21be901..b572b90 100644 --- a/server/server.go +++ b/server/server.go @@ -11,7 +11,7 @@ import ( type Server struct { Host string Mux *mux.Router - Db storage.Storage + Db storage.MongodbStorage } var ErrUnauthorized ApiError = ApiError{Status: http.StatusUnauthorized, Err: "Unauthorized"} @@ -42,6 +42,8 @@ func (s Server) InitHandlers() { bookmarkRouter.Use(s.AuthorizedOnly) bookmarkRouter.Path("").Methods("GET").HandlerFunc(MakeHTTPHandler(s.handleGetBookmarks)) bookmarkRouter.Path("").Methods("POST").HandlerFunc(MakeHTTPHandler(s.handleNewBookmark)) + bookmarkRouter.Path("/random").HandlerFunc(MakeHTTPHandler(s.handleGetRandomBookmarks)) + bookmarkRouter.Path("/random/{count}").HandlerFunc(MakeHTTPHandler(s.handleGetRandomBookmarks)) r.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/"))) } diff --git a/storage/mongoStore.go b/storage/mongoStore.go index 933e7e0..8ca4ead 100644 --- a/storage/mongoStore.go +++ b/storage/mongoStore.go @@ -72,6 +72,7 @@ func (s Store[T]) GetMany(ctx context.Context, filter *bson.D) ([]*T, error) { } return res, nil } + func (s Store[T]) DeleteByID(ctx context.Context, id primitive.ObjectID) error { _, err := s.Coll.DeleteOne(ctx, bson.D{{Key: "_id", Value: id}}) return err @@ -81,10 +82,3 @@ func (s Store[T]) ReplaceByID(ctx context.Context, id primitive.ObjectID, item * _, err := s.Coll.ReplaceOne(ctx, bson.D{{Key: "_id", Value: id}}, item) return err } - -func getFilter(f *bson.D) bson.D { - if f == nil { - return bson.D{} - } - return *f -} diff --git a/storage/mongostorage.go b/storage/mongostorage.go index 044f08c..f3655da 100644 --- a/storage/mongostorage.go +++ b/storage/mongostorage.go @@ -2,6 +2,9 @@ package storage import ( "context" + "log" + "net/url" + "strings" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" @@ -96,6 +99,23 @@ func (m MongodbStorage) GetBookmarks(filter *bson.D) ([]*Bookmark, error) { return store.GetMany(ctx, filter) } +func (m MongodbStorage) GetRandomBookmarks(count int, filter *bson.D) ([]*Bookmark, error) { + passedFilter := getFilter(filter) + ctx := context.TODO() + store := NewStore[Bookmark](&m, "bookmarks") + bookmarks := []*Bookmark{} + cur, err := store.Coll.Aggregate(ctx, bson.A{ + bson.D{{Key: "$match", Value: passedFilter}}, + bson.D{{Key: "$sample", Value: bson.D{bson.E{Key: "size", Value: count}}}}, + }) + if err != nil { + log.Println("Cant aggregate random bookmakrs: ", err) + return bookmarks, err + } + cur.All(ctx, &bookmarks) + return bookmarks, err +} + func (m MongodbStorage) GetUserBookmarks(user *User, filter *bson.D) ([]*Bookmark, error) { passedFilter := getFilter(filter) f := bson.D{{Key: "userid", Value: user.Id}} @@ -108,3 +128,27 @@ func (m MongodbStorage) GetUserBookmarks(user *User, filter *bson.D) ([]*Bookmar func (m MongodbStorage) Collection(col string) *mongo.Collection { return m.Db.Database("nash").Collection(col) } + +func getFilter(f *bson.D) bson.D { + if f == nil { + return bson.D{} + } + return *f +} + +func QueryFilter(q *url.Values) bson.D { + filter := bson.D{} + for k, v := range *q { + var f bson.E + q := strings.Split(k, "_") + if len(q) == 1 { // If param is like "name" or "url" we're using exact matching + f = bson.E{Key: k, Value: v[0]} + } else { + if q[1] == "like" { // If it's like "name_like", we're using regex + f = bson.E{Key: q[0], Value: bson.D{{Key: "$regex", Value: v[0]}}} + } + } + filter = append(filter, f) // Applying query filters to user filter + } + return filter +}