Replace @vercel/postgres, Blob, and Upstash with lib/sql.js, MinIO object storage, and CT 102 Redis rate limits. Add Dockerfile for Dokploy deploy, homelab runbooks, Neon data-copy helper, and point CI smoke/visual at the homelab URL instead of Vercel previews. Co-authored-by: Cursor <cursoragent@cursor.com>
106 lines
No EOL
3.3 KiB
JavaScript
106 lines
No EOL
3.3 KiB
JavaScript
import { sql } from '../../../lib/sql.js';
|
|
|
|
export default async function handler(req, res) {
|
|
const { id } = req.query;
|
|
|
|
if (req.method === 'GET') {
|
|
try {
|
|
const result = await sql`
|
|
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, scryfall_id, verified,
|
|
quantity
|
|
FROM cards
|
|
WHERE id = ${id}
|
|
`;
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Card not found' });
|
|
}
|
|
|
|
const card = result.rows[0];
|
|
|
|
// Parse colors if it's a JSON string
|
|
if (card.colors && typeof card.colors === 'string') {
|
|
try {
|
|
card.colors = JSON.parse(card.colors);
|
|
} catch (e) {
|
|
card.colors = [];
|
|
}
|
|
}
|
|
|
|
res.status(200).json(card);
|
|
} catch (error) {
|
|
console.error('Error fetching card:', error);
|
|
res.status(500).json({ error: 'Failed to fetch card' });
|
|
}
|
|
} else if (req.method === 'PUT') {
|
|
try {
|
|
const {
|
|
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
|
|
} = req.body;
|
|
|
|
// Validate required fields
|
|
if (!name || !game) {
|
|
return res.status(400).json({ error: 'Name and game are required fields' });
|
|
}
|
|
|
|
// Update the card
|
|
const result = await sql`
|
|
UPDATE cards SET
|
|
name = ${name},
|
|
set_name = ${set_name || null},
|
|
set_code = ${set_code || null},
|
|
card_number = ${card_number || null},
|
|
rarity = ${rarity || null},
|
|
game = ${game},
|
|
mana_cost = ${mana_cost || null},
|
|
cmc = ${cmc || null},
|
|
card_type = ${card_type || null},
|
|
colors = ${JSON.stringify(colors || [])},
|
|
oracle_text = ${oracle_text || null},
|
|
power = ${power || null},
|
|
toughness = ${toughness || null},
|
|
image_url = ${image_url || null},
|
|
stock_image_url = ${stock_image_url || null},
|
|
current_price = ${current_price || null},
|
|
market_price = ${market_price || null},
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = ${id}
|
|
RETURNING
|
|
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, scryfall_id, verified,
|
|
quantity, created_at, updated_at
|
|
`;
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Card not found' });
|
|
}
|
|
|
|
const updatedCard = result.rows[0];
|
|
|
|
// Parse colors if it's a JSON string
|
|
if (updatedCard.colors && typeof updatedCard.colors === 'string') {
|
|
try {
|
|
updatedCard.colors = JSON.parse(updatedCard.colors);
|
|
} catch (e) {
|
|
updatedCard.colors = [];
|
|
}
|
|
}
|
|
|
|
res.status(200).json(updatedCard);
|
|
} catch (error) {
|
|
console.error('Error updating card:', error);
|
|
res.status(500).json({ error: 'Failed to update card' });
|
|
}
|
|
} else {
|
|
res.status(405).json({ error: 'Method not allowed' });
|
|
}
|
|
}
|