Browse Source

auto update script executed

Toby Chui 1 year ago
parent
commit
7e1f836d7a

+ 23 - 0
api.go

@@ -0,0 +1,23 @@
+package main
+
+import "net/http"
+
+/*
+	API.go
+
+	This file contains all the API called by the web management interface
+
+*/
+
+func initAPIs() {
+	//Reverse proxy
+	http.HandleFunc("/enable", ReverseProxyHandleOnOff)
+	http.HandleFunc("/add", ReverseProxyHandleAddEndpoint)
+	http.HandleFunc("/status", ReverseProxyStatus)
+	http.HandleFunc("/list", ReverseProxyList)
+	http.HandleFunc("/del", DeleteProxyEndpoint)
+	http.HandleFunc("/setIncoming", HandleIncomingPortSet)
+
+	//TLS / SSL config
+	http.HandleFunc("/cert/upload", handleCertUpload)
+}

+ 77 - 0
cert.go

@@ -0,0 +1,77 @@
+package main
+
+import (
+	"fmt"
+	"io"
+	"net/http"
+	"os"
+	"path/filepath"
+
+	"imuslab.com/arozos/ReverseProxy/mod/utils"
+)
+
+func handleCertUpload(w http.ResponseWriter, r *http.Request) {
+	// check if request method is POST
+	if r.Method != "POST" {
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
+		return
+	}
+
+	// get the key type
+	keytype, err := utils.GetPara(r, "ktype")
+	overWriteFilename := ""
+	if err != nil {
+		http.Error(w, "Not defined key type (pub / pri)", http.StatusBadRequest)
+		return
+	}
+
+	// get the domain
+	domain, err := utils.GetPara(r, "domain")
+	if err != nil {
+		//Assume localhost
+		domain = "localhost"
+	}
+
+	if keytype == "pub" {
+		overWriteFilename = domain + ".crt"
+	} else if keytype == "pri" {
+		overWriteFilename = domain + ".key"
+	} else {
+		http.Error(w, "Not supported keytype: "+keytype, http.StatusBadRequest)
+		return
+	}
+
+	// parse multipart form data
+	err = r.ParseMultipartForm(10 << 20) // 10 MB
+	if err != nil {
+		http.Error(w, "Failed to parse form data", http.StatusBadRequest)
+		return
+	}
+
+	// get file from form data
+	file, _, err := r.FormFile("file")
+	if err != nil {
+		http.Error(w, "Failed to get file", http.StatusBadRequest)
+		return
+	}
+	defer file.Close()
+
+	// create file in upload directory
+	os.MkdirAll("./certs", 0775)
+	f, err := os.Create(filepath.Join("./certs", overWriteFilename))
+	if err != nil {
+		http.Error(w, "Failed to create file", http.StatusInternalServerError)
+		return
+	}
+	defer f.Close()
+
+	// copy file contents to destination file
+	_, err = io.Copy(f, file)
+	if err != nil {
+		http.Error(w, "Failed to save file", http.StatusInternalServerError)
+		return
+	}
+
+	// send response
+	fmt.Fprintln(w, "File upload successful!")
+}

BIN
certs/1.jpg


+ 1 - 1
main.go

