mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 03:46:42 +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
|
* Domain errors — caught by `serviceErrorHandler` from @mana/shared-hono.
|
||||||
* and translated to JSON responses with the right status code.
|
*
|
||||||
|
* 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 {
|
import { HTTPException } from 'hono/http-exception';
|
||||||
constructor(
|
import type { ContentfulStatusCode } from 'hono/utils/http-status';
|
||||||
public status: number,
|
|
||||||
message: string,
|
function makeException(
|
||||||
public code?: string,
|
status: ContentfulStatusCode,
|
||||||
public details?: unknown
|
message: string,
|
||||||
) {
|
code?: string,
|
||||||
super(message);
|
details?: unknown
|
||||||
this.name = 'HttpError';
|
) {
|
||||||
}
|
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') {
|
constructor(message = 'Unauthorized') {
|
||||||
super(401, message, 'unauthorized');
|
super(401, { message, cause: { code: 'unauthorized' } });
|
||||||
this.name = 'UnauthorizedError';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ForbiddenError extends HttpError {
|
export class ForbiddenError extends HTTPException {
|
||||||
constructor(message = 'Forbidden') {
|
constructor(message = 'Forbidden') {
|
||||||
super(403, message, 'forbidden');
|
super(403, { message, cause: { code: 'forbidden' } });
|
||||||
this.name = 'ForbiddenError';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NotFoundError extends HttpError {
|
export class NotFoundError extends HTTPException {
|
||||||
constructor(message = 'Not found') {
|
constructor(message = 'Not found') {
|
||||||
super(404, message, 'not_found');
|
super(404, { message, cause: { code: 'not_found' } });
|
||||||
this.name = 'NotFoundError';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConflictError extends HttpError {
|
export class ConflictError extends HTTPException {
|
||||||
constructor(message = 'Conflict') {
|
constructor(message = 'Conflict') {
|
||||||
super(409, message, 'conflict');
|
super(409, { message, cause: { code: 'conflict' } });
|
||||||
this.name = 'ConflictError';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BadRequestError extends HttpError {
|
export class BadRequestError extends HTTPException {
|
||||||
constructor(message = 'Bad request', details?: unknown) {
|
constructor(message = 'Bad request', details?: unknown) {
|
||||||
super(400, message, 'bad_request', details);
|
super(400, {
|
||||||
this.name = 'BadRequestError';
|
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