Browse Source

Push before testing remove function

Toby Chui 3 years ago
parent
commit
2c96608f72

+ 44 - 0
file_system.go

@@ -31,6 +31,7 @@ import (
 	fsp "imuslab.com/arozos/mod/filesystem/fspermission"
 	"imuslab.com/arozos/mod/filesystem/fuzzy"
 	hidden "imuslab.com/arozos/mod/filesystem/hidden"
+	"imuslab.com/arozos/mod/filesystem/localversion"
 	metadata "imuslab.com/arozos/mod/filesystem/metadata"
 	"imuslab.com/arozos/mod/filesystem/shortcut"
 	module "imuslab.com/arozos/mod/modules"
@@ -90,6 +91,7 @@ func FileSystemInit() {
 	router.HandleFunc("/system/file_system/zipHandler", system_fs_zipHandler)
 	router.HandleFunc("/system/file_system/getProperties", system_fs_getFileProperties)
 	router.HandleFunc("/system/file_system/pathTranslate", system_fs_handlePathTranslate)
+	router.HandleFunc("/system/file_system/versionHistory", system_fs_FileVersionHistory)
 
 	router.HandleFunc("/system/file_system/handleFilePermission", system_fs_handleFilePermission)
 	router.HandleFunc("/system/file_system/search", system_fs_handleFileSearch)
@@ -2524,6 +2526,48 @@ func system_fs_zipHandler(w http.ResponseWriter, r *http.Request) {
 
 }
 
+//Manage file version history
+func system_fs_FileVersionHistory(w http.ResponseWriter, r *http.Request) {
+	userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
+	if err != nil {
+		sendErrorResponse(w, err.Error())
+		return
+	}
+
+	path, err := mv(r, "path", true)
+	if err != nil {
+		sendErrorResponse(w, "Invalid path given")
+		return
+	}
+
+	opr, _ := mv(r, "opr", true)
+	if opr == "" {
+		//List file history
+		rpath, err := userinfo.VirtualPathToRealPath(path)
+		if err != nil {
+			sendErrorResponse(w, "Unable to translate virtual path")
+			return
+		}
+
+		fileVersionData, err := localversion.GetFileVersionData(rpath)
+		if err != nil {
+			sendErrorResponse(w, "Unable to load version information: "+err.Error())
+			return
+		}
+
+		js, _ := json.Marshal(fileVersionData)
+		sendJSONResponse(w, string(js))
+
+	} else if opr == "delete" {
+		//Delete file history of given history ID
+
+	} else if opr == "restore" {
+		//Restore file history of given history ID
+
+	}
+
+}
+
 //Translate path from and to virtual and realpath
 func system_fs_handlePathTranslate(w http.ResponseWriter, r *http.Request) {
 	userinfo, err := userHandler.GetUserInfoFromRequest(w, r)

+ 127 - 0
mod/filesystem/localversion/localversion.go

@@ -0,0 +1,127 @@
+package localversion
+
+import (
+	"errors"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	"imuslab.com/arozos/mod/filesystem"
+)
+
+/*
+	localversion.go
+
+	This is a local version management module for arozos files
+	Author: tobychui
+
+*/
+
+type FileSnapshot struct {
+	HistoryID     string
+	Filename      string
+	ModTime       int64
+	OverwriteTime string
+	Filesize      int64
+	Relpath       string
+}
+
+type VersionList struct {
+	CurrentFile   string
+	LatestModtime int64
+	Versions      []*FileSnapshot
+}
+
+func GetFileVersionData(realFilepath string) (*VersionList, error) {
+	mtime, _ := filesystem.GetModTime(realFilepath)
+	versionList := VersionList{
+		CurrentFile:   filepath.Base(realFilepath),
+		LatestModtime: mtime,
+		Versions:      []*FileSnapshot{},
+	}
+	//Example folder structure: ./.localver/{date_time}/{file}
+	expectedVersionFiles := filepath.Join(filepath.Dir(realFilepath), ".localver", "*", filepath.Base(realFilepath))
+	versions, err := filepath.Glob(filepath.ToSlash(expectedVersionFiles))
+	if err != nil {
+		return &versionList, err
+	}
+
+	//Reverse the versions so latest version on top
+	sort.Sort(sort.Reverse(sort.StringSlice(versions)))
+
+	for _, version := range versions {
+		historyID := filepath.Base(filepath.Dir(version))
+		mtime, _ := filesystem.GetModTime(version)
+		overwriteDisplayTime := strings.ReplaceAll(strings.Replace(strings.Replace(historyID, "-", "/", 2), "-", ":", 2), "_", " ")
+		versionList.Versions = append(versionList.Versions, &FileSnapshot{
+			HistoryID:     historyID,
+			Filename:      filepath.Base(version),
+			ModTime:       mtime,
+			OverwriteTime: overwriteDisplayTime,
+			Filesize:      filesystem.GetFileSize(version),
+			Relpath:       ".localver/" + historyID + "/" + filepath.Base(version),
+		})
+	}
+
+	return &versionList, nil
+}
+
+func RestoreFileHistory(originalFilepath string, histroyID string) error {
+	expectedVersionFile := filepath.Join(filepath.Dir(originalFilepath), ".localver", filepath.Base(histroyID), filepath.Base(originalFilepath))
+	if !filesystem.FileExists(expectedVersionFile) {
+		return errors.New("File version not exists")
+	}
+
+	//Restore it
+	os.Rename(originalFilepath, originalFilepath+".backup")
+	filesystem.BasicFileCopy(expectedVersionFile, originalFilepath)
+
+	//Check if it has been restored correctly
+	versionFileHash, _ := filesystem.GetFileMD5Sum(expectedVersionFile)
+	copiedFileHash, _ := filesystem.GetFileMD5Sum(expectedVersionFile)
+	if versionFileHash != copiedFileHash {
+		//Rollback failed. Restore backup file
+		os.Rename(originalFilepath+".backup", originalFilepath)
+		return errors.New("Unable to restore file: file hash mismatch after restore")
+	}
+
+	//OK! Delete the backup file
+	os.Remove(originalFilepath + ".backup")
+
+	//Delete all history versions that is after the restored versions
+	expectedVersionFiles := filepath.Join(filepath.Dir(originalFilepath), ".localver", "*", filepath.Base(originalFilepath))
+	versions, err := filepath.Glob(filepath.ToSlash(expectedVersionFiles))
+	if err != nil {
+		return err
+	}
+
+	enableRemoval := false
+	for _, version := range versions {
+		if enableRemoval {
+			//Remove this version as this is after the restored version
+			os.Remove(version)
+		} else {
+			thisHistoryId := filepath.Base(filepath.Dir(version))
+			if thisHistoryId == histroyID {
+				//Match. Tag enable Removal
+				enableRemoval = true
+
+				//Remove this version
+				os.Remove(version)
+			}
+		}
+
+	}
+
+	return nil
+}
+
+func RemoveFileHistory(originalFilepath string, histroyID string) error {
+	return nil
+}
+
+func CreateFileSnapshot(realFilepath string) error {
+
+	return nil
+}

+ 33 - 0
mod/filesystem/static.go

@@ -1,8 +1,12 @@
 package filesystem
 
 import (
+	"crypto/md5"
+	"crypto/sha256"
+	"encoding/hex"
 	"errors"
 	"fmt"
+	"io"
 	"log"
 	"os"
 	"os/exec"
@@ -366,3 +370,32 @@ func GetPhysicalRootFromPath(filename string) (string, error) {
 	pathChunks := strings.Split(filename, "/")
 	return pathChunks[0], nil
 }
+
+func GetFileSHA256Sum(filename string) (string, error) {
+	f, err := os.Open(filename)
+	if err != nil {
+		return "", err
+	}
+	defer f.Close()
+
+	h := sha256.New()
+	if _, err := io.Copy(h, f); err != nil {
+		return "", err
+	}
+
+	return hex.EncodeToString(h.Sum(nil)), nil
+}
+
+func GetFileMD5Sum(filename string) (string, error) {
+	file, err := os.Open(filename)
+	if err != nil {
+		return "", err
+	}
+	defer file.Close()
+
+	h := md5.New()
+	if _, err := io.Copy(h, file); err != nil {
+		return "", err
+	}
+	return hex.EncodeToString(h.Sum(nil)), nil
+}

+ 1 - 1
mod/storage/webdav/webdav.go

@@ -221,7 +221,7 @@ func (s *Server) HandleRequest(w http.ResponseWriter, r *http.Request) {
 	username, password, ok := r.BasicAuth()
 	if !ok {
 		//User not logged in.
-		log.Println("Not logged in!")
+		//log.Println("Not logged in!")
 		w.Header().Set("WWW-Authenticate", `Basic realm="Login with your `+s.hostname+` account"`)
 		w.WriteHeader(http.StatusUnauthorized)
 		return

+ 12 - 0
web/SystemAO/file_system/file_properties.html

@@ -28,6 +28,7 @@
             </div>
             <br>
             <button style="display:none;" class="ui small blue fluid button singleFileOnly" onclick="changeDefaultWebApp();" locale="button/changeDefault">Change Default WebApp</button>
+            <button style="display:none; margin-top: 4px;" class="ui small basic fluid button singleFileOnly" onclick="viewVersionHistory();" locale="button/versionHistory">View Version History</button>
             <button style="margin-top: 4px;" class="ui small fluid button linuxonly" onclick="openFilePermissionPanel();" locale="button/changeFilePermission">Change File Permissions</button>
             <br>
         </div>
@@ -73,6 +74,17 @@
                 }
             });
 
+            function viewVersionHistory(){
+                var hashPassthrough = encodeURIComponent(JSON.stringify(files));
+                ao_module_newfw({
+                    url: "SystemAO/file_system/file_versions.html#" + hashPassthrough,
+                    width: 570,
+                    height: 480,
+                    appicon: "SystemAO/file_system/img/properties.png",
+                    title: "File Version History",
+                });
+            }
+
             function getFileProp(vpath, callback){
                 $.ajax({
                     url: "../../system/file_system/getProperties",

+ 105 - 0
web/SystemAO/file_system/file_versions.html

@@ -0,0 +1,105 @@
+<html>
+    <head>
+        <title locale="title/title">File Version History</title>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
+        <link rel="stylesheet" href="../../script/semantic/semantic.css">
+        <script type="text/javascript" src="../../script/jquery.min.js"></script>
+        <script type="text/javascript" src="../../script/semantic/semantic.min.js"></script>
+        <script type="text/javascript" src="../../script/ao_module.js"></script>
+        <script type="text/javascript" src="../../script/applocale.js"></script>
+        <style>
+            body{
+                overflow:hidden;
+            }
+
+        </style>
+    </head>
+    <body id="filePropertiesWindow">
+        <br>
+        <div class="ui container">
+            <h3 class="ui header">
+                <span locale="title/title">File Version History</span>
+                <div class="sub header" locale="title/desc">Any file versions that is more than 30 days old will be automatically removed from the system.</div>
+            </h3>
+            <div class="ui divider"></div>
+            <div>
+                <table class="ui very basic fluid celled table unstackable">
+                    <tbody id="versions">
+                      <tr>
+                        <td>
+                          <h4 class="ui header">
+                                <div class="content">
+                                <span>No File</span>
+                                <div class="sub header">Invalid usage</div>
+                            </div>
+                            </h4>
+                        </td>
+                      </tr>
+                    </tbody>
+                  </table>
+            </div>
+            <br>
+        </div>
+        <script>
+            var files = ao_module_loadInputFiles();
+            var targetFile = "";
+            if (files.length >= 1){
+                targetFile = files[0];
+                loadVersionHistory(targetFile);
+            }else{
+                //No file selected
+
+            }
+
+            function loadVersionHistory(vpath){
+                $.ajax({
+                    url: "../../system/file_system/versionHistory",
+                    data: {path: vpath},
+                    method: "POST",
+                    success: function(data){
+                        if (data.error !== undefined){
+                            alert(data.error);
+                        }else{
+                            $("#versions").html("");
+                            data.Versions.forEach(fileVersionEntry => {
+                                var filesize = ao_module_utils.formatBytes(fileVersionEntry.Filesize, 1);
+                                $("#versions").append(`<tr>
+                                    <td>
+                                    <h4 class="ui header">
+                                        <div class="content">
+                                        <span>${fileVersionEntry.Filename}</span>
+                                        <div class="sub header">${fileVersionEntry.OverwriteTime} / ${filesize}</div>
+                                    </div>
+                                    </h4></td>
+                                    <td>
+                                        <div class="ui icon mini buttons">
+                                            <button relpath="${fileVersionEntry.Relpath}" onclick="downloadVersion(this);" class="ui very basic icon button" title="Download Version"><i class="ui blue download icon"></i></button>
+                                            <button verid="${fileVersionEntry.HistoryID}" class="ui very basic icon button" title="Restore This Version"><i class="ui green sync icon"></i></button>
+                                            <button verid="${fileVersionEntry.HistoryID}" class="ui very basic icon button" title="Delete"><i class="ui red trash icon"></i></button>
+                                        </div>
+                                        
+                                    </td>
+                                </tr>`);
+                            });
+                        }
+                    }
+                });
+            }
+
+            function downloadVersion(object){
+                var relpath = $(object).attr("relpath");
+                var dirname = targetFile.split("/");
+                dirname.pop();
+                var accessPath = dirname.join("/") + "/" + relpath;
+                window.open("../../media?file=" + accessPath + "&download=true")
+            }
+
+            function deleteVersion(object){
+                var versionID = $(object).attr("verid");
+                
+            }
+             
+        </script>
+    </body>
+</html> 

+ 3 - 0
web/SystemAO/locale/file_properties.json

@@ -30,6 +30,7 @@
                 "lastmod/time/ago": "前",
 
                 "button/changeDefault":"更改預設啟動應用",
+                "button/versionHistory":"查看檔案版本記錄",
                 "button/changeFilePermission":"更改檔案存取權限",
                 "loader/loadingFileSize": "正在計算檔案大小",
                 "selection/multi": "已選擇多個檔案",
@@ -68,6 +69,7 @@
                 "lastmod/time/ago": "前",
 
                 "button/changeDefault":"更改預設啟動應用",
+                "button/versionHistory":"查看檔案版本記錄",
                 "button/changeFilePermission":"更改檔案存取權限",
                 "loader/loadingFileSize": "正在計算檔案大小",
                 "selection/multi": "已選擇多個檔案",
@@ -106,6 +108,7 @@
                 "lastmod/time/ago": "前",
 
                 "button/changeDefault":"更改预设启动应用",
+                "button/versionHistory":"查看文件版本记录",
                 "button/changeFilePermission":"更改文件存取权限",
                 "loader/loadingFileSize": "正在计算文件大小",
                 "selection/multi": "已选择多个文件",