@@ -49,7 +49,7 @@ func main() {
 	fs := http.FileServer(http.Dir("./web"))
 
 	http.Handle("/", fs)
-
+	initAPIs()
 	SetupCloseHandler()
 
 	//Create database

+ 16 - 0
mod/utils/conv.go

@@ -0,0 +1,16 @@
+package utils
+
+import "strconv"
+
+func StringToInt64(number string) (int64, error) {
+	i, err := strconv.ParseInt(number, 10, 64)
+	if err != nil {
+		return -1, err
+	}
+	return i, nil
+}
+
+func Int64ToString(number int64) string {
+	convedNumber := strconv.FormatInt(number, 10)
+	return convedNumber
+}

+ 19 - 0
mod/utils/template.go

@@ -0,0 +1,19 @@
+package utils
+
+import (
+	"net/http"
+)
+
+/*
+	Web Template Generator
+
+	This is the main system core module that perform function similar to what PHP did.
+	To replace part of the content of any file, use {{paramter}} to replace it.
+
+
+*/
+
+func SendHTMLResponse(w http.ResponseWriter, msg string) {
+	w.Header().Set("Content-Type", "text/html")
+	w.Write([]byte(msg))
+}

+ 175 - 0
mod/utils/utils.go

@@ -0,0 +1,175 @@
+package utils
+
+import (
+	"bufio"
+	"encoding/base64"
+	"errors"
+	"io"
+	"log"
+	"net/http"
+	"os"
+	"strings"
+	"time"
+)
+
+/*
+	Common
+
+	Some commonly used functions in ArozOS
+
+*/
+
+// Response related
+func SendTextResponse(w http.ResponseWriter, msg string) {
+	w.Write([]byte(msg))
+}
+
+// Send JSON response, with an extra json header
+func SendJSONResponse(w http.ResponseWriter, json string) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Write([]byte(json))
+}
+
+func SendErrorResponse(w http.ResponseWriter, errMsg string) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Write([]byte("{\"error\":\"" + errMsg + "\"}"))
+}
+
+func SendOK(w http.ResponseWriter) {
+	w.Header().Set("Content-Type", "application/json")
+	w.Write([]byte("\"OK\""))
+}
+
+/*
+	The paramter move function (mv)
+
+	You can find similar things in the PHP version of ArOZ Online Beta. You need to pass in
+	r (HTTP Request Object)
+	getParamter (string, aka $_GET['This string])
+
+	Will return
+	Paramter string (if any)
+	Error (if error)
+
+*/
+/*
+func Mv(r *http.Request, getParamter string, postMode bool) (string, error) {
+	if postMode == false {
+		//Access the paramter via GET
+		keys, ok := r.URL.Query()[getParamter]
+
+		if !ok || len(keys[0]) < 1 {
+			//log.Println("Url Param " + getParamter +" is missing")
+			return "", errors.New("GET paramter " + getParamter + " not found or it is empty")
+		}
+
+		// Query()["key"] will return an array of items,
+		// we only want the single item.
+		key := keys[0]
+		return string(key), nil
+	} else {
+		//Access the parameter via POST
+		r.ParseForm()
+		x := r.Form.Get(getParamter)
+		if len(x) == 0 || x == "" {
+			return "", errors.New("POST paramter " + getParamter + " not found or it is empty")
+		}
+		return string(x), nil
+	}
+
+}
+*/
+
+// Get GET parameter
+func GetPara(r *http.Request, key string) (string, error) {
+	keys, ok := r.URL.Query()[key]
+	if !ok || len(keys[0]) < 1 {
+		return "", errors.New("invalid " + key + " given")
+	} else {
+		return keys[0], nil
+	}
+}
+
+// Get POST paramter
+func PostPara(r *http.Request, key string) (string, error) {
+	r.ParseForm()
+	x := r.Form.Get(key)
+	if x == "" {
+		return "", errors.New("invalid " + key + " given")
+	} else {
+		return x, nil
+	}
+}
+
+func FileExists(filename string) bool {
+	_, err := os.Stat(filename)
+	if os.IsNotExist(err) {
+		return false
+	}
+	return true
+}
+
+func IsDir(path string) bool {
+	if FileExists(path) == false {
+		return false
+	}
+	fi, err := os.Stat(path)
+	if err != nil {
+		log.Fatal(err)
+		return false
+	}
+	switch mode := fi.Mode(); {
+	case mode.IsDir():
+		return true
+	case mode.IsRegular():
+		return false
+	}
+	return false
+}
+
+func TimeToString(targetTime time.Time) string {
+	return targetTime.Format("2006-01-02 15:04:05")
+}
+
+func LoadImageAsBase64(filepath string) (string, error) {
+	if !FileExists(filepath) {
+		return "", errors.New("File not exists")
+	}
+	f, _ := os.Open(filepath)
+	reader := bufio.NewReader(f)
+	content, _ := io.ReadAll(reader)
+	encoded := base64.StdEncoding.EncodeToString(content)
+	return string(encoded), nil
+}
+
+// Use for redirections
+func ConstructRelativePathFromRequestURL(requestURI string, redirectionLocation string) string {
+	if strings.Count(requestURI, "/") == 1 {
+		//Already root level
+		return redirectionLocation
+	}
+	for i := 0; i < strings.Count(requestURI, "/")-1; i++ {
+		redirectionLocation = "../" + redirectionLocation
+	}
+
+	return redirectionLocation
+}
+
+// Check if given string in a given slice
+func StringInArray(arr []string, str string) bool {
+	for _, a := range arr {
+		if a == str {
+			return true
+		}
+	}
+	return false
+}
+
+func StringInArrayIgnoreCase(arr []string, str string) bool {
+	smallArray := []string{}
+	for _, item := range arr {
+		smallArray = append(smallArray, strings.ToLower(item))
+	}
+
+	return StringInArray(smallArray, strings.ToLower(str))
+}

+ 0 - 8
reverseproxy.go

