mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-03 06:07:20 +02:00
Merge pull request #509 from adoolaard/dev-tags
Add Tagging Feature for Reverse Proxy Hosts + Search & Filter
This commit is contained in:
commit
6ab91c377f
7
.gitignore
vendored
7
.gitignore
vendored
@ -39,4 +39,9 @@ src/tmp/localhost.pem
|
|||||||
src/www/html/index.html
|
src/www/html/index.html
|
||||||
src/sys.uuid
|
src/sys.uuid
|
||||||
src/zoraxy
|
src/zoraxy
|
||||||
src/log/
|
src/log/
|
||||||
|
|
||||||
|
|
||||||
|
# dev-tags
|
||||||
|
/Dockerfile
|
||||||
|
/Entrypoint.sh
|
@ -1,19 +1,27 @@
|
|||||||
|
# Stage 1: Build Zoraxy
|
||||||
FROM docker.io/golang:alpine AS build-zoraxy
|
FROM docker.io/golang:alpine AS build-zoraxy
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
RUN mkdir -p /opt/zoraxy/source/ &&\
|
RUN mkdir -p /opt/zoraxy/source/ &&\
|
||||||
mkdir -p /usr/local/bin/
|
mkdir -p /usr/local/bin/
|
||||||
|
|
||||||
# If you build it yourself, you will need to add the src directory into the docker directory.
|
|
||||||
COPY ./src/ /opt/zoraxy/source/
|
|
||||||
|
|
||||||
WORKDIR /opt/zoraxy/source/
|
WORKDIR /opt/zoraxy/source/
|
||||||
|
|
||||||
RUN go mod tidy &&\
|
# Copy go.mod and go.sum first to leverage Docker cache
|
||||||
go build -o /usr/local/bin/zoraxy &&\
|
COPY ./src/go.mod ./src/go.sum ./
|
||||||
|
RUN go mod tidy
|
||||||
|
|
||||||
|
# Copy the rest of the source code
|
||||||
|
COPY ./src/ .
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN go build -o /usr/local/bin/zoraxy &&\
|
||||||
chmod 755 /usr/local/bin/zoraxy
|
chmod 755 /usr/local/bin/zoraxy
|
||||||
|
|
||||||
|
# Stage 2: Build ZeroTier
|
||||||
FROM docker.io/ubuntu:latest AS build-zerotier
|
FROM docker.io/ubuntu:latest AS build-zerotier
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
RUN mkdir -p /opt/zerotier/source/ &&\
|
RUN mkdir -p /opt/zerotier/source/ &&\
|
||||||
mkdir -p /usr/local/bin/
|
mkdir -p /usr/local/bin/
|
||||||
|
|
||||||
@ -22,6 +30,7 @@ WORKDIR /opt/zerotier/source/
|
|||||||
RUN apt-get update -y &&\
|
RUN apt-get update -y &&\
|
||||||
apt-get install -y curl jq build-essential pkg-config clang cargo libssl-dev
|
apt-get install -y curl jq build-essential pkg-config clang cargo libssl-dev
|
||||||
|
|
||||||
|
# Download and build ZeroTier
|
||||||
RUN curl -Lo ZeroTierOne.tar.gz https://codeload.github.com/zerotier/ZeroTierOne/tar.gz/refs/tags/1.10.6 &&\
|
RUN curl -Lo ZeroTierOne.tar.gz https://codeload.github.com/zerotier/ZeroTierOne/tar.gz/refs/tags/1.10.6 &&\
|
||||||
tar -xzvf ZeroTierOne.tar.gz &&\
|
tar -xzvf ZeroTierOne.tar.gz &&\
|
||||||
cd ZeroTierOne-* &&\
|
cd ZeroTierOne-* &&\
|
||||||
@ -29,19 +38,24 @@ RUN curl -Lo ZeroTierOne.tar.gz https://codeload.github.com/zerotier/ZeroTierOne
|
|||||||
mv ./zerotier-one /usr/local/bin/zerotier-one &&\
|
mv ./zerotier-one /usr/local/bin/zerotier-one &&\
|
||||||
chmod 755 /usr/local/bin/zerotier-one
|
chmod 755 /usr/local/bin/zerotier-one
|
||||||
|
|
||||||
|
# Stage 3: Final image
|
||||||
FROM docker.io/ubuntu:latest
|
FROM docker.io/ubuntu:latest
|
||||||
|
|
||||||
|
# Install runtime dependencies
|
||||||
RUN apt-get update -y &&\
|
RUN apt-get update -y &&\
|
||||||
apt-get install -y bash sudo netcat-openbsd libssl-dev ca-certificates
|
apt-get install -y bash sudo netcat-openbsd libssl-dev ca-certificates
|
||||||
|
|
||||||
|
# Copy entrypoint script
|
||||||
COPY --chmod=700 ./entrypoint.sh /opt/zoraxy/
|
COPY --chmod=700 ./entrypoint.sh /opt/zoraxy/
|
||||||
|
|
||||||
|
# Copy built binaries from previous stages
|
||||||
COPY --from=build-zoraxy /usr/local/bin/zoraxy /usr/local/bin/zoraxy
|
COPY --from=build-zoraxy /usr/local/bin/zoraxy /usr/local/bin/zoraxy
|
||||||
COPY --from=build-zerotier /usr/local/bin/zerotier-one /usr/local/bin/zerotier-one
|
COPY --from=build-zerotier /usr/local/bin/zerotier-one /usr/local/bin/zerotier-one
|
||||||
|
|
||||||
WORKDIR /opt/zoraxy/config/
|
WORKDIR /opt/zoraxy/config/
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
ENV ZEROTIER="false"
|
ENV ZEROTIER="false"
|
||||||
|
|
||||||
ENV AUTORENEW="86400"
|
ENV AUTORENEW="86400"
|
||||||
ENV CFGUPGRADE="true"
|
ENV CFGUPGRADE="true"
|
||||||
ENV DB="auto"
|
ENV DB="auto"
|
||||||
@ -60,9 +74,12 @@ ENV WEBROOT="./www"
|
|||||||
ENV ZTAUTH=""
|
ENV ZTAUTH=""
|
||||||
ENV ZTPORT="9993"
|
ENV ZTPORT="9993"
|
||||||
|
|
||||||
|
# Define volumes
|
||||||
VOLUME [ "/opt/zoraxy/config/" ]
|
VOLUME [ "/opt/zoraxy/config/" ]
|
||||||
|
|
||||||
|
# Set entrypoint
|
||||||
ENTRYPOINT [ "/opt/zoraxy/entrypoint.sh" ]
|
ENTRYPOINT [ "/opt/zoraxy/entrypoint.sh" ]
|
||||||
|
|
||||||
|
# Healthcheck
|
||||||
HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 CMD nc -vz 127.0.0.1 $PORT || exit 1
|
HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 CMD nc -vz 127.0.0.1 $PORT || exit 1
|
||||||
|
|
||||||
|
@ -54,6 +54,11 @@ func LoadReverseProxyConfig(configFilepath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Make sure the tags are not nil
|
||||||
|
if thisConfigEndpoint.Tags == nil {
|
||||||
|
thisConfigEndpoint.Tags = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
//Matching domain not set. Assume root
|
//Matching domain not set. Assume root
|
||||||
if thisConfigEndpoint.RootOrMatchingDomain == "" {
|
if thisConfigEndpoint.RootOrMatchingDomain == "" {
|
||||||
thisConfigEndpoint.RootOrMatchingDomain = "/"
|
thisConfigEndpoint.RootOrMatchingDomain = "/"
|
||||||
@ -175,8 +180,8 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Set the Content-Type header to indicate it's a zip file
|
// Set the Content-Type header to indicate it's a zip file
|
||||||
w.Header().Set("Content-Type", "application/zip")
|
w.Header().Set("Content-Type", "application/zip")
|
||||||
// Set the Content-Disposition header to specify the file name
|
// Set the Content-Disposition header to specify the file name, add timestamp to the filename
|
||||||
w.Header().Set("Content-Disposition", "attachment; filename=\"config.zip\"")
|
w.Header().Set("Content-Disposition", "attachment; filename=\"zoraxy-config-"+time.Now().Format("2006-01-02-15-04-05")+".zip\"")
|
||||||
|
|
||||||
// Create a zip writer
|
// Create a zip writer
|
||||||
zipWriter := zip.NewWriter(w)
|
zipWriter := zip.NewWriter(w)
|
||||||
|
@ -194,6 +194,7 @@ type ProxyEndpoint struct {
|
|||||||
|
|
||||||
//Internal Logic Elements
|
//Internal Logic Elements
|
||||||
parent *Router `json:"-"`
|
parent *Router `json:"-"`
|
||||||
|
Tags []string // Tags for the proxy endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -305,6 +305,15 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tagStr, _ := utils.PostPara(r, "tags")
|
||||||
|
tags := []string{}
|
||||||
|
if tagStr != "" {
|
||||||
|
tags = strings.Split(tagStr, ",")
|
||||||
|
for i := range tags {
|
||||||
|
tags[i] = strings.TrimSpace(tags[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var proxyEndpointCreated *dynamicproxy.ProxyEndpoint
|
var proxyEndpointCreated *dynamicproxy.ProxyEndpoint
|
||||||
if eptype == "host" {
|
if eptype == "host" {
|
||||||
rootOrMatchingDomain, err := utils.PostPara(r, "rootname")
|
rootOrMatchingDomain, err := utils.PostPara(r, "rootname")
|
||||||
@ -375,6 +384,8 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Rate Limit
|
// Rate Limit
|
||||||
RequireRateLimit: requireRateLimit,
|
RequireRateLimit: requireRateLimit,
|
||||||
RateLimit: int64(proxyRateLimit),
|
RateLimit: int64(proxyRateLimit),
|
||||||
|
|
||||||
|
Tags: tags,
|
||||||
}
|
}
|
||||||
|
|
||||||
preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint)
|
preparedEndpoint, err := dynamicProxyRouter.PrepareProxyRoute(&thisProxyEndpoint)
|
||||||
@ -533,6 +544,15 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tagStr, _ := utils.PostPara(r, "tags")
|
||||||
|
tags := []string{}
|
||||||
|
if tagStr != "" {
|
||||||
|
tags = strings.Split(tagStr, ",")
|
||||||
|
for i := range tags {
|
||||||
|
tags[i] = strings.TrimSpace(tags[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Generate a new proxyEndpoint from the new config
|
//Generate a new proxyEndpoint from the new config
|
||||||
newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry)
|
newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry)
|
||||||
newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS
|
newProxyEndpoint.BypassGlobalTLS = bypassGlobalTLS
|
||||||
@ -557,6 +577,7 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
newProxyEndpoint.RateLimit = proxyRateLimit
|
newProxyEndpoint.RateLimit = proxyRateLimit
|
||||||
newProxyEndpoint.UseStickySession = useStickySession
|
newProxyEndpoint.UseStickySession = useStickySession
|
||||||
newProxyEndpoint.DisableUptimeMonitor = disbleUtm
|
newProxyEndpoint.DisableUptimeMonitor = disbleUtm
|
||||||
|
newProxyEndpoint.Tags = tags
|
||||||
|
|
||||||
//Prepare to replace the current routing rule
|
//Prepare to replace the current routing rule
|
||||||
readyRoutingRule, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
|
readyRoutingRule, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
|
||||||
|
@ -12,6 +12,19 @@
|
|||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
<div class="ui action input" style="margin-bottom: 1em;">
|
||||||
|
<input type="text" id="searchInput" placeholder="Search...">
|
||||||
|
<button class="ui button" onclick="filterProxyList()">Search</button>
|
||||||
|
</div>
|
||||||
|
<div class="ui selection dropdown" id="tagFilterDropdown" style="margin-bottom: 1em;">
|
||||||
|
<input type="hidden" name="tag">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
<div class="default text">Filter by Tag</div>
|
||||||
|
<div class="menu">
|
||||||
|
<div class="item" data-value="">All</div>
|
||||||
|
<!-- Add more tag options dynamically -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div style="width: 100%; overflow-x: auto; margin-bottom: 1em; min-height: 300px;">
|
<div style="width: 100%; overflow-x: auto; margin-bottom: 1em; min-height: 300px;">
|
||||||
<table class="ui celled sortable unstackable compact table">
|
<table class="ui celled sortable unstackable compact table">
|
||||||
<thead>
|
<thead>
|
||||||
@ -19,6 +32,7 @@
|
|||||||
<th>Host</th>
|
<th>Host</th>
|
||||||
<th>Destination</th>
|
<th>Destination</th>
|
||||||
<th>Virtual Directory</th>
|
<th>Virtual Directory</th>
|
||||||
|
<th>Tags</th> <!-- New column for tags -->
|
||||||
<th style="max-width: 300px;">Advanced Settings</th>
|
<th style="max-width: 300px;">Advanced Settings</th>
|
||||||
<th class="no-sort" style="min-width:150px;">Actions</th>
|
<th class="no-sort" style="min-width:150px;">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -124,6 +138,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td data-label="" editable="true" datatype="vdir">${vdList}</td>
|
<td data-label="" editable="true" datatype="vdir">${vdList}</td>
|
||||||
|
<td data-label="tags">
|
||||||
|
${subd.Tags.map(tag => `<span class="ui tiny label tag-select">${tag}</span>`).join("")}
|
||||||
|
</td>
|
||||||
<td data-label="" editable="true" datatype="advanced" style="width: 350px;">
|
<td data-label="" editable="true" datatype="advanced" style="width: 350px;">
|
||||||
${subd.AuthenticationProvider.AuthMethod == 0x1?`<i class="ui grey key icon"></i> Basic Auth`:``}
|
${subd.AuthenticationProvider.AuthMethod == 0x1?`<i class="ui grey key icon"></i> Basic Auth`:``}
|
||||||
${subd.AuthenticationProvider.AuthMethod == 0x2?`<i class="ui blue key icon"></i> Authelia`:``}
|
${subd.AuthenticationProvider.AuthMethod == 0x2?`<i class="ui blue key icon"></i> Authelia`:``}
|
||||||
@ -142,12 +159,43 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>`);
|
</tr>`);
|
||||||
});
|
});
|
||||||
|
populateTagFilterDropdown(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveAccessRuleNameOnHostRPlist();
|
resolveAccessRuleNameOnHostRPlist();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to populate the tag filter dropdown
|
||||||
|
function populateTagFilterDropdown(data) {
|
||||||
|
let tags = new Set();
|
||||||
|
data.forEach(subd => {
|
||||||
|
subd.Tags.forEach(tag => tags.add(tag));
|
||||||
|
});
|
||||||
|
tags = Array.from(tags).sort((a, b) => a.localeCompare(b));
|
||||||
|
let dropdownMenu = $("#tagFilterDropdown .menu");
|
||||||
|
dropdownMenu.html('<div class="item tag-select" data-value="">All</div>');
|
||||||
|
tags.forEach(tag => {
|
||||||
|
dropdownMenu.append(`<div class="item tag-select" data-value="${tag}">${tag}</div>`);
|
||||||
|
});
|
||||||
|
$('#tagFilterDropdown').dropdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to filter the proxy list
|
||||||
|
function filterProxyList() {
|
||||||
|
let searchInput = $("#searchInput").val().toLowerCase();
|
||||||
|
let selectedTag = $("#tagFilterDropdown").dropdown('get value');
|
||||||
|
$("#httpProxyList tr").each(function() {
|
||||||
|
let host = $(this).find("td[data-label='']").text().toLowerCase();
|
||||||
|
let tags = $(this).find("td[data-label='tags']").text().toLowerCase();
|
||||||
|
if ((host.includes(searchInput) || searchInput === "") && (tags.includes(selectedTag) || selectedTag === "")) {
|
||||||
|
$(this).show();
|
||||||
|
} else {
|
||||||
|
$(this).hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//Perform realtime alias update without refreshing the whole page
|
//Perform realtime alias update without refreshing the whole page
|
||||||
function updateAliasListForEndpoint(endpointName, newAliasDomainList){
|
function updateAliasListForEndpoint(endpointName, newAliasDomainList){
|
||||||
let targetEle = $(`.aliasDomains[eptuuid='${endpointName}']`);
|
let targetEle = $(`.aliasDomains[eptuuid='${endpointName}']`);
|
||||||
@ -415,6 +463,7 @@
|
|||||||
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editAliasHostnames('${uuid}');"><i class=" blue at icon"></i> Alias</button>
|
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editAliasHostnames('${uuid}');"><i class=" blue at icon"></i> Alias</button>
|
||||||
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editAccessRule('${uuid}');"><i class="ui filter icon"></i> Access Rule</button>
|
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editAccessRule('${uuid}');"><i class="ui filter icon"></i> Access Rule</button>
|
||||||
<button class="ui basic compact tiny ${enableQuickRequestButton?"":"disabled"} button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="requestCertificateForExistingHost('${uuid}', '${certificateDomains}', this);"><i class="green lock icon"></i> Get Certificate</button>
|
<button class="ui basic compact tiny ${enableQuickRequestButton?"":"disabled"} button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="requestCertificateForExistingHost('${uuid}', '${certificateDomains}', this);"><i class="green lock icon"></i> Get Certificate</button>
|
||||||
|
<button class="ui basic compact tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editTags('${uuid}');"><i class="ui tag icon"></i> Tags</button>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
$(".hostAccessRuleSelector").dropdown();
|
$(".hostAccessRuleSelector").dropdown();
|
||||||
@ -457,6 +506,7 @@
|
|||||||
let requireRateLimit = $(row).find(".RequireRateLimit")[0].checked;
|
let requireRateLimit = $(row).find(".RequireRateLimit")[0].checked;
|
||||||
let rateLimit = $(row).find(".RateLimit").val();
|
let rateLimit = $(row).find(".RateLimit").val();
|
||||||
let bypassGlobalTLS = $(row).find(".BypassGlobalTLS")[0].checked;
|
let bypassGlobalTLS = $(row).find(".BypassGlobalTLS")[0].checked;
|
||||||
|
let tags = $("#proxyTags").val().trim();
|
||||||
|
|
||||||
$.cjax({
|
$.cjax({
|
||||||
url: "/api/proxy/edit",
|
url: "/api/proxy/edit",
|
||||||
@ -470,6 +520,7 @@
|
|||||||
"authprovider" :authProviderType,
|
"authprovider" :authProviderType,
|
||||||
"rate" :requireRateLimit,
|
"rate" :requireRateLimit,
|
||||||
"ratenum" :rateLimit,
|
"ratenum" :rateLimit,
|
||||||
|
"tags": tags,
|
||||||
},
|
},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if (data.error !== undefined){
|
if (data.error !== undefined){
|
||||||
@ -609,4 +660,24 @@
|
|||||||
tabSwitchEventBind["httprp"] = function(){
|
tabSwitchEventBind["httprp"] = function(){
|
||||||
listProxyEndpoints();
|
listProxyEndpoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function editTags(uuid){
|
||||||
|
let payload = encodeURIComponent(JSON.stringify({
|
||||||
|
ept: "host",
|
||||||
|
ep: uuid
|
||||||
|
}));
|
||||||
|
showSideWrapper("snippet/tagEditor.html?t=" + Date.now() + "#" + payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the proxy list on page load
|
||||||
|
$(document).ready(function() {
|
||||||
|
listProxyEndpoints();
|
||||||
|
|
||||||
|
// Event listener for clicking on tags
|
||||||
|
$(document).on('click', '.tag-select', function() {
|
||||||
|
let tag = $(this).text().trim();
|
||||||
|
$('#tagFilterDropdown').dropdown('set selected', tag);
|
||||||
|
filterProxyList();
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
@ -63,6 +63,11 @@
|
|||||||
<label>Sticky Session<br><small>Enable stick session on upstream load balancing</small></label>
|
<label>Sticky Session<br><small>Enable stick session on upstream load balancing</small></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Tags</label>
|
||||||
|
<input type="text" id="proxyTags" placeholder="e.g. mediaserver, management">
|
||||||
|
<small>Comma-separated list of tags for this proxy host.</small>
|
||||||
|
</div>
|
||||||
<div class="ui horizontal divider">
|
<div class="ui horizontal divider">
|
||||||
<i class="ui green lock icon"></i>
|
<i class="ui green lock icon"></i>
|
||||||
Security
|
Security
|
||||||
@ -198,6 +203,7 @@
|
|||||||
let skipWebSocketOriginCheck = $("#skipWebsocketOriginCheck")[0].checked;
|
let skipWebSocketOriginCheck = $("#skipWebsocketOriginCheck")[0].checked;
|
||||||
let accessRuleToUse = $("#newProxyRuleAccessFilter").val();
|
let accessRuleToUse = $("#newProxyRuleAccessFilter").val();
|
||||||
let useStickySessionLB = $("#useStickySessionLB")[0].checked;
|
let useStickySessionLB = $("#useStickySessionLB")[0].checked;
|
||||||
|
let tags = $("#proxyTags").val().trim();
|
||||||
|
|
||||||
if (rootname.trim() == ""){
|
if (rootname.trim() == ""){
|
||||||
$("#rootname").parent().addClass("error");
|
$("#rootname").parent().addClass("error");
|
||||||
@ -231,6 +237,7 @@
|
|||||||
cred: JSON.stringify(credentials),
|
cred: JSON.stringify(credentials),
|
||||||
access: accessRuleToUse,
|
access: accessRuleToUse,
|
||||||
stickysess: useStickySessionLB,
|
stickysess: useStickySessionLB,
|
||||||
|
tags: tags,
|
||||||
},
|
},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
@ -239,6 +246,7 @@
|
|||||||
//Clear old data
|
//Clear old data
|
||||||
$("#rootname").val("");
|
$("#rootname").val("");
|
||||||
$("#proxyDomain").val("");
|
$("#proxyDomain").val("");
|
||||||
|
$("#proxyTags").val("");
|
||||||
credentials = [];
|
credentials = [];
|
||||||
updateTable();
|
updateTable();
|
||||||
reloadUptimeList();
|
reloadUptimeList();
|
||||||
|
77
src/web/snippet/tagEditor.html
Normal file
77
src/web/snippet/tagEditor.html
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<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>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="ui container">
|
||||||
|
<div class="ui header">
|
||||||
|
<div class="content">
|
||||||
|
Edit Tags
|
||||||
|
<div class="sub header" id="epname"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<p>Enter tags for this proxy host. Use commas to separate multiple tags.</p>
|
||||||
|
<div class="ui form">
|
||||||
|
<div class="field">
|
||||||
|
<label>Tags</label>
|
||||||
|
<input type="text" id="tagsInput" placeholder="e.g. mediaserver, management">
|
||||||
|
</div>
|
||||||
|
<button class="ui basic button" onclick="saveTags();"><i class="ui green save icon"></i> Save</button>
|
||||||
|
<button class="ui basic button" style="float: right;" onclick="parent.hideSideWrapper();"><i class="remove icon"></i> Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
let editingEndpoint = {};
|
||||||
|
if (window.location.hash.length > 1){
|
||||||
|
let payloadHash = window.location.hash.substr(1);
|
||||||
|
try{
|
||||||
|
payloadHash = JSON.parse(decodeURIComponent(payloadHash));
|
||||||
|
$("#epname").text(payloadHash.ep);
|
||||||
|
editingEndpoint = payloadHash;
|
||||||
|
loadTags();
|
||||||
|
}catch(ex){
|
||||||
|
console.log("Unable to load endpoint data from hash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTags(){
|
||||||
|
$.get("/api/proxy/detail", { type: "host", epname: editingEndpoint.ep }, function(data){
|
||||||
|
if (data.error == undefined){
|
||||||
|
$("#tagsInput").val(data.Tags.join(", "));
|
||||||
|
} else {
|
||||||
|
alert(data.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveTags(){
|
||||||
|
let tags = $("#tagsInput").val().trim().split(",").map(tag => tag.trim());
|
||||||
|
console.log(tags);
|
||||||
|
$.cjax({
|
||||||
|
url: "/api/proxy/edit",
|
||||||
|
method: "POST",
|
||||||
|
data: {
|
||||||
|
type: "host",
|
||||||
|
rootname: editingEndpoint.ep,
|
||||||
|
tags: tags.join(",")
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
parent.msgbox(data.error, false);
|
||||||
|
} else {
|
||||||
|
parent.msgbox("Tags updated");
|
||||||
|
parent.hideSideWrapper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user