add: login page
This commit is contained in:
@ -1,62 +1,41 @@
|
||||
/*
|
||||
* @NOTE: Prepend a `~` to css file paths that are in your node_modules
|
||||
* See https://github.com/webpack-contrib/sass-loader#imports
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Benzin-Bold';
|
||||
src: url('../../assets/fonts/benzin-bold.eot'); /* IE 9 Compatibility Mode */
|
||||
src:
|
||||
url('../../assets/fonts/benzin-bold.eot?#iefix') format('embedded-opentype'),
|
||||
/* IE < 9 */ url('../../assets/fonts/benzin-bold.woff2') format('woff2'),
|
||||
/* Super Modern Browsers */ url('../../assets/fonts/benzin-bold.woff')
|
||||
format('woff'),
|
||||
/* Firefox >= 3.6, any other modern browser */
|
||||
url('../../assets/fonts/benzin-bold.ttf') format('truetype'),
|
||||
/* Safari, Android, iOS */
|
||||
url('../../assets/fonts/benzin-bold.svg#benzin-bold') format('svg'); /* Chrome < 4, Legacy iOS */
|
||||
}
|
||||
|
||||
body {
|
||||
position: relative;
|
||||
color: white;
|
||||
height: 100vh;
|
||||
background: linear-gradient(
|
||||
200.96deg,
|
||||
#fedc2a -29.09%,
|
||||
#dd5789 51.77%,
|
||||
#7a2c9e 129.35%
|
||||
);
|
||||
font-family: sans-serif;
|
||||
background: linear-gradient(200.96deg, #000000, #3b4187);
|
||||
font-family: 'Benzin-Bold' !important;
|
||||
overflow-y: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: white;
|
||||
padding: 10px 20px;
|
||||
border-radius: 10px;
|
||||
border: none;
|
||||
appearance: none;
|
||||
font-size: 1.3rem;
|
||||
box-shadow: 0px 8px 28px -6px rgba(24, 39, 75, 0.12),
|
||||
0px 18px 88px -4px rgba(24, 39, 75, 0.14);
|
||||
transition: all ease-in 0.1s;
|
||||
cursor: pointer;
|
||||
opacity: 0.9;
|
||||
p {
|
||||
font-family: 'Benzin-Bold' !important;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: scale(1.05);
|
||||
opacity: 1;
|
||||
h1 {
|
||||
font-family: 'Benzin-Bold' !important;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
h2 {
|
||||
font-family: 'Benzin-Bold' !important;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
opacity: 1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Hello {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 20px 0;
|
||||
h3 {
|
||||
font-family: 'Benzin-Bold' !important;
|
||||
}
|
||||
|
@ -1,49 +1,12 @@
|
||||
import { MemoryRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import icon from '../../assets/icon.svg';
|
||||
import Login from './pages/Login';
|
||||
import './App.css';
|
||||
|
||||
function Hello() {
|
||||
return (
|
||||
<div>
|
||||
<div className="Hello">
|
||||
<img width="200" alt="icon" src={icon} />
|
||||
</div>
|
||||
<h1>electron-react-boilerplate</h1>
|
||||
<div className="Hello">
|
||||
<a
|
||||
href="https://electron-react-boilerplate.js.org/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<button type="button">
|
||||
<span role="img" aria-label="books">
|
||||
📚
|
||||
</span>
|
||||
Read our docs
|
||||
</button>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/sponsors/electron-react-boilerplate"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<button type="button">
|
||||
<span role="img" aria-label="folded hands">
|
||||
🙏
|
||||
</span>
|
||||
Donate
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<Hello />} />
|
||||
<Route path="/" element={<Login />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
);
|
||||
|
339
src/renderer/pages/Login.tsx
Normal file
339
src/renderer/pages/Login.tsx
Normal file
@ -0,0 +1,339 @@
|
||||
import { Box, Button, Slider, TextField, Typography } from '@mui/material';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function Login() {
|
||||
const [config, setConfig] = useState({
|
||||
username: '',
|
||||
password: '',
|
||||
memory: 4096,
|
||||
comfortVersion: '',
|
||||
accessToken: '',
|
||||
clientToken: '',
|
||||
});
|
||||
const [status, setStatus] = useState('');
|
||||
|
||||
const loadInitialConfig = () => {
|
||||
try {
|
||||
const savedConfig = localStorage.getItem('launcher_config');
|
||||
if (savedConfig) {
|
||||
return JSON.parse(savedConfig);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Ошибка загрузки конфигурации:', error);
|
||||
}
|
||||
// Возвращаем значения по умолчанию
|
||||
return {
|
||||
username: '',
|
||||
password: '',
|
||||
memory: 4096,
|
||||
comfortVersion: '',
|
||||
accessToken: '',
|
||||
clientToken: '',
|
||||
};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const savedConfig = loadInitialConfig();
|
||||
setConfig(savedConfig);
|
||||
}, []);
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setConfig((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const saveConfig = (
|
||||
username,
|
||||
memory,
|
||||
accessToken = '',
|
||||
clientToken = '',
|
||||
comfortVersion = '',
|
||||
password = '',
|
||||
) => {
|
||||
try {
|
||||
const newConfig = {
|
||||
username,
|
||||
memory,
|
||||
accessToken: accessToken || config.accessToken,
|
||||
clientToken: clientToken || config.clientToken,
|
||||
comfortVersion: comfortVersion || config.comfortVersion,
|
||||
password: password || config.password,
|
||||
};
|
||||
setConfig(newConfig);
|
||||
localStorage.setItem('launcher_config', JSON.stringify(newConfig));
|
||||
} catch (error) {
|
||||
console.log(`Ошибка при сохранении конфигурации: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
const loadConfig = () => {
|
||||
try {
|
||||
const savedConfig = localStorage.getItem('launcher_config');
|
||||
if (savedConfig) {
|
||||
const parsedConfig = JSON.parse(savedConfig);
|
||||
setConfig(parsedConfig);
|
||||
return parsedConfig;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Ошибка при загрузке конфигурации: ${error.message}`);
|
||||
}
|
||||
return config;
|
||||
};
|
||||
|
||||
const refreshSession = async (accessToken, clientToken) => {
|
||||
try {
|
||||
const refreshData = {
|
||||
accessToken: accessToken,
|
||||
clientToken: clientToken,
|
||||
};
|
||||
|
||||
const response = await fetch('https://authserver.ely.by/auth/refresh', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(refreshData),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const newAccessToken = data.accessToken;
|
||||
const profile = data.selectedProfile;
|
||||
const uuid = profile.id;
|
||||
const name = profile.name;
|
||||
|
||||
if (newAccessToken && uuid && name) {
|
||||
saveConfig(
|
||||
config.username,
|
||||
config.memory,
|
||||
newAccessToken,
|
||||
clientToken,
|
||||
);
|
||||
return {
|
||||
accessToken: newAccessToken,
|
||||
uuid: uuid,
|
||||
username: name,
|
||||
clientToken: clientToken,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.log(`Ошибка при обновлении сессии: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const authenticateWithElyBy = async (username, password) => {
|
||||
try {
|
||||
const clientToken = crypto.randomUUID();
|
||||
const authData = {
|
||||
username: username,
|
||||
password: password,
|
||||
clientToken: clientToken,
|
||||
requestUser: true,
|
||||
};
|
||||
|
||||
console.log(`Аутентификация пользователя ${username} на Ely.by...`);
|
||||
|
||||
const response = await fetch(
|
||||
'https://authserver.ely.by/auth/authenticate',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(authData),
|
||||
},
|
||||
);
|
||||
|
||||
const responseData = await response.json();
|
||||
if (response.ok) {
|
||||
const accessToken = responseData.accessToken;
|
||||
const profile = responseData.selectedProfile;
|
||||
const uuid = profile.id;
|
||||
const name = profile.name;
|
||||
|
||||
if (accessToken && uuid && name) {
|
||||
saveConfig(
|
||||
username,
|
||||
config.memory,
|
||||
accessToken,
|
||||
clientToken,
|
||||
'',
|
||||
password,
|
||||
);
|
||||
console.log(`Аутентификация успешна: UUID=${uuid}, Username=${name}`);
|
||||
|
||||
return {
|
||||
accessToken: accessToken,
|
||||
uuid: uuid,
|
||||
username: name,
|
||||
clientToken: clientToken,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (responseData.error === 'Account protected with two factor auth') {
|
||||
const totpToken = prompt(
|
||||
'Введите код двухфакторной аутентификации:',
|
||||
'',
|
||||
);
|
||||
|
||||
if (totpToken) {
|
||||
authData.password = `${password}:${totpToken}`;
|
||||
const totpResponse = await fetch(
|
||||
'https://authserver.ely.by/auth/authenticate',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(authData),
|
||||
},
|
||||
);
|
||||
if (totpResponse.ok) {
|
||||
const totpData = await totpResponse.json();
|
||||
const newAccessToken = totpData.accessToken;
|
||||
const newProfile = totpData.selectedProfile;
|
||||
const newUuid = newProfile.id;
|
||||
const newName = newProfile.name;
|
||||
|
||||
if (newAccessToken && newUuid && newName) {
|
||||
saveConfig(
|
||||
username,
|
||||
config.memory,
|
||||
newAccessToken,
|
||||
clientToken,
|
||||
'',
|
||||
password,
|
||||
);
|
||||
return {
|
||||
accessToken: newAccessToken,
|
||||
uuid: newUuid,
|
||||
username: newName,
|
||||
clientToken: clientToken,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(responseData.error || 'Ошибка авторизации');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Ошибка авторизации: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const authorization = async () => {
|
||||
console.log('Начинаем процесс авторизации...');
|
||||
|
||||
if (!config.username.trim()) {
|
||||
console.log('Ошибка: не указан никнейм');
|
||||
alert('Введите никнейм!');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Проверяем, есть ли сохранённый токен
|
||||
if (config.accessToken && config.clientToken) {
|
||||
console.log('Проверка валидности существующего токена...');
|
||||
const isValid = await validateSession(config.accessToken);
|
||||
|
||||
if (!isValid) {
|
||||
console.log('Токен недействителен, пытаемся обновить...');
|
||||
const refreshedSession = await refreshSession(
|
||||
config.accessToken,
|
||||
config.clientToken,
|
||||
);
|
||||
|
||||
if (!refreshedSession) {
|
||||
console.log(
|
||||
'Не удалось обновить токен, требуется новая авторизация',
|
||||
);
|
||||
const newSession = await authenticateWithElyBy(
|
||||
config.username,
|
||||
config.password,
|
||||
);
|
||||
if (!newSession) {
|
||||
console.log('Авторизация не удалась');
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('Токен действителен');
|
||||
}
|
||||
} else {
|
||||
console.log('Токен отсутствует, выполняем авторизацию...');
|
||||
const session = await authenticateWithElyBy(
|
||||
config.username,
|
||||
config.password,
|
||||
);
|
||||
if (!session) {
|
||||
console.log('Авторизация не удалась');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Авторизация успешно завершена');
|
||||
// Здесь можно добавить логику для запуска игры
|
||||
} catch (error) {
|
||||
console.log(`ОШИБКА при авторизации: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<Typography variant="h3">POPA</Typography>
|
||||
<Typography variant="h3">-</Typography>
|
||||
<Typography
|
||||
variant="h3"
|
||||
sx={{
|
||||
background: '-webkit-linear-gradient(200.96deg, #88BCFF, #FD71FF)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
POPA
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '1.5vw' }}>
|
||||
<TextField
|
||||
required
|
||||
name="username"
|
||||
label="Введите ник"
|
||||
variant="outlined"
|
||||
value={config.username}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
type="password"
|
||||
name="password"
|
||||
label="Введите пароль"
|
||||
variant="outlined"
|
||||
value={config.password}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Slider
|
||||
name="memory"
|
||||
aria-label="Temperature"
|
||||
defaultValue={4096}
|
||||
valueLabelDisplay="auto"
|
||||
shiftStep={1024}
|
||||
step={1024}
|
||||
marks
|
||||
min={1024}
|
||||
max={32628}
|
||||
value={config.memory}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Button onClick={authorization} variant="contained">
|
||||
Войти
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user