deckhearth/pages/api/admin/card-submissions.js
Randall Stillwell 1cc2e28423 Migrate Deck Hearth off Vercel/Neon to homelab Dokploy stack.
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>
2026-08-15 09:32:13 -05:00

95 lines
3 KiB
JavaScript

import { sql } from '../../../lib/sql.js';
import { withAdmin } from '../../../lib/permission-middleware';
export default withAdmin(async function handler(req, res, user) {
try {
if (req.method === 'GET') {
const { status = 'pending' } = req.query;
const result = await sql`
SELECT
cs.*,
u.email AS submitter_email
FROM card_submissions cs
JOIN users u ON cs.user_id = u.id
WHERE cs.status = ${status}
ORDER BY cs.created_at DESC
LIMIT 100
`;
return res.status(200).json({ submissions: result.rows });
}
if (req.method === 'POST') {
const { submissionId, action } = req.body;
if (!submissionId || !['approve', 'reject'].includes(action)) {
return res.status(400).json({ error: 'submissionId and action (approve|reject) are required' });
}
const submissionResult = await sql`
SELECT * FROM card_submissions WHERE id = ${submissionId} LIMIT 1
`;
if (submissionResult.rows.length === 0) {
return res.status(404).json({ error: 'Submission not found' });
}
const submission = submissionResult.rows[0];
if (submission.status !== 'pending') {
return res.status(400).json({ error: 'Submission has already been reviewed' });
}
if (action === 'reject') {
await sql`
UPDATE card_submissions
SET status = 'rejected', reviewed_by = ${user.userId}, updated_at = CURRENT_TIMESTAMP
WHERE id = ${submissionId}
`;
return res.status(200).json({ message: 'Submission rejected' });
}
const payload = submission.ocr_payload || {};
const newCardResult = await sql`
INSERT INTO cards (
name, set_name, set_code, card_number, rarity, game,
mana_cost, card_type, oracle_text, power, verified
) VALUES (
${payload.name || 'Unknown'},
${payload.set || null},
${payload.setCode || null},
${payload.cardNumber || null},
${payload.rarity || null},
${payload.game || 'UNKNOWN'},
${payload.manaCost || null},
${payload.cardType || null},
${payload.rawText || submission.ocr_text || null},
${payload.hp || null},
${true}
)
RETURNING *
`;
const newCard = newCardResult.rows[0];
await sql`
UPDATE card_submissions
SET
status = 'approved',
reviewed_by = ${user.userId},
promoted_card_id = ${newCard.id},
updated_at = CURRENT_TIMESTAMP
WHERE id = ${submissionId}
`;
return res.status(201).json({
message: 'Submission approved and card promoted to catalog',
card: newCard,
});
}
return res.status(405).json({ error: 'Method not allowed' });
} catch (error) {
console.error('[admin/card-submissions]', error);
return res.status(500).json({ error: 'Internal server error' });
}
});