169 lines
No EOL
4.8 KiB
JavaScript
169 lines
No EOL
4.8 KiB
JavaScript
const { Pool } = require('pg');
|
|
const jwt = require('jsonwebtoken');
|
|
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
|
|
});
|
|
|
|
// Middleware to verify user authentication
|
|
function verifyAuth(req) {
|
|
const authHeader = req.headers.authorization;
|
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
throw new Error('No token provided');
|
|
}
|
|
|
|
const token = authHeader.substring(7);
|
|
const jwtSecret = process.env.JWT_SECRET || 'fallback-secret-change-in-production';
|
|
|
|
try {
|
|
const decoded = jwt.verify(token, jwtSecret);
|
|
return decoded;
|
|
} catch (error) {
|
|
throw new Error('Invalid token');
|
|
}
|
|
}
|
|
|
|
export default async function handler(req, res) {
|
|
if (req.method !== 'POST') {
|
|
return res.status(405).json({ error: 'Method not allowed' });
|
|
}
|
|
|
|
const client = await pool.connect();
|
|
|
|
try {
|
|
// Verify authentication
|
|
const user = verifyAuth(req);
|
|
|
|
const {
|
|
name,
|
|
game,
|
|
setName = null,
|
|
setCode = null,
|
|
rarity = null,
|
|
cardType = null,
|
|
manaCost = null,
|
|
ocrConfidence = null,
|
|
ocrRawText = null,
|
|
imageUrl = null
|
|
} = req.body;
|
|
|
|
// Validate required fields
|
|
if (!name || !game) {
|
|
return res.status(400).json({
|
|
error: 'Card name and game are required'
|
|
});
|
|
}
|
|
|
|
// Try to find existing card first
|
|
let searchQuery = `
|
|
SELECT id, name, set_name, set_code, rarity, game, card_type, mana_cost,
|
|
current_price, stock_image_url, image_url, verified
|
|
FROM cards
|
|
WHERE LOWER(name) = LOWER($1) AND UPPER(game) = UPPER($2)
|
|
`;
|
|
|
|
let searchParams = [name.trim(), game.trim()];
|
|
|
|
// If set name is provided, try to match more specifically
|
|
if (setName) {
|
|
searchQuery += ` AND (set_name IS NULL OR LOWER(set_name) = LOWER($3))`;
|
|
searchParams.push(setName.trim());
|
|
}
|
|
|
|
searchQuery += ` ORDER BY verified DESC, created_at DESC LIMIT 1`;
|
|
|
|
const existingCard = await client.query(searchQuery, searchParams);
|
|
|
|
if (existingCard.rows.length > 0) {
|
|
// Card found, return it
|
|
const card = existingCard.rows[0];
|
|
|
|
res.status(200).json({
|
|
success: true,
|
|
found: true,
|
|
message: 'Card found in database',
|
|
card: {
|
|
id: card.id,
|
|
name: card.name,
|
|
setName: card.set_name,
|
|
setCode: card.set_code,
|
|
rarity: card.rarity,
|
|
game: card.game,
|
|
cardType: card.card_type,
|
|
manaCost: card.mana_cost,
|
|
currentPrice: card.current_price,
|
|
stockImageUrl: card.stock_image_url,
|
|
imageUrl: card.image_url,
|
|
verified: card.verified
|
|
}
|
|
});
|
|
} else {
|
|
// Card not found, create new one
|
|
const insertQuery = `
|
|
INSERT INTO cards (
|
|
name, game, set_name, set_code, rarity, card_type, mana_cost,
|
|
ocr_confidence, ocr_raw_text, image_url, verified, created_at, updated_at
|
|
) VALUES (
|
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
|
|
)
|
|
RETURNING id, name, set_name, set_code, rarity, game, card_type, mana_cost,
|
|
ocr_confidence, ocr_raw_text, image_url, verified, created_at
|
|
`;
|
|
|
|
const insertParams = [
|
|
name.trim(),
|
|
game.trim().toUpperCase(),
|
|
setName?.trim() || null,
|
|
setCode?.trim() || null,
|
|
rarity?.trim() || null,
|
|
cardType?.trim() || null,
|
|
manaCost?.trim() || null,
|
|
ocrConfidence || null,
|
|
ocrRawText?.trim() || null,
|
|
imageUrl?.trim() || null,
|
|
false // New cards from OCR are unverified by default
|
|
];
|
|
|
|
const newCard = await client.query(insertQuery, insertParams);
|
|
const card = newCard.rows[0];
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
found: false,
|
|
message: 'New card created from scan data',
|
|
card: {
|
|
id: card.id,
|
|
name: card.name,
|
|
setName: card.set_name,
|
|
setCode: card.set_code,
|
|
rarity: card.rarity,
|
|
game: card.game,
|
|
cardType: card.card_type,
|
|
manaCost: card.mana_cost,
|
|
currentPrice: null,
|
|
stockImageUrl: null,
|
|
imageUrl: card.image_url,
|
|
verified: card.verified,
|
|
ocrConfidence: card.ocr_confidence,
|
|
ocrRawText: card.ocr_raw_text,
|
|
createdAt: card.created_at
|
|
}
|
|
});
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Find or create card error:', error);
|
|
|
|
if (error.message === 'No token provided' || error.message === 'Invalid token') {
|
|
res.status(401).json({ error: error.message });
|
|
} else {
|
|
res.status(500).json({
|
|
error: 'Internal server error',
|
|
details: error.message
|
|
});
|
|
}
|
|
} finally {
|
|
client.release();
|
|
}
|
|
}
|