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:
SnippetsX 2024-11-06 00:38:03 +03:00
parent c711fb949f
commit a73a511fa4
16 changed files with 97 additions and 36 deletions

3
.gitignore vendored
View File

@ -2,14 +2,17 @@
# dependencies
/node_modules
*/node_modules
/.pnp
.pnp.js
# testing
/coverage
/serverside-agent
# production
/build
*/build
# misc
.DS_Store

View File

@ -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
![Image](/screenshots/screenshot1.png)
![Image](/screenshots/screenshot2.png)
## How to run
To run the project, you can download/clone the repository and use these commands to run:
```bash
npm install

View File

@ -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
View File

@ -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",

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
screenshots/screenshot2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -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'))

View File

@ -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/>

View File

@ -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;
}

View File

@ -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"

View File

@ -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
View 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
View 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();
});
}

View File

@ -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);

1
test.js Normal file
View File

@ -0,0 +1 @@
console.log(JSON.parse('{"token":"admin"}'))