Add button to remove peers

This commit is contained in:
2026-08-04 14:38:36 +02:00
parent 196845d739
commit b02e88eb1b
2 changed files with 45 additions and 3 deletions

View File

@@ -2,6 +2,8 @@
import { flip } from "svelte/animate"; import { flip } from "svelte/animate";
import { formatDataVolume } from "util/Formatting"; import { formatDataVolume } from "util/Formatting";
import SortButton from "layout/SortButton.svelte"; import SortButton from "layout/SortButton.svelte";
import { loading_run } from "lib/Loading";
import { admin_decommission_node } from "lib/AdminAPI";
let { let {
peers = $bindable([]) peers = $bindable([])
@@ -33,6 +35,11 @@ let {
}[] }[]
} = $props(); } = $props();
// If not all peers are online we show the decommission button
let all_reachable = $derived(peers.reduce((acc, val) => {
return val.reachable ? acc : false
}, true))
$effect(() => { $effect(() => {
for (let peer of peers) { for (let peer of peers) {
peer.avg_network_total = peer.avg_network_tx + peer.avg_network_rx peer.avg_network_total = peer.avg_network_tx + peer.avg_network_rx
@@ -74,6 +81,16 @@ let sort = (field: string) => {
}) })
peers = peers peers = peers
} }
const decommission = (id: string) => {
loading_run(async () => {
try {
await admin_decommission_node(id)
} catch (err) {
alert(JSON.stringify(err))
}
})
}
</script> </script>
<div class="table_scroll"> <div class="table_scroll">
@@ -92,16 +109,17 @@ let sort = (field: string) => {
<td><SortButton field="cache_threshold" active_field={sort_field} asc={asc} sort_func={sort}>CThresh</SortButton></td> <td><SortButton field="cache_threshold" active_field={sort_field} asc={asc} sort_func={sort}>CThresh</SortButton></td>
<td><SortButton field="free_space" active_field={sort_field} asc={asc} sort_func={sort}>Free</SortButton></td> <td><SortButton field="free_space" active_field={sort_field} asc={asc} sort_func={sort}>Free</SortButton></td>
<td><SortButton field="min_free_space" active_field={sort_field} asc={asc} sort_func={sort}>Min free</SortButton></td> <td><SortButton field="min_free_space" active_field={sort_field} asc={asc} sort_func={sort}>Min free</SortButton></td>
{#if !all_reachable}<td></td>{/if}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#each peers as peer (peer.ip)} {#each peers as peer (peer.id)}
<tr style="border: none;" <tr style="border: none;"
class:highlight_red={!peer.reachable} class:highlight_red={!peer.reachable}
class:highlight_yellow={peer.free_space < peer.min_free_space / 2} class:highlight_yellow={peer.free_space < peer.min_free_space / 2}
class:highlight_blue={peer.free_space < peer.min_free_space} class:highlight_blue={peer.free_space < peer.min_free_space}
class:highlight_green={peer.reachable} class:highlight_green={peer.reachable}
animate:flip={{duration: 1000}} animate:flip={{duration: 500}}
> >
<td>{peer.hostname}</td> <td>{peer.hostname}</td>
<td>{peer.unreachable_count}</td> <td>{peer.unreachable_count}</td>
@@ -115,6 +133,13 @@ let sort = (field: string) => {
<td>{formatDataVolume(peer.cache_threshold, 3)}</td> <td>{formatDataVolume(peer.cache_threshold, 3)}</td>
<td>{formatDataVolume(peer.free_space, 3)}</td> <td>{formatDataVolume(peer.free_space, 3)}</td>
<td>{formatDataVolume(peer.min_free_space, 3)}</td> <td>{formatDataVolume(peer.min_free_space, 3)}</td>
{#if !all_reachable}
<td>
<button class="button flat delete" onclick={() => decommission(peer.id)}>
<i class="icon small">delete</i>
</button>
</td>
{/if}
</tr> </tr>
{/each} {/each}
@@ -135,6 +160,7 @@ let sort = (field: string) => {
<td>{formatDataVolume(peers.reduce((acc, val) => acc += val.cache_threshold, 0), 4)}</td> <td>{formatDataVolume(peers.reduce((acc, val) => acc += val.cache_threshold, 0), 4)}</td>
<td>{formatDataVolume(peers.reduce((acc, val) => acc += val.free_space, 0), 4)}</td> <td>{formatDataVolume(peers.reduce((acc, val) => acc += val.free_space, 0), 4)}</td>
<td>{formatDataVolume(peers.reduce((acc, val) => acc += val.min_free_space, 0), 3)}</td> <td>{formatDataVolume(peers.reduce((acc, val) => acc += val.min_free_space, 0), 3)}</td>
{#if !all_reachable}<td></td>{/if}
</tr> </tr>
<tr> <tr>
<td>Average</td> <td>Average</td>
@@ -153,6 +179,7 @@ let sort = (field: string) => {
<td>{formatDataVolume(peers.reduce((acc, val) => acc += val.cache_threshold, 0) / peers.length, 4)}</td> <td>{formatDataVolume(peers.reduce((acc, val) => acc += val.cache_threshold, 0) / peers.length, 4)}</td>
<td>{formatDataVolume(peers.reduce((acc, val) => acc += val.free_space, 0) / peers.length, 4)}</td> <td>{formatDataVolume(peers.reduce((acc, val) => acc += val.free_space, 0) / peers.length, 4)}</td>
<td>{formatDataVolume(peers.reduce((acc, val) => acc += val.min_free_space, 0) / peers.length, 3)}</td> <td>{formatDataVolume(peers.reduce((acc, val) => acc += val.min_free_space, 0) / peers.length, 3)}</td>
{#if !all_reachable}<td></td>{/if}
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -167,4 +194,7 @@ let sort = (field: string) => {
tr { tr {
text-align: initial; text-align: initial;
} }
.delete {
margin: 0;
}
</style> </style>

View File

@@ -1,5 +1,5 @@
import { countries } from "country-data-list" import { countries } from "country-data-list"
import { check_response, get_endpoint } from "./NovaAPI" import { check_response, get_endpoint, type GenericResponse } from "./NovaAPI"
export const country_name = (country: string) => { export const country_name = (country: string) => {
if (country !== "" && countries[country] !== undefined) { if (country !== "" && countries[country] !== undefined) {
@@ -27,3 +27,15 @@ export const get_admin_invoices = async (year: number, month: number) => {
) )
) as Invoice[] ) as Invoice[]
}; };
export const admin_decommission_node = async (peer_id: string) => {
let form = new FormData()
form.set("peer_id", peer_id)
return await check_response(
await fetch(
get_endpoint() + "/admin/decommission_node",
{ method: "POST", body: form }
)
) as GenericResponse
};