diff --git a/Dockerfile b/Dockerfile
index a4e436a..65ad5be 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -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"]
diff --git a/index.html b/index.html
deleted file mode 100644
index 4669123..0000000
--- a/index.html
+++ /dev/null
@@ -1,221 +0,0 @@
-
-
-
-
-
- IP History
-
-
-
-
- IP History
-
-
-
- Timestamp |
- IP Address |
-
-
-
-
-
-
-
-
-
-
diff --git a/main.go b/main.go
index aadae04..0e3086e 100644
--- a/main.go
+++ b/main.go
@@ -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()
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..6a3b13d
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+ IP History
+
+
+
+
+ IP History
+
+
+
+
+
+
+
+ Timestamp |
+ IP Address |
+
+
+
+
+
+
+
+
diff --git a/web/script.js b/web/script.js
new file mode 100644
index 0000000..2e0f41b
--- /dev/null
+++ b/web/script.js
@@ -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 = $('');
+ row.forEach(cell => {
+ tr.append($('').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 = $('').attr({href: encodedUri, download: 'ip_history.csv'});
+
+ $('body').append(link);
+ link[0].click();
+ link.remove();
+}
diff --git a/web/style.css b/web/style.css
new file mode 100644
index 0000000..c5edc1f
--- /dev/null
+++ b/web/style.css
@@ -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;
+ }
+}
|