This commit is contained in:
Hadi
2026-01-01 19:06:29 +01:00
commit 8a4ca97c40
48 changed files with 3519 additions and 0 deletions

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

@@ -0,0 +1,52 @@
---
import Layout from "../layouts/Layout.astro";
import { House, FolderOpen } from "@lucide/astro";
---
<Layout
title="403 - Forbidden | Another Hadi"
description="You don't have permission to access this page."
>
<div class="min-h-screen flex items-center justify-center px-4 py-20">
<div class="text-center max-w-2xl mx-auto">
<!-- Large 403 Number -->
<h1 class="text-9xl font-bold text-primary mb-4 animate-pulse">403</h1>
<!-- Error Title -->
<h2 class="text-4xl font-bold mb-4">🥀 Forbidden</h2>
<!-- Error Description -->
<p class="text-xl mb-8 text-base-content/70">
Oops! You don't have permission to access this page. Let's get you back
on track.
</p>
<!-- Divider -->
<div class="divider"></div>
<!-- Action Buttons -->
<div class="flex gap-4 justify-center flex-wrap mt-8">
<a href="/" class="btn btn-primary gap-2">
<House class="size-5" />
Go Home
</a>
<a href="/#projects" class="btn btn-outline gap-2">
<FolderOpen class="size-5" />
View Projects
</a>
</div>
<!-- Helpful Links -->
<div class="flex justify-center gap-2 mt-12">
<p class="text-sm text-base-content/60 mb-4">
You might be looking for:
</p>
<div class="flex gap-3 justify-center flex-wrap text-sm">
<a href="/blog" class="link link-hover">Blog</a>
<span class="text-base-content/30">•</span>
<a href="/#about" class="link link-hover">About</a>
</div>
</div>
</div>
</div>
</Layout>

52
src/pages/404.astro Normal file
View File

@@ -0,0 +1,52 @@
---
import Layout from "../layouts/Layout.astro";
import { House, FolderOpen } from "@lucide/astro";
---
<Layout
title="404 - Page Not Found | Another Hadi"
description="The page you're looking for doesn't exist."
>
<div class="min-h-screen flex items-center justify-center px-4 py-20">
<div class="text-center max-w-2xl mx-auto">
<!-- Large 404 Number -->
<h1 class="text-9xl font-bold text-primary mb-4 animate-pulse">404</h1>
<!-- Error Title -->
<h2 class="text-4xl font-bold mb-4">🥀 Page Not Found</h2>
<!-- Error Description -->
<p class="text-xl mb-8 text-base-content/70">
Oops! The page you're looking for doesn't exist or has been moved. Let's
get you back on track.
</p>
<!-- Divider -->
<div class="divider"></div>
<!-- Action Buttons -->
<div class="flex gap-4 justify-center flex-wrap mt-8">
<a href="/" class="btn btn-primary gap-2">
<House class="size-5" />
Go Home
</a>
<a href="/#projects" class="btn btn-outline gap-2">
<FolderOpen class="size-5" />
View Projects
</a>
</div>
<!-- Helpful Links -->
<div class="flex justify-center gap-2 mt-12">
<p class="text-sm text-base-content/60 mb-4">
You might be looking for:
</p>
<div class="flex gap-3 justify-center flex-wrap text-sm">
<a href="/blog" class="link link-hover">Blog</a>
<span class="text-base-content/30">•</span>
<a href="/#about" class="link link-hover">About</a>
</div>
</div>
</div>
</div>
</Layout>

75
src/pages/500.astro Normal file
View File

