mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-04-26 23:49:59 -07:00
better controls, formatting, etc
This commit is contained in:
@@ -2,13 +2,12 @@
|
||||
import { AnalysisStatus } from "$lib/analysisManager.svelte";
|
||||
import { EventType } from "$lib/analysis.svelte";
|
||||
import type { ManifestEntry } from "$lib/manifest.svelte";
|
||||
let { entry, analysis_status }: {
|
||||
let { entry }: {
|
||||
entry: ManifestEntry,
|
||||
analysis_status: AnalysisStatus | undefined,
|
||||
} = $props();
|
||||
|
||||
let summary = $derived.by(() => {
|
||||
if (analysis_status === AnalysisStatus.Queued) {
|
||||
if (entry.analysis_status === AnalysisStatus.Queued) {
|
||||
return 'Queued...';
|
||||
} else if (entry.analysis_status === AnalysisStatus.Running) {
|
||||
return 'Running...';
|
||||
|
||||
23
bin/web/src/lib/components/ControlBar.svelte
Normal file
23
bin/web/src/lib/components/ControlBar.svelte
Normal file
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { req } from "$lib/utils.svelte";
|
||||
import DeleteButton from "./DeleteButton.svelte";
|
||||
import RecordingControls from "./RecordingControls.svelte";
|
||||
let { server_is_recording }: {
|
||||
server_is_recording: boolean;
|
||||
} = $props();
|
||||
|
||||
function confirmDelete() {
|
||||
if (window.confirm(`Permanently delete ALL entries?`)) {
|
||||
req('POST', '/api/delete-all-recordings')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-row gap-2">
|
||||
<RecordingControls {server_is_recording} />
|
||||
<DeleteButton
|
||||
text="Delete ALL Entries"
|
||||
prompt={`Are you sure you want to delete ALL entries?`}
|
||||
url={`/api/delete-all-recordings`}
|
||||
/>
|
||||
</div>
|
||||
28
bin/web/src/lib/components/DeleteButton.svelte
Normal file
28
bin/web/src/lib/components/DeleteButton.svelte
Normal file
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { ManifestEntry } from "$lib/manifest.svelte";
|
||||
import { req } from "$lib/utils.svelte";
|
||||
let { text, url, prompt }: {
|
||||
text?: string,
|
||||
url: string,
|
||||
prompt: string,
|
||||
} = $props();
|
||||
|
||||
function confirmDelete() {
|
||||
if (window.confirm(prompt)) {
|
||||
req('POST', url)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<button class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-md flex flex-row" onclick={confirmDelete} aria-label="delete">
|
||||
<p>{text}</p>
|
||||
<svg
|
||||
style="width:24px;height:24px"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="hsl(200, 40%, 20%)"
|
||||
d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
@@ -3,12 +3,14 @@
|
||||
url: string;
|
||||
text: string;
|
||||
} = $props();
|
||||
|
||||
function download() {
|
||||
window.location.href = url;
|
||||
}
|
||||
</script>
|
||||
|
||||
<a href={url}>📥 {text}</a>
|
||||
|
||||
<style>
|
||||
a {
|
||||
@apply underline text-blue-400;
|
||||
}
|
||||
</style>
|
||||
<button class="text-blue-400 flex flex-row underline" onclick={download}>
|
||||
{text} <svg class="fill-current w-4 h-4 m-1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M13 8V2H7v6H2l8 8 8-8h-5zM0 18h20v2H0v-2z"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<table class="table-auto border">
|
||||
<thead class="p-2">
|
||||
<tr class="bg-gray-300 p-2 m-2">
|
||||
<tr class="bg-gray-300">
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Date Started</th>
|
||||
<th scope="col">Date of Last Message</th>
|
||||
@@ -18,6 +18,7 @@
|
||||
<th scope="col">PCAP</th>
|
||||
<th scope="col">QMDL</th>
|
||||
<th scope="col">Analysis Result</th>
|
||||
<th scope="col">Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@@ -1,23 +1,37 @@
|
||||
<script lang="ts">
|
||||
import { ManifestEntry } from "$lib/manifest.svelte";
|
||||
import DownloadLink from '$lib/components/DownloadLink.svelte';
|
||||
import DeleteButton from "$lib/components/DeleteButton.svelte";
|
||||
import AnalysisStatus from "./AnalysisStatus.svelte";
|
||||
let { entry, current }: {
|
||||
entry: ManifestEntry;
|
||||
current: boolean;
|
||||
} = $props();
|
||||
|
||||
// bg-gray-100
|
||||
// bg-green-300
|
||||
// passing `undefined` as the locale uses the browser default
|
||||
const date_formatter = new Intl.DateTimeFormat(undefined, {
|
||||
timeStyle: "long",
|
||||
dateStyle: "short",
|
||||
});
|
||||
let row_color = current ? "bg-green-300" : "even:bg-gray-100";
|
||||
</script>
|
||||
|
||||
<tr class="{row_color} border-b">
|
||||
<th class="font-bold p-2 border-b bg-blue-100" scope='row'>{entry.name}</th>
|
||||
<td class="p-2">{entry.start_time}</td>
|
||||
<td class="p-2">{entry.last_message_time}</td>
|
||||
<th class="font-bold p-2 bg-blue-100" scope='row'>{entry.name}</th>
|
||||
<td class="p-2">{date_formatter.format(entry.start_time)}</td>
|
||||
<td class="p-2">{date_formatter.format(entry.last_message_time)}</td>
|
||||
<td class="p-2">{entry.qmdl_size_bytes}</td>
|
||||
<td class="p-2"><DownloadLink url={entry.getPcapUrl()} text="pcap" /></td>
|
||||
<td class="p-2"><DownloadLink url={entry.getQmdlUrl()} text="qmdl" /></td>
|
||||
<td class="p-2"><AnalysisStatus analysis_status={entry.analysis_status} entry={entry} /></td>
|
||||
<td class="p-2"><AnalysisStatus entry={entry} /></td>
|
||||
{#if current}
|
||||
<td class="p-2"></td>
|
||||
{:else}
|
||||
<td class="p-2">
|
||||
<DeleteButton
|
||||
prompt={`Are you sure you want to delete entry ${entry.name}?`}
|
||||
url={entry.getDeleteUrl()}
|
||||
/>
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { req } from "$lib/utils.svelte";
|
||||
let { server_is_recording: currently_recording }: {
|
||||
let { server_is_recording }: {
|
||||
server_is_recording: boolean;
|
||||
} = $props();
|
||||
|
||||
let client_set_recording = $state(currently_recording);
|
||||
let waiting_for_server = $derived(client_set_recording !== currently_recording);
|
||||
let client_set_recording = $state(server_is_recording);
|
||||
let waiting_for_server = $derived(client_set_recording !== server_is_recording);
|
||||
|
||||
async function start_recording() {
|
||||
await req('POST', '/api/start-recording');
|
||||
@@ -17,16 +17,16 @@
|
||||
client_set_recording = false;
|
||||
}
|
||||
|
||||
const stop_recording_classes = "bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-full";
|
||||
const start_recording_classes = "bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full";
|
||||
const stop_recording_classes = "bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-md";
|
||||
const start_recording_classes = "bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-md";
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if waiting_for_server}
|
||||
<button class={currently_recording ? stop_recording_classes : start_recording_classes}>
|
||||
{currently_recording ? "Stopping..." : "Starting..."}
|
||||
<button class={server_is_recording ? stop_recording_classes : start_recording_classes}>
|
||||
{server_is_recording ? "Stopping..." : "Starting..."}
|
||||
</button>
|
||||
{:else if currently_recording}
|
||||
{:else if server_is_recording}
|
||||
<button class={stop_recording_classes} onclick={stop_recording}>Stop Recording</button>
|
||||
{:else}
|
||||
<button class={start_recording_classes} onclick={start_recording}>Start Recording</button>
|
||||
|
||||
31
bin/web/src/lib/components/SystemStatsTable.svelte
Normal file
31
bin/web/src/lib/components/SystemStatsTable.svelte
Normal file
@@ -0,0 +1,31 @@
|
||||
<script lang="ts">
|
||||
import { type SystemStats } from "$lib/systemStats";
|
||||
let { stats }: {
|
||||
stats: SystemStats;
|
||||
} = $props();
|
||||
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<p class="text-xl">System Stats</p>
|
||||
<table class="table-auto border">
|
||||
<tbody>
|
||||
<tr class="border">
|
||||
<th class="border">
|
||||
Storage
|
||||
</th>
|
||||
<td class="border">
|
||||
{stats.disk_stats.used_percent} used ({stats.disk_stats.used_size} / {stats.disk_stats.available_size})
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border-b">
|
||||
<th class="border">
|
||||
Memory (RAM)
|
||||
</th>
|
||||
<td class="border">
|
||||
Free: {stats.memory_stats.free}, Used: {stats.memory_stats.used}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -80,4 +80,8 @@ export class ManifestEntry {
|
||||
getAnalysisReportUrl(): string {
|
||||
return `/api/analysis-report/${this.name}`;
|
||||
}
|
||||
|
||||
getDeleteUrl(): string {
|
||||
return `/api/delete-recording/${this.name}`;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user