From 4c063b74bac9ccf6b20303c9bd2987edb24f63f3 Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Wed, 31 Aug 2022 17:26:36 +0200
Subject: [PATCH] chore: commit save point

---
 Makefile.example                              |  72 ++++++++++
 application/source/constants/context.go       |   2 +
 application/source/server/cache.go            |  44 ++++++
 application/source/server/inject.go           | 128 ++++++++++++++++++
 application/source/server/routing.go          |  15 +-
 application/source/watch/resources.go         |   9 ++
 application/source/websocket/connection.go    |  42 +-----
 development/examples/e1/config.yaml           |   4 +
 development/examples/e1/src/main.js           |   5 +-
 development/examples/e1/web/hello.html        |   5 +-
 development/examples/e1/web/scripts/bundle.js |   4 +-
 .../examples/e1/web/scripts/bundle.js.map     |   4 +-
 12 files changed, 287 insertions(+), 47 deletions(-)
 create mode 100644 Makefile.example
 create mode 100644 application/source/server/cache.go
 create mode 100644 application/source/server/inject.go

diff --git a/Makefile.example b/Makefile.example
new file mode 100644
index 0000000..c9d8453
--- /dev/null
+++ b/Makefile.example
@@ -0,0 +1,72 @@
+#############################################################################################
+#############################################################################################
+##
+## PROJECT-DEFINITIONS
+##
+#############################################################################################
+#############################################################################################
+
+COMPONENT_NAME        := Monster
+
+#############################################################################################
+#############################################################################################
+##
+## MORE GENERAL BLOCK WITH STANDARD DEFINITIONS
+##
+#############################################################################################
+#############################################################################################
+
+# get Makefile directory name: http://stackoverflow.com/a/5982798/376773
+THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
+PROJECT_ROOT:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd)/
+THIS_MAKEFILE:=$(PROJECT_ROOT)$(THIS_MAKEFILE_PATH)
+
+
+-include $(PROJECT_ROOT)project.mk
+
+
+## Define the location of Makefiles
+MAKEFILE_IMPORT_PATH?=$(PROJECT_ROOT)makefiles/
+-include $(MAKEFILE_IMPORT_PATH)project.mk
+
+#############################################################################################
+#############################################################################################
+##
+## INCLUSION OF VARIOUS STANDARD RULES
+##
+#############################################################################################
+#############################################################################################
+
+include $(MAKEFILE_IMPORT_PATH)directories-standard.mk
+#include $(MAKEFILE_IMPORT_PATH)jsdoc.mk
+include $(MAKEFILE_IMPORT_PATH)output.mk
+include $(MAKEFILE_IMPORT_PATH)placeholder.mk
+#include $(MAKEFILE_IMPORT_PATH)s3.mk
+#include $(MAKEFILE_IMPORT_PATH)license-agpl3.mk
+#include $(MAKEFILE_IMPORT_PATH)jsdoc-json.mk
+#include $(MAKEFILE_IMPORT_PATH)go.mk
+include $(MAKEFILE_IMPORT_PATH)gitignore.mk
+include $(MAKEFILE_IMPORT_PATH)color.mk
+include $(MAKEFILE_IMPORT_PATH)version.mk
+#include $(MAKEFILE_IMPORT_PATH)node.mk
+include $(MAKEFILE_IMPORT_PATH)terminal.mk
+#include $(MAKEFILE_IMPORT_PATH)target-go-fetch-licenses.mk
+#include $(MAKEFILE_IMPORT_PATH)target-deploy-tool.mk
+#include $(MAKEFILE_IMPORT_PATH)target-jsdoc-build.mk
+#include $(MAKEFILE_IMPORT_PATH)target-jekyll.mk
+#include $(MAKEFILE_IMPORT_PATH)target-minerva.mk
+#include $(MAKEFILE_IMPORT_PATH)target-docman.mk
+#include $(MAKEFILE_IMPORT_PATH)target-node-build.mk
+#include $(MAKEFILE_IMPORT_PATH)target-caddy.mk
+include $(MAKEFILE_IMPORT_PATH)target-update-makefiles.mk
+include $(MAKEFILE_IMPORT_PATH)target-help.mk
+#include $(MAKEFILE_IMPORT_PATH)target-go-build.mk
+#include $(MAKEFILE_IMPORT_PATH)target-node-test.mk
+#include $(MAKEFILE_IMPORT_PATH)target-npm-publish.mk
+include $(MAKEFILE_IMPORT_PATH)target-git.mk
+include $(MAKEFILE_IMPORT_PATH)target-init-standard.mk
+include $(MAKEFILE_IMPORT_PATH)target-variable.mk	
+include $(MAKEFILE_IMPORT_PATH)terminal-check.mk
+
+
+#############################################################################################
diff --git a/application/source/constants/context.go b/application/source/constants/context.go
index 78990c5..1e89c9b 100644
--- a/application/source/constants/context.go
+++ b/application/source/constants/context.go
@@ -10,3 +10,5 @@ const AppStyleDirectory = "styles"
 const AppScriptDirectory = "scripts"
 
 const StandardCacheControl = "no-cache, no-store, must-revalidate"
