Read and review the authentication system in /home/ccuser/the-50-dollar-app. Read these files:
1. /home/ccuser/the-50-dollar-app/middleware.ts
2. /home/ccuser/the-50-dollar-app/src/lib/supabase/ — all files in this directory
3. /home/ccuser/the-50-dollar-app/src/app/auth/ — all page.tsx and route.ts files
4. /home/ccuser/the-50-dollar-app/src/app/(authenticated)/layout.tsx
5. /home/ccuser/the-50-dollar-app/src/hooks/ — all hook files

Check for:
- Session management gaps
- Auth callback security
- Token refresh logic
- Protected route enforcement
- CSRF protection implementation
- Password reset flow security
- Email verification flow
- Any missing auth guards

Return a detailed list of all issues found.


I'll start by reading all the requested files to review the authentication system thoroughly.


     1→import { type NextRequest } from "next/server";
     2→import { updateSession } from "./src/lib/supabase/middleware";
     3→
     4→export async function middleware(request: NextRequest) {
     5→  return await updateSession(request);
     6→}
     7→
     8→export const config = {
     9→  matcher: [
    10→    /*
    11→     * Match all request paths except for the ones starting with:
    12→     * - _next/static (static files)
    13→     * - _next/image (image optimization files)
    14→     * - favicon.ico (favicon file)
    15→     * - public folder
    16→     */
    17→    "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
    18→  ],
    19→};
    20→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


/home/ccuser/the-50-dollar-app/src/lib/supabase/client.ts
/home/ccuser/the-50-dollar-app/src/lib/supabase/server.ts
/home/ccuser/the-50-dollar-app/src/lib/supabase/middleware.ts


1→import { redirect } from 'next/navigation'
     2→import { createClient } from '@/lib/supabase/server'
     3→import Link from 'next/link'
     4→import Image from 'next/image'
     5→import { SignOutButton } from '@/components/sign-out-button'
     6→import { NotificationBell } from '@/components/notification-bell'
     7→
     8→export default async function AuthenticatedLayout({
     9→  children,
    10→}: {
    11→  children: React.ReactNode
    12→}) {
    13→  const supabase = await createClient()
    14→  const { data: { user } } = await supabase.auth.getUser()
    15→
    16→  if (!user) {
    17→    redirect('/auth/login')
    18→  }
    19→
    20→  return (
    21→

    22→      {/* Header */}
    23→
    24→

    25→          <Link href="/dashboard">
    26→            <Image src="/logo_black.svg" alt="RateRight" width={120} height={34} className="h-7 w-auto" />
    27→          </Link>
    28→

    29→            {user.email}
    30→            <NotificationBell />
    31→            <SignOutButton />
    32→

    33→

    34→
    35→
    36→      {/* Main Content */}
    37→
    38→        {children}
    39→
    40→

    41→  )
    42→}
    43→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


Now let me read all the supabase lib files and auth page/route files.


     1→/**
     2→ * Client-side Supabase configuration with enhanced security
     3→ *
     4→ * IMPORTANT: This module does NOT export a singleton instance.
     5→ * Always use createClient() to get a fresh client instance.
     6→ * This prevents stale auth credentials on shared devices.
     7→ */
     8→
     9→import { createBrowserClient } from '@supabase/ssr'
    10→import type { SupabaseClient } from '@supabase/supabase-js'
    11→
    12→const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
    13→const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
    14→
    15→/**
    16→ * Creates a Supabase client for use in browser/Client Components.
    17→ * Uses @supabase/ssr to properly sync cookies with the server.
    18→ *
    19→ * IMPORTANT: This function does NOT export a singleton instance.
    20→ * Always call createClient() to get a fresh client instance.
    21→ * This prevents stale auth credentials on shared devices.
    22→ *
    23→ * @returns Fresh Supabase client instance for browser use
    24→ *
    25→ * @example
    26→ * const supabase = createClient();
    27→ * const { data, error } = await supabase.from('users').select('*');
    28→ */
    29→export function createClient(): SupabaseClient {
    30→  return createBrowserClient(supabaseUrl, supabaseAnonKey)
    31→}

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→import { createServerClient } from "@supabase/ssr";
     2→import { createClient as createSupabaseClient, SupabaseClient } from "@supabase/supabase-js";
     3→import { cookies } from "next/headers";
     4→
     5→/**
     6→ * Cookie-based server client WITH user context.
     7→ * Use this in Server Components, Server Actions, and Route Handlers
     8→ * where you need to know who the logged-in user is.
     9→ *
    10→ * @returns Promise resolving to Supabase client with server-side cookie handling
    11→ *
    12→ * @example
    13→ * const supabase = await createClient();
    14→ * const { data: { user } } = await supabase.auth.getUser();
    15→ */
    16→export async function createClient() {
    17→  const cookieStore = await cookies();
    18→
    19→  return createServerClient(
    20→    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    21→    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    22→    {
    23→      cookies: {
    24→        getAll() {
    25→          return cookieStore.getAll();
    26→        },
    27→        setAll(cookiesToSet) {
    28→          try {
    29→            cookiesToSet.forEach(({ name, value, options }) =>
    30→              cookieStore.set(name, value, options)
    31→            );
    32→          } catch {
    33→            // Cookies can only be set in Server Actions or Route Handlers
    34→          }
    35→        },
    36→      },
    37→    }
    38→  );
    39→}
    40→
    41→// Lazy-initialized admin client to avoid build-time errors (H11 fix)
    42→let _supabaseAdmin: SupabaseClient | null = null;
    43→
    44→/**
    45→ * Admin client with service key — bypasses RLS.
    46→ * Use ONLY for admin operations (email verification, etc.)
    47→ * NEVER use this to read user data in server components.
    48→ *
    49→ * Throws if SUPABASE_SERVICE_KEY is not configured.
    50→ */
    51→export function getSupabaseAdmin(): SupabaseClient {
    52→  if (_supabaseAdmin) {
    53→    return _supabaseAdmin;
    54→  }
    55→
    56→  const serviceKey = process.env.SUPABASE_SERVICE_KEY;
    57→  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
    58→
    59→  if (!serviceKey) {
    60→    throw new Error(
    61→      'SUPABASE_SERVICE_KEY is not configured. ' +
    62→      'Please add it to your .env.local file. ' +
    63→      'See SUPABASE-KEY-ROTATION-GUIDE.md for details.'
    64→    );
    65→  }
    66→
    67→  if (!supabaseUrl) {
    68→    throw new Error(
    69→      'NEXT_PUBLIC_SUPABASE_URL is not configured. ' +
    70→      'Please add it to your .env.local file.'
    71→    );
    72→  }
    73→
    74→  _supabaseAdmin = createSupabaseClient(supabaseUrl, serviceKey, {
    75→    auth: {
    76→      autoRefreshToken: false,
    77→      persistSession: false,
    78→    },
    79→  });
    80→
    81→  return _supabaseAdmin;
    82→}
    83→
    84→/**
    85→ * Helper to verify email addresses (admin operation)
    86→ * Bypasses normal email verification process - use with caution
    87→ *
    88→ * @param userId - User ID to verify
    89→ * @returns Promise resolving to user data
    90→ * @throws Error if verification fails
    91→ *
    92→ * @example
    93→ * await verifyEmail('user-123');
    94→ * // User's email is now marked as verified
    95→ */
    96→export async function verifyEmail(userId: string) {
    97→  const admin = getSupabaseAdmin();
    98→  const { data, error } = await admin.auth.admin.updateUserById(
    99→    userId,
   100→    { email_confirm: true }
   101→  );
   102→
   103→  if (error) throw error;
   104→  return data;
   105→}
   106→
   107→/**
   108→ * Helper to check if user has verified their email (admin operation)
   109→ *
   110→ * @param userId - User ID to check
   111→ * @returns Promise resolving to true if email is verified
   112→ *
   113→ * @example
   114→ * const isVerified = await isEmailVerified('user-123');
   115→ * if (isVerified) {
   116→ *   // Allow access to verified-only features
   117→ * }
   118→ */
   119→export async function isEmailVerified(userId: string): Promise<boolean> {
   120→  const admin = getSupabaseAdmin();
   121→  const { data, error } = await admin.auth.admin.getUserById(userId);
   122→
   123→  if (error) return false;
   124→  return !!data.user?.email_confirmed_at;
   125→}
   126→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→import { createServerClient } from "@supabase/ssr";
     2→import { NextResponse, type NextRequest } from "next/server";
     3→
     4→/**
     5→ * Middleware function to handle Supabase authentication and session management
     6→ * Validates user sessions, handles redirects, and manages authentication state
     7→ *
     8→ * @param request - Next.js request object
     9→ * @returns Promise resolving to NextResponse with authentication handling
    10→ *
    11→ * @example
    12→ * // In middleware.ts
    13→ * export async function middleware(request: NextRequest) {
    14→ *   return await updateSession(request);
    15→ * }
    16→ */
    17→export async function updateSession(request: NextRequest) {
    18→  let supabaseResponse = NextResponse.next({
    19→    request,
    20→  });
    21→
    22→  const supabase = createServerClient(
    23→    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    24→    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    25→    {
    26→      cookies: {
    27→        getAll() {
    28→          return request.cookies.getAll();
    29→        },
    30→        setAll(cookiesToSet) {
    31→          cookiesToSet.forEach(({ name, value }) =>
    32→            request.cookies.set(name, value)
    33→          );
    34→          supabaseResponse = NextResponse.next({
    35→            request,
    36→          });
    37→          cookiesToSet.forEach(({ name, value, options }) =>
    38→            supabaseResponse.cookies.set(name, value, options)
    39→          );
    40→        },
    41→      },
    42→    }
    43→  );
    44→
    45→  // Use getUser() — the only safe server-side auth check (validates JWT with Supabase).
    46→  // Do NOT use getSession() here — it reads unverified cookie data.
    47→  const {
    48→    data: { user },
    49→  } = await supabase.auth.getUser();
    50→
    51→  const pathname = request.nextUrl.pathname;
    52→
    53→  // Route classification
    54→  const protectedPaths = ["/dashboard", "/contractor", "/worker", "/messages", "/profile"];
    55→  const isProtected = protectedPaths.some((p) => pathname.startsWith(p));
    56→  const isSignup = pathname.includes("/signup");
    57→  const isVerifyPage = pathname === "/auth/verify-email";
    58→  const isAuthPage = pathname.startsWith("/auth/login") || pathname.startsWith("/auth/signup");
    59→
    60→  // ── Not logged in ──
    61→  if (!user) {
    62→    // Redirect unauthenticated users away from protected routes (but allow signup pages)
    63→    if (isProtected && !isSignup) {
    64→      const url = request.nextUrl.clone();
    65→      url.pathname = "/auth/login";
    66→      return NextResponse.redirect(url);
    67→    }
    68→    return supabaseResponse;
    69→  }
    70→
    71→  // ── Logged in ──
    72→  const emailVerified = !!user.email_confirmed_at;
    73→
    74→  if (!emailVerified) {
    75→    // Unverified user trying to access protected routes → send to verify page
    76→    // (but let them stay on signup flows to finish onboarding after verification)
    77→    if (isProtected && !isSignup && !isVerifyPage) {
    78→      const url = request.nextUrl.clone();
    79→      url.pathname = "/auth/verify-email";
    80→      return NextResponse.redirect(url);
    81→    }
    82→  } else {
    83→    // Verified user on auth pages → send to dashboard
    84→    if (isAuthPage || isVerifyPage) {
    85→      const url = request.nextUrl.clone();
    86→      url.pathname = "/dashboard";
    87→      return NextResponse.redirect(url);
    88→    }
    89→  }
    90→
    91→  return supabaseResponse;
    92→}
    93→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→import { NextResponse } from "next/server";
     2→import { createServerClient } from "@supabase/ssr";
     3→import { cookies } from "next/headers";
     4→
     5→export async function GET(request: Request) {
     6→  const { searchParams, origin } = new URL(request.url);
     7→  const code = searchParams.get("code");
     8→  const rawNext = searchParams.get("next") ?? "/dashboard";
     9→  // Prevent open redirect — only allow relative paths starting with /
    10→  const next = rawNext.startsWith("/") && !rawNext.startsWith("//") ? rawNext : "/dashboard";
    11→
    12→  if (code) {
    13→    const cookieStore = await cookies();
    14→
    15→    const supabase = createServerClient(
    16→      process.env.NEXT_PUBLIC_SUPABASE_URL!,
    17→      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    18→      {
    19→        cookies: {
    20→          getAll() {
    21→            return cookieStore.getAll();
    22→          },
    23→          setAll(cookiesToSet) {
    24→            try {
    25→              cookiesToSet.forEach(({ name, value, options }) =>
    26→                cookieStore.set(name, value, options)
    27→              );
    28→            } catch {
    29→              // Cookies can only be set in Server Actions or Route Handlers
    30→            }
    31→          },
    32→        },
    33→      }
    34→    );
    35→
    36→    const { error } = await supabase.auth.exchangeCodeForSession(code);
    37→
    38→    if (!error) {
    39→      return NextResponse.redirect(`${origin}${next}`);
    40→    }
    41→  }
    42→
    43→  return NextResponse.redirect(
    44→    `${origin}/auth/login?error=Could+not+authenticate`
    45→  );
    46→}
    47→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


