added working login page
added login page wrong pass animation added backend connect with db added backend support for system monitor added perms check added sha256 hashing updated readme.md added screenshots fully db
This commit is contained in:
parent
c711fb949f
commit
a73a511fa4
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,14 +2,17 @@
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
*/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
/serverside-agent
|
||||
# production
|
||||
/build
|
||||
*/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
# Admin Dashboard writed on React
|
||||
This project is a simple admin panel designed for managing servers. \
|
||||
It utilizes Material UI for the user interface components. \
|
||||
## Screenshots
|
||||

|
||||

|
||||
|
||||
|
||||
## How to run
|
||||
To run the project, you can download/clone the repository and use these commands to run:
|
||||
```bash
|
||||
npm install
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
CREATE TABLE users (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
email VARCHAR(100) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
perms VARCHAR(255) NOT NULL
|
||||
);
|
||||
|
||||
-- Test user with admin permissions
|
||||
INSERT INTO users (username, password_hash, perms)
|
||||
VALUES ('admin', 'ecd71870d1963316a97e3ac3408c9835ad8cf0f3c1bc703527c30265534f75ae', 'admin');
|
||||
|
||||
7
package-lock.json
generated
7
package-lock.json
generated
@ -18,6 +18,7 @@
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"codegen": "^0.1.0",
|
||||
"eslint-plugin-react": "^7.37.1",
|
||||
"js-sha256": "^0.11.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.27.0",
|
||||
@ -11707,6 +11708,12 @@
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"node_modules/js-sha256": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.0.tgz",
|
||||
"integrity": "sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"codegen": "^0.1.0",
|
||||
"eslint-plugin-react": "^7.37.1",
|
||||
"js-sha256": "^0.11.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.27.0",
|
||||
|
||||
BIN
screenshots/screenshot1.png
Normal file
BIN
screenshots/screenshot1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
screenshots/screenshot2.png
Normal file
BIN
screenshots/screenshot2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
13
server.js
13
server.js
@ -1,13 +0,0 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const app = express();
|
||||
|
||||
app.use(cors());
|
||||
|
||||
app.use('/login', (req, res) =>{
|
||||
res.send({
|
||||
token: 'snippetsx'
|
||||
})
|
||||
})
|
||||
|
||||
app.listen(8080, () => console.log('API Running on localhost:8080'))
|
||||
@ -30,20 +30,9 @@ import {SystemMon, WebsiteAvailability} from '../widgets/WidgetsStatistics'
|
||||
const drawerWidth = 240;
|
||||
|
||||
export default function DashboardMain() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const openMenu = Boolean(anchorEl);
|
||||
|
||||
const handleDrawer = () => {
|
||||
if(open === false){
|
||||
setOpen(true);
|
||||
}
|
||||
else{
|
||||
setOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleMenuClick = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
@ -144,6 +133,7 @@ export default function DashboardMain() {
|
||||
sx={{ flexGrow: 1, bgcolor: 'background.default', p: 3 }}
|
||||
>
|
||||
<Toolbar />
|
||||
{/* <Typography variant="h4">Hi {localStorage.getItem('token')}</Typography> */}
|
||||
<Box sx={{ display: 'flex', gap: 3 }}>
|
||||
<SystemMon/>
|
||||
<WebsiteAvailability/>
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
@keyframes shake {
|
||||
0% { transform: translateX(0); }
|
||||
10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
|
||||
20%, 40%, 60%, 80% { transform: translateX(10px); }
|
||||
100% { transform: translateX(0); }
|
||||
}
|
||||
.shake-animation {
|
||||
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
|
||||
}
|
||||
@ -4,18 +4,27 @@ import { Button, Container, TextField, Typography, AppBar, Toolbar, Box } from '
|
||||
import { ThemeProvider } from '@mui/material/styles';
|
||||
import theme from '../theme';
|
||||
import {loginUser} from './LoginServerSend'
|
||||
import './LoginPage.css'
|
||||
import { sha256 } from 'js-sha256';
|
||||
|
||||
export default function Login({ setToken }) {
|
||||
const [username, setUserName] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [msg, setMsg] = useState('');
|
||||
|
||||
const handleSubmit = async e => {
|
||||
e.preventDefault();
|
||||
const token = await loginUser({
|
||||
username,
|
||||
password
|
||||
});
|
||||
setToken(token);
|
||||
try {
|
||||
|
||||
const token = await loginUser({
|
||||
username,
|
||||
password
|
||||
});
|
||||
setToken(token);
|
||||
} catch (error) {
|
||||
// Add shake animation and red color to form if error
|
||||
setMsg(true);
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
@ -63,8 +72,11 @@ export default function Login({ setToken }) {
|
||||
label="Password"
|
||||
type="password"
|
||||
id="password"
|
||||
error={!!msg}
|
||||
helperText={msg ? "Incorrect password" : ""}
|
||||
className={msg ? 'shake-animation' : ''}
|
||||
autoComplete="current-password"
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
onChange={e => setPassword(sha256(e.target.value))}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
|
||||
@ -6,5 +6,10 @@ export async function loginUser(credentials) {
|
||||
},
|
||||
body: JSON.stringify(credentials)
|
||||
})
|
||||
.then(data => data.json())
|
||||
.then(response => {
|
||||
if (response.status === 401) {
|
||||
throw new Error('Invalid credentials');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
}
|
||||
15
src/core/requestPerms.js
Normal file
15
src/core/requestPerms.js
Normal file
@ -0,0 +1,15 @@
|
||||
export async function getPerms() {
|
||||
return fetch('http://localhost:8080/check-perms', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch permissions');
|
||||
}
|
||||
return response.json();
|
||||
});
|
||||
}
|
||||
14
src/core/requestStats.js
Normal file
14
src/core/requestStats.js
Normal file
@ -0,0 +1,14 @@
|
||||
export async function getSystemStats() {
|
||||
return fetch('http://localhost:8080/system-stats', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch system stats');
|
||||
}
|
||||
return response.json();
|
||||
});
|
||||
}
|
||||
@ -6,6 +6,7 @@ import {
|
||||
} from '@mui/material';
|
||||
import { ThemeProvider } from '@mui/material/styles';
|
||||
import theme from '../theme';
|
||||
import { getSystemStats } from '../core/requestStats'
|
||||
|
||||
export const SystemMon = () => {
|
||||
const [cpuUsage, setCpuUsage] = useState(0);
|
||||
@ -14,8 +15,16 @@ export const SystemMon = () => {
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
// Симуляция получения данных об использовании CPU и RAM
|
||||
setCpuUsage(Math.floor(Math.random() * 100));
|
||||
setRamUsage(Math.floor(Math.random() * 100));
|
||||
getSystemStats()
|
||||
.then(stats => {
|
||||
setCpuUsage(stats.cpu);
|
||||
setRamUsage(stats.memory);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Failed to fetch system stats:', error);
|
||||
setCpuUsage(0);
|
||||
setRamUsage(0);
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user