|
@@ -1,7 +1,7 @@
|
|
|
// material-ui
|
|
|
import * as React from 'react';
|
|
|
import { Alert, Button, Stack, Typography } from '@mui/material';
|
|
|
-import { WarningOutlined, PlusCircleOutlined, CopyOutlined, ExclamationCircleOutlined , SyncOutlined, UserOutlined, KeyOutlined, ArrowUpOutlined, FileZipOutlined } from '@ant-design/icons';
|
|
|
+import { WarningOutlined, PlusCircleOutlined, CopyOutlined, ExclamationCircleOutlined, SyncOutlined, UserOutlined, KeyOutlined, ArrowUpOutlined, FileZipOutlined } from '@ant-design/icons';
|
|
|
import Table from '@mui/material/Table';
|
|
|
import TableBody from '@mui/material/TableBody';
|
|
|
import TableCell from '@mui/material/TableCell';
|
|
@@ -28,13 +28,13 @@ import NewDockerService from './newService';
|
|
|
import yaml from 'js-yaml';
|
|
|
|
|
|
function checkIsOnline() {
|
|
|
- API.isOnline().then((res) => {
|
|
|
- window.location.reload();
|
|
|
- }).catch((err) => {
|
|
|
- setTimeout(() => {
|
|
|
- checkIsOnline();
|
|
|
- }, 1000);
|
|
|
- });
|
|
|
+ API.isOnline().then((res) => {
|
|
|
+ window.location.reload();
|
|
|
+ }).catch((err) => {
|
|
|
+ setTimeout(() => {
|
|
|
+ checkIsOnline();
|
|
|
+ }, 1000);
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
const preStyle = {
|
|
@@ -63,30 +63,25 @@ const preStyle = {
|
|
|
marginRight: '0',
|
|
|
}
|
|
|
|
|
|
-const DockerComposeImport = ({refresh}) => {
|
|
|
- const [step, setStep] = useState(0);
|
|
|
- const [isLoading, setIsLoading] = useState(false);
|
|
|
- const [openModal, setOpenModal] = useState(false);
|
|
|
- const [dockerCompose, setDockerCompose] = useState('');
|
|
|
- const [service, setService] = useState({});
|
|
|
- const [ymlError, setYmlError] = useState('');
|
|
|
-
|
|
|
- useEffect(() => {
|
|
|
- if(dockerCompose === '') {
|
|
|
- return;
|
|
|
- }
|
|
|
+const DockerComposeImport = ({ refresh }) => {
|
|
|
+ const [step, setStep] = useState(0);
|
|
|
+ const [isLoading, setIsLoading] = useState(false);
|
|
|
+ const [openModal, setOpenModal] = useState(false);
|
|
|
+ const [dockerCompose, setDockerCompose] = useState('');
|
|
|
+ const [service, setService] = useState({});
|
|
|
+ const [ymlError, setYmlError] = useState('');
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (dockerCompose === '') {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ setYmlError('');
|
|
|
+ let doc;
|
|
|
+ let newService = {};
|
|
|
+ try {
|
|
|
+ doc = yaml.load(dockerCompose);
|
|
|
|
|
|
- setYmlError('');
|
|
|
- let doc;
|
|
|
- let newService = {};
|
|
|
- try {
|
|
|
- doc = yaml.load(dockerCompose);
|
|
|
- } catch (e) {
|
|
|
- console.log(e);
|
|
|
- setYmlError(e.message);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
if (typeof doc === 'object' && doc !== null && Object.keys(doc).length > 0 &&
|
|
|
!doc.services && !doc.networks && !doc.volumes) {
|
|
|
doc = {
|
|
@@ -94,52 +89,66 @@ const DockerComposeImport = ({refresh}) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+
|
|
|
// convert to the proper format
|
|
|
- if(doc.services) {
|
|
|
+ if (doc.services) {
|
|
|
Object.keys(doc.services).forEach((key) => {
|
|
|
+
|
|
|
// convert volumes
|
|
|
- if(doc.services[key].volumes) {
|
|
|
- let volumes = [];
|
|
|
- doc.services[key].volumes.forEach((volume) => {
|
|
|
- let volumeSplit = volume.split(':');
|
|
|
- let volumeObj = {
|
|
|
- Source: volumeSplit[0],
|
|
|
- Target: volumeSplit[1],
|
|
|
- Type: volume[0] === '/' ? 'bind' : 'volume',
|
|
|
- };
|
|
|
- volumes.push(volumeObj);
|
|
|
- });
|
|
|
- doc.services[key].volumes = volumes;
|
|
|
+ if (doc.services[key].volumes) {
|
|
|
+ if(Array.isArray(doc.services[key].volumes)) {
|
|
|
+ let volumes = [];
|
|
|
+ doc.services[key].volumes.forEach((volume) => {
|
|
|
+ if (typeof volume === 'object') {
|
|
|
+ volumes.push(volume);
|
|
|
+ } else {
|
|
|
+ let volumeSplit = volume.split(':');
|
|
|
+ let volumeObj = {
|
|
|
+ Source: volumeSplit[0],
|
|
|
+ Target: volumeSplit[1],
|
|
|
+ Type: volume[0] === '/' ? 'bind' : 'volume',
|
|
|
+ };
|
|
|
+ volumes.push(volumeObj);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ doc.services[key].volumes = volumes;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// convert expose
|
|
|
- if(doc.services[key].expose) {
|
|
|
+ if (doc.services[key].expose) {
|
|
|
doc.services[key].expose = doc.services[key].expose.map((port) => {
|
|
|
- return ''+port;
|
|
|
+ return '' + port;
|
|
|
})
|
|
|
}
|
|
|
|
|
|
//convert user
|
|
|
- if(doc.services[key].user) {
|
|
|
+ if (doc.services[key].user) {
|
|
|
doc.services[key].user = '' + doc.services[key].user;
|
|
|
}
|
|
|
|
|
|
// convert labels:
|
|
|
- if(doc.services[key].labels) {
|
|
|
- if(Array.isArray(doc.services[key].labels)) {
|
|
|
+ if (doc.services[key].labels) {
|
|
|
+ if (Array.isArray(doc.services[key].labels)) {
|
|
|
let labels = {};
|
|
|
doc.services[key].labels.forEach((label) => {
|
|
|
const [key, value] = label.split('=');
|
|
|
- labels[''+key] = ''+value;
|
|
|
+ labels['' + key] = '' + value;
|
|
|
+ });
|
|
|
+ doc.services[key].labels = labels;
|
|
|
+ }
|
|
|
+ if (typeof doc.services[key].labels == 'object') {
|
|
|
+ let labels = {};
|
|
|
+ Object.keys(doc.services[key].labels).forEach((keylabel) => {
|
|
|
+ labels['' + keylabel] = '' + doc.services[key].labels[keylabel];
|
|
|
});
|
|
|
doc.services[key].labels = labels;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// convert environment
|
|
|
- if(doc.services[key].environment) {
|
|
|
- if(!Array.isArray(doc.services[key].environment)) {
|
|
|
+ if (doc.services[key].environment) {
|
|
|
+ if (!Array.isArray(doc.services[key].environment)) {
|
|
|
let environment = [];
|
|
|
Object.keys(doc.services[key].environment).forEach((keyenv) => {
|
|
|
environment.push(keyenv + '=' + doc.services[key].environment[keyenv]);
|
|
@@ -149,100 +158,155 @@ const DockerComposeImport = ({refresh}) => {
|
|
|
}
|
|
|
|
|
|
// convert network
|
|
|
- if(doc.services[key].networks) {
|
|
|
- if(Array.isArray(doc.services[key].networks)) {
|
|
|
+ if (doc.services[key].networks) {
|
|
|
+ if (Array.isArray(doc.services[key].networks)) {
|
|
|
let networks = {};
|
|
|
doc.services[key].networks.forEach((network) => {
|
|
|
- networks[''+network] = {};
|
|
|
+ if (typeof network === 'object') {
|
|
|
+ networks['' + network.name] = network;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ networks['' + network] = {};
|
|
|
});
|
|
|
doc.services[key].networks = networks;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ensure container_name
|
|
|
- if(!doc.services[key].container_name) {
|
|
|
+ if (!doc.services[key].container_name) {
|
|
|
doc.services[key].container_name = key;
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- setService(doc);
|
|
|
- }, [dockerCompose]);
|
|
|
-
|
|
|
- return <>
|
|
|
- <Dialog open={openModal} onClose={() => setOpenModal(false)}>
|
|
|
- <DialogTitle>Import Docker Compose</DialogTitle>
|
|
|
- <DialogContent>
|
|
|
- <DialogContentText>
|
|
|
- {step === 0 && <Stack spacing={2}>
|
|
|
- <Alert severity="warning" icon={<WarningOutlined />}>
|
|
|
- This is a highly experimental feature. It is recommended to use with caution.
|
|
|
- </Alert>
|
|
|
-
|
|
|
- <UploadButtons
|
|
|
- accept='.yml,.yaml'
|
|
|
- OnChange={(e) => {
|
|
|
- const file = e.target.files[0];
|
|
|
- const reader = new FileReader();
|
|
|
- reader.onload = (e) => {
|
|
|
- setDockerCompose(e.target.result);
|
|
|
- };
|
|
|
- reader.readAsText(file);
|
|
|
- }}
|
|
|
- />
|
|
|
-
|
|
|
- <div style={{color: 'red'}}>
|
|
|
- {ymlError}
|
|
|
- </div>
|
|
|
-
|
|
|
- <TextField
|
|
|
- multiline
|
|
|
- placeholder='Paste your docker-compose.yml here or use the file upload button.'
|
|
|
- fullWidth
|
|
|
- value={dockerCompose}
|
|
|
- onChange={(e) => setDockerCompose(e.target.value)}
|
|
|
- sx={preStyle}
|
|
|
- InputProps={{
|
|
|
- sx: {
|
|
|
- color: '#EEE',
|
|
|
- }
|
|
|
- }}
|
|
|
- rows={20}></TextField>
|
|
|
- </Stack>}
|
|
|
- {step === 1 && <Stack spacing={2}>
|
|
|
- <NewDockerService service={service} refresh={refresh}/>
|
|
|
- </Stack>}
|
|
|
- </DialogContentText>
|
|
|
- </DialogContent>
|
|
|
- {!isLoading && <DialogActions>
|
|
|
- <Button onClick={() => {
|
|
|
- setOpenModal(false);
|
|
|
- setStep(0);
|
|
|
- setDockerCompose('');
|
|
|
- setYmlError('');
|
|
|
- }}>Close</Button>
|
|
|
- <Button onClick={() => {
|
|
|
- if(step === 0) {
|
|
|
- setStep(1);
|
|
|
- } else {
|
|
|
- setStep(0);
|
|
|
- }
|
|
|
- }}>
|
|
|
- {step === 0 && 'Next'}
|
|
|
- {step === 1 && 'Back'}
|
|
|
- </Button>
|
|
|
- </DialogActions>}
|
|
|
- </Dialog>
|
|
|
-
|
|
|
- <ResponsiveButton
|
|
|
- color="primary"
|
|
|
- onClick={() => setOpenModal(true)}
|
|
|
- variant="outlined"
|
|
|
- startIcon={<ArrowUpOutlined />}
|
|
|
- >
|
|
|
- Import Docker Compose
|
|
|
- </ResponsiveButton>
|
|
|
- </>;
|
|
|
+ // convert networks
|
|
|
+ if (doc.networks) {
|
|
|
+ if (Array.isArray(doc.networks)) {
|
|
|
+ let networks = {};
|
|
|
+ doc.networks.forEach((network) => {
|
|
|
+ if (typeof network === 'object') {
|
|
|
+ networks['' + network.name] = network;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ networks['' + network] = {};
|
|
|
+ });
|
|
|
+ doc.networks = networks;
|
|
|
+ } else {
|
|
|
+ let networks = {};
|
|
|
+ Object.keys(doc.networks).forEach((key) => {
|
|
|
+ networks['' + key] = doc.networks[key] || {};
|
|
|
+ });
|
|
|
+ doc.networks = networks;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // convert volumes
|
|
|
+ if (doc.volumes) {
|
|
|
+ if (Array.isArray(doc.volumes)) {
|
|
|
+ let volumes = {};
|
|
|
+ doc.volumes.forEach((volume) => {
|
|
|
+ if(!volume) {
|
|
|
+ volume = {};
|
|
|
+ }
|
|
|
+ if (typeof volume === 'object') {
|
|
|
+ volumes['' + volume.name] = volume;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ volumes['' + volume] = {};
|
|
|
+ });
|
|
|
+ doc.volumes = volumes;
|
|
|
+ } else {
|
|
|
+ let volumes = {};
|
|
|
+ Object.keys(doc.volumes).forEach((key) => {
|
|
|
+ volumes['' + key] = doc.volumes[key] || {};
|
|
|
+ });
|
|
|
+ doc.volumes = volumes;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (e) {
|
|
|
+ console.log(e);
|
|
|
+ setYmlError(e.message);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ setService(doc);
|
|
|
+ }, [dockerCompose]);
|
|
|
+
|
|
|
+ return <>
|
|
|
+ <Dialog open={openModal} onClose={() => setOpenModal(false)}>
|
|
|
+ <DialogTitle>Import Docker Compose</DialogTitle>
|
|
|
+ <DialogContent>
|
|
|
+ <DialogContentText>
|
|
|
+ {step === 0 && <Stack spacing={2}>
|
|
|
+ <Alert severity="warning" icon={<WarningOutlined />}>
|
|
|
+ This is a highly experimental feature. It is recommended to use with caution.
|
|
|
+ </Alert>
|
|
|
+
|
|
|
+ <UploadButtons
|
|
|
+ accept='.yml,.yaml'
|
|
|
+ OnChange={(e) => {
|
|
|
+ const file = e.target.files[0];
|
|
|
+ const reader = new FileReader();
|
|
|
+ reader.onload = (e) => {
|
|
|
+ setDockerCompose(e.target.result);
|
|
|
+ };
|
|
|
+ reader.readAsText(file);
|
|
|
+ }}
|
|
|
+ />
|
|
|
+
|
|
|
+ <div style={{ color: 'red' }}>
|
|
|
+ {ymlError}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <TextField
|
|
|
+ multiline
|
|
|
+ placeholder='Paste your docker-compose.yml here or use the file upload button.'
|
|
|
+ fullWidth
|
|
|
+ value={dockerCompose}
|
|
|
+ onChange={(e) => setDockerCompose(e.target.value)}
|
|
|
+ sx={preStyle}
|
|
|
+ InputProps={{
|
|
|
+ sx: {
|
|
|
+ color: '#EEE',
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ rows={20}></TextField>
|
|
|
+ </Stack>}
|
|
|
+ {step === 1 && <Stack spacing={2}>
|
|
|
+ <NewDockerService service={service} refresh={refresh} />
|
|
|
+ </Stack>}
|
|
|
+ </DialogContentText>
|
|
|
+ </DialogContent>
|
|
|
+ {!isLoading && <DialogActions>
|
|
|
+ <Button onClick={() => {
|
|
|
+ setOpenModal(false);
|
|
|
+ setStep(0);
|
|
|
+ setDockerCompose('');
|
|
|
+ setYmlError('');
|
|
|
+ }}>Close</Button>
|
|
|
+ <Button onClick={() => {
|
|
|
+ if (step === 0) {
|
|
|
+ setStep(1);
|
|
|
+ } else {
|
|
|
+ setStep(0);
|
|
|
+ }
|
|
|
+ }}>
|
|
|
+ {step === 0 && 'Next'}
|
|
|
+ {step === 1 && 'Back'}
|
|
|
+ </Button>
|
|
|
+ </DialogActions>}
|
|
|
+ </Dialog>
|
|
|
+
|
|
|
+ <ResponsiveButton
|
|
|
+ color="primary"
|
|
|
+ onClick={() => setOpenModal(true)}
|
|
|
+ variant="outlined"
|
|
|
+ startIcon={<ArrowUpOutlined />}
|
|
|
+ >
|
|
|
+ Import Docker Compose
|
|
|
+ </ResponsiveButton>
|
|
|
+ </>;
|
|
|
};
|
|
|
|
|
|
export default DockerComposeImport;
|