tia/src/app/api/invites/accept/route.ts
Mannu f03484f262 Sprint 2: Invitation System Complete
- /api/invites - GET/POST invites
- /api/invites/accept - POST accept invite
- /invite/[token] - Accept invite page
- Settings page now has invite UI
- Checks member limit for free tier
- Shows upgrade prompt when limit reached

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 21:54:41 +05:30

53 lines
No EOL
1.6 KiB
TypeScript

import { NextResponse } from "next/server";
import { sql } from "@/db";
// POST /api/invites/accept - accept an invite with token
export async function POST(request: Request) {
try {
const body = await request.json();
const { token, userId } = body;
if (!token || !userId) {
return NextResponse.json({ error: "token and userId required" }, { status: 400 });
}
// Find invite
const invites = await sql.unsafe(
`SELECT * FROM family_invites WHERE token = $1 AND expires_at > NOW() AND accepted_at IS NULL`,
[token]
);
if (!invites || invites.length === 0) {
return NextResponse.json({ error: "Invalid or expired invite" }, { status: 404 });
}
const invite = invites[0];
// Check if user already in family
const existingMember = await sql.unsafe(
`SELECT id FROM family_members WHERE family_id = $1 AND user_id = $2`,
[invite.family_id, userId]
);
if (existingMember && existingMember.length > 0) {
return NextResponse.json({ error: "Already a member of this family" }, { status: 400 });
}
// Add member
await sql.unsafe(
`INSERT INTO family_members (family_id, user_id, role, display_name) VALUES ($1, $2, $3, $4)`,
[invite.family_id, userId, invite.role, invite.display_name]
);
// Mark invite as accepted
await sql.unsafe(
`UPDATE family_invites SET accepted_at = NOW() WHERE id = $1`,
[invite.id]
);
return NextResponse.json({ success: true });
} catch (error) {
console.error(error);
return NextResponse.json({ error: String(error) }, { status: 500 });
}
}