From 69e2ac0678005fc7a4de74d556965482eeab6639 Mon Sep 17 00:00:00 2001 From: Viktor AI Date: Tue, 9 Jun 2026 03:39:39 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20full=20site=20audit=20=E2=80=94=2015=20i?= =?UTF-8?q?ssues=20fixed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical: - Sitemap: fall back to anon key when service role key unavailable (fixes 0 experience pages in sitemap) - AdSense: change strategy from beforeInteractive to lazyOnload (unblocks rendering) - Font: switch from Google Fonts to next/font/google (eliminates render-blocking request) Important: - Remove force-dynamic conflicting with revalidate on companies/[company], companies/page, interview-experience/[slug] - Add canonical URLs to companies/[company], companies listing, interview-experience listing, blog listing, tags - Add custom not-found.tsx (404) and error.tsx pages with navigation - Add HTML sanitizer for dangerouslySetInnerHTML on experience pages (XSS prevention) - Fix siteConfig.author from 'Sharmag' to 'Deepak Sharma' Nice-to-have: - Fix footer: GitHub link (depaksharma -> deepu0), Twitter link (generic -> @frontendjunction) - Fix tags page meta description from placeholder to proper SEO text - Add security headers: X-Content-Type-Options, X-Frame-Options, X-DNS-Prefetch-Control, Referrer-Policy - Remove console.log from production components (add-new-experience, session-provider) - Sitemap: add error logging and increase experience limit to 1000 --- app/blog/page.tsx | 3 ++ app/companies/[company]/page.tsx | 6 ++-- app/companies/page.tsx | 6 ++-- app/error.tsx | 36 +++++++++++++++++++++ app/interview-experience/[slug]/page.tsx | 6 ++-- app/interview-experience/page.tsx | 16 +++++----- app/layout.tsx | 24 ++++++-------- app/not-found.tsx | 40 ++++++++++++++++++++++++ app/sitemap.ts | 28 ++++++++++++++--- app/tags/page.tsx | 8 +++-- components/add-new-experience.tsx | 2 +- components/common/footer.tsx | 4 +-- components/session-provider.tsx | 2 +- config/site.ts | 7 +++-- lib/sanitize-html.ts | 30 ++++++++++++++++++ next.config.mjs | 21 +++++++++++++ 16 files changed, 197 insertions(+), 42 deletions(-) create mode 100644 app/error.tsx create mode 100644 app/not-found.tsx create mode 100644 lib/sanitize-html.ts diff --git a/app/blog/page.tsx b/app/blog/page.tsx index 319bd83..0a61126 100644 --- a/app/blog/page.tsx +++ b/app/blog/page.tsx @@ -10,6 +10,9 @@ export const metadata: Metadata = { title: 'Blog | Frontend Development Insights & Tutorials', description: 'Deep dives into frontend development, performance optimization, React patterns, CSS techniques, and industry best practices from the Frontend Junction community.', + alternates: { + canonical: 'https://www.frontend-junction.com/blog', + }, }; const POSTS_PER_PAGE = 6; // Changed to 6 for better grid (2x3) diff --git a/app/companies/[company]/page.tsx b/app/companies/[company]/page.tsx index f061e25..ef50e7b 100644 --- a/app/companies/[company]/page.tsx +++ b/app/companies/[company]/page.tsx @@ -6,8 +6,7 @@ import { Metadata } from 'next'; import Script from 'next/script'; import { FaCalendar, FaUser } from 'react-icons/fa'; -export const dynamic = 'force-dynamic'; -export const revalidate = 600; // Cache for 10 minutes +export const revalidate = 600; // ISR: revalidate every 10 minutes interface Props { params: Promise<{ company: string }>; @@ -32,6 +31,9 @@ export async function generateMetadata({ params }: Props): Promise { `${companyName} software engineer interview`, `${companyName} react interview`, ], + alternates: { + canonical: `https://www.frontend-junction.com/companies/${company}`, + }, openGraph: { title: `${companyName} Frontend Interview Experiences`, description: `Browse real ${companyName} frontend engineer interview experiences.`, diff --git a/app/companies/page.tsx b/app/companies/page.tsx index 86a82db..096f8d5 100644 --- a/app/companies/page.tsx +++ b/app/companies/page.tsx @@ -3,13 +3,15 @@ import Link from 'next/link'; import { Metadata } from 'next'; import Script from 'next/script'; -export const dynamic = 'force-dynamic'; -export const revalidate = 3600; // Cache for 1 hour +export const revalidate = 3600; // ISR: revalidate every 1 hour export const metadata: Metadata = { title: 'Top Companies for Frontend Interviews | Frontend Junction', description: 'Browse frontend interview experiences by company. Prepare for your next interview at Google, Amazon, Uber, and more.', + alternates: { + canonical: 'https://www.frontend-junction.com/companies', + }, openGraph: { title: 'Top Companies for Frontend Interviews', description: diff --git a/app/error.tsx b/app/error.tsx new file mode 100644 index 0000000..724a910 --- /dev/null +++ b/app/error.tsx @@ -0,0 +1,36 @@ +'use client'; + +import Link from 'next/link'; + +export default function Error({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + return ( +
+

