From 019992c4c2497a14659a746d4404dae9f6a8426e Mon Sep 17 00:00:00 2001 From: scratch Date: Sat, 19 Oct 2019 03:52:26 +0000 Subject: [PATCH] Initial --- config/config.go | 50 +++++++++++++++++++++++++ main.go | 86 +++++++++++++++++++++++++++++++++++++++++++ server/dir.go | 31 ++++++++++++++++ server/dir_test.go | 29 +++++++++++++++ server/dirs.go | 21 +++++++++++ server/dirs_test.go | 1 + server/file.go | 14 +++++++ server/file_test.go | 17 +++++++++ server/files.go | 21 +++++++++++ server/files_test.go | 1 + server/notes.go | 29 +++++++++++++++ server/notes_test.go | 13 +++++++ server/path.go | 12 ++++++ server/path_test.go | 1 + server/routes.go | 27 ++++++++++++++ server/routes_test.go | 38 +++++++++++++++++++ server/server.go | 25 +++++++++++++ server/server_test.go | 26 +++++++++++++ 18 files changed, 442 insertions(+) create mode 100644 config/config.go create mode 100644 main.go create mode 100644 server/dir.go create mode 100644 server/dir_test.go create mode 100644 server/dirs.go create mode 100644 server/dirs_test.go create mode 100644 server/file.go create mode 100644 server/file_test.go create mode 100644 server/files.go create mode 100644 server/files_test.go create mode 100644 server/notes.go create mode 100644 server/notes_test.go create mode 100644 server/path.go create mode 100644 server/path_test.go create mode 100644 server/routes.go create mode 100644 server/routes_test.go create mode 100644 server/server.go create mode 100644 server/server_test.go diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..5d85275 --- /dev/null +++ b/config/config.go @@ -0,0 +1,50 @@ +package config + +import ( + "bytes" + "fmt" + "io/ioutil" + "local/args" + "os" + "strings" +) + +var ( + Root string + Port string + Head string + Foot string +) + +func init() { + Refresh() +} + +func Refresh() { + if strings.Contains(fmt.Sprint(os.Args), "-test") { + return + } + + as := args.NewArgSet() + as.Append(args.STRING, "root", "root dir path", "./public") + as.Append(args.STRING, "port", "port to listen on", "39909") + as.Append(args.STRING, "wrap", "file with http header/footer", "./wrapper.html") + if err := as.Parse(); err != nil { + panic(err) + } + + wrap := as.Get("wrap").String() + b, err := ioutil.ReadFile(wrap) + if err != nil { + panic(err) + } + bs := bytes.Split(b, []byte("{{{}}}")) + if len(bs) != 2 { + panic(len(bs)) + } + + Root = as.Get("root").String() + Port = ":" + strings.TrimPrefix(as.Get("port").String(), ":") + Head = string(bs[0]) + Foot = string(bs[1]) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..6547759 --- /dev/null +++ b/main.go @@ -0,0 +1,86 @@ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "os/signal" + "path" + "strings" + + "github.com/gomarkdown/markdown" + "github.com/miekg/mmark" + "gopkg.in/russross/blackfriday.v2" +) + +const ( + GOMARKDOWN = "gomarkdown" + RUSSROSS = "russross" + MIEKG = "miekg" +) + +func envOrDefault(key, def string) string { + if v := os.Getenv(key); v != "" { + return v + } + return def +} + +func main() { + port := flag.String("p", envOrDefault("PORT", ":41913"), "port to run on") + root := flag.String("root", envOrDefault("ROOT", "."), "root dir to serve") + render := flag.String("render", envOrDefault("RENDER", GOMARKDOWN), "renderer to use") + flag.Parse() + + if !strings.HasPrefix(*port, ":") { + *port = ":" + *port + } + + go func() { + log.Printf("Serving %q on %q", *root, *port) + if err := http.ListenAndServe(*port, handler(*render, *root)); err != nil { + panic(err) + } + }() + + // catch stop + stop := make(chan os.Signal) + signal.Notify(stop, os.Interrupt) + <-stop +} + +func handler(code, root string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + path := path.Join(root, path.Clean(r.URL.Path)) + content, err := ioutil.ReadFile(path) + if err != nil { + http.NotFound(w, r) + return + } + fmt.Fprintln(w, ``) + fmt.Fprintln(w, ``) + fmt.Fprintln(w, ` + + `) + fmt.Fprintln(w, ``) + fmt.Fprintln(w, ``) + w.Write(render(code, content)) + fmt.Fprintln(w, ``) + fmt.Fprintln(w, ``) + }) +} + +func render(code string, content []byte) []byte { + switch code { + case GOMARKDOWN: + return markdown.ToHTML(content, nil, nil) + case RUSSROSS: + return blackfriday.Run(content) //, blackfriday.WithExtensions(blackfriday.Extensions(blackfriday.TOC))) + case MIEKG: + return mmark.Parse(content, mmark.HtmlRenderer(0, ``, ``), mmark.EXTENSION_TABLES|int(blackfriday.TOC)).Bytes() + } + return nil +} diff --git a/server/dir.go b/server/dir.go new file mode 100644 index 0000000..87f77a1 --- /dev/null +++ b/server/dir.go @@ -0,0 +1,31 @@ +package server + +import ( + "io/ioutil" + "log" + "net/http" + "os" +) + +func isDir(path string) bool { + stat, err := os.Stat(path) + return err == nil && stat.IsDir() +} + +func notesDir(path string, w http.ResponseWriter, r *http.Request) { + dirs, files := lsDir(path) + log.Println(dirs) + log.Println(files) +} + +func lsDir(p string) ([]Path, []Path) { + dirs := newDirs() + files := newFiles() + + found, _ := ioutil.ReadDir(p) + for _, f := range found { + dirs.Push(p, f) + files.Push(p, f) + } + return *dirs, *files +} diff --git a/server/dir_test.go b/server/dir_test.go new file mode 100644 index 0000000..b534ac2 --- /dev/null +++ b/server/dir_test.go @@ -0,0 +1,29 @@ +package server + +import ( + "testing" +) + +func TestIsDir(t *testing.T) { + if ok := isDir("/tmp"); !ok { + t.Fatal(ok) + } + if ok := isDir("/dev/null"); ok { + t.Fatal(ok) + } + if ok := isDir("/proc/not/real/tho"); ok { + t.Fatal(ok) + } +} + +func TestLsDir(t *testing.T) { + dirs, files := lsDir("/usr/local") + if len(dirs) == 0 { + t.Fatal(len(dirs)) + } + if len(files) == 0 { + t.Fatal(len(files)) + } + t.Log(dirs) + t.Log(files) +} diff --git a/server/dirs.go b/server/dirs.go new file mode 100644 index 0000000..f4d7774 --- /dev/null +++ b/server/dirs.go @@ -0,0 +1,21 @@ +package server + +import ( + "os" +) + +type Dirs []Path + +func newDirs() *Dirs { + d := Dirs([]Path{}) + return &d +} + +func (d *Dirs) Push(p string, f os.FileInfo) { + if f.IsDir() { + *d = append(*d, Path{ + dir: p, + base: f.Name(), + }) + } +} diff --git a/server/dirs_test.go b/server/dirs_test.go new file mode 100644 index 0000000..abb4e43 --- /dev/null +++ b/server/dirs_test.go @@ -0,0 +1 @@ +package server diff --git a/server/file.go b/server/file.go new file mode 100644 index 0000000..936fd91 --- /dev/null +++ b/server/file.go @@ -0,0 +1,14 @@ +package server + +import ( + "net/http" + "os" +) + +func isFile(path string) bool { + stat, err := os.Stat(path) + return err == nil && !stat.IsDir() +} + +func notesFile(path string, w http.ResponseWriter, r *http.Request) { +} diff --git a/server/file_test.go b/server/file_test.go new file mode 100644 index 0000000..7ac1556 --- /dev/null +++ b/server/file_test.go @@ -0,0 +1,17 @@ +package server + +import ( + "testing" +) + +func TestIsFile(t *testing.T) { + if ok := isFile("/tmp"); ok { + t.Fatal(ok) + } + if ok := isFile("/dev/null"); !ok { + t.Fatal(ok) + } + if ok := isFile("/proc/not/real/tho"); ok { + t.Fatal(ok) + } +} diff --git a/server/files.go b/server/files.go new file mode 100644 index 0000000..33f41f9 --- /dev/null +++ b/server/files.go @@ -0,0 +1,21 @@ +package server + +import ( + "os" +) + +type Files []Path + +func newFiles() *Files { + d := Files([]Path{}) + return &d +} + +func (d *Files) Push(p string, f os.FileInfo) { + if !f.IsDir() { + *d = append(*d, Path{ + dir: p, + base: f.Name(), + }) + } +} diff --git a/server/files_test.go b/server/files_test.go new file mode 100644 index 0000000..abb4e43 --- /dev/null +++ b/server/files_test.go @@ -0,0 +1 @@ +package server diff --git a/server/notes.go b/server/notes.go new file mode 100644 index 0000000..d302a8e --- /dev/null +++ b/server/notes.go @@ -0,0 +1,29 @@ +package server + +import ( + "local/notes-server/config" + "net/http" + "path" + "strings" +) + +func (s *Server) notes(w http.ResponseWriter, r *http.Request) { + path := resolvePath(r.URL.Path) + if isDir(path) { + head(w, r) + notesDir(path, w, r) + foot(w, r) + } else if isFile(path) { + head(w, r) + notesFile(path, w, r) + foot(w, r) + } else { + http.NotFound(w, r) + } +} + +func resolvePath(p string) string { + p = strings.TrimPrefix(p, "/notes/") + p = path.Join(config.Root, p) + return p +} diff --git a/server/notes_test.go b/server/notes_test.go new file mode 100644 index 0000000..62e506d --- /dev/null +++ b/server/notes_test.go @@ -0,0 +1,13 @@ +package server + +import ( + "local/notes-server/config" + "testing" +) + +func TestResolvePath(t *testing.T) { + config.Root = "ROOT" + if p := resolvePath("/notes/a/b/c"); p != "ROOT/a/b/c" { + t.Fatal(p) + } +} diff --git a/server/path.go b/server/path.go new file mode 100644 index 0000000..44bdab9 --- /dev/null +++ b/server/path.go @@ -0,0 +1,12 @@ +package server + +import "path" + +type Path struct { + dir string + base string +} + +func (p Path) String() string { + return path.Join(p.dir, p.base) +} diff --git a/server/path_test.go b/server/path_test.go new file mode 100644 index 0000000..abb4e43 --- /dev/null +++ b/server/path_test.go @@ -0,0 +1 @@ +package server diff --git a/server/routes.go b/server/routes.go new file mode 100644 index 0000000..6907f42 --- /dev/null +++ b/server/routes.go @@ -0,0 +1,27 @@ +package server + +import ( + "fmt" + "local/router" + "net/http" +) + +func (s *Server) Routes() error { + wildcard := router.Wildcard + endpoints := []struct { + path string + handler http.HandlerFunc + }{ + { + path: fmt.Sprintf("notes/%s%s", wildcard, wildcard), + handler: s.notes, + }, + } + + for _, endpoint := range endpoints { + if err := s.Add(endpoint.path, endpoint.handler); err != nil { + return err + } + } + return nil +} diff --git a/server/routes_test.go b/server/routes_test.go new file mode 100644 index 0000000..5393412 --- /dev/null +++ b/server/routes_test.go @@ -0,0 +1,38 @@ +package server + +import ( + "net/http" + "net/http/httptest" + "net/url" + "testing" +) + +func TestServerRoutes(t *testing.T) { + s := New() + err := s.Routes() + if err != nil { + t.Fatal(err) + } +} + +func TestServerNotes(t *testing.T) { + s := New() + w := httptest.NewRecorder() + r := &http.Request{ + URL: &url.URL{ + Path: "/", + }, + } + s.notes(w, r) + t.Logf("serve %s: %s", r.URL.Path, w.Body.Bytes()) + + w.Body.Reset() + r.URL.Path = "/serve/" + s.notes(w, r) + t.Logf("serve %s: %s", r.URL.Path, w.Body.Bytes()) + + w.Body.Reset() + r.URL.Path = "/serve/test" + s.notes(w, r) + t.Logf("serve %s: %s", r.URL.Path, w.Body.Bytes()) +} diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..d85977e --- /dev/null +++ b/server/server.go @@ -0,0 +1,25 @@ +package server + +import ( + "local/notes-server/config" + "local/router" + "net/http" +) + +type Server struct { + *router.Router +} + +func New() *Server { + return &Server{ + Router: router.New(), + } +} + +func head(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(config.Head + "\n")) +} + +func foot(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(config.Foot + "\n")) +} diff --git a/server/server_test.go b/server/server_test.go new file mode 100644 index 0000000..62aa97a --- /dev/null +++ b/server/server_test.go @@ -0,0 +1,26 @@ +package server + +import ( + "local/notes-server/config" + "net/http/httptest" + "strings" + "testing" +) + +func TestHead(t *testing.T) { + config.Head = "A" + w := httptest.NewRecorder() + head(w, nil) + if s := strings.TrimSpace(string(w.Body.Bytes())); s != config.Head { + t.Fatal(s) + } +} + +func TestFoot(t *testing.T) { + config.Foot = "A" + w := httptest.NewRecorder() + foot(w, nil) + if s := strings.TrimSpace(string(w.Body.Bytes())); s != config.Foot { + t.Fatal(s) + } +}