diff --git a/dbsync-api/src/health/healthCheck.ts b/dbsync-api/src/health/healthCheck.ts new file mode 100644 index 00000000..956f771e --- /dev/null +++ b/dbsync-api/src/health/healthCheck.ts @@ -0,0 +1,65 @@ +import { Request, Response, Router } from "express"; +import { prisma } from "../config/db"; +import { handlerWrapper } from "../errors/AppError"; + +const router = Router(); + +export const secondsSince = ( + dateIsoString: string, + inMilliseconds?: boolean +): number => { + const parsedDate = new Date(dateIsoString); + const currentDate = new Date(); + const timeDifferenceMs = currentDate.getTime() - parsedDate.getTime(); + return inMilliseconds ? timeDifferenceMs : timeDifferenceMs / 1000; +}; + +const getBlockInfo = async (req: Request, res: Response) => { + try { + const latestBlock = await prisma.block.findFirst({ + orderBy: { + time: "desc", + }, + }); + + if (!latestBlock) { + return res.status(500).json({ + message: + "Unable to retrieve the latest block information from the database.", + }); + } + + const latestBlockTime = latestBlock.time.toISOString(); + const timeDiff = secondsSince(latestBlockTime); + + if (timeDiff > 300) { + return res.status(503).json({ + status: "Service Unavailable", + details: { + currentTime: new Date().toISOString(), + latestBlockTime, + secondsSinceLastUpdate: timeDiff, + }, + }); + } + + return res.status(200).json({ + status: "Healthy", + details: { + currentTime: new Date().toISOString(), + latestBlockTime, + secondsSinceLastUpdate: timeDiff, + }, + }); + } catch (error: any) { + console.log(`Error performing health-check: ${error.message || error}`); + + return res.status(500).json({ + message: "An error occurred while checking the DbSync status.", + }); + } +}; + +router.get("/", handlerWrapper(getBlockInfo)); + +export default router; diff --git a/dbsync-api/src/index.ts b/dbsync-api/src/index.ts index d8fb6c84..091631df 100644 --- a/dbsync-api/src/index.ts +++ b/dbsync-api/src/index.ts @@ -6,6 +6,7 @@ import delegationRoute from "./controllers/delegation"; import drepRoute from "./controllers/drep"; import addressRoute from "./controllers/address"; import proposalRoute from "./controllers/proposal"; +import healthCheckRoute from "./health/healthCheck" import { errorHandler } from "./errors/AppError"; import path from "path"; import setupSwaggerUi from "./swagger-loader"; @@ -45,6 +46,7 @@ app.use('/api/stake-address', stakeAddrRoute); app.use('/api/drep',drepRoute) app.use('/api/address',addressRoute) app.use('/api/proposal',proposalRoute) +app.use('/api/health', healthCheckRoute) setupSwaggerUi(app) const indexFile = path.resolve('.','./index.html') diff --git a/dbsync-api/swagger.yaml b/dbsync-api/swagger.yaml index 3fec1248..5c80946c 100644 --- a/dbsync-api/swagger.yaml +++ b/dbsync-api/swagger.yaml @@ -481,4 +481,67 @@ paths: description: Invalid address provided '500': description: Internal server error - + /api/health: + get: + summary: Check DBSync health status + description: Retrieves the latest block and checks if it was updated within the last 5 minutes. + operationId: getBlockInfo + responses: + '200': + description: DBSync service is healthy. + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: "Healthy" + details: + type: object + properties: + currentTime: + type: string + format: date-time + example: "2024-12-03T12:34:56.789Z" + latestBlockTime: + type: string + format: date-time + example: "2024-12-03T12:30:00.123Z" + secondsSinceLastUpdate: + type: number + example: 296 + '503': + description: DBSync service is unavailable due to a delay in block updates. + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: "Service Unavailable" + details: + type: object + properties: + currentTime: + type: string + format: date-time + example: "2024-12-03T12:34:56.789Z" + latestBlockTime: + type: string + format: date-time + example: "2024-12-03T12:25:00.123Z" + secondsSinceLastUpdate: + type: number + example: 596 + '500': + description: An error occurred while performing the health check. + content: + application/json: + schema: + type: object + properties: + message: + type: string + example: "An error occurred while checking the DbSync status."