diff --git a/caddyhugo.go b/caddyhugo.go index fe7b71f..028464c 100644 --- a/caddyhugo.go +++ b/caddyhugo.go @@ -4,11 +4,16 @@ import ( "errors" "fmt" "html/template" + "io/ioutil" "net/http" "os" "path" "path/filepath" "strings" + "sync" + "sync/atomic" + + "git.stephensearles.com/stephen/acedoc" "github.com/gorilla/websocket" "github.com/mholt/caddy" @@ -31,11 +36,32 @@ type CaddyHugo struct { ServerType string Site *httpserver.SiteConfig + Docs map[string]*acedoc.Document + mtx sync.Mutex + authorTmpl, adminTmpl, editTmpl *template.Template + + ltime uint64 +} + +func (ch *CaddyHugo) ObserveLTime(ltime uint64) uint64 { + ch.mtx.Lock() + defer ch.mtx.Unlock() + + if ch.ltime < ltime { + ch.ltime = ltime + } + + return ch.LTime() +} + +func (ch *CaddyHugo) LTime() uint64 { + return atomic.AddUint64(&ch.ltime, 1) } -func (ch CaddyHugo) Setup(c *caddy.Controller) error { +func (ch *CaddyHugo) Setup(c *caddy.Controller) error { + ch.Docs = make(map[string]*acedoc.Document) ch.Site = httpserver.GetConfig(c) var err error @@ -106,7 +132,7 @@ func (ch CaddyHugo) BasePath() string { func (ch CaddyHugo) Admin() httpserver.Handler { return httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { - err := ch.adminTmpl.Execute(w, ch.TmplData(r)) + err := ch.adminTmpl.Execute(w, ch.TmplData(r, nil)) if err != nil { fmt.Println(err) return http.StatusInternalServerError, err @@ -119,7 +145,7 @@ func (ch CaddyHugo) Admin() httpserver.Handler { func (ch CaddyHugo) AuthorHome() httpserver.Handler { return httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { - err := ch.authorTmpl.Execute(w, ch.TmplData(r)) + err := ch.authorTmpl.Execute(w, ch.TmplData(r, nil)) if err != nil { fmt.Println(err) return http.StatusInternalServerError, err @@ -129,16 +155,23 @@ func (ch CaddyHugo) AuthorHome() httpserver.Handler { }) } -func (ch CaddyHugo) Edit(c *caddy.Controller) httpserver.Handler { +func (ch *CaddyHugo) Edit(c *caddy.Controller) httpserver.Handler { return httpserver.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { if r.URL.Path == "/hugo/edit/new" { return ch.NewContent(w, r) } - if r.URL.Path == "/hugo/edit/websocket" { + + if r.Header.Get("Upgrade") == "websocket" { return ch.DeltaWebsocket(w, r) } - err := ch.editTmpl.Execute(w, ch.TmplData(r)) + doc, err := ch.Doc(r) + if err != nil { + fmt.Println(err) + return http.StatusNotFound, err + } + + err = ch.editTmpl.Execute(w, ch.TmplData(r, doc)) if err != nil { fmt.Println(err) @@ -149,7 +182,27 @@ func (ch CaddyHugo) Edit(c *caddy.Controller) httpserver.Handler { }) } -func (ch CaddyHugo) DeltaWebsocket(w http.ResponseWriter, r *http.Request) (int, error) { +func (ch *CaddyHugo) Doc(r *http.Request) (*acedoc.Document, error) { + ch.mtx.Lock() + defer ch.mtx.Unlock() + + name := r.URL.Path[len("/hugo/edit/"):] + + _, ok := ch.Docs[name] + if !ok { + fmt.Println("opening", name) + contents, err := ioutil.ReadFile(name) + if err != nil { + return nil, err + } + + ch.Docs[name] = acedoc.NewString(string(contents)) + } + + return ch.Docs[name], nil +} + +func (ch *CaddyHugo) DeltaWebsocket(w http.ResponseWriter, r *http.Request) (int, error) { var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, @@ -161,16 +214,43 @@ func (ch CaddyHugo) DeltaWebsocket(w http.ResponseWriter, r *http.Request) (int, return http.StatusBadRequest, err } + doc, err := ch.Doc(r) + if err != nil { + fmt.Println(err) + return http.StatusBadRequest, err + } + + client := doc.Client(acedoc.DeltaHandlerFunc(func(ds []acedoc.Delta) error { + err := conn.WriteJSON(Message{ + Deltas: ds, + LTime: ch.LTime(), + }) + fmt.Println("here", ds, err) + return err + })) + for { - messageType, p, err := conn.ReadMessage() + var message Message + err := conn.ReadJSON(&message) if err != nil { return http.StatusBadRequest, err } - fmt.Println(messageType, string(p)) + ch.ObserveLTime(message.LTime) + fmt.Println(message) + + err = client.PushDeltas(message.Deltas...) + if err != nil { + return http.StatusBadRequest, err + } } } +type Message struct { + Deltas []acedoc.Delta `json:"deltas"` + LTime uint64 `json:"ltime"` +} + func (ch CaddyHugo) NewContent(w http.ResponseWriter, r *http.Request) (int, error) { name := r.FormValue("name") ctype := r.FormValue("type") @@ -209,6 +289,6 @@ func (ch CaddyHugo) NewContent(w http.ResponseWriter, r *http.Request) (int, err return http.StatusFound, nil } -func (ch CaddyHugo) TmplData(r *http.Request) interface{} { - return tmplData{ch.Site, r, ch} +func (ch CaddyHugo) TmplData(r *http.Request, doc *acedoc.Document) interface{} { + return tmplData{ch.Site, r, ch, doc} } diff --git a/templates.go b/templates.go index 1f67d16..10a3222 100644 --- a/templates.go +++ b/templates.go @@ -2,12 +2,13 @@ package caddyhugo import ( "fmt" - "io/ioutil" "net/http" "os" "path" "path/filepath" + "git.stephensearles.com/stephen/acedoc" + "github.com/mholt/caddy/caddyhttp/httpserver" ) @@ -57,17 +58,11 @@ type tmplData struct { Site *httpserver.SiteConfig R *http.Request CaddyHugo + Doc *acedoc.Document } func (t tmplData) LoadContent() (string, error) { - path := t.R.URL.Path[len("/hugo/edit/"):] - f, err := os.Open(path) - if err != nil { - fmt.Println(err) - return "", err - } - out, err := ioutil.ReadAll(f) - return string(out), err + return t.Doc.Contents(), nil } var EditPage = ` @@ -90,21 +85,54 @@ var EditPage = `
{{ .LoadContent }}