Browse Source

Added user web home function

TC pushbot 5 4 years ago
parent
commit
c74d58bfae
10 changed files with 395 additions and 79 deletions
  1. 1 1
      main.flags.go
  2. 0 70
      mod/user/www/www.go
  3. 0 0
      mod/www/common.go
  4. 91 0
      mod/www/handler.go
  5. 130 0
      mod/www/www.go
  6. 23 0
      network.go
  7. 0 0
      subservice/WsTTY/.disabled
  8. 0 8
      user.go
  9. 150 0
      web/SystemAO/www/config.html
  10. BIN
      web/SystemAO/www/img/homepage.png

+ 1 - 1
main.flags.go

@@ -9,7 +9,7 @@ import (
 	db "imuslab.com/arozos/mod/database"
 	permission "imuslab.com/arozos/mod/permission"
 	user "imuslab.com/arozos/mod/user"
-	"imuslab.com/arozos/mod/user/www"
+	"imuslab.com/arozos/mod/www"
 )
 
 /*

+ 0 - 70
mod/user/www/www.go

@@ -1,70 +0,0 @@
-package www
-
-import (
-	"log"
-	"net/http"
-	"path/filepath"
-	"strings"
-
-	"imuslab.com/arozos/mod/user"
-)
-
-/*
-	www package
-
-	This package is the replacement handler for global homepage function in ArozOS.
-	This allow users to host and create their website using any folder within the user
-	access file system.
-
-*/
-
-type Options struct {
-	UserHandler *user.UserHandler
-}
-
-type Handler struct {
-	Options Options
-}
-
-/*
-	New WebRoot Handler create a new handler for handling and routing webroots
-*/
-func NewWebRootHandler(options Options) *Handler {
-	return &Handler{
-		Options: options,
-	}
-}
-
-func (h *Handler) RouteRequest(w http.ResponseWriter, r *http.Request) {
-	//Check if it is reaching www root folder or any files directly under www.
-	if filepath.ToSlash(filepath.Clean(r.RequestURI)) == "/www" {
-		//Direct access of the root folder. Serve the homepage description.
-		http.ServeFile(w, r, "web/SystemAO/www/index.html")
-		return
-	} else if filepath.ToSlash(filepath.Dir(r.RequestURI)) == "/www" {
-		//Reaching file under www root and not root. Redirect to www root
-		http.Redirect(w, r, "/www/", 307)
-		return
-	}
-
-	//Check the user name of the user root
-	parsedRequestURL := strings.Split(filepath.ToSlash(filepath.Clean(r.RequestURI)[1:]), "/")
-
-	//Malparsed URL. Ignore request
-	if len(parsedRequestURL) < 2 {
-		http.NotFound(w, r)
-		return
-	}
-
-	//Extract user information
-	username := parsedRequestURL[1]
-
-	_, err := h.Options.UserHandler.GetUserInfoFromUsername(username)
-	if err != nil {
-		http.NotFound(w, r)
-		return
-	}
-
-	log.Println("Serving user webroot: ", username)
-	sendOK(w)
-}

+ 0 - 0
mod/user/www/common.go → mod/www/common.go


+ 91 - 0
mod/www/handler.go

