83 lines
2.9 KiB
JavaScript
83 lines
2.9 KiB
JavaScript
import 'dotenv/config';
|
|
import express from 'express';
|
|
import cors from 'cors';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import fetch from 'node-fetch';
|
|
|
|
const app = express();
|
|
app.use(cors());
|
|
app.use(express.json());
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
// Serve static frontend
|
|
app.use(express.static(path.join(__dirname, 'public')));
|
|
|
|
// (Mock data routes removed)
|
|
|
|
// eBay completed (sold) listings search
|
|
app.get('/api/ebay/search', async (req, res) => {
|
|
try {
|
|
const { player, team, manufacturer, page = 1 } = req.query;
|
|
const appId = process.env.EBAY_APP_ID;
|
|
if (!appId) {
|
|
return res.status(500).json({ error: 'Server missing EBAY_APP_ID environment variable' });
|
|
}
|
|
const name = String(player || '').trim();
|
|
if (!name) return res.status(400).json({ error: 'Missing required "player" query param' });
|
|
|
|
const keywords = [name, team, manufacturer].filter(Boolean).join(' ');
|
|
|
|
const params = new URLSearchParams({
|
|
'OPERATION-NAME': 'findCompletedItems',
|
|
'SERVICE-VERSION': '1.13.0',
|
|
'SECURITY-APPNAME': appId,
|
|
'RESPONSE-DATA-FORMAT': 'JSON',
|
|
keywords,
|
|
'paginationInput.entriesPerPage': '24',
|
|
'paginationInput.pageNumber': String(page),
|
|
'sortOrder': 'EndTimeSoonest',
|
|
'outputSelector': 'PictureURLLarge'
|
|
});
|
|
// item filters
|
|
params.append('itemFilter(0).name', 'SoldItemsOnly');
|
|
params.append('itemFilter(0).value', 'true');
|
|
params.append('GLOBAL-ID', 'EBAY-US');
|
|
|
|
const url = `https://svcs.ebay.com/services/search/FindingService/v1?${params.toString()}`;
|
|
const resp = await fetch(url);
|
|
if (!resp.ok) {
|
|
const text = await resp.text();
|
|
return res.status(502).json({ error: 'eBay API error', detail: text });
|
|
}
|
|
const data = await resp.json();
|
|
const root = (data && data.findCompletedItemsResponse && data.findCompletedItemsResponse[0]) || {};
|
|
const items = ((root.searchResult && root.searchResult[0] && root.searchResult[0].item) || []).map((it) => {
|
|
const title = it.title?.[0] || '';
|
|
const url = it.viewItemURL?.[0] || '';
|
|
const image = (it.pictureURLLarge?.[0] || it.galleryURL?.[0] || '');
|
|
const price = it.sellingStatus?.[0]?.currentPrice?.[0]?.__value__ || null;
|
|
const currency = it.sellingStatus?.[0]?.currentPrice?.[0]?.['@currencyId'] || null;
|
|
const ended = it.listingInfo?.[0]?.endTime?.[0] || null;
|
|
return { title, url, image, price, currency, ended };
|
|
});
|
|
|
|
res.json({ results: items });
|
|
} catch (err) {
|
|
console.error('eBay search error', err);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
|
|
// Fallback to index.html for root
|
|
app.get('/', (_req, res) => {
|
|
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
|
});
|
|
|
|
const PORT = process.env.PORT || 3000;
|
|
app.listen(PORT, () => {
|
|
console.log(`Baseball Cards Search running on http://localhost:${PORT}`);
|
|
});
|