Browse Source

Completed virtual disk properties tool

TC pushbot 5 4 years ago
parent
commit
22b9f6957b

+ 5 - 0
disk.go

@@ -10,6 +10,7 @@ import (
 	"log"
 	"net/http"
 
+	"imuslab.com/arozos/mod/disk/diskcapacity"
 	"imuslab.com/arozos/mod/disk/diskmg"
 	diskspace "imuslab.com/arozos/mod/disk/diskspace"
 	smart "imuslab.com/arozos/mod/disk/smart"
@@ -32,6 +33,10 @@ func DiskServiceInit() {
 	//Disk Space Display endpoint
 	router.HandleFunc("/system/disk/space/list", diskspace.HandleDiskSpaceList)
 
+	//Handle Virtual Disk Properties display endpoints
+	dc := diskcapacity.NewCapacityResolver(userHandler)
+	router.HandleFunc("/system/disk/space/resolve", dc.HandleCapacityResolving)
+
 	//New Large File Scanner
 	lfs := sortfile.NewLargeFileScanner(userHandler)
 	router.HandleFunc("/system/disk/space/largeFiles", lfs.HandleLargeFileList)

+ 119 - 0
mod/common/common.go

@@ -0,0 +1,119 @@
+package common
+
+import (
+	"bufio"
+	"encoding/base64"
+	"errors"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"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
+	}
+
+}
+
+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, _ := ioutil.ReadAll(reader)
+	encoded := base64.StdEncoding.EncodeToString(content)
+	return string(encoded), nil
+}

+ 131 - 0
mod/disk/diskcapacity/diskcapacity.go

@@ -0,0 +1,131 @@
+package diskcapacity
+
+import (
+	"encoding/json"
+	"errors"
+	"net/http"
+	"path/filepath"
+	"runtime"
+	"strings"
+
+	"imuslab.com/arozos/mod/common"
+	"imuslab.com/arozos/mod/disk/diskspace"
+	"imuslab.com/arozos/mod/user"
+)
+
+/*
+	Disk Capacity
+	This is a simple module to check how many storage space is remaining
+	on a given directory in accessiable file system paths
+
+	Author: tobychui
+*/
+
+type Resolver struct {
+	UserHandler *user.UserHandler
+}
+
+type Capacity struct {
+	PhysicalDevice    string //The ID of the physical device, like C:/ or /dev/sda1
+	MountingHierarchy string //The Mounting Hierarchy of the vroot
+	Used              int64  //Used capacity in bytes
+	Avilable          int64  //Avilable capacity in bytes
+	Total             int64  //Total capacity in bytes
+}
+
+//Create a new Capacity Resolver with the given user handler
+func NewCapacityResolver(u *user.UserHandler) *Resolver {
+	return &Resolver{
+		UserHandler: u,
+	}
+}
+
+func (cr *Resolver) HandleCapacityResolving(w http.ResponseWriter, r *http.Request) {
+	//Check if the request user is authenticated
+	userinfo, err := cr.UserHandler.GetUserInfoFromRequest(w, r)
+	if err != nil {
+		common.SendErrorResponse(w, "User not logged in")
+		return
+	}
+
+	//Get vpath from paramter
+	vpath, err := common.Mv(r, "path", true)
+	if err != nil {
+		common.SendErrorResponse(w, "Vpath is not defined")
+		return
+	}
+
+	capinfo, err := cr.ResolveCapacityInfo(userinfo.Username, vpath)
+	if err != nil {
+		common.SendErrorResponse(w, "Unable to resolve path capacity information: "+err.Error())
+		return
+	}
+
+	//Get Storage Hierarcy
+	fsh, err := userinfo.GetFileSystemHandlerFromVirtualPath(vpath)
+	if err != nil {
+		capinfo.MountingHierarchy = "Unknown"
+	} else {
+		capinfo.MountingHierarchy = fsh.Hierarchy
+	}
+
+	//Send the requested path capacity information
+	js, _ := json.Marshal(capinfo)
+	common.SendJSONResponse(w, string(js))
+
+}
+
+func (cr *Resolver) ResolveCapacityInfo(username string, vpath string) (*Capacity, error) {
+	//Resolve the vpath for this user
+	userinfo, err := cr.UserHandler.GetUserInfoFromUsername(username)
+	if err != nil {
+		return nil, err
+	}
+
+	realpath, err := userinfo.VirtualPathToRealPath(vpath)
+	if err != nil {
+		return nil, err
+	}
+
+	realpath = filepath.ToSlash(filepath.Clean(realpath))
+	return cr.GetCapacityInfo(realpath)
+}
+
+func (cr *Resolver) GetCapacityInfo(realpath string) (*Capacity, error) {
+	if runtime.GOOS == "windows" {
+		//Windows
+		//Extract the root disk number
+		rpathAbs, err := filepath.Abs(realpath)
+		if err != nil {
+			return nil, err
+		}
+
+		//Extract disk ID from path
+		rpathAbs = filepath.ToSlash(filepath.Clean(rpathAbs))
+		diskRoot := strings.Split(rpathAbs, "/")[0]
+
+		//Match the disk space info generated from diskspace
+		logicDiskInfo := diskspace.GetAllLogicDiskInfo()
+		for _, ldi := range logicDiskInfo {
+			if strings.TrimSpace(ldi.Device) == strings.TrimSpace(diskRoot) {
+				//Matching device ID
+				return &Capacity{
+					PhysicalDevice: ldi.Device,
+					Used:           ldi.Used,
+					Avilable:       ldi.Available,
+					Total:          ldi.Volume,
+				}, nil
+			}
+		}
+
+	} else if runtime.GOOS == "darwin" {
+		//MacOS.
+
+	} else {
+		//Assume Linux
+		//Use command: df -P {abs_path}
+
+	}
+
+	return nil, errors.New("Unable to resolve matching disk capacity information")
+}

