Compare commits

...

12 Commits
0.4.0 ... 0.5.4

Author SHA1 Message Date
3d53b15f7b update dependencies 2024-09-30 17:12:46 -05:00
renovate[bot]
d10fe280a5 Update dependency @astrojs/node to v8.3.4 (#12)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-30 17:08:27 -05:00
renovate[bot]
5ea5774042 Update docker/build-push-action digest to 4f58ea7 (#13)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-30 17:08:15 -05:00
renovate[bot]
3c82fb43d8 Update docker/login-action digest to 3b8fed7 (#10)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-30 17:07:40 -05:00
renovate[bot]
c7071ab583 Update docker/metadata-action digest to 70b2cdc (#11)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-30 17:07:27 -05:00
renovate[bot]
125d70d62e Update dependency typescript to v5.6.2 (#9)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-30 17:07:15 -05:00
renovate[bot]
357634d3f0 Update dependency @directus/sdk to v17.0.1 (#8)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-30 17:06:28 -05:00
renovate[bot]
bd4b85c874 Update dependency astro to v4.14.6 (#6)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-30 16:23:34 -05:00
7efa375427 remove author field 2024-08-24 02:09:34 -05:00
358d6b91c6 upgrade base image 2024-08-24 01:35:22 -05:00
92d4be91df upgrade astro 2024-08-24 01:34:16 -05:00
0f5fc27371 convert to use directus 2024-08-24 01:23:12 -05:00
23 changed files with 428 additions and 391 deletions

View File

@@ -20,7 +20,7 @@ jobs:
uses: actions/checkout@v4
- name: Log into the container registry
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
uses: docker/login-action@3b8fed7e4b60203b2aa0ecc6c6d6d91d12c06760
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
@@ -28,7 +28,7 @@ jobs:
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@60a0d343a0d8a18aedee9d34e62251f752153bdb
uses: docker/metadata-action@70b2cdc6480c1a8b86edf1777157f8f437de2166
with:
tags: |
type=ref,event=branch
@@ -36,7 +36,7 @@ jobs:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75
with:
context: .
push: true

View File

@@ -1,6 +1,6 @@
FROM node:20.16.0-alpine3.20 AS base
FROM node:20.17.0-alpine3.20 AS base
LABEL version="0.4.0"
LABEL version="0.5.3"
LABEL description="Astro based website to use as a profile"
ENV PNPM_HOME="/pnpm"

50
lib/directus.ts Normal file
View File

@@ -0,0 +1,50 @@
import { createDirectus, rest, } from '@directus/sdk';
type Global = {
title: string;
description: string;
name: string;
tagline: string;
email: string;
portrait: string;
about: string;
}
type About = {
background: string;
experience: string;
education: string;
certifications: string;
}
type Skills = {
skill_1: string;
skill_1_description: string;
skill_2: string;
skill_2_description: string;
skill_3: string;
skill_3_description: string;
}
export type Post = {
slug: string;
title: string;
content: string;
image: string;
published_date: string;
tags: string[];
image_alt: string;
}
type Schema = {
global: Global;
about: About;
skills: Skills;
posts: Post[];
}
export const directus_url = "https://directus.alexlebens.dev"
const directus = createDirectus<Schema>(directus_url).with(rest());
export default directus;

View File

@@ -1,7 +1,7 @@
{
"name": "site-profile",
"type": "module",
"version": "0.4.0",
"version": "0.5.4",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
@@ -11,9 +11,9 @@
},
"dependencies": {
"@astrojs/check": "^0.9.3",
"@astrojs/node": "^8.3.3",
"@directus/sdk": "^17.0.0",
"astro": "^4.14.2",
"typescript": "^5.5.4"
"@astrojs/node": "^8.3.4",
"@directus/sdk": "^17.0.1",
"astro": "^4.15.9",
"typescript": "^5.6.2"
}
}

483
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -1,11 +1,16 @@
---
import CallToAction from './CallToAction.astro';
import Icon from './Icon.astro';
import directus from "../../lib/directus"
import { readSingleton } from "@directus/sdk";
const global = await directus.request(readSingleton("global"));
---
<aside>
<h2>Interested in working together?</h2>
<CallToAction href="mailto:alexander.lebens@gmail.com">
<CallToAction href=`mailto:${global.email}`>
Send Me a Message
<Icon icon="paper-plane-tilt" size="1.2em" />
</CallToAction>

View File

@@ -1,6 +1,11 @@
---
import Icon from './Icon.astro';
const currentYear = new Date().getFullYear();
import directus from "../../lib/directus"
import { readSingleton } from "@directus/sdk";
const global = await directus.request(readSingleton("global"));
---
<footer>
@@ -9,7 +14,7 @@ const currentYear = new Date().getFullYear();
Designed & Developed in Minnesota with <a href="https://astro.build/">Astro</a>
<Icon icon="rocket-launch" size="1.2em" />
</p>
<p>&copy; {currentYear} Alex Lebens</p>
<p>&copy; {currentYear} {global.name}</p>
</div>
<p class="socials">
<a href="https://github.com/alexlebens"> GitHub</a>

View File

@@ -1,14 +1,19 @@
---
import '../styles/global.css';
import directus from "../../lib/directus"
import { readSingleton } from "@directus/sdk";
interface Props {
title?: string | undefined;
description?: string | undefined;
}
const global = await directus.request(readSingleton("global"));
const {
title = 'Alex Lebens',
description = 'A profile of Alex Lebens',
title = `${global.name}`,
description = `A profile of ${global.name}`,
} = Astro.props;
---

View File

@@ -3,6 +3,9 @@ import Icon from './Icon.astro';
import ThemeToggle from './ThemeToggle.astro';
import type { iconPaths } from './IconPaths';
import directus from "../../lib/directus"
import { readSingleton } from "@directus/sdk";
const textLinks: { label: string; href: string }[] = [
{ label: 'Home', href: '/' },
{ label: 'Projects', href: '/projects/' },
@@ -13,13 +16,15 @@ const iconLinks: { label: string; href: string; icon: keyof typeof iconPaths }[]
{ label: 'GitHub', href: 'https://github.com/alexlebens', icon: 'github-logo' },
{ label: 'LinkedIn', href: 'https://www.linkedin.com/in/alexanderlebens', icon: 'linkedin-logo' },
];
const global = await directus.request(readSingleton("global"));
---
<nav>
<div class="menu-header">
<a href="/" class="site-title">
<Icon icon="terminal-window" color="var(--accent-regular)" size="1.6em" gradient />
Alex Lebens
{global.name}
</a>
<menu-button>
<template>

View File

@@ -1,16 +1,17 @@
---
import type { CollectionEntry } from 'astro:content';
import type { Post } from '../../lib/directus';
import { directus_url } from '../../lib/directus';
interface Props {
project: CollectionEntry<'projects'>;
posts: Post;
}
const { data, slug } = Astro.props.project;
const post: Post = Astro.props.posts;
---
<a class="card" href={`/projects/${slug}`}>
<span class="title">{data.title}</span>
<img src={data.img} alt={data.img_alt || ''} loading="lazy" decoding="async" />
<a class="card" href={`/projects/${post.slug}`}>
<span class="title">{post.title}</span>
<img src={`${directus_url}/assets/${post.image}?width=500`} alt={post.image_alt || ''} loading="lazy" decoding="async" />
</a>
<style>

View File

@@ -1,22 +1,27 @@
---
import Icon from './Icon.astro';
import directus from "../../lib/directus"
import { readSingleton } from "@directus/sdk";
const skills = await directus.request(readSingleton("skills"));
---
<section class="box skills">
<div class="stack gap-2 lg:gap-4">
<Icon icon="cloud" color="var(--accent-regular)" size="2.5rem" gradient />
<h2>AWS</h2>
<p>Certified DevOps Engineer and former AWS Cloud Engineer skilled in deploying, managing, and architecting a wide range of AWS services.</p>
<h2 set:html={skills.skill_1}/>
<p set:html={skills.skill_1_description}/>
</div>
<div class="stack gap-2 lg:gap-4">
<Icon icon="network" color="var(--accent-regular)" size="2.5rem" gradient />
<h2>Kubernetes</h2>
<p>My skills encompass Kubernetes administration and application development, validated by my CKA and CKAD certifications.</p>
<h2 set:html={skills.skill_2}/>
<p set:html={skills.skill_2_description}/>
</div>
<div class="stack gap-2 lg:gap-4">
<Icon icon="strategy" color="var(--accent-regular)" size="2.5rem" gradient />
<h2>GitOps</h2>
<p>Hands-on experience leveraging a variety of IaC tools such as CloudFormation, CDK, Helm, and ArgoCD to streamline infrastructure provisioning and management across multiple projects.</p>
<h2 set:html={skills.skill_3}/>
<p set:html={skills.skill_3_description}/>
</div>
</section>

View File

@@ -1,15 +0,0 @@
import { defineCollection, z } from 'astro:content';
export const collections = {
projects: defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string(),
publishDate: z.coerce.date(),
tags: z.array(z.string()),
img: z.string(),
img_alt: z.string().optional(),
}),
}),
};

View File

@@ -1,21 +0,0 @@
---
title: Placeholder
publishDate: 2019-12-01 00:00:00
img: /assets/stock-2.jpg
img_alt: A bright pink sheet of paper used to wrap flowers curves in front of rich blue background
description: |
Placeholder
tags:
- Dev
- Backend
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur posuere commodo venenatis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam non ligula vel metus efficitur hendrerit. In hac habitasse platea dictumst. Praesent et mauris ut mi dapibus semper. Curabitur tortor justo, efficitur sit amet pretium cursus, porta eget odio. Cras ac venenatis dolor. Donec laoreet posuere malesuada. Curabitur nec mi tempor, placerat leo sit amet, tincidunt est. Quisque pellentesque venenatis magna, eget tristique nibh pulvinar in. Vestibulum vitae volutpat arcu. Aenean ut malesuada odio, sit amet pellentesque odio. Suspendisse nunc elit, blandit nec hendrerit non, aliquet at magna. Donec id leo ut nulla sagittis sodales.
Integer vitae nibh elit. Suspendisse eget urna eu neque bibendum pharetra. Sed interdum lectus sem, in pulvinar magna dignissim vel. Quisque maximus at urna nec laoreet. Suspendisse potenti. Vestibulum rhoncus sem ut mi pellentesque, in vestibulum erat blandit. Aliquam sodales dui ac maximus consectetur. Duis quis est vehicula, imperdiet nisl nec, fermentum erat. Duis tortor diam, pharetra eu euismod in, vehicula non eros. Curabitur facilisis dui at erat ultrices gravida. In at nunc ultricies, pulvinar mi vel, sagittis mauris. Praesent pharetra posuere purus ac imperdiet. Nulla facilisi.
Sed pulvinar porttitor mi in ultricies. Etiam non dolor gravida eros pulvinar pellentesque et dictum ex. Proin eu ornare ligula, sed condimentum dui. Vivamus tincidunt tellus mi, sed semper ipsum pharetra a. Suspendisse sollicitudin at sapien nec volutpat. Etiam justo urna, laoreet ac lacus sed, ultricies facilisis dolor. Integer posuere, metus vel viverra gravida, risus elit ornare magna, id feugiat erat risus ullamcorper libero. Proin vitae diam auctor, laoreet lorem vitae, varius tellus.
Mauris sed eros in ex maximus volutpat. Suspendisse potenti. Donec lacinia justo consectetur sagittis tempor. Proin ullamcorper nisi vitae auctor rhoncus. Sed tristique aliquam augue. Pellentesque vitae fringilla ligula. Nulla arcu elit, efficitur eu nunc malesuada, eleifend tincidunt orci. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer mattis orci in bibendum ultricies. Quisque a dui erat. Phasellus et vulputate ipsum. Proin metus ex, lobortis nec ornare eget, bibendum ut sapien. Aliquam in dolor lobortis, aliquam tellus a, congue augue. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Aenean pretium purus augue, ut bibendum erat convallis quis. Cras condimentum quis velit ac mollis. Suspendisse non purus fringilla, venenatis nisl porta, finibus odio. Curabitur aliquet metus faucibus libero interdum euismod. Morbi sed magna nisl. Morbi odio nibh, facilisis vel sapien eu, tempus tincidunt erat. Nullam erat velit, sagittis at purus quis, tristique scelerisque tortor. Pellentesque lacinia tortor id est aliquam viverra. Vestibulum et diam ac ipsum mollis fringilla.

View File

@@ -3,9 +3,15 @@ import BaseLayout from '../layouts/BaseLayout.astro';
import ContactCTA from '../components/ContactCTA.astro';
import Hero from '../components/Hero.astro';
import directus, { directus_url } from "../../lib/directus"
import { readSingleton } from "@directus/sdk";
const global = await directus.request(readSingleton("global"));
const about = await directus.request(readSingleton("about"));
---
<BaseLayout title="About | Alex Lebens" description="About Alex Lebens">
<BaseLayout title=`About | ${global.name}` description=`About ${global.name}`>
<div class="stack gap-20">
<main class="wrapper about">
<Hero
@@ -15,51 +21,38 @@ import Hero from '../components/Hero.astro';
<img
width="1553"
height="873"
src="/assets/hiking.jpg"
alt="Alex Lebens hiking in Texas"
src=`${directus_url}/assets/${global.about}`
alt=`${global.name} hiking in Texas`
/>
</Hero>
<section>
<h2 class="section-title">Background</h2>
<div class="content">
<p>
Grew up exploring the outdoors just north of the Twin Cities. Now, my passions include tinkering with my homelab,
camping adventures in my Jeep, and hitting the slopes or trails for skiing and hiking. I've also been fortunate
enough to travel extensively, visiting nearly every continent, including Antarctica!
</p>
</div>
<div
class="content"
set:html={about.background}
/>
</section>
<section>
<h2 class="section-title">Expierence</h2>
<div class="content">
<p>
Proudly served 6 years in the US Air Force, gaining valuable experience in Cyber Operations and Reconnaissance
while stationed in Japan, Korea, and Texas.
</p>
<p>
Gained valuable experience working in the heart of Washington, D.C. for several years, including time at AWS.
Relocated to Denver in late 2020 to enjoy the outdoor opportunities Colorado has to offer.
</p>
<p>
Recently returned to my roots in Minnesota and eager to embrace new opportunities and challenges in my home state.
</p>
</div>
</section>
<h2 class="section-title">Experience</h2>
<div
class="content"
set:html={about.experience}
/>
</section>
<section>
<h2 class="section-title">Education</h2>
<div class="content">
<p>Currently completing my B.S. in Cloud Computing at Western Governors University. Excited to graduate in 2025!</p>
</div>
<div
class="content"
set:html={about.education}
/>
</section>
<section>
<h2 class="section-title">Certifications</h2>
<div class="content">
<p>AWS DevOps Engineer</p>
<p>Certified Kubernetes Administrator</p>
<p>Certified Kubernetes Application Developer</p>
<p>CompTIA Cloud+</p>
</div>
<div
class="content"
set:html={about.certifications}
/>
</section>
</main>

View File

@@ -1,6 +1,4 @@
---
import { getCollection } from 'astro:content';
import BaseLayout from '../layouts/BaseLayout.astro';
import CallToAction from '../components/CallToAction.astro';
@@ -13,35 +11,43 @@ import PortfolioPreview from '../components/PortfolioPreview.astro';
import ContactCTA from '../components/ContactCTA.astro';
import Skills from '../components/Skills.astro';
const projects = (await getCollection('projects'))
.sort((a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf())
.slice(0, 4);
import directus, { directus_url } from "../../lib/directus"
import { readItems,readSingleton } from "@directus/sdk";
const global = await directus.request(readSingleton("global"));
const posts = await directus.request(
readItems("posts", {
fields: ['*'],
sort: ["-published_date"],
})
);
---
<BaseLayout
title="Home | Alex Lebens"
title=`Home | ${global.name}`
description=""
>
<div class="stack gap-20 lg:gap-48">
<div class="wrapper stack gap-8 lg:gap-20">
<header class="hero">
<Hero
title="Hello, my name is Alex Lebens"
tagline="I am a Cloud Engineer who is currently based in St. Paul, Minnesota."
title=`Hello, my name is ${global.name}`
tagline={global.tagline}
align="start"
>
<div class="roles">
<Pill><Icon icon="hard-drives" size="1.33em" /> Engineer</Pill>
<Pill><Icon icon="hard-drives" size="1.33em" /> Engineer</Pill>
<Pill><Icon icon="code" size="1.33em" /> Developer</Pill>
<Pill><Icon icon="pencil-line" size="1.33em" /> Writer</Pill>
</div>
</Hero>
<img
alt="Alex Lebens in Antarctica"
alt=`${global.name} in Antarctica`
width="480"
height="620"
src="/assets/portrait.jpg"
src=`${directus_url}/assets/${global.portrait}`
/>
</header>
@@ -57,13 +63,13 @@ const projects = (await getCollection('projects'))
<div class="gallery">
<Grid variant="offset">
{
projects.map((project) => (
<li>
<PortfolioPreview project={project} />
</li>
))
}
{
posts.map((post) => (
<li>
<PortfolioPreview posts={post} />
</li>
))
}
</Grid>
</div>

View File

@@ -1,6 +1,4 @@
---
import { getCollection } from 'astro:content';
import BaseLayout from '../layouts/BaseLayout.astro';
import ContactCTA from '../components/ContactCTA.astro';
@@ -8,14 +6,22 @@ import PortfolioPreview from '../components/PortfolioPreview.astro';
import Hero from '../components/Hero.astro';
import Grid from '../components/Grid.astro';
const projects = (await getCollection('projects')).sort(
(a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf(),
import directus from "../../lib/directus"
import { readItems,readSingleton } from "@directus/sdk";
const global = await directus.request(readSingleton("global"));
const posts = await directus.request(
readItems("posts", {
fields: ['*'],
sort: ["-published_date"],
})
);
---
<BaseLayout
title="My Projects | Alex Lebens"
description="Learn about Alex Lebens's most recent projects"
title=`My Projects | ${global.name}`
description=`Learn about ${global.name}'s most recent projects`
>
<div class="stack gap-20">
<main class="wrapper stack gap-8">
@@ -25,13 +31,13 @@ const projects = (await getCollection('projects')).sort(
align="start"
/>
<Grid variant="offset">
{
projects.map((project) => (
<li>
<PortfolioPreview project={project} />
</li>
))
}
{
posts.map((post) => (
<li>
<PortfolioPreview posts={post} />
</li>
))
}
</Grid>
</main>
<ContactCTA />

View File

@@ -1,6 +1,4 @@
---
import { type CollectionEntry, getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import ContactCTA from '../../components/ContactCTA.astro';
@@ -8,43 +6,44 @@ import Hero from '../../components/Hero.astro';
import Icon from '../../components/Icon.astro';
import Pill from '../../components/Pill.astro';
interface Props {
entry: CollectionEntry<'projects'>;
}
import directus, { directus_url } from "../../../lib/directus";
import { readItems } from "@directus/sdk";
export async function getStaticPaths() {
const projects = await getCollection('projects');
return projects.map((entry) => ({
params: { slug: entry.slug },
props: { entry },
}));
const posts = await directus.request(readItems("posts", {
fields: ['*'],
}));
return posts.map((post) => ({ params: { slug: post.slug }, props: post }));
}
const { entry } = Astro.props;
const { Content } = await entry.render();
const post = Astro.props;
const published_date: string = new Date(post.published_date).toLocaleDateString();
---
<BaseLayout title={entry.data.title} description={entry.data.description}>
<BaseLayout title={post.title}>
<div class="stack gap-20">
<div class="stack gap-15">
<header>
<div class="wrapper stack gap-2">
<a class="back-link" href="/projects/"><Icon icon="arrow-left" /> Projects</a>
<Hero title={entry.data.title} align="start">
<Hero
title={post.title}
tagline=`Published on ${published_date}`
align="start"
>
<div class="details">
<div class="tags">
{entry.data.tags.map((t) => <Pill>{t}</Pill>)}
{post.tags.map((t) => <Pill>{t}</Pill>)}
</div>
<p class="description">{entry.data.description}</p>
</div>
</Hero>
</div>
</header>
<main class="wrapper">
<div class="stack gap-10 content">
{entry.data.img && <img src={entry.data.img} alt={entry.data.img_alt || ''} />}
{post.image && <img src={`${directus_url}/assets/${post.image}?width=500`} alt={post.image_alt || ''} />}
<div class="content">
<Content />
<div set:html={post.content} />
</div>
</div>
</main>