Add RSS feed generator
This commit is contained in:
parent
b4ca8853a9
commit
99f8c1188c
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
runDir = "/tmp/mediocre-blog/run";
|
runDir = "/tmp/mediocre-blog/run";
|
||||||
dataDir = "/tmp/mediocre-blog/data";
|
dataDir = "/tmp/mediocre-blog/data";
|
||||||
|
publicURL = "http://localhost:4000";
|
||||||
|
|
||||||
powSecret = "ssshhh";
|
powSecret = "ssshhh";
|
||||||
mlSMTPAddr = "";
|
mlSMTPAddr = "";
|
||||||
mlSMTPAuth = "";
|
mlSMTPAuth = "";
|
||||||
mlPublicURL = "http://localhost:4000";
|
|
||||||
httpListenProto = "tcp";
|
httpListenProto = "tcp";
|
||||||
httpListenAddr = ":4000";
|
httpListenAddr = ":4000";
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
# mailing list
|
# mailing list
|
||||||
export MEDIOCRE_BLOG_ML_SMTP_ADDR="${config.mlSMTPAddr}"
|
export MEDIOCRE_BLOG_ML_SMTP_ADDR="${config.mlSMTPAddr}"
|
||||||
export MEDIOCRE_BLOG_ML_SMTP_AUTH="${config.mlSMTPAuth}"
|
export MEDIOCRE_BLOG_ML_SMTP_AUTH="${config.mlSMTPAuth}"
|
||||||
export MEDIOCRE_BLOG_ML_PUBLIC_URL="${config.mlPublicURL}"
|
export MEDIOCRE_BLOG_ML_PUBLIC_URL="${config.publicURL}"
|
||||||
|
|
||||||
# redis
|
# redis
|
||||||
export MEDIOCRE_BLOG_REDIS_PROTO=unix
|
export MEDIOCRE_BLOG_REDIS_PROTO=unix
|
||||||
@ -26,8 +26,9 @@
|
|||||||
export MEDIOCRE_BLOG_POW_SECRET="${config.powSecret}"
|
export MEDIOCRE_BLOG_POW_SECRET="${config.powSecret}"
|
||||||
|
|
||||||
# http
|
# http
|
||||||
export MEDIOCRE_BLOG_LISTEN_PROTO="${config.httpListenProto}"
|
export MEDIOCRE_BLOG_HTTP_PUBLIC_URL="${config.publicURL}"
|
||||||
export MEDIOCRE_BLOG_LISTEN_ADDR="${config.httpListenAddr}"
|
export MEDIOCRE_BLOG_HTTP_LISTEN_PROTO="${config.httpListenProto}"
|
||||||
|
export MEDIOCRE_BLOG_HTTP_LISTEN_ADDR="${config.httpListenAddr}"
|
||||||
export MEDIOCRE_BLOG_HTTP_AUTH_USERS='${builtins.toJSON config.httpAuthUsers}'
|
export MEDIOCRE_BLOG_HTTP_AUTH_USERS='${builtins.toJSON config.httpAuthUsers}'
|
||||||
export MEDIOCRE_BLOG_HTTP_AUTH_RATELIMIT='${config.httpAuthRatelimit}'
|
export MEDIOCRE_BLOG_HTTP_AUTH_RATELIMIT='${config.httpAuthRatelimit}'
|
||||||
'';
|
'';
|
||||||
|
@ -10,7 +10,9 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/cfg"
|
||||||
@ -40,6 +42,9 @@ type Params struct {
|
|||||||
GlobalRoom chat.Room
|
GlobalRoom chat.Room
|
||||||
UserIDCalculator *chat.UserIDCalculator
|
UserIDCalculator *chat.UserIDCalculator
|
||||||
|
|
||||||
|
// PublicURL is the base URL which site visitors can navigate to.
|
||||||
|
PublicURL *url.URL
|
||||||
|
|
||||||
// ListenProto and ListenAddr are passed into net.Listen to create the
|
// ListenProto and ListenAddr are passed into net.Listen to create the
|
||||||
// API's listener. Both "tcp" and "unix" protocols are explicitly
|
// API's listener. Both "tcp" and "unix" protocols are explicitly
|
||||||
// supported.
|
// supported.
|
||||||
@ -57,6 +62,9 @@ type Params struct {
|
|||||||
|
|
||||||
// SetupCfg implement the cfg.Cfger interface.
|
// SetupCfg implement the cfg.Cfger interface.
|
||||||
func (p *Params) SetupCfg(cfg *cfg.Cfg) {
|
func (p *Params) SetupCfg(cfg *cfg.Cfg) {
|
||||||
|
|
||||||
|
publicURLStr := cfg.String("http-public-url", "http://localhost:4000", "URL this service is accessible at")
|
||||||
|
|
||||||
cfg.StringVar(&p.ListenProto, "http-listen-proto", "tcp", "Protocol to listen for HTTP requests with")
|
cfg.StringVar(&p.ListenProto, "http-listen-proto", "tcp", "Protocol to listen for HTTP requests with")
|
||||||
cfg.StringVar(&p.ListenAddr, "http-listen-addr", ":4000", "Address/path to listen for HTTP requests on")
|
cfg.StringVar(&p.ListenAddr, "http-listen-addr", ":4000", "Address/path to listen for HTTP requests on")
|
||||||
|
|
||||||
@ -76,15 +84,21 @@ func (p *Params) SetupCfg(cfg *cfg.Cfg) {
|
|||||||
return fmt.Errorf("unmarshaling -http-auth-ratelimit: %w", err)
|
return fmt.Errorf("unmarshaling -http-auth-ratelimit: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*publicURLStr = strings.TrimSuffix(*publicURLStr, "/")
|
||||||
|
if p.PublicURL, err = url.Parse(*publicURLStr); err != nil {
|
||||||
|
return fmt.Errorf("parsing -http-public-url: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Annotate implements mctx.Annotator interface.
|
// Annotate implements mctx.Annotator interface.
|
||||||
func (p *Params) Annotate(a mctx.Annotations) {
|
func (p *Params) Annotate(a mctx.Annotations) {
|
||||||
a["listenProto"] = p.ListenProto
|
a["httpPublicURL"] = p.PublicURL
|
||||||
a["listenAddr"] = p.ListenAddr
|
a["httpListenProto"] = p.ListenProto
|
||||||
a["authRatelimit"] = p.AuthRatelimit
|
a["httpListenAddr"] = p.ListenAddr
|
||||||
|
a["httpAuthRatelimit"] = p.AuthRatelimit
|
||||||
}
|
}
|
||||||
|
|
||||||
// API will listen on the port configured for it, and serve HTTP requests for
|
// API will listen on the port configured for it, and serve HTTP requests for
|
||||||
@ -218,6 +232,7 @@ func (a *api) handler() http.Handler {
|
|||||||
|
|
||||||
mux.Handle("/static/", http.FileServer(http.FS(staticFS)))
|
mux.Handle("/static/", http.FileServer(http.FS(staticFS)))
|
||||||
mux.Handle("/follow", a.renderDumbTplHandler("follow.html"))
|
mux.Handle("/follow", a.renderDumbTplHandler("follow.html"))
|
||||||
|
mux.Handle("/feed.xml", a.renderFeedHandler())
|
||||||
mux.Handle("/", a.renderIndexHandler())
|
mux.Handle("/", a.renderIndexHandler())
|
||||||
|
|
||||||
var globalHandler http.Handler = mux
|
var globalHandler http.Handler = mux
|
||||||
|
63
srv/src/http/feed.go
Normal file
63
srv/src/http/feed.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/gorilla/feeds"
|
||||||
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/http/apiutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *api) renderFeedHandler() http.Handler {
|
||||||
|
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
author := &feeds.Author{
|
||||||
|
Name: "mediocregopher",
|
||||||
|
}
|
||||||
|
|
||||||
|
publicURL := a.params.PublicURL.String()
|
||||||
|
|
||||||
|
feed := feeds.Feed{
|
||||||
|
Title: "Mediocre Blog",
|
||||||
|
Link: &feeds.Link{Href: publicURL + "/"},
|
||||||
|
Description: "A mix of tech, art, travel, and who knows what else.",
|
||||||
|
Author: author,
|
||||||
|
}
|
||||||
|
|
||||||
|
recentPosts, _, err := a.params.PostStore.WithOrderDesc().Get(0, 20)
|
||||||
|
if err != nil {
|
||||||
|
apiutil.InternalServerError(rw, r, fmt.Errorf("fetching recent posts: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, post := range recentPosts {
|
||||||
|
|
||||||
|
if post.PublishedAt.After(feed.Updated) {
|
||||||
|
feed.Updated = post.PublishedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
if post.LastUpdatedAt.After(feed.Updated) {
|
||||||
|
feed.Updated = post.LastUpdatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
postURL := publicURL + filepath.Join("/posts", post.ID)
|
||||||
|
|
||||||
|
feed.Items = append(feed.Items, &feeds.Item{
|
||||||
|
Title: post.Title,
|
||||||
|
Link: &feeds.Link{Href: postURL},
|
||||||
|
Author: author,
|
||||||
|
Description: post.Description,
|
||||||
|
Id: postURL,
|
||||||
|
Updated: post.LastUpdatedAt,
|
||||||
|
Created: post.PublishedAt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := feed.WriteAtom(rw); err != nil {
|
||||||
|
apiutil.InternalServerError(rw, r, fmt.Errorf("writing atom feed: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -57,8 +57,9 @@ func (p *Params) SetupCfg(cfg *cfg.Cfg) {
|
|||||||
|
|
||||||
cfg.OnInit(func(ctx context.Context) error {
|
cfg.OnInit(func(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
|
*publicURLStr = strings.TrimSuffix(*publicURLStr, "/")
|
||||||
if p.PublicURL, err = url.Parse(*publicURLStr); err != nil {
|
if p.PublicURL, err = url.Parse(*publicURLStr); err != nil {
|
||||||
return fmt.Errorf("parsing -public-url: %w", err)
|
return fmt.Errorf("parsing -ml-public-url: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -67,7 +68,7 @@ func (p *Params) SetupCfg(cfg *cfg.Cfg) {
|
|||||||
|
|
||||||
// Annotate implements mctx.Annotator interface.
|
// Annotate implements mctx.Annotator interface.
|
||||||
func (p *Params) Annotate(a mctx.Annotations) {
|
func (p *Params) Annotate(a mctx.Annotations) {
|
||||||
a["publicURL"] = p.PublicURL
|
a["mlPublicURL"] = p.PublicURL
|
||||||
}
|
}
|
||||||
|
|
||||||
// New initializes and returns a MailingList instance using the given Params.
|
// New initializes and returns a MailingList instance using the given Params.
|
||||||
|
Loading…
Reference in New Issue
Block a user