deckhearth/pages/api/cards/find-or-create.js
Randall Stillwell b0f9a9ec71 feat(scanner): move card identification server-side (convoy #2)
Replace browser Gemini/OCR with POST /api/scan/identify, add card_submissions
review queue, remove user-writable cards INSERT, and surface disambiguation
when catalog matching is ambiguous.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 08:44:02 -05:00

79 lines
1.8 KiB
JavaScript

import { getUserFromRequest } from '../../../lib/permission-middleware';
import { matchCardInCatalog } from '../../../lib/card-catalog-match.js';
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const user = await getUserFromRequest(req);
if (!user) {
return res.status(401).json({ error: 'Authentication required' });
}
const {
name,
set,
setCode,
cardNumber,
game,
cardType,
rarity,
hp,
manaCost,
ocrData,
} = req.body;
const matchResult = await matchCardInCatalog({
userId: user.userId,
name,
set,
setCode,
cardNumber,
game,
cardType,
rarity,
hp,
manaCost,
ocrData,
});
if (matchResult.type === 'matched') {
return res.status(200).json({
card: matchResult.card,
isExisting: matchResult.isExisting,
message: matchResult.message,
});
}
if (matchResult.type === 'disambiguation') {
return res.status(200).json({
card: null,
matches: matchResult.matches,
needsUserSelection: true,
message: matchResult.message,
});
}
if (matchResult.type === 'submitted') {
return res.status(200).json({
card: null,
matches: [],
submissionId: matchResult.submissionId,
needsReview: true,
message: matchResult.message,
});
}
return res.status(200).json({
card: null,
matches: [],
needsUserInput: true,
message: matchResult.message,
});
} catch (error) {
console.error('Error in find-or-create card:', error);
return res.status(500).json({ error: 'Internal server error' });
}
}