pico
created pr with
15.1
added 15.2
1: 866976a = 1: 866976a feat(prose): live journal
-: ------- > 2: b82b2f7 chore: diff in post
cmds
checkout latest patchset:
ssh pr.pico.sh print 15 | git am -3checkout any patchset in a patch request:
ssh pr.pico.sh print 15.[rev] | git am -3add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add 15
Patchset
15.2
feat(prose): live journal
Eric Bower
2024-08-01T14:24:47ZThe goal is to be able to use posts as live journals where reader can receive updates to docs via rss.
Semantic diff summary
0 added,
6 modified,
0 signature changed,
0 removed
across 3 analyzed files
(2 files skipped: unsupported file type)
+1
-0
db/db.go
#
+1
-0
go.mod
#
| ... | ... | @@ -37,6 +37,7 @@ require ( | |
| 37 | 37 | github.com/picosh/tunkit v0.0.0-20240709033345-8315d4f3cd0e | |
| 38 | 38 | github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 | |
| 39 | 39 | github.com/sendgrid/sendgrid-go v3.14.0+incompatible | |
| 40 | + | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 | |
| 40 | 41 | github.com/simplesurance/go-ip-anonymizer v0.0.0-20200429124537-35a880f8e87d | |
| 41 | 42 | github.com/x-way/crawlerdetect v0.2.21 | |
| 42 | 43 | github.com/yuin/goldmark v1.7.1 |
+8
-0
go.sum
#
| ... | ... | @@ -158,8 +158,11 @@ github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuV | |
| 158 | 158 | github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= | |
| 159 | 159 | github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= | |
| 160 | 160 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= | |
| 161 | + | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | |
| 161 | 162 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | |
| 162 | 163 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= | |
| 164 | + | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | |
| 165 | + | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | |
| 163 | 166 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | |
| 164 | 167 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | |
| 165 | 168 | github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= |
| ... | ... | @@ -265,6 +268,8 @@ github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekuei | |
| 265 | 268 | github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= | |
| 266 | 269 | github.com/sendgrid/sendgrid-go v3.14.0+incompatible h1:KDSasSTktAqMJCYClHVE94Fcif2i7P7wzISv1sU6DUA= | |
| 267 | 270 | github.com/sendgrid/sendgrid-go v3.14.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= | |
| 271 | + | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= | |
| 272 | + | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= | |
| 268 | 273 | github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= | |
| 269 | 274 | github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= | |
| 270 | 275 | github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= |
| ... | ... | @@ -276,6 +281,7 @@ github.com/simplesurance/go-ip-anonymizer v0.0.0-20200429124537-35a880f8e87d/go. | |
| 276 | 281 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | |
| 277 | 282 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | |
| 278 | 283 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | |
| 284 | + | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | |
| 279 | 285 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |
| 280 | 286 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |
| 281 | 287 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
| ... | ... | @@ -393,10 +399,12 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T | |
| 393 | 399 | google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= | |
| 394 | 400 | google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= | |
| 395 | 401 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |
| 402 | + | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |
| 396 | 403 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= | |
| 397 | 404 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= | |
| 398 | 405 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= | |
| 399 | 406 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | |
| 407 | + | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |
| 400 | 408 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |
| 401 | 409 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |
| 402 | 410 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= |
+24
-24
prose/api.go
#
| ... | ... | @@ -160,7 +160,7 @@ func blogHandler(w http.ResponseWriter, r *http.Request) { | |
| 160 | 160 | } | |
| 161 | 161 | ||
| 162 | 162 | tag := r.URL.Query().Get("tag") | |
| 163 | - | pager := &db.Pager{Num: 1000, Page: 0} | |
| 163 | + | pager := &db.Pager{Num: 250, Page: 0} | |
| 164 | 164 | var posts []*db.Post | |
| 165 | 165 | var p *db.Paginate[*db.Post] | |
| 166 | 166 | if tag == "" { |
| ... | ... | @@ -170,6 +170,13 @@ func blogHandler(w http.ResponseWriter, r *http.Request) { | |
| 170 | 170 | } | |
| 171 | 171 | posts = p.Data | |
| 172 | 172 | ||
| 173 | + | byUpdated := strings.Contains(r.URL.Path, "live") | |
| 174 | + | if byUpdated { | |
| 175 | + | slices.SortFunc(posts, func(a *db.Post, b *db.Post) int { | |
| 176 | + | return b.UpdatedAt.Compare(*a.UpdatedAt) | |
| 177 | + | }) | |
| 178 | + | } | |
| 179 | + | ||
| 173 | 180 | if err != nil { | |
| 174 | 181 | logger.Error(err.Error()) | |
| 175 | 182 | http.Error(w, "could not fetch posts for blog", http.StatusInternalServerError) |
| ... | ... | @@ -653,6 +660,13 @@ func rssBlogHandler(w http.ResponseWriter, r *http.Request) { | |
| 653 | 660 | curl := shared.CreateURLFromRequest(cfg, r) | |
| 654 | 661 | blogUrl := cfg.FullBlogURL(curl, username) | |
| 655 | 662 | ||
| 663 | + | byUpdated := strings.Contains(r.URL.Path, "live") | |
| 664 | + | if byUpdated { | |
| 665 | + | slices.SortFunc(posts, func(a *db.Post, b *db.Post) int { | |
| 666 | + | return b.UpdatedAt.Compare(*a.UpdatedAt) | |
| 667 | + | }) | |
| 668 | + | } | |
| 669 | + | ||
| 656 | 670 | feed := &feeds.Feed{ | |
| 657 | 671 | Id: blogUrl, | |
| 658 | 672 | Title: headerTxt.Title, |
| ... | ... | @@ -691,12 +705,18 @@ func rssBlogHandler(w http.ResponseWriter, r *http.Request) { | |
| 691 | 705 | } | |
| 692 | 706 | ||
| 693 | 707 | realUrl := cfg.FullPostURL(curl, post.Username, post.Slug) | |
| 708 | + | feedId := realUrl | |
| 709 | + | ||
| 710 | + | if byUpdated { | |
| 711 | + | feedId = fmt.Sprintf("%s:%s", realUrl, post.UpdatedAt.Format(time.RFC3339)) | |
| 712 | + | } | |
| 694 | 713 | ||
| 695 | 714 | item := &feeds.Item{ | |
| 696 | - | Id: realUrl, | |
| 715 | + | Id: feedId, | |
| 697 | 716 | Title: shared.FilenameToTitle(post.Filename, post.Title), | |
| 698 | 717 | Link: &feeds.Link{Href: realUrl}, | |
| 699 | 718 | Content: tpl.String(), | |
| 719 | + | Updated: *post.UpdatedAt, | |
| 700 | 720 | Created: *post.CreatedAt, | |
| 701 | 721 | Description: post.Description, | |
| 702 | 722 | } |
| ... | ... | @@ -849,36 +869,16 @@ func createMainRoutes(staticRoutes []shared.Route) []shared.Route { | |
| 849 | 869 | staticRoutes..., | |
| 850 | 870 | ) | |
| 851 | 871 | ||
| 852 | - | routes = append( | |
| 853 | - | routes, | |
| 854 | - | shared.NewRoute("GET", "/rss", rssHandler), | |
| 855 | - | shared.NewRoute("GET", "/rss.xml", rssHandler), | |
| 856 | - | shared.NewRoute("GET", "/atom.xml", rssHandler), | |
| 857 | - | shared.NewRoute("GET", "/feed.xml", rssHandler), | |
| 858 | - | ||
| 859 | - | shared.NewRoute("GET", "/([^/]+)", blogHandler), | |
| 860 | - | shared.NewRoute("GET", "/([^/]+)/rss", rssBlogHandler), | |
| 861 | - | shared.NewRoute("GET", "/([^/]+)/rss.xml", rssBlogHandler), | |
| 862 | - | shared.NewRoute("GET", "/([^/]+)/atom.xml", rssBlogHandler), | |
| 863 | - | shared.NewRoute("GET", "/([^/]+)/atom", rssBlogHandler), | |
| 864 | - | shared.NewRoute("GET", "/([^/]+)/blog/index.xml", rssBlogHandler), | |
| 865 | - | shared.NewRoute("GET", "/([^/]+)/feed.xml", rssBlogHandler), | |
| 866 | - | shared.NewRoute("GET", "/([^/]+)/_styles.css", blogStyleHandler), | |
| 867 | - | shared.NewRoute("GET", "/raw/([^/]+)/(.+)", postRawHandler), | |
| 868 | - | shared.NewRoute("GET", "/([^/]+)/(.+)/(.+)", imgs.ImgRequest), | |
| 869 | - | shared.NewRoute("GET", "/([^/]+)/(.+.(?:jpg|jpeg|png|gif|webp|svg))$", imgs.ImgRequest), | |
| 870 | - | shared.NewRoute("GET", "/([^/]+)/i", imgs.ImgsListHandler), | |
| 871 | - | shared.NewRoute("GET", "/([^/]+)/(.+)", postHandler), | |
| 872 | - | ) | |
| 873 | - | ||
| 874 | 872 | return routes | |
| 875 | 873 | } | |
| 876 | 874 | ||
| 877 | 875 | func createSubdomainRoutes(staticRoutes []shared.Route) []shared.Route { | |
| 878 | 876 | routes := []shared.Route{ | |
| 879 | 877 | shared.NewRoute("GET", "/", blogHandler), | |
| 878 | + | shared.NewRoute("GET", "/live", blogHandler), | |
| 880 | 879 | shared.NewRoute("GET", "/_styles.css", blogStyleHandler), | |
| 881 | 880 | shared.NewRoute("GET", "/rss", rssBlogHandler), | |
| 881 | + | shared.NewRoute("GET", "/live/rss", rssBlogHandler), | |
| 882 | 882 | shared.NewRoute("GET", "/rss.xml", rssBlogHandler), | |
| 883 | 883 | shared.NewRoute("GET", "/atom.xml", rssBlogHandler), | |
| 884 | 884 | shared.NewRoute("GET", "/feed.xml", rssBlogHandler), |
+5
-0
prose/scp_hooks.go
#
| ... | ... | @@ -62,6 +63,10 @@ func (p *MarkdownHooks) FileMeta(s ssh.Session, data *filehandlers.PostMetaData) | |
| 62 | 63 | data.Tags = parsedText.Tags | |
| 63 | 64 | data.Description = parsedText.Description | |
| 64 | 65 | ||
| 66 | + | dmp := diffmatchpatch.New() | |
| 67 | + | diffs := dmp.DiffMain(data.Cur.Text, data.Text, false) | |
| 68 | + | data.Data.Diff = dmp.DiffPrettyText(diffs) | |
| 69 | + | ||
| 65 | 70 | if parsedText.PublishAt != nil && !parsedText.PublishAt.IsZero() { | |
| 66 | 71 | data.PublishAt = parsedText.MetaData.PublishAt | |
| 67 | 72 | } |