The family_invites migration hasn't run yet on production. Work around by: - Removing display_name from INSERT and SELECT (optional field anyway) - Removing accepted_at IS NULL filter from GET and accept queries - DELETE the invite row on accept instead of marking accepted_at — keeps invites single-use without needing the extra column Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
62 lines
No EOL
1.9 KiB
TypeScript
62 lines
No EOL
1.9 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { sql } from "@/db";
|
|
import { requireFamily } from "@/lib/auth";
|
|
import { cookies } from "next/headers";
|
|
|
|
// POST /api/invites/accept - accept an invite with token
|
|
export async function POST(request: Request) {
|
|
// Require authentication (but not family - that's being created)
|
|
const sessionToken = (await cookies()).get("tia_session")?.value;
|
|
|
|
if (!sessionToken) {
|
|
return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
|
|
}
|
|
|
|
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()`,
|
|
[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]
|
|
);
|
|
|
|
// Delete invite so it can't be reused
|
|
await sql.unsafe(
|
|
`DELETE FROM family_invites WHERE id = $1`,
|
|
[invite.id]
|
|
);
|
|
|
|
return NextResponse.json({ success: true });
|
|
} catch (error) {
|
|
console.error(error);
|
|
return NextResponse.json({ error: String(error) }, { status: 500 });
|
|
}
|
|
} |