@@ -0,0 +1,91 @@
+package www
+
+import (
+	"encoding/json"
+	"errors"
+	"net/http"
+)
+
+func (h *Handler) CheckUserHomePageEnabled(username string) bool {
+	result := false
+	currentHomePageMode := "false"
+	err := h.Options.Database.Read("www", username+"_enable", &currentHomePageMode)
+	if err != nil {
+		//Not exists. Assume false
+		result = false
+	} else {
+		result = (currentHomePageMode == "true")
+	}
+
+	return result
+}
+
+func (h *Handler) GetUserWebRoot(username string) (string, error) {
+	webRoot := ""
+	if h.Options.Database.KeyExists("www", username+"_webroot") {
+		err := h.Options.Database.Read("www", username+"_webroot", &webRoot)
+		if err != nil {
+			return "", err
+		}
+
+		return webRoot, nil
+	} else {
+		return "", errors.New("Webroot not defined")
+	}
+
+}
+
+func (h *Handler) HandleToggleHomepage(w http.ResponseWriter, r *http.Request) {
+	userinfo, err := h.Options.UserHandler.GetUserInfoFromRequest(w, r)
+	if err != nil {
+		sendErrorResponse(w, "User not logged in")
+		return
+	}
+
+	set, _ := mv(r, "set", true)
+	if set == "" {
+		//Read mode
+		result := h.CheckUserHomePageEnabled(userinfo.Username)
+		js, _ := json.Marshal(result)
+		sendJSONResponse(w, string(js))
+
+	} else {
+		//Set mode
+		if set == "true" {
+			//Enable homepage
+			h.Options.Database.Write("www", userinfo.Username+"_enable", "true")
+		} else {
+			//Disable homepage
+			h.Options.Database.Write("www", userinfo.Username+"_enable", "false")
+		}
+		sendOK(w)
+	}
+
+}
+
+func (h *Handler) HandleSetWebRoot(w http.ResponseWriter, r *http.Request) {
+	userinfo, err := h.Options.UserHandler.GetUserInfoFromRequest(w, r)
+	if err != nil {
+		sendErrorResponse(w, "User not logged in")
+		return
+	}
+
+	set, _ := mv(r, "set", true)
+	if set == "" {
+		//Read mode
+		webroot, err := h.GetUserWebRoot(userinfo.Username)
+		if err != nil {
+			sendErrorResponse(w, err.Error())
+			return
+		}
+
+		js, _ := json.Marshal(webroot)
+		sendJSONResponse(w, string(js))
+
+	} else {
+		//Set mode
+		h.Options.Database.Write("www", userinfo.Username+"_webroot", set)
+
+		sendOK(w)
+	}
+}

+ 130 - 0
mod/www/www.go

@@ -0,0 +1,130 @@
+package www
+
+import (
+	"log"
+	"net/http"
+	"net/url"
+	"path/filepath"
+	"strings"
+
+	"imuslab.com/arozos/mod/database"
+	"imuslab.com/arozos/mod/user"
+)
+
+/*
+	www package
+
+	This package is the replacement handler for global homepage function in ArozOS.
+	This allow users to host and create their website using any folder within the user
+	access file system.
+
+*/
+
+type Options struct {
+	UserHandler *user.UserHandler
+	Database    *database.Database
+}
+
+type Handler struct {
+	Options Options
+}
+
+/*
+	New WebRoot Handler create a new handler for handling and routing webroots
+*/
+func NewWebRootHandler(options Options) *Handler {
+	//Create the homepage database table
+	options.Database.NewTable("www")
+
+	//Return the handler object
+	return &Handler{
+		Options: options,
+	}
+}
+
+func (h *Handler) RouteRequest(w http.ResponseWriter, r *http.Request) {
+	//Check if it is reaching www root folder or any files directly under www.
+	if filepath.ToSlash(filepath.Clean(r.RequestURI)) == "/www" {
+		//Direct access of the root folder. Serve the homepage description.
+		http.ServeFile(w, r, "web/SystemAO/www/index.html")
+		return
+	} else if filepath.ToSlash(filepath.Dir(r.RequestURI)) == "/www" {
+		//Reaching file under www root and not root. Redirect to www root
+		http.Redirect(w, r, "/www/", 307)
+		return
+	}
+
+	//Escape the URL
+	decodedValue, err := url.QueryUnescape(r.RequestURI)
+	if err != nil {
+		//failed to decode. Just use its raw value
+		decodedValue = r.RequestURI
+	}
+
+	//Check the user name of the user root
+	parsedRequestURL := strings.Split(filepath.ToSlash(filepath.Clean(decodedValue)[1:]), "/")
+	log.Println(parsedRequestURL)
+	//Malparsed URL. Ignore request
+	if len(parsedRequestURL) < 2 {
+
+		http.NotFound(w, r)
+		return
+	}
+
+	//Extract user information
+	username := parsedRequestURL[1]
+
+	userinfo, err := h.Options.UserHandler.GetUserInfoFromUsername(username)
+	if err != nil {
+		http.NotFound(w, r)
+		return
+	}
+
+	//Check if this user enabled homepage
+	enabled := h.CheckUserHomePageEnabled(userinfo.Username)
+	if !enabled {
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte("404 - Page not found"))
+		return
+	}
+
+	//Check if the user have his webroot correctly configured
+	webroot, err := h.GetUserWebRoot(userinfo.Username)
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		w.Write([]byte("500 - Internal Server Error"))
+		return
+	}
+
+	//User webroot real path conversion
+	webrootRealpath, err := userinfo.VirtualPathToRealPath(webroot)
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		w.Write([]byte("500 - Internal Server Error"))
+		return
+	}
+
+	//Perform path rewrite
+	rewrittenPath := strings.Join(parsedRequestURL[2:], "/")
+
+	//Actual accessing file path
+	targetFilePath := filepath.ToSlash(filepath.Join(webrootRealpath, rewrittenPath))
+
+	//Check if the file exists
+	if !fileExists(targetFilePath) {
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte("404 - Page not found"))
+		return
+	}
+
+	//Fix mimetype of js files on Windows 10 bug
+	if filepath.Ext(targetFilePath) == ".js" {
+		w.Header().Set("Content-Type", "application/javascript")
+	}
+
+	//Record the client IP for analysis, to be added in the future if needed
+
+	//Serve the file
+	http.ServeFile(w, r, targetFilePath)
+
+}

