mirror of https://github.com/buster-so/buster.git
Add additional error handling for endpoint
This commit is contained in:
parent
e55632e074
commit
ee8cce71f8
|
@ -0,0 +1,16 @@
|
|||
import { getOrganization } from '@buster/database';
|
||||
import type { GetOrganizationResponse } from '@buster/server-shared/organization';
|
||||
import { Hono } from 'hono';
|
||||
import { requireOrganization } from '../../../middleware/auth';
|
||||
|
||||
const app = new Hono().use('*', requireOrganization).get('/', async (c) => {
|
||||
const userOrg = c.get('userOrganizationInfo');
|
||||
|
||||
const organization: GetOrganizationResponse = await getOrganization({
|
||||
organizationId: userOrg.organizationId,
|
||||
});
|
||||
|
||||
return c.json(organization);
|
||||
});
|
||||
|
||||
export default app;
|
|
@ -14,7 +14,7 @@ import { requireOrganizationAdmin } from '../../../middleware/auth';
|
|||
* Updates organization settings
|
||||
* Currently supports updating organization color palettes
|
||||
*/
|
||||
export async function updateOrganizationHandler(
|
||||
async function updateOrganizationHandler(
|
||||
organizationId: string,
|
||||
request: UpdateOrganizationRequest,
|
||||
user: User
|
||||
|
@ -48,32 +48,34 @@ export async function updateOrganizationHandler(
|
|||
}
|
||||
|
||||
// Create route module for the update endpoint
|
||||
const app = new Hono().put(
|
||||
'/',
|
||||
requireOrganizationAdmin,
|
||||
zValidator('json', UpdateOrganizationRequestSchema),
|
||||
async (c) => {
|
||||
const request = c.req.valid('json');
|
||||
const app = new Hono()
|
||||
.use('*', requireOrganizationAdmin)
|
||||
.put('/', zValidator('json', UpdateOrganizationRequestSchema), async (c) => {
|
||||
const request = await c.req.valid('json');
|
||||
const user = c.get('busterUser');
|
||||
const userOrg = c.get('userOrganizationInfo');
|
||||
|
||||
const organizationId = userOrg.organizationId;
|
||||
//const role = userOrg.role;
|
||||
|
||||
// if (!canEditOrganization(role)) {
|
||||
// throw new HTTPException(403, {
|
||||
// message: 'User does not have permission to edit organization',
|
||||
// });
|
||||
// }
|
||||
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
|
||||
);
|
||||
|
||||
return c.json(response);
|
||||
}
|
||||
);
|
||||
throw new HTTPException(500, {
|
||||
message: 'Failed to update organization',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { Hono } from 'hono';
|
||||
import { requireAuth } from '../../../middleware/auth';
|
||||
import GET from './GET';
|
||||
import PUT from './PUT';
|
||||
|
||||
const app = new Hono()
|
||||
// Apply authentication globally to ALL routes in this router
|
||||
.use('*', requireAuth)
|
||||
// Mount the modular routes
|
||||
.route('/', GET)
|
||||
.route('/', PUT);
|
||||
|
||||
export default app;
|
||||
|
|
|
@ -97,7 +97,8 @@ export async function requireUser(c: Context, next: Next) {
|
|||
}
|
||||
}
|
||||
|
||||
export const requireOrganization = async (c: Context) => {
|
||||
// Utility function to get user organization (can be called from other middleware)
|
||||
const getUserOrganization = async (c: Context) => {
|
||||
const user = c.get('busterUser');
|
||||
const userOrganizationInfo = c.get('userOrganizationInfo');
|
||||
|
||||
|
@ -116,28 +117,28 @@ export const requireOrganization = async (c: Context) => {
|
|||
return userOrg;
|
||||
};
|
||||
|
||||
export const requireOrganizationAdmin = async (c: Context) => {
|
||||
// Middleware version that can be used in route chains
|
||||
export const requireOrganization = async (c: Context, next: Next) => {
|
||||
await getUserOrganization(c);
|
||||
await next();
|
||||
};
|
||||
|
||||
export const requireOrganizationAdmin = async (c: Context, next: Next) => {
|
||||
const user = c.get('busterUser');
|
||||
|
||||
if (!user?.id) {
|
||||
console.warn('This is likely an issue where requireAuth middleware was not called first');
|
||||
return c.json({
|
||||
message: 'User not authenticated',
|
||||
});
|
||||
return c.json({ message: 'User not authenticated' }, 401);
|
||||
}
|
||||
|
||||
const userOrg = await requireOrganization(c);
|
||||
const userOrg = await getUserOrganization(c);
|
||||
|
||||
const isAdmin = isOrganizationAdmin(userOrg.role);
|
||||
|
||||
if (!isAdmin) {
|
||||
return c.json(
|
||||
{
|
||||
message: 'User is not an organization admin',
|
||||
},
|
||||
403
|
||||
);
|
||||
return c.json({ message: 'User is not an organization admin' }, 403);
|
||||
}
|
||||
|
||||
return true;
|
||||
// If all checks pass, continue to the next middleware/handler
|
||||
return await next();
|
||||
};
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { type InferSelectModel, and, eq, isNull } from 'drizzle-orm';
|
||||
import { and, eq, isNull } from 'drizzle-orm';
|
||||
import { z } from 'zod';
|
||||
import { db } from '../../connection';
|
||||
import { organizations } from '../../schema';
|
||||
import { getUserOrganizationId } from './organizations';
|
||||
import type { OrganizationColorPalettes } from '../../schema-types';
|
||||
|
||||
// Organization Color Palette schema
|
||||
const OrganizationColorPaletteSchema = z.object({
|
||||
id: z.string(),
|
||||
color: z.array(z.string()),
|
||||
colors: z.array(z.string()),
|
||||
});
|
||||
|
||||
// Input validation schema
|
||||
|
@ -30,7 +30,7 @@ export const updateOrganization = async (params: UpdateOrganizationInput): Promi
|
|||
// Build update data
|
||||
const updateData: {
|
||||
updatedAt: string;
|
||||
organizationColorPalettes?: Array<{ id: string; color: string[] }>;
|
||||
organizationColorPalettes?: OrganizationColorPalettes;
|
||||
} = {
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
@ -44,12 +44,6 @@ export const updateOrganization = async (params: UpdateOrganizationInput): Promi
|
|||
.update(organizations)
|
||||
.set(updateData)
|
||||
.where(and(eq(organizations.id, organizationId), isNull(organizations.deletedAt)));
|
||||
|
||||
console.info('Organization updated successfully:', {
|
||||
organizationId,
|
||||
|
||||
updatedFields: organizationColorPalettes !== undefined ? ['organizationColorPalettes'] : [],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error updating organization:', {
|
||||
organizationId,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Organization Color Palette Types
|
||||
export type OrganizationColorPalette = {
|
||||
id: string;
|
||||
color: string[];
|
||||
colors: string[];
|
||||
};
|
||||
|
||||
export type OrganizationColorPalettes = OrganizationColorPalette[];
|
||||
|
|
|
@ -35,4 +35,14 @@ This structure ensures a consistent and organized approach to managing types acr
|
|||
|
||||
### Pagination
|
||||
|
||||
If we need to use pagination for a type we can take advantage of the generic types found in the type-utilities/pagination file
|
||||
If we need to use pagination for a type we can take advantage of the generic types found in the type-utilities/pagination file
|
||||
|
||||
|
||||
## Database partity
|
||||
There are cirumstances where we need to ensure that a schema in server-shared and a database type are the same. We should use a pattern like this:
|
||||
|
||||
```
|
||||
type _OrganizationEqualityCheck = Expect<Equal<Organization, typeof organizations.$inferSelect>>;
|
||||
```
|
||||
|
||||
Where organizations is imported as a "type" from @buster/database. This ensure that we are maintaining type parity between the two packages.
|
|
@ -3,9 +3,17 @@ import { z } from 'zod';
|
|||
import type { Equal, Expect } from '../type-utilities';
|
||||
import { OrganizationRoleSchema } from './roles.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)'
|
||||
);
|
||||
|
||||
export const OrganizationColorPaletteSchema = z.object({
|
||||
id: z.string(),
|
||||
color: z.array(z.string()),
|
||||
colors: z.array(HexColorSchema).min(1).max(25),
|
||||
});
|
||||
|
||||
export const OrganizationSchema = z.object({
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import type { z } from 'zod';
|
||||
import { OrganizationSchema } from './organization.types';
|
||||
|
||||
export const GetOrganizationResponseSchema = OrganizationSchema;
|
||||
|
||||
export type GetOrganizationResponse = z.infer<typeof GetOrganizationResponseSchema>;
|
||||
|
||||
export const UpdateOrganizationResponseSchema = OrganizationSchema;
|
||||
|
||||
export type UpdateOrganizationResponse = z.infer<typeof UpdateOrganizationResponseSchema>;
|
||||
|
|
Loading…
Reference in New Issue