import pool, { run } from '../db/index.js'; import { DEFAULT_LAYOUT_PRESETS } from '../db/defaultLayouts.js'; const MAX_NAME_LENGTH = 100; const PRESET_COLUMNS = 'id, seed_key, name, description, config, sort_order, created_at, updated_at'; let seeded = null; /** * Synchronise les dispositions intégrées avec leur définition en code (insertion ou mise à jour). * Une disposition intégrée supprimée reste masquée (active = 0). */ function ensureDefaultLayouts() { if (!seeded) { seeded = (async () => { const { rows } = await pool.query( 'SELECT seed_key, name, description, config, sort_order FROM layout_presets WHERE seed_key IS NOT NULL' ); const existing = new Map(rows.map((r) => [r.seed_key, r])); for (const [index, preset] of DEFAULT_LAYOUT_PRESETS.entries()) { const config = JSON.stringify(preset.config); const row = existing.get(preset.seedKey); if (row) { const unchanged = row.name === preset.name && row.description === preset.description && row.config === config && Number(row.sort_order) === index; if (!unchanged) { await run( `UPDATE layout_presets SET name = $1, description = $2, config = $3, sort_order = $4, updated_at = datetime('now') WHERE seed_key = $5`, [preset.name, preset.description, config, index, preset.seedKey] ); } continue; } await run( `INSERT INTO layout_presets (seed_key, name, description, config, sort_order) VALUES ($1, $2, $3, $4, $5)`, [preset.seedKey, preset.name, preset.description, config, index] ); } })().catch((err) => { seeded = null; throw err; }); } return seeded; } function parsePreset(row) { if (!row) return null; let config = null; try { config = JSON.parse(row.config); } catch { /* config illisible → null */ } return { ...row, builtin: row.seed_key != null, config }; } export async function getLayoutPresets(req, res) { try { await ensureDefaultLayouts(); const { rows } = await pool.query( `SELECT ${PRESET_COLUMNS} FROM layout_presets WHERE active = 1 ORDER BY sort_order ASC, id ASC` ); res.json(rows.map(parsePreset)); } catch (err) { res.status(500).json({ error: err.message }); } } export async function createLayoutPreset(req, res) { try { const name = String(req.body?.name || '').trim(); const description = String(req.body?.description || '').trim() || null; const config = req.body?.config; if (!name) return res.status(400).json({ error: 'Le nom est requis.' }); if (name.length > MAX_NAME_LENGTH) { return res.status(400).json({ error: `Le nom ne peut pas dépasser ${MAX_NAME_LENGTH} caractères.` }); } if (!config || typeof config !== 'object' || Array.isArray(config) || !Array.isArray(config.modules) || config.modules.length === 0) { return res.status(400).json({ error: 'Disposition invalide (au moins une colonne requise).' }); } await ensureDefaultLayouts(); await run( `INSERT INTO layout_presets (name, description, config, sort_order) VALUES ($1, $2, $3, 1000)`, [name, description, JSON.stringify(config)] ); const { rows } = await pool.query( `SELECT ${PRESET_COLUMNS} FROM layout_presets ORDER BY id DESC LIMIT 1` ); res.status(201).json(parsePreset(rows[0])); } catch (err) { res.status(500).json({ error: err.message }); } } export async function deleteLayoutPreset(req, res) { try { const id = Number(req.params.id); if (!Number.isInteger(id) || id < 1) { return res.status(400).json({ error: 'Identifiant invalide.' }); } const { rows } = await pool.query('SELECT id, seed_key FROM layout_presets WHERE id = $1 LIMIT 1', [id]); if (rows.length === 0) return res.status(404).json({ error: 'Disposition introuvable.' }); if (rows[0].seed_key != null) { await run(`UPDATE layout_presets SET active = 0, updated_at = datetime('now') WHERE id = $1`, [id]); } else { await run('DELETE FROM layout_presets WHERE id = $1', [id]); } res.status(204).end(); } catch (err) { res.status(500).json({ error: err.message }); } }