Oops!

+

Something went wrong

+

+ An unexpected error occurred. Please try again or navigate to another + page. +

+
+ + + Go Home + +
+
+ ); +} diff --git a/app/interview-experience/[slug]/page.tsx b/app/interview-experience/[slug]/page.tsx index 0523170..11e5a39 100644 --- a/app/interview-experience/[slug]/page.tsx +++ b/app/interview-experience/[slug]/page.tsx @@ -1,4 +1,5 @@ import { getExperienceBySlug } from '@/lib/getExperienceBySlug'; +import { sanitizeHtml } from '@/lib/sanitize-html'; import { notFound } from 'next/navigation'; import Link from 'next/link'; import { FaExternalLinkAlt, FaCalendar, FaUser } from 'react-icons/fa'; @@ -7,8 +8,7 @@ import ReactMarkdown from 'react-markdown'; import ViewCounter from '@/components/view-counter'; import Script from 'next/script'; -export const dynamic = 'force-dynamic'; -export const revalidate = 60; // ISR +export const revalidate = 60; // ISR: revalidate every 60 seconds interface Props { params: Promise<{ slug: string }>; @@ -202,7 +202,7 @@ export default async function ExperienceSlugPage({ params }: Props) { {/* Content Analysis (AI Generated or Raw) */}
{experience.content?.trim().startsWith('<') ? ( -
+
) : ( {experience.content || experience.summary || ''} diff --git a/app/interview-experience/page.tsx b/app/interview-experience/page.tsx index 0d0f2df..689501b 100644 --- a/app/interview-experience/page.tsx +++ b/app/interview-experience/page.tsx @@ -8,21 +8,23 @@ import { Suspense } from 'react'; import Loading from '../loading'; export const dynamic = 'force-dynamic'; -export const revalidate = 0; export const metadata: Metadata = { - title: 'Interview Experience', + title: 'Frontend Interview Experiences | Real Stories from Top Companies', description: - 'Browse through real front-end interview experiences from candidates at various companies. Get insights, tips, and prepare better for your next interview.', + 'Browse 400+ real frontend interview experiences from Google, Amazon, Meta, Flipkart, and more. Get insights, tips, and prepare better for your next interview.', keywords: - 'front-end interview experiences, front-end developer interviews, front-end interview preparation, front-end interview insights, front-end interview stories', + 'frontend interview experiences, frontend developer interviews, frontend interview preparation, frontend interview insights, frontend interview stories', + alternates: { + canonical: 'https://www.frontend-junction.com/interview-experience', + }, openGraph: { type: 'website', url: 'https://www.frontend-junction.com/interview-experience', - title: 'Front-end Interview Experiences | Front-end Junction', + title: 'Frontend Interview Experiences | Real Stories from Top Companies', description: - 'Browse through real front-end interview experiences from candidates at various companies. Get insights, tips, and prepare better for your next interview.', - siteName: 'Front-end Junction', + 'Browse 400+ real frontend interview experiences from Google, Amazon, Meta, Flipkart, and more.', + siteName: 'Frontend Junction', }, }; diff --git a/app/layout.tsx b/app/layout.tsx index a919598..7d1983e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,6 @@ import './globals.css'; import type { Metadata } from 'next'; +import { Inter } from 'next/font/google'; import { cn } from '@/lib/utils'; import { SiteHeader } from '@/components/common/site-header'; import { ThemeProvider } from '@/components/common/theme-provider'; @@ -13,6 +14,12 @@ import { } from '@/components/structured-data'; import { Analytics } from '@vercel/analytics/next'; +const inter = Inter({ + subsets: ['latin'], + display: 'swap', + variable: '--font-inter', +}); + export const metadata: Metadata = { metadataBase: new URL('https://www.frontend-junction.com'), title: { @@ -99,12 +106,12 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - +