mirror of
https://github.com/skidoodle/iphistory.git
synced 2025-02-15 08:29:16 +01:00
chore: better json and ui
This commit is contained in:
parent
55cef1457c
commit
da96cec13e
4 changed files with 66 additions and 31 deletions
28
main.go
28
main.go
|
@ -19,6 +19,11 @@ const (
|
|||
retryDelay = 10 * time.Second
|
||||
)
|
||||
|
||||
type IPRecord struct {
|
||||
Timestamp string `json:"timestamp"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
}
|
||||
|
||||
func getPublicIP() (string, error) {
|
||||
var ip string
|
||||
var err error
|
||||
|
@ -54,24 +59,31 @@ func fetchIP() (string, error) {
|
|||
return "", fmt.Errorf("IP address not found")
|
||||
}
|
||||
|
||||
func readHistory() ([]string, error) {
|
||||
func readHistory() ([]IPRecord, error) {
|
||||
file, err := os.Open(ipHistoryPath)
|
||||
if os.IsNotExist(err) {
|
||||
return []string{}, nil
|
||||
return []IPRecord{}, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var history []string
|
||||
var history []IPRecord
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
history = append(history, scanner.Text())
|
||||
parts := strings.Split(scanner.Text(), " - ")
|
||||
if len(parts) == 2 {
|
||||
record := IPRecord{
|
||||
Timestamp: parts[0],
|
||||
IPAddress: parts[1],
|
||||
}
|
||||
history = append(history, record)
|
||||
}
|
||||
}
|
||||
return history, scanner.Err()
|
||||
}
|
||||
|
||||
func writeHistory(history []string) error {
|
||||
func writeHistory(history []IPRecord) error {
|
||||
file, err := os.Create(ipHistoryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -80,7 +92,7 @@ func writeHistory(history []string) error {
|
|||
|
||||
writer := bufio.NewWriter(file)
|
||||
for _, entry := range history {
|
||||
_, err := writer.WriteString(entry + "\n")
|
||||
_, err := writer.WriteString(fmt.Sprintf("%s - %s\n", entry.Timestamp, entry.IPAddress))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -98,8 +110,8 @@ func trackIP(ipChan <-chan string) {
|
|||
}
|
||||
|
||||
entry := fmt.Sprintf("%s - %s", time.Now().Format("2006-01-02 15:04:05 MST"), ip)
|
||||
if len(history) == 0 || !strings.Contains(history[0], ip) {
|
||||
history = append([]string{entry}, history...)
|
||||
if len(history) == 0 || !strings.Contains(history[0].IPAddress, ip) {
|
||||
history = append([]IPRecord{{Timestamp: time.Now().Format("2006-01-02 15:04:05 MST"), IPAddress: ip}}, history...)
|
||||
if err := writeHistory(history); err != nil {
|
||||
fmt.Println("Error writing IP history:", err)
|
||||
}
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
<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>
|
||||
<script src="script.js"></script>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>IP History</h1>
|
||||
<h1><a href="/">IP History</a></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">
|
||||
|
@ -26,6 +28,5 @@
|
|||
<button onclick="prevPage()" id="prevBtn" disabled>« Prev</button>
|
||||
<button onclick="nextPage()" id="nextBtn" disabled>Next »</button>
|
||||
</div>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
let ipHistory = [], filteredHistory = [], currentPage = 1, entriesPerPage = 10;
|
||||
let ipHistory = [],
|
||||
filteredHistory = [],
|
||||
currentPage = 1,
|
||||
entriesPerPage = 10;
|
||||
|
||||
$(document).ready(function() {
|
||||
$.getJSON('/history', function(data) {
|
||||
ipHistory = data.map(entry => entry.split(" - "));
|
||||
ipHistory = data;
|
||||
filteredHistory = ipHistory;
|
||||
displayTable();
|
||||
}).fail(function() {
|
||||
|
@ -11,7 +14,7 @@ $(document).ready(function() {
|
|||
|
||||
$('#searchBar').on('input', function() {
|
||||
const query = $(this).val().toLowerCase();
|
||||
filteredHistory = ipHistory.filter(row => row.some(cell => cell.toLowerCase().includes(query)));
|
||||
filteredHistory = ipHistory.filter(entry => entry.timestamp.toLowerCase().includes(query) || entry.ip_address.toLowerCase().includes(query));
|
||||
currentPage = 1;
|
||||
displayTable();
|
||||
});
|
||||
|
@ -23,11 +26,10 @@ function displayTable() {
|
|||
const end = start + entriesPerPage;
|
||||
const currentEntries = filteredHistory.slice(start, end);
|
||||
|
||||
currentEntries.forEach(row => {
|
||||
currentEntries.forEach(entry => {
|
||||
const tr = $('<tr>');
|
||||
row.forEach(cell => {
|
||||
tr.append($('<td>').text(cell));
|
||||
});
|
||||
tr.append($('<td>').text(entry.timestamp));
|
||||
tr.append($('<td>').text(entry.ip_address));
|
||||
tbody.append(tr);
|
||||
});
|
||||
updatePaginationButtons();
|
||||
|
@ -53,21 +55,27 @@ function prevPage() {
|
|||
}
|
||||
|
||||
function sortTable() {
|
||||
const th = $('#ipTable th').eq(0);
|
||||
const isAscending = !th.hasClass('sort-asc');
|
||||
const isAscending = $('#ipTable th').eq(0).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);
|
||||
filteredHistory.sort((a, b) => (a.timestamp.localeCompare(b.timestamp)) * orderModifier);
|
||||
|
||||
$('#ipTable th').removeClass('sort-asc sort-desc');
|
||||
$('#ipTable th').eq(0).addClass(isAscending ? 'sort-desc' : 'sort-asc');
|
||||
|
||||
displayTable();
|
||||
}
|
||||
|
||||
function exportToCSV() {
|
||||
const rows = [['Timestamp', 'IP Address'], ...filteredHistory];
|
||||
const rows = [
|
||||
['Timestamp', 'IP Address'], ...filteredHistory.map(entry => [entry.timestamp, entry.ip_address])
|
||||
];
|
||||
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'});
|
||||
const link = $('<a>').attr({
|
||||
href: encodedUri,
|
||||
download: 'ip_history.csv'
|
||||
});
|
||||
|
||||
$('body').append(link);
|
||||
link[0].click();
|
||||
|
|
|
@ -12,6 +12,13 @@ h1 {
|
|||
text-align: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
h1 a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
h1 a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.controls {
|
||||
width: 90%;
|
||||
max-width: 800px;
|
||||
|
@ -20,14 +27,20 @@ h1 {
|
|||
align-items: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.export, .search-bar {
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #444;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.export {
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
flex-shrink: 0;
|
||||
text-align: center;
|
||||
border: none;
|
||||
}
|
||||
.export:hover {
|
||||
background-color: #0056b3;
|
||||
|
@ -35,13 +48,13 @@ h1 {
|
|||
.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%;
|
||||
border-color: #444;
|
||||
}
|
||||
.search-bar:focus {
|
||||
outline: none;
|
||||
border-color: #007bff;
|
||||
}
|
||||
table {
|
||||
width: 90%;
|
||||
|
@ -62,6 +75,7 @@ th {
|
|||
cursor: pointer;
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
th.sort-asc::after {
|
||||
content: " \u2191";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue