- commit
- d6d75f7
- parent
- 78af75d
- author
- Eric Bower
- date
- 2022-08-27 04:23:24 +0000 UTC
fix(rss): limit blog rss feeds to 10
6 files changed,
+191,
-135
M
db/db.go
+2,
-2
1@@ -135,7 +135,7 @@ type DB interface {
2
3 FindPosts() ([]*Post, error)
4 FindPost(postID string) (*Post, error)
5- FindPostsForUser(userID string, space string) ([]*Post, error)
6+ FindPostsForUser(pager *Pager, userID string, space string) (*Paginate[*Post], error)
7 FindAllPostsForUser(userID string, space string) ([]*Post, error)
8 FindPostsBeforeDate(date *time.Time, space string) ([]*Post, error)
9 FindUpdatedPostsForUser(userID string, space string) ([]*Post, error)
10@@ -149,7 +149,7 @@ type DB interface {
11 RemovePosts(postIDs []string) error
12
13 ReplaceTagsForPost(tags []string, postID string) error
14- FindUserPostsByTag(tag, userID, space string) ([]*Post, error)
15+ FindUserPostsByTag(pager *Pager, tag, userID, space string) (*Paginate[*Post], error)
16 FindPostsByTag(pager *Pager, tag, space string) (*Paginate[*Post], error)
17 FindPopularTags(space string) ([]string, error)
18 FindTagsForPost(postID string) ([]string, error)
+53,
-12
1@@ -69,11 +69,13 @@ var (
2 LEFT OUTER JOIN app_users ON app_users.id = posts.user_id
3 LEFT OUTER JOIN post_tags ON post_tags.post_id = posts.id
4 WHERE
5+ hidden = FALSE AND
6 user_id = $1 AND
7 publish_at::date <= CURRENT_DATE AND
8 cur_space = $2
9 GROUP BY %s
10- ORDER BY publish_at DESC, slug DESC`, SelectPost, SelectPost)
11+ ORDER BY publish_at DESC, slug DESC
12+ LIMIT $3 OFFSET $4`, SelectPost, SelectPost)
13
14 sqlSelectAllPostsForUser = fmt.Sprintf(`
15 SELECT %s
16@@ -113,11 +115,13 @@ var (
17 LEFT OUTER JOIN app_users ON app_users.id = posts.user_id
18 LEFT OUTER JOIN post_tags ON post_tags.post_id = posts.id
19 WHERE
20+ hidden = FALSE AND
21 user_id = $1 AND
22 (post_tags.name = $2 OR hidden = true) AND
23 publish_at::date <= CURRENT_DATE AND
24 cur_space = $3
25- ORDER BY publish_at DESC`, SelectPost)
26+ ORDER BY publish_at DESC
27+ LIMIT $4 OFFSET $5`, SelectPost)
28
29 /* sqlSelectUserPostsWithTags = fmt.Sprintf(`
30 SELECT %s, STRING_AGG(coalesce(post_tags.name, ''), ',') tags
31@@ -705,11 +709,17 @@ func (me *PsqlDB) RemovePosts(postIDs []string) error {
32 return err
33 }
34
35-func (me *PsqlDB) FindPostsForUser(userID string, space string) ([]*db.Post, error) {
36+func (me *PsqlDB) FindPostsForUser(page *db.Pager, userID string, space string) (*db.Paginate[*db.Post], error) {
37 var posts []*db.Post
38- rs, err := me.Db.Query(sqlSelectPostsForUser, userID, space)
39+ rs, err := me.Db.Query(
40+ sqlSelectPostsForUser,
41+ userID,
42+ space,
43+ page.Num,
44+ page.Num*page.Page,
45+ )
46 if err != nil {
47- return posts, err
48+ return nil, err
49 }
50 for rs.Next() {
51 post, err := CreatePostWithTagsFromRow(rs)
52@@ -719,10 +729,22 @@ func (me *PsqlDB) FindPostsForUser(userID string, space string) ([]*db.Post, err
53
54 posts = append(posts, post)
55 }
56+
57 if rs.Err() != nil {
58- return posts, rs.Err()
59+ return nil, rs.Err()
60 }
61- return posts, nil
62+
63+ var count int
64+ err = me.Db.QueryRow(sqlSelectPostCount, space).Scan(&count)
65+ if err != nil {
66+ return nil, err
67+ }
68+
69+ pager := &db.Paginate[*db.Post]{
70+ Data: posts,
71+ Total: int(math.Ceil(float64(count) / float64(page.Num))),
72+ }
73+ return pager, nil
74 }
75
76 func (me *PsqlDB) FindAllPostsForUser(userID string, space string) ([]*db.Post, error) {
77@@ -867,11 +889,18 @@ func (me *PsqlDB) ReplaceTagsForPost(tags []string, postID string) error {
78 return err
79 }
80
81-func (me *PsqlDB) FindUserPostsByTag(tag, userID, space string) ([]*db.Post, error) {
82+func (me *PsqlDB) FindUserPostsByTag(page *db.Pager, tag, userID, space string) (*db.Paginate[*db.Post], error) {
83 var posts []*db.Post
84- rs, err := me.Db.Query(sqlSelectUserPostsByTag, userID, tag, space)
85+ rs, err := me.Db.Query(
86+ sqlSelectUserPostsByTag,
87+ userID,
88+ tag,
89+ space,
90+ page.Num,
91+ page.Num*page.Page,
92+ )
93 if err != nil {
94- return posts, err
95+ return nil, err
96 }
97 for rs.Next() {
98 post, err := CreatePostFromRow(rs)
99@@ -881,10 +910,22 @@ func (me *PsqlDB) FindUserPostsByTag(tag, userID, space string) ([]*db.Post, err
100
101 posts = append(posts, post)
102 }
103+
104 if rs.Err() != nil {
105- return posts, rs.Err()
106+ return nil, rs.Err()
107 }
108- return posts, nil
109+
110+ var count int
111+ err = me.Db.QueryRow(sqlSelectPostCount, space).Scan(&count)
112+ if err != nil {
113+ return nil, err
114+ }
115+
116+ pager := &db.Paginate[*db.Post]{
117+ Data: posts,
118+ Total: int(math.Ceil(float64(count) / float64(page.Num))),
119+ }
120+ return pager, nil
121 }
122
123 func (me *PsqlDB) FindPostsByTag(pager *db.Pager, tag, space string) (*db.Paginate[*db.Post], error) {
+7,
-3
1@@ -115,11 +115,14 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
2
3 tag := r.URL.Query().Get("tag")
4 var posts []*db.Post
5+ var p *db.Paginate[*db.Post]
6+ pager := &db.Pager{Num: 1000, Page: 0}
7 if tag == "" {
8- posts, err = dbpool.FindPostsForUser(user.ID, cfg.Space)
9+ p, err = dbpool.FindPostsForUser(pager, user.ID, cfg.Space)
10 } else {
11- posts, err = dbpool.FindUserPostsByTag(tag, user.ID, cfg.Space)
12+ p, err = dbpool.FindUserPostsByTag(pager, tag, user.ID, cfg.Space)
13 }
14+ posts = p.Data
15
16 if err != nil {
17 logger.Error(err)
18@@ -379,7 +382,8 @@ func rssBlogHandler(w http.ResponseWriter, r *http.Request) {
19 return
20 }
21
22- posts, err := dbpool.FindPostsForUser(user.ID, cfg.Space)
23+ pager, err := dbpool.FindPostsForUser(&db.Pager{Num: 10, Page: 0}, user.ID, cfg.Space)
24+ posts := pager.Data
25
26 if err != nil {
27 logger.Error(err)
+48,
-46
1@@ -93,11 +93,14 @@ func getPostsForUser(r *http.Request, user *db.User, tag string) ([]*db.Post, er
2 var err error
3
4 posts := make([]*db.Post, 0)
5+ pager := &db.Pager{Num: 1000, Page: 0}
6+ var p *db.Paginate[*db.Post]
7 if tag == "" {
8- posts, err = dbpool.FindPostsForUser(user.ID, cfg.Space)
9+ p, err = dbpool.FindPostsForUser(pager, user.ID, cfg.Space)
10 } else {
11- posts, err = dbpool.FindUserPostsByTag(tag, user.ID, cfg.Space)
12+ p, err = dbpool.FindUserPostsByTag(pager, tag, user.ID, cfg.Space)
13 }
14+ posts = p.Data
15
16 if err != nil {
17 return posts, err
18@@ -156,43 +159,46 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
19 Title: GetBlogName(username),
20 Bio: "",
21 }
22+ header, err := dbpool.FindPostWithFilename("_header.txt", user.ID, cfg.Space)
23+ if err == nil {
24+ parsedText := ParseText(header.Text)
25+ if parsedText.MetaData.Title != "" {
26+ headerTxt.Title = parsedText.MetaData.Title
27+ }
28+
29+ if parsedText.MetaData.Description != "" {
30+ headerTxt.Bio = parsedText.MetaData.Description
31+ }
32+
33+ headerTxt.Nav = parsedText.Items
34+ if len(headerTxt.Nav) > 0 {
35+ headerTxt.HasItems = true
36+ }
37+ }
38+
39 readmeTxt := &ReadmeTxt{}
40+ readme, err := dbpool.FindPostWithFilename("_header.txt", user.ID, cfg.Space)
41+ if err == nil {
42+ parsedText := ParseText(readme.Text)
43+ readmeTxt.Items = parsedText.Items
44+ readmeTxt.ListType = parsedText.MetaData.ListType
45+ if len(readmeTxt.Items) > 0 {
46+ readmeTxt.HasItems = true
47+ }
48+ }
49
50 postCollection := make([]PostItemData, 0, len(posts))
51 for _, post := range posts {
52- if post.Filename == "_header.txt" {
53- parsedText := ParseText(post.Text)
54- if parsedText.MetaData.Title != "" {
55- headerTxt.Title = parsedText.MetaData.Title
56- }
57-
58- if parsedText.MetaData.Description != "" {
59- headerTxt.Bio = parsedText.MetaData.Description
60- }
61-
62- headerTxt.Nav = parsedText.Items
63- if len(headerTxt.Nav) > 0 {
64- headerTxt.HasItems = true
65- }
66- } else if post.Filename == "_readme.txt" {
67- parsedText := ParseText(post.Text)
68- readmeTxt.Items = parsedText.Items
69- readmeTxt.ListType = parsedText.MetaData.ListType
70- if len(readmeTxt.Items) > 0 {
71- readmeTxt.HasItems = true
72- }
73- } else {
74- p := PostItemData{
75- URL: template.URL(cfg.FullPostURL(post.Username, post.Slug, onSubdomain, withUserName)),
76- BlogURL: template.URL(cfg.FullBlogURL(post.Username, onSubdomain, withUserName)),
77- Title: shared.FilenameToTitle(post.Filename, post.Title),
78- PublishAt: post.PublishAt.Format("02 Jan, 2006"),
79- PublishAtISO: post.PublishAt.Format(time.RFC3339),
80- UpdatedTimeAgo: shared.TimeAgo(post.UpdatedAt),
81- UpdatedAtISO: post.UpdatedAt.Format(time.RFC3339),
82- }
83- postCollection = append(postCollection, p)
84+ p := PostItemData{
85+ URL: template.URL(cfg.FullPostURL(post.Username, post.Slug, onSubdomain, withUserName)),
86+ BlogURL: template.URL(cfg.FullBlogURL(post.Username, onSubdomain, withUserName)),
87+ Title: shared.FilenameToTitle(post.Filename, post.Title),
88+ PublishAt: post.PublishAt.Format("02 Jan, 2006"),
89+ PublishAtISO: post.PublishAt.Format(time.RFC3339),
90+ UpdatedTimeAgo: shared.TimeAgo(post.UpdatedAt),
91+ UpdatedAtISO: post.UpdatedAt.Format(time.RFC3339),
92 }
93+ postCollection = append(postCollection, p)
94 }
95
96 data := BlogPageData{
97@@ -517,19 +523,15 @@ func rssBlogHandler(w http.ResponseWriter, r *http.Request) {
98 headerTxt := &HeaderTxt{
99 Title: GetBlogName(username),
100 }
101+ header, err := dbpool.FindPostWithFilename("_header.txt", user.ID, cfg.Space)
102+ if err == nil {
103+ parsedText := ParseText(header.Text)
104+ if parsedText.MetaData.Title != "" {
105+ headerTxt.Title = parsedText.MetaData.Title
106+ }
107
108- for _, post := range posts {
109- if post.Filename == "_header.txt" {
110- parsedText := ParseText(post.Text)
111- if parsedText.MetaData.Title != "" {
112- headerTxt.Title = parsedText.MetaData.Title
113- }
114-
115- if parsedText.MetaData.Description != "" {
116- headerTxt.Bio = parsedText.MetaData.Description
117- }
118-
119- break
120+ if parsedText.MetaData.Description != "" {
121+ headerTxt.Bio = parsedText.MetaData.Description
122 }
123 }
124
+3,
-1
1@@ -86,7 +86,9 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
2 http.Error(w, "blog not found", http.StatusNotFound)
3 return
4 }
5- posts, err := dbpool.FindPostsForUser(user.ID, cfg.Space)
6+ pager, err := dbpool.FindPostsForUser(&db.Pager{Num: 1000, Page: 0}, user.ID, cfg.Space)
7+ posts := pager.Data
8+
9 if err != nil {
10 logger.Error(err)
11 http.Error(w, "could not fetch posts for blog", http.StatusInternalServerError)
+78,
-71
1@@ -154,12 +154,15 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
2 }
3
4 tag := r.URL.Query().Get("tag")
5+ pager := &db.Pager{Num: 1000, Page: 0}
6 var posts []*db.Post
7+ var p *db.Paginate[*db.Post]
8 if tag == "" {
9- posts, err = dbpool.FindPostsForUser(user.ID, cfg.Space)
10+ p, err = dbpool.FindPostsForUser(pager, user.ID, cfg.Space)
11 } else {
12- posts, err = dbpool.FindUserPostsByTag(tag, user.ID, cfg.Space)
13+ p, err = dbpool.FindUserPostsByTag(pager, tag, user.ID, cfg.Space)
14 }
15+ posts = p.Data
16
17 if err != nil {
18 logger.Error(err)
19@@ -167,16 +170,16 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
20 return
21 }
22
23+ ts, err := shared.RenderTemplate(cfg, []string{
24+ cfg.StaticPath("html/blog.page.tmpl"),
25+ })
26+
27 hostDomain := strings.Split(r.Host, ":")[0]
28 appDomain := strings.Split(cfg.ConfigCms.Domain, ":")[0]
29
30 onSubdomain := cfg.IsSubdomains() && strings.Contains(hostDomain, appDomain)
31 withUserName := (!onSubdomain && hostDomain == appDomain) || !cfg.IsCustomdomains()
32
33- ts, err := shared.RenderTemplate(cfg, []string{
34- cfg.StaticPath("html/blog.page.tmpl"),
35- })
36-
37 if err != nil {
38 logger.Error(err)
39 http.Error(w, err.Error(), http.StatusInternalServerError)
40@@ -189,57 +192,61 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
41 }
42 readmeTxt := &ReadmeTxt{}
43
44- hasCSS := false
45- postCollection := make([]PostItemData, 0, len(posts))
46- for _, post := range posts {
47- if post.Filename == "_styles.css" && len(post.Text) > 0 {
48- hasCSS = true
49- } else if post.Filename == "_readme.md" {
50- parsedText, err := shared.ParseText(post.Text, imgs.ImgBaseURL(post.Username))
51- if err != nil {
52- logger.Error(err)
53- }
54- headerTxt.Bio = parsedText.Description
55- if parsedText.Title != "" {
56- headerTxt.Title = parsedText.Title
57- }
58+ readme, err := dbpool.FindPostWithFilename("_readme.md", user.ID, cfg.Space)
59+ if err == nil {
60+ parsedText, err := shared.ParseText(readme.Text, imgs.ImgBaseURL(readme.Username))
61+ if err != nil {
62+ logger.Error(err)
63+ }
64+ headerTxt.Bio = parsedText.Description
65+ if parsedText.Title != "" {
66+ headerTxt.Title = parsedText.Title
67+ }
68
69- headerTxt.Nav = []shared.Link{}
70- for _, nav := range parsedText.Nav {
71- u, _ := url.Parse(nav.URL)
72- finURL := nav.URL
73- if !u.IsAbs() {
74- finURL = cfg.FullPostURL(
75- post.Username,
76- nav.URL,
77- onSubdomain,
78- withUserName,
79- )
80- }
81- headerTxt.Nav = append(headerTxt.Nav, shared.Link{
82- URL: finURL,
83- Text: nav.Text,
84- })
85+ headerTxt.Nav = []shared.Link{}
86+ for _, nav := range parsedText.Nav {
87+ u, _ := url.Parse(nav.URL)
88+ finURL := nav.URL
89+ if !u.IsAbs() {
90+ finURL = cfg.FullPostURL(
91+ readme.Username,
92+ nav.URL,
93+ onSubdomain,
94+ withUserName,
95+ )
96 }
97+ headerTxt.Nav = append(headerTxt.Nav, shared.Link{
98+ URL: finURL,
99+ Text: nav.Text,
100+ })
101+ }
102
103- readmeTxt.Contents = template.HTML(parsedText.Html)
104- if len(readmeTxt.Contents) > 0 {
105- readmeTxt.HasText = true
106- }
107- } else {
108- p := PostItemData{
109- URL: template.URL(cfg.FullPostURL(post.Username, post.Slug, onSubdomain, withUserName)),
110- BlogURL: template.URL(cfg.FullBlogURL(post.Username, onSubdomain, withUserName)),
111- Title: shared.FilenameToTitle(post.Filename, post.Title),
112- PublishAt: post.PublishAt.Format("02 Jan, 2006"),
113- PublishAtISO: post.PublishAt.Format(time.RFC3339),
114- UpdatedTimeAgo: shared.TimeAgo(post.UpdatedAt),
115- UpdatedAtISO: post.UpdatedAt.Format(time.RFC3339),
116- }
117- postCollection = append(postCollection, p)
118+ readmeTxt.Contents = template.HTML(parsedText.Html)
119+ if len(readmeTxt.Contents) > 0 {
120+ readmeTxt.HasText = true
121 }
122 }
123
124+ hasCSS := false
125+ _, err = dbpool.FindPostWithFilename("_styles.css", user.ID, cfg.Space)
126+ if err == nil {
127+ hasCSS = true
128+ }
129+
130+ postCollection := make([]PostItemData, 0, len(posts))
131+ for _, post := range posts {
132+ p := PostItemData{
133+ URL: template.URL(cfg.FullPostURL(post.Username, post.Slug, onSubdomain, withUserName)),
134+ BlogURL: template.URL(cfg.FullBlogURL(post.Username, onSubdomain, withUserName)),
135+ Title: shared.FilenameToTitle(post.Filename, post.Title),
136+ PublishAt: post.PublishAt.Format("02 Jan, 2006"),
137+ PublishAtISO: post.PublishAt.Format(time.RFC3339),
138+ UpdatedTimeAgo: shared.TimeAgo(post.UpdatedAt),
139+ UpdatedAtISO: post.UpdatedAt.Format(time.RFC3339),
140+ }
141+ postCollection = append(postCollection, p)
142+ }
143+
144 data := BlogPageData{
145 Site: *cfg.GetSiteData(),
146 PageTitle: headerTxt.Title,
147@@ -541,12 +548,15 @@ func rssBlogHandler(w http.ResponseWriter, r *http.Request) {
148 }
149
150 tag := r.URL.Query().Get("tag")
151+ pager := &db.Pager{Num: 10, Page: 0}
152 var posts []*db.Post
153+ var p *db.Paginate[*db.Post]
154 if tag == "" {
155- posts, err = dbpool.FindPostsForUser(user.ID, cfg.Space)
156+ p, err = dbpool.FindPostsForUser(pager, user.ID, cfg.Space)
157 } else {
158- posts, err = dbpool.FindUserPostsByTag(tag, user.ID, cfg.Space)
159+ p, err = dbpool.FindUserPostsByTag(pager, tag, user.ID, cfg.Space)
160 }
161+ posts = p.Data
162
163 if err != nil {
164 logger.Error(err)
165@@ -565,30 +575,27 @@ func rssBlogHandler(w http.ResponseWriter, r *http.Request) {
166 Title: GetBlogName(username),
167 }
168
169+ readme, err := dbpool.FindPostWithFilename("_readme.md", user.ID, cfg.Space)
170+ if err == nil {
171+ parsedText, err := shared.ParseText(readme.Text, imgs.ImgBaseURL(readme.Username))
172+ if err != nil {
173+ logger.Error(err)
174+ }
175+ if parsedText.Title != "" {
176+ headerTxt.Title = parsedText.Title
177+ }
178+
179+ if parsedText.Description != "" {
180+ headerTxt.Bio = parsedText.Description
181+ }
182+ }
183+
184 hostDomain := strings.Split(r.Host, ":")[0]
185 appDomain := strings.Split(cfg.ConfigCms.Domain, ":")[0]
186
187 onSubdomain := cfg.IsSubdomains() && strings.Contains(hostDomain, appDomain)
188 withUserName := (!onSubdomain && hostDomain == appDomain) || !cfg.IsCustomdomains()
189
190- for _, post := range posts {
191- if post.Filename == "_readme.md" {
192- parsedText, err := shared.ParseText(post.Text, imgs.ImgBaseURL(post.Username))
193- if err != nil {
194- logger.Error(err)
195- }
196- if parsedText.Title != "" {
197- headerTxt.Title = parsedText.Title
198- }
199-
200- if parsedText.Description != "" {
201- headerTxt.Bio = parsedText.Description
202- }
203-
204- break
205- }
206- }
207-
208 feed := &feeds.Feed{
209 Title: headerTxt.Title,
210 Link: &feeds.Link{Href: cfg.FullBlogURL(username, onSubdomain, withUserName)},