From 7b24d167b9e1aa643a2c3489cef0f43038374db7 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Thu, 17 Jul 2025 12:14:58 -0600 Subject: [PATCH] hex values checking before insert into database --- apps/server/src/api/v2/organization/PUT.ts | 65 +++++++++++++------ .../organizations/update-organization.ts | 27 +++++++- .../database/src/schema-types/organization.ts | 4 +- .../src/organization/organization.types.ts | 2 +- 4 files changed, 73 insertions(+), 25 deletions(-) diff --git a/apps/server/src/api/v2/organization/PUT.ts b/apps/server/src/api/v2/organization/PUT.ts index c2df5153e..1f2a7163a 100644 --- a/apps/server/src/api/v2/organization/PUT.ts +++ b/apps/server/src/api/v2/organization/PUT.ts @@ -8,6 +8,7 @@ import { UpdateOrganizationRequestSchema } from '@buster/server-shared/organizat import { zValidator } from '@hono/zod-validator'; import { Hono } from 'hono'; import { HTTPException } from 'hono/http-exception'; +import { z } from 'zod'; import { requireOrganizationAdmin } from '../../../middleware/auth'; /** @@ -21,7 +22,6 @@ async function updateOrganizationHandler( ): Promise { try { // Update the organization - await updateOrganization({ organizationId, ...request, @@ -41,6 +41,11 @@ async function updateOrganizationHandler( error: error instanceof Error ? error.message : error, }); + // Re-throw Zod errors to be handled by the route error handler + if (error instanceof z.ZodError) { + throw error; + } + throw new HTTPException(500, { message: 'Failed to update organization', }); @@ -51,31 +56,53 @@ async function updateOrganizationHandler( const app = new Hono() .use('*', requireOrganizationAdmin) .put('/', zValidator('json', UpdateOrganizationRequestSchema), async (c) => { - const request = await c.req.valid('json'); + const request = c.req.valid('json'); const user = c.get('busterUser'); const userOrg = c.get('userOrganizationInfo'); const organizationId = userOrg.organizationId; - try { - const response: UpdateOrganizationResponse = await updateOrganizationHandler( - organizationId, - request, - user - ); - return c.json(response); - } catch (error) { - console.error('Error in updateOrganizationHandler:', { - organizationId, - userId: user.id, - requestFields: Object.keys(request), - error: error instanceof Error ? error.message : error, - }); + const response: UpdateOrganizationResponse = await updateOrganizationHandler( + organizationId, + request, + user + ); - throw new HTTPException(500, { - message: 'Failed to update organization', - }); + return c.json(response); + }) + .onError((e, c) => { + // Handle Zod validation errors with detailed information + if (e instanceof z.ZodError) { + return c.json( + { + error: 'Validation Error', + message: 'Invalid request data', + issues: e.issues.map((issue) => ({ + path: issue.path.join('.'), + message: issue.message, + code: issue.code, + })), + }, + 400 + ); } + + // Handle HTTP exceptions + if (e instanceof HTTPException) { + return e.getResponse(); + } + + // Log unexpected errors + console.error('Unhandled error in organization PUT:', e); + + // Return generic error for unexpected issues + return c.json( + { + error: 'Internal Server Error', + message: 'Failed to update organization', + }, + 500 + ); }); export default app; diff --git a/packages/database/src/queries/organizations/update-organization.ts b/packages/database/src/queries/organizations/update-organization.ts index 8c47b24b1..a4bfad35e 100644 --- a/packages/database/src/queries/organizations/update-organization.ts +++ b/packages/database/src/queries/organizations/update-organization.ts @@ -4,16 +4,37 @@ import { db } from '../../connection'; import { organizations } from '../../schema'; import type { OrganizationColorPalettes } from '../../schema-types'; +// Hex color validation schema for 3 or 6 digit hex codes +const HexColorSchema = z + .string() + .regex( + /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/, + 'Must be a valid 3 or 6 digit hex color code (e.g., #fff or #ffffff)' + ); + // Organization Color Palette schema const OrganizationColorPaletteSchema = z.object({ - id: z.string(), - colors: z.array(z.string()), + id: z.string().or(z.number()), + colors: z.array(HexColorSchema), }); // Input validation schema const UpdateOrganizationInputSchema = z.object({ organizationId: z.string().uuid('Organization ID must be a valid UUID'), - organizationColorPalettes: z.array(OrganizationColorPaletteSchema).optional(), + organizationColorPalettes: z + .array(OrganizationColorPaletteSchema) + .optional() + .refine( + (palettes) => { + if (!palettes || palettes.length === 0) return true; + const ids = palettes.map((palette) => palette.id); + const uniqueIds = new Set(ids); + return ids.length === uniqueIds.size; + }, + { + message: 'All color palette IDs must be unique', + } + ), }); type UpdateOrganizationInput = z.infer; diff --git a/packages/database/src/schema-types/organization.ts b/packages/database/src/schema-types/organization.ts index 7882e2ea9..7c4dedfd1 100644 --- a/packages/database/src/schema-types/organization.ts +++ b/packages/database/src/schema-types/organization.ts @@ -1,7 +1,7 @@ // Organization Color Palette Types export type OrganizationColorPalette = { - id: string; - colors: string[]; + id: string | number; + colors: string[]; // Hex color codes }; export type OrganizationColorPalettes = OrganizationColorPalette[]; diff --git a/packages/server-shared/src/organization/organization.types.ts b/packages/server-shared/src/organization/organization.types.ts index 046f3a0ee..a92c24780 100644 --- a/packages/server-shared/src/organization/organization.types.ts +++ b/packages/server-shared/src/organization/organization.types.ts @@ -12,7 +12,7 @@ const HexColorSchema = z ); export const OrganizationColorPaletteSchema = z.object({ - id: z.string(), + id: z.string().or(z.number()), colors: z.array(HexColorSchema).min(1).max(25), });