deckhearth/pages/api/collections/[id].js
Randall Stillwell 23d995102f 🎉 COMPLETED: Full Collaborative Collections System
 ALL FEATURES IMPLEMENTED:

🔐 Advanced Permission System:
- Role-based access control (Owner/Editor/Viewer)
- Permission middleware for all API endpoints
- Granular permissions for collection operations
- Activity logging for complete audit trails

🌍 Collection Visibility Types:
- Private: Owner-only access
- Invite-Only: Controlled collaboration
- Public: Community accessible
- Dynamic permission checking across all endpoints

📧 Complete Email Integration:
- Beautiful HTML invitation templates
- Role-based permission descriptions
- Personal message support
- Accept/decline workflow with proper UX
- Bulk invitation system for multiple users

🎨 Rich User Interface:
- Permission indicators with tooltips
- Activity log component with real-time updates
- Collaboration management dashboard
- Bulk invite modal with batch processing
- Permission gates throughout the UI

 Performance & Security:
- Database indexes for optimal queries
- Comprehensive error handling
- CORS headers and preflight support
- JWT-based authentication integration
- Cascading deletes and data integrity

🚀 Ready for Production:
- All API endpoints protected with permissions
- Complete activity logging system
- Beautiful email templates with Resend
- Responsive UI components
- Error handling and loading states

This system now provides enterprise-level collaboration features for community-driven collection building! 🎯
2025-07-25 08:34:28 -05:00

136 lines
No EOL
4 KiB
JavaScript

import { sql } from '@vercel/postgres';
import { withCollectionPermission, getUserFromRequest, logCollectionActivity } from '../../../lib/permission-middleware';
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;
if (req.method === 'GET') {
// GET requests use the permission from middleware
const collection = req.permission.collection;
const userRole = req.permission.role;
try {
// Get detailed collection info with creator
const collectionResult = await sql`
SELECT
c.*,
u.email as creator_email
FROM collections c
LEFT JOIN users u ON c.user_id = u.id
WHERE c.id = ${id}
`;
const collectionDetails = collectionResult.rows[0];
// Get cards in the collection
const cardsResult = await sql`
SELECT
cc.*,
cards.name,
cards.set_name,
cards.rarity,
cards.type,
cards.image_url,
cards.market_price
FROM collection_cards cc
JOIN cards ON cc.card_id = cards.id
WHERE cc.collection_id = ${id}
ORDER BY cc.created_at ASC
`;
const cards = cardsResult.rows;
// Calculate collection stats
const totalCards = cards.reduce((sum, card) => sum + card.quantity, 0);
const totalValue = cards.reduce((sum, card) => sum + (card.market_price * card.quantity), 0);
res.status(200).json({
collection: {
...collectionDetails,
totalCards,
totalValue,
userRole
},
cards
});
} catch (error) {
console.error('Error fetching collection:', error);
res.status(500).json({ error: 'Internal server error' });
}
} else if (req.method === 'PUT') {
// Update collection - requires editor permissions
try {
const { name, description, visibility } = req.body;
if (!['private', 'invite-only', 'public'].includes(visibility)) {
return res.status(400).json({ error: 'Invalid visibility type' });
}
const result = await sql`
UPDATE collections
SET
name = ${name},
description = ${description},
visibility = ${visibility},
updated_at = NOW()
WHERE id = ${id}
RETURNING *
`;
// Log activity
await logCollectionActivity(id, req.user.userId, 'collection_updated', {
name,
description,
visibility
});
res.status(200).json(result.rows[0]);
} catch (error) {
console.error('Error updating collection:', error);
res.status(500).json({ error: 'Internal server error' });
}
} else if (req.method === 'DELETE') {
// Delete collection - requires owner permissions
try {
// Log activity before deletion
await logCollectionActivity(id, req.user.userId, 'collection_deleted', {});
// Delete collection (cascade will handle related records)
const result = await sql`DELETE FROM collections WHERE id = ${id} RETURNING *`;
res.status(200).json({ message: 'Collection deleted successfully' });
} catch (error) {
console.error('Error deleting collection:', error);
res.status(500).json({ error: 'Internal server error' });
}
} else {
res.status(405).json({ error: 'Method not allowed' });
}
}
// Apply permission middleware based on method
export default async function(req, res) {
let requiredPermission = 'viewer'; // Default for GET
if (req.method === 'PUT') {
requiredPermission = 'editor';
} else if (req.method === 'DELETE') {
requiredPermission = 'owner';
}
return withCollectionPermission(requiredPermission)(handler)(req, res);
};