1→"use client";
     2→
     3→import { useState } from "react";
     4→import { useRouter } from "next/navigation";
     5→import Link from "next/link";
     6→import { createClient } from "@/lib/supabase/client";
     7→import { Button } from "@/components/ui/button";
     8→import { Input } from "@/components/ui/input";
     9→import { Label } from "@/components/ui/label";
    10→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
    11→
    12→export default function LoginPage() {
    13→  const [email, setEmail] = useState("");
    14→  const [password, setPassword] = useState("");
    15→  const [error, setError] = useState<string | null>(null);
    16→  const [loading, setLoading] = useState(false);
    17→  const router = useRouter();
    18→  const supabase = createClient();
    19→
    20→  async function handleLogin(e: React.FormEvent) {
    21→    e.preventDefault();
    22→    setLoading(true);
    23→    setError(null);
    24→
    25→    const { data, error } = await supabase.auth.signInWithPassword({
    26→      email,
    27→      password,
    28→    });
    29→
    30→    if (error) {
    31→      setError(error.message);
    32→      setLoading(false);
    33→      return;
    34→    }
    35→
    36→    // Check if email is verified before allowing dashboard access
    37→    if (data.user && !data.user.email_confirmed_at) {
    38→      router.push("/auth/verify-email");
    39→      router.refresh();
    40→      return;
    41→    }
    42→
    43→    router.push("/dashboard");
    44→    router.refresh();
    45→  }
    46→
    47→  return (
    48→

    49→      <Card className="w-full max-w-md">
    50→        <CardHeader className="text-center">
    51→          <CardTitle className="text-2xl font-bold">Welcome back</CardTitle>
    52→          <CardDescription>Sign in to your RateRight account</CardDescription>
    53→        </CardHeader>
    54→        <CardContent>
    55→          <form onSubmit={handleLogin} className="space-y-4">
    56→

    57→              Email
    58→              <Input
    59→                id="email"
    60→                type="email"
    61→                placeholder="you@example.com"
    62→                value={email}
    63→                onChange={(e) => setEmail(e.target.value)}
    64→                required
    65→                autoComplete="email"
    66→                className="h-12 text-base"
    67→              />
    68→

    69→

    70→              Password
    71→              <Input
    72→                id="password"
    73→                type="password"
    74→                placeholder="Your password"
    75→                value={password}
    76→                onChange={(e) => setPassword(e.target.value)}
    77→                required
    78→                autoComplete="current-password"
    79→                className="h-12 text-base"
    80→              />
    81→

    82→

    83→              <Link href="/auth/forgot-password" className="text-sm text-primary underline hover:no-underline">
    84→                Forgot password?
    85→              </Link>
    86→

    87→            {error && (
    88→

    89→
{error}

    90→

    91→            )}
    92→            <Button type="submit" className="w-full h-12 text-base font-semibold" disabled={loading}>
    93→              {loading ? "Signing in..." : "Sign in"}
    94→            </Button>
    95→          </form>
    96→

    97→            Don't have an account?{" "}
    98→            <Link href="/auth/signup" className="text-primary underline hover:no-underline">
    99→              Sign up
   100→            </Link>
   101→

   102→        </CardContent>
   103→      </Card>
   104→

   105→  );
   106→}
   107→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


1→"use client";
     2→import { useState } from "react";
     3→import { useRouter } from "next/navigation";
     4→import Link from "next/link";
     5→import { createClient } from "@/lib/supabase/client";
     6→import { Button } from "@/components/ui/button";
     7→import { Input } from "@/components/ui/input";
     8→import { Label } from "@/components/ui/label";
     9→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
    10→import { HardHat, Building2 } from "lucide-react";
    11→import type { UserType } from "@/lib/types";
    12→
    13→export default function SignupPage() {
    14→  const [email, setEmail] = useState("");
    15→  const [password, setPassword] = useState("");
    16→  const [userType, setUserType] = useState<UserType | null>(null);
    17→  const [agreedToTerms, setAgreedToTerms] = useState(false);
    18→  const [error, setError] = useState<string | null>(null);
    19→  const [loading, setLoading] = useState(false);
    20→  const router = useRouter();
    21→  const supabase = createClient();
    22→
    23→  async function handleSignup(e: React.FormEvent) {
    24→    e.preventDefault();
    25→    if (!userType) {
    26→      setError("Please select whether you're hiring or looking for work");
    27→      return;
    28→    }
    29→    if (!agreedToTerms) {
    30→      setError("Please agree to the Terms of Service and Privacy Policy");
    31→      return;
    32→    }
    33→
    34→    setLoading(true);
    35→    setError(null);
    36→
    37→    const { error } = await supabase.auth.signUp({
    38→      email,
    39→      password,
    40→      options: {
    41→        data: { type: userType },
    42→        emailRedirectTo: `${window.location.origin}/auth/callback`,
    43→      },
    44→    });
    45→
    46→    if (error) {
    47→      setError(error.message);
    48→      setLoading(false);
    49→      return;
    50→    }
    51→
    52→    // Redirect to email verification page instead of dashboard
    53→    router.push("/auth/verify-email");
    54→    router.refresh();
    55→  }
    56→
    57→  return (
    58→

    59→      <Card className="w-full max-w-md">
    60→        <CardHeader className="text-center">
    61→          <CardTitle className="text-2xl font-bold">Create your account</CardTitle>
    62→          <CardDescription>
    63→            Sign up to find work or hire workers
    64→          </CardDescription>
    65→        </CardHeader>
    66→        <CardContent>
    67→          <form onSubmit={handleSignup} className="space-y-4">
    68→

    69→

    70→                Email
    71→                <Input
    72→                  id="email"
    73→                  type="email"
    74→                  value={email}
    75→                  onChange={(e) => setEmail(e.target.value)}
    76→                  required
    77→                  placeholder="you@example.com"
    78→                  className="h-12 text-base"
    79→                />
    80→

    81→

    82→                Password
    83→                <Input
    84→                  id="password"
    85→                  type="password"
    86→                  value={password}
    87→                  onChange={(e) => setPassword(e.target.value)}
    88→                  required
    89→                  placeholder="Create a password"
    90→                  minLength={6}
    91→                  className="h-12 text-base"
    92→                />
    93→

    94→

    95→
    96→

    97→              I am a...
    98→

    99→                <Button
   100→                  type="button"
   101→                  variant={userType === "contractor" ? "default" : "outline"}
   102→                  onClick={() => setUserType("contractor")}
   103→                  className="h-24 flex-col gap-2"
   104→                >
   105→                  <Building2 className="h-6 w-6" />
   106→                  Contractor
   107→                  Hiring workers
   108→                </Button>
   109→                <Button
   110→                  type="button"
   111→                  variant={userType === "worker" ? "default" : "outline"}
   112→                  onClick={() => setUserType("worker")}
   113→                  className="h-24 flex-col gap-2"
   114→                >
   115→                  <HardHat className="h-6 w-6" />
   116→                  Worker
   117→                  Looking for work
   118→                </Button>
   119→

   120→

   121→
   122→

   123→              <input
   124→                type="checkbox"
   125→                id="terms"
   126→                checked={agreedToTerms}
   127→                onChange={(e) => setAgreedToTerms(e.target.checked)}
   128→                className="mt-1 h-4 w-4 rounded border-border accent-primary"
   129→              />
   130→
   131→                I agree to the{" "}
   132→                <Link href="/legal/terms-of-service" className="text-primary underline hover:no-underline" target="_blank">
   133→                  Terms of Service
   134→                </Link>{" "}
   135→                and{" "}
   136→                <Link href="/legal/privacy-policy" className="text-primary underline hover:no-underline" target="_blank">
   137→                  Privacy Policy
   138→                </Link>
   139→
   140→

   141→
   142→            {error && (
   143→

   144→
{error}

   145→

   146→            )}
   147→
   148→            <Button type="submit" className="w-full h-12 text-base font-semibold" disabled={loading || !agreedToTerms}>
   149→              {loading ? "Creating account..." : "Create account"}
   150→            </Button>
   151→
   152→

   153→              Already have an account?{" "}
   154→              <Link href="/auth/login" className="text-primary underline hover:no-underline">
   155→                Sign in
   156→              </Link>
   157→

   158→          </form>
   159→        </CardContent>
   160→      </Card>
   161→

   162→  );
   163→}
   164→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


