<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>this.env Logs</title>
<style>
body {
margin: 0;
padding: 0;
background: #1e1e1e;
font-family: 'Roboto', sans-serif;
color: #eee;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
min-height: 100vh;
padding-top: 2px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1 {
color: #a3a1a1;
margin: 12px 1% 18px;
font-weight: 500;
font-size: 18px;
align-self: flex-start;
font-family: 'Roboto', sans-serif;
}
table {
width: 99%;
border-collapse: collapse;
background-color: #2d2d2d;
border-radius: 1px;
overflow: hidden;
}
th, td {
padding: 10px 14px;
font-size: 14px;
border-bottom: 1px solid #444;
text-align: left;
}
th {
background-color: #222;
font-weight: 600;
color: #cfcfcf;
}
tr:hover {
background-color: #3a3a3a;
}
td {
color: #d5d5d5;
}
td.timestamp {
color: #999;
}
td.decision-approve {
color: rgb(0, 154, 137);
font-weight: bold;
}
td.decision-block {
color: rgb(200, 80, 80);
font-weight: bold;
}
td.decision-pending {
color: rgb(210, 180, 70);
font-weight: 500;
}
td.path-cell {
max-width: 140px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
position: relative;
}
</style>
</head>
<body>
<nav style="width: 90%; padding: 6px 5%; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #444;">
<div style="display: flex; align-items: center;">
<img src="https://neurons.me/media/neurons-grey.png" alt="neurons logo" style="height: 34px; margin-right: 12px;">
<span style="font-size: 16px; font-weight: bold; color: white;">this.env</span>
</div>
<div style="display: flex; gap: 20px;">
<a href="/__env/" style="color: #cfcfcf; text-decoration: none; font-weight: 500; display: flex; align-items: center;">
<i data-feather="home" style="margin-right: 6px;"></i>
Home
</a>
<a href="/__env/docs" style="color: #cfcfcf; text-decoration: none; font-weight: 500; display: flex; align-items: center;">
<i data-feather="book" style="margin-right: 6px;"></i>
Docs
</a>
<a href="/__env/status" style="color: #cfcfcf; text-decoration: none; font-weight: 500; display: flex; align-items: center;">
<i data-feather="activity" style="margin-right: 6px;"></i>
Status
</a>
</div>
</nav>
<h1>Request Logs</h1>
<div style="width: 99%; display: flex; justify-content: flex-end; margin-bottom: 8px;">
<button id="delete-selected" style="background: none; border: none; cursor: pointer;" title="Delete Selected">
<i data-feather="trash-2" style="color: #ccc; width: 20px; height: 20px;"></i>
</button>
</div>
<table id="log-table">
<thead>
<tr>
<th><input type="checkbox" id="select-all-checkbox" /></th>
<th>Timestamp</th>
<th>Domain</th>
<th>Path</th>
<th>Decision</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script>
let offset = 0;
const limit = 50;
let loading = false;
function loadLogs(append = false) {
if (loading) return;
loading = true;
fetch(`/__env/log-history?limit=${limit}&offset=${offset}`)
.then(res => res.json())
.then(data => {
const tbody = document.querySelector('#log-table tbody');
if (!append) tbody.innerHTML = '';
if (Array.isArray(data)) {
data.forEach(log => {
const row = document.createElement('tr');
const decisionClass = log.decision === "Approved"
? "decision-approve"
: log.decision === "Blocked"
? "decision-block"
: "decision-pending";
row.innerHTML = `
<td><input type="checkbox" class="log-checkbox" /></td>
<td class="timestamp">${log.timestamp}</td>
<td>${log.domain}</td>
<td class="path-cell" title="${log.path}">${log.path}</td>
<td class="${decisionClass}">${log.decision}</td>
<td>${log.reason}</td>
`;
tbody.appendChild(row);
});
offset += data.length;
}
})
.catch(err => {
console.error(err);
})
.finally(() => {
loading = false;
});
}
function onScroll() {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = document.documentElement.clientHeight;
if (scrollTop + clientHeight >= scrollHeight - 100) {
loadLogs(true);
}
}
loadLogs();
window.addEventListener('scroll', onScroll);
setInterval(() => {
fetch(`/__env/log-history?limit=${limit}&offset=0`)
.then(res => res.json())
.then(data => {
const tbody = document.querySelector('#log-table tbody');
if (Array.isArray(data) && data.length > 0) {
const existingTimestamps = Array.from(tbody.children).map(row => row.children[1].textContent);
data.reverse().forEach(log => {
if (!existingTimestamps.includes(log.timestamp)) {
const row = document.createElement('tr');
const decisionClass = log.decision === "Approved"
? "decision-approve"
: log.decision === "Blocked"
? "decision-block"
: "decision-pending";
row.innerHTML = `
<td><input type="checkbox" class="log-checkbox" /></td>
<td class="timestamp">${log.timestamp}</td>
<td>${log.domain}</td>
<td class="path-cell" title="${log.path}">${log.path}</td>
<td class="${decisionClass}">${log.decision}</td>
<td>${log.reason}</td>
`;
tbody.insertBefore(row, tbody.firstChild);
offset++; }
});
}
})
.catch(err => console.error(err));
}, 1250);
document.getElementById('select-all-checkbox').addEventListener('change', (e) => {
const checked = e.target.checked;
const checkboxes = document.querySelectorAll('.log-checkbox');
checkboxes.forEach(checkbox => checkbox.checked = checked);
});
document.getElementById('delete-selected').addEventListener('click', () => {
const checkedRows = document.querySelectorAll('.log-checkbox:checked');
checkedRows.forEach(row => {
const tr = row.closest('tr');
tr.remove();
});
document.getElementById('select-all-checkbox').checked = false;
});
</script>
<script src="https://unpkg.com/feather-icons"></script>
<script>feather.replace();</script>
</body>
</html>