Skip to content

Commit

Permalink
Merge pull request #53 from juanolon/hotreload
Browse files Browse the repository at this point in the history
Hotreload extension
  • Loading branch information
emad-elsaid committed Jun 5, 2024
2 parents bc972fd + a899715 commit c9c215a
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 0 deletions.
1 change: 1 addition & 0 deletions events.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const (
BeforeWrite PageEvent = iota
AfterWrite
AfterDelete
Changed
)

// a map to keep all page events and respective list of event handlers
Expand Down
1 change: 1 addition & 0 deletions extensions/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ import (
_ "github.com/emad-elsaid/xlog/extensions/todo"
_ "github.com/emad-elsaid/xlog/extensions/upload_file"
_ "github.com/emad-elsaid/xlog/extensions/versions"
_ "github.com/emad-elsaid/xlog/extensions/hotreload"
)
101 changes: 101 additions & 0 deletions extensions/hotreload/hotreload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package hotreload

import (
"fmt"
"html/template"
"log"
"sync"

"github.com/emad-elsaid/xlog"
. "github.com/emad-elsaid/xlog"
"github.com/gorilla/websocket"
)

var (
upgrader = websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024}
clients = make(map[*websocket.Conn]bool)
clientsMutex sync.Mutex
)

func init() {
Listen(Changed, NotifyPageChange)
Get(`/+/hotreload`, handleWebSocket)
RegisterWidget(AFTER_VIEW_WIDGET, 0, clientWidget)
}

func NotifyPageChange(p Page) error {
if !p.Exists() {
return nil
}

message := map[string]string{"url": fmt.Sprintf("/%s", p.Name())}

clientsMutex.Lock()
defer clientsMutex.Unlock()

for client := range clients {
err := client.WriteJSON(message)
if err != nil {
client.Close()
delete(clients, client)
}
}
return nil
}

func handleWebSocket(w Response, r Request) Output {
if READONLY {
return NoContent()
}

conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("Upgrade error:", err)
return BadRequest(err.Error())
}

// keep connection open
go func() {
defer func() {
clientsMutex.Lock()
delete(clients, conn)
clientsMutex.Unlock()
conn.Close()
}()

for {
mt, _, err := conn.ReadMessage()
if err != nil || mt == websocket.CloseMessage {
break
}
}
}()

clientsMutex.Lock()
clients[conn] = true
clientsMutex.Unlock()

return xlog.Noop
}

// TODO use same HOST and PORT than server
const clientScript = `
<script>
(() => {
const socketUrl = 'ws://'+window.location.host+'/+/hotreload';
let socket = new WebSocket(socketUrl);
socket.addEventListener('message', (evt) => {
let data = JSON.parse(evt.data)
window.location.href = data.url;
});
})();
</script>
`

func clientWidget(p Page) template.HTML {
if !READONLY {
return template.HTML(clientScript)
}

return ""
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ require (
golang.org/x/sync v0.6.0
)

require (
github.com/gorilla/websocket v1.5.1 // indirect
golang.org/x/net v0.22.0 // indirect
)

require (
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
Expand Down Expand Up @@ -76,6 +78,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down
3 changes: 3 additions & 0 deletions markdown_fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ func newMarkdownFS(p string) *markdownFS {

name := strings.TrimSuffix(event.Name, ".md")
name, _ = filepath.Rel(m.path, name)
cp := m._page(name)
Trigger(Changed, cp)

m.cache.Remove(name)
}
case err, ok := <-watcher.Errors:
Expand Down
3 changes: 3 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ func NoContent() Output {
return noContent
}

// Noop is an output that doesn't do anything to the request. can be useful for a websocket upgrader
func Noop(w Response, r Request) {}

func noContent(w Response, r Request) {
w.WriteHeader(http.StatusNoContent)
}
Expand Down

0 comments on commit c9c215a

Please sign in to comment.