@@ -0,0 +1,75 @@
---
import Layout from "../layouts/Layout.astro";
import { House, ArrowLeft } from "@lucide/astro";
interface Props {
error?: unknown;
}
const { error } = Astro.props;
// Sanitize error for display - NEVER expose sensitive server details in production
const displayMessage =
"An unexpected error occurred while processing your request.";
// Log full error server-side for debugging (only visible in server logs)
if (error instanceof Error) {
console.error("500 Server Error:", {
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString(),
});
}
---
<Layout
title="500 - Server Error | Another Hadi"
description="An error occurred while processing your request."
>
<div class="min-h-screen flex items-center justify-center px-4 py-20">
<div class="text-center max-w-2xl mx-auto">
<!-- Large 500 Number -->
<h1 class="text-9xl font-bold text-error mb-4 animate-pulse">500</h1>
<!-- Error Title -->
<h2 class="text-4xl font-bold mb-4">Server Error</h2>
<!-- Error Description -->
<p class="text-xl mb-8 text-base-content/70">
{displayMessage}
<br />
Please try again later or contact me if the problem persists.
</p>
<!-- Divider -->
<div class="divider"></div>
<!-- Action Buttons -->
<div class="flex gap-4 justify-center flex-wrap mt-8">
<a href="/" class="btn btn-primary gap-2">
<House class="size-5" />
Go Home
</a>
<button onclick="history.back()" class="btn btn-outline gap-2">
<ArrowLeft class="size-5" />
Go Back
</button>
</div>
<!-- Helpful Links -->
<div class="mt-8">
<p class="text-sm text-base-content/60 mb-4">Need help?</p>
<div class="flex gap-3 justify-center flex-wrap text-sm">
<a href="/#contact" class="link link-hover">Contact</a>
<span class="text-base-content/30">•</span>
<a
href="https://github.com/anotherhadi/blog/issues"
target="_blank"
rel="noopener noreferrer"
class="link link-hover">Report Issue</a
>
</div>
</div>
</div>
</div>
</Layout>

75
src/pages/503.astro Normal file
View File

@@ -0,0 +1,75 @@
---
import Layout from "../layouts/Layout.astro";
import { House, ArrowLeft } from "@lucide/astro";
interface Props {
error?: unknown;
}
const { error } = Astro.props;
// Sanitize error for display - NEVER expose sensitive server details in production
const displayMessage =
"An unexpected error occurred while processing your request.";
// Log full error server-side for debugging (only visible in server logs)
if (error instanceof Error) {
console.error("503 Service Unavaiable:", {
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString(),
});
}
---
<Layout
title="503 - Service Unavaiable | Another Hadi"
description="An error occurred while processing your request."
>
<div class="min-h-screen flex items-center justify-center px-4 py-20">
<div class="text-center max-w-2xl mx-auto">
<!-- Large 503 Number -->
<h1 class="text-9xl font-bold text-error mb-4 animate-pulse">503</h1>
<!-- Error Title -->
<h2 class="text-4xl font-bold mb-4">Service Unavailable</h2>
<!-- Error Description -->
<p class="text-xl mb-8 text-base-content/70">
{displayMessage}
<br />
Please try again later or contact me if the problem persists.
</p>
<!-- Divider -->
<div class="divider"></div>
<!-- Action Buttons -->
<div class="flex gap-4 justify-center flex-wrap mt-8">
<a href="/" class="btn btn-primary gap-2">
<House class="size-5" />
Go Home
</a>
<button onclick="history.back()" class="btn btn-outline gap-2">
<ArrowLeft class="size-5" />
Go Back
</button>
</div>
<!-- Helpful Links -->
<div class="mt-8">
<p class="text-sm text-base-content/60 mb-4">Need help?</p>
<div class="flex gap-3 justify-center flex-wrap text-sm">
<a href="/#contact" class="link link-hover">Contact</a>
<span class="text-base-content/30">•</span>
<a
href="https://github.com/anotherhadi/blog/issues"
target="_blank"
rel="noopener noreferrer"
class="link link-hover">Report Issue</a
>
</div>
</div>
</div>
</div>
</Layout>

View File

@@ -0,0 +1,27 @@
---
import { getCollection, render } from "astro:content";
import BlogLayout from "../../layouts/BlogLayout.astro";
export async function getStaticPaths() {
const blogEntries = await getCollection("blog");
return blogEntries.map((entry) => ({
params: { slug: entry.id },
props: { entry },
}));
}
const { entry } = Astro.props;
const { Content } = await render(entry);
---
<BlogLayout
title={entry.data.title}
description={entry.data.description}
publishDate={entry.data.publishDate}
updatedDate={entry.data.updatedDate}
image={entry.data.image}
tags={entry.data.tags}
>
<Content />
</BlogLayout>

View File

