Publish new posts to mailing list
This commit is contained in:
parent
3e67823205
commit
3059909deb
@ -131,11 +131,17 @@ func (a *api) renderPostHandler() http.Handler {
|
|||||||
|
|
||||||
func (a *api) renderPostsIndexHandler() http.Handler {
|
func (a *api) renderPostsIndexHandler() http.Handler {
|
||||||
|
|
||||||
|
renderEditPostHandler := a.renderEditPostHandler()
|
||||||
tpl := a.mustParseBasedTpl("posts.html")
|
tpl := a.mustParseBasedTpl("posts.html")
|
||||||
const pageCount = 20
|
const pageCount = 20
|
||||||
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
if _, ok := r.URL.Query()["edit"]; ok {
|
||||||
|
renderEditPostHandler.ServeHTTP(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
page, err := apiutil.StrToInt(r.FormValue("p"), 0)
|
page, err := apiutil.StrToInt(r.FormValue("p"), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiutil.BadRequest(
|
apiutil.BadRequest(
|
||||||
@ -239,13 +245,28 @@ func (a *api) postPostHandler() http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := a.params.PostStore.Set(p, time.Now()); err != nil {
|
first, err := a.params.PostStore.Set(p, time.Now())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
apiutil.InternalServerError(
|
apiutil.InternalServerError(
|
||||||
rw, r, fmt.Errorf("storing post with id %q: %w", p.ID, err),
|
rw, r, fmt.Errorf("storing post with id %q: %w", p.ID, err),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
|
||||||
|
a.params.Logger.Info(r.Context(), "publishing blog post to mailing list")
|
||||||
|
urlStr := a.params.PublicURL.String() + filepath.Join("/posts", p.ID)
|
||||||
|
|
||||||
|
if err := a.params.MailingList.Publish(p.Title, urlStr); err != nil {
|
||||||
|
apiutil.InternalServerError(
|
||||||
|
rw, r, fmt.Errorf("publishing post with id %q: %w", p.ID, err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
redirectPath := fmt.Sprintf("posts/%s?edit", p.ID)
|
redirectPath := fmt.Sprintf("posts/%s?edit", p.ID)
|
||||||
|
|
||||||
a.executeRedirectTpl(rw, r, redirectPath)
|
a.executeRedirectTpl(rw, r, redirectPath)
|
||||||
|
@ -48,9 +48,10 @@ type StoredPost struct {
|
|||||||
// Store is used for storing posts to a persistent storage.
|
// Store is used for storing posts to a persistent storage.
|
||||||
type Store interface {
|
type Store interface {
|
||||||
|
|
||||||
// Set sets the Post data into the storage, keyed by the Post's ID. It
|
// Set sets the Post data into the storage, keyed by the Post's ID. If there
|
||||||
// overwrites a previous Post with the same ID, if there was one.
|
// was not a previously existing Post with the same ID then Set returns
|
||||||
Set(post Post, now time.Time) error
|
// true. It overwrites the previous Post with the same ID otherwise.
|
||||||
|
Set(post Post, now time.Time) (bool, error)
|
||||||
|
|
||||||
// Get returns count StoredPosts, sorted time descending, offset by the
|
// Get returns count StoredPosts, sorted time descending, offset by the
|
||||||
// given page number. The returned boolean indicates if there are more pages
|
// given page number. The returned boolean indicates if there are more pages
|
||||||
@ -114,13 +115,15 @@ func (s *store) withTx(cb func(*sql.Tx) error) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *store) Set(post Post, now time.Time) error {
|
func (s *store) Set(post Post, now time.Time) (bool, error) {
|
||||||
|
|
||||||
if post.ID == "" {
|
if post.ID == "" {
|
||||||
return errors.New("post ID can't be empty")
|
return false, errors.New("post ID can't be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.withTx(func(tx *sql.Tx) error {
|
var first bool
|
||||||
|
|
||||||
|
err := s.withTx(func(tx *sql.Tx) error {
|
||||||
|
|
||||||
nowTS := now.Unix()
|
nowTS := now.Unix()
|
||||||
|
|
||||||
@ -173,8 +176,17 @@ func (s *store) Set(post Post, now time.Time) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = tx.QueryRow(
|
||||||
|
`SELECT 1 FROM posts WHERE id=? AND last_updated_at IS NULL`,
|
||||||
|
post.ID,
|
||||||
|
).Scan(new(int))
|
||||||
|
|
||||||
|
first = !errors.Is(err, sql.ErrNoRows)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return first, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *store) get(
|
func (s *store) get(
|
||||||
|
@ -108,7 +108,9 @@ func TestStore(t *testing.T) {
|
|||||||
post := testPost(0)
|
post := testPost(0)
|
||||||
post.Tags = []string{"foo", "bar"}
|
post.Tags = []string{"foo", "bar"}
|
||||||
|
|
||||||
assert.NoError(t, h.store.Set(post, now))
|
first, err := h.store.Set(post, now)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, first)
|
||||||
|
|
||||||
gotPost, err := h.store.GetByID(post.ID)
|
gotPost, err := h.store.GetByID(post.ID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -129,7 +131,9 @@ func TestStore(t *testing.T) {
|
|||||||
post.Body = "anything"
|
post.Body = "anything"
|
||||||
post.Tags = []string{"bar", "baz"}
|
post.Tags = []string{"bar", "baz"}
|
||||||
|
|
||||||
assert.NoError(t, h.store.Set(post, newNow))
|
first, err = h.store.Set(post, newNow)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.False(t, first)
|
||||||
|
|
||||||
gotPost, err = h.store.GetByID(post.ID)
|
gotPost, err = h.store.GetByID(post.ID)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -160,7 +164,8 @@ func TestStore(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, post := range posts {
|
for _, post := range posts {
|
||||||
assert.NoError(t, h.store.Set(post.Post, now))
|
_, err := h.store.Set(post.Post, now)
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gotPosts, hasMore, err := h.store.Get(0, 2)
|
gotPosts, hasMore, err := h.store.Get(0, 2)
|
||||||
@ -174,7 +179,8 @@ func TestStore(t *testing.T) {
|
|||||||
assertPostsEqual(t, posts[2:4], gotPosts)
|
assertPostsEqual(t, posts[2:4], gotPosts)
|
||||||
|
|
||||||
posts = append([]StoredPost{h.testStoredPost(4)}, posts...)
|
posts = append([]StoredPost{h.testStoredPost(4)}, posts...)
|
||||||
assert.NoError(t, h.store.Set(posts[0].Post, now))
|
_, err = h.store.Set(posts[0].Post, now)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
gotPosts, hasMore, err = h.store.Get(1, 2)
|
gotPosts, hasMore, err = h.store.Get(1, 2)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -204,7 +210,8 @@ func TestStore(t *testing.T) {
|
|||||||
posts[2].Series = "bar"
|
posts[2].Series = "bar"
|
||||||
|
|
||||||
for _, post := range posts {
|
for _, post := range posts {
|
||||||
assert.NoError(t, h.store.Set(post.Post, now))
|
_, err := h.store.Set(post.Post, now)
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fooPosts, err := h.store.GetBySeries("foo")
|
fooPosts, err := h.store.GetBySeries("foo")
|
||||||
@ -238,7 +245,8 @@ func TestStore(t *testing.T) {
|
|||||||
posts[2].Tags = []string{"bar"}
|
posts[2].Tags = []string{"bar"}
|
||||||
|
|
||||||
for _, post := range posts {
|
for _, post := range posts {
|
||||||
assert.NoError(t, h.store.Set(post.Post, now))
|
_, err := h.store.Set(post.Post, now)
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fooPosts, err := h.store.GetByTag("foo")
|
fooPosts, err := h.store.GetByTag("foo")
|
||||||
|
Loading…
Reference in New Issue
Block a user