deckhearth/api/cards/index.js

267 lines
7.4 KiB
JavaScript
Raw Normal View History

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 }
);
}
}