- Separated conditional SQL queries to avoid template literal issues - Added proper error handling in CollaboratorFacepile component - Added error handling for favorites functionality - Improved fallback states for failed API calls This resolves the 'syntax error at or near AND' and '' parameter errors.
137 lines
No EOL
4.3 KiB
JavaScript
137 lines
No EOL
4.3 KiB
JavaScript
import { sql } from '@vercel/postgres';
|
|
import jwt from 'jsonwebtoken';
|
|
|
|
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
|
|
|
export default async function handler(req, res) {
|
|
// Set CORS headers
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
|
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
|
|
if (req.method === 'OPTIONS') {
|
|
res.status(200).end();
|
|
return;
|
|
}
|
|
|
|
// Verify authentication
|
|
const authHeader = req.headers.authorization;
|
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
const token = authHeader.substring(7);
|
|
let user;
|
|
try {
|
|
user = jwt.verify(token, JWT_SECRET);
|
|
} catch (error) {
|
|
return res.status(401).json({ error: 'Invalid token' });
|
|
}
|
|
|
|
try {
|
|
if (req.method === 'GET') {
|
|
// Get user's favorites
|
|
const { type } = req.query; // Optional filter by type
|
|
|
|
let result;
|
|
if (type) {
|
|
result = await sql`
|
|
SELECT uf.*,
|
|
CASE
|
|
WHEN uf.item_type = 'collection' THEN c.name
|
|
WHEN uf.item_type = 'card' THEN cards.name
|
|
WHEN uf.item_type = 'deck' THEN d.name
|
|
END as item_name
|
|
FROM user_favorites uf
|
|
LEFT JOIN collections c ON uf.item_type = 'collection' AND uf.item_id = c.id
|
|
LEFT JOIN cards ON uf.item_type = 'card' AND uf.item_id = cards.id
|
|
LEFT JOIN decks d ON uf.item_type = 'deck' AND uf.item_id = d.id
|
|
WHERE uf.user_id = ${user.userId} AND uf.item_type = ${type}
|
|
ORDER BY uf.created_at DESC
|
|
`;
|
|
} else {
|
|
result = await sql`
|
|
SELECT uf.*,
|
|
CASE
|
|
WHEN uf.item_type = 'collection' THEN c.name
|
|
WHEN uf.item_type = 'card' THEN cards.name
|
|
WHEN uf.item_type = 'deck' THEN d.name
|
|
END as item_name
|
|
FROM user_favorites uf
|
|
LEFT JOIN collections c ON uf.item_type = 'collection' AND uf.item_id = c.id
|
|
LEFT JOIN cards ON uf.item_type = 'card' AND uf.item_id = cards.id
|
|
LEFT JOIN decks d ON uf.item_type = 'deck' AND uf.item_id = d.id
|
|
WHERE uf.user_id = ${user.userId}
|
|
ORDER BY uf.created_at DESC
|
|
`;
|
|
}
|
|
|
|
res.status(200).json({
|
|
favorites: result.rows
|
|
});
|
|
|
|
} else if (req.method === 'POST') {
|
|
// Add favorite
|
|
const { itemType, itemId } = req.body;
|
|
|
|
if (!itemType || !itemId) {
|
|
return res.status(400).json({ error: 'itemType and itemId are required' });
|
|
}
|
|
|
|
if (!['card', 'collection', 'deck'].includes(itemType)) {
|
|
return res.status(400).json({ error: 'Invalid itemType. Must be card, collection, or deck' });
|
|
}
|
|
|
|
// Check if already favorited
|
|
const existing = await sql`
|
|
SELECT id FROM user_favorites
|
|
WHERE user_id = ${user.userId} AND item_type = ${itemType} AND item_id = ${itemId}
|
|
`;
|
|
|
|
if (existing.rows.length > 0) {
|
|
return res.status(409).json({ error: 'Item already favorited' });
|
|
}
|
|
|
|
// Add favorite
|
|
const result = await sql`
|
|
INSERT INTO user_favorites (user_id, item_type, item_id)
|
|
VALUES (${user.userId}, ${itemType}, ${itemId})
|
|
RETURNING *
|
|
`;
|
|
|
|
res.status(201).json({
|
|
favorite: result.rows[0]
|
|
});
|
|
|
|
} else if (req.method === 'DELETE') {
|
|
// Remove favorite
|
|
const { itemType, itemId } = req.body;
|
|
|
|
if (!itemType || !itemId) {
|
|
return res.status(400).json({ error: 'itemType and itemId are required' });
|
|
}
|
|
|
|
const result = await sql`
|
|
DELETE FROM user_favorites
|
|
WHERE user_id = ${user.userId} AND item_type = ${itemType} AND item_id = ${itemId}
|
|
RETURNING *
|
|
`;
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Favorite not found' });
|
|
}
|
|
|
|
res.status(200).json({
|
|
message: 'Favorite removed',
|
|
favorite: result.rows[0]
|
|
});
|
|
|
|
} else {
|
|
res.status(405).json({ error: 'Method not allowed' });
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Favorites API error:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
}
|