+
+const WebsocketPath = "/websocketPath"
diff --git a/application/source/server/cache.go b/application/source/server/cache.go
new file mode 100644
index 0000000..a61ba6f
--- /dev/null
+++ b/application/source/server/cache.go
@@ -0,0 +1,44 @@
+package server
+
+import (
+	"net/http"
+	"time"
+)
+
+var epoch = time.Unix(0, 0).Format(time.RFC1123)
+
+var noCacheHeaders = map[string]string{
+	"Expires":         epoch,
+	"Cache-Control":   "no-cache, private, max-age=0",
+	"Pragma":          "no-cache",
+	"X-Accel-Expires": "0",
+}
+
+var etagHeaders = []string{
+	"ETag",
+	"If-Modified-Since",
+	"If-Match",
+	"If-None-Match",
+	"If-Range",
+	"If-Unmodified-Since",
+}
+
+func NoCache(h http.Handler) http.Handler {
+	fn := func(w http.ResponseWriter, r *http.Request) {
+		// Delete any ETag headers that may have been set
+		for _, v := range etagHeaders {
+			if r.Header.Get(v) != "" {
+				r.Header.Del(v)
+			}
+		}
+
+		// Set our NoCache headers
+		for k, v := range noCacheHeaders {
+			w.Header().Set(k, v)
+		}
+
+		h.ServeHTTP(w, r)
+	}
+
+	return http.HandlerFunc(fn)
+}
diff --git a/application/source/server/inject.go b/application/source/server/inject.go
new file mode 100644
index 0000000..dcedc6b
--- /dev/null
+++ b/application/source/server/inject.go
@@ -0,0 +1,128 @@
+package server
+
+import (
+	"gitlab.schukai.com/oss/utilities/conan/configuration"
+	"gitlab.schukai.com/oss/utilities/conan/constants"
+	"gitlab.schukai.com/oss/utilities/conan/logging"
+	"net/http"
+	"os"
+	"regexp"
+	"strings"
+)
+
+func injectHandler(next http.Handler) http.Handler {
+
+	fn := func(w http.ResponseWriter, r *http.Request) {
+
+		if strings.HasSuffix(r.URL.Path, ".html") {
+
+			path := configuration.GetServerWebPath() + "/" + r.URL.Path
+			p, err := os.ReadFile(path)
+			if err != nil {
+				logging.LogError("injectHandler error %s", err.Error())
+				return
+			}
+
+			p = injectScript(p)
+
+			w.Header().Set("Content-Type", "text/html")
+			w.Write(p)
+
+			return
+
+		}
+
+		next.ServeHTTP(w, r)
+
+	}
+
+	return http.HandlerFunc(fn)
+
+}
+
+var script string
+
+func init() {
+
+	script = `<head>
+    <script type="module">
+
+        try {
+            let counter = 0;
+            let socket
+            let url = ""
+            if (document.location.protocol == 'https:') {
+                url = 'wss://'
+            } else {
+                url = 'ws://'
+            }
+            url += document.location.host + "{{ constants.WebsocketPath }}";
+
+            function connectWebsocket() {
+                
+                if (socket) {
+                    socket.close()
+                }
+                
+                if (counter++ > 20) {
+                    console.error("Failed 20 times to connect to websocket. Giving up.")
+                    return
+                }
+                
+                
+                socket = new WebSocket(url)
+
+                socket.onopen = function (e) {
+                    console.log("[conan] Connection established");
+                    counter = 0
+                };
+
+                socket.onmessage = function (event) {
+                    console.log("[conan] Data received from server: " + event?.data);
+
+                    if (event?.data == "reload") {
+                        console.log("[conan] Reloading page")
+                        window.location.reload();
+                    }
+                };
+
+                socket.onclose = function (event) {
+                    if (event.wasClean) {
+                        console.log("[conan] Connection closed cleanly, code=" + event?.code + " reason=" + event?.reason + "");
+                    } else {
+                        console.error("[conan] Connection died");
+                        setTimeout(connectWebsocket, 3000*counter)
+                    }
+                };
+
+                socket.onerror = function (error) {
+                    console.error("[conan] " + error?.message);
+                };
+
+            }
+
+            document.addEventListener("DOMContentLoaded", () => {
+                connectWebsocket()
+            })
+
+        } catch (e) {
+            console.error(e)
+        }
+
+
+    </script>`
+
+	script = strings.Replace(script, "{{ constants.WebsocketPath }}", constants.WebsocketPath, -1)
+
+}
+
+func injectScript(p []byte) []byte {
+
+	s := string(p)
+
+	reg := regexp.MustCompile(`<head>`)
+	s = reg.ReplaceAllString(s, script)
+
+	return []byte(s)
+
+}
diff --git a/application/source/server/routing.go b/application/source/server/routing.go
index 6ba8265..f781697 100644
--- a/application/source/server/routing.go
+++ b/application/source/server/routing.go
@@ -2,11 +2,14 @@ package server
 
 import (
 	"crypto/tls"
+	"gitea.com/go-chi/session"
 	"github.com/go-chi/chi/v5"
 	"github.com/go-chi/chi/v5/middleware"
 	"gitlab.schukai.com/oss/utilities/conan/configuration"
+	"gitlab.schukai.com/oss/utilities/conan/constants"
 	"gitlab.schukai.com/oss/utilities/conan/header"
 	"gitlab.schukai.com/oss/utilities/conan/logging"
+	"gitlab.schukai.com/oss/utilities/conan/websocket"
 	"net/http"
 )
 
