Skip to content

Commit

Permalink
Merge branch 'master' into feat-add-repo-supports
Browse files Browse the repository at this point in the history
  • Loading branch information
SquirrelDeveloper authored Nov 15, 2024
2 parents 4debc65 + 88c778d commit 57648ca
Show file tree
Hide file tree
Showing 22 changed files with 538 additions and 27 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

[Full Changelog](https://github.com/SquirrelCorporation/SquirrelServersManager/compare/v0.1.22...HEAD)

**Implemented enhancements:**

- \[FEAT\] Add advanced diagnostic checks for device connections [\#482](https://github.com/SquirrelCorporation/SquirrelServersManager/pull/482) ([SquirrelDeveloper](https://github.com/SquirrelDeveloper))

**Merged pull requests:**

- \[CHORE\] Increase JSON request size limit to 50mb [\#480](https://github.com/SquirrelCorporation/SquirrelServersManager/pull/480) ([SquirrelDeveloper](https://github.com/SquirrelDeveloper))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { MedicalSearchDiagnosisSolid } from '@/components/Icons/CustomIcons';
import { postDeviceDiagnostic } from '@/services/rest/device';
import { socket } from '@/socket';
import { history } from '@umijs/max';
import {
Avatar,
Button,
Card,
Col,
message,
Row,
StepProps,
Steps,
} from 'antd';
import React, { useEffect } from 'react';
import { API, SsmDeviceDiagnostic, SsmEvents } from 'ssm-shared-lib';

type ExistingDeviceAdvancedDiagnosticProps = {
device: Partial<API.DeviceItem>;
};

const items: (StepProps & any)[] = [
{
title: 'Ssh Connect',
description: 'Waiting...',
key: SsmDeviceDiagnostic.Checks.SSH_CONNECT,
},
{
title: 'Ssh Docker Connect',
description: 'Waiting...',
key: SsmDeviceDiagnostic.Checks.SSH_DOCKER_CONNECT,
},
{
title: 'Ssh Docker Socket Connectivity',
description: 'Waiting...',
key: SsmDeviceDiagnostic.Checks.DOCKER_SOCKET,
},
{
title: 'Disk Space',
description: 'Waiting...',
key: SsmDeviceDiagnostic.Checks.DISK_SPACE,
},
{
title: 'Memory & CPU',
description: 'Waiting...',
key: SsmDeviceDiagnostic.Checks.CPU_MEMORY_INFO,
},
];

const ExistingDeviceAdvancedDiagnostic: React.FC<
ExistingDeviceAdvancedDiagnosticProps
> = ({ device }) => {
const [diagInProgress, setDiagInProgress] = React.useState(false);
const [steps, setSteps] = React.useState<any[]>(items);
const onDiagnosticProgress = (payload: any) => {
setSteps((prevSteps) =>
prevSteps.map((step) =>
step.key === payload?.data?.check
? {
...step,
description: payload.message || step.description, // Update description if provided
status: !payload.success ? 'error' : 'success',
}
: step,
),
);
};

useEffect(() => {
socket.connect();
socket.on(SsmEvents.Diagnostic.PROGRESS, onDiagnosticProgress);

return () => {
socket.off(SsmEvents.Diagnostic.PROGRESS, onDiagnosticProgress);
socket.disconnect();
};
}, []);

const onStartDiag = async () => {
setDiagInProgress(true);
setSteps(items);
await postDeviceDiagnostic(device?.uuid as string)
.then(() => {
message.loading({ content: 'Diagnostic in progress...', duration: 5 });
})
.catch(() => {
setDiagInProgress(false);
setSteps(items);
});
};

return (
<Card
type="inner"
title={
<Row>
<Col>
<Avatar
style={{ backgroundColor: '#8e165e' }}
shape="square"
icon={<MedicalSearchDiagnosisSolid />}
/>
</Col>
<Col
style={{
marginLeft: 10,
marginTop: 'auto',
marginBottom: 'auto',
}}
>
Advanced Diagnostic
</Col>
</Row>
}
style={{ marginBottom: 10 }}
styles={{
header: { height: 55, minHeight: 55, paddingLeft: 15 },
body: { paddingBottom: 0 },
}}
extra={
<Button
onClick={() => {
history.push({
pathname: '/admin/logs',
// @ts-expect-error lib missing type
search: `?msg=DEVICE_DIAGNOSTIC`,
});
}}
>
Show Diagnostic Logs
</Button>
}
>
{diagInProgress && (
<Steps size="small" direction="vertical" items={steps} />
)}
<Button style={{ marginBottom: 20 }} onClick={onStartDiag}>
Run Advanced Diagnostic
</Button>
</Card>
);
};

export default ExistingDeviceAdvancedDiagnostic;
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import {
getCheckDeviceAnsibleConnection,
getCheckDeviceDockerConnection,
} from '@/services/rest/device';
import { ProForm } from '@ant-design/pro-components';
import { Avatar, Button, Card, Col, Row } from 'antd';
import React, { useState } from 'react';
import { API } from 'ssm-shared-lib';

export type ConnectionTestTabProps = {
type ConnectionTestTabProps = {
device: Partial<API.DeviceItem>;
};

const ConnectionTestTab: React.FC<ConnectionTestTabProps> = (props) => {
const ExistingDeviceConnectionTest: React.FC<ConnectionTestTabProps> = ({
device,
}) => {
const [execId, setExecId] = useState<string | undefined>();
const [dockerConnectionStatus, setDockerConnectionStatus] = useState<
string | undefined
Expand All @@ -22,17 +23,17 @@ const ConnectionTestTab: React.FC<ConnectionTestTabProps> = (props) => {
useState<string | undefined>();
const [testStarted, setTestStarted] = useState(false);
const asyncFetch = async () => {
if (!props.device.uuid) {
if (!device.uuid) {
return;
}
setExecId(undefined);
setDockerConnectionErrorMessage(undefined);
setDockerConnectionStatus('running...');
setTestStarted(true);
await getCheckDeviceAnsibleConnection(props.device.uuid).then((e) => {
await getCheckDeviceAnsibleConnection(device.uuid).then((e) => {
setExecId(e.data.taskId);
});
await getCheckDeviceDockerConnection(props.device.uuid).then((e) => {
await getCheckDeviceDockerConnection(device.uuid).then((e) => {
setDockerConnectionStatus(e.data.connectionStatus);
setDockerConnectionErrorMessage(e.data.errorMessage);
});
Expand Down Expand Up @@ -80,4 +81,4 @@ const ConnectionTestTab: React.FC<ConnectionTestTabProps> = (props) => {
);
};

export default ConnectionTestTab;
export default ExistingDeviceConnectionTest;
21 changes: 21 additions & 0 deletions client/src/components/Icons/CustomIcons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2564,3 +2564,24 @@ const FileSystemSvg = React.memo((props) => (
export const FileSystem = (props: Partial<CustomIconComponentProps>) => (
<Icon component={FileSystemSvg} {...props} />
);

const MedicalSearchDiagnosisSolidSvg = React.memo((props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 14 14"
{...props}
>
<path
fill="currentColor"
fillRule="evenodd"
d="M6.25 1.5a4.75 4.75 0 1 0 0 9.5a4.75 4.75 0 0 0 0-9.5M0 6.25a6.25 6.25 0 1 1 11.32 3.656l2.387 2.387a1 1 0 0 1-1.414 1.414L9.906 11.32A6.25 6.25 0 0 1 0 6.25m5.438-3.22a.5.5 0 0 0-.5.5v1.406H3.53a.5.5 0 0 0-.5.5v1.625a.5.5 0 0 0 .5.5h1.406V8.97a.5.5 0 0 0 .5.5h1.625a.5.5 0 0 0 .5-.5V7.562H8.97a.5.5 0 0 0 .5-.5V5.437a.5.5 0 0 0-.5-.5H7.563V3.531a.5.5 0 0 0-.5-.5H5.438Z"
clipRule="evenodd"
/>
</svg>
));

export const MedicalSearchDiagnosisSolid = (
props: Partial<CustomIconComponentProps>,
) => <Icon component={MedicalSearchDiagnosisSolidSvg} {...props} />;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ServerEnvironmentSvg } from '@/components/Icons/CustomIcons';
import AgentConfigurationTab from '@/pages/Admin/Inventory/components/AgentConfigurationTab';
import ConnectionTestTab from '@/pages/Admin/Inventory/components/ConnectionTestTab';
import DiagnosticTab from '@/pages/Admin/Inventory/components/DiagnosticTab';
import DockerConfigurationForm from '@/pages/Admin/Inventory/components/DockerConfigurationForm';
import SSHConfigurationForm from '@/pages/Admin/Inventory/components/SSHConfigurationForm';
import { DockerOutlined } from '@ant-design/icons';
Expand Down Expand Up @@ -28,8 +28,8 @@ const ConfigurationModal: React.FC<ConfigurationModalProps> = (props) => {
},
{
key: '3',
label: 'Connection test',
children: <ConnectionTestTab device={props.values} />,
label: 'Diagnostic',
children: <DiagnosticTab device={props.values} />,
},
{
key: '4',
Expand Down
17 changes: 17 additions & 0 deletions client/src/pages/Admin/Inventory/components/DiagnosticTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import ExistingDeviceAdvancedDiagnostic from '@/components/DeviceConfiguration/diagnostic/ExistingDeviceAdvancedDiagnostic';
import ExistingDeviceConnectionTest from '@/components/DeviceConfiguration/diagnostic/ExistingDeviceConnectionTest';
import React from 'react';
import { API } from 'ssm-shared-lib';

export type ConnectionTestTabProps = {
device: Partial<API.DeviceItem>;
};

const DiagnosticTab: React.FC<ConnectionTestTabProps> = (props) => (
<>
<ExistingDeviceConnectionTest device={props.device} />
<ExistingDeviceAdvancedDiagnostic device={props.device} />
</>
);

export default DiagnosticTab;
3 changes: 1 addition & 2 deletions client/src/pages/Admin/Logs/ServerLogsColums.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ const ServerLogsColumns: ProColumns<API.ServerLog>[] = [
title: 'Message',
dataIndex: 'msg',
key: 'msg',
filters: true,
onFilter: true,
hideInSearch: true,
responsive: ['xs'],
},
{
Expand Down
3 changes: 3 additions & 0 deletions client/src/pages/Admin/Logs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ const Index: React.FC = () => {
if (searchParams.get('moduleId')) {
form.setFieldsValue({ moduleId: searchParams.get('moduleId') });
}
if (searchParams.get('msg')) {
form.setFieldsValue({ msg: searchParams.get('msg') });
}

const logsTabItems: TabsProps['items'] = [
{
Expand Down
13 changes: 13 additions & 0 deletions client/src/services/rest/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,16 @@ export async function updateAgentInstallMethod(
},
);
}

export async function postDeviceDiagnostic(
uuid: string,
options?: { [key: string]: any },
) {
return request<API.SimpleResult>(
`/api/devices/${uuid}/check-connection/diagnostic`,
{
method: 'POST',
...(options || {}),
},
);
}
19 changes: 19 additions & 0 deletions server/src/controllers/rest/devices/check-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import DeviceAuthRepo from '../../../data/database/repository/DeviceAuthRepo';
import DeviceRepo from '../../../data/database/repository/DeviceRepo';
import { ForbiddenError, InternalError, NotFoundError } from '../../../middlewares/api/ApiError';
import { SuccessResponse } from '../../../middlewares/api/ApiResponse';
import Diagnostic from '../../../modules/diagnostic/Diagnostic';
import DeviceUseCases from '../../../services/DeviceUseCases';

export const postCheckAnsibleConnection = async (req, res) => {
Expand Down Expand Up @@ -115,3 +116,21 @@ export const getCheckDeviceAnsibleConnection = async (req, res) => {
throw new InternalError(error.message);
}
};

export const postDiagnostic = async (req, res) => {
const { uuid } = req.params;
const device = await DeviceRepo.findOneByUuid(uuid);
if (!device) {
throw new NotFoundError('Device ID not found');
}
const deviceAuth = await DeviceAuthRepo.findOneByDevice(device);
if (!deviceAuth) {
throw new NotFoundError('Device Auth not found');
}
try {
void Diagnostic.run(device, deviceAuth);
new SuccessResponse('Get Device Diagnostic').send(res);
} catch (error: any) {
throw new InternalError(error.message);
}
};
10 changes: 10 additions & 0 deletions server/src/controllers/rest/devices/check-connection.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,13 @@ export const getCheckDeviceAnsibleConnectionValidator = [
.withMessage('Uuid is not valid'),
validator,
];

export const postDiagnosticValidator = [
param('uuid')
.exists()
.notEmpty()
.withMessage('Uuid is required')
.isUUID()
.withMessage('Uuid is not valid'),
validator,
];
2 changes: 1 addition & 1 deletion server/src/controllers/rest/logs/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const getServerLogs = async (req, res) => {
'time',
'pid',
'level',
'message',
'msg',
'module',
'moduleId',
'moduleName',
Expand Down
Loading

0 comments on commit 57648ca

Please sign in to comment.