Skip to content

Commit

Permalink
Merge pull request #767 from SquirrelCorporation/654-featadd-custom-a…
Browse files Browse the repository at this point in the history
…nsible-vault

[FEAT] 654 featadd custom ansible vault
  • Loading branch information
SquirrelDeveloper authored Feb 22, 2025
2 parents ca4438f + afa051e commit 4cfa6c2
Show file tree
Hide file tree
Showing 31 changed files with 638 additions and 52 deletions.
1 change: 1 addition & 0 deletions client/src/components/Template/Title.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum TitleColors {
ANSIBLE_CONF = '#c1660e',
COMPOSE = '#8e7418',
HOST_URL = '#368e18',
SECRET = '#16739a',
}

export type PageContainerTitleProps = {
Expand Down
95 changes: 91 additions & 4 deletions client/src/pages/Admin/Settings/components/PlaybooksSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import {
ErrorCircleSettings20Regular,
SimpleIconsGit,
StreamlineLocalStorageFolderSolid,
UserSecret,
} from '@/components/Icons/CustomIcons';
import Title, { TitleColors } from '@/components/Template/Title';
import CustomVaultModal from '@/pages/Admin/Settings/components/subcomponents/CustomVaultModal';
import PlaybooksGitRepositoryModal from '@/pages/Admin/Settings/components/subcomponents/PlaybooksGitRepositoryModal';
import PlaybooksLocalRepositoryModal from '@/pages/Admin/Settings/components/subcomponents/PlaybooksLocalRepositoryModal';
import { getAnsibleVaults } from '@/services/rest/ansible';
import {
getGitPlaybooksRepositories,
getPlaybooksLocalRepositories,
Expand Down Expand Up @@ -51,6 +54,7 @@ const PlaybookSettings: React.FC = () => {
const [localRepositories, setLocalRepositories] = useState<
API.LocalPlaybooksRepository[]
>([]);
const [customVaults, setCustomVaults] = useState<API.AnsibleVault[]>([]);

const asyncFetch = async () => {
await getGitPlaybooksRepositories().then((list) => {
Expand All @@ -63,16 +67,26 @@ const PlaybookSettings: React.FC = () => {
setLocalRepositories(list.data);
}
});
await getAnsibleVaults().then((list) => {
if (list?.data) {
setCustomVaults(list.data);
}
});
};

useEffect(() => {
void asyncFetch();
}, []);

const [gitModalOpened, setGitModalOpened] = useState<boolean>(false);
const [selectedGitRecord, setSelectedGitRecord] = useState<any>();
const [selectedGitRecord, setSelectedGitRecord] =
useState<API.GitPlaybooksRepository>();
const [localModalOpened, setLocalModalOpened] = useState<boolean>(false);
const [selectedLocalRecord, setSelectedLocalRecord] = useState<any>();
const [selectedLocalRecord, setSelectedLocalRecord] =
useState<API.LocalPlaybooksRepository>();
const [selectedVaultRecord, setSelectedVaultRecord] =
useState<API.AnsibleVault>();
const [vaultModalOpened, setVaultModalOpened] = useState<boolean>(false);

const onChange = async (newValue: number | null) => {
if (newValue !== null) {
Expand All @@ -93,14 +107,21 @@ const PlaybookSettings: React.FC = () => {
setModalOpened={setGitModalOpened}
modalOpened={gitModalOpened}
asyncFetch={asyncFetch}
selectedRecord={selectedGitRecord}
selectedRecord={selectedGitRecord as API.GitPlaybooksRepository}
/>
<PlaybooksLocalRepositoryModal
repositories={localRepositories}
setModalOpened={setLocalModalOpened}
modalOpened={localModalOpened}
asyncFetch={asyncFetch}
selectedRecord={selectedLocalRecord}
selectedRecord={selectedLocalRecord as API.LocalPlaybooksRepository}
/>
<CustomVaultModal
vaults={customVaults}
setModalOpened={setVaultModalOpened}
modalOpened={vaultModalOpened}
asyncFetch={asyncFetch}
selectedRecord={selectedVaultRecord}
/>
<Card
type="inner"
Expand Down Expand Up @@ -372,6 +393,72 @@ const PlaybookSettings: React.FC = () => {
dataSource={gitRepositories}
/>
</Card>
<Card
type="inner"
title={
<Title.SubTitle
title={'Vaults'}
backgroundColor={TitleColors.SECRET}
icon={<UserSecret />}
/>
}
style={{ marginTop: 16 }}
extra={
<Space>
<Button
type={'primary'}
icon={<AddCircleOutline />}
onClick={() => {
setSelectedVaultRecord(undefined);
setVaultModalOpened(true);
}}
>
Add a new vault
</Button>
<Tooltip title={'Add & update your Ansible Vaults'}>
<InfoCircleFilled />
</Tooltip>
</Space>
}
>
<ProList<API.AnsibleVault>
ghost={true}
itemCardProps={{
ghost: true,
}}
pagination={
customVaults?.length > 8
? {
defaultPageSize: 8,
showSizeChanger: false,
showQuickJumper: false,
}
: false
}
rowSelection={false}
grid={{ gutter: 0, xs: 1, sm: 2, md: 2, lg: 2, xl: 4, xxl: 4 }}
onItem={(record: API.AnsibleVault) => {
return {
onMouseEnter: () => {
console.log(record);
},
onClick: () => {
setSelectedVaultRecord(record);
setVaultModalOpened(true);
},
};
}}
metas={{
title: {
dataIndex: 'vaultId',
},
avatar: {
render: () => <Avatar src={<UserSecret />} />,
},
}}
dataSource={customVaults}
/>
</Card>
</Card>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { UserSecret } from '@/components/Icons/CustomIcons';
import {
deleteAnsibleVault,
postAnsibleVault,
updateAnsibleVault,
} from '@/services/rest/ansible';
import { DeleteOutlined } from '@ant-design/icons';
import { ModalForm, ProForm, ProFormText } from '@ant-design/pro-components';
import { Avatar, Button, message, Popconfirm } from 'antd';
import React, { FC, useState } from 'react';
import { API } from 'ssm-shared-lib';

type CustomVaultModalProps = {
selectedRecord?: Partial<API.CustomVault>;
modalOpened: boolean;
setModalOpened: any;
asyncFetch: () => Promise<void>;
vaults: API.CustomVault[];
};

const PlaybooksLocalRepositoryModal: FC<CustomVaultModalProps> = ({
selectedRecord,
modalOpened,
setModalOpened,
asyncFetch,
vaults,
}) => {
const [loading, setLoading] = useState(false);

const editionMode = selectedRecord
? [
<Popconfirm
key="delete"
title="Are you sure to delete this Vault?"
onConfirm={async () => {
setLoading(true);
if (selectedRecord && selectedRecord.vaultId) {
await deleteAnsibleVault(selectedRecord.vaultId)
.then(() =>
message.warning({
content: 'Vault deleted',
duration: 5,
}),
)
.finally(() => {
setModalOpened(false);
});
await asyncFetch();
}
setLoading(false);
}}
>
<Button icon={<DeleteOutlined />} danger loading={loading}>
Delete
</Button>
</Popconfirm>,
]
: [];
return (
<ModalForm<API.CustomVault>
title={
<>
<Avatar
size={50}
shape="square"
style={{
marginRight: 4,
backgroundColor: 'rgba(41,70,147,0.51)',
}}
src={<UserSecret />}
/>
{(selectedRecord && <>Edit vault {selectedRecord?.vaultId}</>) || (
<>Add a new vault</>
)}
</>
}
open={modalOpened}
autoFocusFirstInput
modalProps={{
destroyOnClose: true,
onCancel: () => setModalOpened(false),
}}
onFinish={async (values) => {
if (selectedRecord) {
await updateAnsibleVault(values);
setModalOpened(false);
await asyncFetch();
} else {
await postAnsibleVault(values);
setModalOpened(false);
await asyncFetch();
}
}}
submitter={{
searchConfig: {
submitText: 'Save',
},
render: (_, defaultDoms) => {
return [...editionMode, ...defaultDoms];
},
}}
>
<ProForm.Group>
<ProFormText
width={'md'}
name={'vaultId'}
label={'Vault ID'}
disabled={!!selectedRecord?.vaultId}
initialValue={selectedRecord?.vaultId}
rules={[
{ required: true },
{
validator(_, value) {
if (
vaults.find((e) => e.vaultId === value) === undefined ||
selectedRecord?.vaultId === value
) {
return Promise.resolve();
}
return Promise.reject('Vault ID already exists');
},
},
]}
/>
<ProFormText.Password
width={'md'}
name={'password'}
label={'Password'}
rules={[{ required: true }]}
/>
</ProForm.Group>
</ModalForm>
);
};

export default PlaybooksLocalRepositoryModal;
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SimpleIconsGit } from '@/components/Icons/CustomIcons';
import CustomVault from '@/pages/Admin/Settings/components/subcomponents/forms/CustomVault';
import DirectoryExclusionForm from '@/pages/Admin/Settings/components/subcomponents/forms/DirectoryExclusionForm';
import GitForm from '@/pages/Admin/Settings/components/subcomponents/forms/GitForm';
import {
Expand Down Expand Up @@ -220,6 +221,7 @@ const PlaybooksGitRepositoryModal: React.FC<
/>
<ProForm.Group>
<DirectoryExclusionForm selectedRecord={props.selectedRecord} />
<CustomVault selectedRecord={props.selectedRecord} />
</ProForm.Group>
</ModalForm>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { SetAction, SimpleIconsGit } from '@/components/Icons/CustomIcons';
import { SimpleIconsGit } from '@/components/Icons/CustomIcons';
import CustomVault from '@/pages/Admin/Settings/components/subcomponents/forms/CustomVault';
import DirectoryExclusionForm from '@/pages/Admin/Settings/components/subcomponents/forms/DirectoryExclusionForm';
import {
deletePlaybooksLocalRepository,
postPlaybooksLocalRepositories,
putPlaybooksLocalRepositories,
syncToDatabasePlaybooksLocalRepository,
} from '@/services/rest/playbooks-repositories';
import {
DeleteOutlined,
TableOutlined,
UnorderedListOutlined,
} from '@ant-design/icons';
import { DeleteOutlined, UnorderedListOutlined } from '@ant-design/icons';
import { ModalForm, ProForm, ProFormText } from '@ant-design/pro-components';
import { history } from '@umijs/max';
import { Avatar, Button, Dropdown, MenuProps, message, Popconfirm } from 'antd';
Expand Down Expand Up @@ -144,6 +141,7 @@ const PlaybooksLocalRepositoryModal: FC<LocalRepositoryModalProps> = (
...props.selectedRecord,
name: values.name,
directoryExclusionList: values.directoryExclusionList,
vaults: values.vaults,
},
);
props.setModalOpened(false);
Expand Down Expand Up @@ -193,6 +191,9 @@ const PlaybooksLocalRepositoryModal: FC<LocalRepositoryModalProps> = (
</ProForm.Group>
<ProForm.Group>
<DirectoryExclusionForm selectedRecord={props.selectedRecord} />
{!props.selectedRecord?.default && (
<CustomVault selectedRecord={props.selectedRecord} />
)}
</ProForm.Group>
</ModalForm>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { getAnsibleVaults } from '@/services/rest/ansible';
import { ProFormSelect } from '@ant-design/pro-form';
import React from 'react';
import { API } from 'ssm-shared-lib';

type CustomVaultProps = {
selectedRecord?:
| Partial<API.GitPlaybooksRepository | API.LocalPlaybooksRepository>
| undefined;
};

const CustomVault: React.FC<CustomVaultProps> = ({ selectedRecord }) => {
return (
<ProFormSelect
width={'md'}
tooltip={
'Vaults to be used by the playbooks. The vaults are defined in the Vaults section of this page.'
}
label={'Custom Vaults'}
name={'vaults'}
initialValue={selectedRecord?.vaults}
request={async () =>
(await getAnsibleVaults())?.data?.map((e: API.AnsibleVault) => ({
label: e.vaultId,
value: e._id,
}))
}
fieldProps={{
mode: 'tags',
}}
placeholder={'Your custom vaults'}
/>
);
};

export default CustomVault;
Loading

0 comments on commit 4cfa6c2

Please sign in to comment.