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) => {