+ 0 - 2
mod/disk/diskspace/diskspace.go

@@ -142,8 +142,6 @@ func GetAllLogicDiskInfo() []LogicalDiskSpaceInfo {
 
 		return arr
 	}
-
-	return []LogicalDiskSpaceInfo{}
 }
 
 func stringToInt64(value string) (int64, error) {

+ 71 - 12
web/SystemAO/disk/diskprop.html

@@ -43,7 +43,11 @@
            border-top: 0px solid transparent !important; 
         }
         
-
+        .overlap.bar{
+            position: absolute !important; 
+            top:0px; 
+            left: 0px;
+        }
     </style>
 </head>
 <body>
@@ -56,11 +60,11 @@
         <div class="ui divider"></div>
         <div class="ui container">
             <div class="ui grid">
-                <div class="three wide center aligned column" style="padding: 0px;">
-                    <img class="ui image" style="max-width: 50px; pointer-events: none;" src="../../img/system/drive-virtual.svg">
+                <div class="three wide right aligned column" style="text-align: center; padding: 0px;">
+                    <img class="ui image" style="position: absolute; top: 0px; left: calc(50% - 25px); max-width: 50px; pointer-events: none;" src="../../img/system/drive-virtual.svg">
                 </div>
-                <div class="thirteen wide column" style="padding: 0px; padding-top: 1em; padding-left: 1em;">
-                    <div class="ui fluid mini input">
+                <div class="thirteen wide column" style="padding: 0px; padding-top: 0.8em; padding-left: 1em;">
+                    <div class="ui fluid small input">
                         <input id="vrootName" type="text" placeholder="" readonly="true">
                     </div>
                 </div>
@@ -72,23 +76,23 @@
             <tbody>
             <tr>
                 <td>
-                    Disk Hierarchy: 
+                    Physical Disk: 
                 </td>
-                <td>
+                <td id="phydisk">
                     
                 </td>
             </tr>
             <tr>
                 <td>
-                    File System: 
+                    Disk Hierarchy: 
                 </td>
-                <td>
+                <td id="hierarchy">
                     
                 </td>
             </tr>
         </table>
         <div class="ui divider" style="margin-top: 8px; margin-bottom: 8px;"></div>
