import { NextResponse } from "next/server"; import { cookies } from "next/headers"; import { randomUUID } from "crypto"; import { and, eq, gt, isNull } from "drizzle-orm"; import { db } from "@/lib/db"; import { loginTokens, sessions, users, subscriptions } from "@/lib/schema"; import { hashToken } from "@/lib/auth/tokens"; export async function POST(req: Request) { // -------------------------------------------------- // 1️⃣ Parse token from request // -------------------------------------------------- const { token } = await req.json(); if (!token) { return NextResponse.json( { error: "Missing token" }, { status: 400 } ); } // -------------------------------------------------- // 2️⃣ Validate login token (magic link) // -------------------------------------------------- const tokenHash = hashToken(token); const loginToken = await db .select() .from(loginTokens) .where( and( eq(loginTokens.tokenHash, tokenHash), isNull(loginTokens.usedAt), gt(loginTokens.expiresAt, new Date()) ) ) .limit(1) .then((rows) => rows[0]); if (!loginToken) { return NextResponse.json( { error: "Invalid or expired token" }, { status: 401 } ); } // -------------------------------------------------- // 3️⃣ Ensure user still exists // -------------------------------------------------- const user = await db .select() .from(users) .where(eq(users.id, loginToken.userId)) .limit(1) .then((rows) => rows[0]); if (!user) { return NextResponse.json( { error: "User not found" }, { status: 404 } ); } // -------------------------------------------------- // 4️⃣ Consume login token (one-time use) // -------------------------------------------------- await db .update(loginTokens) .set({ usedAt: new Date() }) .where(eq(loginTokens.id, loginToken.id)); // -------------------------------------------------- // 5️⃣ Ensure subscription record exists // -------------------------------------------------- const existingSubscription = await db .select() .from(subscriptions) .where(eq(subscriptions.userId, user.id)) .limit(1) .then((rows) => rows[0]); if (!existingSubscription) { // Check if user was created within the last 2 weeks const twoWeeksAgo = new Date(Date.now() - 1000 * 60 * 60 * 24 * 14); const userCreatedWithinTwoWeeks = new Date(user.createdAt) > twoWeeksAgo; const subscriptionStatus = userCreatedWithinTwoWeeks ? "trialing" : "expired"; await db.insert(subscriptions).values({ id: randomUUID(), userId: user.id, status: subscriptionStatus, stripeCustomerId: null, stripeSubscriptionId: null, currentPeriodStart: null, currentPeriodEnd: null, }); } // -------------------------------------------------- // 6️⃣ Create DB-backed session // -------------------------------------------------- const sessionId = randomUUID(); const expiresAt = new Date( Date.now() + 1000 * 60 * 60 * 24 * 14 // 14 days ); await db.insert(sessions).values({ id: sessionId, userId: user.id, expiresAt, }); // -------------------------------------------------- // 7️⃣ Set secure session cookie // -------------------------------------------------- const cookieStore = await cookies(); cookieStore.set("session", sessionId, { httpOnly: true, sameSite: "lax", secure: process.env.NODE_ENV === "production", path: "/", maxAge: 60 * 60 * 24 * 14, // 14 days }); // -------------------------------------------------- // 8️⃣ Done // -------------------------------------------------- return NextResponse.json({ ok: true }); }