hex values checking before insert into database

This commit is contained in:
Nate Kelley 2025-07-17 12:14:58 -06:00
parent ee8cce71f8
commit 7b24d167b9
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
4 changed files with 73 additions and 25 deletions

View File

@ -8,6 +8,7 @@ import { UpdateOrganizationRequestSchema } from '@buster/server-shared/organizat
import { zValidator } from '@hono/zod-validator'; import { zValidator } from '@hono/zod-validator';
import { Hono } from 'hono'; import { Hono } from 'hono';
import { HTTPException } from 'hono/http-exception'; import { HTTPException } from 'hono/http-exception';
import { z } from 'zod';
import { requireOrganizationAdmin } from '../../../middleware/auth'; import { requireOrganizationAdmin } from '../../../middleware/auth';
/** /**
@ -21,7 +22,6 @@ async function updateOrganizationHandler(
): Promise<UpdateOrganizationResponse> { ): Promise<UpdateOrganizationResponse> {
try { try {
// Update the organization // Update the organization
await updateOrganization({ await updateOrganization({
organizationId, organizationId,
...request, ...request,
@ -41,6 +41,11 @@ async function updateOrganizationHandler(
error: error instanceof Error ? error.message : error, 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, { throw new HTTPException(500, {
message: 'Failed to update organization', message: 'Failed to update organization',
}); });
@ -51,31 +56,53 @@ async function updateOrganizationHandler(
const app = new Hono() const app = new Hono()
.use('*', requireOrganizationAdmin) .use('*', requireOrganizationAdmin)
.put('/', zValidator('json', UpdateOrganizationRequestSchema), async (c) => { .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 user = c.get('busterUser');
const userOrg = c.get('userOrganizationInfo'); const userOrg = c.get('userOrganizationInfo');
const organizationId = userOrg.organizationId; const organizationId = userOrg.organizationId;
try {
const response: UpdateOrganizationResponse = await updateOrganizationHandler( const response: UpdateOrganizationResponse = await updateOrganizationHandler(
organizationId, organizationId,
request, request,
user 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,
});
throw new HTTPException(500, { return c.json(response);
message: 'Failed to update organization', })
}); .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; export default app;

View File

@ -4,16 +4,37 @@ import { db } from '../../connection';
import { organizations } from '../../schema'; import { organizations } from '../../schema';
import type { OrganizationColorPalettes } from '../../schema-types'; 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 // Organization Color Palette schema
const OrganizationColorPaletteSchema = z.object({ const OrganizationColorPaletteSchema = z.object({
id: z.string(), id: z.string().or(z.number()),
colors: z.array(z.string()), colors: z.array(HexColorSchema),
}); });
// Input validation schema // Input validation schema
const UpdateOrganizationInputSchema = z.object({ const UpdateOrganizationInputSchema = z.object({
organizationId: z.string().uuid('Organization ID must be a valid UUID'), 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<typeof UpdateOrganizationInputSchema>; type UpdateOrganizationInput = z.infer<typeof UpdateOrganizationInputSchema>;

View File

@ -1,7 +1,7 @@
// Organization Color Palette Types // Organization Color Palette Types
export type OrganizationColorPalette = { export type OrganizationColorPalette = {
id: string; id: string | number;
colors: string[]; colors: string[]; // Hex color codes
}; };
export type OrganizationColorPalettes = OrganizationColorPalette[]; export type OrganizationColorPalettes = OrganizationColorPalette[];

View File

@ -12,7 +12,7 @@ const HexColorSchema = z
); );
export const OrganizationColorPaletteSchema = z.object({ export const OrganizationColorPaletteSchema = z.object({
id: z.string(), id: z.string().or(z.number()),
colors: z.array(HexColorSchema).min(1).max(25), colors: z.array(HexColorSchema).min(1).max(25),
}); });