Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/api/src/modules/utility/utility.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
14 changes: 14 additions & 0 deletions apps/api/src/modules/utility/utility.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
}
11 changes: 11 additions & 0 deletions apps/api/src/modules/utility/utility.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -43,11 +51,14 @@ export type GetServerHardwareInfoResponse = z.infer<
typeof getServerHardwareInfoResponseSchema
>;

export type GetServerStatusResponse = z.infer<typeof getServerStatusResponseSchema>;

export const { schemas: utilitySchemas, $ref } = buildJsonSchemas(
{
getApiStatusResponseSchema,
getServerHardwareInfoResponseSchema,
getApiUptimeResponseSchema,
getServerStatusResponseSchema,
} as const,
{
$id: "utilitySchema",
Expand Down
23 changes: 23 additions & 0 deletions apps/api/src/modules/utility/utility.service.ts
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down Expand Up @@ -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')
Comment thread
Vonglory176 marked this conversation as resolved.
Outdated
]);

return {
environment: process.env.NODE_ENV || "local",
database: databaseStatus,
cache: cacheStatus,
time: new Date().toISOString(),
uptime: Math.floor(process.uptime()),
};
}
}
32 changes: 32 additions & 0 deletions apps/api/src/test/utility.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import supertest from "supertest";
import type {
GetApiStatusResponse,
GetServerHardwareInfoResponse,
GetServerStatusResponse,
} from "@/modules/utility/utility.schema";
import { startServer } from "../server";

Expand Down Expand Up @@ -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")
Comment thread
Vonglory176 marked this conversation as resolved.
Outdated
.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
Comment thread
fazlulShanto marked this conversation as resolved.
Outdated

// Check uptime
expect(response.body).toHaveProperty("uptime");
expect(response.body.uptime).toBeTypeOf("number");
expect(response.body.uptime).toBeGreaterThan(0);
});

afterAll(async () => {
await app.close();
});