mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +02:00
Updated example plugins
- Updated example plugins - Added debugger - Removed some trash files
This commit is contained in:
parent
3993ac954c
commit
214b69b0b8
1
.gitignore
vendored
1
.gitignore
vendored
@ -46,3 +46,4 @@ src/log/
|
|||||||
/Dockerfile
|
/Dockerfile
|
||||||
/Entrypoint.sh
|
/Entrypoint.sh
|
||||||
example/plugins/zerotiernc/authtoken.secret
|
example/plugins/zerotiernc/authtoken.secret
|
||||||
|
example/plugins/ztnc/ztnc.db
|
||||||
|
22
example/plugins/build_all.sh
Normal file
22
example/plugins/build_all.sh
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Iterate over all directories in the current directory
|
||||||
|
for dir in */; do
|
||||||
|
if [ -d "$dir" ]; then
|
||||||
|
echo "Processing directory: $dir"
|
||||||
|
cd "$dir"
|
||||||
|
|
||||||
|
# Execute go mod tidy
|
||||||
|
echo "Running go mod tidy in $dir"
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
|
# Execute go build
|
||||||
|
echo "Running go build in $dir"
|
||||||
|
go build
|
||||||
|
|
||||||
|
# Return to the parent directory
|
||||||
|
cd ..
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Build process completed for all directories."
|
3
example/plugins/debugger/go.mod
Normal file
3
example/plugins/debugger/go.mod
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module aroz.org/zoraxy/debugger
|
||||||
|
|
||||||
|
go 1.23.6
|
70
example/plugins/debugger/main.go
Normal file
70
example/plugins/debugger/main.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
plugin "aroz.org/zoraxy/debugger/mod/zoraxy_plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PLUGIN_ID = "org.aroz.zoraxy.debugger"
|
||||||
|
UI_PATH = "/debug"
|
||||||
|
)
|
||||||
|
|
||||||
|
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: "org.aroz.zoraxy.debugger",
|
||||||
|
Name: "Plugin Debugger",
|
||||||
|
Author: "aroz.org",
|
||||||
|
AuthorContact: "https://aroz.org",
|
||||||
|
Description: "A debugger for Zoraxy <-> plugin communication pipeline",
|
||||||
|
URL: "https://zoraxy.aroz.org",
|
||||||
|
Type: plugin.PluginType_Router,
|
||||||
|
VersionMajor: 1,
|
||||||
|
VersionMinor: 0,
|
||||||
|
VersionPatch: 0,
|
||||||
|
|
||||||
|
GlobalCapturePaths: []plugin.CaptureRule{
|
||||||
|
{
|
||||||
|
CapturePath: "/debug_test", //Capture all traffic of all HTTP proxy rule
|
||||||
|
IncludeSubPaths: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GlobalCaptureIngress: "",
|
||||||
|
AlwaysCapturePaths: []plugin.CaptureRule{},
|
||||||
|
AlwaysCaptureIngress: "",
|
||||||
|
|
||||||
|
UIPath: UI_PATH,
|
||||||
|
|
||||||
|
/*
|
||||||
|
SubscriptionPath: "/subept",
|
||||||
|
SubscriptionsEvents: []plugin.SubscriptionEvent{
|
||||||
|
*/
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
//Terminate or enter standalone mode here
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the shutdown handler
|
||||||
|
plugin.RegisterShutdownHandler(func() {
|
||||||
|
// Do cleanup here if needed
|
||||||
|
fmt.Println("Debugger Terminated")
|
||||||
|
})
|
||||||
|
|
||||||
|
http.HandleFunc(UI_PATH+"/", RenderDebugUI)
|
||||||
|
http.HandleFunc("/gcapture", HandleIngressCapture)
|
||||||
|
fmt.Println("Debugger started at http://127.0.0.1:" + strconv.Itoa(runtimeCfg.Port))
|
||||||
|
http.ListenAndServe("127.0.0.1:"+strconv.Itoa(runtimeCfg.Port), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the captured request
|
||||||
|
func HandleIngressCapture(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprint(w, "Capture request received")
|
||||||
|
w.Header().Set("Content-Type", "text/html")
|
||||||
|
w.Write([]byte("This request is captured by the debugger"))
|
||||||
|
}
|
19
example/plugins/debugger/mod/zoraxy_plugin/README.txt
Normal file
19
example/plugins/debugger/mod/zoraxy_plugin/README.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Zoraxy Plugin
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This module serves as a template for building your own plugins for the Zoraxy Reverse Proxy. By copying this module to your plugin mod folder, you can create a new plugin with the necessary structure and components.
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
1. **Copy the Module:**
|
||||||
|
- Copy the entire `zoraxy_plugin` module to your plugin mod folder.
|
||||||
|
|
||||||
|
2. **Include the Structure:**
|
||||||
|
- Ensure that you maintain the directory structure and file organization as provided in this module.
|
||||||
|
|
||||||
|
3. **Modify as Needed:**
|
||||||
|
- Customize the copied module to implement the desired functionality for your plugin.
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
zoraxy_plugin: Handle -introspect and -configuration process required for plugin loading and startup
|
||||||
|
embed_webserver: Handle embeded web server routing and injecting csrf token to your plugin served UI pages
|
106
example/plugins/debugger/mod/zoraxy_plugin/embed_webserver.go
Normal file
106
example/plugins/debugger/mod/zoraxy_plugin/embed_webserver.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package zoraxy_plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PluginUiRouter struct {
|
||||||
|
PluginID string //The ID of the plugin
|
||||||
|
TargetFs *embed.FS //The embed.FS where the UI files are stored
|
||||||
|
TargetFsPrefix string //The prefix of the embed.FS where the UI files are stored, e.g. /web
|
||||||
|
HandlerPrefix string //The prefix of the handler used to route this router, e.g. /ui
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPluginEmbedUIRouter creates a new PluginUiRouter with embed.FS
|
||||||
|
// The targetFsPrefix is the prefix of the embed.FS where the UI files are stored
|
||||||
|
// The targetFsPrefix should be relative to the root of the embed.FS
|
||||||
|
// The targetFsPrefix should start with a slash (e.g. /web) that corresponds to the root folder of the embed.FS
|
||||||
|
// The handlerPrefix is the prefix of the handler used to route this router
|
||||||
|
// The handlerPrefix should start with a slash (e.g. /ui) that matches the http.Handle path
|
||||||
|
// All prefix should not end with a slash
|
||||||
|
func NewPluginEmbedUIRouter(pluginID string, targetFs *embed.FS, targetFsPrefix string, handlerPrefix string) *PluginUiRouter {
|
||||||
|
//Make sure all prefix are in /prefix format
|
||||||
|
if !strings.HasPrefix(targetFsPrefix, "/") {
|
||||||
|
targetFsPrefix = "/" + targetFsPrefix
|
||||||
|
}
|
||||||
|
targetFsPrefix = strings.TrimSuffix(targetFsPrefix, "/")
|
||||||
|
|
||||||
|
if !strings.HasPrefix(handlerPrefix, "/") {
|
||||||
|
handlerPrefix = "/" + handlerPrefix
|
||||||
|
}
|
||||||
|
handlerPrefix = strings.TrimSuffix(handlerPrefix, "/")
|
||||||
|
|
||||||
|
//Return the PluginUiRouter
|
||||||
|
return &PluginUiRouter{
|
||||||
|
PluginID: pluginID,
|
||||||
|
TargetFs: targetFs,
|
||||||
|
TargetFsPrefix: targetFsPrefix,
|
||||||
|
HandlerPrefix: handlerPrefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginUiRouter) populateCSRFToken(r *http.Request, fsHandler http.Handler) http.Handler {
|
||||||
|
//Get the CSRF token from header
|
||||||
|
csrfToken := r.Header.Get("X-Zoraxy-Csrf")
|
||||||
|
if csrfToken == "" {
|
||||||
|
csrfToken = "missing-csrf-token"
|
||||||
|
}
|
||||||
|
|
||||||
|
//Return the middleware
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Check if the request is for an HTML file
|
||||||
|
if strings.HasSuffix(r.URL.Path, "/") {
|
||||||
|
// Redirect to the index.html
|
||||||
|
http.Redirect(w, r, r.URL.Path+"index.html", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(r.URL.Path, ".html") {
|
||||||
|
//Read the target file from embed.FS
|
||||||
|
targetFilePath := strings.TrimPrefix(r.URL.Path, "/")
|
||||||
|
targetFilePath = p.TargetFsPrefix + "/" + targetFilePath
|
||||||
|
targetFilePath = strings.TrimPrefix(targetFilePath, "/")
|
||||||
|
targetFileContent, err := fs.ReadFile(*p.TargetFs, targetFilePath)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "File not found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body := string(targetFileContent)
|
||||||
|
body = strings.ReplaceAll(body, "{{.csrfToken}}", csrfToken)
|
||||||
|
http.ServeContent(w, r, r.URL.Path, time.Now(), strings.NewReader(body))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Call the next handler
|
||||||
|
fsHandler.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHttpHandler returns the http.Handler for the PluginUiRouter
|
||||||
|
func (p *PluginUiRouter) Handler() http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
//Remove the plugin UI handler path prefix
|
||||||
|
rewrittenURL := r.RequestURI
|
||||||
|
rewrittenURL = strings.TrimPrefix(rewrittenURL, p.HandlerPrefix)
|
||||||
|
rewrittenURL = strings.ReplaceAll(rewrittenURL, "//", "/")
|
||||||
|
r.URL, _ = url.Parse(rewrittenURL)
|
||||||
|
r.RequestURI = rewrittenURL
|
||||||
|
|
||||||
|
//Serve the file from the embed.FS
|
||||||
|
subFS, err := fs.Sub(*p.TargetFs, strings.TrimPrefix(p.TargetFsPrefix, "/"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace {{csrf_token}} with the actual CSRF token and serve the file
|
||||||
|
p.populateCSRFToken(r, http.FileServer(http.FS(subFS))).ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
198
example/plugins/debugger/mod/zoraxy_plugin/zoraxy_plugin.go
Normal file
198
example/plugins/debugger/mod/zoraxy_plugin/zoraxy_plugin.go
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
package zoraxy_plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Plugins Includes.go
|
||||||
|
|
||||||
|
This file is copied from Zoraxy source code
|
||||||
|
You can always find the latest version under mod/plugins/includes.go
|
||||||
|
Usually this file are backward compatible
|
||||||
|
*/
|
||||||
|
|
||||||
|
type PluginType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
PluginType_Router PluginType = 0 //Router Plugin, used for handling / routing / forwarding traffic
|
||||||
|
PluginType_Utilities PluginType = 1 //Utilities Plugin, used for utilities like Zerotier or Static Web Server that do not require interception with the dpcore
|
||||||
|
)
|
||||||
|
|
||||||
|
type CaptureRule struct {
|
||||||
|
CapturePath string `json:"capture_path"`
|
||||||
|
IncludeSubPaths bool `json:"include_sub_paths"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ControlStatusCode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ControlStatusCode_CAPTURED ControlStatusCode = 280 //Traffic captured by plugin, ask Zoraxy not to process the traffic
|
||||||
|
ControlStatusCode_UNHANDLED ControlStatusCode = 284 //Traffic not handled by plugin, ask Zoraxy to process the traffic
|
||||||
|
ControlStatusCode_ERROR ControlStatusCode = 580 //Error occurred while processing the traffic, ask Zoraxy to process the traffic and log the error
|
||||||
|
)
|
||||||
|
|
||||||
|
type SubscriptionEvent struct {
|
||||||
|
EventName string `json:"event_name"`
|
||||||
|
EventSource string `json:"event_source"`
|
||||||
|
Payload string `json:"payload"` //Payload of the event, can be empty
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuntimeConstantValue struct {
|
||||||
|
ZoraxyVersion string `json:"zoraxy_version"`
|
||||||
|
ZoraxyUUID string `json:"zoraxy_uuid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
IntroSpect Payload
|
||||||
|
|
||||||
|
When the plugin is initialized with -introspect flag,
|
||||||
|
the plugin shell return this payload as JSON and exit
|
||||||
|
*/
|
||||||
|
type IntroSpect struct {
|
||||||
|
/* Plugin metadata */
|
||||||
|
ID string `json:"id"` //Unique ID of your plugin, recommended using your own domain in reverse like com.yourdomain.pluginname
|
||||||
|
Name string `json:"name"` //Name of your plugin
|
||||||
|
Author string `json:"author"` //Author name of your plugin
|
||||||
|
AuthorContact string `json:"author_contact"` //Author contact of your plugin, like email
|
||||||
|
Description string `json:"description"` //Description of your plugin
|
||||||
|
URL string `json:"url"` //URL of your plugin
|
||||||
|
Type PluginType `json:"type"` //Type of your plugin, Router(0) or Utilities(1)
|
||||||
|
VersionMajor int `json:"version_major"` //Major version of your plugin
|
||||||
|
VersionMinor int `json:"version_minor"` //Minor version of your plugin
|
||||||
|
VersionPatch int `json:"version_patch"` //Patch version of your plugin
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Endpoint Settings
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Global Capture Settings
|
||||||
|
|
||||||
|
Once plugin is enabled these rules always applies, no matter which HTTP Proxy rule it is enabled on
|
||||||
|
This captures the whole traffic of Zoraxy
|
||||||
|
|
||||||
|
*/
|
||||||
|
GlobalCapturePaths []CaptureRule `json:"global_capture_path"` //Global traffic capture path of your plugin
|
||||||
|
GlobalCaptureIngress string `json:"global_capture_ingress"` //Global traffic capture ingress path of your plugin (e.g. /g_handler)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Always Capture Settings
|
||||||
|
|
||||||
|
Once the plugin is enabled on a given HTTP Proxy rule,
|
||||||
|
these always applies
|
||||||
|
*/
|
||||||
|
AlwaysCapturePaths []CaptureRule `json:"always_capture_path"` //Always capture path of your plugin when enabled on a HTTP Proxy rule (e.g. /myapp)
|
||||||
|
AlwaysCaptureIngress string `json:"always_capture_ingress"` //Always capture ingress path of your plugin when enabled on a HTTP Proxy rule (e.g. /a_handler)
|
||||||
|
|
||||||
|
/* UI Path for your plugin */
|
||||||
|
UIPath string `json:"ui_path"` //UI path of your plugin (e.g. /ui), will proxy the whole subpath tree to Zoraxy Web UI as plugin UI
|
||||||
|
|
||||||
|
/* Subscriptions Settings */
|
||||||
|
SubscriptionPath string `json:"subscription_path"` //Subscription event path of your plugin (e.g. /notifyme), a POST request with SubscriptionEvent as body will be sent to this path when the event is triggered
|
||||||
|
SubscriptionsEvents map[string]string `json:"subscriptions_events"` //Subscriptions events of your plugin, see Zoraxy documentation for more details
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ServeIntroSpect Function
|
||||||
|
|
||||||
|
This function will check if the plugin is initialized with -introspect flag,
|
||||||
|
if so, it will print the intro spect and exit
|
||||||
|
|
||||||
|
Place this function at the beginning of your plugin main function
|
||||||
|
*/
|
||||||
|
func ServeIntroSpect(pluginSpect *IntroSpect) {
|
||||||
|
if len(os.Args) > 1 && os.Args[1] == "-introspect" {
|
||||||
|
//Print the intro spect and exit
|
||||||
|
jsonData, _ := json.MarshalIndent(pluginSpect, "", " ")
|
||||||
|
fmt.Println(string(jsonData))
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ConfigureSpec Payload
|
||||||
|
|
||||||
|
Zoraxy will start your plugin with -configure flag,
|
||||||
|
the plugin shell read this payload as JSON and configure itself
|
||||||
|
by the supplied values like starting a web server at given port
|
||||||
|
that listens to 127.0.0.1:port
|
||||||
|
*/
|
||||||
|
type ConfigureSpec struct {
|
||||||
|
Port int `json:"port"` //Port to listen
|
||||||
|
RuntimeConst RuntimeConstantValue `json:"runtime_const"` //Runtime constant values
|
||||||
|
//To be expanded
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RecvExecuteConfigureSpec Function
|
||||||
|
|
||||||
|
This function will read the configure spec from Zoraxy
|
||||||
|
and return the ConfigureSpec object
|
||||||
|
|
||||||
|
Place this function after ServeIntroSpect function in your plugin main function
|
||||||
|
*/
|
||||||
|
func RecvConfigureSpec() (*ConfigureSpec, error) {
|
||||||
|
for i, arg := range os.Args {
|
||||||
|
if strings.HasPrefix(arg, "-configure=") {
|
||||||
|
var configSpec ConfigureSpec
|
||||||
|
if err := json.Unmarshal([]byte(arg[11:]), &configSpec); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &configSpec, nil
|
||||||
|
} else if arg == "-configure" {
|
||||||
|
var configSpec ConfigureSpec
|
||||||
|
var nextArg string
|
||||||
|
if len(os.Args) > i+1 {
|
||||||
|
nextArg = os.Args[i+1]
|
||||||
|
if err := json.Unmarshal([]byte(nextArg), &configSpec); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("No port specified after -configure flag")
|
||||||
|
}
|
||||||
|
return &configSpec, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("No -configure flag found")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ServeAndRecvSpec Function
|
||||||
|
|
||||||
|
This function will serve the intro spect and return the configure spec
|
||||||
|
See the ServeIntroSpect and RecvConfigureSpec for more details
|
||||||
|
*/
|
||||||
|
func ServeAndRecvSpec(pluginSpect *IntroSpect) (*ConfigureSpec, error) {
|
||||||
|
ServeIntroSpect(pluginSpect)
|
||||||
|
return RecvConfigureSpec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Shutdown handler
|
||||||
|
|
||||||
|
This function will register a shutdown handler for the plugin
|
||||||
|
The shutdown callback will be called when the plugin is shutting down
|
||||||
|
You can use this to clean up resources like closing database connections
|
||||||
|
*/
|
||||||
|
|
||||||
|
func RegisterShutdownHandler(shutdownCallback func()) {
|
||||||
|
// Set up a channel to receive OS signals
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
// Start a goroutine to listen for signals
|
||||||
|
go func() {
|
||||||
|
<-sigChan
|
||||||
|
shutdownCallback()
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
}
|
26
example/plugins/debugger/ui_info.go
Normal file
26
example/plugins/debugger/ui_info.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Render the debug UI
|
||||||
|
func RenderDebugUI(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprint(w, "**Plugin UI Debug Interface**\n\n[Recv Headers] \n")
|
||||||
|
|
||||||
|
headerKeys := make([]string, 0, len(r.Header))
|
||||||
|
for name := range r.Header {
|
||||||
|
headerKeys = append(headerKeys, name)
|
||||||
|
}
|
||||||
|
sort.Strings(headerKeys)
|
||||||
|
for _, name := range headerKeys {
|
||||||
|
values := r.Header[name]
|
||||||
|
for _, value := range values {
|
||||||
|
fmt.Fprintf(w, "%s: %s\n", name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "text/html")
|
||||||
|
}
|
@ -19,6 +19,7 @@
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
|
background:none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
@ -65,7 +65,6 @@ func (p *PluginUiRouter) populateCSRFToken(r *http.Request, fsHandler http.Handl
|
|||||||
targetFilePath := strings.TrimPrefix(r.URL.Path, "/")
|
targetFilePath := strings.TrimPrefix(r.URL.Path, "/")
|
||||||
targetFilePath = p.TargetFsPrefix + "/" + targetFilePath
|
targetFilePath = p.TargetFsPrefix + "/" + targetFilePath
|
||||||
targetFilePath = strings.TrimPrefix(targetFilePath, "/")
|
targetFilePath = strings.TrimPrefix(targetFilePath, "/")
|
||||||
fmt.Println(targetFilePath)
|
|
||||||
targetFileContent, err := fs.ReadFile(*p.TargetFs, targetFilePath)
|
targetFileContent, err := fs.ReadFile(*p.TargetFs, targetFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "File not found", http.StatusNotFound)
|
http.Error(w, "File not found", http.StatusNotFound)
|
||||||
|
@ -1 +0,0 @@
|
|||||||
hgaode9ptnpuaoi1ilbdw9i4
|
|
@ -11,7 +11,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"aroz.org/zoraxy/zerotiernc/mod/database/dbinc"
|
"aroz.org/zoraxy/ztnc/mod/database/dbinc"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"os/user"
|
"os/user"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"aroz.org/zoraxy/zerotiernc/mod/utils"
|
"aroz.org/zoraxy/ztnc/mod/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func readAuthTokenAsAdmin() (string, error) {
|
func readAuthTokenAsAdmin() (string, error) {
|
||||||
|
@ -15,6 +15,11 @@
|
|||||||
<script src="/script/chart.js"></script>
|
<script src="/script/chart.js"></script>
|
||||||
<script src="/script/utils.js"></script>
|
<script src="/script/utils.js"></script>
|
||||||
<link rel="stylesheet" href="/main.css">
|
<link rel="stylesheet" href="/main.css">
|
||||||
|
<style>
|
||||||
|
body{
|
||||||
|
background:none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Dark theme script must be included after body tag-->
|
<!-- Dark theme script must be included after body tag-->
|
||||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user