master
scratch 2019-10-19 03:52:26 +00:00
commit 019992c4c2
18 changed files with 442 additions and 0 deletions

50
config/config.go Normal file
View File

@ -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])
}

86
main.go Normal file
View File

@ -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, `<html>`)
fmt.Fprintln(w, `<head>`)
fmt.Fprintln(w, `
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/kognise/water.css@latest/dist/dark.min.css">
`)
fmt.Fprintln(w, `</head>`)
fmt.Fprintln(w, `<body>`)
w.Write(render(code, content))
fmt.Fprintln(w, `</body>`)
fmt.Fprintln(w, `</html>`)
})
}
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
}

31
server/dir.go Normal file
View File

@ -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
}

29
server/dir_test.go Normal file
View File

@ -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)
}

21
server/dirs.go Normal file
View File

@ -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(),
})
}
}

1
server/dirs_test.go Normal file
View File

@ -0,0 +1 @@
package server

14
server/file.go Normal file
View File

@ -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) {
}

17
server/file_test.go Normal file
View File

@ -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)
}
}

21
server/files.go Normal file
View File

@ -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(),
})
}
}

1
server/files_test.go Normal file
View File

@ -0,0 +1 @@
package server

29
server/notes.go Normal file
View File

@ -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
}

13
server/notes_test.go Normal file
View File

@ -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)
}
}

12
server/path.go Normal file
View File

@ -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)
}

1
server/path_test.go Normal file
View File

@ -0,0 +1 @@
package server

27
server/routes.go Normal file
View File

@ -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
}

38
server/routes_test.go Normal file
View File

@ -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())
}

25
server/server.go Normal file
View File

@ -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"))
}

26
server/server_test.go Normal file
View File

@ -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)
}
}