Merge pull request 'fresh-main' (#1) from fresh-main into main

Reviewed-on: https://gitea.argobox.com/InovinLabs/argobox/pulls/1
This commit is contained in:
Daniel LaForce 2025-05-01 22:37:52 +00:00
commit 003ad9395b
67 changed files with 1883 additions and 1193 deletions

View File

@ -3,9 +3,9 @@
## Quick Start ## Quick Start
```bash ```bash
# Clone and initialize the blog repository # Clone and initialize the blog repository
git clone https://git.argobox.com/KeyArgo/laforceit-blog.git git clone https://gitea.argobox.com/InovinLabs/argobox.git # Updated URL
cd laforceit-blog cd argobox # Updated directory name
./scripts/init-blog-repo.sh # ./scripts/init-blog-repo.sh # This script might need review/removal depending on its purpose
``` ```
## What This Does ## What This Does

View File

@ -3,19 +3,19 @@ import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx'; import mdx from '@astrojs/mdx';
import sitemap from '@astrojs/sitemap'; import sitemap from '@astrojs/sitemap';
import tailwind from '@astrojs/tailwind'; import tailwind from '@astrojs/tailwind';
// import cloudflare from '@astrojs/cloudflare'; // Commented out for local development import cloudflare from '@astrojs/cloudflare'; // Import cloudflare adapter
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
site: 'https://argobox.com', // Updated site URL for ArgoBox site: 'https://argobox.com', // Keep original site URL
output: 'static', output: 'static',
// adapter: cloudflare(), // Commented out for local development adapter: cloudflare(), // Enable cloudflare adapter
integrations: [ integrations: [
mdx(), mdx(),
sitemap(), sitemap(),
tailwind(), tailwind(),
], ],
markdown: { markdown: { // Add markdown config
shikiConfig: { shikiConfig: {
theme: 'one-dark-pro', theme: 'one-dark-pro',
wrap: true wrap: true
@ -24,7 +24,7 @@ export default defineConfig({
rehypePlugins: [] rehypePlugins: []
}, },
compressHTML: false, // Disable HTML compression to avoid parsing errors compressHTML: false, // Disable HTML compression to avoid parsing errors
build: { build: { // Add build format
format: 'file', // Use 'file' instead of 'directory' format format: 'file',
} }
}); });

337
package-lock.json generated
View File

@ -1,22 +1,22 @@
{ {
"name": "laforceit-blog", "name": "argobox-astro",
"version": "0.0.1", "version": "0.0.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "laforceit-blog", "name": "argobox-astro",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@astrojs/cloudflare": "latest", "@astrojs/cloudflare": "12.5.0",
"@astrojs/mdx": "latest", "@astrojs/mdx": "4.2.4",
"@astrojs/rss": "latest", "@astrojs/rss": "4.0.11",
"@astrojs/sitemap": "latest", "@astrojs/sitemap": "3.3.0",
"@astrojs/tailwind": "latest", "@astrojs/tailwind": "6.0.2",
"astro": "latest", "astro": "5.7.4",
"nodemailer": "^6.10.1", "nodemailer": "^6.10.1",
"resend": "^4.4.1", "resend": "^4.4.1",
"tailwindcss": "^3.0.24" "tailwindcss": "3.4.17"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/typography": "^0.5.16" "@tailwindcss/typography": "^0.5.16"
@ -280,9 +280,9 @@
} }
}, },
"node_modules/@cloudflare/workerd-darwin-64": { "node_modules/@cloudflare/workerd-darwin-64": {
"version": "1.20250417.0", "version": "1.20250424.0",
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250417.0.tgz", "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250424.0.tgz",
"integrity": "sha512-4Adfl92aKepjxb8e6af2d+xpD2sBOADgHqvkyXsFmoLb80weMEDDRGJi1p1m5q1M78/oVnGcpdmuRCAathanRg==", "integrity": "sha512-E+9tyQfwKwg7iz+vI50UeF9m9MhO6uCTnn6VPBTobhgi0rKcfmCteUGz6YJejG6ex9OIfFHg/tIcr1+ywGZtiA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -296,9 +296,9 @@
} }
}, },
"node_modules/@cloudflare/workerd-darwin-arm64": { "node_modules/@cloudflare/workerd-darwin-arm64": {
"version": "1.20250417.0", "version": "1.20250424.0",
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250417.0.tgz", "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250424.0.tgz",
"integrity": "sha512-dSlk18F4i3T1OTzFBxx3pKpXRMP6w2xZ26+oIV32BFWrCi/HxGzUd6gVA0q37oLGqITRt8xU693J4Gl1CwC/Ag==", "integrity": "sha512-5vReSs+Gx4vPNR3zoU3a7BVBoTEc7aoe2gGcaxSSQKMOvVkp3bo9poOGZbISodhYnCCRXltZcl8Vgyi0l/YZLA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -312,9 +312,9 @@
} }
}, },
"node_modules/@cloudflare/workerd-linux-64": { "node_modules/@cloudflare/workerd-linux-64": {
"version": "1.20250417.0", "version": "1.20250424.0",
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250417.0.tgz", "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250424.0.tgz",
"integrity": "sha512-27MVzOa/lENcqewC2L9EcqstXW843UhjBMcwV1umDfsjwLyZOEv6Gtm/6j5r0L0gASvkRTam3fAmtPk/gt48TA==", "integrity": "sha512-8kBNy7LpW/E4XKGrx/1Xql3Hfy8viDb+tFudu+sN/b6A2tNczNoOzDyNeWeWa99/zfyzncah1l0Wl2RBmVvY+Q==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -328,9 +328,9 @@
} }
}, },
"node_modules/@cloudflare/workerd-linux-arm64": { "node_modules/@cloudflare/workerd-linux-arm64": {
"version": "1.20250417.0", "version": "1.20250424.0",
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250417.0.tgz", "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250424.0.tgz",
"integrity": "sha512-34qBk0htAXmUneOTQxW6/g6pjNVR91r0vJzz2FID84cAIOYVl4hZLijkjmVl+MMDU6boXUs+yDwhItdg06YvAg==", "integrity": "sha512-R4wLZNobQo5K96e3BEaTwCbZhyspeoW81k/yrkSRseLpSoIpLNguw6ckk5sGCjUkXEZQyu9TG6PzdYqlQo70gw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -344,9 +344,9 @@
} }
}, },
"node_modules/@cloudflare/workerd-windows-64": { "node_modules/@cloudflare/workerd-windows-64": {
"version": "1.20250417.0", "version": "1.20250424.0",
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250417.0.tgz", "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250424.0.tgz",
"integrity": "sha512-PDwATFioff+geVHfgTzSWsxgwjgotrdXStb0EL0lMyMT5zNmHArAnOx83CbDtud63Uv9rVX1BAfPP4tyD1O+5A==", "integrity": "sha512-uwzZhNaKjJKq6NGFPd0hQWecpf5OTZCrlWKQZm4kkufZ7uIzkn5t3kOjh/J3L9puM/GvIPxCiDUE2aG66P6YxA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -360,9 +360,9 @@
} }
}, },
"node_modules/@cloudflare/workers-types": { "node_modules/@cloudflare/workers-types": {
"version": "4.20250423.0", "version": "4.20250428.0",
"resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20250423.0.tgz", "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20250428.0.tgz",
"integrity": "sha512-uzy7fvgYIs9YCEaPScC+RnZvd+yJJCqLCEe/n/6p2PZTxWbDmiZjtJQiP5Zx6G0p64ZD/0ZRmtALfDZDNYBDHg==", "integrity": "sha512-r/K1xuR+lW6SeGKRloVdR+BnK+9shBQci717070EBB2VLbcrZCCh0eo6NweLFa1rD8use5rE5JTVDZ/mXqMeNg==",
"license": "MIT OR Apache-2.0" "license": "MIT OR Apache-2.0"
}, },
"node_modules/@cspotcode/source-map-support": { "node_modules/@cspotcode/source-map-support": {
@ -1406,9 +1406,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz",
"integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", "integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -1419,9 +1419,9 @@
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz",
"integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", "integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1432,9 +1432,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz",
"integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", "integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1445,9 +1445,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz",
"integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", "integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1458,9 +1458,9 @@
] ]
}, },
"node_modules/@rollup/rollup-freebsd-arm64": { "node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz",
"integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", "integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1471,9 +1471,9 @@
] ]
}, },
"node_modules/@rollup/rollup-freebsd-x64": { "node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz",
"integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", "integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1484,9 +1484,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz",
"integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", "integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -1497,9 +1497,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-musleabihf": { "node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz",
"integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", "integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -1510,9 +1510,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz",
"integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", "integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1523,9 +1523,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz",
"integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", "integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1536,9 +1536,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-loongarch64-gnu": { "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz",
"integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", "integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@ -1549,9 +1549,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": { "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz",
"integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", "integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@ -1562,9 +1562,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz",
"integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", "integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -1575,9 +1575,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-musl": { "node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz",
"integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", "integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -1588,9 +1588,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz",
"integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", "integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@ -1601,9 +1601,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz",
"integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", "integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1614,9 +1614,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz",
"integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", "integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1627,9 +1627,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz",
"integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", "integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -1640,9 +1640,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz",
"integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", "integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -1653,9 +1653,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz",
"integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", "integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -1834,9 +1834,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.14.1", "version": "22.15.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz",
"integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.21.0" "undici-types": "~6.21.0"
@ -2890,9 +2890,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.140", "version": "1.5.143",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.140.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.143.tgz",
"integrity": "sha512-o82Rj+ONp4Ip7Cl1r7lrqx/pXhbp/lh9DpKcMNscFJdh8ebyRofnc7Sh01B4jx403RI0oqTBvlZ7OBIZLMr2+Q==", "integrity": "sha512-QqklJMOFBMqe46k8iIOwA9l2hz57V2OKMmP5eSWcUvwx+mASAsbU+wkF1pHjn9ZVSBPrsYWr4/W/95y5SwYg2g==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
@ -2901,12 +2901,6 @@
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/emoji-regex-xs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz",
"integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==",
"license": "MIT"
},
"node_modules/entities": { "node_modules/entities": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz",
@ -3380,19 +3374,19 @@
"license": "BSD-2-Clause" "license": "BSD-2-Clause"
}, },
"node_modules/h3": { "node_modules/h3": {
"version": "1.15.1", "version": "1.15.3",
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.1.tgz", "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.3.tgz",
"integrity": "sha512-+ORaOBttdUm1E2Uu/obAyCguiI7MbBvsLTndc3gyK3zU+SYLoZXlyCP9Xgy0gikkGufFLTZXCXD6+4BsufnmHA==", "integrity": "sha512-z6GknHqyX0h9aQaTx22VZDf6QyZn+0Nh+Ym8O/u0SGSkyF5cuTJYKlc8MkzW3Nzf9LE1ivcpmYC3FUGpywhuUQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cookie-es": "^1.2.2", "cookie-es": "^1.2.2",
"crossws": "^0.3.3", "crossws": "^0.3.4",
"defu": "^6.1.4", "defu": "^6.1.4",
"destr": "^2.0.3", "destr": "^2.0.5",
"iron-webcrypto": "^1.2.1", "iron-webcrypto": "^1.2.1",
"node-mock-http": "^1.0.0", "node-mock-http": "^1.0.0",
"radix3": "^1.1.2", "radix3": "^1.1.2",
"ufo": "^1.5.4", "ufo": "^1.6.1",
"uncrypto": "^0.1.3" "uncrypto": "^0.1.3"
} }
}, },
@ -5150,9 +5144,9 @@
} }
}, },
"node_modules/miniflare": { "node_modules/miniflare": {
"version": "4.20250417.0", "version": "4.20250424.1",
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250417.0.tgz", "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250424.1.tgz",
"integrity": "sha512-bROKLQKr4CoS93tnGuw5e08VaNwM3VowTL3Z2Cps1HzY6a4Bq8uNtggQ7WogriMq77jcHn6kbz64bvWyF//Jkw==", "integrity": "sha512-CqBzp8DPO76DLRBSx5/1GM200B5SbfpkNA9n/IxFGY7n6YNc1ypPYy/J0tQqj7vOA62jyD/3kPVbUXxbPKe5SQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@cspotcode/source-map-support": "0.8.1", "@cspotcode/source-map-support": "0.8.1",
@ -5162,7 +5156,7 @@
"glob-to-regexp": "0.4.1", "glob-to-regexp": "0.4.1",
"stoppable": "1.1.0", "stoppable": "1.1.0",
"undici": "^5.28.5", "undici": "^5.28.5",
"workerd": "1.20250417.0", "workerd": "1.20250424.0",
"ws": "8.18.0", "ws": "8.18.0",
"youch": "3.3.4", "youch": "3.3.4",
"zod": "3.22.3" "zod": "3.22.3"
@ -5395,19 +5389,18 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/oniguruma-parser": { "node_modules/oniguruma-parser": {
"version": "0.11.2", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.11.2.tgz", "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.0.tgz",
"integrity": "sha512-F7Ld4oDZJCI5/wCZ8AOffQbqjSzIRpKH7I/iuSs1SkhZeCj0wS6PMZ4W6VA16TWHrAo0Y9bBKEJOe7tvwcTXnw==", "integrity": "sha512-fD9o5ebCmEAA9dLysajdQvuKzLL7cj+w7DQjuO3Cb6IwafENfx6iL+RGkmyW82pVRsvgzixsWinHvgxTMJvdIA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/oniguruma-to-es": { "node_modules/oniguruma-to-es": {
"version": "4.2.0", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.2.0.tgz", "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.1.tgz",
"integrity": "sha512-MDPs6KSOLS0tKQ7joqg44dRIRZUyotfTy0r+7oEEs6VwWWP0+E2PPDYWMFN0aqOjRyWHBYq7RfKw9GQk2S2z5g==", "integrity": "sha512-VtX1kepWO+7HG7IWV5v72JhiqofK7XsiHmtgnvurnNOTdIvE5mrdWYtsOrQyrXCv1L2Ckm08hywp+MFO7rC4Ug==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"emoji-regex-xs": "^1.0.0", "oniguruma-parser": "^0.12.0",
"oniguruma-parser": "^0.11.0",
"regex": "^6.0.1", "regex": "^6.0.1",
"regex-recursion": "^6.0.2" "regex-recursion": "^6.0.2"
} }
@ -6314,9 +6307,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz",
"integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", "integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "1.0.7" "@types/estree": "1.0.7"
@ -6329,26 +6322,26 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.40.0", "@rollup/rollup-android-arm-eabi": "4.40.1",
"@rollup/rollup-android-arm64": "4.40.0", "@rollup/rollup-android-arm64": "4.40.1",
"@rollup/rollup-darwin-arm64": "4.40.0", "@rollup/rollup-darwin-arm64": "4.40.1",
"@rollup/rollup-darwin-x64": "4.40.0", "@rollup/rollup-darwin-x64": "4.40.1",
"@rollup/rollup-freebsd-arm64": "4.40.0", "@rollup/rollup-freebsd-arm64": "4.40.1",
"@rollup/rollup-freebsd-x64": "4.40.0", "@rollup/rollup-freebsd-x64": "4.40.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.40.0", "@rollup/rollup-linux-arm-gnueabihf": "4.40.1",
"@rollup/rollup-linux-arm-musleabihf": "4.40.0", "@rollup/rollup-linux-arm-musleabihf": "4.40.1",
"@rollup/rollup-linux-arm64-gnu": "4.40.0", "@rollup/rollup-linux-arm64-gnu": "4.40.1",
"@rollup/rollup-linux-arm64-musl": "4.40.0", "@rollup/rollup-linux-arm64-musl": "4.40.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.40.0", "@rollup/rollup-linux-loongarch64-gnu": "4.40.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1",
"@rollup/rollup-linux-riscv64-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-gnu": "4.40.1",
"@rollup/rollup-linux-riscv64-musl": "4.40.0", "@rollup/rollup-linux-riscv64-musl": "4.40.1",
"@rollup/rollup-linux-s390x-gnu": "4.40.0", "@rollup/rollup-linux-s390x-gnu": "4.40.1",
"@rollup/rollup-linux-x64-gnu": "4.40.0", "@rollup/rollup-linux-x64-gnu": "4.40.1",
"@rollup/rollup-linux-x64-musl": "4.40.0", "@rollup/rollup-linux-x64-musl": "4.40.1",
"@rollup/rollup-win32-arm64-msvc": "4.40.0", "@rollup/rollup-win32-arm64-msvc": "4.40.1",
"@rollup/rollup-win32-ia32-msvc": "4.40.0", "@rollup/rollup-win32-ia32-msvc": "4.40.1",
"@rollup/rollup-win32-x64-msvc": "4.40.0", "@rollup/rollup-win32-x64-msvc": "4.40.1",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
@ -6543,9 +6536,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/smol-toml": { "node_modules/smol-toml": {
"version": "1.3.3", "version": "1.3.4",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.3.tgz", "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.4.tgz",
"integrity": "sha512-KMVLNWu490KlNfD0lbfDBUktJIEaZRBj1eeK0SMfdpO/rfyARIzlnPVI1Ge4l0vtSJmQUAiGKxMyLGrCT38iyA==", "integrity": "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">= 18" "node": ">= 18"
@ -6952,9 +6945,9 @@
"license": "0BSD" "license": "0BSD"
}, },
"node_modules/type-fest": { "node_modules/type-fest": {
"version": "4.40.0", "version": "4.40.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.0.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.1.tgz",
"integrity": "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==", "integrity": "sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA==",
"license": "(MIT OR CC0-1.0)", "license": "(MIT OR CC0-1.0)",
"engines": { "engines": {
"node": ">=16" "node": ">=16"
@ -7212,19 +7205,19 @@
} }
}, },
"node_modules/unstorage": { "node_modules/unstorage": {
"version": "1.15.0", "version": "1.16.0",
"resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.15.0.tgz", "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.16.0.tgz",
"integrity": "sha512-m40eHdGY/gA6xAPqo8eaxqXgBuzQTlAKfmB1iF7oCKXE1HfwHwzDJBywK+qQGn52dta+bPlZluPF7++yR3p/bg==", "integrity": "sha512-WQ37/H5A7LcRPWfYOrDa1Ys02xAbpPJq6q5GkO88FBXVSQzHd7+BjEwfRqyaSWCv9MbsJy058GWjjPjcJ16GGA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"anymatch": "^3.1.3", "anymatch": "^3.1.3",
"chokidar": "^4.0.3", "chokidar": "^4.0.3",
"destr": "^2.0.3", "destr": "^2.0.5",
"h3": "^1.15.0", "h3": "^1.15.2",
"lru-cache": "^10.4.3", "lru-cache": "^10.4.3",
"node-fetch-native": "^1.6.6", "node-fetch-native": "^1.6.6",
"ofetch": "^1.4.1", "ofetch": "^1.4.1",
"ufo": "^1.5.4" "ufo": "^1.6.1"
}, },
"peerDependencies": { "peerDependencies": {
"@azure/app-configuration": "^1.8.0", "@azure/app-configuration": "^1.8.0",
@ -7233,7 +7226,7 @@
"@azure/identity": "^4.6.0", "@azure/identity": "^4.6.0",
"@azure/keyvault-secrets": "^4.9.0", "@azure/keyvault-secrets": "^4.9.0",
"@azure/storage-blob": "^12.26.0", "@azure/storage-blob": "^12.26.0",
"@capacitor/preferences": "^6.0.3", "@capacitor/preferences": "^6.0.3 || ^7.0.0",
"@deno/kv": ">=0.9.0", "@deno/kv": ">=0.9.0",
"@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0",
"@planetscale/database": "^1.19.0", "@planetscale/database": "^1.19.0",
@ -7410,17 +7403,17 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "6.3.2", "version": "6.3.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.2.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.3.tgz",
"integrity": "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==", "integrity": "sha512-5nXH+QsELbFKhsEfWLkHrvgRpTdGJzqOZ+utSdmPTvwHmvU6ITTm3xx+mRusihkcI8GeC7lCDyn3kDtiki9scw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"fdir": "^6.4.3", "fdir": "^6.4.4",
"picomatch": "^4.0.2", "picomatch": "^4.0.2",
"postcss": "^8.5.3", "postcss": "^8.5.3",
"rollup": "^4.34.9", "rollup": "^4.34.9",
"tinyglobby": "^0.2.12" "tinyglobby": "^0.2.13"
}, },
"bin": { "bin": {
"vite": "bin/vite.js" "vite": "bin/vite.js"
@ -7567,9 +7560,9 @@
} }
}, },
"node_modules/workerd": { "node_modules/workerd": {
"version": "1.20250417.0", "version": "1.20250424.0",
"resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250417.0.tgz", "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250424.0.tgz",
"integrity": "sha512-naz6oJiVODd3/Lkp9l3vtc56HKOOvx+AWDvEsTa5eSfi5SI9V0HYpLYSPblAwrfazbQ4ff1Vl3jkTl/5JxqCAA==", "integrity": "sha512-3Nb69De9pfC21vLMW8Xpp5JXEPYd7e8MGcaEfo/6z1jOX9CFJVaqrAXr8RwYxDgN528ZahHqM51YQEcVlOu1Cw==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
@ -7579,27 +7572,27 @@
"node": ">=16" "node": ">=16"
}, },
"optionalDependencies": { "optionalDependencies": {
"@cloudflare/workerd-darwin-64": "1.20250417.0", "@cloudflare/workerd-darwin-64": "1.20250424.0",
"@cloudflare/workerd-darwin-arm64": "1.20250417.0", "@cloudflare/workerd-darwin-arm64": "1.20250424.0",
"@cloudflare/workerd-linux-64": "1.20250417.0", "@cloudflare/workerd-linux-64": "1.20250424.0",
"@cloudflare/workerd-linux-arm64": "1.20250417.0", "@cloudflare/workerd-linux-arm64": "1.20250424.0",
"@cloudflare/workerd-windows-64": "1.20250417.0" "@cloudflare/workerd-windows-64": "1.20250424.0"
} }
}, },
"node_modules/wrangler": { "node_modules/wrangler": {
"version": "4.12.1", "version": "4.13.2",
"resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.12.1.tgz", "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.13.2.tgz",
"integrity": "sha512-jYrz8y2ffhsRqvQLO2dXFi9HLvPUJk3jn7U71GWfBBCHm0I6r2ik7Vs9ajpRcTGlbNw1RY0uIHVJBVR/7bEN5A==", "integrity": "sha512-CryA3MRzjNceFVef78ymqhxXrIYQoYKQIPITvvd/Yn3SX4UAADZOOrztatNcgRAyXssjdGH4JRw7fKoSnOaOog==",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"dependencies": { "dependencies": {
"@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/kv-asset-handler": "0.4.0",
"@cloudflare/unenv-preset": "2.3.1", "@cloudflare/unenv-preset": "2.3.1",
"blake3-wasm": "2.1.5", "blake3-wasm": "2.1.5",
"esbuild": "0.25.2", "esbuild": "0.25.2",
"miniflare": "4.20250417.0", "miniflare": "4.20250424.1",
"path-to-regexp": "6.3.0", "path-to-regexp": "6.3.0",
"unenv": "2.0.0-rc.15", "unenv": "2.0.0-rc.15",
"workerd": "1.20250417.0" "workerd": "1.20250424.0"
}, },
"bin": { "bin": {
"wrangler": "bin/wrangler.js", "wrangler": "bin/wrangler.js",
@ -7613,7 +7606,7 @@
"sharp": "^0.33.5" "sharp": "^0.33.5"
}, },
"peerDependencies": { "peerDependencies": {
"@cloudflare/workers-types": "^4.20250417.0" "@cloudflare/workers-types": "^4.20250424.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@cloudflare/workers-types": { "@cloudflare/workers-types": {

View File

@ -1,5 +1,5 @@
{ {
"name": "laforceit-blog", "name": "argobox-astro",
"type": "module", "type": "module",
"version": "0.0.1", "version": "0.0.1",
"scripts": { "scripts": {
@ -9,15 +9,15 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/cloudflare": "latest", "@astrojs/cloudflare": "12.5.0",
"@astrojs/mdx": "latest", "@astrojs/mdx": "4.2.4",
"@astrojs/rss": "latest", "@astrojs/rss": "4.0.11",
"@astrojs/sitemap": "latest", "@astrojs/sitemap": "3.3.0",
"@astrojs/tailwind": "latest", "@astrojs/tailwind": "6.0.2",
"astro": "latest", "astro": "5.7.4",
"nodemailer": "^6.10.1", "nodemailer": "^6.10.1",
"resend": "^4.4.1", "resend": "^4.4.1",
"tailwindcss": "^3.0.24" "tailwindcss": "3.4.17"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/typography": "^0.5.16" "@tailwindcss/typography": "^0.5.16"

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
public/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

BIN
public/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

Before

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@ -1 +0,0 @@
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAEAAAAAAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCABAAIADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9/KKKKACiiigAooooAKKKKACiiigAorH8Y+MdH8DaRLqWt6nY6PpluAZ7y/uEt7eEE4G+R2CqM9MkDNfOPx3/AOCt/wAFvhPdXFnJ4ys/EGoxlkjs/DsL37sR6soWEeu6ZW9qAPpeivgHXf8AgrT4nv8AxDLpujfCS61HTwcJfS6zNazjP8bQLZTBlHbcN3WvMPGH/BZ3xlcSyx+Gvh5pVn5RKrJq2oT3HL9SsQtgV4+6d2e9AH6F/Gj44eFvgJ4JuPEXizV7fRdIhkSFZpAzO8jsAFREBZ2JIAAAJJHv18x+F/8AgqX8KfFfiGz0uPU9Us7q9lEMU2oaFe2lsGPTfM8YjQY7yMoycZAJFfnd8U/jl8Qv2hdQ87xV4q1XW7GcmS1tJ5/KsR/dhgTCKRnoQDgc5ry7WNXS2UbGywUFztyAT3Jzzn+lAH9H+k6pa61p8N5ZzxXlncIJIpreQSRyKRkFWUkMD6EVPXwD/wAEh/jnqOrXGq+AtWupL6yt7dNR02aViXgQuskiAnkDcHwPRwOmK++aACiiigAooooAKKKKACvI/wBpz9rHwX+zD4ca91+9aXULiPfZaRZESX14OwRCRtTIBLsFUZ6kkA+uV+bP7UF/N+07+0lqlxbyFtKhuJIYPlyqWsSny/woGwvGdzkscmgDpvit/wAFNPiB8TI5YvDceneELA/LHLBCLrUZPRvNf5ExzjbHGcfxGvnXWdWvfEGotdX93cX93KAHmuZmllfHbcxyeB7/AErOTQY7eXft+Vdwww5BIwOB0B7jPervh7Ql1jWvJkb5TncVyDsHXA7ZwQCeOv0oA5/UpRGRFEGxyNmcDjqT9O30p9jpdxeXCxwRySO/AVBn8/T/APXXvNn8P9NigZYdOhMgwySPl2HHJDMc5+o49sVoQ+HbaJVX7PCoXJGIgMnBGeD3z+VSpsrlOCPL/D3wuuLaaNZreSSRyNwVSFX1z7fXB7ZzXrfhvwFc2JWTy2ji6KWUgj6Ajg+x59K6bRbGKLBjWP5ecCIHnrz6Y/l7V0Vrb71VchvU46Y9+MdfpUOpJkxopHOaH4eElu0Yjhj3LjBQY9D06H37fTnlv2atZn+B37Wfgm9h3Nb6hew6fOoOcxzkRZBx1BkTOMdO9eo6FY7djtAyY+YbCQwGexGeORkVyPx48Iv4q8H2ur20bS32hyi5TZ96SLOJFx6qu8gdtoHvVRlcbR+p9FYvgDxnb+O/A+ja9a7/ACNWsYL6LcMNtkjWRcj0O4VtVRIUUUUAFFFFABX5bfteeEvEXgf426ja6kZLiO6aWS3vQMLdRsSGDD+E4IKnqOCMMpx+pNcf8dPhLY/Gj4Y6toV1tSa4jL2tw6bvs9yo3RyAdSAwww/iVmGRmgD8sdN0+bUJ1WFZHkY5VQM7vbA6mvZPBHw7ijsnmvIlkknXeI+uF4HUdMAfzruPh7+zlNoWrTR3Fq9vcK5ErBgJOP73HzDHbjHSvXvDvwUW/wBMWTaMYBEecHdng5PQ+nH0PeoY0eU+BvBNxJcRlo9ix4AVQFwPZQAMDPsK9o8OeE4okUNGFVRnkYb6evTHPtxXc+GPg3b2e3zE24xnYpBx3x9Pp0rd1HwhHZWrNGvzAZHG7PoMetJ6DR50mi/ZmZnbyyyhdoUqGI6dOh6/h+VEulP5O7btZt2OcgY4HH4V0d5pTWzKdo27iMhhnkY6dCe351mzxKDjaoTg85wMdTk9KmwGfJ5i4X5V2nI6jH8umOD7da4H4y6QniD4da1CNu5baScZGQXtw0y/+QxXpl1aGRTtKsVGMg//AFqqmCOaIwyIsse4hlPVXQncD7gqQfqKpCZ5t+xh8SJvFnwjl0e6kaS88O3BtgW+9JbSfPC3pxlx9EFe111P7K/gC5+H3we+yx/ubrUWN9dJn/VNJgJGf9pI0jU+6muWrbkCiiikAUUUUAFFFFAHhH7YHwcj8XeHj4ksYFGraSgNwUXDXNuDg5x1KElfoWxnbzg/hF8fLjwzeR6fdzNNBOyqyP8Ae4Gc47HtnPQD3I/TuvlD9qH9lddS1abxJocYjulYzXltENqSrzukT0cZJI7gkHndjzsNbldzow83J8sj3P4ZfG601nTI8OskbKu5JBuVh3B9P0r1fRfiCt9b7lLbh1z1H+fw+or8xNC8Wa18MNbhmtpJFUOA8bNmOVScgg/hzX0N4G+OcN1bxs0jNIADJG5wy5HX+h9wazlTa2LVSMtGfV9/rkdy5kb5pOcZ/wD1d6pXl4u0/MWYdeDj3//XxXkWh/F+3vYY2jm3Y6g88Ht9e3QdK3o/Ho8tflYL94/Nz/n86i7NIxdrt2OrvAJFba3yqN2DnJ96yri5aGRlAZcfMNowSep9x+FZMvjpYtzbvl6q24kevHrWfeeNY4wrNIHyecHAHua0i2c1SSN9tTCx8MCw3dMEHsR7dv8AGsa2mmS7/dv5cyK2CF2hieOQM8/j0/Osm68Yx6hKy75G29H/AMMj86h0vxR9mvGlhbejR7cE5yOnPseK1S1Mmz7Q+GPhweFfAmkabuWR9PsobdpE+7IY41Usp9CQSPaugoooNCkFFFFAgooooAKKKKACiiigDH8VeB9H8Z2n2fVtNs9St/4UuoFk2H1U4O0+6kGvHfFf7Cuhak7zaVqF3pMjZIiY/aYc9wA21lHsWb/dHavdqKAPjjxp+x94i8P3DfYbCS+iVsbI3EM49MJJgMfZWPGBk15Z4k+CniSNW3aHdSRgFnEUfmKwxwAEJAGMnjAwRntX6HZGM1BdWUV9D5c0SSxtw0cihlYehB60XYrI/OG/8Iahp0zeY11as+Su+Jo2HPbcAD07E844qZLW4W2aSaG4dOSRsdmJ+gBP5V9++IfhH4X8SCSOS0FlNztuLQiNh7nGFYehBrx3xT+x7d2Lc2kbr03QyhkPqdk21hjtljx6UlMd0fJQsZbyRWjjboc71O32HHOM/StLTdJlK42yMVOflGcH07/5+le06n+zdf2J33VtuUZxujUOvuI3UuPcHGf4cGsPUPg/HZYZ9PvLbPXzIfPGPUSL+6/HK5q+cxdI2v2XfDUmufEizkk3C10+N7qZs4BIIWNQO/ztuPspr7Rqj4V8K2fgzw7Z6TYo0dpaJ5a7xhm5JLMe5JJJPua0BRYQtQooopgFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFR3FvHcxNHLGssbjDI6hlYeoI6iiigCrL4X06b79haP8A71sivt61Avhfw9b/APHhpVqnH/LHYmPyGKKKaXYTbJYYEgXbGiRr2VFCgfgBUlFFMQUUUUAFFFFABRRRQAUUUUAf/9k=

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1200" height="600" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#050a18;stop-opacity:1" />
<stop offset="100%" style="stop-color:#0d1529;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="1200" height="600" fill="url(#grad1)"/>
<text x="600" y="250" font-family="Arial" font-size="50" fill="#e2e8f0" text-anchor="middle">K3s Kubernetes</text>
<text x="600" y="320" font-family="Arial" font-size="30" fill="#3b82f6" text-anchor="middle">Lightweight Kubernetes for Edge and IoT</text>
<g transform="translate(550,150) scale(0.6)">
<path d="M 50,50 L 150,50 L 150,150 L 50,150 Z" fill="none" stroke="#06b6d4" stroke-width="10"/>
<path d="M 75,75 L 175,75 L 175,175 L 75,175 Z" fill="none" stroke="#3b82f6" stroke-width="10"/>
<path d="M 100,100 L 200,100 L 200,200 L 100,200 Z" fill="none" stroke="#8b5cf6" stroke-width="10"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -23,14 +23,15 @@ const categories = [
{ name: 'Docker Compose', path: '/resources/docker-compose' }, { name: 'Docker Compose', path: '/resources/docker-compose' },
{ name: 'Configuration Files', path: '/resources/config-files' }, { name: 'Configuration Files', path: '/resources/config-files' },
{ name: 'Infrastructure Code', path: '/resources/iac' }, { name: 'Infrastructure Code', path: '/resources/iac' },
{ name: 'Tutorials', path: '/resources/tutorials' } { name: 'Tutorials', path: '/resources/tutorials' },
{ name: 'All Resources', path: '/resources' } // Added Resources link
] ]
}, },
{ {
title: 'Projects', title: 'Projects',
links: [ links: [
{ name: 'HomeLab Setup', url: 'https://argobox.com' }, { name: 'HomeLab Setup', path: '/homelab' },
{ name: 'Tech Stack', url: 'https://argobox.com/#services' }, { name: 'Tech Stack', path: '/tech-stack' }, // Updated Tech Stack link
{ name: 'Github Repos', path: '/projects/github' }, { name: 'Github Repos', path: '/projects/github' },
{ name: 'Live Services', path: '/projects/services' }, { name: 'Live Services', path: '/projects/services' },
{ name: 'Obsidian Templates', path: '/projects/obsidian' } { name: 'Obsidian Templates', path: '/projects/obsidian' }
@ -47,12 +48,12 @@ const socialLinks = [
}, },
{ {
name: 'Twitter', name: 'Twitter',
url: 'https://twitter.com/yourusername', url: 'https://www.x.com/danlaforce', // Updated Twitter URL
icon: '<path d="M23.643 4.937c-.835.37-1.732.62-2.675.733a4.67 4.67 0 0 0 2.048-2.578 9.3 9.3 0 0 1-2.958 1.13 4.66 4.66 0 0 0-7.938 4.25 13.229 13.229 0 0 1-9.602-4.868c-.4.69-.63 1.49-.63 2.342A4.66 4.66 0 0 0 3.96 9.824a4.647 4.647 0 0 1-2.11-.583v.06a4.66 4.66 0 0 0 3.737 4.568 4.692 4.692 0 0 1-2.104.08 4.661 4.661 0 0 0 4.352 3.234 9.348 9.348 0 0 1-5.786 1.995 9.5 9.5 0 0 1-1.112-.065 13.175 13.175 0 0 0 7.14 2.093c8.57 0 13.255-7.098 13.255-13.254 0-.2-.005-.402-.014-.602a9.47 9.47 0 0 0 2.323-2.41l.002-.003z" />' icon: '<path d="M23.643 4.937c-.835.37-1.732.62-2.675.733a4.67 4.67 0 0 0 2.048-2.578 9.3 9.3 0 0 1-2.958 1.13 4.66 4.66 0 0 0-7.938 4.25 13.229 13.229 0 0 1-9.602-4.868c-.4.69-.63 1.49-.63 2.342A4.66 4.66 0 0 0 3.96 9.824a4.647 4.647 0 0 1-2.11-.583v.06a4.66 4.66 0 0 0 3.737 4.568 4.692 4.692 0 0 1-2.104.08 4.661 4.661 0 0 0 4.352 3.234 9.348 9.348 0 0 1-5.786 1.995 9.5 9.5 0 0 1-1.112-.065 13.175 13.175 0 0 0 7.14 2.093c8.57 0 13.255-7.098 13.255-13.254 0-.2-.005-.402-.014-.602a9.47 9.47 0 0 0 2.323-2.41l.002-.003z" />'
}, },
{ {
name: 'LinkedIn', name: 'LinkedIn',
url: 'https://linkedin.com/in/yourusername', url: 'https://www.linkedin.com/in/danlaforce', // Updated LinkedIn URL
icon: '<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 0 1-2.063-2.065 2.064 2.064 0 1 1 2.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.225 0z" />' icon: '<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 0 1-2.063-2.065 2.064 2.064 0 1 1 2.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.225 0z" />'
}, },
{ {
@ -115,7 +116,11 @@ const services = [
{socialLinks.map(social => ( {socialLinks.map(social => (
<a href={social.url} class="social-link" aria-label={social.name} target="_blank" rel="noopener"> <a href={social.url} class="social-link" aria-label={social.name} target="_blank" rel="noopener">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<Fragment set:html={social.icon} /> {/* Render SVG paths conditionally based on name */}
{social.name === 'GitHub' && <path fill-rule="evenodd" clip-rule="evenodd" d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385c.6.105.825-.255.825-.57c0-.285-.015-1.23-.015-2.235c-3.015.555-3.795-.735-4.035-1.41c-.135-.345-.72-1.41-1.23-1.695c-.42-.225-1.02-.78-.015-.795c.945-.015 1.62.87 1.845 1.23c1.08 1.815 2.805 1.305 3.495.99c.105-.78.42-1.305.765-1.605c-2.67-.3-5.46-1.335-5.46-5.925c0-1.305.465-2.385 1.23-3.225c-.12-.3-.54-1.53.12-3.18c0 0 1.005-.315 3.3 1.23c.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23c.66 1.65.24 2.88.12 3.18c.765.84 1.23 1.905 1.23 3.225c0 4.605-2.805 5.625-5.475 5.925c.435.375.81 1.095.81 2.22c0 1.605-.015 2.895-.015 3.3c0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z" />}
{social.name === 'Twitter' && <path d="M23.643 4.937c-.835.37-1.732.62-2.675.733a4.67 4.67 0 0 0 2.048-2.578 9.3 9.3 0 0 1-2.958 1.13 4.66 4.66 0 0 0-7.938 4.25 13.229 13.229 0 0 1-9.602-4.868c-.4.69-.63 1.49-.63 2.342A4.66 4.66 0 0 0 3.96 9.824a4.647 4.647 0 0 1-2.11-.583v.06a4.66 4.66 0 0 0 3.737 4.568 4.692 4.692 0 0 1-2.104.08 4.661 4.661 0 0 0 4.352 3.234 9.348 9.348 0 0 1-5.786 1.995 9.5 9.5 0 0 1-1.112-.065 13.175 13.175 0 0 0 7.14 2.093c8.57 0 13.255-7.098 13.255-13.254 0-.2-.005-.402-.014-.602a9.47 9.47 0 0 0 2.323-2.41l.002-.003z" />}
{social.name === 'LinkedIn' && <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 0 1-2.063-2.065 2.064 2.064 0 1 1 2.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.225 0z" />}
{social.name === 'RSS Feed' && <path d="M4 11a9 9 0 0 1 9 9M4 4a16 16 0 0 1 16 16M6 19a1 1 0 1 1 0-2 1 1 0 0 1 0 2z" />}
</svg> </svg>
</a> </a>
))} ))}
@ -175,6 +180,8 @@ const services = [
<span class="link-divider">|</span> <span class="link-divider">|</span>
<a href="/terms" class="meta-link">Terms of Use</a> <a href="/terms" class="meta-link">Terms of Use</a>
<span class="link-divider">|</span> <span class="link-divider">|</span>
<a href="/contact" class="meta-link">Contact</a>
<span class="link-divider">|</span>
<a href="/sitemap.xml" class="meta-link">Sitemap</a> <a href="/sitemap.xml" class="meta-link">Sitemap</a>
</div> </div>
</div> </div>

View File

@ -11,7 +11,7 @@ const navItems = [
{ name: 'Home Lab', url: '/homelab' }, // Updated URL { name: 'Home Lab', url: '/homelab' }, // Updated URL
{ name: 'Resources', url: '/resources' }, { name: 'Resources', url: '/resources' },
{ name: 'About', url: 'https://ArgoBox.com' }, { name: 'About', url: 'https://ArgoBox.com' },
{ name: 'Contact', url: 'https://ArgoBox.com/index.html#contact' } { name: 'Contact', url: '/contact' }
]; ];
// Get current URL path for active nav item highlighting // Get current URL path for active nav item highlighting

View File

@ -413,6 +413,49 @@ const nodeTypeCounts = {
setTimeout(() => { loadingEl.classList.add('hidden'); }, 500); setTimeout(() => { loadingEl.classList.add('hidden'); }, 500);
} }
// --- Fullscreen Toggle Functionality ---
if (fullscreenToggle) {
fullscreenToggle.addEventListener('click', toggleFullscreen);
}
// Toggle fullscreen function
function toggleFullscreen() {
isFullscreen = !isFullscreen;
if (isFullscreen) {
// Enable fullscreen mode
graphWrapper.classList.add('fullscreen');
fullscreenEnterIcon.classList.add('hidden');
fullscreenExitIcon.classList.remove('hidden');
// Hide the node details panel if it's visible
if (nodeDetailsEl) nodeDetailsEl.classList.remove('active');
// In fullscreen, we adjust the cytoscape layout to fit
setTimeout(() => {
cy.resize();
cy.fit(null, 30);
}, 300); // Wait for transition to complete
} else {
// Disable fullscreen mode
graphWrapper.classList.remove('fullscreen');
fullscreenEnterIcon.classList.remove('hidden');
fullscreenExitIcon.classList.add('hidden');
// Hide the full post content panel
if (fullPostContent) {
fullPostContent.classList.remove('active');
// Ensure display is set back to none if needed, though CSS handles visibility
// fullPostContent.style.display = 'none';
}
// Reset the cytoscape layout
setTimeout(() => {
cy.resize();
cy.fit(null, 30);
}, 300); // Wait for transition to complete
}
}
// --- Fullscreen Toggle Functionality --- // --- Fullscreen Toggle Functionality ---
if (fullscreenToggle) { if (fullscreenToggle) {
fullscreenToggle.addEventListener('click', toggleFullscreen); fullscreenToggle.addEventListener('click', toggleFullscreen);
@ -764,6 +807,7 @@ const nodeTypeCounts = {
if (nodeDetailsEl) nodeDetailsEl.classList.remove('active'); if (nodeDetailsEl) nodeDetailsEl.classList.remove('active');
if (isFullscreen && fullPostContent) { if (isFullscreen && fullPostContent) {
fullPostContent.classList.remove('active'); fullPostContent.classList.remove('active');
fullPostContent.style.display = 'none'; // Ensure display is none
} }
cy.elements().removeClass('selected highlighted faded'); cy.elements().removeClass('selected highlighted faded');
} }
@ -776,6 +820,17 @@ const nodeTypeCounts = {
cy.elements().removeClass('highlighted faded'); cy.elements().removeClass('highlighted faded');
}); });
} }
// Close full post panel button
if (closeFullPostBtn) {
closeFullPostBtn.addEventListener('click', () => {
if (fullPostContent) {
fullPostContent.classList.remove('active');
fullPostContent.style.display = 'none'; // Ensure display is none
}
cy.$(':selected').unselect();
cy.elements().removeClass('highlighted faded');
});
}
// Close full post panel button // Close full post panel button
if (closeFullPostBtn) { if (closeFullPostBtn) {
@ -1979,4 +2034,350 @@ const nodeTypeCounts = {
@keyframes spin { @keyframes spin {
to { transform: rotate(360deg); } to { transform: rotate(360deg); }
} }
/* Fullscreen mode styles */
.graph-container-wrapper.fullscreen {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
max-height: 100vh;
z-index: 9999;
margin: 0;
padding: 20px;
background: var(--bg-primary);
backdrop-filter: blur(10px);
display: flex;
border-radius: 0;
transition: all 0.5s cubic-bezier(0.19, 1, 0.22, 1);
}
/* Adjust graph container in fullscreen mode */
.graph-container-wrapper.fullscreen .graph-container {
position: relative;
width: 60%;
height: 100%;
border-radius: 12px;
margin-right: 20px;
}
/* Fullscreen Toggle Button */
.fullscreen-toggle {
position: absolute;
top: 20px;
right: 20px;
width: 40px;
height: 40px;
border-radius: 8px;
background: rgba(30, 41, 59, 0.7);
border: 1px solid var(--border-primary);
color: var(--text-primary);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 4;
transition: all 0.2s ease;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.fullscreen-toggle:hover {
background: rgba(30, 41, 59, 0.9);
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
}
.graph-container-wrapper.fullscreen .fullscreen-toggle {
top: 25px;
right: 25px;
}
/* Hide one of the fullscreen icons */
.hidden {
display: none;
}
/* Full Post Content Panel */
.full-post-content {
position: absolute;
top: 0;
right: 0;
width: 0;
height: 100%;
overflow: hidden;
transition: all 0.3s cubic-bezier(0.19, 1, 0.22, 1);
background: var(--bg-secondary);
border-radius: 12px;
opacity: 0;
z-index: 4;
border: 1px solid var(--card-border);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
visibility: hidden;
display: none;
}
/* Full post panel in fullscreen mode */
.graph-container-wrapper.fullscreen .full-post-content {
position: relative;
width: 40%;
height: 100%;
background: rgba(30, 41, 59, 0.7);
backdrop-filter: blur(10px);
display: flex;
flex-direction: column;
opacity: 1;
visibility: visible;
}
.graph-container-wrapper.fullscreen .full-post-content.active {
display: flex;
}
.full-post-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 1.5rem;
border-bottom: 1px solid var(--border-primary);
}
.full-post-title {
font-size: 1.5rem;
margin: 0;
color: var(--text-primary);
font-weight: 600;
line-height: 1.3;
}
.full-post-metadata {
padding: 1rem 1.5rem;
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.full-post-category {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.85rem;
font-weight: 500;
font-family: var(--font-mono);
}
.full-post-tags {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.post-tag {
background: rgba(16, 185, 129, 0.1);
color: #10B981;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
font-family: var(--font-mono);
}
.full-post-container {
flex: 1;
padding: 1.5rem;
overflow-y: auto;
color: var(--text-primary);
}
.full-post-container::-webkit-scrollbar {
width: 8px;
}
.full-post-container::-webkit-scrollbar-track {
background: rgba(30, 41, 59, 0.5);
border-radius: 4px;
}
.full-post-container::-webkit-scrollbar-thumb {
background: rgba(226, 232, 240, 0.1);
border-radius: 4px;
}
.full-post-container::-webkit-scrollbar-thumb:hover {
background: rgba(226, 232, 240, 0.2);
}
.full-post-placeholder {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: var(--text-secondary);
text-align: center;
padding: 2rem;
}
.full-post-placeholder svg {
margin-bottom: 1rem;
opacity: 0.5;
}
.post-content {
line-height: 1.7;
font-size: 1rem;
}
.post-content h1, .post-content h2, .post-content h3,
.post-content h4, .post-content h5, .post-content h6 {
margin-top: 1.5rem;
margin-bottom: 1rem;
color: var(--text-primary);
font-weight: 600;
}
.post-content p {
margin-bottom: 1.25rem;
}
.post-content a {
color: var(--accent-primary);
text-decoration: none;
border-bottom: 1px dotted var(--accent-primary);
transition: all 0.2s ease;
}
.post-content a:hover {
color: var(--accent-secondary);
border-bottom-style: solid;
}
.post-content pre {
background: rgba(15, 23, 42, 0.6);
padding: 1rem;
border-radius: 6px;
overflow-x: auto;
margin: 1.5rem 0;
border: 1px solid var(--border-primary);
}
.post-content code {
font-family: var(--font-mono);
font-size: 0.9rem;
}
.post-content img {
max-width: 100%;
height: auto;
margin: 1.5rem 0;
border-radius: 6px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.post-content blockquote {
border-left: 4px solid var(--accent-primary);
padding-left: 1rem;
margin-left: 0;
margin-right: 0;
font-style: italic;
color: var(--text-secondary);
}
.post-content ul, .post-content ol {
padding-left: 1.5rem;
margin-bottom: 1.25rem;
}
.post-content li {
margin-bottom: 0.5rem;
}
.full-post-link {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 1rem;
background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary));
color: var(--bg-primary);
text-decoration: none;
font-weight: 500;
transition: all 0.3s ease;
}
.full-post-link:hover {
background: linear-gradient(90deg, var(--accent-secondary), var(--accent-primary));
}
/* Media Queries */
@media screen and (max-width: 992px) {
/* Adjust fullscreen layout for smaller screens */
.graph-container-wrapper.fullscreen {
flex-direction: column;
padding: 15px;
}
.graph-container-wrapper.fullscreen .graph-container {
width: 100%;
height: 50%;
margin-right: 0;
margin-bottom: 15px;
}
.graph-container-wrapper.fullscreen .full-post-content {
width: 100%;
height: 50%;
}
}
@media screen and (max-width: 768px) {
.fullscreen-toggle {
top: 15px;
right: 15px;
width: 36px;
height: 36px;
}
.graph-container-wrapper.fullscreen .graph-container {
height: 40%;
}
.graph-container-wrapper.fullscreen .full-post-content {
height: 60%;
}
.full-post-header {
padding: 1rem;
}
.full-post-title {
font-size: 1.25rem;
}
.full-post-metadata {
padding: 0.75rem 1rem;
}
.full-post-container {
padding: 1rem;
}
}
@media screen and (max-width: 480px) {
.graph-container-wrapper.fullscreen {
padding: 10px;
}
.graph-container-wrapper.fullscreen .graph-container {
height: 35%;
}
.graph-container-wrapper.fullscreen .full-post-content {
height: 65%;
}
.full-post-title {
font-size: 1.1rem;
}
}
</style> </style>

View File

@ -0,0 +1,230 @@
---
// src/components/homelab/HeroSection.astro - Ultra minimal version
const { servicesCount } = Astro.props;
---
<section id="home" class="hero">
<div class="container">
<div class="hero-content">
<div class="hero-text">
<h1 class="hero-title">
Enterprise-Grade <span class="highlight">Home Lab</span> Environment
</h1>
<p class="hero-description">
A production-ready infrastructure platform for DevOps experimentation, distributed systems, and automating everything with code.
</p>
<!-- Simple stats display -->
<div class="stats-box">
<div class="stat-item">
<div class="stat-label">Services:</div>
<div class="stat-value">{servicesCount}+</div>
</div>
<div class="stat-item">
<div class="stat-label">CPU Cores:</div>
<div class="stat-value">32+</div>
</div>
<div class="stat-item">
<div class="stat-label">Memory:</div>
<div class="stat-value">64GB</div>
</div>
<div class="stat-item">
<div class="stat-label">Storage:</div>
<div class="stat-value">12TB</div>
</div>
</div>
<div class="cta-buttons">
<a href="/ansible-sandbox" class="btn btn-danger" id="ansible-sandbox-btn">
<span class="btn-text">Try Ansible Sandbox</span>
<span class="offline-badge">Offline</span>
</a>
<a href="#architecture" class="btn btn-outline">
<span class="btn-text">Explore Architecture</span>
</a>
</div>
</div>
<!-- Simple status box instead of terminal -->
<div class="status-box">
<div class="status-header">
<div class="status-title">System Status</div>
</div>
<div class="status-body">
<div class="status-line">
<span class="status-label">Nodes:</span>
<span class="status-value">2 (Ready)</span>
</div>
<div class="status-line">
<span class="status-label">Running Pods:</span>
<span class="status-value">32</span>
</div>
<div class="status-line">
<span class="status-label">Uptime:</span>
<span class="status-value">154 days</span>
</div>
<div class="status-line">
<span class="status-label">Load:</span>
<span class="status-value">0.22, 0.18, 0.15</span>
</div>
</div>
</div>
</div>
</div>
</section>
<style>
.hero {
min-height: 100vh;
display: flex;
align-items: center;
position: relative;
overflow: hidden;
padding: 6rem 0 4rem;
background: linear-gradient(180deg, var(--bg-secondary), var(--bg-primary));
}
.hero-content {
display: flex;
flex-wrap: wrap;
gap: 2rem;
align-items: center;
justify-content: space-between;
}
.hero-text {
flex: 1;
min-width: 300px;
}
.hero-title {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.highlight {
color: var(--primary, #3b82f6);
}
.hero-description {
font-size: 1.125rem;
margin-bottom: 2rem;
color: var(--text-secondary);
}
.stats-box {
background-color: rgba(15, 23, 42, 0.7);
border: 1px solid var(--border);
border-radius: 0.5rem;
padding: 1.5rem;
margin: 2rem 0;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1rem;
}
.stat-item {
display: flex;
flex-direction: column;
}
.stat-label {
font-size: 0.875rem;
color: var(--text-secondary);
}
.stat-value {
font-size: 1.5rem;
font-weight: bold;
color: var(--success, #10b981);
}
.cta-buttons {
display: flex;
gap: 1rem;
margin-top: 2rem;
flex-wrap: wrap;
}
.btn {
display: inline-flex;
align-items: center;
padding: 0.75rem 1.5rem;
border-radius: 0.375rem;
font-weight: 500;
text-decoration: none;
transition: all 0.2s ease;
}
.btn-danger {
background-color: var(--error, #ef4444);
color: white;
}
.btn-outline {
background-color: transparent;
border: 1px solid var(--border);
color: var(--text-primary);
}
.offline-badge {
background-color: rgba(0, 0, 0, 0.2);
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
border-radius: 1rem;
margin-left: 0.5rem;
}
.status-box {
background-color: rgba(15, 23, 42, 0.9);
border-radius: 0.5rem;
overflow: hidden;
width: 100%;
max-width: 350px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
}
.status-header {
padding: 0.75rem 1rem;
background-color: rgba(30, 41, 59, 0.8);
}
.status-title {
font-size: 0.875rem;
color: var(--text-secondary);
}
.status-body {
padding: 1rem;
}
.status-line {
display: flex;
justify-content: space-between;
margin-bottom: 0.75rem;
}
.status-label {
color: var(--text-secondary);
}
.status-value {
color: var(--success, #10b981);
}
@media (max-width: 768px) {
.hero-content {
flex-direction: column;
align-items: flex-start;
}
.stats-box {
grid-template-columns: repeat(2, 1fr);
}
.status-box {
max-width: none;
width: 100%;
}
}
</style>

View File

@ -0,0 +1,119 @@
---
// src/components/homelab/ProjectsSection.astro
// Making sure we properly handle the projectsData prop
const { projectsData } = Astro.props;
// If projectsData is undefined or not an array, provide a fallback
const projects = Array.isArray(projectsData) ? projectsData : [];
// Debug logging
console.log("ProjectsSection received projectsData:", !!projectsData);
console.log("ProjectsSection projects count:", projects.length);
---
<section id="projects" class="projects section-padding alt-bg">
<div class="container">
<div class="section-header">
<h2 class="section-title">Related Projects</h2>
<p class="section-description">
Explore associated projects and repositories related to the ArgoBox lab.
</p>
</div>
<div class="projects-grid">
{projects.length > 0 ? (
projects.map(project => (
<a
href={project.url}
class="project-card"
target="_blank"
rel="noopener noreferrer"
>
<div class="project-header">
<div class="project-icon"><i class={project.icon}></i></div>
<h3 class="project-title">{project.title}</h3>
</div>
<p class="project-description">{project.description}</p>
<div class="project-tech">
{project.tech && project.tech.map(tech => (
<span class="tech-badge">{tech}</span>
))}
</div>
</a>
))
) : (
<div class="no-projects">
<p>No projects available at this time.</p>
</div>
)}
</div>
</div>
</section>
<style>
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.project-card {
padding: 1.5rem;
border-radius: 0.5rem;
background-color: var(--bg-card);
border: 1px solid var(--border);
transition: all 0.3s ease;
text-decoration: none;
color: var(--text-primary);
}
.project-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
}
.project-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.project-icon {
font-size: 1.5rem;
color: var(--primary);
}
.project-title {
font-size: 1.25rem;
margin: 0;
}
.project-description {
margin-bottom: 1.5rem;
color: var(--text-secondary);
}
.project-tech {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.tech-badge {
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
background-color: var(--bg-secondary);
font-size: 0.75rem;
}
.no-projects {
grid-column: 1 / -1;
text-align: center;
padding: 3rem;
background-color: var(--bg-card);
border-radius: 0.5rem;
border: 1px solid var(--border);
}
</style>

View File

@ -0,0 +1,80 @@
// src/components/homelab/ServiceCategory.astro
---
const { category } = Astro.props;
function getCategoryIcon(categoryKey) {
if (categoryKey === 'development') return 'fa-code';
if (categoryKey === 'media') return 'fa-photo-video';
if (categoryKey === 'utilities') return 'fa-tools';
return 'fa-cogs'; // Default for infrastructure
}
const categoryIcon = getCategoryIcon(category.key);
const categoryTitle = category.key.charAt(0).toUpperCase() + category.key.slice(1);
---
<div class="services-category" data-category={category.key}>
<h3 class="category-title">
<i class={`fas ${categoryIcon} category-icon`}></i>
{categoryTitle} Tools
</h3>
<div class="service-items">
{category.services.map(service => (
<a
href={service.available ? service.url : '#'}
class={`service-item ${service.available ? 'available' : ''} service-${service.name.toLowerCase().replace(/\s+/g, '-')}`}
target={service.available ? "_blank" : null}
rel={service.available ? "noopener noreferrer" : null}
aria-disabled={!service.available}
data-service={service.name.toLowerCase().replace(/\s+/g, '-')}
>
<div class="service-icon"><i class={service.icon}></i></div>
<div class="service-info">
<div class="service-name">{service.name}</div>
<p class="service-description">{service.description}</p>
</div>
<span class={`service-status ${service.status}`}>
<span class="status-dot"></span>
{service.status.charAt(0).toUpperCase() + service.status.slice(1)}
</span>
</a>
))}
</div>
</div>
<style>
.services-category {
margin-bottom: 2rem;
}
.category-title {
display: flex;
align-items: center;
margin-bottom: 1rem;
font-size: 1.5rem;
}
.category-icon {
margin-right: 0.75rem;
opacity: 0.8;
}
.service-items {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
}
.service-item {
display: flex;
padding: 1.25rem;
border-radius: 0.5rem;
background-color: var(--bg-card);
border: 1px solid var(--border);
transition: all 0.3s ease;
text-decoration: none;
color: var(--text-primary);
position: relative;
}
</style>

View File

@ -0,0 +1,201 @@
---
// src/components/homelab/ServicesSection.astro
// Making sure servicesData is properly handled
const { servicesData } = Astro.props;
// Helper functions
function getCategoryIcon(category) {
if (category === 'development') return 'fa-code';
if (category === 'media') return 'fa-photo-video';
if (category === 'utilities') return 'fa-tools';
return 'fa-cogs'; // Default for infrastructure
}
function formatServiceName(name) {
return name.toLowerCase().replace(/\s+/g, '-');
}
// Create a safe version of the data in case it's undefined
const safeServicesData = servicesData || {};
// Prepare data for rendering to minimize template expressions
const categoryEntries = Object.entries(safeServicesData).map(([key, services]) => {
return {
key,
title: key.charAt(0).toUpperCase() + key.slice(1),
services: Array.isArray(services) ? services : []
};
});
// Debug logging
console.log("ServicesSection received servicesData:", !!servicesData);
console.log("ServicesSection categories count:", categoryEntries.length);
---
<section id="services" class="services section-padding">
<div class="container">
<div class="section-header">
<h2 class="section-title">Available Services</h2>
<p class="section-description">
Explore the various services and applications hosted in the ArgoBox environment.
</p>
</div>
<div class="services-info-banner">
<i class="fas fa-info-circle info-icon"></i>
<p>Some services require authentication and are restricted. Available public services are highlighted and clickable.</p>
</div>
<div class="services-grid">
{categoryEntries.length > 0 ? (
categoryEntries.map(category => (
<div class="services-category" data-category={category.key}>
<h3 class="category-title">
<i class={`fas ${getCategoryIcon(category.key)} category-icon`}></i>
{category.title} Tools
</h3>
<div class="service-items">
{category.services.map(service => (
<a
href={service.available ? service.url : '#'}
class={`service-item ${service.available ? 'available' : ''} service-${formatServiceName(service.name)}`}
target={service.available ? "_blank" : null}
rel={service.available ? "noopener noreferrer" : null}
aria-disabled={!service.available}
>
<div class="service-icon"><i class={service.icon}></i></div>
<div class="service-info">
<div class="service-name">{service.name}</div>
<p class="service-description">{service.description}</p>
</div>
<span class={`service-status ${service.status}`}>
<span class="status-dot"></span>
{service.status.charAt(0).toUpperCase() + service.status.slice(1)}
</span>
</a>
))}
</div>
</div>
))
) : (
<div class="no-services">
<p>No services available at this time.</p>
</div>
)}
</div>
</div>
</section>
<style>
.services-grid {
display: grid;
grid-template-columns: 1fr;
gap: 2rem;
margin-top: 2rem;
}
.services-info-banner {
display: flex;
align-items: center;
padding: 1rem;
background-color: rgba(59, 130, 246, 0.1);
border-radius: 0.5rem;
margin-top: 1rem;
}
.info-icon {
margin-right: 1rem;
color: var(--primary, #3b82f6);
}
.services-category {
margin-bottom: 2rem;
}
.category-title {
display: flex;
align-items: center;
margin-bottom: 1rem;
font-size: 1.5rem;
}
.category-icon {
margin-right: 0.75rem;
opacity: 0.8;
}
.service-items {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
}
.service-item {
display: flex;
padding: 1.25rem;
border-radius: 0.5rem;
background-color: var(--bg-card);
border: 1px solid var(--border);
transition: all 0.3s ease;
text-decoration: none;
color: var(--text-primary);
position: relative;
}
.service-item.available:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.service-icon {
margin-right: 1rem;
font-size: 1.25rem;
color: var(--primary);
}
.service-info {
flex: 1;
}
.service-name {
font-weight: 500;
margin-bottom: 0.5rem;
}
.service-description {
font-size: 0.875rem;
color: var(--text-secondary);
}
.service-status {
display: flex;
align-items: center;
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
border-radius: 1rem;
background-color: rgba(0, 0, 0, 0.1);
margin-left: 1rem;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 0.25rem;
background-color: var(--success);
}
.service-status.offline .status-dot {
background-color: var(--error);
}
.no-services {
grid-column: 1 / -1;
text-align: center;
padding: 3rem;
background-color: var(--bg-card);
border-radius: 0.5rem;
border: 1px solid var(--border);
}
</style>

View File

@ -35,22 +35,6 @@ const postsCollection = defineCollection({
}), }),
}); });
// Define the external posts collection (for external articles)
const externalPostsCollection = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string().optional(),
pubDate: z.coerce.date(),
url: z.string().url(),
heroImage: z.string().optional(),
source: z.string().optional(),
category: z.string().optional(),
categories: z.array(z.string()).optional(),
tags: z.array(z.string()).default([]),
}),
});
// Define the configurations collection (for config files) // Define the configurations collection (for config files)
const configurationsCollection = defineCollection({ const configurationsCollection = defineCollection({
type: 'content', type: 'content',
@ -94,7 +78,6 @@ const projectsCollection = defineCollection({
// Export the collections // Export the collections
export const collections = { export const collections = {
posts: postsCollection, posts: postsCollection,
'external-posts': externalPostsCollection,
configurations: configurationsCollection, configurations: configurationsCollection,
projects: projectsCollection, projects: projectsCollection,
}; };

View File

@ -0,0 +1,65 @@
---
title: 'Getting Started with Infrastructure as Code'
description: 'Learn the basics of Infrastructure as Code and how to start using it in your projects.'
pubDate: '2023-11-15'
heroImage: '/images/posts/blog-infrastructure-as-code.jpg'
categories: ['Infrastructure', 'DevOps']
tags: ['terraform', 'infrastructure', 'cloud', 'automation']
minutesRead: '5 min'
---
# Getting Started with Infrastructure as Code
Infrastructure as Code (IaC) is a key DevOps practice that involves managing and provisioning infrastructure through code instead of manual processes. This approach brings the same rigor, transparency, and version control to infrastructure that developers have long applied to application code.
## Why Infrastructure as Code?
IaC offers numerous benefits for modern DevOps teams:
- **Consistency**: Infrastructure deployments become reproducible and standardized
- **Version Control**: Track changes to your infrastructure just like application code
- **Automation**: Reduce manual errors and increase deployment speed
- **Documentation**: Your code becomes self-documenting
- **Testing**: Infrastructure can be tested before deployment
## Popular IaC Tools
There are several powerful tools for implementing IaC:
1. **Terraform**: Cloud-agnostic, works with multiple providers
2. **AWS CloudFormation**: Specific to AWS infrastructure
3. **Azure Resource Manager**: Microsoft's native IaC solution
4. **Google Cloud Deployment Manager**: For Google Cloud resources
5. **Pulumi**: Uses general-purpose programming languages
## Basic Terraform Example
Here's a simple example of Terraform code that provisions an AWS EC2 instance:
```hcl
provider "aws" {
region = "us-west-2"
}
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "Web Server"
Environment = "Development"
}
}
```
## Getting Started
To begin your IaC journey:
1. Choose a tool that fits your infrastructure needs
2. Start small with a simple resource
3. Learn about state management
4. Implement CI/CD for your infrastructure code
5. Consider using modules for reusability
Infrastructure as Code transforms how teams provision and manage resources, enabling more reliable, consistent deployments while reducing overhead and errors.

View File

@ -1,27 +0,0 @@
---
title: "Blog Posts Collection"
description: "Documentation for blog posts"
pubDate: 2025-04-18
draft: true
---
# Blog Posts Collection
This directory contains blog posts for the ArgoBox digital garden.
## Content Guidelines
- All posts should include proper frontmatter
- Use Markdown for formatting content
- Images should be placed in the public/blog/images directory
## Frontmatter Requirements
Every post needs at minimum:
```
---
title: "Post Title"
pubDate: YYYY-MM-DD
---
```

View File

@ -1,12 +0,0 @@
---
title: This is a test
description: How to set up Cloudflare Tunnels for secure remote access to your home lab services
pubDate: Jul 22 2023
heroImage: /images/posts/prometheusk8.png
category: networking
tags:
- Tag A
- Tag B
- Tag C
readTime: "7 min read"
---

View File

@ -2,7 +2,7 @@
title: Secure Remote Access with Cloudflare Tunnels title: Secure Remote Access with Cloudflare Tunnels
description: How to set up Cloudflare Tunnels for secure remote access to your home lab services description: How to set up Cloudflare Tunnels for secure remote access to your home lab services
pubDate: 2025-04-19 pubDate: 2025-04-19
heroImage: /images/posts/prometheusk8.png heroImage: /images/posts/blog-cloudflare-tunnel.jpg
category: networking category: networking
tags: tags:
- cloudflare - cloudflare

View File

@ -10,7 +10,7 @@ tags:
- kubernetes - kubernetes
- docker - docker
- file-management - file-management
heroImage: /images/posts/prometheusk8.png heroImage: /images/posts/blog-filebrowser-setup.jpg
--- ---
I've said it before, and I'll say it again - the journey to a well-organized digital life begins with proper file management. If you're like me, you've got files scattered across multiple devices, cloud services, and servers. What if I told you there's a lightweight, sleek solution that puts you back in control without relying on third-party services? I've said it before, and I'll say it again - the journey to a well-organized digital life begins with proper file management. If you're like me, you've got files scattered across multiple devices, cloud services, and servers. What if I told you there's a lightweight, sleek solution that puts you back in control without relying on third-party services?

View File

@ -10,7 +10,7 @@ tags:
- self-hosted - self-hosted
- devops - devops
- kubernetes - kubernetes
heroImage: /images/posts/prometheusk8.png heroImage: /images/posts/blog-self-hosted-git.jpg
--- ---
If you're a developer like me who values ownership and privacy, you've probably wondered if there's a way to get the convenience of GitHub or GitLab without handing over your code to a third party. Enter Gitea - a painless, self-hosted Git service written in Go that I've been using for my personal projects for the past year. If you're a developer like me who values ownership and privacy, you've probably wondered if there's a way to get the convenience of GitHub or GitLab without handing over your code to a third party. Enter Gitea - a painless, self-hosted Git service written in Go that I've been using for my personal projects for the past year.

View File

@ -2,7 +2,7 @@
title: GitOps with Flux CD title: GitOps with Flux CD
description: Implementing GitOps workflows on Kubernetes using Flux CD description: Implementing GitOps workflows on Kubernetes using Flux CD
pubDate: 2025-04-19 pubDate: 2025-04-19
heroImage: /images/posts/prometheusk8.png heroImage: /images/posts/blog-gitops-with-flux-cd.jpg
category: devops category: devops
tags: tags:
- kubernetes - kubernetes

View File

@ -2,7 +2,7 @@
title: K3s Installation Guide title: K3s Installation Guide
description: A comprehensive guide to installing and configuring K3s for your home lab description: A comprehensive guide to installing and configuring K3s for your home lab
pubDate: 2025-04-19 pubDate: 2025-04-19
heroImage: /images/posts/k3installation.png heroImage: /images/posts/guide-k3-installation.jpg
category: kubernetes category: kubernetes
tags: tags:
- kubernetes - kubernetes

View File

@ -2,7 +2,7 @@
title: "Setting Up a K3s Kubernetes Cluster" title: "Setting Up a K3s Kubernetes Cluster"
description: "A comprehensive guide to setting up a K3s cluster for your home lab or edge environment, with high availability and persistent storage." description: "A comprehensive guide to setting up a K3s cluster for your home lab or edge environment, with high availability and persistent storage."
pubDate: "2023-11-15" pubDate: "2023-11-15"
heroImage: "/blog/images/posts/k3installation.png" heroImage: "/images/posts/guide-k3s-cluster.jpg"
category: "Kubernetes" category: "Kubernetes"
tags: ["kubernetes", "k3s", "homelab", "infrastructure"] tags: ["kubernetes", "k3s", "homelab", "infrastructure"]
draft: false draft: false

View File

@ -2,7 +2,7 @@
title: "Monitoring Your Kubernetes Cluster with Prometheus and Grafana" title: "Monitoring Your Kubernetes Cluster with Prometheus and Grafana"
description: "A comprehensive guide to setting up a robust monitoring solution for your Kubernetes cluster using Prometheus and Grafana." description: "A comprehensive guide to setting up a robust monitoring solution for your Kubernetes cluster using Prometheus and Grafana."
pubDate: "2023-09-25" pubDate: "2023-09-25"
heroImage: "/blog/images/posts/prometheus-dashboard.svg" heroImage: "/images/posts/blog-prometheus-monitoring.jpg"
category: "Monitoring" category: "Monitoring"
tags: ["kubernetes", "prometheus", "grafana", "monitoring", "observability"] tags: ["kubernetes", "prometheus", "grafana", "monitoring", "observability"]
draft: false draft: false

View File

@ -2,7 +2,7 @@
title: Complete Proxmox VE Setup Guide title: Complete Proxmox VE Setup Guide
description: A step-by-step guide to setting up Proxmox VE for your home lab virtualization needs description: A step-by-step guide to setting up Proxmox VE for your home lab virtualization needs
pubDate: 2025-04-19 pubDate: 2025-04-19
heroImage: /images/posts/prometheusk8.png heroImage: /images/posts/blog-proxmox-setup.jpg
category: infrastructure category: infrastructure
tags: tags:
- proxmox - proxmox

View File

@ -10,7 +10,7 @@ tags:
- digital-garden - digital-garden
- knowledge-management - knowledge-management
- astro - astro
heroImage: /images/posts/prometheusk8.png heroImage: /images/posts/blog-quartz-digital-garden.jpg
--- ---
I've been taking digital notes for decades now. From simple `.txt` files to OneNote, Evernote, Notion, and now Obsidian. But for years, I've been wrestling with a question: how do I share my knowledge with others in a way that preserves the connections between ideas? I've been taking digital notes for decades now. From simple `.txt` files to OneNote, Evernote, Notion, and now Obsidian. But for years, I've been wrestling with a question: how do I share my knowledge with others in a way that preserves the connections between ideas?

View File

@ -10,7 +10,7 @@ tags:
- k3s - k3s
- devops - devops
- containers - containers
heroImage: /images/posts/prometheusk8.png heroImage: /images/posts/blog-home-lab-with-rancher.jpg
--- ---
I've been running Kubernetes at home for years now, and I've tried just about every management tool out there. From kubectl and a bunch of YAML files to various dashboards and UIs, I've experimented with it all. But the one tool that's been a constant in my home lab journey is [Rancher](https://rancher.com/) - a complete container management platform that makes Kubernetes management almost... dare I say it... enjoyable? I've been running Kubernetes at home for years now, and I've tried just about every management tool out there. From kubectl and a bunch of YAML files to various dashboards and UIs, I've experimented with it all. But the one tool that's been a constant in my home lab journey is [Rancher](https://rancher.com/) - a complete container management platform that makes Kubernetes management almost... dare I say it... enjoyable?

View File

@ -2,7 +2,7 @@
title: "Starting My Digital Garden" title: "Starting My Digital Garden"
description: "How and why I'm approaching this blog as a digital garden rather than a traditional chronological blog." description: "How and why I'm approaching this blog as a digital garden rather than a traditional chronological blog."
pubDate: "2023-10-05" pubDate: "2023-10-05"
heroImage: "/blog/images/placeholders/default.jpg" heroImage: "/images/posts/blog-digital-garden.jpg"
category: "Meta" category: "Meta"
tags: ["digital-garden", "knowledge-management", "learning-in-public"] tags: ["digital-garden", "knowledge-management", "learning-in-public"]
draft: false draft: false

View File

@ -1,14 +0,0 @@
---
title: Test Post
pubDate: 2024-03-20
description: This is a test post to verify the blog setup
category: Test
tags:
- test
draft: true
heroImage: /images/posts/prometheusk8.png
---
# Test Post
This is a test post to verify that the blog setup is working correctly.

View File

@ -10,7 +10,7 @@ tags:
- self-hosted - self-hosted
- coding - coding
- homelab - homelab
heroImage: /images/posts/prometheusk8.png heroImage: /images/posts/blog-vs-code-server.jpg
--- ---
If you're like me, you probably find yourself coding on multiple devices - maybe a desktop at home, a laptop when traveling, or even occasionally on a tablet. For years, keeping development environments in sync was a pain point. Enter [VS Code Server](https://code.visualstudio.com/docs/remote/vscode-server), the solution that has completely transformed my development workflow. If you're like me, you probably find yourself coding on multiple devices - maybe a desktop at home, a laptop when traveling, or even occasionally on a tablet. For years, keeping development environments in sync was a pain point. Enter [VS Code Server](https://code.visualstudio.com/docs/remote/vscode-server), the solution that has completely transformed my development workflow.

40
src/data/homelabData.ts Normal file
View File

@ -0,0 +1,40 @@
export const servicesData = {
development: [
{ name: "Gitea", description: "Self-hosted Git service", url: "https://git.argobox.com", status: "live", icon: "fas fa-code-branch", available: true },
{ name: "VS Code Server", description: "Remote development environment", url: "https://code.argobox.com", status: "live", icon: "fas fa-terminal", available: true },
{ name: "Drone CI", description: "Continuous Integration server", url: "https://drone.argobox.com", status: "live", icon: "fas fa-cogs", available: true },
],
media: [
{ name: "Plex", description: "Media streaming server", url: "https://plex.argobox.com", status: "live", icon: "fas fa-play-circle", available: true },
{ name: "Jellyfin", description: "Open source media system", url: "https://jellyfin.argobox.com", status: "live", icon: "fas fa-film", available: true },
{ name: "Sonarr", description: "TV show management", url: "#", status: "restricted", icon: "fas fa-tv", available: false },
{ name: "Radarr", description: "Movie management", url: "#", status: "restricted", icon: "fas fa-video", available: false },
{ name: "Prowlarr", description: "Indexer management", url: "#", status: "restricted", icon: "fas fa-search", available: false },
],
utilities: [
{ name: "File Browser", description: "Web file manager", url: "https://files.argobox.com", status: "live", icon: "fas fa-folder-open", available: true },
{ name: "Vaultwarden", description: "Password manager", url: "#", status: "restricted", icon: "fas fa-key", available: false },
{ name: "Homepage", description: "Service dashboard", url: "https://dash.argobox.com", status: "live", icon: "fas fa-tachometer-alt", available: true },
{ name: "Uptime Kuma", description: "Status monitoring", url: "https://status.argobox.com", status: "live", icon: "fas fa-heartbeat", available: true },
],
infrastructure: [
{ name: "Proxmox VE", description: "Virtualization platform", url: "#", status: "restricted", icon: "fas fa-server", available: false },
{ name: "Kubernetes (K3s)", description: "Container orchestration", url: "#", status: "restricted", icon: "fas fa-dharmachakra", available: false },
{ name: "Traefik", description: "Ingress controller", url: "#", status: "restricted", icon: "fas fa-route", available: false },
{ name: "OPNsense", description: "Firewall/Router", url: "#", status: "restricted", icon: "fas fa-shield-alt", available: false },
]
};
export const projectsData = [
{ title: "Ansible Playbooks", description: "Collection of playbooks for automating system configuration and application deployment.", icon: "fab fa-ansible", tech: ["Ansible", "YAML", "Jinja2"], url: "/resources/iac" },
{ title: "Kubernetes Manifests", description: "YAML definitions for deploying various applications and services on Kubernetes.", icon: "fas fa-dharmachakra", tech: ["Kubernetes", "YAML", "Helm"], url: "/resources/kubernetes" },
{ title: "Monitoring Dashboards", description: "Grafana dashboards for visualizing infrastructure and application metrics.", icon: "fas fa-chart-line", tech: ["Grafana", "PromQL", "JSON"], url: "/resources/config-files" },
{ title: "Cloudflare Tunnel Setup", description: "Securely exposing home lab services to the internet using Cloudflare Tunnels.", icon: "fas fa-cloud", tech: ["Cloudflare", "Networking", "Security"], url: "/posts/cloudflare-tunnel-setup" }
];
export const dashboardsData = [
{ title: "Infrastructure Overview", description: "Key metrics for Proxmox hosts, network devices, and storage.", previewClass: "infrastructure", url: "https://dash.argobox.com/goto/...", icon: "fas fa-server" },
{ title: "Kubernetes Cluster", description: "Detailed view of K3s cluster resources, node status, and pod health.", previewClass: "kubernetes", url: "https://dash.argobox.com/goto/...", icon: "fas fa-dharmachakra" },
{ title: "Network Traffic", description: "Real-time and historical network usage, firewall logs, and connection tracking.", previewClass: "network", url: "https://dash.argobox.com/goto/...", icon: "fas fa-network-wired" },
{ title: "Service Performance", description: "Application-specific metrics, request latency, and error rates.", previewClass: "services", url: "https://dash.argobox.com/goto/...", icon: "fas fa-cogs" }
];

View File

@ -79,6 +79,9 @@ const {
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<!-- Theme CSS --> <!-- Theme CSS -->
<link rel="stylesheet" href="/src/styles/theme.css" /> <link rel="stylesheet" href="/src/styles/theme.css" />

View File

@ -0,0 +1,15 @@
---
// src/layouts/MinimalLayout.astro
---
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Minimal Test</title>
</head>
<body>
<main>
<slot />
</main>
</body>
</html>

View File

@ -1,10 +1,6 @@
import type { APIRoute } from 'astro'; import type { APIRoute } from 'astro';
import { Resend } from 'resend'; import { Resend } from 'resend';
// Initialize Resend with API key from environment variables
// IMPORTANT: Set RESEND_API_KEY in your deployment environment (e.g., Cloudflare Pages)
const resend = new Resend(import.meta.env.RESEND_API_KEY);
// Define the expected structure of the form data // Define the expected structure of the form data
interface FormData { interface FormData {
name: string; name: string;
@ -14,6 +10,10 @@ interface FormData {
} }
export const POST: APIRoute = async ({ request }) => { export const POST: APIRoute = async ({ request }) => {
// Initialize Resend inside the handler to access runtime env vars/secrets
// IMPORTANT: Set RESEND_API_KEY in your deployment environment (e.g., Cloudflare Pages)
const resend = new Resend(import.meta.env.RESEND_API_KEY);
// Check if API key is configured // Check if API key is configured
if (!import.meta.env.RESEND_API_KEY) { if (!import.meta.env.RESEND_API_KEY) {
console.error("Resend API key is not configured."); console.error("Resend API key is not configured.");
@ -56,7 +56,7 @@ export const POST: APIRoute = async ({ request }) => {
<p><strong>Message:</strong></p> <p><strong>Message:</strong></p>
<p>${data.message.replace(/\n/g, '<br>')}</p> <p>${data.message.replace(/\n/g, '<br>')}</p>
`, `,
reply_to: data.email, // Set the reply-to header to the sender's email replyTo: data.email, // Set the reply-to header to the sender's email
}); });
if (emailError) { if (emailError) {

View File

@ -99,17 +99,17 @@ sortedPosts
// Terminal commands for tech effect // Terminal commands for tech effect
const commands = [ const commands = [
{ {
prompt: "[laforceit@argobox]$ ", prompt: "[user@argobox]$ ",
command: "find ./posts -type f -name \"*.md\" | sort -n | wc -l", command: "find ./posts -type f -name \"*.md\" | sort -n | wc -l",
output: [`${allPosts.length} posts found`] output: [`${allPosts.length} posts found`]
}, },
{ {
prompt: "[laforceit@argobox]$ ", prompt: "[user@argobox]$ ",
command: "ls -la ./tags", command: "ls -la ./tags",
output: allTags.map(tag => `${tag}`) output: allTags.map(tag => `${tag}`)
}, },
{ {
prompt: "[laforceit@argobox]$ ", prompt: "[user@argobox]$ ",
command: "grep -r \"kubernetes\" --include=\"*.md\" ./posts | wc -l", command: "grep -r \"kubernetes\" --include=\"*.md\" ./posts | wc -l",
output: [`${allPosts.filter(post => output: [`${allPosts.filter(post =>
post.data.tags?.includes('kubernetes') || post.data.tags?.includes('kubernetes') ||

View File

@ -1,838 +1,56 @@
--- ---
// src/pages/homelab.astro - Converted from static homelab.html // src/pages/homelab.astro - Fixed main file with proper component imports
import BaseLayout from '../layouts/BaseLayout.astro'; import BaseLayout from '../layouts/BaseLayout.astro';
import Header from '../components/Header.astro'; import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro'; import Footer from '../components/Footer.astro';
import HeroSection from '../components/homelab/HeroSection.astro';
import ServicesSection from '../components/homelab/ServicesSection.astro';
import ProjectsSection from '../components/homelab/ProjectsSection.astro';
import { servicesData, projectsData, dashboardsData } from '../data/homelabData';
const title = "Home Lab | Argobox - ArgoBox Tech Hub"; const title = "Home Lab | ArgoBox - ArgoBox Tech Hub";
const description = "ArgoBox - A production-grade Kubernetes homelab for DevOps experimentation, infrastructure automation, and containerized application deployment."; const description = "ArgoBox - A production-grade Kubernetes homelab for DevOps experimentation, infrastructure automation, and containerized application deployment.";
// Data for services (can be fetched or defined here) // Calculate total services count for the hero section
// Example structure - replace with actual data source if needed const servicesCount = Object.values(servicesData).flat().length;
const servicesData = {
development: [
{ name: "Gitea", description: "Self-hosted Git service", url: "https://git.argobox.com", status: "live", icon: "fas fa-code-branch", available: true },
{ name: "VS Code Server", description: "Remote development environment", url: "https://code.argobox.com", status: "live", icon: "fas fa-terminal", available: true },
{ name: "Drone CI", description: "Continuous Integration server", url: "https://drone.argobox.com", status: "live", icon: "fas fa-cogs", available: true },
],
media: [
{ name: "Plex", description: "Media streaming server", url: "https://plex.argobox.com", status: "live", icon: "fas fa-play-circle", available: true },
{ name: "Jellyfin", description: "Open source media system", url: "https://jellyfin.argobox.com", status: "live", icon: "fas fa-film", available: true },
{ name: "Sonarr", description: "TV show management", url: "#", status: "restricted", icon: "fas fa-tv", available: false },
{ name: "Radarr", description: "Movie management", url: "#", status: "restricted", icon: "fas fa-video", available: false },
{ name: "Prowlarr", description: "Indexer management", url: "#", status: "restricted", icon: "fas fa-search", available: false },
],
utilities: [
{ name: "File Browser", description: "Web file manager", url: "https://files.argobox.com", status: "live", icon: "fas fa-folder-open", available: true },
{ name: "Vaultwarden", description: "Password manager", url: "https://vault.argobox.com", status: "live", icon: "fas fa-key", available: true },
{ name: "Homepage", description: "Service dashboard", url: "https://dash.argobox.com", status: "live", icon: "fas fa-tachometer-alt", available: true },
{ name: "Uptime Kuma", description: "Status monitoring", url: "https://status.argobox.com", status: "live", icon: "fas fa-heartbeat", available: true },
],
infrastructure: [
{ name: "Proxmox VE", description: "Virtualization platform", url: "#", status: "restricted", icon: "fas fa-server", available: false },
{ name: "Kubernetes (K3s)", description: "Container orchestration", url: "#", status: "restricted", icon: "fas fa-dharmachakra", available: false },
{ name: "Traefik", description: "Ingress controller", url: "#", status: "restricted", icon: "fas fa-route", available: false },
{ name: "OPNsense", description: "Firewall/Router", url: "#", status: "restricted", icon: "fas fa-shield-alt", available: false },
]
};
// Data for projects (can be fetched or defined here)
const projectsData = [
{ title: "Ansible Playbooks", description: "Collection of playbooks for automating system configuration and application deployment.", icon: "fab fa-ansible", tech: ["Ansible", "YAML", "Jinja2"], url: "/resources/iac" }, // Link to relevant resource page
{ title: "Kubernetes Manifests", description: "YAML definitions for deploying various applications and services on Kubernetes.", icon: "fas fa-dharmachakra", tech: ["Kubernetes", "YAML", "Helm"], url: "/resources/kubernetes" },
{ title: "Monitoring Dashboards", description: "Grafana dashboards for visualizing infrastructure and application metrics.", icon: "fas fa-chart-line", tech: ["Grafana", "PromQL", "JSON"], url: "/resources/config-files" }, // Link to relevant resource page
{ title: "Cloudflare Tunnel Setup", description: "Securely exposing home lab services to the internet using Cloudflare Tunnels.", icon: "fas fa-cloud", tech: ["Cloudflare", "Networking", "Security"], url: "/posts/cloudflare-tunnel-setup" } // Link to blog post
];
// Data for dashboards (can be fetched or defined here)
const dashboardsData = [
{ title: "Infrastructure Overview", description: "Key metrics for Proxmox hosts, network devices, and storage.", previewClass: "infrastructure", url: "https://dash.argobox.com/goto/...", icon: "fas fa-server" },
{ title: "Kubernetes Cluster", description: "Detailed view of K3s cluster resources, node status, and pod health.", previewClass: "kubernetes", url: "https://dash.argobox.com/goto/...", icon: "fas fa-dharmachakra" },
{ title: "Network Traffic", description: "Real-time and historical network usage, firewall logs, and connection tracking.", previewClass: "network", url: "https://dash.argobox.com/goto/...", icon: "fas fa-network-wired" },
{ title: "Service Performance", description: "Application-specific metrics, request latency, and error rates.", previewClass: "services", url: "https://dash.argobox.com/goto/...", icon: "fas fa-cogs" }
];
// Data for contact form (if keeping it on this page)
// const contactInfo = { email: "daniel.laforce@argobox.com", /* other info */ };
--- ---
<BaseLayout {title} {description}> <BaseLayout {title} {description}>
{/* Add Font Awesome if not loaded globally by BaseLayout */}
{/* <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> */}
<Header slot="header" /> <Header slot="header" />
<main class="homelab-page"> <main class="homelab-page">
<HeroSection servicesCount={servicesCount} />
<!-- Hero Section --> <ServicesSection servicesData={servicesData} />
<section id="home" class="hero"> <ProjectsSection projectsData={projectsData} />
<div class="particles-container" id="particles-container"></div>
<div class="container">
<div class="hero-content">
<div class="hero-text">
<h1 class="hero-title">
Enterprise-Grade <span class="highlight">Home Lab</span> Environment
</h1>
<p class="hero-description">
A production-ready infrastructure platform for DevOps experimentation, distributed systems, and automating everything with code.
</p>
<div class="hero-stats">
<div class="stat-item">
<div class="stat-icon"><i class="fas fa-microchip"></i></div>
<div class="stat-detail">
<div class="stat-value">32+</div>
<div class="stat-name">CPU Cores</div>
</div>
</div>
<div class="stat-item">
<div class="stat-icon"><i class="fas fa-memory"></i></div>
<div class="stat-detail">
<div class="stat-value">64GB</div>
<div class="stat-name">RAM</div>
</div>
</div>
<div class="stat-item">
<div class="stat-icon"><i class="fas fa-hdd"></i></div>
<div class="stat-detail">
<div class="stat-value">12TB</div>
<div class="stat-name">Storage</div>
</div>
</div>
<div class="stat-item">
<div class="stat-icon"><i class="fas fa-server"></i></div>
<div class="stat-detail">
<div class="stat-value">{Object.values(servicesData).flat().length}+</div>
<div class="stat-name">Services</div>
</div>
</div>
</div>
<div class="cta-buttons">
{/* Link to the Astro page for Ansible Sandbox */}
<a href="/ansible-sandbox" class="btn btn-danger" id="ansible-sandbox-btn">
<i class="fab fa-ansible btn-icon"></i>
<span class="btn-text">Try Ansible Sandbox</span>
<span class="offline-badge">Offline</span> {/* Status updated by JS */}
</a>
<a href="#architecture" class="btn btn-outline">
<i class="fas fa-network-wired btn-icon"></i>
<span class="btn-text">Explore Architecture</span>
</a>
</div>
</div>
<div class="hero-terminal">
<div class="terminal-header">
<div class="terminal-buttons">
<span class="terminal-btn close"></span>
<span class="terminal-btn minimize"></span>
<span class="terminal-btn maximize"></span>
</div>
<div class="terminal-title">argobox ~ k8s-status</div>
</div>
<div class="terminal-body">
<div class="terminal-line">$ kubectl get nodes</div>
<div class="terminal-line output">NAME STATUS ROLES AGE VERSION</div>
<div class="terminal-line output">argobox Ready control-plane,master 154d v1.25.16+k3s1</div>
<div class="terminal-line output">argobox-lite Ready worker 154d v1.25.16+k3s1</div>
<div class="terminal-line blank">&nbsp;</div>
<div class="terminal-line">$ kubectl get pods -A | grep Running | wc -l</div>
<div class="terminal-line output">32</div>
<div class="terminal-line blank">&nbsp;</div>
<div class="terminal-line">$ uptime</div>
<div class="terminal-line output">14:30:25 up 154 days, 23:12, 1 user, load average: 0.22, 0.18, 0.15</div>
<div class="terminal-line blank">&nbsp;</div>
<div class="terminal-line">$ ansible-playbook status.yml</div>
<div class="terminal-line output">PLAY [Check system status] *******************************************</div>
<div class="terminal-line output">TASK [Gathering Facts] **********************************************</div>
<div class="terminal-line output success">ok: [argobox]</div>
<div class="terminal-line output success">ok: [argobox-lite]</div>
<div class="terminal-line output">TASK [Check service status] *****************************************</div>
<div class="terminal-line output success">ok: [argobox]</div>
<div class="terminal-line output success">ok: [argobox-lite]</div>
<div class="terminal-line output">PLAY RECAP **********************************************************</div>
<div class="terminal-line output success">argobox : ok=2 changed=0 unreachable=0 failed=0 skipped=0</div>
<div class="terminal-line output success">argobox-lite: ok=2 changed=0 unreachable=0 failed=0 skipped=0</div>
<div class="terminal-line typing">$ <span class="cursor">|</span></div>
</div>
</div>
</div>
</div>
</section>
<!-- Architecture Section -->
<section id="architecture" class="architecture section-padding">
<div class="container">
<div class="section-header">
<h2 class="section-title">Infrastructure Architecture</h2>
<p class="section-description">
Enterprise-grade network topology with redundancy, virtualization, and secure segmentation.
</p>
</div>
{/* Consider replacing with an actual image or a more maintainable diagram component */}
<img src="/images/homelab/argobox-architecture.svg" alt="ArgoBox Architecture Diagram" class="architecture-diagram-image" />
{/* Fallback or simplified diagram if image isn't available */}
<div class="architecture-details">
<div class="detail-card">
<div class="detail-icon"><i class="fas fa-shield-alt"></i></div>
<h3 class="detail-title">Network Security</h3>
<p class="detail-description">
Enterprise firewall with network segmentation using VLANs and strict access controls. Redundant routing with automatic failover between OPNsense and OpenWrt.
</p>
</div>
<div class="detail-card">
<div class="detail-icon"><i class="fas fa-cloud"></i></div>
<h3 class="detail-title">Virtualization</h3>
<p class="detail-description">
Proxmox virtualization platform with ZFS storage pools in RAID10 configuration. Optimized storage pools for VMs and containers with proper resource allocation.
</p>
</div>
<div class="detail-card">
<div class="detail-icon"><i class="fas fa-route"></i></div>
<h3 class="detail-title">High Availability</h3>
<p class="detail-description">
Full redundancy with failover routing, replicated storage, and resilient services. Automatic service recovery and load balancing across nodes.
</p>
</div>
</div>
</div>
</section>
<!-- Technologies Section -->
<section id="technologies" class="technologies section-padding alt-bg">
<div class="container">
<div class="section-header">
<h2 class="section-title">Core Technologies</h2>
<p class="section-description">
The ArgoBox lab leverages cutting-edge open source technologies to create a powerful, flexible infrastructure.
</p>
</div>
<div class="tech-grid">
{/* Data could be externalized */}
<div class="tech-card">
<div class="tech-icon"><i class="fas fa-dharmachakra"></i></div>
<h3 class="tech-title">Kubernetes (K3s)</h3>
<p class="tech-description">Lightweight Kubernetes distribution running across multiple nodes for container orchestration. Powers all microservices and applications.</p>
<div class="tech-features"><span class="tech-feature">Multi-node cluster</span><span class="tech-feature">Persistent volumes</span><span class="tech-feature">Traefik ingress</span><span class="tech-feature">Auto-healing</span></div>
</div>
<div class="tech-card featured">
<div class="tech-icon"><i class="fab fa-ansible"></i></div>
<h3 class="tech-title">Ansible Automation</h3>
<p class="tech-description">Infrastructure as code platform for automated provisioning, configuration management, and application deployment across the entire environment.</p>
<div class="tech-features"><span class="tech-feature">Playbook library</span><span class="tech-feature">Role-based configs</span><span class="tech-feature">Interactive sandbox</span><span class="tech-feature">Idempotent workflows</span></div>
</div>
<div class="tech-card">
<div class="tech-icon"><i class="fas fa-server"></i></div>
<h3 class="tech-title">Proxmox</h3>
<p class="tech-description">Enterprise-class virtualization platform running virtual machines and containers with ZFS storage backend for data integrity.</p>
<div class="tech-features"><span class="tech-feature">ZFS storage</span><span class="tech-feature">Resource balancing</span><span class="tech-feature">Live migration</span><span class="tech-feature">Hardware passthrough</span></div>
</div>
<div class="tech-card">
<div class="tech-icon"><i class="fas fa-shield-alt"></i></div>
<h3 class="tech-title">Zero Trust Security</h3>
<p class="tech-description">Comprehensive security architecture with Cloudflare tunnels, network segmentation, and authentication at all service boundaries.</p>
<div class="tech-features"><span class="tech-feature">Cloudflare tunnels</span><span class="tech-feature">OPNsense firewall</span><span class="tech-feature">VLAN segmentation</span><span class="tech-feature">WireGuard VPN</span></div>
</div>
<div class="tech-card">
<div class="tech-icon"><i class="fas fa-database"></i></div>
<h3 class="tech-title">PostgreSQL</h3>
<p class="tech-description">Enterprise database cluster for application data storage with automated backups, replication, and performance optimization.</p>
<div class="tech-features"><span class="tech-feature">Automated backups</span><span class="tech-feature">Connection pooling</span><span class="tech-feature">Optimized for K8s</span><span class="tech-feature">Multi-app support</span></div>
</div>
<div class="tech-card">
<div class="tech-icon"><i class="fas fa-chart-line"></i></div>
<h3 class="tech-title">Monitoring Stack</h3>
<p class="tech-description">Comprehensive monitoring with Prometheus, Grafana, and AlertManager for real-time visibility into all infrastructure components.</p>
<div class="tech-features"><span class="tech-feature">Prometheus metrics</span><span class="tech-feature">Grafana dashboards</span><span class="tech-feature">Automated alerts</span><span class="tech-feature">Historical data</span></div>
</div>
</div>
</div>
</section>
<!-- Services Section -->
<section id="services" class="services section-padding">
<div class="container">
<div class="section-header">
<h2 class="section-title">Available Services</h2>
<p class="section-description">
Explore the various services and applications hosted in the ArgoBox environment.
</p>
</div>
<div class="services-info-banner">
<i class="fas fa-info-circle info-icon"></i>
<p>Some services require authentication and are restricted. Available public services are highlighted and clickable.</p>
</div>
<div class="services-grid">
{Object.entries(servicesData).map(([categoryKey, categoryServices]) => (
<div class="services-category">
<h3 class="category-title">
<i class={`fas ${
categoryKey === 'development' ? 'fa-code' :
categoryKey === 'media' ? 'fa-photo-video' :
categoryKey === 'utilities' ? 'fa-tools' :
'fa-cogs' // Default for infrastructure
} category-icon`}></i>
{categoryKey.charAt(0).toUpperCase() + categoryKey.slice(1)} Tools
</h3>
<div class="service-items">
{categoryServices.map(service => (
<a
href={service.available ? service.url : '#'}
class:list={["service-item", { available: service.available }, `service-${service.name.toLowerCase().replace(/\s+/g, '-')}`]}
target={service.available ? "_blank" : undefined}
rel={service.available ? "noopener noreferrer" : undefined}
aria-disabled={!service.available}
>
<div class="service-icon"><i class={service.icon}></i></div>
<div class="service-info">
<div class="service-name">{service.name}</div>
<p class="service-description">{service.description}</p>
</div>
<span class={`service-status ${service.status}`}>
<span class="status-dot"></span>
{service.status.charAt(0).toUpperCase() + service.status.slice(1)}
</span>
</a>
))}
</div>
</div>
))}
</div>
</div>
</section>
<!-- Projects Section -->
<section id="projects" class="projects section-padding alt-bg">
<div class="container">
<div class="section-header">
<h2 class="section-title">Related Projects & Code</h2>
<p class="section-description">
Explore associated projects, configurations, and code repositories related to the ArgoBox lab.
</p>
</div>
<div class="projects-grid">
{projectsData.map(project => (
<a href={project.url} class="project-card" target={project.url.startsWith('http') ? '_blank' : undefined} rel={project.url.startsWith('http') ? 'noopener noreferrer' : undefined}>
<div class="project-header">
<div class="project-icon"><i class={project.icon}></i></div>
<h3 class="project-title">{project.title}</h3>
</div>
<p class="project-description">{project.description}</p>
<div class="project-tech">
{project.tech.map(tech => <span class="tech-badge">{tech}</span>)}
</div>
<div class="project-cta">
<span class="btn btn-sm btn-outline">
{project.url.startsWith('http') ? 'View Project' : 'View Details'} <i class="fas fa-arrow-right"></i>
</span>
</div>
</a>
))}
</div>
</div>
</section>
<!-- Dashboards Section -->
<section id="dashboards" class="dashboards section-padding">
<div class="container">
<div class="section-header">
<h2 class="section-title">Live Dashboards</h2>
<p class="section-description">
Real-time monitoring dashboards providing insights into the lab's performance and status. (Authentication Required)
</p>
</div>
<div class="services-info-banner"> {/* Reusing banner style */}
<i class="fas fa-lock info-icon"></i>
<p>Access to live dashboards requires authentication via Cloudflare Access.</p>
</div>
<div class="dashboard-grid">
{dashboardsData.map(dash => (
<a href={dash.url} class="dashboard-card" target="_blank" rel="noopener noreferrer">
<div class={`dashboard-preview ${dash.previewClass}`}>
<div class="dashboard-overlay">
<div class="overlay-content">
<div class="overlay-icon"><i class={dash.icon}></i></div>
<div class="overlay-text">View Dashboard</div>
</div>
</div>
</div>
<div class="dashboard-info">
<h3 class="dashboard-title">{dash.title}</h3>
<p class="dashboard-description">{dash.description}</p>
<div class="dashboard-cta">
<span class="btn btn-sm btn-primary">Access Dashboard</span>
</div>
</div>
</a>
))}
</div>
</div>
</section>
{/* Contact Section - Optional: Can be moved to a separate page */}
{/*
<section id="contact" class="contact section-padding alt-bg">
<div class="container">
<div class="section-header">
<h2 class="section-title">Get In Touch</h2>
<p class="section-description">Have questions or want to collaborate? Reach out!</p>
</div>
<div class="contact-grid">
<div class="contact-info">
<div class="contact-item">
<div class="contact-icon"><i class="fas fa-envelope"></i></div>
<h3 class="contact-title">Email</h3>
<a href={`mailto:${contactInfo.email}`} class="contact-link">{contactInfo.email}</a>
</div>
Add other contact items like LinkedIn, GitHub etc.
</div>
<div class="contact-form-container">
<form id="contact-form" class="contact-form">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" required />
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
</div>
<div class="form-group">
<label for="subject">Subject</label>
<input type="text" id="subject" name="subject" required />
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" name="message" required></textarea>
</div>
<button type="submit" class="btn btn-primary">Send Message</button>
</form>
</div>
</div>
</div>
</section>
*/}
</main> </main>
<Footer slot="footer" /> <Footer slot="footer" />
</BaseLayout> </BaseLayout>
<style is:global> <style is:global>
/* Import relevant styles from styles.css, adapted for Astro */ /* Global styles - can be moved to a separate CSS file if needed */
/* NOTE: General resets, body, typography, basic buttons are handled by global.css/theme.css */ .section-padding {
padding: 5rem 0;
/* Hero Section Specific Styles */
.hero {
min-height: 100vh; /* Use min-height instead of height */
display: flex;
align-items: center;
position: relative;
overflow: hidden;
padding-top: 6rem; /* Adjust as needed */
padding-bottom: 4rem; /* Add padding */
background: linear-gradient(180deg, var(--bg-secondary), var(--bg-primary)); /* Use theme vars */
} }
.hero-content {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 3rem;
align-items: center;
position: relative; /* Ensure content is above particles */
z-index: 1;
}
.particles-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
pointer-events: none;
z-index: 0;
}
.particle {
position: absolute;
background-color: var(--accent); /* Use theme var */
border-radius: 50%;
pointer-events: none;
/* Animation will be added by script */
}
@keyframes float-particle { /* Keep keyframes */
0% { transform: translateY(0) translateX(0); opacity: 0.3; }
50% { transform: translateY(-100px) translateX(50px); opacity: 0.1; }
100% { transform: translateY(0) translateX(0); opacity: 0.3; }
}
.hero-text { /* Styles seem okay */ }
.hero-title {
font-size: clamp(2.5rem, 5vw, 3.25rem); /* Adjust size */
margin-bottom: 1.5rem;
line-height: 1.2;
font-weight: 700; /* Use theme font weight */
color: var(--text-primary); /* Use theme var */
}
.hero-title .highlight {
background: var(--accent-gradient); /* Use theme var */
-webkit-background-clip: text;
background-clip: text;
color: transparent;
position: relative;
}
.hero-title .highlight::after { /* Optional underline effect */
content: '';
position: absolute;
width: 100%;
height: 4px; /* Adjust thickness */
bottom: 2px; /* Adjust position */
left: 0;
background: var(--accent-gradient);
border-radius: 2px;
opacity: 0.3;
z-index: -1;
}
.hero-description {
font-size: var(--font-size-lg); /* Use theme var */
color: var(--text-secondary); /* Use theme var */
margin-bottom: 2rem;
max-width: 550px;
}
.hero-stats {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
margin: 2rem 0;
}
.stat-item { display: flex; align-items: center; gap: 0.75rem; }
.stat-icon {
width: 3rem; height: 3rem; display: flex; align-items: center; justify-content: center;
background: var(--accent-gradient); border-radius: 0.5rem; color: white; font-size: 1.25rem;
}
.stat-detail { display: flex; flex-direction: column; }
.stat-value { font-weight: 700; font-size: 1.25rem; color: var(--text-primary); }
.stat-name { font-size: 0.875rem; color: var(--text-secondary); }
.cta-buttons { display: flex; flex-wrap: wrap; gap: 1rem; }
/* Use Astro theme button classes (.cta-button.primary/secondary) if possible */
/* Or adapt these styles */
.btn {
display: inline-flex; align-items: center; justify-content: center; padding: 0.75rem 1.5rem;
font-weight: 600; border-radius: 0.5rem; transition: all var(--transition-normal);
cursor: pointer; position: relative; overflow: hidden; border: none; outline: none; gap: 0.75rem;
text-decoration: none; /* Ensure links look like buttons */
}
.btn-primary { background: var(--accent-gradient); color: white; /* ... other styles */ }
.btn-primary:hover { /* ... hover styles */ }
.btn-outline { background-color: transparent; border: 2px solid var(--accent); color: var(--accent); }
.btn-outline:hover { background-color: var(--accent); color: white; }
.btn-danger { background-color: var(--error); border-color: var(--error); color: white; } /* Use theme error color */
.btn-icon { font-size: 0.9rem; }
.offline-badge, .online-badge { /* Styles from inline block */
display: inline-block; padding: 0.25rem 0.5rem; border-radius: 0.25rem;
font-size: 0.75rem; font-weight: 600; margin-left: 0.5rem;
}
.offline-badge { background-color: #991b1b; color: white; } /* Darker red */
.online-badge { background-color: var(--success); color: white; display: none; } /* Use theme success */
/* Hero Terminal Styles */
.hero-terminal {
width: 100%; border-radius: 0.75rem; overflow: hidden;
box-shadow: var(--card-shadow); /* Use theme shadow */
background-color: #0d1117; /* Keep specific terminal bg */
border: 1px solid var(--border); /* Use theme border */
/* transform: perspective(1000px) rotateY(5deg) rotateX(2deg); */ /* Remove or adjust transform */
transition: all 0.3s ease;
}
/* .hero-terminal:hover { transform: perspective(1000px) rotateY(0deg) rotateX(0deg); } */
.terminal-header { display: flex; align-items: center; background-color: #161b22; padding: 0.5rem 1rem; border-bottom: 1px solid var(--border); }
.terminal-buttons { display: flex; gap: 0.5rem; margin-right: 1rem; }
.terminal-btn { width: 12px; height: 12px; border-radius: 50%; }
.terminal-btn.close { background-color: #ff5f56; }
.terminal-btn.minimize { background-color: #ffbd2e; }
.terminal-btn.maximize { background-color: #27c93f; }
.terminal-title { color: var(--text-secondary); font-size: 0.875rem; font-family: var(--font-mono); }
.terminal-body { padding: 1rem; font-family: var(--font-mono); font-size: 0.875rem; color: var(--text-primary); line-height: 1.4; max-height: 400px; overflow-y: auto; }
.terminal-line { margin-bottom: 0.25rem; white-space: pre-wrap; }
.terminal-line.output { color: var(--text-secondary); }
.terminal-line.success { color: var(--success); } /* Use theme color */
.terminal-line.error { color: var(--error); } /* Use theme color */
.terminal-line.typing { color: var(--accent); } /* Use theme color */
.terminal-line .cursor { display: inline-block; width: 8px; height: 15px; background-color: var(--accent); animation: blink 1s step-end infinite; }
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
/* Architecture Section */
.architecture { /* Use section-padding */ }
.section-header { text-align: center; margin-bottom: 3rem; } /* Adjust margin */
.section-title { font-size: var(--font-size-3xl); margin-bottom: 1rem; position: relative; display: inline-block; color: var(--text-primary); }
.section-title::after { content: ''; position: absolute; bottom: -0.5rem; left: 50%; transform: translateX(-50%); width: 80px; height: 4px; background: var(--accent-gradient); border-radius: 2px; }
.section-description { max-width: 700px; margin: 0 auto; color: var(--text-secondary); font-size: var(--font-size-lg); }
.architecture-diagram-image { display: block; max-width: 800px; margin: 3rem auto; border-radius: 8px; box-shadow: var(--card-shadow); }
.architecture-details { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 2rem; margin-top: 3rem; }
.detail-card { background-color: var(--card-bg); border: 1px solid var(--border); border-radius: 0.75rem; padding: 1.5rem; transition: all var(--transition-normal); }
.detail-card:hover { transform: translateY(-5px); box-shadow: var(--card-shadow); border-color: var(--accent); }
.detail-icon { font-size: 2rem; color: var(--accent); margin-bottom: 1rem; }
.detail-title { font-size: 1.25rem; margin-bottom: 0.75rem; color: var(--text-primary); }
.detail-description { color: var(--text-secondary); font-size: var(--font-size-sm); line-height: 1.6; }
/* Technologies Section */
.technologies { /* Use section-padding alt-bg */ }
.tech-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 2rem; margin-top: 3rem; }
.tech-card { background-color: var(--card-bg); border: 1px solid var(--border); border-radius: 0.75rem; padding: 2rem; transition: all var(--transition-normal); display: flex; flex-direction: column; }
.tech-card:hover { transform: translateY(-10px); box-shadow: var(--card-shadow); border-color: var(--accent); }
.tech-card.featured { background: linear-gradient(145deg, rgba(59, 130, 246, 0.1), rgba(37, 99, 235, 0.05)); border-color: var(--accent); position: relative; overflow: hidden; }
.tech-card.featured::before { content: ''; position: absolute; top: 0; right: 0; width: 100px; height: 100px; background: var(--accent-gradient); transform: rotate(45deg) translate(20px, -60px); opacity: 0.3; }
.tech-icon { font-size: 2.5rem; color: var(--accent); margin-bottom: 1.5rem; transition: all var(--transition-normal); }
.tech-card:hover .tech-icon { transform: scale(1.1); /* text-shadow: var(--accent-glow); */ } /* Glow might be too much */
.tech-title { font-size: 1.5rem; margin-bottom: 1rem; color: var(--text-primary); }
.tech-description { color: var(--text-secondary); margin-bottom: 1.5rem; flex-grow: 1; font-size: var(--font-size-sm); }
.tech-features { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: auto; } /* Push to bottom */
.tech-feature { background-color: rgba(59, 130, 246, 0.1); border: 1px solid rgba(59, 130, 246, 0.3); color: var(--accent); font-size: 0.8rem; font-weight: 500; padding: 0.25rem 0.75rem; border-radius: 9999px; }
/* Services Section */
.services { /* Use section-padding */ }
.services-info-banner { display: flex; align-items: center; gap: 1rem; background-color: rgba(59, 130, 246, 0.1); border: 1px solid rgba(59, 130, 246, 0.2); border-radius: 0.75rem; padding: 1rem 1.5rem; margin-bottom: 2rem; }
.info-icon { font-size: 1.5rem; color: var(--accent); }
.services-info-banner p { color: var(--text-secondary); margin: 0; font-size: var(--font-size-sm); }
.services-grid { display: flex; flex-direction: column; gap: 2.5rem; margin-top: 2rem; }
.services-category { background-color: var(--card-bg); border: 1px solid var(--border); border-radius: 0.75rem; overflow: hidden; transition: all var(--transition-normal); }
.services-category:hover { transform: translateY(-5px); box-shadow: var(--card-shadow); border-color: var(--accent); }
.category-title { display: flex; align-items: center; gap: 0.75rem; padding: 1.25rem; background-color: rgba(15, 23, 42, 0.5); border-bottom: 1px solid var(--border); font-size: 1.35rem; color: var(--text-primary); }
.category-icon { color: var(--accent); }
.service-items { display: flex; flex-direction: column; }
.service-item { display: flex; align-items: center; padding: 1.25rem; border-bottom: 1px solid var(--border); transition: all var(--transition-normal); color: var(--text-primary); text-decoration: none; }
.service-item:last-child { border-bottom: none; }
.service-item.available { cursor: pointer; }
.service-item.available:hover { background-color: rgba(15, 23, 42, 0.3); }
.service-item:not(.available) { cursor: default; opacity: 0.6; } /* Dim unavailable */
.service-icon { width: 2.5rem; height: 2.5rem; display: flex; align-items: center; justify-content: center; margin-right: 1rem; background-color: rgba(59, 130, 246, 0.1); border-radius: 0.5rem; font-size: 1.25rem; color: var(--accent); transition: all var(--transition-normal); flex-shrink: 0; }
.service-item.available:hover .service-icon { background-color: rgba(59, 130, 246, 0.2); transform: scale(1.1); }
.service-info { flex: 1; }
.service-name { font-size: 1.1rem; font-weight: 600; margin-bottom: 0.25rem; color: var(--text-primary); }
.service-description { font-size: 0.9rem; color: var(--text-secondary); }
.service-status { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; font-weight: 500; padding: 0.25rem 0.75rem; border-radius: 9999px; margin-left: 1rem; }
.service-status.live { background-color: rgba(16, 185, 129, 0.1); color: var(--success); }
.service-status.maintenance { background-color: rgba(245, 158, 11, 0.1); color: var(--warning); }
.service-status.restricted { background-color: rgba(107, 114, 128, 0.1); color: var(--text-secondary); }
.status-dot { width: 8px; height: 8px; border-radius: 50%; }
.service-status.live .status-dot { background-color: var(--success); }
.service-status.maintenance .status-dot { background-color: var(--warning); }
.service-status.restricted .status-dot { background-color: var(--text-secondary); }
/* Offline service styling from inline */
.offline-service { position: relative; }
.offline-service::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.3); border-radius: 0.5rem; pointer-events: none; z-index: 1; }
.offline-service .service-icon { color: var(--error); }
.offline-service .service-status.offline .status-dot { background-color: var(--error); box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2); animation: pulse-red 2s infinite; }
@keyframes pulse-red { 0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); } 70% { box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); } 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } }
/* Projects Section */
.projects { /* Use section-padding alt-bg */ }
.projects-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 2rem; margin-top: 3rem; }
.project-card { background-color: var(--card-bg); border: 1px solid var(--border); border-radius: 0.75rem; padding: 1.5rem; transition: all var(--transition-normal); display: flex; flex-direction: column; text-decoration: none; } /* Added text-decoration */
.project-card:hover { transform: translateY(-10px); box-shadow: var(--card-shadow); border-color: var(--accent); }
.project-header { display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem; }
.project-icon { font-size: 1.75rem; color: var(--accent); transition: all var(--transition-normal); }
.project-card:hover .project-icon { transform: scale(1.1); }
.project-title { font-size: 1.25rem; margin: 0; color: var(--text-primary); }
.project-description { color: var(--text-secondary); margin-bottom: 1.5rem; flex-grow: 1; font-size: var(--font-size-sm); }
.project-tech { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.5rem; }
.tech-badge { background-color: rgba(59, 130, 246, 0.1); border: 1px solid rgba(59, 130, 246, 0.3); color: var(--accent); font-size: 0.8rem; font-weight: 500; padding: 0.25rem 0.75rem; border-radius: 9999px; }
.project-cta { margin-top: auto; }
.btn-sm { padding: 0.5rem 1rem; font-size: 0.875rem; } /* Define btn-sm if not global */
/* Dashboards Section */
.dashboards { /* Use section-padding */ }
.dashboard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; margin-top: 3rem; } /* Use auto-fit */
.dashboard-card { background: var(--card-bg); border-radius: 8px; overflow: hidden; transition: transform 0.3s ease, box-shadow 0.3s ease; border: 1px solid var(--border); text-decoration: none; }
.dashboard-card:hover { transform: translateY(-5px); box-shadow: var(--card-shadow); border-color: var(--accent); }
.dashboard-preview { height: 200px; background-size: cover; background-position: center; position: relative; background-color: var(--secondary-bg); /* Fallback bg */ }
/* Add specific preview backgrounds if needed, or use actual images */
.dashboard-preview.infrastructure { /* background-image: ... */ }
.dashboard-preview.kubernetes { /* background-image: ... */ }
.dashboard-preview.network { /* background-image: ... */ }
.dashboard-preview.services { /* background-image: ... */ }
.dashboard-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.7); display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.3s ease; }
.dashboard-card:hover .dashboard-overlay { opacity: 1; }
.overlay-content { text-align: center; color: #ffffff; }
.overlay-icon { font-size: 2rem; margin-bottom: 0.5rem; }
.overlay-text { font-size: 1rem; opacity: 0.8; }
.dashboard-info { padding: 1.5rem; }
.dashboard-title { font-size: 1.25rem; margin-bottom: 0.5rem; color: var(--text-primary); }
.dashboard-description { font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 1rem; }
.dashboard-cta .btn-sm { padding: 0.5rem 1rem; font-size: 0.9rem; }
/* General Responsive */
.section-padding { padding: 4rem 1rem; } /* Add horizontal padding */
.alt-bg { .alt-bg {
/* Use padding instead of negative margins for full bleed */
padding-top: 4rem;
padding-bottom: 4rem;
background-color: var(--bg-secondary); background-color: var(--bg-secondary);
border-top: 1px solid var(--border-secondary);
border-bottom: 1px solid var(--border-secondary);
} }
.alt-bg > .container { /* Ensure container inside alt-bg keeps padding */
padding-left: 1rem; .section-header {
padding-right: 1rem; text-align: center;
margin-bottom: 3rem;
} }
@media (max-width: 1024px) { .section-title {
.hero-content { grid-template-columns: 1fr; text-align: center; } font-size: 2.5rem;
.hero-text { order: 2; } margin-bottom: 1rem;
.hero-terminal { order: 1; margin: 0 auto 2rem; max-width: 600px; }
.hero-stats { justify-content: center; }
.cta-buttons { justify-content: center; }
} }
@media (max-width: 768px) {
.section-padding { padding: 3rem 1rem; } .section-description {
.alt-bg { padding-top: 3rem; padding-bottom: 3rem; } font-size: 1.125rem;
.hero { padding-top: 5rem; padding-bottom: 3rem; min-height: auto; } color: var(--text-secondary);
.hero-title { font-size: 2.25rem; } max-width: 700px;
.section-title { font-size: var(--font-size-2xl); } margin: 0 auto;
.section-header { flex-direction: column; align-items: center; gap: 0.5rem; margin-bottom: 2rem; }
.section-description { font-size: var(--font-size-base); }
.tech-grid, .projects-grid, .dashboard-grid, .architecture-details { grid-template-columns: 1fr; }
.services-grid { gap: 1.5rem; }
.service-item { flex-direction: column; align-items: flex-start; }
.service-status { margin-left: 0; margin-top: 0.5rem; }
} }
</style>
</style>
<script>
// Adapted from script.js
// --- Particle Animation ---
function createBackgroundParticles(count = 30) { // Reduced default count
const particlesContainer = document.getElementById('particles-container');
if (!particlesContainer) return;
particlesContainer.innerHTML = ''; // Clear existing
for (let i = 0; i < count; i++) {
const particle = document.createElement('div');
particle.classList.add('particle');
const size = Math.random() * 3 + 1; // Smaller size
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.left = `${Math.random() * 100}%`;
particle.style.top = `${Math.random() * 100}%`;
particle.style.opacity = (Math.random() * 0.2 + 0.05).toString(); // More subtle
particle.style.animation = `float-particle ${Math.random() * 25 + 15}s linear infinite`;
particle.style.animationDelay = `${Math.random() * 15}s`;
particlesContainer.appendChild(particle);
}
}
// --- Terminal Cursor ---
function initTerminalTyping() {
const cursor = document.querySelector('.hero-terminal .cursor');
if (cursor) {
setInterval(() => {
cursor.style.opacity = cursor.style.opacity === '0' ? '1' : '0';
}, 600);
}
}
// --- Status Checks (Placeholder - Requires actual API endpoints) ---
function checkAnsibleSandboxStatus() {
const btn = document.getElementById('ansible-sandbox-btn');
const badge = btn?.querySelector('.offline-badge');
if (!btn || !badge) return;
// Replace with actual API call
const isOnline = false; // Simulate offline for now
if (isOnline) {
btn.classList.remove('btn-danger');
btn.classList.add('btn-primary', 'pulse'); // Add pulse if desired
badge.textContent = 'Online';
badge.classList.remove('offline-badge');
badge.classList.add('online-badge');
badge.style.display = 'inline-block';
} else {
btn.classList.remove('btn-primary', 'pulse');
btn.classList.add('btn-danger');
badge.textContent = 'Offline';
badge.classList.remove('online-badge');
badge.classList.add('offline-badge');
badge.style.display = 'inline-block'; // Ensure it's shown
}
}
function checkLiveDashboardStatus() {
const indicator = document.querySelector('.dashboard-link .live-indicator');
if (!indicator) return;
// Replace with actual API call
const isOnline = false; // Simulate offline
if (isOnline) {
indicator.classList.remove('offline');
} else {
indicator.classList.add('offline');
}
}
// --- Initialization ---
document.addEventListener('DOMContentLoaded', function() {
if (document.getElementById('particles-container')) {
createBackgroundParticles();
// Optional: Add resize listener if particle count should change
// window.addEventListener('resize', () => createBackgroundParticles(window.innerWidth <= 768 ? 15 : 30));
}
if (document.querySelector('.hero-terminal .cursor')) {
initTerminalTyping();
}
// Initial status checks
checkAnsibleSandboxStatus();
checkLiveDashboardStatus();
// Optional: Set interval to re-check status periodically
// setInterval(checkAnsibleSandboxStatus, 60000); // Check every minute
// setInterval(checkLiveDashboardStatus, 60000);
});
// --- Contact Form Submission (If form is included on this page) ---
/*
const contactForm = document.getElementById('contact-form');
if (contactForm) {
contactForm.addEventListener('submit', async (e) => {
e.preventDefault();
const submitButton = contactForm.querySelector('button[type="submit"]');
const originalButtonText = submitButton.innerHTML;
const formMessages = contactForm.querySelector('.form-messages') || document.createElement('div'); // Add a div for messages if needed
formMessages.className = 'form-messages';
if (!contactForm.contains(formMessages)) {
contactForm.insertBefore(formMessages, submitButton);
}
formMessages.innerHTML = ''; // Clear previous messages
try {
submitButton.disabled = true;
submitButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Sending...';
const formData = new FormData(contactForm);
const data = Object.fromEntries(formData.entries());
const response = await fetch('/api/contact', { // Use the Astro API route
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await response.json();
if (response.ok) {
formMessages.innerHTML = `<div class="alert alert-success"><i class="fas fa-check-circle"></i> ${result.message}</div>`;
contactForm.reset();
} else {
throw new Error(result.message || 'Failed to send message');
}
} catch (error) {
console.error('Error sending contact form:', error);
formMessages.innerHTML = `<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ${error.message}</div>`;
} finally {
submitButton.disabled = false;
submitButton.innerHTML = originalButtonText;
setTimeout(() => { formMessages.innerHTML = ''; }, 5000); // Clear message after 5s
}
});
}
*/
</script>

View File

@ -6,7 +6,6 @@ import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro'; import Footer from '../components/Footer.astro';
import Terminal from '../components/Terminal.astro'; import Terminal from '../components/Terminal.astro';
import PostCard from '../components/PostCard.astro'; import PostCard from '../components/PostCard.astro';
import MiniKnowledgeGraph from '../components/MiniKnowledgeGraph.astro';
import '../styles/card-animations.css'; import '../styles/card-animations.css';
const title = "ArgoBox | Enterprise-Grade Home Lab & DevOps Hub"; const title = "ArgoBox | Enterprise-Grade Home Lab & DevOps Hub";
@ -48,32 +47,6 @@ const projectHighlights = [
} }
]; ];
// Enhanced Graph Data for Homepage Feature
const miniGraphData = {
nodes: [
{ id: 'kubernetes', label: 'Kubernetes', type: 'tag' },
{ id: 'homelab', label: 'Home Lab', type: 'tag' },
{ id: 'devops', label: 'DevOps', type: 'tag' },
{ id: 'automation', label: 'Automation', type: 'tag' },
{ id: 'infrastructure', label: 'Infrastructure', type: 'tag' },
{ id: 'post1', label: 'K3s Cluster Setup', type: 'post' },
{ id: 'post2', label: 'GitOps Workflow', type: 'post' },
{ id: 'post3', label: 'Home Lab Monitoring', type: 'post' },
{ id: 'post4', label: 'IaC Best Practices', type: 'post' },
],
edges: [
{ source: 'post1', target: 'kubernetes', type: 'post-tag' },
{ source: 'post1', target: 'homelab', type: 'post-tag' },
{ source: 'post2', target: 'devops', type: 'post-tag' },
{ source: 'post2', target: 'kubernetes', type: 'post-tag' },
{ source: 'post2', target: 'automation', type: 'post-tag' },
{ source: 'post3', target: 'homelab', type: 'post-tag' },
{ source: 'post3', target: 'infrastructure', type: 'post-tag' },
{ source: 'post4', target: 'infrastructure', type: 'post-tag' },
{ source: 'post4', target: 'automation', type: 'post-tag' },
]
};
// Define Commands for Hero Terminal // Define Commands for Hero Terminal
const heroCommands = [ const heroCommands = [
{ prompt: "[user@argobox]$ ", command: "uname -a", output: ["Linux argobox 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux"] }, { prompt: "[user@argobox]$ ", command: "uname -a", output: ["Linux argobox 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux"] },
@ -99,7 +72,7 @@ const heroCommands = [
</p> </p>
<div class="hero-cta"> <div class="hero-cta">
<a href="/blog" class="cta-button primary">Explore Guides</a> <a href="/blog" class="cta-button primary">Explore Guides</a>
<a href="#knowledge-graph" class="cta-button secondary">Discover Connections</a> <a href="/blog#knowledge-graph" class="cta-button secondary">Discover Connections</a>
</div> </div>
</div> </div>
<div class="terminal-container"> <div class="terminal-container">
@ -109,24 +82,6 @@ const heroCommands = [
</div> </div>
</section> </section>
<!-- Knowledge Graph Feature - Moved up for impact -->
<section id="knowledge-graph" class="graph-section section-padding">
<div class="container">
<div class="section-header">
<h2 class="section-title">Knowledge Graph</h2>
<p class="section-description">
Explore connections between infrastructure components, technologies, and implementation guides.
</p>
</div>
<div class="graph-container">
<MiniKnowledgeGraph graphData={miniGraphData} height="450px" />
</div>
<div class="graph-link-container">
<a href="/blog#knowledge-graph" class="cta-button primary">Explore Full Graph</a>
</div>
</div>
</section>
<!-- Intro Section - Reframed as community resource --> <!-- Intro Section - Reframed as community resource -->
<section class="intro-section section-padding"> <section class="intro-section section-padding">
<div class="container"> <div class="container">
@ -310,27 +265,6 @@ const heroCommands = [
.hero-cta { display: flex; gap: 1rem; } .hero-cta { display: flex; gap: 1rem; }
.terminal-container { flex: 1; max-width: 550px; } .terminal-container { flex: 1; max-width: 550px; }
/* Graph Section - New Prominent Section */
.graph-section {
background: var(--bg-primary);
position: relative;
overflow: hidden;
}
.graph-container {
border: 1px solid var(--border-primary);
border-radius: 12px;
overflow: hidden;
background: var(--bg-secondary);
margin-bottom: 2rem;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
position: relative;
}
.graph-link-container {
text-align: center;
}
/* Intro Section - Adjusted for community focus */ /* Intro Section - Adjusted for community focus */
.intro-section .section-title { text-align: center; margin-bottom: 1.5rem; } .intro-section .section-title { text-align: center; margin-bottom: 1.5rem; }
.intro-text { .intro-text {

View File

@ -0,0 +1,470 @@
---
// src/pages/projects/infrastructure-templates.astro
import BaseLayout from '../../layouts/BaseLayout.astro';
import Header from '../../components/Header.astro';
import Footer from '../../components/Footer.astro';
const title = "Infrastructure as Code Templates | ArgoBox";
const description = "Curated collection of reusable Terraform, Ansible, and Kubernetes manifests for building modern infrastructure environments.";
---
<BaseLayout title={title} description={description}>
<Header slot="header" />
<div class="container">
<div class="page-header">
<h1>Infrastructure as Code Templates</h1>
<div class="header-accent"></div>
</div>
<div class="coming-soon-container">
<div class="coming-soon-card">
<div class="icon-container">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="60" height="60" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<polyline points="16 18 22 12 16 6"></polyline>
<polyline points="8 6 2 12 8 18"></polyline>
<line x1="12" y1="2" x2="12" y2="22"></line>
</svg>
</div>
<h2>IaC Templates Coming Soon</h2>
<p class="description">
This section will provide a comprehensive collection of reusable Infrastructure as Code templates for building modern tech environments. These templates are designed to help you accelerate your infrastructure deployments with best practices built in.
</p>
<div class="template-categories">
<div class="template-category">
<div class="category-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect>
<rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect>
<line x1="6" y1="6" x2="6.01" y2="6"></line>
<line x1="6" y1="18" x2="6.01" y2="18"></line>
</svg>
</div>
<div class="category-content">
<h3>Terraform Modules</h3>
<p>Modular infrastructure components for cloud providers and on-premises environments.</p>
<ul class="template-list">
<li>Multi-cloud networking templates</li>
<li>Kubernetes cluster provisioning</li>
<li>Database deployment patterns</li>
<li>Security & compliance configurations</li>
</ul>
</div>
</div>
<div class="template-category">
<div class="category-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 20V10"></path>
<path d="M12 20V4"></path>
<path d="M6 20v-6"></path>
<path d="M18 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"></path>
<path d="M12 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"></path>
<path d="M6 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"></path>
</svg>
</div>
<div class="category-content">
<h3>Ansible Playbooks</h3>
<p>Automation workflows for system configuration and application deployment.</p>
<ul class="template-list">
<li>Server hardening & compliance</li>
<li>Application deployment patterns</li>
<li>Monitoring setup automation</li>
<li>Disaster recovery procedures</li>
</ul>
</div>
</div>
<div class="template-category">
<div class="category-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M22 12H2"></path>
<path d="M5 12l3-3"></path>
<path d="M5 12l3 3"></path>
<path d="M19 12l-3-3"></path>
<path d="M19 12l-3 3"></path>
</svg>
</div>
<div class="category-content">
<h3>Kubernetes Manifests</h3>
<p>Production-ready configurations for containerized applications.</p>
<ul class="template-list">
<li>Application deployment blueprints</li>
<li>GitOps-ready repository structure</li>
<li>Security policies & network configurations</li>
<li>Stateful workload templates</li>
</ul>
</div>
</div>
</div>
<div class="cta-container">
<a href="/blog?tag=infrastructure-as-code" class="cta-button secondary">
<span>Read Related Articles</span>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"></line>
<polyline points="12 5 19 12 12 19"></polyline>
</svg>
</a>
<button class="cta-button primary">
<span>Notify Me When Available</span>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
</svg>
</button>
</div>
</div>
</div>
<!-- Related Projects Section -->
<div class="related-projects">
<h2 class="section-title">Related Projects</h2>
<div class="projects-grid">
<a href="/projects/github" class="related-project">
<div class="project-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
</svg>
</div>
<h3>GitHub Repositories</h3>
<p>Open-source infrastructure code and automation scripts.</p>
<span class="project-link">View Project <span class="arrow">→</span></span>
</a>
<a href="/projects/tech-stack" class="related-project">
<div class="project-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="16.5" y1="9.4" x2="7.5" y2="4.21"></line>
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
<polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline>
<line x1="12" y1="22.08" x2="12" y2="12"></line>
</svg>
</div>
<h3>Tech Stack</h3>
<p>Explore the technologies used in the ArgoBox infrastructure.</p>
<span class="project-link">View Project <span class="arrow">→</span></span>
</a>
<a href="/homelab" class="related-project">
<div class="project-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect>
<rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect>
<line x1="6" y1="6" x2="6.01" y2="6"></line>
<line x1="6" y1="18" x2="6.01" y2="18"></line>
</svg>
</div>
<h3>Home Lab Infrastructure</h3>
<p>Enterprise-grade home lab environment with production services.</p>
<span class="project-link">View Project <span class="arrow">→</span></span>
</a>
</div>
</div>
</div>
<Footer slot="footer" />
</BaseLayout>
<style>
.container {
max-width: 1280px;
margin: 0 auto;
padding: 0 var(--container-padding, 1.5rem);
}
.page-header {
margin: 3rem 0 4rem;
position: relative;
}
h1 {
font-size: var(--font-size-4xl, 2.5rem);
background: linear-gradient(90deg, var(--accent-secondary, #3b82f6), var(--accent-primary, #06b6d4));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
display: inline-block;
margin-bottom: 0.5rem;
}
.header-accent {
width: 80px;
height: 4px;
background: linear-gradient(90deg, var(--accent-secondary, #3b82f6), var(--accent-primary, #06b6d4));
border-radius: 2px;
}
.coming-soon-container {
display: flex;
justify-content: center;
padding: 2rem 0 4rem;
}
.coming-soon-card {
background: var(--card-bg, #1e293b);
border: 1px solid var(--card-border, #334155);
border-radius: 16px;
padding: 2.5rem;
max-width: 900px;
width: 100%;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
position: relative;
overflow: hidden;
}
.coming-soon-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(circle at 30% 50%, rgba(59, 130, 246, 0.1), transparent 70%);
pointer-events: none;
}
.icon-container {
display: flex;
justify-content: center;
margin-bottom: 1.5rem;
color: var(--accent-secondary, #3b82f6);
}
h2 {
text-align: center;
margin-bottom: 1.5rem;
font-size: var(--font-size-3xl, 1.875rem);
color: var(--text-primary, #f1f5f9);
}
.description {
text-align: center;
margin-bottom: 3rem;
color: var(--text-secondary, #cbd5e1);
font-size: var(--font-size-lg, 1.125rem);
line-height: 1.7;
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.template-categories {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-bottom: 3rem;
}
.template-category {
background: rgba(30, 41, 59, 0.5);
border: 1px solid var(--border-primary, #334155);
border-radius: 12px;
padding: 1.5rem;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
}
.template-category:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
border-color: var(--accent-secondary, #3b82f6);
}
.category-icon {
color: var(--accent-secondary, #3b82f6);
margin-bottom: 1.5rem;
display: flex;
justify-content: center;
}
.category-content {
flex: 1;
}
.category-content h3 {
font-size: var(--font-size-xl, 1.25rem);
margin-bottom: 1rem;
color: var(--text-primary, #f1f5f9);
text-align: center;
}
.category-content p {
color: var(--text-secondary, #cbd5e1);
margin-bottom: 1.5rem;
text-align: center;
font-size: var(--font-size-md, 1rem);
}
.template-list {
list-style-type: none;
padding: 0;
margin: 0;
}
.template-list li {
padding: 0.75rem 0;
border-bottom: 1px solid rgba(203, 213, 225, 0.1);
color: var(--text-tertiary, #94a3b8);
font-size: var(--font-size-sm, 0.875rem);
position: relative;
padding-left: 1.5rem;
}
.template-list li:last-child {
border-bottom: none;
}
.template-list li::before {
content: '→';
position: absolute;
left: 0;
color: var(--accent-primary, #06b6d4);
}
.cta-container {
display: flex;
justify-content: center;
gap: 1.5rem;
margin-top: 2rem;
}
.cta-button {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 500;
font-size: var(--font-size-md, 1rem);
transition: all 0.3s ease;
cursor: pointer;
text-decoration: none;
}
.cta-button.primary {
background: linear-gradient(90deg, var(--accent-secondary, #3b82f6), var(--accent-primary, #06b6d4));
color: var(--bg-primary, #0f172a);
border: none;
box-shadow: 0 5px 15px rgba(59, 130, 246, 0.2);
}
.cta-button.secondary {
background: transparent;
border: 1px solid var(--accent-secondary, #3b82f6);
color: var(--accent-secondary, #3b82f6);
}
.cta-button.primary:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(59, 130, 246, 0.3);
}
.cta-button.secondary:hover {
transform: translateY(-3px);
background: rgba(59, 130, 246, 0.1);
}
/* Related Projects Section */
.related-projects {
margin: 4rem 0;
}
.section-title {
font-size: var(--font-size-2xl, 1.5rem);
color: var(--text-primary, #f1f5f9);
margin-bottom: 2rem;
position: relative;
padding-bottom: 0.75rem;
}
.section-title::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 60px;
height: 3px;
background: linear-gradient(90deg, var(--accent-secondary, #3b82f6), var(--accent-primary, #06b6d4));
border-radius: 2px;
}
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
.related-project {
background: var(--card-bg, #1e293b);
border: 1px solid var(--border-primary, #334155);
border-radius: 12px;
padding: 1.5rem;
text-decoration: none;
transition: all 0.3s ease;
}
.related-project:hover {
transform: translateY(-5px);
border-color: var(--accent-primary, #06b6d4);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
}
.project-icon {
color: var(--accent-primary, #06b6d4);
margin-bottom: 1rem;
}
.related-project h3 {
font-size: var(--font-size-lg, 1.125rem);
color: var(--text-primary, #f1f5f9);
margin-bottom: 0.75rem;
}
.related-project p {
color: var(--text-tertiary, #94a3b8);
font-size: var(--font-size-sm, 0.875rem);
margin-bottom: 1.5rem;
}
.project-link {
color: var(--accent-secondary, #3b82f6);
font-size: var(--font-size-sm, 0.875rem);
font-weight: 500;
display: flex;
align-items: center;
}
.arrow {
margin-left: 0.5rem;
transition: transform 0.3s ease;
}
.related-project:hover .arrow {
transform: translateX(5px);
}
@media (max-width: 768px) {
.template-categories {
grid-template-columns: 1fr;
}
.cta-container {
flex-direction: column;
}
.coming-soon-card {
padding: 2rem 1.5rem;
}
h1 {
font-size: var(--font-size-3xl, 1.875rem);
}
h2 {
font-size: var(--font-size-2xl, 1.5rem);
}
}
</style>

View File

@ -1,3 +1,4 @@
---
// src/pages/resources/docker-compose.astro // src/pages/resources/docker-compose.astro
import BaseLayout from '../../layouts/BaseLayout.astro'; import BaseLayout from '../../layouts/BaseLayout.astro';
import Header from '../../components/Header.astro'; import Header from '../../components/Header.astro';
@ -64,6 +65,7 @@ const filters = ["all", "media", "networking", "utilities"]; // Example filters
{filter.charAt(0).toUpperCase() + filter.slice(1)} {filter.charAt(0).toUpperCase() + filter.slice(1)}
</button> </button>
))} ))}
{/* <button class="filter-button active" data-filter="all">All</button> */} {/* Remove Placeholder */}
</div> </div>
<h2 class="section-title">Docker Compose Files</h2> <h2 class="section-title">Docker Compose Files</h2>
@ -387,7 +389,7 @@ const filters = ["all", "media", "networking", "utilities"]; // Example filters
} }
</style> </style>
<script> <script is:inline>
// Copy button functionality & Filter functionality // Copy button functionality & Filter functionality
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// Copy Button // Copy Button

9
src/pages/test.astro Normal file
View File

@ -0,0 +1,9 @@
---
// src/pages/test.astro
import MinimalLayout from '../layouts/MinimalLayout.astro';
---
<MinimalLayout>
<h1>Test Page</h1>
<p>This is a minimal test page to isolate the build issue.</p>
</MinimalLayout>