v2.1
Ship configurator updates with catalog DB included for server deploy. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
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 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user