mirror of
https://github.com/crypto-pro-web/crypto-pro-js.git
synced 2024-11-27 21:06:01 +03:00
Актуализировал пример использования с React
This commit is contained in:
parent
3d71049078
commit
864c05f81c
@ -1,38 +0,0 @@
|
|||||||
.App {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-logo {
|
|
||||||
height: 40vmin;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
.App-logo {
|
|
||||||
animation: App-logo-spin infinite 20s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-header {
|
|
||||||
background-color: #282c34;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: calc(10px + 2vmin);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-link {
|
|
||||||
color: #61dafb;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes App-logo-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +1,118 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import './App.css';
|
import { createAttachedSignature, createDetachedSignature, createHash } from 'crypto-pro';
|
||||||
import CryptoPro from './components/CryptoPro';
|
import Message from './components/Message';
|
||||||
|
import Certificate from './components/Certificate';
|
||||||
|
import SignatureType from './components/SignatureType';
|
||||||
|
import Hash from './components/Hash';
|
||||||
|
import Signature from './components/Signature';
|
||||||
|
import SystemInfo from './components/SystemInfo';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const [message, setMessage] = useState('');
|
||||||
|
const [certificate, setCertificate] = useState(null);
|
||||||
|
const [detachedSignature, setSignatureType] = useState(null);
|
||||||
|
const [hash, setHash] = useState('');
|
||||||
|
const [hashStatus, setHashStatus] = useState('Не вычислен');
|
||||||
|
const [hashError, setHashError] = useState(null);
|
||||||
|
const [signature, setSignature] = useState('');
|
||||||
|
const [signatureStatus, setSignatureStatus] = useState('Не создана');
|
||||||
|
const [signatureError, setSignatureError] = useState(null);
|
||||||
|
|
||||||
|
async function createSignature(event) {
|
||||||
|
let hash;
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
setSignature('');
|
||||||
|
setSignatureError(null);
|
||||||
|
|
||||||
|
setHash('');
|
||||||
|
setHashError(null);
|
||||||
|
setHashStatus('Вычисляется...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
hash = await createHash(message);
|
||||||
|
|
||||||
|
setHash(hash);
|
||||||
|
} catch (error) {
|
||||||
|
setHashError(error.message);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setHashStatus('Не вычислен');
|
||||||
|
setSignatureStatus('Создается...');
|
||||||
|
|
||||||
|
if (detachedSignature) {
|
||||||
|
try {
|
||||||
|
setSignature(await createDetachedSignature(certificate.thumbprint, hash));
|
||||||
|
} catch (error) {
|
||||||
|
setSignatureError(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSignatureStatus('Не создана');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setSignature(await createAttachedSignature(certificate.thumbprint, message));
|
||||||
|
} catch (error) {
|
||||||
|
setSignatureError(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSignatureStatus('Не создана');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CryptoPro>{({certificate, setCertificate, signature, setSignature, error, setError}) =>
|
|
||||||
<>
|
<>
|
||||||
<CryptoPro.CertList onChange={setCertificate} onError={setError}/>
|
<form onSubmit={createSignature} noValidate>
|
||||||
<CryptoPro.Sign certificate={certificate} onSign={setSignature} onError={setError}>
|
<fieldset>
|
||||||
|
<Message onChange={setMessage}/>
|
||||||
|
|
||||||
|
<br/><br/>
|
||||||
|
|
||||||
|
<Certificate onChange={setCertificate}/>
|
||||||
|
|
||||||
|
<SignatureType onChange={setSignatureType}/>
|
||||||
|
|
||||||
|
<br/><br/>
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={!certificate || !message}>
|
||||||
Создать подпись
|
Создать подпись
|
||||||
</CryptoPro.Sign>
|
</button>
|
||||||
<CryptoPro.CertInfo certificate={certificate} onError={setError}/>
|
</fieldset>
|
||||||
<textarea value={signature} readOnly cols="100" rows="30"/>
|
</form>
|
||||||
<pre>{error}</pre>
|
|
||||||
<CryptoPro.SystemInfo onError={setError}/>
|
<fieldset>
|
||||||
|
<Hash
|
||||||
|
hash={hash}
|
||||||
|
hashStatus={hashStatus}
|
||||||
|
hashError={hashError}/>
|
||||||
|
|
||||||
|
<Signature
|
||||||
|
signature={signature}
|
||||||
|
signatureStatus={signatureStatus}
|
||||||
|
signatureError={signatureError}/>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Для <a href="https://www.gosuslugi.ru/pgu/eds/"
|
||||||
|
target="_blank"
|
||||||
|
rel="nofollow noopener noreferrer"
|
||||||
|
title="Перейти к проверке подписи">проверки</a> нужно
|
||||||
|
создать файл со сгенерированной подписью в кодировке UTF-8 с расширением *.sgn
|
||||||
|
<br/>
|
||||||
|
для отделенной подписи (или *.sig для совмещенной).
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<SystemInfo/>
|
||||||
|
</fieldset>
|
||||||
</>
|
</>
|
||||||
}</CryptoPro>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
101
examples/react/src/components/Certificate.js
Normal file
101
examples/react/src/components/Certificate.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { getCertificate, getUserCertificates } from 'crypto-pro';
|
||||||
|
|
||||||
|
function Certificate({onChange}) {
|
||||||
|
const [certificates, setCertificates] = useState([]);
|
||||||
|
const [certificatesError, setCertificatesError] = useState([]);
|
||||||
|
const [certificate, setCertificate] = useState(null);
|
||||||
|
const [certificateDetails, setCertificateDetails] = useState(null);
|
||||||
|
const [detailsError, setDetailsError] = useState(null);
|
||||||
|
|
||||||
|
function selectCertificate(event) {
|
||||||
|
const certificate = certificates.find(({thumbprint}) => thumbprint === event.target.value);
|
||||||
|
|
||||||
|
setCertificate(certificate);
|
||||||
|
onChange(certificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadCertificateDetails(thumbprint) {
|
||||||
|
try {
|
||||||
|
const certificate = await getCertificate(thumbprint);
|
||||||
|
|
||||||
|
setCertificateDetails({
|
||||||
|
name: certificate.name,
|
||||||
|
issuerName: certificate.issuerName,
|
||||||
|
subjectName: certificate.subjectName,
|
||||||
|
thumbprint: certificate.thumbprint,
|
||||||
|
validFrom: certificate.validFrom,
|
||||||
|
validTo: certificate.validTo,
|
||||||
|
isValid: await certificate.isValid(),
|
||||||
|
version: await certificate.getCadesProp('Version'),
|
||||||
|
base64: await certificate.exportBase64(),
|
||||||
|
algorithm: await certificate.getAlgorithm(),
|
||||||
|
extendedKeyUsage: await certificate.getExtendedKeyUsage(),
|
||||||
|
ownerInfo: await certificate.getOwnerInfo(),
|
||||||
|
issuerInfo: await certificate.getIssuerInfo(),
|
||||||
|
decodedExtendedKeyUsage: await certificate.getDecodedExtendedKeyUsage(),
|
||||||
|
'1.3.6.1.4.1.311.80.1': await certificate.hasExtendedKeyUsage('1.3.6.1.4.1.311.80.1'),
|
||||||
|
'[\'1.3.6.1.5.5.7.3.2\', \'1.3.6.1.4.1.311.10.3.12\']': await certificate.hasExtendedKeyUsage([
|
||||||
|
'1.3.6.1.5.5.7.3.2',
|
||||||
|
'1.3.6.1.4.1.311.10.3.12'
|
||||||
|
]),
|
||||||
|
'1.3.6.1.4.1.311.80.2': await certificate.hasExtendedKeyUsage('1.3.6.1.4.1.311.80.2'),
|
||||||
|
'\'1.3.6.1.5.5.7.3.3\', \'1.3.6.1.4.1.311.10.3.12\'': await certificate.hasExtendedKeyUsage([
|
||||||
|
'1.3.6.1.5.5.7.3.3',
|
||||||
|
'1.3.6.1.4.1.311.10.3.12'
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
setDetailsError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
setCertificates(await getUserCertificates());
|
||||||
|
} catch (error) {
|
||||||
|
setCertificatesError(error.message);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label htmlFor="certificate">Сертификат: *</label>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<select id="certificate" onChange={selectCertificate}>
|
||||||
|
<option defaultValue={null}>Не выбран</option>
|
||||||
|
|
||||||
|
{certificates.map(({name, thumbprint, validTo}) =>
|
||||||
|
<option key={thumbprint} value={thumbprint}>
|
||||||
|
{name + ' (действителен до: ' + validTo + ')'}
|
||||||
|
</option>
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<pre>{certificatesError || null}</pre>
|
||||||
|
|
||||||
|
{certificate ? (
|
||||||
|
<>
|
||||||
|
<details
|
||||||
|
onClick={loadCertificateDetails.bind(this, certificate.thumbprint)}>
|
||||||
|
<summary>Информация о сертификате</summary>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{certificateDetails ? (
|
||||||
|
JSON.stringify(certificateDetails, null, ' ')
|
||||||
|
) : 'Запрашивается...'}
|
||||||
|
</pre>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<pre>{detailsError || null}</pre>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Certificate;
|
@ -1,55 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
function CertInfo({certificate, onError}) {
|
|
||||||
const [certInfo, setCertInfo] = useState(null);
|
|
||||||
|
|
||||||
async function showCertInfo() {
|
|
||||||
try {
|
|
||||||
setCertInfo({
|
|
||||||
name: certificate.name,
|
|
||||||
issuerName: certificate.issuerName,
|
|
||||||
subjectName: certificate.subjectName,
|
|
||||||
thumbprint: certificate.thumbprint,
|
|
||||||
validFrom: certificate.validFrom,
|
|
||||||
validTo: certificate.validTo,
|
|
||||||
isValid: await certificate.isValid(),
|
|
||||||
version: await certificate.getCadesProp('Version'),
|
|
||||||
base64: await certificate.exportBase64(),
|
|
||||||
algorithm: await certificate.getAlgorithm(),
|
|
||||||
extendedKeyUsage: await certificate.getExtendedKeyUsage(),
|
|
||||||
ownerInfo: await certificate.getOwnerInfo(),
|
|
||||||
issuerInfo: await certificate.getIssuerInfo(),
|
|
||||||
decodedExtendedKeyUsage: await certificate.getDecodedExtendedKeyUsage(),
|
|
||||||
'1.3.6.1.4.1.311.80.1': await certificate.hasExtendedKeyUsage('1.3.6.1.4.1.311.80.1'),
|
|
||||||
"['1.3.6.1.5.5.7.3.2', '1.3.6.1.4.1.311.10.3.12']": await certificate.hasExtendedKeyUsage([
|
|
||||||
'1.3.6.1.5.5.7.3.2',
|
|
||||||
'1.3.6.1.4.1.311.10.3.12'
|
|
||||||
]),
|
|
||||||
'1.3.6.1.4.1.311.80.2': await certificate.hasExtendedKeyUsage('1.3.6.1.4.1.311.80.2'),
|
|
||||||
"'1.3.6.1.5.5.7.3.3', '1.3.6.1.4.1.311.10.3.12'": await certificate.hasExtendedKeyUsage([
|
|
||||||
'1.3.6.1.5.5.7.3.3',
|
|
||||||
'1.3.6.1.4.1.311.10.3.12'
|
|
||||||
]),
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
onError(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={showCertInfo}
|
|
||||||
disabled={!certificate}>
|
|
||||||
Информация о сертификате
|
|
||||||
</button>
|
|
||||||
<br/>
|
|
||||||
{certInfo ? (
|
|
||||||
<pre>{JSON.stringify(certInfo, null, ' ')}</pre>
|
|
||||||
) : null}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CertInfo;
|
|
@ -1,31 +0,0 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { getUserCertificates } from 'crypto-pro';
|
|
||||||
|
|
||||||
function CertList({onChange, onError}) {
|
|
||||||
const [certificates, setCertificates] = useState([]);
|
|
||||||
|
|
||||||
function selectCertificate(event) {
|
|
||||||
onChange(certificates.find(({thumbprint}) => thumbprint === event.target.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
setCertificates(await getUserCertificates());
|
|
||||||
} catch (error) {
|
|
||||||
onError(error.message);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, [onError]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<select onChange={selectCertificate}>
|
|
||||||
<option defaultValue={null}>Не выбран</option>
|
|
||||||
{certificates.map(({name, thumbprint}) =>
|
|
||||||
<option key={thumbprint} value={thumbprint}>{name}</option>
|
|
||||||
)}
|
|
||||||
</select>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CertList;
|
|
@ -1,27 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { createSignature } from 'crypto-pro';
|
|
||||||
|
|
||||||
function Sign({certificate, onSign, onError, children}) {
|
|
||||||
async function sign() {
|
|
||||||
// Вычислинный hash по ГОСТ Р 34.11-94 для строки: "abc"
|
|
||||||
const hash = 'b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c';
|
|
||||||
const hashBase64 = window.btoa(hash);
|
|
||||||
|
|
||||||
try {
|
|
||||||
onSign(await createSignature(certificate.thumbprint, hashBase64));
|
|
||||||
} catch (error) {
|
|
||||||
onError(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
disabled={!certificate}
|
|
||||||
onClick={sign}>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Sign;
|
|
@ -1,27 +0,0 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import CertList from './CertList';
|
|
||||||
import CertInfo from './CertInfo';
|
|
||||||
import Sign from './Sign';
|
|
||||||
import SystemInfo from './SystemInfo';
|
|
||||||
|
|
||||||
const CryptoPro = ({children}) => {
|
|
||||||
const [certificate, setCertificate] = useState(null);
|
|
||||||
const [signature, setSignature] = useState('');
|
|
||||||
const [error, setError] = useState('');
|
|
||||||
|
|
||||||
return children({
|
|
||||||
certificate,
|
|
||||||
setCertificate,
|
|
||||||
signature,
|
|
||||||
setSignature,
|
|
||||||
error,
|
|
||||||
setError
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
CryptoPro.CertList = CertList;
|
|
||||||
CryptoPro.Sign = Sign;
|
|
||||||
CryptoPro.CertInfo = CertInfo;
|
|
||||||
CryptoPro.SystemInfo = SystemInfo;
|
|
||||||
|
|
||||||
export default CryptoPro;
|
|
25
examples/react/src/components/Hash.js
Normal file
25
examples/react/src/components/Hash.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function Hash({hash, hashStatus, hashError}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<legend>Результат</legend>
|
||||||
|
|
||||||
|
<label htmlFor="hash">Хэш (ГОСТ Р 34.11-2012 256 бит):</label>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
id="hash"
|
||||||
|
cols="80"
|
||||||
|
rows="5"
|
||||||
|
value={hash}
|
||||||
|
placeholder={hashStatus}
|
||||||
|
readOnly/>
|
||||||
|
|
||||||
|
<pre>{hashError || null}</pre>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Hash;
|
35
examples/react/src/components/Message.js
Normal file
35
examples/react/src/components/Message.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
function Message({onChange}) {
|
||||||
|
const [message, setMessage] = useState('Привет мир!');
|
||||||
|
|
||||||
|
function onMessageChange(event) {
|
||||||
|
setMessage(event.target.value);
|
||||||
|
onChange(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => onChange(message));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<legend>Создание подписи</legend>
|
||||||
|
|
||||||
|
<label htmlFor="message">Подписываемое сообщение: *</label>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
id="message"
|
||||||
|
name="message"
|
||||||
|
cols="80"
|
||||||
|
rows="5"
|
||||||
|
placeholder="Введите сообщение"
|
||||||
|
value={message}
|
||||||
|
onChange={onMessageChange}
|
||||||
|
autoFocus
|
||||||
|
required/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Message;
|
23
examples/react/src/components/Signature.js
Normal file
23
examples/react/src/components/Signature.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function Signature({signature, signatureStatus, signatureError}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label htmlFor="signature">Подпись (PKCS7):</label>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
id="signature"
|
||||||
|
cols="80"
|
||||||
|
rows="30"
|
||||||
|
value={signature}
|
||||||
|
placeholder={signatureStatus}
|
||||||
|
readOnly/>
|
||||||
|
|
||||||
|
<pre>{signatureError || null}</pre>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Signature;
|
36
examples/react/src/components/SignatureType.js
Normal file
36
examples/react/src/components/SignatureType.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
function SignatureType({onChange}) {
|
||||||
|
const [type, setType] = useState(true);
|
||||||
|
|
||||||
|
function onTypeToggle() {
|
||||||
|
setType(!type);
|
||||||
|
onChange(!type);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => onChange(type));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label>Тип подписи: *</label>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
checked={!type}
|
||||||
|
onChange={onTypeToggle}/>Совмещенная</label>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
checked={type}
|
||||||
|
onChange={onTypeToggle}/>Отделенная</label>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SignatureType;
|
@ -1,8 +1,9 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { getSystemInfo, isValidSystemSetup } from 'crypto-pro';
|
import { getSystemInfo, isValidSystemSetup } from 'crypto-pro';
|
||||||
|
|
||||||
function SystemInfo({onError}) {
|
function SystemInfo() {
|
||||||
const [systemInfo, setSystemInfo] = useState(null);
|
const [systemInfo, setSystemInfo] = useState(null);
|
||||||
|
const [systemInfoError, setSystemInfoError] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
@ -12,17 +13,27 @@ function SystemInfo({onError}) {
|
|||||||
isValidSystemSetup: await isValidSystemSetup()
|
isValidSystemSetup: await isValidSystemSetup()
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
onError(error.message);
|
setSystemInfoError(error.message);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [onError]);
|
});
|
||||||
|
|
||||||
if (!systemInfo) {
|
if (!systemInfo) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<pre>{JSON.stringify(systemInfo, null, ' ')}</pre>
|
<>
|
||||||
|
<legend>Информация о системе</legend>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{systemInfo ? (
|
||||||
|
JSON.stringify(systemInfo, null, ' ')
|
||||||
|
) : (
|
||||||
|
systemInfoError || null
|
||||||
|
)}
|
||||||
|
</pre>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
||||||
sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
|
||||||
monospace;
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ import 'react-app-polyfill/stable';
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import './index.css';
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import * as serviceWorker from './serviceWorker';
|
import * as serviceWorker from './serviceWorker';
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user