import { sql } from '@vercel/postgres'; import { Resend } from 'resend'; const resend = new Resend(process.env.RESEND_API_KEY); export default async function handler(req, res) { // Set CORS headers res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // Handle preflight requests if (req.method === 'OPTIONS') { res.status(200).end(); return; } const { id } = req.query; // collection id if (req.method === 'GET') { // Get all permissions for a collection try { const result = await sql` SELECT cp.*, u.email, u.id as user_id, u.is_pending FROM collection_permissions cp JOIN users u ON cp.user_id = u.id WHERE cp.collection_id = ${id} ORDER BY cp.role, cp.created_at `; res.status(200).json(result.rows); } catch (error) { console.error('Error fetching permissions:', error); res.status(500).json({ error: 'Internal server error' }); } } else if (req.method === 'POST') { // Invite user to collection try { const { email, role = 'viewer', message = '' } = req.body; if (!email || !['owner', 'editor', 'viewer'].includes(role)) { return res.status(400).json({ error: 'Valid email and role are required' }); } // Check if user exists const userResult = await sql` SELECT id, email FROM users WHERE email = ${email} `; let userId; if (userResult.rows.length === 0) { // Create pending user record const newUserResult = await sql` INSERT INTO users (email, password, role, is_pending) VALUES (${email}, '', 'user', true) RETURNING id `; userId = newUserResult.rows[0].id; } else { userId = userResult.rows[0].id; } // Check if permission already exists const existingPermission = await sql` SELECT * FROM collection_permissions WHERE collection_id = ${id} AND user_id = ${userId} `; if (existingPermission.rows.length > 0) { return res.status(409).json({ error: 'User already has access to this collection' }); } // Get collection details for email const collectionResult = await sql` SELECT c.name, u.email as owner_email FROM collections c JOIN users u ON c.user_id = u.id WHERE c.id = ${id} `; if (collectionResult.rows.length === 0) { return res.status(404).json({ error: 'Collection not found' }); } const collection = collectionResult.rows[0]; // Create permission record const permissionResult = await sql` INSERT INTO collection_permissions (collection_id, user_id, role, status, invited_by) VALUES (${id}, ${userId}, ${role}, 'pending', 1) RETURNING * `; // Generate invitation token const inviteToken = Buffer.from(`${id}:${userId}:${Date.now()}`).toString('base64'); await sql` UPDATE collection_permissions SET invite_token = ${inviteToken} WHERE id = ${permissionResult.rows[0].id} `; // Send invitation email const acceptUrl = `${process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'}/invite/accept?token=${inviteToken}`; const declineUrl = `${process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'}/invite/decline?token=${inviteToken}`; try { await resend.emails.send({ from: 'TCG Vault ', to: email, subject: `You've been invited to collaborate on "${collection.name}"`, html: `

🃏 TCG Vault

Collection Collaboration Invite

You've been invited to collaborate!

${collection.owner_email} has invited you to collaborate on the collection "${collection.name}" with ${role} permissions.

${message ? `

Personal message:

"${message}"

` : ''}

What you can do as a ${role}:

    ${role === 'owner' ? `
  • Full control over the collection
  • Add and remove cards
  • Edit collection details
  • Manage permissions and invite others
  • Delete the collection
  • ` : role === 'editor' ? `
  • Add and remove cards
  • Edit collection details
  • View all collection content
  • ` : `
  • View all collection content
  • Browse and search cards
  • Export collection data
  • `}
Accept Invitation Decline

This invitation will expire in 7 days. If you have any questions, please contact ${collection.owner_email}.

If you didn't expect this invitation, you can safely ignore this email.

` }); } catch (emailError) { console.error('Email sending failed:', emailError); // Continue anyway - the invitation is still created } // Log activity await sql` INSERT INTO collection_activity (collection_id, user_id, action, details) VALUES (${id}, 1, 'user_invited', ${JSON.stringify({ email, role, inviteToken })}) `; res.status(201).json({ message: 'Invitation sent successfully', permission: { ...permissionResult.rows[0], email, invite_token: inviteToken } }); } catch (error) { console.error('Error inviting user:', error); res.status(500).json({ error: 'Internal server error' }); } } else if (req.method === 'PUT') { // Update user permission try { const { userId, role, status } = req.body; if (!userId || !['owner', 'editor', 'viewer'].includes(role)) { return res.status(400).json({ error: 'Valid user ID and role are required' }); } const result = await sql` UPDATE collection_permissions SET role = ${role}, status = ${status || 'active'}, updated_at = NOW() WHERE collection_id = ${id} AND user_id = ${userId} RETURNING * `; if (result.rows.length === 0) { return res.status(404).json({ error: 'Permission not found' }); } // Log activity await sql` INSERT INTO collection_activity (collection_id, user_id, action, details) VALUES (${id}, 1, 'permission_updated', ${JSON.stringify({ userId, role, status })}) `; res.status(200).json(result.rows[0]); } catch (error) { console.error('Error updating permission:', error); res.status(500).json({ error: 'Internal server error' }); } } else if (req.method === 'DELETE') { // Remove user permission try { const { userId } = req.body; if (!userId) { return res.status(400).json({ error: 'User ID is required' }); } const result = await sql` DELETE FROM collection_permissions WHERE collection_id = ${id} AND user_id = ${userId} RETURNING * `; if (result.rows.length === 0) { return res.status(404).json({ error: 'Permission not found' }); } // Log activity await sql` INSERT INTO collection_activity (collection_id, user_id, action, details) VALUES (${id}, 1, 'user_removed', ${JSON.stringify({ userId })}) `; res.status(200).json({ message: 'Permission removed successfully' }); } catch (error) { console.error('Error removing permission:', error); res.status(500).json({ error: 'Internal server error' }); } } else { res.status(405).json({ error: 'Method not allowed' }); } }