From 06339a5593c19ab390b7ff5069562215638c7c35 Mon Sep 17 00:00:00 2001 From: Vonglory176 Date: Wed, 19 Feb 2025 14:10:53 -0800 Subject: [PATCH 1/6] Completed "/status" route logic --- .../src/modules/utility/utility.controller.ts | 9 ++++++++ apps/api/src/modules/utility/utility.route.ts | 14 +++++++++++ .../api/src/modules/utility/utility.schema.ts | 11 +++++++++ .../src/modules/utility/utility.service.ts | 23 +++++++++++++++++++ 4 files changed, 57 insertions(+) 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..4d0901c 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,25 @@ export default class UtilityService { version: process.version, }; } + + public async getServerStatus() { + + // Checking database / cache connections + const [databaseStatus, cacheStatus] = await Promise.all([ + checkConnection() + .then(connected => connected ? 'connected' : 'disconnected') + .catch(() => 'disconnected'), + globalCacheDb.ping() + .then(response => response === 'PONG' ? 'connected' : 'disconnected') // "PONG" for Redis ping response + .catch(() => 'disconnected') + ]); + + return { + environment: process.env.NODE_ENV || "local", + database: databaseStatus, + cache: cacheStatus, + time: new Date().toISOString(), + uptime: Math.floor(process.uptime()), + }; + } } From 20dd553e5f85d4d5f8d80a5db06eaa859d295d68 Mon Sep 17 00:00:00 2001 From: Vonglory176 Date: Wed, 19 Feb 2025 14:28:28 -0800 Subject: [PATCH 2/6] Created tests for "Server Status" --- apps/api/src/test/utility.test.ts | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/apps/api/src/test/utility.test.ts b/apps/api/src/test/utility.test.ts index 4e887e0..29d5889 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,37 @@ 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/server-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"); + expect(response.body.time).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/); // ISO 8601 format + + // Check uptime + expect(response.body).toHaveProperty("uptime"); + expect(response.body.uptime).toBeTypeOf("number"); + expect(response.body.uptime).toBeGreaterThan(0); +}); + afterAll(async () => { await app.close(); }); From fce299d9f8b79e323fd51ee97544bf466e6d6eb2 Mon Sep 17 00:00:00 2001 From: Vonglory176 Date: Mon, 24 Feb 2025 11:49:01 -0800 Subject: [PATCH 3/6] Updated "getServerStatus" Cache & Time logic --- apps/api/src/modules/utility/utility.service.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/api/src/modules/utility/utility.service.ts b/apps/api/src/modules/utility/utility.service.ts index 4d0901c..e30fd8e 100644 --- a/apps/api/src/modules/utility/utility.service.ts +++ b/apps/api/src/modules/utility/utility.service.ts @@ -41,20 +41,16 @@ export default class UtilityService { public async getServerStatus() { // Checking database / cache connections - const [databaseStatus, cacheStatus] = await Promise.all([ - checkConnection() - .then(connected => connected ? 'connected' : 'disconnected') - .catch(() => 'disconnected'), - globalCacheDb.ping() - .then(response => response === 'PONG' ? 'connected' : 'disconnected') // "PONG" for Redis ping response - .catch(() => 'disconnected') - ]); + 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: new Date().toISOString(), + time: Date.now(), uptime: Math.floor(process.uptime()), }; } From 57341a6abf455ed55fa637c936f2a35c295a7630 Mon Sep 17 00:00:00 2001 From: Vonglory176 Date: Mon, 24 Feb 2025 11:50:12 -0800 Subject: [PATCH 4/6] Updated "Server Status" API route typo & "time" tests after ISO change --- apps/api/src/test/utility.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/api/src/test/utility.test.ts b/apps/api/src/test/utility.test.ts index 29d5889..231e28d 100644 --- a/apps/api/src/test/utility.test.ts +++ b/apps/api/src/test/utility.test.ts @@ -56,7 +56,7 @@ test("Server Status", async () => { const response: { body: GetServerStatusResponse } = await supertest( app.server ) - .get("/api/stable/utility/server-status") + .get("/api/stable/utility/status") .expect("Content-Type", "application/json; charset=utf-8") .expect(200); @@ -74,8 +74,7 @@ test("Server Status", async () => { // Check server time expect(response.body).toHaveProperty("time"); - expect(response.body.time).toBeTypeOf("string"); - expect(response.body.time).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/); // ISO 8601 format + expect(response.body.time).toBeTypeOf("number"); // Check uptime expect(response.body).toHaveProperty("uptime"); From 2dd876fea9809920a7672728a06066e87b3bd4c4 Mon Sep 17 00:00:00 2001 From: Vonglory176 Date: Mon, 24 Feb 2025 21:01:44 -0800 Subject: [PATCH 5/6] Changed time type check in "Server Status" from number to string --- apps/api/src/test/utility.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/test/utility.test.ts b/apps/api/src/test/utility.test.ts index 231e28d..c3a7397 100644 --- a/apps/api/src/test/utility.test.ts +++ b/apps/api/src/test/utility.test.ts @@ -74,7 +74,7 @@ test("Server Status", async () => { // Check server time expect(response.body).toHaveProperty("time"); - expect(response.body.time).toBeTypeOf("number"); + expect(response.body.time).toBeTypeOf("string"); // Check uptime expect(response.body).toHaveProperty("uptime"); From 508e17fef85281cf9a1eb88a26cb0ca0ea1fff2b Mon Sep 17 00:00:00 2001 From: Vonglory176 Date: Wed, 19 Feb 2025 14:10:53 -0800 Subject: [PATCH 6/6] Completed "/status" route logic --- .../src/modules/utility/utility.controller.ts | 9 ++++++ apps/api/src/modules/utility/utility.route.ts | 14 +++++++++ .../api/src/modules/utility/utility.schema.ts | 11 +++++++ .../src/modules/utility/utility.service.ts | 19 ++++++++++++ apps/api/src/test/utility.test.ts | 31 +++++++++++++++++++ 5 files changed, 84 insertions(+) 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(); });