mirror of
				https://github.com/skidoodle/iphistory.git
				synced 2025-02-15 08:29:16 +01:00 
			
		
		
		
	chore: Update Dockerfile and main.go for serving web content from the 'web' directory
This commit is contained in:
		| @@ -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"] | ||||
|   | ||||
							
								
								
									
										221
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										221
									
								
								index.html
									
									
									
									
									
								
							| @@ -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">« Prev</button> | ||||
|         <button onclick="nextPage()" id="nextBtn">Next »</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> | ||||
							
								
								
									
										4
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								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() | ||||
|   | ||||
							
								
								
									
										31
									
								
								web/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								web/index.html
									
									
									
									
									
										Normal 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>« Prev</button> | ||||
|         <button onclick="nextPage()" id="nextBtn" disabled>Next »</button> | ||||
|     </div> | ||||
|     <script src="script.js"></script> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										75
									
								
								web/script.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								web/script.js
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										108
									
								
								web/style.css
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user