feat(devices): PATCH accepts icon ref

Export reusable iconRef zod validator (set:<set>:<name> | brand:<slug> | null)
and add it as an optional field to patchBody so PATCH /devices/:mac accepts icon.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
root
2026-06-09 08:46:07 +10:00
parent 4efeca74b2
commit b049aedd22
2 changed files with 18 additions and 1 deletions

View File

@@ -53,12 +53,14 @@ router.get('/discovered', requireOwner, asyncWrap(async (_req, res) => {
}));
const macParam = z.object({ mac: z.string().regex(/^[0-9a-f]{2}(:[0-9a-f]{2}){5}$/i) });
export const iconRef = z.string().regex(/^(set:[a-z0-9-]+:[a-z0-9-]+|brand:[a-z0-9-]+)$/).nullable();
const patchBody = z.object({
name: z.string().max(120).optional(),
grp: z.enum(['Smart Home', 'Entertainment', 'Personal', 'Network', 'Flagged']).optional(),
status: z.enum(['new', 'known', 'ignored']).optional(),
note: z.string().max(500).optional(),
flagged: z.boolean().optional()
flagged: z.boolean().optional(),
icon: iconRef.optional()
});
// PATCH /devices/:mac — name / edit / promote (owner). This is "add from discovered".

View File

@@ -0,0 +1,15 @@
import { describe, it, expect } from 'vitest';
import { z } from 'zod';
import { iconRef } from '../../lib/api/routes/devices.js';
describe('icon ref validation', () => {
it('accepts set + brand refs and null', () => {
expect(iconRef.safeParse('set:devices:router').success).toBe(true);
expect(iconRef.safeParse('brand:apple').success).toBe(true);
expect(iconRef.safeParse(null).success).toBe(true);
});
it('rejects junk', () => {
expect(iconRef.safeParse('set:bad').success).toBe(false);
expect(iconRef.safeParse('javascript:alert').success).toBe(false);
});
});