Files
zoraxy/example/plugins/api-call-example/ui.go
2025-07-19 22:35:44 -07:00

269 lines
8.2 KiB
Go

package main
import (
"fmt"
"html"
"net/http"
"net/http/httputil"
"strconv"
plugin "aroz.org/zoraxy/api-call-example/mod/zoraxy_plugin"
)
func allowedEndpoint(cfg *plugin.ConfigureSpec) (string, error) {
// Make an API call to the permitted endpoint
client := &http.Client{}
apiURL := fmt.Sprintf("http://localhost:%d/plugin/api/access/list", cfg.ZoraxyPort)
req, err := http.NewRequest(http.MethodGet, apiURL, nil)
if err != nil {
return "", fmt.Errorf("error creating request: %v", err)
}
// Make sure to set the Authorization header
req.Header.Set("Authorization", "Bearer "+cfg.APIKey) // Use the API key from the runtime config
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("error making API call: %v", err)
}
defer resp.Body.Close()
// Check if the response status is OK
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("received non-OK response status %d", resp.StatusCode)
}
respDump, err := httputil.DumpResponse(resp, true)
if err != nil {
return "", fmt.Errorf("error dumping response: %v", err)
}
return string(respDump), nil
}
func allowedEndpointInvalidKey(cfg *plugin.ConfigureSpec) (string, error) {
// Make an API call to the permitted endpoint with an invalid key
client := &http.Client{}
apiURL := fmt.Sprintf("http://localhost:%d/plugin/api/access/list", cfg.ZoraxyPort)
req, err := http.NewRequest(http.MethodGet, apiURL, nil)
if err != nil {
return "", fmt.Errorf("error creating request: %v", err)
}
// Use an invalid API key
req.Header.Set("Authorization", "Bearer invalid-key")
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("error making API call: %v", err)
}
defer resp.Body.Close()
respDump, err := httputil.DumpResponse(resp, true)
if err != nil {
return "", fmt.Errorf("error dumping response: %v", err)
}
return string(respDump), nil
}
func unaccessibleEndpoint(cfg *plugin.ConfigureSpec) (string, error) {
// Make an API call to an endpoint that is not permitted
client := &http.Client{}
apiURL := fmt.Sprintf("http://localhost:%d/api/acme/listExpiredDomains", cfg.ZoraxyPort)
req, err := http.NewRequest(http.MethodGet, apiURL, nil)
if err != nil {
return "", fmt.Errorf("error creating request: %v", err)
}
// Use the API key from the runtime config
req.Header.Set("Authorization", "Bearer "+cfg.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("error making API call: %v", err)
}
defer resp.Body.Close()
respDump, err := httputil.DumpResponse(resp, true)
if err != nil {
return "", fmt.Errorf("error dumping response: %v", err)
}
return string(respDump), nil
}
func unpermittedEndpoint(cfg *plugin.ConfigureSpec) (string, error) {
// Make an API call to an endpoint that is plugin-accessible but is not permitted
client := &http.Client{}
apiURL := fmt.Sprintf("http://localhost:%d/plugin/api/proxy/list", cfg.ZoraxyPort)
req, err := http.NewRequest(http.MethodGet, apiURL, nil)
if err != nil {
return "", fmt.Errorf("error creating request: %v", err)
}
// Use the API key from the runtime config
req.Header.Set("Authorization", "Bearer "+cfg.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("error making API call: %v", err)
}
defer resp.Body.Close()
respDump, err := httputil.DumpResponse(resp, true)
if err != nil {
return "", fmt.Errorf("error dumping response: %v", err)
}
return string(respDump), nil
}
func RenderUI(config *plugin.ConfigureSpec, w http.ResponseWriter, r *http.Request) {
// make several types of API calls to demonstrate the plugin functionality
accessList, err := allowedEndpoint(config)
var RenderedAccessListHTML string
if err != nil {
RenderedAccessListHTML = fmt.Sprintf("<p>Error fetching access list: %v</p>", err)
} else {
// Render the access list as HTML
RenderedAccessListHTML = fmt.Sprintf("<pre>%s</pre>", html.EscapeString(accessList))
}
// Make an API call with an invalid key
invalidKeyResponse, err := allowedEndpointInvalidKey(config)
var RenderedInvalidKeyResponseHTML string
if err != nil {
RenderedInvalidKeyResponseHTML = fmt.Sprintf("<p>Error with invalid key: %v</p>", err)
} else {
// Render the invalid key response as HTML
RenderedInvalidKeyResponseHTML = fmt.Sprintf("<pre>%s</pre>", html.EscapeString(invalidKeyResponse))
}
// Make an API call to an endpoint that is not plugin-accessible
unaccessibleResponse, err := unaccessibleEndpoint(config)
var RenderedUnaccessibleResponseHTML string
if err != nil {
RenderedUnaccessibleResponseHTML = fmt.Sprintf("<p>Error with unaccessible endpoint: %v</p>", err)
} else {
// Render the unaccessible response as HTML
RenderedUnaccessibleResponseHTML = fmt.Sprintf("<pre>%s</pre>", html.EscapeString(unaccessibleResponse))
}
// Make an API call to an endpoint that is plugin-accessible but is not permitted
unpermittedResponse, err := unpermittedEndpoint(config)
var RenderedUnpermittedResponseHTML string
if err != nil {
RenderedUnpermittedResponseHTML = fmt.Sprintf("<p>Error with unpermitted endpoint: %v</p>", err)
} else {
// Render the unpermitted response as HTML
RenderedUnpermittedResponseHTML = fmt.Sprintf("<pre>%s</pre>", html.EscapeString(unpermittedResponse))
}
// Render the UI for the plugin
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
html := `
<!DOCTYPE html>
<html>
<head>
<title>API Call Example Plugin UI</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="/main.css">
<script src="/script/jquery-3.6.0.min.js"></script>
<style>
.response-block {
background-color: var(--theme_bg_primary);
border: 1px solid var(--theme_divider);
border-radius: 8px;
padding: 20px;
margin: 15px 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: box-shadow 0.3s ease;
}
.response-block:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.response-block h3 {
margin-top: 0;
color: var(--text_color);
border-bottom: 2px solid #007bff;
padding-bottom: 8px;
}
.response-block.success {
border-left: 4px solid #28a745;
}
.response-block.error {
border-left: 4px solid #dc3545;
}
.response-block.warning {
border-left: 4px solid #ffc107;
}
.response-content {
margin-top: 10px;
}
.response-content pre {
background-color: var(--theme_highlight);
border: 1px solid var(--theme_divider);
border-radius: 4px;
padding: 10px;
overflow: auto;
font-size: 12px;
line-height: 1.4;
height: 200px;
max-height: 80vh;
min-height: 100px;
resize: vertical;
box-sizing: border-box;
}
</style>
</head>
<body>
<!-- Dark theme script must be included after body tag-->
<link rel="stylesheet" href="/darktheme.css">
<script src="/script/darktheme.js"></script>
<h1>Welcome to the API Call Example Plugin UI</h1>
<p>Plugin is running on port: ` + strconv.Itoa(config.Port) + `</p>
<h2>API Call Examples</h2>
<div class="response-block success">
<h3>✅ Allowed Endpoint (Valid API Key)</h3>
<p>Making a GET request to <code>/plugin/api/access/list</code> with a valid API key:</p>
<div class="response-content">
` + RenderedAccessListHTML + `
</div>
</div>
<div class="response-block warning">
<h3>⚠️ Invalid API Key</h3>
<p>Making a GET request to <code>/plugin/api/access/list</code> with an invalid API key:</p>
<div class="response-content">
` + RenderedInvalidKeyResponseHTML + `
</div>
</div>
<div class="response-block warning">
<h3>⚠️ Unpermitted Endpoint</h3>
<p>Making a GET request to <code>/plugin/api/proxy/list</code> (not a permitted endpoint):</p>
<div class="response-content">
` + RenderedUnpermittedResponseHTML + `
</div>
</div>
<div class="response-block error">
<h3>❌ Disallowed Endpoint</h3>
<p>Making a GET request to <code>/api/acme/listExpiredDomains</code> (not a plugin-accessible endpoint):</p>
<div class="response-content">
` + RenderedUnaccessibleResponseHTML + `
</div>
</div>
</body>
</html>`
w.Write([]byte(html))
}