v0.0.3: Dark Mode and User List

This commit is contained in:
Yann Stepienik 2023-03-13 00:49:27 +00:00
parent cfadca8c06
commit b6f872343d
21 changed files with 157 additions and 34 deletions

BIN
client/Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

View file

@ -2,9 +2,16 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cosmos</title>
<link rel="icon" type="image/x-icon" href="/Logo.png">
<style>
@media (prefers-color-scheme: dark) {
html {
background-color: #141414;
}
}
</style>
</head>
<body>
<div id="root"></div>

View file

@ -1,4 +1,6 @@
import * as auth from './authentication.jsx';
import * as users from './users.jsx';
export {
auth
auth,
users
};

14
client/src/api/users.jsx Normal file
View file

@ -0,0 +1,14 @@
function list() {
return fetch('/cosmos/api/users', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
})
.then((res) => res.json())
}
export {
list,
};

View file

@ -9,7 +9,7 @@ import logo from '../../../../../Logo.png';
const AuthBackground = () => {
const theme = useTheme();
return (
<Box sx={{ position: 'fixed', float: 'left', height: 'calc(100vh - 50px)', overflow: 'hidden', filter: 'blur(25px)', zIndex: -1, top: 100, left: -500 }}>
<Box sx={{ position: 'fixed', float: 'left', height: 'calc(100vh - 50px)', overflow: 'hidden', filter: 'blur(25px)', zIndex: 0, top: 100, left: -500 }}>
<img src={logo} style={{ display:'inline'}} alt="Cosmos" width="1100" />
</Box>
);

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36"><defs><style>.cls-1{fill:#fff;}</style></defs><g id="图层_2" data-name="图层 2"><g id="Discord_Logos" data-name="Discord Logos"><g id="Discord_Logo_-_Large_-_White" data-name="Discord Logo - Large - White"><path class="cls-1" d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 985 B

View file

@ -28,7 +28,7 @@ const MainCard = forwardRef(
divider = true,
elevation,
secondary,
shadow,
// shadow,
sx = {},
title,
codeHighlight,
@ -37,7 +37,7 @@ const MainCard = forwardRef(
ref
) => {
const theme = useTheme();
boxShadow = theme.palette.mode === 'dark' ? boxShadow || true : boxShadow;
boxShadow = false; // theme.palette.mode === 'dark' ? boxShadow || true : boxShadow;
return (
<Card

View file

@ -6,7 +6,7 @@ const config = {
i18n: 'en',
miniDrawer: false,
container: true,
mode: 'light',
mode: 'dark',
presetColor: 'default',
themeDirection: 'ltr'
};

View file

@ -62,8 +62,8 @@ const Notification = () => {
setOpen(false);
};
const iconBackColorOpen = 'grey.300';
const iconBackColor = 'grey.100';
const iconBackColor = theme.palette.mode === 'dark' ? 'grey.700' : 'grey.100';
const iconBackColorOpen = theme.palette.mode === 'dark' ? 'grey.800' : 'grey.200';
return (
<Box sx={{ flexShrink: 0, ml: 0.75 }}>

View file

@ -17,8 +17,8 @@ const Header = ({ open, handleDrawerToggle }) => {
const theme = useTheme();
const matchDownMD = useMediaQuery(theme.breakpoints.down('lg'));
const iconBackColor = 'grey.100';
const iconBackColorOpen = 'grey.200';
const iconBackColor = theme.palette.mode === 'dark' ? 'grey.700' : 'grey.100';
const iconBackColorOpen = theme.palette.mode === 'dark' ? 'grey.800' : 'grey.200';
// common header
const mainHeader = (
@ -28,7 +28,7 @@ const Header = ({ open, handleDrawerToggle }) => {
aria-label="open drawer"
onClick={handleDrawerToggle}
edge="start"
color="secondary"
color="red"
sx={{ color: 'text.primary', bgcolor: open ? iconBackColorOpen : iconBackColor, ml: { xs: 0, lg: -2 } }}
>
{!open ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}

View file

@ -50,7 +50,7 @@ const MainLayout = () => {
<Drawer open={open} handleDrawerToggle={handleDrawerToggle} />
<Box component="main" sx={{ width: '100%', flexGrow: 1, p: { xs: 2, sm: 3 } }}>
<Toolbar />
<Breadcrumbs navigation={navigation} title titleBottom card={false} divider={false} />
<Breadcrumbs navigation={navigation} title divider={false} />
<Outlet />
</Box>
</Box>

View file

@ -1,12 +1,16 @@
// assets
import { GithubOutlined, QuestionOutlined } from '@ant-design/icons';
import DiscordOutlined from '../assets/images/icons/discord.svg'
import DiscordOutlinedWhite from '../assets/images/icons/discord_white.svg'
import { useTheme } from '@mui/material/styles';
// ==============================|| MENU ITEMS - SAMPLE PAGE & DOCUMENTATION ||============================== //
const DiscordOutlinedIcon = (props) => {
const theme = useTheme();
return (
<img src={DiscordOutlined} width="16px" alt="Discord" {...props} />
<img src={
theme.palette.mode === 'dark' ? DiscordOutlinedWhite : DiscordOutlined} width="16px" alt="Discord" {...props} />
);
};

View file

@ -8,16 +8,16 @@ import MainCard from '../../components/MainCard';
// ==============================|| AUTHENTICATION - CARD WRAPPER ||============================== //
const AuthCard = ({ children, ...other }) => (
<MainCard
const AuthCard = ({ children, ...other }) => (<MainCard
sx={{
maxWidth: { xs: 400, lg: 475 },
margin: { xs: 2.5, md: 3 },
'& > *': {
flexGrow: 1,
flexBasis: '50%'
}
},
}}
style={{ zIndex: 1, position: 'relative' }}
content={false}
{...other}
border={false}

View file

@ -0,0 +1,69 @@
// material-ui
import { Button, Typography } from '@mui/material';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
import * as API from '../../../api';
// project import
import MainCard from '../../../components/MainCard';
import isLoggedIn from '../../../isLoggedIn';
import { useEffect, useState } from 'react';
// ==============================|| SAMPLE PAGE ||============================== //
const UserManagement = () => {
const [rows, setRows] = useState([
{ id: 0, nickname: '12313', reset:'123132' },
{ id: 1, nickname: '354345', reset:'345345' },
]);
isLoggedIn();
useEffect(() => {
API.users.list()
.then(data => {
console.log(data);
setRows(data.data);
})
}, [])
return <MainCard title="Users">
<TableContainer component={Paper}>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Nickname</TableCell>
<TableCell>Role</TableCell>
<TableCell>Password</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.nickname}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.nickname}
</TableCell>
<TableCell>User</TableCell>
<TableCell><Button variant="contained" color="primary">Send Password Link</Button></TableCell>
<TableCell><Button variant="contained" color="error">Delete</Button></TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</MainCard>
};
export default UserManagement;

View file

@ -3,6 +3,7 @@ import { lazy } from 'react';
// project import
import Loadable from '../components/Loadable';
import MainLayout from '../layout/MainLayout';
import UserManagement from '../pages/config/users/usermanagement';
// render - dashboard
const DashboardDefault = Loadable(lazy(() => import('../pages/dashboard')));
@ -40,8 +41,8 @@ const MainRoutes = {
]
},
{
path: 'sample-page',
element: <SamplePage />
path: 'config/users',
element: <UserManagement />
},
{
path: 'shadow',

View file

@ -14,7 +14,9 @@ import componentsOverride from './overrides';
// ==============================|| DEFAULT THEME - MAIN ||============================== //
export default function ThemeCustomization({ children }) {
const theme = Palette('light', 'default');
const theme = Palette(
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ?
'dark' : 'light');
// eslint-disable-next-line react-hooks/exhaustive-deps
const themeTypography = Typography(`'Public Sans', sans-serif`);

View file

@ -32,28 +32,48 @@ const Palette = (mode) => {
const paletteColor = ThemeOption(colors);
return createTheme({
return createTheme(mode === 'dark' ? {
palette: {
mode,
common: {
black: '#000',
white: '#fff'
black: '#fff',
white: '#000'
},
...paletteColor,
text: {
primary: paletteColor.grey[700],
secondary: paletteColor.grey[500],
disabled: paletteColor.grey[400]
primary: paletteColor.grey[0],
secondary: paletteColor.grey[200],
disabled: paletteColor.grey[300]
},
action: {
disabled: paletteColor.grey[300]
},
divider: paletteColor.grey[200],
divider: paletteColor.grey[600],
background: {
paper: paletteColor.grey[0],
default: paletteColor.grey.A50
paper: paletteColor.grey[700],
default: paletteColor.grey[800]
}
}
} : {
mode,
common: {
black: '#000',
white: '#fff'
},
...paletteColor,
text: {
primary: paletteColor.grey[700],
secondary: paletteColor.grey[500],
disabled: paletteColor.grey[400]
},
action: {
disabled: paletteColor.grey[300]
},
divider: paletteColor.grey[200],
background: {
paper: paletteColor.grey[0],
default: paletteColor.grey.A50
}
});
};

View file

@ -33,6 +33,7 @@
"npm://@esbuild/linux-x64": "0.16.17",
"npm://@mui/lab": "^5.0.0-alpha.100",
"npm://@mui/material": "^5.10.6",
"npm://@mui/x-data-grid": "6.0.1",
"npm://@reduxjs/toolkit": "^1.8.5",
"npm://@testing-library/jest-dom": "^5.16.5",
"npm://@testing-library/react": "^13.4.0",
@ -85,6 +86,6 @@
},
"description": "Cosmos Server",
"name": "cosmos-server",
"version": "0.0.2",
"version": "0.0.3",
"wrapInstallFolder": "src"
}

View file

@ -5,14 +5,16 @@
# Cosmos Server
```
Disclaimer: Cosmos is still in early Alpha stage, please be careful when you use it. It is not (yet, at least ;p) a replacement for proper control and mindfulness of your own security.
**Disclaimer**: Cosmos is still in early Alpha stage, please be careful when you use it. It is not (yet, at least ;p) a replacement for proper control and mindfulness of your own security.
```
Looking for a **secure** and **robust** way to run your **self-hosted applications**? With **Cosmos**, you can take control of your data and privacy without sacrificing security and stability.
Looking for the right way to run your **self-hosted applications**? With **Cosmos**, you can take control of your data and privacy without sacrificing security and stability. **Safe** and **secure** platform by design, and most importantly, **easy to setup** without ambiguity. It is a combination of a **reverse proxy**, an **authentication provider** and an **application manager**.
![screenshot1](./screenshot1.png)
Whether you have a **server**, a **NAS**, or a **Raspberry Pi** with applications such as **Plex**, **HomeAssistant** or even a blog, Cosmos is the perfect solution to secure it all. Simply install Cosmos on your server and connect to your applications through it to enjoy built-in security and robustness for all your services, right out of the box.
* **Authentication** Connect to all your application with the same account, including strong security and multi-factor authentication
* **Authentication** Connect to all your application with the same account, including strong security and **multi-factor authentication**
* **Automatic HTTPS** certificates provision
* **Anti-bot** protections such as Captcha and IP rate limiting
* **Anti-DDOS** protections such as variable timeouts/throttling, IP rate limiting and IP blacklisting

BIN
screenshot1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

View file

@ -172,8 +172,8 @@ func StartServer() {
// srapi.Use(utils.AcceptHeader("*/*"))
srapi.Use(utils.CORSHeader(utils.GetMainConfig().HTTPConfig.Hostname))
srapi.Use(utils.MiddlewareTimeout(5 * time.Second))
srapi.Use(httprate.Limit(20, 1*time.Minute,
srapi.Use(utils.MiddlewareTimeout(20 * time.Second))
srapi.Use(httprate.Limit(60, 1*time.Minute,
httprate.WithKeyFuncs(httprate.KeyByIP),
httprate.WithLimitHandler(func(w http.ResponseWriter, r *http.Request) {
utils.Error("Too many requests. Throttling", nil)