This commit is contained in:
Hadi
2026-03-07 14:23:16 +01:00
commit 2aa7a0abf1
777 changed files with 20307 additions and 0 deletions

30
src/pages/403.astro Normal file
View File

@@ -0,0 +1,30 @@
---
import Layout from "../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
src/pages/404.astro Normal file
View File

@@ -0,0 +1,26 @@
---
import Layout from "../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
src/pages/500.astro Normal file
View File

@@ -0,0 +1,32 @@
---
import Layout from "../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>

175
src/pages/api-docs.astro Normal file
View File

@@ -0,0 +1,175 @@
---
import Layout from "../layouts/Layout.astro";
import { Terminal, Settings, Database, Info, Code } from "lucide-svelte";
---
<Layout title="API Documentation">
<main class="max-w-4xl mx-auto p-6 space-y-12 my-10">
<header class="space-y-4 border-b border-white/5 pb-8">
<h1
class="text-5xl font-black uppercase tracking-tighter flex items-center gap-4 text-primary"
>
<Terminal size={48} />
<span class="logo-gradient">API Docs</span>
</h1>
<p class="text-xl opacity-70">
Integrate our community-driven default credentials database into your
own security tools.
</p>
</header>
<section class="space-y-4">
<h2
class="text-2xl font-bold italic text-primary flex items-center gap-2"
>
<Code size={24} />Base Endpoint
</h2>
<div
class="mockup-code bg-base-300 border border-white/5 shadow-2xl before:content-none"
>
<pre
data-prefix="GET"
class="px-5"><code>/api/search?q=&#123;query&#125;</code></pre>
</div>
</section>
<section class="space-y-4">
<h2
class="text-2xl font-bold italic text-secondary flex items-center gap-2"
>
<Settings size={24} /> Query Parameters
</h2>
<div
class="overflow-x-auto bg-base-200 rounded-box border border-white/5 shadow-lg"
>
<table class="table w-full">
<thead>
<tr class="text-secondary opacity-60 uppercase text-xs">
<th>Parameter</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr class="hover:bg-white/5 transition-colors">
<td class="font-mono text-primary font-bold">q</td>
<td><span class="badge badge-outline badge-sm">string</span></td>
<td class="opacity-50">-</td>
<td
>Target name or tag (e.g. <code class="text-accent"
>"admin"</code
>, <code class="text-accent">"ubnt"</code>)</td
>
</tr>
<tr class="hover:bg-white/5 transition-colors">
<td class="font-mono text-primary font-bold">page</td>
<td><span class="badge badge-outline badge-sm">number</span></td>
<td class="text-secondary">1</td>
<td>Page number for pagination results.</td>
</tr>
<tr class="hover:bg-white/5 transition-colors">
<td class="font-mono text-primary font-bold">size</td>
<td><span class="badge badge-outline badge-sm">number</span></td>
<td class="text-secondary">10</td>
<td
>Results per page <span class="text-xs opacity-50"
>(Min: 1, Max: 20)</span
></td
>
</tr>
</tbody>
</table>
</div>
</section>
<section class="space-y-6">
<h2 class="text-2xl font-bold italic text-accent flex items-center gap-2">
<Terminal size={24} /> Usage Examples
</h2>
<div class="space-y-2">
<p class="text-sm font-mono opacity-50">
// 1. Basic search for cisco admin panels
</p>
<div
class="mockup-code bg-base-300 border border-white/5 shadow-xl group"
>
<pre
data-prefix="$"><code>curl -X GET "https://default-creds.hadi.diy/api/search?q=cisco"</code></pre>
<pre
data-prefix=">"
class="text-success"><code>HTTP/1.1 200 OK</code></pre>
</div>
</div>
<div class="space-y-2 pt-4">
<p class="text-sm font-mono opacity-50">
// 2. Specific pagination request
</p>
<div class="mockup-code bg-base-300 border border-white/5 shadow-xl">
<pre
data-prefix="$"><code>curl -G "https://default-creds.hadi.diy/api/search" \
-d "q=cisco" \
-d "page=2" \
-d "size=5"</code></pre>
</div>
</div>
</section>
<section class="space-y-4">
<h2
class="text-2xl font-bold italic text-primary flex items-center gap-2"
>
<Database size={24} /> JSON Response Structure
</h2>
<div class="mockup-code bg-base-300 border border-white/5 shadow-inner">
<pre
class="text-xs lg:text-sm"><code>&#123;
"results": [
&#123;
"manufacturer": "Ubiquiti",
"name": "UniFi",
"icon": "ubnt-icon",
"tags": ["network", "router"],
"version": "all",
"user": "ubnt",
"pass": "ubnt"
&#125;
...
],
"pagination": &#123;
"totalResults": 42,
"totalPages": 5,
"currentPage": 1,
"pageSize": 10
&#125;
&#125;</code></pre>
</div>
</section>
<div class="alert bg-base-300 border border-primary/20 shadow-lg mt-10">
<div class="flex items-center gap-4">
<div class="bg-primary/10 p-3 rounded-full">
<Info class="text-primary" size={24} />
</div>
<div>
<h3
class="font-bold text-primary italic uppercase text-xs tracking-widest"
>
Rate Limiting & Ethics
</h3>
<p class="text-xs opacity-70 mt-1 leading-relaxed">
This API is provided for educational and authorized penetration
testing only. Excessive requests may lead to temporary IP
blacklisting. You can found the full database on Github. Contribute
to the database on <a
href="https://github.com/anotherhadi/default-creds/blob/main/CONTRIBUTING.md"
class="link link-primary">GitHub</a
>.
</p>
</div>
</div>
</div>
</main>
</Layout>

