import { NextRequest, NextResponse } from "next/server"; import { auth, getAuthSession } from "@/lib/auth"; import { db } from "@/lib/db"; import { users, invitations } from "@/lib/db/schema"; import { checkRateLimit, getClientIp } from "@/lib/security/rateLimit"; import { sendInvitationEmail } from "@/lib/email"; import { z } from "zod"; import { nanoid } from "nanoid"; import { eq, ne } from "drizzle-orm"; const InviteSchema = z.object({ email: z.string().email().max(254), role: z.enum(["admin", "moderator"]), playerUuid: z.string().optional(), }); export async function GET(req: NextRequest) { const session = await getAuthSession(req.headers); if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); if (session.user.role !== "superadmin") { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const teamUsers = await db .select() .from(users) .where(ne(users.id, session.user.id)); const pendingInvites = await db .select() .from(invitations) .where(eq(invitations.acceptedAt, null as unknown as number)); return NextResponse.json({ users: teamUsers, pendingInvites }); } export async function POST(req: NextRequest) { const session = await getAuthSession(req.headers); if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); if (session.user.role !== "superadmin") { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const ip = getClientIp(req); const { allowed } = checkRateLimit(ip, 10); if (!allowed) return NextResponse.json({ error: "Too many requests" }, { status: 429 }); let body: z.infer; try { body = InviteSchema.parse(await req.json()); } catch { return NextResponse.json({ error: "Invalid request" }, { status: 400 }); } // Check if user already exists const existing = await db.select().from(users).where(eq(users.email, body.email)).get(); if (existing) { return NextResponse.json({ error: "User already exists" }, { status: 409 }); } // Create invitation const token = nanoid(48); const expiresAt = Date.now() + 48 * 60 * 60 * 1000; // 48 hours await db.insert(invitations).values({ id: nanoid(), email: body.email, role: body.role, invitedBy: session.user.id, token, expiresAt, createdAt: Date.now(), }); const baseUrl = process.env.BETTER_AUTH_URL ?? "http://localhost:3000"; const inviteUrl = `${baseUrl}/accept-invite?token=${token}`; try { await sendInvitationEmail({ to: body.email, invitedByName: session.user.name ?? "An admin", inviteUrl, role: body.role, }); } catch (err) { // Log but don't fail — admin can resend console.error("[Team] Failed to send invitation email:", err); } return NextResponse.json({ success: true, inviteUrl }, { status: 201 }); }