1→"use client";
     2→
     3→import { useState } from "react";
     4→import Link from "next/link";
     5→import { createClient } from "@/lib/supabase/client";
     6→import { Button } from "@/components/ui/button";
     7→import { Input } from "@/components/ui/input";
     8→import { Label } from "@/components/ui/label";
     9→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
    10→import { Mail, CheckCircle2 } from "lucide-react";
    11→
    12→export default function ForgotPasswordPage() {
    13→  const [email, setEmail] = useState("");
    14→  const [error, setError] = useState<string | null>(null);
    15→  const [loading, setLoading] = useState(false);
    16→  const [emailSent, setEmailSent] = useState(false);
    17→  const supabase = createClient();
    18→
    19→  async function handleSubmit(e: React.FormEvent) {
    20→    e.preventDefault();
    21→    setLoading(true);
    22→    setError(null);
    23→
    24→    const { error } = await supabase.auth.resetPasswordForEmail(email, {
    25→      redirectTo: `${window.location.origin}/auth/callback?next=/auth/reset-password`,
    26→    });
    27→
    28→    if (error) {
    29→      setError(error.message);
    30→      setLoading(false);
    31→      return;
    32→    }
    33→
    34→    setEmailSent(true);
    35→    setLoading(false);
    36→  }
    37→
    38→  if (emailSent) {
    39→    return (
    40→

    41→        <Card className="w-full max-w-md">
    42→          <CardHeader className="text-center">
    43→

    44→              <Mail className="h-6 w-6 text-primary" />
    45→

    46→            <CardTitle className="text-2xl font-bold">Check your email</CardTitle>
    47→            <CardDescription className="mt-2">
    48→              We've sent a password reset link to {email}. Click the link in the email to reset your password.
    49→            </CardDescription>
    50→          </CardHeader>
    51→          <CardContent className="space-y-3">
    52→

    53→

    54→                <CheckCircle2 className="h-4 w-4 text-green-600 shrink-0" />
    55→
Reset link sent

    56→

    57→

    58→            <Button
    59→              onClick={() => { setEmailSent(false); setEmail(""); }}
    60→              variant="outline"
    61→              className="w-full h-12 text-base"
    62→            >
    63→              Try a different email
    64→            </Button>
    65→            <Button
    66→              asChild
    67→              variant="ghost"
    68→              className="w-full h-12 text-base"
    69→            >
    70→              <Link href="/auth/login">Back to login</Link>
    71→            </Button>
    72→          </CardContent>
    73→        </Card>
    74→

    75→    );
    76→  }
    77→
    78→  return (
    79→

    80→      <Card className="w-full max-w-md">
    81→        <CardHeader className="text-center">
    82→          <CardTitle className="text-2xl font-bold">Reset your password</CardTitle>
    83→          <CardDescription>Enter your email and we'll send you a reset link</CardDescription>
    84→        </CardHeader>
    85→        <CardContent>
    86→          <form onSubmit={handleSubmit} className="space-y-4">
    87→

    88→              Email
    89→              <Input
    90→                id="email"
    91→                type="email"
    92→                placeholder="you@example.com"
    93→                value={email}
    94→                onChange={(e) => setEmail(e.target.value)}
    95→                required
    96→                autoComplete="email"
    97→                className="h-12 text-base"
    98→              />
    99→

   100→            {error && (
   101→

   102→
{error}

   103→

   104→            )}
   105→            <Button type="submit" className="w-full h-12 text-base font-semibold" disabled={loading}>
   106→              {loading ? "Sending..." : "Send reset link"}
   107→            </Button>
   108→          </form>
   109→

   110→            Remember your password?{" "}
   111→            <Link href="/auth/login" className="text-primary underline hover:no-underline">
   112→              Sign in
   113→            </Link>
   114→

   115→        </CardContent>
   116→      </Card>
   117→

   118→  );
   119→}
   120→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


1→"use client";
     2→
     3→import { useState, useEffect } from "react";
     4→import { useRouter } from "next/navigation";
     5→import { createClient } from "@/lib/supabase/client";
     6→import { Button } from "@/components/ui/button";
     7→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
     8→import { Mail, CheckCircle2, Clock } from "lucide-react";
     9→
    10→export default function VerifyEmailPage() {
    11→  const [emailSent, setEmailSent] = useState(false);
    12→  const [loading, setLoading] = useState(false);
    13→  const [error, setError] = useState<string | null>(null);
    14→  const router = useRouter();
    15→  const supabase = createClient();
    16→
    17→  useEffect(() => {
    18→    // Check if user is already logged in and email is verified
    19→    checkEmailStatus();
    20→  }, []);
    21→
    22→  async function checkEmailStatus() {
    23→    const { data: { user } } = await supabase.auth.getUser();
    24→
    25→    if (user?.email_confirmed_at) {
    26→      // Email is already verified, redirect to dashboard
    27→      router.push('/dashboard');
    28→    }
    29→  }
    30→
    31→  async function resendVerificationEmail() {
    32→    setLoading(true);
    33→    setError(null);
    34→
    35→    try {
    36→      const { data: { user } } = await supabase.auth.getUser();
    37→
    38→      if (!user) {
    39→        setError("No user found. Please log in again.");
    40→        setLoading(false);
    41→        return;
    42→      }
    43→
    44→      const { error } = await supabase.auth.resend({
    45→        type: 'signup',
    46→        email: user.email as string,
    47→      });
    48→
    49→      if (error) {
    50→        setError(error.message);
    51→      } else {
    52→        setEmailSent(true);
    53→      }
    54→    } catch (error) {
    55→      setError("Failed to resend verification email.");
    56→    } finally {
    57→      setLoading(false);
    58→    }
    59→  }
    60→
    61→  return (
    62→

    63→      <Card className="w-full max-w-md">
    64→        <CardHeader className="text-center">
    65→

    66→            <Mail className="h-6 w-6 text-primary" />
    67→

    68→          <CardTitle className="text-2xl font-bold">
    69→            Check your email
    70→          </CardTitle>
    71→          <CardDescription className="mt-2">
    72→            We've sent a verification email to your address. Please check your inbox and click the link to verify.
    73→          </CardDescription>
    74→        </CardHeader>
    75→        <CardContent className="space-y-4">
    76→

    77→

    78→              <Clock className="h-5 w-5 text-primary shrink-0 mt-0.5" />
    79→

    80→
Didn't receive the email?

    81→

    82→                  Check your spam folder or click the button below to resend.
    83→

    84→

    85→

    86→

    87→
    88→          {error && (
    89→

    90→
{error}

    91→

    92→          )}
    93→
    94→          {emailSent && (
    95→

    96→

    97→                <CheckCircle2 className="h-4 w-4 text-green-600 shrink-0" />
    98→

    99→                  Verification email resent. Please check your inbox.
   100→

   101→

   102→

   103→          )}
   104→
   105→

   106→            <Button
   107→              onClick={resendVerificationEmail}
   108→              disabled={loading || emailSent}
   109→              className="w-full h-12 text-base font-semibold"
   110→              variant="outline"
   111→            >
   112→              {loading ? "Sending..." : emailSent ? "Email sent" : "Resend verification email"}
   113→            </Button>
   114→
   115→            <Button
   116→              onClick={() => router.push('/auth/login')}
   117→              variant="ghost"
   118→              className="w-full h-12 text-base"
   119→            >
   120→              Back to login
   121→            </Button>
   122→

   123→        </CardContent>
   124→      </Card>
   125→

   126→  );
   127→}
   128→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