109
src/pages/api/search.ts Normal file
View File

@@ -0,0 +1,109 @@
import type { APIRoute } from "astro";
import fs from "node:fs";
import path from "node:path";
import yaml from "js-yaml";
export interface CredentialEntry {
manufacturer: string;
name: string;
icon: string;
tags: string[];
version: string;
user: string;
pass: string;
comment: string;
searchStr: string;
}
let cachedData: CredentialEntry[] | null = null;
export function getAllData(): CredentialEntry[] {
if (cachedData) return cachedData;
const dataDir = path.join(process.cwd(), "src", "data");
if (!fs.existsSync(dataDir)) {
console.warn(`Data directory not found at ${dataDir}`);
return [];
}
const files = fs.readdirSync(dataDir);
const allResults: CredentialEntry[] = [];
for (const file of files) {
if (file.endsWith(".yaml") || file.endsWith(".yml")) {
try {
const content = fs.readFileSync(path.join(dataDir, file), "utf8");
const doc = yaml.load(content) as any;
if (!doc || !doc.entries || !Array.isArray(doc.entries)) continue;
const manufacturerTags = doc.tags || [];
const manufacturerName = doc.name || "Unknown";
const manufacturerIcon = doc.icon || "terminal";
for (const entry of doc.entries) {
allResults.push({
manufacturer: manufacturerName,
name: entry.name || "Generic Device",
icon: manufacturerIcon,
tags: manufacturerTags,
version: entry.version || "all",
user: String(entry.user ?? ""),
pass: String(entry.pass ?? ""),
comment: entry.comment || "",
searchStr:
`${manufacturerName} ${entry.name} ${entry.comment} ${manufacturerTags.join(" ")}`.toLowerCase(),
});
}
} catch (e) {
console.error(`Error parsing ${file}:`, e);
}
}
}
cachedData = allResults;
return allResults;
}
export const GET: APIRoute = async ({ url }) => {
const query = url.searchParams.get("q")?.trim().toLowerCase() || "";
const page = Math.max(1, parseInt(url.searchParams.get("page") || "1"));
const size = Math.min(
50,
Math.max(1, parseInt(url.searchParams.get("size") || "10")),
);
const allEntries = getAllData();
let filtered = allEntries;
if (query) {
const queryTokens = query.split(/\s+/).filter((t) => t.length > 0);
filtered = allEntries.filter((entry) =>
queryTokens.every((token) => entry.searchStr.includes(token)),
);
}
const totalResults = filtered.length;
const totalPages = Math.ceil(totalResults / size);
const start = (page - 1) * size;
return new Response(
JSON.stringify({
results: filtered.slice(start, start + size),
pagination: {
totalResults,
totalPages,
currentPage: page,
pageSize: size,
},
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=3600",
},
},
);
};

31
src/pages/api/stats.ts Normal file
View File

@@ -0,0 +1,31 @@
import type { APIRoute } from "astro";
import { getAllData } from "./search";
export const GET: APIRoute = async () => {
try {
const allEntries = getAllData();
const uniqueManufacturers = new Set(
allEntries.map((entry) => entry.manufacturer),
);
return new Response(
JSON.stringify({
totalApps: uniqueManufacturers.size,
totalPasswords: allEntries.length,
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=3600",
},
},
);
} catch (error) {
console.error("Error generating stats:", error);
return new Response(JSON.stringify({ error: "Internal Server Error" }), {
status: 500,
});
}
};

22
src/pages/index.astro Normal file
View File

@@ -0,0 +1,22 @@
---
import Layout from "../layouts/Layout.astro";
import SearchApp from "../components/SearchApp.svelte";
---
<Layout>
<img src="/logo.svg" class="m-auto size-28" />
<header class="text-center mb-12">
<h2 class="text-md tracking-widest uppercase mt-5 mb-4">
Open-source Default Credentials Database
</h2>
<p class="max-w-xl text-center m-auto">
Find default credentials for many devices and software. <a
href="https://github.com/anotherhadi/default-creds/blob/main/CONTRIBUTING.md"
class="text-primary">Contribute</a
> to the database by submitting new credentials or updating existing ones.
</p>
</header>
<SearchApp client:load />
</Layout>