+ 23 - 0
network.go

@@ -10,6 +10,7 @@ import (
 	ssdp "imuslab.com/arozos/mod/network/ssdp"
 	upnp "imuslab.com/arozos/mod/network/upnp"
 	prout "imuslab.com/arozos/mod/prouter"
+	"imuslab.com/arozos/mod/www"
 )
 
 var (
@@ -56,6 +57,28 @@ func NetworkServiceInit() {
 	//Start the port forward configuration interface
 	portForwardInit()
 
+	//Start userhomepage if enabled
+	//Handle user webroot routings if homepage is enabled
+	if *allow_homepage {
+		userWwwHandler = www.NewWebRootHandler(www.Options{
+			UserHandler: userHandler,
+			Database:    sysdb,
+		})
+
+		router.HandleFunc("/system/network/www/toggle", userWwwHandler.HandleToggleHomepage)
+		router.HandleFunc("/system/network/www/webRoot", userWwwHandler.HandleSetWebRoot)
+
+		//Register as a system setting
+		registerSetting(settingModule{
+			Name:     "Personal Page",
+			Desc:     "Personal Web Page",
+			IconPath: "SystemAO/www/img/homepage.png",
+			Group:    "Network",
+			StartDir: "SystemAO/www/config.html",
+		})
+
+	}
+
 }
 
 func StartNetworkServices() {

+ 0 - 0
subservice/WsTTY/.disabled


+ 0 - 8
user.go

@@ -19,7 +19,6 @@ import (
 	module "imuslab.com/arozos/mod/modules"
 	prout "imuslab.com/arozos/mod/prouter"
 	user "imuslab.com/arozos/mod/user"
-	"imuslab.com/arozos/mod/user/www"
 )
 
 func UserSystemInit() {
@@ -71,13 +70,6 @@ func UserSystemInit() {
 		RequireAdmin: true,
 	})
 
-	//Handle user webroot routings if homepage is enabled
-	if *allow_homepage {
-		userWwwHandler = www.NewWebRootHandler(www.Options{
-			UserHandler: userHandler,
-		})
-	}
-
 	//Handle db / auth / permissions related functions that requires user permission systems. See user.go
 	user_createPostUserHandlers()
 }

+ 150 - 0
web/SystemAO/www/config.html

@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <!-- Meta headers for mobile devices-->
+        <meta name="apple-mobile-web-app-capable" content="yes" />
+        <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
+        <meta charset="UTF-8">
+        <link rel="icon" href="favicon.png">
+
+        <!-- CSS and JavaScript, access to scripe folder is not blocked for non logged in users-->
+        <link rel="stylesheet" href="../../../script/semantic/semantic.min.css">
+        <script src="../../../script/jquery.min.js"></script>
+        <script src="../../../script/ao_module.js"></script>
+        <script src="../../../script/semantic/semantic.min.js"></script>
+        <title>Personal Homepage</title>
+        <style>
+            body{
+                background-color:white;
+            }
+        </style>
+    </head>
+    <body>
+        <br>
+        <div class="ui container">
+            <div class="ui basic segment">
+                <h3 class="ui header">
+                    <i class="home icon"></i>
+                    <div class="content">
+                        Personal Home Page
+                        <div class="sub header">Create your own home page on ArozOS</div>
+                    </div>
+                </h3>
+            </div>
+            <div class="ui divider"></div>
+            <div class="ui message">
+                <div class="header">
+                    What is Personal Home Page?
+                </div>
+                <ul class="list">
+                    <li>You can host a static website using ArozOS</li>
+                    <li>All website source are accessiable by your File Manager located inside your user file system</li>
+                    <li>Everyone can access your home page using <a><span class=".protocol">http:</span>//<span class="hostname"></span>:<span class="port"></span>/www/<span class="username">{your_username}</span>/</a></li>
+                </ul>
+            </div>
+            <div class="ui container">
+                <h4>Toggle Personal Home Page</h4>
+                <div class="ui toggle checkbox">
+                    <input type="checkbox" id="enablehp" onchange="updateHomepageStatus(this.checked);">
+                    <label>Enable personal home page</label>
+                </div>
+                <div id="updateSuceed" style="display:none;" class="ui green inverted segment"><i class="checkmark icon"></i> Home Page Status Updated</div>
+                <br>
+                <h4>Select web root folder location</h4>
+                <div class="ui action fluid input">
+                    <input id="webroot" type="text" placeholder="Select Location" readonly="true">
+                    <button class="ui black button" onclick="selectWebRootLocation();"><i class="folder open icon"></i> Open</button>
+                </div>
+                <div class="ui yellow message"><i class="warning icon"></i> All files located inside the web root will be accessiable by everyone on the internet.</div>
+                <div id="webrootSetSuceed" style="display:none;" class="ui green inverted segment"><i class="checkmark icon"></i> Web Root Location Updated</div>
+                <br>
+                <small>*All changes will be saved automatically upon updates.</small>
+            </div>
+            <br><br>
+        </div>
+        <script>
+            var ignoreChange = true;
+
+            //Update the user information
+            $.get("../../../system/desktop/user", function(data){
+                if (data.error == undefined){
+                    $(".username").text(data.Username);
+                }
+            });
+
+            //Update tutorial information
+            $(".hostname").text(window.location.hostname);
+            $(".port").text(window.location.port);
+            if (window.location.port == ""){
+                $(".port").text("80");
+            }
+            $(".protocol").text(location.protocol);
+
+            initHomePageInfo();
+            function initHomePageInfo(){
+                $.get("../../../system/network/www/toggle", function(data){
+                    if (data.error == undefined){
+                        $("#enablehp")[0].checked = data;
+                        ignoreChange = false;
+                    }
+                });
+
+                $.get("../../../system/network/www/webRoot", function(data){
+                    if (data.error !== undefined){
+                        //Not defined.
+
+                    }else{
+                        //Defined. Set it to the input field
+                        $("#webroot").val(data);
+                    }
+                });
+            }
+
+
+            function updateHomepageStatus(enableHomePage = false){
+                if (ignoreChange){
+                    return;
+                }
+
+                $.ajax({
+                    url: "../../../system/network/www/toggle",
+                    data: {set: enableHomePage},
+                    success: function(data){
+                        if (data.error !== undefined){
+                            alert(data.error);
+                        }else{
+                            $("#updateSuceed").slideDown("fast").delay(3000).slideUp("fast");
+                        }
+                        
+                    }
+                });
+            }
+
+            function selectWebRootLocation(){
+                ao_module_openFileSelector(webrootSelected, "user:/", "folder",false);
+            }
+
+            function webrootSelected(filedata){
+                for (var i=0; i < filedata.length; i++){
+                    var filename = filedata[i].filename;
+                    var filepath = filedata[i].filepath;
+                    $("#webroot").val(filepath);
+                }
+
+                //Update the server database
+                $.ajax({
+                    url: "../../../system/network/www/webRoot",
+                    data: {set: $("#webroot").val()},
+                    success: function(data){
+                        if (data.error !== undefined){
+                            alert(data.error);
+                        }else{
+                            $("#webrootSetSuceed").slideDown("fast").delay(3000).slideUp("fast");
+                        }
+                        
+                    }
+                });
+            }
+        </script>
+    </body>
+</html>

BIN
web/SystemAO/www/img/homepage.png