1→"use client";
     2→
     3→import { useState } from "react";
     4→import { useRouter } from "next/navigation";
     5→import Link from "next/link";
     6→import { createClient } from "@/lib/supabase/client";
     7→import { Button } from "@/components/ui/button";
     8→import { Input } from "@/components/ui/input";
     9→import { Label } from "@/components/ui/label";
    10→import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
    11→import { CheckCircle2 } from "lucide-react";
    12→import { DELAY_RESET_PASSWORD_REDIRECT } from "@/lib/constants";
    13→
    14→export default function ResetPasswordPage() {
    15→  const [password, setPassword] = useState("");
    16→  const [confirmPassword, setConfirmPassword] = useState("");
    17→  const [error, setError] = useState<string | null>(null);
    18→  const [loading, setLoading] = useState(false);
    19→  const [success, setSuccess] = useState(false);
    20→  const router = useRouter();
    21→  const supabase = createClient();
    22→
    23→  async function handleReset(e: React.FormEvent) {
    24→    e.preventDefault();
    25→    setError(null);
    26→
    27→    if (password.length < 6) {
    28→      setError("Password must be at least 6 characters.");
    29→      return;
    30→    }
    31→
    32→    if (password !== confirmPassword) {
    33→      setError("Passwords do not match.");
    34→      return;
    35→    }
    36→
    37→    setLoading(true);
    38→
    39→    const { error } = await supabase.auth.updateUser({ password });
    40→
    41→    if (error) {
    42→      setError(error.message);
    43→      setLoading(false);
    44→      return;
    45→    }
    46→
    47→    setSuccess(true);
    48→    setLoading(false);
    49→
    50→    setTimeout(() => {
    51→      router.push("/dashboard");
    52→      router.refresh();
    53→    }, DELAY_RESET_PASSWORD_REDIRECT);
    54→  }
    55→
    56→  if (success) {
    57→    return (
    58→

    59→        <Card className="w-full max-w-md">
    60→          <CardHeader className="text-center">
    61→

    62→              <CheckCircle2 className="h-6 w-6 text-green-600" />
    63→

    64→            <CardTitle className="text-2xl font-bold">Password updated</CardTitle>
    65→            <CardDescription className="mt-2">
    66→              Your password has been reset. Redirecting to dashboard...
    67→            </CardDescription>
    68→          </CardHeader>
    69→        </Card>
    70→

    71→    );
    72→  }
    73→
    74→  return (
    75→

    76→      <Card className="w-full max-w-md">
    77→        <CardHeader className="text-center">
    78→          <CardTitle className="text-2xl font-bold">Set new password</CardTitle>
    79→          <CardDescription>Enter your new password below</CardDescription>
    80→        </CardHeader>
    81→        <CardContent>
    82→          <form onSubmit={handleReset} className="space-y-4">
    83→

    84→              New password
    85→              <Input
    86→                id="password"
    87→                type="password"
    88→                placeholder="At least 6 characters"
    89→                value={password}
    90→                onChange={(e) => setPassword(e.target.value)}
    91→                required
    92→                minLength={6}
    93→                autoComplete="new-password"
    94→                className="h-12 text-base"
    95→              />
    96→

    97→

    98→              Confirm password
    99→              <Input
   100→                id="confirmPassword"
   101→                type="password"
   102→                placeholder="Confirm your password"
   103→                value={confirmPassword}
   104→                onChange={(e) => setConfirmPassword(e.target.value)}
   105→                required
   106→                minLength={6}
   107→                autoComplete="new-password"
   108→                className="h-12 text-base"
   109→              />
   110→

   111→            {error && (
   112→

   113→
{error}

   114→

   115→            )}
   116→            <Button type="submit" className="w-full h-12 text-base font-semibold" disabled={loading}>
   117→              {loading ? "Updating..." : "Update password"}
   118→            </Button>
   119→          </form>
   120→

   121→            <Link href="/auth/login" className="text-primary underline hover:no-underline">
   122→              Back to login
   123→            </Link>
   124→

   125→        </CardContent>
   126→      </Card>
   127→

   128→  );
   129→}
   130→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→"use client";
     2→
     3→import { useState, useEffect, useCallback, useRef } from "react";
     4→import { createClient } from "@/lib/supabase/client";
     5→import type { Notification } from "@/lib/types";
     6→import type { RealtimeChannel } from "@supabase/supabase-js";
     7→
     8→interface UseNotificationsOptions {
     9→  limit?: number;
    10→  unreadOnly?: boolean;
    11→}
    12→
    13→interface UseNotificationsReturn {
    14→  notifications: Notification[];
    15→  unreadCount: number;
    16→  loading: boolean;
    17→  error: Error | null;
    18→  markAsRead: (id: string) => Promise<void>;
    19→  markAllAsRead: () => Promise<void>;
    20→  refetch: () => Promise<void>;
    21→}
    22→
    23→/**
    24→ * Hook to fetch and manage notifications from Supabase
    25→ *
    26→ * Features:
    27→ * - Fetches notifications for the current user
    28→ * - Real-time updates via Supabase subscriptions
    29→ * - Mark individual or all notifications as read
    30→ * - Unread count tracking
    31→ *
    32→ * @example
    33→ * ```tsx
    34→ * const { notifications, unreadCount, markAsRead } = useNotifications({ limit: 10 });
    35→ * ```
    36→ */
    37→export function useNotifications(options: UseNotificationsOptions = {}): UseNotificationsReturn {
    38→  const { limit = 50, unreadOnly = false } = options;
    39→  const [notifications, setNotifications] = useState<Notification[]>([]);
    40→  const [loading, setLoading] = useState(true);
    41→  const [error, setError] = useState<Error | null>(null);
    42→  const channelRef = useRef<RealtimeChannel | null>(null);
    43→  const supabase = createClient();
    44→
    45→  const fetchNotifications = useCallback(async () => {
    46→    try {
    47→      setLoading(true);
    48→      setError(null);
    49→
    50→      const { data: { user } } = await supabase.auth.getUser();
    51→      if (!user) {
    52→        setNotifications([]);
    53→        setLoading(false);
    54→        return;
    55→      }
    56→
    57→      let query = supabase
    58→        .from("notifications")
    59→        .select("id, profile_id, type, title, body, data, read, created_at")
    60→        .eq("profile_id", user.id)
    61→        .order("created_at", { ascending: false });
    62→
    63→      if (unreadOnly) {
    64→        query = query.eq("read", false);
    65→      }
    66→
    67→      if (limit) {
    68→        query = query.limit(limit);
    69→      }
    70→
    71→      const { data, error: fetchError } = await query;
    72→
    73→      if (fetchError) {
    74→        throw new Error(fetchError.message);
    75→      }
    76→
    77→      setNotifications((data as Notification[]) ?? []);
    78→    } catch (err) {
    79→      setError(err instanceof Error ? err : new Error("Failed to fetch notifications"));
    80→    } finally {
    81→      setLoading(false);
    82→    }
    83→  }, [supabase, limit, unreadOnly]);
    84→
    85→  // Calculate unread count from notifications
    86→  const unreadCount = notifications.filter((n) => !n.read).length;
    87→
    88→  // Mark a single notification as read
    89→  const markAsRead = useCallback(async (id: string) => {
    90→    try {
    91→      const { error: updateError } = await supabase
    92→        .from("notifications")
    93→        .update({ read: true })
    94→        .eq("id", id);
    95→
    96→      if (updateError) {
    97→        throw new Error(updateError.message);
    98→      }
    99→
   100→      // Optimistically update local state
   101→      setNotifications((prev) =>
   102→        prev.map((n) => (n.id === id ? { ...n, read: true } : n))
   103→      );
   104→    } catch (err) {
   105→      throw err;
   106→    }
   107→  }, [supabase]);
   108→
   109→  // Mark all notifications as read
   110→  const markAllAsRead = useCallback(async () => {
   111→    try {
   112→      const { data: { user } } = await supabase.auth.getUser();
   113→      if (!user) return;
   114→
   115→      const { error: updateError } = await supabase
   116→        .from("notifications")
   117→        .update({ read: true })
   118→        .eq("profile_id", user.id)
   119→        .eq("read", false);
   120→
   121→      if (updateError) {
   122→        throw new Error(updateError.message);
   123→      }
   124→
   125→      // Optimistically update local state
   126→      setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));
   127→    } catch (err) {
   128→      throw err;
   129→    }
   130→  }, [supabase]);
   131→
   132→  // Refetch notifications
   133→  const refetch = useCallback(async () => {
   134→    await fetchNotifications();
   135→  }, [fetchNotifications]);
   136→
   137→  // Initial fetch
   138→  useEffect(() => {
   139→    fetchNotifications();
   140→  }, [fetchNotifications]);
   141→
   142→  // Subscribe to real-time updates
   143→  useEffect(() => {
   144→    // Clean up previous channel if it exists
   145→    if (channelRef.current) {
   146→      supabase.removeChannel(channelRef.current);
   147→    }
   148→
   149→    const channel = supabase
   150→      .channel(`notifications-${Date.now()}`)
   151→      .on(
   152→        "postgres_changes",
   153→        {
   154→          event: "INSERT",
   155→          schema: "public",
   156→          table: "notifications",
   157→        },
   158→        (payload) => {
   159→          // Add new notification to the list
   160→          const newNotification = payload.new as Notification;
   161→          setNotifications((prev) => {
   162→            // Avoid duplicates
   163→            if (prev.some((n) => n.id === newNotification.id)) {
   164→              return prev;
   165→            }
   166→            return [newNotification, ...prev].slice(0, limit);
   167→          });
   168→        }
   169→      )
   170→      .on(
   171→        "postgres_changes",
   172→        {
   173→          event: "UPDATE",
   174→          schema: "public",
   175→          table: "notifications",
   176→        },
   177→        (payload) => {
   178→          // Update existing notification
   179→          const updatedNotification = payload.new as Notification;
   180→          setNotifications((prev) =>
   181→            prev.map((n) => (n.id === updatedNotification.id ? updatedNotification : n))
   182→          );
   183→        }
   184→      )
   185→      .on(
   186→        "postgres_changes",
   187→        {
   188→          event: "DELETE",
   189→          schema: "public",
   190→          table: "notifications",
   191→        },
   192→        (payload) => {
   193→          // Remove deleted notification
   194→          const deletedId = payload.old.id;
   195→          setNotifications((prev) => prev.filter((n) => n.id !== deletedId));
   196→        }
   197→      )
   198→      .subscribe();
   199→
   200→    channelRef.current = channel;
   201→
   202→    return () => {
   203→      if (channelRef.current) {
   204→        supabase.removeChannel(channelRef.current);
   205→      }
   206→    };
   207→  }, [supabase, limit]);
   208→
   209→  return {
   210→    notifications,
   211→    unreadCount,
   212→    loading,
   213→    error,
   214→    markAsRead,
   215→    markAllAsRead,
   216→    refetch,
   217→  };
   218→}
   219→
   220→/**
   221→ * Hook to get only the unread notification count
   222→ * Lightweight version for header/badge use
   223→ *
   224→ * @example
   225→ * ```tsx
   226→ * const { count } = useUnreadCount();
   227→ * ```
   228→ */
   229→export function useUnreadCount(): { count: number; loading: boolean; error: Error | null } {
   230→  const [count, setCount] = useState(0);
   231→  const [loading, setLoading] = useState(true);
   232→  const [error, setError] = useState<Error | null>(null);
   233→  const channelRef = useRef<RealtimeChannel | null>(null);
   234→  const supabase = createClient();
   235→
   236→  const fetchCount = useCallback(async () => {
   237→    try {
   238→      const { data: { user } } = await supabase.auth.getUser();
   239→      if (!user) {
   240→        setCount(0);
   241→        setLoading(false);
   242→        return;
   243→      }
   244→
   245→      const { count: unreadCount, error: countError } = await supabase
   246→        .from("notifications")
   247→        .select("*", { count: "exact", head: true })
   248→        .eq("profile_id", user.id)
   249→        .eq("read", false);
   250→
   251→      if (countError) {
   252→        throw new Error(countError.message);
   253→      }
   254→
   255→      setCount(unreadCount ?? 0);
   256→      setError(null);
   257→    } catch (err) {
   258→      setError(err instanceof Error ? err : new Error("Failed to fetch unread count"));
   259→    } finally {
   260→      setLoading(false);
   261→    }
   262→  }, [supabase]);
   263→
   264→  useEffect(() => {
   265→    fetchCount();
   266→  }, [fetchCount]);
   267→
   268→  // Subscribe to real-time updates
   269→  useEffect(() => {
   270→    // Clean up previous channel if it exists
   271→    if (channelRef.current) {
   272→      supabase.removeChannel(channelRef.current);
   273→    }
   274→
   275→    const channel = supabase
   276→      .channel(`unread-count-${Date.now()}`)
   277→      .on(
   278→        "postgres_changes",
   279→        {
   280→          event: "INSERT",
   281→          schema: "public",
   282→          table: "notifications",
   283→        },
   284→        () => fetchCount()
   285→      )
   286→      .on(
   287→        "postgres_changes",
   288→        {
   289→          event: "UPDATE",
   290→          schema: "public",
   291→          table: "notifications",
   292→        },
   293→        () => fetchCount()
   294→      )
   295→      .subscribe();
   296→
   297→    channelRef.current = channel;
   298→
   299→    return () => {
   300→      if (channelRef.current) {
   301→        supabase.removeChannel(channelRef.current);
   302→      }
   303→    };
   304→  }, [supabase, fetchCount]);
   305→
   306→  return { count, loading, error };
   307→}
   308→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


