Compare commits

..

1 Commits

Author SHA1 Message Date
354288f93e chore(deps): update dependency node to v24.14.0
All checks were successful
test-build / guarddog (pull_request) Successful in 35s
test-build / build (pull_request) Successful in 4m5s
2026-02-26 01:46:09 +00:00
24 changed files with 647 additions and 551 deletions

View File

@@ -1,4 +1,4 @@
FROM docker.io/node:24.14.0-alpine AS builder FROM docker.io/node:24.13.1-alpine AS builder
ENV PNPM_HOME="/pnpm" ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH" ENV PATH="$PNPM_HOME:$PATH"
@@ -17,12 +17,12 @@ FROM build-deps AS build
COPY . . COPY . .
RUN pnpm run build RUN pnpm run build
FROM dhi.io/node:24.14.0 AS runtime FROM dhi.io/node:24.13.1 AS runtime
WORKDIR /app WORKDIR /app
COPY --from=prod-deps /app/node_modules /app/node_modules COPY --from=prod-deps /app/node_modules /app/node_modules
COPY --from=build /app/dist /app/dist COPY --from=build /app/dist /app/dist
LABEL version="2.17.0" LABEL version="2.15.1"
LABEL description="Astro based personal website" LABEL description="Astro based personal website"
ENV HOST=0.0.0.0 ENV HOST=0.0.0.0

View File

