diff options
Diffstat (limited to 'cmd/server/feed.go')
| -rw-r--r-- | cmd/server/feed.go | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/cmd/server/feed.go b/cmd/server/feed.go new file mode 100644 index 0000000..f3ccd8c --- /dev/null +++ b/cmd/server/feed.go @@ -0,0 +1,183 @@ +package main + +import ( + "bytes" + "encoding/xml" + "errors" + "fmt" + "log" + "net/http" + "strings" + "time" + + "github.com/gorilla/mux" + "riedstra.dev/mitch/go-website/page" +) + +type Author struct { + Name string `xml:"name"` // Required + Uri string `xml:"uri,omitempty"` + Email string `xml:"email,omitempty"` +} + +type Link struct { + Href string `xml:"href,attr,omitempty"` + Rel string `xml:"rel,attr,omitempty"` + Type string `xml:"type,attr,omitempty"` + Title string `xml:"Title,attr,omitempty"` + Length string `xml:"Length,attr,omitempty"` +} + +type Content struct { + Type string `xml:"type,attr"` + Data []byte `xml:"chardata"` +} + +type Entry struct { + // Spec requires this, autogenerated from Title and updated if otherwise + // left empty + Id string `xml:"id"` + + Title string `xml:"title"` // Required + Updated *time.Time `xml:"updated"` // Required + Author *Author `xml:"author,omitempty"` + Published time.Time `xml:"published,omitempty"` + Links []Link `xml:"link,omitempty"` + Content Content `xml:"content,omitempty"` +} + +func (i Entry) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + type Alias Entry + + errs := []string{} + if i.Title == "" { + errs = append(errs, "Title Cannot be empty") + } + if i.Updated == nil { + errs = append(errs, "Updated cannot be nil") + } + + if i.Id == "" { + i.Id = fmt.Sprintf("%s::%d", i.Title, i.Updated.Unix()) + } + + if len(errs) > 0 { + return errors.New(strings.Join(errs, ",")) + } + + return e.EncodeElement( + &struct { + *Alias + }{ + Alias: (*Alias)(&i), + }, start) +} + +type Atom struct { + XMLName xml.Name `xml:"feed"` + Ns string `xml:"xmlns,attr"` + Title string `xml:"title"` // Required + Id string `xml:"id"` // Required + Author Author `xml:"author,omitempty"` // Required + Updated *time.Time `xml:"updated"` // Required + Published *time.Time `xml:"published,omitempty"` + Subtitle string `xml:"subtitle,omitempty"` + Entries []Entry `xml:"entry"` +} + +func (a Atom) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + type Alias Atom + + 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 e.EncodeElement( + &struct { + *Alias + }{ + Alias: (*Alias)(&a), + }, start) +} + +func (a *App) FeedHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + 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 + } + + feed := &Atom{ + Author: a.Author, + Title: a.Title, + Id: a.FeedId, + Updated: &a.Updated.Time, + Subtitle: a.Description, + } + + entries := []Entry{} + + for _, p := range pages { + + if p.Date == nil { + log.Printf("Warning, page %s has no Date field. Skipping inclusion on feed", p) + continue + } + + content := &bytes.Buffer{} + err := p.Render(content) + if err != nil { + log.Println(err) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + + entries = append(entries, Entry{ + Title: p.Title, + Updated: &p.Date.Time, + Links: []Link{Link{Href: p.Path()}}, + Content: Content{Type: "html", Data: content.Bytes()}, + }) + } + + feed.Entries = entries + + enc := xml.NewEncoder(w) + enc.Indent("", " ") + + err = enc.Encode(feed) + if err != nil { + log.Println(err) + http.Error(w, "Internal server error", http.StatusInternalServerError) + } + + return +} |