Now let me check for any additional auth-related files such as server actions, API routes, and the sign-out component to get a complete picture.


No files found


Found 3 files
/home/ccuser/the-50-dollar-app/src/components/sign-out-button.tsx
/home/ccuser/the-50-dollar-app/src/app/(authenticated)/dashboard/page.tsx
/home/ccuser/the-50-dollar-app/src/app/(authenticated)/layout.tsx


/home/ccuser/the-50-dollar-app/src/app/api/auth/csrf/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/auth/verify-email/route.ts


Found 17 files
/home/ccuser/the-50-dollar-app/src/app/api/auth/verify-email/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/payments/create/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/ai/transcribe-voice/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/ai/parse-profile-voice/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/ai/generate-profile/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/ai/voice-complete/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/ai/generate-job/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/company-logo/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/match/find-matches/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/abn-lookup/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/messages/start/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/messages/[id]/send/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/messages/[id]/route.ts
/home/ccuser/the-50-dollar-app/src/app/api/messages/route.ts
/home/ccuser/the-50-dollar-app/src/lib/rate-limit.ts
/home/ccuser/the-50-dollar-app/src/lib/api-errors.ts
/home/ccuser/the-50-dollar-app/src/lib/constants.ts


     1→// Payment constants
     2→export const PAYMENT_AMOUNT = 50.0;
     3→
     4→// Rate limiting constants
     5→export const RATE_LIMIT_REQUESTS_PER_IP = 5;
     6→export const RATE_LIMIT_REQUESTS_PER_USER = 10;
     7→export const RATE_LIMIT_WINDOW_MINUTES = 15;
     8→export const RATE_LIMIT_WINDOW_MS = RATE_LIMIT_WINDOW_MINUTES * 60 * 1000;
     9→export const SEND_MESSAGE_RATE_LIMIT = 60; // 60 messages per 15 min window per IP
    10→export const VERIFY_RATE_LIMIT = 5; // 5 email verification attempts per window
    11→export const VERIFY_WINDOW_MS = RATE_LIMIT_WINDOW_MS;
    12→
    13→// AI rate limiting constants
    14→export const AI_RATE_LIMIT = 5;
    15→export const AI_WINDOW_MS = RATE_LIMIT_WINDOW_MS;
    16→
    17→// Input validation constants
    18→export const MAX_LENGTH_TRADE = 100;
    19→export const MAX_LENGTH_LOCATION = 200;
    20→export const MAX_LENGTH_AVAILABILITY = 50;
    21→export const MAX_LENGTH_TRANSCRIPT = 5000;
    22→export const MAX_LENGTH_VOICE_TRANSCRIPT = 2000;
    23→export const MAX_LENGTH_MESSAGE_CONTENT = 5000;
    24→export const MAX_LENGTH_EMAIL = 254;
    25→export const MAX_RATING_TEXT_LENGTH = 500;
    26→
    27→// Timeout constants (in milliseconds)
    28→export const TIMEOUT_ABN_LOOKUP = 10000;
    29→export const DELAY_RESET_PASSWORD_REDIRECT = 2000;
    30→
    31→// HTTP status codes
    32→export const HTTP_STATUS_INTERNAL_SERVER_ERROR = 500;
    33→export const HTTP_STATUS_OK = 200;
    34→
    35→// Australian postcode constants (for testing/validation)
    36→export const POSTCODE_SYDNEY = '2000';
    37→export const POSTCODE_MELBOURNE = '3000';
    38→
    39→// Percentage constants
    40→export const PERCENTAGE_WORKER_KEEP = 100; // Workers keep 100% of wages
    41→
    42→// Worker trades - used in worker profiles and AI validation
    43→export const TRADES = [
    44→  "Labourer",
    45→  "Steel Fixer",
    46→  "Formworker",
    47→  "Carpenter",
    48→  "Electrician",
    49→  "Plumber",
    50→  "Concreter",
    51→  "Bricklayer",
    52→  "Painter",
    53→  "Roofer",
    54→  "Scaffolder",
    55→  "Tiler",
    56→  "Plasterer",
    57→  "Welder",
    58→  "Crane Operator",
    59→  "Excavator Operator",
    60→] as const;
    61→
    62→export type Trade = typeof TRADES[number];
    63→
    64→// Contractor job categories - used in job posting
    65→export const TRADE_CATEGORIES = [
    66→  { id: "steel-fixer", label: "Steel Fixer" },
    67→  { id: "formworker", label: "Formworker" },
    68→  { id: "labourer", label: "Labourer" },
    69→  { id: "concreter", label: "Concreter" },
    70→  { id: "carpenter", label: "Carpenter" },
    71→  { id: "scaffolder", label: "Scaffolder" },
    72→  { id: "other", label: "Other" },
    73→] as const;
    74→
    75→export type TradeCategory = typeof TRADE_CATEGORIES[number];
    76→
    77→/**
    78→ * Convert job category ID to trade name
    79→ *
    80→ * @param categoryId - ID from TRADE_CATEGORIES
    81→ * @returns Trade name label or null if not found
    82→ *
    83→ * @example
    84→ * getTradeFromCategoryId('steel-fixer')
    85→ * // Returns: 'Steel Fixer'
    86→ *
    87→ * @example
    88→ * getTradeFromCategoryId('invalid-id')
    89→ * // Returns: null
    90→ */
    91→export function getTradeFromCategoryId(categoryId: string): string | null {
    92→  const category = TRADE_CATEGORIES.find(cat => cat.id === categoryId);
    93→  return category ? category.label : null;
    94→}

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→"use client";
     2→
     3→import { useState } from "react";
     4→import { useRouter } from "next/navigation";
     5→import { createClient } from "@/lib/supabase/client";
     6→import { logger } from "@/lib/logger";
     7→import { Button } from "@/components/ui/button";
     8→import { LogOut, Loader2 } from "lucide-react";
     9→
    10→export function SignOutButton() {
    11→  const [loading, setLoading] = useState(false);
    12→  const router = useRouter();
    13→  const supabase = createClient();
    14→
    15→  async function handleSignOut() {
    16→    setLoading(true);
    17→    try {
    18→      await supabase.auth.signOut();
    19→      router.push("/");
    20→      router.refresh();
    21→    } catch (error) {
    22→      logger.error("Sign out error:", error);
    23→      setLoading(false);
    24→    }
    25→  }
    26→
    27→  return (
    28→    <Button
    29→      variant="ghost"
    30→      size="icon"
    31→      onClick={handleSignOut}
    32→      title="Sign out"
    33→      aria-label="Sign out"
    34→      disabled={loading}
    35→    >
    36→      {loading ? (
    37→        <Loader2 className="h-4 w-4 animate-spin" />
    38→      ) : (
    39→        <LogOut className="h-4 w-4" />
    40→      )}
    41→    </Button>
    42→  );
    43→}
    44→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→import { NextRequest, NextResponse } from 'next/server';
     2→import { logger } from '@/lib/logger';
     3→import { createCSRFEndpoint } from '@/lib/csrf';
     4→import { createClient } from '@/lib/supabase/server';
     5→import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '@/lib/constants';
     6→
     7→/**
     8→ * CSRF Token Endpoint
     9→ * Generates and returns a CSRF token for authenticated users
    10→ * This should be called after successful authentication
    11→ */
    12→export async function GET(request: NextRequest) {
    13→  try {
    14→    // Check if user is authenticated
    15→    const supabase = await createClient();
    16→    const { data: { user } } = await supabase.auth.getUser();
    17→
    18→    if (!user) {
    19→      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
    20→    }
    21→
    22→    // Create and return CSRF token
    23→    return createCSRFEndpoint(request);
    24→  } catch (error) {
    25→    logger.error('CSRF token generation error:', error);
    26→    return NextResponse.json({ error: 'Internal server error' }, { status: HTTP_STATUS_INTERNAL_SERVER_ERROR });
    27→  }
    28→}

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→/**
     2→ * Email verification API endpoint
     3→ * Placeholder for email verification
     4→ */
     5→
     6→import { NextRequest, NextResponse } from 'next/server'
     7→import { logger } from '@/lib/logger'
     8→import { checkRateLimit, getClientIP, createRateLimitResponse } from '@/lib/rate-limit'
     9→import { withCSRFProtection } from '@/lib/csrf'
    10→import {
    11→  VERIFY_RATE_LIMIT,
    12→  VERIFY_WINDOW_MS,
    13→  HTTP_STATUS_INTERNAL_SERVER_ERROR
    14→} from '@/lib/constants'
    15→
    16→const handler = async (request: NextRequest) => {
    17→  try {
    18→    // --- Rate limiting ---
    19→    const clientIP = getClientIP(request);
    20→    const rateLimit = await checkRateLimit(`verify-email:${clientIP}`, VERIFY_RATE_LIMIT, VERIFY_WINDOW_MS);
    21→
    22→    if (!rateLimit.allowed) {
    23→      const retryAfter = Math.ceil((rateLimit.resetTime - Date.now()) / 1000);
    24→      return createRateLimitResponse(clientIP, retryAfter);
    25→    }
    26→
    27→    const { userId, token } = await request.json()
    28→
    29→    if (!userId || !token) {
    30→      return NextResponse.json({ error: 'Missing required fields' }, { status: 400 })
    31→    }
    32→
    33→    // In production, implement proper token validation
    34→    // For now, return success
    35→
    36→    return NextResponse.json({
    37→      success: true,
    38→      message: 'Email verification endpoint ready'
    39→    })
    40→
    41→  } catch (error) {
    42→    logger.error('Email verification error:', error)
    43→    return NextResponse.json(
    44→      { error: 'Internal server error' },
    45→      { status: HTTP_STATUS_INTERNAL_SERVER_ERROR }
    46→    )
    47→  }
    48→}
    49→
    50→// Apply CSRF protection to the handler
    51→export const POST = withCSRFProtection(handler);
    52→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→import { NextRequest, NextResponse } from 'next/server';
     2→import { randomBytes, timingSafeEqual as cryptoTimingSafeEqual } from 'crypto';
     3→import { logger } from './logger';
     4→
     5→// CSRF token configuration
     6→const CSRF_TOKEN_LENGTH = 32;
     7→const CSRF_COOKIE_NAME = 'csrf-token';
     8→const CSRF_HEADER_NAME = 'x-csrf-token';
     9→const CSRF_FORM_FIELD_NAME = 'csrf_token';
    10→
    11→/**
    12→ * Generate a cryptographically secure CSRF token
    13→ */
    14→export function generateCSRFToken(): string {
    15→  return randomBytes(CSRF_TOKEN_LENGTH).toString('hex');
    16→}
    17→
    18→/**
    19→ * Create a CSRF token and set it in a cookie
    20→ */
    21→export function createCSRFToken(): { token: string; cookie: string } {
    22→  const token = generateCSRFToken();
    23→  let cookie = `${CSRF_COOKIE_NAME}=${token}; Path=/; SameSite=Strict; Max-Age=86400`; // 24 hours
    24→
    25→  // Add secure flag in production
    26→  if (process.env.NODE_ENV === 'production' && process.env.NEXT_PUBLIC_APP_URL?.startsWith('https://')) {
    27→    cookie += '; Secure';
    28→  }
    29→
    30→  return { token, cookie };
    31→}
    32→
    33→/**
    34→ * Get CSRF token from cookie
    35→ */
    36→export function getCSRFCookieToken(request: Request | NextRequest): string | null {
    37→  // Check if it's a NextRequest with cookies property
    38→  if ('cookies' in request && typeof request.cookies.get === 'function') {
    39→    const cookie = request.cookies.get(CSRF_COOKIE_NAME);
    40→    return cookie?.value || null;
    41→  }
    42→
    43→  // Fall back to parsing Cookie header for standard Request
    44→  const cookieHeader = request.headers.get('cookie');
    45→  if (!cookieHeader) return null;
    46→
    47→  const cookies = cookieHeader.split(';');
    48→  for (const cookie of cookies) {
    49→    const [name, value] = cookie.trim().split('=');
    50→    if (name === CSRF_COOKIE_NAME) {
    51→      return value;
    52→    }
    53→  }
    54→  return null;
    55→}
    56→
    57→/**
    58→ * Get CSRF token from request (header or form data)
    59→ */
    60→export function getCSRFRequestToken(request: Request | NextRequest): string | null {
    61→  // Check header first
    62→  const headerToken = request.headers.get(CSRF_HEADER_NAME);
    63→  if (headerToken) return headerToken;
    64→
    65→  // For form data, check in the request body (will need to parse appropriately)
    66→  // This is handled in the validation function for JSON requests
    67→  return null;
    68→}
    69→
    70→/**
    71→ * Validate CSRF token from request
    72→ * For JSON requests: checks x-csrf-token header
    73→ * For form requests: checks form field
    74→ */
    75→export async function validateCSRFToken(request: Request | NextRequest): Promise<boolean> {
    76→  const cookieToken = getCSRFCookieToken(request);
    77→
    78→  if (!cookieToken) {
    79→    logger.error('CSRF validation failed: No token in cookie');
    80→    return false;
    81→  }
    82→
    83→  let requestToken: string | null = null;
    84→
    85→  // For JSON requests
    86→  if (request.headers.get('content-type')?.includes('application/json')) {
    87→    requestToken = request.headers.get(CSRF_HEADER_NAME);
    88→
    89→    // If no header, check in JSON body
    90→    if (!requestToken) {
    91→      try {
    92→        const body = await request.clone().json();
    93→        requestToken = body[CSRF_FORM_FIELD_NAME] || null;
    94→      } catch {
    95→        // Ignore JSON parsing errors
    96→      }
    97→    }
    98→  }
    99→
   100→  // For form data requests
   101→  else if (request.headers.get('content-type')?.includes('multipart/form-data')) {
   102→    try {
   103→      const formData = await request.clone().formData();
   104→      requestToken = formData.get(CSRF_FORM_FIELD_NAME) as string;
   105→    } catch {
   106→      // Ignore form parsing errors
   107→    }
   108→  }
   109→
   110→  if (!requestToken) {
   111→    logger.error('CSRF validation failed: No token in request');
   112→    return false;
   113→  }
   114→
   115→  // Constant-time comparison to prevent timing attacks
   116→  const tokensMatch = timingSafeEqual(cookieToken, requestToken);
   117→
   118→  if (!tokensMatch) {
   119→    logger.error('CSRF validation failed: Token mismatch');
   120→    return false;
   121→  }
   122→
   123→  return true;
   124→}
   125→
   126→/**
   127→ * Timing-safe string comparison using Node.js crypto.timingSafeEqual
   128→ * Prevents timing attacks by using constant-time comparison
   129→ */
   130→function timingSafeEqual(a: string, b: string): boolean {
   131→  // crypto.timingSafeEqual requires buffers of equal length
   132→  // Pad the shorter string to match lengths to avoid leaking length info
   133→  const maxLength = Math.max(a.length, b.length);
   134→  const bufferA = Buffer.alloc(maxLength);
   135→  const bufferB = Buffer.alloc(maxLength);
   136→
   137→  bufferA.write(a);
   138→  bufferB.write(b);
   139→
   140→  try {
   141→    // This performs constant-time comparison
   142→    return cryptoTimingSafeEqual(bufferA, bufferB) && a.length === b.length;
   143→  } catch {
   144→    // If timingSafeEqual throws (shouldn't happen with equal-length buffers), fail closed
   145→    return false;
   146→  }
   147→}
   148→
   149→/**
   150→ * Check if request is same-origin
   151→ */
   152→export function isSameOrigin(request: Request | NextRequest): boolean {
   153→  const origin = request.headers.get('origin');
   154→  const referer = request.headers.get('referer');
   155→
   156→  if (!origin && !referer) {
   157→    // No origin/referer headers - reject the request to prevent CSRF bypass
   158→    return false;
   159→  }
   160→
   161→  const checkUrl = origin || referer;
   162→  if (!checkUrl) return false;
   163→
   164→  try {
   165→    const parsedUrl = new URL(checkUrl);
   166→    const appUrl = new URL(process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000');
   167→
   168→    return parsedUrl.origin === appUrl.origin;
   169→  } catch {
   170→    return false;
   171→  }
   172→}
   173→
   174→/**
   175→ * CSRF protection middleware
   176→ * Use this for routes that need CSRF protection
   177→ */
   178→export function withCSRFProtection<T extends NextRequest>(
   179→  handler: (request: T) => Promise<NextResponse | Response>,
   180→  options?: {
   181→    requireSameOrigin?: boolean;
   182→    skipValidation?: boolean;
   183→  }
   184→): (request: T) => Promise<NextResponse | Response> {
   185→  return async (request: T) => {
   186→    // Skip CSRF for GET, HEAD, OPTIONS requests
   187→    if (['GET', 'HEAD', 'OPTIONS'].includes(request.method)) {
   188→      return handler(request);
   189→    }
   190→
   191→    // Check if request is same-origin (optional, but recommended)
   192→    if (options?.requireSameOrigin !== false && !isSameOrigin(request)) {
   193→      return NextResponse.json(
   194→        { error: 'Cross-origin request not allowed' },
   195→        { status: 403 }
   196→      );
   197→    }
   198→
   199→    // Validate CSRF token (unless explicitly skipped)
   200→    if (!options?.skipValidation) {
   201→      const isValid = await validateCSRFToken(request);
   202→      if (!isValid) {
   203→        return NextResponse.json(
   204→          { error: 'Invalid or missing CSRF token' },
   205→          { status: 403 }
   206→        );
   207→      }
   208→    }
   209→
   210→    return handler(request);
   211→  };
   212→}
   213→
   214→/**
   215→ * Create a CSRF token endpoint
   216→ * This should be called when a user session starts
   217→ */
   218→export async function createCSRFEndpoint(request: NextRequest): Promise<NextResponse> {
   219→  if (request.method !== 'GET') {
   220→    return NextResponse.json({ error: 'Method not allowed' }, { status: 405 });
   221→  }
   222→
   223→  const { token, cookie } = createCSRFToken();
   224→
   225→  const response = NextResponse.json({ token });
   226→  response.headers.append('Set-Cookie', cookie);
   227→
   228→  return response;
   229→}

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→/**
     2→ * Client-side CSRF protection utilities
     3→ */
     4→
     5→import { logger } from './logger';
     6→
     7→const CSRF_HEADER_NAME = 'x-csrf-token';
     8→const CSRF_COOKIE_NAME = 'csrf-token';
     9→
    10→/**
    11→ * Get CSRF token from browser cookies
    12→ *
    13→ * @returns CSRF token value or null if not found
    14→ *
    15→ * @example
    16→ * const token = getCSRFCookie();
    17→ * if (token) {
    18→ *   // Use token in API requests
    19→ * }
    20→ */
    21→export function getCSRFCookie(): string | null {
    22→  if (typeof document === 'undefined') return null;
    23→
    24→  const cookies = document.cookie.split(';');
    25→  for (const cookie of cookies) {
    26→    const [name, value] = cookie.trim().split('=');
    27→    if (name === CSRF_COOKIE_NAME) {
    28→      return value;
    29→    }
    30→  }
    31→  return null;
    32→}
    33→
    34→/**
    35→ * Fetch a new CSRF token from the server
    36→ * This should be called after authentication to get a fresh token
    37→ *
    38→ * @returns Promise resolving to CSRF token or null if fetch failed
    39→ *
    40→ * @example
    41→ * const token = await fetchCSRFToken();
    42→ * if (token) {
    43→ *   // Store or use the new token
    44→ * }
    45→ */
    46→export async function fetchCSRFToken(): Promise<string | null> {
    47→  try {
    48→    const response = await fetch('/api/auth/csrf', {
    49→      method: 'GET',
    50→      credentials: 'include', // Include cookies
    51→    });
    52→
    53→    if (!response.ok) {
    54→      logger.error('Failed to fetch CSRF token:', response.status);
    55→      return null;
    56→    }
    57→
    58→    const data = await response.json();
    59→    return data.token || null;
    60→  } catch (error) {
    61→    logger.error('Error fetching CSRF token:', error);
    62→    return null;
    63→  }
    64→}
    65→
    66→/**
    67→ * Add CSRF token to fetch options
    68→ * Enhances existing request options with CSRF protection headers
    69→ *
    70→ * @param options - Existing fetch options to enhance
    71→ * @returns Enhanced fetch options with CSRF token header
    72→ *
    73→ * @example
    74→ * const response = await fetch('/api/data', withCSRFToken({
    75→ *   method: 'POST',
    76→ *   body: JSON.stringify(data)
    77→ * }));
    78→ */
    79→export function withCSRFToken(options: RequestInit = {}): RequestInit {
    80→  const token = getCSRFCookie();
    81→
    82→  if (!token) {
    83→    logger.warn('No CSRF token found in cookies');
    84→    return options;
    85→  }
    86→
    87→  return {
    88→    ...options,
    89→    headers: {
    90→      ...options.headers,
    91→      [CSRF_HEADER_NAME]: token,
    92→    },
    93→  };
    94→}
    95→
    96→/**
    97→ * Enhanced fetch with CSRF protection
    98→ * Automatically adds CSRF token to requests, falling back to regular fetch if no token
    99→ *
   100→ * @param input - URL or Request object
   101→ * @param options - Fetch options
   102→ * @returns Promise resolving to Response
   103→ *
   104→ * @example
   105→ * const response = await csrfFetch('/api/data', {
   106→ *   method: 'POST',
   107→ *   body: JSON.stringify(data)
   108→ * });
   109→ */
   110→export async function csrfFetch(input: RequestInfo | URL, options?: RequestInit): Promise<Response> {
   111→  const enhancedOptions = withCSRFToken(options);
   112→  return fetch(input, enhancedOptions);
   113→}
   114→
   115→/**
   116→ * Add CSRF token to FormData for multipart/form-data requests
   117→ * Appends the CSRF token as a form field
   118→ *
   119→ * @param formData - FormData object to enhance
   120→ * @returns The same FormData object with CSRF token added
   121→ *
   122→ * @example
   123→ * const formData = new FormData();
   124→ * formData.append('file', file);
   125→ * addCSRFTokenToFormData(formData);
   126→ * // formData now includes csrf_token field
   127→ */
   128→export function addCSRFTokenToFormData(formData: FormData): FormData {
   129→  const token = getCSRFCookie();
   130→
   131→  if (token) {
   132→    formData.append('csrf_token', token);
   133→  } else {
   134→    logger.warn('No CSRF token found in cookies');
   135→  }
   136→
   137→  return formData;
   138→}
   139→
   140→/**
   141→ * Initialize CSRF token after authentication
   142→ * Call this after successful login/signup to get a fresh CSRF token
   143→ *
   144→ * @returns Promise resolving to true if token was successfully fetched
   145→ *
   146→ * @example
   147→ * const success = await initializeCSRF();
   148→ * if (success) {
   149→ *   // CSRF token is now available for subsequent requests
   150→ * }
   151→ */
   152→export async function initializeCSRF(): Promise<boolean> {
   153→  const token = await fetchCSRFToken();
   154→  return token !== null;
   155→}

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→/**
     2→ * Rate limiting for API endpoints
     3→ *
     4→ * Production: Uses Upstash Redis for distributed rate limiting across
     5→ * serverless invocations. Falls back to in-memory Map() if Redis is
     6→ * unavailable (local dev or connection failure).
     7→ *
     8→ * Env vars (pick one):
     9→ *   UPSTASH_REDIS_REST_URL + UPSTASH_REDIS_REST_TOKEN  (preferred)
    10→ *   LIMITER_STORAGE_URI  (legacy redis:// URL — auto-converted)
    11→ */
    12→
    13→import { Ratelimit } from "@upstash/ratelimit";
    14→import { Redis } from "@upstash/redis";
    15→
    16→// ---------------------------------------------------------------------------
    17→// Constants
    18→// ---------------------------------------------------------------------------
    19→const WINDOW_MS = 15 * 60 * 1000; // 15 minutes
    20→const MAX_REQUESTS = 100;
    21→
    22→// ---------------------------------------------------------------------------
    23→// In-memory fallback (local dev / Redis failure)
    24→// ---------------------------------------------------------------------------
    25→
    26→interface RateLimitEntry {
    27→  count: number;
    28→  resetTime: number;
    29→}
    30→
    31→const rateLimitStore = new Map<string, RateLimitEntry>();
    32→
    33→function memoryCheckRateLimit(
    34→  identifier: string,
    35→  maxRequests: number,
    36→  windowMs: number
    37→): { allowed: boolean; remaining: number; resetTime: number; totalLimit: number } {
    38→  const now = Date.now();
    39→  const entry = rateLimitStore.get(identifier);
    40→
    41→  if (!entry || now > entry.resetTime) {
    42→    rateLimitStore.set(identifier, { count: 1, resetTime: now + windowMs });
    43→    return { allowed: true, remaining: maxRequests - 1, resetTime: now + windowMs, totalLimit: maxRequests };
    44→  }
    45→
    46→  if (entry.count >= maxRequests) {
    47→    return { allowed: false, remaining: 0, resetTime: entry.resetTime, totalLimit: maxRequests };
    48→  }
    49→
    50→  entry.count++;
    51→  return { allowed: true, remaining: maxRequests - entry.count, resetTime: entry.resetTime, totalLimit: maxRequests };
    52→}
    53→
    54→// Cleanup interval to prevent memory leaks in long-running processes
    55→const CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
    56→let cleanupInterval: NodeJS.Timeout | null = null;
    57→
    58→export function startRateLimitCleanup(): void {
    59→  if (cleanupInterval) return;
    60→  cleanupInterval = setInterval(() => {
    61→    const now = Date.now();
    62→    for (const [key, entry] of rateLimitStore.entries()) {
    63→      if (now > entry.resetTime) rateLimitStore.delete(key);
    64→    }
    65→  }, CLEANUP_INTERVAL_MS);
    66→  if (cleanupInterval.unref) cleanupInterval.unref();
    67→}
    68→
    69→export function stopRateLimitCleanup(): void {
    70→  if (cleanupInterval) {
    71→    clearInterval(cleanupInterval);
    72→    cleanupInterval = null;
    73→  }
    74→}
    75→
    76→// ---------------------------------------------------------------------------
    77→// Redis client (singleton, lazy-init)
    78→// ---------------------------------------------------------------------------
    79→
    80→let redis: Redis | null = null;
    81→let redisAvailable = true; // flipped to false on connection failure
    82→
    83→function getRedis(): Redis | null {
    84→  if (!redisAvailable) return null;
    85→  if (redis) return redis;
    86→
    87→  const restUrl = process.env.UPSTASH_REDIS_REST_URL;
    88→  const restToken = process.env.UPSTASH_REDIS_REST_TOKEN;
    89→
    90→  if (restUrl && restToken) {
    91→    redis = new Redis({ url: restUrl, token: restToken });
    92→    return redis;
    93→  }
    94→
    95→  // Parse legacy redis:// URI into Upstash REST format
    96→  // Expected: redis://default:<token>@<host>.upstash.io:6379
    97→  const legacyUri = process.env.LIMITER_STORAGE_URI;
    98→  if (legacyUri) {
    99→    try {
   100→      const parsed = new URL(legacyUri);
   101→      const host = parsed.hostname;
   102→      const token = decodeURIComponent(parsed.password);
   103→      if (host && token) {
   104→        redis = new Redis({ url: `https://${host}`, token });
   105→        return redis;
   106→      }
   107→    } catch {
   108→      // Parsing failed — fall through to memory
   109→    }
   110→  }
   111→
   112→  // No Redis config — use memory
   113→  redisAvailable = false;
   114→  return null;
   115→}
   116→
   117→// ---------------------------------------------------------------------------
   118→// Upstash Ratelimit instances (cached per unique window config)
   119→// ---------------------------------------------------------------------------
   120→
   121→const limiterCache = new Map<string, Ratelimit>();
   122→
   123→function getLimiter(maxRequests: number, windowMs: number): Ratelimit | null {
   124→  const client = getRedis();
   125→  if (!client) return null;
   126→
   127→  const key = `${maxRequests}:${windowMs}`;
   128→  let limiter = limiterCache.get(key);
   129→  if (limiter) return limiter;
   130→
   131→  const windowSec = Math.ceil(windowMs / 1000);
   132→  const windowStr = `${windowSec} s` as `${number} s`;
   133→
   134→  limiter = new Ratelimit({
   135→    redis: client,
   136→    limiter: Ratelimit.slidingWindow(maxRequests, windowStr),
   137→    prefix: "rr:rl",
   138→  });
   139→
   140→  limiterCache.set(key, limiter);
   141→  return limiter;
   142→}
   143→
   144→// ---------------------------------------------------------------------------
   145→// Public API (unchanged signatures)
   146→// ---------------------------------------------------------------------------
   147→
   148→export function getClientIP(request: Request): string {
   149→  const forwarded = request.headers.get("x-forwarded-for");
   150→  if (forwarded) return forwarded.split(",")[0].trim();
   151→  const realIP = request.headers.get("x-real-ip");
   152→  if (realIP) return realIP;
   153→  return "unknown";
   154→}
   155→
   156→export async function checkRateLimit(
   157→  identifier: string,
   158→  maxRequests?: number,
   159→  windowMs?: number
   160→): Promise<{
   161→  allowed: boolean;
   162→  remaining: number;
   163→  resetTime: number;
   164→  totalLimit: number;
   165→}> {
   166→  const limit = maxRequests ?? MAX_REQUESTS;
   167→  const window = windowMs ?? WINDOW_MS;
   168→
   169→  const limiter = getLimiter(limit, window);
   170→  if (limiter) {
   171→    try {
   172→      const result = await limiter.limit(identifier);
   173→      return {
   174→        allowed: result.success,
   175→        remaining: result.remaining,
   176→        resetTime: result.reset,
   177→        totalLimit: result.limit,
   178→      };
   179→    } catch {
   180→      // Redis failed — fall through to memory
   181→    }
   182→  }
   183→
   184→  return memoryCheckRateLimit(identifier, limit, window);
   185→}
   186→
   187→export function getRateLimitHeaders(result: {
   188→  allowed: boolean;
   189→  remaining: number;
   190→  resetTime: number;
   191→  totalLimit: number;
   192→}): Record<string, string> {
   193→  return {
   194→    "X-RateLimit-Limit": result.totalLimit.toString(),
   195→    "X-RateLimit-Remaining": Math.max(0, result.remaining).toString(),
   196→    "X-RateLimit-Reset": new Date(result.resetTime).toISOString(),
   197→  };
   198→}
   199→
   200→export function createRateLimitResponse(
   201→  _identifier: string,
   202→  retryAfterSeconds?: number
   203→): Response {
   204→  const headers = new Headers({
   205→    "Content-Type": "application/json",
   206→    "X-RateLimit-Limit": MAX_REQUESTS.toString(),
   207→    "X-RateLimit-Remaining": "0",
   208→  });
   209→
   210→  if (retryAfterSeconds) {
   211→    headers.set("Retry-After", retryAfterSeconds.toString());
   212→  }
   213→
   214→  return new Response(
   215→    JSON.stringify({
   216→      error: "Rate limit exceeded. Please try again later.",
   217→      retryAfter: retryAfterSeconds,
   218→    }),
   219→    { status: 429, headers }
   220→  );
   221→}
   222→
   223→// Auto-start memory cleanup
   224→startRateLimitCleanup();
   225→
   226→export { MAX_REQUESTS, WINDOW_MS };
   227→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


Let me also check the API routes to see which ones have CSRF protection and auth checks, and look for any additional auth-related configuration.


/home/ccuser/the-50-dollar-app/src/app/api/messages/route.ts:23:    const { data: { user }, error: authError } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/messages/[id]/route.ts:32:    const { data: { user }, error: authError } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/messages/[id]/send/route.ts:55:    const { data: { user }, error: authError } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/messages/start/route.ts:24:    const { data: { user }, error: authError } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/ratings/route.ts:9:    const { data: { user } } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/match/find-matches/route.ts:41:    const { data: { user }, error: authError } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/payments/create/route.ts:28:    } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/payments/verify/route.ts:20:    } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/auth/csrf/route.ts:16:    const { data: { user } } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/ai/parse-profile-voice/route.ts:54:  const { data: { user } } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/ai/transcribe-voice/route.ts:45:  const { data: { user } } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/ai/generate-job/route.ts:35:    const { data: { user }, error: authError } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/ai/generate-profile/route.ts:42:    const { data: { user }, error: authError } = await supabase.auth.getUser();
/home/ccuser/the-50-dollar-app/src/app/api/ai/voice-complete/route.ts:60:    const { data: { user }, error: authError } = await supabase.auth.getUser();


