diff --git a/api/v1/rss.go b/api/v1/rss.go index 9ad4055a..ec2f8acf 100644 --- a/api/v1/rss.go +++ b/api/v1/rss.go @@ -13,14 +13,17 @@ import ( "github.com/labstack/echo/v4" "github.com/usememos/memos/internal/util" + "github.com/usememos/memos/plugin/gomark/ast" "github.com/usememos/memos/plugin/gomark/parser" "github.com/usememos/memos/plugin/gomark/parser/tokenizer" "github.com/usememos/memos/plugin/gomark/renderer" "github.com/usememos/memos/store" ) -const maxRSSItemCount = 100 -const maxRSSItemTitleLength = 100 +const ( + maxRSSItemCount = 100 + maxRSSItemTitleLength = 128 +) func (s *APIV1Service) registerRSSRoutes(g *echo.Group) { g.GET("/explore/rss.xml", s.GetExploreRSS) @@ -171,29 +174,24 @@ func (s *APIV1Service) getSystemCustomizedProfile(ctx context.Context) (*Customi } func getRSSItemTitle(content string) string { - var title string - if isTitleDefined(content) { - title = strings.Split(content, "\n")[0][2:] - } else { - title = strings.Split(content, "\n")[0] - var titleLengthLimit = util.Min(len(title), maxRSSItemTitleLength) - if titleLengthLimit < len(title) { - title = title[:titleLengthLimit] + "..." - } + tokens := tokenizer.Tokenize(content) + nodes, _ := parser.Parse(tokens) + if len(nodes) > 0 { + firstNode := nodes[0] + title := renderer.NewStringRenderer().Render([]ast.Node{firstNode}) + return title + } + + title := strings.Split(content, "\n")[0] + var titleLengthLimit = util.Min(len(title), maxRSSItemTitleLength) + if titleLengthLimit < len(title) { + title = title[:titleLengthLimit] + "..." } return title } func getRSSItemDescription(content string) (string, error) { - var description string - if isTitleDefined(content) { - var firstLineEnd = strings.Index(content, "\n") - description = strings.Trim(content[firstLineEnd+1:], " ") - } else { - description = content - } - - tokens := tokenizer.Tokenize(description) + tokens := tokenizer.Tokenize(content) nodes, err := parser.Parse(tokens) if err != nil { return "", err @@ -201,7 +199,3 @@ func getRSSItemDescription(content string) (string, error) { result := renderer.NewHTMLRenderer().Render(nodes) return result, nil } - -func isTitleDefined(content string) bool { - return strings.HasPrefix(content, "# ") -} diff --git a/server/frontend/frontend.go b/server/frontend/frontend.go index 561d7f97..3cc7ea91 100644 --- a/server/frontend/frontend.go +++ b/server/frontend/frontend.go @@ -1,6 +1,7 @@ package frontend import ( + "context" "fmt" "html/template" "net/http" @@ -10,7 +11,7 @@ import ( "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" - v1 "github.com/usememos/memos/api/v1" + apiv1 "github.com/usememos/memos/api/v1" "github.com/usememos/memos/internal/util" "github.com/usememos/memos/plugin/gomark/parser" "github.com/usememos/memos/plugin/gomark/parser/tokenizer" @@ -19,6 +20,11 @@ import ( "github.com/usememos/memos/store" ) +const ( + // maxMetadataDescriptionLength is the maximum length of metadata description. + maxMetadataDescriptionLength = 256 +) + type FrontendService struct { Profile *profile.Profile Store *store.Store @@ -31,86 +37,24 @@ func NewFrontendService(profile *profile.Profile, store *store.Store) *FrontendS } } -func (s *FrontendService) Serve(e *echo.Echo) { +func (s *FrontendService) Serve(ctx context.Context, e *echo.Echo) { // Use echo static middleware to serve the built dist folder. // refer: https://github.com/labstack/echo/blob/master/middleware/static.go e.Use(middleware.StaticWithConfig(middleware.StaticConfig{ - HTML5: true, - Filesystem: http.Dir("dist"), + Root: "dist", + HTML5: true, Skipper: func(c echo.Context) bool { return util.HasPrefixes(c.Path(), "/api", "/memos.api.v2", "/robots.txt", "/sitemap.xml", "/m/:memoID") }, })) s.registerRoutes(e) + s.registerFileRoutes(ctx, e) } func (s *FrontendService) registerRoutes(e *echo.Echo) { rawIndexHTML := getRawIndexHTML() - e.GET("/robots.txt", func(c echo.Context) error { - ctx := c.Request().Context() - instanceURLSetting, err := s.Store.GetSystemSetting(ctx, &store.FindSystemSetting{ - Name: v1.SystemSettingInstanceURLName.String(), - }) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get instance URL system setting").SetInternal(err) - } - if instanceURLSetting == nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Instance URL system setting is not set") - } - instanceURL := instanceURLSetting.Value - if instanceURL == "" { - return echo.NewHTTPError(http.StatusInternalServerError, "Instance URL system setting is not set") - } - - robotsTxt := fmt.Sprintf(`User-agent: * -Allow: / -Host: %s -Sitemap: %s/sitemap.xml`, instanceURL, instanceURL) - return c.String(http.StatusOK, robotsTxt) - }) - - e.GET("/sitemap.xml", func(c echo.Context) error { - ctx := c.Request().Context() - instanceURLSetting, err := s.Store.GetSystemSetting(ctx, &store.FindSystemSetting{ - Name: v1.SystemSettingInstanceURLName.String(), - }) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get instance URL system setting").SetInternal(err) - } - if instanceURLSetting == nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Instance URL system setting is not set") - } - instanceURL := instanceURLSetting.Value - if instanceURL == "" { - return echo.NewHTTPError(http.StatusInternalServerError, "Instance URL system setting is not set") - } - - urlsets := []string{} - // Append memo list. - memoList, err := s.Store.ListMemos(ctx, &store.FindMemo{ - VisibilityList: []store.Visibility{store.Public}, - }) - if err != nil { - return err - } - for _, memo := range memoList { - urlsets = append(urlsets, fmt.Sprintf(`%s`, fmt.Sprintf("%s/m/%d", instanceURL, memo.ID))) - } - // Append user list. - userList, err := s.Store.ListUsers(ctx, &store.FindUser{}) - if err != nil { - return err - } - for _, user := range userList { - urlsets = append(urlsets, fmt.Sprintf(`%s`, fmt.Sprintf("%s/u/%s", instanceURL, user.Username))) - } - - sitemap := fmt.Sprintf(`%s`, strings.Join(urlsets, "\n")) - return c.XMLBlob(http.StatusOK, []byte(sitemap)) - }) - e.GET("/m/:memoID", func(c echo.Context) error { ctx := c.Request().Context() memoID, err := util.ConvertStringToInt32(c.Param("memoID")) @@ -141,6 +85,53 @@ Sitemap: %s/sitemap.xml`, instanceURL, instanceURL) }) } +func (s *FrontendService) registerFileRoutes(ctx context.Context, e *echo.Echo) { + instanceURLSetting, err := s.Store.GetSystemSetting(ctx, &store.FindSystemSetting{ + Name: apiv1.SystemSettingInstanceURLName.String(), + }) + if err != nil || instanceURLSetting == nil { + return + } + instanceURL := instanceURLSetting.Value + if instanceURL == "" { + return + } + + e.GET("/robots.txt", func(c echo.Context) error { + robotsTxt := fmt.Sprintf(`User-agent: * +Allow: / +Host: %s +Sitemap: %s/sitemap.xml`, instanceURL, instanceURL) + return c.String(http.StatusOK, robotsTxt) + }) + + e.GET("/sitemap.xml", func(c echo.Context) error { + ctx := c.Request().Context() + urlsets := []string{} + // Append memo list. + memoList, err := s.Store.ListMemos(ctx, &store.FindMemo{ + VisibilityList: []store.Visibility{store.Public}, + }) + if err != nil { + return err + } + for _, memo := range memoList { + urlsets = append(urlsets, fmt.Sprintf(`%s`, fmt.Sprintf("%s/m/%d", instanceURL, memo.ID))) + } + // Append user list. + userList, err := s.Store.ListUsers(ctx, &store.FindUser{}) + if err != nil { + return err + } + for _, user := range userList { + urlsets = append(urlsets, fmt.Sprintf(`%s`, fmt.Sprintf("%s/u/%s", instanceURL, user.Username))) + } + + sitemap := fmt.Sprintf(`%s`, strings.Join(urlsets, "\n")) + return c.XMLBlob(http.StatusOK, []byte(sitemap)) + }) +} + func generateMemoMetadata(memo *store.Memo, creator *store.User) string { description := "" if memo.Visibility == store.Private { @@ -154,8 +145,8 @@ func generateMemoMetadata(memo *store.Memo, creator *store.User) string { if len(description) == 0 { description = memo.Content } - if len(description) > 200 { - description = description[:200] + "..." + if len(description) > maxMetadataDescriptionLength { + description = description[:maxMetadataDescriptionLength] + "..." } }