try add live reloading
This commit is contained in:
parent
7e0d7a1625
commit
66086b1a91
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@ -1,3 +1,10 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": ["apihub", "spahandler"]
|
"cSpell.words": [
|
||||||
|
"apihub",
|
||||||
|
"appender",
|
||||||
|
"livereloader",
|
||||||
|
"onclose",
|
||||||
|
"spahandler",
|
||||||
|
"Upgrader"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -1,3 +1,5 @@
|
|||||||
module git.apihub24.de/admin/spa-handler
|
module git.apihub24.de/admin/spa-handler
|
||||||
|
|
||||||
go 1.22.0
|
go 1.22.0
|
||||||
|
|
||||||
|
require github.com/gorilla/websocket v1.5.3
|
||||||
|
|||||||
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
28
handler.go
28
handler.go
@ -11,6 +11,8 @@ import (
|
|||||||
type handler struct {
|
type handler struct {
|
||||||
staticFS embed.FS
|
staticFS embed.FS
|
||||||
indexPath string
|
indexPath string
|
||||||
|
addLiveReloadScript bool
|
||||||
|
clientVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -28,7 +30,11 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer indexFile.Close()
|
defer indexFile.Close()
|
||||||
|
if h.addLiveReloadScript {
|
||||||
|
tryToDeliverFile(indexFile, h.indexPath, w, r, clientScript(h.clientVersion))
|
||||||
|
} else {
|
||||||
tryToDeliverFile(indexFile, h.indexPath, w, r)
|
tryToDeliverFile(indexFile, h.indexPath, w, r)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
@ -37,14 +43,34 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
tryToDeliverFile(f, h.indexPath, w, r)
|
tryToDeliverFile(f, h.indexPath, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleFiles(path string, options HandlerOptions) {
|
func HandleFiles(path string, options HandlerOptions, mux *http.ServeMux) {
|
||||||
if options.AdditionalMimeTypeMapping != nil {
|
if options.AdditionalMimeTypeMapping != nil {
|
||||||
for key, value := range options.AdditionalMimeTypeMapping {
|
for key, value := range options.AdditionalMimeTypeMapping {
|
||||||
contentTypeMapping[key] = value
|
contentTypeMapping[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if mux == nil {
|
||||||
http.Handle(path, handler{
|
http.Handle(path, handler{
|
||||||
staticFS: options.Files,
|
staticFS: options.Files,
|
||||||
indexPath: options.IndexPath,
|
indexPath: options.IndexPath,
|
||||||
|
addLiveReloadScript: options.ActivateLiveReloading,
|
||||||
|
clientVersion: options.ClientVersion,
|
||||||
|
})
|
||||||
|
if options.ActivateLiveReloading {
|
||||||
|
mux.Handle("/_livereloader_", reloader{
|
||||||
|
clientVersion: options.ClientVersion,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
mux.Handle(path, handler{
|
||||||
|
staticFS: options.Files,
|
||||||
|
indexPath: options.IndexPath,
|
||||||
|
})
|
||||||
|
if options.ActivateLiveReloading {
|
||||||
|
mux.Handle("/_livereloader_", reloader{
|
||||||
|
clientVersion: options.ClientVersion,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
20
helper.go
20
helper.go
@ -1,19 +1,27 @@
|
|||||||
package spa_handler
|
package spa_handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func tryToDeliverFile(file fs.File, path string, w http.ResponseWriter, r *http.Request) {
|
func tryToDeliverFile(file fs.File, path string, w http.ResponseWriter, r *http.Request, appender ...string) {
|
||||||
fileSeeker, ok := file.(io.ReadSeeker)
|
content, err := io.ReadAll(file)
|
||||||
if !ok {
|
if err != nil {
|
||||||
file.Close()
|
file.Close()
|
||||||
http.Error(w, "Index file does not implement ReadSeeker", http.StatusInternalServerError)
|
http.Error(w, "can not read file", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
modified := []byte{}
|
||||||
|
for _, a := range appender {
|
||||||
|
modified = append(modified, []byte(a)...)
|
||||||
|
}
|
||||||
|
modified = append(modified, content...)
|
||||||
|
|
||||||
stats, statErr := file.Stat()
|
stats, statErr := file.Stat()
|
||||||
if statErr != nil {
|
if statErr != nil {
|
||||||
http.Error(w, "Cannot stat index file", http.StatusInternalServerError)
|
http.Error(w, "Cannot stat index file", http.StatusInternalServerError)
|
||||||
@ -24,8 +32,10 @@ func tryToDeliverFile(file fs.File, path string, w http.ResponseWriter, r *http.
|
|||||||
contentType = "text/html"
|
contentType = "text/html"
|
||||||
}
|
}
|
||||||
contentType = getContentTypeDetail(contentType, stats)
|
contentType = getContentTypeDetail(contentType, stats)
|
||||||
|
|
||||||
|
fileSeeker := bytes.NewReader(modified)
|
||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", contentType)
|
||||||
http.ServeContent(w, r, path, stats.ModTime(), fileSeeker)
|
http.ServeContent(w, r, path, time.Now(), fileSeeker)
|
||||||
}
|
}
|
||||||
|
|
||||||
func detectContentType(file fs.File) (string, error) {
|
func detectContentType(file fs.File) (string, error) {
|
||||||
|
|||||||
@ -5,5 +5,7 @@ import "embed"
|
|||||||
type HandlerOptions struct {
|
type HandlerOptions struct {
|
||||||
Files embed.FS
|
Files embed.FS
|
||||||
IndexPath string
|
IndexPath string
|
||||||
|
ClientVersion string
|
||||||
|
ActivateLiveReloading bool
|
||||||
AdditionalMimeTypeMapping map[string]string
|
AdditionalMimeTypeMapping map[string]string
|
||||||
}
|
}
|
||||||
|
|||||||
74
reloader.go
Normal file
74
reloader.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package spa_handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func clientScript(clientVersion string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
<!-- [spa_handler] Live Reloading Script Start -->
|
||||||
|
<script type="application/javascript">
|
||||||
|
function spaHandlerLiveReloaderConnect() {
|
||||||
|
const ws = new WebSocket("/_livereloader_");
|
||||||
|
ws.onopen = function() {
|
||||||
|
console.info('[spa_handler]: live reloader connected!');
|
||||||
|
};
|
||||||
|
ws.onmessage = function(event) {
|
||||||
|
try {
|
||||||
|
const message = JSON.parse(event.data);
|
||||||
|
if (message && message.clientVersion && message.clientVersion !== '%s') {
|
||||||
|
console.info('[spa_handler]: receive reload');
|
||||||
|
location.reload(true);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warning('[spa_handler]: something was wrong! ', err)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ws.onclose = function() {
|
||||||
|
console.info('[spa_handler]: connection closed');
|
||||||
|
setTimeout(function() {
|
||||||
|
console.info('[spa_handler]: try reconnect');
|
||||||
|
spaHandlerLiveReloaderConnect();
|
||||||
|
}, 5000);
|
||||||
|
};
|
||||||
|
ws.onerror = function(err) {
|
||||||
|
console.warning('[spa_handler]: something was wrong with websocket connection! ', err)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
document.onload = function() {
|
||||||
|
spaHandlerLiveReloaderConnect();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<!-- [spa_handler] Live Reloading Script End -->
|
||||||
|
`, clientVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
var upgrader = websocket.Upgrader{
|
||||||
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type reloader struct {
|
||||||
|
clientVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rel reloader) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
conn, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error upgrading:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
_ = conn.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("{\"clientVersion\":\"%s\"}", rel.clientVersion)))
|
||||||
|
for {
|
||||||
|
_, _, err := conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error reading message:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user