- custom_frames: per-user frames with full 9-slot palette (JSONB),
unique names; designer frame picker lists them alongside starters,
click to use, edit/delete via inline editor with live preview
- custom_symbols: upload cost icons (PNG/WebP/SVG, 2MB) keyed by short
code; re-uploading a code replaces the old icon; ManaPips renders
icon pips for {CODE} tokens with graceful text fallback
- custom_cards.custom_frame_id links designs to custom frames; API GETs
join and nest the palette; deleting a frame falls back to starter
- migration 1787700511000
41 lines
1.3 KiB
JavaScript
41 lines
1.3 KiB
JavaScript
import { del } from '../../../lib/object-storage.js';
|
|
import { sql } from '../../../lib/sql.js';
|
|
import { getUserFromRequest } from '../../../lib/permission-middleware';
|
|
|
|
export default async function handler(req, res) {
|
|
try {
|
|
const user = await getUserFromRequest(req);
|
|
if (!user) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
const symbolId = parseInt(req.query.id, 10);
|
|
if (!Number.isInteger(symbolId)) {
|
|
return res.status(400).json({ error: 'Invalid symbol id' });
|
|
}
|
|
|
|
if (req.method !== 'DELETE') {
|
|
return res.status(405).json({ error: 'Method not allowed' });
|
|
}
|
|
|
|
const found = await sql`
|
|
SELECT id, image_url FROM custom_symbols
|
|
WHERE id = ${symbolId} AND user_id = ${user.userId}
|
|
`;
|
|
if (found.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Symbol not found' });
|
|
}
|
|
|
|
try {
|
|
await del(found.rows[0].image_url);
|
|
} catch (blobError) {
|
|
console.warn('Failed to delete symbol image:', blobError);
|
|
}
|
|
|
|
await sql`DELETE FROM custom_symbols WHERE id = ${symbolId}`;
|
|
return res.status(200).json({ message: 'Symbol deleted' });
|
|
} catch (error) {
|
|
console.error('Custom symbol API error:', error);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
}
|