@@ -32,14 +32,6 @@ func ReverseProxtInit() {
 
 	dynamicProxyRouter = dprouter
 
-	http.HandleFunc("/enable", ReverseProxyHandleOnOff)
-	http.HandleFunc("/add", ReverseProxyHandleAddEndpoint)
-	http.HandleFunc("/status", ReverseProxyStatus)
-	http.HandleFunc("/list", ReverseProxyList)
-	http.HandleFunc("/del", DeleteProxyEndpoint)
-
-	http.HandleFunc("/setIncoming", HandleIncomingPortSet)
-
 	//Load all conf from files
 	confs, _ := filepath.Glob("./conf/*.config")
 	for _, conf := range confs {

BIN
sys.db


+ 43 - 0
web/components/cert.html

@@ -0,0 +1,43 @@
+
+<button onclick="uploadCert();">Upload</button>
+
+<script>
+    function uploadCert() {
+    // create file input element
+    const input = document.createElement('input');
+    input.type = 'file';
+    
+    // add change listener to file input
+    input.addEventListener('change', () => {
+        // create form data object
+        const formData = new FormData();
+        
+        // add selected file to form data
+        formData.append('file', input.files[0]);
+
+        // send form data to server
+        fetch('/cert/upload', {
+            method: 'POST',
+            body: formData
+        })
+        .then(response => {
+            if (response.ok) {
+                alert('File upload successful!');
+            } else {
+                response.text().then(text => {
+                    alert(text);
+                });
+                //console.log(response.text());
+                //alert('File upload failed!');
+            }
+            })
+        .catch(error => {
+            alert('An error occurred while uploading the file.');
+            console.error(error);
+        });
+    });
+    
+    // click file input to open file selector
+    input.click();
+    }
+</script>

+ 33 - 0
web/components/rproot.html

@@ -0,0 +1,33 @@
+<h3><i class="ui home icon"></i> Set Proxy Root</h3>
+<p>For all routing not found in the proxy rule, will be redirected to the proxy root server.</p>
+<div class="ui form">
+    <div class="field">
+        <label>Proxy Root</label>
+        <input type="text" id="proxyRoot">
+        <small>E.g. localhost:8080</small>
+    </div>
+    <div class="field">
+        <div class="ui checkbox">
+            <input type="checkbox" id="rootReqTLS">
+            <label>Root require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
+        </div>
+    </div>
+</div>
+<br>
+<button class="ui teal button" onclick="setProxyRoot()"><i class="home icon" ></i> Set Proxy Root</button>
+<div class="ui green message" id="ProxyRootUpdate" style="display:none">
+    <i class="ui checkmark icon"></i> Proxy Root Updated
+</div>
+
+<script>
+     initRootInfo();
+    function initRootInfo(){
+        $.get("list?type=root", function(data){
+            if (data == null){
+
+            }else{
+                $("#proxyRoot").val(data.Domain);
+            }
+        });
+    }
+</script>

+ 86 - 0
web/components/rules.html

@@ -0,0 +1,86 @@
+<h3><i class="ui exchange icon"></i> New Proxy Endpoint</h3>
+<p>You can create a proxy endpoing by subdomain or virtual directories</p>
+<div class="ui form">
+    <div class="field">
+        <label>Proxy Type</label>
+        <div class="ui selection dropdown">
+            <input type="hidden" id="ptype" value="subd">
+            <i class="dropdown icon"></i>
+            <div class="default text">Proxy Type</div>
+            <div class="menu">
+                <div class="item" data-value="subd">Sub-domain</div>
+                <div class="item" data-value="vdir">Virtual Directory</div>
+            </div>
+        </div>
+    </div>
+    <div class="field">
+        <label>Subdomain Matching Keyword / Virtual Directory Name</label>
+        <input type="text" id="rootname" placeholder="s1.mydomain.com">
+        <div class="ui message">
+            Example of subdomain matching keyword:<br>
+            <code>s1.arozos.com</code> <br>(Any access starting with s1.arozos.com will be proxy to the IP address below)<br>
+            Example of virtual directory name: <br>
+            <code>/s1/home</code> <br>(Any access to {this_server}/s1/ will be proxy to the IP address below)
+        </div>
+    </div>
+    <div class="field">
+        <label>IP Address or Domain Name with port</label>
+        <input type="text" id="proxyDomain">
+        <small>E.g. 192.168.0.101:8000 or example.com</small>
+    </div>
+    <div class="field">
+        <div class="ui checkbox">
+            <input type="checkbox" id="reqTls">
+            <label>Proxy Target require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
+        </div>
+    </div>
+    <button class="ui teal button" onclick="newProxyEndpoint();">Create Proxy Endpoint</button>
+</div>
+<div class="ui green message" id="proxyaddSucc" style="display:none">
+    <i class="ui checkmark icon"></i> Proxy Endpoint Added
+</div>
+
+<script>
+    //New Proxy Endpoint
+    function newProxyEndpoint(){
+        var type = $("#ptype").val();
+        var rootname = $("#rootname").val();
+        var proxyDomain = $("#proxyDomain").val();
+        var useTLS = $("#reqTls")[0].checked;
+
+        if (rootname.trim() == ""){
+            $("#rootname").parent().addClass("error");
+            return
+        }else{
+            $("#rootname").parent().removeClass("error");
+        }
+
+        if (proxyDomain.trim() == ""){
+            $("#proxyDomain").parent().addClass("error");
+            return
+        }else{
+            $("#proxyDomain").parent().removeClass("error");
+        }
+
+        //Create the endpoint by calling add
+        $.ajax({
+            url: "./add",
+            data: {type: type, rootname: rootname, tls: useTLS, ep: proxyDomain},
+            success: function(data){
+                if (data.error != undefined){
+                    alert(data.error);
+                }else{
+                    //OK
+                    listVdirs();
+                    listSubd();
+                    $("#proxyaddSucc").stop().slideDown('fast').delay(3000).slideUp('fast');
+
+                    //Clear old data
+                    $("#rootname").val("");
+                    $("#proxyDomain").val("");
+                }
+            }
+        });
+        
+    }
+</script>

+ 75 - 0
web/components/status.html

@@ -0,0 +1,75 @@
+<div id="serverstatus" class="ui message">
+    <h3 class="ui header">
+        <i class="exchange icon"></i>
+        <div class="content">
+          <span id="statusTitle">Offline</span>
+          <div class="sub header" id="statusText">Reverse proxy server is offline</div>
+        </div>
+    </h3>
+</div>
+<div class="ui divider"></div>
+<p>Inbound Port (Port to be proxied)</p>
+<div class="ui action fluid input">
+    <input type="text" id="incomingPort" placeholder="Incoming Port" value="80">
+    <button class="ui button" onclick="handlePortChange();">Apply</button>
+</div>
+<Br>
+<button id="startbtn" class="ui teal button" onclick="startService();">Start Service</button>
+<button id="stopbtn" class="ui red disabled button" onclick="stopService();">Stop Service</button>
+
+<script>
+    //Get the latest server status from proxy server
+    function initRPStaste(){
+        $.get("status", function(data){
+            if (data.Running == true){
+                $("#startbtn").addClass("disabled");
+                $("#stopbtn").removeClass("disabled");
+                $("#serverstatus").addClass("green");
+                $("#statusTitle").text("Online");
+                $("#statusText").text("Reverse proxying request on port: " + data.ListenPort);
+            }else{
+                $("#startbtn").removeClass("disabled");
+                $("#stopbtn").addClass("disabled");
+                $("#statusTitle").text("Offline");
+                $("#statusText").text("Reverse proxy server is offline");
+                $("#serverstatus").removeClass("green");
+            }
+            $("#incomingPort").val(data.ListenPort);
+        });
+    }
+
+    //Start and stop service button
+    function startService(){
+        $.post("enable", {enable: true}, function(data){
+            if (data.error != undefined){
+                errmsg(data.error);
+            }
+            initRPStaste();
+        });
+    }   
+
+    function stopService(){
+        $.post("enable", {enable: false}, function(data){
+            if (data.error != undefined){
+                errmsg(data.error);
+            }
+            initRPStaste();
+        });
+    }
+
+    function handlePortChange(){
+        var newPortValue = $("#incomingPort").val();
+        if (isNaN(newPortValue - 1)){
+            alert("Invalid incoming port value");
+            return;
+        }
+
+        $.post("setIncoming", {incoming: newPortValue}, function(data){
+            if (data.error != undefined){
+                errmsg(data.error);
+            }
+            initRPStaste();
+        });
+    }
+
+</script>

+ 42 - 0
web/components/subd.html

@@ -0,0 +1,42 @@
+<table class="ui celled unstackable compact table">
+    <thead>
+        <tr>
+            <th>Matching Domain</th>
+            <th>Proxy To</th>
+            <th>Remove</th>
+        </tr>
+    </thead>
+    <tbody id="subdList">
+       
+    </tbody>
+</table>
+<button class="ui icon green basic button" onclick="listSubd();"><i class="refresh icon"></i> Refresh</button>
+<script>
+    listSubd();
+    function listSubd(){
+        $("#subdList").html(``);
+        $.get("list?type=subd", function(data){
+            if (data.error !== undefined){
+                    $("#subdList").append(`<tr>
+                    <td data-label="" colspan="3"><i class="remove icon"></i> ${data.error}</td>
+                </tr>`);
+            }else if (data.length == 0){
+                $("#subdList").append(`<tr>
+                    <td data-label="" colspan="3"><i class="checkmark icon"></i> No Subdomain Proxy Record</td>
+                </tr>`);
+            }else{
+                data.forEach(subd => {
+                    let tlsIcon = "";
+                    if (subd.RequireTLS){
+                        tlsIcon = `<i class="lock icon"></i>`;
+                    }
+                    $("#subdList").append(`<tr>
+                        <td data-label="">${subd.MatchingDomain}</td>
+                        <td data-label="">${subd.Domain} ${tlsIcon}</td>
+                        <td data-label=""><button class="ui circular mini red basic button" onclick='deleteEndpoint("subd","${subd.MatchingDomain}")'><i class="remove icon"></i> Remove Subdomain</button></td>
+                    </tr>`);
+                });
+            }
+        });
+    }
+</script>

+ 48 - 0
web/components/vdir.html

@@ -0,0 +1,48 @@
+<table class="ui celled unstackable compact table">
+    <thead>
+        <tr>
+            <th>Virtual Directory</th>
+            <th>Proxy To</th>
+            <th>Remove</th>
+        </tr>
+    </thead>
+    <tbody id="vdirList">
+        <tr>
+            <td data-label="">test</td>
+            <td data-label="">test</td>
+            <td data-label=""><button class="ui circular mini red basic button"><i class="remove icon"></i> Remove Proxy</button></td>
+        </tr>
+    </tbody>
+</table>
+<button class="ui icon green basic button" onclick="listVdirs();"><i class="refresh icon"></i> Refresh</button>
+<script>
+     //Virtual directories functions
+    listVdirs();
+    function listVdirs(){
+        $("#vdirList").html(``);
+        $.get("list?type=vdir", function(data){
+            if (data.error !== undefined){
+                $("#vdirList").append(`<tr>
+                    <td data-label="" colspan="3"><i class="remove icon"></i> ${data.error}</td>
+                </tr>`);
+            }else if (data.length == 0){
+                $("#vdirList").append(`<tr>
+                    <td data-label="" colspan="3"><i class="checkmark icon"></i> No Virtual Directory Record</td>
+                </tr>`);
+            }else{
+                data.forEach(vdir => {
+                    let tlsIcon = "";
+                    if (vdir.RequireTLS){
+                        tlsIcon = `<i title="TLS mode" class="lock icon"></i>`;
+                    }
+                    $("#vdirList").append(`<tr>
+                        <td data-label="">${vdir.Root}</td>
+                        <td data-label="">${vdir.Domain} ${tlsIcon}</td>
+                        <td data-label=""><button class="ui circular mini red basic button"  onclick='deleteEndpoint("vdir","${vdir.Root}")'><i class="remove icon"></i> Remove Virtual Directory</button></td>
+                    </tr>`);
+                });
+            }
+        });
+    }
+
+</script>

+ 10 - 1
web/hosterror.html

@@ -32,9 +32,18 @@
                 margin-top: 0.2em;
             }
 
-            @media (max-width:960px) { 
+            @media (max-width:512px) { 
                 .widescreenOnly{
                     display: none !important;
+                    
+                }
+
+                .four.wide.column:not(.widescreenOnly){
+                    width: 50% !important;
+                }
+
+                .ui.grid{
+                    justify-content: center !important;
                 }
             }
         </style>

+ 107 - 292
web/index.html

@@ -31,151 +31,46 @@
                 <div class="four wide column">
                     <div id="mainmenu" class="ui secondary vertical pointing menu">
                         <a class="item active" tag="status">
-                            Status
+                            <i class="info circle icon"></i>Status
                         </a>
                         <a class="item" tag="vdir">
-                            Virtual Directory
+                            <i class="folder icon"></i> Virtual Directory
                         </a>
                         <a class="item" tag="subd">
-                            Subdomain Proxy
+                            <i class="sitemap icon"></i> Subdomain Proxy
                         </a>
                         <a class="item" tag="rules">
-                            Create Proxy Rules
+                            <i class="plus square icon"></i> Create Proxy Rules
                         </a>
                         <a class="item" tag="setroot">
-                            Set Proxy Root
+                            <i class="home icon"></i> Set Proxy Root
+                        </a>
+                        <div class="ui divider"></div>
+                        <a class="item" tag="cert">
+                            <i class="lock icon"></i> TLS / SSL certificate
                         </a>
                     </div>
                 </div>
                 <div class="twelve wide column">
                     <!-- Status Tab -->
-                    <div id="status" class="functiontab">
-                        <div id="serverstatus" class="ui message">
-                            <h3 class="ui header">
-                                <i class="exchange icon"></i>
-                                <div class="content">
-                                  <span id="statusTitle">Offline</span>
-                                  <div class="sub header" id="statusText">Reverse proxy server is offline</div>
-                                </div>
-                            </h3>
-                        </div>
-                        <div class="ui divider"></div>
-                        <p>Inbound Port (Port to be proxied)</p>
-                        <div class="ui action fluid input">
-                            <input type="text" id="incomingPort" placeholder="Incoming Port" value="80">
-                            <button class="ui button" onclick="handlePortChange();">Apply</button>
-                        </div>
-                        <Br>
-                        <button id="startbtn" class="ui teal button" onclick="startService();">Start Service</button>
-                        <button id="stopbtn" class="ui red disabled button" onclick="stopService();">Stop Service</button>
+                    <div id="status" class="functiontab" target="status.html" style="display: block ;">
+                        <br><br><div class="ui active centered inline loader"></div>
                     </div>
 
                     <!-- Virtual Directory Tab -->
-                    <div id="vdir" class="functiontab">
-                        <table class="ui celled table">
-                            <thead>
-                                <tr>
-                                    <th>Virtual Directory</th>
-                                    <th>Proxy To</th>
-                                    <th>Remove</th>
-                                </tr>
-                            </thead>
-                            <tbody id="vdirList">
-                                <tr>
-                                    <td data-label="">test</td>
-                                    <td data-label="">test</td>
-                                    <td data-label=""><button class="ui circular mini red basic button"><i class="remove icon"></i> Remove Proxy</button></td>
-                                </tr>
-                            </tbody>
-                        </table>
-                    </div>
+                    <div id="vdir" class="functiontab" target="vdir.html"></div>
 
                     <!-- Subdomain Proxy -->
-                    <div id="subd" class="functiontab">
-                        <table class="ui celled table">
-                            <thead>
-                                <tr>
-                                    <th>Matching Domain</th>
-                                    <th>Proxy To</th>
-                                    <th>Remove</th>
-                                </tr>
-                            </thead>
-                            <tbody id="subdList">
-                               
-                            </tbody>
-                        </table>
-                    </div>
+                    <div id="subd" class="functiontab" target="subd.html"></div>
 
-                     <!-- Create Rules -->
-                     <div id="rules" class="functiontab">
-                        <div class=""> 
-                            <h3><i class="ui exchange icon"></i> New Proxy Endpoint</h3>
-                            <p>You can create a proxy endpoing by subdomain or virtual directories</p>
-                            <div class="ui form">
-                                <div class="field">
-                                    <label>Proxy Type</label>
-                                    <div class="ui selection dropdown">
-                                        <input type="hidden" id="ptype" value="subd">
-                                        <i class="dropdown icon"></i>
-                                        <div class="default text">Proxy Type</div>
-                                        <div class="menu">
-                                            <div class="item" data-value="subd">Sub-domain</div>
-                                            <div class="item" data-value="vdir">Virtual Directory</div>
-                                        </div>
-                                    </div>
-                                </div>
-                                <div class="field">
-                                    <label>Subdomain Matching Keyword / Virtual Directory Name</label>
-                                    <input type="text" id="rootname" placeholder="s1.mydomain.com">
-                                    <div class="ui message">
-                                        Example of subdomain matching keyword:<br>
-                                        <code>s1.arozos.com</code> <br>(Any access starting with s1.arozos.com will be proxy to the IP address below)<br>
-                                        Example of virtual directory name: <br>
-                                        <code>/s1/home</code> <br>(Any access to {this_server}/s1/ will be proxy to the IP address below)
-                                    </div>
-                                </div>
-                                <div class="field">
-                                    <label>IP Address or Domain Name with port</label>
-                                    <input type="text" id="proxyDomain">
-                                    <small>E.g. 192.168.0.101:8000 or example.com</small>
-                                </div>
-                                <div class="field">
-                                    <div class="ui checkbox">
-                                        <input type="checkbox" id="reqTls">
-                                        <label>Proxy Target require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
-                                    </div>
-                                </div>
-                                <button class="ui teal button" onclick="newProxyEndpoint();">Create Proxy Endpoint</button>
-                            </div>
-                            <div class="ui green message" id="proxyaddSucc" style="display:none">
-                                <i class="ui checkmark icon"></i> Proxy Endpoint Added
-                            </div>
-                        </div>
-                     </div>
+                    <!-- Create Rules -->
+                    <div id="rules" class="functiontab" target="rules.html"></div>
 
                     <!-- Set proxy root -->
-                    <div id="setroot" class="functiontab">
-                        <h3><i class="ui home icon"></i> Set Proxy Root</h3>
-                        <p>For all routing not found in the proxy rule, will be redirected to the proxy root server.</p>
-                        <div class="ui form">
-                            <div class="field">
-                                <label>Proxy Root</label>
-                                <input type="text" id="proxyRoot">
-                                <small>E.g. localhost:8080</small>
-                            </div>
-                            <div class="field">
-                                <div class="ui checkbox">
-                                    <input type="checkbox" id="rootReqTLS">
-                                    <label>Root require TLS Connection <br><small>(i.e. Your proxy target starts with https://)</small></label>
-                                </div>
-                            </div>
-                        </div>
-                        <br>
-                        <button class="ui teal button" onclick="setProxyRoot()"><i class="home icon" ></i> Set Proxy Root</button>
-                        <div class="ui green message" id="ProxyRootUpdate" style="display:none">
-                            <i class="ui checkmark icon"></i> Proxy Root Updated
-                        </div>
-                    </div>
+                    <div id="setroot" class="functiontab" target="rproot.html"></div>
+
+                     <!-- Set TLS cert -->
+                     <div id="cert" class="functiontab" target="cert.html"></div>
                 </div>
               </div>
             </div>
@@ -186,189 +81,118 @@
             </div>
            
         </div>
-        
+        <br><br>
         <script>
-            initRPStaste();
-            $("#status").slideDown('fast');
-            $(".ui.dropdown").dropdown();
-            $(".ui.checkbox").checkbox();
-
-            $("#mainmenu").find(".item").each(function(){
-                $(this).on("click", function(e){
-                    $("#mainmenu").find(".item").removeClass("active");
-                    $(this).addClass("active");
-                    let targetOpeningTab = $(this).attr("tag");
-                    $(".functiontab").hide();
-                    $("#" + targetOpeningTab).slideDown('fast');
-                });
-            });
-
-            function initRPStaste(){
-                $.get("status", function(data){
-                    if (data.Running == true){
-                        $("#startbtn").addClass("disabled");
-                        $("#stopbtn").removeClass("disabled");
-                        $("#serverstatus").addClass("green");
-                        $("#statusTitle").text("Online");
-                        $("#statusText").text("Reverse proxying request on port: " + data.ListenPort);
+            /*
+                Loader function
+
+                Load all the components view from the 
+                components/ directory into their corrisponding divs
+            */
+            let loadingComponents = 0;
+            function initTabs(callback=undefined){
+                $('.functiontab').each(function(){
+                    let loadTarget = $(this).attr("target");
+                    if (loadTarget != undefined){
+                        $(this).load("./components/" + loadTarget, function(){
+                            loadingComponents--;
+                        });
+                        loadingComponents++;
                     }else{
-                        $("#startbtn").removeClass("disabled");
-                        $("#stopbtn").addClass("disabled");
-                        $("#statusTitle").text("Offline");
-                        $("#statusText").text("Reverse proxy server is offline");
-                        $("#serverstatus").removeClass("green");
+                        $(this).html(`<p>Unable to load components for this tab</p>`);
                     }
-                    $("#incomingPort").val(data.ListenPort);
-                });
+                })
+                if (callback != undefined){
+                    waitInit(callback);
+                }
             }
 
-            function deleteEndpoint(ptype, epoint){
-                if (confirm("Confirm remove proxy for :" + epoint + " (type: " + ptype + ")?")){
-                    $.ajax({
-                        url: "./del",
-                        data: {ep: epoint, ptype: ptype},
-                        success: function(){
-                            listVdirs();
-                            listSubd();
-                        }
-                    })
+            function waitInit(callback = undefined, retryCount = 0){
+                if (loadingComponents > 0 && retryCount < 5){
+                    setTimeout(function(){
+                        waitInit(callback, retryCount++);
+                    }, 300);
+                }else if (loadingComponents == 0){
+                    callback();
+                }else{
+                    alert("Missing component. Please check if your installation is complete.")
                 }
-               
             }
 
-            function startService(){
-                $.post("enable", {enable: true}, function(data){
-                    if (data.error != undefined){
-                        errmsg(data.error);
-                    }
-                    initRPStaste();
-                });
-            }   
+            initTabs(function(){
+                initRPStaste();
 
-            function stopService(){
-                $.post("enable", {enable: false}, function(data){
-                    if (data.error != undefined){
-                        errmsg(data.error);
-                    }
-                    initRPStaste();
-                });
-            }
-
-            function handlePortChange(){
-                var newPortValue = $("#incomingPort").val();
-                if (isNaN(newPortValue - 1)){
-                    alert("Invalid incoming port value");
-                    return;
+                if (window.location.hash.length > 1){
+                    let tabID = window.location.hash.substr(1);
+                    openTabById(tabID);
+                }else{
+                    openTabById("status");
                 }
-
-                $.post("setIncoming", {incoming: newPortValue}, function(data){
-                    if (data.error != undefined){
-                        errmsg(data.error);
-                    }
-                    initRPStaste();
+                $(".ui.dropdown").dropdown();
+                $(".ui.checkbox").checkbox();
+
+                //Click on the current tab
+                $("#mainmenu").find(".item").each(function(){
+                    $(this).on("click", function(event){
+                        let tabid = $(this).attr("tag");
+                        openTabById(tabid);
+                    });
                 });
-            }
 
-            //Virtual directories functions
-            listVdirs();
-            function listVdirs(){
-                $("#vdirList").html(``);
-                $.get("list?type=vdir", function(data){
-                    if (data.error !== undefined){
-                         $("#vdirList").append(`<tr>
-                            <td data-label="" colspan="3"><i class="remove icon"></i> ${data.error}</td>
-                        </tr>`);
-                    }else if (data.length == 0){
-                        $("#vdirList").append(`<tr>
-                            <td data-label="" colspan="3"><i class="checkmark icon"></i> No Virtual Directory Record</td>
-                        </tr>`);
-                    }else{
-                        data.forEach(vdir => {
-                            let tlsIcon = "";
-                            if (vdir.RequireTLS){
-                                tlsIcon = `<i class="lock icon"></i>`;
-                            }
-                            $("#vdirList").append(`<tr>
-                                <td data-label="">${vdir.Root}</td>
-                                <td data-label="">${vdir.Domain} ${tlsIcon}</td>
-                                <td data-label=""><button class="ui circular mini red basic button"  onclick='deleteEndpoint("vdir","${vdir.Root}")'><i class="remove icon"></i> Remove Vdir</button></td>
-                            </tr>`);
-                        });
-                    }
-                });
-            }
+            });
 
-            listSubd();
-            function listSubd(){
-                $("#subdList").html(``);
-                $.get("list?type=subd", function(data){
-                    if (data.error !== undefined){
-                         $("#subdList").append(`<tr>
-                            <td data-label="" colspan="3"><i class="remove icon"></i> ${data.error}</td>
-                        </tr>`);
-                    }else if (data.length == 0){
-                        $("#subdList").append(`<tr>
-                            <td data-label="" colspan="3"><i class="checkmark icon"></i> No Subdomain Proxy Record</td>
-                        </tr>`);
-                    }else{
-                        data.forEach(subd => {
-                            let tlsIcon = "";
-                            if (subd.RequireTLS){
-                                tlsIcon = `<i class="lock icon"></i>`;
-                            }
-                            $("#subdList").append(`<tr>
-                                <td data-label="">${subd.MatchingDomain}</td>
-                                <td data-label="">${subd.Domain} ${tlsIcon}</td>
-                                <td data-label=""><button class="ui circular mini red basic button" onclick='deleteEndpoint("subd","${subd.MatchingDomain}")'><i class="remove icon"></i> Remove Subd</button></td>
-                            </tr>`);
-                        });
+            function getTabButtonById(targetTabId){
+                let targetTabBtn = undefined;
+                $("#mainmenu").find(".item").each(function(){
+                    let tabid = $(this).attr("tag");
+                    
+                    if (tabid == targetTabId){
+                        targetTabBtn = $(this);
                     }
                 });
-            }
 
-            //New Proxy Endpoint
-            function newProxyEndpoint(){
-                var type = $("#ptype").val();
-                var rootname = $("#rootname").val();
-                var proxyDomain = $("#proxyDomain").val();
-                var useTLS = $("#reqTls")[0].checked;
+                return targetTabBtn;
+            }
 
-                if (rootname.trim() == ""){
-                    $("#rootname").parent().addClass("error");
-                    return
-                }else{
-                    $("#rootname").parent().removeClass("error");
+            //Select and open a tab by its tag id
+            function openTabById(tabID){
+                let targetBtn = getTabButtonById(tabID);
+                if (targetBtn == undefined){
+                    alert("Invalid tabid given");
+                    return;
                 }
+                $("#mainmenu").find(".item").removeClass("active");
+                $(targetBtn).addClass("active");
+                $(".functiontab").hide();
+                $("#" + tabID).slideDown('fast');
+                window.location.hash = tabID;
+            }
 
-                if (proxyDomain.trim() == ""){
-                    $("#proxyDomain").parent().addClass("error");
-                    return
-                }else{
-                    $("#proxyDomain").parent().removeClass("error");
-                }
+            
 
-                //Create the endpoint by calling add
-                $.ajax({
-                    url: "./add",
-                    data: {type: type, rootname: rootname, tls: useTLS, ep: proxyDomain},
-                    success: function(data){
-                        if (data.error != undefined){
-                            alert(data.error);
-                        }else{
-                            //OK
+           //Generic functions  
+            function deleteEndpoint(ptype, epoint){
+                if (confirm("Confirm remove proxy for :" + epoint + " (type: " + ptype + ")?")){
+                    $.ajax({
+                        url: "./del",
+                        data: {ep: epoint, ptype: ptype},
+                        success: function(){
                             listVdirs();
                             listSubd();
-                            $("#proxyaddSucc").stop().slideDown('fast').delay(3000).slideUp('fast');
-
-                            //Clear old data
-                            $("#rootname").val("");
-                            $("#proxyDomain").val("");
                         }
-                    }
-                });
+                    })
+                }
                
             }
 
+            
+            
+
+           
+           
+
+            
+
             function setProxyRoot(){
                 var newpr = $("#proxyRoot").val();
                 if (newpr.trim() == ""){
@@ -397,17 +221,8 @@
 
             }
 
-            initRootInfo();
-            function initRootInfo(){
-                $.get("list?type=root", function(data){
-                    if (data == null){
-
-                    }else{
-                        $("#proxyRoot").val(data.Domain);
-                    }
-                });
-            }
-
+           
+            //Show error message
             function errmsg(message){
                 $("#errmsg").html(`<i class="red remove icon"></i> ${message}`);
                 $("#errmsg").slideDown('fast').delay(5000).slideUp('fast');

+ 20 - 4
web/rperror.html

@@ -31,6 +31,22 @@
             .diagramHeader{
                 margin-top: 0.2em;
             }
+
+
+            @media (max-width:512px) { 
+                .widescreenOnly{
+                    display: none !important;
+                    
+                }
+
+                .four.wide.column:not(.widescreenOnly){
+                    width: 50% !important;
+                }
+
+                .ui.grid{
+                    justify-content: center !important;
+                }
+            }
         </style>
     </head>
     <body>
@@ -46,7 +62,7 @@
         <div class="diagram">
             <div class="ui text container">
                 <div class="ui grid">
-                    <div class="four wide column" align="center">
+                    <div class="four wide column widescreenOnly" align="center">
                         <svg version="1.1" id="client_svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
                             width="100%" height="" viewBox="0 0 200 200" enable-background="new 0 0 200 200" xml:space="preserve">
                         <path fill="#C9CACA" d="M184.795,143.037c0,9.941-8.059,18-18,18H33.494c-9.941,0-18-8.059-18-18V44.952c0-9.941,8.059-18,18-18
@@ -64,10 +80,10 @@
                         <h2 class="diagramHeader">Browser</h2>
                         <p style="font-weight: 500; color: #9bca3e;">Working</p>
                     </div>  
-                    <div class="two wide column" style="margin-top: 8em; text-align: center;">
+                    <div class="two wide column widescreenOnly" style="margin-top: 8em; text-align: center;">
                         <i class="ui big grey exchange alternate icon" style="color:rgb(167, 167, 167) !important;"></i>
                     </div>
-                    <div class="four wide column" align="center">
+                    <div class="four wide column widescreenOnly" align="center">
                         <svg version="1.1" id="cloud_svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
                             width="100%" height="" viewBox="0 0 200 200" enable-background="new 0 0 200 200" xml:space="preserve">
                             <ellipse fill="#9FA0A0" cx="46.979" cy="108.234" rx="25.399" ry="25.139"/>
@@ -85,7 +101,7 @@
                         <h2 class="diagramHeader">Reverse Proxy</h2>
                         <p style="font-weight: 500; color: #9bca3e;">Working</p>
                     </div>  
-                    <div class="two wide column" style="margin-top: 8em; text-align: center;">
+                    <div class="two wide column widescreenOnly" style="margin-top: 8em; text-align: center;">
                         <i class="ui big grey exchange alternate icon" style="color:rgb(167, 167, 167) !important;"></i>
                     </div>
                     <div class="four wide column" align="center">