feat: test connection before hand

This commit is contained in:
AP 2019-12-14 13:43:33 +01:00
parent e91d5f156f
commit 37ec4a78d3
6 changed files with 275 additions and 166 deletions

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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>
)}
</>
);
};

View file

@ -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>

View file

@ -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) {