5 Commits

Author SHA1 Message Date
fc932dbc7e fix(security): resolve audit findings — logging, endpoints, contact details
- Stop logging submitted password forms to the browser console
- Drive analytics from NEXT_PUBLIC_UMAMI_* instead of a hardcoded vendor
  script URL and site ID; renders nothing when unset
- Replace the wildcard image remote host "**" with an explicit allowlist
  (adds DigitalOcean Spaces)
- Fix the socket URL fallback to the API port (:3001, was :4000)
- Replace placeholder and personal contact emails with support@re-quest.com,
  including the privacy policy and terms pages

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 11:30:46 +05:30
9c0e7457e5 Merge pull request 'Added home page with dashboard content and SEO metadata' (#2) from fix/profile-contact-improvements into main
Reviewed-on: #2
2026-06-23 11:44:37 +00:00
pradeepkumar
a07484af7e refactor: decouple /home from cross-route page/layout imports
Move /home into the (user) route group so it inherits UserLayout via the
router instead of importing (user)/layout.tsx manually, and render shared
HomeDashboard content in both /home and /user/dashboard instead of importing
the dashboard route's default page export. Removes the fragile cross-route
coupling while keeping the same URL, SEO metadata, and rendered output.
2026-06-23 17:13:55 +05:30
Chinraj P
987ca11a4d Added home page with dashboard content and SEO metadata 2026-06-23 14:14:08 +05:30
pradeepkumar
2245584734 fix: prevent profile bio/description horizontal overflow
Add min-w-0 to the flex-1 right content column so it can shrink below
its content width (flex items default to min-width:auto), and break-words
on the bio paragraph for long unbroken strings.
2026-06-23 12:58:56 +05:30
19 changed files with 187 additions and 131 deletions

View File

@@ -7,18 +7,11 @@ const nextConfig: NextConfig = {
// Image optimization // Image optimization
images: { images: {
remotePatterns: [ remotePatterns: [
{ { protocol: "https", hostname: "*.contabostorage.com" },
protocol: "https", { protocol: "https", hostname: "*.amazonaws.com" },
hostname: "**", { protocol: "https", hostname: "*.digitaloceanspaces.com" },
}, { protocol: "http", hostname: "localhost" },
{ { protocol: "http", hostname: "127.0.0.1" },
protocol: "http",
hostname: "localhost",
},
{
protocol: "http",
hostname: "127.0.0.1",
},
], ],
// Don't proxy external images through Next.js server // Don't proxy external images through Next.js server
// Avoids SSL cert issues with Contabo S3 (sin1.contabostorage.com) // Avoids SSL cert issues with Contabo S3 (sin1.contabostorage.com)

View File

@@ -3,8 +3,8 @@
import { SettingsSidebar, PasswordSecurityForm } from '@/components/settings'; import { SettingsSidebar, PasswordSecurityForm } from '@/components/settings';
export default function PasswordSecurityPage() { export default function PasswordSecurityPage() {
const handleSave = (data: { currentPassword: string; newPassword: string }) => { const handleSave = (_data: { currentPassword: string; newPassword: string }) => {
console.log('Updating agent password:', data); // TODO: call the change-password API. Do not log password payloads.
}; };
return ( return (

View File

@@ -0,0 +1,11 @@
import { HomeDashboard } from '@/components/home/HomeDashboard';
export const metadata = {
title: 'RE-Quest - Connect with Trusted Real Estate Professionals',
description:
'Discover verified real estate professionals for buying, selling, renting, and investing. Search by location, specialization, and expertise to connect with trusted agents on RE-Quest.',
};
export default function HomePage() {
return <HomeDashboard />;
}

View File

@@ -1,23 +1,26 @@
'use client'; "use client";
import { useSession } from 'next-auth/react'; import { useSession } from "next-auth/react";
import { useRouter, usePathname } from 'next/navigation'; import { useRouter, usePathname } from "next/navigation";
import { useEffect } from 'react'; import { useEffect } from "react";
import Image from 'next/image'; import Image from "next/image";
import { Footer } from '@/components/layout/Footer'; import { Footer } from "@/components/layout/Footer";
import { CommonHeader } from '@/components/layout/CommonHeader'; import { CommonHeader } from "@/components/layout/CommonHeader";
import { PresenceProvider } from '@/components/providers/presence-provider'; import { PresenceProvider } from "@/components/providers/presence-provider";
// Pages that don't require authentication // Pages that don't require authentication
const publicPaths = [ const publicPaths = [
'/user/dashboard', "/home",
'/user/profiles', "/user/dashboard",
'/user/profile/', // Agent profile view (includes /user/profile/[id]) "/user/profiles",
"/user/profile/", // Agent profile view (includes /user/profile/[id])
]; ];
// Check if current path is public // Check if current path is public
const isPublicPath = (pathname: string) => { const isPublicPath = (pathname: string) => {
return publicPaths.some(path => pathname === path || pathname.startsWith(path)); return publicPaths.some(
(path) => pathname === path || pathname.startsWith(path),
);
}; };
export default function UserLayout({ export default function UserLayout({
@@ -28,11 +31,12 @@ export default function UserLayout({
const { data: session, status } = useSession(); const { data: session, status } = useSession();
const router = useRouter(); const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
const isDashboard = pathname === '/user/dashboard'; // const isDashboard = pathname === "/user/dashboard";
const isDashboard = pathname === "/user/dashboard" || pathname === "/home";
const isPublic = isPublicPath(pathname); const isPublic = isPublicPath(pathname);
useEffect(() => { useEffect(() => {
if (status === 'loading') return; if (status === "loading") return;
// Allow public pages without authentication. // Allow public pages without authentication.
// Agents are intentionally allowed to land on /user/dashboard — the logo // Agents are intentionally allowed to land on /user/dashboard — the logo
@@ -49,19 +53,31 @@ export default function UserLayout({
// Redirect agents to agent dashboard for protected user pages // Redirect agents to agent dashboard for protected user pages
const userRole = (session.user as any)?.role; const userRole = (session.user as any)?.role;
if (userRole === 'AGENT') { if (userRole === "AGENT") {
router.replace('/agent/dashboard'); router.replace("/agent/dashboard");
} }
}, [session, status, router, pathname, isPublic]); }, [session, status, router, pathname, isPublic]);
const splashLoading = ( const splashLoading = (
<div <div
className="min-h-screen flex flex-col items-center justify-center" className="min-h-screen flex flex-col items-center justify-center"
style={{ background: 'linear-gradient(to bottom, #c4d9d4, #f0f5fc)' }} style={{ background: "linear-gradient(to bottom, #c4d9d4, #f0f5fc)" }}
> >
<Image src="/assets/images/splash-house.png" alt="" width={150} height={108} priority /> <Image
src="/assets/images/splash-house.png"
alt=""
width={150}
height={108}
priority
/>
<div className="mt-[35px]"> <div className="mt-[35px]">
<Image src="/assets/images/splash-logo.png" alt="RE-Quest" width={264} height={55} priority /> <Image
src="/assets/images/splash-logo.png"
alt="RE-Quest"
width={264}
height={55}
priority
/>
</div> </div>
<div className="mt-8"> <div className="mt-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#00293d]" /> <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#00293d]" />
@@ -70,7 +86,7 @@ export default function UserLayout({
); );
// Show loading only for protected pages while checking auth // Show loading only for protected pages while checking auth
if (status === 'loading' && !isPublic) { if (status === "loading" && !isPublic) {
return splashLoading; return splashLoading;
} }
@@ -105,9 +121,7 @@ export default function UserLayout({
</div> </div>
{/* Main Content */} {/* Main Content */}
<main className="flex-1"> <main className="flex-1">{children}</main>
{children}
</main>
</> </>
)} )}

View File

@@ -1,53 +1,7 @@
'use client'; 'use client';
import { useState, useEffect } from 'react'; import { HomeDashboard } from '@/components/home/HomeDashboard';
import { HeroSection } from '@/components/home/HeroSection';
import { FeaturesSection } from '@/components/home/FeaturesSection';
import { TopProfessionals } from '@/components/home/TopProfessionals';
import { TestimonialsSection } from '@/components/home/TestimonialsSection';
import { cmsService, resolveImageUrl } from '@/services/cms.service';
import type { HeroContent, FeaturesContent, TopProfessionalsContent, TestimonialsContent, CmsContentRecord } from '@/types/cms';
export default function UserDashboard() { export default function UserDashboard() {
const [cmsData, setCmsData] = useState<Record<string, unknown>>({}); return <HomeDashboard />;
const [cmsLoaded, setCmsLoaded] = useState(false);
useEffect(() => {
const fetchCms = async () => {
try {
const sections = await cmsService.getPageContent('landing');
const data: Record<string, unknown> = {};
for (const s of sections) {
const content = s.content as Record<string, unknown>;
// Resolve S3 keys in image fields
if (s.sectionKey === 'features' && Array.isArray(content.features)) {
for (const feat of content.features as { iconPath?: string }[]) {
if (feat.iconPath) feat.iconPath = await resolveImageUrl(feat.iconPath);
}
}
if (s.sectionKey === 'testimonials' && Array.isArray(content.stats)) {
for (const stat of content.stats as { iconPath?: string }[]) {
if (stat.iconPath) stat.iconPath = await resolveImageUrl(stat.iconPath);
}
}
data[s.sectionKey] = content;
}
setCmsData(data);
} catch {
// Use default content on error
} finally {
setCmsLoaded(true);
}
};
fetchCms();
}, []);
return (
<div>
<HeroSection content={cmsData.hero as HeroContent | undefined} />
{cmsLoaded && <FeaturesSection content={cmsData.features as FeaturesContent | undefined} />}
<TopProfessionals content={cmsData.topProfessionals as TopProfessionalsContent | undefined} />
<TestimonialsSection content={cmsData.testimonials as TestimonialsContent | undefined} />
</div>
);
} }

View File

@@ -533,7 +533,7 @@ export default function AgentProfileView() {
</div> </div>
{/* Right Content - Profile Info + Experience + All Sections */} {/* Right Content - Profile Info + Experience + All Sections */}
<div className="flex-1 space-y-4"> <div className="flex-1 min-w-0 space-y-4">
{/* Profile Card - No edit button for user view */} {/* Profile Card - No edit button for user view */}
<ProfileCard <ProfileCard
firstName={agentProfile.firstName} firstName={agentProfile.firstName}

View File

@@ -3,8 +3,8 @@
import { SettingsSidebar, PasswordSecurityForm } from '@/components/settings'; import { SettingsSidebar, PasswordSecurityForm } from '@/components/settings';
export default function UserPasswordSecurityPage() { export default function UserPasswordSecurityPage() {
const handleSave = (data: { currentPassword: string; newPassword: string }) => { const handleSave = (_data: { currentPassword: string; newPassword: string }) => {
console.log('Updating user password:', data); // TODO: call the change-password API. Do not log password payloads.
}; };
return ( return (

View File

@@ -31,7 +31,7 @@ interface ContactCta {
const defaultContactDetails: ContactDetails = { const defaultContactDetails: ContactDetails = {
title: 'Get In Touch', title: 'Get In Touch',
description: 'Have a question about a property or need assistance? Fill out the form below and our team will get back to you shortly.', description: 'Have a question about a property or need assistance? Fill out the form below and our team will get back to you shortly.',
email: '123support@gmail.com', email: 'support@re-quest.com',
phone: '1234567890', phone: '1234567890',
phoneHours: 'Mon-Fri 9am-6pm', phoneHours: 'Mon-Fri 9am-6pm',
officeAddress: '123 Market Street', officeAddress: '123 Market Street',

View File

@@ -262,7 +262,7 @@ export default function FAQPage() {
Start Live Chat Start Live Chat
</Link> </Link>
<a <a
href="mailto:support@requesn.com" href="mailto:support@re-quest.com"
className="flex items-center justify-center gap-2 w-[174px] h-[51px] border border-[#00293d] rounded-[7px] font-fractul text-[16px] text-[#00293d] hover:bg-gray-50 transition-colors" className="flex items-center justify-center gap-2 w-[174px] h-[51px] border border-[#00293d] rounded-[7px] font-fractul text-[16px] text-[#00293d] hover:bg-gray-50 transition-colors"
> >
<Image <Image

View File

@@ -158,12 +158,15 @@ export default function RootLayout({
<NotificationProvider /> <NotificationProvider />
{children} {children}
</SessionProvider> </SessionProvider>
{/* Umami analytics — loaded after page becomes interactive */} {/* Umami analytics — set NEXT_PUBLIC_UMAMI_URL and NEXT_PUBLIC_UMAMI_WEBSITE_ID to enable */}
<Script {process.env.NEXT_PUBLIC_UMAMI_URL &&
src="https://analytics.superlabs.co/script.js" process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID && (
data-website-id="00e1ce31-e174-4519-8b59-63e8d4556b01" <Script
strategy="afterInteractive" src={process.env.NEXT_PUBLIC_UMAMI_URL}
/> data-website-id={process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID}
strategy="afterInteractive"
/>
)}
{/* Microsoft Clarity */} {/* Microsoft Clarity */}
{process.env.NEXT_PUBLIC_CLARITY_ID && ( {process.env.NEXT_PUBLIC_CLARITY_ID && (
<Script id="ms-clarity" strategy="afterInteractive"> <Script id="ms-clarity" strategy="afterInteractive">

View File

@@ -1,27 +1,27 @@
'use client'; "use client";
import { useSession } from 'next-auth/react'; import { useSession } from "next-auth/react";
import { useRouter } from 'next/navigation'; import { useRouter } from "next/navigation";
import { useEffect } from 'react'; import { useEffect } from "react";
export default function Home() { export default function Home() {
const { data: session, status } = useSession(); const { data: session, status } = useSession();
const router = useRouter(); const router = useRouter();
useEffect(() => { useEffect(() => {
if (status === 'loading') return; if (status === "loading") return;
// Redirect based on user role, or to public dashboard if not logged in // Redirect based on user role, or to public dashboard if not logged in
if (session) { if (session) {
const userRole = (session.user as any)?.role; const userRole = (session.user as any)?.role;
if (userRole === 'AGENT') { if (userRole === "AGENT") {
router.replace('/agent/dashboard'); router.replace("/agent/dashboard");
} else { } else {
router.replace('/user/dashboard'); router.replace("/user/dashboard");
} }
} else { } else {
// Not logged in - go to public user dashboard // Not logged in - go to public user dashboard
router.replace('/user/dashboard'); router.replace("/home");
} }
}, [session, status, router]); }, [session, status, router]);

View File

@@ -477,10 +477,10 @@ export default function PrivacyPolicyPage() {
<p className="mb-1"> <p className="mb-1">
Email:{' '} Email:{' '}
<a <a
href="mailto:request.sha@gmail.com" href="mailto:support@re-quest.com"
className="text-[#e58625] underline hover:opacity-80" className="text-[#e58625] underline hover:opacity-80"
> >
request.sha@gmail.com support@re-quest.com
</a> </a>
</p> </p>
<p>Address: 1975 Peralta Point, Colorado Springs, CO 80910</p> <p>Address: 1975 Peralta Point, Colorado Springs, CO 80910</p>

View File

@@ -163,7 +163,7 @@ export default function TermsOfServicePage() {
<p className="font-serif text-[15px] leading-[24px] text-[#00293d] mb-4"> <p className="font-serif text-[15px] leading-[24px] text-[#00293d] mb-4">
If you experience any threatening, abusive, or suspicious behavior from another user, please report the If you experience any threatening, abusive, or suspicious behavior from another user, please report the
interaction immediately using the in-app reporting feature or by contacting us at interaction immediately using the in-app reporting feature or by contacting us at
officialteam.request@gmail.com. RE-Quest will investigate reported incidents and take appropriate action, support@re-quest.com. RE-Quest will investigate reported incidents and take appropriate action,
which may include account suspension or referral to law enforcement. which may include account suspension or referral to law enforcement.
</p> </p>
@@ -464,7 +464,7 @@ export default function TermsOfServicePage() {
If you have any questions or concerns about these Terms, please contact us: If you have any questions or concerns about these Terms, please contact us:
</p> </p>
<p className="font-serif text-[15px] leading-[24px] text-[#00293d] mb-1"> <p className="font-serif text-[15px] leading-[24px] text-[#00293d] mb-1">
<span className="font-bold">Email:</span> officialteam.request@gmail.com <span className="font-bold">Email:</span> support@re-quest.com
</p> </p>
<p className="font-serif text-[15px] leading-[24px] text-[#00293d]"> <p className="font-serif text-[15px] leading-[24px] text-[#00293d]">
<span className="font-bold">Address:</span> 1975 Peralta Point, Colorado Springs, CO 80910 <span className="font-bold">Address:</span> 1975 Peralta Point, Colorado Springs, CO 80910

View File

@@ -0,0 +1,54 @@
'use client';
import { useState, useEffect } from 'react';
import { HeroSection } from '@/components/home/HeroSection';
import { FeaturesSection } from '@/components/home/FeaturesSection';
import { TopProfessionals } from '@/components/home/TopProfessionals';
import { TestimonialsSection } from '@/components/home/TestimonialsSection';
import { cmsService, resolveImageUrl } from '@/services/cms.service';
import type { HeroContent, FeaturesContent, TopProfessionalsContent, TestimonialsContent } from '@/types/cms';
// Shared landing/dashboard content rendered by both /home and /user/dashboard.
export function HomeDashboard() {
const [cmsData, setCmsData] = useState<Record<string, unknown>>({});
const [cmsLoaded, setCmsLoaded] = useState(false);
useEffect(() => {
const fetchCms = async () => {
try {
const sections = await cmsService.getPageContent('landing');
const data: Record<string, unknown> = {};
for (const s of sections) {
const content = s.content as Record<string, unknown>;
// Resolve S3 keys in image fields
if (s.sectionKey === 'features' && Array.isArray(content.features)) {
for (const feat of content.features as { iconPath?: string }[]) {
if (feat.iconPath) feat.iconPath = await resolveImageUrl(feat.iconPath);
}
}
if (s.sectionKey === 'testimonials' && Array.isArray(content.stats)) {
for (const stat of content.stats as { iconPath?: string }[]) {
if (stat.iconPath) stat.iconPath = await resolveImageUrl(stat.iconPath);
}
}
data[s.sectionKey] = content;
}
setCmsData(data);
} catch {
// Use default content on error
} finally {
setCmsLoaded(true);
}
};
fetchCms();
}, []);
return (
<div>
<HeroSection content={cmsData.hero as HeroContent | undefined} />
{cmsLoaded && <FeaturesSection content={cmsData.features as FeaturesContent | undefined} />}
<TopProfessionals content={cmsData.topProfessionals as TopProfessionalsContent | undefined} />
<TestimonialsSection content={cmsData.testimonials as TestimonialsContent | undefined} />
</div>
);
}

View File

@@ -7,6 +7,7 @@ import { useSession } from "next-auth/react";
import { useHeaderData } from "@/components/providers/header-provider"; import { useHeaderData } from "@/components/providers/header-provider";
const navLinks = [ const navLinks = [
{ label: "Professional", href: "/user/profiles" },
{ label: "Education", href: "/education" }, { label: "Education", href: "/education" },
{ label: "About Us", href: "/about" }, { label: "About Us", href: "/about" },
{ label: "FAQ's", href: "/faq" }, { label: "FAQ's", href: "/faq" },
@@ -56,6 +57,7 @@ export function CommonHeader() {
if (showProfileMenu || showGuestMenu || showMobileMenu) { if (showProfileMenu || showGuestMenu || showMobileMenu) {
document.addEventListener("mousedown", handleClickOutside); document.addEventListener("mousedown", handleClickOutside);
} }
// const showProfessional = userRole === "AGENT" || userRole === "LENDER";
return () => { return () => {
document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("mousedown", handleClickOutside);
@@ -66,12 +68,13 @@ export function CommonHeader() {
const userName = profileName || session?.user?.name; const userName = profileName || session?.user?.name;
const userEmail = session?.user?.email; const userEmail = session?.user?.email;
const userRole = (session?.user as any)?.role; const userRole = (session?.user as any)?.role;
const showProfessional = userRole === "AGENT" || userRole === "LENDER"; // const showProfessional = userRole === "AGENT" || userRole === "LENDER";
// Use fetched profile image, fallback to session image // Use fetched profile image, fallback to session image
const userImage = profileImage || session?.user?.image; const userImage = profileImage || session?.user?.image;
// Logo destination — always lands on the user dashboard regardless of role. // Logo destination — always lands on the user dashboard regardless of role.
const dashboardLink = "/user/dashboard"; // const dashboardLink = "/user/dashboard";
const dashboardLink = "/home";
return ( return (
<header <header
@@ -92,7 +95,7 @@ export function CommonHeader() {
</Link> </Link>
{/* Navigation - Desktop only */} {/* Navigation - Desktop only */}
{/* <nav className="hidden md:flex items-center gap-8 ml-auto mr-8"> <nav className="hidden md:flex items-center gap-8 ml-auto mr-8">
{navLinks.map((link) => ( {navLinks.map((link) => (
<Link <Link
key={link.href} key={link.href}
@@ -102,8 +105,8 @@ export function CommonHeader() {
{link.label} {link.label}
</Link> </Link>
))} ))}
</nav> */} </nav>
<nav className="hidden md:flex items-center gap-8 ml-auto mr-8"> {/* <nav className="hidden md:flex items-center gap-8 ml-auto mr-8">
{showProfessional && ( {showProfessional && (
<Link <Link
href="/user/profiles" href="/user/profiles"
@@ -122,7 +125,7 @@ export function CommonHeader() {
{link.label} {link.label}
</Link> </Link>
))} ))}
</nav> </nav> */}
{/* Right Side Icons */} {/* Right Side Icons */}
<div className="flex items-center gap-2 md:gap-4"> <div className="flex items-center gap-2 md:gap-4">
@@ -470,7 +473,7 @@ export function CommonHeader() {
{/* Mobile Navigation Menu */} {/* Mobile Navigation Menu */}
{showMobileMenu && ( {showMobileMenu && (
<div className="md:hidden border-t border-white/20 py-3 pb-4"> <div className="md:hidden border-t border-white/20 py-3 pb-4">
{/* <nav className="flex flex-col gap-1"> <nav className="flex flex-col gap-1">
{navLinks.map((link) => ( {navLinks.map((link) => (
<Link <Link
key={link.href} key={link.href}
@@ -481,8 +484,8 @@ export function CommonHeader() {
{link.label} {link.label}
</Link> </Link>
))} ))}
</nav> */} </nav>
<nav className="flex flex-col gap-1"> {/* <nav className="flex flex-col gap-1">
{showProfessional && ( {showProfessional && (
<Link <Link
href="/user/profiles" href="/user/profiles"
@@ -503,7 +506,7 @@ export function CommonHeader() {
{link.label} {link.label}
</Link> </Link>
))} ))}
</nav> </nav> */}
</div> </div>
)} )}
</header> </header>

View File

@@ -276,7 +276,7 @@ export function ProfileCard({
</div> </div>
{/* Bio */} {/* Bio */}
<p className="text-[14px] font-normal text-[#00293D] font-serif leading-[20px] mb-4 text-center lg:text-left"> <p className="text-[14px] font-normal text-[#00293D] font-serif leading-[20px] mb-4 text-center lg:text-left break-words">
{bio} {bio}
</p> </p>

View File

@@ -270,10 +270,10 @@ export function SubscriptionForm() {
{/* Support Email */} {/* Support Email */}
<Link <Link
href="mailto:support@example.com" href="mailto:support@re-quest.com"
className="font-serif font-bold text-[14px] text-[#e58625] underline hover:text-[#d47920] transition-colors" className="font-serif font-bold text-[14px] text-[#e58625] underline hover:text-[#d47920] transition-colors"
> >
support@example.com support@re-quest.com
</Link> </Link>
</div> </div>

View File

@@ -2,10 +2,30 @@ import { auth } from "@/auth";
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
// Auth routes - logged-in users should be redirected away from these // Auth routes - logged-in users should be redirected away from these
const authRoutes = ["/login", "/signup", "/forgot-password", "/reset-password", "/verify-email"]; const authRoutes = [
"/login",
"/signup",
"/forgot-password",
"/reset-password",
"/verify-email",
];
// Public routes - accessible to everyone (logged in or not) // Public routes - accessible to everyone (logged in or not)
const publicRoutes = ["/", "/contact", "/about", "/faq", "/education", "/coming-soon", "/privacy-policy", "/terms-of-service", "/logout", "/user/dashboard", "/user/profiles", "/user/profile"]; const publicRoutes = [
"/",
"/home",
"/contact",
"/about",
"/faq",
"/education",
"/coming-soon",
"/privacy-policy",
"/terms-of-service",
"/logout",
"/user/dashboard",
"/user/profiles",
"/user/profile",
];
// Routes that should NEVER be redirected away from (even if logged in) // Routes that should NEVER be redirected away from (even if logged in)
const noRedirectRoutes = ["/logout"]; const noRedirectRoutes = ["/logout"];
@@ -16,23 +36,27 @@ export default auth((req) => {
const userRole = (req.auth?.user as any)?.role; const userRole = (req.auth?.user as any)?.role;
const isAuthRoute = authRoutes.some( const isAuthRoute = authRoutes.some(
(route) => nextUrl.pathname === route || nextUrl.pathname.startsWith(route + "/") (route) =>
nextUrl.pathname === route || nextUrl.pathname.startsWith(route + "/"),
); );
const isPublicRoute = publicRoutes.some( const isPublicRoute = publicRoutes.some(
(route) => nextUrl.pathname === route || nextUrl.pathname.startsWith(route + "/") (route) =>
nextUrl.pathname === route || nextUrl.pathname.startsWith(route + "/"),
); );
const isAgentRoute = nextUrl.pathname.startsWith("/agent"); const isAgentRoute = nextUrl.pathname.startsWith("/agent");
const isUserRoute = nextUrl.pathname.startsWith("/user"); const isUserRoute = nextUrl.pathname.startsWith("/user");
const isApiRoute = nextUrl.pathname.startsWith("/api"); const isApiRoute = nextUrl.pathname.startsWith("/api");
const isStaticRoute = nextUrl.pathname.startsWith("/_next") || const isStaticRoute =
nextUrl.pathname.startsWith("/assets") || nextUrl.pathname.startsWith("/_next") ||
nextUrl.pathname.includes("."); nextUrl.pathname.startsWith("/assets") ||
nextUrl.pathname.includes(".");
const isNoRedirectRoute = noRedirectRoutes.some( const isNoRedirectRoute = noRedirectRoutes.some(
(route) => nextUrl.pathname === route || nextUrl.pathname.startsWith(route + "/") (route) =>
nextUrl.pathname === route || nextUrl.pathname.startsWith(route + "/"),
); );
// Skip middleware for API routes and static files // Skip middleware for API routes and static files

View File

@@ -59,7 +59,7 @@ class SocketService {
} }
// Extract base URL without /api/v1 path for Socket.io connection // Extract base URL without /api/v1 path for Socket.io connection
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000'; const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001/api/v1';
const baseUrl = apiUrl.replace(/\/api\/v1\/?$/, ''); const baseUrl = apiUrl.replace(/\/api\/v1\/?$/, '');
this.socket = io(baseUrl, { this.socket = io(baseUrl, {