2016-12-14 14:20:25 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2016-12-14 19:53:26 +00:00
|
|
|
"bytes"
|
2016-12-14 14:20:25 +00:00
|
|
|
"encoding/json"
|
2016-12-14 19:53:26 +00:00
|
|
|
"io/ioutil"
|
2016-12-14 14:20:25 +00:00
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2016-12-14 16:56:15 +00:00
|
|
|
"sort"
|
2016-12-14 14:20:25 +00:00
|
|
|
"strings"
|
2016-12-14 16:56:15 +00:00
|
|
|
"time"
|
2016-12-14 14:20:25 +00:00
|
|
|
|
2017-01-09 21:41:39 +00:00
|
|
|
"github.com/Xe/asarfs"
|
2017-03-29 07:26:50 +00:00
|
|
|
"github.com/Xe/ln"
|
2016-12-14 14:20:25 +00:00
|
|
|
"github.com/gernest/front"
|
2017-03-29 07:26:50 +00:00
|
|
|
"github.com/gorilla/feeds"
|
2017-01-09 22:58:07 +00:00
|
|
|
"github.com/urfave/negroni"
|
2016-12-14 14:20:25 +00:00
|
|
|
)
|
|
|
|
|
2016-12-14 17:30:48 +00:00
|
|
|
// Post is a single post summary for the menu.
|
2016-12-14 14:20:25 +00:00
|
|
|
type Post struct {
|
|
|
|
Title string `json:"title"`
|
|
|
|
Link string `json:"link"`
|
2016-12-14 19:53:26 +00:00
|
|
|
Summary string `json:"summary,omitifempty"`
|
|
|
|
Body string `json:"body, omitifempty"`
|
2016-12-14 14:20:25 +00:00
|
|
|
Date string `json:"date"`
|
|
|
|
}
|
|
|
|
|
2016-12-14 17:30:48 +00:00
|
|
|
// Posts implements sort.Interface for a slice of Post objects.
|
2016-12-14 16:56:15 +00:00
|
|
|
type Posts []*Post
|
|
|
|
|
|
|
|
func (p Posts) Len() int { return len(p) }
|
|
|
|
func (p Posts) Less(i, j int) bool {
|
|
|
|
iDate, _ := time.Parse("2006-01-02", p[i].Date)
|
|
|
|
jDate, _ := time.Parse("2006-01-02", p[j].Date)
|
|
|
|
|
|
|
|
return iDate.Unix() < jDate.Unix()
|
|
|
|
}
|
|
|
|
func (p Posts) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
|
|
|
|
2016-12-18 16:51:32 +00:00
|
|
|
var (
|
|
|
|
posts Posts
|
|
|
|
rbody string
|
|
|
|
)
|
2016-12-14 14:20:25 +00:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
err := filepath.Walk("./blog/", func(path string, info os.FileInfo, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if info.IsDir() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
fin, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer fin.Close()
|
|
|
|
|
2016-12-14 19:53:26 +00:00
|
|
|
content, err := ioutil.ReadAll(fin)
|
|
|
|
if err != nil {
|
|
|
|
// handle error
|
|
|
|
}
|
|
|
|
|
2016-12-14 14:20:25 +00:00
|
|
|
m := front.NewMatter()
|
|
|
|
m.Handle("---", front.YAMLHandler)
|
2016-12-14 19:53:26 +00:00
|
|
|
front, _, err := m.Parse(bytes.NewReader(content))
|
2016-12-14 14:20:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-12-14 19:53:26 +00:00
|
|
|
sp := strings.Split(string(content), "\n")
|
|
|
|
sp = sp[4:]
|
|
|
|
data := strings.Join(sp, "\n")
|
|
|
|
|
2016-12-14 14:20:25 +00:00
|
|
|
p := &Post{
|
|
|
|
Title: front["title"].(string),
|
|
|
|
Date: front["date"].(string),
|
|
|
|
Link: strings.Split(path, ".")[0],
|
2016-12-14 19:53:26 +00:00
|
|
|
Body: data,
|
2016-12-14 14:20:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
posts = append(posts, p)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2016-12-14 16:56:15 +00:00
|
|
|
|
2016-12-14 17:15:16 +00:00
|
|
|
sort.Sort(sort.Reverse(posts))
|
2016-12-18 16:51:32 +00:00
|
|
|
|
|
|
|
resume, err := ioutil.ReadFile("./static/resume/resume.md")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
rbody = string(resume)
|
2016-12-14 14:20:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2017-01-10 02:31:33 +00:00
|
|
|
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {})
|
2016-12-14 14:20:25 +00:00
|
|
|
http.HandleFunc("/api/blog/posts", writeBlogPosts)
|
2016-12-14 19:53:26 +00:00
|
|
|
http.HandleFunc("/api/blog/post", func(w http.ResponseWriter, r *http.Request) {
|
2016-12-14 14:20:25 +00:00
|
|
|
q := r.URL.Query()
|
|
|
|
name := q.Get("name")
|
|
|
|
|
2016-12-14 19:53:26 +00:00
|
|
|
if name == "" {
|
|
|
|
goto fail
|
2016-12-14 14:20:25 +00:00
|
|
|
}
|
|
|
|
|
2016-12-14 19:53:26 +00:00
|
|
|
for _, p := range posts {
|
|
|
|
if strings.HasSuffix(p.Link, name) {
|
|
|
|
json.NewEncoder(w).Encode(p)
|
|
|
|
return
|
|
|
|
}
|
2016-12-14 14:20:25 +00:00
|
|
|
}
|
2016-12-14 19:53:26 +00:00
|
|
|
|
|
|
|
fail:
|
|
|
|
http.Error(w, "Not Found", http.StatusNotFound)
|
2016-12-14 14:20:25 +00:00
|
|
|
})
|
2016-12-18 16:51:32 +00:00
|
|
|
http.HandleFunc("/api/resume", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
json.NewEncoder(w).Encode(struct {
|
|
|
|
Body string `json:"body"`
|
|
|
|
}{
|
|
|
|
Body: rbody,
|
|
|
|
})
|
|
|
|
})
|
2017-01-09 21:41:39 +00:00
|
|
|
|
|
|
|
if os.Getenv("USE_ASAR") == "yes" {
|
2017-01-09 22:58:07 +00:00
|
|
|
log.Println("serving site frontend from asar file")
|
|
|
|
|
|
|
|
do404 := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
http.Error(w, "Not found", http.StatusNotFound)
|
|
|
|
})
|
|
|
|
fe, err := asarfs.New("./frontend.asar", do404)
|
2017-01-09 21:41:39 +00:00
|
|
|
if err != nil {
|
2017-01-09 22:58:07 +00:00
|
|
|
log.Fatal("frontend: ", err)
|
2017-01-09 21:41:39 +00:00
|
|
|
}
|
|
|
|
|
2017-01-10 20:38:57 +00:00
|
|
|
http.Handle("/dist/", http.FileServer(fe))
|
2017-01-09 21:41:39 +00:00
|
|
|
} else {
|
2017-01-09 22:58:07 +00:00
|
|
|
log.Println("serving site frontend from filesystem")
|
2017-01-09 21:41:39 +00:00
|
|
|
http.Handle("/dist/", http.FileServer(http.Dir("./frontend/static/")))
|
|
|
|
}
|
2017-01-09 22:58:07 +00:00
|
|
|
|
|
|
|
http.Handle("/static/", http.FileServer(http.Dir(".")))
|
2016-12-14 14:20:25 +00:00
|
|
|
http.HandleFunc("/", writeIndexHTML)
|
|
|
|
|
2016-12-18 20:11:06 +00:00
|
|
|
port := os.Getenv("PORT")
|
|
|
|
if port == "" {
|
|
|
|
port = "9090"
|
|
|
|
}
|
|
|
|
|
2017-03-29 07:26:50 +00:00
|
|
|
http.HandleFunc("/blog.rss", createFeed)
|
|
|
|
http.HandleFunc("/blog.atom", createAtom)
|
|
|
|
|
2017-01-09 22:58:07 +00:00
|
|
|
n := negroni.Classic()
|
|
|
|
n.UseHandler(http.DefaultServeMux)
|
|
|
|
|
|
|
|
log.Fatal(http.ListenAndServe(":"+port, n))
|
2016-12-14 14:20:25 +00:00
|
|
|
}
|
|
|
|
|
2017-03-29 07:26:50 +00:00
|
|
|
var bootTime = time.Now()
|
|
|
|
|
|
|
|
var feed = &feeds.Feed{
|
|
|
|
Title: "Christine Dodrill's Blog",
|
|
|
|
Link: &feeds.Link{Href: "https://christine.website/blog"},
|
|
|
|
Description: "My blog posts and rants about various technology things.",
|
|
|
|
Author: &feeds.Author{Name: "Christine Dodrill", Email: "me@christine.website"},
|
|
|
|
Created: bootTime,
|
|
|
|
Copyright: "This work is copyright Christine Dodrill. My viewpoints are my own and not the view of any employer past, current or future.",
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
for _, item := range posts {
|
|
|
|
itime, _ := time.Parse("2006-01-02", item.Date)
|
|
|
|
feed.Items = append(feed.Items, &feeds.Item{
|
|
|
|
Title: item.Title,
|
|
|
|
Link: &feeds.Link{Href: "https://christine.website/" + item.Link},
|
|
|
|
Description: item.Summary,
|
|
|
|
Created: itime,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// IncrediblySecureSalt *******
|
|
|
|
const IncrediblySecureSalt = "hunter2"
|
|
|
|
|
|
|
|
func createFeed(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Content-Type", "application/rss+xml")
|
|
|
|
w.Header().Set("ETag", Hash(bootTime.String(), IncrediblySecureSalt))
|
|
|
|
|
|
|
|
err := feed.WriteRss(w)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
|
|
ln.Error(err, ln.F{
|
|
|
|
"remote_addr": r.RemoteAddr,
|
|
|
|
"action": "generating_rss",
|
|
|
|
"uri": r.RequestURI,
|
|
|
|
"host": r.Host,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func createAtom(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Content-Type", "application/atom+xml")
|
|
|
|
w.Header().Set("ETag", Hash(bootTime.String(), IncrediblySecureSalt))
|
|
|
|
|
|
|
|
err := feed.WriteAtom(w)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
|
|
ln.Error(err, ln.F{
|
|
|
|
"remote_addr": r.RemoteAddr,
|
|
|
|
"action": "generating_rss",
|
|
|
|
"uri": r.RequestURI,
|
|
|
|
"host": r.Host,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-14 14:20:25 +00:00
|
|
|
func writeBlogPosts(w http.ResponseWriter, r *http.Request) {
|
2016-12-18 16:56:35 +00:00
|
|
|
p := []interface{}{}
|
|
|
|
for _, post := range posts {
|
|
|
|
p = append(p, struct {
|
|
|
|
Title string `json:"title"`
|
|
|
|
Link string `json:"link"`
|
|
|
|
Summary string `json:"summary,omitifempty"`
|
|
|
|
Date string `json:"date"`
|
|
|
|
}{
|
|
|
|
Title: post.Title,
|
|
|
|
Link: post.Link,
|
|
|
|
Summary: post.Summary,
|
|
|
|
Date: post.Date,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(p)
|
2016-12-14 14:20:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func writeIndexHTML(w http.ResponseWriter, r *http.Request) {
|
|
|
|
http.ServeFile(w, r, "./frontend/static/dist/index.html")
|
|
|
|
}
|