diff --git a/apps/api/src/modules/utility/utility.controller.ts b/apps/api/src/modules/utility/utility.controller.ts index 707beb4..69a03b1 100644 --- a/apps/api/src/modules/utility/utility.controller.ts +++ b/apps/api/src/modules/utility/utility.controller.ts @@ -46,4 +46,13 @@ export default class UtilityController { return reply.code(200).send(info); } + + public async getServerStatusHandler( + request: FastifyRequest, + reply: FastifyReply + ) { + const info = await this.utilityService.getServerStatus(); // Awaits for DB/Cache checks + + return reply.code(200).send(info); + } } diff --git a/apps/api/src/modules/utility/utility.route.ts b/apps/api/src/modules/utility/utility.route.ts index 982dadd..ef820d7 100644 --- a/apps/api/src/modules/utility/utility.route.ts +++ b/apps/api/src/modules/utility/utility.route.ts @@ -61,4 +61,18 @@ export default function UtilityRoute(fastify: FastifyInstance) { }, utilityController.getServerHardwareInfoHandler.bind(utilityController) ); + + fastify.get( + "/status", // "/server-status" to distinguish from API specific routes? + { + schema: { + tags: ["Utility"], + description: "Get the status of the server if it's running", + response: { + 200: $ref("getServerStatusResponseSchema"), + }, + }, + }, + utilityController.getServerStatusHandler.bind(utilityController) + ); } diff --git a/apps/api/src/modules/utility/utility.schema.ts b/apps/api/src/modules/utility/utility.schema.ts index fdcdf56..9a4ea08 100644 --- a/apps/api/src/modules/utility/utility.schema.ts +++ b/apps/api/src/modules/utility/utility.schema.ts @@ -31,6 +31,14 @@ const getServerHardwareInfoResponseSchema = z.object({ ), }); +const getServerStatusResponseSchema = z.object({ + environment: z.string(), + database: z.string(), + cache: z.string(), + time: z.string(), + uptime: z.number() +}); + const getApiUptimeResponseSchema = z.object({ uptime: z.number(), message: z.string(), @@ -43,11 +51,14 @@ export type GetServerHardwareInfoResponse = z.infer< typeof getServerHardwareInfoResponseSchema >; +export type GetServerStatusResponse = z.infer; + export const { schemas: utilitySchemas, $ref } = buildJsonSchemas( { getApiStatusResponseSchema, getServerHardwareInfoResponseSchema, getApiUptimeResponseSchema, + getServerStatusResponseSchema, } as const, { $id: "utilitySchema", diff --git a/apps/api/src/modules/utility/utility.service.ts b/apps/api/src/modules/utility/utility.service.ts index aa0a2a9..e30fd8e 100644 --- a/apps/api/src/modules/utility/utility.service.ts +++ b/apps/api/src/modules/utility/utility.service.ts @@ -1,4 +1,6 @@ import os from "node:os"; +import globalCacheDb from "@/db/redis/redis"; +import { checkConnection } from "@/db/postgres.db"; export default class UtilityService { public getApiUptime() { @@ -35,4 +37,21 @@ export default class UtilityService { version: process.version, }; } + + public async getServerStatus() { + + // Checking database / cache connections + const databaseStatus = await checkConnection() + .then(connected => connected ? 'connected' : 'disconnected') + .catch(() => 'disconnected'); + const cacheStatus = globalCacheDb.status; + + return { + environment: process.env.NODE_ENV || "local", + database: databaseStatus, + cache: cacheStatus, + time: Date.now(), + uptime: Math.floor(process.uptime()), + }; + } } diff --git a/apps/api/src/test/utility.test.ts b/apps/api/src/test/utility.test.ts index 4e887e0..c3a7397 100644 --- a/apps/api/src/test/utility.test.ts +++ b/apps/api/src/test/utility.test.ts @@ -4,6 +4,7 @@ import supertest from "supertest"; import type { GetApiStatusResponse, GetServerHardwareInfoResponse, + GetServerStatusResponse, } from "@/modules/utility/utility.schema"; import { startServer } from "../server"; @@ -51,6 +52,36 @@ test("Server Info", async () => { expect(response.body).toHaveProperty("cpus"); }); +test("Server Status", async () => { + const response: { body: GetServerStatusResponse } = await supertest( + app.server + ) + .get("/api/stable/utility/status") + .expect("Content-Type", "application/json; charset=utf-8") + .expect(200); + + // Check environment + expect(response.body).toHaveProperty("environment"); + expect(response.body.environment).toBeTypeOf("string"); + + // Check database status + expect(response.body).toHaveProperty("database"); + expect(response.body.database).toBeTypeOf("string"); + + // Check cache status + expect(response.body).toHaveProperty("cache"); + expect(response.body.cache).toBeTypeOf("string"); + + // Check server time + expect(response.body).toHaveProperty("time"); + expect(response.body.time).toBeTypeOf("string"); + + // Check uptime + expect(response.body).toHaveProperty("uptime"); + expect(response.body.uptime).toBeTypeOf("number"); + expect(response.body.uptime).toBeGreaterThan(0); +}); + afterAll(async () => { await app.close(); });