Procházet zdrojové kódy

Added WIP backend wrapper and test fix for folder render big

Toby Chui před 3 roky
rodič
revize
039ebf9407

+ 9 - 3
mod/agi/agi.file.go

@@ -188,6 +188,8 @@ func (g *Gateway) injectFileLibFunctions(vm *otto.Otto, u *user.User) {
 			return reply
 		}
 
+		rpath = filepath.ToSlash(filepath.Clean(rpath)) + "/*"
+
 		fileList, err := specialGlob(rpath)
 		if err != nil {
 			g.raiseError(err)
@@ -198,10 +200,14 @@ func (g *Gateway) injectFileLibFunctions(vm *otto.Otto, u *user.User) {
 		//Translate all paths to virtual paths
 		results := []string{}
 		for _, file := range fileList {
-			if IsDir(file) {
+			//if IsDir(file) {
+			isHidden, _ := hidden.IsHidden(file, true)
+			if !isHidden {
 				thisRpath, _ := realpathToVirtualpath(file, u)
 				results = append(results, thisRpath)
 			}
+
+			//}
 		}
 
 		reply, _ := vm.ToValue(results)
@@ -551,7 +557,7 @@ func (g *Gateway) injectFileLibFunctions(vm *otto.Otto, u *user.User) {
 		return otto.TrueValue()
 	})
 
-	//Get MD5 of the given filepath
+	//Get MD5 of the given filepath, not implemented
 	vm.Set("_filelib_md5", func(call otto.FunctionCall) otto.Value {
 		log.Println("Call to MD5 Functions!")
 		return otto.FalseValue()
@@ -656,6 +662,6 @@ func (g *Gateway) injectFileLibFunctions(vm *otto.Otto, u *user.User) {
 		filelib.md5 = _filelib_md5;
 		filelib.mkdir = _filelib_mkdir;
 		filelib.mtime = _filelib_mtime;
-		filelib.rname = _filelib_rname;
+		filelib.rootName = _filelib_rname;
 	`)
 }

+ 33 - 1
mod/agi/agi.go

@@ -8,6 +8,7 @@ import (
 	"net/http"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"github.com/robertkrimen/otto"
 
@@ -28,7 +29,11 @@ import (
 */
 
 var (
-	AgiVersion string = "1.6" //Defination of the agi runtime version. Update this when new function is added
+	AgiVersion string = "1.7" //Defination of the agi runtime version. Update this when new function is added
+
+	//AGI Internal Error Standard
+	exitcall  = errors.New("Exit")
+	timelimit = errors.New("Timelimit")
 )
 
 type AgiLibIntergface func(*otto.Otto, *user.User) //Define the lib loader interface for AGI Libraries
@@ -338,6 +343,33 @@ func (g *Gateway) ExecuteAGIScriptAsUser(scriptFile string, targetUser *user.Use
 	g.injectStandardLibs(vm, scriptFile, "")
 	g.injectUserFunctions(vm, scriptFile, "", targetUser, nil, nil)
 
+	//Inject interrupt Channel
+	vm.Interrupt = make(chan func(), 1)
+
+	//Create a panic recovery logic
+	defer func() {
+		if caught := recover(); caught != nil {
+			if caught == timelimit {
+				log.Println("[AGI] Execution timeout: " + scriptFile)
+				return
+			} else if caught == exitcall {
+				//Exit gracefully
+
+				return
+			} else {
+				panic(caught)
+			}
+		}
+	}()
+
+	//Create a max runtime of 5 minutes
+	go func() {
+		time.Sleep(300 * time.Second) // Stop after 300 seconds
+		vm.Interrupt <- func() {
+			panic(timelimit)
+		}
+	}()
+
 	//Try to read the script content
 	scriptContent, err := ioutil.ReadFile(scriptFile)
 	if err != nil {

+ 9 - 1
mod/agi/systemFunc.go

@@ -21,7 +21,7 @@ func (g *Gateway) injectStandardLibs(vm *otto.Otto, scriptFile string, scriptSco
 
 	//Define VM global variables
 	vm.Set("BUILD_VERSION", g.Option.BuildVersion)
-	vm.Set("INTERNVAL_VERSION", g.Option.InternalVersion)
+	vm.Set("INTERNAL_VERSION", g.Option.InternalVersion)
 	vm.Set("LOADED_MODULES", g.Option.LoadedModule)
 	vm.Set("LOADED_STORAGES", g.Option.UserHandler.GetStoragePool())
 	vm.Set("HTTP_RESP", "")
@@ -374,4 +374,12 @@ func (g *Gateway) injectStandardLibs(vm *otto.Otto, scriptFile string, scriptSco
 		time.Sleep(time.Duration(delayTime) * time.Millisecond)
 		return otto.TrueValue()
 	})
+
+	//Exit
+	vm.Set("exit", func(call otto.FunctionCall) otto.Value {
+		vm.Interrupt <- func() {
+			panic(exitcall)
+		}
+		return otto.NullValue()
+	})
 }

+ 5 - 3
mod/filesystem/metadata/folder.go

@@ -66,7 +66,7 @@ func generateThumbnailForFolder(cacheFolder string, file string, generateOnly bo
 		defer image2.Close()
 		resizedBackImg := resize.Resize(250, 250, backImage, resize.Lanczos3)
 		draw.Draw(resultThumbnail, resizedBackImg.Bounds().Add(backImgOffset), resizedBackImg, image.ZP, draw.Over)
-	} else if len(contentCache) == 0 {
+	} else {
 		//Nothing to preview inside this folder
 		return "", errors.New("No previewable files")
 	}
@@ -74,12 +74,14 @@ func generateThumbnailForFolder(cacheFolder string, file string, generateOnly bo
 	//Render the top image
 	image3, err := os.Open(contentCache[0])
 	if err != nil {
-		log.Fatalf("failed to open: %s", err)
+		return "", errors.New("failed to open: " + err.Error())
 	}
 
 	topImage, err := jpeg.Decode(image3)
 	if err != nil {
-		log.Fatalf("failed to decode: %s", err)
+		//Fail to decode the image. Maybe damaged
+		os.Remove(contentCache[0])
+		return "", errors.New("failed to decode: " + err.Error())
 	}
 	defer image3.Close()
 

+ 1 - 1
web/Music/functions/listSong.js

@@ -224,7 +224,7 @@ function handleUserRequest(){
                         thisMusicDir = thisRoot;
                     }
                     
-                    var rootName = filelib.rname(thisRoot);
+                    var rootName = filelib.rootName(thisRoot);
                     if (rootName == false){
                         rootName = thisRoot
                     }

+ 138 - 0
web/UnitTest/ao_backend.js

@@ -0,0 +1,138 @@
+/*
+    ao_backend.js
+    Author: tobychui
+
+    JavaScript wrapper for AGI Gateway script
+    Designed for front-end WebApps 
+
+    Usage: 
+    1. Copy and paste this file into your module's root
+    2. In your html, include this script in <script> element
+    3. Start the script using aoBackend.start("MyWebApp/ao_backend.js", "../");
+*/
+
+if (typeof(BUILD_VERSION) != "undefined"){
+    //Executing in backend VM
+    if (typeof(opr) != "undefined"){
+        /*
+            Appdata Library
+        */
+        if (opr == "appdata.readFile"){
+            requirelib("appdata");
+            var content = appdata.readFile(filepath);
+            if (content == false){
+                sendJSONResp(JSON.stringify({
+                    error: "Unable to get appdata from app folder"
+                }));
+            }else{
+                sendResp(content)
+            }
+        }else if (opr == "appdata.listDir"){
+            requirelib("appdata");
+            var content = appdata.listDir(filepath);
+            if (content == false){
+                sendJSONResp(JSON.stringify({
+                    error: "Unable to list backend appdata"
+                }));
+            }else{
+                sendJSONResp(content);
+            }
+        
+        /*
+            File library
+        */
+        }else if (opr == "file.writeFile"){
+            requirelib("filelib");
+            filelib.writeFile(filepath,content);
+            sendOK();
+        }else if (opr == "file.readFile"){
+            requirelib("filelib");
+            sendResp(filelib.readFile(filepath));
+        }else if (opr == "file.deleteFile"){
+            requirelib("filelib");
+            filelib.deleteFile(filepath);
+            sendOK();
+        }else if (opr == "file.readdir"){
+            requirelib("filelib");
+            var dirlist = filelib.readdir(filepath);
+            sendJSONResp(JSON.stringify(dirlist));
+        }else if (opr == "file.walk"){
+            requirelib("filelib");
+            var filelist = filelib.walk(filepath, mode);
+            sendJSONResp(JSON.stringify(filelist));
+        }else if (opr == "file.glob"){
+            requirelib("filelib");
+            var filelist = filelib.glob(wildcard, sort);
+            sendJSONResp(JSON.stringify(filelist));
+        }else if (opr == "file.aglob"){
+            requirelib("filelib");
+            var filelist = filelib.aglob(wildcard, sort);
+            sendJSONResp(JSON.stringify(filelist));
+        }else if (opr == "file.filesize"){
+            requirelib("filelib");
+            var fileSize = filelib.filesize(filepath);
+            sendJSONResp(JSON.stringify(fileSize));
+        }else if (opr == "file.fileExists"){
+            requirelib("filelib");
+            var exists = filelib.fileExists(filepath);
+            sendJSONResp(JSON.stringify(exists));
+        }else if (opr == "file.isDir"){
+            requirelib("filelib");
+            sendJSONResp(JSON.stringify(filelib.isDir(filepath)));
+        }else if (opr == "file.mkdir"){
+            requirelib("filelib");
+            filelib.mkdir(filepath);
+            sendOK();
+        }else if (opr == "file.mtime"){
+            requirelib("filelib");
+            var unixmtime = filelib.mtime(filepath, true);
+            sendJSONResp(JSON.stringify(unixmtime));
+        }else if (opr == "file.rootName"){
+            requirelib("filelib");
+            var rootname = filelib.rootName(filepath);
+            sendJSONResp(JSON.stringify(rootname));
+        
+        /*
+            HTTP library
+        */
+        }else if (opr == "http.get"){
+            requirelib("http");
+            var respbody = http.get(targetURL);
+            sendResp(respbody);
+        }else if (opr == "http.post"){
+            requirelib("http");
+            if (postdata == ""){
+                postdata = {};
+            }
+            var respbody = http.post(targetURL,postdata);
+            sendResp(respbody);
+        }else if (opr == "http.head"){
+            requirelib("http");
+            var respHeader = JSON.parse(http.head(targetURL, header));
+            sendJSONResp(JSON.stringify(respHeader));
+        }else if (opr == "http.download"){
+            requirelib("http");
+            var success = http.download(targetURL,saveDir,saveFilename);
+            if (success){
+                sendOk();
+            }else{
+                sendJSONResp(JSON.stringify({
+                    error: "Download failed"
+                }));
+            }
+        }else{
+            sendJSONResp(JSON.stringify({
+                error: "Unknown operator: " + opr
+            }));
+        }
+    }else{
+        //Invalid request operation
+        sendJSONResp(JSON.stringify({
+            "error":"invalid or not supported operation given"
+        }));
+    }
+}else{
+    console.log("INVALID USAGE")
+}
+
+

+ 82 - 0
web/UnitTest/backend.html

@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <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">
+        <meta name="theme-color" content="#4b75ff">
+        <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>ao_backend Test</title>
+    </head>
+    <body>
+        <br><br>
+        <div class="ui container">
+           <h2>ao_backend Testing Interface</h2>
+           <p>See console for more information</p>
+        </div>
+        
+         <!-- <script src="ao_backend.js"></script> -->
+        <script>
+            //Register the backend wrapper path
+            var backendWrapper = ao_module_backend();
+            backendWrapper.start("UnitTest/ao_backend.js");
+
+            //Test appdata
+            backendWrapper.appdata.readFile("UnitTest/appdata.txt", function(content){
+                console.log(content);
+            });
+
+            backendWrapper.appdata.listDir("UnitTest/", function(list){
+                console.log(list);
+            });
+
+            //Test File operation
+            backendWrapper.file.readdir("user:/Desktop/", function(files){
+                console.log(files);
+            });
+
+            //Try file read write
+            backendWrapper.file.writeFile("user:/Desktop/hello.txt", "Hello World!",function(data){
+                console.log("Write File: ", data);
+            });
+
+            backendWrapper.file.readFile("user:/Desktop/hello.txt",function(content){
+                console.log("Read File: " + content);
+            });
+
+            backendWrapper.file.mtime("user:/Desktop/hello.txt",function(data){
+                console.log("Test file mtime", data);
+            });
+
+            backendWrapper.file.isDir("user:/Desktop/hello.txt",function(data){
+                console.log("Test file is Dir", data);
+            });
+
+            backendWrapper.file.filesize("user:/Desktop/hello.txt",function(data){
+                console.log("Test file size", data, " bytes");
+            });
+
+            backendWrapper.file.aglob("user:/Desktop/*.mp3",undefined, function(data){
+                console.log("mp3 on desktop: ", data);
+            });
+
+            //http test
+            backendWrapper.http.download("https://ccns.arozos.com/share/download/6341f7fe-e03e-4200-88f2-6d4800b846fc/imuslab%20%E4%B8%BB%E9%A1%8C%E6%9B%B2.mp3", "tmp:/", "", function(data){
+                console.log("Download status: ", data)
+            });
+
+            backendWrapper.http.get("https://www.google.com/robots.txt", function(data){
+                console.log("HTTP GET test: ", data);
+            })
+
+            backendWrapper.http.post("https://www.google.com/robots.txt", undefined, function(data){
+                console.log("HTTP POST test: ", data);
+            })
+
+
+        </script>
+    </body>
+</html>

+ 1 - 1
web/UnitTest/backend/filelib.delete.js

@@ -7,7 +7,7 @@ requirelib("filelib");
 //Create a file
 if (filelib.writeFile("user:/Desktop/test.txt")){
     //Delete the file
-    filelib.deleteFile("user:/Desktop/test.txt")
+    filelib.deleteFile("user:/Desktop/test.txt");
 }else{
     console.log("File create failed")
 }

+ 1 - 1
web/UnitTest/backend/filelib.mkdir.js

@@ -10,7 +10,7 @@ if (loaded) {
 	if (success){
 		sendResp("OK")
 	}else{
-		sendResp("Failed to resize image");
+		sendResp("Failed to mkdir");
 	}
 } else {
     console.log("Failed to load lib: filelib");

+ 1 - 0
web/UnitTest/index.html

@@ -31,6 +31,7 @@
             <button class="ui button" onclick="openGateway();">Open AGI Gateway (No File)</button>
             <button class="ui button" onclick="runtest();">Run Unit Test</button>
             <a class="ui button" href="wstest.html">Open WebSocket Test</a>
+            <a class="ui blue button" href="backend.html">Open Backend Unit Test</a>
             <div class="ui divider"></div>
             <p>Test Results</p>
             <table class="ui celled table">

+ 176 - 2
web/script/ao_module.js

@@ -530,7 +530,7 @@ function ao_module_agirun(scriptpath, data, callback, failedcallback = undefined
             }
         },
         error: function(){
-            if (typeof failedcallback != "undefined"){
+            if (typeof(failedcallback) != "undefined"){
                 failedcallback();
             }
         },
@@ -971,4 +971,178 @@ class ao_module_utils{
     }
     
     static formatBytes(a,b=2){if(0===a)return"0 Bytes";const c=0>b?0:b,d=Math.floor(Math.log(a)/Math.log(1024));return parseFloat((a/Math.pow(1024,d)).toFixed(c))+" "+["Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"][d]}
-}
+}
+
+/*
+    Backend Programming Logic
+
+    These code are design to use with ao_backend.js for wrapping
+    AGI programming gateway content
+*/
+
+function ao_module_backend(){
+    return {
+        timeout: 500,
+        start: function(libraryPath){
+            this.libpath = libraryPath;
+    
+            //Initialize the parent objects
+            this.appdata.parent = this;
+            this.file.parent = this;
+            this.http.parent = this;
+        },
+        _agi_run: function(data, callback, failedcallback = undefined){
+                    $.ajax({
+                        url: ao_root + "system/ajgi/interface?script=" + this.libpath,
+                        method: "POST",
+                        data: data,
+                        success: function(data){
+                            if (typeof(callback) != "undefined"){
+                                callback(data);
+                            }
+                        },
+                        error: function(){
+                            if (typeof(failedcallback) != "undefined"){
+                                failedcallback();
+                            }
+                            console.log("Request failed");
+                        },
+                        timeout: this.timeout
+                    })
+                },
+        appdata: {
+            readFile: function(filepath, callback=undefined){
+                this.parent._agi_run({
+                    opr: "appdata.readFile",
+                    filepath: filepath
+                }, callback)
+            },
+            listDir: function(filepath, callback=undefined){
+                this.parent._agi_run({
+                    opr: "appdata.listDir",
+                    filepath: filepath
+                }, callback)
+            },
+        },
+        file: {
+            writeFile: function(filepath, content, callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.writeFile",
+                    filepath: filepath,
+                    content: content,
+                }, callback)
+            },
+            readFile: function(filepath, callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.readFile",
+                    filepath: filepath
+                }, callback)
+            },
+            deleteFile: function(filepath, callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.deleteFile",
+                    filepath: filepath
+                }, callback)
+            },
+            readdir: function(filepath, callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.readdir",
+                    filepath: filepath
+                }, callback)
+            },
+            walk: function(filepath, mode="all", callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.walk",
+                    filepath: filepath,
+                    mode: mode,
+                }, callback)
+            },
+            glob: function(wildcard, sort="user", callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.glob",
+                    wildcard: wildcard,
+                    sort: sort,
+                }, callback)
+            },
+            aglob: function(wildcard, sort="user", callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.aglob",
+                    wildcard: wildcard,
+                    sort: sort,
+                }, callback)
+            },
+            filesize: function(filepath, callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.filesize",
+                    filepath: filepath
+                }, callback)
+            },
+            fileExists: function(filepath, callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.fileExists",
+                    filepath: filepath
+                }, callback)
+            },
+            isDir: function(filepath, callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.isDir",
+                    filepath: filepath
+                }, callback)
+            },
+            mkdir: function(filepath, callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.mkdir",
+                    filepath: filepath
+                }, callback)
+            },
+            mtime: function(filepath, callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.mtime",
+                    filepath: filepath
+                }, callback)
+            },
+            rootName: function(filepath, callback=undefined){
+                this.parent._agi_run({
+                    opr: "file.rootName",
+                    filepath: filepath
+                }, callback)
+            }
+        },
+        http: {
+            get: function(targetURL, callback=undefined){
+                this.parent._agi_run({
+                    opr: "http.get",
+                    targetURL: targetURL
+                }, callback)
+            },
+            post: function(targetURL, postdata="", callback=undefined){
+                this.parent._agi_run({
+                    opr: "http.post",
+                    targetURL: targetURL,
+                    postdata: postdata
+                }, callback)
+            },
+            head: function(targetURL, header="", callback=undefined){
+                this.parent._agi_run({
+                    opr: "http.head",
+                    targetURL: targetURL,
+                    header: header
+                }, callback)
+            },
+            download: function(targetURL, saveDir="tmp:/", saveFilename="", callback=undefined){
+                if (saveFilename == ""){
+                    saveFilename = targetURL.split("/").pop();
+                    saveFilename = decodeURIComponent(saveFilename);
+                }
+
+                this.parent._agi_run({
+                    opr: "http.download",
+                    targetURL: targetURL,
+                    saveDir: saveDir,
+                    saveFilename: saveFilename
+                }, callback)
+            },
+        }
+    }
+}
+