2025-02-01 21:48:13 +00:00
|
|
|
import { NextFunction, Request, Response } from "express";
|
|
|
|
|
import httpStatus from "http-status";
|
2025-02-03 13:12:36 +00:00
|
|
|
import { ApiError } from "./api-error";
|
2025-02-01 21:48:13 +00:00
|
|
|
|
|
|
|
|
export abstract class ExpressController {
|
|
|
|
|
protected req!: Request;
|
|
|
|
|
protected res!: Response;
|
|
|
|
|
protected next!: NextFunction;
|
|
|
|
|
|
|
|
|
|
protected abstract executeImpl(): Promise<void | any>;
|
|
|
|
|
|
|
|
|
|
protected ok<T>(dto?: T) {
|
|
|
|
|
return dto ? this.res.status(httpStatus.OK).json(dto) : this.res.status(httpStatus.OK).send();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected created<T>(dto?: T) {
|
|
|
|
|
return dto
|
|
|
|
|
? this.res.status(httpStatus.CREATED).json(dto)
|
|
|
|
|
: this.res.status(httpStatus.CREATED).send();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected noContent() {
|
|
|
|
|
return this.res.status(httpStatus.NO_CONTENT).send();
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-03 13:12:36 +00:00
|
|
|
protected errorResponse(apiError: ApiError) {
|
|
|
|
|
return this.res.status(apiError.status).json(apiError);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 🔹 Respuesta para errores de cliente (400 Bad Request)
|
|
|
|
|
*/
|
|
|
|
|
protected clientError(message: string, errors?: any[]) {
|
|
|
|
|
return this.errorResponse(
|
|
|
|
|
new ApiError({
|
|
|
|
|
status: 400,
|
|
|
|
|
title: "Bad Request",
|
|
|
|
|
detail: message,
|
|
|
|
|
errors,
|
|
|
|
|
})
|
|
|
|
|
);
|
2025-02-01 21:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
2025-02-03 13:12:36 +00:00
|
|
|
/**
|
|
|
|
|
* 🔹 Respuesta para errores de autenticación (401 Unauthorized)
|
|
|
|
|
*/
|
2025-02-01 21:48:13 +00:00
|
|
|
protected unauthorizedError(message?: string) {
|
2025-02-03 13:12:36 +00:00
|
|
|
return this.errorResponse(
|
|
|
|
|
new ApiError({
|
|
|
|
|
status: 401,
|
|
|
|
|
title: "Unauthorized",
|
|
|
|
|
detail: message ?? "You are not authorized to access this resource.",
|
|
|
|
|
})
|
|
|
|
|
);
|
2025-02-01 21:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
2025-02-03 13:12:36 +00:00
|
|
|
/**
|
|
|
|
|
* 🔹 Respuesta para errores de autorización (403 Forbidden)
|
|
|
|
|
*/
|
2025-02-01 21:48:13 +00:00
|
|
|
protected forbiddenError(message?: string) {
|
2025-02-03 13:12:36 +00:00
|
|
|
return this.errorResponse(
|
|
|
|
|
new ApiError({
|
|
|
|
|
status: 403,
|
|
|
|
|
title: "Forbidden",
|
|
|
|
|
detail: message ?? "You do not have permission to perform this action.",
|
|
|
|
|
})
|
|
|
|
|
);
|
2025-02-01 21:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
2025-02-03 13:12:36 +00:00
|
|
|
/**
|
|
|
|
|
* 🔹 Respuesta para recursos no encontrados (404 Not Found)
|
|
|
|
|
*/
|
|
|
|
|
protected notFoundError(message: string) {
|
|
|
|
|
return this.errorResponse(
|
|
|
|
|
new ApiError({
|
|
|
|
|
status: 404,
|
|
|
|
|
title: "Not Found",
|
|
|
|
|
detail: message,
|
|
|
|
|
})
|
|
|
|
|
);
|
2025-02-01 21:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
2025-02-03 13:12:36 +00:00
|
|
|
/**
|
|
|
|
|
* 🔹 Respuesta para conflictos (409 Conflict)
|
|
|
|
|
*/
|
|
|
|
|
protected conflictError(message: string, errors?: any[]) {
|
|
|
|
|
return this.errorResponse(
|
|
|
|
|
new ApiError({
|
|
|
|
|
status: 409,
|
|
|
|
|
title: "Conflict",
|
|
|
|
|
detail: message,
|
|
|
|
|
errors,
|
|
|
|
|
})
|
|
|
|
|
);
|
2025-02-01 21:48:13 +00:00
|
|
|
}
|
|
|
|
|
|
2025-02-03 13:12:36 +00:00
|
|
|
/**
|
|
|
|
|
* 🔹 Respuesta para errores de validación de entrada (422 Unprocessable Entity)
|
|
|
|
|
*/
|
|
|
|
|
protected invalidInputError(message: string, errors?: any[]) {
|
|
|
|
|
return this.errorResponse(
|
|
|
|
|
new ApiError({
|
|
|
|
|
status: 422,
|
|
|
|
|
title: "Invalid Input",
|
|
|
|
|
detail: message,
|
|
|
|
|
errors,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Respuesta para errores de servidor no disponible (503 Service Unavailable)
|
|
|
|
|
* @param message
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
|
|
|
|
protected unavailableError(message?: string) {
|
|
|
|
|
return this.errorResponse(
|
|
|
|
|
new ApiError({
|
|
|
|
|
status: 503,
|
|
|
|
|
title: "Service Unavailable",
|
|
|
|
|
detail: message ?? "The service is currently unavailable. Please try again later.",
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 🔹 Respuesta para errores internos del servidor (500 Internal Server Error)
|
|
|
|
|
*/
|
|
|
|
|
protected internalServerError(message?: string) {
|
|
|
|
|
return this.errorResponse(
|
|
|
|
|
new ApiError({
|
|
|
|
|
status: 500,
|
|
|
|
|
title: "Internal Server Error",
|
|
|
|
|
detail: message ?? "An unexpected error occurred. Please try again later.",
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public execute(req: Request, res: Response, next: NextFunction): void {
|
|
|
|
|
this.req = req;
|
|
|
|
|
this.res = res;
|
|
|
|
|
this.next = next;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
this.executeImpl();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
this.internalServerError((error as Error).message);
|
|
|
|
|
}
|
2025-02-01 21:48:13 +00:00
|
|
|
}
|
|
|
|
|
}
|