@@ -25,12 +28,16 @@ func getRouter() *chi.Mux {
 	router.Use(middleware.Compress(5))
 	router.Use(logging.LoggerMiddleware)
 	router.Use(reloadMiddleware)
+	router.Use(NoCache)
 
-	router.Handle("/*", http.FileServer(
-		http.Dir(configuration.GetServerWebPath()+"/")))
+	router.Use(session.Sessioner())
 
-	return router
+	router.Handle(constants.WebsocketPath, websocket.GetWebsocketHandler())
 
-}
+	//fs := NoListFileSystem{http.Dir(configuration.GetServerWebPath() + "/")}
+	//	router.Handle("*.html", injectHandler(http.FileServer(http.Dir(configuration.GetServerWebPath()+"/"))))
+	router.Handle("/*", injectHandler(http.FileServer(http.Dir(configuration.GetServerWebPath()+"/"))))
 
+	return router
 
+}
diff --git a/application/source/watch/resources.go b/application/source/watch/resources.go
index 6647ce2..03538ce 100644
--- a/application/source/watch/resources.go
+++ b/application/source/watch/resources.go
@@ -7,6 +7,7 @@ import (
 	"gitlab.schukai.com/oss/utilities/conan/configuration"
 	"gitlab.schukai.com/oss/utilities/conan/constants"
 	"gitlab.schukai.com/oss/utilities/conan/logging"
+	"gitlab.schukai.com/oss/utilities/conan/websocket"
 	"os"
 	"path"
 	"path/filepath"
@@ -65,6 +66,12 @@ func executeWatchAction(watchPath string) {
 			}
 		}
 
+		if w.Command == "" {
+			logging.LogInfo("watching: no command defined for %s", watchPath)
+			websocket.SendReloadMessage()
+			continue
+		}
+
 		logging.LogDebug("watching: execute watch action: %s", w.Command)
 
 		t, err := template.New("command").Parse(w.Command)
