diff --git a/client/package.json b/client/package.json index 036623c5..c255071a 100644 --- a/client/package.json +++ b/client/package.json @@ -47,13 +47,13 @@ ], "dependencies": { "ssm-shared-lib": "file:../shared-lib/", - "antd": "^5.18.0", + "antd": "^5.18.1", "@ant-design/icons": "^5.3.7", "@ant-design/pro-components": "^2.7.10", "@ant-design/use-emotion-css": "1.0.4", "@ant-design/charts": "^2.1.1", "@antv/g2plot": "^2.4.31", - "@umijs/max": "^4.2.10", + "@umijs/max": "^4.2.11", "@umijs/route-utils": "^4.0.1", "@umijs/plugin-antd-dayjs": "^0.3.0", "umi-presets-pro": "^2.0.3", @@ -94,13 +94,13 @@ "@types/react-dom": "^18.3.0", "@types/react-helmet": "^6.1.11", "@umijs/fabric": "^4.0.1", - "@umijs/lint": "^4.2.10", + "@umijs/lint": "^4.2.11", "cross-env": "^7.0.3", "eslint": "^9.4.0", "express": "^4.19.2", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "lint-staged": "^15.2.5", + "lint-staged": "^15.2.7", "mockjs": "^1.1.0", "prettier": "^3.3.2", "swagger-ui-dist": "^5.17.14", diff --git a/client/src/components/DeviceComponents/DeviceLogos.tsx b/client/src/components/DeviceComponents/DeviceLogos.tsx index 0615896a..2553f459 100644 --- a/client/src/components/DeviceComponents/DeviceLogos.tsx +++ b/client/src/components/DeviceComponents/DeviceLogos.tsx @@ -2,10 +2,10 @@ import { CPULogo } from '@/components/DeviceComponents/CPULogo'; import { OsLogo } from '@/components/DeviceComponents/OsLogo/OsLogo'; import Avatar from 'antd/es/avatar/avatar'; import React from 'react'; -import { DeviceItem } from 'ssm-shared-lib/distribution/types/api'; +import { API } from 'ssm-shared-lib'; type DeviceLogosProps = { - device?: DeviceItem; + device?: API.DeviceItem; }; const DeviceLogos: React.FC = (props: DeviceLogosProps) => ( diff --git a/client/src/components/DeviceConfiguration/DockerConnectionForm.tsx b/client/src/components/DeviceConfiguration/DockerConnectionForm.tsx index ab771ee0..b5e525e6 100644 --- a/client/src/components/DeviceConfiguration/DockerConnectionForm.tsx +++ b/client/src/components/DeviceConfiguration/DockerConnectionForm.tsx @@ -25,7 +25,7 @@ import React, { useEffect, useState } from 'react'; import { API } from 'ssm-shared-lib'; import Cron from 'react-js-cron'; import 'react-js-cron/dist/styles.css'; -import { privateKeyRegexp } from 'ssm-shared-lib/distribution/validation'; +import { Validation } from 'ssm-shared-lib'; const connectionTypes = [ { @@ -402,7 +402,7 @@ export const DockerConnectionForm = (props: ConfigurationFormDockerProps) => { { required: true }, { required: true }, { - pattern: privateKeyRegexp, + pattern: Validation.privateKeyRegexp, message: 'The ssh key doesnt seems in a correct format', }, diff --git a/client/src/components/DeviceConfiguration/SSHConnectionForm.tsx b/client/src/components/DeviceConfiguration/SSHConnectionForm.tsx index 9d89ce57..aad7d40c 100644 --- a/client/src/components/DeviceConfiguration/SSHConnectionForm.tsx +++ b/client/src/components/DeviceConfiguration/SSHConnectionForm.tsx @@ -20,8 +20,7 @@ import { } from '@ant-design/pro-components'; import { Avatar, Card, Col, Flex, Row, Space, Switch, Tooltip } from 'antd'; import React from 'react'; -import { AnsibleBecomeMethod } from 'ssm-shared-lib/distribution/enums/ansible'; -import { privateKeyRegexp } from 'ssm-shared-lib/distribution/validation'; +import { Validation, SsmAnsible } from 'ssm-shared-lib'; const connectionTypes = [ { @@ -167,7 +166,7 @@ const SSHConnectionForm: React.FC = (props) => { }, ]} width="xs" - options={Object.values(AnsibleBecomeMethod).map((e) => ({ + options={Object.values(SsmAnsible.AnsibleBecomeMethod).map((e) => ({ value: e, label: e, }))} @@ -372,7 +371,7 @@ const SSHConnectionForm: React.FC = (props) => { rules={[ { required: true }, { - pattern: privateKeyRegexp, + pattern: Validation.privateKeyRegexp, message: 'The ssh key doesnt seems in a correct format', }, diff --git a/client/src/pages/Admin/Settings/components/GeneralSettings.tsx b/client/src/pages/Admin/Settings/components/GeneralSettings.tsx index dc4f1fa2..b24d1ef7 100644 --- a/client/src/pages/Admin/Settings/components/GeneralSettings.tsx +++ b/client/src/pages/Admin/Settings/components/GeneralSettings.tsx @@ -34,7 +34,7 @@ import { postResetSettings, } from '@/services/rest/settings'; import SystemPerformanceCard from '@/pages/Dashboard/Components/SystemPerformanceCard'; -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; const GeneralSettings: React.FC = () => { const { initialState } = useModel('@@initialState'); @@ -67,7 +67,7 @@ const GeneralSettings: React.FC = () => { const onChangeMaxCpu = async (newValue: number | null) => { if (newValue) { await postDashboardSetting( - GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_CPU_IF_LOWER, + SettingsKeys.GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_CPU_IF_LOWER, newValue, ).then(() => { setDashboardMaxCpuInPercent(newValue); @@ -82,7 +82,8 @@ const GeneralSettings: React.FC = () => { const onChangeMinMem = async (newValue: number | null) => { if (newValue) { await postDashboardSetting( - GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_MEM_IF_GREATER, + SettingsKeys.GeneralSettingsKeys + .CONSIDER_PERFORMANCE_GOOD_MEM_IF_GREATER, newValue, ).then(() => { setDashboardMinMemInPercent(newValue); @@ -97,7 +98,8 @@ const GeneralSettings: React.FC = () => { const onChangeConsiderDeviceOnline = async (newValue: number | null) => { if (newValue) { await postDeviceSetting( - GeneralSettingsKeys.CONSIDER_DEVICE_OFFLINE_AFTER_IN_MINUTES, + SettingsKeys.GeneralSettingsKeys + .CONSIDER_DEVICE_OFFLINE_AFTER_IN_MINUTES, newValue, ).then(() => { setConsiderDeviceOnlineInMinutes(newValue); @@ -112,7 +114,8 @@ const GeneralSettings: React.FC = () => { const onChangeAnsibleCleanUp = async (newValue: number | null) => { if (newValue) { await postLogsSetting( - GeneralSettingsKeys.CLEAN_UP_ANSIBLE_STATUSES_AND_TASKS_AFTER_IN_SECONDS, + SettingsKeys.GeneralSettingsKeys + .CLEAN_UP_ANSIBLE_STATUSES_AND_TASKS_AFTER_IN_SECONDS, newValue, ).then(() => { setAnsibleCleanUpInSeconds(newValue); @@ -126,7 +129,7 @@ const GeneralSettings: React.FC = () => { const onChangeServerLogsRetention = async (newValue: number | null) => { if (newValue) { await postLogsSetting( - GeneralSettingsKeys.SERVER_LOG_RETENTION_IN_DAYS, + SettingsKeys.GeneralSettingsKeys.SERVER_LOG_RETENTION_IN_DAYS, newValue, ).then(() => { setServerLogsRetentionInDays(newValue); @@ -141,7 +144,7 @@ const GeneralSettings: React.FC = () => { const onChangeRegisterDeviceStatEvery = async (newValue: number | null) => { if (newValue) { await postDeviceSetting( - GeneralSettingsKeys.REGISTER_DEVICE_STAT_EVERY_IN_SECONDS, + SettingsKeys.GeneralSettingsKeys.REGISTER_DEVICE_STAT_EVERY_IN_SECONDS, newValue, ).then(() => { setRegisterDeviceStatEveryXSeconds(newValue); @@ -162,7 +165,7 @@ const GeneralSettings: React.FC = () => { const onChangeDeviceStatsRetention = async (newValue: number | null) => { if (newValue) { await postDeviceStatsSettings( - GeneralSettingsKeys.DEVICE_STATS_RETENTION_IN_DAYS, + SettingsKeys.GeneralSettingsKeys.DEVICE_STATS_RETENTION_IN_DAYS, newValue, ).then(() => { setDeviceStatsRetentionInDays(newValue); @@ -177,7 +180,7 @@ const GeneralSettings: React.FC = () => { const onChangeContainerStatsRetention = async (newValue: number | null) => { if (newValue) { await postContainerStatsSettings( - GeneralSettingsKeys.CONTAINER_STATS_RETENTION_IN_DAYS, + SettingsKeys.GeneralSettingsKeys.CONTAINER_STATS_RETENTION_IN_DAYS, newValue, ).then(() => { setContainerStatsRetentionInDays(newValue); diff --git a/client/src/pages/Admin/Settings/components/RegistrySettings.tsx b/client/src/pages/Admin/Settings/components/RegistrySettings.tsx index 88185c03..1b048f7d 100644 --- a/client/src/pages/Admin/Settings/components/RegistrySettings.tsx +++ b/client/src/pages/Admin/Settings/components/RegistrySettings.tsx @@ -45,7 +45,6 @@ import { import { DeleteOutline } from 'antd-mobile-icons'; import React, { useEffect, useState } from 'react'; import { API } from 'ssm-shared-lib'; -import { ContainerRegistry } from 'ssm-shared-lib/distribution/types/api'; const getRegistryLogo = (provider: string) => { switch (provider) { @@ -83,7 +82,7 @@ const RegistrySettings: React.FC = () => { if (list?.data) { setRegistries( list.data?.registries.sort( - (a: ContainerRegistry, b: ContainerRegistry) => + (a: API.ContainerRegistry, b: API.ContainerRegistry) => a.authSet && !b.authSet ? -1 : 1, ) || [], ); diff --git a/client/src/pages/Dashboard/Components/MainChartCard.tsx b/client/src/pages/Dashboard/Components/MainChartCard.tsx index 92404f3e..fb152b85 100644 --- a/client/src/pages/Dashboard/Components/MainChartCard.tsx +++ b/client/src/pages/Dashboard/Components/MainChartCard.tsx @@ -86,6 +86,7 @@ const MainChartCard: React.FC = ({}) => { useEffect(() => { asyncFetch(); }, [devices, type, rangePickerValue]); + // see https://ant-design-charts-next.antgroup.com/en/options/plots/component/legend const config = { data: graphData, diff --git a/client/src/pages/Dashboard/Components/ServicesCard.tsx b/client/src/pages/Dashboard/Components/ServicesCard.tsx index 295cbe25..2341d87b 100644 --- a/client/src/pages/Dashboard/Components/ServicesCard.tsx +++ b/client/src/pages/Dashboard/Components/ServicesCard.tsx @@ -8,8 +8,7 @@ import { Tiny } from '@ant-design/charts'; import { InfoCircleFilled } from '@ant-design/icons'; import { Tooltip, Typography } from 'antd'; import React, { useEffect } from 'react'; -import { ContainerStatus } from 'ssm-shared-lib/distribution/enums/status'; -import { API } from 'ssm-shared-lib'; +import { SsmStatus, API } from 'ssm-shared-lib'; const ServicesCard: React.FC = () => { const [loading, setLoading] = React.useState(false); @@ -18,9 +17,11 @@ const ServicesCard: React.FC = () => { const [stats, setStats] = React.useState(); const asyncFetch = async () => { - await getNbContainersByStatus(ContainerStatus.RUNNING).then((response) => { - setNbRunning(response.data); - }); + await getNbContainersByStatus(SsmStatus.ContainerStatus.RUNNING).then( + (response) => { + setNbRunning(response.data); + }, + ); await getNbContainersByStatus('all').then((response) => { setNbTotal(response.data); }); diff --git a/client/src/pages/Playbooks/ExtraVarsViewEditor.tsx b/client/src/pages/Playbooks/components/ExtraVarsViewEditor.tsx similarity index 100% rename from client/src/pages/Playbooks/ExtraVarsViewEditor.tsx rename to client/src/pages/Playbooks/components/ExtraVarsViewEditor.tsx diff --git a/client/src/pages/Playbooks/components/GalaxyStoreModal.tsx b/client/src/pages/Playbooks/components/GalaxyStoreModal.tsx new file mode 100644 index 00000000..87006882 --- /dev/null +++ b/client/src/pages/Playbooks/components/GalaxyStoreModal.tsx @@ -0,0 +1,309 @@ +import { GrommetIconsInstall } from '@/components/Icons/CustomIcons'; +import { + getCollection, + getCollections, + postInstallCollection, +} from '@/services/rest/ansible'; +import { + ProDescriptions, + ProForm, + ProList, + ProTable, +} from '@ant-design/pro-components'; +import { + Avatar, + Button, + Input, + message, + Modal, + Space, + Tag, + Typography, +} from 'antd'; +import Link from 'antd/lib/typography/Link'; +import React from 'react'; +import { AnsibleAPI } from 'ssm-shared-lib'; + +export type GalaxyStoreModalProps = { + open: boolean; + setOpen: any; +}; + +const GalaxyStoreModal: React.FC = ( + props: GalaxyStoreModalProps, +) => { + const [selectedRow, setSelectedRow] = React.useState(); + const [loading, setLoading] = React.useState(false); + return ( + { + setSelectedRow(undefined); + props.setOpen(false); + }} + onCancel={() => { + setSelectedRow(undefined); + props.setOpen(false); + }} + width={1500} + > + {(!selectedRow && ( + + ghost={false} + itemCardProps={{ + ghost: false, + }} + pagination={{ + defaultPageSize: 9, + showSizeChanger: false, + }} + showActions="hover" + rowSelection={false} + grid={{ gutter: 8, column: 3 }} + request={getCollections} + onItem={(record: any) => { + return { + onMouseEnter: () => { + console.log(record); + }, + onClick: () => { + console.log(record); + }, + }; + }} + search={{ + filterType: 'light', + }} + metas={{ + title: { + search: false, + dataIndex: ['collection_version', 'name'], + }, + subTitle: { + title: 'Namespace', + dataIndex: 'namespace', + render: (_, row) => ( + + {row.collection_version.namespace} + + ), + }, + avatar: { + search: false, + dataIndex: 'logo', + render: (_, row) => ( + <> + {(row?.namespace_metadata?.avatar_url && ( + + )) || ( + + )} + + ), + }, + content: { + title: 'Keywords', + render: (_, row) => ( + + {row.collection_version.description} + + ), + }, + actions: { + search: false, + cardActionProps: 'actions', + render: (_, row) => { + return [ + setSelectedRow(row)}> + Details + , + ]; + }, + }, + }} + /> + )) || ( + { + return await getCollection({ + name: selectedRow?.collection_version.name || '', + namespace: selectedRow?.collection_version.namespace || '', + version: selectedRow?.collection_version.version || '', + }); + }} + extra={ + <> + {' '} + + + + } + style={{ height: 600, marginTop: 20 }} + column={4} + > + { + return ( + (selectedRow?.namespace_metadata?.avatar_url && ( + + )) || ( + + ) + ); + }} + /> + + + ( + + {row.manifest?.collection_info?.repository} + + )} + /> + + row.manifest?.collection_info?.tags?.map((e: string) => ( + {e} + )) + } + /> + + ( + <> + + $ansible-galaxy collection install {row.namespace}.{row.name} + + + )} + /> + + ( + + )} + /> + + )} + + ); +}; + +export default GalaxyStoreModal; diff --git a/client/src/pages/Playbooks/components/NewPlaybookModalForm.tsx b/client/src/pages/Playbooks/components/NewPlaybookModalForm.tsx new file mode 100644 index 00000000..6dae3a9a --- /dev/null +++ b/client/src/pages/Playbooks/components/NewPlaybookModalForm.tsx @@ -0,0 +1,68 @@ +import { ModalForm, ProFormText } from '@ant-design/pro-components'; +import { Button } from 'antd'; +import { AddCircleOutline } from 'antd-mobile-icons'; +import React from 'react'; +import { API } from 'ssm-shared-lib'; + +export type NewPlaybookModalFormProps = { + submitNewPlaybook: (name: string) => Promise; + playbookFilesList: API.PlaybookFileList[]; +}; + +const NewPlaybookModalForm: React.FC = (props) => { + return ( + + title={'Create a new playbook'} + trigger={ + + } + autoFocusFirstInput + modalProps={{ + destroyOnClose: true, + }} + onFinish={async (values) => { + return await props.submitNewPlaybook(values.name); + }} + > + e.label === value) === + -1 + ) { + return Promise.resolve(); + } + return Promise.reject('Playbook name already exists'); + }, + }, + ]} + /> + + ); +}; + +export default NewPlaybookModalForm; diff --git a/client/src/pages/Playbooks/index.tsx b/client/src/pages/Playbooks/index.tsx index 91d8eee6..eacfd7ae 100644 --- a/client/src/pages/Playbooks/index.tsx +++ b/client/src/pages/Playbooks/index.tsx @@ -1,5 +1,7 @@ import Title, { PageContainerTitleColors } from '@/components/Template/Title'; -import ExtraVarsViewEditor from '@/pages/Playbooks/ExtraVarsViewEditor'; +import ExtraVarsViewEditor from '@/pages/Playbooks/components/ExtraVarsViewEditor'; +import GalaxyStoreModal from '@/pages/Playbooks/components/GalaxyStoreModal'; +import NewPlaybookModalForm from '@/pages/Playbooks/components/NewPlaybookModalForm'; import { deletePlaybook, getPlaybooks, @@ -8,6 +10,7 @@ import { readPlaybookContent, } from '@/services/rest/ansible'; import { + AppstoreOutlined, FileOutlined, FileSearchOutlined, PlaySquareOutlined, @@ -38,7 +41,7 @@ import type { DirectoryTreeProps } from 'antd/es/tree'; import { editor } from 'monaco-editor'; import { configureMonacoYaml } from 'monaco-yaml'; import React, { useEffect } from 'react'; -import { PlaybookFileList } from 'ssm-shared-lib/distribution/types/api'; +import { API } from 'ssm-shared-lib'; import IStandaloneCodeEditor = editor.IStandaloneCodeEditor; window.MonacoEnvironment = { @@ -62,16 +65,17 @@ const { Paragraph, Text } = Typography; const Index: React.FC = () => { const [playbookFilesList, setPlaybookFilesList] = React.useState< - PlaybookFileList[] + API.PlaybookFileList[] >([]); const [selectedFile, setSelectedFile] = React.useState< - PlaybookFileList | undefined + API.PlaybookFileList | undefined >(); const [downloadedContent, setDownloadedContent] = React.useState< string | undefined >(); const [isLoading, setIsLoading] = React.useState(false); const editorRef = React.useRef(null); + const [storeModal, setStoreModal] = React.useState(false); const asyncFetchPlaybookContent = async () => { if (selectedFile) { @@ -216,7 +220,16 @@ const Index: React.FC = () => { title="List of playbooks" bordered={false} style={{ width: '300px', minHeight: '90vh' }} + extra={[ + , + ]} > + { })} selectedKeys={[selectedFile?.value as React.Key]} /> - - title={'Create a new playbook'} - trigger={ - - } - autoFocusFirstInput - modalProps={{ - destroyOnClose: true, - }} - onFinish={async (values) => { - return await submitNewPlaybook(values.name); - }} - > - e.label === value, - ) === -1 - ) { - return Promise.resolve(); - } - return Promise.reject('Playbook name already exists'); - }, - }, - ]} - /> - + diff --git a/client/src/pages/Services/components/ContainerStatsDetail.tsx b/client/src/pages/Services/components/ContainerStatsDetail.tsx index ef373267..33a05c20 100644 --- a/client/src/pages/Services/components/ContainerStatsDetail.tsx +++ b/client/src/pages/Services/components/ContainerStatsDetail.tsx @@ -1,7 +1,7 @@ import { getContainerStats } from '@/services/rest/containersstats'; import moment from 'moment'; import React, { useEffect } from 'react'; -import { API } from 'ssm-shared-lib/distribution'; +import { API } from 'ssm-shared-lib'; import { Line } from '@ant-design/plots'; export type ContainerStatsDetailProps = { diff --git a/client/src/pages/Services/components/StatusTag.tsx b/client/src/pages/Services/components/StatusTag.tsx index 07bafddc..690a8659 100644 --- a/client/src/pages/Services/components/StatusTag.tsx +++ b/client/src/pages/Services/components/StatusTag.tsx @@ -1,6 +1,6 @@ import { Tag } from 'antd'; import React from 'react'; -import { ContainerStatus } from 'ssm-shared-lib/distribution/enums/status'; +import { SsmStatus } from 'ssm-shared-lib'; export type StatusTagProps = { status?: string; @@ -8,9 +8,9 @@ export type StatusTagProps = { const StatusTag: React.FC = (props: StatusTagProps) => { switch (props.status) { - case ContainerStatus.RUNNING: + case SsmStatus.ContainerStatus.RUNNING: return Running; - case ContainerStatus.PAUSED: + case SsmStatus.ContainerStatus.PAUSED: return Paused; default: return {props.status}; diff --git a/client/src/services/rest/ansible.ts b/client/src/services/rest/ansible.ts index f672018b..2646c861 100644 --- a/client/src/services/rest/ansible.ts +++ b/client/src/services/rest/ansible.ts @@ -107,3 +107,44 @@ export async function postExtraVarValue(extraVar: string, value: string) { ...{}, }); } + +export async function getCollections( + params?: any, + options?: Record, +) { + return request('/api/ansible/galaxy/collection', { + method: 'GET', + params: { + ...params, + }, + ...(options || {}), + }); +} + +export async function getCollection( + params: { name: string; namespace: string; version: string }, + options?: Record, +) { + return request('/api/ansible/galaxy/collection/details', { + method: 'GET', + params: { + ...params, + }, + ...(options || {}), + }); +} + +export async function postInstallCollection( + body: { name: string; namespace: string }, + params?: any, + options?: Record, +) { + return request('/api/ansible/galaxy/collection/install', { + method: 'POST', + data: body, + params: { + ...params, + }, + ...(options || {}), + }); +} diff --git a/client/src/services/rest/containersstats.ts b/client/src/services/rest/containersstats.ts index 6044c75a..67c21dec 100644 --- a/client/src/services/rest/containersstats.ts +++ b/client/src/services/rest/containersstats.ts @@ -1,6 +1,5 @@ import { request } from '@umijs/max'; import { API } from 'ssm-shared-lib'; -import { ContainerAveragedStats } from 'ssm-shared-lib/distribution/types/api'; export async function getContainerStats( containerId: string, diff --git a/client/src/services/rest/deviceauth.ts b/client/src/services/rest/deviceauth.ts index 99fcf286..81cd8c66 100644 --- a/client/src/services/rest/deviceauth.ts +++ b/client/src/services/rest/deviceauth.ts @@ -2,7 +2,6 @@ /* eslint-disable */ import { request } from '@umijs/max'; import { API } from 'ssm-shared-lib'; -import { SimpleResult } from 'ssm-shared-lib/distribution/types/api'; export async function getDeviceAuth( deviceId: string, diff --git a/server/package.json b/server/package.json index 1d4524cc..58812c39 100644 --- a/server/package.json +++ b/server/package.json @@ -35,16 +35,16 @@ "passport-http": "^0.3.0", "passport-http-bearer": "^1.0.1", "passport-jwt": "^4.0.1", - "pino": "^9.1.0", + "pino": "^9.2.0", "pino-http": "^10.1.0", "pino-mongodb": "^4.3.0", - "pino-pretty": "^11.2.0", + "pino-pretty": "^11.2.1", "redis": "^4.6.14", "rewire": "^7.0.0", "semver": "^7.6.2", "shelljs": "^0.8.5", "ssm-shared-lib": "file:../shared-lib/", - "@aws-sdk/client-ecr": "^3.592.0" + "@aws-sdk/client-ecr": "^3.596.0" }, "devDependencies": { "@types/bcrypt": "^5.0.2", diff --git a/server/src/data/database/model/Device.ts b/server/src/data/database/model/Device.ts index e7f98895..d58dc504 100644 --- a/server/src/data/database/model/Device.ts +++ b/server/src/data/database/model/Device.ts @@ -1,6 +1,6 @@ import { Schema, model } from 'mongoose'; import { v4 as uuidv4 } from 'uuid'; -import { DeviceStatus } from 'ssm-shared-lib/distribution/enums/status'; +import { SsmStatus } from 'ssm-shared-lib'; import { API } from 'ssm-shared-lib'; export const DOCUMENT_NAME = 'Device'; @@ -89,7 +89,7 @@ const schema = new Schema( }, status: { type: Schema.Types.Number, - default: DeviceStatus.REGISTERING, + default: SsmStatus.DeviceStatus.REGISTERING, required: true, }, osArch: { diff --git a/server/src/data/database/model/DeviceAuth.ts b/server/src/data/database/model/DeviceAuth.ts index b7ecc893..b2866e8a 100644 --- a/server/src/data/database/model/DeviceAuth.ts +++ b/server/src/data/database/model/DeviceAuth.ts @@ -1,5 +1,5 @@ import { Schema, model } from 'mongoose'; -import { SSHType } from 'ssm-shared-lib/distribution/enums/ansible'; +import { SsmAnsible } from 'ssm-shared-lib'; import Device from './Device'; export const DOCUMENT_NAME = 'DeviceAuth'; @@ -7,7 +7,7 @@ export const COLLECTION_NAME = 'deviceauth'; export default interface DeviceAuth { device: Device; - authType?: SSHType; + authType?: SsmAnsible.SSHType; sshUser?: string; sshPwd?: string; sshKey?: string; @@ -22,7 +22,7 @@ export default interface DeviceAuth { sshCommonArgs?: string; sshExecutable?: string; customDockerSSH?: boolean; - dockerCustomAuthType?: SSHType; + dockerCustomAuthType?: SsmAnsible.SSHType; dockerCustomSshUser?: string; dockerCustomSshPwd?: string; dockerCustomSshKeyPass?: string; @@ -48,7 +48,7 @@ const schema = new Schema( }, authType: { type: Schema.Types.String, - enum: SSHType, + enum: SsmAnsible.SSHType, required: true, }, sshUser: { @@ -125,7 +125,7 @@ const schema = new Schema( }, dockerCustomAuthType: { type: Schema.Types.String, - enum: SSHType, + enum: SsmAnsible.SSHType, required: false, }, dockerCustomSshKey: { diff --git a/server/src/data/database/repository/ContainerStatsRepo.ts b/server/src/data/database/repository/ContainerStatsRepo.ts index 82599080..e316ee91 100644 --- a/server/src/data/database/repository/ContainerStatsRepo.ts +++ b/server/src/data/database/repository/ContainerStatsRepo.ts @@ -1,7 +1,7 @@ import Dockerode from 'dockerode'; import { DateTime } from 'luxon'; import mongoose from 'mongoose'; -import { ContainerStats } from 'ssm-shared-lib/distribution/types/api'; +import { API } from 'ssm-shared-lib'; import logger from '../../../logger'; import Container from '../model/Container'; import { ContainerStatModel } from '../model/ContainerStat'; @@ -66,7 +66,7 @@ async function findStatsByDeviceAndType( container: Container, type: string, from: number, -): Promise { +): Promise { const ObjectId = mongoose.Types.ObjectId; return await ContainerStatModel.aggregate([ { @@ -90,7 +90,7 @@ async function findAllAveragedStatsByType( type: string, from: Date, to: Date, -): Promise { +): Promise { return await ContainerStatModel.aggregate([ { $match: { diff --git a/server/src/data/database/repository/DeviceRepo.ts b/server/src/data/database/repository/DeviceRepo.ts index 4d929da7..f594931b 100644 --- a/server/src/data/database/repository/DeviceRepo.ts +++ b/server/src/data/database/repository/DeviceRepo.ts @@ -1,5 +1,5 @@ import { DateTime } from 'luxon'; -import { DeviceStatus } from 'ssm-shared-lib/distribution/enums/status'; +import { SsmStatus } from 'ssm-shared-lib'; import logger from '../../../logger'; import Device, { DeviceModel } from '../model/Device'; import DeviceDownTimeEventRepo from './DeviceDownTimeEventRepo'; @@ -36,9 +36,9 @@ async function setDeviceOfflineAfter(inactivityInMinutes: number) { const devices = await DeviceModel.find({ updatedAt: { $lt: DateTime.now().minus({ minute: inactivityInMinutes }).toJSDate() }, $and: [ - { status: { $ne: DeviceStatus.OFFLINE } }, - { status: { $ne: DeviceStatus.UNMANAGED } }, - { status: { $ne: DeviceStatus.REGISTERING } }, + { status: { $ne: SsmStatus.DeviceStatus.OFFLINE } }, + { status: { $ne: SsmStatus.DeviceStatus.UNMANAGED } }, + { status: { $ne: SsmStatus.DeviceStatus.REGISTERING } }, ], }) .lean() @@ -49,7 +49,7 @@ async function setDeviceOfflineAfter(inactivityInMinutes: number) { await DeviceModel.updateOne( { uuid: device.uuid }, { - $set: { status: DeviceStatus.OFFLINE }, + $set: { status: SsmStatus.DeviceStatus.OFFLINE }, }, ) .lean() diff --git a/server/src/integrations/ansible/AnsibleGalaxyCmd.ts b/server/src/integrations/ansible/AnsibleGalaxyCmd.ts new file mode 100644 index 00000000..a0491f84 --- /dev/null +++ b/server/src/integrations/ansible/AnsibleGalaxyCmd.ts @@ -0,0 +1,14 @@ +class AnsibleGalaxyCommandBuilder { + static readonly ansibleGalaxy = 'ansible-galaxy'; + static readonly collection = 'collection'; + + getInstallCollectionCmd(name: string, namespace: string) { + return `${AnsibleGalaxyCommandBuilder.ansibleGalaxy} ${AnsibleGalaxyCommandBuilder.collection} install ${namespace}.${name}`; + } + + getListCollectionsCmd(name: string, namespace: string) { + return `${AnsibleGalaxyCommandBuilder.ansibleGalaxy} ${AnsibleGalaxyCommandBuilder.collection} list`; + } +} + +export default new AnsibleGalaxyCommandBuilder(); diff --git a/server/src/integrations/ansible/utils/ExtraVars.ts b/server/src/integrations/ansible/utils/ExtraVars.ts index dde539d0..d8dd162f 100644 --- a/server/src/integrations/ansible/utils/ExtraVars.ts +++ b/server/src/integrations/ansible/utils/ExtraVars.ts @@ -1,15 +1,15 @@ -import { AnsibleReservedExtraVarsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; import { API } from 'ssm-shared-lib'; -import { SSMReservedExtraVars } from 'ssm-shared-lib/distribution/enums/ansible'; +import { SsmAnsible } from 'ssm-shared-lib'; import Playbook from '../../../data/database/model/Playbook'; import { getFromCache } from '../../../data/cache'; import logger from '../../../logger'; function getDefaultExtraVars(playbook: Playbook, target?: string[]) { const defaultExtraVars = target - ? (JSON.parse(`[{"extraVar": "${SSMReservedExtraVars.DEVICE_ID}", "value": "${target}"}]`) as [ - API.ExtraVar, - ]) + ? (JSON.parse( + `[{"extraVar": "${SsmAnsible.SSMReservedExtraVars.DEVICE_ID}", "value": "${target}"}]`, + ) as [API.ExtraVar]) : []; logger.debug(JSON.stringify(playbook.extraVars)); return defaultExtraVars; @@ -18,12 +18,12 @@ function getDefaultExtraVars(playbook: Playbook, target?: string[]) { async function substitutedExtraVar(extraVar: string, forcedValues?: API.ExtraVars) { const forcedValue = forcedValues?.find((e) => e.extraVar === extraVar)?.value; switch (extraVar) { - case SSMReservedExtraVars.DEVICE_ID: + case SsmAnsible.SSMReservedExtraVars.DEVICE_ID: return forcedValue; - case SSMReservedExtraVars.MASTER_NODE_URL: + case SsmAnsible.SSMReservedExtraVars.MASTER_NODE_URL: return forcedValue ? forcedValue - : await getFromCache(AnsibleReservedExtraVarsKeys.MASTER_NODE_URL); + : await getFromCache(SettingsKeys.AnsibleReservedExtraVarsKeys.MASTER_NODE_URL); default: return await getFromCache(extraVar); } diff --git a/server/src/integrations/ansible/utils/InventoryTransformer.ts b/server/src/integrations/ansible/utils/InventoryTransformer.ts index 7231a638..14428999 100644 --- a/server/src/integrations/ansible/utils/InventoryTransformer.ts +++ b/server/src/integrations/ansible/utils/InventoryTransformer.ts @@ -1,4 +1,4 @@ -import { SSHType } from 'ssm-shared-lib/distribution/enums/ansible'; +import { SsmAnsible } from 'ssm-shared-lib'; import DeviceAuth from '../../../data/database/model/DeviceAuth'; import logger from '../../../logger'; import { Ansible } from '../../../types/typings'; @@ -64,13 +64,13 @@ function getAuth(deviceAuth: Partial): Auth { const auth: Auth = {}; switch (deviceAuth.authType) { - case SSHType.KeyBased: + case SsmAnsible.SSHType.KeyBased: auth.ansible_ssh_private_key_file = `/tmp/${deviceAuth.device?.uuid}.key`; if (deviceAuth.sshKeyPass) { auth.ansible_paramiko_pass = { __ansible_vault: deviceAuth.sshKeyPass }; } break; - case SSHType.UserPassword: + case SsmAnsible.SSHType.UserPassword: auth.ansible_ssh_pass = { __ansible_vault: deviceAuth.sshPwd }; break; default: diff --git a/server/src/integrations/docker/core/DockerAPIHelper.ts b/server/src/integrations/docker/core/DockerAPIHelper.ts index 338764ce..c7d13c61 100644 --- a/server/src/integrations/docker/core/DockerAPIHelper.ts +++ b/server/src/integrations/docker/core/DockerAPIHelper.ts @@ -1,10 +1,12 @@ import Dockerode from 'dockerode'; import { ConnectConfig } from 'ssh2'; -import { SSHType } from 'ssm-shared-lib/distribution/enums/ansible'; +import { SsmAnsible } from 'ssm-shared-lib'; import Device from '../../../data/database/model/Device'; import DeviceAuth from '../../../data/database/model/DeviceAuth'; import { DEFAULT_VAULT_ID, vaultDecrypt } from '../../ansible-vault/vault'; +const SSHType = SsmAnsible.SSHType; + function getDockerApiAuth() { const auth = { username: 'XXXX', diff --git a/server/src/integrations/shell/index.ts b/server/src/integrations/shell/index.ts index 9ee23df2..21d15c16 100644 --- a/server/src/integrations/shell/index.ts +++ b/server/src/integrations/shell/index.ts @@ -5,12 +5,17 @@ import User from '../../data/database/model/User'; import AnsibleTaskRepo from '../../data/database/repository/AnsibleTaskRepo'; import DeviceAuthRepo from '../../data/database/repository/DeviceAuthRepo'; import logger from '../../logger'; +import AnsibleGalaxyCmd from '../ansible/AnsibleGalaxyCmd'; import Inventory from '../ansible/utils/InventoryTransformer'; import { Ansible } from '../../types/typings'; import ansibleCmd from '../ansible/AnsibleCmd'; export const ANSIBLE_PATH = '/server/src/ansible/'; +function timeout(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + async function executePlaybook( playbook: string, user: User, @@ -170,6 +175,33 @@ async function saveSshKey(key: string, uuid: string) { } } +async function installAnsibleGalaxyCollection(name: string, namespace: string) { + try { + logger.info('[SHELL] - installAnsibleGalaxyCollection Starting...'); + const result = shell.exec(AnsibleGalaxyCmd.getInstallCollectionCmd(name, namespace)); + if (result.code !== 0) { + throw new Error('[SHELL] - installAnsibleGalaxyCollection has failed'); + } + let collectionList = ''; + let i = 0; + while (!collectionList.includes(`${namespace}.${name}`) && i++ < 60) { + await timeout(2000); + const resultList = shell.exec( + AnsibleGalaxyCmd.getListCollectionsCmd(name, namespace) + + ' > /tmp/ansible-collection-output.tmp.txt', + ); + await timeout(2000); + collectionList = shell.cat('/tmp/ansible-collection-output.tmp.txt').toString(); + } + if (!collectionList.includes(`${namespace}.${name}`)) { + throw new Error('[SHELL] - installAnsibleGalaxyCollection has failed'); + } + } catch (error) { + logger.error('[SHELL] - installAnsibleGalaxyCollection'); + throw error; + } +} + export default { executePlaybook, listPlaybooks, @@ -181,4 +213,5 @@ export default { getAnsibleVersion, saveSshKey, executePlaybookOnInventory, + installAnsibleGalaxyCollection, }; diff --git a/server/src/routes/ansible.ts b/server/src/routes/ansible.ts index ebbcc3b4..05af72be 100644 --- a/server/src/routes/ansible.ts +++ b/server/src/routes/ansible.ts @@ -8,6 +8,16 @@ import { } from '../services/ansible/execution.validator'; import { addOrUpdateExtraVarValue } from '../services/ansible/extravar'; import { addOrUpdateExtraVarValueValidator } from '../services/ansible/extravar.validator'; +import { + getAnsibleGalaxyCollection, + getAnsibleGalaxyCollections, + postInstallAnsibleGalaxyCollection, +} from '../services/ansible/galaxy'; +import { + getAnsibleGalaxyCollectionsValidator, + getAnsibleGalaxyCollectionValidator, + postInstallAnsibleGalaxyCollectionValidator, +} from '../services/ansible/galaxy.validator'; import { addTaskEvent, addTaskStatus } from '../services/ansible/hook'; import { getInventory } from '../services/ansible/inventory'; import { @@ -42,6 +52,18 @@ router.get(`/inventory`, passport.authenticate('bearer', { session: false }), ge router.use(passport.authenticate('jwt', { session: false })); +router.get(`/galaxy/collection`, getAnsibleGalaxyCollectionsValidator, getAnsibleGalaxyCollections); +router.get( + `/galaxy/collection/details`, + getAnsibleGalaxyCollectionValidator, + getAnsibleGalaxyCollection, +); +router.post( + `/galaxy/collection/install`, + postInstallAnsibleGalaxyCollectionValidator, + postInstallAnsibleGalaxyCollection, +); + router.post(`/exec/playbook/:playbook`, execPlaybookValidator, execPlaybook); router.get(`/exec/:id/logs`, getLogsValidator, getLogs); router.get(`/exec/:id/status`, getStatusValidator, getStatus); diff --git a/server/src/services/ansible/galaxy.ts b/server/src/services/ansible/galaxy.ts new file mode 100644 index 00000000..31f75ee3 --- /dev/null +++ b/server/src/services/ansible/galaxy.ts @@ -0,0 +1,58 @@ +import axios from 'axios'; +import { parse } from 'url'; +import { InternalError } from '../../core/api/ApiError'; +import { SuccessResponse } from '../../core/api/ApiResponse'; +import asyncHandler from '../../helpers/AsyncHandler'; +import { API } from 'ssm-shared-lib'; +import Shell from '../../integrations/shell'; +import logger from '../../logger'; + +export const getAnsibleGalaxyCollections = asyncHandler(async (req, res) => { + const realUrl = req.url; + const { current = 1, pageSize = 9 } = req.query; + const params = parse(realUrl, true).query as unknown as API.PageParams & { + sorter: any; + filter: any; + } & { + content?: string; + namespace?: string; + }; + const response = await axios.get( + `https://galaxy.ansible.com/api/v3/plugin/ansible/search/collection-versions/?${params.namespace ? 'namespace=' + encodeURIComponent(params.namespace) + '&' : ''}${params.content ? 'keywords=' + encodeURIComponent(params.content) + '&' : ''}is_deprecated=false&repository_label=!hide_from_search&is_highest=true&offset=${current}&limit=${pageSize}&order_by=name`, + ); + new SuccessResponse('Get Ansible Galaxy Collections successful', response.data?.data || [], { + total: response.data?.meta?.count, + success: true, + pageSize, + current: parseInt(`${params.current}`, 10) || 1, + }).send(res); +}); + +export const getAnsibleGalaxyCollection = asyncHandler(async (req, res) => { + const realUrl = req.url; + const params = parse(realUrl, true).query as unknown as API.PageParams & { + sorter: any; + filter: any; + } & { + name: string; + namespace: string; + version: string; + }; + const response = await axios.get( + `https://galaxy.ansible.com/api/pulp/api/v3/content/ansible/collection_versions/?namespace=${encodeURIComponent(params.namespace)}&name=${encodeURIComponent(params.name)}&version=${encodeURIComponent(params.version)}&offset=0&limit=10`, + ); + new SuccessResponse( + 'Get Ansible Galaxy Collection successful', + response.data?.results ? response.data.results[0] : undefined, + ).send(res); +}); + +export const postInstallAnsibleGalaxyCollection = asyncHandler(async (req, res) => { + const { name, namespace } = req.body; + try { + await Shell.installAnsibleGalaxyCollection(name, namespace); + } catch (error: any) { + throw new InternalError(error.message); + } + new SuccessResponse('Install Ansible Galaxy Collection successful').send(res); +}); diff --git a/server/src/services/ansible/galaxy.validator.ts b/server/src/services/ansible/galaxy.validator.ts new file mode 100644 index 00000000..8fd9d46a --- /dev/null +++ b/server/src/services/ansible/galaxy.validator.ts @@ -0,0 +1,31 @@ +import { body, query } from 'express-validator'; +import validator from '../../middlewares/validator'; + +export const getAnsibleGalaxyCollectionsValidator = [ + query('offset') + .optional() + .notEmpty() + .isNumeric() + .withMessage('offset query param needs to be numeric'), + query('limit') + .optional() + .notEmpty() + .isNumeric() + .withMessage('limit query param needs to be numeric'), + query('content').optional().notEmpty().isString(), + query('namespace').optional().notEmpty().isString(), + validator, +]; + +export const getAnsibleGalaxyCollectionValidator = [ + query('name').notEmpty().isString(), + query('namespace').notEmpty().isString(), + query('version').notEmpty().isString(), + validator, +]; + +export const postInstallAnsibleGalaxyCollectionValidator = [ + body('name').notEmpty().isString(), + body('namespace').notEmpty().isString(), + validator, +]; diff --git a/server/src/services/ansible/playbook.validator.ts b/server/src/services/ansible/playbook.validator.ts index d2fa58cc..e8e088f7 100644 --- a/server/src/services/ansible/playbook.validator.ts +++ b/server/src/services/ansible/playbook.validator.ts @@ -1,5 +1,5 @@ import { body, param } from 'express-validator'; -import { playbookNameRegexp } from 'ssm-shared-lib/distribution/validation'; +import { Validation } from 'ssm-shared-lib'; import validator from '../../middlewares/validator'; export const getPlaybookValidator = [ @@ -18,7 +18,7 @@ export const addPlaybookValidator = [ .exists() .notEmpty() .withMessage('Playbook name required') - .matches(playbookNameRegexp) + .matches(Validation?.playbookNameRegexp) .withMessage('Forbidden characters in playbook name'), validator, ]; diff --git a/server/src/services/containers/containerstats.validator.ts b/server/src/services/containers/containerstats.validator.ts index e773ec45..91b6f3a1 100644 --- a/server/src/services/containers/containerstats.validator.ts +++ b/server/src/services/containers/containerstats.validator.ts @@ -1,23 +1,22 @@ import { oneOf, param } from 'express-validator'; -import { ContainerStatsType } from 'ssm-shared-lib/distribution/enums/stats'; -import { ContainerStatus } from 'ssm-shared-lib/distribution/enums/status'; +import { SsmStatus, StatsType } from 'ssm-shared-lib'; import validator from '../../middlewares/validator'; export const getContainerStatByContainerIdValidator = [ param('id').exists().notEmpty().isString(), - param('type').exists().notEmpty().isString().isIn(Object.values(ContainerStatsType)), + param('type').exists().notEmpty().isString().isIn(Object.values(StatsType.ContainerStatsType)), validator, ]; export const getContainerStatsByContainerIdValidator = [ param('id').exists().notEmpty().isString(), - param('type').exists().notEmpty().isString().isIn(Object.values(ContainerStatsType)), + param('type').exists().notEmpty().isString().isIn(Object.values(StatsType.ContainerStatsType)), validator, ]; export const getNbContainersByStatusValidator = [ oneOf([ - param('status').exists().notEmpty().isString().isIn(Object.values(ContainerStatus)), + param('status').exists().notEmpty().isString().isIn(Object.values(SsmStatus.ContainerStatus)), param('status').exists().notEmpty().isString().equals('all'), ]), validator, diff --git a/server/src/services/devices/check-connection.validator.ts b/server/src/services/devices/check-connection.validator.ts index e19776a4..2822e198 100644 --- a/server/src/services/devices/check-connection.validator.ts +++ b/server/src/services/devices/check-connection.validator.ts @@ -1,5 +1,5 @@ import { body, param } from 'express-validator'; -import { SSHType } from 'ssm-shared-lib/distribution/enums/ansible'; +import { SsmAnsible } from 'ssm-shared-lib'; import validator from '../../middlewares/validator'; export const postCheckAnsibleConnectionValidator = [ @@ -12,7 +12,7 @@ export const postCheckAnsibleConnectionValidator = [ body('authType') .exists() .withMessage('authType in body is required') - .isIn([SSHType.UserPassword, SSHType.KeyBased]) + .isIn([SsmAnsible.SSHType.UserPassword, SsmAnsible.SSHType.KeyBased]) .withMessage('authType is not in enum value SSHType'), body('sshPort').exists().notEmpty().isNumeric().withMessage('sshPort is not a number'), body('unManaged').optional().isBoolean().withMessage('unManaged is not a boolean'), @@ -21,9 +21,21 @@ export const postCheckAnsibleConnectionValidator = [ .isString() .isURL({ protocols: ['http', 'https'], require_protocol: true, require_tld: false }) .withMessage('Master node url is not correctly formatted'), - body('sshKey').if(body('authType').equals(SSHType.KeyBased)).exists().notEmpty().isString(), - body('sshUser').if(body('authType').equals(SSHType.UserPassword)).exists().notEmpty().isString(), - body('sshPwd').if(body('authType').equals(SSHType.UserPassword)).exists().notEmpty().isString(), + body('sshKey') + .if(body('authType').equals(SsmAnsible.SSHType.KeyBased)) + .exists() + .notEmpty() + .isString(), + body('sshUser') + .if(body('authType').equals(SsmAnsible.SSHType.UserPassword)) + .exists() + .notEmpty() + .isString(), + body('sshPwd') + .if(body('authType').equals(SsmAnsible.SSHType.UserPassword)) + .exists() + .notEmpty() + .isString(), validator, ]; @@ -37,12 +49,24 @@ export const postCheckDockerConnectionValidator = [ body('authType') .exists() .withMessage('authType in body is required') - .isIn([SSHType.UserPassword, SSHType.KeyBased]) + .isIn([SsmAnsible.SSHType.UserPassword, SsmAnsible.SSHType.KeyBased]) .withMessage('authType is not in enum value SSHType'), body('sshPort').exists().notEmpty().isNumeric().withMessage('sshPort is not a number'), - body('sshKey').if(body('authType').equals(SSHType.KeyBased)).exists().notEmpty().isString(), - body('sshUser').if(body('authType').equals(SSHType.UserPassword)).exists().notEmpty().isString(), - body('sshPwd').if(body('authType').equals(SSHType.UserPassword)).exists().notEmpty().isString(), + body('sshKey') + .if(body('authType').equals(SsmAnsible.SSHType.KeyBased)) + .exists() + .notEmpty() + .isString(), + body('sshUser') + .if(body('authType').equals(SsmAnsible.SSHType.UserPassword)) + .exists() + .notEmpty() + .isString(), + body('sshPwd') + .if(body('authType').equals(SsmAnsible.SSHType.UserPassword)) + .exists() + .notEmpty() + .isString(), validator, ]; diff --git a/server/src/services/devices/device.ts b/server/src/services/devices/device.ts index 1cd391ca..4c49e073 100644 --- a/server/src/services/devices/device.ts +++ b/server/src/services/devices/device.ts @@ -1,6 +1,5 @@ import { parse } from 'url'; -import { AnsibleReservedExtraVarsKeys } from 'ssm-shared-lib/distribution/enums/settings'; -import { SsmStatus } from 'ssm-shared-lib'; +import { SsmStatus, SettingsKeys } from 'ssm-shared-lib'; import { API } from 'ssm-shared-lib'; import { BadRequestError, NotFoundError } from '../../core/api/ApiError'; import { SuccessResponse } from '../../core/api/ApiResponse'; @@ -34,7 +33,7 @@ export const addDevice = asyncHandler(async (req, res) => { sshKeyPass, } = req.body; if (masterNodeUrl) { - await setToCache(AnsibleReservedExtraVarsKeys.MASTER_NODE_URL, masterNodeUrl); + await setToCache(SettingsKeys.AnsibleReservedExtraVarsKeys.MASTER_NODE_URL, masterNodeUrl); } try { const isUnManagedDevice = unManaged === true; diff --git a/server/src/services/devices/device.validator.ts b/server/src/services/devices/device.validator.ts index 8ec951a4..e8787e73 100644 --- a/server/src/services/devices/device.validator.ts +++ b/server/src/services/devices/device.validator.ts @@ -1,5 +1,5 @@ import { body, param } from 'express-validator'; -import { SSHType } from 'ssm-shared-lib/distribution/enums/ansible'; +import { SsmAnsible } from 'ssm-shared-lib'; import validator from '../../middlewares/validator'; export const addDeviceValidator = [ @@ -12,7 +12,7 @@ export const addDeviceValidator = [ body('authType') .exists() .withMessage('authType in body is required') - .isIn([SSHType.UserPassword, SSHType.KeyBased]) + .isIn([SsmAnsible.SSHType.UserPassword, SsmAnsible.SSHType.KeyBased]) .withMessage('authType is not in enum value SSHType'), body('sshPort').exists().notEmpty().isNumeric().withMessage('sshPort is not a number'), body('unManaged').optional().isBoolean().withMessage('unManaged is not a boolean'), @@ -21,9 +21,21 @@ export const addDeviceValidator = [ .isString() .isURL({ protocols: ['http', 'https'], require_protocol: true, require_tld: false }) .withMessage('Master node url is not correctly formatted'), - body('sshKey').if(body('authType').equals(SSHType.KeyBased)).exists().notEmpty().isString(), - body('sshUser').if(body('authType').equals(SSHType.UserPassword)).exists().notEmpty().isString(), - body('sshPwd').if(body('authType').equals(SSHType.UserPassword)).exists().notEmpty().isString(), + body('sshKey') + .if(body('authType').equals(SsmAnsible.SSHType.KeyBased)) + .exists() + .notEmpty() + .isString(), + body('sshUser') + .if(body('authType').equals(SsmAnsible.SSHType.UserPassword)) + .exists() + .notEmpty() + .isString(), + body('sshPwd') + .if(body('authType').equals(SsmAnsible.SSHType.UserPassword)) + .exists() + .notEmpty() + .isString(), validator, ]; diff --git a/server/src/services/devices/deviceauth.validator.ts b/server/src/services/devices/deviceauth.validator.ts index f05fb3c2..ece8eeb5 100644 --- a/server/src/services/devices/deviceauth.validator.ts +++ b/server/src/services/devices/deviceauth.validator.ts @@ -1,6 +1,6 @@ import { body, param } from 'express-validator'; -import { AnsibleBecomeMethod, SSHType } from 'ssm-shared-lib/distribution/enums/ansible'; -import { privateKeyRegexp } from 'ssm-shared-lib/distribution/validation'; +import { SsmAnsible } from 'ssm-shared-lib'; +import { Validation } from 'ssm-shared-lib'; import validator from '../../middlewares/validator'; export const getDeviceAuthValidator = [ @@ -23,22 +23,34 @@ export const addOrUpdateDeviceAuthValidator = [ body('authType') .exists() .withMessage('authType in body is required') - .isIn(Object.values(SSHType)) + .isIn(Object.values(SsmAnsible.SSHType)) .withMessage('authType is not in enum value SSHType'), body('sshPort').exists().notEmpty().isNumeric().withMessage('sshPort is not a number'), body('sshKey') - .if(body('authType').equals(SSHType.KeyBased)) + .if(body('authType').equals(SsmAnsible.SSHType.KeyBased)) .exists() .notEmpty() .isString() - .matches(privateKeyRegexp), - body('sshUser').if(body('authType').equals(SSHType.KeyBased)).exists().notEmpty().isString(), - body('sshUser').if(body('authType').equals(SSHType.UserPassword)).exists().notEmpty().isString(), - body('sshPwd').if(body('authType').equals(SSHType.UserPassword)).exists().notEmpty().isString(), + .matches(Validation.privateKeyRegexp), + body('sshUser') + .if(body('authType').equals(SsmAnsible.SSHType.KeyBased)) + .exists() + .notEmpty() + .isString(), + body('sshUser') + .if(body('authType').equals(SsmAnsible.SSHType.UserPassword)) + .exists() + .notEmpty() + .isString(), + body('sshPwd') + .if(body('authType').equals(SsmAnsible.SSHType.UserPassword)) + .exists() + .notEmpty() + .isString(), body('becomeMethod') .exists() .notEmpty() - .isIn(Object.values(AnsibleBecomeMethod)) + .isIn(Object.values(SsmAnsible.AnsibleBecomeMethod)) .withMessage('becomeMethod is not supported'), validator, ]; @@ -53,12 +65,12 @@ export const updateDockerAuthValidator = [ body('customDockerSSH').optional().isBoolean(), body('dockerCustomAuthType') .optional() - .isIn(Object.values(SSHType)) + .isIn(Object.values(SsmAnsible.SSHType)) .withMessage('authType is not in enum value SSHType'), body('dockerCustomSshUser').optional().isString(), body('dockerCustomSshPwd').optional().isString(), body('dockerCustomSshKeyPass').optional().isString(), - body('dockerCustomSshKey').optional().isString().matches(privateKeyRegexp), + body('dockerCustomSshKey').optional().isString().matches(Validation.privateKeyRegexp), body('customDockerForcev6').optional().isBoolean(), body('customDockerForcev4').optional().isBoolean(), body('customDockerAgentForward').optional().isBoolean(), diff --git a/server/src/services/devices/devicestats.validator.ts b/server/src/services/devices/devicestats.validator.ts index 3416f2bb..7a13bd21 100644 --- a/server/src/services/devices/devicestats.validator.ts +++ b/server/src/services/devices/devicestats.validator.ts @@ -1,5 +1,5 @@ import { param } from 'express-validator'; -import { DeviceStatsType } from 'ssm-shared-lib/distribution/enums/stats'; +import { StatsType } from 'ssm-shared-lib'; import validator from '../../middlewares/validator'; export const updateDeviceAndAddDeviceStatValidator = [ @@ -22,7 +22,7 @@ export const getDeviceStatsByDeviceUuidValidator = [ param('type') .exists() .notEmpty() - .isIn(Object.values(DeviceStatsType)) + .isIn(Object.values(StatsType.DeviceStatsType)) .withMessage('Type is required'), validator, ]; @@ -37,7 +37,7 @@ export const getDeviceStatByDeviceUuidValidator = [ param('type') .exists() .notEmpty() - .isIn(Object.values(DeviceStatsType)) + .isIn(Object.values(StatsType.DeviceStatsType)) .withMessage('Type is required'), validator, ]; diff --git a/server/src/services/settings/containerstats.ts b/server/src/services/settings/containerstats.ts index e9435860..034280d1 100644 --- a/server/src/services/settings/containerstats.ts +++ b/server/src/services/settings/containerstats.ts @@ -1,5 +1,5 @@ -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; -import { InternalError } from '../../core/api/ApiError'; +import { SettingsKeys } from 'ssm-shared-lib'; +import { InternalError, NotFoundError } from '../../core/api/ApiError'; import { SuccessResponse } from '../../core/api/ApiResponse'; import { setToCache } from '../../data/cache'; import asyncHandler from '../../helpers/AsyncHandler'; @@ -11,13 +11,11 @@ export const postContainerStatsSettings = asyncHandler(async (req, res) => { logger.info(`[CONTROLLER] - POST - /settings/container-stats/${key}`); try { switch (key) { - case GeneralSettingsKeys.CONTAINER_STATS_RETENTION_IN_DAYS: - await setToCache(GeneralSettingsKeys.CONTAINER_STATS_RETENTION_IN_DAYS, value); + case SettingsKeys.GeneralSettingsKeys.CONTAINER_STATS_RETENTION_IN_DAYS: + await setToCache(SettingsKeys.GeneralSettingsKeys.CONTAINER_STATS_RETENTION_IN_DAYS, value); return new SuccessResponse(`${key} successfully updated`).send(res); default: - return res.status(404).send({ - success: false, - }); + return new NotFoundError(); } } catch (error: any) { logger.error(error); diff --git a/server/src/services/settings/containerstats.validator.ts b/server/src/services/settings/containerstats.validator.ts index 6e4ad262..02fd9a8d 100644 --- a/server/src/services/settings/containerstats.validator.ts +++ b/server/src/services/settings/containerstats.validator.ts @@ -1,5 +1,5 @@ import { body, param } from 'express-validator'; -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; import validator from '../../middlewares/validator'; export const postContainerStatsSettingsValidator = [ @@ -7,7 +7,7 @@ export const postContainerStatsSettingsValidator = [ .exists() .notEmpty() .withMessage('Key param is required') - .equals(GeneralSettingsKeys.CONTAINER_STATS_RETENTION_IN_DAYS) + .equals(SettingsKeys.GeneralSettingsKeys.CONTAINER_STATS_RETENTION_IN_DAYS) .withMessage('Unknown key'), body('value').exists().isNumeric().withMessage('Value must be numeric'), validator, diff --git a/server/src/services/settings/dashboard.ts b/server/src/services/settings/dashboard.ts index 250fbe39..d7458ecb 100644 --- a/server/src/services/settings/dashboard.ts +++ b/server/src/services/settings/dashboard.ts @@ -1,4 +1,4 @@ -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; import { InternalError } from '../../core/api/ApiError'; import { SuccessResponse } from '../../core/api/ApiResponse'; import { setToCache } from '../../data/cache'; @@ -11,11 +11,17 @@ export const postDashboardSettings = asyncHandler(async (req, res) => { logger.info(`[CONTROLLER] - POST - /settings/dashboard/${key}`); try { switch (key) { - case GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_CPU_IF_LOWER: - await setToCache(GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_CPU_IF_LOWER, value); + case SettingsKeys.GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_CPU_IF_LOWER: + await setToCache( + SettingsKeys.GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_CPU_IF_LOWER, + value, + ); return new SuccessResponse(`${key} successfully updated`).send(res); - case GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_MEM_IF_GREATER: - await setToCache(GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_MEM_IF_GREATER, value); + case SettingsKeys.GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_MEM_IF_GREATER: + await setToCache( + SettingsKeys.GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_MEM_IF_GREATER, + value, + ); return new SuccessResponse(`${key} successfully updated`).send(res); default: return res.status(404).send({ diff --git a/server/src/services/settings/dashboard.validator.ts b/server/src/services/settings/dashboard.validator.ts index 552b068b..61efcd4a 100644 --- a/server/src/services/settings/dashboard.validator.ts +++ b/server/src/services/settings/dashboard.validator.ts @@ -1,5 +1,5 @@ import { body, param } from 'express-validator'; -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; import validator from '../../middlewares/validator'; export const postDashboardSettingsValidator = [ @@ -7,7 +7,7 @@ export const postDashboardSettingsValidator = [ .exists() .notEmpty() .withMessage('Key param is required') - .isIn(Object.values(GeneralSettingsKeys)) + .isIn(Object.values(SettingsKeys.GeneralSettingsKeys)) .withMessage('Unknown key'), body('value').exists().isNumeric().withMessage('Value must be numeric'), validator, diff --git a/server/src/services/settings/devices.ts b/server/src/services/settings/devices.ts index b17d61a8..fa883784 100644 --- a/server/src/services/settings/devices.ts +++ b/server/src/services/settings/devices.ts @@ -1,4 +1,4 @@ -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; import { InternalError } from '../../core/api/ApiError'; import { SuccessResponse } from '../../core/api/ApiResponse'; import { setToCache } from '../../data/cache'; @@ -11,11 +11,17 @@ export const postDevicesSettings = asyncHandler(async (req, res) => { logger.info(`[CONTROLLER] - POST - /settings/devices/${key}`); try { switch (key) { - case GeneralSettingsKeys.CONSIDER_DEVICE_OFFLINE_AFTER_IN_MINUTES: - await setToCache(GeneralSettingsKeys.CONSIDER_DEVICE_OFFLINE_AFTER_IN_MINUTES, value); + case SettingsKeys.GeneralSettingsKeys.CONSIDER_DEVICE_OFFLINE_AFTER_IN_MINUTES: + await setToCache( + SettingsKeys.GeneralSettingsKeys.CONSIDER_DEVICE_OFFLINE_AFTER_IN_MINUTES, + value, + ); return new SuccessResponse(`${key} successfully updated`).send(res); - case GeneralSettingsKeys.REGISTER_DEVICE_STAT_EVERY_IN_SECONDS: - await setToCache(GeneralSettingsKeys.REGISTER_DEVICE_STAT_EVERY_IN_SECONDS, value); + case SettingsKeys.GeneralSettingsKeys.REGISTER_DEVICE_STAT_EVERY_IN_SECONDS: + await setToCache( + SettingsKeys.GeneralSettingsKeys.REGISTER_DEVICE_STAT_EVERY_IN_SECONDS, + value, + ); return new SuccessResponse(`${key} successfully updated`).send(res); default: return res.status(404).send({ diff --git a/server/src/services/settings/devices.validator.ts b/server/src/services/settings/devices.validator.ts index 961544cd..5a1ebeb3 100644 --- a/server/src/services/settings/devices.validator.ts +++ b/server/src/services/settings/devices.validator.ts @@ -1,5 +1,5 @@ import { body, param } from 'express-validator'; -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; import validator from '../../middlewares/validator'; export const postDevicesSettingsValidator = [ @@ -7,7 +7,7 @@ export const postDevicesSettingsValidator = [ .exists() .notEmpty() .withMessage('Key param is required') - .isIn(Object.values(GeneralSettingsKeys)) + .isIn(Object.values(SettingsKeys.GeneralSettingsKeys)) .withMessage('Unknown key'), body('value').exists().isNumeric().withMessage('Value must be numeric'), validator, diff --git a/server/src/services/settings/devicestats.ts b/server/src/services/settings/devicestats.ts index e8c1462e..ff19c6a7 100644 --- a/server/src/services/settings/devicestats.ts +++ b/server/src/services/settings/devicestats.ts @@ -1,4 +1,4 @@ -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; import { InternalError } from '../../core/api/ApiError'; import { SuccessResponse } from '../../core/api/ApiResponse'; import { setToCache } from '../../data/cache'; @@ -11,8 +11,8 @@ export const postDeviceStatsSettings = asyncHandler(async (req, res) => { logger.info(`[CONTROLLER] - POST - /settings/device-stats/${key}`); try { switch (key) { - case GeneralSettingsKeys.DEVICE_STATS_RETENTION_IN_DAYS: - await setToCache(GeneralSettingsKeys.DEVICE_STATS_RETENTION_IN_DAYS, value); + case SettingsKeys.GeneralSettingsKeys.DEVICE_STATS_RETENTION_IN_DAYS: + await setToCache(SettingsKeys.GeneralSettingsKeys.DEVICE_STATS_RETENTION_IN_DAYS, value); return new SuccessResponse(`${key} successfully updated`).send(res); default: return res.status(404).send({ diff --git a/server/src/services/settings/devicestats.validator.ts b/server/src/services/settings/devicestats.validator.ts index 502923d3..974f8394 100644 --- a/server/src/services/settings/devicestats.validator.ts +++ b/server/src/services/settings/devicestats.validator.ts @@ -1,5 +1,5 @@ import { body, param } from 'express-validator'; -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; import validator from '../../middlewares/validator'; export const postDeviceStatsSettingsValidator = [ @@ -7,7 +7,7 @@ export const postDeviceStatsSettingsValidator = [ .exists() .notEmpty() .withMessage('Key param is required') - .equals(GeneralSettingsKeys.DEVICE_STATS_RETENTION_IN_DAYS) + .equals(SettingsKeys.GeneralSettingsKeys.DEVICE_STATS_RETENTION_IN_DAYS) .withMessage('Unknown key'), body('value').exists().isNumeric().withMessage('Value must be numeric'), validator, diff --git a/server/src/services/settings/logs.ts b/server/src/services/settings/logs.ts index 896f7a5b..ab19f450 100644 --- a/server/src/services/settings/logs.ts +++ b/server/src/services/settings/logs.ts @@ -1,4 +1,4 @@ -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; import { InternalError } from '../../core/api/ApiError'; import { SuccessResponse } from '../../core/api/ApiResponse'; import { setToCache } from '../../data/cache'; @@ -11,14 +11,14 @@ export const postLogsSettings = asyncHandler(async (req, res) => { logger.info(`[CONTROLLER] - POST - /settings/logs/${key}`); try { switch (key) { - case GeneralSettingsKeys.CLEAN_UP_ANSIBLE_STATUSES_AND_TASKS_AFTER_IN_SECONDS: + case SettingsKeys.GeneralSettingsKeys.CLEAN_UP_ANSIBLE_STATUSES_AND_TASKS_AFTER_IN_SECONDS: await setToCache( - GeneralSettingsKeys.CLEAN_UP_ANSIBLE_STATUSES_AND_TASKS_AFTER_IN_SECONDS, + SettingsKeys.GeneralSettingsKeys.CLEAN_UP_ANSIBLE_STATUSES_AND_TASKS_AFTER_IN_SECONDS, value, ); return new SuccessResponse(`${key} successfully updated`).send(res); - case GeneralSettingsKeys.SERVER_LOG_RETENTION_IN_DAYS: - await setToCache(GeneralSettingsKeys.SERVER_LOG_RETENTION_IN_DAYS, value); + case SettingsKeys.GeneralSettingsKeys.SERVER_LOG_RETENTION_IN_DAYS: + await setToCache(SettingsKeys.GeneralSettingsKeys.SERVER_LOG_RETENTION_IN_DAYS, value); return new SuccessResponse(`${key} successfully updated`).send(res); default: return res.status(404).send({ diff --git a/server/src/services/settings/logs.validator.ts b/server/src/services/settings/logs.validator.ts index 741bdc61..b7272add 100644 --- a/server/src/services/settings/logs.validator.ts +++ b/server/src/services/settings/logs.validator.ts @@ -1,5 +1,5 @@ import { body, param } from 'express-validator'; -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; import validator from '../../middlewares/validator'; export const postLogsSettingsValidator = [ @@ -7,7 +7,7 @@ export const postLogsSettingsValidator = [ .exists() .notEmpty() .withMessage('Key param is required') - .isIn(Object.values(GeneralSettingsKeys)) + .isIn(Object.values(SettingsKeys.GeneralSettingsKeys)) .withMessage('Unknown key'), body('value').exists().isNumeric().withMessage('Value must be numeric'), validator, diff --git a/server/src/services/user/user.ts b/server/src/services/user/user.ts index 8beef97a..44e6cc4f 100644 --- a/server/src/services/user/user.ts +++ b/server/src/services/user/user.ts @@ -1,4 +1,4 @@ -import { GeneralSettingsKeys } from 'ssm-shared-lib/distribution/enums/settings'; +import { SettingsKeys } from 'ssm-shared-lib'; import { AuthFailureError } from '../../core/api/ApiError'; import { SuccessResponse } from '../../core/api/ApiResponse'; import { getAnsibleVersion } from '../../core/system/version'; @@ -16,28 +16,28 @@ export const getCurrentUser = asyncHandler(async (req, res) => { const { online, offline, totalCpu, totalMem, overview } = await DeviceUseCases.getDevicesOverview(); const considerDeviceOffline = await getIntConfFromCache( - GeneralSettingsKeys.CONSIDER_DEVICE_OFFLINE_AFTER_IN_MINUTES, + SettingsKeys.GeneralSettingsKeys.CONSIDER_DEVICE_OFFLINE_AFTER_IN_MINUTES, ); const serverLogRetention = await getIntConfFromCache( - GeneralSettingsKeys.SERVER_LOG_RETENTION_IN_DAYS, + SettingsKeys.GeneralSettingsKeys.SERVER_LOG_RETENTION_IN_DAYS, ); const containerStatsRetention = await getIntConfFromCache( - GeneralSettingsKeys.CONTAINER_STATS_RETENTION_IN_DAYS, + SettingsKeys.GeneralSettingsKeys.CONTAINER_STATS_RETENTION_IN_DAYS, ); const deviceStatsRetention = await getIntConfFromCache( - GeneralSettingsKeys.DEVICE_STATS_RETENTION_IN_DAYS, + SettingsKeys.GeneralSettingsKeys.DEVICE_STATS_RETENTION_IN_DAYS, ); const ansibleLogRetention = await getIntConfFromCache( - GeneralSettingsKeys.CLEAN_UP_ANSIBLE_STATUSES_AND_TASKS_AFTER_IN_SECONDS, + SettingsKeys.GeneralSettingsKeys.CLEAN_UP_ANSIBLE_STATUSES_AND_TASKS_AFTER_IN_SECONDS, ); const performanceMinMem = await getIntConfFromCache( - GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_MEM_IF_GREATER, + SettingsKeys.GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_MEM_IF_GREATER, ); const performanceMaxCpu = await getIntConfFromCache( - GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_CPU_IF_LOWER, + SettingsKeys.GeneralSettingsKeys.CONSIDER_PERFORMANCE_GOOD_CPU_IF_LOWER, ); const registerDeviceStatEvery = await getIntConfFromCache( - GeneralSettingsKeys.REGISTER_DEVICE_STAT_EVERY_IN_SECONDS, + SettingsKeys.GeneralSettingsKeys.REGISTER_DEVICE_STAT_EVERY_IN_SECONDS, ); const systemPerformance = await DashboardUseCase.getSystemPerformance(); diff --git a/server/src/use-cases/ContainerStatsUseCases.ts b/server/src/use-cases/ContainerStatsUseCases.ts index d2ada37f..80ab95d3 100644 --- a/server/src/use-cases/ContainerStatsUseCases.ts +++ b/server/src/use-cases/ContainerStatsUseCases.ts @@ -1,6 +1,6 @@ import { DateTime } from 'luxon'; import { API } from 'ssm-shared-lib'; -import { ContainerStatsType } from 'ssm-shared-lib/distribution/enums/stats'; +import { StatsType } from 'ssm-shared-lib'; import Container from '../data/database/model/Container'; import ContainerStatsRepo from '../data/database/repository/ContainerStatsRepo'; import logger from '../logger'; @@ -13,9 +13,9 @@ async function getStatByDeviceAndType( `[USECASE][CONTAINERSTATS] - getStatByDeviceAndType - type: ${type}, device: ${container.id}`, ); switch (type) { - case ContainerStatsType.CPU: + case StatsType.ContainerStatsType.CPU: return await ContainerStatsRepo.findStatByDeviceAndType(container, '$cpuUsedPercentage'); - case ContainerStatsType.MEM: + case StatsType.ContainerStatsType.MEM: return await ContainerStatsRepo.findStatByDeviceAndType(container, '$memUsedPercentage'); default: throw new Error('Unknown Type'); @@ -31,13 +31,13 @@ async function getStatsByDeviceAndType( `[USECASE][CONTAINERSTATS] - getStatsByDeviceAndType - type: ${type}, from: ${from}, container: ${container.id}`, ); switch (type) { - case ContainerStatsType.CPU: + case StatsType.ContainerStatsType.CPU: return await ContainerStatsRepo.findStatsByDeviceAndType( container, '$cpuUsedPercentage', from, ); - case ContainerStatsType.MEM: + case StatsType.ContainerStatsType.MEM: return await ContainerStatsRepo.findStatsByDeviceAndType( container, '$memUsedPercentage', @@ -68,5 +68,5 @@ async function getCpUAndMemAveragedStats() { export default { getStatByDeviceAndType, getStatsByDeviceAndType, - getCpUAndMemAveragedStats + getCpUAndMemAveragedStats, }; diff --git a/server/src/use-cases/DeviceStatsUseCases.ts b/server/src/use-cases/DeviceStatsUseCases.ts index 652ecb26..da2dbedf 100644 --- a/server/src/use-cases/DeviceStatsUseCases.ts +++ b/server/src/use-cases/DeviceStatsUseCases.ts @@ -1,6 +1,5 @@ import { DateTime } from 'luxon'; -import { API, SettingsKeys } from 'ssm-shared-lib'; -import { DeviceStatsType } from 'ssm-shared-lib/distribution/enums/stats'; +import { API, SettingsKeys, StatsType } from 'ssm-shared-lib'; import Device from '../data/database/model/Device'; import DeviceStat from '../data/database/model/DeviceStat'; import ContainerRepo from '../data/database/repository/ContainerRepo'; @@ -57,11 +56,11 @@ async function getStatsByDeviceAndType( `[USECASE][DEVICESTATS] - getStatsByDeviceAndType - type: ${type}, from: ${from}, device: ${device.uuid}`, ); switch (type) { - case DeviceStatsType.CPU: + case StatsType.DeviceStatsType.CPU: return await DeviceStatRepo.findStatsByDeviceAndType(device, '$cpuUsage', from); - case DeviceStatsType.MEM_USED: + case StatsType.DeviceStatsType.MEM_USED: return await DeviceStatRepo.findStatsByDeviceAndType(device, '$memUsedPercentage', from); - case DeviceStatsType.MEM_FREE: + case StatsType.DeviceStatsType.MEM_FREE: return await DeviceStatRepo.findStatsByDeviceAndType(device, '$memFreePercentage', from); default: throw new Error('Unknown Type'); @@ -78,16 +77,16 @@ async function getStatsByDevicesAndType( `[USECASE][DEVICESTATS] - findStatsByDevicesAndType - type: ${type}, from: ${from}, nb devices: ${devices.length}`, ); switch (type) { - case DeviceStatsType.CPU: + case StatsType.DeviceStatsType.CPU: return await DeviceStatRepo.findStatsByDevicesAndType(devices, '$cpuUsage', from, to); - case DeviceStatsType.MEM_USED: + case StatsType.DeviceStatsType.MEM_USED: return await DeviceStatRepo.findStatsByDevicesAndType( devices, '$memUsedPercentage', from, to, ); - case DeviceStatsType.MEM_FREE: + case StatsType.DeviceStatsType.MEM_FREE: return await DeviceStatRepo.findStatsByDevicesAndType( devices, '$memFreePercentage', @@ -109,21 +108,21 @@ async function getSingleAveragedStatsByDevicesAndType( `[USECASE][DEVICESTATS] - findSingleAveragedStatByDevicesAndType - type: ${type}, from: ${from}, to: ${to}, nb devices: ${devices.length}`, ); switch (type) { - case DeviceStatsType.CPU: + case StatsType.DeviceStatsType.CPU: return await DeviceStatRepo.findSingleAveragedStatByDevicesAndType( devices, '$cpuUsage', from, to, ); - case DeviceStatsType.MEM_USED: + case StatsType.DeviceStatsType.MEM_USED: return await DeviceStatRepo.findSingleAveragedStatByDevicesAndType( devices, '$memUsedPercentage', from, to, ); - case DeviceStatsType.MEM_FREE: + case StatsType.DeviceStatsType.MEM_FREE: return await DeviceStatRepo.findSingleAveragedStatByDevicesAndType( devices, '$memFreePercentage', @@ -143,13 +142,13 @@ async function getStatByDeviceAndType( `[USECASE][DEVICESTATS] - getStatByDeviceAndType - type: ${type}, device: ${device.uuid}`, ); switch (type) { - case DeviceStatsType.CPU: + case StatsType.DeviceStatsType.CPU: return await DeviceStatRepo.findStatByDeviceAndType(device, '$cpuUsage'); - case DeviceStatsType.MEM_USED: + case StatsType.DeviceStatsType.MEM_USED: return await DeviceStatRepo.findStatByDeviceAndType(device, '$memUsedPercentage'); - case DeviceStatsType.MEM_FREE: + case StatsType.DeviceStatsType.MEM_FREE: return await DeviceStatRepo.findStatByDeviceAndType(device, '$memFreePercentage'); - case DeviceStatsType.SERVICES: + case StatsType.DeviceStatsType.SERVICES: return [{ value: await ContainerRepo.countByDeviceId(device._id) }]; default: throw new Error('Unknown Type'); @@ -163,11 +162,11 @@ async function getSingleAveragedStatByType( ): Promise<[{ value: number }] | null> { logger.info(`[USECASE][DEVICESTATS] - getStatByType - type: ${type}`); switch (type) { - case DeviceStatsType.CPU: + case StatsType.DeviceStatsType.CPU: return await DeviceStatRepo.findSingleAveragedStatAndType('$cpuUsage', from, to); - case DeviceStatsType.MEM_USED: + case StatsType.DeviceStatsType.MEM_USED: return await DeviceStatRepo.findSingleAveragedStatAndType('$memUsedPercentage', from, to); - case DeviceStatsType.MEM_FREE: + case StatsType.DeviceStatsType.MEM_FREE: return await DeviceStatRepo.findSingleAveragedStatAndType('$memFreePercentage', from, to); default: throw new Error('Unknown Type'); diff --git a/server/src/use-cases/DeviceUseCases.ts b/server/src/use-cases/DeviceUseCases.ts index b3b59232..84ba7885 100644 --- a/server/src/use-cases/DeviceUseCases.ts +++ b/server/src/use-cases/DeviceUseCases.ts @@ -1,11 +1,6 @@ import DockerModem from 'docker-modem'; import Dockerode from 'dockerode'; -import { req } from 'pino-std-serializers'; -import { AuthenticationType } from 'ssh2'; -import { SSHType } from 'ssm-shared-lib/distribution/enums/ansible'; -import { AnsibleReservedExtraVarsKeys } from 'ssm-shared-lib/distribution/enums/settings'; -import { DeviceStatus } from 'ssm-shared-lib/distribution/enums/status'; -import { API } from 'ssm-shared-lib'; +import { API, SsmAnsible, SsmStatus, SettingsKeys } from 'ssm-shared-lib'; import { InternalError } from '../core/api/ApiError'; import { setToCache } from '../data/cache'; import Device, { DeviceModel } from '../data/database/model/Device'; @@ -27,11 +22,11 @@ import PlaybookUseCases from './PlaybookUseCases'; async function getDevicesOverview() { logger.info(`[USECASES][DEVICE] - getDevicesOverview`); const devices = await DeviceRepo.findAll(); - const offline = devices?.filter((e) => e.status === DeviceStatus.OFFLINE).length; - const online = devices?.filter((e) => e.status === DeviceStatus.ONLINE).length; + const offline = devices?.filter((e) => e.status === SsmStatus.DeviceStatus.OFFLINE).length; + const online = devices?.filter((e) => e.status === SsmStatus.DeviceStatus.ONLINE).length; const overview = devices?.map((e) => { return { - name: e.status !== DeviceStatus.UNMANAGED ? e.fqdn : e.ip, + name: e.status !== SsmStatus.DeviceStatus.UNMANAGED ? e.fqdn : e.ip, status: e.status, uuid: e.uuid, cpu: e.cpuSpeed, @@ -79,9 +74,9 @@ async function updateDeviceFromJson(deviceInfo: API.DeviceInfo, device: Device) device.versions = deviceInfo.os?.versionData; device.cpuSpeed = deviceInfo.cpu?.speed; device.mem = deviceInfo.mem?.memTotalMb; - if (device.status !== DeviceStatus.ONLINE) { + if (device.status !== SsmStatus.DeviceStatus.ONLINE) { await DeviceDownTimeEventRepo.closeDownTimeEvent(device); - device.status = DeviceStatus.ONLINE; + device.status = SsmStatus.DeviceStatus.ONLINE; } device.raspberry = deviceInfo.system?.raspberry; await DeviceRepo.update(device); @@ -133,7 +128,7 @@ async function checkAnsibleConnection( user: User, masterNodeUrl?: string, ip?: string, - authType?: SSHType, + authType?: SsmAnsible.SSHType, sshKey?: string, sshUser?: string, sshPwd?: string, @@ -143,7 +138,7 @@ async function checkAnsibleConnection( sshKeyPass?: string, ) { if (masterNodeUrl) { - await setToCache(AnsibleReservedExtraVarsKeys.MASTER_NODE_URL, masterNodeUrl); + await setToCache(SettingsKeys.AnsibleReservedExtraVarsKeys.MASTER_NODE_URL, masterNodeUrl); } if (sshKey) { await Shell.saveSshKey(sshKey, 'tmp'); @@ -154,7 +149,7 @@ async function checkAnsibleConnection( _id: 'tmp', ip, uuid: 'tmp', - status: DeviceStatus.REGISTERING, + status: SsmStatus.DeviceStatus.REGISTERING, }, authType, sshKey: sshKey ? await vaultEncrypt(sshKey, DEFAULT_VAULT_ID) : undefined, @@ -182,7 +177,7 @@ async function checkAnsibleConnection( async function checkDockerConnection( ip?: string, - authType?: SSHType, + authType?: SsmAnsible.SSHType, sshKey?: string, sshUser?: string, sshPwd?: string, @@ -197,7 +192,7 @@ async function checkDockerConnection( _id: 'tmp', ip, uuid: 'tmp', - status: DeviceStatus.REGISTERING, + status: SsmStatus.DeviceStatus.REGISTERING, }, authType, sshKey: sshKey ? await vaultEncrypt(sshKey, DEFAULT_VAULT_ID) : undefined, diff --git a/shared-lib/src/index.ts b/shared-lib/src/index.ts index ad4826b9..92e46ab7 100644 --- a/shared-lib/src/index.ts +++ b/shared-lib/src/index.ts @@ -1,4 +1,5 @@ export * as API from './types/api'; +export * as AnsibleAPI from './types/ansible'; export * as SettingsKeys from './enums/settings'; export * as SsmStatus from './enums/status'; export * as SsmAnsible from './enums/ansible'; diff --git a/shared-lib/src/types/ansible.ts b/shared-lib/src/types/ansible.ts new file mode 100644 index 00000000..4eadd35e --- /dev/null +++ b/shared-lib/src/types/ansible.ts @@ -0,0 +1,59 @@ +export type Data = { + repository: { + pulp_href: string; + pulp_created: string; + versions_href: string; + pulp_labels: { + [key: string]: string; + }; + latest_version_href: string; + name: string; + description: string; + retain_repo_versions: number; + remote: string; + }; + collection_version: { + pulp_href: string; + namespace: string; + name: string; + version: string; + requires_ansible: string; + pulp_created: string; + contents: { + name: string; + description: string; + content_type: string; + }[]; + dependencies: {}; + description?: string; + tags: { + name: string; + }[]; + }; + repository_version: string; + namespace_metadata: { + pulp_href: string; + name: string; + company: string; + description: string; + avatar_url: string; + }; + is_highest: boolean; + is_deprecated: boolean; + is_signed: boolean; +}; + +export type Links = { + first: string; + previous?: null | string; + next: string; + last: string; +}; + +export type JsonResponse = { + meta: { + count: number; + }; + links: Links; + data: Data[]; +};