Compare commits

...

26 Commits

Author SHA1 Message Date
d171292dd2 chore(deps): update deps
All checks were successful
test-build / guarddog (push) Successful in 38s
test-build / build (push) Successful in 1m15s
release-image-harbor / build (push) Successful in 1m14s
release-image-gitea / build (push) Successful in 1m20s
release-image-gitea / release (push) Successful in 4m12s
release-image-harbor / release (push) Successful in 4m20s
renovate / renovate (push) Successful in 1m18s
2026-02-11 15:37:05 -06:00
f52d285013 Merge pull request 'chore(deps): update dependency marked to v17.0.2' (#336) from renovate/marked-17.x-lockfile into main
All checks were successful
renovate / renovate (push) Successful in 49s
test-build / build (push) Successful in 1m22s
test-build / guarddog (push) Successful in 1m58s
2026-02-11 21:32:43 +00:00
a79f53e90c chore(deps): update dependency marked to v17.0.2
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
test-build / build (pull_request) Successful in 1m39s
test-build / guarddog (pull_request) Successful in 1m55s
2026-02-11 21:32:29 +00:00
5ad7e33c8a Merge pull request 'chore(deps): update dependency @types/react to v19.2.14' (#335) from renovate/react-monorepo into main
Some checks failed
renovate / renovate (push) Has been cancelled
test-build / guarddog (push) Has been cancelled
test-build / build (push) Has been cancelled
2026-02-11 21:32:12 +00:00
87f266a3e2 chore(deps): update dependency @types/react to v19.2.14
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
test-build / guarddog (pull_request) Successful in 37s
test-build / build (pull_request) Successful in 1m33s
2026-02-11 21:32:02 +00:00
dc039046fe Merge pull request 'chore(deps): update astro monorepo' (#334) from renovate/astro-monorepo into main
Some checks failed
renovate / renovate (push) Has been cancelled
test-build / build (push) Has been cancelled
test-build / guarddog (push) Has been cancelled
2026-02-11 21:31:47 +00:00
9c53f37b39 chore(deps): update astro monorepo
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
test-build / guarddog (pull_request) Successful in 42s
test-build / build (pull_request) Successful in 1m23s
2026-02-11 21:31:05 +00:00
093e1e2ccb fix: remove argument
All checks were successful
test-build / guarddog (push) Successful in 32s
renovate / renovate (push) Successful in 54s
test-build / build (push) Successful in 1m28s
2026-02-11 15:24:39 -06:00
7a77f0d2d2 fix: downgrade python
Some checks failed
renovate / renovate (push) Successful in 1m0s
test-build / build (push) Successful in 1m14s
test-build / guarddog (push) Failing after 1m27s
2026-02-11 15:21:44 -06:00
e29631c4af fix: install and run
Some checks failed
renovate / renovate (push) Successful in 58s
test-build / build (push) Successful in 1m18s
test-build / guarddog (push) Failing after 2m4s
2026-02-11 15:19:19 -06:00
31aad5511f fix: only binary
Some checks failed
test-build / guarddog (push) Failing after 30s
renovate / renovate (push) Successful in 1m2s
test-build / build (push) Successful in 1m27s
2026-02-11 15:14:20 -06:00
976bc0c413 fix: add paths
Some checks failed
test-build / guarddog (push) Failing after 44s
test-build / build (push) Successful in 1m10s
renovate / renovate (push) Successful in 1m19s
2026-02-11 15:10:54 -06:00
0a2979ecfe fix: command order
Some checks failed
renovate / renovate (push) Successful in 1m27s
test-build / guarddog (push) Failing after 46s
test-build / build (push) Successful in 2m10s
2026-02-11 15:01:17 -06:00
c3e4519682 fix: use uvx
Some checks failed
renovate / renovate (push) Successful in 53s
test-build / guarddog (push) Failing after 1m9s
test-build / build (push) Successful in 1m22s
2026-02-11 14:56:32 -06:00
d9833e1c27 fix: path
Some checks failed
renovate / renovate (push) Successful in 55s
test-build / build (push) Successful in 1m19s
test-build / guarddog (push) Failing after 1m30s
2026-02-11 14:53:36 -06:00
19e80809c1 feat: enable guarddog
Some checks failed
renovate / renovate (push) Successful in 50s
test-build / build (push) Successful in 1m13s
test-build / guarddog (push) Failing after 1m24s
2026-02-11 14:50:17 -06:00
00ef91b644 feat: release 2.7.0
All checks were successful
renovate / renovate (push) Successful in 56s
test-build / build (push) Successful in 1m23s
2026-02-11 14:44:34 -06:00
7f7f710fe8 feat: make weather fetching dynamic
Some checks failed
renovate / renovate (push) Successful in 45s
test-build / build (push) Has been cancelled
2026-02-11 14:43:13 -06:00
1573331f87 feat: disable
All checks were successful
renovate / renovate (push) Successful in 56s
test-build / build (push) Successful in 1m32s
2026-02-10 22:30:21 -06:00
14f7bdc024 feat: add guarddog scan to workflow
Some checks failed
renovate / renovate (push) Successful in 37s
test-build / build (push) Successful in 1m8s
test-build / guarddog (push) Failing after 1m46s
2026-02-10 22:26:15 -06:00
0b116a05df Merge pull request 'chore(deps): update dependency node to v24.13.1' (#330) from renovate/node-24.x into main
All checks were successful
renovate / renovate (push) Successful in 46s
test-build / build (push) Successful in 1m6s
release-image-harbor / build (push) Successful in 1m10s
release-image-harbor / release (push) Successful in 6m56s
release-image-gitea / build (push) Successful in 1m8s
release-image-gitea / release (push) Successful in 3m10s
Reviewed-on: #330
2026-02-11 03:56:27 +00:00
849ca78598 chore(deps): update dependency node to v24.13.1
All checks were successful
test-build / build (pull_request) Successful in 1m27s
2026-02-11 03:54:23 +00:00
8377aefaf7 chore(deps): update deps
All checks were successful
renovate / renovate (push) Successful in 49s
test-build / build (push) Successful in 1m54s
2026-02-10 21:53:32 -06:00
3f5682f80c feat: release 2.6.0 2026-02-10 21:52:57 -06:00
ae84560ddd Merge pull request 'chore(deps): update dependency @eslint-react/eslint-plugin to v2.12.4' (#331) from renovate/eslint-react-eslint-plugin-2.x-lockfile into main
All checks were successful
renovate / renovate (push) Successful in 1m9s
test-build / build (push) Successful in 1m48s
2026-02-11 03:47:31 +00:00
1f7253d954 chore(deps): update dependency @eslint-react/eslint-plugin to v2.12.4
Some checks are pending
renovate/stability-days Updates have not met minimum release age requirement
test-build / build (pull_request) Successful in 1m53s
2026-02-11 03:47:23 +00:00
11 changed files with 510 additions and 199 deletions

View File

@@ -22,7 +22,7 @@ jobs:
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6
with: with:
node-version: 24.13.0 node-version: 24.13.1
cache: pnpm cache: pnpm
- name: Install Dependencies - name: Install Dependencies

View File

@@ -22,7 +22,7 @@ jobs:
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6
with: with:
node-version: 24.13.0 node-version: 24.13.1
cache: pnpm cache: pnpm
- name: Install Dependencies - name: Install Dependencies

View File

@@ -24,7 +24,7 @@ jobs:
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6
with: with:
node-version: 24.13.0 node-version: 24.13.1
cache: pnpm cache: pnpm
- name: Install Dependencies - name: Install Dependencies
@@ -50,3 +50,38 @@ jobs:
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png' icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=test-build.yaml", "clear": true}]' actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=test-build.yaml", "clear": true}]'
image: true image: true
guarddog:
runs-on: ubuntu-js
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Install GuardDog
run: |
python3 -m pip install --upgrade pip
python3 -m pip install guarddog
- name: Run GuardDog
run: |
guarddog npm scan ./
- name: ntfy Failed
uses: niniyas/ntfy-action@master
if: failure()
with:
url: '${{ secrets.NTFY_URL }}'
topic: '${{ secrets.NTFY_TOPIC }}'
title: 'Security Failure - Site Profile'
priority: 4
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
tags: action,failed
details: 'Guarddog scan failed for Site Profile'
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=test-build.yaml", "clear": true}]'
image: true

View File

@@ -29,7 +29,7 @@ ENV SITE_URL=https://www.alexlebens.dev
ENV DIRECTUS_URL=https://directus.alexlebens.net ENV DIRECTUS_URL=https://directus.alexlebens.net
ENV PORT=4321 ENV PORT=4321
LABEL version="2.5.0" LABEL version="2.7.0"
LABEL description="Astro based personal website" LABEL description="Astro based personal website"
EXPOSE $PORT EXPOSE $PORT

View File

@@ -1,7 +1,7 @@
{ {
"name": "site-profile", "name": "site-profile",
"type": "module", "type": "module",
"version": "2.5.0", "version": "2.7.0",
"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.2", "@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",
@@ -42,12 +42,12 @@
"@swup/astro": "^1.7.0", "@swup/astro": "^1.7.0",
"@tailwindcss/postcss": "^4.1.18", "@tailwindcss/postcss": "^4.1.18",
"@tailwindcss/vite": "^4.1.18", "@tailwindcss/vite": "^4.1.18",
"@types/react": "^19.2.13", "@types/react": "^19.2.14",
"@types/unist": "^3.0.3", "@types/unist": "^3.0.3",
"astro": "^5.17.1", "astro": "^5.17.2",
"astro-compressor": "^1.2.0", "astro-compressor": "^1.2.0",
"astro-icon": "^1.1.5", "astro-icon": "^1.1.5",
"marked": "^17.0.1", "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",
"motion": "^12.34.0", "motion": "^12.34.0",
@@ -62,7 +62,7 @@
"ultrahtml": "^1.6.0" "ultrahtml": "^1.6.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint-react/eslint-plugin": "^2.12.2", "@eslint-react/eslint-plugin": "^2.12.4",
"@tailwindcss/forms": "^0.5.11", "@tailwindcss/forms": "^0.5.11",
"@tailwindcss/typography": "^0.5.19", "@tailwindcss/typography": "^0.5.19",
"astro-icon": "^1.1.5", "astro-icon": "^1.1.5",

554
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -108,7 +108,23 @@ const currentYear = new Date().getFullYear();
© {currentYear} All rights reserved. © {currentYear} All rights reserved.
</p> </p>
<div class="flex items-center space-x-2"> <div class="flex items-center">
<p class="text-xs text-neutral-500 dark:text-neutral-400">
Weather provided by
</p>
<a
href="https://open-meteo.com/"
target="_blank"
rel="noopener noreferrer"
class="group inline-flex items-center text-xs text-neutral-600 transition-colors hover:text-neutral-900 dark:text-neutral-400 dark:hover:text-neutral-100"
>
<span class="relative ml-1">
Open-Meteo.
</span>
</a>
<div class="ml-4"></div>
<span class="text-xs text-neutral-500 dark:text-neutral-400">Built with </span> <span class="text-xs text-neutral-500 dark:text-neutral-400">Built with </span>
<a <a
href="https://astro.build" href="https://astro.build"
@@ -116,24 +132,8 @@ const currentYear = new Date().getFullYear();
rel="noopener noreferrer" rel="noopener noreferrer"
class="group inline-flex items-center text-xs text-neutral-600 transition-colors hover:text-neutral-900 dark:text-neutral-400 dark:hover:text-neutral-100" class="group inline-flex items-center text-xs text-neutral-600 transition-colors hover:text-neutral-900 dark:text-neutral-400 dark:hover:text-neutral-100"
> >
<svg class="mr-1 h-4 w-4 text-[#FF5D01]" viewBox="0 0 36 36" fill="none"> <span class="relative ml-1">
<path Astro.
fill-rule="evenodd"
clip-rule="evenodd"
d="M8.833 22.958c.622-1.185 1.832-1.918 3.18-1.918 2.292 0 4.145 1.86 4.145 4.153 0 1.34-.626 2.54-1.601 3.303 1.223-1.299 1.97-3.048 1.97-4.971 0-3.994-3.243-7.233-7.242-7.233-2.818 0-5.26 1.6-6.469 3.933.78-2.912 3.428-5.06 6.577-5.06 3.75 0 6.79 3.035 6.79 6.78 0 2.606-1.468 4.868-3.616 6.002a4.163 4.163 0 0 0 2.285-3.724c0-2.293-1.853-4.153-4.145-4.153-1.348 0-2.558.733-3.18 1.918l1.306-3.03Z"
fill="currentColor"></path>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M22.155 12.056c-.622 1.185-1.832 1.918-3.18 1.918-2.292 0-4.145-1.86-4.145-4.153 0-1.34.626-2.54 1.601-3.303-1.223 1.299-1.97 3.048-1.97 4.971 0 3.994 3.243 7.233 7.242 7.233 2.818 0 5.26-1.6 6.469-3.933-.78 2.912-3.428 5.06-6.577 5.06-3.75 0-6.79-3.035-6.79-6.78 0-2.606 1.468-4.868 3.616-6.002a4.163 4.163 0 0 0-2.285 3.724c0 2.293 1.853 4.153 4.145 4.153 1.348 0 2.558-.733 3.18-1.918l-1.306 3.03Z"
fill="currentColor"></path>
</svg>
<span class="relative">
Astro
<span
class="absolute bottom-0 left-0 h-0.5 w-0 bg-[#FF5D01] transition-all duration-300 group-hover:w-full"
>
</span>
</span> </span>
</a> </a>
</div> </div>

View File

@@ -1,8 +1,8 @@
--- ---
import { getFiveDayForecast } from '@support/weather'; import { getFiveDayForecast } from '@support/weather';
const { latitude = "44.95", longitude = "-93.09", cityName = "St. Paul, Minnesota" } = Astro.props; const { latitude = "44.95", longitude = "-93.09", cityName = "St. Paul, Minnesota", timezone = "America/Chicago" } = Astro.props;
const { forecastDays, error } = await getFiveDayForecast(latitude, longitude); const { forecastDays, error } = await getFiveDayForecast(latitude, longitude, timezone);
const borderClasses = 'border border-neutral-100 dark:border-stone-500/20'; const borderClasses = 'border border-neutral-100 dark:border-stone-500/20';
const bgColorClasses = 'bg-neutral-100/80 hover:bg-neutral-100 dark:bg-neutral-800/60 dark:hover:bg-neutral-800/90'; const bgColorClasses = 'bg-neutral-100/80 hover:bg-neutral-100 dark:bg-neutral-800/60 dark:hover:bg-neutral-800/90';
@@ -16,7 +16,7 @@ const shadowClasses = 'shadow-xs hover:shadow-md dark:shadow-md dark:hover:shado
</h1> </h1>
<div class="smooth-reveal mx-auto mt-5 max-w-3xl text-center"> <div class="smooth-reveal mx-auto mt-5 max-w-3xl text-center">
<p class="text-lg text-pretty text-neutral-600 dark:text-neutral-400"> <p class="text-lg text-pretty text-neutral-600 dark:text-neutral-400">
5 day forecast for {cityName} Five day forecast for {cityName}
</p> </p>
</div> </div>
</div> </div>

View File

@@ -28,6 +28,7 @@ export type Weather = {
location: string; location: string;
latitude: string; latitude: string;
longitude: string; longitude: string;
timezone: string;
} }
export type Post = { export type Post = {

View File

@@ -45,10 +45,13 @@ const weather = await directus.request(readSingleton('site_weather'));
<FeaturesSection /> <FeaturesSection />
<WeatherSection <WeatherSection
server:defer
latitude={weather.latitude} latitude={weather.latitude}
longitude={weather.longitude} longitude={weather.longitude}
cityName={weather.location} cityName={weather.location}
/> timezone={weather.timezone}
>
</WeatherSection>
<LatestPosts /> <LatestPosts />
@@ -109,5 +112,11 @@ const weather = await directus.request(readSingleton('site_weather'));
}; };
animateContent(); animateContent();
const observer = new MutationObserver(() => {
animateContent();
});
observer.observe(document.body, { childList: true, subtree: true });
}); });
</script> </script>

View File

@@ -28,40 +28,38 @@ const getWeatherInfo = (code: number) => {
return { label: 'Unknown', icon: '03d' }; return { label: 'Unknown', icon: '03d' };
}; };
const getDayName = (dateStr: string) => { export const getDayName = (dateStr: string) => {
return new Date(dateStr).toLocaleDateString('en-US', { weekday: 'short' }); const date = new Date(`${dateStr}T00:00:00`);
return date.toLocaleDateString('en-US', { weekday: 'short' });
}; };
async function getFiveDayForecast(latitude: string, longitude: string): Promise<WeatherResult> { async function getFiveDayForecast(latitude: string, longitude: string, timezone: string): Promise<WeatherResult> {
const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=weather_code,temperature_2m_max&timezone=auto&temperature_unit=fahrenheit`; const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=weather_code,temperature_2m_max&timezone=${timezone}&temperature_unit=fahrenheit`;
let data: any;
try { try {
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) throw new Error("Weather service unavailable"); if (!response.ok) throw new Error("Weather service unavailable");
data = await response.json();
const data = await response.json(); } catch (e: unknown) {
return { forecastDays: [], error: "Failed to load weather" };
const forecastDays = data.daily.time.map((date: string, index: number): DayForecast => { }
const code = data.daily.weather_code[index];
const info = getWeatherInfo(code);
const forecastDays = data.daily.time
.slice(0, 5)
.map((date: string, index: number): DayForecast => {
return { return {
date, date,
temp: Math.round(data.daily.temperature_2m_max[index]), temp: Math.round(data.daily.temperature_2m_max[index]),
code, code: data.daily.weather_code[index],
label: info.label, label: getWeatherInfo(data.daily.weather_code[index]).label,
icon: info.icon, icon: getWeatherInfo(data.daily.weather_code[index]).icon,
dayName: getDayName(date) dayName: getDayName(date)
}; };
}).slice(0, 5); });
return { forecastDays, error: null }; return { forecastDays, error: null };
} catch (e: unknown) {
return {
forecastDays: [],
error: e instanceof Error ? e.message : "An unexpected error occurred"
};
}
} }
export { getFiveDayForecast }; export { getFiveDayForecast };