chore: Update Dockerfile and main.go for serving web content from the 'web' directory

This commit is contained in:
skidoodle 2024-05-31 22:15:38 +02:00
parent 23d176dcc7
commit 9b9d9ba192
6 changed files with 216 additions and 225 deletions

View file

@ -7,6 +7,6 @@ FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/iphistory .
COPY --from=builder /app/index.html .
COPY --from=builder /app/web ./web
EXPOSE 8080
CMD ["./iphistory"]

View file

@ -1,221 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IP History</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #222;
color: #fff;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
text-align: center;
margin-top: 20px;
margin-bottom: 10px;
}
button.export {
background-color: #007bff;
color: #fff;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 5px;
position: absolute;
top: 20px;
left: 20px;
}
button.export:hover {
background-color: #0056b3;
}
table {
width: 80%;
border-collapse: collapse;
margin: 0 auto;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
background-color: #333;
margin-bottom: 20px;
}
th, td {
border: 1px solid #444;
padding: 12px;
text-align: left;
}
th {
cursor: pointer;
background-color: #007bff;
color: #fff;
}
th.sort-asc::after, th.sort-desc::after {
content: "-ascending";
color: #fff;
}
th.sort-desc::after {
content: "-descending";
}
tbody tr:nth-child(even) {
background-color: #444;
}
.pagination {
margin-top: 20px;
}
.pagination button {
background-color: #007bff;
color: #fff;
border: none;
padding: 8px 16px;
cursor: pointer;
border-radius: 5px;
margin-right: 5px;
}
.pagination button:hover {
background-color: #0056b3;
}
.pagination button.active {
background-color: #0056b3;
}
</style>
</head>
<body>
<button class="export" onclick="exportToCSV()">Export to CSV</button>
<h1>IP History</h1>
<table id="ipTable">
<thead>
<tr>
<th onclick="sortTable()">Timestamp</th>
<th>IP Address</th>
</tr>
</thead>
<tbody></tbody>
</table>
<div class="pagination" id="pagination">
<button onclick="prevPage()" id="prevBtn">&laquo; Prev</button>
<button onclick="nextPage()" id="nextBtn">Next &raquo;</button>
</div>
<script>
let ipHistory = [];
let currentPage = 1;
const entriesPerPage = 10;
function loadIPHistory() {
fetch('/history')
.then(response => response.json())
.then(data => {
ipHistory = data.map(entry => entry.split(" - "));
displayTable();
})
.catch(error => console.error('Error fetching IP history:', error));
}
function displayTable() {
const tbody = document.getElementById('ipTable').getElementsByTagName('tbody')[0];
tbody.innerHTML = '';
const start = (currentPage - 1) * entriesPerPage;
const end = start + entriesPerPage;
const currentEntries = ipHistory.slice(start, end);
currentEntries.forEach(row => {
const tr = document.createElement('tr');
row.forEach(cell => {
const td = document.createElement('td');
td.textContent = cell;
tr.appendChild(td);
});
tbody.appendChild(tr);
});
updatePaginationButtons();
}
function updatePaginationButtons() {
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
if (currentPage === 1) {
prevBtn.style.display = 'none';
} else {
prevBtn.style.display = 'inline-block';
}
if (currentPage === Math.ceil(ipHistory.length / entriesPerPage)) {
nextBtn.style.display = 'none';
} else {
nextBtn.style.display = 'inline-block';
}
}
function nextPage() {
if (currentPage < Math.ceil(ipHistory.length / entriesPerPage)) {
currentPage++;
displayTable();
}
}
function prevPage() {
if (currentPage > 1) {
currentPage--;
displayTable();
}
}
function sortTable() {
const table = document.getElementById('ipTable');
const tbody = table.getElementsByTagName('tbody')[0];
const rows = Array.from(tbody.getElementsByTagName('tr'));
const th = table.getElementsByTagName('th')[0];
const isAscending = !th.classList.contains('sort-asc');
const orderModifier = isAscending ? 1 : -1;
rows.sort((a, b) => {
const aText = a.getElementsByTagName('td')[0].textContent;
const bText = b.getElementsByTagName('td')[0].textContent;
return aText.localeCompare(bText) * orderModifier;
});
th.classList.toggle('sort-asc', isAscending);
th.classList.toggle('sort-desc', !isAscending);
rows.forEach(row => tbody.appendChild(row));
}
function exportToCSV() {
const rows = [['Timestamp', 'IP Address'], ...ipHistory];
let csvContent = "data:text/csv;charset=utf-8,";
rows.forEach(rowArray => {
const row = rowArray.join(",");
csvContent += row + "\r\n";
});
const encodedUri = encodeURI(csvContent);
const link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", "ip_history.csv");
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
window.onload = loadIPHistory;
</script>
</body>
</html>

View file

@ -115,9 +115,7 @@ func trackIP(ipChan <-chan string) {
}
func serveWeb() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.html")
})
http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir("web"))))
http.HandleFunc("/history", func(w http.ResponseWriter, r *http.Request) {
history, err := readHistory()

31
web/index.html Normal file
View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>IP History</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
</head>
<body>
<h1>IP History</h1>
<div class="controls">
<button class="export" onclick="exportToCSV()">Export to CSV</button>
<input type="text" id="searchBar" class="search-bar" placeholder="Search IP history..." autocomplete="off">
</div>
<table id="ipTable">
<thead>
<tr>
<th onclick="sortTable()">Timestamp</th>
<th>IP Address</th>
</tr>
</thead>
<tbody></tbody>
</table>
<div class="pagination" id="pagination">
<button onclick="prevPage()" id="prevBtn" disabled>&laquo; Prev</button>
<button onclick="nextPage()" id="nextBtn" disabled>Next &raquo;</button>
</div>
<script src="script.js"></script>
</body>
</html>

75
web/script.js Normal file
View file

@ -0,0 +1,75 @@
let ipHistory = [], filteredHistory = [], currentPage = 1, entriesPerPage = 10;
$(document).ready(function() {
$.getJSON('/history', function(data) {
ipHistory = data.map(entry => entry.split(" - "));
filteredHistory = ipHistory;
displayTable();
}).fail(function() {
console.error('Error fetching IP history');
});
$('#searchBar').on('input', function() {
const query = $(this).val().toLowerCase();
filteredHistory = ipHistory.filter(row => row.some(cell => cell.toLowerCase().includes(query)));
currentPage = 1;
displayTable();
});
});
function displayTable() {
const tbody = $('#ipTable tbody').empty();
const start = (currentPage - 1) * entriesPerPage;
const end = start + entriesPerPage;
const currentEntries = filteredHistory.slice(start, end);
currentEntries.forEach(row => {
const tr = $('<tr>');
row.forEach(cell => {
tr.append($('<td>').text(cell));
});
tbody.append(tr);
});
updatePaginationButtons();
}
function updatePaginationButtons() {
$('#prevBtn').prop('disabled', currentPage === 1);
$('#nextBtn').prop('disabled', currentPage === Math.ceil(filteredHistory.length / entriesPerPage));
}
function nextPage() {
if (currentPage < Math.ceil(filteredHistory.length / entriesPerPage)) {
currentPage++;
displayTable();
}
}
function prevPage() {
if (currentPage > 1) {
currentPage--;
displayTable();
}
}
function sortTable() {
const th = $('#ipTable th').eq(0);
const isAscending = !th.hasClass('sort-asc');
const orderModifier = isAscending ? 1 : -1;
filteredHistory.sort((a, b) => a[0].localeCompare(b[0]) * orderModifier);
th.toggleClass('sort-asc', isAscending).toggleClass('sort-desc', !isAscending);
displayTable();
}
function exportToCSV() {
const rows = [['Timestamp', 'IP Address'], ...filteredHistory];
const csvContent = "data:text/csv;charset=utf-8," + rows.map(e => e.join(",")).join("\n");
const encodedUri = encodeURI(csvContent);
const link = $('<a>').attr({href: encodedUri, download: 'ip_history.csv'});
$('body').append(link);
link[0].click();
link.remove();
}

108
web/style.css Normal file
View file

@ -0,0 +1,108 @@
body {
font-family: Arial, sans-serif;
background-color: #222;
color: #fff;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
text-align: center;
margin: 20px 0;
}
.controls {
width: 90%;
max-width: 800px;
display: flex;
justify-content: space-between;
align-items: center;
margin: 20px 0;
}
.export {
background-color: #007bff;
color: #fff;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 5px;
flex-shrink: 0;
}
.export:hover {
background-color: #0056b3;
}
.search-bar {
flex-grow: 1;
margin-left: 20px;
padding: 10px;
border-radius: 5px;
border: 1px solid #444;
background-color: #333;
color: #fff;
font-size: 16px;
max-width: 100%;
}
table {
width: 90%;
max-width: 800px;
border-collapse: collapse;
margin: 0 auto 20px;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
background-color: #333;
}
th, td {
border: 1px solid #444;
padding: 12px;
text-align: left;
}
th {
cursor: pointer;
background-color: #007bff;
color: #fff;
}
th.sort-asc::after {
content: " \u2191";
color: #fff;
}
th.sort-desc::after {
content: " \u2193";
color: #fff;
}
tbody tr:nth-child(even) {
background-color: #444;
}
.pagination {
margin: 20px;
}
.pagination button {
background-color: #007bff;
color: #fff;
border: none;
padding: 8px 16px;
cursor: pointer;
border-radius: 5px;
margin: 0 5px;
}
.pagination button:hover {
background-color: #0056b3;
}
.pagination button:disabled {
background-color: #555;
cursor: not-allowed;
}
@media (max-width: 600px) {
th, td {
padding: 8px;
}
.export, .pagination button {
padding: 8px 12px;
}
.search-bar {
margin-left: 10px;
padding: 8px;
font-size: 14px;
}
}