Copy all dataleaks information button

Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
Hadi
2025-10-04 18:22:53 +02:00
parent 6d23bc3ee6
commit e7a9f9a0fe
2 changed files with 116 additions and 36 deletions

View File

@@ -1,8 +1,10 @@
<script lang="ts">
import type { Dataleak } from "$src/lib/types";
import { Info, Search } from "@lucide/svelte";
import { Clipboard, Search } from "@lucide/svelte";
import FaviconOrIcon from "../../favicon-or-icon.svelte";
import DatawellPopup from "./datawell-popup.svelte";
import { serverUrl, serverPassword } from "$src/lib/stores/server";
import axios from "axios";
let {
dataleaks,
@@ -20,37 +22,90 @@
let paginatedDataleaks = $state<Dataleak[]>([]);
let totalPages = $state(0);
  function sortByModTime(arr: Dataleak[]): Dataleak[] {
    return arr.slice().sort((a, b) => {
      return new Date(b.ModTime).getTime() - new Date(a.ModTime).getTime();
    });
  }
let copyText = $state("Copy to clipboard");
  $effect(() => {
    const sortedData = sortByModTime(dataleaks);
async function copyDataleaksInformation(withSample: boolean) {
if (!filteredDataleaks || filteredDataleaks.length === 0) {
copyText = "No dataleaks to copy";
return;
}
    if (filter.trim() === "") {
      filteredDataleaks = sortedData;
    } else {
      const lowerFilter = filter.toLowerCase();
      filteredDataleaks = sortedData.filter((item) =>
        item.Name.toLowerCase().includes(lowerFilter),
      );
    }
    page = 1;
  });
let fullText = "";
  $effect(() => {
    if (filteredDataleaks) {
      totalPages = Math.ceil(filteredDataleaks.length / perPage);
      const start = (page - 1) * perPage;
      const end = start + perPage;
      paginatedDataleaks = filteredDataleaks.slice(start, end);
      if (page > totalPages) {
        page = totalPages > 0 ? totalPages : 1;
      }
    }
  });
for (const dataleak of filteredDataleaks) {
fullText += `Name: ${dataleak.Name}\n`;
fullText += `Length: ${dataleak.Length.toLocaleString()}\n`;
if (withSample) {
await axios
.get(`${$serverUrl}/dataleak/sample`, {
params: { path: dataleak.Path },
headers: { "X-Password": $serverPassword },
})
.then((r) => {
const samples: string[][] = r.data.Sample;
if (samples && samples.length > 0) {
fullText += "Sample:\n";
const headers = samples[0];
const rows = samples.slice(1);
fullText += headers.join(", ") + "\n";
for (const row of rows) {
fullText += row.join(", ") + "\n";
}
}
})
.catch((err) => {
console.error("Failed to fetch sample for", dataleak.Name, err);
fullText += "Sample: [Failed to fetch]\n";
});
}
fullText += "\n";
}
try {
await navigator.clipboard.writeText(fullText);
copyText = "Copied!";
setTimeout(() => (copyText = "Copy to clipboard"), 2000);
} catch (err) {
console.error("Failed to copy:", err);
copyText = "Copy failed";
setTimeout(() => (copyText = "Copy to clipboard"), 2000);
}
}
function sortByModTime(arr: Dataleak[]): Dataleak[] {
return arr.slice().sort((a, b) => {
return new Date(b.ModTime).getTime() - new Date(a.ModTime).getTime();
});
}
$effect(() => {
const sortedData = sortByModTime(dataleaks);
if (filter.trim() === "") {
filteredDataleaks = sortedData;
} else {
const lowerFilter = filter.toLowerCase();
filteredDataleaks = sortedData.filter((item) =>
item.Name.toLowerCase().includes(lowerFilter),
);
}
page = 1;
});
$effect(() => {
if (filteredDataleaks) {
totalPages = Math.ceil(filteredDataleaks.length / perPage);
const start = (page - 1) * perPage;
const end = start + perPage;
paginatedDataleaks = filteredDataleaks.slice(start, end);
if (page > totalPages) {
page = totalPages > 0 ? totalPages : 1;
}
}
});
function getDomain(dataleakName: string) {
const firstPart = dataleakName.split(" ")[0].toLowerCase();
@@ -76,11 +131,36 @@
</script>
<div class="my-4 flex flex-col gap-2">
<label class="input input-xs w-full">
<div class="flex items-center gap-1">
<label class="input input-xs w-full grow">
<Search size={12} />
<input class="grow" placeholder="Filter" bind:value={filter} />
</label>
<div class="dropdown dropdown-end">
<div
tabindex="0"
role="button"
class="btn btn-ghost btn-sm size-6 btn-square m-1"
>
<Clipboard size={12} />
</div>
<div
class="dropdown-content bg-base-300 rounded-box z-1 w-52 p-2 shadow-2xl grid gap-2"
>
<button
class="btn btn-xs"
onclick={() => copyDataleaksInformation(false)}>{copyText}</button
>
<button
class="btn btn-xs"
onclick={() => copyDataleaksInformation(true)}
>{copyText} (w/ sample)</button
>
</div>
</div>
</div>
<div class="overflow-x-auto">
<table class="table">
<!-- head -->