182 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			182 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import React, { useState } from 'react';
 | |
| import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 | |
| import { faDatabase, faEye, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
 | |
| import Modal from '@/components/elements/Modal';
 | |
| import { Form, Formik, FormikHelpers } from 'formik';
 | |
| import Field from '@/components/elements/Field';
 | |
| import { object, string } from 'yup';
 | |
| import FlashMessageRender from '@/components/FlashMessageRender';
 | |
| import { ServerContext } from '@/state/server';
 | |
| import deleteServerDatabase from '@/api/server/deleteServerDatabase';
 | |
| import { httpErrorToHuman } from '@/api/http';
 | |
| import RotatePasswordButton from '@/components/server/databases/RotatePasswordButton';
 | |
| import Can from '@/components/elements/Can';
 | |
| import { ServerDatabase } from '@/api/server/getServerDatabases';
 | |
| import useFlash from '@/plugins/useFlash';
 | |
| import tw from 'twin.macro';
 | |
| import Button from '@/components/elements/Button';
 | |
| import Label from '@/components/elements/Label';
 | |
| import Input from '@/components/elements/Input';
 | |
| import GreyRowBox from '@/components/elements/GreyRowBox';
 | |
| 
 | |
| interface Props {
 | |
|     database: ServerDatabase;
 | |
|     className?: string;
 | |
| }
 | |
| 
 | |
| export default ({ database, className }: Props) => {
 | |
|     const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
 | |
|     const { addError, clearFlashes } = useFlash();
 | |
|     const [ visible, setVisible ] = useState(false);
 | |
|     const [ connectionVisible, setConnectionVisible ] = useState(false);
 | |
| 
 | |
|     const appendDatabase = ServerContext.useStoreActions(actions => actions.databases.appendDatabase);
 | |
|     const removeDatabase = ServerContext.useStoreActions(actions => actions.databases.removeDatabase);
 | |
| 
 | |
|     const schema = object().shape({
 | |
|         confirm: string()
 | |
|             .required('The database name must be provided.')
 | |
|             .oneOf([ database.name.split('_', 2)[1], database.name ], 'The database name must be provided.'),
 | |
|     });
 | |
| 
 | |
|     const submit = (values: { confirm: string }, { setSubmitting }: FormikHelpers<{ confirm: string }>) => {
 | |
|         clearFlashes();
 | |
|         deleteServerDatabase(uuid, database.id)
 | |
|             .then(() => {
 | |
|                 setVisible(false);
 | |
|                 setTimeout(() => removeDatabase(database.id), 150);
 | |
|             })
 | |
|             .catch(error => {
 | |
|                 console.error(error);
 | |
|                 setSubmitting(false);
 | |
|                 addError({ key: 'database:delete', message: httpErrorToHuman(error) });
 | |
|             });
 | |
|     };
 | |
| 
 | |
|     return (
 | |
|         <>
 | |
|             <Formik
 | |
|                 onSubmit={submit}
 | |
|                 initialValues={{ confirm: '' }}
 | |
|                 validationSchema={schema}
 | |
|                 isInitialValid={false}
 | |
|             >
 | |
|                 {
 | |
|                     ({ isSubmitting, isValid, resetForm }) => (
 | |
|                         <Modal
 | |
|                             visible={visible}
 | |
|                             dismissable={!isSubmitting}
 | |
|                             showSpinnerOverlay={isSubmitting}
 | |
|                             onDismissed={() => {
 | |
|                                 setVisible(false);
 | |
|                                 resetForm();
 | |
|                             }}
 | |
|                         >
 | |
|                             <FlashMessageRender byKey={'database:delete'} css={tw`mb-6`}/>
 | |
|                             <h2 css={tw`text-2xl mb-6`}>Confirm database deletion</h2>
 | |
|                             <p css={tw`text-sm`}>
 | |
|                                 Deleting a database is a permanent action, it cannot be undone. This will permanetly
 | |
|                                 delete the <strong>{database.name}</strong> database and remove all associated data.
 | |
|                             </p>
 | |
|                             <Form css={tw`m-0 mt-6`}>
 | |
|                                 <Field
 | |
|                                     type={'text'}
 | |
|                                     id={'confirm_name'}
 | |
|                                     name={'confirm'}
 | |
|                                     label={'Confirm Database Name'}
 | |
|                                     description={'Enter the database name to confirm deletion.'}
 | |
|                                 />
 | |
|                                 <div css={tw`mt-6 text-right`}>
 | |
|                                     <Button
 | |
|                                         type={'button'}
 | |
|                                         isSecondary
 | |
|                                         css={tw`mr-2`}
 | |
|                                         onClick={() => setVisible(false)}
 | |
|                                     >
 | |
|                                         Cancel
 | |
|                                     </Button>
 | |
|                                     <Button
 | |
|                                         type={'submit'}
 | |
|                                         color={'red'}
 | |
|                                         disabled={!isValid}
 | |
|                                     >
 | |
|                                         Delete Database
 | |
|                                     </Button>
 | |
|                                 </div>
 | |
|                             </Form>
 | |
|                         </Modal>
 | |
|                     )
 | |
|                 }
 | |
|             </Formik>
 | |
|             <Modal visible={connectionVisible} onDismissed={() => setConnectionVisible(false)}>
 | |
|                 <FlashMessageRender byKey={'database-connection-modal'} css={tw`mb-6`}/>
 | |
|                 <h3 css={tw`mb-6`}>Database connection details</h3>
 | |
|                 <div>
 | |
|                     <Label>Endpoint</Label>
 | |
|                     <Input type={'text'} readOnly value={database.connectionString} />
 | |
|                 </div>
 | |
|                 <div css={tw`mt-6`}>
 | |
|                     <Label>Connections from</Label>
 | |
|                     <Input type={'text'} readOnly value={database.allowConnectionsFrom} />
 | |
|                 </div>
 | |
|                 <div css={tw`mt-6`}>
 | |
|                     <Label>Username</Label>
 | |
|                     <Input type={'text'} readOnly value={database.username} />
 | |
|                 </div>
 | |
|                 <Can action={'database.view_password'}>
 | |
|                     <div css={tw`mt-6`}>
 | |
|                         <Label>Password</Label>
 | |
|                         <Input type={'text'} readOnly value={database.password}/>
 | |
|                     </div>
 | |
|                 </Can>
 | |
|                 <div css={tw`mt-6`}>
 | |
|                     <Label>JBDC Connection String</Label>
 | |
|                     <Input
 | |
|                         type={'text'}
 | |
|                         readOnly
 | |
|                         value={`jdbc:mysql://${database.username}:${database.password}@${database.connectionString}/${database.name}`}
 | |
|                     />
 | |
|                 </div>
 | |
|                 <div css={tw`mt-6 text-right`}>
 | |
|                     <Can action={'database.update'}>
 | |
|                         <RotatePasswordButton databaseId={database.id} onUpdate={appendDatabase}/>
 | |
|                     </Can>
 | |
|                     <Button isSecondary onClick={() => setConnectionVisible(false)}>
 | |
|                         Close
 | |
|                     </Button>
 | |
|                 </div>
 | |
|             </Modal>
 | |
|             <GreyRowBox $hoverable={false} className={className} css={tw`mb-2`}>
 | |
|                 <div css={tw`hidden md:block`}>
 | |
|                     <FontAwesomeIcon icon={faDatabase} fixedWidth/>
 | |
|                 </div>
 | |
|                 <div css={tw`flex-1 ml-4`}>
 | |
|                     <p css={tw`text-lg`}>{database.name}</p>
 | |
|                 </div>
 | |
|                 <div css={tw`ml-8 text-center hidden md:block`}>
 | |
|                     <p css={tw`text-sm`}>{database.connectionString}</p>
 | |
|                     <p css={tw`mt-1 text-2xs text-neutral-500 uppercase select-none`}>Endpoint</p>
 | |
|                 </div>
 | |
|                 <div css={tw`ml-8 text-center hidden md:block`}>
 | |
|                     <p css={tw`text-sm`}>{database.allowConnectionsFrom}</p>
 | |
|                     <p css={tw`mt-1 text-2xs text-neutral-500 uppercase select-none`}>Connections from</p>
 | |
|                 </div>
 | |
|                 <div css={tw`ml-8 text-center hidden md:block`}>
 | |
|                     <p css={tw`text-sm`}>{database.username}</p>
 | |
|                     <p css={tw`mt-1 text-2xs text-neutral-500 uppercase select-none`}>Username</p>
 | |
|                 </div>
 | |
|                 <div css={tw`ml-8`}>
 | |
|                     <Button isSecondary css={tw`mr-2`} onClick={() => setConnectionVisible(true)}>
 | |
|                         <FontAwesomeIcon icon={faEye} fixedWidth/>
 | |
|                     </Button>
 | |
|                     <Can action={'database.delete'}>
 | |
|                         <Button color={'red'} isSecondary onClick={() => setVisible(true)}>
 | |
|                             <FontAwesomeIcon icon={faTrashAlt} fixedWidth/>
 | |
|                         </Button>
 | |
|                     </Can>
 | |
|                 </div>
 | |
|             </GreyRowBox>
 | |
|         </>
 | |
|     );
 | |
| };
 | 
