diff --git a/changelog.md b/changelog.md
index 49352b5..722e468 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,7 @@
+## Version 0.8.0
+ - Stop showing Docker not connected when first loading status
+ - Add privacy settings to external links
+
## Version 0.7.1 -> 0.7.10
- Fix issue where multiple DBs get created at the setup
- Add more special characters to be used for password validation
diff --git a/client/src/App.jsx b/client/src/App.jsx
index 6d22200..b83485a 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -4,23 +4,68 @@ import * as React from 'react';
import ThemeCustomization from './themes';
import ScrollTop from './components/ScrollTop';
import Snackbar from '@mui/material/Snackbar';
-import {Alert} from '@mui/material';
+import {Alert, Box} from '@mui/material';
+import logo from './assets/images/icons/cosmos.png';
+
+import * as API from './api';
import { setSnackit } from './api/wrap';
// ==============================|| APP - THEME, ROUTER, LOCAL ||============================== //
+const LoadingAnimation = () => (
+
+ );
+
+export let SetPrimaryColor = () => {};
+export let SetSecondaryColor = () => {};
+export let GlobalPrimaryColor = '';
+export let GlobalSecondaryColor = '';
+
const App = () => {
const [open, setOpen] = React.useState(false);
const [message, setMessage] = React.useState('');
const [severity, setSeverity] = React.useState('error');
+ const [statusLoaded, setStatusLoaded] = React.useState(false);
+ const [PrimaryColor, setPrimaryColor] = React.useState(API.PRIMARY_COLOR);
+ const [SecondaryColor, setSecondaryColor] = React.useState(API.SECONDARY_COLOR);
+
+ SetPrimaryColor = (color) => {
+ setPrimaryColor(color);
+ GlobalPrimaryColor = color;
+ }
+
+ SetSecondaryColor = (color) => {
+ setSecondaryColor(color);
+ GlobalSecondaryColor = color;
+ }
+
+ React.useEffect(() => {
+ API.getStatus(true).then((r) => {
+ if(r) {
+ setStatusLoaded(true);
+ }
+ setPrimaryColor(API.PRIMARY_COLOR);
+ setSecondaryColor(API.SECONDARY_COLOR);
+ }).catch(() => {
+ setStatusLoaded(true);
+ setPrimaryColor(API.PRIMARY_COLOR);
+ setSecondaryColor(API.SECONDARY_COLOR);
+ });
+ }, []);
+
setSnackit((message, severity='error') => {
setMessage(message);
setOpen(true);
setSeverity(severity);
})
- return (
-
+
+ return statusLoaded ?
+
{
- )
+
+ :
+
+ {/* */}
+
+
+
+
}
export default App;
diff --git a/client/src/api/index.demo.jsx b/client/src/api/index.demo.jsx
index 098580c..5f52259 100644
--- a/client/src/api/index.demo.jsx
+++ b/client/src/api/index.demo.jsx
@@ -60,4 +60,14 @@ export const checkHost = (host) => {
100
);
});
-}
\ No newline at end of file
+}
+
+export const uploadBackground = (file) => {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve({
+ "status": "ok",
+ "data": ""
+ })}, 100 );
+ });
+ }
\ No newline at end of file
diff --git a/client/src/api/index.jsx b/client/src/api/index.jsx
index 2ba38fa..9b9997a 100644
--- a/client/src/api/index.jsx
+++ b/client/src/api/index.jsx
@@ -16,17 +16,52 @@ import wrap from './wrap';
export let CPU_ARCH = 'amd64';
export let CPU_AVX = true;
-let getStatus = () => {
+export let HOME_BACKGROUND;
+export let PRIMARY_COLOR;
+export let SECONDARY_COLOR;
+
+export let FIRST_LOAD = false;
+
+let getStatus = (initial) => {
return wrap(fetch('/cosmos/api/status', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
- }))
+ }), initial)
.then(async (response) => {
CPU_ARCH = response.data.CPU;
CPU_AVX = response.data.AVX;
+ HOME_BACKGROUND = response.data.homepage.Background;
+ PRIMARY_COLOR = response.data.theme.PrimaryColor;
+ SECONDARY_COLOR = response.data.theme.SecondaryColor;
+ FIRST_LOAD = true;
return response
+ }).catch((response) => {
+ const urlSearch = encodeURIComponent(window.location.search);
+ const redirectTo = (window.location.pathname + urlSearch);
+
+ if(response.status != 'OK') {
+ if(
+ window.location.href.indexOf('/cosmos-ui/newInstall') == -1 &&
+ window.location.href.indexOf('/cosmos-ui/login') == -1 &&
+ window.location.href.indexOf('/cosmos-ui/loginmfa') == -1 &&
+ window.location.href.indexOf('/cosmos-ui/newmfa') == -1 &&
+ window.location.href.indexOf('/cosmos-ui/register') == -1 &&
+ window.location.href.indexOf('/cosmos-ui/forgot-password') == -1) {
+ if(response.status == 'NEW_INSTALL') {
+ window.location.href = '/cosmos-ui/newInstall';
+ } else if (response.status == 'error' && response.code == "HTTP004") {
+ window.location.href = '/cosmos-ui/login?redirect=' + redirectTo;
+ } else if (response.status == 'error' && response.code == "HTTP006") {
+ window.location.href = '/cosmos-ui/loginmfa?redirect=' + redirectTo;
+ } else if (response.status == 'error' && response.code == "HTTP007") {
+ window.location.href = '/cosmos-ui/newmfa?redirect=' + redirectTo;
+ }
+ } else {
+ return "nothing";
+ }
+ }
});
}
@@ -157,6 +192,15 @@ let getDNS = (host) => {
});
}
+let uploadBackground = (file) => {
+ const formData = new FormData();
+ formData.append('background', file);
+ return wrap(fetch('/cosmos/api/background', {
+ method: 'POST',
+ body: formData
+ }));
+};
+
const isDemo = import.meta.env.MODE === 'demo';
let auth = _auth;
@@ -176,6 +220,7 @@ if(isDemo) {
isOnline = indexDemo.isOnline;
checkHost = indexDemo.checkHost;
getDNS = indexDemo.getDNS;
+ uploadBackground = indexDemo.uploadBackground;
}
export {
@@ -188,5 +233,6 @@ export {
newInstall,
isOnline,
checkHost,
- getDNS
+ getDNS,
+ uploadBackground
};
\ No newline at end of file
diff --git a/client/src/api/wrap.js b/client/src/api/wrap.js
index c6b174e..e6e45ba 100644
--- a/client/src/api/wrap.js
+++ b/client/src/api/wrap.js
@@ -1,20 +1,32 @@
let snackit;
-export default function wrap(apicall) {
+export default function wrap(apicall, noError = false) {
return apicall.then(async (response) => {
let rep;
try {
rep = await response.json();
- } catch {
- snackit('Server error');
- throw new Error('Server error');
+ } catch (err) {
+ if (!noError) {
+ snackit('Server error');
+ throw new Error('Server error');
+ } else {
+ const e = new Error(rep.message);
+ e.status = rep.status;
+ e.code = rep.code;
+ throw e;
+ }
}
+
if (response.status == 200) {
return rep;
}
- snackit(rep.message);
+
+ if (!noError) {
+ snackit(rep.message);
+ }
+
const e = new Error(rep.message);
- e.status = response.status;
+ e.status = rep.status;
e.code = rep.code;
throw e;
});
diff --git a/client/src/assets/images/icons/cosmos_simple_black.png b/client/src/assets/images/icons/cosmos_simple_black.png
new file mode 100644
index 0000000..2df8670
Binary files /dev/null and b/client/src/assets/images/icons/cosmos_simple_black.png differ
diff --git a/client/src/assets/images/icons/cosmos_simple_white.png b/client/src/assets/images/icons/cosmos_simple_white.png
new file mode 100644
index 0000000..3c6cbd9
Binary files /dev/null and b/client/src/assets/images/icons/cosmos_simple_white.png differ
diff --git a/client/src/components/Logo/Logo.jsx b/client/src/components/Logo/Logo.jsx
index 72c401f..0d8cecd 100644
--- a/client/src/components/Logo/Logo.jsx
+++ b/client/src/components/Logo/Logo.jsx
@@ -2,12 +2,14 @@
import { useTheme } from '@mui/material/styles';
import { fontWeight } from '@mui/system';
-import logo from '../../assets/images/icons/cosmos.png';
+import logo from '../../assets/images/icons/cosmos_simple_black.png';
+import logoDark from '../../assets/images/icons/cosmos_simple_white.png';
// ==============================|| LOGO SVG ||============================== //
const Logo = () => {
const theme = useTheme();
+ const isLight = theme.palette.mode === 'light';
return (
/**
@@ -17,7 +19,7 @@ const Logo = () => {
*
*/
<>
-
+
Cosmos
>
);
diff --git a/client/src/components/fileUpload.jsx b/client/src/components/fileUpload.jsx
index eca1932..a7ba9fc 100644
--- a/client/src/components/fileUpload.jsx
+++ b/client/src/components/fileUpload.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import { Button } from '@mui/material';
import { UploadOutlined } from '@ant-design/icons';
-export default function UploadButtons({OnChange, accept}) {
+export default function UploadButtons({OnChange, accept, label}) {
return (
}>
- Upload
+ {label || 'Upload'}
diff --git a/client/src/index.css b/client/src/index.css
index 5c5b5bc..3c6d9ab 100644
--- a/client/src/index.css
+++ b/client/src/index.css
@@ -21,6 +21,58 @@
}
+@keyframes pulsing {
+ 0% { -webkit-transform: scale(1); }
+ 50% { -webkit-transform: scale(1.1); }
+ 100% { -webkit-transform: scale(1); }
+}
+
+@keyframes bounce {
+ 0%, 80%, 100% {
+ transform: scale(0);
+ }
+ 40% {
+ transform: scale(1.0);
+ }
+}
+
+.dot {
+ background-color: #222;
+ border-radius: 100%;
+ width: 1.5rem;
+ height: 1.5rem;
+ margin: 0 0.25rem;
+
+ /* Animation */
+ animation: bounce 1.4s infinite;
+ animation-fill-mode: both;
+}
+
+@media (prefers-color-scheme: dark) {
+ .dot {
+ background-color: #eee;
+ }
+}
+
+.dot:nth-child(1) {
+ animation-delay: -0.32s;
+}
+
+.dot:nth-child(2) {
+ animation-delay: -0.16s;
+}
+
+.dot:nth-child(3) {
+ animation-delay: 0s;
+}
+
+.loader {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+}
+
.shinyButton {
overflow: hidden;
}
@@ -87,4 +139,8 @@
.raw-table table th, .raw-table table td {
padding: 5px;
border: 1px solid #ccc;
+}
+
+.pulsing {
+ animation: pulsing 2s ease-in-out infinite;
}
\ No newline at end of file
diff --git a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx
index 2be4088..c0750e8 100644
--- a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx
+++ b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx
@@ -23,7 +23,9 @@ const NavItem = ({ item, level }) => {
itemTarget = '_blank';
}
- let listItemProps = { component: forwardRef((props, ref) => ) };
+ let listItemProps = { component: forwardRef((props, ref) => ) };
if (item?.external) {
listItemProps = { component: 'a', href: item.url, target: itemTarget };
}
diff --git a/client/src/pages/config/users/configman.jsx b/client/src/pages/config/users/configman.jsx
index 6c5b386..e40e378 100644
--- a/client/src/pages/config/users/configman.jsx
+++ b/client/src/pages/config/users/configman.jsx
@@ -22,6 +22,7 @@ import {
Collapse,
TextField,
MenuItem,
+ Skeleton,
} from '@mui/material';
import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons';
@@ -31,11 +32,17 @@ import { WarningOutlined, PlusCircleOutlined, CopyOutlined, ExclamationCircleOut
import { CosmosCheckbox, CosmosFormDivider, CosmosInputPassword, CosmosInputText, CosmosSelect } from './formShortcuts';
import CountrySelect, { countries } from '../../../components/countrySelect';
import { DnsChallengeComp } from '../../../utils/dns-challenge-comp';
+import { values } from 'lodash';
+import UploadButtons from '../../../components/fileUpload';
+import { TwitterPicker
+ } from 'react-color';
+import {SetPrimaryColor, SetSecondaryColor} from '../../../App';
const ConfigManagement = () => {
const [config, setConfig] = React.useState(null);
const [openModal, setOpenModal] = React.useState(false);
+ const [uploadingBackground, setUploadingBackground] = React.useState(false);
function refresh() {
API.config.get().then((res) => {
@@ -82,7 +89,11 @@ const ConfigManagement = () => {
Email_UseTLS : config.EmailConfig.UseTLS,
SkipPruneNetwork: config.DockerConfig.SkipPruneNetwork,
- DefaultDataPath: config.DockerConfig.DefaultDataPath || "/usr"
+ DefaultDataPath: config.DockerConfig.DefaultDataPath || "/usr",
+
+ Background: config && config.HomepageConfig && config.HomepageConfig.Background,
+ PrimaryColor: config && config.ThemeConfig && config.ThemeConfig.PrimaryColor,
+ SecondaryColor: config && config.ThemeConfig && config.ThemeConfig.SecondaryColor,
}}
validationSchema={Yup.object().shape({
Hostname: Yup.string().max(255).required('Hostname is required'),
@@ -124,7 +135,16 @@ const ConfigManagement = () => {
...config.DockerConfig,
SkipPruneNetwork: values.SkipPruneNetwork,
DefaultDataPath: values.DefaultDataPath
- }
+ },
+ HomepageConfig: {
+ ...config.HomepageConfig,
+ Background: values.Background
+ },
+ ThemeConfig: {
+ ...config.ThemeConfig,
+ PrimaryColor: values.PrimaryColor,
+ SecondaryColor: values.SecondaryColor
+ },
}
API.config.set(toSave).then((data) => {
@@ -153,6 +173,29 @@ const ConfigManagement = () => {
{(formik) => (