Skip to content

Commit

Permalink
Issue- DiceDB#26 Add Feature to Show DiceDB Server Connection Status …
Browse files Browse the repository at this point in the history
…On Playground.
  • Loading branch information
pawansoni007 committed Oct 4, 2024
1 parent 572ca60 commit 66cdaf7
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 14 deletions.
6 changes: 4 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// types
import { ReactNode } from 'react';
import type { Metadata } from 'next';

import { ServerStatusProvider } from '@/components/ServerStatus/context/ServerContext';
// styles
import '@/styles/globals.css';

Expand All @@ -21,7 +21,9 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body>{children}</body>
<ServerStatusProvider>
<body>{children}</body>
</ServerStatusProvider>
</html>
);
}
26 changes: 15 additions & 11 deletions src/components/CLI/hooks/useCli.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useState, useEffect, useRef, KeyboardEvent, ChangeEvent } from 'react';
// utils
import { handleCommand } from '@/shared/utils/cliUtils';
import blacklistedCommands from '@/shared/utils/blacklist'; // Assuming you added blacklist here
import { useServerStatus } from '@/components/ServerStatus/context/ServerContext';

export const useCli = (decreaseCommandsLeft: () => void) => {
// states
Expand All @@ -13,22 +14,25 @@ export const useCli = (decreaseCommandsLeft: () => void) => {
//Initialise the command history with sessionStorage
const [commandHistory, setCommandHistory] = useState<string[]>([]);
const [historyIndex, setHistoryIndex] = useState<number>(-1);

const { checkServerStatus } = useServerStatus();

// useRefs
const terminalRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);

const handleCommandWrapper = () => {
const commandName = command.trim().split(' ')[0].toUpperCase(); // Extract the command
const handleCommandWrapper = async (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
const commandName = command.trim().split(' ')[0].toUpperCase(); // Extract the command

if (blacklistedCommands.includes(commandName)) {
setOutput((prevOutput) => [
...prevOutput,
`(error) ERR unknown command '${commandName}'`,
]);
} else {
handleCommand({ command, setOutput }); // Execute if not blacklisted
}
if (blacklistedCommands.includes(commandName)) {
setOutput((prevOutput) => [
...prevOutput,
`(error) ERR unknown command '${commandName}'`,
]);
} else {
await checkServerStatus();
handleCommand({ command, setOutput }); // Execute if not blacklisted
}

setCommand(''); // Clear input
decreaseCommandsLeft(); // Call to update remaining commands
Expand Down
4 changes: 3 additions & 1 deletion src/components/Playground/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { formatTime } from '@/shared/utils/commonUtils';
// images and icons
import Image from 'next/image';
import { usePlayground } from './hooks/usePlayground';
import TerminalStatus from '../ServerStatus/TerminalStatus';

export default function Playground() {
const { decreaseCommandsLeft, search, timeLeft, commandsLeft, setSearch } =
Expand All @@ -35,12 +36,13 @@ export default function Playground() {
<main className="flex flex-col lg:flex-row gap-10 flex-grow overflow-hidden px-4">
<div className="w-full lg:w-1/2 flex flex-col">
<div className="bg-gray-900 rounded-lg">
<div className="bg-gray-900 px-4 py-4 flex items-center rounded-lg">
<div className="bg-gray-900 px-4 py-4 flex items-center rounded-lg justify-between">
<div className="flex space-x-2">
<Dice5 className="w-4 h-4 bg-red-500" />
<Dice1 className="w-4 h-4 bg-yellow-500" />
<Dice3 className="w-4 h-4 bg-green-500" />
</div>
<TerminalStatus />
</div>
<div className="h-64 md:h-96 bg-gray-100 rounded-lg overflow-hidden shadow-md">
<Cli decreaseCommandsLeft={decreaseCommandsLeft} />
Expand Down
41 changes: 41 additions & 0 deletions src/components/ServerStatus/TerminalStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { useEffect } from 'react';
import { Wifi, WifiOff, Loader } from 'lucide-react';

import { RESPONSE_CODES } from '@/shared/constants/ResponseCode';
import { useServerStatus } from './context/ServerContext';

const TerminalStatus = () => {
const { serverStatus, responseStatusCode, checkServerStatus } =
useServerStatus();

useEffect(() => {
checkServerStatus();
}, []); // eslint-disable-line react-hooks/exhaustive-deps

return (
<div className="flex items-center gap-2">
{responseStatusCode === RESPONSE_CODES.OK ? (
<Wifi className="text-green-500" />
) : (
<WifiOff className="text-red-500" />
)}
<span
className={`text-sm ${
responseStatusCode === RESPONSE_CODES.OK
? 'text-green-400'
: 'text-red-400'
}`}
>
{serverStatus === 'Connected' ? (
serverStatus
) : responseStatusCode === RESPONSE_CODES.SERVICE_UNAVAILABLE ? (
'Failed to connect'
) : (
<Loader className="animate-spin" />
)}
</span>
</div>
);
};

export default TerminalStatus;
59 changes: 59 additions & 0 deletions src/components/ServerStatus/context/ServerContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use client';

import { SERVER_STATUS_URL } from '@/shared/constants/apiEndpoints';
import { RESPONSE_CODES } from '@/shared/constants/ResponseCode';
import React,{ createContext, useContext, useState } from 'react';

interface ServerStatusContextType {
serverStatus: string;
responseStatusCode: number;
checkServerStatus: () => Promise<boolean>;
}

export const ServerStatusContext = createContext<
ServerStatusContextType | undefined
>(undefined);

export const ServerStatusProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [serverStatus, setServerStatus] = useState<string>('Failed to connect');
const [responseStatusCode, setResponseStatusCode] = useState<number>(0);


const checkServerStatus = async () => {
try {
const response = await fetch(SERVER_STATUS_URL, {
method: 'GET',
});
setResponseStatusCode(response.status);
setServerStatus(
response.status === RESPONSE_CODES.OK
? 'Connected'
: 'Failed to connect',
);
return response.status === RESPONSE_CODES.OK;
} catch (error) {
console.error(`Error occurred at useServerStatus: ${error}`);
setServerStatus('Failed to connect');
setResponseStatusCode(RESPONSE_CODES.SERVICE_UNAVAILABLE);
return false;
}
};

return (
<ServerStatusContext.Provider
value={{ serverStatus, responseStatusCode, checkServerStatus }}
>
{children}
</ServerStatusContext.Provider>
);
};

export const useServerStatus = () => {
const context = useContext(ServerStatusContext);
if (!context) {
throw new Error('useServerStatus must be used within a ServerStatusProvider');
}
return context;
};
9 changes: 9 additions & 0 deletions src/shared/constants/ResponseCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const RESPONSE_CODES = {
OK: 200,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
INTERNAL_SERVER_ERROR: 500,
SERVICE_UNAVAILABLE: 503,
};
1 change: 1 addition & 0 deletions src/shared/constants/apiEndpoints.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// This folder will contain or hold constants. For example in this particular file we can store the api endpoints as constants
export const CLI_COMMAND_URL = 'http://localhost:8080/cli';
export const SERVER_STATUS_URL = 'http://localhost:8080/health';

0 comments on commit 66cdaf7

Please sign in to comment.