-        <table class="ui very basic collapsing compact unstackable table" style="vertical-align: middle;">
+        <table class="ui very basic collapsing compact unstackable table" style="vertical-align: middle; font-size:90%;">
             <tbody>
             <tr>
                 <td>
@@ -135,6 +139,17 @@
                 </td>
             </tr>
         </table>
+        <div class="ui small progress">
+            <div class="overlap bar" style="width: 100%; background-color: #b9b9b9;">
+                <div class="progress"></div>
+            </div>
+            <div id="totalUsedSpace" class="overlap bar" style="width: 50%; background-color: #52bdf2;">
+                <div class="progress"></div>
+            </div>
+            <div id="userUsedSpace" class="overlap bar" style="width: 30%; background-color: #e5e75c;">
+                <div class="progress"></div>
+            </div>
+        </div>
         <div class="ui divider" style="margin-top: 8px; margin-bottom: 8px;"></div>
         <br>
            <button class="ui right floated button" onclick="ao_module_close();">Close</button>
@@ -147,14 +162,58 @@
         var files = ao_module_loadInputFiles();
         var fileProperties = [];
         var fileInfo = {};
+        var diskInfo = {};
 
         //There are something to load. Load the vroot properties
         if (window.location.hash.length > 0 && typeof(files) != null){
-            getFileProp(files[0], function(data){
-                console.log(data);
+            var rootName = files[0].split("/")[0];
+            $("#vrootName").val(rootName + "/");
+
+            getDiskProp(files[0], function(data){
+                $("#usedSpaceInBytes").text(new Intl.NumberFormat('en-US').format(data.Used));
+                $("#usedSpaceInHumanReadableFormat").text(bytesToSize(data.Used));
+
+                $("#usableSpaceInBytes").text(new Intl.NumberFormat('en-US').format(data.Avilable));
+                $("#usableSpaceInHumanReadableFormat").text(bytesToSize(data.Avilable));
+
+                $("#totalSpaceInBytes").text(new Intl.NumberFormat('en-US').format(data.Total));
+                $("#totalSpaceInHumanReadableFormat").text(bytesToSize(data.Total));
+
+                $("#phydisk").text(data.PhysicalDevice + "/");
+                var s = data.MountingHierarchy;
+                $("#hierarchy").text(s[0].toUpperCase() + s.slice(1));
+                
+                $("#totalUsedSpace").css("width", (data.Used / data.Total) * 100 + "%");
+
+                getFileProp(files[0], function(folderdata){
+                    //User Used Space
+                    $("#usedVirtualSpaceInByte").text(new Intl.NumberFormat('en-US').format(folderdata.Filesize));
+                    $("#usedVirtualSpaceInHumanReadableFormat").text(bytesToSize(folderdata.Filesize));
+                    
+                    $("#userUsedSpace").css("width", (folderdata.Filesize / data.Total) * 100 + "%");
+                });
             });
         }
 
+        function bytesToSize(bytes) {
+            var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
+            if (bytes == 0) return '0 Byte';
+            var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
+            return (bytes / Math.pow(1024, i) * 100 / 100).toFixed(1) + ' ' + sizes[i];
+        }
+
+        function getDiskProp(vpath, callback){
+            $.ajax({
+                url: "../../system/disk/space/resolve",
+                data: {path: vpath},
+                method: "POST",
+                success: function(data){
+                    diskInfo = data;
+                    callback(data);
+                }
+            })
+        }
+
         function getFileProp(vpath, callback){
             $.ajax({
                 url: "../../system/file_system/getProperties",

+ 1 - 1
web/SystemAO/file_system/file_explorer.html

@@ -4055,7 +4055,7 @@
                 url: "SystemAO/disk/diskprop.html#" + hashPassthrough,
                 width: 410,
                 height: 580,
-                appicon: "SystemAO/file_system/img/properties.png",
+                appicon: "img/system/drive.svg",
                 title: "Disk Properties",
             });
         }