Update to 0.7.0-unstable5
This commit is contained in:
parent
8d113b5337
commit
aa9a72b787
25 changed files with 1636 additions and 108 deletions
|
@ -1,3 +1,10 @@
|
|||
## Version 0.7.0
|
||||
- Add Cosmos Market
|
||||
- Fix issue with docker compose timeout healthcheck as string, and inverted ports
|
||||
|
||||
## Version 0.6.0
|
||||
// todo
|
||||
|
||||
## Version 0.6.0
|
||||
- OpenID support!
|
||||
- Add hostname check when adding new routes to Cosmos
|
||||
|
|
|
@ -73,4 +73,18 @@
|
|||
|
||||
.loading-image {
|
||||
background: url('/assets/images/icons/cosmos_gray.png') no-repeat center center;
|
||||
}
|
||||
|
||||
.raw-table table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.raw-table table th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.raw-table table th, .raw-table table td {
|
||||
padding: 5px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
|
@ -183,7 +183,7 @@ const RouteManagement = ({ routeConfig, routeNames, TargetContainer, noControls
|
|||
: <CosmosInputText
|
||||
name="Target"
|
||||
label={formik.values.Mode == "PROXY" ? "Target URL" : "Target Folder Path"}
|
||||
placeholder={formik.values.Mode == "PROXY" ? "localhost:8080" : "/path/to/my/app"}
|
||||
placeholder={formik.values.Mode == "PROXY" ? "http://localhost:8080" : "/path/to/my/app"}
|
||||
formik={formik}
|
||||
/>
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import RestartModal from './restart';
|
|||
import { WarningOutlined, PlusCircleOutlined, CopyOutlined, ExclamationCircleOutlined , SyncOutlined, UserOutlined, KeyOutlined } from '@ant-design/icons';
|
||||
import { CosmosCheckbox, CosmosFormDivider, CosmosInputPassword, CosmosInputText, CosmosSelect } from './formShortcuts';
|
||||
import CountrySelect, { countries } from '../../../components/countrySelect';
|
||||
import { DnsChallengeComp } from '../../../utils/dns-challenge-comp';
|
||||
|
||||
|
||||
const ConfigManagement = () => {
|
||||
|
@ -70,6 +71,7 @@ const ConfigManagement = () => {
|
|||
UseWildcardCertificate: config.HTTPConfig.UseWildcardCertificate,
|
||||
HTTPSCertificateMode: config.HTTPConfig.HTTPSCertificateMode,
|
||||
DNSChallengeProvider: config.HTTPConfig.DNSChallengeProvider,
|
||||
DNSChallengeConfig: config.HTTPConfig.DNSChallengeConfig,
|
||||
|
||||
Email_Enabled: config.EmailConfig.Enabled,
|
||||
Email_Host: config.EmailConfig.Host,
|
||||
|
@ -103,6 +105,7 @@ const ConfigManagement = () => {
|
|||
UseWildcardCertificate: values.UseWildcardCertificate,
|
||||
HTTPSCertificateMode: values.HTTPSCertificateMode,
|
||||
DNSChallengeProvider: values.DNSChallengeProvider,
|
||||
DNSChallengeConfig: values.DNSChallengeConfig,
|
||||
},
|
||||
EmailConfig: {
|
||||
...config.EmailConfig,
|
||||
|
@ -371,6 +374,18 @@ const ConfigManagement = () => {
|
|||
<Alert severity="info">For security reasons, It is not possible to remotely change the Private keys of any certificates on your instance. It is advised to manually edit the config file, or better, use Environment Variables to store them.</Alert>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
|
||||
<Field
|
||||
type="checkbox"
|
||||
name="GenerateMissingAuthCert"
|
||||
as={FormControlLabel}
|
||||
control={<Checkbox size="large" />}
|
||||
label="Generate missing Authentication Certificates automatically (Default: true)"
|
||||
/>
|
||||
</Stack>
|
||||
</Grid>
|
||||
|
||||
<CosmosSelect
|
||||
name="HTTPSCertificateMode"
|
||||
label="HTTPS Certificates"
|
||||
|
@ -400,26 +415,15 @@ const ConfigManagement = () => {
|
|||
|
||||
{
|
||||
formik.values.HTTPSCertificateMode === "LETSENCRYPT" && (
|
||||
<CosmosInputText
|
||||
<DnsChallengeComp
|
||||
label="Pick a DNS provider (if you are using a DNS Challenge, otherwise leave empty)"
|
||||
name="DNSChallengeProvider"
|
||||
label="DNS provider (if you are using a DNS Challenge)"
|
||||
configName="DNSChallengeConfig"
|
||||
formik={formik}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
|
||||
<Field
|
||||
type="checkbox"
|
||||
name="GenerateMissingAuthCert"
|
||||
as={FormControlLabel}
|
||||
control={<Checkbox size="large" />}
|
||||
label="Generate missing Authentication Certificates automatically (Default: true)"
|
||||
/>
|
||||
</Stack>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<h4>Authentication Public Key</h4>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
|
||||
|
|
|
@ -33,7 +33,7 @@ import defaultport from '../../servapps/defaultport.json';
|
|||
|
||||
import * as API from '../../../api';
|
||||
|
||||
export function CosmosContainerPicker({formik, nameOnly, lockTarget, TargetContainer, onTargetChange}) {
|
||||
export function CosmosContainerPicker({formik, nameOnly, lockTarget, TargetContainer, onTargetChange, label = "Container Name", name = "Target"}) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [containers, setContainers] = React.useState([]);
|
||||
const [hasPublicPorts, setHasPublicPorts] = React.useState(false);
|
||||
|
@ -42,8 +42,6 @@ export function CosmosContainerPicker({formik, nameOnly, lockTarget, TargetConta
|
|||
const [portsOptions, setPortsOptions] = React.useState(null);
|
||||
const loading = options === null;
|
||||
|
||||
const name = "Target"
|
||||
const label = "Container Name"
|
||||
let targetResult = {
|
||||
container: 'null',
|
||||
port: "",
|
||||
|
@ -51,6 +49,7 @@ export function CosmosContainerPicker({formik, nameOnly, lockTarget, TargetConta
|
|||
}
|
||||
|
||||
let preview = formik.values[name];
|
||||
console.log(name)
|
||||
|
||||
if(preview) {
|
||||
let protocols = preview.split("://")
|
||||
|
|
|
@ -151,8 +151,8 @@ const MarketPage = () => {
|
|||
height: '100%',
|
||||
}}></Link>
|
||||
|
||||
<Stack direction="row" spacing={2} style={{ height: '100%', overflow: 'hidden' }} justifyContent="flex-end">
|
||||
<Stack direction="column" spacing={3} style={{ height: '100%' }} sx={{
|
||||
<Stack direction="row" spacing={2} style={{ height: '100%'}} justifyContent="flex-end">
|
||||
<Stack direction="column" spacing={3} style={{ height: '100%', overflow: "auto"}} sx={{
|
||||
backgroundColor: isDark ? '#1A2027' : '#fff',
|
||||
padding: '80px 80px',
|
||||
width: '100%',
|
||||
|
@ -193,9 +193,7 @@ const MarketPage = () => {
|
|||
<div><strong>compose:</strong> <LinkMUI href={openedApp.compose}>{openedApp.compose}</LinkMUI></div>
|
||||
</div>
|
||||
|
||||
<div dangerouslySetInnerHTML={{ __html: openedApp.longDescription }} style={{
|
||||
overflow: 'hidden',
|
||||
}}></div>
|
||||
<div dangerouslySetInnerHTML={{ __html: openedApp.longDescription }}></div>
|
||||
|
||||
<div>
|
||||
<DockerComposeImport installerInit defaultName={openedApp.name} dockerComposeInit={openedApp.compose} />
|
||||
|
|
|
@ -20,6 +20,7 @@ import AnimateButton from '../../components/@extended/AnimateButton';
|
|||
import { Box } from '@mui/system';
|
||||
import { pull } from 'lodash';
|
||||
import { isDomain } from '../../utils/indexs';
|
||||
import { DnsChallengeComp } from '../../utils/dns-challenge-comp';
|
||||
// ================================|| LOGIN ||================================ //
|
||||
|
||||
const debounce = (func, wait) => {
|
||||
|
@ -272,6 +273,7 @@ const NewInstall = () => {
|
|||
HTTPSCertificateMode: "LETSENCRYPT",
|
||||
UseWildcardCertificate: false,
|
||||
DNSChallengeProvider: '',
|
||||
DNSChallengeConfig: {},
|
||||
}}
|
||||
validationSchema={Yup.object().shape({
|
||||
SSLEmail: Yup.string().when('HTTPSCertificateMode', {
|
||||
|
@ -305,6 +307,7 @@ const NewInstall = () => {
|
|||
TLSCert: values.HTTPSCertificateMode === "PROVIDED" ? values.TLSCert : '',
|
||||
Hostname: values.Hostname,
|
||||
DNSChallengeProvider: values.DNSChallengeProvider,
|
||||
DNSChallengeConfig: values.DNSChallengeConfig,
|
||||
});
|
||||
if(res.status == "OK") {
|
||||
setStatus({ success: true });
|
||||
|
@ -350,10 +353,10 @@ const NewInstall = () => {
|
|||
Cosmos after this installer. See doc here: <a target="_blank" href="https://go-acme.github.io/lego/dns/">https://go-acme.github.io/lego/dns/</a>
|
||||
</Alert>
|
||||
)}
|
||||
<CosmosInputText
|
||||
label={"DNS Provider (only set if you want to use the DNS challenge)"}
|
||||
<DnsChallengeComp
|
||||
label="Pick a DNS provider (if you are using a DNS Challenge, otherwise leave empty)"
|
||||
name="DNSChallengeProvider"
|
||||
placeholder={"provider"}
|
||||
configName="DNSChallengeConfig"
|
||||
formik={formik}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -26,10 +26,14 @@ import ResponsiveButton from '../../../components/responseiveButton';
|
|||
import UploadButtons from '../../../components/fileUpload';
|
||||
import NewDockerService from './newService';
|
||||
import yaml from 'js-yaml';
|
||||
import { CosmosCollapse, CosmosFormDivider, CosmosInputText } from '../../config/users/formShortcuts';
|
||||
import { CosmosCollapse, CosmosFormDivider, CosmosInputPassword, CosmosInputText } from '../../config/users/formShortcuts';
|
||||
import VolumeContainerSetup from './volumes';
|
||||
import DockerContainerSetup from './setup';
|
||||
import whiskers from 'whiskers';
|
||||
import {version} from '../../../../../package.json';
|
||||
import cmp from 'semver-compare';
|
||||
import { HostnameChecker } from '../../../utils/routes';
|
||||
import { CosmosContainerPicker } from '../../config/users/containerPicker';
|
||||
|
||||
function checkIsOnline() {
|
||||
API.isOnline().then((res) => {
|
||||
|
@ -67,8 +71,8 @@ const preStyle = {
|
|||
marginRight: '0',
|
||||
}
|
||||
|
||||
const isNewerVersion = (v1, v2) => {
|
||||
return false;
|
||||
const isNewerVersion = (minver) => {
|
||||
return cmp(version, minver) === -1;
|
||||
}
|
||||
|
||||
const getHostnameFromName = (name) => {
|
||||
|
@ -224,6 +228,11 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
if (!doc.services[key].container_name) {
|
||||
doc.services[key].container_name = key;
|
||||
}
|
||||
|
||||
// convert healthcheck
|
||||
if (doc.services[key].healthcheck && typeof doc.services[key].healthcheck.timeout === 'string') {
|
||||
doc.services[key].healthcheck.timeout = parseInt(doc.services[key].healthcheck.timeout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -293,7 +302,8 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
|
||||
try {
|
||||
if (installer) {
|
||||
const rendered = whiskers.render(dockerCompose, {
|
||||
console.log(hostnames)
|
||||
const rendered = whiskers.render(dockerCompose.replace(/{StaticServiceName}/ig, serviceName), {
|
||||
ServiceName: serviceName,
|
||||
Hostnames: hostnames,
|
||||
Context: context,
|
||||
|
@ -333,13 +343,34 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
|
||||
let newVolumes = [];
|
||||
|
||||
// SET DEFAULT CONTEXT
|
||||
let newContext = {};
|
||||
if (jsoned['cosmos-installer'] && jsoned['cosmos-installer'].form) {
|
||||
jsoned['cosmos-installer'].form.forEach((field) => {
|
||||
[field.name, field['name-container']].forEach((fieldName) => {
|
||||
if(typeof context[fieldName] === "undefined" && typeof field.initialValue !== "undefined") {
|
||||
newContext[fieldName] = field.initialValue;
|
||||
} else if (typeof context[fieldName] !== "undefined") {
|
||||
newContext[fieldName] = context[fieldName];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if(JSON.stringify(Object.keys(newContext)) !== JSON.stringify(Object.keys(context)))
|
||||
setContext({...newContext});
|
||||
|
||||
Object.keys(jsoned.services).forEach((key) => {
|
||||
// APPLY OVERRIDE
|
||||
if (overrides[key]) {
|
||||
// prevent customizing static volumes
|
||||
if (jsoned.services[key].volumes && jsoned['cosmos-installer'] && jsoned['cosmos-installer']['frozen-volumes']) {
|
||||
jsoned['cosmos-installer']['frozen-volumes'].forEach((volume) => {
|
||||
delete overrides[key].volumes;
|
||||
jsoned['cosmos-installer']['frozen-volumes'].forEach((volumeName) => {
|
||||
const keyVolume = overrides[key].volumes.findIndex((v) => {
|
||||
console.log(v)
|
||||
return v.source === volumeName;
|
||||
});
|
||||
delete overrides[key].volumes[keyVolume];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -363,7 +394,7 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
// CREATE NEW VOLUMES
|
||||
if (jsoned.services[key].volumes) {
|
||||
jsoned.services[key].volumes.forEach((volume) => {
|
||||
if (typeof volume === 'object' && !volume.existing) {
|
||||
if (typeof volume === 'object' && !volume.source.startsWith('/') && !volume.existing) {
|
||||
newVolumes.push(volume);
|
||||
} else if (typeof volume === 'object' && volume.existing) {
|
||||
delete volume.existing;
|
||||
|
@ -396,6 +427,7 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
setYmlError(null);
|
||||
setOverrides({});
|
||||
setHostnames({});
|
||||
setContext({});
|
||||
setDockerCompose('');
|
||||
setInstaller(installerInit);
|
||||
setServiceName(defaultName || 'default-name');
|
||||
|
@ -452,15 +484,61 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
<TextField label="Service Name" value={serviceName} onChange={(e) => setServiceName(e.target.value)} />
|
||||
|
||||
{service['cosmos-installer'] && service['cosmos-installer'].form.map((formElement) => {
|
||||
return formElement.type === 'checkbox' ? <FormControlLabel
|
||||
control={<Checkbox checked={context[formElement.name] || formElement.initialValue} onChange={(e) => {
|
||||
return formElement.type === 'checkbox' ?
|
||||
<FormControlLabel
|
||||
control={<Checkbox checked={context[formElement.name]} onChange={(e) => {
|
||||
setContext({ ...context, [formElement.name]: e.target.checked });
|
||||
}
|
||||
} />}
|
||||
label={formElement.label}
|
||||
/> : <TextField
|
||||
/> : (formElement.type === 'password' || formElement.type === 'email') ?
|
||||
<TextField
|
||||
label={formElement.label}
|
||||
value={context[formElement.name] || formElement.initialValue}
|
||||
value={context[formElement.name]}
|
||||
type={formElement.type}
|
||||
onChange={(e) => {
|
||||
setContext({ ...context, [formElement.name]: e.target.value });
|
||||
}
|
||||
} />
|
||||
: formElement.type === 'hostname' ?
|
||||
<>
|
||||
<TextField
|
||||
label={formElement.label}
|
||||
value={context[formElement.name]}
|
||||
onChange={(e) => {
|
||||
setContext({ ...context, [formElement.name]: e.target.value });
|
||||
}
|
||||
} />
|
||||
<HostnameChecker hostname={context[formElement.name]} />
|
||||
</>
|
||||
: formElement.type === 'container' || formElement.type === 'container-full' ?
|
||||
<CosmosContainerPicker
|
||||
name={formElement.name}
|
||||
formik={{
|
||||
values: {
|
||||
[formElement.name] : context[formElement.name]
|
||||
},
|
||||
errors: {},
|
||||
setFieldValue: (name, value) => {
|
||||
setContext({ ...context, [formElement.name]: value });
|
||||
},
|
||||
}}
|
||||
nameOnly={formElement.type === 'container'}
|
||||
label={formElement.label}
|
||||
onTargetChange={(_, name) => {
|
||||
console.log(formElement['name-container'], name)
|
||||
console.log(context)
|
||||
setContext({ ...context, [formElement['name-container']]: name });
|
||||
}}
|
||||
/>
|
||||
: formElement.type === 'error' || formElement.type === 'info' || formElement.type === 'warning' ?
|
||||
<Alert severity={formElement.type}>
|
||||
{formElement.label}
|
||||
</Alert>
|
||||
|
||||
: <TextField
|
||||
label={formElement.label}
|
||||
value={context[formElement.name]}
|
||||
onChange={(e) => {
|
||||
setContext({ ...context, [formElement.name]: e.target.value });
|
||||
}
|
||||
|
@ -479,6 +557,7 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
hostnames[serviceIndex][hostname.name].host = e.target.value;
|
||||
setHostnames({...hostnames});
|
||||
}} />
|
||||
<HostnameChecker hostname={hostname.host} />
|
||||
</>
|
||||
})
|
||||
})}
|
||||
|
@ -518,7 +597,7 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
containerInfo={{
|
||||
HostConfig: {
|
||||
Binds: [],
|
||||
Mounts: Object.keys(value.volumes).map(k => {
|
||||
Mounts: value.volumes && Object.keys(value.volumes).map(k => {
|
||||
return {
|
||||
Type: value.volumes[k].type || (k.startsWith('/') ? 'bind' : 'volume'),
|
||||
Source: value.volumes[k].source || "",
|
||||
|
@ -560,14 +639,23 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
</Stack>}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
{!isLoading && <DialogActions>
|
||||
{(installerInit && service.minVersion && isNewerVersion(service.minVersion)) ?
|
||||
<Alert severity="error" icon={<WarningOutlined />}>
|
||||
This service requires a newer version of Cosmos. Please update Cosmos to install this service.
|
||||
</Alert>
|
||||
:
|
||||
(!isLoading && <DialogActions>
|
||||
<Button onClick={() => {
|
||||
setOpenModal(false);
|
||||
setStep(0);
|
||||
setDockerCompose('');
|
||||
setYmlError('');
|
||||
setInstaller(false);
|
||||
}}>Cancel</Button>
|
||||
setServiceName('');
|
||||
setContext({});
|
||||
setHostnames({});
|
||||
setOverrides({});
|
||||
}}>Close</Button>
|
||||
<Button disabled={!dockerCompose || ymlError} onClick={() => {
|
||||
if (step === 0) {
|
||||
setStep(1);
|
||||
|
@ -578,24 +666,20 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
|
|||
{step === 0 && 'Next'}
|
||||
{step === 1 && 'Back'}
|
||||
</Button>
|
||||
</DialogActions>}
|
||||
</DialogActions>)}
|
||||
</Dialog>
|
||||
|
||||
{(installerInit && service.minVersion && isNewerVersion(service.minVersion)) ?
|
||||
<Alert severity="error" icon={<WarningOutlined />}>
|
||||
This service requires a newer version of Cosmos. Please update Cosmos to install this service.
|
||||
</Alert>
|
||||
: <ResponsiveButton
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
openModalFunc();
|
||||
}}
|
||||
variant={(installerInit ? "contained" : "outlined")}
|
||||
startIcon={(installerInit ? <ArrowDownOutlined /> : <ArrowUpOutlined />)}
|
||||
>
|
||||
{installerInit ? 'Install' : 'Import Compose File'}
|
||||
</ResponsiveButton>
|
||||
}
|
||||
<ResponsiveButton
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
openModalFunc();
|
||||
}}
|
||||
variant={(installerInit ? "contained" : "outlined")}
|
||||
startIcon={(installerInit ? <ArrowDownOutlined /> : <ArrowUpOutlined />)}
|
||||
>
|
||||
{installerInit ? 'Install' : 'Import Compose File'}
|
||||
</ResponsiveButton>
|
||||
|
||||
</>;
|
||||
};
|
||||
|
||||
|
|
|
@ -146,18 +146,6 @@ const NetworkContainerSetup = ({ config, containerInfo, refresh, newContainer, O
|
|||
<div>
|
||||
{formik.values.ports.map((port, idx) => (
|
||||
<Grid container key={idx}>
|
||||
<Grid item xs={4} style={{ padding }}>
|
||||
<TextField
|
||||
label="Container Port"
|
||||
fullWidth
|
||||
value={port.port}
|
||||
onChange={(e) => {
|
||||
const newports = [...formik.values.ports];
|
||||
newports[idx].port = e.target.value;
|
||||
formik.setFieldValue('ports', newports);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4} style={{ padding }}>
|
||||
<TextField
|
||||
fullWidth
|
||||
|
@ -170,6 +158,18 @@ const NetworkContainerSetup = ({ config, containerInfo, refresh, newContainer, O
|
|||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={4} style={{ padding }}>
|
||||
<TextField
|
||||
label="Container Port"
|
||||
fullWidth
|
||||
value={port.port}
|
||||
onChange={(e) => {
|
||||
const newports = [...formik.values.ports];
|
||||
newports[idx].port = e.target.value;
|
||||
formik.setFieldValue('ports', newports);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={3} style={{ padding }}>
|
||||
<TextField
|
||||
className="px-2 my-2"
|
||||
|
|
|
@ -60,6 +60,8 @@ const NewDockerService = ({service, refresh}) => {
|
|||
const [openModal, setOpenModal] = React.useState(false);
|
||||
const preRef = React.useRef(null);
|
||||
|
||||
const installer = {...service['cosmos-installer']};
|
||||
service = {...service};
|
||||
delete service['cosmos-installer'];
|
||||
|
||||
React.useEffect(() => {
|
||||
|
@ -99,6 +101,9 @@ const NewDockerService = ({service, refresh}) => {
|
|||
>Create</LoadingButton>}
|
||||
{isDone && <Stack spacing={1}>
|
||||
<Alert severity="success">Service Created!</Alert>
|
||||
{installer && installer['post-install'] && installer['post-install'].map(m =>{
|
||||
return <Alert severity={m.type}>{m.label}</Alert>
|
||||
})}
|
||||
{needsRestart && <Alert severity="warning">Cosmos needs to be restarted to apply changes to the URLs</Alert>}
|
||||
{needsRestart &&
|
||||
<Button
|
||||
|
|
84
client/src/utils/dns-challenge-comp.jsx
Normal file
84
client/src/utils/dns-challenge-comp.jsx
Normal file
|
@ -0,0 +1,84 @@
|
|||
import dnsList from './dns-list.json';
|
||||
import dnsConfig from './dns-config.json';
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Checkbox,
|
||||
Divider,
|
||||
FormControlLabel,
|
||||
Grid,
|
||||
InputLabel,
|
||||
OutlinedInput,
|
||||
Stack,
|
||||
Typography,
|
||||
FormHelperText,
|
||||
TextField,
|
||||
MenuItem,
|
||||
AccordionSummary,
|
||||
AccordionDetails,
|
||||
Accordion,
|
||||
Chip,
|
||||
Box,
|
||||
FormControl,
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
Alert,
|
||||
|
||||
} from '@mui/material';
|
||||
import { Field } from 'formik';
|
||||
import { DownOutlined, UpOutlined } from '@ant-design/icons';
|
||||
|
||||
import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons';
|
||||
import { useState } from 'react';
|
||||
import { CosmosCollapse, CosmosSelect } from '../pages/config/users/formShortcuts';
|
||||
|
||||
|
||||
export const DnsChallengeComp = ({ name, configName, style, multiline, type, placeholder, onChange, label, formik }) => {
|
||||
const [dnsSetConfig, setDnsSetConfig] = useState({});
|
||||
|
||||
return <><CosmosSelect
|
||||
name={name}
|
||||
label={label}
|
||||
formik={formik}
|
||||
onChange={(e) => {
|
||||
onChange && onChange(e);
|
||||
}}
|
||||
options={dnsList.map((dns) => ([dns,dns]))}
|
||||
/>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Stack spacing={2}>
|
||||
{formik.values[name] && dnsConfig[formik.values[name]] &&<>
|
||||
{dnsConfig[formik.values[name]].vars.length > 0 && <CosmosCollapse title="DNS Challenge setup">
|
||||
|
||||
<Stack spacing={2}>
|
||||
<Alert severity="info">
|
||||
Please be careful you are filling the correct values. Check the doc if unsure. Leave blank unused variables. <br />
|
||||
Doc link: <a href={dnsConfig[formik.values[name]].url} target="_blank">{dnsConfig[formik.values[name]].url}</a>
|
||||
</Alert>
|
||||
<div className="raw-table">
|
||||
<div dangerouslySetInnerHTML={{__html: dnsConfig[formik.values[name]].docs}}></div>
|
||||
</div>
|
||||
{dnsConfig[formik.values[name]].vars.map((dnsVar) => <div>
|
||||
{dnsVar}:
|
||||
<OutlinedInput
|
||||
type={type ? type : 'text'}
|
||||
value={formik.values[configName][dnsVar] || ''}
|
||||
onChange={(...ar) => {
|
||||
const newConfig = {
|
||||
...formik.values[configName],
|
||||
[dnsVar]: ar[0].target.value
|
||||
};
|
||||
formik.setFieldValue(configName, newConfig);
|
||||
}}
|
||||
placeholder={"leave blank if unused"}
|
||||
fullWidth
|
||||
/>
|
||||
</div>)}
|
||||
</Stack>
|
||||
</CosmosCollapse>}
|
||||
</>}
|
||||
</Stack>
|
||||
</Grid>
|
||||
</>;
|
||||
}
|
1072
client/src/utils/dns-config.json
Normal file
1072
client/src/utils/dns-config.json
Normal file
File diff suppressed because it is too large
Load diff
117
client/src/utils/dns-list.json
Normal file
117
client/src/utils/dns-list.json
Normal file
|
@ -0,0 +1,117 @@
|
|||
[
|
||||
"acme-dns",
|
||||
"alidns",
|
||||
"allinkl",
|
||||
"arvancloud",
|
||||
"azure",
|
||||
"auroradns",
|
||||
"autodns",
|
||||
"bindman",
|
||||
"bluecat",
|
||||
"brandit",
|
||||
"bunny",
|
||||
"checkdomain",
|
||||
"civo",
|
||||
"clouddns",
|
||||
"cloudflare",
|
||||
"cloudns",
|
||||
"cloudxns",
|
||||
"conoha",
|
||||
"constellix",
|
||||
"derak",
|
||||
"desec",
|
||||
"designate",
|
||||
"digitalocean",
|
||||
"dnshomede",
|
||||
"dnsimple",
|
||||
"dnsmadeeasy",
|
||||
"dnspod",
|
||||
"dode",
|
||||
"domeneshop",
|
||||
"dreamhost",
|
||||
"duckdns",
|
||||
"dyn",
|
||||
"dynu",
|
||||
"easydns",
|
||||
"edgedns",
|
||||
"epik",
|
||||
"exoscale",
|
||||
"freemyip",
|
||||
"gandi",
|
||||
"gandiv5",
|
||||
"gcloud",
|
||||
"gcore",
|
||||
"glesys",
|
||||
"godaddy",
|
||||
"googledomains",
|
||||
"hetzner",
|
||||
"hostingde",
|
||||
"hosttech",
|
||||
"httpreq",
|
||||
"hurricane",
|
||||
"ibmcloud",
|
||||
"iij",
|
||||
"iijdpf",
|
||||
"infoblox",
|
||||
"infomaniak",
|
||||
"internetbs",
|
||||
"inwx",
|
||||
"ionos",
|
||||
"iwantmyname",
|
||||
"joker",
|
||||
"liara",
|
||||
"lightsail",
|
||||
"linode",
|
||||
"liquidweb",
|
||||
"luadns",
|
||||
"loopia",
|
||||
"mydnsjp",
|
||||
"mythicbeasts",
|
||||
"namecheap",
|
||||
"namedotcom",
|
||||
"namesilo",
|
||||
"nearlyfreespeech",
|
||||
"netcup",
|
||||
"netlify",
|
||||
"nicmanager",
|
||||
"nifcloud",
|
||||
"njalla",
|
||||
"nodion",
|
||||
"ns1",
|
||||
"oraclecloud",
|
||||
"otc",
|
||||
"ovh",
|
||||
"pdns",
|
||||
"plesk",
|
||||
"porkbun",
|
||||
"rackspace",
|
||||
"regru",
|
||||
"rfc2136",
|
||||
"rimuhosting",
|
||||
"route53",
|
||||
"safedns",
|
||||
"sakuracloud",
|
||||
"scaleway",
|
||||
"selectel",
|
||||
"servercow",
|
||||
"simply",
|
||||
"sonic",
|
||||
"stackpath",
|
||||
"tencentcloud",
|
||||
"transip",
|
||||
"ultradns",
|
||||
"variomedia",
|
||||
"vegadns",
|
||||
"vercel",
|
||||
"versio",
|
||||
"vinyldns",
|
||||
"vkcloud",
|
||||
"vscale",
|
||||
"vultr",
|
||||
"websupport",
|
||||
"wedos",
|
||||
"yandex",
|
||||
"yandexcloud",
|
||||
"zoneee",
|
||||
"zonomi"
|
||||
]
|
|
@ -27,23 +27,11 @@ export function isDomain(hostname) {
|
|||
return true;
|
||||
}
|
||||
|
||||
export function debounce(func, wait, immediate) {
|
||||
var timeout;
|
||||
|
||||
return () => {
|
||||
var context = this, args = arguments;
|
||||
|
||||
var later = () => {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
|
||||
var callNow = immediate && !timeout;
|
||||
|
||||
export const debounce = (func, wait) => {
|
||||
let timeout;
|
||||
return function (...args) {
|
||||
const context = this;
|
||||
clearTimeout(timeout);
|
||||
|
||||
timeout = setTimeout(later, wait);
|
||||
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
};
|
||||
timeout = setTimeout(() => func.apply(context, args), wait);
|
||||
};
|
||||
};
|
|
@ -3,9 +3,14 @@ import demoicons from './icons.demo.json';
|
|||
import logogray from '../assets/images/icons/cosmos_gray.png';
|
||||
|
||||
import * as Yup from 'yup';
|
||||
import { Alert, Grid } from '@mui/material';
|
||||
import { debounce, isDomain } from './indexs';
|
||||
|
||||
import * as API from '../api';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export const sanitizeRoute = (_route) => {
|
||||
let route = {..._route};
|
||||
let route = { ..._route };
|
||||
|
||||
if (!route.UseHost) {
|
||||
route.Host = "";
|
||||
|
@ -13,14 +18,14 @@ export const sanitizeRoute = (_route) => {
|
|||
if (!route.UsePathPrefix) {
|
||||
route.PathPrefix = "";
|
||||
}
|
||||
|
||||
|
||||
route.Name = route.Name.trim();
|
||||
|
||||
if(!route.SmartShield) {
|
||||
if (!route.SmartShield) {
|
||||
route.SmartShield = {};
|
||||
}
|
||||
|
||||
if(typeof route._SmartShield_Enabled !== "undefined") {
|
||||
if (typeof route._SmartShield_Enabled !== "undefined") {
|
||||
route.SmartShield.Enabled = route._SmartShield_Enabled;
|
||||
delete route._SmartShield_Enabled;
|
||||
}
|
||||
|
@ -46,13 +51,13 @@ export const getFullOrigin = (route) => {
|
|||
const isDemo = import.meta.env.MODE === 'demo';
|
||||
|
||||
export const getFaviconURL = (route) => {
|
||||
if(isDemo) {
|
||||
if (isDemo) {
|
||||
if (route.Mode == "STATIC")
|
||||
return Folder;
|
||||
return demoicons[route.Name] || logogray;
|
||||
}
|
||||
|
||||
if(!route) {
|
||||
if (!route) {
|
||||
return logogray;
|
||||
}
|
||||
|
||||
|
@ -60,7 +65,7 @@ export const getFaviconURL = (route) => {
|
|||
return '/cosmos/api/favicon?q=' + encodeURIComponent(url)
|
||||
}
|
||||
|
||||
if(route.Mode == "SERVAPP" || route.Mode == "PROXY") {
|
||||
if (route.Mode == "SERVAPP" || route.Mode == "PROXY") {
|
||||
return addRemote(route.Target)
|
||||
} else if (route.Mode == "STATIC") {
|
||||
return Folder;
|
||||
|
@ -75,6 +80,9 @@ export const ValidateRouteSchema = Yup.object().shape({
|
|||
Target: Yup.string().required('Target is required').when('Mode', {
|
||||
is: 'SERVAPP',
|
||||
then: Yup.string().matches(/:[0-9]+$/, 'Invalid Target, must have a port'),
|
||||
}).when('Mode', {
|
||||
is: 'PROXY',
|
||||
then: Yup.string().matches(/^(https?:\/\/)/, 'Invalid Target, must start with http:// or https://'),
|
||||
}),
|
||||
|
||||
Host: Yup.string().when('UseHost', {
|
||||
|
@ -96,7 +104,7 @@ export const ValidateRouteSchema = Yup.object().shape({
|
|||
})
|
||||
|
||||
export const ValidateRoute = (routeConfig, config) => {
|
||||
let routeNames= config.HTTPConfig.ProxyConfig.Routes.map((r) => r.Name);
|
||||
let routeNames = config.HTTPConfig.ProxyConfig.Routes.map((r) => r.Name);
|
||||
|
||||
try {
|
||||
ValidateRouteSchema.validateSync(routeConfig);
|
||||
|
@ -114,4 +122,36 @@ export const getContainersRoutes = (config, containerName) => {
|
|||
let reg = new RegExp(`^(([a-z]+):\/\/)?${containerName}(:?[0-9]+)?$`, 'i');
|
||||
return route.Mode == "SERVAPP" && reg.test(route.Target)
|
||||
})) || [];
|
||||
}
|
||||
}
|
||||
|
||||
const checkHost = debounce((host, setHostError, setHostIp) => {
|
||||
console.log(host)
|
||||
if (isDomain(host)) {
|
||||
API.getDNS(host).then((data) => {
|
||||
setHostError(null)
|
||||
setHostIp(data.data)
|
||||
}).catch((err) => {
|
||||
setHostError(err.message)
|
||||
setHostIp(null)
|
||||
});
|
||||
} else {
|
||||
setHostError(null);
|
||||
setHostIp(null);
|
||||
}
|
||||
}, 500)
|
||||
|
||||
export const HostnameChecker = ({hostname}) => {
|
||||
const [hostError, setHostError] = useState(null);
|
||||
const [hostIp, setHostIp] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (hostname) {
|
||||
checkHost(hostname, setHostError, setHostIp);
|
||||
}
|
||||
}, [hostname]);
|
||||
|
||||
return <>{hostError && <Alert color='error'>{hostError}</Alert>}
|
||||
|
||||
{hostIp && <Alert color='info'>This hostname is pointing to <strong>{hostIp}</strong>, make sure it is your server IP!</Alert>}
|
||||
</>
|
||||
};
|
32
gen-doc-dns.js
Normal file
32
gen-doc-dns.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
const dnsList = require('./client/src/utils/dns-list.json');
|
||||
|
||||
console.log(dnsList);
|
||||
|
||||
// for each make a request to https://go-acme.github.io/lego/dns/{dns}/#credentials
|
||||
|
||||
let finalList = {};
|
||||
|
||||
let i = 0;
|
||||
(async () => {
|
||||
for(const dns of dnsList) {
|
||||
console.log(`Fetching ${dns} infos`)
|
||||
let result = (await (await fetch(`https://go-acme.github.io/lego/dns/${dns}/#credentials`)).text());
|
||||
result = result.split(`<h2 id="credentials">Credentials</h2>`)[1];
|
||||
result = result.split(`</table>`)[0] + `</table>`;
|
||||
let vars = result.match(/<code>(.*?)<\/code>/g);
|
||||
vars = vars.map(v => v.replace(/<\/?code>/g, ''));
|
||||
|
||||
finalList[dns] = {
|
||||
name: dns,
|
||||
url: `https://go-acme.github.io/lego/dns/${dns}/#credentials`,
|
||||
docs: result,
|
||||
vars: vars
|
||||
}
|
||||
|
||||
console.log(`${i++}/${dnsList.length} done`)
|
||||
}
|
||||
|
||||
// save to file
|
||||
const fs = require('fs');
|
||||
fs.writeFileSync('./client/src/utils/dns-config.json', JSON.stringify(finalList, null, 2));
|
||||
})();
|
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "cosmos-server",
|
||||
"version": "0.7.0-unstable2",
|
||||
"version": "0.7.0-unstable4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cosmos-server",
|
||||
"version": "0.7.0-unstable2",
|
||||
"version": "0.7.0-unstable4",
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^6.0.0",
|
||||
"@ant-design/icons": "^4.7.0",
|
||||
|
@ -48,6 +48,7 @@
|
|||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-window": "^1.8.7",
|
||||
"redux": "^4.2.0",
|
||||
"semver-compare": "^1.0.0",
|
||||
"simplebar": "^5.3.8",
|
||||
"simplebar-react": "^2.4.1",
|
||||
"typescript": "4.8.3",
|
||||
|
@ -9160,6 +9161,11 @@
|
|||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/semver-compare": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
|
||||
"integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="
|
||||
},
|
||||
"node_modules/set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cosmos-server",
|
||||
"version": "0.7.0-unstable4",
|
||||
"version": "0.7.0-unstable5",
|
||||
"description": "",
|
||||
"main": "test-server.js",
|
||||
"bugs": {
|
||||
|
@ -48,6 +48,7 @@
|
|||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-window": "^1.8.7",
|
||||
"redux": "^4.2.0",
|
||||
"semver-compare": "^1.0.0",
|
||||
"simplebar": "^5.3.8",
|
||||
"simplebar-react": "^2.4.1",
|
||||
"typescript": "4.8.3",
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
<!-- sponsors -->
|
||||
<h3 align="center">Thanks to the sponsors:</h3></br>
|
||||
<p align="center"><a href="https://github.com/zarevskaya"><img src="https://avatars.githubusercontent.com/zarevskaya" style="border-radius:48px" width="48" height="48" alt="zarev" title="zarev" /></a>
|
||||
<a href="https://github.com/DrMxrcy"><img src="https://avatars.githubusercontent.com/DrMxrcy" style="border-radius:48px" width="48" height="48" alt="null" title="null" /></a>
|
||||
<a href="https://github.com/JustDalek"><img src="https://avatars.githubusercontent.com/JustDalek" style="border-radius:48px" width="48" height="48" alt="JustDalek" title="JustDalek" /></a>
|
||||
</p><!-- /sponsors -->
|
||||
|
||||
---
|
||||
|
|
|
@ -37,6 +37,7 @@ type ContainerCreateRequestContainer struct {
|
|||
Volumes []mount.Mount `json:"volumes"`
|
||||
Networks map[string]ContainerCreateRequestServiceNetwork `json:"networks"`
|
||||
Routes []utils.ProxyRouteConfig `json:"routes"`
|
||||
Links []string `json:"links,omitempty"`
|
||||
|
||||
RestartPolicy string `json:"restart,omitempty"`
|
||||
Devices []string `json:"devices"`
|
||||
|
@ -49,6 +50,8 @@ type ContainerCreateRequestContainer struct {
|
|||
Entrypoint string `json:"entrypoint,omitempty"`
|
||||
WorkingDir string `json:"working_dir,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
UID int `json:"uid,omitempty"`
|
||||
GID int `json:"gid,omitempty"`
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
Domainname string `json:"domainname,omitempty"`
|
||||
MacAddress string `json:"mac_address,omitempty"`
|
||||
|
@ -66,7 +69,6 @@ type ContainerCreateRequestContainer struct {
|
|||
DNS []string `json:"dns,omitempty"`
|
||||
DNSSearch []string `json:"dns_search,omitempty"`
|
||||
ExtraHosts []string `json:"extra_hosts,omitempty"`
|
||||
Links []string `json:"links,omitempty"`
|
||||
SecurityOpt []string `json:"security_opt,omitempty"`
|
||||
StorageOpt map[string]string `json:"storage_opt,omitempty"`
|
||||
Sysctls map[string]string `json:"sysctls,omitempty"`
|
||||
|
@ -412,8 +414,8 @@ func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)
|
|||
PortBindings := nat.PortMap{}
|
||||
|
||||
for _, port := range container.Ports {
|
||||
portContainer := strings.Split(port, ":")[0]
|
||||
portHost := strings.Split(port, ":")[1]
|
||||
portHost := strings.Split(port, ":")[0]
|
||||
portContainer := strings.Split(port, ":")[1]
|
||||
|
||||
containerConfig.ExposedPorts[nat.Port(portContainer)] = struct{}{}
|
||||
PortBindings[nat.Port(portContainer)] = []nat.PortBinding{
|
||||
|
@ -445,20 +447,26 @@ func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)
|
|||
utils.Log(fmt.Sprintf("Not found. Creating directory %s for bind mount", newSource))
|
||||
OnLog(fmt.Sprintf("Not found. Creating directory %s for bind mount\n", newSource))
|
||||
|
||||
err := os.MkdirAll(newSource, 0755)
|
||||
err := os.MkdirAll(newSource, 0750)
|
||||
if err != nil {
|
||||
utils.Error("CreateService: Unable to create directory for bind mount. Make sure parent directories exist, and that Cosmos has permissions to create directories in the host directory", err)
|
||||
OnLog(fmt.Sprintf("[ERROR] Unable to create directory for bind mount. Make sure parent directories exist, and that Cosmos has permissions to create directories in the host directory: %s\n", err.Error()))
|
||||
Rollback(rollbackActions, OnLog)
|
||||
return err
|
||||
}
|
||||
|
||||
if container.User != "" {
|
||||
if container.UID != 0 {
|
||||
// Change the ownership of the directory to the container.UID
|
||||
err = os.Chown(newSource, container.UID, container.GID)
|
||||
if err != nil {
|
||||
utils.Error("CreateService: Unable to change ownership of directory", err)
|
||||
OnLog(fmt.Sprintf("[ERROR] Unable to change ownership of directory: " + err.Error()))
|
||||
}
|
||||
} else if container.User != "" {
|
||||
// Change the ownership of the directory to the container.User
|
||||
userInfo, err := user.Lookup(container.User)
|
||||
if err != nil {
|
||||
utils.Error("CreateService: Unable to lookup user", err)
|
||||
OnLog(fmt.Sprintf("[ERROR] Unable to lookup user " + container.User + "." +err.Error()))
|
||||
OnLog(fmt.Sprintf("[ERROR] Unable to lookup user " + container.User + ". " +err.Error()))
|
||||
} else {
|
||||
uid, _ := strconv.Atoi(userInfo.Uid)
|
||||
gid, _ := strconv.Atoi(userInfo.Gid)
|
||||
|
@ -484,7 +492,6 @@ func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)
|
|||
DNS: container.DNS,
|
||||
DNSSearch: container.DNSSearch,
|
||||
ExtraHosts: container.ExtraHosts,
|
||||
Links: container.Links,
|
||||
SecurityOpt: container.SecurityOpt,
|
||||
StorageOpt: container.StorageOpt,
|
||||
Sysctls: container.Sysctls,
|
||||
|
@ -566,6 +573,26 @@ func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Create the networks for links
|
||||
for _, targetContainer := range container.Links {
|
||||
if strings.Contains(targetContainer, ":") {
|
||||
err = errors.New("Link network cannot contain ':' please use container name only")
|
||||
utils.Error("CreateService: Rolling back changes because of -- Link network", err)
|
||||
OnLog(fmt.Sprintf("[ERROR] Rolling back changes because of -- Link network creation error: "+err.Error()))
|
||||
Rollback(rollbackActions, OnLog)
|
||||
return err
|
||||
}
|
||||
|
||||
err = CreateLinkNetwork(container.Name, targetContainer)
|
||||
if err != nil {
|
||||
utils.Error("CreateService: Rolling back changes because of -- Link network", err)
|
||||
OnLog(fmt.Sprintf("[ERROR] Rolling back changes because of -- Link network creation error: "+err.Error()))
|
||||
Rollback(rollbackActions, OnLog)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Write a response to the client
|
||||
utils.Log(fmt.Sprintf("Container %s created", container.Name))
|
||||
OnLog(fmt.Sprintf("Container %s created", container.Name))
|
||||
|
|
|
@ -246,6 +246,41 @@ func _debounceNetworkCleanUp() func(string) {
|
|||
}
|
||||
}
|
||||
|
||||
func CreateLinkNetwork(containerName string, container2Name string) error {
|
||||
// create network
|
||||
networkName := "cosmos-link-" + containerName + "-" + container2Name + "-" + utils.GenerateRandomString(2)
|
||||
_, err := DockerClient.NetworkCreate(DockerContext, networkName, types.NetworkCreate{
|
||||
CheckDuplicate: true,
|
||||
Labels: map[string]string{
|
||||
"cosmos-link": "true",
|
||||
"cosmos-link-name": networkName,
|
||||
"cosmos-link-container1": containerName,
|
||||
"cosmos-link-container2": container2Name,
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// connect containers to network
|
||||
err = ConnectToNetworkSync(networkName, containerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ConnectToNetworkSync(networkName, container2Name)
|
||||
if err != nil {
|
||||
// disconnect first container
|
||||
DockerClient.NetworkDisconnect(DockerContext, networkName, containerName, true)
|
||||
// destroy network
|
||||
DockerClient.NetworkRemove(DockerContext, networkName)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var DebouncedNetworkCleanUp = _debounceNetworkCleanUp()
|
||||
|
||||
func NetworkCleanUp() {
|
||||
|
|
|
@ -57,6 +57,13 @@ func startHTTPSServer(router *mux.Router, tlsCert string, tlsKey string) {
|
|||
var certReloader *simplecert.CertReloader
|
||||
var errSimCert error
|
||||
if(config.HTTPConfig.HTTPSCertificateMode == utils.HTTPSCertModeList["LETSENCRYPT"]) {
|
||||
if(config.HTTPConfig.DNSChallengeProvider != "") {
|
||||
newEnv := config.HTTPConfig.DNSChallengeConfig
|
||||
for key, value := range newEnv {
|
||||
os.Setenv(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
certReloader, errSimCert = simplecert.Init(cfg, nil)
|
||||
if errSimCert != nil {
|
||||
// Temporary before we have a better way to handle this
|
||||
|
|
|
@ -34,6 +34,7 @@ type NewInstallJSON struct {
|
|||
SSLEmail string `json:"sslEmail",validate:"omitempty,email"`
|
||||
UseWildcardCertificate bool `json:"useWildcardCertificate",validate:"omitempty"`
|
||||
DNSChallengeProvider string `json:"dnsChallengeProvider",validate:"omitempty"`
|
||||
DNSChallengeConfig map[string]string
|
||||
}
|
||||
|
||||
type AdminJSON struct {
|
||||
|
@ -110,6 +111,7 @@ func NewInstallRoute(w http.ResponseWriter, req *http.Request) {
|
|||
newConfig.HTTPConfig.SSLEmail = request.SSLEmail
|
||||
newConfig.HTTPConfig.UseWildcardCertificate = request.UseWildcardCertificate
|
||||
newConfig.HTTPConfig.DNSChallengeProvider = request.DNSChallengeProvider
|
||||
newConfig.HTTPConfig.DNSChallengeConfig = request.DNSChallengeConfig
|
||||
newConfig.HTTPConfig.TLSCert = request.TLSCert
|
||||
newConfig.HTTPConfig.TLSKey = request.TLSKey
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ func UserLogin(w http.ResponseWriter, req *http.Request) {
|
|||
c, errCo := utils.GetCollection(utils.GetRootAppId(), "users")
|
||||
if errCo != nil {
|
||||
utils.Error("Database Connect", errCo)
|
||||
utils.HTTPError(w, "Database", http.StatusInternalServerError, "DB001")
|
||||
utils.HTTPError(w, "Database Error", http.StatusInternalServerError, "DB001")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,7 @@ type HTTPConfig struct {
|
|||
SSLEmail string `validate:"omitempty,email"`
|
||||
UseWildcardCertificate bool
|
||||
AcceptAllInsecureHostname bool
|
||||
DNSChallengeConfig map[string]string
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
Loading…
Add table
Reference in a new issue