@@ -2,7 +2,6 @@
Personal site used for information about myself and blog. Personal site used for information about myself and blog.
## Development ## Development
With dependencies installed, you can utilize the following npm scripts to manage your project's development lifecycle: With dependencies installed, you can utilize the following npm scripts to manage your project's development lifecycle:
@@ -13,13 +12,9 @@ With dependencies installed, you can utilize the following npm scripts to manage
For detailed help with Astro CLI commands, visit [Astro's documentation](https://docs.astro.build/en/reference/cli-reference/). For detailed help with Astro CLI commands, visit [Astro's documentation](https://docs.astro.build/en/reference/cli-reference/).
## Thanks ## Thanks
Based the site on: [gblog-template](https://github.com/godruoyi/gblog/tree/gblog-template) and [ScrewFast](https://github.com/mearashadowfax/ScrewFast) Thanks https://github.com/godruoyi/gblog/tree/gblog-template, https://github.com/mearashadowfax/ScrewFast,
Using Icons from [Icons8](https://icons8.com/)
## License ## License

View File

@@ -1,7 +1,7 @@
{ {
"name": "site-profile", "name": "site-profile",
"type": "module", "type": "module",
"version": "2.17.0", "version": "2.15.1",
"homepage": "https://www.alexlebens.dev", "homepage": "https://www.alexlebens.dev",
"bugs": { "bugs": {
"url": "https://gitea.alexlebens.dev/alexlebens/site-profile/issues", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/issues",
@@ -28,7 +28,7 @@
}, },
"dependencies": { "dependencies": {
"@astrojs/check": "^0.9.6", "@astrojs/check": "^0.9.6",
"@astrojs/node": "^9.5.4", "@astrojs/node": "^9.5.3",
"@astrojs/partytown": "^2.1.4", "@astrojs/partytown": "^2.1.4",
"@astrojs/react": "^4.4.2", "@astrojs/react": "^4.4.2",
"@astrojs/rss": "^4.0.15", "@astrojs/rss": "^4.0.15",
@@ -37,44 +37,44 @@
"@giscus/react": "^3.1.0", "@giscus/react": "^3.1.0",
"@iconify-json/mdi": "^1.2.3", "@iconify-json/mdi": "^1.2.3",
"@iconify-json/pajamas": "^1.2.15", "@iconify-json/pajamas": "^1.2.15",
"@iconify-json/simple-icons": "^1.2.71", "@iconify-json/simple-icons": "^1.2.70",
"@playform/compress": "^0.2.1", "@playform/compress": "^0.2.1",
"@swup/astro": "^1.8.0", "@swup/astro": "^1.8.0",
"@tailwindcss/postcss": "^4.2.1", "@tailwindcss/postcss": "^4.1.18",
"@tailwindcss/vite": "^4.2.1", "@tailwindcss/vite": "^4.1.18",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/unist": "^3.0.3", "@types/unist": "^3.0.3",
"astro": "^5.18.0", "astro": "^5.17.2",
"astro-icon": "^1.1.5", "astro-icon": "^1.1.5",
"marked": "^17.0.3", "marked": "^17.0.2",
"marked-shiki": "^1.2.1", "marked-shiki": "^1.2.1",
"mdast-util-to-string": "^4.0.0", "mdast-util-to-string": "^4.0.0",
"preline": "^4.1.1", "preline": "^4.0.1",
"react": "^19.2.4", "react": "^19.2.4",
"react-dom": "^19.2.4", "react-dom": "^19.2.4",
"reading-time": "^1.5.0", "reading-time": "^1.5.0",
"sharp": "^0.34.5", "sharp": "^0.34.5",
"sharp-ico": "^0.1.5", "sharp-ico": "^0.1.5",
"shiki": "^3.23.0", "shiki": "^3.22.0",
"tailwindcss": "^4.2.1", "tailwindcss": "^4.1.18",
"ultrahtml": "^1.6.0" "ultrahtml": "^1.6.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint-react/eslint-plugin": "^2.13.0", "@eslint-react/eslint-plugin": "^2.13.0",
"@tailwindcss/forms": "^0.5.11", "@tailwindcss/forms": "^0.5.11",
"@tailwindcss/typography": "^0.5.19", "@tailwindcss/typography": "^0.5.19",
"eslint": "^10.0.2", "eslint": "^10.0.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-astro": "^1.6.0", "eslint-plugin-astro": "^1.6.0",
"eslint-plugin-format": "^2.0.1", "eslint-plugin-format": "^1.4.0",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2", "eslint-plugin-react-refresh": "^0.5.0",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"prettier-plugin-astro": "^0.14.1", "prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.7.2", "prettier-plugin-tailwindcss": "^0.7.2",
"timeago.js": "^4.0.2", "timeago.js": "^4.0.2",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.56.1" "typescript-eslint": "^8.55.0"
} }
} }

974
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,13 @@
--- ---
import Logo from '@components/images/Logo.astro';
import { getDirectusImageURL } from '@/support/url';
interface Props { interface Props {
slug: string; slug: string;
title: string; title: string;
description: string; description: string;
logoLight: string;
logoDark?: string;
count: number; count: number;
publishDate: string; publishDate: string;
} }
const { slug, title, description, logoLight, logoDark, count, publishDate } = Astro.props; const { slug, title, description, count, publishDate } = Astro.props;
--- ---
<div class="smooth-reveal-cards group h-full"> <div class="smooth-reveal-cards group h-full">
@@ -23,19 +18,10 @@ const { slug, title, description, logoLight, logoDark, count, publishDate } = As
> >
<div class="relative grow overflow-hidden"> <div class="relative grow overflow-hidden">
<div class="absolute inset-1 flex flex-col p-3 md:p-4 lg:p-5"> <div class="absolute inset-1 flex flex-col p-3 md:p-4 lg:p-5">
<div class="flex flex-row items-center mb-4"> <div class="overflow-hidden">
<div class="card-hover-icon-scale shrink-0 mr-3"> <h3 class="card-text-title-major card-hover-text-title whitespace-nowrap mb-4">
<Logo
srcLight={getDirectusImageURL(logoLight)}
srcDark={getDirectusImageURL(logoDark!)}
alt={`Logo of ${title}`}
/>
</div>
<h3 class="card-text-title-major card-hover-text-title whitespace-nowrap">
{title} {title}
</h3> </h3>
</div>
<div>
<p class="card-text-description mb-4"> <p class="card-text-description mb-4">
{description} {description}
</p> </p>

View File

@@ -1,7 +1,8 @@
--- ---
import { getCollection } from 'astro:content';
import { readItems } from '@directus/sdk'; import { readItems } from '@directus/sdk';
import type { Post, Category } from '@lib/directusTypes'; import type { Post } from '@lib/directusTypes';
import CategoryCard from '@components/cards/CategoryCard.astro'; import CategoryCard from '@components/cards/CategoryCard.astro';
import directus from '@lib/directus'; import directus from '@lib/directus';
@@ -42,7 +43,7 @@ const postMap: Map<string, Post[]> = posts
return acc; return acc;
}, new Map<string, Post[]>()); }, new Map<string, Post[]>());
const categories = (await directus.request(readItems('categories'))) const categories = (await getCollection('categories'))
.sort((a, b) => { .sort((a, b) => {
const aCount = postMap.get(a.slug)?.length ?? 0; const aCount = postMap.get(a.slug)?.length ?? 0;
const bCount = postMap.get(b.slug)?.length ?? 0; const bCount = postMap.get(b.slug)?.length ?? 0;
@@ -51,12 +52,11 @@ const categories = (await directus.request(readItems('categories')))
.map((c, index) => { .map((c, index) => {
const posts = postMap.get(c.slug); const posts = postMap.get(c.slug);
const pattern = layoutPattern[index % layoutPattern.length]; const pattern = layoutPattern[index % layoutPattern.length];
const smColSpan = Math.min(pattern.col, 1); const smColSpan = Math.min(pattern.col, 2);
const mdColSpan = Math.min(pattern.col, 2); const mdColSpan = Math.min(pattern.col, 4);
const lgColSpan = Math.min(pattern.col, 4);
const rowSpan = pattern.row; const rowSpan = pattern.row;
const rowSpanClass = rowSpan > 1 ? `row-span-${rowSpan}` : 'row-span-1'; const rowSpanClass = rowSpan > 1 ? `row-span-${rowSpan}` : 'row-span-1';
const gridItemClass = `col-span-${smColSpan} md:col-span-${mdColSpan} lg:col-span-${lgColSpan} ${rowSpanClass}`; const gridItemClass = `col-span-${smColSpan} md:col-span-${mdColSpan} ${rowSpanClass}`;
return { return {
...c, ...c,
posts, posts,
@@ -64,7 +64,6 @@ const categories = (await directus.request(readItems('categories')))
layoutPattern: { layoutPattern: {
smCol: smColSpan, smCol: smColSpan,
mdCol: mdColSpan, mdCol: mdColSpan,
lgCol: lgColSpan,
row: rowSpan, row: rowSpan,
index, index,
}, },
@@ -73,7 +72,7 @@ const categories = (await directus.request(readItems('categories')))
--- ---
<section class="mx-auto px-4 py-10 sm:px-6 lg:px-8 lg:py-14 lg:pt-10 2xl:max-w-full"> <section class="mx-auto px-4 py-10 sm:px-6 lg:px-8 lg:py-14 lg:pt-10 2xl:max-w-full">
<div class="grid grid-flow-row-dense grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> <div class="grid grid-flow-row-dense grid-cols-2 md:grid-cols-4 gap-4">
{categories.map((category) => { {categories.map((category) => {
return ( return (
<div <div
@@ -82,10 +81,8 @@ const categories = (await directus.request(readItems('categories')))
> >
<CategoryCard <CategoryCard
slug={category.slug} slug={category.slug}
title={category.title} title={category.data.title}
description={category.description} description={category.data.description}
logoLight={category.logoLight}
logoDark={category.logoDark}
count={postMap.get(category.slug)?.length ?? 0} count={postMap.get(category.slug)?.length ?? 0}
publishDate={timeago(postMap.get(category.slug)?.[0]?.published_date)} publishDate={timeago(postMap.get(category.slug)?.[0]?.published_date)}
/> />

View File

@@ -1,39 +1,23 @@
--- ---
import GoLinkPrimaryButton from '@components/buttons/GoLinkPrimaryButton.astro'; import GoLinkPrimaryButton from '@components/buttons/GoLinkPrimaryButton.astro';
import Logo from '@components/images/Logo.astro';
import { getDirectusImageURL } from '@/support/url';
interface Props { interface Props {
title: string; title: string;
subTitle: string; subTitle: string;
logoExists?: boolean;
logoLight?: string;
logoDark?: string;
btnExists?: boolean; btnExists?: boolean;
btnTitle?: string; btnTitle?: string;
btnURL?: string; btnURL?: string;
} }
const { title, subTitle, logoExists, logoLight, logoDark, btnExists, btnTitle, btnURL } = Astro.props; const { title, subTitle, btnExists, btnTitle, btnURL } = Astro.props;
--- ---
<section class="mx-auto mt-10 px-4 sm:px-6 lg:px-8 lg:pt-10 2xl:max-w-full"> <section class="mx-auto mt-10 px-4 sm:px-6 lg:px-8 lg:pt-10 2xl:max-w-full">
<div class="flex-wrap md:flex md:items-center md:justify-between"> <div class="flex-wrap md:flex md:items-center md:justify-between">
<div class="w-full md:w-auto"> <div class="w-full md:w-auto">
<div class="smooth-reveal flex flex-row items-center mb-4"> <h1 class="smooth-reveal card-text-header block lg:text-6xl">
{logoExists ? ( {title}
<div class="shrink-0 mr-5"> </h1>
<Logo
srcLight={getDirectusImageURL(logoLight!)}
srcDark={getDirectusImageURL(logoDark!)}
alt={`Logo of ${title}`}
/>
</div>
) : null}
<h1 class="card-text-header block lg:text-6xl">
{title}
</h1>
</div>
<p class="smooth-reveal card-text-header-description mt-4"> <p class="smooth-reveal card-text-header-description mt-4">
{subTitle} {subTitle}
</p> </p>

View File

@@ -0,0 +1,4 @@
---
title: 'Books 📖'
description: 'Books I have read or listened to'
---

View File

@@ -0,0 +1,4 @@
---
title: 'Cloud ☁️'
description: "Its just someone else's server"
---

View File

@@ -0,0 +1,4 @@
---
title: 'Homelab 🏠'
description: 'What happens when rack servers find a home'
---

View File

@@ -0,0 +1,4 @@
---
title: 'Kubernetes ☸️'
description: 'The container orchestration system'
---

View File

@@ -0,0 +1,4 @@
---
title: 'Life 🏃🏻'
description: 'Just random musings on everyday stuff'
---

View File

@@ -0,0 +1,4 @@
---
title: 'Minnesota 🌳'
description: 'Land of 10,000 Lakes'
---

View File

@@ -0,0 +1,4 @@
---
title: 'PostgreSQL'
description: 'PostgreSQL is an open-source relational database management system (RDBMS)'
---

View File

@@ -0,0 +1,4 @@
---
title: 'Python 🐍'
description: 'Generally my go to language'
---

View File

@@ -0,0 +1,4 @@
---
title: 'Tool 🪜'
description: 'Usually just the software kind'
---

View File

@@ -0,0 +1,4 @@
---
title: 'What Is?'
description: 'A series on discovery'
---

12
src/content/config.ts Normal file
View File

@@ -0,0 +1,12 @@
import { defineCollection, z } from 'astro:content';
const categoryCollection = defineCollection({
type: 'content',
schema: () =>
z.object({
title: z.string(),
description: z.string(),
}),
});
export const collections = { categories: categoryCollection };

View File

@@ -120,7 +120,7 @@ const normalizeTitle = !title ? global.name : `${title} | ${global.name}`;
to bottom, to bottom,
transparent 0px, transparent 0px,
transparent 16px, transparent 16px,
black 60px, black 80px,
black 100% black 100%
); );

View File

@@ -4,7 +4,6 @@ import type {
Global, Global,
Weather, Weather,
Post, Post,
Category,
Application, Application,
Experience, Experience,
Education, Education,
@@ -19,7 +18,6 @@ type Schema = {
site_global: Global; site_global: Global;
site_weather: Weather; site_weather: Weather;
posts: Post[]; posts: Post[];
categories: Category[];
site_applications: Application; site_applications: Application;
site_experience: Experience; site_experience: Experience;
site_education: Education; site_education: Education;

View File

@@ -49,14 +49,6 @@ export type Post = {
updated_date: Date; updated_date: Date;
}; };
export type Category = {
slug: string;
title: string;
description: string;
logoLight: string;
logoDark: string;
};
export type Application = { export type Application = {
id: string; id: string;
name: string; name: string;

View File

@@ -1,5 +1,5 @@
--- ---
import { type CollectionEntry, getCollection } from 'astro:content';
import { Image } from 'astro:assets'; import { Image } from 'astro:assets';
import getReadingTime from 'reading-time'; import getReadingTime from 'reading-time';
import { marked } from 'marked'; import { marked } from 'marked';
@@ -8,7 +8,6 @@ import { createHighlighter } from 'shiki';
import { readItems, readSingleton } from '@directus/sdk'; import { readItems, readSingleton } from '@directus/sdk';
import SocialShareButton from '@components/buttons/SocialShareButton.astro'; import SocialShareButton from '@components/buttons/SocialShareButton.astro';
import Logo from '@components/images/Logo.astro';
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from '@layouts/BaseLayout.astro';
import directus from '@lib/directus'; import directus from '@lib/directus';
import { formatDate } from '@support/time'; import { formatDate } from '@support/time';
@@ -25,14 +24,11 @@ export async function getStaticPaths() {
} }
const global = await directus.request(readSingleton('site_global')); const global = await directus.request(readSingleton('site_global'));
const [category] = post.category ? await directus.request( const category: CollectionEntry<'categories'> = (await getCollection('categories'))
readItems('categories', { .filter((c) => c.slug === post.category)
filter: { slug: { _eq: post.category },}, .pop() as CollectionEntry<'categories'>;
limit: 1,
}))
: [];
const readingTime = getReadingTime(post.content || ''); const readingTime = getReadingTime(post.content);
const highlighter = await createHighlighter({ const highlighter = await createHighlighter({
themes: ['github-light', 'github-dark'], themes: ['github-light', 'github-dark'],
@@ -52,7 +48,7 @@ marked.use(markedShiki({
} }
})); }));
const content = marked.parse(post.content || ''); const content = marked.parse(post.content);
--- ---
<BaseLayout <BaseLayout
@@ -104,31 +100,18 @@ const content = marked.parse(post.content || '');
{post.title} {post.title}
</h2> </h2>
<ol class="flex items-center justify-center sm:justify-start whitespace-nowrap gap-2 sm:gap-0 mt-6 sm:mt-4"> <ol class="flex items-center justify-center sm:justify-start whitespace-nowrap gap-2 sm:gap-0 mt-6 sm:mt-4">
{category && (
<li class="inline-flex items-center"> <li class="inline-flex items-center">
<a <a
class="inline-flex items-center text-secondary hover:text-secondary-hover text-sm transition-all duration-300" class="inline-flex items-center text-secondary hover:text-secondary-hover text-sm transition-all duration-300"
href=`/categories/${category.slug}` href=`/categories/${category.slug}`
data-astro-prefetch data-astro-prefetch
> >
<div class="flex flex-row items-center shrink-0"> {category?.data?.title}
<div class="mr-2">
<Logo
srcLight={getDirectusImageURL(category.logoLight)}
srcDark={getDirectusImageURL(category.logoDark)}
alt={`Logo of ${category.title}`}
width = 18,
height = 18,
/>
</div>
{category.title}
</div>
</a> </a>
<span class="shrink-0 text-secondary text-sm mx-2 sm:mx-4"> <span class="shrink-0 text-secondary text-sm mx-2 sm:mx-4">
/ /
</span> </span>
</li> </li>
)}
<li class="inline-flex items-center"> <li class="inline-flex items-center">
<span class="shrink-0 text-secondary text-sm"> <span class="shrink-0 text-secondary text-sm">
{formatDate(post.published_date)} {formatDate(post.published_date)}

View File

@@ -1,4 +1,5 @@
--- ---
import { getCollection } from 'astro:content';
import { readItems, readSingleton } from '@directus/sdk'; import { readItems, readSingleton } from '@directus/sdk';
import type { Post } from '@lib/directusTypes'; import type { Post } from '@lib/directusTypes';
@@ -8,13 +9,13 @@ import BlogCard from '@components/cards/BlogCard.astro';
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from '@layouts/BaseLayout.astro';
import directus from '@lib/directus'; import directus from '@lib/directus';
const category = Astro.props; const { category } = Astro.props;
export async function getStaticPaths() { export async function getStaticPaths() {
const categories = await directus.request(readItems('categories')); const categories = await getCollection('categories');
return categories.map((category) => ({ return categories.map((category) => ({
params: { slug: category.slug }, params: { slug: category.slug },
props: category, props: { category },
})); }));
} }
@@ -35,16 +36,16 @@ const categoriesPosts = posts
--- ---
<BaseLayout <BaseLayout
title={category.title} title={category.data.title}
description={category.description} description={category.data.description}
structuredData={{ structuredData={{
'@context': 'https://schema.org', '@context': 'https://schema.org',
'@type': 'WebPage', '@type': 'WebPage',
inLanguage: 'en-US', inLanguage: 'en-US',
'@id': Astro.url.href, '@id': Astro.url.href,
url: Astro.url.href, url: Astro.url.href,
name: `${category.title} | ${global.name}`, name: `${category.data.title} | ${global.name}`,
description: category.description, description: category.data.description,
isPartOf: { isPartOf: {
url: `${global.site_url}/categories`, url: `${global.site_url}/categories`,
name: global.name, name: global.name,
@@ -54,11 +55,8 @@ const categoriesPosts = posts
> >
<HeaderSection <HeaderSection
title=`${category.title}` title={category.data.title}
subTitle={category.description} subTitle={category.data.description}
logoExists
logoLight={category.logoLight}
logoDark={category.logoDark}
btnExists btnExists
btnTitle="Back to Categories" btnTitle="Back to Categories"
btnURL="/categories" btnURL="/categories"

View File

@@ -1,4 +1,5 @@
@import 'tailwindcss'; @import 'tailwindcss';
@import 'preline/variants.css';
@import './utilities.css'; @import './utilities.css';
@plugin '@tailwindcss/typography'; @plugin '@tailwindcss/typography';