- {CODE} tokens in description/actions/flavor render as inline symbol
icons (RichText), including inside cost pips
- custom frames gain an optional background texture (upload/replace/
remove via /api/custom-frames/[id]/texture); renders behind panels
- custom games can be shared to the community (is_public): toggle in the
game space, public listing at /community/games, read-only game view,
/api/public/games endpoints (no auth, public rows only)
- /designer/print: multi-card print sheets on US Letter at 300dpi
(63x88mm cards, 3x3 or 2x2, dashed cut guides, full-sheet PNG export)
- migration 1787711511000
62 lines
2 KiB
JavaScript
62 lines
2 KiB
JavaScript
import { sql } from '../../../../lib/sql.js';
|
|
|
|
/**
|
|
* Public detail for one community-shared custom game plus its designs.
|
|
* No auth — read-only; 404 unless the game is marked public.
|
|
*/
|
|
export default async function handler(req, res) {
|
|
try {
|
|
if (req.method !== 'GET') {
|
|
return res.status(405).json({ error: 'Method not allowed' });
|
|
}
|
|
|
|
const gameId = parseInt(req.query.id, 10);
|
|
if (!Number.isInteger(gameId)) {
|
|
return res.status(400).json({ error: 'Invalid game id' });
|
|
}
|
|
|
|
const found = await sql`
|
|
SELECT g.id, g.name, g.description, g.updated_at,
|
|
COALESCE(NULLIF(u.username, ''), split_part(u.email, '@', 1)) AS author
|
|
FROM custom_games g
|
|
JOIN users u ON u.id = g.user_id
|
|
WHERE g.id = ${gameId} AND g.is_public = true
|
|
`;
|
|
if (found.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Game not found' });
|
|
}
|
|
|
|
const cards = await sql`
|
|
SELECT c.id, c.name, c.mana_cost, c.card_type, c.rarity,
|
|
c.rules_text, c.actions, c.flavor_quote, c.power, c.toughness,
|
|
c.frame_id, c.artwork_url, c.art_mode,
|
|
f.id AS frame_pk, f.name AS frame_name, f.palette AS frame_palette,
|
|
f.texture_url AS frame_texture
|
|
FROM custom_cards c
|
|
LEFT JOIN custom_frames f ON f.id = c.custom_frame_id
|
|
WHERE c.custom_game_id = ${gameId}
|
|
ORDER BY c.updated_at DESC
|
|
`;
|
|
|
|
const designs = cards.rows.map((row) => {
|
|
const { frame_pk, frame_name, frame_palette, frame_texture, ...rest } = row;
|
|
return {
|
|
...rest,
|
|
custom_frame:
|
|
frame_pk != null
|
|
? {
|
|
id: frame_pk,
|
|
name: frame_name,
|
|
palette: frame_palette,
|
|
texture_url: frame_texture,
|
|
}
|
|
: null,
|
|
};
|
|
});
|
|
|
|
return res.status(200).json({ game: found.rows[0], designs });
|
|
} catch (error) {
|
|
console.error('Public game API error:', error);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
}
|