diff --git a/Gopkg.lock b/Gopkg.lock index 969dad73..3dfa5021 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -69,6 +69,12 @@ packages = ["."] revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" +[[projects]] + name = "github.com/gorilla/feeds" + packages = ["."] + revision = "6edcbcd2d57fd0bbd7f39947a593ed0c06648388" + version = "v1.1.0" + [[projects]] name = "github.com/gorilla/securecookie" packages = ["."] @@ -195,6 +201,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "f4e55d0be501f335781d2365e71384e7aee319599603a8f3fef16759e9fd57d1" + inputs-digest = "b3e210953878a43cfcd3a8082639f5abc321ff78e68543913b64b4c62400805f" solver-name = "gps-cdcl" solver-version = 1 diff --git a/README.md b/README.md index 00d6cc7f..542e3b54 100644 --- a/README.md +++ b/README.md @@ -315,7 +315,7 @@ Sort can be `time` or `score`. Supported sort order with prefix -/+, i.e. `-time ``` * `GET /api/v1/user` - get user info, _auth required_ * `PUT /api/v1/vote/{id}?site=site-id&url=post-url&vote=1` - vote for comment. `vote`=1 will increase score, -1 decrease. _auth required_ -* `GET /api/v1/config?site=siteID` - returns configuration (parameters) for given site +* `GET /api/v1/config?site=site-id` - returns configuration (parameters) for given site ```go type config struct { @@ -325,6 +325,7 @@ Sort can be `time` or `score`. Supported sort order with prefix -/+, i.e. `-time Auth []string `json:"auth_providers"` } ``` +* `GET /api/v1/rss/post?site=site-id&url=post-url` - rss feed for a post ### Admin diff --git a/app/rest/api/rest.go b/app/rest/api/rest.go index c2971518..42fb4cd6 100644 --- a/app/rest/api/rest.go +++ b/app/rest/api/rest.go @@ -97,6 +97,7 @@ func (s *Rest) Run(port int) { rapi.Get("/list", s.listCtrl) rapi.Get("/config", s.configCtrl) rapi.Post("/preview", s.previewCommentCtrl) + rapi.Get("/rss", s.rssPostCommentsCtrl) // protected routes, require auth rapi.With(s.Authenticator.Auth(true)).Group(func(rauth chi.Router) { diff --git a/app/rest/api/rss.go b/app/rest/api/rss.go new file mode 100644 index 00000000..f91eb7dc --- /dev/null +++ b/app/rest/api/rss.go @@ -0,0 +1,74 @@ +package api + +import ( + "log" + "net/http" + "time" + + "github.com/go-chi/render" + "github.com/gorilla/feeds" + "github.com/umputun/remark/app/rest" + "github.com/umputun/remark/app/store" +) + +// GET /rss/post?site=siteID&url=post-url +func (s *Rest) rssPostCommentsCtrl(w http.ResponseWriter, r *http.Request) { + locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")} + sort := "-time" + log.Printf("[DEBUG] get rss for post %+v", locator) + + data, err := s.Cache.Get(rest.URLKey(r), time.Hour, func() ([]byte, error) { + comments, e := s.DataService.Find(locator, sort) + if e != nil { + return nil, e + } + maskedComments := s.adminService.alterComments(comments, r) + + rss, e := s.toRssFeed(locator.URL, maskedComments) + if e != nil { + return nil, e + } + return []byte(rss), e + }) + + if err != nil { + rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't find comments") + return + } + + w.Header().Set("Content-Type", "application/xml; charset=utf-8") + if status, ok := r.Context().Value(render.StatusCtxKey).(int); ok { + w.WriteHeader(status) + } + if _, err := w.Write(data); err != nil { + log.Printf("[WARN] failed to send response to %s, %s", r.RemoteAddr, err) + } +} + +func (s *Rest) toRssFeed(url string, comments []store.Comment) (string, error) { + + lastCommentTS := time.Unix(0, 0) + if len(comments) > 0 { + lastCommentTS = comments[0].Timestamp + } + + feed := &feeds.Feed{ + Title: "Remark42 comments", + Link: &feeds.Link{Href: url}, + Description: "comment updates", + Created: lastCommentTS, + } + + feed.Items = []*feeds.Item{} + for _, c := range comments { + f := feeds.Item{ + Title: "update", + Link: &feeds.Link{Href: url}, + Description: c.Text, + Created: c.Timestamp, + Author: &feeds.Author{Name: c.User.Name}, + } + feed.Items = append(feed.Items, &f) + } + return feed.ToRss() +} diff --git a/app/rest/auth/avatar.go b/app/rest/auth/avatar.go index 67323660..961e0dcf 100644 --- a/app/rest/auth/avatar.go +++ b/app/rest/auth/avatar.go @@ -55,7 +55,7 @@ func (p *AvatarProxy) Put(u store.User) (avatarURL string, err error) { // get ID and location of locally cached avatar encID := store.EncodeID(u.ID) - location := p.location(encID) // location adds partion to path + location := p.location(encID) // location adds partition to path if _, err = os.Stat(location); os.IsNotExist(err) { if e := os.Mkdir(location, 0700); e != nil {