diff --git a/handler/routes.go b/handler/routes.go index 11ec048..780d463 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -988,6 +988,13 @@ func MachineIPAddresses() echo.HandlerFunc { } } +// GetOrderedSubnetRanges handler to get the ordered list of subnet ranges +func GetOrderedSubnetRanges() echo.HandlerFunc { + return func(c echo.Context) error { + return c.JSON(http.StatusOK, util.SubnetRangesOrder) + } +} + // SuggestIPAllocation handler to get the list of ip address for client func SuggestIPAllocation(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { @@ -1009,15 +1016,27 @@ func SuggestIPAllocation(db store.IStore) echo.HandlerFunc { false, "Cannot suggest ip allocation: failed to get list of allocated ip addresses", }) } - for _, cidr := range server.Interface.Addresses { - ip, err := util.GetAvailableIP(cidr, allocatedIPs) + + sr := c.QueryParam("sr") + searchCIDRList := make([]string, 0) + found := false + + // Use subnet range or default to interface addresses + if util.SubnetRanges[sr] != nil { + for _, cidr := range util.SubnetRanges[sr] { + searchCIDRList = append(searchCIDRList, cidr.String()) + } + } else { + searchCIDRList = append(searchCIDRList, server.Interface.Addresses...) + } + + for _, cidr := range searchCIDRList { + ip, err := util.GetAvailableIP(cidr, allocatedIPs, server.Interface.Addresses) if err != nil { log.Error("Failed to get available ip from a CIDR: ", err) - return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{ - false, - fmt.Sprintf("Cannot suggest ip allocation: failed to get available ip from network %s", cidr), - }) + continue } + found = true if strings.Contains(ip, ":") { suggestedIPs = append(suggestedIPs, fmt.Sprintf("%s/128", ip)) } else { @@ -1025,6 +1044,13 @@ func SuggestIPAllocation(db store.IStore) echo.HandlerFunc { } } + if !found { + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{ + false, + "Cannot suggest ip allocation: failed to get available ip. Try a different subnet or deallocate some ips.", + }) + } + return c.JSON(http.StatusOK, suggestedIPs) } } diff --git a/main.go b/main.go index 4996d89..5f6e341 100644 --- a/main.go +++ b/main.go @@ -233,6 +233,7 @@ func main() { app.GET(util.BasePath+"/api/clients", handler.GetClients(db), handler.ValidSession) app.GET(util.BasePath+"/api/client/:id", handler.GetClient(db), handler.ValidSession) app.GET(util.BasePath+"/api/machine-ips", handler.MachineIPAddresses(), handler.ValidSession) + app.GET(util.BasePath+"/api/subnet-ranges", handler.GetOrderedSubnetRanges(), handler.ValidSession) app.GET(util.BasePath+"/api/suggest-client-ips", handler.SuggestIPAllocation(db), handler.ValidSession) app.POST(util.BasePath+"/api/apply-wg-config", handler.ApplyServerConfig(db, tmplDir), handler.ValidSession, handler.ContentTypeJson) app.GET(util.BasePath+"/wake_on_lan_hosts", handler.GetWakeOnLanHosts(db), handler.ValidSession) diff --git a/templates/base.html b/templates/base.html index c2fa367..bca6ee6 100644 --- a/templates/base.html +++ b/templates/base.html @@ -209,6 +209,12 @@ +
+ + +
@@ -368,7 +374,36 @@ $(document).ready(function () { - $.ajax({ + + addGlobalStyle(` + .toast-top-right-fix { + top: 67px; + right: 12px; + } + `, 'toastrToastStyleFix') + + toastr.options.closeDuration = 100; + // toastr.options.timeOut = 10000; + toastr.options.positionClass = 'toast-top-right-fix'; + + updateApplyConfigVisibility() + + }); + + function addGlobalStyle(css, id) { + if (!document.querySelector('#' + id)) { + let head = document.head + if (!head) { return } + let style = document.createElement('style') + style.type = 'text/css' + style.id = id + style.innerHTML = css + head.appendChild(style) + } + } + + function updateApplyConfigVisibility() { + $.ajax({ cache: false, method: 'GET', url: '{{.basePath}}/test-hash', @@ -388,8 +423,7 @@ toastr.error(responseJson['message']); } }); - - }); + } // populateClient function for render new client info @@ -466,19 +500,32 @@ // updateIPAllocationSuggestion function for automatically fill // the IP Allocation input with suggested ip addresses - function updateIPAllocationSuggestion() { + function updateIPAllocationSuggestion(forceDefault = false) { + let subnetRange = $("#subnet_ranges").select2('val'); + + if (forceDefault || !subnetRange || subnetRange.length === 0) { + subnetRange = '__default_any__' + } $.ajax({ cache: false, method: 'GET', - url: '{{.basePath}}/api/suggest-client-ips', + url: `{{.basePath}}/api/suggest-client-ips?sr=${subnetRange}`, dataType: 'json', contentType: "application/json", success: function(data) { + const allocated_ips = $("#client_allocated_ips").val().split(","); + allocated_ips.forEach(function (item, index) { + $('#client_allocated_ips').removeTag(escape(item)); + }) data.forEach(function (item, index) { $('#client_allocated_ips').addTag(item); }) }, error: function(jqXHR, exception) { + const allocated_ips = $("#client_allocated_ips").val().split(","); + allocated_ips.forEach(function (item, index) { + $('#client_allocated_ips').removeTag(escape(item)); + }) const responseJson = jQuery.parseJSON(jqXHR.responseText); toastr.error(responseJson['message']); } @@ -565,10 +612,17 @@ $("#client_preshared_key").val(""); $("#client_allocated_ips").importTags(''); $("#client_extra_allowed_ips").importTags(''); - updateIPAllocationSuggestion(); + updateSubnetRangesList(); + updateIPAllocationSuggestion(true); }); }); + // handle subnet range select + $('#subnet_ranges').on('select2:select', function (e) { + // console.log('Selected Option: ', $("#subnet_ranges").select2('val')); + updateIPAllocationSuggestion(); + }); + // apply_config_confirm button event $(document).ready(function () { $("#apply_config_confirm").click(function () { @@ -579,6 +633,7 @@ dataType: 'json', contentType: "application/json", success: function(data) { + updateApplyConfigVisibility() $("#modal_apply_config").modal('hide'); toastr.success('Applied config successfully'); }, diff --git a/templates/clients.html b/templates/clients.html index 8b4e4ab..c7a6c96 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -260,6 +260,25 @@ Wireguard Clients const divElement = document.getElementById("paused_" + clientID); divElement.style.visibility = "visible"; } + + function updateSubnetRangesList() { + $.getJSON("{{.basePath}}/api/subnet-ranges", null, function(data) { + console.log(data); + $("#subnet_ranges option").remove(); + $("#subnet_ranges").append( + $("") + .text("Any") + .val("__default_any__") + ); + $.each(data, function(index, item) { + $("#subnet_ranges").append( + $("") + .text(item) + .val(item) + ); + }); + }); + }