feat(frontend): implement bulk user creation UI
This commit is contained in:
@ -160,8 +160,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- Add User Modal -->
|
<!-- Add User Modal -->
|
||||||
<div class="modal fade" id="addUserModal" tabindex="-1" role="dialog" aria-labelledby="addUserModalLabel"
|
<div class="modal fade" id="addUserModal" tabindex="-1" role="dialog" aria-labelledby="addUserModalLabel" aria-hidden="true">
|
||||||
aria-hidden="true">
|
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@ -171,31 +170,77 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form id="addUserForm">
|
<ul class="nav nav-tabs" id="addUserTab" role="tablist">
|
||||||
<div class="form-group">
|
<li class="nav-item">
|
||||||
<label for="addUsername">Username</label>
|
<a class="nav-link active" id="single-user-tab" data-toggle="tab" href="#single-user" role="tab" aria-controls="single-user" aria-selected="true">Add User</a>
|
||||||
<input type="text" class="form-control" id="addUsername" name="username" required>
|
</li>
|
||||||
<small class="form-text text-danger" id="addUsernameError"></small>
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" id="bulk-users-tab" data-toggle="tab" href="#bulk-users" role="tab" aria-controls="bulk-users" aria-selected="false">Bulk Add</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content" id="addUserTabContent">
|
||||||
|
<div class="tab-pane fade show active" id="single-user" role="tabpanel" aria-labelledby="single-user-tab">
|
||||||
|
<form id="addUserForm" class="mt-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="addUsername">Username</label>
|
||||||
|
<input type="text" class="form-control" id="addUsername" name="username" required>
|
||||||
|
<small class="form-text text-danger" id="addUsernameError"></small>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="addTrafficLimit">Traffic Limit (GB)</label>
|
||||||
|
<input type="number" class="form-control" id="addTrafficLimit" name="traffic_limit" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="addExpirationDays">Expiration Days</label>
|
||||||
|
<input type="number" class="form-control" id="addExpirationDays" name="expiration_days" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-3 requires-iplimit-service" style="display: none;">
|
||||||
|
<input type="checkbox" class="form-check-input" id="addUnlimited" name="unlimited">
|
||||||
|
<label class="form-check-label" for="addUnlimited">Unlimited IP (Exempt from IP limit checks)</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary" id="addSubmitButton">Add User</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="tab-pane fade" id="bulk-users" role="tabpanel" aria-labelledby="bulk-users-tab">
|
||||||
<label for="addTrafficLimit">Traffic Limit (GB)</label>
|
<form id="addBulkUsersForm" class="mt-3">
|
||||||
<input type="number" class="form-control" id="addTrafficLimit" name="traffic_limit" required>
|
<div class="form-group">
|
||||||
|
<label for="addBulkPrefix">Username Prefix</label>
|
||||||
|
<input type="text" class="form-control" id="addBulkPrefix" name="prefix" required>
|
||||||
|
<small class="form-text text-danger" id="addBulkPrefixError"></small>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label for="addBulkCount">Number of Users</label>
|
||||||
|
<input type="number" class="form-control" id="addBulkCount" name="count" value="10" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label for="addBulkStartNumber">Start Number</label>
|
||||||
|
<input type="number" class="form-control" id="addBulkStartNumber" name="start_number" value="1" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label for="addBulkTrafficLimit">Traffic Limit (GB)</label>
|
||||||
|
<input type="number" class="form-control" id="addBulkTrafficLimit" name="traffic_gb" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-md-6">
|
||||||
|
<label for="addBulkExpirationDays">Expiration Days</label>
|
||||||
|
<input type="number" class="form-control" id="addBulkExpirationDays" name="expiration_days" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-3 requires-iplimit-service" style="display: none;">
|
||||||
|
<input type="checkbox" class="form-check-input" id="addBulkUnlimited" name="unlimited">
|
||||||
|
<label class="form-check-label" for="addBulkUnlimited">Unlimited IP (Exempt from IP limit checks)</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary" id="addBulkSubmitButton">Add Bulk Users</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<label for="addExpirationDays">Expiration Days</label>
|
|
||||||
<input type="number" class="form-control" id="addExpirationDays" name="expiration_days"
|
|
||||||
required>
|
|
||||||
</div>
|
|
||||||
<div class="form-check mb-3 requires-iplimit-service" style="display: none;">
|
|
||||||
<input type="checkbox" class="form-check-input" id="addUnlimited" name="unlimited">
|
|
||||||
<label class="form-check-label" for="addUnlimited">Unlimited IP (Exempt from IP limit checks)</label>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-primary" id="addSubmitButton">Add User</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Edit User Modal -->
|
<!-- Edit User Modal -->
|
||||||
<div class="modal fade" id="editUserModal" tabindex="-1" role="dialog" aria-labelledby="editUserModalLabel"
|
<div class="modal fade" id="editUserModal" tabindex="-1" role="dialog" aria-labelledby="editUserModalLabel"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
@ -277,14 +322,18 @@
|
|||||||
|
|
||||||
checkIpLimitServiceStatus();
|
checkIpLimitServiceStatus();
|
||||||
|
|
||||||
const usernameRegex = /^[a-zA-Z0-9]+$/;
|
const usernameRegex = /^[a-zA-Z0-9_]+$/;
|
||||||
|
|
||||||
function validateUsername(username, errorElementId) {
|
function validateUsername(username, errorElementId) {
|
||||||
const isValid = usernameRegex.test(username);
|
|
||||||
const errorElement = $("#" + errorElementId);
|
const errorElement = $("#" + errorElementId);
|
||||||
|
if (!username) {
|
||||||
|
errorElement.text("");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const isValid = usernameRegex.test(username);
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
errorElement.text("Usernames can only contain letters and numbers.");
|
errorElement.text("Usernames can only contain letters, numbers, and underscores.");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
errorElement.text("");
|
errorElement.text("");
|
||||||
@ -297,14 +346,18 @@
|
|||||||
$("#addUsername").on("input", function () {
|
$("#addUsername").on("input", function () {
|
||||||
const username = $(this).val();
|
const username = $(this).val();
|
||||||
const isValid = validateUsername(username, "addUsernameError");
|
const isValid = validateUsername(username, "addUsernameError");
|
||||||
$("#addUserForm button[type='submit']").prop("disabled", !isValid);
|
|
||||||
$("#addSubmitButton").prop("disabled", !isValid);
|
$("#addSubmitButton").prop("disabled", !isValid);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#addBulkPrefix").on("input", function () {
|
||||||
|
const prefix = $(this).val();
|
||||||
|
const isValid = validateUsername(prefix, "addBulkPrefixError");
|
||||||
|
$("#addBulkSubmitButton").prop("disabled", !isValid);
|
||||||
|
});
|
||||||
|
|
||||||
$("#editUsername").on("input", function () {
|
$("#editUsername").on("input", function () {
|
||||||
const username = $(this).val();
|
const username = $(this).val();
|
||||||
const isValid = validateUsername(username, "editUsernameError");
|
const isValid = validateUsername(username, "editUsernameError");
|
||||||
$("#editUserForm button[type='submit']").prop("disabled", !isValid);
|
|
||||||
$("#editSubmitButton").prop("disabled", !isValid);
|
$("#editSubmitButton").prop("disabled", !isValid);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -466,6 +519,54 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#addBulkUsersForm").on("submit", function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!validateUsername($("#addBulkPrefix").val(), "addBulkPrefixError")) {
|
||||||
|
$("#addBulkSubmitButton").prop("disabled", true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$("#addBulkSubmitButton").prop("disabled", true);
|
||||||
|
|
||||||
|
const jsonData = {
|
||||||
|
prefix: $("#addBulkPrefix").val(),
|
||||||
|
count: parseInt($("#addBulkCount").val()),
|
||||||
|
start_number: parseInt($("#addBulkStartNumber").val()),
|
||||||
|
traffic_gb: parseFloat($("#addBulkTrafficLimit").val()),
|
||||||
|
expiration_days: parseInt($("#addBulkExpirationDays").val()),
|
||||||
|
unlimited: $("#addBulkUnlimited").is(":checked")
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "{{ url_for('add_bulk_users_api') }}",
|
||||||
|
method: "POST",
|
||||||
|
contentType: "application/json",
|
||||||
|
data: JSON.stringify(jsonData),
|
||||||
|
success: function(response) {
|
||||||
|
Swal.fire({
|
||||||
|
title: "Success!",
|
||||||
|
text: response.detail || "Bulk user creation started successfully!",
|
||||||
|
icon: "success",
|
||||||
|
confirmButtonText: "OK",
|
||||||
|
}).then(() => {
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(jqXHR) {
|
||||||
|
let errorMessage = "An error occurred during bulk user creation.";
|
||||||
|
if (jqXHR.responseJSON && jqXHR.responseJSON.detail) {
|
||||||
|
errorMessage = jqXHR.responseJSON.detail;
|
||||||
|
}
|
||||||
|
Swal.fire({
|
||||||
|
title: "Error!",
|
||||||
|
text: errorMessage,
|
||||||
|
icon: "error",
|
||||||
|
confirmButtonText: "OK",
|
||||||
|
});
|
||||||
|
$("#addBulkSubmitButton").prop("disabled", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$(document).on("click", ".edit-user", function () {
|
$(document).on("click", ".edit-user", function () {
|
||||||
const username = $(this).data("user");
|
const username = $(this).data("user");
|
||||||
const row = $(this).closest("tr");
|
const row = $(this).closest("tr");
|
||||||
@ -795,10 +896,17 @@
|
|||||||
|
|
||||||
$('#addUserModal').on('show.bs.modal', function (event) {
|
$('#addUserModal').on('show.bs.modal', function (event) {
|
||||||
$('#addUserForm')[0].reset();
|
$('#addUserForm')[0].reset();
|
||||||
|
$('#addBulkUsersForm')[0].reset();
|
||||||
$('#addUsernameError').text('');
|
$('#addUsernameError').text('');
|
||||||
|
$('#addBulkPrefixError').text('');
|
||||||
$('#addTrafficLimit').val('30');
|
$('#addTrafficLimit').val('30');
|
||||||
$('#addExpirationDays').val('30');
|
$('#addExpirationDays').val('30');
|
||||||
|
$('#addBulkTrafficLimit').val('30');
|
||||||
|
$('#addBulkExpirationDays').val('30');
|
||||||
|
$('#addBulkStartNumber').val('1');
|
||||||
$('#addSubmitButton').prop('disabled', true);
|
$('#addSubmitButton').prop('disabled', true);
|
||||||
|
$('#addBulkSubmitButton').prop('disabled', true);
|
||||||
|
$('#addUserModal a[data-toggle="tab"]').first().tab('show');
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#searchButton").on("click", filterUsers);
|
$("#searchButton").on("click", filterUsers);
|
||||||
|
|||||||
@ -123,7 +123,7 @@ clone_repository() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if git clone https://github.com/ReturnFI/Blitz /etc/hysteria &> /dev/null; then
|
if git clone -b beta https://github.com/ReturnFI/Blitz /etc/hysteria &> /dev/null; then
|
||||||
log_success "Repository cloned successfully"
|
log_success "Repository cloned successfully"
|
||||||
else
|
else
|
||||||
log_error "Failed to clone repository"
|
log_error "Failed to clone repository"
|
||||||
|
|||||||
Reference in New Issue
Block a user