make a layout column refuse a non-layout

This commit is contained in:
2026-08-07 09:24:16 +00:00
parent 2efd7ffba3
commit 92b89052a4
2 changed files with 31 additions and 3 deletions
+8
View File
@@ -19,6 +19,12 @@ export const dashboardsRouter = createRouter();
// `apps/Terminal/index.tsx`; the 400 at the bottom of the PATCH loop is what tells you when it is not.
const PANEL_STATE_PREFIXES = ['tmux', 'nvim', 'claude-code'];
// A `LayoutNode` is an object. Everything from the PATCH body down to jsonb is `unknown`, so without this
// the only thing standing between a wrong-shaped body and the layout columns is the CHECK constraint —
// which does hold the line, but as a 500 that says nothing the caller can act on. Say what was wrong
// instead. Null is handled separately by both layout branches: there it means "delete", not "store".
const isLayout = (value: unknown): boolean => typeof value === 'object' && value !== null && !Array.isArray(value);
// GET /dashboards
dashboardsRouter.get('/', async (ctx) => {
const userId = ctx.get('user').id;
@@ -51,6 +57,7 @@ dashboardsRouter.patch('/', async (ctx) => {
if (value === null) {
await deleteDashboard(userId, id);
} else {
if (!isLayout(value)) return ctx.json({ error: `"${key}" must be a layout object` }, 400);
await upsertDashboard(userId, id, { layout: value });
}
continue;
@@ -110,6 +117,7 @@ dashboardsRouter.patch('/', async (ctx) => {
if (value === null) {
await deleteScreen(userId, name);
} else {
if (!isLayout(value)) return ctx.json({ error: `"${key}" must be a layout object` }, 400);
await upsertScreen(userId, name, { layout: value });
}
continue;