diff --git a/docs/plugins/cssOptimizer.go b/docs/plugins/cssOptimizer.go
index f6147a0..fe6dbb9 100644
--- a/docs/plugins/cssOptimizer.go
+++ b/docs/plugins/cssOptimizer.go
@@ -106,7 +106,6 @@ func optimizeCss(htmlContent []byte) ([]byte, error) {
endIndex += startIndex + 6
codeSegment := originalHTMLContent[startIndex : endIndex+7] // Include
- fmt.Println(">>>>", codeSegment)
if !strings.Contains(codeSegment, "class=") {
replacement := strings.Replace(codeSegment, "", "", 1)
replacement = strings.Replace(replacement, "
", "", 1)
diff --git a/docs/plugins/docs/1. Introduction/1. What is Zoraxy Plugin.md b/docs/plugins/docs/1. Introduction/1. What is Zoraxy Plugin.md
index 1ef835a..3e79889 100644
--- a/docs/plugins/docs/1. Introduction/1. What is Zoraxy Plugin.md
+++ b/docs/plugins/docs/1. Introduction/1. What is Zoraxy Plugin.md
@@ -1,4 +1,9 @@
# What is Zoraxy Plugin?
+
+Last Update: 25/05/2025
+
+---
+
Zoraxy Plugin is a powerful extension feature designed to enhance the functionality of the Zoraxy system. It provides additional features and capabilities that are not part of the core system, allowing users to customize their experience and optimize performance. The plugin is built to be modular and flexible, enabling users to tailor their Zoraxy environment to meet specific needs.
Zoraxy plugins are distributed as binaries, and developers have the flexibility to choose whether to open source them or not **as the plugin library and interface are open source under the LGPL license**.
@@ -7,7 +12,7 @@ There are two primary types of plugins:
- **Router plugins**: Involved with connections from HTTP proxy rules.
- **Utility plugins**: Provide user interfaces for various network features that operate independently of the Zoraxy core.
----
+---
## How plugins are distributed & installed
Zoraxy plugins are distributed as platform-dependent binaries, tailored to specific operating systems and CPU architectures. These binaries follow a naming convention that includes the operating system, CPU architecture, and plugin name, such as `linux_amd64_foobar`, `windows_amd64_foobar.exe`, or `linux_arm64_foobar`.
@@ -18,7 +23,7 @@ To manually install a plugin for testing, place the binary file into the `/plugi
For distribution, a plugin store system is used. The plugin store architecture is similar to the one built into the Arduino IDE, with a manager URL (a JSON file) listing all the plugins supported by that store. See the documentation section for more details on how to implement your own plugin store.
----
+---
## Plugin vs Pull Request
The Zoraxy plugin was introduced to address specific use cases that enhance its functionality. It serves as an extension to the core Zoraxy system, providing additional features and capabilities while maintaining the integrity of the core system.
diff --git a/docs/plugins/docs/1. Introduction/2. Getting Started.md b/docs/plugins/docs/1. Introduction/2. Getting Started.md
index 4be9aa9..1d02cab 100644
--- a/docs/plugins/docs/1. Introduction/2. Getting Started.md
+++ b/docs/plugins/docs/1. Introduction/2. Getting Started.md
@@ -1,5 +1,9 @@
# Getting Started
+Last Update: 25/05/2025
+
+---
+
To start developing plugins, you will need the following installed on your computer
1. The source code of Zoraxy
diff --git a/docs/plugins/docs/1. Introduction/3. Installing Plugin.md b/docs/plugins/docs/1. Introduction/3. Installing Plugin.md
new file mode 100644
index 0000000..cf35531
--- /dev/null
+++ b/docs/plugins/docs/1. Introduction/3. Installing Plugin.md
@@ -0,0 +1,26 @@
+# Installing Plugin
+
+Last Update: 25/05/2025
+
+---
+
+### Install via Plugin Store
+
+(Work in progress)
+
+### Manual Install
+
+The plugin shall be placed inside the `plugins/{{plugin_name}}/` directory where the binary executable name must be matching with the plugin name.
+
+If you are on Linux, also make sure Zoraxy have the execution permission of the plugin. You can use the following command to enable execution of the plugin binary on Linux with the current user (Assume Zoraxy is run by the current user)
+
+```bash
+cd ./plugins/{{plugin_name}}/
+chmod +x ./{{plugin_name}}
+```
+
+
+
+Sometime plugins might come with additional assets other than the binary file. If that is the case, extract all of the plugins content into the folder with the plugin's name.
+
+After the folder structure is ready, restart Zoraxy. If you are using systemd for Zoraxy, use `sudo systemctl restart zoraxy` to restart Zoraxy via systemd service.
\ No newline at end of file
diff --git a/docs/plugins/docs/1. Introduction/4. Enable Plugins.md b/docs/plugins/docs/1. Introduction/4. Enable Plugins.md
new file mode 100644
index 0000000..d1aa702
--- /dev/null
+++ b/docs/plugins/docs/1. Introduction/4. Enable Plugins.md
@@ -0,0 +1,40 @@
+# Enable Plugins
+
+Last Update: 25/05/2025
+
+---
+
+To enable and assign a plugin to a certain HTTP Proxy Rule, you will need to do the following steps
+
+## 1. Create a tag for your HTTP Proxy Rules
+
+Let say you want to enable debugger on some of your HTTP Proxy Rules. You can do that by first creating a tag in the tag editor. In the example below, we will be using the tag "debug". After adding the tag to the HTTP Proxy rule, you will see something like this.
+
+
+
+
+
+---
+
+## 2. Enable Plugin
+
+Click on the "Enable" button on the plugin which you want to enable
+
+
+
+---
+
+## 3. Assign Plugin to HTTP Proxy Rule
+
+Finally, select the tag that you just created in the dropdown menu
+
+
+
+
+
+Afterward, you will see the plugin is attached to the target tag
+
+
+
+It means the plugin is enabled on the HTTP proxy rule
+
diff --git a/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527193601017.png b/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527193601017.png
new file mode 100644
index 0000000..d32e67b
Binary files /dev/null and b/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527193601017.png differ
diff --git a/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527193748456.png b/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527193748456.png
new file mode 100644
index 0000000..40e248d
Binary files /dev/null and b/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527193748456.png differ
diff --git a/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527194052408.png b/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527194052408.png
new file mode 100644
index 0000000..4336e78
Binary files /dev/null and b/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527194052408.png differ
diff --git a/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527195703464.png b/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527195703464.png
new file mode 100644
index 0000000..7dac9fa
Binary files /dev/null and b/docs/plugins/docs/1. Introduction/img/3. Enable Plugins/image-20250527195703464.png differ
diff --git a/docs/plugins/docs/2. Architecture/1. Plugin Architecture.md b/docs/plugins/docs/2. Architecture/1. Plugin Architecture.md
index 16995da..8975975 100644
--- a/docs/plugins/docs/2. Architecture/1. Plugin Architecture.md
+++ b/docs/plugins/docs/2. Architecture/1. Plugin Architecture.md
@@ -1,5 +1,9 @@
# Plugin Architecture
+Last Update: 25/05/2025
+
+---
+
The Zoraxy Plugin uses a 3 steps approach to get information from plugin, setup the plugin and forward request to plugin. The name of the steps are partially referred from dbus designs as followings.
1. Introspect
@@ -10,3 +14,4 @@ The overall flow looks like this.

+This design make sure that the Zoraxy plugins do not depends on platform dependent implementations that uses, for example, unix socket. This also avoided protocol that require complex conversion to and from HTTP request (data structure) like gRPC, while making sure the plugin can be cross compile into different CPU architecture or OS environment with little to no effect on its performance.
diff --git a/docs/plugins/docs/2. Architecture/2. Introspect.md b/docs/plugins/docs/2. Architecture/2. Introspect.md
index 00d3186..16fa8ab 100644
--- a/docs/plugins/docs/2. Architecture/2. Introspect.md
+++ b/docs/plugins/docs/2. Architecture/2. Introspect.md
@@ -1,5 +1,9 @@
# Introspect
+Last Update: 25/05/2025
+
+---
+
Introspect, similar to the one in dbus design, is used to get the information from plugin when Zoraxy starts (or manually triggered in development mode or force reload plugin list).
**This is a pre-defined structure where the plugin must provide to Zoraxy** when the plugin is being started with the `-introspect` flag.
@@ -55,6 +59,12 @@ type IntroSpect struct {
}
```
+
+
+The introspect provide Zoraxy the required information to start the plugin and how to interact with it. For more details on what those capture settings are for, see "Capture Mode" section.
+
+---
+
## Introspect Manual Triggering
To manually test if the introspect return is correct, you can try using the `-introspect` flag on any Zoraxy plugin. You should be able to see an output like so.
diff --git a/docs/plugins/docs/2. Architecture/5. Plugin UI.md b/docs/plugins/docs/2. Architecture/5. Plugin UI.md
new file mode 100644
index 0000000..b4e64b4
--- /dev/null
+++ b/docs/plugins/docs/2. Architecture/5. Plugin UI.md
@@ -0,0 +1,23 @@
+# Plugin UI
+
+Last Update: 25/05/2025
+
+---
+
+A plugin can optionally expose a Web UI interface for user configuration.
+
+**It is generally recommended that a plugin have such UI exposed for easy configurations.** As plugin installed via plugin store provides limited ways for a user to configure the plugin, the plugin web UI will be the best way for user to setup your plugin.
+
+## Plugin Web UI Access
+
+If a plugin provide a Web UI endpoint for Zoraxy during the introspect process, a new item will be shown in the Plugins section on Zoraxy side menu. Below is an example of the Web UI of UPnP Port Forwarder plugin.
+
+
+
+
+
+## Front-end Developer Notes
+
+The Web UI is implemented as a reverse proxy and embed in an iframe. So you do not need to handle CORS issues with the web UI (as it will be proxy internally by Zoraxy as exposed as something like a virtual directory mounted website).
+
+However, the plugin web UI is exposed via the path `/plugin.ui/{{plugin_uuid}}/`, for example, `/plugin.ui/org.aroz.zoraxy.plugins.upnp/`. **When developing the plugin web UI, do not use absolute path for any resources used in the HTML file**, unless you are trying to re-use Zoraxy components like css or image elements stored in Zoraxy embedded web file system (e.g. `/img/logo.svg`).
\ No newline at end of file
diff --git a/docs/plugins/docs/2. Architecture/img/5. Plugin UI/image-20250527201750613.png b/docs/plugins/docs/2. Architecture/img/5. Plugin UI/image-20250527201750613.png
new file mode 100644
index 0000000..4792a4e
Binary files /dev/null and b/docs/plugins/docs/2. Architecture/img/5. Plugin UI/image-20250527201750613.png differ
diff --git a/docs/plugins/docs/3. Basic Examples/1. Hello World.md b/docs/plugins/docs/3. Basic Examples/1. Hello World.md
new file mode 100644
index 0000000..6729085
--- /dev/null
+++ b/docs/plugins/docs/3. Basic Examples/1. Hello World.md
@@ -0,0 +1,325 @@
+# Hello World!
+
+Last Update: 25/05/2025
+
+---
+
+Let start with a really simple Hello World plugin. This only function of this plugin is to print "Hello World" in the plugin web UI.
+
+---
+
+## 1. Name your plugin
+
+First things first, give your plugin a name. In this example, we are using the name "helloworld".
+
+**Plugin name cannot contain space or special characters**, so you must use a file name that satisfy the requirement. No worry, the plugin file name is not the same as the plugin display name in the introspect.
+
+---
+
+## 2. Create the plugin folder
+
+ If your zoraxy root folder do not contains a folder named "plugins", it might implies that your Zoraxy is freshly clone from Github. **You will need to build and run it once to start working on your plugin**, so if you have a newly cloned source code of Zoraxy, do the followings.
+
+```bash
+git clone https://github.com/tobychui/zoraxy
+cd src
+go mod tidy
+go build
+sudo ./zoraxy
+```
+
+Afterward, create a plugin folder under your Zoraxy development environment that is exactly matching your plugin name. In the above example, the folder name should be "helloworld".
+
+```bash
+# Assume you are already inside the src/ folder
+mkdir helloworld
+cd ./helloworld
+```
+
+---
+
+## 3. Create a go project
+
+Similar to any Go project, you can start by creating a `main.go` file. Next, you would want to let the go compiler knows your plugin name so when generating a binary file, it knows what to name it. This can be done via using the `go mod init` command.
+
+```bash
+touch main.go
+go mod init example.com/zoraxy/helloworld
+ls
+# After you are done, you should see the followings
+# go.mod main.go
+```
+
+---
+
+## 4. Copy the Zoraxy plugin lib from Zoraxy source code
+
+ Locate the Zoraxy plugin library from the Zoraxy source code. You can find the `zoraxy_plugin` Go module under `src/mod/plugins/zoraxy_plugin`
+
+Copy the `zoraxy_plugin` folder from the Zoraxy source code mod folder into the your plugin’s mod folder. Let assume you use the same mod folder name as Zoraxy as `mod`, then your copied library path should be `plugins/helloword/mod/zoraxy_plugin`
+
+```bash
+mkdir ./mod
+cp -r "mod/plugins/zoraxy_plugin" ./mod/
+ls ./mod/zoraxy_plugin/
+# You should see something like this (might be different in future versions)
+# dev_webserver.go dynamic_router.go embed_webserver.go README.txt static_router.go zoraxy_plugin.go
+```
+
+---
+
+## 5. Create a web resources folder
+
+Lets create a www folder and put all our web resources, we need to create an `index.html` file as our plugin web ui homepage. This can be done by creating a HTML file in the www folder.
+
+```bash
+# Assuming you are currently in the src/plugins/helloworld/ folder
+mkdir www
+cd www
+touch index.html
+```
+
+
+
+And here is an example `index.html` file that uses the Zoraxy internal resources like css and dark theme toggle mechanism. That csrf token template is not straightly needed in this example as helloworld plugin do not make any POST request to Zoraxy webmin interface, but it might come in handy later.
+
+```html
+
+
+
Welcome to your first Zoraxy plugin
++
+ Last Update: 25/05/2025 +
+ +
Zoraxy Plugin is a powerful extension feature designed to enhance the functionality of the Zoraxy system. It provides additional features and capabilities that are not part of the core system, allowing users to customize their experience and optimize performance. The plugin is built to be modular and flexible, enabling users to tailor their Zoraxy environment to meet specific needs. diff --git a/docs/plugins/html/1. Introduction/2. Getting Started.html b/docs/plugins/html/1. Introduction/2. Getting Started.html index e0b2111..3721d96 100644 --- a/docs/plugins/html/1. Introduction/2. Getting Started.html +++ b/docs/plugins/html/1. Introduction/2. Getting Started.html @@ -95,6 +95,12 @@ Getting Started + + Installing Plugin + + + Enable Plugins + Architecture @@ -113,11 +119,19 @@ Capture Modes + + Plugin UI + - Getting Started + Basic Examples +
index @@ -130,6 +144,12 @@+
+ Last Update: 25/05/2025 +
+ +
To start developing plugins, you will need the following installed on your computer diff --git a/docs/plugins/html/1. Introduction/3. Installing Plugin.html b/docs/plugins/html/1. Introduction/3. Installing Plugin.html new file mode 100644 index 0000000..90828c9 --- /dev/null +++ b/docs/plugins/html/1. Introduction/3. Installing Plugin.html @@ -0,0 +1,216 @@ + + +
+ + + ++
+ Last Update: 25/05/2025 +
+ + ++
+ (Work in progress) +
+ ++
+ The plugin shall be placed inside the + + plugins/{{plugin_name}}/ + + directory where the binary executable name must be matching with the plugin name. +
+ ++
+ If you are on Linux, also make sure Zoraxy have the execution permission of the plugin. You can use the following command to enable execution of the plugin binary on Linux with the current user (Assume Zoraxy is run by the current user) +
+ +cd ./plugins/{{plugin_name}}/
+chmod +x ./{{plugin_name}}
+
+ + Sometime plugins might come with additional assets other than the binary file. If that is the case, extract all of the plugins content into the folder with the plugin’s name. +
++
+ After the folder structure is ready, restart Zoraxy. If you are using systemd for Zoraxy, use + + sudo systemctl restart zoraxy + + to restart Zoraxy via systemd service. +
+ ++
+ Last Update: 25/05/2025 +
+ + ++
+ To enable and assign a plugin to a certain HTTP Proxy Rule, you will need to do the following steps +
+ ++ Let say you want to enable debugger on some of your HTTP Proxy Rules. You can do that by first creating a tag in the tag editor. In the example below, we will be using the tag “debug”. After adding the tag to the HTTP Proxy rule, you will see something like this. +
++
+ Click on the “Enable” button on the plugin which you want to enable +
++
+
+ Finally, select the tag that you just created in the dropdown menu +
+ ++
+
+ Afterward, you will see the plugin is attached to the target tag +
+ ++
+
+ It means the plugin is enabled on the HTTP proxy rule +
+ ++
+ Last Update: 25/05/2025 +
+ +
The Zoraxy Plugin uses a 3 steps approach to get information from plugin, setup the plugin and forward request to plugin. The name of the steps are partially referred from dbus designs as followings.
@@ -156,6 +176,11 @@
+
+ This design make sure that the Zoraxy plugins do not depends on platform dependent implementations that uses, for example, unix socket. This also avoided protocol that require complex conversion to and from HTTP request (data structure) like gRPC, while making sure the plugin can be cross compile into different CPU architecture or OS environment with little to no effect on its performance. +
++
+ Last Update: 25/05/2025 +
+ +
Introspect, similar to the one in dbus design, is used to get the information from plugin when Zoraxy starts (or manually triggered in development mode or force reload plugin list). @@ -203,6 +223,10 @@ SubscriptionsEvents map[string]string `json:"subscriptions_events"` //Subscriptions events of your plugin, see Zoraxy documentation for more details } +
+ The introspect provide Zoraxy the required information to start the plugin and how to interact with it. For more details on what those capture settings are for, see “Capture Mode” section. +
++
+ Last Update: 25/05/2025 +
+ + ++
+ A plugin can optionally expose a Web UI interface for user configuration. +
+ ++
+ + It is generally recommended that a plugin have such UI exposed for easy configurations. + + As plugin installed via plugin store provides limited ways for a user to configure the plugin, the plugin web UI will be the best way for user to setup your plugin. +
+ ++
+ If a plugin provide a Web UI endpoint for Zoraxy during the introspect process, a new item will be shown in the Plugins section on Zoraxy side menu. Below is an example of the Web UI of UPnP Port Forwarder plugin. +
+ ++
+
+ The Web UI is implemented as a reverse proxy and embed in an iframe. So you do not need to handle CORS issues with the web UI (as it will be proxy internally by Zoraxy as exposed as something like a virtual directory mounted website). +
+ ++
+ However, the plugin web UI is exposed via the path + + /plugin.ui/{{plugin_uuid}}/ + + , for example, + + /plugin.ui/org.aroz.zoraxy.plugins.upnp/ + + . + + When developing the plugin web UI, do not use absolute path for any resources used in the HTML file + + , unless you are trying to re-use Zoraxy components like css or image elements stored in Zoraxy embedded web file system (e.g. + + /img/logo.svg + + ). +
+ ++
+ Last Update: 25/05/2025 +
+ + ++ Let start with a really simple Hello World plugin. This only function of this plugin is to print “Hello World” in the plugin web UI. +
+ ++ First things first, give your plugin a name. In this example, we are using the name “helloworld”. +
++
+ + Plugin name cannot contain space or special characters + + , so you must use a file name that satisfy the requirement. No worry, the plugin file name is not the same as the plugin display name in the introspect. +
+ + ++ If your zoraxy root folder do not contains a folder named “plugins”, it might implies that your Zoraxy is freshly clone from Github. + + You will need to build and run it once to start working on your plugin + + , so if you have a newly cloned source code of Zoraxy, do the followings. +
+git clone https://github.com/tobychui/zoraxy
+cd src
+go mod tidy
+go build
+sudo ./zoraxy
+
+ + Afterward, create a plugin folder under your Zoraxy development environment that is exactly matching your plugin name. In the above example, the folder name should be “helloworld”. +
+# Assume you are already inside the src/ folder
+mkdir helloworld
+cd ./helloworld
+
+
+ +
+ Similar to any Go project, you can start by creating a + + main.go + + file. Next, you would want to let the go compiler knows your plugin name so when generating a binary file, it knows what to name it. This can be done via using the + + go mod init + + command. +
+ +touch main.go
+go mod init example.com/zoraxy/helloworld
+ls
+# After you are done, you should see the followings
+# go.mod main.go
+
+
+ +
+ Locate the Zoraxy plugin library from the Zoraxy source code. You can find the + + zoraxy_plugin + + Go module under + + src/mod/plugins/zoraxy_plugin + +
+ ++
+ Copy the + + zoraxy_plugin + + folder from the Zoraxy source code mod folder into the your plugin’s mod folder. Let assume you use the same mod folder name as Zoraxy as + + mod + + , then your copied library path should be + + plugins/helloword/mod/zoraxy_plugin + +
+ +mkdir ./mod
+cp -r "mod/plugins/zoraxy_plugin" ./mod/
+ls ./mod/zoraxy_plugin/
+# You should see something like this (might be different in future versions)
+# dev_webserver.go dynamic_router.go embed_webserver.go README.txt static_router.go zoraxy_plugin.go
+
+
+ +
+ Lets create a www folder and put all our web resources, we need to create an + + index.html + + file as our plugin web ui homepage. This can be done by creating a HTML file in the www folder. +
+ +# Assuming you are currently in the src/plugins/helloworld/ folder
+mkdir www
+cd www
+touch index.html
+
+ +
+ And here is an example + + index.html + + file that uses the Zoraxy internal resources like css and dark theme toggle mechanism. That csrf token template is not straightly needed in this example as helloworld plugin do not make any POST request to Zoraxy webmin interface, but it might come in handy later. +
+ +<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <!-- CSRF token, if your plugin need to make POST request to backend -->
+ <meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
+ <link rel="stylesheet" href="/script/semantic/semantic.min.css">
+ <script src="/script/jquery-3.6.0.min.js"></script>
+ <script src="/script/semantic/semantic.min.js"></script>
+ <script src="/script/utils.js"></script>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link rel="stylesheet" href="/main.css">
+ <title>Hello World</title>
+ <style>
+ body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+ font-family: Arial, sans-serif;
+ background:none;
+ }
+ </style>
+</head>
+<body>
+ <!-- Dark theme script must be included after body tag-->
+ <link rel="stylesheet" href="/darktheme.css">
+ <script src="/script/darktheme.js"></script>
+ <div style="text-align: center;">
+ <h1>Hello World</h1>
+ <p>Welcome to your first Zoraxy plugin</p>
+ </div>
+</body>
+</html>
+
+
+ +
+ To create a handler for introspect, you can first start your plugin with a few constants. +
+ ++
+ After you have defined these constant, we can use + + plugin.ServeAndRecvSpec + + function to handle the handshake between Zoraxy and your plugin. +
+ +const (
+ PLUGIN_ID = "com.example.helloworld"
+ UI_PATH = "/"
+ WEB_ROOT = "/www"
+)
+
+func main(){
+ runtimeCfg, err := plugin.ServeAndRecvSpec(&plugin.IntroSpect{
+ ID: "com.example.helloworld",
+ Name: "Hello World Plugin",
+ Author: "foobar",
+ AuthorContact: "admin@example.com",
+ Description: "A simple hello world plugin",
+ URL: "https://example.com",
+ Type: plugin.PluginType_Utilities,
+ VersionMajor: 1,
+ VersionMinor: 0,
+ VersionPatch: 0,
+
+ // As this is a utility plugin, we don't need to capture any traffic
+ // but only serve the UI, so we set the UI (relative to the plugin path) to "/"
+ UIPath: UI_PATH,
+ })
+ if err != nil {
+ //Terminate or enter standalone mode here
+ panic(err)
+ }
+}
+
+ + + Notes: If some post processing is needed between Introspect and Configure, you can use two seperate function to handle the first start and the second starting of your plugin. The “seperated version” of + + ServeAndRecvSpec + + is defined as + + ServeIntroSpect(pluginSpect *IntroSpect) + + and + + RecvConfigureSpec() (*ConfigureSpec, error) + + . See + + zoraxy_plugin.go + + for more information. + +
+ ++
+ After that, we need to create a web server to serve our plugin UI to Zoraxy via HTTP. This can be done via the + + http.FileServer + + but for simplicity and ease of upgrade, the Zoraxy plugin library provided an easy to use embedded web FS server API for plugin developers. +
+ ++
+ To use the Zoraxy plugin embedded web server, you first need to embed your web fs into Zoraxy as such. +
+ +import (
+ _ "embed"
+ "fmt"
+
+ plugin "example.com/zoraxy/helloworld/mod/zoraxy_plugin"
+)
+
+//go:embed www/*
+var content embed.FS
+
+ +
+ Then call to the + + NewPluginEmbedUIRouter + + to create a new UI router from the embedded Fs. +
+ +// Create a new PluginEmbedUIRouter that will serve the UI from web folder
+// The router will also help to handle the termination of the plugin when
+// a user wants to stop the plugin via Zoraxy Web UI
+embedWebRouter := plugin.NewPluginEmbedUIRouter(PLUGIN_ID, &content, WEB_ROOT, UI_PATH)
+
+ +
+ Here is the tricky part. since not all platform support cross process signaling, Zoraxy plugin uses HTTP request to request a plugin to shutdown. The + + embedWebRouter + + object has a function named + + RegisterTerminateHandler + + where you can easily use this function to register actions that needed to be done before shutdown. +
+ +embedWebRouter.RegisterTerminateHandler(func() {
+ // Do cleanup here if needed
+ fmt.Println("Hello World Plugin Exited")
+}, nil)
+
+ + + Notes: This is a blocking function. That is why Zoraxy has a build-in timeout context where if the terminate request takes more than 3 seconds, the plugin process will be treated as “freezed” and forcefully terminated. So please make sure the terminate handler complete its shutdown procedures within 3 seconds. + +
+ ++
+ After you have created a embedded web router, you can register it to the UI PATH as follows. +
+ +// Serve the hello world page in the www folder
+http.Handle(UI_PATH, embedWebRouter.Handler())
+fmt.Println("Hello World started at http://127.0.0.1:" + strconv.Itoa(runtimeCfg.Port))
+err = http.ListenAndServe("127.0.0.1:"+strconv.Itoa(runtimeCfg.Port), nil)
+if err != nil {
+ panic(err)
+}
+
+ +
+ As this is just the standard golang net/http package, you can of course add more Function Handlers to it based on your needs. There are something that you need to know about adding API endpoints, we will discuss this in the later sections. +
+ + ++
+ After saving the + + main.go + + file, you can now build your plugin with + + go build + + . It should generate the plugin in your platform architecture and OS. If you are on Linux, it will be + + helloworld + + and if you are on Windows, it will be + + helloworld.exe + + . +
+ ++
+ After you are done, restart Zoraxy and enable your plugin in the Plugin List. Now you can test and debug your plugin with your HTTP Proxy Rules. All the STDOUT and STDERR of your plugin will be forwarded to the STDOUT of Zoraxy as well as the log file. +
+ ++
+ + Tips + +
+ ++
+ You can also enable the Developer Option - Plugin Auto Reload function if you are too lazy to restart Zoraxy everytime the plugin binary changed. +
+ ++
+
+ This is the full code of the helloworld plugin main.go file. +
+ +package main
+
+import (
+ "embed"
+ _ "embed"
+ "fmt"
+ "net/http"
+ "strconv"
+
+ plugin "example.com/zoraxy/helloworld/mod/zoraxy_plugin"
+)
+
+const (
+ PLUGIN_ID = "com.example.helloworld"
+ UI_PATH = "/"
+ WEB_ROOT = "/www"
+)
+
+//go:embed www/*
+var content embed.FS
+
+func main() {
+ // Serve the plugin intro spect
+ // This will print the plugin intro spect and exit if the -introspect flag is provided
+ runtimeCfg, err := plugin.ServeAndRecvSpec(&plugin.IntroSpect{
+ ID: "com.example.helloworld",
+ Name: "Hello World Plugin",
+ Author: "foobar",
+ AuthorContact: "admin@example.com",
+ Description: "A simple hello world plugin",
+ URL: "https://example.com",
+ Type: plugin.PluginType_Utilities,
+ VersionMajor: 1,
+ VersionMinor: 0,
+ VersionPatch: 0,
+
+ // As this is a utility plugin, we don't need to capture any traffic
+ // but only serve the UI, so we set the UI (relative to the plugin path) to "/"
+ UIPath: UI_PATH,
+ })
+ if err != nil {
+ //Terminate or enter standalone mode here
+ panic(err)
+ }
+
+ // Create a new PluginEmbedUIRouter that will serve the UI from web folder
+ // The router will also help to handle the termination of the plugin when
+ // a user wants to stop the plugin via Zoraxy Web UI
+ embedWebRouter := plugin.NewPluginEmbedUIRouter(PLUGIN_ID, &content, WEB_ROOT, UI_PATH)
+ embedWebRouter.RegisterTerminateHandler(func() {
+ // Do cleanup here if needed
+ fmt.Println("Hello World Plugin Exited")
+ }, nil)
+
+ // Serve the hello world page in the www folder
+ http.Handle(UI_PATH, embedWebRouter.Handler())
+ fmt.Println("Hello World started at http://127.0.0.1:" + strconv.Itoa(runtimeCfg.Port))
+ err = http.ListenAndServe("127.0.0.1:"+strconv.Itoa(runtimeCfg.Port), nil)
+ if err != nil {
+ panic(err)
+ }
+
+}
+
+
+ If you are not redirected automatically, follow this link.
+If you are not redirected automatically, follow this link.