diff --git a/src/app/admin/login/page.tsx b/src/app/admin/login/page.tsx new file mode 100644 index 0000000..57dc437 --- /dev/null +++ b/src/app/admin/login/page.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { Suspense, useState } from "react"; +import { signIn } from "next-auth/react"; +import { useRouter, useSearchParams } from "next/navigation"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; + +function AdminLoginForm() { + const router = useRouter(); + const searchParams = useSearchParams(); + const callbackUrl = searchParams.get("callbackUrl") ?? "/admin"; + + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + setLoading(true); + setError(null); + + const result = await signIn("credentials", { + email, + password, + redirect: false, // handle redirect manually to show errors + }); + + if (result?.error) { + setError("Email o password non corretti."); + setLoading(false); + return; + } + + router.replace(callbackUrl); + } + + return ( +
+
+ + setEmail(e.target.value)} + required + autoComplete="email" + /> +
+
+ + setPassword(e.target.value)} + required + autoComplete="current-password" + /> +
+ {error && ( +

{error}

+ )} + +
+ ); +} + +export default function AdminLoginPage() { + return ( +
+ + + Admin — ClientHub + + + Caricamento...
}> + + + + + + ); +} diff --git a/src/proxy.ts b/src/proxy.ts index 13bb305..fefca07 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -1,35 +1,64 @@ -import { NextRequest, NextResponse } from 'next/server'; +import { NextRequest, NextResponse } from "next/server"; +import { getToken } from "next-auth/jwt"; export async function proxy(request: NextRequest) { const pathname = request.nextUrl.pathname; - // Extract token from path: /c/[token]/... - const tokenMatch = pathname.match(/^\/c\/([a-zA-Z0-9_-]+)/); - if (!tokenMatch) { - return NextResponse.rewrite(new URL('/not-found', request.url)); - } + // ── ADMIN GUARD ────────────────────────────────────────────────────────── + if (pathname.startsWith("/admin")) { + // Allow the login page and NextAuth API routes through without session check + if ( + pathname === "/admin/login" || + pathname.startsWith("/api/auth") + ) { + return NextResponse.next(); + } - const token = tokenMatch[1]; + const token = await getToken({ + req: request, + secret: process.env.NEXTAUTH_SECRET, + }); - try { - // Call internal Node.js API route — Edge middleware cannot use postgres-js directly - // postgres-js requires Node.js net/tls which are unavailable in the Edge runtime - const validateUrl = new URL( - `/api/internal/validate-token?token=${encodeURIComponent(token)}`, - request.url - ); - const res = await fetch(validateUrl.toString()); - - if (!res.ok) { - return NextResponse.rewrite(new URL('/not-found', request.url)); + if (!token) { + const loginUrl = new URL("/admin/login", request.url); + loginUrl.searchParams.set("callbackUrl", pathname); + return NextResponse.redirect(loginUrl); } return NextResponse.next(); - } catch { - return NextResponse.rewrite(new URL('/not-found', request.url)); } + + // ── CLIENT TOKEN GUARD ─────────────────────────────────────────────────── + if (pathname.startsWith("/c/")) { + const tokenMatch = pathname.match(/^\/c\/([a-zA-Z0-9_-]+)/); + if (!tokenMatch) { + return NextResponse.rewrite(new URL("/not-found", request.url)); + } + + const clientToken = tokenMatch[1]; + + try { + // Call internal Node.js API route — Edge middleware cannot use postgres-js directly + // postgres-js requires Node.js net/tls which are unavailable in the Edge runtime + const validateUrl = new URL( + `/api/internal/validate-token?token=${encodeURIComponent(clientToken)}`, + request.url + ); + const res = await fetch(validateUrl.toString()); + + if (!res.ok) { + return NextResponse.rewrite(new URL("/not-found", request.url)); + } + + return NextResponse.next(); + } catch { + return NextResponse.rewrite(new URL("/not-found", request.url)); + } + } + + return NextResponse.next(); } export const config = { - matcher: ['/c/:path*'], -}; \ No newline at end of file + matcher: ["/admin/:path*", "/c/:path*"], +};