267 lines
7.4 KiB
JavaScript
267 lines
7.4 KiB
JavaScript
|
|
import { NextResponse } from 'next/server';
|
|||
|
|
import { sql } from '@vercel/postgres';
|
|||
|
|
import { verifyToken } from '../auth-utils.js';
|
|||
|
|
|
|||
|
|
// GET /api/cards - Search cards from database
|
|||
|
|
export async function GET(request) {
|
|||
|
|
try {
|
|||
|
|
const { searchParams } = new URL(request.url);
|
|||
|
|
const query = searchParams.get('q') || searchParams.get('search') || '';
|
|||
|
|
const game = searchParams.get('game');
|
|||
|
|
const page = parseInt(searchParams.get('page') || '1');
|
|||
|
|
const limit = parseInt(searchParams.get('limit') || '20');
|
|||
|
|
const offset = (page - 1) * limit;
|
|||
|
|
|
|||
|
|
console.log(`🔍 Searching cards: "${query}" game: "${game}" page: ${page}`);
|
|||
|
|
|
|||
|
|
// Build the SQL query
|
|||
|
|
let sqlQuery = `
|
|||
|
|
SELECT
|
|||
|
|
id,
|
|||
|
|
name,
|
|||
|
|
set_name,
|
|||
|
|
set_code,
|
|||
|
|
card_number,
|
|||
|
|
rarity,
|
|||
|
|
game,
|
|||
|
|
mana_cost,
|
|||
|
|
cmc,
|
|||
|
|
card_type,
|
|||
|
|
colors,
|
|||
|
|
oracle_text,
|
|||
|
|
power,
|
|||
|
|
toughness,
|
|||
|
|
image_url,
|
|||
|
|
stock_image_url,
|
|||
|
|
current_price,
|
|||
|
|
market_price,
|
|||
|
|
verified,
|
|||
|
|
created_at,
|
|||
|
|
updated_at
|
|||
|
|
FROM cards
|
|||
|
|
WHERE 1=1
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
const params = [];
|
|||
|
|
let paramIndex = 1;
|
|||
|
|
|
|||
|
|
// Add search filter
|
|||
|
|
if (query.trim()) {
|
|||
|
|
sqlQuery += ` AND (
|
|||
|
|
name ILIKE $${paramIndex} OR
|
|||
|
|
oracle_text ILIKE $${paramIndex} OR
|
|||
|
|
card_type ILIKE $${paramIndex} OR
|
|||
|
|
set_name ILIKE $${paramIndex}
|
|||
|
|
)`;
|
|||
|
|
params.push(`%${query}%`);
|
|||
|
|
paramIndex++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Add game filter
|
|||
|
|
if (game && game !== 'ALL') {
|
|||
|
|
sqlQuery += ` AND game = $${paramIndex}`;
|
|||
|
|
params.push(game);
|
|||
|
|
paramIndex++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Add ordering and pagination
|
|||
|
|
sqlQuery += ` ORDER BY name ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
|
|||
|
|
params.push(limit, offset);
|
|||
|
|
|
|||
|
|
console.log(`📝 SQL Query: ${sqlQuery}`);
|
|||
|
|
console.log(`📝 Parameters:`, params);
|
|||
|
|
|
|||
|
|
// Execute the query
|
|||
|
|
const result = await sql.query(sqlQuery, params);
|
|||
|
|
|
|||
|
|
// Get total count for pagination
|
|||
|
|
let countQuery = `
|
|||
|
|
SELECT COUNT(*) as total
|
|||
|
|
FROM cards
|
|||
|
|
WHERE 1=1
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
const countParams = [];
|
|||
|
|
let countParamIndex = 1;
|
|||
|
|
|
|||
|
|
if (query.trim()) {
|
|||
|
|
countQuery += ` AND (
|
|||
|
|
name ILIKE $${countParamIndex} OR
|
|||
|
|
oracle_text ILIKE $${countParamIndex} OR
|
|||
|
|
card_type ILIKE $${countParamIndex} OR
|
|||
|
|
set_name ILIKE $${countParamIndex}
|
|||
|
|
)`;
|
|||
|
|
countParams.push(`%${query}%`);
|
|||
|
|
countParamIndex++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (game && game !== 'ALL') {
|
|||
|
|
countQuery += ` AND game = $${countParamIndex}`;
|
|||
|
|
countParams.push(game);
|
|||
|
|
countParamIndex++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const countResult = await sql.query(countQuery, countParams);
|
|||
|
|
const total = parseInt(countResult.rows[0].total);
|
|||
|
|
|
|||
|
|
// Transform the results
|
|||
|
|
const cards = result.rows.map(row => ({
|
|||
|
|
id: row.id,
|
|||
|
|
name: row.name,
|
|||
|
|
set_name: row.set_name,
|
|||
|
|
set_code: row.set_code,
|
|||
|
|
card_number: row.card_number,
|
|||
|
|
rarity: row.rarity,
|
|||
|
|
game: row.game,
|
|||
|
|
mana_cost: row.mana_cost,
|
|||
|
|
cmc: row.cmc,
|
|||
|
|
card_type: row.card_type,
|
|||
|
|
colors: row.colors ? JSON.parse(row.colors) : [],
|
|||
|
|
oracle_text: row.oracle_text,
|
|||
|
|
power: row.power,
|
|||
|
|
toughness: row.toughness,
|
|||
|
|
image_url: row.image_url,
|
|||
|
|
stock_image_url: row.stock_image_url,
|
|||
|
|
current_price: row.current_price,
|
|||
|
|
market_price: row.market_price,
|
|||
|
|
verified: row.verified,
|
|||
|
|
createdAt: row.created_at,
|
|||
|
|
updatedAt: row.updated_at,
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
console.log(`✅ Found ${cards.length} cards (total: ${total})`);
|
|||
|
|
|
|||
|
|
return NextResponse.json({
|
|||
|
|
success: true,
|
|||
|
|
data: cards,
|
|||
|
|
pagination: {
|
|||
|
|
page,
|
|||
|
|
limit,
|
|||
|
|
total,
|
|||
|
|
totalPages: Math.ceil(total / limit),
|
|||
|
|
hasNext: page * limit < total,
|
|||
|
|
hasPrev: page > 1
|
|||
|
|
},
|
|||
|
|
search: {
|
|||
|
|
query,
|
|||
|
|
game,
|
|||
|
|
results: cards.length
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Error searching cards:', error);
|
|||
|
|
return NextResponse.json(
|
|||
|
|
{ error: 'Failed to search cards', details: error.message },
|
|||
|
|
{ status: 500 }
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// POST /api/cards - Find or create card
|
|||
|
|
export async function POST(request) {
|
|||
|
|
try {
|
|||
|
|
const token = request.headers.get('authorization')?.replace('Bearer ', '');
|
|||
|
|
const user = await verifyToken(token);
|
|||
|
|
|
|||
|
|
if (!user) {
|
|||
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const { name, game, set_name, set_code, card_number } = await request.json();
|
|||
|
|
|
|||
|
|
if (!name || !game) {
|
|||
|
|
return NextResponse.json({ error: 'Name and game are required' }, { status: 400 });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`🔍 Finding or creating card: "${name}" (${game})`);
|
|||
|
|
|
|||
|
|
// Try to find existing card
|
|||
|
|
let result = await sql.query(`
|
|||
|
|
SELECT * FROM cards
|
|||
|
|
WHERE name = $1 AND game = $2
|
|||
|
|
ORDER BY created_at DESC
|
|||
|
|
LIMIT 1
|
|||
|
|
`, [name, game]);
|
|||
|
|
|
|||
|
|
if (result.rows.length > 0) {
|
|||
|
|
const card = result.rows[0];
|
|||
|
|
console.log(`✅ Found existing card: ${card.name}`);
|
|||
|
|
|
|||
|
|
return NextResponse.json({
|
|||
|
|
success: true,
|
|||
|
|
data: {
|
|||
|
|
id: card.id,
|
|||
|
|
name: card.name,
|
|||
|
|
set_name: card.set_name,
|
|||
|
|
set_code: card.set_code,
|
|||
|
|
card_number: card.card_number,
|
|||
|
|
rarity: card.rarity,
|
|||
|
|
game: card.game,
|
|||
|
|
mana_cost: card.mana_cost,
|
|||
|
|
cmc: card.cmc,
|
|||
|
|
card_type: card.card_type,
|
|||
|
|
colors: card.colors ? JSON.parse(card.colors) : [],
|
|||
|
|
oracle_text: card.oracle_text,
|
|||
|
|
power: card.power,
|
|||
|
|
toughness: card.toughness,
|
|||
|
|
image_url: card.image_url,
|
|||
|
|
stock_image_url: card.stock_image_url,
|
|||
|
|
current_price: card.current_price,
|
|||
|
|
market_price: card.market_price,
|
|||
|
|
verified: card.verified,
|
|||
|
|
createdAt: card.created_at,
|
|||
|
|
updatedAt: card.updated_at,
|
|||
|
|
},
|
|||
|
|
message: 'Card found'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Create new card if not found
|
|||
|
|
console.log(`➕ Creating new card: ${name}`);
|
|||
|
|
|
|||
|
|
result = await sql.query(`
|
|||
|
|
INSERT INTO cards (
|
|||
|
|
name, game, set_name, set_code, card_number, verified
|
|||
|
|
) VALUES ($1, $2, $3, $4, $5, $6)
|
|||
|
|
RETURNING *
|
|||
|
|
`, [name, game, set_name || '', set_code || '', card_number || '', false]);
|
|||
|
|
|
|||
|
|
const newCard = result.rows[0];
|
|||
|
|
|
|||
|
|
return NextResponse.json({
|
|||
|
|
success: true,
|
|||
|
|
data: {
|
|||
|
|
id: newCard.id,
|
|||
|
|
name: newCard.name,
|
|||
|
|
set_name: newCard.set_name,
|
|||
|
|
set_code: newCard.set_code,
|
|||
|
|
card_number: newCard.card_number,
|
|||
|
|
rarity: newCard.rarity,
|
|||
|
|
game: newCard.game,
|
|||
|
|
mana_cost: newCard.mana_cost,
|
|||
|
|
cmc: newCard.cmc,
|
|||
|
|
card_type: newCard.card_type,
|
|||
|
|
colors: newCard.colors ? JSON.parse(newCard.colors) : [],
|
|||
|
|
oracle_text: newCard.oracle_text,
|
|||
|
|
power: newCard.power,
|
|||
|
|
toughness: newCard.toughness,
|
|||
|
|
image_url: newCard.image_url,
|
|||
|
|
stock_image_url: newCard.stock_image_url,
|
|||
|
|
current_price: newCard.current_price,
|
|||
|
|
market_price: newCard.market_price,
|
|||
|
|
verified: newCard.verified,
|
|||
|
|
createdAt: newCard.created_at,
|
|||
|
|
updatedAt: newCard.updated_at,
|
|||
|
|
},
|
|||
|
|
message: 'Card created'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Error finding/creating card:', error);
|
|||
|
|
return NextResponse.json(
|
|||
|
|
{ error: 'Failed to find/create card', details: error.message },
|
|||
|
|
{ status: 500 }
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|