86 lines
3.2 KiB
TypeScript
86 lines
3.2 KiB
TypeScript
|
|
import React, { useState } from 'react';
|
|||
|
|
import Button from './Button';
|
|||
|
|
import Logo from './Logo';
|
|||
|
|
import { APP_NAME } from '../constants';
|
|||
|
|
|
|||
|
|
interface AdminLoginPageProps {
|
|||
|
|
onLoginSuccess: () => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SHA-256 hash of "IZI348ax@" is 8b1f38e07289947545ea88540871147573199ce071804100412e0b5f590d7c71
|
|||
|
|
const STORED_HASH = '8b1f38e07289947545ea88540871147573199ce071804100412e0b5f590d7c71';
|
|||
|
|
|
|||
|
|
async function sha256(message: string): Promise<string> {
|
|||
|
|
const msgBuffer = new TextEncoder().encode(message);
|
|||
|
|
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
|
|||
|
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|||
|
|
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|||
|
|
return hashHex;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const AdminLoginPage: React.FC<AdminLoginPageProps> = ({ onLoginSuccess }) => {
|
|||
|
|
const [password, setPassword] = useState('');
|
|||
|
|
const [error, setError] = useState('');
|
|||
|
|
const [isLoading, setIsLoading] = useState(false);
|
|||
|
|
|
|||
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|||
|
|
e.preventDefault();
|
|||
|
|
setIsLoading(true);
|
|||
|
|
setError('');
|
|||
|
|
const inputHash = await sha256(password);
|
|||
|
|
if (inputHash === STORED_HASH) {
|
|||
|
|
onLoginSuccess();
|
|||
|
|
} else {
|
|||
|
|
setError('Неверный пароль. Пожалуйста, попробуйте снова.');
|
|||
|
|
}
|
|||
|
|
setIsLoading(false);
|
|||
|
|
setPassword('');
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="min-h-screen flex flex-col items-center justify-center bg-white p-4">
|
|||
|
|
<div className="w-full max-w-md">
|
|||
|
|
<div className="text-center mb-8">
|
|||
|
|
<Logo width={120} height={90} className="mx-auto" />
|
|||
|
|
<h1 className="mt-6 text-3xl font-quicksand font-bold text-slate-800">
|
|||
|
|
Админ-панель {APP_NAME}
|
|||
|
|
</h1>
|
|||
|
|
<p className="mt-2 text-sm text-slate-600">
|
|||
|
|
Редактирование констант приложения
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
<form onSubmit={handleSubmit} className="bg-white rounded-xl p-8 space-y-6">
|
|||
|
|
<div>
|
|||
|
|
<label htmlFor="password" className="block text-sm font-medium text-slate-700 font-quicksand">
|
|||
|
|
Пароль
|
|||
|
|
</label>
|
|||
|
|
<input
|
|||
|
|
type="password"
|
|||
|
|
id="password"
|
|||
|
|
value={password}
|
|||
|
|
onChange={(e) => setPassword(e.target.value)}
|
|||
|
|
required
|
|||
|
|
className="mt-1 block w-full px-3 py-2 bg-white border border-slate-300 rounded-md placeholder-slate-400 focus:outline-none focus:ring-slate-500 focus:border-slate-500 sm:text-sm text-slate-900"
|
|||
|
|
placeholder="Введите ваш пароль"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
{error && <p className="text-xs text-red-600">{error}</p>}
|
|||
|
|
<Button
|
|||
|
|
type="submit"
|
|||
|
|
variant="primary"
|
|||
|
|
size="lg"
|
|||
|
|
className="w-full"
|
|||
|
|
disabled={isLoading}
|
|||
|
|
>
|
|||
|
|
{isLoading ? 'Вход...' : 'Войти'}
|
|||
|
|
</Button>
|
|||
|
|
</form>
|
|||
|
|
<p className="mt-8 text-center text-xs text-slate-500">
|
|||
|
|
Только для авторизованного персонала. Все действия могут отслеживаться.
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default AdminLoginPage;
|