aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/server/app.go16
-rw-r--r--cmd/server/feed.go48
-rw-r--r--cmd/server/handlers.go17
-rw-r--r--cmd/server/main.go38
4 files changed, 93 insertions, 26 deletions
diff --git a/cmd/server/app.go b/cmd/server/app.go
index 743a389..290b44a 100644
--- a/cmd/server/app.go
+++ b/cmd/server/app.go
@@ -1,8 +1,10 @@
package main
import (
+ "fmt"
"os"
+ "github.com/gomodule/redigo/redis"
"gopkg.in/yaml.v3"
"riedstra.dev/mitch/go-website/page"
)
@@ -10,6 +12,8 @@ import (
var FeedPrefixDefault = ".feeds"
type App struct {
+ redisPool *redis.Pool
+
ReIndexPath string
StaticDirectory string
BaseTemplate string
@@ -21,7 +25,7 @@ type App struct {
Description string // aka, "subtitle"
Author Author
SiteURL string
- FeedId string
+ FeedId string //nolint
Updated page.PageTime
FeedPrefix string
}
@@ -29,28 +33,34 @@ type App struct {
func loadConf(fn string) (*App, error) {
fh, err := os.Open(fn)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("loading config: %w", err)
}
+
dec := yaml.NewDecoder(fh)
app := &App{}
+
err = dec.Decode(app)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("decoding yaml: %w", err)
}
if app.StaticDirectory == "" {
app.StaticDirectory = "static"
}
+
if app.FeedPrefix == "" {
app.FeedPrefix = FeedPrefixDefault
}
+
if app.BaseTemplate != "" {
page.BaseTemplate = app.BaseTemplate
}
+
if app.DocumentSplit != "" {
page.DocumentSplit = app.DocumentSplit
}
+
if app.Suffix != "" {
page.Suffix = app.Suffix
}
diff --git a/cmd/server/feed.go b/cmd/server/feed.go
index c478365..880eeb4 100644
--- a/cmd/server/feed.go
+++ b/cmd/server/feed.go
@@ -16,8 +16,8 @@ import (
)
type Author struct {
- Name string `xml:"name"` // Required
- Uri string `xml:"uri,omitempty"`
+ Name string `xml:"name"` // Required
+ Uri string `xml:"uri,omitempty"` //nolint:golint,stylecheck
Email string `xml:"email,omitempty"`
}
@@ -37,7 +37,7 @@ type Content struct {
type Entry struct {
// Spec requires this, autogenerated from Title and updated if otherwise
// left empty
- Id string `xml:"id"`
+ Id string `xml:"id"` //nolint:golint,stylecheck
Title string `xml:"title"` // Required
Updated *time.Time `xml:"updated"` // Required
@@ -54,12 +54,13 @@ func (i Entry) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if i.Title == "" {
errs = append(errs, "Title Cannot be empty")
}
+
if i.Updated == nil {
errs = append(errs, "Updated cannot be nil")
}
if len(errs) > 0 {
- return errors.New(strings.Join(errs, ","))
+ return errors.New(strings.Join(errs, ",")) //nolint:goerr113
}
if i.Id == "" {
@@ -71,6 +72,7 @@ func (i Entry) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.EncodeElement(i2, start)
}
+//nolint:stylecheck,golint
type Atom struct {
Ns string `xml:"xmlns,attr"`
Title string `xml:"title"` // Required
@@ -87,18 +89,21 @@ func (a Atom) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
a.Ns = "http://www.w3.org/2005/Atom"
errs := []string{}
+
if a.Id == "" {
errs = append(errs, "ID Cannot be empty")
}
+
if a.Author.Name == "" {
errs = append(errs, "Author Name cannot be empty")
}
+
if a.Updated == nil {
errs = append(errs, "Updated cannot be empty")
}
if len(errs) > 0 {
- return errors.New(strings.Join(errs, ","))
+ return errors.New(strings.Join(errs, ",")) //nolint:goerr113
}
start.Name = xml.Name{Local: "feed"}
@@ -114,12 +119,15 @@ func (a Atom) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
// Relevant query parameters are:
//
// "content" if unset, or set to false content is omitted from the feed
-// "limit=n" stop at "n" and return the feed
+// "limit=n" stop at "n" and return the feed.
//
-func (a *App) FeedHandler(w http.ResponseWriter, r *http.Request) {
+func (a *App) FeedHandler(w http.ResponseWriter, r *http.Request) { //nolint:funlen
vars := mux.Vars(r)
- var addContent bool
- var limit int
+
+ var (
+ addContent bool
+ limit int
+ )
if _, ok := r.URL.Query()["content"]; ok {
if r.URL.Query().Get("content") != "false" {
@@ -137,20 +145,24 @@ func (a *App) FeedHandler(w http.ResponseWriter, r *http.Request) {
tag, ok := vars["tag"]
if !ok {
http.Error(w, "Tag not found or supplied", http.StatusNotFound)
+
return
}
p := page.NewPage("index")
+
index, err := p.Index()
if err != nil {
log.Println(err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
+
return
}
pages, ok := index[tag]
if !ok {
http.Error(w, "Invalid tag", http.StatusNotFound)
+
return
}
@@ -158,6 +170,7 @@ func (a *App) FeedHandler(w http.ResponseWriter, r *http.Request) {
for _, p := range dateless {
log.Printf("Warning, page %s has no Date field. Skipping inclusion on feed", p)
}
+
pages.SortDate()
feed := &Atom{
@@ -180,22 +193,24 @@ func (a *App) FeedHandler(w http.ResponseWriter, r *http.Request) {
}
content := &bytes.Buffer{}
+
err := p.Render(content)
if err != nil {
log.Println(err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
+
return
}
entry := Entry{
Title: p.Title,
Updated: &p.Date.Time,
- Links: []Link{Link{Href: strings.Join([]string{a.SiteURL, p.Path()}, "/")}},
+ Links: []Link{{Href: strings.Join([]string{a.SiteURL, p.Path()}, "/")}},
}
if p.AuthorName != "" {
entry.Author = &Author{
- Name: p.AuthorName,
+ Name: p.AuthorName,
}
if p.AuthorEmail != "" {
entry.Author.Email = p.AuthorEmail
@@ -207,13 +222,18 @@ func (a *App) FeedHandler(w http.ResponseWriter, r *http.Request) {
}
entries = append(entries, entry)
-
}
feed.Entries = entries
w.Header().Add("Content-type", "application/xml")
- w.Write([]byte(xml.Header))
+
+ _, err = w.Write([]byte(xml.Header))
+ if err != nil {
+ log.Println("Writing xml: ", err)
+
+ return
+ }
enc := xml.NewEncoder(w)
enc.Indent("", " ")
@@ -224,6 +244,4 @@ func (a *App) FeedHandler(w http.ResponseWriter, r *http.Request) {
// Headers probably already sent, but we'll try anyway
http.Error(w, "Internal server error", http.StatusInternalServerError)
}
-
- return
}
diff --git a/cmd/server/handlers.go b/cmd/server/handlers.go
index cb63774..869e5ba 100644
--- a/cmd/server/handlers.go
+++ b/cmd/server/handlers.go
@@ -7,14 +7,23 @@ import (
"github.com/gorilla/mux"
"riedstra.dev/mitch/go-website/page"
+ "riedstra.dev/mitch/go-website/rediscache"
)
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
rtr := mux.NewRouter()
rtr.HandleFunc(a.ReIndexPath, a.RebuildIndexHandler)
rtr.PathPrefix("/static/").Handler(a.StaticHandler())
- rtr.PathPrefix(fmt.Sprintf("/%s/{tag}", a.FeedPrefix)).HandlerFunc(a.FeedHandler)
- rtr.PathPrefix("/").HandlerFunc(a.PageHandler)
+ rtr.PathPrefix(fmt.Sprintf("/%s/{tag}", a.FeedPrefix)).HandlerFunc(
+ a.FeedHandler)
+
+ if a.redisPool != nil {
+ rtr.PathPrefix("/").Handler(rediscache.Handle(
+ a.redisPool, http.HandlerFunc(a.PageHandler)))
+ } else {
+ rtr.PathPrefix("/").Handler(http.HandlerFunc(a.PageHandler))
+ }
+
rtr.ServeHTTP(w, r)
}
@@ -23,6 +32,7 @@ func (a *App) PageHandler(w http.ResponseWriter, r *http.Request) {
if u == "/" {
u = "/index"
}
+
u = filepath.Join(".", u)
page.RenderForPath(w, r, u)
@@ -33,6 +43,7 @@ func (a *App) RebuildIndexHandler(w http.ResponseWriter, r *http.Request) {
if u == "/" {
u = "/index"
}
+
u = filepath.Join(".", u)
p := page.NewPage("index")
@@ -44,7 +55,7 @@ func (a *App) RebuildIndexHandler(w http.ResponseWriter, r *http.Request) {
}
// StaticHandler simply returns a HTTP handler that looks at the current
-// directory and exposes `static` via HTTP `/static`
+// directory and exposes `static` via HTTP `/static`.
func (a *App) StaticHandler() http.Handler {
return http.StripPrefix("/static/", http.FileServer(http.Dir(a.StaticDirectory)))
}
diff --git a/cmd/server/main.go b/cmd/server/main.go
index 00aecbf..d76f938 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -3,12 +3,13 @@ package main
import (
"flag"
"fmt"
- "gopkg.in/yaml.v3"
"log"
"net/http"
"os"
"time"
+ "github.com/gomodule/redigo/redis"
+ "gopkg.in/yaml.v3"
"riedstra.dev/mitch/go-website/page"
)
@@ -19,17 +20,28 @@ func VersionPrint() {
os.Exit(0)
}
-func main() {
+func main() { //nolint:funlen
fl := flag.NewFlagSet("Website", flag.ExitOnError)
listen := fl.String("l", "0.0.0.0:8001", "Listening address")
directory := fl.String("d", ".", "Directory to serve.")
version := fl.Bool("v", false, "Print the version then exit")
confFn := fl.String("c", "conf.yml", "Location for the config file")
verbose := fl.Bool("V", false, "Be more verbose ( dump config, etc ) ")
- fl.StringVar(&page.TimeFormat, "T", page.TimeFormat, "Set the page time format, be careful with this")
+ fl.StringVar(&page.TimeFormat, "T", page.TimeFormat,
+ "Set the page time format, be careful with this")
+
defaultIndexPath := "/reIndex"
+
indexPath := fl.String("i", defaultIndexPath,
"Path in which, when called will rebuild the index and clear the cache")
+ redisAddr := fl.String("r", "127.0.0.1:6379",
+ "Redis server set to \"\" to disable")
+
+ pageTimeout := fl.Int("timeout", 15, "Seconds until page timeout for read and write")
+
+ fl.BoolVar(&page.CacheIndex, "cache-index", true,
+ "If set to false do not cache index")
+
_ = fl.Parse(os.Args[1:])
if *version {
@@ -43,6 +55,7 @@ func main() {
app, err := loadConf(*confFn)
if err != nil {
log.Println(err)
+
app = &App{}
}
@@ -50,6 +63,21 @@ func main() {
app.ReIndexPath = *indexPath
}
+ if *redisAddr != "" {
+ app.redisPool = &redis.Pool{
+ MaxIdle: 80, //nolint:gomnd
+ MaxActive: 12000, //nolint:gomnd
+ Dial: func() (redis.Conn, error) {
+ c, err := redis.Dial("tcp", *redisAddr)
+ if err != nil {
+ log.Println("Redis dial error: ", err)
+ }
+
+ return c, err //nolint
+ },
+ }
+ }
+
if *verbose {
b, _ := yaml.Marshal(app)
os.Stderr.Write(b)
@@ -58,8 +86,8 @@ func main() {
srv := &http.Server{
Handler: app,
Addr: *listen,
- WriteTimeout: 15 * time.Second,
- ReadTimeout: 15 * time.Second,
+ WriteTimeout: time.Duration(*pageTimeout) * time.Second,
+ ReadTimeout: time.Duration(*pageTimeout) * time.Second,
}
log.Fatal(srv.ListenAndServe())
}