mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-22 14:22:33 +01:00
feat: test connection before hand
This commit is contained in:
parent
e91d5f156f
commit
37ec4a78d3
6 changed files with 275 additions and 166 deletions
|
@ -1,13 +1,16 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Input, SingleLine, Sub4Title } from '../../components/generic/Styled';
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
import { getConfigLnd, saveUserAuth } from '../../utils/auth';
|
||||
import { getConfigLnd, saveUserAuth, getAuthString } from '../../utils/auth';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import { LoginButton, PasswordInput } from './Password';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useLazyQuery } from '@apollo/react-hooks';
|
||||
import { GET_CAN_CONNECT } from '../../graphql/query';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
|
||||
interface AuthProps {
|
||||
available?: number;
|
||||
available: number;
|
||||
callback?: () => void;
|
||||
}
|
||||
|
||||
|
@ -20,9 +23,54 @@ export const BTCLoginForm = ({ available, callback }: AuthProps) => {
|
|||
const [hasInfo, setHasInfo] = useState(false);
|
||||
const [isPass, setPass] = useState('');
|
||||
|
||||
if (!available) return null;
|
||||
const [tryToConnect, { data, loading }] = useLazyQuery(GET_CAN_CONNECT, {
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
const canConnect = isJson !== '' && !!available;
|
||||
useEffect(() => {
|
||||
if (!loading && data && data.getNodeInfo && data.getNodeInfo.alias) {
|
||||
const { cert, macaroon, readMacaroon, host } = getConfigLnd(isJson);
|
||||
|
||||
if (!host) {
|
||||
toast.error('Invalid connection credentials');
|
||||
return;
|
||||
}
|
||||
|
||||
const encryptedAdmin =
|
||||
macaroon && isPass !== ''
|
||||
? CryptoJS.AES.encrypt(macaroon, isPass).toString()
|
||||
: undefined;
|
||||
|
||||
saveUserAuth({
|
||||
available,
|
||||
name: isName,
|
||||
host,
|
||||
admin: encryptedAdmin,
|
||||
read: readMacaroon,
|
||||
cert,
|
||||
});
|
||||
|
||||
setAccount({
|
||||
loggedIn: true,
|
||||
host,
|
||||
admin: macaroon,
|
||||
read: readMacaroon,
|
||||
cert,
|
||||
});
|
||||
|
||||
toast.success('Connected!');
|
||||
callback && callback();
|
||||
}
|
||||
}, [
|
||||
data,
|
||||
loading,
|
||||
available,
|
||||
callback,
|
||||
isJson,
|
||||
isName,
|
||||
isPass,
|
||||
setAccount,
|
||||
]);
|
||||
|
||||
const handleClick = () => {
|
||||
try {
|
||||
|
@ -34,66 +82,50 @@ export const BTCLoginForm = ({ available, callback }: AuthProps) => {
|
|||
};
|
||||
|
||||
const handleConnect = () => {
|
||||
const { cert, macaroon, readMacaroon, host } = getConfigLnd(isJson);
|
||||
|
||||
const encryptedAdmin =
|
||||
macaroon && isPass !== ''
|
||||
? CryptoJS.AES.encrypt(macaroon, isPass).toString()
|
||||
: undefined;
|
||||
const { cert, readMacaroon, host } = getConfigLnd(isJson);
|
||||
|
||||
if (!host) {
|
||||
toast.error('Invalid connection credentials');
|
||||
return;
|
||||
}
|
||||
|
||||
saveUserAuth({
|
||||
available,
|
||||
name: isName,
|
||||
host,
|
||||
admin: encryptedAdmin,
|
||||
read: readMacaroon,
|
||||
cert,
|
||||
tryToConnect({
|
||||
variables: { auth: getAuthString(host, readMacaroon, cert) },
|
||||
});
|
||||
|
||||
setAccount({
|
||||
loggedIn: true,
|
||||
host,
|
||||
admin: macaroon,
|
||||
read: readMacaroon,
|
||||
cert,
|
||||
});
|
||||
|
||||
callback && callback();
|
||||
};
|
||||
|
||||
const renderContent = () => (
|
||||
<>
|
||||
<SingleLine>
|
||||
<Sub4Title>Name:</Sub4Title>
|
||||
<Input onChange={e => setName(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>BTCPay Connect Url:</Sub4Title>
|
||||
<Input onChange={e => setJson(e.target.value)} />
|
||||
</SingleLine>
|
||||
{canConnect && (
|
||||
<LoginButton
|
||||
disabled={!canConnect}
|
||||
enabled={canConnect}
|
||||
onClick={handleClick}
|
||||
color={'yellow'}
|
||||
>
|
||||
Connect
|
||||
</LoginButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
const renderContent = () => {
|
||||
const canConnect = isJson !== '' && !!available;
|
||||
return (
|
||||
<>
|
||||
<SingleLine>
|
||||
<Sub4Title>Name:</Sub4Title>
|
||||
<Input onChange={e => setName(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>BTCPay Connect Url:</Sub4Title>
|
||||
<Input onChange={e => setJson(e.target.value)} />
|
||||
</SingleLine>
|
||||
{canConnect && (
|
||||
<LoginButton
|
||||
disabled={!canConnect}
|
||||
enabled={canConnect}
|
||||
onClick={handleClick}
|
||||
color={'yellow'}
|
||||
>
|
||||
Connect
|
||||
</LoginButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return hasInfo ? (
|
||||
<PasswordInput
|
||||
isPass={isPass}
|
||||
setPass={setPass}
|
||||
callback={handleConnect}
|
||||
loading={loading}
|
||||
/>
|
||||
) : (
|
||||
renderContent()
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Input, SingleLine, Sub4Title } from '../../components/generic/Styled';
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getBase64CertfromDerFormat,
|
||||
saveUserAuth,
|
||||
getAuthString,
|
||||
} from '../../utils/auth';
|
||||
import { LoginButton, PasswordInput } from './Password';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import { useLazyQuery } from '@apollo/react-hooks';
|
||||
import { GET_CAN_CONNECT } from '../../graphql/query';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
|
||||
interface AuthProps {
|
||||
available?: number;
|
||||
available: number;
|
||||
callback?: () => void;
|
||||
}
|
||||
|
||||
|
@ -23,71 +28,86 @@ export const ConnectLoginForm = ({ available, callback }: AuthProps) => {
|
|||
const [hasInfo, setHasInfo] = useState(false);
|
||||
const [isPass, setPass] = useState('');
|
||||
|
||||
if (!available) return null;
|
||||
const [tryToConnect, { data, loading }] = useLazyQuery(GET_CAN_CONNECT, {
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
const canConnect = isUrl !== '' && !!available;
|
||||
useEffect(() => {
|
||||
if (!loading && data && data.getNodeInfo && data.getNodeInfo.alias) {
|
||||
const { cert, macaroon, socket } = getAuthLnd(isUrl);
|
||||
|
||||
const base64Cert = getBase64CertfromDerFormat(cert) || '';
|
||||
|
||||
const encryptedAdmin = CryptoJS.AES.encrypt(
|
||||
macaroon,
|
||||
isPass,
|
||||
).toString();
|
||||
|
||||
saveUserAuth({
|
||||
available,
|
||||
name: isName,
|
||||
host: socket,
|
||||
admin: encryptedAdmin,
|
||||
cert: base64Cert,
|
||||
});
|
||||
|
||||
sessionStorage.setItem('session', macaroon);
|
||||
|
||||
setAccount({
|
||||
loggedIn: true,
|
||||
host: socket,
|
||||
admin: encryptedAdmin,
|
||||
read: macaroon,
|
||||
cert: base64Cert,
|
||||
});
|
||||
|
||||
toast.success('Connected!');
|
||||
callback && callback();
|
||||
}
|
||||
}, [data, loading, available, callback, isName, isPass, isUrl, setAccount]);
|
||||
|
||||
const handleConnect = () => {
|
||||
const { cert, macaroon, socket } = getAuthLnd(isUrl);
|
||||
|
||||
const base64Cert = getBase64CertfromDerFormat(cert) || '';
|
||||
|
||||
const encryptedAdmin = CryptoJS.AES.encrypt(
|
||||
macaroon,
|
||||
isPass,
|
||||
).toString();
|
||||
|
||||
console.log(encryptedAdmin);
|
||||
|
||||
saveUserAuth({
|
||||
available,
|
||||
name: isName,
|
||||
host: socket,
|
||||
admin: encryptedAdmin,
|
||||
cert: base64Cert,
|
||||
tryToConnect({
|
||||
variables: { auth: getAuthString(socket, macaroon, base64Cert) },
|
||||
});
|
||||
|
||||
sessionStorage.setItem('session', macaroon);
|
||||
|
||||
setAccount({
|
||||
loggedIn: true,
|
||||
host: socket,
|
||||
admin: encryptedAdmin,
|
||||
read: macaroon,
|
||||
cert: base64Cert,
|
||||
});
|
||||
|
||||
callback && callback();
|
||||
};
|
||||
|
||||
const renderContent = () => (
|
||||
<>
|
||||
<SingleLine>
|
||||
<Sub4Title>Name:</Sub4Title>
|
||||
<Input onChange={e => setName(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>LN Connect Url:</Sub4Title>
|
||||
<Input onChange={e => setUrl(e.target.value)} />
|
||||
</SingleLine>
|
||||
{canConnect && (
|
||||
<LoginButton
|
||||
disabled={!canConnect}
|
||||
enabled={canConnect}
|
||||
onClick={() => setHasInfo(true)}
|
||||
color={'yellow'}
|
||||
>
|
||||
Connect
|
||||
</LoginButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
const renderContent = () => {
|
||||
const canConnect = isUrl !== '' && !!available;
|
||||
return (
|
||||
<>
|
||||
<SingleLine>
|
||||
<Sub4Title>Name:</Sub4Title>
|
||||
<Input onChange={e => setName(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>LN Connect Url:</Sub4Title>
|
||||
<Input onChange={e => setUrl(e.target.value)} />
|
||||
</SingleLine>
|
||||
{canConnect && (
|
||||
<LoginButton
|
||||
disabled={!canConnect}
|
||||
enabled={canConnect}
|
||||
onClick={() => setHasInfo(true)}
|
||||
color={'yellow'}
|
||||
>
|
||||
Connect
|
||||
</LoginButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return hasInfo ? (
|
||||
<PasswordInput
|
||||
isPass={isPass}
|
||||
setPass={setPass}
|
||||
callback={handleConnect}
|
||||
loading={loading}
|
||||
/>
|
||||
) : (
|
||||
renderContent()
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Input, SingleLine, Sub4Title } from '../generic/Styled';
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
import { saveUserAuth } from '../../utils/auth';
|
||||
import { saveUserAuth, getAuthString } from '../../utils/auth';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import base64url from 'base64url';
|
||||
import { PasswordInput, LoginButton } from './Password';
|
||||
import { useLazyQuery } from '@apollo/react-hooks';
|
||||
import { GET_CAN_CONNECT } from '../../graphql/query';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
interface AuthProps {
|
||||
available?: number;
|
||||
available: number;
|
||||
callback?: () => void;
|
||||
}
|
||||
|
||||
|
@ -23,10 +27,9 @@ export const LoginForm = ({ available, callback }: AuthProps) => {
|
|||
const [hasInfo, setHasInfo] = useState(false);
|
||||
const [isPass, setPass] = useState('');
|
||||
|
||||
if (!available) return null;
|
||||
|
||||
const canConnect =
|
||||
isName !== '' && isHost !== '' && isRead !== '' && !!available;
|
||||
const [tryToConnect, { data, loading }] = useLazyQuery(GET_CAN_CONNECT, {
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
const handleClick = () => {
|
||||
if (isAdmin !== '') {
|
||||
|
@ -36,76 +39,110 @@ export const LoginForm = ({ available, callback }: AuthProps) => {
|
|||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && data && data.getNodeInfo && data.getNodeInfo.alias) {
|
||||
const admin = base64url.fromBase64(isAdmin);
|
||||
const read = base64url.fromBase64(isRead);
|
||||
const cert = base64url.fromBase64(isCert);
|
||||
|
||||
const encryptedAdmin =
|
||||
admin && isPass !== ''
|
||||
? CryptoJS.AES.encrypt(admin, isPass).toString()
|
||||
: undefined;
|
||||
|
||||
saveUserAuth({
|
||||
available,
|
||||
name: isName,
|
||||
host: isHost,
|
||||
admin: encryptedAdmin,
|
||||
read,
|
||||
cert,
|
||||
});
|
||||
|
||||
setAccount({
|
||||
loggedIn: true,
|
||||
host: isHost,
|
||||
admin: encryptedAdmin,
|
||||
read,
|
||||
cert,
|
||||
});
|
||||
|
||||
toast.success('Connected!');
|
||||
callback && callback();
|
||||
}
|
||||
}, [
|
||||
data,
|
||||
loading,
|
||||
available,
|
||||
callback,
|
||||
isAdmin,
|
||||
isCert,
|
||||
isHost,
|
||||
isName,
|
||||
isPass,
|
||||
isRead,
|
||||
setAccount,
|
||||
]);
|
||||
|
||||
const handleConnect = () => {
|
||||
const admin = base64url.fromBase64(isAdmin);
|
||||
const read = base64url.fromBase64(isRead);
|
||||
const cert = base64url.fromBase64(isCert);
|
||||
|
||||
const encryptedAdmin =
|
||||
admin && isPass !== ''
|
||||
? CryptoJS.AES.encrypt(admin, isPass).toString()
|
||||
: undefined;
|
||||
const correctMacaroon = read ? read : admin;
|
||||
|
||||
saveUserAuth({
|
||||
available,
|
||||
name: isName,
|
||||
host: isHost,
|
||||
admin: encryptedAdmin,
|
||||
read,
|
||||
cert,
|
||||
tryToConnect({
|
||||
variables: {
|
||||
auth: getAuthString(isHost, correctMacaroon, cert),
|
||||
},
|
||||
});
|
||||
|
||||
setAccount({
|
||||
loggedIn: true,
|
||||
host: isHost,
|
||||
admin: encryptedAdmin,
|
||||
read,
|
||||
cert,
|
||||
});
|
||||
|
||||
callback && callback();
|
||||
};
|
||||
|
||||
const renderContent = () => (
|
||||
<>
|
||||
<SingleLine>
|
||||
<Sub4Title>Name:</Sub4Title>
|
||||
<Input onChange={e => setName(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Host:</Sub4Title>
|
||||
<Input onChange={e => setHost(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Admin:</Sub4Title>
|
||||
<Input onChange={e => setAdmin(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Readonly:</Sub4Title>
|
||||
<Input onChange={e => setRead(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Certificate:</Sub4Title>
|
||||
<Input onChange={e => setCert(e.target.value)} />
|
||||
</SingleLine>
|
||||
{canConnect && (
|
||||
<LoginButton
|
||||
disabled={!canConnect}
|
||||
enabled={canConnect}
|
||||
onClick={handleClick}
|
||||
color={'yellow'}
|
||||
>
|
||||
Connect
|
||||
</LoginButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
const renderContent = () => {
|
||||
const canConnect =
|
||||
isName !== '' && isHost !== '' && isRead !== '' && !!available;
|
||||
return (
|
||||
<>
|
||||
<SingleLine>
|
||||
<Sub4Title>Name:</Sub4Title>
|
||||
<Input onChange={e => setName(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Host:</Sub4Title>
|
||||
<Input onChange={e => setHost(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Admin:</Sub4Title>
|
||||
<Input onChange={e => setAdmin(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Readonly:</Sub4Title>
|
||||
<Input onChange={e => setRead(e.target.value)} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Certificate:</Sub4Title>
|
||||
<Input onChange={e => setCert(e.target.value)} />
|
||||
</SingleLine>
|
||||
{canConnect && (
|
||||
<LoginButton
|
||||
disabled={!canConnect}
|
||||
enabled={canConnect}
|
||||
onClick={handleClick}
|
||||
color={'yellow'}
|
||||
>
|
||||
Connect
|
||||
</LoginButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return hasInfo ? (
|
||||
<PasswordInput
|
||||
isPass={isPass}
|
||||
setPass={setPass}
|
||||
callback={handleConnect}
|
||||
loading={loading}
|
||||
/>
|
||||
) : (
|
||||
renderContent()
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
import zxcvbn from 'zxcvbn';
|
||||
import styled from 'styled-components';
|
||||
import { progressBackground, textColor } from '../../styles/Themes';
|
||||
import { Loader } from '../generic/Icons';
|
||||
|
||||
const Progress = styled.div`
|
||||
width: 80%;
|
||||
|
@ -65,9 +66,15 @@ interface PasswordProps {
|
|||
isPass: string;
|
||||
setPass: (pass: string) => void;
|
||||
callback: () => void;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export const PasswordInput = ({ isPass, setPass, callback }: PasswordProps) => {
|
||||
export const PasswordInput = ({
|
||||
isPass,
|
||||
setPass,
|
||||
callback,
|
||||
loading = false,
|
||||
}: PasswordProps) => {
|
||||
const strength = (100 * Math.min(zxcvbn(isPass).guesses_log10, 40)) / 40;
|
||||
const needed = 1;
|
||||
return (
|
||||
|
@ -86,7 +93,7 @@ export const PasswordInput = ({ isPass, setPass, callback }: PasswordProps) => {
|
|||
/>
|
||||
</Progress>
|
||||
</SingleLine>
|
||||
{strength >= needed && (
|
||||
{strength >= needed && !loading && (
|
||||
<LoginButton
|
||||
disabled={strength < needed}
|
||||
enabled={strength >= needed}
|
||||
|
@ -96,6 +103,11 @@ export const PasswordInput = ({ isPass, setPass, callback }: PasswordProps) => {
|
|||
Connect
|
||||
</LoginButton>
|
||||
)}
|
||||
{loading && (
|
||||
<LoginButton disabled={true} color={'grey'}>
|
||||
<Loader />
|
||||
</LoginButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -99,7 +99,7 @@ export const ForwardChannelsReport = ({ isTime, isType, color }: Props) => {
|
|||
|
||||
const renderRoute = (parsed: {}[]) => {
|
||||
const routes = parsed.map((channel: any, index: number) => (
|
||||
<ChannelRow>
|
||||
<ChannelRow key={index}>
|
||||
<TableLine>{channel.aliasIn}</TableLine>
|
||||
<TableLine>{channel.aliasOut}</TableLine>
|
||||
<LastTableLine>
|
||||
|
|
|
@ -15,6 +15,14 @@ export const GET_NETWORK_INFO = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
export const GET_CAN_CONNECT = gql`
|
||||
query GetNodeInfo($auth: String!) {
|
||||
getNodeInfo(auth: $auth) {
|
||||
alias
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_NODE_INFO = gql`
|
||||
query GetNodeInfo($auth: String!) {
|
||||
getNodeInfo(auth: $auth) {
|
||||
|
|
Loading…
Add table
Reference in a new issue