Fix stats reporting. Add Vite compatibility

This commit is contained in:
2025-11-04 16:16:50 +01:00
parent aa29de9029
commit e54dc2dbd7
21 changed files with 1060 additions and 361 deletions

View File

@@ -1,22 +1,21 @@
<script>
import { onDestroy, onMount } from "svelte";
<script lang="ts">
import { onMount } from "svelte";
import { formatDataVolume, formatThousands, formatDate, formatNumber, formatDuration } from "util/Formatting";
import Chart from "util/Chart.svelte";
import { color_by_name } from "util/Util";
import ServerDiagnostics from "./ServerDiagnostics.svelte";
import PeerTable from "./PeerTable.svelte";
import { get_endpoint } from "lib/PixeldrainAPI";
let graphViews = $state()
let graphBandwidth = $state()
let graphTimeout = null
let graphEgress: Chart = $state()
let graphDownloads: Chart = $state()
let graphTimeout: NodeJS.Timeout = null
let start_time = $state("")
let end_time = $state("")
let total_bandwidth = $state(0)
let total_bandwidth_paid = $state(0)
let total_views = $state(0)
let total_egress = $state(0)
let total_downloads = $state(0)
const loadGraph = (minutes, interval, live) => {
const loadGraph = (minutes: number, interval: number, live: boolean) => {
if (graphTimeout !== null) { clearTimeout(graphTimeout) }
if (live) {
graphTimeout = setTimeout(() => { loadGraph(minutes, interval, true) }, 10000)
@@ -27,7 +26,7 @@ const loadGraph = (minutes, interval, live) => {
start.setMinutes(start.getMinutes() - minutes)
fetch(
window.api_endpoint + "/admin/files/timeseries" +
get_endpoint() + "/admin/files/timeseries" +
"?start=" + start.toISOString() +
"&end=" + today.toISOString() +
"&interval=" + interval
@@ -35,37 +34,34 @@ const loadGraph = (minutes, interval, live) => {
if (!resp.ok) { return Promise.reject("Error: " + resp.status); }
return resp.json();
}).then(resp => {
resp.views.timestamps.forEach((val, idx) => {
resp.egress.timestamps.forEach((val, idx) => {
let date = new Date(val);
let dateStr = date.getFullYear();
let dateStr: string = date.getFullYear().toString();
dateStr += "-" + ("00" + (date.getMonth() + 1)).slice(-2);
dateStr += "-" + ("00" + date.getDate()).slice(-2);
dateStr += " " + ("00" + date.getHours()).slice(-2);
dateStr += ":" + ("00" + date.getMinutes()).slice(-2);
resp.views.timestamps[idx] = " " + dateStr + " "; // Poor man's padding
resp.egress.timestamps[idx] = " " + dateStr + " "; // Poor man's padding
});
graphViews.data().labels = resp.views.timestamps;
graphViews.data().datasets[0].data = resp.views.amounts;
graphViews.data().datasets[1].data = resp.downloads.amounts;
graphBandwidth.data().labels = resp.views.timestamps;
graphBandwidth.data().datasets[0].data = resp.bandwidth.amounts
graphBandwidth.data().datasets[1].data = resp.bandwidth_paid.amounts
graphViews.update()
graphBandwidth.update()
graphEgress.data().labels = resp.egress.timestamps;
graphEgress.data().datasets[0].data = resp.egress.amounts;
graphDownloads.data().labels = resp.downloads.timestamps;
graphDownloads.data().datasets[0].data = resp.downloads.amounts
graphEgress.update()
graphDownloads.update()
start_time = resp.views.timestamps[0]
end_time = resp.views.timestamps.slice(-1)[0];
total_bandwidth = resp.bandwidth.amounts.reduce((acc, val) => acc + val)
total_bandwidth_paid = resp.bandwidth_paid.amounts.reduce((acc, val) => acc + val)
total_views = resp.views.amounts.reduce((acc, val) => acc + val)
start_time = resp.egress.timestamps[0]
end_time = resp.egress.timestamps.slice(-1)[0];
total_egress = resp.egress.amounts.reduce((acc, val) => acc + val)
total_downloads = resp.downloads.amounts.reduce((acc, val) => acc + val)
})
}
// Load performance statistics
let lastOrder = $state();
let lastOrder: string = $state();
let status = $state({
pid: 0,
cpu_profile_running_since: "",
db_latency: 0,
db_time: "",
@@ -74,33 +70,32 @@ let status = $state({
local_read_size_per_sec: 0,
local_reads: 0,
local_reads_per_sec: 0,
local_readers: 0,
neighbour_read_size: 0,
neighbour_read_size_per_sec: 0,
neighbour_reads: 0,
neighbour_reads_per_sec: 0,
peers: [],
query_statistics: [],
neighbour_readers: 0,
remote_read_size: 0,
remote_read_size_per_sec: 0,
remote_reads: 0,
remote_reads_per_sec: 0,
remote_readers: 0,
peers: [],
query_statistics: [],
running_since: "",
stats_watcher_listeners: 0,
stats_watcher_threads: 0,
filesystem_watcher_listeners: 0,
filesystem_watcher_threads: 0,
rate_limit_watcher_threads: 0,
rate_limit_watcher_listeners: 0,
download_clients: 0,
download_connections: 0,
})
let total_reads = $derived(status.local_reads + status.neighbour_reads + status.remote_reads)
let total_read_size = $derived(status.local_read_size + status.neighbour_read_size + status.remote_read_size)
function getStats(order) {
function getStats(order: string) {
lastOrder = order
fetch(window.api_endpoint + "/status").then(
fetch(get_endpoint() + "/status").then(
resp => resp.json()
).then(resp => {
// Sort all queries by the selected sort column
@@ -126,37 +121,25 @@ function getStats(order) {
let statsInterval = null
onMount(() => {
// Prepare chart datasets
graphBandwidth.data().datasets = [
graphEgress.data().datasets = [
{
label: "Bandwidth (free)",
label: "Egress",
data: [],
borderWidth: 2,
pointRadius: 0,
borderColor: color_by_name("highlight_color"),
backgroundColor: color_by_name("highlight_color"),
},
{
label: "Bandwidth (premium)",
borderWidth: 2,
pointRadius: 0,
borderColor: color_by_name("danger_color"),
backgroundColor: color_by_name("danger_color"),
},
];
graphViews.data().datasets = [
graphDownloads.data().datasets = [
{
label: "Views",
label: "Downloads",
data: [],
borderWidth: 2,
pointRadius: 0,
borderColor: color_by_name("highlight_color"),
backgroundColor: color_by_name("highlight_color"),
},
{
label: "Downloads",
borderWidth: 2,
pointRadius: 0,
borderColor: color_by_name("danger_color"),
backgroundColor: color_by_name("danger_color"),
},
];
loadGraph(10080, 10, true);
@@ -164,13 +147,14 @@ onMount(() => {
statsInterval = setInterval(() => {
getStats(lastOrder)
}, 10000)
})
onDestroy(() => {
if (graphTimeout !== null) {
clearTimeout(graphTimeout)
}
if (statsInterval !== null) {
clearInterval(statsInterval)
return () => {
if (graphTimeout !== null) {
clearTimeout(graphTimeout)
}
if (statsInterval !== null) {
clearInterval(statsInterval)
}
}
})
</script>
@@ -188,14 +172,12 @@ onDestroy(() => {
<button onclick={() => loadGraph(1051200, 1440, false)}>Two Years 1d</button>
<button onclick={() => loadGraph(2628000, 1440, false)}>Five Years 1d</button>
</div>
<Chart bind:this={graphBandwidth} data_type="bytes" />
<Chart bind:this={graphViews} data_type="number" />
<Chart bind:this={graphEgress} data_type="bytes" />
<Chart bind:this={graphDownloads} data_type="number" />
<div class="highlight_border">
Total usage from {start_time} to {end_time}<br/>
{formatDataVolume(total_bandwidth, 3)} bandwidth,
{formatDataVolume(total_bandwidth_paid, 3)} paid bandwidth,
{formatThousands(total_views, 3)} views and
{formatThousands(total_downloads, 3)} downloads
{formatDataVolume(total_egress, 3)} egress,
{formatThousands(total_downloads)} downloads
</div>
<br/>
@@ -222,6 +204,7 @@ onDestroy(() => {
<thead>
<tr>
<td>Source</td>
<td>Current</td>
<td>Reads</td>
<td>Reads %</td>
<td>Reads / s</td>
@@ -233,6 +216,7 @@ onDestroy(() => {
<tbody>
<tr>
<td>Local cache</td>
<td>{status.local_readers}</td>
<td>{status.local_reads}</td>
<td>{((status.local_reads / total_reads) * 100).toPrecision(3)}%</td>
<td>{status.local_reads_per_sec.toPrecision(4)}/s</td>
@@ -242,6 +226,7 @@ onDestroy(() => {
</tr>
<tr>
<td>Neighbour</td>
<td>{status.neighbour_readers}</td>
<td>{status.neighbour_reads}</td>
<td>{((status.neighbour_reads / total_reads) * 100).toPrecision(3)}%</td>
<td>{status.neighbour_reads_per_sec.toPrecision(4)}/s</td>
@@ -251,6 +236,7 @@ onDestroy(() => {
</tr>
<tr>
<td>Reed-solomon</td>
<td>{status.remote_readers}</td>
<td>{status.remote_reads}</td>
<td>{((status.remote_reads / total_reads) * 100).toPrecision(3)}%</td>
<td>{status.remote_reads_per_sec.toPrecision(4)}/s</td>
@@ -258,6 +244,16 @@ onDestroy(() => {
<td>{((status.remote_read_size / total_read_size) * 100).toPrecision(3)}%</td>
<td>{formatDataVolume(status.remote_read_size_per_sec, 4)}/s</td>
</tr>
<tr>
<td>Total</td>
<td>{status.local_readers+status.neighbour_readers+status.remote_readers}</td>
<td>{status.local_reads+status.neighbour_reads+status.remote_reads}</td>
<td></td>
<td>{(status.local_reads_per_sec+status.neighbour_reads_per_sec+status.remote_reads_per_sec).toPrecision(4)}/s</td>
<td>{formatDataVolume(status.local_read_size+status.neighbour_read_size+status.remote_read_size, 4)}</td>
<td></td>
<td>{formatDataVolume(status.local_read_size_per_sec+status.neighbour_read_size_per_sec+status.remote_read_size_per_sec, 4)}/s</td>
</tr>
</tbody>
</table>
</div>
@@ -282,12 +278,6 @@ onDestroy(() => {
<td>{status.filesystem_watcher_listeners}</td>
<td>{(status.filesystem_watcher_listeners / status.filesystem_watcher_threads).toPrecision(3)}</td>
</tr>
<tr>
<td>Downloads (per IP)</td>
<td>{status.download_clients}</td>
<td>{status.download_connections}</td>
<td>{(status.download_connections / status.download_clients).toPrecision(3)}</td>
</tr>
</tbody>
</table>

View File

@@ -3,7 +3,7 @@ import { preventDefault, stopPropagation } from 'svelte/legacy';
import { onMount } from "svelte";
import { formatDate } from "util/Formatting";
import Expandable from "util/Expandable.svelte";
import { loading_finish } from "lib/Loading";
import { loading_finish, loading_start } from "lib/Loading";
const abuse_types = [
"copyright",

View File

@@ -1,11 +1,39 @@
<script lang="ts">
import { run } from 'svelte/legacy';
import { flip } from "svelte/animate";
import { formatDataVolume } from "util/Formatting";
import SortButton from "layout/SortButton.svelte";
let { peers = $bindable([]) } = $props();
let update_peers = (peers) => {
let {
peers = $bindable([])
}: {
peers: {
id: string
ip: string
port: number
hostname: string
role: string
reachable: boolean
unreachable_count: number
latency: number
last_seen: string
free_space: number
min_free_space: number
load_1_min: number
load_5_min: number
load_15_min: number
avg_network_tx: number
avg_network_rx: number
port_speed: number
cache_threshold: number
// Our props
avg_network_total?: number
usage_percent?: number
network_ratio?: number
}[]
} = $props();
$effect(() => {
for (let peer of peers) {
peer.avg_network_total = peer.avg_network_tx + peer.avg_network_rx
peer.usage_percent = (peer.avg_network_tx / peer.port_speed) * 100
@@ -13,11 +41,11 @@ let update_peers = (peers) => {
}
sort("")
}
})
let sort_field = $state("hostname")
let asc = $state(true)
let sort = (field) => {
let sort = (field: string) => {
if (field !== "" && field === sort_field) {
asc = !asc
}
@@ -26,7 +54,7 @@ let sort = (field) => {
}
sort_field = field
console.log("sorting by", field, "asc", asc)
console.log("sorting by", field, "asc", $state.snapshot(asc))
peers.sort((a, b) => {
if (typeof (a[field]) === "number") {
// Sort ints from high to low
@@ -46,9 +74,6 @@ let sort = (field) => {
})
peers = peers
}
run(() => {
update_peers(peers)
});
</script>
<div class="table_scroll">
@@ -84,7 +109,7 @@ run(() => {
<td>{(peer.latency/1000).toFixed(3)}</td>
<td>{formatDataVolume(peer.avg_network_tx, 3)}/s</td>
<td>{formatDataVolume(peer.avg_network_rx, 3)}/s</td>
<td>{peer.network_ratio.toFixed(2)}</td>
<td>{peer.network_ratio === undefined ? "" : peer.network_ratio.toFixed(2)}</td>
<td>{formatDataVolume(peer.avg_network_total, 3)}/s</td>
<td>{Math.round(peer.usage_percent)}%</td>
<td>{formatDataVolume(peer.cache_threshold, 3)}</td>