267 lines
No EOL
7.4 KiB
JavaScript
267 lines
No EOL
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 }
|
||
);
|
||
}
|
||
}
|