mirror of
https://github.com/ngoduykhanh/wireguard-ui
synced 2024-06-11 10:22:40 +02:00
Full implementation of subnet ranges for a new client, fixes
fix: free ip search was stopping after no free ip on the first interface fix: toast obstructing the buttons fix: stuck apply config button
This commit is contained in:
parent
92333a08d8
commit
53eaab0079
|
@ -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
|
// SuggestIPAllocation handler to get the list of ip address for client
|
||||||
func SuggestIPAllocation(db store.IStore) echo.HandlerFunc {
|
func SuggestIPAllocation(db store.IStore) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
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",
|
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 {
|
if err != nil {
|
||||||
log.Error("Failed to get available ip from a CIDR: ", err)
|
log.Error("Failed to get available ip from a CIDR: ", err)
|
||||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{
|
continue
|
||||||
false,
|
|
||||||
fmt.Sprintf("Cannot suggest ip allocation: failed to get available ip from network %s", cidr),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
found = true
|
||||||
if strings.Contains(ip, ":") {
|
if strings.Contains(ip, ":") {
|
||||||
suggestedIPs = append(suggestedIPs, fmt.Sprintf("%s/128", ip))
|
suggestedIPs = append(suggestedIPs, fmt.Sprintf("%s/128", ip))
|
||||||
} else {
|
} 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)
|
return c.JSON(http.StatusOK, suggestedIPs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
main.go
1
main.go
|
@ -233,6 +233,7 @@ func main() {
|
||||||
app.GET(util.BasePath+"/api/clients", handler.GetClients(db), handler.ValidSession)
|
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/client/:id", handler.GetClient(db), handler.ValidSession)
|
||||||
app.GET(util.BasePath+"/api/machine-ips", handler.MachineIPAddresses(), 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.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.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)
|
app.GET(util.BasePath+"/wake_on_lan_hosts", handler.GetWakeOnLanHosts(db), handler.ValidSession)
|
||||||
|
|
|
@ -209,6 +209,12 @@
|
||||||
<label for="client_email" class="control-label">Email</label>
|
<label for="client_email" class="control-label">Email</label>
|
||||||
<input type="text" class="form-control" id="client_email" name="client_email">
|
<input type="text" class="form-control" id="client_email" name="client_email">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="subnet_ranges" class="control-label">Subnet ranges</label>
|
||||||
|
<select id="subnet_ranges" class="select2"
|
||||||
|
data-placeholder="Select a subnet range" style="width: 100%;">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="client_allocated_ips" class="control-label">IP Allocation</label>
|
<label for="client_allocated_ips" class="control-label">IP Allocation</label>
|
||||||
<input type="text" data-role="tagsinput" class="form-control" id="client_allocated_ips">
|
<input type="text" data-role="tagsinput" class="form-control" id="client_allocated_ips">
|
||||||
|
@ -368,6 +374,35 @@
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
|
||||||
|
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({
|
$.ajax({
|
||||||
cache: false,
|
cache: false,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -388,8 +423,7 @@
|
||||||
toastr.error(responseJson['message']);
|
toastr.error(responseJson['message']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// populateClient function for render new client info
|
// populateClient function for render new client info
|
||||||
|
@ -466,19 +500,32 @@
|
||||||
|
|
||||||
// updateIPAllocationSuggestion function for automatically fill
|
// updateIPAllocationSuggestion function for automatically fill
|
||||||
// the IP Allocation input with suggested ip addresses
|
// 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({
|
$.ajax({
|
||||||
cache: false,
|
cache: false,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '{{.basePath}}/api/suggest-client-ips',
|
url: `{{.basePath}}/api/suggest-client-ips?sr=${subnetRange}`,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
success: function(data) {
|
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) {
|
data.forEach(function (item, index) {
|
||||||
$('#client_allocated_ips').addTag(item);
|
$('#client_allocated_ips').addTag(item);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
error: function(jqXHR, exception) {
|
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);
|
const responseJson = jQuery.parseJSON(jqXHR.responseText);
|
||||||
toastr.error(responseJson['message']);
|
toastr.error(responseJson['message']);
|
||||||
}
|
}
|
||||||
|
@ -565,10 +612,17 @@
|
||||||
$("#client_preshared_key").val("");
|
$("#client_preshared_key").val("");
|
||||||
$("#client_allocated_ips").importTags('');
|
$("#client_allocated_ips").importTags('');
|
||||||
$("#client_extra_allowed_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
|
// apply_config_confirm button event
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$("#apply_config_confirm").click(function () {
|
$("#apply_config_confirm").click(function () {
|
||||||
|
@ -579,6 +633,7 @@
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
|
updateApplyConfigVisibility()
|
||||||
$("#modal_apply_config").modal('hide');
|
$("#modal_apply_config").modal('hide');
|
||||||
toastr.success('Applied config successfully');
|
toastr.success('Applied config successfully');
|
||||||
},
|
},
|
||||||
|
|
|
@ -260,6 +260,25 @@ Wireguard Clients
|
||||||
const divElement = document.getElementById("paused_" + clientID);
|
const divElement = document.getElementById("paused_" + clientID);
|
||||||
divElement.style.visibility = "visible";
|
divElement.style.visibility = "visible";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateSubnetRangesList() {
|
||||||
|
$.getJSON("{{.basePath}}/api/subnet-ranges", null, function(data) {
|
||||||
|
console.log(data);
|
||||||
|
$("#subnet_ranges option").remove();
|
||||||
|
$("#subnet_ranges").append(
|
||||||
|
$("<option></option>")
|
||||||
|
.text("Any")
|
||||||
|
.val("__default_any__")
|
||||||
|
);
|
||||||
|
$.each(data, function(index, item) {
|
||||||
|
$("#subnet_ranges").append(
|
||||||
|
$("<option></option>")
|
||||||
|
.text(item)
|
||||||
|
.val(item)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
// load client list
|
// load client list
|
||||||
|
|
25
util/util.go
25
util/util.go
|
@ -326,15 +326,32 @@ func GetBroadcastIP(n *net.IPNet) net.IP {
|
||||||
return broadcast
|
return broadcast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBroadcastAndNetworkAddrsLookup get the ip address that can't be used with current server interfaces
|
||||||
|
func GetBroadcastAndNetworkAddrsLookup(interfaceAddresses []string) map[string]bool {
|
||||||
|
list := make(map[string]bool, 0)
|
||||||
|
for _, ifa := range interfaceAddresses {
|
||||||
|
_, net, err := net.ParseCIDR(ifa)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastAddr := GetBroadcastIP(net).String()
|
||||||
|
networkAddr := net.IP.String()
|
||||||
|
list[broadcastAddr] = true
|
||||||
|
list[networkAddr] = true
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
// GetAvailableIP get the ip address that can be allocated from an CIDR
|
// GetAvailableIP get the ip address that can be allocated from an CIDR
|
||||||
func GetAvailableIP(cidr string, allocatedList []string) (string, error) {
|
// We need interfaceAddresses to find real broadcast and network addresses
|
||||||
|
func GetAvailableIP(cidr string, allocatedList, interfaceAddresses []string) (string, error) {
|
||||||
ip, net, err := net.ParseCIDR(cidr)
|
ip, net, err := net.ParseCIDR(cidr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastAddr := GetBroadcastIP(net).String()
|
unavailableIPs := GetBroadcastAndNetworkAddrsLookup(interfaceAddresses)
|
||||||
networkAddr := net.IP.String()
|
|
||||||
|
|
||||||
for ip := ip.Mask(net.Mask); net.Contains(ip); inc(ip) {
|
for ip := ip.Mask(net.Mask); net.Contains(ip); inc(ip) {
|
||||||
available := true
|
available := true
|
||||||
|
@ -345,7 +362,7 @@ func GetAvailableIP(cidr string, allocatedList []string) (string, error) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if available && suggestedAddr != networkAddr && suggestedAddr != broadcastAddr {
|
if available && !unavailableIPs[suggestedAddr] {
|
||||||
return suggestedAddr, nil
|
return suggestedAddr, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue