diff --git a/go.mod b/go.mod index a348ff33c..eea5ea7b1 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( require ( github.com/VictoriaMetrics/fastcache v1.10.0 + github.com/gorilla/feeds v1.1.1 github.com/gorilla/securecookie v1.1.1 github.com/gorilla/sessions v1.2.1 github.com/labstack/echo-contrib v0.13.0 @@ -35,4 +36,5 @@ require ( require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/kr/pretty v0.3.1 // indirect ) diff --git a/go.sum b/go.sum index aec210424..fe3cd5f43 100644 --- a/go.sum +++ b/go.sum @@ -166,6 +166,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/feeds v1.1.1 h1:HwKXxqzcRNg9to+BbvJog4+f3s/xzvtZXICcQGutYfY= +github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= @@ -200,8 +202,11 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo-contrib v0.13.0 h1:bzSG0SpuZZd7BmJLvsWtPfU23W0Enh3K0tok3aENVKA= github.com/labstack/echo-contrib v0.13.0/go.mod h1:IF9+MJu22ADOZEHD+bAV67XMIO3vNXUy7Naz/ABPHEs= @@ -238,6 +243,7 @@ github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.4.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -268,6 +274,8 @@ github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0V github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= diff --git a/server/rss.go b/server/rss.go new file mode 100644 index 000000000..845ec3a3d --- /dev/null +++ b/server/rss.go @@ -0,0 +1,72 @@ +package server + +import ( + "net/http" + "strconv" + "time" + + "github.com/gorilla/feeds" + "github.com/labstack/echo/v4" + "github.com/usememos/memos/api" +) + +func (s *Server) registerRSSRoutes(g *echo.Group) { + g.GET("/u/:id/rss.xml", func(c echo.Context) error { + ctx := c.Request().Context() + + id, err := strconv.Atoi(c.Param("id")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "User id is not a number").SetInternal(err) + } + + normalStatus := api.Normal + memoFind := api.MemoFind{ + CreatorID: &id, + RowStatus: &normalStatus, + VisibilityList: []api.Visibility{ + api.Public, + }, + } + memoList, err := s.Store.FindMemoList(ctx, &memoFind) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find memo list").SetInternal(err) + } + + userFind := api.UserFind{ + ID: &id, + } + user, err := s.Store.FindUser(ctx, &userFind) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user").SetInternal(err) + } + + baseURL := c.Scheme() + "://" + c.Request().Host + + feed := &feeds.Feed{ + Title: "Memos", + Link: &feeds.Link{Href: baseURL}, + Description: "Memos", + Author: &feeds.Author{Name: user.Name}, + Created: time.Now(), + } + + feed.Items = make([]*feeds.Item, len(memoList)) + for i, memo := range memoList { + feed.Items[i] = &feeds.Item{ + Title: memo.Content, + Link: &feeds.Link{Href: baseURL + "/m/" + strconv.Itoa(memo.ID)}, + Description: memo.Content, + Created: time.Unix(memo.CreatedTs, 0), + } + } + + rss, err := feed.ToRss() + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate rss").SetInternal(err) + } + + rssPrefix := `` + + return c.XMLBlob(http.StatusOK, []byte(rss[len(rssPrefix):])) + }) +} diff --git a/server/server.go b/server/server.go index ed17905e7..351b12efe 100644 --- a/server/server.go +++ b/server/server.go @@ -56,6 +56,9 @@ func NewServer(profile *profile.Profile) *Server { Profile: profile, } + rootGroup := e.Group("") + s.registerRSSRoutes(rootGroup) + webhookGroup := e.Group("/h") s.registerResourcePublicRoutes(webhookGroup) diff --git a/web/vite.config.ts b/web/vite.config.ts index f6d949b27..c7c379073 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -20,6 +20,10 @@ export default defineConfig({ target: "http://localhost:8080/", changeOrigin: true, }, + "^/u/\\d*/rss.xml": { + target: "http://localhost:8080/", + changeOrigin: true, + }, }, }, });