Add CORS proxy for Lorcana APIs and improve error handling - fix CORS issues and 500 errors
This commit is contained in:
parent
d27a04e460
commit
5aa168bd24
3 changed files with 214 additions and 24 deletions
67
api/proxy/lorcana.js
Normal file
67
api/proxy/lorcana.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { NextResponse } from 'next/server';
|
||||
|
||||
// Proxy for Lorcana APIs to handle CORS
|
||||
export async function GET(request) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const query = searchParams.get('q');
|
||||
const search = searchParams.get('search');
|
||||
const limit = searchParams.get('limit') || '20';
|
||||
const api = searchParams.get('api') || 'lorcana'; // 'lorcana' or 'lorcast'
|
||||
|
||||
let url;
|
||||
let headers = {};
|
||||
|
||||
if (api === 'lorcana') {
|
||||
// Lorcana API
|
||||
if (search) {
|
||||
url = `https://lorcana-api.com/api/v1/cards?search=${encodeURIComponent(search)}`;
|
||||
} else {
|
||||
url = `https://lorcana-api.com/api/v1/cards?limit=${limit}`;
|
||||
}
|
||||
headers = {
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'TCG-Vault/1.0'
|
||||
};
|
||||
} else {
|
||||
// Lorcast API
|
||||
if (query) {
|
||||
url = `https://api.lorcast.com/v0/cards?q=${encodeURIComponent(query)}`;
|
||||
} else {
|
||||
url = `https://api.lorcast.com/v0/cards`;
|
||||
}
|
||||
headers = {
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'TCG-Vault/1.0'
|
||||
};
|
||||
}
|
||||
|
||||
console.log(`🔗 Proxying request to: ${url}`);
|
||||
|
||||
const response = await fetch(url, { headers });
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`❌ Proxy error: ${response.status} ${response.statusText}`);
|
||||
return NextResponse.json(
|
||||
{ error: `External API error: ${response.status}` },
|
||||
{ status: response.status }
|
||||
);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log(`✅ Proxy success: ${url}`);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: data,
|
||||
source: api
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Proxy error:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch from external API' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -158,6 +158,8 @@ export async function GET(request) {
|
|||
}
|
||||
}));
|
||||
|
||||
console.log(`✅ Found ${userCards.length} user cards for user ${user.id}`);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: userCards,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const POKEMON_API_BASE_URL = 'https://api.pokemontcg.io/v2';
|
|||
const YUGIOH_API_BASE_URL = 'https://db.ygoprodeck.com/api/v7';
|
||||
const LORCAST_API_BASE_URL = 'https://api.lorcast.com/v0';
|
||||
const LORCANA_API_BASE_URL = 'https://lorcana-api.com/api/v1';
|
||||
const PROXY_API_BASE_URL = 'https://tcg-vault.vercel.app/api/proxy/lorcana';
|
||||
|
||||
|
||||
// Rate limiting utilities
|
||||
|
|
@ -287,7 +288,48 @@ export const lorcanaService = {
|
|||
await lorcastLimiter.waitForSlot();
|
||||
|
||||
try {
|
||||
// Try Lorcana API first (more reliable)
|
||||
// Try proxy API first (handles CORS)
|
||||
try {
|
||||
const response = await fetch(`${PROXY_API_BASE_URL}?search=${encodeURIComponent(query)}&api=lorcana`);
|
||||
|
||||
if (response.ok) {
|
||||
const proxyData = await response.json();
|
||||
if (proxyData.success && proxyData.data) {
|
||||
const cards = proxyData.data.cards || proxyData.data.data || proxyData.data || [];
|
||||
|
||||
if (cards.length > 0) {
|
||||
console.log(`✅ Found ${cards.length} Lorcana cards via proxy (${proxyData.source})`);
|
||||
return cards.map((card: any) => ({
|
||||
id: card.id || card.uuid || `lorcana-${Math.random()}`,
|
||||
name: card.name,
|
||||
set_name: card.set?.name || card.set_name || '',
|
||||
set_code: card.set?.code || card.set_code || '',
|
||||
card_number: card.number || card.card_number || '',
|
||||
rarity: card.rarity,
|
||||
game: 'LORCANA',
|
||||
mana_cost: card.cost?.toString() || card.mana_cost,
|
||||
cmc: card.cost || card.cmc,
|
||||
card_type: card.type || card.card_type,
|
||||
colors: card.colors || [],
|
||||
oracle_text: card.text || card.oracle_text || '',
|
||||
power: card.strength?.toString() || card.power,
|
||||
toughness: card.willpower?.toString() || card.toughness,
|
||||
image_url: card.image_url || card.images?.small || card.images?.png,
|
||||
stock_image_url: card.image_url || card.images?.small || card.images?.png,
|
||||
current_price: card.price?.market || card.current_price || undefined,
|
||||
market_price: card.price?.low || card.market_price || undefined,
|
||||
verified: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('❌ Proxy API failed, trying direct APIs');
|
||||
}
|
||||
|
||||
// Fallback to direct APIs
|
||||
try {
|
||||
const response = await fetch(`${LORCANA_API_BASE_URL}/cards?search=${encodeURIComponent(query)}`);
|
||||
|
||||
|
|
@ -296,7 +338,7 @@ export const lorcanaService = {
|
|||
const cards = data.cards || data.data || data || [];
|
||||
|
||||
if (cards.length > 0) {
|
||||
console.log(`✅ Found ${cards.length} Lorcana cards from Lorcana API`);
|
||||
console.log(`✅ Found ${cards.length} Lorcana cards from direct Lorcana API`);
|
||||
return cards.map((card: any) => ({
|
||||
id: card.id || card.uuid || `lorcana-${Math.random()}`,
|
||||
name: card.name,
|
||||
|
|
@ -323,18 +365,14 @@ export const lorcanaService = {
|
|||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('❌ Lorcana API failed, trying Lorcast API');
|
||||
console.log('❌ Direct Lorcana API failed');
|
||||
}
|
||||
|
||||
// Fallback to Lorcast API with multiple endpoints
|
||||
// Final fallback to Lorcast API
|
||||
const searchEndpoints = [
|
||||
`/cards?q=${encodeURIComponent(query)}`,
|
||||
`/cards?search=${encodeURIComponent(query)}`,
|
||||
`/cards?query=${encodeURIComponent(query)}`,
|
||||
`/api/cards?q=${encodeURIComponent(query)}`,
|
||||
`/api/cards?search=${encodeURIComponent(query)}`,
|
||||
`/v0/cards?q=${encodeURIComponent(query)}`,
|
||||
`/v0/cards?search=${encodeURIComponent(query)}`
|
||||
`/cards?query=${encodeURIComponent(query)}`
|
||||
];
|
||||
|
||||
for (const endpoint of searchEndpoints) {
|
||||
|
|
@ -390,7 +428,49 @@ export const lorcanaService = {
|
|||
await lorcastLimiter.waitForSlot();
|
||||
|
||||
try {
|
||||
// Try Lorcana API first
|
||||
// Try proxy API first (handles CORS)
|
||||
try {
|
||||
const response = await fetch(`${PROXY_API_BASE_URL}?search=${encodeURIComponent(name)}&api=lorcana`);
|
||||
|
||||
if (response.ok) {
|
||||
const proxyData = await response.json();
|
||||
if (proxyData.success && proxyData.data) {
|
||||
const cards = proxyData.data.cards || proxyData.data.data || proxyData.data || [];
|
||||
|
||||
if (cards.length > 0) {
|
||||
const card = cards[0];
|
||||
console.log(`✅ Found Lorcana card "${name}" via proxy (${proxyData.source})`);
|
||||
return {
|
||||
id: card.id || card.uuid || `lorcana-${Math.random()}`,
|
||||
name: card.name,
|
||||
set_name: card.set?.name || card.set_name || '',
|
||||
set_code: card.set?.code || card.set_code || '',
|
||||
card_number: card.number || card.card_number || '',
|
||||
rarity: card.rarity,
|
||||
game: 'LORCANA',
|
||||
mana_cost: card.cost?.toString() || card.mana_cost,
|
||||
cmc: card.cost || card.cmc,
|
||||
card_type: card.type || card.card_type,
|
||||
colors: card.colors || [],
|
||||
oracle_text: card.text || card.oracle_text || '',
|
||||
power: card.strength?.toString() || card.power,
|
||||
toughness: card.willpower?.toString() || card.toughness,
|
||||
image_url: card.image_url || card.images?.small || card.images?.png,
|
||||
stock_image_url: card.image_url || card.images?.small || card.images?.png,
|
||||
current_price: card.price?.market || card.current_price || undefined,
|
||||
market_price: card.price?.low || card.market_price || undefined,
|
||||
verified: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('❌ Proxy API failed for card lookup, trying direct APIs');
|
||||
}
|
||||
|
||||
// Fallback to direct Lorcana API
|
||||
try {
|
||||
const response = await fetch(`${LORCANA_API_BASE_URL}/cards?search=${encodeURIComponent(name)}`);
|
||||
|
||||
|
|
@ -400,7 +480,7 @@ export const lorcanaService = {
|
|||
|
||||
if (cards.length > 0) {
|
||||
const card = cards[0];
|
||||
console.log(`✅ Found Lorcana card "${name}" from Lorcana API`);
|
||||
console.log(`✅ Found Lorcana card "${name}" from direct Lorcana API`);
|
||||
return {
|
||||
id: card.id || card.uuid || `lorcana-${Math.random()}`,
|
||||
name: card.name,
|
||||
|
|
@ -427,18 +507,14 @@ export const lorcanaService = {
|
|||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('❌ Lorcana API failed for card lookup, trying Lorcast API');
|
||||
console.log('❌ Direct Lorcana API failed for card lookup');
|
||||
}
|
||||
|
||||
// Fallback to Lorcast API
|
||||
// Final fallback to Lorcast API
|
||||
const searchEndpoints = [
|
||||
`/cards?q=name:${encodeURIComponent(name)}`,
|
||||
`/cards?search=${encodeURIComponent(name)}`,
|
||||
`/cards?query=${encodeURIComponent(name)}`,
|
||||
`/api/cards?q=name:${encodeURIComponent(name)}`,
|
||||
`/api/cards?search=${encodeURIComponent(name)}`,
|
||||
`/v0/cards?q=name:${encodeURIComponent(name)}`,
|
||||
`/v0/cards?search=${encodeURIComponent(name)}`
|
||||
`/cards?query=${encodeURIComponent(name)}`
|
||||
];
|
||||
|
||||
for (const endpoint of searchEndpoints) {
|
||||
|
|
@ -495,7 +571,53 @@ export const lorcanaService = {
|
|||
await lorcastLimiter.waitForSlot();
|
||||
|
||||
try {
|
||||
// Try Lorcana API first
|
||||
// Try proxy API first (handles CORS)
|
||||
try {
|
||||
const response = await fetch(`${PROXY_API_BASE_URL}?limit=${count}&api=lorcana`);
|
||||
|
||||
if (response.ok) {
|
||||
const proxyData = await response.json();
|
||||
if (proxyData.success && proxyData.data) {
|
||||
const allCards = proxyData.data.cards || proxyData.data.data || proxyData.data || [];
|
||||
|
||||
if (allCards.length > 0) {
|
||||
console.log(`✅ Found ${allCards.length} cards via proxy (${proxyData.source})`);
|
||||
|
||||
// Randomly select cards
|
||||
const shuffled = allCards.sort(() => 0.5 - Math.random());
|
||||
const selectedCards = shuffled.slice(0, count);
|
||||
|
||||
return selectedCards.map((card: any) => ({
|
||||
id: card.id || card.uuid || `lorcana-${Math.random()}`,
|
||||
name: card.name,
|
||||
set_name: card.set?.name || card.set_name || '',
|
||||
set_code: card.set?.code || card.set_code || '',
|
||||
card_number: card.number || card.card_number || '',
|
||||
rarity: card.rarity,
|
||||
game: 'LORCANA',
|
||||
mana_cost: card.cost?.toString() || card.mana_cost,
|
||||
cmc: card.cost || card.cmc,
|
||||
card_type: card.type || card.card_type,
|
||||
colors: card.colors || [],
|
||||
oracle_text: card.text || card.oracle_text || '',
|
||||
power: card.strength?.toString() || card.power,
|
||||
toughness: card.willpower?.toString() || card.toughness,
|
||||
image_url: card.image_url || card.images?.small || card.images?.png,
|
||||
stock_image_url: card.image_url || card.images?.small || card.images?.png,
|
||||
current_price: card.price?.market || card.current_price || undefined,
|
||||
market_price: card.price?.low || card.market_price || undefined,
|
||||
verified: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('❌ Proxy API failed for random cards, trying direct APIs');
|
||||
}
|
||||
|
||||
// Fallback to direct Lorcana API
|
||||
try {
|
||||
const response = await fetch(`${LORCANA_API_BASE_URL}/cards?limit=${count}`);
|
||||
|
||||
|
|
@ -504,7 +626,7 @@ export const lorcanaService = {
|
|||
const allCards = data.cards || data.data || data || [];
|
||||
|
||||
if (allCards.length > 0) {
|
||||
console.log(`✅ Found ${allCards.length} cards from Lorcana API`);
|
||||
console.log(`✅ Found ${allCards.length} cards from direct Lorcana API`);
|
||||
|
||||
// Randomly select cards
|
||||
const shuffled = allCards.sort(() => 0.5 - Math.random());
|
||||
|
|
@ -536,15 +658,14 @@ export const lorcanaService = {
|
|||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('❌ Lorcana API failed for random cards, trying Lorcast API');
|
||||
console.log('❌ Direct Lorcana API failed for random cards');
|
||||
}
|
||||
|
||||
// Fallback to Lorcast API
|
||||
// Final fallback to Lorcast API
|
||||
const endpoints = [
|
||||
'/cards',
|
||||
'/api/cards',
|
||||
'/v0/cards',
|
||||
'/api/v0/cards'
|
||||
'/v0/cards'
|
||||
];
|
||||
|
||||
let allCards: any[] = [];
|
||||
|
|
|
|||
Loading…
Reference in a new issue