@@ -0,0 +1,64 @@
---
import Layout from "../../layouts/Layout.astro";
import { getCollection } from "astro:content";
import { Image } from "astro:assets";
import TagBadge from "../../components/TagBadge.astro";
import { ChevronLeft } from "@lucide/astro";
import BlogCard from "../../components/BlogCard.astro";
const blogPosts = await getCollection("blog");
// Sort by publish date, most recent first
const sortedPosts = blogPosts.sort(
(a, b) => b.data.publishDate.getTime() - a.data.publishDate.getTime(),
);
function formatDate(date: Date) {
return date.toLocaleDateString("en-US", {
month: "long",
day: "numeric",
year: "numeric",
});
}
---
<Layout
title="Blog - Another Hadi"
description="Read my latest blog posts and articles about cybersecurity, OSINT and technology."
>
<main class="max-w-6xl mx-auto px-4 py-20">
<!-- Header -->
<div class="text-center mb-16">
<h1 class="text-5xl font-bold mb-4">Blog</h1>
<p class="text-xl text-base-content/70">
Thoughts, insights, and tutorials on cybersecurity, OSINT, and
technology.
</p>
</div>
<!-- Back to Home -->
<div class="mb-8">
<a href="/" class="btn btn-ghost btn-sm">
<ChevronLeft size={18} />
Back to Home
</a>
</div>
<!-- Blog Posts Grid -->
{
sortedPosts.length === 0 ? (
<div class="text-center py-20">
<p class="text-2xl text-base-content/60">
No blog posts yet. Check back soon!
</p>
</div>
) : (
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{sortedPosts.map((post) => (
<BlogCard post={post} />
))}
</div>
)
}
</main>
</Layout>

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

@@ -0,0 +1,27 @@
---
import Layout from "../layouts/Layout.astro";
import Hero from "../components/Hero.astro";
import Projects from "../components/Projects.astro";
import Blog from "../components/Blog.astro";
import Contact from "../components/Contact.astro";
import { siteConfig } from "../config";
import avatar from "../../public/avatar.jpg";
---
<Layout title={`Another Hadi`} description={siteConfig.description}>
<main>
<Hero
name={siteConfig.name}
title={siteConfig.title}
description={siteConfig.description}
avatar={avatar}
location={siteConfig.location}
socialLinks={siteConfig.socialLinks}
gpgKey={siteConfig.gpgKey}
/>
<Blog />
<Projects />
<Contact />
</main>
</Layout>

View File

@@ -0,0 +1,28 @@
---
import { getCollection, render } from "astro:content";
import ProjectLayout from "../../layouts/ProjectLayout.astro";
export async function getStaticPaths() {
const projectEntries = await getCollection("projects");
return projectEntries.map((entry) => ({
params: { slug: entry.id },
props: { entry },
}));
}
const { entry } = Astro.props;
const { Content } = await render(entry);
---
<ProjectLayout
title={entry.data.title}
description={entry.data.description}
image={entry.data.image}
tags={entry.data.tags}
demoLink={entry.data.demoLink}
url={entry.data.url}
sourceLink={entry.data.sourceLink}
>
<Content />
</ProjectLayout>

View File

@@ -0,0 +1,62 @@
---
import Layout from "../../layouts/Layout.astro";
import { getCollection } from "astro:content";
import ProjectCard from "../../components/ProjectCard.astro";
import { ChevronLeft } from "@lucide/astro";
import { ArrowRight } from "lucide-astro";
const projects = await getCollection("projects");
---
<Layout
title="Projects - Another Hadi"
description="Explore my latest projects and work"
>
<main class="max-w-6xl mx-auto px-4 py-20">
<!-- Header -->
<div class="text-center mb-16">
<h1 class="text-5xl font-bold mb-4">Projects</h1>
<p class="text-xl text-base-content/70">
I enjoy the challenge of reimagining existing programs & scripts in my
own unique way. By creating these projects from scratch, I can ensure
complete control over every aspect of their design and functionality.
</p>
</div>
<!-- Back to Home -->
<div class="mb-8">
<a href="/" class="btn btn-ghost btn-sm">
<ChevronLeft size={18} />
Back to Home
</a>
</div>
<!-- Projects Grid -->
{
projects.length === 0 ? (
<div class="text-center py-20">
<p class="text-2xl text-base-content/60">
No projects yet. Check back soon!
</p>
</div>
) : (
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{projects.map((project) => (
<ProjectCard project={project} />
))}
</div>
)
}
<div class="text-center mt-12">
<a
href="https://github.com/anotherhadi"
target="_blank"
class="btn btn-ghost gap-2"
>
View All Projects
<ArrowRight class="size-4" />
</a>
</div>
</main>
</Layout>