[release] version 0.5.0-unstable25

This commit is contained in:
Yann Stepienik 2023-05-17 12:44:14 +01:00
parent 7134301f64
commit 1722f3f832
12 changed files with 129 additions and 98 deletions

View file

@ -1,13 +1,15 @@
## Version 0.5.0 ## Version 0.5.0
- Add Terminal to containers - Add Terminal to containers
- Add Create Container - Add "Create ServApp"
- Add support for importing Docker Compose - Add support for importing Docker Compose
- Improved icon fetching
- Change Home background and style (especially fixing the awckward light theme)
- Fixed 2 bugs with the smart shield, that made it too strict - Fixed 2 bugs with the smart shield, that made it too strict
- Fixed issues that prevented from login in with different hostnames - Fixed issues that prevented from login in with different hostnames
- Added more infoon the shield when blocking someone - Added more info on the shield when blocking someone
- Fixed home background image
- Fixed issue where the UI would have missing icon images - Fixed issue where the UI would have missing icon images
- Improved icon fetching for apps by following redirections - Fixed Homepage showing stopped containers
- Fixed bug where you can't save changes on the URLs Screen
## Version 0.4.3 ## Version 0.4.3
- Fix for exposing routes from the details page - Fix for exposing routes from the details page

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View file

@ -61,10 +61,6 @@ const RouteConfigPage = () => {
routeConfig={currentRoute} routeConfig={currentRoute}
/> />
}, },
{
title: 'Permissions',
children: <div>WIP</div>
},
]}/>} ]}/>}
{!config && <div style={{textAlign: 'center'}}> {!config && <div style={{textAlign: 'center'}}>

View file

@ -215,20 +215,6 @@ const ProxyManagement = () => {
disableElevation disableElevation
fullWidth fullWidth
onClick={() => { onClick={() => {
if(routes.some((route, key) => {
let errors = ValidateRoute(route, config);
if (errors && errors.length > 0) {
errors = errors.map((err) => {
return `${route.Name}: ${err}`;
});
setSubmitErrors(errors);
return true;
}
})) {
return;
} else {
setSubmitErrors([]);
}
API.config.set(cleanRoutes(updateRoutes(routes))).then(() => { API.config.set(cleanRoutes(updateRoutes(routes))).then(() => {
setNeedSave(false); setNeedSave(false);
setOpenModal(true); setOpenModal(true);

View file

@ -3,7 +3,8 @@ import Back from "../../components/back";
import { Alert, Box, CircularProgress, Grid, Stack, useTheme } from "@mui/material"; import { Alert, Box, CircularProgress, Grid, Stack, useTheme } from "@mui/material";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import * as API from "../../api"; import * as API from "../../api";
import wallpaper from '../../assets/images/wallpaper.jpg'; import wallpaper from '../../assets/images/wallpaper2.jpg';
import wallpaperLight from '../../assets/images/wallpaper2_light.jpg';
import Grid2 from "@mui/material/Unstable_Grid2/Grid2"; import Grid2 from "@mui/material/Unstable_Grid2/Grid2";
import { getFaviconURL } from "../../utils/routes"; import { getFaviconURL } from "../../utils/routes";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@ -13,28 +14,48 @@ import IsLoggedIn from "../../isLoggedIn";
const HomeBackground = () => { const HomeBackground = () => {
const theme = useTheme(); const theme = useTheme();
const isDark = theme.palette.mode === 'dark';
return ( return (
<Box sx={{ position: 'fixed', float: 'left', overflow: 'hidden', zIndex: 0, top: 0, left: 0, right: 0, bottom: 0 }}> <Box sx={{ position: 'fixed', float: 'left', overflow: 'hidden', zIndex: 0, top: 0, left: 0, right: 0, bottom: 0,
<img src={wallpaper} style={{ display: 'inline' }} alt="Cosmos" width="100%" height="100%" /> // gradient
// backgroundImage: isDark ?
// `linear-gradient(#371d53, #26143a)` :
// `linear-gradient(#e6d3fb, #c8b0e2)`,
}}>
<img src={isDark ? wallpaper : wallpaperLight } style={{ display: 'inline' }} alt="Cosmos" width="100%" height="100%" />
</Box> </Box>
); );
}; };
const blockStyle = {
margin: 0,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: '78%',
verticalAlign: 'middle',
}
const HomePage = () => { const HomePage = () => {
const { routeName } = useParams(); const { routeName } = useParams();
const [serveApps, setServeApps] = useState([]); const [serveApps, setServeApps] = useState([]);
const [config, setConfig] = useState(null); const [config, setConfig] = useState(null);
const [coStatus, setCoStatus] = useState(null); const [coStatus, setCoStatus] = useState(null);
const [containers, setContainers] = useState(null); const [containers, setContainers] = useState(null);
const theme = useTheme();
const isDark = theme.palette.mode === 'dark';
const blockStyle = {
margin: 0,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
maxWidth: '78%',
verticalAlign: 'middle',
}
const appColor = isDark ? {
color: 'white',
background: 'rgba(0,0,0,0.35)',
} : {
color: 'black',
background: 'rgba(255,255,255,0.35)',
}
const backColor = isDark ? '0,0,0' : '255,255,255';
const textColor = isDark ? 'white' : 'dark';
const refreshStatus = () => { const refreshStatus = () => {
API.getStatus().then((res) => { API.getStatus().then((res) => {
@ -64,18 +85,19 @@ const HomePage = () => {
<HomeBackground /> <HomeBackground />
<style> <style>
{`header { {`header {
background: rgba(0.2,0.2,0.2,0.2) !important; background: rgba(${backColor},0.3) !important;
border-bottom-color: rgba(0.4,0.4,0.4,0.4) !important; border-bottom-color: rgba(${backColor},0.4) !important;
color: white !important; color: ${textColor} !important;
font-weight: bold;
} }
header .MuiChip-label { header .MuiChip-label {
color: #eee !important; color: ${textColor} !important;
} }
header .MuiButtonBase-root { header .MuiButtonBase-root, header .MuiChip-colorDefault {
color: #eee !important; color: ${textColor} !important;
background: rgba(0.2,0.2,0.2,0.2) !important; background: rgba(${backColor},0.5) !important;
} }
.app { .app {
@ -85,9 +107,15 @@ const HomePage = () => {
.app:hover { .app:hover {
cursor: pointer; cursor: pointer;
background: rgba(0.4,0.4,0.4,0.4) !important; background: rgba(${backColor},0.8) !important;
transform: scale(1.05); transform: scale(1.05);
} }
.MuiAlert-standard {
background: rgba(${backColor},0.3) !important;
color: ${textColor} !important;
font-weight: bold;
}
`} `}
</style> </style>
<Stack style={{ zIndex: 2 }} spacing={1}> <Stack style={{ zIndex: 2 }} spacing={1}>
@ -144,8 +172,8 @@ const HomePage = () => {
} }
} }
return !skip && <Grid2 item xs={12} sm={6} md={4} lg={3} xl={3} xxl={2} key={route.Name}> return !skip && <Grid2 item xs={12} sm={6} md={4} lg={3} xl={3} xxl={2} key={route.Name}>
<Box className='app' style={{ padding: 10, color: 'white', background: 'rgba(0,0,0,0.35)', borderRadius: 5 }}> <Box className='app' style={{ padding: 10, borderRadius: 5, ...appColor }}>
<Link to={getFullOrigin(route)} target="_blank" style={{ textDecoration: 'none', color: 'white' }}> <Link to={getFullOrigin(route)} target="_blank" style={{ textDecoration: 'none', ...appColor }}>
<Stack direction="row" spacing={2} alignItems="center"> <Stack direction="row" spacing={2} alignItems="center">
<img className="loading-image" alt="" src={getFaviconURL(route)} width="64px" height="64px"/> <img className="loading-image" alt="" src={getFaviconURL(route)} width="64px" height="64px"/>
@ -162,7 +190,7 @@ const HomePage = () => {
{config && config.HTTPConfig.ProxyConfig.Routes.length === 0 && ( {config && config.HTTPConfig.ProxyConfig.Routes.length === 0 && (
<Grid2 item xs={12} sm={12} md={12} lg={12} xl={12}> <Grid2 item xs={12} sm={12} md={12} lg={12} xl={12}>
<Box style={{ padding: 10, color: 'white', background: 'rgba(0,0,0,0.35)', borderRadius: 5 }}> <Box style={{ padding: 10, borderRadius: 5, ...appColor }}>
<Stack direction="row" spacing={2} alignItems="center"> <Stack direction="row" spacing={2} alignItems="center">
<div style={{ width: '100%' }}> <div style={{ width: '100%' }}>
<h3 style={blockStyle}>No Apps</h3> <h3 style={blockStyle}>No Apps</h3>

View file

@ -19,6 +19,7 @@ import VolumeContainerSetup from './volumes';
import DockerTerminal from './terminal'; import DockerTerminal from './terminal';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { smartDockerLogConcat, tryParseProgressLog } from '../../../utils/docker'; import { smartDockerLogConcat, tryParseProgressLog } from '../../../utils/docker';
import { LoadingButton } from '@mui/lab';
const preStyle = { const preStyle = {
backgroundColor: '#000', backgroundColor: '#000',
@ -64,7 +65,9 @@ const NewDockerService = ({service, refresh}) => {
}, []); }, []);
const create = () => { const create = () => {
setLog([]) setLog([
'Creating Service... ',
])
API.docker.createService(service, (newlog) => { API.docker.createService(service, (newlog) => {
setLog((old) => smartDockerLogConcat(old, newlog)); setLog((old) => smartDockerLogConcat(old, newlog));
preRef.current.scrollTop = preRef.current.scrollHeight; preRef.current.scrollTop = preRef.current.scrollHeight;
@ -75,7 +78,7 @@ const NewDockerService = ({service, refresh}) => {
}); });
} }
const needsRestart = service && service.service && service.service.some((c) => { const needsRestart = service && service.services && Object.values(service.services).some((c) => {
return c.routes && c.routes.length > 0; return c.routes && c.routes.length > 0;
}); });
@ -83,13 +86,14 @@ const NewDockerService = ({service, refresh}) => {
<MainCard title="Create Service"> <MainCard title="Create Service">
<RestartModal openModal={openModal} setOpenModal={setOpenModal} /> <RestartModal openModal={openModal} setOpenModal={setOpenModal} />
<Stack spacing={1}> <Stack spacing={1}>
{!isDone && <Button {!isDone && <LoadingButton
onClick={create} onClick={create}
variant="contained" variant="contained"
color="primary" color="primary"
fullWidth fullWidth
loading={log.length && !isDone}
startIcon={<PlusCircleOutlined />} startIcon={<PlusCircleOutlined />}
>Create</Button>} >Create</LoadingButton>}
{isDone && <Stack spacing={1}> {isDone && <Stack spacing={1}>
<Alert severity="success">Service Created!</Alert> <Alert severity="success">Service Created!</Alert>
{needsRestart && <Alert severity="warning">Cosmos needs to be restarted to apply changes to the URLs</Alert>} {needsRestart && <Alert severity="warning">Cosmos needs to be restarted to apply changes to the URLs</Alert>}

View file

@ -1,7 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import MainCard from '../../../components/MainCard'; import MainCard from '../../../components/MainCard';
import RestartModal from '../../config/users/restart'; import RestartModal from '../../config/users/restart';
import { Alert, Button, Chip, Divider, Stack, useMediaQuery } from '@mui/material'; import { Alert, Button, Checkbox, Chip, Divider, Stack, useMediaQuery } from '@mui/material';
import HostChip from '../../../components/hostChip'; import HostChip from '../../../components/hostChip';
import { RouteMode, RouteSecurity } from '../../../components/routeComponents'; import { RouteMode, RouteSecurity } from '../../../components/routeComponents';
import { getFaviconURL } from '../../../utils/routes'; import { getFaviconURL } from '../../../utils/routes';
@ -56,7 +56,7 @@ const NewDockerServiceForm = () => {
}); });
let service = { let service = {
Services: { services: {
container_name : { container_name : {
container_name: containerInfo.Name, container_name: containerInfo.Name,
image: containerInfo.Config.Image, image: containerInfo.Config.Image,
@ -72,7 +72,7 @@ const NewDockerServiceForm = () => {
acc[cur] = {}; acc[cur] = {};
return acc; return acc;
}, {}), }, {}),
Routes: [containerInfo.Route] routes: containerInfo.CreateRoute ? [containerInfo.Route] : [],
} }
}, },
} }
@ -164,40 +164,53 @@ const NewDockerServiceForm = () => {
{ {
title: 'URL', title: 'URL',
disabled: maxTab < 1, disabled: maxTab < 1,
children: <Stack spacing={2}><RouteManagement TargetContainer={containerInfo} children: <Stack spacing={2}>
routeConfig={{ <MainCard style={{ maxWidth: '1000px', width: '100%', margin: '', position: 'relative' }}>
Target: "http://"+containerInfo.Name.replace('/', '') + ":", <Checkbox
Mode: "SERVAPP", checked={containerInfo.CreateRoute}
Name: containerInfo.Name.replace('/', ''), onChange={(e) => {
Description: "Expose " + containerInfo.Name.replace('/', '') + " to the internet", const newValues = {
UseHost: true, ...containerInfo,
Host: getHostnameFromName(containerInfo.Name), CreateRoute: e.target.checked,
UsePathPrefix: false, }
PathPrefix: '', setContainerInfo(newValues);
CORSOrigin: '', }}
StripPathPrefix: false, />Create a URL to access this ServApp
AuthEnabled: false, </MainCard>
Timeout: 14400000, {containerInfo.CreateRoute && <RouteManagement TargetContainer={containerInfo}
ThrottlePerMinute: 10000, routeConfig={{
BlockCommonBots: true, Target: "http://"+containerInfo.Name.replace('/', '') + ":",
SmartShield: { Mode: "SERVAPP",
Enabled: true, Name: containerInfo.Name.replace('/', ''),
} Description: "Expose " + containerInfo.Name.replace('/', '') + " to the internet",
}} UseHost: true,
routeNames={[]} Host: getHostnameFromName(containerInfo.Name),
setRouteConfig={(newRoute) => { UsePathPrefix: false,
const newValues = { PathPrefix: '',
...containerInfo, CORSOrigin: '',
Route: newRoute, StripPathPrefix: false,
} AuthEnabled: false,
setContainerInfo(newValues); Timeout: 14400000,
}} ThrottlePerMinute: 10000,
up={() => {}} BlockCommonBots: true,
down={() => {}} SmartShield: {
deleteRoute={() => {}} Enabled: true,
noControls }
lockTarget }}
/>{nav()}</Stack> routeNames={[]}
setRouteConfig={(newRoute) => {
const newValues = {
...containerInfo,
Route: newRoute,
}
setContainerInfo(newValues);
}}
up={() => {}}
down={() => {}}
deleteRoute={() => {}}
noControls
lockTarget
/>}{nav()}</Stack>
}, },
{ {
title: 'Network', title: 'Network',

View file

@ -123,7 +123,7 @@ const ContainerOverview = ({ containerInfo, config, refresh }) => {
disabled={isUpdating} disabled={isUpdating}
onChange={(e) => { onChange={(e) => {
setIsUpdating(true); setIsUpdating(true);
API.docker.secure(Name, e.target.checked).then(() => { API.docker.secure(Name.replace('/', ''), e.target.checked).then(() => {
setTimeout(() => { setTimeout(() => {
refreshAll(); refreshAll();
}, 3000); }, 3000);
@ -137,7 +137,7 @@ const ContainerOverview = ({ containerInfo, config, refresh }) => {
disabled={isUpdating} disabled={isUpdating}
onChange={(e) => { onChange={(e) => {
setIsUpdating(true); setIsUpdating(true);
API.docker.autoUpdate(Name, e.target.checked).then(() => { API.docker.autoUpdate(Name.replace('/', ''), e.target.checked).then(() => {
setTimeout(() => { setTimeout(() => {
refreshAll(); refreshAll();
}, 3000); }, 3000);

View file

@ -226,7 +226,7 @@ const ServeApps = () => {
})} })}
</Stack> </Stack>
</Stack> </Stack>
{isUpdating[app.Id] ? <div> {isUpdating[app.Names[0].replace('/', '')] ? <div>
<CircularProgress color="inherit" /> <CircularProgress color="inherit" />
</div> </div>
: :
@ -239,14 +239,15 @@ const ServeApps = () => {
checked={app.Labels['cosmos-force-network-secured'] === 'true'} checked={app.Labels['cosmos-force-network-secured'] === 'true'}
disabled={app.State !== 'running'} disabled={app.State !== 'running'}
onChange={(e) => { onChange={(e) => {
setIsUpdatingId(app.Id, true); const name = app.Names[0].replace('/', '');
API.docker.secure(app.Id, e.target.checked).then(() => { setIsUpdatingId(name, true);
API.docker.secure(name, e.target.checked).then(() => {
setTimeout(() => { setTimeout(() => {
setIsUpdatingId(app.Id, false); setIsUpdatingId(name, false);
refreshServeApps(); refreshServeApps();
}, 3000); }, 3000);
}).catch(() => { }).catch(() => {
setIsUpdatingId(app.Id, false); setIsUpdatingId(name, false);
refreshServeApps(); refreshServeApps();
}) })
}} }}
@ -257,14 +258,15 @@ const ServeApps = () => {
checked={app.Labels['cosmos-auto-update'] === 'true'} checked={app.Labels['cosmos-auto-update'] === 'true'}
disabled={app.State !== 'running'} disabled={app.State !== 'running'}
onChange={(e) => { onChange={(e) => {
setIsUpdatingId(app.Id, true); const name = app.Names[0].replace('/', '');
API.docker.autoUpdate(app.Id, e.target.checked).then(() => { setIsUpdatingId(name, true);
API.docker.autoUpdate(name, e.target.checked).then(() => {
setTimeout(() => { setTimeout(() => {
setIsUpdatingId(app.Id, false); setIsUpdatingId(name, false);
refreshServeApps(); refreshServeApps();
}, 3000); }, 3000);
}).catch(() => { }).catch(() => {
setIsUpdatingId(app.Id, false); setIsUpdatingId(name, false);
refreshServeApps(); refreshServeApps();
}) })
}} }}

View file

@ -1,6 +1,6 @@
{ {
"name": "cosmos-server", "name": "cosmos-server",
"version": "0.5.0-unstable24", "version": "0.5.0-unstable25",
"description": "", "description": "",
"main": "test-server.js", "main": "test-server.js",
"bugs": { "bugs": {