Ship configurator updates with catalog DB included for server deploy. Co-authored-by: Cursor <[email protected]>
126 lines
4.3 KiB
JavaScript
126 lines
4.3 KiB
JavaScript
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 });
|
|
}
|
|
}
|