From cddf7ed45147869a9d2eee2e40f59a97c472ea72 Mon Sep 17 00:00:00 2001 From: Mitch Riedstra Date: Sat, 26 Sep 2020 20:59:34 -0400 Subject: Split up http related stuff out of the page library. Make it an interface. Rename page to be 'local' reflecting that it reads the website off the local disk. Update the build script to include the go version. Switch to gorilla/mux Remove the convert command, since we're no longer utilizing that old layout or have any need to convert from it. --- build.sh | 2 + cmd/convert/main.go | 94 -------------------------- cmd/server/main.go | 19 +++++- go.mod | 4 +- go.sum | 4 +- http/main.go | 78 ++++++++++++++++++++++ local/index.go | 69 +++++++++++++++++++ local/page.go | 139 +++++++++++++++++++++++++++++++++++++++ local/pagelist.go | 22 +++++++ local/time.go | 21 ++++++ page/http.go | 58 ---------------- page/main.go | 10 +++ page/page.go | 186 ---------------------------------------------------- page/pagelist.go | 19 ------ page/time.go | 21 ------ 15 files changed, 361 insertions(+), 385 deletions(-) delete mode 100644 cmd/convert/main.go create mode 100644 http/main.go create mode 100644 local/index.go create mode 100644 local/page.go create mode 100644 local/pagelist.go create mode 100644 local/time.go delete mode 100644 page/http.go create mode 100644 page/main.go delete mode 100644 page/page.go delete mode 100644 page/pagelist.go delete mode 100644 page/time.go diff --git a/build.sh b/build.sh index 21f46a5..de1ac51 100755 --- a/build.sh +++ b/build.sh @@ -4,6 +4,8 @@ set -e LICENSE="$(cat LICENSE)" version="$(git log --format="%h %d" -1) +$(go version) + Source code can be found here: https://git.riedstra.dev/mitch/go-website diff --git a/cmd/convert/main.go b/cmd/convert/main.go deleted file mode 100644 index 3cc4135..0000000 --- a/cmd/convert/main.go +++ /dev/null @@ -1,94 +0,0 @@ -package main - -import ( - "flag" - "io/ioutil" - "log" - "os" - "os/exec" - "path/filepath" - "regexp" - - "gopkg.in/yaml.v2" -) - -// Assumes we're getting a pageName ending in .page -func convertPage(newDir, pageName string) error { - yamlName := pageName[:len(pageName)-5] + ".yml" - headName := pageName[:len(pageName)-5] + ".head" - newPageName := pageName[:len(pageName)-5] + ".md" - hasHead := false - headBytes := []byte{} - if _, err := os.Stat(headName); err == nil { - hasHead = true - } - - if hasHead { - var err error - headBytes, err = ioutil.ReadFile(headName) - if err != nil { - return err - } - } - - y, err := yaml.Marshal(map[string]string{ - "head": string(headBytes), - }) - if err != nil { - return err - } - - cmd := exec.Command("mkdir", "-p", filepath.Join(newDir, filepath.Dir(pageName))) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Run() - if err != nil { - return err - } - cmd = exec.Command("cp", "-v", pageName, filepath.Join(newDir, newPageName)) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Run() - if err != nil { - return err - } - - if len(headBytes) == 0 { - return nil - } else { - return ioutil.WriteFile(newDir+"/"+yamlName, y, 0644) - } -} - -func main() { - fl := flag.NewFlagSet("Website converter", flag.ExitOnError) - origDirectory := fl.String("d", ".", "Directory to read from") - newDirectory := fl.String("D", ".", "Directory to write to") - _ = fl.Parse(os.Args[1:]) - - newDir, _ := filepath.Abs(*newDirectory) - origDir, _ := filepath.Abs(*origDirectory) - - if err := os.Chdir(origDir); err != nil { - log.Fatal(err) - } - - pageRe := regexp.MustCompile(`\.page$`) - _ = filepath.Walk(".", func(path string, info os.FileInfo, err error) error { - if err != nil { - // Prevents panic - log.Println(err) - return err - } - - if pageRe.MatchString(info.Name()) { - if err := convertPage(newDir, path); err != nil { - log.Println(err) - return err - } - } - - return nil - }) - -} diff --git a/cmd/server/main.go b/cmd/server/main.go index c18aa55..f034cfa 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -6,8 +6,11 @@ import ( "log" "net/http" "os" + "time" - "git.riedstra.us/mitch/go-website/page" + siteHttp "git.riedstra.dev/mitch/go-website/http" + // "git.riedstra.dev/mitch/go-website/local" + // "git.riedstra.dev/mitch/go-website/page" ) var VersionString = "" @@ -32,6 +35,16 @@ func main() { log.Fatal(err) } - page.SetupHandlers() - _ = http.ListenAndServe(*listen, nil) + // We're going to utilize the local version of the website + // siteHttp.NewPage = func(u string) page.Page { + // return &local.Page{Path: u} + // } + + srv := &http.Server{ + Handler: siteHttp.GetHandler(), + Addr: *listen, + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + log.Fatal(srv.ListenAndServe()) } diff --git a/go.mod b/go.mod index b97fd83..f15f431 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ -module git.riedstra.us/mitch/go-website +module git.riedstra.dev/mitch/go-website go 1.13 require ( + github.com/gorilla/mux v1.8.0 github.com/kr/pretty v0.1.0 // indirect github.com/russross/blackfriday v2.0.0+incompatible github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/yaml.v2 v2.2.7 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 ) diff --git a/go.sum b/go.sum index da16122..8208b32 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -11,7 +13,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/http/main.go b/http/main.go new file mode 100644 index 0000000..875f4b9 --- /dev/null +++ b/http/main.go @@ -0,0 +1,78 @@ +// A mostly self explanatory package, it handles website specific +// HTTP requests +package http + +import ( + "fmt" + "log" + "net/http" + "os" + "path/filepath" + "strings" + + "git.riedstra.dev/mitch/go-website/local" + "git.riedstra.dev/mitch/go-website/page" + + "github.com/gorilla/mux" +) + +// NewPage is required to specify how we acquire a new page from a given URL +// since an interface is used, you can swap this out with a different +// library to load pages from different sources +var NewPage func(string) page.Page = func(u string) page.Page { + return &local.Page{Path: u} +} + +func GetHandler() http.Handler { + if NewPage == nil { + fmt.Fprintln(os.Stderr, "Warning, global NewPage method is not defined!") + } + + r := mux.NewRouter() + r.HandleFunc("/rebuildIndex/", RebuildIndexHandler) + r.PathPrefix("/static/").Handler(StaticHandler()) + r.PathPrefix("/").HandlerFunc(PageHandler) + return r +} + +func PageHandler(w http.ResponseWriter, r *http.Request) { + u := r.URL.Path + if u == "/" { + u = "/index" + } + u = filepath.Join(".", u) + log.Println(u) + + p := NewPage(u) + err := p.Render(w) + if err != nil { + if strings.HasSuffix(err.Error(), "no such file or directory") { + log.Printf("Page '%s' not found, trying 404", u) + p = NewPage("404") + w.WriteHeader(404) + err := p.Render(w) + if err != nil { + log.Println(err) + http.Error(w, "Internal server error", 500) + return + } + return + } else { + log.Println(err) + http.Error(w, "Internal server error", 500) + return + } + } + +} + +func RebuildIndexHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + p := NewPage("index") + _ = p.RebuildIndex() + } +} + +func StaticHandler() http.Handler { + return http.StripPrefix("/static/", http.FileServer(http.Dir("static"))) +} diff --git a/local/index.go b/local/index.go new file mode 100644 index 0000000..a90276e --- /dev/null +++ b/local/index.go @@ -0,0 +1,69 @@ +package local + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "time" +) + +var pageIndex map[string]PageList + +func (p *Page) RebuildIndex() error { + pageIndex = nil + _, err := p.Index() + return err +} + +// Index returns a map of all pages below the current Page's Path seperated +// into their respective tags If a Page has multiple tags it will be listed +// under each +func (p *Page) Index() (map[string]PageList, error) { + if pageIndex != nil { + return pageIndex, nil + } + fmt.Fprintln(os.Stderr, "Rebuilding index...") + + out := make(map[string]PageList) + + var pageErr error + + filepath.Walk(filepath.Dir(p.Path), + func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() && strings.HasSuffix(info.Name(), ".md") { + + p2 := &Page{Path: strings.ReplaceAll(path, ".md", "")} + err = p2.Read() + + if err != nil { + pageErr = err + fmt.Fprintln(os.Stderr, "Error encountered: ", err) + return err + } + + for tag, _ := range p2.Tags { + if _, ok := out[tag]; !ok { + out[tag] = []*Page{p2} + } else { + out[tag] = append(out[tag], p2) + } + } + + } + + return nil + }) + + pageIndex = out + + return out, nil +} + +func (p *Page) Time() time.Time { + return p.Date.Time +} diff --git a/local/page.go b/local/page.go new file mode 100644 index 0000000..f514b4a --- /dev/null +++ b/local/page.go @@ -0,0 +1,139 @@ +package local + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "text/template" + + "github.com/russross/blackfriday" + "gopkg.in/yaml.v3" +) + +type Page struct { + Path string + Title string + Head string + Description string + Tags map[string]interface{} + Date *PageTime + Published bool + Vars map[string]interface{} + Markdown []byte +} + +// Can be adjusted to change the base template used in rendering +var BaseTemplate = "inc/base.html" + +// Used to split the .md files into yaml and markdown +var DocumentSplit = "|---\n" + +// Allow for the creation of a new page from the page +func (p Page) NewPage(pth string) *Page { + return &Page{Path: pth} +} + +// Renders a page +func (p *Page) Render(wr io.Writer) error { + if err := p.Read(); err != nil { + return err + } + + t, err := template.ParseFiles(BaseTemplate) + if err != nil { + return err + } + + // Automatically pull from the yml file if applicable + if p.Head != "" { + t, err = t.Parse(` + {{define "head"}} + {{.RenderHead}} + {{end}} + `) + if err != nil { + return err + } + } + + return t.Execute(wr, p) +} + +func (p *Page) RenderHead() (string, error) { + buf := &bytes.Buffer{} + t, err := template.New("Head").Parse(p.Head) + if err != nil { + return "", err + } + err = t.Execute(buf, p) + if err != nil { + return "", err + } + return string(buf.Bytes()), nil +} + +// Reads in the special markdown file format for the website off of the disk +func (p *Page) Read() error { + yamlBuf := bytes.NewBuffer(nil) + markdownBuf := bytes.NewBuffer(nil) + + fh, err := os.Open(p.Path + ".md") + if err != nil { + return err + } + defer fh.Close() + rdr := bufio.NewReader(fh) + + // Read in the file and split between markdown and yaml buffers + yamlDone := false + for { + + bytes, err := rdr.ReadBytes('\n') + if err == io.EOF { + break + } else if err != nil { + return err + } + + // Is this the line where we stop reading the yaml and start reading markdown? + if DocumentSplit == string(bytes) && !yamlDone { + yamlDone = true + continue + } + + if !yamlDone { + yamlBuf.Write(bytes) + } else { + markdownBuf.Write(bytes) + } + } + + err = yaml.Unmarshal(yamlBuf.Bytes(), p) + if err != nil { + return err + } + + p.Markdown = markdownBuf.Bytes() + return nil +} + +func (p *Page) RenderBody() (string, error) { + buf := &bytes.Buffer{} + t, err := template.New("Body").Parse(string(p.Markdown)) + if err != nil { + return "", err + } + + err = t.Execute(buf, p) + if err != nil { + return "", err + } + + return string(blackfriday.Run(buf.Bytes())), nil +} + +func (p Page) String() string { + return fmt.Sprintf("Page: %s", p.Path) +} diff --git a/local/pagelist.go b/local/pagelist.go new file mode 100644 index 0000000..75106f1 --- /dev/null +++ b/local/pagelist.go @@ -0,0 +1,22 @@ +package local + +import ( + "sort" + // "git.riedstra.dev/mitch/go-website/page" +) + +type PageList []*Page + +func (p PageList) SortDate() PageList { + sort.Slice(p, func(i, j int) bool { + return p[i].Time().After(p[j].Time()) + }) + return p +} + +func (p PageList) SortDateReverse() PageList { + sort.Slice(p, func(i, j int) bool { + return p[i].Time().Before(p[j].Time()) + }) + return p +} diff --git a/local/time.go b/local/time.go new file mode 100644 index 0000000..72ff3c6 --- /dev/null +++ b/local/time.go @@ -0,0 +1,21 @@ +package local + +import ( + "gopkg.in/yaml.v3" + "time" +) + +type PageTime struct { + time.Time +} + +var TimeFormat = "01.02.2006 15:04:05 MST" + +func (pt *PageTime) UnmarshalYAML(n *yaml.Node) error { + t, err := time.Parse(TimeFormat, n.Value) + if err != nil { + return err + } + pt.Time = t + return nil +} diff --git a/page/http.go b/page/http.go deleted file mode 100644 index b3622be..0000000 --- a/page/http.go +++ /dev/null @@ -1,58 +0,0 @@ -package page - -import ( - "log" - "net/http" - "path/filepath" - "strings" -) - -func SetupHandlers() { - http.HandleFunc("/rebuildIndex/", RebuildIndexHandler) - http.Handle("/static/", StaticHandler()) - http.HandleFunc("/", PageHandler) -} - -func PageHandler(w http.ResponseWriter, r *http.Request) { - u := r.URL.Path - if u == "/" { - u = "/index" - } - u = filepath.Join(".", u) - log.Println(u) - - p := &Page{Path: u} - err := p.Render(w) - if err != nil { - if strings.HasSuffix(err.Error(), "no such file or directory") { - log.Printf("Page '%s' not found, trying 404", p.Path) - p.Path = "404" - w.WriteHeader(404) - err := p.Render(w) - if err != nil { - log.Println(err) - http.Error(w, "Internal server error", 500) - return - } - return - } else { - log.Println(err) - http.Error(w, "Internal server error", 500) - return - } - } - -} - -func RebuildIndexHandler(w http.ResponseWriter, r *http.Request) { - if r.Method != "POST" { - pageIndex = nil - p := &Page{Path: "index"} - _, _ = p.Index() - } - return -} - -func StaticHandler() (h http.Handler) { - return http.StripPrefix("/static/", http.FileServer(http.Dir("static"))) -} diff --git a/page/main.go b/page/main.go new file mode 100644 index 0000000..18445d4 --- /dev/null +++ b/page/main.go @@ -0,0 +1,10 @@ +package page + +import ( + "io" +) + +type Page interface { + Render(io.Writer) error + RebuildIndex() error +} diff --git a/page/page.go b/page/page.go deleted file mode 100644 index c5230c6..0000000 --- a/page/page.go +++ /dev/null @@ -1,186 +0,0 @@ -package page - -import ( - "bufio" - "bytes" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "text/template" - - "github.com/russross/blackfriday" - "gopkg.in/yaml.v3" -) - -type Page struct { - Path string - Title string - Head string - Description string - Tags map[string]interface{} - Date *PageTime - Published bool - Vars map[string]interface{} - Markdown []byte -} - -// Can be adjusted to change the base template used in rendering -var BaseTemplate = "inc/base.html" - -// Used to split the .md files into yaml and markdown -var DocumentSplit = "|---\n" - -// Renders a page -func (p *Page) Render(wr io.Writer) error { - if err := p.Read(); err != nil { - return err - } - - t, err := template.ParseFiles(BaseTemplate) - if err != nil { - return err - } - - // Automatically pull from the yml file if applicable - if p.Head != "" { - t, err = t.Parse(` - {{define "head"}} - {{.RenderHead}} - {{end}} - `) - if err != nil { - return err - } - } - - return t.Execute(wr, p) -} - -func (p *Page) RenderHead() (string, error) { - buf := &bytes.Buffer{} - t, err := template.New("Head").Parse(p.Head) - if err != nil { - return "", err - } - err = t.Execute(buf, p) - if err != nil { - return "", err - } - return string(buf.Bytes()), nil -} - -// Reads in the special markdown file format for the website off of the disk -func (p *Page) Read() error { - yamlBuf := bytes.NewBuffer(nil) - markdownBuf := bytes.NewBuffer(nil) - - fh, err := os.Open(p.Path + ".md") - if err != nil { - return err - } - defer fh.Close() - rdr := bufio.NewReader(fh) - - // Read in the file and split between markdown and yaml buffers - yamlDone := false - for { - - bytes, err := rdr.ReadBytes('\n') - if err == io.EOF { - break - } else if err != nil { - return err - } - - // Is this the line where we stop reading the yaml and start reading markdown? - if DocumentSplit == string(bytes) && !yamlDone { - yamlDone = true - continue - } - - if !yamlDone { - yamlBuf.Write(bytes) - } else { - markdownBuf.Write(bytes) - } - } - - err = yaml.Unmarshal(yamlBuf.Bytes(), p) - if err != nil { - return err - } - - p.Markdown = markdownBuf.Bytes() - return nil -} - -func (p *Page) RenderBody() (string, error) { - buf := &bytes.Buffer{} - t, err := template.New("Body").Parse(string(p.Markdown)) - if err != nil { - return "", err - } - - err = t.Execute(buf, p) - if err != nil { - return "", err - } - - return string(blackfriday.Run(buf.Bytes())), nil -} - -func (p Page) String() string { - return fmt.Sprintf("Page: %s", p.Path) -} - -var pageIndex map[string]PageList - -// Index returns a map of all pages below the current Page's Path seperated -// into their respective tags If a Page has multiple tags it will be listed -// under each -func (p *Page) Index() (map[string]PageList, error) { - if pageIndex != nil { - return pageIndex, nil - } - fmt.Fprintln(os.Stderr, "Rebuilding index...") - - out := make(map[string]PageList) - - var pageErr error - - filepath.Walk(filepath.Dir(p.Path), - func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if !info.IsDir() && strings.HasSuffix(info.Name(), ".md") { - - p2 := &Page{Path: strings.ReplaceAll(path, ".md", "")} - err = p2.Read() - - if err != nil { - pageErr = err - fmt.Fprintln(os.Stderr, "Error encountered: ", err) - return err - } - - for tag, _ := range p2.Tags { - if _, ok := out[tag]; !ok { - out[tag] = []*Page{p2} - } else { - out[tag] = append(out[tag], p2) - } - } - - } - - return nil - }) - - pageIndex = out - - return out, nil -} diff --git a/page/pagelist.go b/page/pagelist.go deleted file mode 100644 index e326ac9..0000000 --- a/page/pagelist.go +++ /dev/null @@ -1,19 +0,0 @@ -package page - -import "sort" - -type PageList []*Page - -func (p PageList) SortDate() PageList { - sort.Slice(p, func(i, j int) bool { - return p[i].Date.Time.After(p[j].Date.Time) - }) - return p -} - -func (p PageList) SortDateReverse() PageList { - sort.Slice(p, func(i, j int) bool { - return p[i].Date.Time.Before(p[j].Date.Time) - }) - return p -} diff --git a/page/time.go b/page/time.go deleted file mode 100644 index 0ac29ab..0000000 --- a/page/time.go +++ /dev/null @@ -1,21 +0,0 @@ -package page - -import ( - "gopkg.in/yaml.v3" - "time" -) - -type PageTime struct { - time.Time -} - -var TimeFormat = "01.02.2006 15:04:05 MST" - -func (pt *PageTime) UnmarshalYAML(n *yaml.Node) error { - t, err := time.Parse(TimeFormat, n.Value) - if err != nil { - return err - } - pt.Time = t - return nil -} -- cgit v1.2.3