mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 17:41:09 +02:00
fix(cards-server): error classes extend Hono HTTPException
Shared-hono's serviceErrorHandler only translates HTTPException instances; anything else degrades to 500. Our custom Error subclasses were silently bypassing the translation layer, so a missing JWT came back as `500 Internal server error` instead of the expected `401 Unauthorized`. Confirmed in prod logs after the Phase-β deploy. Switching the error hierarchy to extend HTTPException directly. The JSON body now carries the right status code + the existing `cause` object surfaces our `code` discriminator + zod-style `details` for BadRequest. No call-site changes needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
044d948155
commit
be155ca737
1 changed files with 39 additions and 27 deletions
|
|
@ -1,51 +1,63 @@
|
|||
/**
|
||||
* Domain errors — caught by serviceErrorHandler from @mana/shared-hono
|
||||
* and translated to JSON responses with the right status code.
|
||||
* Domain errors — caught by `serviceErrorHandler` from @mana/shared-hono.
|
||||
*
|
||||
* The shared handler only translates Hono `HTTPException`s; anything
|
||||
* else degrades to 500. So our errors extend HTTPException directly
|
||||
* rather than maintaining a parallel hierarchy.
|
||||
*
|
||||
* `details` (e.g. zod issue tree) is passed via `cause` because the
|
||||
* shared handler picks that up and surfaces it in the JSON body.
|
||||
*/
|
||||
|
||||
export class HttpError extends Error {
|
||||
constructor(
|
||||
public status: number,
|
||||
message: string,
|
||||
public code?: string,
|
||||
public details?: unknown
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'HttpError';
|
||||
}
|
||||
import { HTTPException } from 'hono/http-exception';
|
||||
import type { ContentfulStatusCode } from 'hono/utils/http-status';
|
||||
|
||||
function makeException(
|
||||
status: ContentfulStatusCode,
|
||||
message: string,
|
||||
code?: string,
|
||||
details?: unknown
|
||||
) {
|
||||
return new HTTPException(status, {
|
||||
message,
|
||||
cause: details ? { code, details } : code ? { code } : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
export class UnauthorizedError extends HttpError {
|
||||
export class HttpError extends HTTPException {}
|
||||
|
||||
export class UnauthorizedError extends HTTPException {
|
||||
constructor(message = 'Unauthorized') {
|
||||
super(401, message, 'unauthorized');
|
||||
this.name = 'UnauthorizedError';
|
||||
super(401, { message, cause: { code: 'unauthorized' } });
|
||||
}
|
||||
}
|
||||
|
||||
export class ForbiddenError extends HttpError {
|
||||
export class ForbiddenError extends HTTPException {
|
||||
constructor(message = 'Forbidden') {
|
||||
super(403, message, 'forbidden');
|
||||
this.name = 'ForbiddenError';
|
||||
super(403, { message, cause: { code: 'forbidden' } });
|
||||
}
|
||||
}
|
||||
|
||||
export class NotFoundError extends HttpError {
|
||||
export class NotFoundError extends HTTPException {
|
||||
constructor(message = 'Not found') {
|
||||
super(404, message, 'not_found');
|
||||
this.name = 'NotFoundError';
|
||||
super(404, { message, cause: { code: 'not_found' } });
|
||||
}
|
||||
}
|
||||
|
||||
export class ConflictError extends HttpError {
|
||||
export class ConflictError extends HTTPException {
|
||||
constructor(message = 'Conflict') {
|
||||
super(409, message, 'conflict');
|
||||
this.name = 'ConflictError';
|
||||
super(409, { message, cause: { code: 'conflict' } });
|
||||
}
|
||||
}
|
||||
|
||||
export class BadRequestError extends HttpError {
|
||||
export class BadRequestError extends HTTPException {
|
||||
constructor(message = 'Bad request', details?: unknown) {
|
||||
super(400, message, 'bad_request', details);
|
||||
this.name = 'BadRequestError';
|
||||
super(400, {
|
||||
message,
|
||||
cause: details ? { code: 'bad_request', details } : { code: 'bad_request' },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Keep makeException exported in case future code wants the raw factory.
|
||||
export { makeException };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue