mirror of
https://github.com/anotherhadi/iknowyou.git
synced 2026-04-12 00:47:26 +02:00
init
This commit is contained in:
30
front/src/pages/403.astro
Normal file
30
front/src/pages/403.astro
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
import Layout from "@src/layouts/Layout.astro";
|
||||
import { ShieldAlert, Home } from "@lucide/svelte";
|
||||
---
|
||||
|
||||
<Layout title="403 - Access Denied">
|
||||
<main
|
||||
class="flex flex-col items-center justify-center gap-6 px-4 text-center"
|
||||
>
|
||||
<div class="flex flex-col items-center mt-20">
|
||||
<ShieldAlert size={80} class="text-warning" />
|
||||
<h1 class="text-4xl font-black text-warning opacity-50 mb-10">403</h1>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-3xl font-bold logo-gradient italic">Access Denied</h2>
|
||||
<p class="opacity-60 max-w-xs mx-auto mt-2">
|
||||
You don't have the necessary clearance to access this sector of the app.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="badge badge-outline badge-warning font-mono text-xs p-3">
|
||||
ERROR_CODE: INSUFFICIENT_PERMISSIONS
|
||||
</div>
|
||||
|
||||
<a href="/" class="btn btn-soft btn-warning gap-2">
|
||||
<Home size={16} /> Return to Surface
|
||||
</a>
|
||||
</main>
|
||||
</Layout>
|
||||
26
front/src/pages/404.astro
Normal file
26
front/src/pages/404.astro
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
import Layout from "@src/layouts/Layout.astro";
|
||||
import { Ghost, Home } from "@lucide/svelte";
|
||||
---
|
||||
|
||||
<Layout title="404 - Page Not Found">
|
||||
<main
|
||||
class="flex flex-col items-center justify-center gap-6 px-4 text-center"
|
||||
>
|
||||
<div class="flex flex-col items-center mt-20">
|
||||
<Ghost size={80} class="text-primary" />
|
||||
<h1 class="text-4xl font-black text-primary opacity-50 mb-10">404</h1>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-3xl font-bold logo-gradient italic">Lost?</h2>
|
||||
<p class="opacity-60 max-w-xs mx-auto mt-2">
|
||||
The page you are looking for doesn't exist or has been moved.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a href="/" class="btn btn-soft btn-primary gap-2">
|
||||
<Home size={16} /> Back to Home
|
||||
</a>
|
||||
</main>
|
||||
</Layout>
|
||||
32
front/src/pages/500.astro
Normal file
32
front/src/pages/500.astro
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
import Layout from "@src/layouts/Layout.astro";
|
||||
import { AlertTriangle, RefreshCw } from "@lucide/svelte";
|
||||
---
|
||||
|
||||
<Layout title="500 - Server Error">
|
||||
<main
|
||||
class="flex flex-col items-center justify-center gap-6 px-4 text-center"
|
||||
>
|
||||
<div class="flex flex-col items-center mt-20">
|
||||
<AlertTriangle size={80} class="text-error" />
|
||||
<h1 class="text-4xl font-black text-error opacity-50 mb-10">500</h1>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="text-3xl font-bold logo-gradient italic">System Failure</h2>
|
||||
<p class="opacity-60 max-w-xs mx-auto mt-2">
|
||||
The server encountered an unexpected error.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4">
|
||||
<button
|
||||
onclick="window.location.reload()"
|
||||
class="btn btn-soft btn-error gap-2"
|
||||
>
|
||||
<RefreshCw size={16} /> Retry
|
||||
</button>
|
||||
<a href="/" class="btn btn-soft btn-primary">Go Home</a>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
126
front/src/pages/cheatsheets/[slug].astro
Normal file
126
front/src/pages/cheatsheets/[slug].astro
Normal file
@@ -0,0 +1,126 @@
|
||||
---
|
||||
import Layout from "@src/layouts/Layout.astro";
|
||||
import { getCollection, render } from "astro:content";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const sheets = await getCollection("cheatsheets");
|
||||
return sheets.map((sheet) => ({
|
||||
params: { slug: sheet.id },
|
||||
props: { sheet },
|
||||
}));
|
||||
}
|
||||
|
||||
const { sheet } = Astro.props;
|
||||
const { Content, headings } = await render(sheet);
|
||||
|
||||
const toc = headings.filter((h) => h.depth === 2 || h.depth === 3);
|
||||
---
|
||||
|
||||
<Layout title={`${sheet.data.title} - Cheatsheets`} description={sheet.data.description}>
|
||||
<div class="pb-4">
|
||||
|
||||
<div class="mb-6">
|
||||
<a href="/cheatsheets" class="btn btn-ghost btn-sm gap-1">← Cheatsheets</a>
|
||||
</div>
|
||||
|
||||
<div class="mb-8">
|
||||
<h1 class="text-2xl font-bold tracking-tight">{sheet.data.title}</h1>
|
||||
{sheet.data.description && (
|
||||
<p class="text-base-content/50 text-sm mt-1">{sheet.data.description}</p>
|
||||
)}
|
||||
{sheet.data.tags && sheet.data.tags.length > 0 && (
|
||||
<div class="flex gap-2 mt-3">
|
||||
{sheet.data.tags.map((tag) => (
|
||||
<a href={`/cheatsheets?tag=${tag}`} class="badge badge-ghost badge-sm">{tag}</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{toc.length > 0 && (
|
||||
<details class="lg:hidden mb-6 border border-base-300 rounded-lg">
|
||||
<summary class="cursor-pointer px-4 py-3 text-sm font-semibold select-none list-none flex items-center justify-between">
|
||||
<span>On this page</span>
|
||||
<svg class="size-4 opacity-50 details-chevron" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</summary>
|
||||
<nav class="px-4 pb-4 pt-1">
|
||||
<ul class="flex flex-col gap-1">
|
||||
{toc.map((h) => (
|
||||
<li style={h.depth === 3 ? "padding-left: 0.875rem" : ""}>
|
||||
<a href={`#${h.slug}`} class="toc-link text-sm text-base-content/60 hover:text-base-content transition-colors">
|
||||
{h.text}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</details>
|
||||
)}
|
||||
|
||||
<div class="flex gap-10 items-start">
|
||||
<div class="prose prose-sm max-w-none flex-1 min-w-0 break-words">
|
||||
<Content />
|
||||
</div>
|
||||
|
||||
{toc.length > 0 && (
|
||||
<aside class="hidden lg:block w-48 shrink-0">
|
||||
<nav class="sticky top-8">
|
||||
<p class="text-xs font-semibold uppercase tracking-wider text-base-content/40 mb-3">
|
||||
On this page
|
||||
</p>
|
||||
<ul class="flex flex-col gap-1.5">
|
||||
{toc.map((h) => (
|
||||
<li style={h.depth === 3 ? "padding-left: 0.75rem" : ""}>
|
||||
<a
|
||||
href={`#${h.slug}`}
|
||||
data-toc-link
|
||||
class="toc-link text-xs text-base-content/50 hover:text-base-content transition-colors leading-snug block"
|
||||
>
|
||||
{h.text}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
details[open] .details-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.details-chevron {
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.toc-link.active {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const links = document.querySelectorAll<HTMLAnchorElement>("[data-toc-link]");
|
||||
if (links.length > 0) {
|
||||
const headingEls = [...links].map((l) =>
|
||||
document.querySelector(decodeURIComponent(new URL(l.href).hash))
|
||||
);
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.isIntersecting) {
|
||||
const idx = headingEls.indexOf(entry.target);
|
||||
links.forEach((l) => l.classList.remove("active"));
|
||||
if (idx !== -1) links[idx].classList.add("active");
|
||||
}
|
||||
}
|
||||
},
|
||||
{ rootMargin: "-20% 0px -70% 0px" }
|
||||
);
|
||||
|
||||
headingEls.forEach((el) => el && observer.observe(el));
|
||||
}
|
||||
</script>
|
||||
33
front/src/pages/cheatsheets/index.astro
Normal file
33
front/src/pages/cheatsheets/index.astro
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
import Layout from "@src/layouts/Layout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import CheatsheetList from "@src/components/CheatsheetList.svelte";
|
||||
|
||||
const sheets = (await getCollection("cheatsheets"))
|
||||
.sort((a, b) => (a.data.order ?? 99) - (b.data.order ?? 99))
|
||||
.map((s) => ({
|
||||
id: s.id,
|
||||
title: s.data.title,
|
||||
description: s.data.description,
|
||||
tags: s.data.tags,
|
||||
}));
|
||||
---
|
||||
|
||||
<Layout title="Cheatsheets">
|
||||
<div class="max-w-3xl mx-auto px-4 pb-4">
|
||||
|
||||
<div class="mb-6">
|
||||
<a href="/" class="btn btn-ghost btn-sm gap-1">← Back</a>
|
||||
</div>
|
||||
|
||||
<div class="mb-8">
|
||||
<h1 class="text-2xl font-bold tracking-tight">OSINT Cheatsheets</h1>
|
||||
<p class="text-base-content/50 text-sm mt-1">
|
||||
Quick reference cards for common OSINT techniques.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<CheatsheetList {sheets} client:only="svelte" />
|
||||
|
||||
</div>
|
||||
</Layout>
|
||||
172
front/src/pages/help.astro
Normal file
172
front/src/pages/help.astro
Normal file
@@ -0,0 +1,172 @@
|
||||
---
|
||||
import Layout from "@src/layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout title="How it works">
|
||||
<div class="max-w-3xl mx-auto px-4 pb-4">
|
||||
|
||||
<div class="mb-6">
|
||||
<a href="/" class="btn btn-ghost btn-sm gap-1">← Back</a>
|
||||
</div>
|
||||
|
||||
<div class="mb-8">
|
||||
<h1 class="text-2xl font-bold tracking-tight">How it works</h1>
|
||||
<p class="text-base-content/50 text-sm mt-1">
|
||||
A guide to iknowyou: concepts, tools, profiles, and configuration.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-8">
|
||||
|
||||
<section class="flex flex-col gap-3">
|
||||
<h2 class="text-lg font-bold flex items-center gap-2">
|
||||
<span class="size-2 rounded-full bg-primary inline-block"></span>
|
||||
What is it?
|
||||
</h2>
|
||||
<p class="text-base-content/70 text-sm leading-relaxed">
|
||||
<strong>Iknowyou</strong> (IKY) is an OSINT aggregation platform. It runs multiple
|
||||
open-source intelligence tools against a target in parallel and presents the results
|
||||
in a unified interface. Targets can be email addresses, usernames, phone numbers, IP
|
||||
addresses, domains, and more.
|
||||
</p>
|
||||
<p class="text-base-content/70 text-sm leading-relaxed">
|
||||
Instead of running each tool manually, IKY handles orchestration, config management,
|
||||
and result rendering so you can focus on analysis.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="divider my-0"></div>
|
||||
|
||||
<section class="flex flex-col gap-3">
|
||||
<h2 class="text-lg font-bold flex items-center gap-2">
|
||||
<span class="size-2 rounded-full bg-primary inline-block"></span>
|
||||
Tools
|
||||
</h2>
|
||||
<p class="text-base-content/70 text-sm leading-relaxed">
|
||||
Each <strong>tool</strong> is a Go module that knows how to query one data source
|
||||
(a website, an API, a local binary...). Tools declare:
|
||||
</p>
|
||||
<ul class="list-disc list-inside text-base-content/70 text-sm leading-relaxed space-y-1 ml-2">
|
||||
<li>Which <strong>input types</strong> they accept (email, username, IP...)</li>
|
||||
<li>Optional <strong>configuration fields</strong> (API keys, options)</li>
|
||||
<li>Whether they require an <strong>external binary</strong> to be installed</li>
|
||||
</ul>
|
||||
<p class="text-base-content/70 text-sm leading-relaxed">
|
||||
The <a href="/tools" class="link link-primary">Tools page</a> shows all registered tools
|
||||
grouped by status:
|
||||
</p>
|
||||
<div class="flex flex-col gap-2 ml-2">
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<span class="size-2 rounded-full bg-success shrink-0"></span>
|
||||
<span><strong>Active</strong> - ready to run</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<span class="size-2 rounded-full bg-warning shrink-0"></span>
|
||||
<span><strong>Active: config missing</strong> - needs an API key or required field</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<span class="size-2 rounded-full bg-error shrink-0"></span>
|
||||
<span><strong>Active: unavailable</strong> - required binary not found on the system</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<span class="size-2 rounded-full bg-base-content/20 shrink-0"></span>
|
||||
<span><strong>Disabled</strong> - excluded by the selected profile</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="divider my-0"></div>
|
||||
|
||||
<section class="flex flex-col gap-3">
|
||||
<h2 class="text-lg font-bold flex items-center gap-2">
|
||||
<span class="size-2 rounded-full bg-primary inline-block"></span>
|
||||
Profiles
|
||||
</h2>
|
||||
<p class="text-base-content/70 text-sm leading-relaxed">
|
||||
A <strong>profile</strong> is a named search configuration. When you start a search,
|
||||
you pick which profile to use. Profiles control:
|
||||
</p>
|
||||
<ul class="list-disc list-inside text-base-content/70 text-sm leading-relaxed space-y-1 ml-2">
|
||||
<li><strong>Enabled list</strong> (whitelist): if set, only these tools run</li>
|
||||
<li><strong>Disabled list</strong> (blacklist): these tools are always skipped</li>
|
||||
<li><strong>Tool overrides</strong>: per-profile config that overrides global settings for specific tools</li>
|
||||
<li><strong>Notes</strong>: a description of what the profile is for</li>
|
||||
</ul>
|
||||
<p class="text-base-content/70 text-sm leading-relaxed">
|
||||
Two profiles are built-in and cannot be modified:
|
||||
</p>
|
||||
<div class="flex flex-col gap-2 ml-2">
|
||||
<div class="card bg-base-200 p-3 text-sm">
|
||||
<span class="font-mono font-bold">default</span>
|
||||
<p class="text-base-content/60 text-xs mt-1">
|
||||
All tools active with default settings. No restrictions.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card bg-base-200 p-3 text-sm">
|
||||
<span class="font-mono font-bold">hard</span>
|
||||
<p class="text-base-content/60 text-xs mt-1">
|
||||
Aggressive mode. All tools active, including those that may send
|
||||
notifications or leave traces at the target.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-base-content/70 text-sm leading-relaxed">
|
||||
You can create custom profiles on the <a href="/profiles" class="link link-primary">Profiles page</a>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="divider my-0"></div>
|
||||
|
||||
<section class="flex flex-col gap-3">
|
||||
<h2 class="text-lg font-bold flex items-center gap-2">
|
||||
<span class="size-2 rounded-full bg-primary inline-block"></span>
|
||||
Configuration & Overrides
|
||||
</h2>
|
||||
<p class="text-base-content/70 text-sm leading-relaxed">
|
||||
Tool configuration works in two layers:
|
||||
</p>
|
||||
<ol class="list-decimal list-inside text-base-content/70 text-sm leading-relaxed space-y-2 ml-2">
|
||||
<li>
|
||||
<strong>Global config</strong>: set on the
|
||||
<a href="/tools" class="link link-primary">Tools page</a>. Applied to every
|
||||
search regardless of profile.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Profile override</strong>: set inside a profile. Takes precedence over
|
||||
global config when that profile is used. Useful when you want a tool to behave
|
||||
differently in specific contexts (e.g. slower rate-limiting in a "quiet" profile).
|
||||
</li>
|
||||
</ol>
|
||||
<p class="text-base-content/70 text-sm leading-relaxed">
|
||||
Config is stored in <code class="font-mono bg-base-300 px-1 rounded text-xs">config.yaml</code>.
|
||||
Built-in profiles are hardcoded in Go and are never written to disk.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="divider my-0"></div>
|
||||
|
||||
<section class="flex flex-col gap-3">
|
||||
<h2 class="text-lg font-bold flex items-center gap-2">
|
||||
<span class="size-2 rounded-full bg-primary inline-block"></span>
|
||||
How a search runs
|
||||
</h2>
|
||||
<ol class="list-decimal list-inside text-base-content/70 text-sm leading-relaxed space-y-2 ml-2">
|
||||
<li>You enter a target, select its type (email, username...) and pick a profile.</li>
|
||||
<li>
|
||||
The backend filters tools by input type and the profile's enabled/disabled rules,
|
||||
then skips any tool with a missing required config field.
|
||||
</li>
|
||||
<li>All eligible tools run in parallel against the target.</li>
|
||||
<li>
|
||||
The frontend polls for results and renders them progressively as each tool finishes.
|
||||
</li>
|
||||
</ol>
|
||||
<p class="text-base-content/70 text-sm leading-relaxed">
|
||||
A search can be cancelled at any time from the results page.
|
||||
Completed searches are kept in memory.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
19
front/src/pages/index.astro
Normal file
19
front/src/pages/index.astro
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
import Layout from "@src/layouts/Layout.astro";
|
||||
import HomePage from "@src/components/HomePage.svelte";
|
||||
---
|
||||
<Layout title="iknowyou">
|
||||
<div class="max-w-3xl mx-auto px-4 py-6 flex flex-col gap-10">
|
||||
<header class="text-center">
|
||||
<img src="/logo.svg" class="m-auto w-36" alt="iknowyou" />
|
||||
<h1 class="font-unbounded logo-gradient text-6xl tracking-[-0.11em] leading-normal mb-2 mt-0">i know you</h1>
|
||||
<p class="text-base-content/50 text-sm max-w-xl mx-auto">
|
||||
Centralizing your OSINT tools in one place.<br/>
|
||||
<strong>Iknowyou</strong> is a self-hosted OSINT (Open-Source Intelligence) platform that centralises reconnaissance tools
|
||||
into a single reactive web interface. Instead of juggling terminals, browser tabs, and disconnected CLI tools, you type a target once and get results in real time.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<HomePage client:only="svelte" />
|
||||
</div>
|
||||
</Layout>
|
||||
21
front/src/pages/profiles.astro
Normal file
21
front/src/pages/profiles.astro
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
import Layout from "@src/layouts/Layout.astro";
|
||||
import ProfileSettings from "@src/components/ProfileSettings.svelte";
|
||||
---
|
||||
|
||||
<Layout title="Profiles">
|
||||
<div class="max-w-4xl mx-auto px-4 pb-4">
|
||||
<div class="mb-6">
|
||||
<a href="/" class="btn btn-ghost btn-sm gap-1">← Back</a>
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<h1 class="text-xl font-bold tracking-tight">Profiles</h1>
|
||||
<p class="text-base-content/50 text-sm mt-1">
|
||||
Manage search profiles: allowed/blocked tools and per-tool config overrides.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ProfileSettings client:only="svelte" />
|
||||
</div>
|
||||
</Layout>
|
||||
22
front/src/pages/search/[id].astro
Normal file
22
front/src/pages/search/[id].astro
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
import Layout from "@src/layouts/Layout.astro";
|
||||
import SearchDetail from "@src/components/SearchDetail.svelte";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return [{ params: { id: "_" } }];
|
||||
}
|
||||
|
||||
// Shell page — SearchDetail reads the real ID from window.location.
|
||||
const id = null;
|
||||
---
|
||||
<Layout title="Search">
|
||||
<div class="max-w-4xl mx-auto px-4 pb-4">
|
||||
|
||||
<div class="mb-6">
|
||||
<a href="/" class="btn btn-ghost btn-sm gap-1">← Back</a>
|
||||
</div>
|
||||
|
||||
<SearchDetail {id} client:only="svelte" />
|
||||
|
||||
</div>
|
||||
</Layout>
|
||||
22
front/src/pages/tools/[name].astro
Normal file
22
front/src/pages/tools/[name].astro
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
import Layout from "@src/layouts/Layout.astro";
|
||||
import ToolDetail from "@src/components/ToolDetail.svelte";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return [{ params: { name: "_" } }];
|
||||
}
|
||||
|
||||
// Shell page — ToolDetail reads the real name from window.location.
|
||||
const name = null;
|
||||
---
|
||||
<Layout title="Tool">
|
||||
<div class="max-w-3xl mx-auto px-4 pb-4">
|
||||
|
||||
<div class="mb-6">
|
||||
<a href="/tools" class="btn btn-ghost btn-sm gap-1">← Tools</a>
|
||||
</div>
|
||||
|
||||
<ToolDetail {name} client:only="svelte" />
|
||||
|
||||
</div>
|
||||
</Layout>
|
||||
20
front/src/pages/tools/index.astro
Normal file
20
front/src/pages/tools/index.astro
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
import Layout from "@src/layouts/Layout.astro";
|
||||
import ToolList from "@src/components/ToolList.svelte";
|
||||
---
|
||||
<Layout title="Tools">
|
||||
<div class="max-w-4xl mx-auto px-4 pb-4">
|
||||
|
||||
<div class="mb-6">
|
||||
<a href="/" class="btn btn-ghost btn-sm gap-1">← Back</a>
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<h1 class="text-xl font-bold tracking-tight">Tools</h1>
|
||||
<p class="text-base-content/50 text-sm mt-1">All registered OSINT tools.</p>
|
||||
</div>
|
||||
|
||||
<ToolList client:only="svelte" />
|
||||
|
||||
</div>
|
||||
</Layout>
|
||||
Reference in New Issue
Block a user