mirror of
https://github.com/skinnyrad/Lora-Scanner.git
synced 2026-08-06 16:42:54 +02:00
Merge pull request #5 from skinnyrad/feat/two_gateways
Feat/two gateways
This commit is contained in:
@@ -9,6 +9,7 @@ from collections import deque
|
||||
import re
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
import ipaddress
|
||||
|
||||
app = Flask(__name__)
|
||||
socketio = SocketIO(app)
|
||||
@@ -20,6 +21,9 @@ port3_status = True
|
||||
global ser1
|
||||
global ser2
|
||||
global ser3
|
||||
gateway_ips = {'gateway1': '192.168.0.101',
|
||||
'gateway2': '192.168.0.102',
|
||||
'gateway3': '192.168.0.103'}
|
||||
frequency = lambda port: {'port1': 433, 'port2': 868,'port3': 915}.get(port, None)
|
||||
surveydata = {}
|
||||
parsed_entries = set()
|
||||
@@ -79,61 +83,104 @@ def read_serial_data(port, ser, buffer):
|
||||
#print(f"Error: {e}")
|
||||
pass
|
||||
|
||||
@app.route('/set_gateways', methods=['POST'])
|
||||
def set_gateways():
|
||||
global gateway_ips
|
||||
data = request.form
|
||||
for key in ['gateway1', 'gateway2', 'gateway3']:
|
||||
input_ip = data.get(key, '').strip()
|
||||
if input_ip: # Proceed only if the input is not empty
|
||||
try:
|
||||
# Validate the IP address
|
||||
ipaddress.ip_address(input_ip)
|
||||
# Update the IP address if valid
|
||||
gateway_ips[key] = input_ip
|
||||
except ValueError:
|
||||
# Return an error if the IP address is invalid
|
||||
return jsonify({"error": f"Invalid IP address provided for {key}"}), 400
|
||||
|
||||
for gateway, ip_address in gateway_ips.items():
|
||||
print(f"Gateway {gateway} has IP address: {ip_address}")
|
||||
return jsonify({"message": "Gateway IPs updated successfully"}), 200
|
||||
|
||||
def parse_and_store_data():
|
||||
global surveydata
|
||||
url = "http://10.130.1.1/cgi-bin/log-traffic.has" # Your target URL
|
||||
global parsed_entries
|
||||
global gateway_ips
|
||||
# Include the port number (8000) in your gateway URLs
|
||||
gateway_urls = [
|
||||
f"http://{gateway_ips['gateway1']}:8000/cgi-bin/log-traffic.has", # Gateway 1
|
||||
f"http://{gateway_ips['gateway2']}:8000/cgi-bin/log-traffic.has", # Gateway 2
|
||||
f"http://{gateway_ips['gateway3']}:8000/cgi-bin/log-traffic.has" # Gateway 3
|
||||
]
|
||||
|
||||
headers = {
|
||||
"Host": "10.130.1.1",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:122.0) Gecko/20100101 Firefox/122.0",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
||||
"Accept-Language": "en-US,en;q=0.5",
|
||||
"Accept-Encoding": "gzip, deflate",
|
||||
"DNT": "1",
|
||||
"Sec-GPC": "1",
|
||||
"Authorization": "Basic cm9vdDpkcmFnaW5v",
|
||||
"Authorization": "Basic cm9vdDpkcmFnaW5v", # Assumes the same credentials for both gateways
|
||||
"Connection": "keep-alive",
|
||||
"Referer": "http://10.130.1.1/cgi-bin/log-lora.has",
|
||||
"Upgrade-Insecure-Requests": "1"
|
||||
}
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
for url in gateway_urls:
|
||||
try:
|
||||
response = requests.get(url, headers=headers, timeout=10)
|
||||
if response.status_code == 200:
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
table = soup.find('table')
|
||||
if table:
|
||||
rows = table.find_all('tr')[1:] # Skip the header row
|
||||
|
||||
for row in rows:
|
||||
# Skip hidden rows in this iteration
|
||||
if row.get('style') == 'display: none;':
|
||||
continue
|
||||
|
||||
cells = row.find_all('td')
|
||||
if not cells:
|
||||
continue
|
||||
|
||||
# Prepare formatted_row from visible cells, skipping the first cell for Chevron icon
|
||||
formatted_row = ' | '.join(cell.text.strip() for cell in cells[1:])
|
||||
|
||||
# Extract dev_id and freq from the visible row
|
||||
dev_id = extract_dev_id(formatted_row)
|
||||
freq = extract_freq(formatted_row)
|
||||
|
||||
if response.status_code == 200:
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
table = soup.find('table')
|
||||
rows = table.find_all('tr')
|
||||
headers = [header.text.strip() for header in rows[0].find_all('th')][1:]
|
||||
# Extract RSSI from the next hidden row
|
||||
hidden_row = row.find_next_sibling('tr')
|
||||
if hidden_row and 'display: none;' in hidden_row.get('style', ''):
|
||||
hidden_data = hidden_row.td.text.strip()
|
||||
rssi_match = re.search(r'"Rssi":(-?\d+)', hidden_data)
|
||||
if rssi_match:
|
||||
rssi = int(rssi_match.group(1))
|
||||
else:
|
||||
rssi = None
|
||||
else:
|
||||
rssi = None
|
||||
|
||||
for row in rows[1:]:
|
||||
cells = row.find_all('td')
|
||||
cell_data = [cell.text.strip() for cell in cells[1:] if cells.index(cell) < len(headers) + 1]
|
||||
formatted_row = ' | '.join(cell_data)
|
||||
# Save data into the surveydata structure
|
||||
if dev_id and freq is not None:
|
||||
entry_identifier = f"{dev_id}_{freq}_{formatted_row}"
|
||||
if entry_identifier not in parsed_entries:
|
||||
parsed_entries.add(entry_identifier)
|
||||
if dev_id not in surveydata:
|
||||
surveydata[dev_id] = []
|
||||
surveydata[dev_id].append([freq, rssi, formatted_row])
|
||||
|
||||
dev_id = extract_dev_id(formatted_row) # Your existing function to extract DevEui or DevAddr
|
||||
freq = extract_freq(formatted_row) # Your existing function to extract frequency
|
||||
|
||||
if dev_id and freq:
|
||||
entry_identifier = f"{dev_id}_{formatted_row}" # Create a unique identifier for the entry
|
||||
|
||||
# Only process the entry if we haven't seen this identifier before
|
||||
if entry_identifier not in parsed_entries:
|
||||
parsed_entries.add(entry_identifier) # Add the identifier to the set
|
||||
|
||||
# Initialize dictionary for dev_id if not present
|
||||
if dev_id not in surveydata:
|
||||
surveydata[dev_id] = []
|
||||
|
||||
# Append new data to the list associated with the DevEui or DevAddr
|
||||
surveydata[dev_id].append([freq, 0, formatted_row])
|
||||
|
||||
print("Data parsed and stored.")
|
||||
else:
|
||||
print(f"Request failed with status code: {response.status_code}")
|
||||
print(f"Data parsed and stored from {url}.")
|
||||
else:
|
||||
print(f"Request to {url} failed with status code: {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f"An error occurred while processing {url}: {e}")
|
||||
|
||||
|
||||
# Schedule the next call to this function
|
||||
Timer(60, parse_and_store_data).start() # Call this function every 60 seconds
|
||||
Timer(30, parse_and_store_data).start() # Call this function every 30 seconds
|
||||
|
||||
|
||||
def extract_dev_id(formatted_row):
|
||||
@@ -346,5 +393,5 @@ def get_table_data():
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
Timer(60, parse_and_store_data).start()
|
||||
Timer(30, parse_and_store_data).start()
|
||||
socketio.run(app, debug=True)
|
||||
|
||||
+41
-42
@@ -1,10 +1,35 @@
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
# Define the URL and headers
|
||||
url = "http://10.130.1.1/cgi-bin/log-traffic.has"
|
||||
def get_gateway_data(url, headers):
|
||||
"""
|
||||
Makes a request to the specified gateway URL and parses the HTML table content.
|
||||
Returns a list of formatted strings for each row in the table.
|
||||
"""
|
||||
formatted_rows = []
|
||||
response = requests.get(url, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
table = soup.find('table')
|
||||
if table:
|
||||
rows = table.find_all('tr')
|
||||
headers = [header.text.strip() for header in rows[0].find_all('th')][1:]
|
||||
for row in rows[1:]:
|
||||
cells = row.find_all('td')
|
||||
cell_data = [cell.text.strip() for cell in cells[1:] if cells.index(cell) < len(headers) + 1]
|
||||
formatted_row = ' | '.join(cell_data)
|
||||
formatted_rows.append(formatted_row)
|
||||
else:
|
||||
print(f"Request to {url} failed with status code:", response.status_code)
|
||||
|
||||
return formatted_rows
|
||||
|
||||
|
||||
# Define URLs and headers for both gateways
|
||||
gateway_1 = "http://192.168.1.23/cgi-bin/log-traffic.has"
|
||||
gateway_2 = "http://192.168.1.24/cgi-bin/log-traffic.has"
|
||||
headers = {
|
||||
"Host": "10.130.1.1",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:122.0) Gecko/20100101 Firefox/122.0",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
||||
"Accept-Language": "en-US,en;q=0.5",
|
||||
@@ -13,46 +38,20 @@ headers = {
|
||||
"Sec-GPC": "1",
|
||||
"Authorization": "Basic cm9vdDpkcmFnaW5v",
|
||||
"Connection": "keep-alive",
|
||||
"Referer": "http://10.130.1.1/cgi-bin/log-lora.has",
|
||||
"Upgrade-Insecure-Requests": "1"
|
||||
}
|
||||
|
||||
# Send the GET request
|
||||
response = requests.get(url, headers=headers)
|
||||
# Fetch and print data from both gateways
|
||||
print("Fetching data from Gateway 1 (192.168.1.23)...")
|
||||
data_1 = get_gateway_data(gateway_1, headers)
|
||||
for row in data_1:
|
||||
print(row)
|
||||
|
||||
print("\nFetching data from Gateway 2 (192.168.1.24)...")
|
||||
# Update the 'Host' header for the second gateway if necessary
|
||||
headers["Host"] = "192.168.1.24"
|
||||
data_2 = get_gateway_data(gateway_2, headers)
|
||||
for row in data_2:
|
||||
print(row)
|
||||
|
||||
|
||||
if response.status_code == 200:
|
||||
# Parse the HTML content using BeautifulSoup
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
|
||||
# Find the table
|
||||
table = soup.find('table')
|
||||
|
||||
# Initialize an empty list to store formatted strings for each row
|
||||
formatted_rows = []
|
||||
|
||||
# Find all table rows
|
||||
rows = table.find_all('tr')
|
||||
|
||||
# Get column headers from the first row
|
||||
# Using .text.strip() to clean the text and [1:] to skip the empty first column
|
||||
headers = [header.text.strip() for header in rows[0].find_all('th')][1:]
|
||||
|
||||
# Iterate through each row (skipping the first row with the headers)
|
||||
for row in rows[1:]:
|
||||
# Find all data cells (td tags) in the row
|
||||
cells = row.find_all('td')
|
||||
|
||||
# Extract text from each cell
|
||||
# Using [1:] to skip the first cell with the arrow icon
|
||||
cell_data = [cell.text.strip() for cell in cells[1:] if cells.index(cell) < len(headers) + 1]
|
||||
|
||||
# Format the row data into a neat line
|
||||
formatted_row = ' | '.join(cell_data)
|
||||
|
||||
# Append the formatted string to the list
|
||||
formatted_rows.append(formatted_row)
|
||||
|
||||
# Print the formatted string to display the row
|
||||
print(formatted_row)
|
||||
else:
|
||||
print("Request failed with status code:", response.status_code)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 775 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Vendored
+7
File diff suppressed because one or more lines are too long
Vendored
+2
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -5,22 +5,21 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="description" content="" />
|
||||
<meta name="author" content="" />
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.1.2/socket.io.js"></script>
|
||||
<script type="text/javascript" src="static/js/socket.io.js"></script>
|
||||
<title>LoRa Scanner</title>
|
||||
<link rel="icon" type="image/x-icon" href="assets/img/favicon.ico" />
|
||||
<!-- Font Awesome icons (free version)-->
|
||||
<script src="https://use.fontawesome.com/releases/v6.3.0/js/all.js" crossorigin="anonymous"></script>
|
||||
<script src="static/js/all.js" crossorigin="anonymous"></script>
|
||||
<!-- Google fonts-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Saira+Extra+Condensed:500,700" rel="stylesheet" type="text/css" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Muli:400,400i,800,800i" rel="stylesheet" type="text/css" />
|
||||
|
||||
<!-- Core theme CSS (includes Bootstrap)-->
|
||||
<link href="static/css/styles.css" rel="stylesheet" />
|
||||
<!-- Include SweetAlert2 CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@10">
|
||||
<link rel="stylesheet" href="static/css/sweetalert2.css">
|
||||
|
||||
<!-- Include SweetAlert2 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="static/js/sweetalert2.js"></script>
|
||||
<script src="static/js/jquery.min.js"></script>
|
||||
|
||||
</head>
|
||||
<body id="page-top">
|
||||
@@ -637,7 +636,7 @@
|
||||
|
||||
</div>
|
||||
<!-- Bootstrap core JS-->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="static/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Core theme JS-->
|
||||
<script src="static/js/scripts.js"></script>
|
||||
</body>
|
||||
|
||||
+75
-8
@@ -5,21 +5,20 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="description" content="" />
|
||||
<meta name="author" content="" />
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.1.2/socket.io.js"></script>
|
||||
<script type="text/javascript" src="static/js/socket.io.js"></script>
|
||||
<title>LoRa Scanner</title>
|
||||
<link rel="icon" type="image/x-icon" href="assets/img/favicon.ico" />
|
||||
<!-- Font Awesome icons (free version)-->
|
||||
<script src="https://use.fontawesome.com/releases/v6.3.0/js/all.js" crossorigin="anonymous"></script>
|
||||
<script src="static/js/all.js" crossorigin="anonymous"></script>
|
||||
<!-- Google fonts-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Saira+Extra+Condensed:500,700" rel="stylesheet" type="text/css" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Muli:400,400i,800,800i" rel="stylesheet" type="text/css" />
|
||||
|
||||
<!-- Core theme CSS (includes Bootstrap)-->
|
||||
<link href="static/css/styles.css" rel="stylesheet" />
|
||||
<!-- Include SweetAlert2 CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@10">
|
||||
<link rel="stylesheet" href="static/css/sweetalert2.css">
|
||||
|
||||
<!-- Include SweetAlert2 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<script src="static/js/sweetalert2.js"></script>
|
||||
|
||||
|
||||
</head>
|
||||
@@ -33,7 +32,7 @@
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
|
||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#about">About</a></li>
|
||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#about">Home</a></li>
|
||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/analysis">Analysis Mode</a></li>
|
||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/survey">Survey Mode</a></li>
|
||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/tracking">Tracking Mode</a></li>
|
||||
@@ -61,9 +60,77 @@
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Add this form within the body where it fits best, possibly under the About section -->
|
||||
<section class="resume-section">
|
||||
<div class="resume-section-content">
|
||||
<h2 class="mb-0" style="text-align: center;">LoRaWAN Gateway Configuration</h2><br>
|
||||
<img src="static/assets/img/lorawan.webp" style="width:100%; "><br><br>
|
||||
<div style="display: flex; justify-content: center; margin-top: 20px;"><button class="transmit-button" id="configureGatewayBtn">Configure Gateway</button></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('configureGatewayBtn').addEventListener('click', function() {
|
||||
let gatewayIPs = {};
|
||||
|
||||
const ipPrompt = (title) => {
|
||||
return Swal.fire({
|
||||
title: title,
|
||||
input: 'text',
|
||||
inputPlaceholder: 'Leave empty to keep current IP',
|
||||
inputValidator: (value) => {
|
||||
if (value && !value.match(/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/)) {
|
||||
return 'Please enter a valid IP address or leave it empty';
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ipPrompt('Enter Gateway 1 IP Address').then((result) => {
|
||||
if (result.value) gatewayIPs.gateway1 = result.value;
|
||||
|
||||
ipPrompt('Enter Gateway 2 IP Address').then((result) => {
|
||||
if (result.value) gatewayIPs.gateway2 = result.value;
|
||||
|
||||
ipPrompt('Enter Gateway 3 IP Address').then((result) => {
|
||||
if (result.value) gatewayIPs.gateway3 = result.value;
|
||||
|
||||
// Now send the IPs to the server only if they are not undefined
|
||||
let queryString = Object.keys(gatewayIPs).reduce((acc, key) => {
|
||||
if (gatewayIPs[key] !== undefined) {
|
||||
acc.push(`${key}=${encodeURIComponent(gatewayIPs[key])}`);
|
||||
}
|
||||
return acc;
|
||||
}, []).join('&');
|
||||
|
||||
if (queryString) {
|
||||
fetch('/set_gateways', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: queryString
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => Swal.fire('Success', 'Gateway IPs updated successfully', 'success'))
|
||||
.catch(error => Swal.fire('Error', 'There was an issue updating the Gateway IPs', 'error'));
|
||||
} else {
|
||||
Swal.fire('No Changes', 'No IP addresses were changed.', 'info');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<!-- Bootstrap core JS-->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="static/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Core theme JS-->
|
||||
<script src="static/js/scripts.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -5,22 +5,21 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="description" content="" />
|
||||
<meta name="author" content="" />
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.1.2/socket.io.js"></script>
|
||||
<script type="text/javascript" src="static/js/socket.io.js"></script>
|
||||
<title>LoRa Scanner</title>
|
||||
<link rel="icon" type="image/x-icon" href="assets/img/favicon.ico" />
|
||||
<!-- Font Awesome icons (free version)-->
|
||||
<script src="https://use.fontawesome.com/releases/v6.3.0/js/all.js" crossorigin="anonymous"></script>
|
||||
<script src="static/js/all.js" crossorigin="anonymous"></script>
|
||||
<!-- Google fonts-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Saira+Extra+Condensed:500,700" rel="stylesheet" type="text/css" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Muli:400,400i,800,800i" rel="stylesheet" type="text/css" />
|
||||
|
||||
<!-- Core theme CSS (includes Bootstrap)-->
|
||||
<link href="static/css/styles.css" rel="stylesheet" />
|
||||
<!-- Include SweetAlert2 CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@10">
|
||||
<link rel="stylesheet" href="static/css/sweetalert2.css">
|
||||
|
||||
<!-- Include SweetAlert2 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
<script src="static/js/sweetalert2.js"></script>
|
||||
<script src="static/js/jquery.min.js"></script>
|
||||
|
||||
</head>
|
||||
<body id="page-top">
|
||||
|
||||
@@ -5,22 +5,21 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="description" content="" />
|
||||
<meta name="author" content="" />
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.1.2/socket.io.js"></script>
|
||||
<script type="text/javascript" src="static/js/socket.io.js"></script>
|
||||
<title>LoRa Scanner</title>
|
||||
<link rel="icon" type="image/x-icon" href="assets/img/favicon.ico" />
|
||||
<!-- Font Awesome icons (free version)-->
|
||||
<script src="https://use.fontawesome.com/releases/v6.3.0/js/all.js" crossorigin="anonymous"></script>
|
||||
<script src="static/js/all.js" crossorigin="anonymous"></script>
|
||||
<!-- Google fonts-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Saira+Extra+Condensed:500,700" rel="stylesheet" type="text/css" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Muli:400,400i,800,800i" rel="stylesheet" type="text/css" />
|
||||
|
||||
<!-- Core theme CSS (includes Bootstrap)-->
|
||||
<link href="static/css/styles.css" rel="stylesheet" />
|
||||
<!-- Include SweetAlert2 CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@10">
|
||||
<link rel="stylesheet" href="static/css/sweetalert2.css">
|
||||
|
||||
<!-- Include SweetAlert2 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="static/js/sweetalert2.js"></script>
|
||||
<script src="static/js/jquery.min.js"></script>
|
||||
|
||||
</head>
|
||||
<body id="page-top">
|
||||
@@ -946,7 +945,7 @@
|
||||
|
||||
</div>
|
||||
<!-- Bootstrap core JS-->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="static/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Core theme JS-->
|
||||
<script src="static/js/scripts.js"></script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user