@@ -109,6 +116,8 @@ func executeWatchAction(watchPath string) {
 		r, err := result.String()
 		logging.LogDebug("%s", r)
 
+		websocket.SendReloadMessage()
+
 		if err != nil {
 			logging.LogError("watching: execute watch action error: %v", err.Error())
 			continue
diff --git a/application/source/websocket/connection.go b/application/source/websocket/connection.go
index 14c190b..01eb4dd 100644
--- a/application/source/websocket/connection.go
+++ b/application/source/websocket/connection.go
@@ -14,20 +14,6 @@ var (
 	connections = make(map[string]*websocket.Conn)
 )
 
-//func getCmd(input string) string {
-//	inputArr := strings.Split(input, " ")
-//	return inputArr[0]
-//}
-//
-//func getMessage(input string) string {
-//	inputArr := strings.Split(input, " ")
-//	var result string
-//	for i := 1; i < len(inputArr); i++ {
-//		result += inputArr[i]
-//	}
-//	return result
-//}
-
 func SendMessageToAll(message []byte) {
 
 	for _, conn := range connections {
@@ -45,6 +31,10 @@ func closeConnection(session string) error {
 	return nil
 }
 
+func SendReloadMessage() {
+	SendMessageToAll([]byte("reload"))
+}
+
 func GetWebsocketHandler() http.Handler {
 
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -69,11 +59,6 @@ func GetWebsocketHandler() http.Handler {
 			closeConnection(session)
 		}()
 
-		//if sessionStore.Get("user") == nil {
-		//	http.Error(w, "Forbidden", http.StatusForbidden)
-		//	return
-		//}
-
 		// Continuosly read and write message
 		for {
 			mt, message, err := conn.ReadMessage()
@@ -87,25 +72,6 @@ func GetWebsocketHandler() http.Handler {
 			}
 
 			fmt.Println(mt, string(message), "!!!!!!!!!!!!!!!!!!")
-			//input := string(message)
-			//cmd := getCmd(input)
-			//msg := getMessage(input)
-			//if cmd == "x" {
-			//	todoList = append(todoList, msg)
-			//} else if cmd == "done" {
-			//	updateTodoList(msg)
-			//}
-			//output := "Current Todos: \n"
-			//for _, todo := range todoList {
-			//	output += "\n - " + todo + "\n"
-			//}
-			//output += "\n----------------------------------------"
-			//message = []byte(output)
-			//err = conn.WriteMessage(mt, message)
-			//if err != nil {
-			//	log.Println("write failed:", err)
-			//	break
-			//}
 		}
 	})
 }
diff --git a/development/examples/e1/config.yaml b/development/examples/e1/config.yaml
index 84514f3..0b0f8b8 100644
--- a/development/examples/e1/config.yaml
+++ b/development/examples/e1/config.yaml
@@ -22,6 +22,10 @@ Server:
       Exclude:
         - ~$
         - ^\.
+    - Path: web
+      Exclude:
+        - ~$
+        - ^\.
     
   Flags:
     FollowSymlinks: true
diff --git a/development/examples/e1/src/main.js b/development/examples/e1/src/main.js
index a657bdc..8e3ac04 100644
--- a/development/examples/e1/src/main.js
+++ b/development/examples/e1/src/main.js
@@ -1 +1,4 @@
-console.log('Hello ME!?');
\ No newline at end of file
+document.addEventListener("DOMContentLoaded", () => {
+    document.querySelector("#app").innerHTML = "Hello!!! World?!";
+})
+// Language: javascript   
\ No newline at end of file
diff --git a/development/examples/e1/web/hello.html b/development/examples/e1/web/hello.html
index 4e8540f..f82764d 100644
--- a/development/examples/e1/web/hello.html
+++ b/development/examples/e1/web/hello.html
@@ -6,5 +6,8 @@
     <script src="scripts/bundle.js"></script>
 </head>
 <body>
-    <h1>Hello World</h1>
+    <h1>Hello ...ddd</h1>
+
+<div id="app"></div>
+
 </body>
diff --git a/development/examples/e1/web/scripts/bundle.js b/development/examples/e1/web/scripts/bundle.js
index b5d19a8..4bd7bb6 100644
--- a/development/examples/e1/web/scripts/bundle.js
+++ b/development/examples/e1/web/scripts/bundle.js
@@ -1,5 +1,7 @@
 (() => {
   // src/main.js
-  console.log("Hello ME!?");
+  document.addEventListener("DOMContentLoaded", () => {
+    document.querySelector("#app").innerHTML = "Hello!!! World?!";
+  });
 })();
 //# sourceMappingURL=bundle.js.map
diff --git a/development/examples/e1/web/scripts/bundle.js.map b/development/examples/e1/web/scripts/bundle.js.map
index e98b515..11dffab 100644
--- a/development/examples/e1/web/scripts/bundle.js.map
+++ b/development/examples/e1/web/scripts/bundle.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../../src/main.js"],
-  "sourcesContent": ["console.log('Hello ME!?');"],
-  "mappings": ";;AAAA,UAAQ,IAAI,YAAY;",
+  "sourcesContent": ["document.addEventListener(\"DOMContentLoaded\", () => {\n    document.querySelector(\"#app\").innerHTML = \"Hello!!! World?!\";\n})\n// Language: javascript   "],
+  "mappings": ";;AAAA,WAAS,iBAAiB,oBAAoB,MAAM;AAChD,aAAS,cAAc,MAAM,EAAE,YAAY;AAAA,EAC/C,CAAC;",
   "names": []
 }
-- 
GitLab