mirror of
https://github.com/anotherhadi/blog.git
synced 2026-04-02 11:42:10 +02:00
Update projects structure
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 58 KiB |
101
src/components/GiteaProjectCard.astro
Normal file
101
src/components/GiteaProjectCard.astro
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
---
|
||||||
|
import type { GiteaRepoWithMirrors } from "../lib/gitea";
|
||||||
|
import { getBannerUrl } from "../lib/gitea";
|
||||||
|
import { ExternalLink, ChevronDown } from "@lucide/astro";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
repo: GiteaRepoWithMirrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { repo } = Astro.props;
|
||||||
|
const bannerUrl = getBannerUrl(repo);
|
||||||
|
|
||||||
|
const platforms = [
|
||||||
|
...(repo.mirrors.github ? [{ label: "GitHub", url: repo.mirrors.github }] : []),
|
||||||
|
...(repo.mirrors.gitlab ? [{ label: "GitLab", url: repo.mirrors.gitlab }] : []),
|
||||||
|
{ label: "Gitea", url: repo.html_url },
|
||||||
|
];
|
||||||
|
|
||||||
|
const hasMultiplePlatforms = platforms.length > 1;
|
||||||
|
---
|
||||||
|
|
||||||
|
<article
|
||||||
|
class="card bg-base-100 shadow-xl border border-base-200 rounded-lg hover:shadow-2xl transition-shadow"
|
||||||
|
>
|
||||||
|
<figure class="aspect-video bg-base-200 overflow-hidden">
|
||||||
|
<img
|
||||||
|
src={bannerUrl}
|
||||||
|
alt={repo.name}
|
||||||
|
class="w-full h-full object-cover"
|
||||||
|
onerror="this.parentElement.style.display='none'"
|
||||||
|
/>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title hover:text-primary transition-colors">
|
||||||
|
<a href={repo.html_url} target="_blank" rel="noopener noreferrer">
|
||||||
|
{repo.name}
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{repo.description && (
|
||||||
|
<p class="text-base-content/80">{repo.description}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div class="flex flex-wrap gap-2 mt-2">
|
||||||
|
{repo.topics.map((topic) => (
|
||||||
|
<span class="badge badge-sm rounded-sm badge-soft badge-accent">
|
||||||
|
{topic}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-actions justify-end mt-4 gap-2">
|
||||||
|
{repo.website && (
|
||||||
|
|
||||||
|
<a
|
||||||
|
href={repo.website}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="btn btn-soft btn-sm gap-1"
|
||||||
|
>
|
||||||
|
<ExternalLink class="size-4" />
|
||||||
|
Website
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasMultiplePlatforms ? (
|
||||||
|
<div class="dropdown dropdown-end">
|
||||||
|
<div tabindex="0" role="button" class="btn btn-primary btn-sm gap-1">
|
||||||
|
<ExternalLink class="size-4" />
|
||||||
|
View Source
|
||||||
|
<ChevronDown class="size-3" />
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
tabindex="0"
|
||||||
|
class="dropdown-content menu bg-base-100 rounded-xl z-10 w-36 p-1.5 shadow border border-base-200 text-sm"
|
||||||
|
>
|
||||||
|
{platforms.map(({ label, url }) => (
|
||||||
|
<li>
|
||||||
|
<a href={url} target="_blank" rel="noopener noreferrer">
|
||||||
|
{label}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
|
||||||
|
<a
|
||||||
|
href={repo.html_url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="btn btn-primary btn-sm gap-1"
|
||||||
|
>
|
||||||
|
<ExternalLink class="size-4" />
|
||||||
|
View on Gitea
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
@@ -10,6 +10,8 @@ interface Props {
|
|||||||
location?: string;
|
location?: string;
|
||||||
socialLinks?: {
|
socialLinks?: {
|
||||||
github?: string;
|
github?: string;
|
||||||
|
gitlab?: string;
|
||||||
|
gitea?: string;
|
||||||
linkedin?: string;
|
linkedin?: string;
|
||||||
twitter?: string;
|
twitter?: string;
|
||||||
bluesky?: string;
|
bluesky?: string;
|
||||||
@@ -68,6 +70,44 @@ const { name, title, description, avatar, location, socialLinks, gpgKey } =
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{socialLinks.gitlab && (
|
||||||
|
<div class="tooltip" data-tip="Gitlab">
|
||||||
|
<a
|
||||||
|
href={socialLinks.gitlab}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="btn btn-circle btn-ghost"
|
||||||
|
aria-label="Gitlab"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="currentColor"
|
||||||
|
role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m23.6004 9.5927-.0337-.0862L20.3.9814a.851.851 0 0 0-.3362-.405.8748.8748 0 0 0-.9997.0539.8748.8748 0 0 0-.29.4399l-2.2055 6.748H7.5375l-2.2057-6.748a.8573.8573 0 0 0-.29-.4412.8748.8748 0 0 0-.9997-.0537.8585.8585 0 0 0-.3362.4049L.4332 9.5015l-.0325.0862a6.0657 6.0657 0 0 0 2.0119 7.0105l.0113.0087.03.0213 4.976 3.7264 2.462 1.8633 1.4995 1.1321a1.0085 1.0085 0 0 0 1.2197 0l1.4995-1.1321 2.4619-1.8633 5.006-3.7489.0125-.01a6.0682 6.0682 0 0 0 2.0094-7.003z"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{socialLinks.gitea && (
|
||||||
|
<div class="tooltip" data-tip="Gitea">
|
||||||
|
<a
|
||||||
|
href={socialLinks.gitea}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="btn btn-circle btn-ghost"
|
||||||
|
aria-label="Gitea"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="currentColor"
|
||||||
|
role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4.209 4.603c-.247 0-.525.02-.84.088-.333.07-1.28.283-2.054 1.027C-.403 7.25.035 9.685.089 10.052c.065.446.263 1.687 1.21 2.768 1.749 2.141 5.513 2.092 5.513 2.092s.462 1.103 1.168 2.119c.955 1.263 1.936 2.248 2.89 2.367 2.406 0 7.212-.004 7.212-.004s.458.004 1.08-.394c.535-.324 1.013-.893 1.013-.893s.492-.527 1.18-1.73c.21-.37.385-.729.538-1.068 0 0 2.107-4.471 2.107-8.823-.042-1.318-.367-1.55-.443-1.627-.156-.156-.366-.153-.366-.153s-4.475.252-6.792.306c-.508.011-1.012.023-1.512.027v4.474l-.634-.301c0-1.39-.004-4.17-.004-4.17-1.107.016-3.405-.084-3.405-.084s-5.399-.27-5.987-.324c-.187-.011-.401-.032-.648-.032zm.354 1.832h.111s.271 2.269.6 3.597C5.549 11.147 6.22 13 6.22 13s-.996-.119-1.641-.348c-.99-.324-1.409-.714-1.409-.714s-.73-.511-1.096-1.52C1.444 8.73 2.021 7.7 2.021 7.7s.32-.859 1.47-1.145c.395-.106.863-.12 1.072-.12zm8.33 2.554c.26.003.509.127.509.127l.868.422-.529 1.075a.686.686 0 0 0-.614.359.685.685 0 0 0 .072.756l-.939 1.924a.69.69 0 0 0-.66.527.687.687 0 0 0 .347.763.686.686 0 0 0 .867-.206.688.688 0 0 0-.069-.882l.916-1.874a.667.667 0 0 0 .237-.02.657.657 0 0 0 .271-.137 8.826 8.826 0 0 1 1.016.512.761.761 0 0 1 .286.282c.073.21-.073.569-.073.569-.087.29-.702 1.55-.702 1.55a.692.692 0 0 0-.676.477.681.681 0 1 0 1.157-.252c.073-.141.141-.282.214-.431.19-.397.515-1.16.515-1.16.035-.066.218-.394.103-.814-.095-.435-.48-.638-.48-.638-.467-.301-1.116-.58-1.116-.58s0-.156-.042-.27a.688.688 0 0 0-.148-.241l.516-1.062 2.89 1.401s.48.218.583.619c.073.282-.019.534-.069.657-.24.587-2.1 4.317-2.1 4.317s-.232.554-.748.588a1.065 1.065 0 0 1-.393-.045l-.202-.08-4.31-2.1s-.417-.218-.49-.596c-.083-.31.104-.691.104-.691l2.073-4.272s.183-.37.466-.497a.855.855 0 0 1 .35-.077z"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{socialLinks.linkedin && (
|
{socialLinks.linkedin && (
|
||||||
<div class="tooltip" data-tip="Linkedin">
|
<div class="tooltip" data-tip="Linkedin">
|
||||||
<a
|
<a
|
||||||
@@ -212,7 +252,7 @@ const { name, title, description, avatar, location, socialLinks, gpgKey } =
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{socialLinks.kofi && (
|
{socialLinks.kofi && (
|
||||||
<div class="tooltip" data-tip="Ko-fi">
|
<div class="tooltip" data-tip="Ko-fi - Support me">
|
||||||
<a
|
<a
|
||||||
href={socialLinks.kofi}
|
href={socialLinks.kofi}
|
||||||
class="btn btn-circle btn-ghost"
|
class="btn btn-circle btn-ghost"
|
||||||
@@ -282,13 +322,17 @@ const { name, title, description, avatar, location, socialLinks, gpgKey } =
|
|||||||
|
|
||||||
<div class="mt-12 flex gap-5">
|
<div class="mt-12 flex gap-5">
|
||||||
<a href="/blog" class="btn btn-ghost gap-2">
|
<a href="/blog" class="btn btn-ghost gap-2">
|
||||||
Blog
|
Blog Posts
|
||||||
<ArrowRight class="size-4" />
|
<ArrowRight class="size-4" />
|
||||||
</a>
|
</a>
|
||||||
<a href="/projects" class="btn btn-ghost gap-2">
|
<a href="/projects" class="btn btn-ghost gap-2">
|
||||||
Projects
|
Projects
|
||||||
<ArrowRight class="size-4" />
|
<ArrowRight class="size-4" />
|
||||||
</a>
|
</a>
|
||||||
|
<a href="/#contact" class="btn btn-ghost gap-2">
|
||||||
|
Contact Me
|
||||||
|
<ArrowRight class="size-4" />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
---
|
|
||||||
import { Image } from "astro:assets";
|
|
||||||
import TagBadge from "./TagBadge.astro";
|
|
||||||
import { ExternalLink, Eye } from "@lucide/astro";
|
|
||||||
import type { CollectionEntry } from "astro:content";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
project: CollectionEntry<"projects">;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { project } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<article
|
|
||||||
class="card bg-base-100 shadow-xl border border-base-200 rounded-lg hover:shadow-2xl transition-shadow"
|
|
||||||
>
|
|
||||||
<figure class="aspect-video">
|
|
||||||
<Image
|
|
||||||
src={project.data.image}
|
|
||||||
alt={project.data.title}
|
|
||||||
class="w-full h-full object-cover"
|
|
||||||
width={600}
|
|
||||||
height={400}
|
|
||||||
/>
|
|
||||||
</figure>
|
|
||||||
<div class="card-body">
|
|
||||||
<h2 class="card-title hover:text-primary transition-colors">
|
|
||||||
<a href={`/projects/${project.id}`}>{project.data.title}</a>
|
|
||||||
</h2>
|
|
||||||
<p class="text-base-content/80">{project.data.description}</p>
|
|
||||||
{
|
|
||||||
project.data.tags && project.data.tags.length > 0 && (
|
|
||||||
<div class="flex flex-wrap gap-2 mt-2">
|
|
||||||
{project.data.tags.map((tag) => (
|
|
||||||
<TagBadge tag={tag} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<div class="card-actions justify-end mt-4 gap-2">
|
|
||||||
{
|
|
||||||
project.data.demoLink && (
|
|
||||||
<a
|
|
||||||
href={project.data.demoLink}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="btn btn-sm btn-ghost gap-1"
|
|
||||||
>
|
|
||||||
<ExternalLink class="size-4" />
|
|
||||||
Demo
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
project.data.url && (
|
|
||||||
<a
|
|
||||||
href={project.data.url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="btn btn-sm btn-ghost gap-1"
|
|
||||||
>
|
|
||||||
<ExternalLink class="size-4" />
|
|
||||||
Website
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
project.data.sourceLink && (
|
|
||||||
<a
|
|
||||||
href={project.data.sourceLink}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="btn btn-sm btn-ghost gap-1"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 32 32"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path d="M16,2.345c7.735,0,14,6.265,14,14-.002,6.015-3.839,11.359-9.537,13.282-.7,.14-.963-.298-.963-.665,0-.473,.018-1.978,.018-3.85,0-1.312-.437-2.152-.945-2.59,3.115-.35,6.388-1.54,6.388-6.912,0-1.54-.543-2.783-1.435-3.762,.14-.35,.63-1.785-.14-3.71,0,0-1.173-.385-3.85,1.435-1.12-.315-2.31-.472-3.5-.472s-2.38,.157-3.5,.472c-2.677-1.802-3.85-1.435-3.85-1.435-.77,1.925-.28,3.36-.14,3.71-.892,.98-1.435,2.24-1.435,3.762,0,5.355,3.255,6.563,6.37,6.913-.403,.35-.77,.963-.893,1.872-.805,.368-2.818,.963-4.077-1.155-.263-.42-1.05-1.452-2.152-1.435-1.173,.018-.472,.665,.017,.927,.595,.332,1.277,1.575,1.435,1.978,.28,.787,1.19,2.293,4.707,1.645,0,1.173,.018,2.275,.018,2.607,0,.368-.263,.787-.963,.665-5.719-1.904-9.576-7.255-9.573-13.283,0-7.735,6.265-14,14-14Z" />
|
|
||||||
</svg>
|
|
||||||
Source
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<div class="tooltip" data-tip="View project details">
|
|
||||||
<a href={`/projects/${project.id}`} class="btn btn-sm btn-primary">
|
|
||||||
<Eye class="size-4" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
import { getCollection } from "astro:content";
|
import GiteaProjectCard from "./GiteaProjectCard.astro";
|
||||||
import ProjectCard from "./ProjectCard.astro";
|
|
||||||
import { ArrowRight } from "@lucide/astro";
|
import { ArrowRight } from "@lucide/astro";
|
||||||
|
import { fetchGiteaRepos } from "../lib/gitea";
|
||||||
|
|
||||||
const projectEntries = await getCollection("projects");
|
const repos = await fetchGiteaRepos();
|
||||||
|
const latestRepos = repos.slice(0, 3); // 3 plus récents sur la homepage
|
||||||
---
|
---
|
||||||
|
|
||||||
<section id="projects" class="py-20 px-4">
|
<section id="projects" class="py-20 px-4">
|
||||||
@@ -12,21 +13,16 @@ const projectEntries = await getCollection("projects");
|
|||||||
<h2 class="text-4xl font-bold mb-4">Check out my latest work</h2>
|
<h2 class="text-4xl font-bold mb-4">Check out my latest work</h2>
|
||||||
<p class="text-lg text-base-content/70">
|
<p class="text-lg text-base-content/70">
|
||||||
I enjoy the challenge of reimagining existing programs & scripts in my
|
I enjoy the challenge of reimagining existing programs & scripts in my
|
||||||
own unique way. By creating these projects from scratch, I can ensure
|
own unique way.
|
||||||
complete control over every aspect of their design and functionality.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
{projectEntries.map((project) => <ProjectCard project={project} />)}
|
{latestRepos.map((repo) => <GiteaProjectCard repo={repo} />)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center mt-12">
|
<div class="text-center mt-12">
|
||||||
<a
|
<a href="/projects" class="btn btn-ghost gap-2">
|
||||||
href="https://github.com/anotherhadi"
|
|
||||||
target="_blank"
|
|
||||||
class="btn btn-ghost gap-2"
|
|
||||||
>
|
|
||||||
View All Projects
|
View All Projects
|
||||||
<ArrowRight class="size-4" />
|
<ArrowRight class="size-4" />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
*/
|
*/
|
||||||
export interface SocialLinks {
|
export interface SocialLinks {
|
||||||
github?: string;
|
github?: string;
|
||||||
|
gitlab?: string;
|
||||||
|
gitea?: string;
|
||||||
linkedin?: string;
|
linkedin?: string;
|
||||||
twitter?: string;
|
twitter?: string;
|
||||||
bluesky?: string;
|
bluesky?: string;
|
||||||
@@ -39,6 +41,8 @@ export const siteConfig: SiteConfig = {
|
|||||||
location: "🇫🇷 France",
|
location: "🇫🇷 France",
|
||||||
socialLinks: {
|
socialLinks: {
|
||||||
github: "https://github.com/anotherhadi",
|
github: "https://github.com/anotherhadi",
|
||||||
|
gitlab: "https://gitlab.com/anotherhadi",
|
||||||
|
gitea: "https://git.hadi.icu",
|
||||||
twitter: "",
|
twitter: "",
|
||||||
bluesky: "",
|
bluesky: "",
|
||||||
kofi: "https://ko-fi.com/anotherhadi",
|
kofi: "https://ko-fi.com/anotherhadi",
|
||||||
|
|||||||
@@ -1,20 +1,6 @@
|
|||||||
import { defineCollection, z } from "astro:content";
|
import { defineCollection, z } from "astro:content";
|
||||||
import { glob } from "astro/loaders";
|
import { glob } from "astro/loaders";
|
||||||
|
|
||||||
const projects = defineCollection({
|
|
||||||
loader: glob({ pattern: "**/*.md", base: "./src/content/projects" }),
|
|
||||||
schema: ({ image }) =>
|
|
||||||
z.object({
|
|
||||||
title: z.string(),
|
|
||||||
description: z.string(),
|
|
||||||
image: image(),
|
|
||||||
tags: z.array(z.string()),
|
|
||||||
demoLink: z.string().url().optional(),
|
|
||||||
url: z.string().url().optional(),
|
|
||||||
sourceLink: z.string().url().optional(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const blog = defineCollection({
|
const blog = defineCollection({
|
||||||
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/blog" }),
|
loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/blog" }),
|
||||||
schema: ({ image }) =>
|
schema: ({ image }) =>
|
||||||
@@ -29,6 +15,5 @@ const blog = defineCollection({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const collections = {
|
export const collections = {
|
||||||
projects,
|
|
||||||
blog,
|
blog,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Default-Creds"
|
|
||||||
description: "Default Creds is a centralized, community-driven repository of factory-set credentials. Designed for pentesters and security researchers, it helps identify weak access points during engagement phases or audit internal infrastructure before they become a security liability."
|
|
||||||
image: "../../../public/images/projects/default-creds.png"
|
|
||||||
tags: ["default-password", "cybersecurity"]
|
|
||||||
demoLink: "https://default-creds.hadi.icu"
|
|
||||||
sourceLink: "https://github.com/anotherhadi/default-creds"
|
|
||||||
---
|
|
||||||
|
|
||||||
<a href="https://github.com/anotherhadi/default-creds/stargazers">
|
|
||||||
<img src="https://img.shields.io/github/stars/anotherhadi/default-creds?color=8FD0CB&labelColor=0b0b0b&style=for-the-badge&logo=starship&logoColor=8FD0CB">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/anotherhadi/default-creds/">
|
|
||||||
<img src="https://img.shields.io/github/repo-size/anotherhadi/default-creds?color=8FD0CB&labelColor=0b0b0b&style=for-the-badge&logo=github&logoColor=8FD0CB">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/anotherhadi/default-creds/tree/main/src/data">
|
|
||||||
<img src="https://img.shields.io/badge/dynamic/json?url=https://api.github.com/repos/anotherhadi/default-creds/contents/src/data&query=%24.length&label=Manufacturers&style=for-the-badge&color=8FD0CB&labelColor=0b0b0b&logo=github&logoColor=8FD0CB">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/anotherhadi/default-creds/blob/main/LICENSE">
|
|
||||||
<img src="https://img.shields.io/static/v1.svg?style=for-the-badge&label=License&message=MIT&colorA=0b0b0b&colorB=8FD0CB&logo=unlicense&logoColor=8FD0CB"/>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
**Default Creds** is a centralized, community-driven repository of factory-set credentials. Designed for pentesters and security researchers, it helps identify weak access points during engagement phases or audit internal infrastructure before they become a security liability.
|
|
||||||
|
|
||||||
**Live Instance:** [default-creds.hadi.icu](https://default-creds.hadi.icu)
|
|
||||||
|
|
||||||
**API Documentation:** [default-creds.hadi.icu/api-docs](https://default-creds.hadi.icu/api-docs)
|
|
||||||
|
|
||||||
## 🎯 The Mission
|
|
||||||
|
|
||||||
In the world of cybersecurity, "low-hanging fruit" often comes in the form of unchanged default passwords. Our mission is to provide a fast, reliable, and searchable database of these credentials to help security professionals secure systems before attackers exploit them.
|
|
||||||
|
|
||||||
## ⚙️ How it Works
|
|
||||||
|
|
||||||
This application is built with **Astro** and **Svelte 5** for maximum performance. It operates as a "Flat-File Database":
|
|
||||||
|
|
||||||
1. **YAML Powered:** All credentials are stored as structured `.yaml` files in the `src/data/` directory.
|
|
||||||
2. **Real-time Search:** The API parses these files on-the-fly (or via build cache) to provide instant results based on service names, versions, or tags.
|
|
||||||
3. **Developer Friendly:** A public API is available to integrate these credentials into your own automated scanning tools.
|
|
||||||
|
|
||||||
## 🤝 Community Driven & Contributing
|
|
||||||
|
|
||||||
Security is a collective effort. This project only grows as the community discovers and adds new default configurations.
|
|
||||||
|
|
||||||
- **Want to add an app?** Just create a new YAML file in `src/data/`.
|
|
||||||
- **Found a mistake?** Submit a Pull Request to update existing entries.
|
|
||||||
|
|
||||||
Before contributing, please read our [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines on how to format your YAML files.
|
|
||||||
|
|
||||||
## 💻 Local Development
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
- Bun (or nix, just `nix develop`)
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
1. **Clone the repo:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/anotherhadi/default-creds.git
|
|
||||||
cd default-creds
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Start the dev server:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun dev
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Add your data:**
|
|
||||||
Drop a `.yaml` file into `src/data/` and it will appear in the search results instantly!
|
|
||||||
|
|
||||||
### 🛠️ Tech Stack
|
|
||||||
|
|
||||||
- **Framework:** [Astro](https://astro.build/)
|
|
||||||
- **UI Logic:** [Svelte 5 (Runes)](https://svelte.dev/)
|
|
||||||
- **Styling:** [Tailwind CSS](https://tailwindcss.com/) + [DaisyUI](https://daisyui.com/)
|
|
||||||
|
|
||||||
## ⚠️ Legal Disclaimer
|
|
||||||
|
|
||||||
Usage of **Default Creds** for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state, and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program.
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Github Recon"
|
|
||||||
description: "Retrieves and aggregates public OSINT data about a GitHub user using Go and the GitHub API. Finds hidden emails in commit history, previous usernames, friends, other GitHub accounts, and more."
|
|
||||||
image: "../../../public/images/projects/github-recon.png"
|
|
||||||
tags: ["osint", "github", "cybersecurity"]
|
|
||||||
sourceLink: "https://github.com/anotherhadi/github-recon"
|
|
||||||
---
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://github.com/anotherhadi/github-recon/releases"><img src="https://img.shields.io/github/release/anotherhadi/github-recon.svg" alt="Latest Release"></a>
|
|
||||||
<a href="https://pkg.go.dev/github.com/anotherhadi/github-recon?tab=doc"><img src="https://godoc.org/github.com/anotherhadi/github-recon?status.svg" alt="GoDoc"></a>
|
|
||||||
<a href="https://goreportcard.com/report/github.com/anotherhadi/github-recon"><img src="https://goreportcard.com/badge/github.com/anotherhadi/github-recon" alt="GoReportCard"></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
- [🧾 Project Overview](#-project-overview)
|
|
||||||
- [🚀 Features](#-features)
|
|
||||||
- [⚠️ Disclaimer](#%EF%B8%8F-disclaimer)
|
|
||||||
- [📦 Installation](#-installation)
|
|
||||||
- [With Go](#with-go)
|
|
||||||
- [With Nix/NixOS](#with-nixnixos)
|
|
||||||
- [🧪 Usage](#-usage)
|
|
||||||
- [Flags](#flags)
|
|
||||||
- [Token](#token)
|
|
||||||
- [How does the email spoofing work?](#how-does-the-email-spoofing-work)
|
|
||||||
- [💡 Examples](#-examples)
|
|
||||||
- [🕵️♂️ Cover your tracks](#%EF%B8%8F%EF%B8%8F-cover-your-tracks)
|
|
||||||
- [🤝 Contributing](#-contributing)
|
|
||||||
- [🙏 Credits](#-credits)
|
|
||||||
|
|
||||||
## 🧾 Project Overview
|
|
||||||
|
|
||||||
Retrieves and aggregates public OSINT data about a GitHub user using Go and the
|
|
||||||
GitHub API. Finds hidden emails in commit history, previous usernames, friends,
|
|
||||||
other GitHub accounts, and more.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Screenshot</summary>
|
|
||||||
<img src="https://raw.githubusercontent.com/anotherhadi/github-recon/main/.github/assets/example.png" alt="example screenshot">
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## 🚀 Features
|
|
||||||
|
|
||||||
- Export results to JSON
|
|
||||||
|
|
||||||
**From usernames:**
|
|
||||||
|
|
||||||
- Retrieve basic user profile information (username, ID, avatar, bio, creation
|
|
||||||
date)
|
|
||||||
- Display avatars directly in the terminal
|
|
||||||
- List organizations and roles
|
|
||||||
- Fetch SSH and GPG keys
|
|
||||||
- Enumerate social accounts
|
|
||||||
- Extract unique commit authors (name + email)
|
|
||||||
- Find close friends
|
|
||||||
- Deep scan option (clone repositories, run regex searches, analyze licenses,
|
|
||||||
etc.)
|
|
||||||
- Use Levenshtein distance for matching usernames and emails
|
|
||||||
- TruffleHog integration to find secrets
|
|
||||||
|
|
||||||
**From emails:**
|
|
||||||
|
|
||||||
- Search for a specific email across all GitHub commits
|
|
||||||
- Spoof an email to discover the associated user account
|
|
||||||
|
|
||||||
## ⚠️ Disclaimer
|
|
||||||
|
|
||||||
This tool is intended for educational purposes only. Use responsibly and ensure
|
|
||||||
you have permission to access the data you are querying.
|
|
||||||
|
|
||||||
## 📦 Installation
|
|
||||||
|
|
||||||
### With Go
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go install github.com/anotherhadi/github-recon@latest
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Nix/NixOS
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Click to expand</summary>
|
|
||||||
|
|
||||||
**From anywhere (using the repo URL):**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nix run github:anotherhadi/github-recon -- [--flags value] target_username_or_email
|
|
||||||
```
|
|
||||||
|
|
||||||
**Permanent Installation:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# add the flake to your flake.nix
|
|
||||||
{
|
|
||||||
inputs = {
|
|
||||||
github-recon.url = "github:anotherhadi/github-recon";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
# then add it to your packages
|
|
||||||
environment.systemPackages = with pkgs; [ # or home.packages
|
|
||||||
inputs.github-recon.defaultPackage.${pkgs.system}
|
|
||||||
];
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## 🧪 Usage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
github-recon [--flags value] target_username_or_email
|
|
||||||
```
|
|
||||||
|
|
||||||
### Flags
|
|
||||||
|
|
||||||
```txt
|
|
||||||
-t, --token string Github personal access token (e.g. ghp_aaa...). Can also be set via GITHUB_RECON_TOKEN environment variable. You also need to set the token in $HOME/.config/github-recon/env file if you want to use this tool without passing the token every time. (default "null")
|
|
||||||
-d, --deepscan Enable deep scan (clone repos, regex search, analyse licenses, etc.)
|
|
||||||
--max-size int Limit the size of repositories to scan (in MB) (only for deep scan) (default 150)
|
|
||||||
-e, --exclude-repo strings Exclude repos from deep scan (comma-separated list, only for deep scan)
|
|
||||||
-r, --refresh Refresh the cache (only for deep scan)
|
|
||||||
-s, --show-source Show where the information (authors, emails, etc) were found (only for deep scan)
|
|
||||||
-m, --max-distance int Maximum Levenshtein distance for matching usernames & emails (only for deep scan) (default 20)
|
|
||||||
--trufflehog Run trufflehog on cloned repositories (only for deep scan) (default true)
|
|
||||||
-S, --silent Suppress all non-essential output
|
|
||||||
--spoof-email Spoof email (only for email mode) (default true)
|
|
||||||
-a, --print-avatar Show the avatar in the output
|
|
||||||
-j, --json string Write results to specified JSON file
|
|
||||||
```
|
|
||||||
|
|
||||||
### Token
|
|
||||||
|
|
||||||
For the best experience, provide a **GitHub Personal Access Token**. Without a
|
|
||||||
token, you will quickly hit the **rate limit** and have to wait.
|
|
||||||
|
|
||||||
- For **basic usage**, you can create a token **without any permissions**.
|
|
||||||
- For the **email spoofing feature**, you need to add the **`repo`** and
|
|
||||||
**`delete_repo`** permissions.
|
|
||||||
|
|
||||||
You can set the token in multiple ways:
|
|
||||||
|
|
||||||
- **Command-line flag**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
github-recon -t "ghp_xxx..."
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Environment variable**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export GITHUB_RECON_TOKEN=ghp_xxx...
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Config file**: Create the file `~/.config/github-recon/env` and add:
|
|
||||||
|
|
||||||
```env
|
|
||||||
GITHUB_RECON_TOKEN=ghp_xxx...
|
|
||||||
```
|
|
||||||
|
|
||||||
> For safety, it is recommended to create the Personal Access Token on a
|
|
||||||
> **separate GitHub account** rather than your main account. This way, if
|
|
||||||
> anything goes wrong, your primary account remains safe.
|
|
||||||
|
|
||||||
### How does the email spoofing work?
|
|
||||||
|
|
||||||
Here’s the process:
|
|
||||||
|
|
||||||
1. Create a new repository.
|
|
||||||
2. Make a commit using the **target's email** as the author.
|
|
||||||
3. Push the commit to GitHub.
|
|
||||||
4. Observe which GitHub account the commit is linked to. This method **always
|
|
||||||
works**, but it only reveals the account if the email is set as the user’s
|
|
||||||
**primary email**.
|
|
||||||
|
|
||||||
All of these steps are handled **automatically by the tool**, so you just need
|
|
||||||
to provide the target email.
|
|
||||||
|
|
||||||
## 💡 Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
github-recon anotherhadi --token ghp_ABC123...
|
|
||||||
github-recon myemail@gmail.com # Find github accounts by email
|
|
||||||
github-recon anotherhadi --json output.json --deepscan # Clone the repo and search for leaked email
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🕵️♂️ Cover your tracks
|
|
||||||
|
|
||||||
Understanding what information about you is publicly visible is the first step
|
|
||||||
to managing your online presence. github-recon can help you identify your own
|
|
||||||
publicly available data on GitHub. Here’s how you can take steps to protect your
|
|
||||||
privacy and security:
|
|
||||||
|
|
||||||
- **Review your public profile**: Regularly check your GitHub profile and
|
|
||||||
repositories to ensure that you are not unintentionally exposing sensitive
|
|
||||||
information.
|
|
||||||
- **Manage email exposure**: Use GitHub's settings to control which email
|
|
||||||
addresses are visible on your profile and in commit history. You can also use
|
|
||||||
a no-reply email address for commits, and an
|
|
||||||
[alias email](https://proton.me/support/addresses-and-aliases) for your
|
|
||||||
account. Delete/modify any sensitive information in your commit history.
|
|
||||||
- **Be Mindful of Repository Content**: Avoid including sensitive information in
|
|
||||||
your repositories, such as API keys, passwords, emails or personal data. Use
|
|
||||||
`.gitignore` to exclude files that contain sensitive information.
|
|
||||||
|
|
||||||
You can also use a tool like [TruffleHog](github.com/trufflesecurity/trufflehog)
|
|
||||||
to scan your repositories specifically for exposed secrets and tokens.
|
|
||||||
|
|
||||||
**Useful links:**
|
|
||||||
|
|
||||||
- [Blocking command line pushes that expose your personal email address](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/blocking-command-line-pushes-that-expose-your-personal-email-address)
|
|
||||||
- [No-reply email address](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/setting-your-commit-email-address)
|
|
||||||
|
|
||||||
## 🤝 Contributing
|
|
||||||
|
|
||||||
Feel free to contribute! See [CONTRIBUTING.md](https://github.com/anotherhadi/github-recon/blob/main/CONTRIBUTING.md) for details.
|
|
||||||
|
|
||||||
## 🙏 Credits
|
|
||||||
|
|
||||||
Some features and ideas in this project were inspired by the following tools:
|
|
||||||
|
|
||||||
- [gitrecon](https://github.com/GONZOsint/gitrecon) by GONZOsint
|
|
||||||
- [gitfive](https://github.com/mxrch/gitfive) by mxrch
|
|
||||||
|
|
||||||
Big thanks to their authors for sharing their work with the community.
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Nix 4 cyber"
|
|
||||||
description: "A modular, open‑source toolkit & knowledge-base for cyber‑security professionals built with nix & markdown, for CTF, OSINT or Pentest."
|
|
||||||
image: "../../../public/images/projects/n4c.png"
|
|
||||||
tags: ["nix", "ctf", "cybersecurity", "cheatsheets"]
|
|
||||||
url: "https://n4c.hadi.icu"
|
|
||||||
sourceLink: "https://github.com/nix4cyber/n4c"
|
|
||||||
---
|
|
||||||
|
|
||||||
A modular, open‑source toolkit for cyber‑security professionals built with nix & markdown
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
[](https://app.netlify.com/projects/nix4cyber/deploys)
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
N4C (**nix4cyber**) is a personal knowledge-base and toolbox for CTF (capture the flag) & OSINT
|
|
||||||
|
|
||||||
It combines three core components:
|
|
||||||
|
|
||||||
- [Nix-based shells](https://n4c.hadi.icu/shells): pre-configured environments for specific security domains (web, cracking, networking, forensic, ...).
|
|
||||||
- [Cheat‑sheets](https://n4c.hadi.icu/cheatsheets/cheatsheets): quick reference guides organized by category.
|
|
||||||
- [CTF write‑ups](https://n4c.hadi.icu/writeups): markdown-formatted reports of challenges we've solved.
|
|
||||||
|
|
||||||
All content is served through a static website built with [Hugo](https://gohugo.io/) and the [Doks](https://github.com/DELIGHT-LABS/hugo-theme-doks) (<3) theme, hosted on Netlify. The project is fully open‑source under the MIT license and lives on GitHub.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
To use nix4cyber, you need to have [Nix](https://nixos.org/) installed on your system.
|
|
||||||
You can then start a shell with the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
nix develop github:nix4cyber/n4c#<toolkit>
|
|
||||||
```
|
|
||||||
|
|
||||||
You could also install the alias `n4c` ([see here](https://n4c.hadi.icu/shells#alias)) and only type `n4c <toolkit>`
|
|
||||||
|
|
||||||
More informations about shells & toolkits [here](https://n4c.hadi.icu/shells)
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Example: Launch the web pentesting toolkit
|
|
||||||
nix develop github:nix4cyber/n4c#web
|
|
||||||
|
|
||||||
# Inside the shell
|
|
||||||
nmap -A target.com
|
|
||||||
```
|
|
||||||
|
|
||||||
## Disclaimer
|
|
||||||
|
|
||||||
Nix4cyber is intended solely for lawful, ethical, and educational purposes.
|
|
||||||
It is designed to assist cybersecurity professionals, researchers, and students in conducting authorized security assessments, penetration testing, and digital forensics within environments where they have explicit permission to operate.
|
|
||||||
|
|
||||||
By using this project, you agree to comply with all applicable laws and regulations. The maintainers of Nix 4 Cyber are not responsible for any misuse of the tools or scripts provided. Unauthorized or malicious use of this project is strictly prohibited and may violate local, national, or international laws.
|
|
||||||
|
|
||||||
Use responsibly. Always obtain proper authorization before conducting any security testing.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Contributions are welcome!
|
|
||||||
Feel free to open issues, propose new toolkits, or share CTF write-ups via pull requests.
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Nixy"
|
|
||||||
description: "Nixy simplifies and unifies the Hyprland ecosystem with a modular, easily customizable setup. It provides a structured way to manage your system configuration and dotfiles with minimal effort."
|
|
||||||
image: "../../../public/images/projects/nixy.png"
|
|
||||||
tags: ["dotfiles", "nixos", "hyprland", "rice"]
|
|
||||||
demoLink: "https://github.com/anotherhadi/nixy#screenshots"
|
|
||||||
sourceLink: "https://github.com/anotherhadi/nixy"
|
|
||||||
---
|
|
||||||
|
|
||||||
<a href="https://github.com/anotherhadi/nixy/stargazers">
|
|
||||||
<img src="https://img.shields.io/github/stars/anotherhadi/nixy?color=A89AD1&labelColor=0b0b0b&style=for-the-badge&logo=starship&logoColor=A89AD1">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/anotherhadi/nixy/">
|
|
||||||
<img src="https://img.shields.io/github/repo-size/anotherhadi/nixy?color=A89AD1&labelColor=0b0b0b&style=for-the-badge&logo=github&logoColor=A89AD1">
|
|
||||||
</a>
|
|
||||||
<a href="https://nixos.org">
|
|
||||||
<img src="https://img.shields.io/badge/NixOS-unstable-blue.svg?style=for-the-badge&labelColor=0b0b0b&logo=NixOS&logoColor=A89AD1&color=A89AD1">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/anotherhadi/nixy/blob/main/LICENSE">
|
|
||||||
<img src="https://img.shields.io/static/v1.svg?style=for-the-badge&label=License&message=MIT&colorA=0b0b0b&colorB=A89AD1&logo=unlicense&logoColor=A89AD1"/>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
**Nixy simplifies and unifies** the Hyprland ecosystem with a modular, easily
|
|
||||||
customizable setup. It provides a structured way to manage your system
|
|
||||||
configuration and dotfiles with minimal effort. It includes _home-manager_,
|
|
||||||
_secrets_, and _custom theming_ all in one place.
|
|
||||||
|
|
||||||
**Features:**
|
|
||||||
|
|
||||||
- 💻 Hyprland & Caelestia: Preconfigured Hyprland ecosystem with Caelestia-shell (Ty to both projects!)
|
|
||||||
- 🎨 Consistent Theming: Base16 & Stylix-powered themes
|
|
||||||
- ⌨️ Vim-like Everywhere: Unified keybindings (Hyprland, nvim, vimium, etc.)
|
|
||||||
|
|
||||||
## Table of Content
|
|
||||||
|
|
||||||
- [Table of Content](#table-of-content)
|
|
||||||
- [Installation](#installation)
|
|
||||||
- [Documentation](#documentation)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
1. [Fork](https://github.com/anotherhadi/nixy/fork) this repo and clone it to
|
|
||||||
your system:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git clone https://github.com/anotherhadi/nixy ~/.config/nixos
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Copy the `hosts/laptop` folder, rename it to match your system’s hostname,
|
|
||||||
and update `variables.nix` with your machine’s settings.
|
|
||||||
3. Copy your `hardware-configuration.nix` into your new host's folder to ensure
|
|
||||||
proper hardware support.
|
|
||||||
4. Register your new host in `flake.nix` by adding it under nixosConfigurations.
|
|
||||||
|
|
||||||
> `# CHANGEME` comments are placed throughout the config to
|
|
||||||
> indicate necessary modifications. Use the following command to quickly locate
|
|
||||||
> them:
|
|
||||||
>
|
|
||||||
> ```sh
|
|
||||||
> rg "CHANGEME" ~/.config/nixos
|
|
||||||
> ```
|
|
||||||
|
|
||||||
> When you add new files, don't forget to run `git add .` to add them to the git
|
|
||||||
> repository
|
|
||||||
|
|
||||||
5. Build the system
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo nixos-rebuild switch --flake ~/.config/nixos#yourhostname
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
- [SERVER](https://github.com/anotherhadi/nixy/blob/main/docs/SERVER.md): Check out the server documentation
|
|
||||||
- [THEMES](https://github.com/anotherhadi/nixy/blob/main/docs/THEMES.md): How themes work and how to create your own
|
|
||||||
- [WALLPAPERS](https://github.com/anotherhadi/awesome-wallpapers): An awesome
|
|
||||||
collection of wallpapers
|
|
||||||
|
|
||||||
- [CONTRIBUTING](https://github.com/anotherhadi/nixy/blob/main/docs/CONTRIBUTING.md): How to contribute
|
|
||||||
- [LICENSE](https://github.com/anotherhadi/nixy/blob/main/LICENSE): MIT License
|
|
||||||
@@ -21,7 +21,7 @@ const { title, description, image, tags, demoLink, url, sourceLink } =
|
|||||||
<Layout title={`${title} - Another Hadi`} description={description}>
|
<Layout title={`${title} - Another Hadi`} description={description}>
|
||||||
<article class="max-w-4xl mx-auto px-4 py-20">
|
<article class="max-w-4xl mx-auto px-4 py-20">
|
||||||
<!-- Back button -->
|
<!-- Back button -->
|
||||||
<div class="mb-8">
|
<div class="mb-8 flex flex-wrap justify-between gap-5">
|
||||||
<a href="/projects" class="btn btn-ghost btn-sm">
|
<a href="/projects" class="btn btn-ghost btn-sm">
|
||||||
<ChevronLeft size={18} />
|
<ChevronLeft size={18} />
|
||||||
Back to Projects
|
Back to Projects
|
||||||
@@ -124,7 +124,7 @@ const { title, description, image, tags, demoLink, url, sourceLink } =
|
|||||||
<!-- Back to projects link -->
|
<!-- Back to projects link -->
|
||||||
<div class="flex justify-center gap-2 mt-12">
|
<div class="flex justify-center gap-2 mt-12">
|
||||||
<div class="flex gap-3 justify-center flex-wrap text-sm">
|
<div class="flex gap-3 justify-center flex-wrap text-sm">
|
||||||
<a href="https://github.com/anotherhadi" class="link link-hover"
|
<a href="/projects" class="link link-hover"
|
||||||
>View All Projects</a
|
>View All Projects</a
|
||||||
>
|
>
|
||||||
<span class="text-base-content/30">•</span>
|
<span class="text-base-content/30">•</span>
|
||||||
|
|||||||
79
src/lib/gitea.ts
Normal file
79
src/lib/gitea.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
export interface GiteaRepo {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
full_name: string;
|
||||||
|
description: string;
|
||||||
|
html_url: string;
|
||||||
|
updated_at: string;
|
||||||
|
topics: string[];
|
||||||
|
stars_count: number;
|
||||||
|
language: string | null;
|
||||||
|
fork: boolean;
|
||||||
|
private: boolean;
|
||||||
|
website: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RepoMirrors {
|
||||||
|
github?: string;
|
||||||
|
gitlab?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GiteaRepoWithMirrors extends GiteaRepo {
|
||||||
|
mirrors: RepoMirrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GITEA_BASE = "https://git.hadi.icu";
|
||||||
|
const GITEA_USER = "anotherhadi";
|
||||||
|
const GITHUB_USER = "anotherhadi";
|
||||||
|
const GITLAB_USER = "anotherhadi";
|
||||||
|
|
||||||
|
async function checkMirrors(repoName: string): Promise<RepoMirrors> {
|
||||||
|
const mirrors: RepoMirrors = {};
|
||||||
|
|
||||||
|
const [githubRes, gitlabRes] = await Promise.allSettled([
|
||||||
|
fetch(`https://github.com/${GITHUB_USER}/${repoName}`, { method: "HEAD" }),
|
||||||
|
fetch(`https://gitlab.com/${GITLAB_USER}/${repoName}`, { method: "HEAD" }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (githubRes.status === "fulfilled" && githubRes.value.ok) {
|
||||||
|
mirrors.github = `https://github.com/${GITHUB_USER}/${repoName}`;
|
||||||
|
}
|
||||||
|
if (gitlabRes.status === "fulfilled" && gitlabRes.value.ok) {
|
||||||
|
mirrors.gitlab = `https://gitlab.com/${GITLAB_USER}/${repoName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mirrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchGiteaRepos(): Promise<GiteaRepoWithMirrors[]> {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`${GITEA_BASE}/api/v1/users/${GITEA_USER}/repos?limit=50&page=1`
|
||||||
|
);
|
||||||
|
if (!res.ok) throw new Error(`Gitea API: ${res.status}`);
|
||||||
|
|
||||||
|
const repos: GiteaRepo[] = await res.json();
|
||||||
|
const filtered = repos
|
||||||
|
.filter((r) => !r.fork && !r.private)
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
const reposWithMirrors = await Promise.all(
|
||||||
|
filtered.map(async (repo) => ({
|
||||||
|
...repo,
|
||||||
|
mirrors: await checkMirrors(repo.name),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
return reposWithMirrors;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to fetch Gitea repos:", e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBannerUrl(repo: GiteaRepo): string {
|
||||||
|
return `${GITEA_BASE}/${repo.full_name}/raw/branch/main/.github/assets/banner.png`;
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ import { House, FolderOpen } from "@lucide/astro";
|
|||||||
<House class="size-5" />
|
<House class="size-5" />
|
||||||
Go Home
|
Go Home
|
||||||
</a>
|
</a>
|
||||||
<a href="/#projects" class="btn btn-outline gap-2">
|
<a href="/projects" class="btn btn-outline gap-2">
|
||||||
<FolderOpen class="size-5" />
|
<FolderOpen class="size-5" />
|
||||||
View Projects
|
View Projects
|
||||||
</a>
|
</a>
|
||||||
@@ -44,7 +44,7 @@ import { House, FolderOpen } from "@lucide/astro";
|
|||||||
<div class="flex gap-3 justify-center flex-wrap text-sm">
|
<div class="flex gap-3 justify-center flex-wrap text-sm">
|
||||||
<a href="/blog" class="link link-hover">Blog</a>
|
<a href="/blog" class="link link-hover">Blog</a>
|
||||||
<span class="text-base-content/30">•</span>
|
<span class="text-base-content/30">•</span>
|
||||||
<a href="/#about" class="link link-hover">About</a>
|
<a href="/projects" class="link link-hover">All my Projects</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { House, FolderOpen } from "@lucide/astro";
|
|||||||
<House class="size-5" />
|
<House class="size-5" />
|
||||||
Go Home
|
Go Home
|
||||||
</a>
|
</a>
|
||||||
<a href="/#projects" class="btn btn-outline gap-2">
|
<a href="/projects" class="btn btn-outline gap-2">
|
||||||
<FolderOpen class="size-5" />
|
<FolderOpen class="size-5" />
|
||||||
View Projects
|
View Projects
|
||||||
</a>
|
</a>
|
||||||
@@ -44,7 +44,7 @@ import { House, FolderOpen } from "@lucide/astro";
|
|||||||
<div class="flex gap-3 justify-center flex-wrap text-sm">
|
<div class="flex gap-3 justify-center flex-wrap text-sm">
|
||||||
<a href="/blog" class="link link-hover">Blog</a>
|
<a href="/blog" class="link link-hover">Blog</a>
|
||||||
<span class="text-base-content/30">•</span>
|
<span class="text-base-content/30">•</span>
|
||||||
<a href="/#about" class="link link-hover">About</a>
|
<a href="/projects" class="link link-hover">All my Projects</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
---
|
|
||||||
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>
|
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
---
|
---
|
||||||
import Layout from "../../layouts/Layout.astro";
|
import Layout from "../../layouts/Layout.astro";
|
||||||
import { getCollection } from "astro:content";
|
import GiteaProjectCard from "../../components/GiteaProjectCard.astro";
|
||||||
import ProjectCard from "../../components/ProjectCard.astro";
|
|
||||||
import { ChevronLeft } from "@lucide/astro";
|
import { ChevronLeft } from "@lucide/astro";
|
||||||
import { ArrowRight } from "lucide-astro";
|
import { fetchGiteaRepos } from "../../lib/gitea";
|
||||||
|
|
||||||
const projects = await getCollection("projects");
|
const repos = await fetchGiteaRepos();
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout
|
<Layout
|
||||||
@@ -13,17 +12,14 @@ const projects = await getCollection("projects");
|
|||||||
description="Explore my latest projects and work"
|
description="Explore my latest projects and work"
|
||||||
>
|
>
|
||||||
<main class="max-w-6xl mx-auto px-4 py-20">
|
<main class="max-w-6xl mx-auto px-4 py-20">
|
||||||
<!-- Header -->
|
|
||||||
<div class="text-center mb-16">
|
<div class="text-center mb-16">
|
||||||
<h1 class="text-5xl font-bold mb-4">Projects</h1>
|
<h1 class="text-5xl font-bold mb-4">Projects</h1>
|
||||||
<p class="text-xl text-base-content/70">
|
<p class="text-xl text-base-content/70">
|
||||||
I enjoy the challenge of reimagining existing programs & scripts in my
|
I enjoy the challenge of reimagining existing programs & scripts in my
|
||||||
own unique way. By creating these projects from scratch, I can ensure
|
own unique way.
|
||||||
complete control over every aspect of their design and functionality.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Back to Home -->
|
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<a href="/" class="btn btn-ghost btn-sm">
|
<a href="/" class="btn btn-ghost btn-sm">
|
||||||
<ChevronLeft size={18} />
|
<ChevronLeft size={18} />
|
||||||
@@ -31,32 +27,20 @@ const projects = await getCollection("projects");
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Projects Grid -->
|
|
||||||
{
|
{
|
||||||
projects.length === 0 ? (
|
repos.length === 0 ? (
|
||||||
<div class="text-center py-20">
|
<div class="text-center py-20">
|
||||||
<p class="text-2xl text-base-content/60">
|
<p class="text-2xl text-base-content/60">
|
||||||
No projects yet. Check back soon!
|
No projects found. Check back soon!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
{projects.map((project) => (
|
{repos.map((repo) => (
|
||||||
<ProjectCard project={project} />
|
<GiteaProjectCard repo={repo} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</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>
|
</main>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
Reference in New Issue
Block a user