Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/app/api/collections/[id]/join/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export async function POST(
const session = await getServerSession(authOptions);

if (!session?.user) {
console.log('[collections/:id/join] unauthorized: no session');
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

Expand Down Expand Up @@ -109,6 +110,7 @@ export async function POST(
}
}
if (!collection.isPublic && !inviteOk) {
console.log('[collections/:id/join] private and no valid invite');
return NextResponse.json(
{ error: 'This collection is private and requires an invitation' },
{ status: 403 }
Expand Down Expand Up @@ -143,6 +145,7 @@ export async function POST(
}
} else {
// Create new membership
console.log('[collections/:id/join] creating membership');
await db.collectionMember.create({
data: {
userId,
Expand Down Expand Up @@ -214,7 +217,7 @@ export async function POST(
res.cookies.set('invite_library', '', { path: '/', maxAge: 0 });
return res;
} catch (error) {
console.error('Error joining collection:', error);
console.error('[collections/:id/join] error', error);
return NextResponse.json(
{ error: 'Failed to join collection' },
{ status: 500 }
Expand Down
33 changes: 32 additions & 1 deletion src/app/api/invite/consume/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,27 @@ import { db } from '@/lib/db';
export async function POST(request: NextRequest) {
try {
const session = await getServerSession(authOptions);
console.log('[invite/consume] start', {
url: request.url,
hasSessionUser: !!session?.user,
hasCookieInviteToken: !!request.cookies.get('invite_token')?.value,
inviteLibrary: request.cookies.get('invite_library')?.value,
});
if (!session?.user) {
console.log('[invite/consume] no session user');
return NextResponse.json({ redirect: null }, { status: 200 });
}

const inviteToken = request.cookies.get('invite_token')?.value;
const inviteLibrary = request.cookies.get('invite_library')?.value;
if (!inviteToken || !inviteLibrary) {
console.log('[invite/consume] missing cookies');
return NextResponse.json({ redirect: null }, { status: 200 });
}

const userId = (session.user as { id?: string } | undefined)?.id;
if (!userId) {
console.log('[invite/consume] missing userId from session');
return NextResponse.json({ redirect: null }, { status: 200 });
}

Expand All @@ -32,13 +41,19 @@ export async function POST(request: NextRequest) {
},
select: { id: true, expiresAt: true },
});
console.log('[invite/consume] invitation lookup', {
found: !!invitation,
expired: invitation ? new Date() > invitation.expiresAt : undefined,
});


// Build base response now so we can always clear cookies
const res = NextResponse.json({ redirect: `/collection/${inviteLibrary}` });
res.cookies.set('invite_token', '', { path: '/', maxAge: 0 });
res.cookies.set('invite_library', '', { path: '/', maxAge: 0 });

if (!invitation || new Date() > invitation.expiresAt) {
console.log('[invite/consume] invalid or expired invite; returning');
return res;
}

Expand All @@ -48,6 +63,10 @@ export async function POST(request: NextRequest) {
select: { id: true, isActive: true },
});
if (!existing) {
console.log('[invite/consume] creating membership', {
userId,
inviteLibrary,
});
await db.collectionMember.create({
data: {
userId,
Expand All @@ -57,20 +76,32 @@ export async function POST(request: NextRequest) {
},
});
} else if (!existing.isActive) {
console.log('[invite/consume] reactivating membership', {
id: existing.id,
});

await db.collectionMember.update({
where: { id: existing.id },
data: { isActive: true },
});
} else {
console.log('[invite/consume] membership already active');
}

// Mark invite accepted
console.log('[invite/consume] marking invite accepted');

await db.invitation.updateMany({
where: { token: inviteToken, libraryId: inviteLibrary },
data: { status: 'ACCEPTED', acceptedAt: new Date(), receiverId: userId },
});

console.log('[invite/consume] returning redirect', {
to: `/collection/${inviteLibrary}`,
});
return res;
} catch {
} catch (e) {
console.error('[invite/consume] error', e);
return NextResponse.json({ redirect: null }, { status: 200 });
}
}
8 changes: 8 additions & 0 deletions src/app/collection/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ export default async function CollectionPage({
const inviteLibrary = cookieStore.get('invite_library')?.value;
const { id: collectionIdForCheck } = await params;
const allowGuest = inviteToken && inviteLibrary === collectionIdForCheck;
console.log('[collection page] auth gate', {
hasSessionUser: !!session?.user,
guestParam: guest,
hasInviteToken: !!inviteToken,
inviteLibrary,
collectionIdForCheck,
allowGuest,
});
if (!allowGuest) {
redirect('/auth/signin');
}
Expand Down