Przeglądaj źródła

Added share API to AGI script

Toby Chui 3 lat temu
rodzic
commit
c067221ab2

+ 25 - 1
AGI Documentation.md

@@ -742,4 +742,28 @@ if (iot.ready() == true){
 
 ```
 
-For detailed example for other functions, see the js file located at ```UnitTest/backend/iot.*.js```
+For detailed example for other functions, see the js file located at ```UnitTest/backend/iot.*.js```
+
+### Share
+
+The share API allow access to the ArozOS share interface and generate UUID based on the shared file.
+
+```
+requirelib("share");
+```
+
+#### share functions
+
+```
+share.shareFile("user:/Desktop/test.pptx", 300); //File virtual path and timeout in seconds.
+share.checkShareExists(shareUUID); //Return true / false
+share.fileIsShared("user:/Desktop/test.pptx"); //Return true / false
+share.removeShare(shareUUID);
+```
+
+#### Share Timeout
+
+For ```shareFile``` timeout value, **if set to 0 or unset, it will default to "forever"**. Hence, the share will not be automatically removed after timeout 
+
+Please also note that the share timeout is done by the AGI gateway system runtime. Hence, if you have shutdown / reset your ArozOS within the set period of time, your share **will not get automatically removed after the system startup again**.
+

+ 1 - 0
agi.go

@@ -24,6 +24,7 @@ func AGIInit() {
 		StartupRoot:          "./web",
 		ActivateScope:        []string{"./web", "./subservice"},
 		FileSystemRender:     thumbRenderHandler,
+		ShareManager:         shareManager,
 	})
 	if err != nil {
 		log.Println("AGI Gateway Initialization Failed")

+ 1 - 8
cluster.go

@@ -4,7 +4,6 @@ import (
 	"log"
 	"net/http"
 
-	"imuslab.com/arozos/mod/cluster/aclient"
 	"imuslab.com/arozos/mod/network/neighbour"
 	prout "imuslab.com/arozos/mod/prouter"
 )
@@ -60,13 +59,7 @@ func ClusterInit() {
 		/*
 			Start and Cluster Server and Client
 		*/
-
-		if *allow_clustering {
-			aclient.NewClient(aclient.AclientOption{
-				MDNS: MDNS,
-			})
-		}
-
+		//WIP
 	} else {
 		log.Println("MDNS not enabled or startup failed. Skipping Cluster Scanner initiation.")
 	}

+ 3 - 0
mod/agi/agi.go

@@ -14,6 +14,7 @@ import (
 	auth "imuslab.com/arozos/mod/auth"
 	metadata "imuslab.com/arozos/mod/filesystem/metadata"
 	"imuslab.com/arozos/mod/iot"
+	"imuslab.com/arozos/mod/share"
 	user "imuslab.com/arozos/mod/user"
 )
 
@@ -48,6 +49,7 @@ type AgiSysInfo struct {
 	AuthAgent            *auth.AuthAgent
 	FileSystemRender     *metadata.RenderHandler
 	IotManager           *iot.Manager
+	ShareManager         *share.Manager
 
 	//Scanning Roots
 	StartupRoot   string
@@ -93,6 +95,7 @@ func NewGateway(option AgiSysInfo) (*Gateway, error) {
 	gatewayObject.ImageLibRegister()
 	gatewayObject.FileLibRegister()
 	gatewayObject.HTTPLibRegister()
+	gatewayObject.ShareLibRegister()
 	gatewayObject.IoTLibRegister()
 	gatewayObject.AppdataLibRegister()
 

+ 94 - 1
mod/agi/agi.share.go

@@ -2,18 +2,111 @@ package agi
 
 import (
 	"log"
+	"time"
 
 	"github.com/robertkrimen/otto"
 	user "imuslab.com/arozos/mod/user"
 )
 
 func (g *Gateway) ShareLibRegister() {
-	err := g.RegisterLib("share", g.injectIoTFunctions)
+	err := g.RegisterLib("share", g.injectShareFunctions)
 	if err != nil {
 		log.Fatal(err)
 	}
 }
 
 func (g *Gateway) injectShareFunctions(vm *otto.Otto, u *user.User) {
+	vm.Set("_share_file", func(call otto.FunctionCall) otto.Value {
+		//Get the vpath of file to share
+		vpath, err := call.Argument(0).ToString()
+		if err != nil {
+			return otto.New().MakeCustomError("Unable to decode filepath", "No given filepath for sharing")
+		}
 
+		//Get the timeout from the 2nd parameter for how long this share will exists
+		timeout, err := call.Argument(1).ToInteger()
+		if err != nil {
+			//Not defined -> Do not expire
+			timeout = 0
+		}
+
+		//Resolve the file path
+		rpath, err := u.VirtualPathToRealPath(vpath)
+		if err != nil {
+			return otto.New().MakeCustomError("Path resolve failed", "Unable to resolve virtual path: "+err.Error())
+		}
+
+		//Check if file exists
+		if !fileExists(rpath) {
+			return otto.New().MakeCustomError("File not exists", "Share file vpath not exists")
+		}
+
+		//Create a share object for this request
+		shareID, err := g.Option.ShareManager.CreateNewShare(u, vpath)
+		if err != nil {
+			log.Println("[AGI] Create Share Failed: " + err.Error())
+			return otto.New().MakeCustomError("Share failed", err.Error())
+		}
+
+		if timeout > 0 {
+			go func(timeout int) {
+				time.Sleep(time.Duration(timeout) * time.Second)
+				g.Option.ShareManager.RemoveShareByUUID(shareID.UUID)
+				log.Println("[AGI] Share auto-removed: " + shareID.UUID)
+			}(int(timeout))
+		}
+
+		r, _ := otto.ToValue(shareID.UUID)
+		return r
+	})
+
+	vm.Set("_share_removeShare", func(call otto.FunctionCall) otto.Value {
+		shareUUID, err := call.Argument(0).ToString()
+		if err != nil {
+			return otto.New().MakeCustomError("Failed to remove share", "No share UUID given")
+		}
+		err = g.Option.ShareManager.RemoveShareByUUID(shareUUID)
+		if err != nil {
+			log.Println("[AGI] Share remove failed: " + err.Error())
+			return otto.New().MakeCustomError("Failed to remove share", err.Error())
+		}
+
+		return otto.TrueValue()
+	})
+
+	vm.Set("_share_checkShareExists", func(call otto.FunctionCall) otto.Value {
+		shareUUID, err := call.Argument(0).ToString()
+		if err != nil {
+			return otto.New().MakeCustomError("Failed to check share exists", "No share UUID given")
+		}
+
+		shareObject := g.Option.ShareManager.GetShareObjectFromUUID(shareUUID)
+		r, _ := otto.ToValue(!(shareObject == nil))
+		return r
+	})
+
+	vm.Set("_share_fileIsShared", func(call otto.FunctionCall) otto.Value {
+		vpath, err := call.Argument(0).ToString()
+		if err != nil {
+			return otto.New().MakeCustomError("Failed to check share exists", "No filepath given")
+		}
+
+		rpath, err := u.VirtualPathToRealPath(vpath)
+		if err != nil {
+			return otto.New().MakeCustomError("Filepath resolve failed", err.Error())
+		}
+
+		isShared := g.Option.ShareManager.FileIsShared(rpath)
+		r, _ := otto.ToValue(isShared)
+		return r
+	})
+
+	//Wrap all the native code function into an imagelib class
+	vm.Run(`
+		var share = {};
+		share.shareFile = _share_file;
+		share.removeShare = _share_removeShare;
+		share.checkShareExists = _share_checkShareExists;
+		share.fileIsShared = _share_fileIsShared;
+	`)
 }

+ 0 - 30
mod/cluster/aclient/aclient.go

@@ -1,30 +0,0 @@
-package aclient
-
-import "imuslab.com/arozos/mod/network/mdns"
-
-/*
-	ArOZ Cluster Client Module
-	author: tobychui
-
-	This module is designed to connect this host to a remote host and act as a client
-	for sending commands
-
-*/
-
-type Aclient struct {
-	Options AclientOption
-}
-
-type AclientOption struct {
-	MDNS *mdns.MDNSHost
-}
-
-func NewClient(option AclientOption) *Aclient {
-	return &Aclient{
-		Options: option,
-	}
-}
-
-func (a *Aclient) DiscoverServices(serviceType string) {
-
-}

+ 0 - 10
mod/cluster/aserver/aserver.go

@@ -1,10 +0,0 @@
-package aserver
-
-/*
-	ArOZ Cluster Server
-	author: tobychui
-
-	This module is used to act as an ArOZ CLuster Server and receive
-	command from client and do things
-
-*/

+ 70 - 1
mod/network/network.go

@@ -2,9 +2,14 @@ package network
 
 import (
 	"encoding/json"
+	"errors"
+	"io/ioutil"
 	"log"
 	"net"
 	"net/http"
+	"strings"
+
+	"gitlab.com/NebulousLabs/go-upnp"
 )
 
 type NICS struct {
@@ -19,7 +24,7 @@ type NICS struct {
 	Name               string
 }
 
-func GetNICInfo(w http.ResponseWriter, r *http.Request){
+func GetNICInfo(w http.ResponseWriter, r *http.Request) {
 	interfaces, err := net.Interfaces()
 	if err != nil {
 		sendJSONResponse(w, err.Error())
@@ -122,6 +127,70 @@ func GetOutboundIP() (net.IP, error) {
 	return localAddr.IP, nil
 }
 
+//Get External IP address, will require 3rd party services
+func GetExternalIPAddr() (string, error) {
+	u, err := upnp.Discover()
+	if err != nil {
+		return "", err
+	}
+	// discover external IP
+	ip, err := u.ExternalIP()
+	if err != nil {
+		return "", err
+	}
+	return ip, nil
+}
+
+func GetExternalIPAddrVia3rdPartyServices() (string, error) {
+	//Fallback to using Amazon AWS IP resolve service
+	resp, err := http.Get("http://checkip.amazonaws.com/")
+	if err != nil {
+		return "", err
+	}
+
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return "", err
+	}
+
+	return strings.TrimSpace(string(body)), nil
+}
+
+func IsPublicIP(IP net.IP) bool {
+	if IP.IsLoopback() || IP.IsLinkLocalMulticast() || IP.IsLinkLocalUnicast() {
+		return false
+	}
+	if ip4 := IP.To4(); ip4 != nil {
+		switch {
+		case ip4[0] == 10:
+			return false
+		case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
+			return false
+		case ip4[0] == 192 && ip4[1] == 168:
+			return false
+		default:
+			return true
+		}
+	}
+	return false
+}
+
+func IsIPv6Addr(ip string) (bool, error) {
+	if net.ParseIP(ip) == nil {
+		return false, errors.New("Address parsing failed")
+	}
+	for i := 0; i < len(ip); i++ {
+		switch ip[i] {
+		case '.':
+			return false, nil
+		case ':':
+			return true, nil
+		}
+	}
+	return false, errors.New("Unable to determine address type")
+}
+
 func GetPing(w http.ResponseWriter, r *http.Request) {
 	sendJSONResponse(w, "pong")
 }

+ 36 - 0
web/UnitTest/backend/share.shareFile.js

@@ -0,0 +1,36 @@
+//File Share API
+//This script demonstrate how to share a file on ArozOS using AGI script
+
+//Require the share lib
+requirelib("share");
+
+function main(){
+    //Share the file for 10 seconds
+    var shareUUID = share.shareFile("user:/Desktop/test.pptx", 10);
+    console.log(shareUUID);
+    if (shareUUID == null){
+        //Share error
+        sendResp("Share failed");
+    }else{
+        //Share success.
+        //Check if share UUID exists
+        console.log("Share UUID is valid: " + share.checkShareExists(shareUUID));
+
+        //Check if the source file is shared
+        console.log("Source file is shared: " + share.fileIsShared("user:/Desktop/test.pptx"));
+
+        //Remove the share using UUID
+        //share.removeShare(shareUUID);
+
+        //Delay 11 seconds
+        delay(11000)
+
+        //Check is the share is still valid
+        console.log("Share UUID valid after share expired: " + share.checkShareExists(shareUUID));
+
+        //Return the share UUID
+        sendResp("OK");
+    }
+}
+
+main();