Improve MineGeo exploration and mapping features

This commit is contained in:
2026-10-05 13:41:36 +05:00
parent aec0641f87
commit c5368fa8cc
69 changed files with 3581 additions and 333 deletions

View File

@@ -1,13 +1,53 @@
import { NextRequest, NextResponse } from "next/server";
import { authClient } from "@/lib/supabase";
import { requestOrigin } from "@/lib/request-origin";
import { authDestination } from "@/shared/account";
import { createRecoveryGrant, recoveryCookie } from "@/lib/password-recovery";
import { accountProfile } from "@/lib/profile";
export async function GET(request: NextRequest) {
const origin = requestOrigin(request, process.env.MINEGEO_URL);
const code = request.nextUrl.searchParams.get("code");
if (code) {
const tokenHash = request.nextUrl.searchParams.get("token_hash");
const type = request.nextUrl.searchParams.get("type");
const next = authDestination(request.nextUrl.searchParams.get("next"));
if (
code ||
(tokenHash &&
(type === "email" || type === "recovery" || type === "magiclink"))
) {
const client = await authClient();
const { error } = await client.auth.exchangeCodeForSession(code);
if (!error) return NextResponse.redirect(new URL("/dashboard", origin));
const { data, error } = code
? await client.auth.exchangeCodeForSession(code)
: await client.auth.verifyOtp({
token_hash: tokenHash!,
type: type as "email" | "recovery" | "magiclink",
});
if (!error && data.user) {
await accountProfile(data.user);
const destination = type === "recovery" ? "/auth/reset-password" : next;
const response = NextResponse.redirect(new URL(destination, origin));
if (destination === "/auth/reset-password") {
const { data: claims } = await client.auth.getClaims();
const sessionId = claims?.claims.session_id;
if (typeof sessionId === "string")
response.cookies.set(
recoveryCookie,
createRecoveryGrant(
data.user.id,
sessionId,
process.env.SUPABASE_SECRET_KEY!,
),
{
httpOnly: true,
sameSite: "lax",
secure: new URL(origin).protocol === "https:",
path: "/",
maxAge: 600,
},
);
}
return response;
}
}
return NextResponse.redirect(new URL("/dashboard?auth=failed", origin));
return NextResponse.redirect(new URL("/log-in?auth=failed", origin));
}

View File

@@ -0,0 +1 @@
export { GET } from "../callback/route";

View File

@@ -0,0 +1,56 @@
import { cookies } from "next/headers";
import Link from "next/link";
import { Header, Footer } from "@/components/Shell";
import { PasswordForm } from "@/components/PasswordForm";
import { authClient, user } from "@/lib/supabase";
import { recoveryCookie, verifyRecoveryGrant } from "@/lib/password-recovery";
export const metadata = { title: "Reset password" };
export default async function ResetPassword() {
const [account, jar, client] = await Promise.all([
user(),
cookies(),
authClient(),
]);
const { data } = account ? await client.auth.getClaims() : { data: null };
const sessionId = data?.claims.session_id;
const valid =
account &&
typeof sessionId === "string" &&
verifyRecoveryGrant(
jar.get(recoveryCookie)?.value,
account.id,
sessionId,
process.env.SUPABASE_SECRET_KEY!,
);
return (
<>
<Header />
<main className="section page-content">
<section className="auth-card reset-card">
<span className="eyebrow">ACCOUNT RECOVERY</span>
<h1>A fresh start.</h1>
{valid ? (
<>
<p className="muted">Choose a new password for your account.</p>
<PasswordForm recovery />
</>
) : (
<>
<p>
This reset link has expired or has already been used. Request a
fresh link to continue.
</p>
<Link
className="button primary"
href={account ? "/settings" : "/log-in"}
>
Request a new reset link →
</Link>
</>
)}
</section>
</main>
<Footer />
</>
);
}