Copy all dataleaks information button
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Dataleak } from "$src/lib/types";
|
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 FaviconOrIcon from "../../favicon-or-icon.svelte";
|
||||||
import DatawellPopup from "./datawell-popup.svelte";
|
import DatawellPopup from "./datawell-popup.svelte";
|
||||||
|
import { serverUrl, serverPassword } from "$src/lib/stores/server";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
dataleaks,
|
dataleaks,
|
||||||
@@ -20,37 +22,90 @@
|
|||||||
let paginatedDataleaks = $state<Dataleak[]>([]);
|
let paginatedDataleaks = $state<Dataleak[]>([]);
|
||||||
let totalPages = $state(0);
|
let totalPages = $state(0);
|
||||||
|
|
||||||
function sortByModTime(arr: Dataleak[]): Dataleak[] {
|
let copyText = $state("Copy to clipboard");
|
||||||
return arr.slice().sort((a, b) => {
|
|
||||||
return new Date(b.ModTime).getTime() - new Date(a.ModTime).getTime();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$effect(() => {
|
async function copyDataleaksInformation(withSample: boolean) {
|
||||||
const sortedData = sortByModTime(dataleaks);
|
if (!filteredDataleaks || filteredDataleaks.length === 0) {
|
||||||
|
copyText = "No dataleaks to copy";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (filter.trim() === "") {
|
let fullText = "";
|
||||||
filteredDataleaks = sortedData;
|
|
||||||
} else {
|
|
||||||
const lowerFilter = filter.toLowerCase();
|
|
||||||
filteredDataleaks = sortedData.filter((item) =>
|
|
||||||
item.Name.toLowerCase().includes(lowerFilter),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
page = 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
$effect(() => {
|
for (const dataleak of filteredDataleaks) {
|
||||||
if (filteredDataleaks) {
|
fullText += `Name: ${dataleak.Name}\n`;
|
||||||
totalPages = Math.ceil(filteredDataleaks.length / perPage);
|
fullText += `Length: ${dataleak.Length.toLocaleString()}\n`;
|
||||||
const start = (page - 1) * perPage;
|
|
||||||
const end = start + perPage;
|
if (withSample) {
|
||||||
paginatedDataleaks = filteredDataleaks.slice(start, end);
|
await axios
|
||||||
if (page > totalPages) {
|
.get(`${$serverUrl}/dataleak/sample`, {
|
||||||
page = totalPages > 0 ? totalPages : 1;
|
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) {
|
function getDomain(dataleakName: string) {
|
||||||
const firstPart = dataleakName.split(" ")[0].toLowerCase();
|
const firstPart = dataleakName.split(" ")[0].toLowerCase();
|
||||||
@@ -76,10 +131,35 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="my-4 flex flex-col gap-2">
|
<div class="my-4 flex flex-col gap-2">
|
||||||
<label class="input input-xs w-full">
|
<div class="flex items-center gap-1">
|
||||||
<Search size={12} />
|
<label class="input input-xs w-full grow">
|
||||||
<input class="grow" placeholder="Filter" bind:value={filter} />
|
<Search size={12} />
|
||||||
</label>
|
<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">
|
<div class="overflow-x-auto">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
@@ -109,7 +189,7 @@
|
|||||||
/>
|
/>
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<DatawellPopup dataleak={item}/>
|
<DatawellPopup dataleak={item} />
|
||||||
</th>
|
</th>
|
||||||
<td>{item.Length.toLocaleString("fr")}</td>
|
<td>{item.Length.toLocaleString("fr")}</td>
|
||||||
{#if showColumns}
|
{#if showColumns}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Datawells from "$src/lib/components/index/search/datawells.svelte";
|
import Datawells from "$src/lib/components/index/search/datawells.svelte";
|
||||||
import Stats from "$src/lib/components/index/search/stats.svelte";
|
import Stats from "$src/lib/components/index/search/stats.svelte";
|
||||||
|
|
||||||
import { serverPassword, serverUrl } from "$src/lib/stores/server";
|
import { serverPassword, serverUrl } from "$src/lib/stores/server";
|
||||||
import type { Server } from "$src/lib/types";
|
import type { Server } from "$src/lib/types";
|
||||||
|
|||||||
Reference in New Issue
Block a user