mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-06 07:37:21 +02:00
Added alias support
+ Added alias support (use , when adding a new proxy target to automatically add alias hostnames) + Fixed some UI issues
This commit is contained in:
parent
8e648a8e1f
commit
3c78211800
@ -51,6 +51,7 @@ func initAPIs() {
|
|||||||
authRouter.HandleFunc("/api/proxy/list", ReverseProxyList)
|
authRouter.HandleFunc("/api/proxy/list", ReverseProxyList)
|
||||||
authRouter.HandleFunc("/api/proxy/detail", ReverseProxyListDetail)
|
authRouter.HandleFunc("/api/proxy/detail", ReverseProxyListDetail)
|
||||||
authRouter.HandleFunc("/api/proxy/edit", ReverseProxyHandleEditEndpoint)
|
authRouter.HandleFunc("/api/proxy/edit", ReverseProxyHandleEditEndpoint)
|
||||||
|
authRouter.HandleFunc("/api/proxy/setAlias", ReverseProxyHandleAlias)
|
||||||
authRouter.HandleFunc("/api/proxy/del", DeleteProxyEndpoint)
|
authRouter.HandleFunc("/api/proxy/del", DeleteProxyEndpoint)
|
||||||
authRouter.HandleFunc("/api/proxy/updateCredentials", UpdateProxyBasicAuthCredentials)
|
authRouter.HandleFunc("/api/proxy/updateCredentials", UpdateProxyBasicAuthCredentials)
|
||||||
authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS)
|
authRouter.HandleFunc("/api/proxy/tlscheck", HandleCheckSiteSupportTLS)
|
||||||
|
@ -34,23 +34,45 @@ func (router *Router) getProxyEndpointFromHostname(hostname string) *ProxyEndpoi
|
|||||||
var targetSubdomainEndpoint *ProxyEndpoint = nil
|
var targetSubdomainEndpoint *ProxyEndpoint = nil
|
||||||
ep, ok := router.ProxyEndpoints.Load(hostname)
|
ep, ok := router.ProxyEndpoints.Load(hostname)
|
||||||
if ok {
|
if ok {
|
||||||
|
//Exact hit
|
||||||
targetSubdomainEndpoint = ep.(*ProxyEndpoint)
|
targetSubdomainEndpoint = ep.(*ProxyEndpoint)
|
||||||
|
if !targetSubdomainEndpoint.Disabled {
|
||||||
|
return targetSubdomainEndpoint
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//No hit. Try with wildcard
|
//No hit. Try with wildcard and alias
|
||||||
matchProxyEndpoints := []*ProxyEndpoint{}
|
matchProxyEndpoints := []*ProxyEndpoint{}
|
||||||
router.ProxyEndpoints.Range(func(k, v interface{}) bool {
|
router.ProxyEndpoints.Range(func(k, v interface{}) bool {
|
||||||
ep := v.(*ProxyEndpoint)
|
ep := v.(*ProxyEndpoint)
|
||||||
match, err := filepath.Match(ep.RootOrMatchingDomain, hostname)
|
match, err := filepath.Match(ep.RootOrMatchingDomain, hostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//Continue
|
//Bad pattern. Skip this rule
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if match {
|
if match {
|
||||||
//targetSubdomainEndpoint = ep
|
//Wildcard matches. Skip checking alias
|
||||||
matchProxyEndpoints = append(matchProxyEndpoints, ep)
|
matchProxyEndpoints = append(matchProxyEndpoints, ep)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Wildcard not match. Check for alias
|
||||||
|
if ep.MatchingDomainAlias != nil && len(ep.MatchingDomainAlias) > 0 {
|
||||||
|
for _, aliasDomain := range ep.MatchingDomainAlias {
|
||||||
|
match, err := filepath.Match(aliasDomain, hostname)
|
||||||
|
if err != nil {
|
||||||
|
//Bad pattern. Skip this alias
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if match {
|
||||||
|
//This alias match
|
||||||
|
matchProxyEndpoints = append(matchProxyEndpoints, ep)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -92,9 +92,10 @@ type VirtualDirectoryEndpoint struct {
|
|||||||
|
|
||||||
// A proxy endpoint record, a general interface for handling inbound routing
|
// A proxy endpoint record, a general interface for handling inbound routing
|
||||||
type ProxyEndpoint struct {
|
type ProxyEndpoint struct {
|
||||||
ProxyType int //The type of this proxy, see const def
|
ProxyType int //The type of this proxy, see const def
|
||||||
RootOrMatchingDomain string //Matching domain for host, also act as key
|
RootOrMatchingDomain string //Matching domain for host, also act as key
|
||||||
Domain string //Domain or IP to proxy to
|
MatchingDomainAlias []string //A list of domains that alias to this rule
|
||||||
|
Domain string //Domain or IP to proxy to
|
||||||
|
|
||||||
//TLS/SSL Related
|
//TLS/SSL Related
|
||||||
RequireTLS bool //Target domain require TLS
|
RequireTLS bool //Target domain require TLS
|
||||||
|
@ -195,6 +195,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
useTLS := (tls == "true")
|
useTLS := (tls == "true")
|
||||||
|
|
||||||
|
//Bypass global TLS value / allow direct access from port 80?
|
||||||
bypassGlobalTLS, _ := utils.PostPara(r, "bypassGlobalTLS")
|
bypassGlobalTLS, _ := utils.PostPara(r, "bypassGlobalTLS")
|
||||||
if bypassGlobalTLS == "" {
|
if bypassGlobalTLS == "" {
|
||||||
bypassGlobalTLS = "false"
|
bypassGlobalTLS = "false"
|
||||||
@ -202,6 +203,7 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
useBypassGlobalTLS := bypassGlobalTLS == "true"
|
useBypassGlobalTLS := bypassGlobalTLS == "true"
|
||||||
|
|
||||||
|
//Enable TLS validation?
|
||||||
stv, _ := utils.PostPara(r, "tlsval")
|
stv, _ := utils.PostPara(r, "tlsval")
|
||||||
if stv == "" {
|
if stv == "" {
|
||||||
stv = "false"
|
stv = "false"
|
||||||
@ -209,6 +211,17 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
skipTlsValidation := (stv == "true")
|
skipTlsValidation := (stv == "true")
|
||||||
|
|
||||||
|
//Get access rule ID
|
||||||
|
accessRuleID, _ := utils.PostPara(r, "access")
|
||||||
|
if accessRuleID == "" {
|
||||||
|
accessRuleID = "default"
|
||||||
|
}
|
||||||
|
if !accessController.AccessRuleExists(accessRuleID) {
|
||||||
|
utils.SendErrorResponse(w, "invalid access rule ID selected")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Require basic auth?
|
||||||
rba, _ := utils.PostPara(r, "bauth")
|
rba, _ := utils.PostPara(r, "bauth")
|
||||||
if rba == "" {
|
if rba == "" {
|
||||||
rba = "false"
|
rba = "false"
|
||||||
@ -255,19 +268,37 @@ func ReverseProxyHandleAddEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
if eptype == "host" {
|
if eptype == "host" {
|
||||||
rootOrMatchingDomain, err := utils.PostPara(r, "rootname")
|
rootOrMatchingDomain, err := utils.PostPara(r, "rootname")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "subdomain not defined")
|
utils.SendErrorResponse(w, "hostname not defined")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
rootOrMatchingDomain = strings.TrimSpace(rootOrMatchingDomain)
|
||||||
|
|
||||||
|
//Check if it contains ",", if yes, split the remainings as alias
|
||||||
|
aliasHostnames := []string{}
|
||||||
|
if strings.Contains(rootOrMatchingDomain, ",") {
|
||||||
|
matchingDomains := strings.Split(rootOrMatchingDomain, ",")
|
||||||
|
if len(matchingDomains) > 1 {
|
||||||
|
rootOrMatchingDomain = matchingDomains[0]
|
||||||
|
for _, aliasHostname := range matchingDomains[1:] {
|
||||||
|
//Filter out any space
|
||||||
|
aliasHostnames = append(aliasHostnames, strings.TrimSpace(aliasHostname))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Generate a proxy endpoint object
|
||||||
thisProxyEndpoint := dynamicproxy.ProxyEndpoint{
|
thisProxyEndpoint := dynamicproxy.ProxyEndpoint{
|
||||||
//I/O
|
//I/O
|
||||||
ProxyType: dynamicproxy.ProxyType_Host,
|
ProxyType: dynamicproxy.ProxyType_Host,
|
||||||
RootOrMatchingDomain: rootOrMatchingDomain,
|
RootOrMatchingDomain: rootOrMatchingDomain,
|
||||||
|
MatchingDomainAlias: aliasHostnames,
|
||||||
Domain: endpoint,
|
Domain: endpoint,
|
||||||
//TLS
|
//TLS
|
||||||
RequireTLS: useTLS,
|
RequireTLS: useTLS,
|
||||||
BypassGlobalTLS: useBypassGlobalTLS,
|
BypassGlobalTLS: useBypassGlobalTLS,
|
||||||
SkipCertValidations: skipTlsValidation,
|
SkipCertValidations: skipTlsValidation,
|
||||||
SkipWebSocketOriginCheck: bypassWebsocketOriginCheck,
|
SkipWebSocketOriginCheck: bypassWebsocketOriginCheck,
|
||||||
|
AccessFilterUUID: accessRuleID,
|
||||||
//VDir
|
//VDir
|
||||||
VirtualDirectories: []*dynamicproxy.VirtualDirectoryEndpoint{},
|
VirtualDirectories: []*dynamicproxy.VirtualDirectoryEndpoint{},
|
||||||
//Custom headers
|
//Custom headers
|
||||||
@ -440,6 +471,62 @@ func ReverseProxyHandleEditEndpoint(w http.ResponseWriter, r *http.Request) {
|
|||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReverseProxyHandleAlias(w http.ResponseWriter, r *http.Request) {
|
||||||
|
rootNameOrMatchingDomain, err := utils.PostPara(r, "ep")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "Invalid ep given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//No need to check for type as root (/) can be set to default route
|
||||||
|
//and hence, you will not need alias
|
||||||
|
|
||||||
|
//Load the previous alias from current proxy rules
|
||||||
|
targetProxyEntry, err := dynamicProxyRouter.LoadProxy(rootNameOrMatchingDomain)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "Target proxy config not found or could not be loaded")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newAliasJSON, err := utils.PostPara(r, "alias")
|
||||||
|
if err != nil {
|
||||||
|
//No new set of alias given
|
||||||
|
utils.SendErrorResponse(w, "new alias not given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write new alias to runtime and file
|
||||||
|
newAlias := []string{}
|
||||||
|
err = json.Unmarshal([]byte(newAliasJSON), &newAlias)
|
||||||
|
if err != nil {
|
||||||
|
SystemWideLogger.PrintAndLog("Proxy", "Unable to parse new alias list", err)
|
||||||
|
utils.SendErrorResponse(w, "Invalid alias list given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set the current alias
|
||||||
|
newProxyEndpoint := dynamicproxy.CopyEndpoint(targetProxyEntry)
|
||||||
|
newProxyEndpoint.MatchingDomainAlias = newAlias
|
||||||
|
|
||||||
|
// Prepare to replace the current routing rule
|
||||||
|
readyRoutingRule, err := dynamicProxyRouter.PrepareProxyRoute(newProxyEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
targetProxyEntry.Remove()
|
||||||
|
dynamicProxyRouter.AddProxyRouteToRuntime(readyRoutingRule)
|
||||||
|
|
||||||
|
// Save it to file
|
||||||
|
err = SaveReverseProxyConfig(newProxyEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "Alias update failed")
|
||||||
|
SystemWideLogger.PrintAndLog("Proxy", "Unable to save alias update", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.SendOK(w)
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
|
func DeleteProxyEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
ep, err := utils.GetPara(r, "ep")
|
ep, err := utils.GetPara(r, "ep")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -7,6 +7,10 @@
|
|||||||
#httpProxyList .ui.toggle.checkbox input:checked ~ label::before{
|
#httpProxyList .ui.toggle.checkbox input:checked ~ label::before{
|
||||||
background-color: #00ca52 !important;
|
background-color: #00ca52 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subdEntry td:not(.ignoremw){
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<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">
|
||||||
@ -44,6 +48,8 @@
|
|||||||
<td data-label="" colspan="5"><i class="green check circle icon"></i> No HTTP Proxy Record</td>
|
<td data-label="" colspan="5"><i class="green check circle icon"></i> No HTTP Proxy Record</td>
|
||||||
</tr>`);
|
</tr>`);
|
||||||
}else{
|
}else{
|
||||||
|
//Sort by RootOrMatchingDomain field
|
||||||
|
data.sort((a,b) => (a.RootOrMatchingDomain > b.RootOrMatchingDomain) ? 1 : ((b.RootOrMatchingDomain > a.RootOrMatchingDomain) ? -1 : 0))
|
||||||
data.forEach(subd => {
|
data.forEach(subd => {
|
||||||
let tlsIcon = "";
|
let tlsIcon = "";
|
||||||
let subdData = encodeURIComponent(JSON.stringify(subd));
|
let subdData = encodeURIComponent(JSON.stringify(subd));
|
||||||
@ -75,14 +81,25 @@
|
|||||||
vdList = `<small style="opacity: 0.3; pointer-events: none; user-select: none;"><i class="check icon"></i> No Virtual Directory</small>`;
|
vdList = `<small style="opacity: 0.3; pointer-events: none; user-select: none;"><i class="check icon"></i> No Virtual Directory</small>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
var enableChecked = "checked";
|
let enableChecked = "checked";
|
||||||
if (subd.Disabled){
|
if (subd.Disabled){
|
||||||
enableChecked = "";
|
enableChecked = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let aliasDomains = ``;
|
||||||
|
if (subd.MatchingDomainAlias != undefined && subd.MatchingDomainAlias.length > 0){
|
||||||
|
aliasDomains = `<small class="aliasDomains" eptuuid="${subd.RootOrMatchingDomain}" style="color: #636363;">Alias: `;
|
||||||
|
subd.MatchingDomainAlias.forEach(alias => {
|
||||||
|
aliasDomains += `<a href="//${alias}" target="_blank">${alias}</a>, `;
|
||||||
|
});
|
||||||
|
aliasDomains = aliasDomains.substr(0, aliasDomains.length - 2); //Remove the last tailing seperator
|
||||||
|
aliasDomains += `</small><br>`;
|
||||||
|
}
|
||||||
|
|
||||||
$("#httpProxyList").append(`<tr eptuuid="${subd.RootOrMatchingDomain}" payload="${subdData}" class="subdEntry">
|
$("#httpProxyList").append(`<tr eptuuid="${subd.RootOrMatchingDomain}" payload="${subdData}" class="subdEntry">
|
||||||
<td data-label="" editable="true" datatype="inbound">
|
<td data-label="" editable="true" datatype="inbound">
|
||||||
<a href="//${subd.RootOrMatchingDomain}" target="_blank">${subd.RootOrMatchingDomain}</a> ${inboundTlsIcon}<br>
|
<a href="//${subd.RootOrMatchingDomain}" target="_blank">${subd.RootOrMatchingDomain}</a> ${inboundTlsIcon}<br>
|
||||||
|
${aliasDomains}
|
||||||
<small class="accessRuleNameUnderHost" ruleid="${subd.AccessFilterUUID}"></small>
|
<small class="accessRuleNameUnderHost" ruleid="${subd.AccessFilterUUID}"></small>
|
||||||
</td>
|
</td>
|
||||||
<td data-label="" editable="true" datatype="domain">${subd.Domain} ${tlsIcon}</td>
|
<td data-label="" editable="true" datatype="domain">${subd.Domain} ${tlsIcon}</td>
|
||||||
@ -90,7 +107,7 @@
|
|||||||
<td data-label="" editable="true" datatype="basicauth">
|
<td data-label="" editable="true" datatype="basicauth">
|
||||||
${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}
|
${subd.RequireBasicAuth?`<i class="ui green check icon"></i>`:`<i class="ui grey remove icon"></i>`}
|
||||||
</td>
|
</td>
|
||||||
<td class="center aligned" editable="true" datatype="action" data-label="">
|
<td class="center aligned ignoremw" editable="true" datatype="action" data-label="">
|
||||||
<div class="ui toggle tiny fitted checkbox" style="margin-bottom: -0.5em; margin-right: 0.4em;" title="Enable / Disable Rule">
|
<div class="ui toggle tiny fitted checkbox" style="margin-bottom: -0.5em; margin-right: 0.4em;" title="Enable / Disable Rule">
|
||||||
<input type="checkbox" class="enableToggle" name="active" ${enableChecked} eptuuid="${subd.RootOrMatchingDomain}" onchange="handleProxyRuleToggle(this);">
|
<input type="checkbox" class="enableToggle" name="active" ${enableChecked} eptuuid="${subd.RootOrMatchingDomain}" onchange="handleProxyRuleToggle(this);">
|
||||||
<label></label>
|
<label></label>
|
||||||
@ -106,6 +123,28 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Perform realtime alias update without refreshing the whole page
|
||||||
|
function updateAliasListForEndpoint(endpointName, newAliasDomainList){
|
||||||
|
let targetEle = $(`.aliasDomains[eptuuid='${endpointName}']`);
|
||||||
|
console.log(targetEle);
|
||||||
|
if (targetEle.length == 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let aliasDomains = ``;
|
||||||
|
if (newAliasDomainList != undefined && newAliasDomainList.length > 0){
|
||||||
|
aliasDomains = `Alias: `;
|
||||||
|
newAliasDomainList.forEach(alias => {
|
||||||
|
aliasDomains += `<a href="//${alias}" target="_blank">${alias}</a>, `;
|
||||||
|
});
|
||||||
|
aliasDomains = aliasDomains.substr(0, aliasDomains.length - 2); //Remove the last tailing seperator
|
||||||
|
$(targetEle).html(aliasDomains);
|
||||||
|
$(targetEle).show();
|
||||||
|
}else{
|
||||||
|
$(targetEle).hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Resolve & Update all rule names on host PR list
|
//Resolve & Update all rule names on host PR list
|
||||||
function resolveAccessRuleNameOnHostRPlist(){
|
function resolveAccessRuleNameOnHostRPlist(){
|
||||||
//Resolve the access filters
|
//Resolve the access filters
|
||||||
@ -277,7 +316,9 @@
|
|||||||
<label>Allow plain HTTP access<br>
|
<label>Allow plain HTTP access<br>
|
||||||
<small>Allow inbound connections without TLS/SSL</small></label>
|
<small>Allow inbound connections without TLS/SSL</small></label>
|
||||||
</div><br>
|
</div><br>
|
||||||
<button class="ui basic tiny button" style="margin-left: 0.4em; margin-top: 0.4em;" onclick="editAccessRule('${uuid}');"><i class="ui filter icon"></i> Edit Access Rule</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>
|
||||||
|
|
||||||
`);
|
`);
|
||||||
|
|
||||||
$(".hostAccessRuleSelector").dropdown();
|
$(".hostAccessRuleSelector").dropdown();
|
||||||
@ -352,6 +393,14 @@
|
|||||||
showSideWrapper("snippet/hostAccessEditor.html?t=" + Date.now() + "#" + payload);
|
showSideWrapper("snippet/hostAccessEditor.html?t=" + Date.now() + "#" + payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function editAliasHostnames(uuid){
|
||||||
|
let payload = encodeURIComponent(JSON.stringify({
|
||||||
|
ept: "host",
|
||||||
|
ep: uuid
|
||||||
|
}));
|
||||||
|
showSideWrapper("snippet/aliasEditor.html?t=" + Date.now() + "#" + payload);
|
||||||
|
}
|
||||||
|
|
||||||
function quickEditVdir(uuid){
|
function quickEditVdir(uuid){
|
||||||
openTabById("vdir");
|
openTabById("vdir");
|
||||||
$("#vdirBaseRoutingRule").parent().dropdown("set selected", uuid);
|
$("#vdirBaseRoutingRule").parent().dropdown("set selected", uuid);
|
||||||
|
@ -5,6 +5,12 @@
|
|||||||
color: var(--theme_lgrey);
|
color: var(--theme_lgrey);
|
||||||
border-radius: 1em !important;
|
border-radius: 1em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui.form .sub.field{
|
||||||
|
background-color: var(--theme_advance);
|
||||||
|
border-radius: 0.6em;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="standardContainer">
|
<div class="standardContainer">
|
||||||
<div class="ui stackable grid">
|
<div class="ui stackable grid">
|
||||||
@ -16,7 +22,7 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Matching Keyword / Domain</label>
|
<label>Matching Keyword / Domain</label>
|
||||||
<input type="text" id="rootname" placeholder="mydomain.com">
|
<input type="text" id="rootname" placeholder="mydomain.com">
|
||||||
<small>Support subdomain and wildcard, e.g. s1.mydomain.com or *.test.mydomain.com</small>
|
<small>Support subdomain and wildcard, e.g. s1.mydomain.com or *.test.mydomain.com. Use comma (,) for alias hostnames. </small>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Target IP Address or Domain Name with port</label>
|
<label>Target IP Address or Domain Name with port</label>
|
||||||
@ -37,7 +43,18 @@
|
|||||||
Advance Settings
|
Advance Settings
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p></p>
|
<div class="field">
|
||||||
|
<label>Access Rule</label>
|
||||||
|
<div class="ui selection dropdown">
|
||||||
|
<input type="hidden" id="newProxyRuleAccessFilter" value="default">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
<div class="default text">Default</div>
|
||||||
|
<div class="menu" id="newProxyRuleAccessList">
|
||||||
|
<div class="item" data-value="default"><i class="ui yellow star icon"></i> Default</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<small>Allow regional access control using blacklist or whitelist. Use "default" for "allow all".</small>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui checkbox">
|
<div class="ui checkbox">
|
||||||
<input type="checkbox" id="skipTLSValidation">
|
<input type="checkbox" id="skipTLSValidation">
|
||||||
@ -121,8 +138,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
$("#advanceProxyRules").accordion();
|
|
||||||
|
|
||||||
|
|
||||||
//New Proxy Endpoint
|
//New Proxy Endpoint
|
||||||
function newProxyEndpoint(){
|
function newProxyEndpoint(){
|
||||||
@ -133,6 +148,7 @@
|
|||||||
var bypassGlobalTLS = $("#bypassGlobalTLS")[0].checked;
|
var bypassGlobalTLS = $("#bypassGlobalTLS")[0].checked;
|
||||||
var requireBasicAuth = $("#requireBasicAuth")[0].checked;
|
var requireBasicAuth = $("#requireBasicAuth")[0].checked;
|
||||||
var skipWebSocketOriginCheck = $("#skipWebsocketOriginCheck")[0].checked;
|
var skipWebSocketOriginCheck = $("#skipWebsocketOriginCheck")[0].checked;
|
||||||
|
var accessRuleToUse = $("#newProxyRuleAccessFilter").val();
|
||||||
|
|
||||||
if (rootname.trim() == ""){
|
if (rootname.trim() == ""){
|
||||||
$("#rootname").parent().addClass("error");
|
$("#rootname").parent().addClass("error");
|
||||||
@ -161,7 +177,7 @@
|
|||||||
bypassGlobalTLS: bypassGlobalTLS,
|
bypassGlobalTLS: bypassGlobalTLS,
|
||||||
bauth: requireBasicAuth,
|
bauth: requireBasicAuth,
|
||||||
cred: JSON.stringify(credentials),
|
cred: JSON.stringify(credentials),
|
||||||
|
access: accessRuleToUse,
|
||||||
},
|
},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
@ -343,4 +359,47 @@
|
|||||||
return back;
|
return back;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Access Rule dropdown Initialization
|
||||||
|
*/
|
||||||
|
|
||||||
|
function initNewProxyRuleAccessDropdownList(callback=undefined){
|
||||||
|
$.get("/api/access/list", function(data){
|
||||||
|
if (data.error == undefined){
|
||||||
|
$("#newProxyRuleAccessList").html("");
|
||||||
|
data.forEach(function(rule){
|
||||||
|
let icon = `<i class="ui grey filter icon"></i>`;
|
||||||
|
if (rule.ID == "default"){
|
||||||
|
icon = `<i class="ui yellow star icon"></i>`;
|
||||||
|
}else if (rule.BlacklistEnabled && !rule.WhitelistEnabled){
|
||||||
|
//This is a blacklist filter
|
||||||
|
icon = `<i class="ui red filter icon"></i>`;
|
||||||
|
}else if (rule.WhitelistEnabled && !rule.BlacklistEnabled){
|
||||||
|
//This is a whitelist filter
|
||||||
|
icon = `<i class="ui green filter icon"></i>`;
|
||||||
|
}
|
||||||
|
$("#newProxyRuleAccessList").append(`<div class="item" data-value="${rule.ID}">${icon} ${rule.Name}</div>`);
|
||||||
|
});
|
||||||
|
$("#newProxyRuleAccessFilter").parent().dropdown();
|
||||||
|
if (callback != undefined){
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
msgbox("Access rule load failed: " + data.error, false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
initNewProxyRuleAccessDropdownList();
|
||||||
|
|
||||||
|
//Bind on tab switch events
|
||||||
|
tabSwitchEventBind["rules"] = function(){
|
||||||
|
//Update the access rule list
|
||||||
|
initNewProxyRuleAccessDropdownList();
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
$("#advanceProxyRules").accordion();
|
||||||
|
$("#newProxyRuleAccessFilter").parent().dropdown();
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
178
src/web/snippet/aliasEditor.html
Normal file
178
src/web/snippet/aliasEditor.html
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Notes: This should be open in its original path-->
|
||||||
|
<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>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<br>
|
||||||
|
<div class="ui container">
|
||||||
|
<div class="ui header">
|
||||||
|
<div class="content">
|
||||||
|
Alias Hostname
|
||||||
|
<div class="sub header epname"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="scrolling content ui form">
|
||||||
|
<div id="inlineEditBasicAuthCredentials" class="field">
|
||||||
|
<p>Enter alias hostname or wildcard matching keywords for <code class="epname"></code></p>
|
||||||
|
<table class="ui very basic compacted unstackable celled table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Alias Hostname</th>
|
||||||
|
<th>Remove</th>
|
||||||
|
</tr></thead>
|
||||||
|
<tbody id="inlineEditTable">
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"><i class="ui green circle check icon"></i> No Alias Hostname</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="three small fields">
|
||||||
|
<div class="field">
|
||||||
|
<label>Alias Hostname</label>
|
||||||
|
<input id="aliasHostname" type="text" placeholder="alias.mydomain.com" autocomplete="off">
|
||||||
|
<small>Support wildcards e.g. alias.mydomain.com or *.alias.mydomain.com</small>
|
||||||
|
</div>
|
||||||
|
<div class="field" >
|
||||||
|
<button class="ui basic button" onclick="addAliasToRoutingRule();"><i class="green add icon"></i> Add Alias</button>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
<div class="field" >
|
||||||
|
<button class="ui basic button" style="float: right;" onclick="closeThisWrapper();">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br><br><br><br>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
let aliasList = [];
|
||||||
|
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;
|
||||||
|
}catch(ex){
|
||||||
|
console.log("Unable to load endpoint data from hash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initAliasNames(){
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/proxy/detail",
|
||||||
|
method: "POST",
|
||||||
|
data: {
|
||||||
|
"type":"host",
|
||||||
|
"epname": editingEndpoint.ep
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
//This endpoint not exists?
|
||||||
|
alert(data.error);
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
$("#inlineEditTable").html("");
|
||||||
|
if (data.MatchingDomainAlias != undefined){
|
||||||
|
aliasList = data.MatchingDomainAlias;
|
||||||
|
renderAliasList();
|
||||||
|
}else{
|
||||||
|
//Assume no alias
|
||||||
|
$("#inlineEditTable").html(`<tr>
|
||||||
|
<td colspan="2"><i class="ui green circle check icon"></i> No Alias Hostname</td>
|
||||||
|
</tr>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
initAliasNames();
|
||||||
|
|
||||||
|
function removeAliasDomain(targetDomain){
|
||||||
|
aliasList.splice(aliasList.indexOf(targetDomain), 1);
|
||||||
|
saveCurrentAliasList(function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
parent.msgbox(data.error);
|
||||||
|
}else{
|
||||||
|
initAliasNames();
|
||||||
|
parent.msgbox("Alias hostname removed")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAliasToRoutingRule(){
|
||||||
|
let newAliasHostname = $("#aliasHostname").val().trim();
|
||||||
|
aliasList.push(newAliasHostname);
|
||||||
|
$("#aliasHostname").val("");
|
||||||
|
saveCurrentAliasList(function(data){
|
||||||
|
if (data.error != undefined){
|
||||||
|
parent.msgbox(data.error);
|
||||||
|
}else{
|
||||||
|
parent.msgbox("New alias hostname added")
|
||||||
|
initAliasNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveCurrentAliasList(callback=undefined){
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/proxy/setAlias",
|
||||||
|
method: "POST",
|
||||||
|
data:{
|
||||||
|
"ep":editingEndpoint.ep,
|
||||||
|
"alias": JSON.stringify(aliasList)
|
||||||
|
},
|
||||||
|
success: function(data){
|
||||||
|
if (callback != undefined){
|
||||||
|
callback(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.error == undefined && parent != undefined && parent.document != undefined){
|
||||||
|
//Try to update the parent object's rules if exists
|
||||||
|
parent.updateAliasListForEndpoint(editingEndpoint.ep, aliasList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAliasList(){
|
||||||
|
$("#inlineEditTable").html("");
|
||||||
|
aliasList.forEach(aliasDomain => {
|
||||||
|
let domainLink = `<a href="//${aliasDomain}" target="_blank">${aliasDomain}</a>`
|
||||||
|
if (aliasDomain.includes("*")){
|
||||||
|
//This is a wildcard hostname
|
||||||
|
domainLink = aliasDomain;
|
||||||
|
}
|
||||||
|
$("#inlineEditTable").append(`<tr>
|
||||||
|
<td>${domainLink}</td>
|
||||||
|
<td><button class="ui basic button" onclick="removeAliasDomain('${aliasDomain}');"><i class="red remove icon"></i> Remove</button></td>
|
||||||
|
</tr>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (aliasList.length == 0){
|
||||||
|
$("#inlineEditTable").html(`<tr>
|
||||||
|
<td colspan="2"><i class="ui green circle check icon"></i> No Alias Hostname</td>
|
||||||
|
</tr>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeThisWrapper(){
|
||||||
|
parent.hideSideWrapper(true);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user