diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index a667266..d5cecf7 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -165,7 +165,7 @@ export default function Footer({ fullName = 'Cris Charles Garcia', socialLinks =
•
Contact
•
- Links Directory
+ Links Directory
diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro
index 1ab21bd..2dd0179 100644
--- a/src/layouts/Layout.astro
+++ b/src/layouts/Layout.astro
@@ -14,6 +14,7 @@ interface Props {
image?: string;
preloadImage?: string;
type?: 'website' | 'article' | 'profile';
+ canonicalURL?: string;
}
const {
@@ -21,7 +22,8 @@ const {
description = "Personal portfolio website of Cris Charles Garcia, showcasing projects, skills, experience, and custom AI chatbot assistant.",
image = "/logo.png",
preloadImage,
- type = "website"
+ type = "website",
+ canonicalURL
} = Astro.props;
const isRootPage = Astro.url.pathname === '/';
@@ -51,7 +53,7 @@ const isRootPage = Astro.url.pathname === '/';
-
+
diff --git a/src/middleware.ts b/src/middleware.ts
index cad60ec..bd8307e 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -1,25 +1,54 @@
import { defineMiddleware } from 'astro:middleware';
-export const onRequest = defineMiddleware((context, next) => {
+export const onRequest = defineMiddleware(async (context, next) => {
const isMaintenanceMode = import.meta.env.MAINTENANCE_MODE === 'true';
-
- // If maintenance mode is off, let all requests through
- if (!isMaintenanceMode) {
- return next();
- }
-
const pathname = context.url.pathname;
+ const hostname = context.url.hostname.toLowerCase();
- // Always allow the maintenance page itself, static assets, and API routes
- if (
- pathname === '/maintenance' ||
+ // Static assets and internal Astro assets pass through immediately
+ const isStaticOrInternal =
pathname.startsWith('/_astro') ||
+ pathname.startsWith('/_image') ||
pathname.startsWith('/api') ||
- /\.(png|svg|ico|webp|jpg|jpeg|gif|css|js|txt|xml|json|woff2?|ttf|eot|pdf)$/i.test(pathname)
- ) {
+ /\.(png|svg|ico|webp|jpg|jpeg|gif|css|js|txt|xml|json|woff2?|ttf|eot|pdf)$/i.test(pathname);
+
+ if (isStaticOrInternal) {
return next();
}
- // Redirect all other traffic to /maintenance
- return context.redirect('/maintenance', 307);
+ // Maintenance mode handling
+ if (isMaintenanceMode) {
+ if (pathname === '/maintenance') {
+ return next();
+ }
+ return context.redirect('/maintenance', 307);
+ }
+
+ // Subdomain handling: links.crischarles.com (or links.localhost in dev)
+ const isLinksSubdomain =
+ hostname === 'links.crischarles.com' ||
+ (hostname.startsWith('links.') && (hostname.includes('localhost') || hostname.includes('127.0.0.1')));
+
+ if (isLinksSubdomain) {
+ // Root on subdomain renders the /links page seamlessly
+ if (pathname === '/') {
+ return context.rewrite('/links');
+ }
+ // Redundant /links path redirects to /
+ if (pathname === '/links' || pathname === '/links/') {
+ return context.redirect('/', 301);
+ }
+ // Any other path on links subdomain redirects to main portfolio
+ return context.redirect(`https://crischarles.com${pathname}`, 302);
+ }
+
+ // Main domain: redirect /links to dedicated links.crischarles.com subdomain (in production)
+ if (pathname === '/links' || pathname === '/links/') {
+ if (hostname === 'crischarles.com' || hostname === 'www.crischarles.com') {
+ return context.redirect('https://links.crischarles.com', 301);
+ }
+ }
+
+ return next();
});
+
diff --git a/src/pages/links.astro b/src/pages/links.astro
index e0c9497..d2eb735 100644
--- a/src/pages/links.astro
+++ b/src/pages/links.astro
@@ -51,6 +51,7 @@ const getIconSvg = (platform: string) => {