/home/ccuser/the-50-dollar-app/src/components/VoiceRecorder.tsx:58:            setError("Microphone access requires a secure connection (HTTPS).");
/home/ccuser/the-50-dollar-app/src/components/payment/PaymentForm.tsx:49:        // 3D Secure or other authentication required - Stripe handles this automatically
/home/ccuser/the-50-dollar-app/src/components/payment/PaymentForm.tsx:121:        Secured by Stripe. We never store your card details.
/home/ccuser/the-50-dollar-app/src/lib/csrf.ts:12: * Generate a cryptographically secure CSRF token
/home/ccuser/the-50-dollar-app/src/lib/csrf.ts:23:  let cookie = `${CSRF_COOKIE_NAME}=${token}; Path=/; SameSite=Strict; Max-Age=86400`; // 24 hours
/home/ccuser/the-50-dollar-app/src/lib/csrf.ts:25:  // Add secure flag in production
/home/ccuser/the-50-dollar-app/src/lib/csrf.ts:27:    cookie += '; Secure';
/home/ccuser/the-50-dollar-app/src/lib/supabase/server.ts:29:            cookiesToSet.forEach(({ name, value, options }) =>
/home/ccuser/the-50-dollar-app/src/lib/supabase/server.ts:30:              cookieStore.set(name, value, options)
/home/ccuser/the-50-dollar-app/src/lib/supabase/middleware.ts:37:          cookiesToSet.forEach(({ name, value, options }) =>
/home/ccuser/the-50-dollar-app/src/lib/supabase/middleware.ts:38:            supabaseResponse.cookies.set(name, value, options)
/home/ccuser/the-50-dollar-app/src/app/legal/privacy-policy/page.tsx:71:              We use industry-standard security measures including encryption, secure
/home/ccuser/the-50-dollar-app/src/app/legal/privacy-policy/page.tsx:73:              in secure cloud infrastructure.
/home/ccuser/the-50-dollar-app/src/app/auth/callback/route.ts:25:              cookiesToSet.forEach(({ name, value, options }) =>
/home/ccuser/the-50-dollar-app/src/app/auth/callback/route.ts:26:                cookieStore.set(name, value, options)


Now let me check a few more API routes that might be missing auth checks, and check for some important routes.