quality / commitlint (push) Skipped
CD / update-wiki (push) Failing after 8s
quality / changes (push) Successful in 5s
quality / docker-check (push) Skipped
quality / web (push) Successful in 50s
quality / api (push) Successful in 41s
CD / quality (push) Successful in 1m40s
CD / publish (push) Successful in 1m33s
- Introduced origin health check routes and integrated them into the application. - Updated health check configuration to include success recovery thresholds. - Expanded error handling with new error codes for health check failures. - Added new service routes for managing health checks, including creation and listing. - Improved health check service logic to track consecutive successes and failures. This commit enhances the health check capabilities, providing better monitoring and management of service health.
113 lines
3.0 KiB
TypeScript
113 lines
3.0 KiB
TypeScript
import { NotFoundError, ConflictError } from "@cfdm/db";
|
|
import { ValidationError } from "@cfdm/shared";
|
|
|
|
export type ErrorCode =
|
|
| "NOT_FOUND"
|
|
| "VALIDATION_ERROR"
|
|
| "UNAUTHORIZED"
|
|
| "FORBIDDEN"
|
|
| "CONFLICT"
|
|
| "CLOUDFLARE_ERROR"
|
|
| "DNS_UPDATE_FAILED"
|
|
| "HEALTHCHECK_CREATE_FAILED"
|
|
| "ZONE_NOT_FOUND"
|
|
| "INVALID_IP"
|
|
| "INVALID_HOSTNAME"
|
|
| "RATE_LIMITED"
|
|
| "CLOUDFLARE_AUTH_FAILED"
|
|
| "SYNC_FAILED"
|
|
| "INTERNAL_ERROR";
|
|
|
|
export class AppError extends Error {
|
|
constructor(
|
|
public readonly code: ErrorCode,
|
|
message: string,
|
|
public readonly statusCode: number,
|
|
) {
|
|
super(message);
|
|
this.name = "AppError";
|
|
}
|
|
|
|
static notFound(message: string) {
|
|
return new AppError("NOT_FOUND", message, 404);
|
|
}
|
|
|
|
static validation(message: string) {
|
|
return new AppError("VALIDATION_ERROR", message, 400);
|
|
}
|
|
|
|
static unauthorized() {
|
|
return new AppError("UNAUTHORIZED", "unauthorized", 401);
|
|
}
|
|
|
|
static forbidden(message = "forbidden") {
|
|
return new AppError("FORBIDDEN", message, 403);
|
|
}
|
|
|
|
static conflict(message: string) {
|
|
return new AppError("CONFLICT", message, 409);
|
|
}
|
|
|
|
static cloudflare(message: string) {
|
|
return new AppError("CLOUDFLARE_ERROR", message, 502);
|
|
}
|
|
|
|
static dnsUpdateFailed(message: string) {
|
|
return new AppError(
|
|
"DNS_UPDATE_FAILED",
|
|
message,
|
|
502,
|
|
);
|
|
}
|
|
|
|
static healthcheckCreateFailed(message: string) {
|
|
return new AppError("HEALTHCHECK_CREATE_FAILED", message, 502);
|
|
}
|
|
|
|
static zoneNotFound(message = "зона Cloudflare не найдена") {
|
|
return new AppError("ZONE_NOT_FOUND", message, 404);
|
|
}
|
|
|
|
static invalidIp(message = "Некорректный IP-адрес") {
|
|
return new AppError("INVALID_IP", message, 400);
|
|
}
|
|
|
|
static invalidHostname(message = "Некорректное имя хоста") {
|
|
return new AppError("INVALID_HOSTNAME", message, 400);
|
|
}
|
|
|
|
static rateLimited(message = "Cloudflare временно ограничил запросы. Повторите попытку.") {
|
|
return new AppError("RATE_LIMITED", message, 429);
|
|
}
|
|
|
|
static cloudflareAuthFailed(message = "Cloudflare отклонил токен доступа") {
|
|
return new AppError("CLOUDFLARE_AUTH_FAILED", message, 401);
|
|
}
|
|
|
|
static syncFailed(message: string) {
|
|
return new AppError("SYNC_FAILED", message, 502);
|
|
}
|
|
|
|
static internal(message: string) {
|
|
return new AppError("INTERNAL_ERROR", message, 500);
|
|
}
|
|
}
|
|
|
|
export function toAppError(err: unknown): AppError {
|
|
if (err instanceof AppError) return err;
|
|
if (err instanceof NotFoundError) return AppError.notFound(err.message);
|
|
if (err instanceof ConflictError) return AppError.conflict(err.message);
|
|
if (err instanceof ValidationError) return AppError.validation(err.message);
|
|
if (err instanceof Error) return AppError.internal(err.message);
|
|
return AppError.internal(String(err));
|
|
}
|
|
|
|
export function errorBody(err: AppError) {
|
|
return {
|
|
error: {
|
|
code: err.code,
|
|
message: err.message,
|
|
},
|
|
};
|
|
}
|