Browse Source

Added PyServer python adapter example

tobychui 4 years ago
parent
commit
b95daa19b4

+ 3 - 0
.gitignore

@@ -47,6 +47,9 @@ subservice/*
 !subservice/WsTTY/
 !subservice/WsTTY/*
 
+!subservice/PyServer
+!subservice/PyServer/*
+
 #Binary related
 build/*
 aroz_online.exe

+ 41 - 24
mod/subservice/subservice.go

@@ -80,8 +80,8 @@ func (sr *SubServiceRouter) LoadSubservicesFromRootPath(rootpath string) {
 }
 
 func (sr *SubServiceRouter) Launch(servicePath string, startupMode bool) error {
-
 	//Get the executable name from its path
+	servicePath = filepath.ToSlash(servicePath)
 	binaryname := filepath.Base(servicePath)
 	serviceRoot := filepath.Base(servicePath)
 	binaryExecPath := filepath.ToSlash(binaryname)
@@ -91,34 +91,51 @@ func (sr *SubServiceRouter) Launch(servicePath string, startupMode bool) error {
 		binaryExecPath = binaryExecPath + "_" + runtime.GOOS + "_" + runtime.GOARCH
 	}
 
-	if runtime.GOOS == "windows" && !fileExists(servicePath+"/"+binaryExecPath) {
-		if startupMode {
-			log.Println("Failed to load subservice: "+serviceRoot, " File not exists "+servicePath+"/"+binaryExecPath+". Skipping this service")
-			return errors.New("Failed to load subservice")
-		} else {
+	//Check if startscript exists. If no, try to launch the binaries
+	if fileExists(servicePath + "/.startscript") {
+		//Launch from start.bat or start.sh
+		if !(fileExists(servicePath+"/start.sh") || fileExists(servicePath+"/start.bat")) {
+			log.Println("Failed to load subservice: " + serviceRoot + ", .startscript flag is TRUE but no start script found")
 			return errors.New("Failed to load subservice")
 		}
 
-	} else if runtime.GOOS == "linux" {
-		//Check if service installed using which
-		cmd := exec.Command("which", serviceRoot)
-		searchResults, _ := cmd.CombinedOutput()
-		if len(strings.TrimSpace(string(searchResults))) == 0 {
-			//This is not installed. Check if it exists as a binary (aka ./myservice)
-			if !fileExists(servicePath + "/" + binaryExecPath) {
-				if startupMode {
-					log.Println("Package not installed. " + serviceRoot)
-					return errors.New("Failed to load subservice: Package not installed")
-				} else {
-					return errors.New("Package not installed.")
+		startScriptName := "start.sh"
+		if runtime.GOOS == "windows" {
+			startScriptName = "start.bat"
+		}
+
+		binaryExecPath = startScriptName
+	} else {
+		//No startscript defined. Start from binary files if exists
+		if runtime.GOOS == "windows" && !fileExists(servicePath+"/"+binaryExecPath) {
+			if startupMode {
+				log.Println("Failed to load subservice: "+serviceRoot, " File not exists "+servicePath+"/"+binaryExecPath+". Skipping this service")
+				return errors.New("Failed to load subservice")
+			} else {
+				return errors.New("Failed to load subservice")
+			}
+
+		} else if runtime.GOOS == "linux" {
+			//Check if service installed using which
+			cmd := exec.Command("which", serviceRoot)
+			searchResults, _ := cmd.CombinedOutput()
+			if len(strings.TrimSpace(string(searchResults))) == 0 {
+				//This is not installed. Check if it exists as a binary (aka ./myservice)
+				if !fileExists(servicePath + "/" + binaryExecPath) {
+					if startupMode {
+						log.Println("Package not installed. " + serviceRoot)
+						return errors.New("Failed to load subservice: Package not installed")
+					} else {
+						return errors.New("Package not installed.")
+					}
 				}
 			}
-		}
-	} else if runtime.GOOS == "darwin" {
-		//Skip the whereis approach that linux use
-		if !fileExists(servicePath + "/" + binaryExecPath) {
-			log.Println("Failed to load subservice: "+serviceRoot, " File not exists "+servicePath+"/"+binaryExecPath+". Skipping this service")
-			return errors.New("Failed to load subservice")
+		} else if runtime.GOOS == "darwin" {
+			//Skip the whereis approach that linux use
+			if !fileExists(servicePath + "/" + binaryExecPath) {
+				log.Println("Failed to load subservice: "+serviceRoot, " File not exists "+servicePath+"/"+binaryExecPath+". Skipping this service")
+				return errors.New("Failed to load subservice")
+			}
 		}
 	}
 

+ 4 - 5
mod/www/www.go

@@ -44,28 +44,27 @@ func NewWebRootHandler(options Options) *Handler {
 
 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" {
+	if filepath.ToSlash(filepath.Clean(r.URL.Path)) == "/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" {
+	} else if filepath.ToSlash(filepath.Dir(r.URL.Path)) == "/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)
+	decodedValue, err := url.QueryUnescape(r.URL.Path)
 	if err != nil {
 		//failed to decode. Just use its raw value
-		decodedValue = r.RequestURI
+		decodedValue = r.URL.Path
 	}
 
 	//Check the user name of the user root
 	parsedRequestURL := strings.Split(filepath.ToSlash(filepath.Clean(decodedValue)[1:]), "/")
 	//Malparsed URL. Ignore request
 	if len(parsedRequestURL) < 2 {
-
 		http.NotFound(w, r)
 		return
 	}

+ 0 - 0
reverseproxy.go → reverseproxy.go_disabled


+ 2 - 3
startup.go

@@ -58,9 +58,8 @@ func RunStartup() {
 	//StorageDaemonInit() //Start File System handler daemon (for backup and other sync process)
 
 	//8 Start AGI and Subservice modules (Must start after module)
-	AGIInit()          //ArOZ Javascript Gateway Interface, must start after fs
-	SubserviceInit()   //Subservice Handler
-	ReverseProxtInit() //Start Dynamic Reverse Proxy
+	AGIInit()        //ArOZ Javascript Gateway Interface, must start after fs
+	SubserviceInit() //Subservice Handler
 
 	//9. Initiate System Settings Handlers
 	SystemSettingInit()       //Start System Setting Core

+ 0 - 0
subservice/PyServer/.startscript


+ 14 - 0
subservice/PyServer/README.md

@@ -0,0 +1,14 @@
+# PyServer
+
+PyServer is a basic example showcasing the method for bridging Python written web application into ArozOS with a bash script and a few flag files.
+
+
+
+### Installation
+
+1. Create a new folder inside arozos/subservices named "PyServer"
+2. Drag and drop all the files into the newly created PyServer folder
+3. Restart arozos
+
+
+

+ 96 - 0
subservice/PyServer/main.py

@@ -0,0 +1,96 @@
+from http.server import BaseHTTPRequestHandler, HTTPServer
+import time
+import requests
+import sys
+import os
+
+arozRequestEndpoint = "http://localhost:8080/api/ajgi/interface"
+serverPort = 8000
+
+def getMime(filename):
+    ext = filename.split(".").pop()
+    if ext == "png" or ext == "jpg" or ext == "jpeg" or ext == "gif":
+        return "image/" + ext
+    elif ext == "html" or ext == "htm":
+        return "text/" + ext
+    
+    return "application/" + ext
+    
+def resolveVirtualPath(path, token):
+    # Create an AGI script to perform a specific operation on ArozOS
+    # For example, this script get the user's desktop path on host file system
+    script = ""
+    script += 'var abspath = decodeAbsoluteVirtualPath("' + path + '");'
+    script += 'sendResp(abspath);'
+    
+    # Put the script into the POST request payload
+    payload = {'script':script}
+    
+    # Post the script with the token to ArozOS
+    session = requests.Session()
+    resp = session.post(arozRequestEndpoint + "?token=" + token, data = payload)
+    print(resp.content.decode('UTF-8'))
+    
+    # Return as a simple JSON string (Replace \\ with / for Windows)
+    return '"' + str(resp.content.decode('UTF-8')).replace("\\", "/") + '"'
+
+class Router(BaseHTTPRequestHandler):
+    def do_GET(self):
+        # Get the request token and username from the request header
+        username = self.headers.get('aouser') # This is the username of the requesting user
+        token = self.headers.get('aotoken') # This is the token for requesting ArozOS for futher file operation / information
+        
+        print("Req: " + self.path + " by " + username)
+        if self.path == "/":
+            self.path = "/index.html"
+        if self.path == "/api":
+            # Demo for getting information from the ArozOS AGI gateway
+            resp = resolveVirtualPath("user:/Desktop", token);
+            self.send_response(200)
+            self.send_header('Content-type',"application/json")
+            self.end_headers()
+            self.wfile.write(bytes(str(resp), "utf-8"))
+            return
+        if os.path.exists("web" + self.path):
+            # Serve the file
+            self.send_response(200)
+            self.send_header('Content-type',getMime(self.path))
+            self.end_headers()
+            f = open("web" + self.path, 'rb')
+            self.wfile.write(f.read())
+            f.close()
+            return
+        else:
+            self.send_response(404)
+            self.send_header("Content-type", "text/html")
+            self.wfile.write(bytes("404 Not Found", "utf-8"))
+            return
+        return
+        self.send_response(200)
+        self.send_header("Content-type", "text/html")
+        self.end_headers()
+        self.wfile.write(bytes("<html><head><title>https://pythonbasics.org</title></head>", "utf-8"))
+        self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
+        self.wfile.write(bytes("<body>", "utf-8"))
+        self.wfile.write(bytes("<p>This is an example web server.</p>", "utf-8"))
+        self.wfile.write(bytes("</body></html>", "utf-8"))
+
+if __name__ == "__main__":
+    # Parse the input from ArozOS
+    # ArozOS will pass in port and parent calling endpoint and parsed by the start.sh / start.bat
+    # print(sys.argv)
+    if (len(sys.argv) > 1):
+        serverPort = int(sys.argv[1][1:])
+        arozRequestEndpoint = sys.argv[2]
+    
+    
+    webServer = HTTPServer(("localhost", serverPort), Router)
+    print("PyServer started http://%s:%s" % ("localhost", serverPort))
+
+    try:
+        webServer.serve_forever()
+    except KeyboardInterrupt:
+        pass
+
+    webServer.server_close()
+    print("PyServer stopped.")

+ 15 - 0
subservice/PyServer/moduleInfo.json

@@ -0,0 +1,15 @@
+{
+    "Name": "PyServer",
+	"Desc": "A basic example for showing how to bridge Python web server to ArozOS",
+	"Group": "Development",
+	"IconPath": "PyServer/img/small_icon.png",
+	"Version": "1.0",
+	"StartDir": "PyServer/index.html",
+	"SupportFW": true,
+	"LaunchFWDir": "PyServer/index.html",
+	"InitFWSize": [475, 550],
+	"SupportEmb": true,
+	"LaunchEmb": "PyServer/embedded.html",
+	"InitEmbSize": [360, 240],
+	"SupportedExt": [".py"]
+}

+ 1 - 0
subservice/PyServer/start.bat

@@ -0,0 +1 @@
+python3 main.py %2 %4

+ 2 - 0
subservice/PyServer/start.sh

@@ -0,0 +1,2 @@
+#!/bin/bash
+python3 main.py %2 %4

BIN
subservice/PyServer/web/img/desktop_icon.png


BIN
subservice/PyServer/web/img/desktop_icon.psd


BIN
subservice/PyServer/web/img/small_icon.png


+ 44 - 0
subservice/PyServer/web/index.html

@@ -0,0 +1,44 @@
+<!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>PyServer</title>
+        <style>
+            body{
+                background-color:white;
+            }
+        </style>
+    </head>
+    <body>
+        <br><br>
+        <div class="ui container">
+            <h2>Hello World!</h2>
+            <p>This is a Python written web server running inside ArozOS</p>
+			<p>Your Desktop folder path on the disk is: <span id="desktopPath">Loading...</span></p>
+			<p>For CSS Examples, see <a href="https://semantic-ui.com/">https://semantic-ui.com/</a></p>
+			<button class="ui blue button" onclick="openDesktop()"><i class="folder open icon"></i> Open Desktop</button>
+        </div>
+		<script>
+			//See arozos/web/script/ao_module.js for more floatWindow APIs
+			ao_module_setWindowTitle("PyServer - Example");
+			
+			function openDesktop(){
+				ao_module_openPath("user:/Desktop/");
+			}
+			
+			//This will request the /api endpoint from the Python Server
+			setTimeout(function(){
+				$.get("./api", function(data){
+					$("#desktopPath").text(data);
+				})
+			}, 1000);
+		</script>
+    </body>
+</html>

+ 0 - 5
user.go

@@ -70,11 +70,6 @@ func UserSystemInit() {
 		RequireAdmin: true,
 	})
 
-	//Handle db / auth / permissions related functions that requires user permission systems. See user.go
-	user_createPostUserHandlers()
-}
-
-func user_createPostUserHandlers() {
 	//Register auth management events that requires user handler
 	adminRouter := prout.NewModuleRouter(prout.RouterOption{
 		ModuleName:  "System Settings",

+ 0 - 0
web/SystemAO/reverse_proxy/img/desktop_icon.png → web/SystemAO/reverse_proxy_disabled/img/desktop_icon.png


+ 0 - 0
web/SystemAO/reverse_proxy/img/desktop_icon.psd → web/SystemAO/reverse_proxy_disabled/img/desktop_icon.psd


+ 0 - 0
web/SystemAO/reverse_proxy/img/small_icon.png → web/SystemAO/reverse_proxy_disabled/img/small_icon.png


+ 0 - 0
web/SystemAO/reverse_proxy/img/small_icon.psd → web/SystemAO/reverse_proxy_disabled/img/small_icon.psd


+ 0 - 0
web/SystemAO/reverse_proxy/index.html → web/SystemAO/reverse_proxy_disabled/index.html


+ 2 - 1
web/SystemAO/www/config.html

@@ -39,7 +39,7 @@
                 <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>
+                    <li>Everyone can access your home page using <a id="homepageLink" target="_blank"><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">
@@ -69,6 +69,7 @@
             $.get("../../../system/desktop/user", function(data){
                 if (data.error == undefined){
                     $(".username").text(data.Username);
+                    $("#homepageLink").attr('href', $("#homepageLink").text());
                 }
             });