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:
Toby Chui 2024-04-16 23:33:24 +08:00
parent 8e648a8e1f
commit 3c78211800
7 changed files with 413 additions and 16 deletions

View File

@ -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)

View File

@ -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
}) })

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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>

View 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>