diff --git a/example/plugins/debugger/mod/zoraxy_plugin/zoraxy_plugin.go b/example/plugins/debugger/mod/zoraxy_plugin/zoraxy_plugin.go index 2cf494e..737e928 100644 --- a/example/plugins/debugger/mod/zoraxy_plugin/zoraxy_plugin.go +++ b/example/plugins/debugger/mod/zoraxy_plugin/zoraxy_plugin.go @@ -42,8 +42,9 @@ type SubscriptionEvent struct { } type RuntimeConstantValue struct { - ZoraxyVersion string `json:"zoraxy_version"` - ZoraxyUUID string `json:"zoraxy_uuid"` + ZoraxyVersion string `json:"zoraxy_version"` + ZoraxyUUID string `json:"zoraxy_uuid"` + DevelopmentBuild bool `json:"development_build"` //Whether the Zoraxy is a development build or not } /* diff --git a/example/plugins/helloworld/mod/zoraxy_plugin/zoraxy_plugin.go b/example/plugins/helloworld/mod/zoraxy_plugin/zoraxy_plugin.go index 2cf494e..737e928 100644 --- a/example/plugins/helloworld/mod/zoraxy_plugin/zoraxy_plugin.go +++ b/example/plugins/helloworld/mod/zoraxy_plugin/zoraxy_plugin.go @@ -42,8 +42,9 @@ type SubscriptionEvent struct { } type RuntimeConstantValue struct { - ZoraxyVersion string `json:"zoraxy_version"` - ZoraxyUUID string `json:"zoraxy_uuid"` + ZoraxyVersion string `json:"zoraxy_version"` + ZoraxyUUID string `json:"zoraxy_uuid"` + DevelopmentBuild bool `json:"development_build"` //Whether the Zoraxy is a development build or not } /* diff --git a/example/plugins/upnp/mod/zoraxy_plugin/zoraxy_plugin.go b/example/plugins/upnp/mod/zoraxy_plugin/zoraxy_plugin.go index 2cf494e..737e928 100644 --- a/example/plugins/upnp/mod/zoraxy_plugin/zoraxy_plugin.go +++ b/example/plugins/upnp/mod/zoraxy_plugin/zoraxy_plugin.go @@ -42,8 +42,9 @@ type SubscriptionEvent struct { } type RuntimeConstantValue struct { - ZoraxyVersion string `json:"zoraxy_version"` - ZoraxyUUID string `json:"zoraxy_uuid"` + ZoraxyVersion string `json:"zoraxy_version"` + ZoraxyUUID string `json:"zoraxy_uuid"` + DevelopmentBuild bool `json:"development_build"` //Whether the Zoraxy is a development build or not } /* diff --git a/example/plugins/upnp/upnp.json b/example/plugins/upnp/upnp.json index 627b5bb..2f45679 100644 --- a/example/plugins/upnp/upnp.json +++ b/example/plugins/upnp/upnp.json @@ -1 +1 @@ -{"ForwardRules":[{"RuleName":"EarlySpring","PortNumber":2016}],"Enabled":false} \ No newline at end of file +{"ForwardRules":[],"Enabled":false} \ No newline at end of file diff --git a/example/plugins/ztnc/mod/zoraxy_plugin/zoraxy_plugin.go b/example/plugins/ztnc/mod/zoraxy_plugin/zoraxy_plugin.go index 2cf494e..737e928 100644 --- a/example/plugins/ztnc/mod/zoraxy_plugin/zoraxy_plugin.go +++ b/example/plugins/ztnc/mod/zoraxy_plugin/zoraxy_plugin.go @@ -42,8 +42,9 @@ type SubscriptionEvent struct { } type RuntimeConstantValue struct { - ZoraxyVersion string `json:"zoraxy_version"` - ZoraxyUUID string `json:"zoraxy_uuid"` + ZoraxyVersion string `json:"zoraxy_version"` + ZoraxyUUID string `json:"zoraxy_uuid"` + DevelopmentBuild bool `json:"development_build"` //Whether the Zoraxy is a development build or not } /* diff --git a/example/plugins/ztnc/web/details.html b/example/plugins/ztnc/web/details.html index 37db9a0..766644c 100644 --- a/example/plugins/ztnc/web/details.html +++ b/example/plugins/ztnc/web/details.html @@ -226,9 +226,9 @@ }, success: function(data){ if (data.error != undefined){ - msgbox(data.error, false, 5000) + parent.msgbox(data.error, false, 5000) }else{ - msgbox("Network Range Updated") + parent.msgbox("Network Range Updated") } } }) @@ -253,7 +253,7 @@ initNetNameAndDesc(); if (object != undefined){ $(object).removeClass("loading"); - msgbox("Network Metadata Updated"); + parent.msgbox("Network Metadata Updated"); } $("#gannetDetailEdit").slideUp("fast"); } @@ -264,7 +264,7 @@ //Get the details of the net $.get("./api/gan/network/name?netid=" + currentGANetID, function(data){ if (data.error !== undefined){ - msgbox(data.error, false, 6000); + parent.msgbox(data.error, false, 6000); }else{ $("#gaNetNameInput").val(data[0]); $(".ganetName").html(data[0]); @@ -278,7 +278,7 @@ //Get the details of the net $.get("./api/gan/network/list?netid=" + currentGANetID, function(data){ if (data.error !== undefined){ - msgbox(data.error, false, 6000); + parent.msgbox(data.error, false, 6000); }else{ currentGaNetDetails = data; highlightCurrentGANetCIDR(); @@ -299,9 +299,9 @@ }, success: function(data){ if (data.error != undefined){ - msgbox(data.error, false, 5000); + parent.msgbox(data.error, false, 5000); }else{ - msgbox("IP removed from member " + memberid) + parent.msgbox("IP removed from member " + memberid) } renderMemeberTable(); } @@ -331,7 +331,7 @@ } if (!isValidIPv4Address(newip)){ - msgbox(newip + " is not a valid IPv4 address", false, 5000) + parent.msgbox(newip + " is not a valid IPv4 address", false, 5000) return } @@ -346,9 +346,9 @@ }, success: function(data){ if (data.error != undefined){ - msgbox(data.error, false, 5000); + parent.msgbox(data.error, false, 5000); }else{ - msgbox("IP added to member " + memberid) + parent.msgbox("IP added to member " + memberid) } renderMemeberTable(); } @@ -482,7 +482,7 @@ function renameMember(targetMemberAddr){ if (targetMemberAddr == ""){ - msgbox("Member address cannot be empty", false, 5000) + parent.msgbox("Member address cannot be empty", false, 5000) return } @@ -498,9 +498,9 @@ }, success: function(data){ if (data.error != undefined){ - msgbox(data.error, false, 6000); + parent.msgbox(data.error, false, 6000); }else{ - msgbox("Member Name Updated"); + parent.msgbox("Member Name Updated"); } renderMemeberTable(true); } @@ -564,12 +564,12 @@ }, success: function(data){ if (data.error != undefined){ - msgbox(data.error, false, 6000); + parent.msgbox(data.error, false, 6000); }else{ if (isAuthed){ - msgbox("Member Authorized"); + parent.msgbox("Member Authorized"); }else{ - msgbox("Member Deauthorized"); + parent.msgbox("Member Deauthorized"); } } @@ -580,25 +580,26 @@ } function handleMemberDelete(addr){ - if (confirm("Confirm delete member " + addr + " ?")){ - $.cjax({ - url: "./api/gan/members/delete", - method: "POST", - data: { - netid:currentGANetID, - memid: addr, - }, - success: function(data){ - if (data.error != undefined){ - msgbox(data.error, false, 6000); - }else{ - msgbox("Member Deleted"); + parent.confirmBox("Confirm delete member " + addr + " ?", function(choice){ + if (choice){ + $.cjax({ + url: "./api/gan/members/delete", + method: "POST", + data: { + netid:currentGANetID, + memid: addr, + }, + success: function(data){ + if (data.error != undefined){ + parent.msgbox(data.error, false, 6000); + }else{ + parent.msgbox("Member Deleted"); + } + renderMemeberTable(true); } - renderMemeberTable(true); - } - }); - } - + }); + } + }); } //Add and remove this controller node to network as member @@ -616,13 +617,18 @@ $(".addControllerToNetworkBtn").removeClass("disabled"); $(".addControllerToNetworkBtn").removeClass("loading"); if (data.error != undefined){ - msgbox(data.error, false, 6000); + parent.msgbox(data.error, false, 6000); }else{ - msgbox("Controller joint " + currentGANetID); + parent.msgbox("Controller joint " + currentGANetID); } setTimeout(function(){ renderMemeberTable(true); }, 3000) + }, + error: function(){ + $(".addControllerToNetworkBtn").removeClass("disabled"); + $(".addControllerToNetworkBtn").removeClass("loading"); + } }); } @@ -639,9 +645,9 @@ }, success: function(data){ if (data.error != undefined){ - msgbox(data.error, false, 6000); + parent.msgbox(data.error, false, 6000); }else{ - msgbox("Controller left " + currentGANetID); + parent.msgbox("Controller left " + currentGANetID); } renderMemeberTable(true); $(".removeControllerFromNetworkBtn").removeClass("disabled"); @@ -655,7 +661,7 @@ currentGANetID = ganetId; $(".ganetID").text(ganetId); initNetNameAndDesc(ganetId); - generateIPRangeTable(netRanges);msgbox + generateIPRangeTable(netRanges); initNetDetails(); renderMemeberTable(true); @@ -676,7 +682,6 @@ } //Debug functions - if (typeof(msgbox) == "undefined"){ msgbox = function(msg, error=false, timeout=3000){ console.log(msg); diff --git a/example/plugins/ztnc/web/index.html b/example/plugins/ztnc/web/index.html index c470a65..3753ed4 100644 --- a/example/plugins/ztnc/web/index.html +++ b/example/plugins/ztnc/web/index.html @@ -92,7 +92,7 @@ function handleAddNetwork(){ let networkName = $("#networkName").val().trim(); if (networkName == ""){ - msgbox("Network name cannot be empty", false, 5000); + parent.msgbox("Network name cannot be empty", false, 5000); return; } @@ -104,7 +104,7 @@ function initGANetID(){ $.get("./api/gan/network/info", function(data){ if (data.error !== undefined){ - msgbox(data.error, false, 5000) + parent.msgbox(data.error, false, 5000) }else{ if (data != ""){ $(".ganControllerID").text(data); @@ -121,9 +121,9 @@ data: {}, success: function(response) { if (response.error != undefined){ - msgbox(response.error, false, 5000); + parent.msgbox(response.error, false, 5000); }else{ - msgbox("Network added successfully"); + parent.msgbox("Network added successfully"); } console.log("Network added successfully:", response); listGANet(); @@ -141,7 +141,7 @@ $("#GANetList").empty(); if (data.error != undefined){ console.log(data.error); - msgbox("Unable to load auth token for GANet", false, 5000); + parent.msgbox("Unable to load auth token for GANet", false, 5000); //token error or no zerotier found $(".gansnetworks").addClass("disabled"); $("#GANetList").append(` @@ -217,23 +217,28 @@ //Remove the given GANet function removeGANet(netid){ - if (confirm("Confirm remove Network " + netid + " PERMANENTLY ?")) - $.cjax({ - url: "./api/gan/network/remove", - type: "POST", - dataType: "json", - data: { - id: netid, - }, - success: function(data){ - if (data.error != undefined){ - msgbox(data.error, false, 5000); - }else{ - msgbox("Net " + netid + " removed"); - } - listGANet(); + //Reusing Zoraxy confirm box + parent.confirmBox("Confirm remove " + netid + "?", function(choice){ + if (choice == true){ + $.cjax({ + url: "./api/gan/network/remove", + type: "POST", + dataType: "json", + data: { + id: netid, + }, + success: function(data){ + if (data.error != undefined){ + parent.msgbox(data.error, false, 5000); + }else{ + parent.msgbox("Net " + netid + " removed"); + } + listGANet(); + } + }); } }); + } function openGANetDetails(netid){ diff --git a/src/api.go b/src/api.go index 66000b3..c991521 100644 --- a/src/api.go +++ b/src/api.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "io/fs" "net/http" "net/http/pprof" @@ -315,13 +316,20 @@ func initAPIs(targetMux *http.ServeMux) { }, }) - //Register the standard web services urls - fs := http.FileServer(http.FS(webres)) + // Register the standard web services URLs + var staticWebRes http.Handler if DEVELOPMENT_BUILD { - fs = http.FileServer(http.Dir("web/")) + staticWebRes = http.FileServer(http.Dir("web/")) + } else { + subFS, err := fs.Sub(webres, "web") + if err != nil { + panic("Failed to strip 'web/' from embedded resources: " + err.Error()) + } + staticWebRes = http.FileServer(http.FS(subFS)) } + //Add a layer of middleware for advance control - advHandler := FSHandler(fs) + advHandler := FSHandler(staticWebRes) targetMux.Handle("/", advHandler) //Register the APIs diff --git a/src/def.go b/src/def.go index b2eae7f..c717597 100644 --- a/src/def.go +++ b/src/def.go @@ -97,6 +97,7 @@ var ( path_uuid = flag.String("uuid", "./sys.uuid", "sys.uuid file path") path_logFile = flag.String("log", "./log", "Log folder path") path_webserver = flag.String("webroot", "./www", "Static web server root folder. Only allow change in start paramters") + path_plugin = flag.String("plugin", "./plugins", "Plugin folder path") /* Maintaince Function Flags */ geoDbUpdate = flag.Bool("update_geoip", false, "Download the latest GeoIP data and exit") diff --git a/src/mod/plugins/typdef.go b/src/mod/plugins/typdef.go index d1c867c..991651c 100644 --- a/src/mod/plugins/typdef.go +++ b/src/mod/plugins/typdef.go @@ -34,10 +34,10 @@ type ManagerOptions struct { PluginGroupsConfig string //The group / tag configuration file, if set the plugin groups will be loaded from this file /* Runtime */ - SystemConst *zoraxyPlugin.RuntimeConstantValue - CSRFTokenGen func(*http.Request) string `json:"-"` //The CSRF token generator function - Database *database.Database `json:"-"` - Logger *logger.Logger `json:"-"` + SystemConst *zoraxyPlugin.RuntimeConstantValue //The system constant value + CSRFTokenGen func(*http.Request) string `json:"-"` //The CSRF token generator function + Database *database.Database `json:"-"` + Logger *logger.Logger `json:"-"` /* Internal */ pluginGroupsMutex sync.RWMutex //Mutex for the pluginGroups diff --git a/src/mod/plugins/zoraxy_plugin/zoraxy_plugin.go b/src/mod/plugins/zoraxy_plugin/zoraxy_plugin.go index 2cf494e..737e928 100644 --- a/src/mod/plugins/zoraxy_plugin/zoraxy_plugin.go +++ b/src/mod/plugins/zoraxy_plugin/zoraxy_plugin.go @@ -42,8 +42,9 @@ type SubscriptionEvent struct { } type RuntimeConstantValue struct { - ZoraxyVersion string `json:"zoraxy_version"` - ZoraxyUUID string `json:"zoraxy_uuid"` + ZoraxyVersion string `json:"zoraxy_version"` + ZoraxyUUID string `json:"zoraxy_uuid"` + DevelopmentBuild bool `json:"development_build"` //Whether the Zoraxy is a development build or not } /* diff --git a/src/router.go b/src/router.go index c15748f..05f91dd 100644 --- a/src/router.go +++ b/src/router.go @@ -3,7 +3,6 @@ package main import ( "fmt" "net/http" - "net/url" "os" "path/filepath" "strings" @@ -23,27 +22,12 @@ import ( func FSHandler(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - /* - Development Mode Override - => Web root is located in / - */ - if DEVELOPMENT_BUILD && strings.HasPrefix(r.URL.Path, "/web/") { - u, _ := url.Parse(strings.TrimPrefix(r.URL.Path, "/web")) - r.URL = u - } - - /* - Production Mode Override - => Web root is located in /web - */ - if !DEVELOPMENT_BUILD && r.URL.Path == "/" { - //Redirect to web UI - http.Redirect(w, r, "/web/", http.StatusTemporaryRedirect) - return - } - // Allow access to /script/*, /img/pubic/* and /login.html without authentication - if strings.HasPrefix(r.URL.Path, ppf("/script/")) || strings.HasPrefix(r.URL.Path, ppf("/img/public/")) || r.URL.Path == ppf("/login.html") || r.URL.Path == ppf("/reset.html") || r.URL.Path == ppf("/favicon.png") { + if strings.HasPrefix(r.URL.Path, "/script/") || + strings.HasPrefix(r.URL.Path, "/img/public/") || + r.URL.Path == "/login.html" || + r.URL.Path == "/reset.html" || + r.URL.Path == "/favicon.png" { if isHTMLFilePath(r.URL.Path) { handleInjectHTML(w, r, r.URL.Path) return @@ -54,7 +38,7 @@ func FSHandler(handler http.Handler) http.Handler { // Check authentication if !authAgent.CheckAuth(r) && requireAuth { - http.Redirect(w, r, ppf("/login.html"), http.StatusTemporaryRedirect) + http.Redirect(w, r, "/login.html", http.StatusTemporaryRedirect) return } @@ -105,14 +89,6 @@ func FSHandler(handler http.Handler) http.Handler { }) } -// Production path fix wrapper. Fix the path on production or development environment -func ppf(relativeFilepath string) string { - if !DEVELOPMENT_BUILD { - return strings.ReplaceAll(filepath.Join("/web/", relativeFilepath), "\\", "/") - } - return relativeFilepath -} - func isHTMLFilePath(requestURI string) bool { return strings.HasSuffix(requestURI, ".html") || strings.HasSuffix(requestURI, "/") } @@ -136,9 +112,10 @@ func handleInjectHTML(w http.ResponseWriter, r *http.Request, relativeFilepath s } else { //Load from embedded fs, require trimming off the prefix slash for relative path relativeFilepath = strings.TrimPrefix(relativeFilepath, "/") + relativeFilepath = filepath.ToSlash(filepath.Join("web/", relativeFilepath)) content, err = webres.ReadFile(relativeFilepath) if err != nil { - SystemWideLogger.Println("load embedded web file failed: ", err) + SystemWideLogger.Println("Load embedded web file failed: ", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } diff --git a/src/start.go b/src/start.go index 890bd9a..d2ac88a 100644 --- a/src/start.go +++ b/src/start.go @@ -94,7 +94,7 @@ func startupSequence() { } authAgent = auth.NewAuthenticationAgent(SYSTEM_NAME, []byte(sessionKey), sysdb, true, SystemWideLogger, func(w http.ResponseWriter, r *http.Request) { //Not logged in. Redirecting to login page - http.Redirect(w, r, ppf("/login.html"), http.StatusTemporaryRedirect) + http.Redirect(w, r, "/login.html", http.StatusTemporaryRedirect) }) //Create a TLS certificate manager @@ -305,12 +305,14 @@ func startupSequence() { /* Plugin Manager */ - + pluginFolder := *path_plugin + pluginFolder = strings.TrimSuffix(pluginFolder, "/") pluginManager = plugins.NewPluginManager(&plugins.ManagerOptions{ - PluginDir: "./plugins", + PluginDir: pluginFolder, SystemConst: &zoraxy_plugin.RuntimeConstantValue{ - ZoraxyVersion: SYSTEM_VERSION, - ZoraxyUUID: nodeUUID, + ZoraxyVersion: SYSTEM_VERSION, + ZoraxyUUID: nodeUUID, + DevelopmentBuild: DEVELOPMENT_BUILD, }, Database: sysdb, Logger: SystemWideLogger, diff --git a/src/web/components/plugincontext.html b/src/web/components/plugincontext.html index 2f32956..77cdbbf 100644 --- a/src/web/components/plugincontext.html +++ b/src/web/components/plugincontext.html @@ -1,6 +1,6 @@
-