From 12f7ea8d1c8dd786f620a729c150cb4f58022602 Mon Sep 17 00:00:00 2001 From: DIKER0K Date: Sat, 5 Jul 2025 19:48:05 +0500 Subject: [PATCH] refactoring, check auth --- src/renderer/App.tsx | 73 ++++- src/renderer/components/Login/AuthForm.tsx | 39 +++ .../components/Login/MemorySlider.tsx | 26 ++ src/renderer/hooks/useAuth.ts | 177 +++++++++++ src/renderer/hooks/useConfig.ts | 69 +++++ src/renderer/pages/Dashboard.tsx | 31 ++ src/renderer/pages/Login.tsx | 291 ++---------------- tsconfig.json | 1 + 8 files changed, 440 insertions(+), 267 deletions(-) create mode 100644 src/renderer/components/Login/AuthForm.tsx create mode 100644 src/renderer/components/Login/MemorySlider.tsx create mode 100644 src/renderer/hooks/useAuth.ts create mode 100644 src/renderer/hooks/useConfig.ts create mode 100644 src/renderer/pages/Dashboard.tsx diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 181977a..1247173 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,13 +1,78 @@ -import { MemoryRouter as Router, Routes, Route } from 'react-router-dom'; +import { + MemoryRouter as Router, + Routes, + Route, + Navigate, +} from 'react-router-dom'; import Login from './pages/Login'; +import Dashboard from './pages/Dashboard'; +import { ReactNode, useEffect, useState } from 'react'; import './App.css'; -export default function App() { +const AuthCheck = ({ children }: { children: ReactNode }) => { + const [isAuthenticated, setIsAuthenticated] = useState(null); + + useEffect(() => { + const checkAuth = async () => { + try { + const savedConfig = localStorage.getItem('launcher_config'); + if (savedConfig) { + const config = JSON.parse(savedConfig); + if (config.accessToken) { + // Можно добавить дополнительную проверку токена + const isValid = await validateToken(config.accessToken); + setIsAuthenticated(isValid); + return; + } + } + setIsAuthenticated(false); + } catch (error) { + console.error('Ошибка проверки авторизации:', error); + setIsAuthenticated(false); + } + }; + + checkAuth(); + }, []); + + const validateToken = async (token: string) => { + try { + const response = await fetch('https://authserver.ely.by/auth/validate', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ accessToken: token }), + }); + return response.ok; + } catch (error) { + return false; + } + }; + + if (isAuthenticated === null) { + return
Loading...
; + } + + return isAuthenticated ? children : ; +}; + +const App = () => { return ( - } /> + } /> + + + + } + /> ); -} +}; + +export default App; diff --git a/src/renderer/components/Login/AuthForm.tsx b/src/renderer/components/Login/AuthForm.tsx new file mode 100644 index 0000000..68df9d3 --- /dev/null +++ b/src/renderer/components/Login/AuthForm.tsx @@ -0,0 +1,39 @@ +import { Box, Button, TextField, Typography } from '@mui/material'; + +interface AuthFormProps { + config: { + username: string; + password: string; + }; + handleInputChange: (e: React.ChangeEvent) => void; + onLogin: () => void; +} + +const AuthForm = ({ config, handleInputChange, onLogin }: AuthFormProps) => { + return ( + + + + + + ); +}; + +export default AuthForm; diff --git a/src/renderer/components/Login/MemorySlider.tsx b/src/renderer/components/Login/MemorySlider.tsx new file mode 100644 index 0000000..4911179 --- /dev/null +++ b/src/renderer/components/Login/MemorySlider.tsx @@ -0,0 +1,26 @@ +import { Slider } from '@mui/material'; + +interface MemorySliderProps { + memory: number; + onChange: (e: Event, value: number | number[]) => void; +} + +const MemorySlider = ({ memory, onChange }: MemorySliderProps) => { + return ( + + ); +}; + +export default MemorySlider; diff --git a/src/renderer/hooks/useAuth.ts b/src/renderer/hooks/useAuth.ts new file mode 100644 index 0000000..f8fbfb1 --- /dev/null +++ b/src/renderer/hooks/useAuth.ts @@ -0,0 +1,177 @@ +import { useState } from 'react'; + +const useAuth = () => { + const [status, setStatus] = useState(''); + + const validateSession = async (accessToken: string) => { + try { + const response = await fetch('https://authserver.ely.by/auth/validate', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + accessToken: accessToken, + }), + }); + return response.ok; + } catch (error) { + console.log(`Ошибка при проверке токена: ${error.message}`); + return false; + } + }; + + const refreshSession = async (accessToken: string, clientToken: string) => { + 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) { + return { + accessToken: newAccessToken, + uuid: uuid, + username: name, + clientToken: clientToken, + }; + } + } + return null; + } catch (error) { + console.log(`Ошибка при обновлении сессии: ${error.message}`); + return null; + } + }; + + const authenticateWithElyBy = async ( + username: string, + password: string, + saveConfig: Function, + ) => { + 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, + 4096, // default 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, + 4096, // default 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; + } + }; + + return { + status, + setStatus, + validateSession, + refreshSession, + authenticateWithElyBy, + }; +}; + +export default useAuth; diff --git a/src/renderer/hooks/useConfig.ts b/src/renderer/hooks/useConfig.ts new file mode 100644 index 0000000..9f3f81c --- /dev/null +++ b/src/renderer/hooks/useConfig.ts @@ -0,0 +1,69 @@ +import { useState, useEffect } from 'react'; + +const useConfig = () => { + const [config, setConfig] = useState({ + username: '', + password: '', + memory: 4096, + comfortVersion: '', + accessToken: '', + clientToken: '', + }); + + 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 saveConfig = ( + username: string, + memory: number, + 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 handleInputChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setConfig((prev) => ({ ...prev, [name]: value })); + }; + + return { config, setConfig, saveConfig, handleInputChange }; +}; + +export default useConfig; diff --git a/src/renderer/pages/Dashboard.tsx b/src/renderer/pages/Dashboard.tsx new file mode 100644 index 0000000..47c858c --- /dev/null +++ b/src/renderer/pages/Dashboard.tsx @@ -0,0 +1,31 @@ +import { Box, Typography, Button } from '@mui/material'; +import { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; + +const Dashboard = () => { + const navigate = useNavigate(); + + useEffect(() => { + // Проверяем авторизацию при монтировании компонента + const savedConfig = localStorage.getItem('launcher_config'); + if (!savedConfig || !JSON.parse(savedConfig).accessToken) { + navigate('/login'); + } + }, [navigate]); + + const handleLogout = () => { + localStorage.removeItem('launcher_config'); + navigate('/login'); + }; + + return ( + + Добро пожаловать в лаунчер + + + ); +}; + +export default Dashboard; diff --git a/src/renderer/pages/Login.tsx b/src/renderer/pages/Login.tsx index db56926..1a501cb 100644 --- a/src/renderer/pages/Login.tsx +++ b/src/renderer/pages/Login.tsx @@ -1,230 +1,15 @@ -import { Box, Button, Slider, TextField, Typography } from '@mui/material'; -import { useEffect, useState } from 'react'; +import { Box, Typography } from '@mui/material'; +import useConfig from '../hooks/useConfig'; +import useAuth from '../hooks/useAuth'; +import AuthForm from '../components/Login/AuthForm'; +import MemorySlider from '../components/Login/MemorySlider'; +import { useNavigate } from 'react-router-dom'; -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 Login = () => { + const navigate = useNavigate(); + const { config, setConfig, saveConfig, handleInputChange } = useConfig(); + const { status, validateSession, refreshSession, authenticateWithElyBy } = + useAuth(); const authorization = async () => { console.log('Начинаем процесс авторизации...'); @@ -255,6 +40,7 @@ export default function Login() { const newSession = await authenticateWithElyBy( config.username, config.password, + saveConfig, ); if (!newSession) { console.log('Авторизация не удалась'); @@ -269,6 +55,7 @@ export default function Login() { const session = await authenticateWithElyBy( config.username, config.password, + saveConfig, ); if (!session) { console.log('Авторизация не удалась'); @@ -277,7 +64,7 @@ export default function Login() { } console.log('Авторизация успешно завершена'); - // Здесь можно добавить логику для запуска игры + navigate('/'); } catch (error) { console.log(`ОШИБКА при авторизации: ${error.message}`); } @@ -299,41 +86,19 @@ export default function Login() { POPA - - - - - - + + { + setConfig((prev) => ({ ...prev, memory: value as number })); + }} + /> ); -} +}; + +export default Login; diff --git a/tsconfig.json b/tsconfig.json index bb92a6b..cdb1862 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "useUnknownInCatchVariables": false, "incremental": true, "target": "es2022", "module": "node16",