more changes
This commit is contained in:
50
app/app.py
50
app/app.py
@@ -563,6 +563,56 @@ def add_user():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({'success': False, 'message': 'Unknown error'}), 500
|
return jsonify({'success': False, 'message': 'Unknown error'}), 500
|
||||||
|
|
||||||
|
@app.route('/duplicate_user', methods=['POST'])
|
||||||
|
def duplicate_user():
|
||||||
|
"""
|
||||||
|
Retrieves user data (MAC address, description, VLAN ID) from the database
|
||||||
|
based on the provided MAC address. This data is intended to be used to
|
||||||
|
pre-populate a "duplicate user" form in the frontend.
|
||||||
|
"""
|
||||||
|
mac_address = request.form['mac_address'] # Get the MAC address from the POST request.
|
||||||
|
|
||||||
|
db = get_db() # Get a database connection.
|
||||||
|
if db:
|
||||||
|
cursor = db.cursor(dictionary=True) # Create a cursor that returns results as dictionaries.
|
||||||
|
try:
|
||||||
|
# Construct the SQL query. This query retrieves the MAC address,
|
||||||
|
# description, and VLAN ID for the specified user.
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT
|
||||||
|
rc.username AS mac_address,
|
||||||
|
IFNULL((SELECT value FROM radgroupreply rgr
|
||||||
|
WHERE rgr.groupname = (SELECT groupname FROM radusergroup rug WHERE rug.username = rc.username LIMIT 1)
|
||||||
|
AND rgr.attribute = 'Tunnel-Private-Group-Id' LIMIT 1), 'N/A') AS vlan_id,
|
||||||
|
IFNULL((SELECT value FROM radcheck rch
|
||||||
|
WHERE rch.username = rc.username AND rch.attribute = 'User-Description' LIMIT 1), 'N/A') AS description
|
||||||
|
FROM radcheck rc
|
||||||
|
WHERE rc.username = %s /* %s is a placeholder for the MAC address */
|
||||||
|
GROUP BY rc.username;
|
||||||
|
""", (mac_address,)) # Execute the query with the MAC address as a parameter.
|
||||||
|
|
||||||
|
user_data = cursor.fetchone() # Fetch the first (and should be only) result.
|
||||||
|
cursor.close() # Close the cursor.
|
||||||
|
db.close() # Close the database connection.
|
||||||
|
|
||||||
|
if user_data:
|
||||||
|
# If user data was found, return it as a JSON response.
|
||||||
|
return jsonify(user_data)
|
||||||
|
else:
|
||||||
|
# If no user data was found (e.g., invalid MAC address), return an empty JSON object.
|
||||||
|
return jsonify({})
|
||||||
|
|
||||||
|
except mysql.connector.Error as err:
|
||||||
|
# Handle database errors. Log the error and return an error message.
|
||||||
|
print(f"Database Error: {err}")
|
||||||
|
cursor.close()
|
||||||
|
db.close()
|
||||||
|
return jsonify({}) # Return an empty JSON object on error, to avoid crashing.
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Handle the case where the database connection could not be established.
|
||||||
|
return jsonify({}) # Return empty JSON object
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=True, host='0.0.0.0', port=8080)
|
app.run(debug=True, host='0.0.0.0', port=8080)
|
||||||
|
|||||||
@@ -78,9 +78,36 @@
|
|||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<dialog id="duplicate-dialog">
|
<dialog id="duplicate-dialog">
|
||||||
<div id="duplicate-dialog-content"></div>
|
<div id="duplicate-dialog-content">
|
||||||
<button id="close-dialog">❌</button>
|
<table border="1">
|
||||||
<button id="save-duplicated-user">Save</button>
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>MAC Address</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>VLAN ID</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><input type="text" id="dup-mac"></td>
|
||||||
|
<td><input type="text" id="dup-description"></td>
|
||||||
|
<td>
|
||||||
|
<select id="dup-vlan_id">
|
||||||
|
{% for group in groups %}
|
||||||
|
<option value="{{ group.groupname }}">
|
||||||
|
{{ group.groupname }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; justify-content: flex-end; margin-top: 10px;">
|
||||||
|
<button id="close-duplicate-dialog">Cancel</button>
|
||||||
|
<button id="save-duplicated-user">Save</button>
|
||||||
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -160,53 +187,47 @@
|
|||||||
|
|
||||||
|
|
||||||
function duplicateUser(mac_address) {
|
function duplicateUser(mac_address) {
|
||||||
fetch('/duplicate_user', {
|
console.log("duplicateUser called with mac_address:", mac_address);
|
||||||
method: 'POST',
|
fetch('/duplicate_user', {
|
||||||
headers: {
|
method: 'POST',
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
headers: {
|
||||||
},
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
body: `mac_address=${mac_address}`
|
},
|
||||||
})
|
body: `mac_address=${mac_address}`
|
||||||
.then(response => response.json())
|
})
|
||||||
.then(data => {
|
.then(response => {
|
||||||
const userData = data;
|
console.log("Response status:", response.status);
|
||||||
let newTable = `<table border="1">
|
console.log("Response text:", response.text());
|
||||||
<thead>
|
return response.json()
|
||||||
<tr>
|
})
|
||||||
<th>MAC Address</th>
|
.then(data => {
|
||||||
<th>Description</th>
|
console.log("Response data:", data);
|
||||||
<th>VLAN ID</th>
|
const userData = data;
|
||||||
</tr>
|
if (userData) {
|
||||||
</thead>
|
document.getElementById('dup-mac').value = userData.mac_address;
|
||||||
<tbody>
|
document.getElementById('dup-description').value = userData.description;
|
||||||
<tr>
|
|
||||||
<td><input type="text" id="new-mac" value="${userData.mac_address}"></td>
|
|
||||||
<td><input type="text" class="new-description" value="${userData.description}"></td>
|
|
||||||
<td>
|
|
||||||
<select id="new-vlan_id">
|
|
||||||
{% for group in groups %}
|
|
||||||
<option value="{{ group.groupname }}" ${userData.vlan_id === group.groupname ? 'selected' : ''}>
|
|
||||||
{{ group.groupname }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>`;
|
|
||||||
|
|
||||||
|
const vlanSelect = document.getElementById('dup-vlan_id');
|
||||||
|
vlanSelect.innerHTML = '';
|
||||||
|
{% for group in groups %}
|
||||||
|
let option = document.createElement('option');
|
||||||
|
option.value = "{{ group.groupname }}";
|
||||||
|
option.textContent = "{{ group.groupname }}";
|
||||||
|
if ("{{ group.groupname }}" === userData.vlan_id) {
|
||||||
|
option.selected = true;
|
||||||
|
}
|
||||||
|
vlanSelect.appendChild(option);
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
document.getElementById('duplicate-dialog').showModal();
|
||||||
newTable += `<tr>
|
} else {
|
||||||
<td colspan="3" class="merged-cell">
|
alert("Failed to retrieve user data for duplication.");
|
||||||
<button onclick="addDuplicatedUserRow(this)">➕</button>
|
}
|
||||||
</td>
|
});
|
||||||
</tr></tbody></table>`;
|
}
|
||||||
|
|
||||||
document.getElementById('duplicate-dialog-content').innerHTML = newTable;
|
|
||||||
document.getElementById('duplicate-dialog').showModal();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('close-dialog').addEventListener('click', () => {
|
document.getElementById('close-duplicate-dialog').addEventListener('click', () => {
|
||||||
document.getElementById('duplicate-dialog').close();
|
document.getElementById('duplicate-dialog').close();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -215,74 +236,40 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
function saveDuplicatedUser() {
|
function saveDuplicatedUser() {
|
||||||
let rows = document.querySelectorAll('#duplicate-dialog-content table tbody tr');
|
let mac = document.getElementById('dup-mac').value;
|
||||||
let new_mac_address = rows[0].querySelector('#new-mac').value; //changed
|
let description = document.getElementById('dup-description').value;
|
||||||
let attributes = [];
|
let vlan_id = document.getElementById('dup-vlan_id').value;
|
||||||
for (let i = 1; i < rows.length - 1; i++) {
|
|
||||||
const descriptionInput = rows[i].querySelector(`.new-description`);
|
|
||||||
const vlanIdInput = rows[i].querySelector(`.new-vlan_id`);
|
|
||||||
|
|
||||||
|
fetch('/add_user', {
|
||||||
if (descriptionInput && vlanIdInput) {
|
|
||||||
attributes.push({
|
|
||||||
description: descriptionInput.value,
|
|
||||||
vlan_id: vlanIdInput.value,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
|
|
||||||
console.warn(`Input elements not found for row ${i}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch('/save_duplicated_user', {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ mac_address: new_mac_address, attributes: attributes }) //changed
|
body: JSON.stringify({ mac_address: mac, description: description, vlan_id: vlan_id }),
|
||||||
})
|
})
|
||||||
.then(response => response.text())
|
.then((response) => {
|
||||||
.then(data => {
|
if (!response.ok) {
|
||||||
if (data === 'success') {
|
return response.text().then((text) => {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}, body: ${text}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
console.log("Server response:", data);
|
||||||
|
if (data && data.success) {
|
||||||
document.getElementById('duplicate-dialog').close();
|
document.getElementById('duplicate-dialog').close();
|
||||||
location.reload();
|
location.reload();
|
||||||
} else {
|
} else {
|
||||||
alert('Error saving duplicated user: ' + data);
|
alert("Error adding user: " + (data && data.message ? data.message : "Unknown error"));
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Fetch error:", error);
|
||||||
|
alert("Error adding user: " + error.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function addDuplicatedUserRow(button) {
|
|
||||||
const table = button.parentNode.parentNode.parentNode; //get the table
|
|
||||||
const newRow = table.insertRow(table.rows.length - 1);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const cell1 = newRow.insertCell(0);
|
|
||||||
const cell2 = newRow.insertCell(1);
|
|
||||||
const cell3 = newRow.insertCell(2);
|
|
||||||
const cell4 = newRow.insertCell(3);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cell1.classList.add('merged-cell');
|
|
||||||
cell2.innerHTML = `<input type="text" class="new-description" value="">`;
|
|
||||||
cell3.innerHTML = `<select class="new-vlan_id">
|
|
||||||
{% for group in groups %}
|
|
||||||
<option value="{{ group.groupname }}">
|
|
||||||
{{ group.groupname }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>`;
|
|
||||||
cell4.innerHTML = `<button onclick="removeDuplicatedUserRow(this)">🗑️</button>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeDuplicatedUserRow(button) {
|
|
||||||
const row = button.parentNode.parentNode;
|
|
||||||
row.parentNode.removeChild(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addNewUserRow() {
|
function addNewUserRow() {
|
||||||
document.getElementById('add-user-dialog').showModal();
|
document.getElementById('add-user-dialog').showModal();
|
||||||
}
|
}
|
||||||
@@ -295,7 +282,7 @@
|
|||||||
saveNewUser();
|
saveNewUser();
|
||||||
});
|
});
|
||||||
|
|
||||||
function saveNewUser() {
|
function saveNewUser() {
|
||||||
const mac = document.getElementById('new-mac').value;
|
const mac = document.getElementById('new-mac').value;
|
||||||
const description = document.getElementById('new-description').value;
|
const description = document.getElementById('new-description').value;
|
||||||
const vlan_id = document.getElementById('new-vlan_id').value;
|
const vlan_id = document.getElementById('new-vlan_id').value;
|
||||||
@@ -321,28 +308,28 @@
|
|||||||
},
|
},
|
||||||
body: JSON.stringify(userData), // Send the data as a JSON string
|
body: JSON.stringify(userData), // Send the data as a JSON string
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
// Handle HTTP errors (e.g., 400, 500)
|
// Handle HTTP errors (e.g., 400, 500)
|
||||||
return response.text().then(text => {
|
return response.text().then(text => {
|
||||||
throw new Error(`HTTP error! status: ${response.status}, body: ${text}`);
|
throw new Error(`HTTP error! status: ${response.status}, body: ${text}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return response.json(); // Expect JSON response from server
|
return response.json(); // Expect JSON response from server
|
||||||
})
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
console.log("Server response:", data);
|
console.log("Server response:", data);
|
||||||
if (data && data.success) { // Check for success property in JSON response
|
if (data && data.success) { // Check for success property in JSON response
|
||||||
document.getElementById('add-user-dialog').close();
|
document.getElementById('add-user-dialog').close();
|
||||||
location.reload();
|
location.reload();
|
||||||
} else {
|
} else {
|
||||||
alert('Error adding user: ' + (data && data.message ? data.message : 'Unknown error')); // Show error from server or a generic message
|
alert('Error adding user: ' + (data && data.message ? data.message : 'Unknown error')); // Show error from server or a generic message
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Fetch error:', error); // Log the error for debugging
|
console.error('Fetch error:', error); // Log the error for debugging
|
||||||
alert('Error adding user: ' + error.message); // Show a user-friendly error message
|
alert('Error adding user: ' + error.message); // Show a user-friendly error message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user