chore: πŸ”§ enable strict tsconfig (#96)

* chore: πŸ”§ strict enable wip

* Improve typings, add type dependencies (#97)

We improve the TypeScript situation by enabling `noImplicitAny`. This
leads to a bunch of errors, as expected. We fix some of these by
installing the correct types, and some are fixed manually by an educated
guess. There are still a lot left, and these seem to be a big mix of
lacking types for the `ln-service` dependency and potential bugs.

* Strict null (#100)

* chore: πŸ”§ enable strict-null

* chore: πŸ”§ more checks

* chore: πŸ”§ some fixes

* chore: πŸ”§ more types

* chore: πŸ”§ more types

* chore: πŸ”§ more types

* chore: πŸ”§ more types

* chore: πŸ”§ more types

* chore: πŸ”§ more types

* chore: πŸ”§ more types

* chore: πŸ”§ enable strict

* fix: πŸ› input

* fix: πŸ› forward report

* fix: πŸ› chat sending

* fix: πŸ› chat bubble input

Co-authored-by: Torkel Rogstad <torkel@rogstad.io>
This commit is contained in:
Anthony Potdevin 2020-07-21 23:31:12 +02:00 committed by Anthony Potdevin
parent 34d4586a41
commit bc4415cde7
No known key found for this signature in database
GPG Key ID: 4403F1DFBE779457
166 changed files with 1904 additions and 1361 deletions

17
@types/index.d.ts vendored
View File

@ -3,3 +3,20 @@ declare module '*.jpg';
declare module '*.jpeg';
declare module '*.svg';
declare module '*.gif';
/**
* ln-service does not have proper types. This is slightly
* problematic, as this leads to types being `any` **a ton**
* of places.
*
* Here's an issue tracking this: https://github.com/alexbosworth/ln-service/issues/112
*
* It seems like the library is generated from Proto files.
* Could it be an idea to try and generate TypeScript declarations
* from those files?
*/
declare module 'ln-service';
declare module '@alexbosworth/request';
declare module 'balanceofsatoshis/request';
declare module 'balanceofsatoshis/swaps';
declare module 'balanceofsatoshis/balances';

View File

@ -6,22 +6,26 @@ import { ApolloClient } from 'apollo-client';
import {
InMemoryCache,
IntrospectionFragmentMatcher,
NormalizedCacheObject,
} from 'apollo-cache-inmemory';
import getConfig from 'next/config';
import introspectionQueryResultData from 'src/graphql/fragmentTypes.json';
import { SchemaLink } from 'apollo-link-schema';
import { NextPage } from 'next';
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
let globalApolloClient = null;
let globalApolloClient: ReturnType<typeof createApolloClient> | null = null;
const { publicRuntimeConfig } = getConfig();
const { apiUrl: uri } = publicRuntimeConfig;
function createIsomorphLink(ctx) {
type Context = SchemaLink.ResolverContextFunction | Record<string, any>;
function createIsomorphLink(ctx: Context) {
if (typeof window === 'undefined') {
const { SchemaLink } = require('apollo-link-schema');
const schema = require('server/schema');
return new SchemaLink({ schema, context: ctx });
} else {
@ -36,9 +40,11 @@ function createIsomorphLink(ctx) {
/**
* Creates and configures the ApolloClient
* @param {Object} [initialState={}]
*/
function createApolloClient(ctx = {}, initialState = {}) {
function createApolloClient(
ctx: Context = {},
initialState: NormalizedCacheObject = {}
) {
const ssrMode = typeof window === 'undefined';
const cache = new InMemoryCache({ fragmentMatcher }).restore(initialState);
@ -53,9 +59,8 @@ function createApolloClient(ctx = {}, initialState = {}) {
/**
* Always creates a new apollo client on the server
* Creates or reuses apollo client in the browser.
* @param {Object} initialState
*/
function initApolloClient(ctx, initialState?) {
function initApolloClient(ctx?: Context, initialState?: NormalizedCacheObject) {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (typeof window === 'undefined') {
@ -70,16 +75,29 @@ function initApolloClient(ctx, initialState?) {
return globalApolloClient;
}
interface WithApolloProps {
apolloClient?: ApolloClient<NormalizedCacheObject>;
apolloState?: NormalizedCacheObject;
}
interface WithApolloOptions {
ssr?: boolean;
}
/**
* Creates and provides the apolloContext
* to a next.js PageTree. Use it by wrapping
* your PageComponent via HOC pattern.
* @param {Function|Class} PageComponent
* @param {Object} [config]
* @param {Boolean} [config.ssr=true]
*/
export function withApollo(PageComponent, { ssr = true } = {}) {
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
export function withApollo(
PageComponent: NextPage,
{ ssr }: WithApolloOptions = { ssr: true }
) {
const WithApollo: NextPage<WithApolloProps> = ({
apolloClient,
apolloState,
...pageProps
}) => {
const client = apolloClient || initApolloClient(undefined, apolloState);
return (
<ApolloProvider client={client}>
@ -101,15 +119,16 @@ export function withApollo(PageComponent, { ssr = true } = {}) {
}
if (ssr || PageComponent.getInitialProps) {
WithApollo.getInitialProps = async ctx => {
WithApollo.getInitialProps = async (ctx): Promise<WithApolloProps> => {
const { AppTree } = ctx;
// Initialize ApolloClient, add it to the ctx object so
// we can use it in `PageComponent.getInitialProp`.
const apolloClient = (ctx.apolloClient = initApolloClient({
const apolloClient = initApolloClient({
res: ctx.res,
req: ctx.req,
}));
});
(ctx as any).apolloClient = apolloClient;
// Run wrapped getInitialProps methods
let pageProps = {};
@ -122,7 +141,7 @@ export function withApollo(PageComponent, { ssr = true } = {}) {
// When redirecting, the response is finished.
// No point in continuing to render
if (ctx.res && ctx.res.finished) {
return pageProps;
return pageProps as WithApolloProps;
}
// Only if ssr is enabled

133
package-lock.json generated
View File

@ -4766,6 +4766,12 @@
"@babel/types": "^7.3.0"
}
},
"@types/bcryptjs": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz",
"integrity": "sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==",
"dev": true
},
"@types/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
@ -4802,6 +4808,12 @@
"resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-P1bffQfhD3O4LW0ioENXUhZ9OIa0Zn+P7M+pWgkCKaT53wVLSq0mrKksCID/FGHpFhRSxRGhgrQmfhRuzwtKdg=="
},
"@types/cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-y7mImlc/rNkvCRmg8gC3/lj87S7pTUIJ6QGjwHR9WQJcFs+ZMTOaoPrkdFA/YdbuqVEmEbb5RdhVxMkAcgOnpg==",
"dev": true
},
"@types/cookies": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.4.tgz",
@ -4822,6 +4834,12 @@
"@types/express": "*"
}
},
"@types/crypto-js": {
"version": "3.1.47",
"resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-3.1.47.tgz",
"integrity": "sha512-eI6gvpcGHLk3dAuHYnRCAjX+41gMv1nz/VP55wAe5HtmAKDOoPSfr3f6vkMc08ov1S0NsjvUBxDtHHxqQY1LGA==",
"dev": true
},
"@types/eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@ -4866,6 +4884,15 @@
"@types/node": "*"
}
},
"@types/graphql-iso-date": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@types/graphql-iso-date/-/graphql-iso-date-3.4.0.tgz",
"integrity": "sha512-V3jITHTsoI2E8TGt9+/HPDz6LWt3z9/HYnPJYWI6WwiLRexsngg7KzaQlCgQkA4jkEbGPROUD0hJFc9F02W9WA==",
"dev": true,
"requires": {
"graphql": "^15.1.0"
}
},
"@types/graphql-upload": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/@types/graphql-upload/-/graphql-upload-8.0.3.tgz",
@ -4937,6 +4964,18 @@
"pretty-format": "^25.2.1"
}
},
"@types/js-cookie": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.6.tgz",
"integrity": "sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw==",
"dev": true
},
"@types/js-yaml": {
"version": "3.12.5",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.5.tgz",
"integrity": "sha512-JCcp6J0GV66Y4ZMDAQCXot4xprYB+Zfd3meK9+INSJeVZwJmHAW30BBEEkPzXswMXuiyReUGOP3GxrADc9wPww==",
"dev": true
},
"@types/json-schema": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
@ -4985,6 +5024,48 @@
"@types/koa": "*"
}
},
"@types/lodash": {
"version": "4.14.157",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.157.tgz",
"integrity": "sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ==",
"dev": true
},
"@types/lodash.groupby": {
"version": "4.6.6",
"resolved": "https://registry.npmjs.org/@types/lodash.groupby/-/lodash.groupby-4.6.6.tgz",
"integrity": "sha512-kwg3T7Ia63KtDNoQQR8hKrLHCAgrH4I44l5uEMuA6JCbj7DiSccaV4tNV1vbjtAOpX990SolVthJCmBVtRVRgw==",
"dev": true,
"requires": {
"@types/lodash": "*"
}
},
"@types/lodash.merge": {
"version": "4.6.6",
"resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.6.tgz",
"integrity": "sha512-IB90krzMf7YpfgP3u/EvZEdXVvm4e3gJbUvh5ieuI+o+XqiNEt6fCzqNRaiLlPVScLI59RxIGZMQ3+Ko/DJ8vQ==",
"dev": true,
"requires": {
"@types/lodash": "*"
}
},
"@types/lodash.omit": {
"version": "4.5.6",
"resolved": "https://registry.npmjs.org/@types/lodash.omit/-/lodash.omit-4.5.6.tgz",
"integrity": "sha512-KXPpOSNX2h0DAG2w7ajpk7TXvWF28ZHs5nJhOJyP0BQHkehgr948RVsToItMme6oi0XJkp19CbuNXkIX8FiBlQ==",
"dev": true,
"requires": {
"@types/lodash": "*"
}
},
"@types/lodash.sortby": {
"version": "4.7.6",
"resolved": "https://registry.npmjs.org/@types/lodash.sortby/-/lodash.sortby-4.7.6.tgz",
"integrity": "sha512-EnvAOmKvEg7gdYpYrS6+fVFPw5dL9rBnJi3vcKI7wqWQcLJVF/KRXK9dH29HjGNVvFUj0s9prRP3J8jEGnGKDw==",
"dev": true,
"requires": {
"@types/lodash": "*"
}
},
"@types/long": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
@ -5021,6 +5102,12 @@
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
"@types/numeral": {
"version": "0.0.28",
"resolved": "https://registry.npmjs.org/@types/numeral/-/numeral-0.0.28.tgz",
"integrity": "sha512-Sjsy10w6XFHDktJJdXzBJmoondAKW+LcGpRFH+9+zXEDj0cOH8BxJuZA9vUDSMAzU1YRJlsPKmZEEiTYDlICLw==",
"dev": true
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -5043,6 +5130,15 @@
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug=="
},
"@types/qrcode.react": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/qrcode.react/-/qrcode.react-1.0.1.tgz",
"integrity": "sha512-PcVCjpsiT2KFKfJibOgTQtkt0QQT/6GbQUp1Np/hMPhwUzMJ2DRUkR9j7tXN9Q8X06qukw+RbaJ8lJ22SBod+Q==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/qs": {
"version": "6.9.3",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.3.tgz",
@ -5063,6 +5159,15 @@
"csstype": "^2.2.0"
}
},
"@types/react-copy-to-clipboard": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-4.3.0.tgz",
"integrity": "sha512-iideNPRyroENqsOFh1i2Dv3zkviYS9r/9qD9Uh3Z9NNoAAqqa2x53i7iGndGNnJFIo20wIu7Hgh77tx1io8bgw==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/react-native": {
"version": "0.62.18",
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.62.18.tgz",
@ -5115,6 +5220,16 @@
"csstype": "^2.2.0"
}
},
"@types/styled-react-modal": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@types/styled-react-modal/-/styled-react-modal-1.2.0.tgz",
"integrity": "sha512-g+mtpYcekjAgnyKRN+xQ4bo5GQmiNdoXZ+XGb374uIgxSXx3+Wnb643o49MprYYcTTupzHf8scK3SDTzcf42Gw==",
"dev": true,
"requires": {
"@types/react": "*",
"@types/styled-components": "*"
}
},
"@types/styled-theming": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@types/styled-theming/-/styled-theming-2.2.4.tgz",
@ -5134,6 +5249,18 @@
"@types/jest": "*"
}
},
"@types/underscore": {
"version": "1.10.9",
"resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.9.tgz",
"integrity": "sha512-mKzbGzZ02nXKSV2VJdY92KNzwV7x4Zq626/4qR+7nJEceyKGk5gJXCi0mQqp3tEUa3GTc8uiuxqm4QyIaP0I1Q==",
"dev": true
},
"@types/uuid": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.0.0.tgz",
"integrity": "sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==",
"dev": true
},
"@types/websocket": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.0.tgz",
@ -5176,6 +5303,12 @@
"resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz",
"integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg=="
},
"@types/zxcvbn": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@types/zxcvbn/-/zxcvbn-4.4.0.tgz",
"integrity": "sha512-GQLOT+SN20a+AI51y3fAimhyTF4Y0RG+YP3gf91OibIZ7CJmPFgoZi+ZR5a+vRbS01LbQosITWum4ATmJ1Z6Pg==",
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.0.tgz",

View File

@ -102,11 +102,28 @@
"@next/bundle-analyzer": "^9.4.4",
"@testing-library/jest-dom": "^5.11.0",
"@testing-library/react": "^10.4.5",
"@types/bcryptjs": "^2.4.2",
"@types/cookie": "^0.4.0",
"@types/crypto-js": "^3.1.47",
"@types/graphql-iso-date": "^3.4.0",
"@types/js-cookie": "^2.2.6",
"@types/js-yaml": "^3.12.5",
"@types/jsonwebtoken": "^8.5.0",
"@types/lodash.groupby": "^4.6.6",
"@types/lodash.merge": "^4.6.6",
"@types/lodash.omit": "^4.5.6",
"@types/lodash.sortby": "^4.7.6",
"@types/node": "^14.0.22",
"@types/numeral": "0.0.28",
"@types/qrcode.react": "^1.0.1",
"@types/react": "^16.9.43",
"@types/react-copy-to-clipboard": "^4.3.0",
"@types/styled-components": "^5.1.1",
"@types/styled-react-modal": "^1.2.0",
"@types/styled-theming": "^2.2.4",
"@types/underscore": "^1.10.9",
"@types/uuid": "^8.0.0",
"@types/zxcvbn": "^4.4.0",
"@typescript-eslint/eslint-plugin": "^3.6.0",
"@typescript-eslint/parser": "^3.6.0",
"apollo-server": "^2.15.1",

View File

@ -4,6 +4,8 @@ import { ModalProvider, BaseModalBackground } from 'styled-react-modal';
import { useRouter } from 'next/router';
import Head from 'next/head';
import { StyledToastContainer } from 'src/components/toastContainer/ToastContainer';
import { NextPage } from 'next';
import { AppProps } from 'next/app';
import { ContextProvider } from '../src/context/ContextProvider';
import { useConfigState, ConfigProvider } from '../src/context/ConfigContext';
import { GlobalStyles } from '../src/styles/GlobalStyle';
@ -36,7 +38,14 @@ const Wrapper: React.FC = ({ children }) => {
);
};
const App = ({ Component, pageProps, initialConfig }) => (
type InitialProps = { initialConfig: string };
type MyAppProps = InitialProps & AppProps;
const App: NextPage<MyAppProps, InitialProps> = ({
Component,
pageProps,
initialConfig,
}) => (
<>
<Head>
<title>ThunderHub - Lightning Node Manager</title>
@ -52,8 +61,13 @@ const App = ({ Component, pageProps, initialConfig }) => (
</>
);
App.getInitialProps = async props => {
const cookies = parseCookies(props.ctx.req);
/*
* Props should be NextPageContext but something wierd
* happens and the context object received is not this
* type.
*/
App.getInitialProps = async ({ ctx }: any) => {
const cookies = parseCookies(ctx?.req);
if (!cookies?.theme) {
return { initialConfig: 'dark' };

View File

@ -10,7 +10,7 @@ import {
readCookie,
getAccounts,
} from 'server/helpers/fileHelpers';
import { ContextType } from 'server/types/apiTypes';
import { ContextType, SSOType } from 'server/types/apiTypes';
import cookie from 'cookie';
import schema from 'server/schema';
@ -33,6 +33,16 @@ const ssoMacaroon = readMacaroons(macaroonPath);
const ssoCert = readFile(lnCertPath);
const accountConfig = getAccounts(accountConfigPath);
let ssoAccount: SSOType | null = null;
if (ssoMacaroon && lnServerUrl) {
ssoAccount = {
macaroon: ssoMacaroon,
host: lnServerUrl,
cert: ssoCert,
};
}
readCookie(cookiePath);
const apolloServer = new ApolloServer({
@ -56,12 +66,16 @@ const apolloServer = new ApolloServer({
}
}
let account = null;
let account = '';
if (AccountAuth) {
logger.silly('AccountAuth cookie found in request');
try {
const cookieAccount = jwt.verify(AccountAuth, secret);
account = cookieAccount['id'] || '';
if (typeof cookieAccount === 'object') {
account = (cookieAccount as { id?: string })['id'] ?? '';
} else {
account = cookieAccount;
}
} catch (error) {
logger.silly('Account authentication cookie failed');
}
@ -72,7 +86,7 @@ const apolloServer = new ApolloServer({
secret,
ssoVerified,
account,
sso: { macaroon: ssoMacaroon, cert: ssoCert, host: lnServerUrl || null },
sso: ssoVerified ? ssoAccount : null,
accounts: accountConfig,
res,
};

View File

@ -33,7 +33,7 @@ const ChatView = () => {
const { minorVersion } = useStatusState();
const { chats, sender, sentChats, initialized } = useChatState();
const bySender = separateBySender([...chats, ...sentChats]);
const senders = getSenders(bySender);
const senders = getSenders(bySender) || [];
const [user, setUser] = React.useState('');
const [showContacts, setShowContacts] = React.useState(false);

View File

@ -165,9 +165,9 @@ const FeesView = () => {
<CardWithTitle>
<SubTitle>Channel Details</SubTitle>
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
{data.getChannelFees.map((channel: ChannelFeeType, index: number) => (
{data.getChannelFees.map((channel, index) => (
<FeeCard
channel={channel}
channel={channel as ChannelFeeType}
index={index + 1}
setIndexOpen={setIndexOpen}
indexOpen={indexOpen}

View File

@ -4,6 +4,7 @@ import { useAccountState } from 'src/context/AccountContext';
import { useGetForwardsQuery } from 'src/graphql/queries/__generated__/getForwards.generated';
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
import { withApollo } from 'config/client';
import { ForwardType } from 'src/graphql/types';
import {
SubTitle,
Card,
@ -63,11 +64,13 @@ const ForwardsView = () => {
{renderButton('threeMonths', '3M')}
</SingleLine>
</CardTitle>
{data.getForwards.forwards.length <= 0 && renderNoForwards()}
{data?.getForwards?.forwards &&
data.getForwards.forwards.length <= 0 &&
renderNoForwards()}
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
{data.getForwards.forwards.map((forward, index: number) => (
{data?.getForwards?.forwards?.map((forward, index) => (
<ForwardCard
forward={forward}
forward={forward as ForwardType}
key={index}
index={index + 1}
setIndexOpen={setIndexOpen}

View File

@ -3,6 +3,7 @@ import { useAccountState } from 'src/context/AccountContext';
import { useGetPeersQuery } from 'src/graphql/queries/__generated__/getPeers.generated';
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
import { withApollo } from 'config/client';
import { PeerType } from 'src/graphql/types';
import {
CardWithTitle,
SubTitle,
@ -21,7 +22,7 @@ const PeersView = () => {
variables: { auth },
});
if (loading || !data || !data.getPeers) {
if (loading || !data?.getPeers) {
return <LoadingCard title={'Peers'} />;
}
@ -33,11 +34,11 @@ const PeersView = () => {
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
{data.getPeers.map((peer, index: number) => (
<PeersCard
peer={peer}
peer={peer as PeerType}
index={index + 1}
setIndexOpen={setIndexOpen}
indexOpen={indexOpen}
key={`${index}-${peer.public_key}`}
key={`${index}-${peer?.public_key}`}
/>
))}
</Card>

View File

@ -5,6 +5,7 @@ import { useGetOffersQuery } from 'src/graphql/hodlhodl/__generated__/query.gene
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
import { withApollo } from 'config/client';
import getConfig from 'next/config';
import { HodlOfferType } from 'src/graphql/types';
import {
CardWithTitle,
SubTitle,
@ -27,7 +28,7 @@ export interface QueryProps {
limit: number;
offset: number;
};
filters: {};
filters: { [key: string]: string };
sort: {
by: string;
direction: string;
@ -119,13 +120,13 @@ const TradingView = () => {
</Card>
<Card bottom={'8px'} mobileCardPadding={'0'} mobileNoBackground={true}>
{amountOfOffers <= 0 && <DarkSubTitle>No Offers Found</DarkSubTitle>}
{data.getOffers.map((offer, index: number) => (
{data.getOffers.map((offer, index) => (
<OfferCard
offer={offer}
offer={offer as HodlOfferType}
index={index + 1}
setIndexOpen={setIndexOpen}
indexOpen={indexOpen}
key={`${index}-${offer.id}`}
key={`${index}-${offer?.id}`}
/>
))}
</Card>

View File

@ -49,7 +49,10 @@ const TransactionsView = () => {
<CardWithTitle>
<SubTitle>Transactions</SubTitle>
<Card bottom={'8px'} mobileCardPadding={'0'} mobileNoBackground={true}>
{resumeList.map((entry, index: number) => {
{resumeList?.map((entry, index: number) => {
if (!entry) {
return null;
}
if (entry.__typename === 'InvoiceType') {
return (
<InvoiceCard
@ -61,15 +64,18 @@ const TransactionsView = () => {
/>
);
}
return (
<PaymentsCard
payment={entry}
key={index}
index={index + 1}
setIndexOpen={setIndexOpen}
indexOpen={indexOpen}
/>
);
if (entry.__typename === 'PaymentType') {
return (
<PaymentsCard
payment={entry}
key={index}
index={index + 1}
setIndexOpen={setIndexOpen}
indexOpen={indexOpen}
/>
);
}
return null;
})}
<ColorButton
fullWidth={true}
@ -79,14 +85,14 @@ const TransactionsView = () => {
variables: { auth, token },
updateQuery: (
prev,
{
fetchMoreResult: result,
}: { fetchMoreResult: GetResumeQuery }
) => {
if (!result) return prev;
const newToken = result.getResume.token || '';
const prevEntries = prev.getResume.resume;
const newEntries = result.getResume.resume;
{ fetchMoreResult }: { fetchMoreResult?: GetResumeQuery }
): GetResumeQuery => {
if (!fetchMoreResult?.getResume) return prev;
const newToken = fetchMoreResult.getResume.token || '';
const prevEntries = prev?.getResume
? prev.getResume.resume
: [];
const newEntries = fetchMoreResult.getResume.resume;
const allTransactions = newToken
? [...prevEntries, ...newEntries]

View File

@ -71,7 +71,7 @@ describe('getParsedAccount', () => {
return 'something else ';
});
const account = getParsedAccount(raw, 0, masterPassword, 'regtest');
expect(account.macaroon).toContain('macaroon');
expect(account?.macaroon).toContain('macaroon');
});
it('picks up other networks', () => {
@ -94,7 +94,7 @@ describe('getParsedAccount', () => {
return 'something else ';
});
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
expect(account.macaroon).toContain('macaroon');
expect(account?.macaroon).toContain('macaroon');
});
describe('macaroon handling', () => {
@ -109,7 +109,7 @@ describe('getParsedAccount', () => {
};
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
expect(account.macaroon).toBe('RAW MACAROON');
expect(account?.macaroon).toBe('RAW MACAROON');
});
it('falls back to macaroon path after that', () => {
const raw = {
@ -130,7 +130,7 @@ describe('getParsedAccount', () => {
}
});
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
expect(account.macaroon).toBe('yay');
expect(account?.macaroon).toBe('yay');
});
it('falls back to lnd dir finally', () => {
@ -152,7 +152,7 @@ describe('getParsedAccount', () => {
}
});
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
expect(account.macaroon).toBe('yay');
expect(account?.macaroon).toBe('yay');
});
});
@ -168,7 +168,7 @@ describe('getParsedAccount', () => {
};
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
expect(account.cert).toBe('RAW CERT');
expect(account?.cert).toBe('RAW CERT');
});
it('falls back to certificate path after that', () => {
@ -188,7 +188,7 @@ describe('getParsedAccount', () => {
}
});
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
expect(account.cert).toBe('yay');
expect(account?.cert).toBe('yay');
});
it('falls back to lnd dir finally', () => {
@ -207,7 +207,7 @@ describe('getParsedAccount', () => {
}
});
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
expect(account.cert).toBe('yay');
expect(account?.cert).toBe('yay');
});
});
});

View File

@ -1,7 +1,7 @@
import { getErrorMsg } from './helpers';
import { logger } from './logger';
export const to = promise => {
export const to = async <T>(promise: Promise<T>) => {
return promise
.then(data => data)
.catch(err => {
@ -10,6 +10,15 @@ export const to = promise => {
});
};
export const toWithError = promise => {
return promise.then(data => [data, undefined]).catch(err => [undefined, err]);
/*
* This is hard/impossible to type correctly. What we are describing
* here is a set of two states: either we have a result and no error,
* _or_ we have no result and an error. Unfortunately TypeScript is
* not able to infer this correctly...
* https://github.com/microsoft/TypeScript/issues/12184
*/
export const toWithError = async <T>(promise: Promise<T>) => {
return promise
.then(data => [data, undefined] as const)
.catch(err => [undefined, err] as const);
};

View File

@ -68,10 +68,15 @@ export const createCustomRecords = ({
];
};
type DecodeMessageType = {
type: string;
value: string;
};
export const decodeMessage = ({
type,
value,
}): { [key: string]: string } | {} => {
}: DecodeMessageType): { [key: string]: string } | {} => {
switch (type) {
case MESSAGE_TYPE:
return { message: bufferHexToUtf(value) };

View File

@ -6,6 +6,7 @@ import { logger } from 'server/helpers/logger';
import yaml from 'js-yaml';
import { getUUID } from 'src/utils/auth';
import bcrypt from 'bcryptjs';
import { AccountType as ContextAccountType } from 'server/types/apiTypes';
type EncodingType = 'hex' | 'utf-8';
type BitcoinNetwork = 'mainnet' | 'regtest' | 'testnet';
@ -36,7 +37,7 @@ type AccountConfigType = {
accounts: AccountType[];
};
const isValidNetwork = (network: string): network is BitcoinNetwork =>
const isValidNetwork = (network: string | null): network is BitcoinNetwork =>
network === 'mainnet' || network === 'regtest' || network === 'testnet';
export const PRE_PASS_STRING = 'thunderhub-';
@ -78,7 +79,8 @@ export const parseYaml = (filePath: string): AccountConfigType | null => {
try {
const yamlObject = yaml.safeLoad(yamlConfig);
return yamlObject;
// TODO: validate this, before returning?
return yamlObject as AccountConfigType;
} catch (err) {
logger.error(
'Something went wrong while parsing the YAML config file: \n' + err
@ -115,12 +117,15 @@ export const hashPasswords = (
const cloned = { ...config };
let hashedMasterPassword = config.masterPassword;
let hashedMasterPassword = config?.masterPassword || '';
if (hashedMasterPassword?.indexOf(PRE_PASS_STRING) < 0) {
if (
hashedMasterPassword &&
hashedMasterPassword.indexOf(PRE_PASS_STRING) < 0
) {
hasChanged = true;
hashedMasterPassword = `${PRE_PASS_STRING}${bcrypt.hashSync(
config.masterPassword,
hashedMasterPassword,
12
)}`;
}
@ -178,7 +183,7 @@ const getCertificate = ({
const getMacaroon = (
{ macaroon, macaroonPath, network, lndDir }: AccountType,
defaultNetwork: BitcoinNetwork
): string => {
): string | null => {
if (macaroon) {
return macaroon;
}
@ -203,24 +208,24 @@ const getMacaroon = (
);
};
export const getAccounts = (filePath: string) => {
export const getAccounts = (filePath: string): ContextAccountType[] => {
if (filePath === '') {
logger.verbose('No account config file path provided');
return null;
return [];
}
const accountConfig = parseYaml(filePath);
if (!accountConfig) {
logger.info(`No account config file found at path ${filePath}`);
return null;
return [];
}
return getAccountsFromYaml(accountConfig, filePath);
return getAccountsFromYaml(accountConfig, filePath) as ContextAccountType[];
};
export const getParsedAccount = (
account: AccountType,
index: number,
masterPassword: string,
masterPassword: string | null,
defaultNetwork: BitcoinNetwork
): ParsedAccount | null => {
const {
@ -276,12 +281,12 @@ export const getParsedAccount = (
const id = getUUID(`${name}${serverUrl}${macaroon}${cert}`);
return {
name,
name: name || '',
id,
host: serverUrl,
host: serverUrl || '',
macaroon,
cert,
password: password || masterPassword,
cert: cert || '',
password: password || masterPassword || '',
};
};
@ -297,7 +302,7 @@ export const getAccountsFromYaml = (
}
const { defaultNetwork, masterPassword, accounts } = hashPasswords(
hashed,
hashed || false,
config,
filePath
);
@ -314,7 +319,7 @@ export const getAccountsFromYaml = (
logger.info(
`Server accounts that will be available: ${parsedAccounts
.map(({ name }) => name)
.map(account => account?.name)
.join(', ')}`
);

View File

@ -1,6 +1,11 @@
import { getNode, getChannel } from 'ln-service';
import { logger } from 'server/helpers/logger';
import { toWithError } from 'server/helpers/async';
import {
LndObject,
GetChannelType,
GetNodeType,
} from 'server/types/ln-service.types';
const errorNode = {
alias: 'Partner node not found',
@ -10,7 +15,7 @@ const errorNode = {
export const getNodeFromChannel = async (
id: string,
publicKey: string,
lnd
lnd: LndObject
) => {
const [channelInfo, channelError] = await toWithError(
getChannel({
@ -19,15 +24,15 @@ export const getNodeFromChannel = async (
})
);
if (channelError) {
if (channelError || !channelInfo) {
logger.verbose(`Error getting channel with id ${id}: %o`, channelError);
return errorNode;
}
const partnerPublicKey =
channelInfo.policies[0].public_key !== publicKey
? channelInfo.policies[0].public_key
: channelInfo.policies[1].public_key;
(channelInfo as GetChannelType).policies[0].public_key !== publicKey
? (channelInfo as GetChannelType).policies[0].public_key
: (channelInfo as GetChannelType).policies[1].public_key;
const [nodeInfo, nodeError] = await toWithError(
getNode({
@ -37,7 +42,7 @@ export const getNodeFromChannel = async (
})
);
if (nodeError) {
if (nodeError || !nodeInfo) {
logger.verbose(
`Error getting node with public key ${partnerPublicKey}: %o`,
nodeError
@ -46,7 +51,7 @@ export const getNodeFromChannel = async (
}
return {
alias: nodeInfo.alias,
color: nodeInfo.color,
alias: (nodeInfo as GetNodeType).alias,
color: (nodeInfo as GetNodeType).color,
};
};

View File

@ -14,7 +14,7 @@ const { nodeEnv } = serverRuntimeConfig || {};
const { noClient } = publicRuntimeConfig || {};
type LndAuthType = {
cert: string;
cert: string | null;
macaroon: string;
host: string;
};
@ -37,9 +37,9 @@ export const getCorrectAuth = (
): LndAuthType => {
if (auth.type === 'test' && nodeEnv !== 'production') {
return {
host: process.env.TEST_HOST,
macaroon: process.env.TEST_MACAROON,
cert: process.env.TEST_CERT,
host: process.env.TEST_HOST || '',
macaroon: process.env.TEST_MACAROON || '',
cert: process.env.TEST_CERT || '',
};
}
if (auth.type === SERVER_ACCOUNT) {
@ -65,7 +65,7 @@ export const getCorrectAuth = (
return verifiedAccount;
}
if (auth.type === SSO_ACCOUNT) {
if (!context.ssoVerified) {
if (!context.ssoVerified || !context.sso) {
logger.debug('SSO Account is not verified');
throw new Error('AccountNotAuthenticated');
}

View File

@ -13,9 +13,9 @@ export const accountResolvers = {
const { ip, accounts, account, sso, ssoVerified } = context;
await requestLimiter(ip, 'getServerAccounts');
const { macaroon, cert, host } = sso;
let ssoAccount = null;
if (macaroon && host && ssoVerified) {
if (ssoVerified && sso) {
const { cert, host } = sso;
logger.debug(
`Macaroon${
cert ? ', certificate' : ''

View File

@ -21,6 +21,11 @@ export const authResolvers = {
const { ip, secret, sso, res } = context;
await requestLimiter(ip, 'getAuthToken');
if (!sso) {
logger.warn('No SSO account available');
return null;
}
if (!sso.host || !sso.macaroon) {
logger.warn('Host and macaroon are required for SSO');
return null;
@ -38,7 +43,7 @@ export const authResolvers = {
const cookieFile = readCookie(cookiePath);
if (
cookieFile.trim() === params.cookie.trim() ||
(cookieFile && cookieFile.trim() === params.cookie.trim()) ||
nodeEnv === 'development'
) {
refreshCookie(cookiePath);

View File

@ -7,6 +7,7 @@ import { AuthType } from 'src/context/AccountContext';
import { rebalance } from 'balanceofsatoshis/swaps';
import { getAccountingReport } from 'balanceofsatoshis/balances';
import request from '@alexbosworth/request';
import { RebalanceResponseType } from 'server/types/balanceofsatoshis.types';
type RebalanceType = {
auth: AuthType;
@ -80,9 +81,9 @@ export const bosResolvers = {
out_channels,
...(in_through && { in_through }),
...(is_avoiding_high_inbound && { is_avoiding_high_inbound }),
...(max_fee > 0 && { max_fee }),
...(max_fee_rate > 0 && { max_fee_rate }),
...(max_rebalance > 0 && { max_rebalance }),
...(max_fee && max_fee > 0 && { max_fee }),
...(max_fee_rate && max_fee_rate > 0 && { max_fee_rate }),
...(max_rebalance && max_rebalance > 0 && { max_rebalance }),
...(node && { node }),
...(out_through && { out_through }),
...(target && { target }),
@ -90,7 +91,7 @@ export const bosResolvers = {
logger.info('Rebalance Params: %o', filteredParams);
const response = await to(
const response = await to<RebalanceResponseType>(
rebalance({
lnd,
logger,

View File

@ -1,6 +1,6 @@
import {
getChainBalance as getBalance,
getPendingChainBalance as getPending,
getChainBalance,
getPendingChainBalance,
getChainTransactions,
getUtxos,
sendToChainAddress,
@ -15,6 +15,14 @@ import {
getCorrectAuth,
} from 'server/helpers/helpers';
import { sortBy } from 'underscore';
import { to } from 'server/helpers/async';
import {
GetChainBalanceType,
GetPendingChainBalanceType,
GetChainTransactionsType,
GetUtxosType,
SendToChainAddressType,
} from 'server/types/ln-service.types';
interface ChainBalanceProps {
chain_balance: number;
@ -36,15 +44,12 @@ export const chainResolvers = {
const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
try {
const value: ChainBalanceProps = await getBalance({
const value: ChainBalanceProps = await to<GetChainBalanceType>(
getChainBalance({
lnd,
});
return value.chain_balance;
} catch (error) {
logger.error('Error getting chain balance: %o', error);
throw new Error(getErrorMsg(error));
}
})
);
return value.chain_balance;
},
getPendingChainBalance: async (
_: undefined,
@ -56,15 +61,14 @@ export const chainResolvers = {
const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
try {
const pendingValue: PendingChainBalanceProps = await getPending({
const pendingValue: PendingChainBalanceProps = await to<
GetPendingChainBalanceType
>(
getPendingChainBalance({
lnd,
});
return pendingValue.pending_chain_balance;
} catch (error) {
logger.error('Error getting pending chain balance: %o', error);
throw new Error(getErrorMsg(error));
}
})
);
return pendingValue.pending_chain_balance;
},
getChainTransactions: async (
_: undefined,
@ -76,20 +80,17 @@ export const chainResolvers = {
const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
try {
const transactionList = await getChainTransactions({
const transactionList = await to<GetChainTransactionsType>(
getChainTransactions({
lnd,
});
})
);
const transactions = sortBy(
transactionList.transactions,
'created_at'
).reverse();
return transactions;
} catch (error) {
logger.error('Error getting chain transactions: %o', error);
throw new Error(getErrorMsg(error));
}
const transactions = sortBy(
transactionList.transactions,
'created_at'
).reverse();
return transactions;
},
getUtxos: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'getUtxos');
@ -97,14 +98,9 @@ export const chainResolvers = {
const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
try {
const { utxos } = await getUtxos({ lnd });
const info = await to<GetUtxosType>(getUtxos({ lnd }));
return utxos;
} catch (error) {
logger.error('Error getting utxos: %o', error);
throw new Error(getErrorMsg(error));
}
return info?.utxos;
},
},
Mutation: {
@ -143,26 +139,23 @@ export const chainResolvers = {
const sendAll = params.sendAll ? { is_send_all: true } : {};
try {
const send = await sendToChainAddress({
const send = await to<SendToChainAddressType>(
sendToChainAddress({
lnd,
address: params.address,
...(params.tokens && { tokens: params.tokens }),
...props,
...sendAll,
});
})
);
return {
confirmationCount: send.confirmation_count,
id: send.id,
isConfirmed: send.is_confirmed,
isOutgoing: send.is_outgoing,
...(send.tokens && { tokens: send.tokens }),
};
} catch (error) {
logger.error('Error sending to chain address: %o', error);
throw new Error(getErrorMsg(error));
}
return {
confirmationCount: send.confirmation_count,
id: send.id,
isConfirmed: send.is_confirmed,
isOutgoing: send.is_outgoing,
...(send.tokens && { tokens: send.tokens }),
};
},
},
};

View File

@ -2,19 +2,19 @@ import { gql } from 'apollo-server-micro';
export const chainTypes = gql`
type getUtxosType {
address: String
address_format: String
confirmation_count: Int
output_script: String
tokens: Int
transaction_id: String
transaction_vout: Int
address: String!
address_format: String!
confirmation_count: Int!
output_script: String!
tokens: Int!
transaction_id: String!
transaction_vout: Int!
}
type sendToType {
confirmationCount: String
id: String
isConfirmed: Boolean
isOutgoing: Boolean
confirmationCount: String!
id: String!
isConfirmed: Boolean!
isOutgoing: Boolean!
tokens: Int
}
@ -22,10 +22,10 @@ export const chainTypes = gql`
block_id: String
confirmation_count: Int
confirmation_height: Int
created_at: String
created_at: String!
fee: Int
id: String
output_addresses: [String]
tokens: Int
id: String!
output_addresses: [String]!
tokens: Int!
}
`;

View File

@ -1,6 +1,7 @@
import { logger } from 'server/helpers/logger';
import { toWithError } from 'server/helpers/async';
import { getChannel } from 'ln-service';
import { GetChannelType } from 'server/types/ln-service.types';
import { openChannel } from './resolvers/mutation/openChannel';
import { closeChannel } from './resolvers/mutation/closeChannel';
import { updateFees } from './resolvers/mutation/updateFees';
@ -53,7 +54,7 @@ export const channelResolvers = {
let node_policies = null;
let partner_node_policies = null;
channel.policies.forEach(policy => {
(channel as GetChannelType).policies.forEach(policy => {
if (localKey && localKey === policy.public_key) {
node_policies = {
...policy,
@ -67,7 +68,11 @@ export const channelResolvers = {
}
});
return { ...channel, node_policies, partner_node_policies };
return {
...(channel as GetChannelType),
node_policies,
partner_node_policies,
};
},
},
};

View File

@ -1,12 +1,8 @@
import { getChannelBalance as getLnChannelBalance } from 'ln-service';
import { ContextType } from 'server/types/apiTypes';
import { logger } from 'server/helpers/logger';
import { requestLimiter } from 'server/helpers/rateLimiter';
import {
getAuthLnd,
getErrorMsg,
getCorrectAuth,
} from 'server/helpers/helpers';
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
import { to } from 'server/helpers/async';
interface ChannelBalanceProps {
channel_balance: number;
@ -23,16 +19,13 @@ export const getChannelBalance = async (
const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
try {
const channelBalance: ChannelBalanceProps = await getLnChannelBalance({
const channelBalance: ChannelBalanceProps = await to(
getLnChannelBalance({
lnd,
});
return {
confirmedBalance: channelBalance.channel_balance,
pendingBalance: channelBalance.pending_balance,
};
} catch (error) {
logger.error('Error getting channel balance: %o', error);
throw new Error(getErrorMsg(error));
}
})
);
return {
confirmedBalance: channelBalance.channel_balance,
pendingBalance: channelBalance.pending_balance,
};
};

View File

@ -4,6 +4,7 @@ import { to } from 'server/helpers/async';
import { requestLimiter } from 'server/helpers/rateLimiter';
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
import { getChannelAge } from 'server/schema/health/helpers';
import { GetChannelsType } from 'server/types/ln-service.types';
export const getChannels = async (
_: undefined,
@ -17,7 +18,7 @@ export const getChannels = async (
const { public_key, current_block_height } = await to(getWalletInfo({ lnd }));
const { channels } = await to(
const { channels } = await to<GetChannelsType>(
getLnChannels({
lnd,
is_active: params.active,

View File

@ -55,36 +55,36 @@ export const channelTypes = gql`
}
type channelBalanceType {
confirmedBalance: Int
pendingBalance: Int
confirmedBalance: Int!
pendingBalance: Int!
}
type channelType {
capacity: Int
commit_transaction_fee: Int
commit_transaction_weight: Int
id: String
is_active: Boolean
is_closing: Boolean
is_opening: Boolean
is_partner_initiated: Boolean
is_private: Boolean
capacity: Int!
commit_transaction_fee: Int!
commit_transaction_weight: Int!
id: String!
is_active: Boolean!
is_closing: Boolean!
is_opening: Boolean!
is_partner_initiated: Boolean!
is_private: Boolean!
is_static_remote_key: Boolean
local_balance: Int
local_reserve: Int
partner_public_key: String
received: Int
remote_balance: Int
remote_reserve: Int
sent: Int
local_balance: Int!
local_reserve: Int!
partner_public_key: String!
received: Int!
remote_balance: Int!
remote_reserve: Int!
sent: Int!
time_offline: Int
time_online: Int
transaction_id: String
transaction_vout: Int
unsettled_balance: Int
partner_node_info: Node
transaction_id: String!
transaction_vout: Int!
unsettled_balance: Int!
partner_node_info: Node!
partner_fee_info: Channel
channel_age: Int
channel_age: Int!
}
type closeChannelType {
@ -93,21 +93,21 @@ export const channelTypes = gql`
}
type closedChannelType {
capacity: Int
capacity: Int!
close_confirm_height: Int
close_transaction_id: String
final_local_balance: Int
final_time_locked_balance: Int
final_local_balance: Int!
final_time_locked_balance: Int!
id: String
is_breach_close: Boolean
is_cooperative_close: Boolean
is_funding_cancel: Boolean
is_local_force_close: Boolean
is_remote_force_close: Boolean
partner_public_key: String
transaction_id: String
transaction_vout: Int
partner_node_info: Node
is_breach_close: Boolean!
is_cooperative_close: Boolean!
is_funding_cancel: Boolean!
is_local_force_close: Boolean!
is_remote_force_close: Boolean!
partner_public_key: String!
transaction_id: String!
transaction_vout: Int!
partner_node_info: Node!
}
type openChannelType {
@ -117,19 +117,19 @@ export const channelTypes = gql`
type pendingChannelType {
close_transaction_id: String
is_active: Boolean
is_closing: Boolean
is_opening: Boolean
local_balance: Int
local_reserve: Int
partner_public_key: String
received: Int
remote_balance: Int
remote_reserve: Int
sent: Int
is_active: Boolean!
is_closing: Boolean!
is_opening: Boolean!
local_balance: Int!
local_reserve: Int!
partner_public_key: String!
received: Int!
remote_balance: Int!
remote_reserve: Int!
sent: Int!
transaction_fee: Int
transaction_id: String
transaction_vout: Int
partner_node_info: Node
transaction_id: String!
transaction_vout: Int!
partner_node_info: Node!
}
`;

View File

@ -16,6 +16,10 @@ import {
decodeMessage,
} from 'server/helpers/customRecords';
import { logger } from 'server/helpers/logger';
import {
GetInvoicesType,
GetWalletInfoType,
} from 'server/types/ln-service.types';
export const chatResolvers = {
Query: {
@ -25,7 +29,7 @@ export const chatResolvers = {
const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
const invoiceList = await to(
const invoiceList = await to<GetInvoicesType>(
getInvoices({
lnd,
limit: params.initialize ? 100 : 5,
@ -72,7 +76,11 @@ export const chatResolvers = {
logger.debug(`Error verifying message: ${messageToVerify}`);
}
if (!error && verified?.signed_by === customRecords.sender) {
if (
!error &&
(verified as { signed_by: string })?.signed_by ===
customRecords.sender
) {
isVerified = true;
}
}
@ -88,7 +96,7 @@ export const chatResolvers = {
);
const filtered = await getFiltered();
const final = filtered.filter(message => !!message);
const final = filtered.filter(Boolean) || [];
return { token: invoiceList.next, messages: final };
},
@ -126,7 +134,7 @@ export const chatResolvers = {
messageToSend = `${params.tokens},${params.message}`;
}
const nodeInfo = await to(
const nodeInfo = await to<GetWalletInfoType>(
getWalletInfo({
lnd,
})
@ -167,7 +175,8 @@ export const chatResolvers = {
messages: customRecords,
})
);
return safe_fee;
// +1 is needed so that a fee of 0 doesnt evaluate to false
return safe_fee + 1;
},
},
};

View File

@ -3,13 +3,13 @@ import { gql } from 'apollo-server-micro';
export const chatTypes = gql`
type getMessagesType {
token: String
messages: [messagesType]
messages: [messagesType]!
}
type messagesType {
date: String
id: String
verified: Boolean
date: String!
id: String!
verified: Boolean!
contentType: String
sender: String
alias: String

View File

@ -15,7 +15,7 @@ export const githubResolvers = {
const [response, error] = await toWithError(fetch(appUrls.github));
if (error) {
if (error || !response) {
logger.debug('Unable to get latest github version');
throw new Error('NoGithubVersion');
}

View File

@ -1,6 +1,15 @@
import { groupBy } from 'underscore';
import { ForwardType } from 'server/types/ln-service.types';
export const getChannelVolume = forwards => {
type GroupedObject = {
[key: string]: ForwardType[];
};
type TotalGroupedObject = {
[key: string]: { tokens: number }[];
};
export const getChannelVolume = (forwards: ForwardType[]) => {
const orderedIncoming = groupBy(forwards, f => f.incoming_channel);
const orderedOutgoing = groupBy(forwards, f => f.outgoing_channel);
@ -14,7 +23,7 @@ export const getChannelVolume = forwards => {
return reduceTokens(together);
};
const reduceTokens = array => {
const reduceTokens = (array: GroupedObject | TotalGroupedObject) => {
const reducedArray = [];
for (const key in array) {
if (Object.prototype.hasOwnProperty.call(array, key)) {

View File

@ -4,6 +4,7 @@ import { requestLimiter } from 'server/helpers/rateLimiter';
import { to, toWithError } from 'server/helpers/async';
import { logger } from 'server/helpers/logger';
import { ContextType } from 'server/types/apiTypes';
import { GetChannelsType, GetChannelType } from 'server/types/ln-service.types';
import { getFeeScore, getAverage, getMyFeeScore } from '../helpers';
type ChannelFeesType = {
@ -22,28 +23,30 @@ export default async (_: undefined, params: any, context: ContextType) => {
const lnd = getAuthLnd(auth);
const { public_key } = await to(getWalletInfo({ lnd }));
const { channels } = await to(getChannels({ lnd }));
const { channels } = await to<GetChannelsType>(getChannels({ lnd }));
const getChannelList = () =>
Promise.all(
channels
.map(async channel => {
const { id, partner_public_key: publicKey } = channel;
const [{ policies }, channelError] = await toWithError(
const [channelInfo, channelError] = await toWithError(
getChannel({
lnd,
id,
})
);
if (channelError) {
if (channelError || !channelInfo) {
logger.debug(
`Error getting channel with id ${id}: %o`,
channelError
);
return;
return null;
}
const policies = (channelInfo as GetChannelType).policies;
let partnerBaseFee = 0;
let partnerFeeRate = 0;
let myBaseFee = 0;
@ -77,7 +80,7 @@ export default async (_: undefined, params: any, context: ContextType) => {
const list = await getChannelList();
const health = list.map((channel: ChannelFeesType) => {
const health = (list as ChannelFeesType[]).map((channel: ChannelFeesType) => {
const partnerRateScore = getFeeScore(2000, channel.partnerFeeRate);
const partnerBaseScore = getFeeScore(100000, channel.partnerBaseFee);
const myRateScore = getMyFeeScore(2000, channel.myFeeRate, 200);

View File

@ -3,6 +3,7 @@ import { getCorrectAuth, getAuthLnd } from 'server/helpers/helpers';
import { requestLimiter } from 'server/helpers/rateLimiter';
import { to } from 'server/helpers/async';
import { ContextType } from 'server/types/apiTypes';
import { GetChannelsType } from 'server/types/ln-service.types';
import { getAverage } from '../helpers';
const halfMonthInMilliSeconds = 1296000000;
@ -13,7 +14,7 @@ export default async (_: undefined, params: any, context: ContextType) => {
const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
const { channels } = await to(getChannels({ lnd }));
const { channels } = await to<GetChannelsType>(getChannels({ lnd }));
const health = channels.map(channel => {
const {

View File

@ -4,6 +4,10 @@ import { getCorrectAuth, getAuthLnd } from 'server/helpers/helpers';
import { to } from 'server/helpers/async';
import { subMonths } from 'date-fns';
import { ContextType } from 'server/types/apiTypes';
import {
GetChannelsType,
GetForwardsType,
} from 'server/types/ln-service.types';
import { getChannelVolume, getChannelIdInfo, getAverage } from '../helpers';
const monthInBlocks = 4380;
@ -18,15 +22,20 @@ export default async (_: undefined, params: any, context: ContextType) => {
const after = subMonths(new Date(), 1).toISOString();
const { current_block_height } = await to(getWalletInfo({ lnd }));
const { channels } = await to(getChannels({ lnd }));
const { forwards } = await to(getForwards({ lnd, after, before }));
const { channels } = await to<GetChannelsType>(getChannels({ lnd }));
const { forwards } = await to<GetForwardsType>(
getForwards({ lnd, after, before })
);
const channelVolume = getChannelVolume(forwards);
const channelVolume: { channel: string; tokens: number }[] = getChannelVolume(
forwards
);
const channelDetails = channels
.map(channel => {
const { tokens } =
channelVolume.find(c => c.channel === channel.id) || {};
const { tokens } = channelVolume.find(c => c.channel === channel.id) || {
tokens: 0,
};
const info = getChannelIdInfo(channel.id);
if (!info) return;
@ -45,23 +54,26 @@ export default async (_: undefined, params: any, context: ContextType) => {
})
.filter(Boolean);
const average = getAverage(channelDetails.map(c => c.volumeNormalized));
const average = getAverage(channelDetails.map(c => c?.volumeNormalized || 0));
const health = channelDetails.map(channel => {
const diff = (channel.volumeNormalized - average) / average || -1;
const score = Math.round((diff + 1) * 100);
const health = channelDetails
.map(channel => {
if (!channel) return null;
const diff = (channel.volumeNormalized - average) / average || -1;
const score = Math.round((diff + 1) * 100);
return {
id: channel.id,
score,
volumeNormalized: channel.volumeNormalized,
averageVolumeNormalized: average,
partner: { publicKey: channel.publicKey, lnd },
};
});
return {
id: channel.id,
score,
volumeNormalized: channel.volumeNormalized,
averageVolumeNormalized: average,
partner: { publicKey: channel.publicKey, lnd },
};
})
.filter(Boolean);
const globalAverage = Math.round(
getAverage(health.map(c => Math.min(c.score, 100)))
getAverage(health.map(c => Math.min(c?.score || 0, 100)))
);
return { score: globalAverage, channels: health };

View File

@ -16,6 +16,7 @@ import {
getLnd,
} from 'server/helpers/helpers';
import { to } from 'server/helpers/async';
import { DecodedType } from 'server/types/ln-service.types';
const KEYSEND_TYPE = '5482373484';
@ -26,7 +27,7 @@ export const invoiceResolvers = {
const lnd = getLnd(params.auth, context);
const decoded = await to(
const decoded = await to<DecodedType>(
decodePaymentRequest({
lnd,
request: params.request,

View File

@ -4,16 +4,16 @@ export const invoiceTypes = gql`
type decodeType {
chain_address: String
cltv_delta: Int
description: String
description: String!
description_hash: String
destination: String
expires_at: String
id: String
mtokens: String
destination: String!
expires_at: String!
id: String!
mtokens: String!
payment: String
routes: [[RouteType]]
safe_tokens: Int
tokens: Int
routes: [[RouteType]]!
safe_tokens: Int!
tokens: Int!
destination_node: Node!
probe_route: ProbeRoute
}
@ -23,7 +23,7 @@ export const invoiceTypes = gql`
channel: String
cltv_delta: Int
fee_rate: Int
public_key: String
public_key: String!
}
type payType {

View File

@ -6,14 +6,18 @@ import { appUrls } from 'server/utils/appUrls';
export const lnpayResolvers = {
Query: {
getLnPay: async (_: undefined, params: any, context: ContextType) => {
getLnPay: async (
_: undefined,
params: { amount: number },
context: ContextType
) => {
await requestLimiter(context.ip, 'getLnPay');
const [response, error] = await toWithError(
fetch(`${appUrls.lnpay}?amount=${params.amount}`)
);
if (error) {
if (error || !response) {
logger.debug('Unable to get lnpay invoice: %o', error);
throw new Error('NoLnPayInvoice');
}
@ -26,7 +30,7 @@ export const lnpayResolvers = {
const [response, error] = await toWithError(fetch(appUrls.lnpay));
if (error) {
if (error || !response) {
logger.debug('Unable to connect to ThunderHub LNPAY');
throw new Error('NoLnPay');
}

View File

@ -1,16 +1,24 @@
import {
getNode as getLnNode,
getWalletInfo,
getClosedChannels,
} from 'ln-service';
import { getNode, getWalletInfo, getClosedChannels } from 'ln-service';
import { to, toWithError } from 'server/helpers/async';
import { requestLimiter } from 'server/helpers/rateLimiter';
import {
ClosedChannelsType,
LndObject,
GetWalletInfoType,
GetNodeType,
} from 'server/types/ln-service.types';
import { getAuthLnd, getCorrectAuth, getLnd } from '../../helpers/helpers';
import { ContextType } from '../../types/apiTypes';
import { logger } from '../../helpers/logger';
const errorNode = { alias: 'Node not found' };
type NodeParent = {
lnd: LndObject;
publicKey: string;
withChannels?: boolean;
};
export const nodeResolvers = {
Query: {
getNode: async (_: undefined, params: any, context: ContextType) => {
@ -27,13 +35,13 @@ export const nodeResolvers = {
const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
const info = await to(
const info = await to<GetWalletInfoType>(
getWalletInfo({
lnd,
})
);
const closedChannels = await to(
const closedChannels: ClosedChannelsType = await to(
getClosedChannels({
lnd,
})
@ -46,7 +54,7 @@ export const nodeResolvers = {
},
},
Node: {
node: async parent => {
node: async (parent: NodeParent) => {
const { lnd, withChannels, publicKey } = parent;
if (!lnd) {
@ -60,19 +68,19 @@ export const nodeResolvers = {
}
const [info, error] = await toWithError(
getLnNode({
getNode({
lnd,
is_omitting_channels: !withChannels,
public_key: publicKey,
})
);
if (error) {
if (error || !info) {
logger.debug(`Error getting node with key: ${publicKey}`);
return errorNode;
}
return { ...info, public_key: publicKey };
return { ...(info as GetNodeType), public_key: publicKey };
},
},
};

View File

@ -11,24 +11,24 @@ export const nodeTypes = gql`
}
type Node {
node: nodeType
node: nodeType!
}
type nodeInfoType {
chains: [String]
color: String
active_channels_count: Int
closed_channels_count: Int
alias: String
current_block_hash: String
current_block_height: Int
is_synced_to_chain: Boolean
is_synced_to_graph: Boolean
latest_block_at: String
peers_count: Int
pending_channels_count: Int
public_key: String
uris: [String]
version: String
chains: [String!]!
color: String!
active_channels_count: Int!
closed_channels_count: Int!
alias: String!
current_block_hash: String!
current_block_height: Int!
is_synced_to_chain: Boolean!
is_synced_to_graph: Boolean!
latest_block_at: String!
peers_count: Int!
pending_channels_count: Int!
public_key: String!
uris: [String!]!
version: String!
}
`;

View File

@ -2,15 +2,15 @@ import { gql } from 'apollo-server-micro';
export const peerTypes = gql`
type peerType {
bytes_received: Int
bytes_sent: Int
is_inbound: Boolean
bytes_received: Int!
bytes_sent: Int!
is_inbound: Boolean!
is_sync_peer: Boolean
ping_time: Int
public_key: String
socket: String
tokens_received: Int
tokens_sent: Int
partner_node_info: Node
ping_time: Int!
public_key: String!
socket: String!
tokens_received: Int!
tokens_sent: Int!
partner_node_info: Node!
}
`;

View File

@ -8,6 +8,13 @@ import { logger } from 'server/helpers/logger';
import { requestLimiter } from 'server/helpers/rateLimiter';
import { getLnd } from 'server/helpers/helpers';
import { toWithError, to } from 'server/helpers/async';
import { LndObject, ProbeForRouteType } from 'server/types/ln-service.types';
type RouteParent = {
lnd: LndObject;
destination: string;
tokens: number;
};
export const routeResolvers = {
Query: {
@ -37,7 +44,7 @@ export const routeResolvers = {
},
},
ProbeRoute: {
route: async parent => {
route: async (parent: RouteParent) => {
const { lnd, destination, tokens } = parent;
if (!lnd) {
@ -61,19 +68,20 @@ export const routeResolvers = {
return null;
}
if (!info.route) {
if (!(info as ProbeForRouteType).route) {
logger.debug(
`No route found to destination ${destination} for ${tokens} tokens`
);
return null;
}
const hopsWithNodes = info.route.hops.map(h => ({
...h,
node: { lnd, publicKey: h.public_key },
}));
const hopsWithNodes =
(info as ProbeForRouteType).route?.hops.map(h => ({
...h,
node: { lnd, publicKey: h.public_key },
})) || [];
return { ...info.route, hops: hopsWithNodes };
return { ...(info as ProbeForRouteType).route, hops: hopsWithNodes };
},
},
};

View File

@ -19,12 +19,17 @@ export const tbaseResolvers = {
body: JSON.stringify({ query }),
})
);
if (fetchError) return [];
if (fetchError || !response) return [];
const result = await response.json();
const { errors, data } = result || {};
if (errors) return [];
return data?.getNodes?.filter(n => n.public_key && n.socket) || [];
return (
data?.getNodes?.filter(
(n: { public_key: string; socket: string }) =>
n.public_key && n.socket
) || []
);
},
},
};

View File

@ -4,7 +4,7 @@ export const tbaseTypes = gql`
type baseNodesType {
_id: String
name: String
public_key: String
socket: String
public_key: String!
socket: String!
}
`;

View File

@ -16,6 +16,7 @@ import {
getLnd,
} from 'server/helpers/helpers';
import { toWithError } from 'server/helpers/async';
import { ChannelType } from 'server/types/ln-service.types';
export const toolsResolvers = {
Query: {
@ -25,7 +26,7 @@ export const toolsResolvers = {
const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
let backupObj = { backup: '', channels: [] };
let backupObj = { backup: '', channels: [] as ChannelType[] };
try {
backupObj = JSON.parse(params.backup);
} catch (error) {

View File

@ -1,67 +0,0 @@
export interface PaymentProps {
created_at: string;
destination: string;
fee: number;
fee_mtokens: string;
hops: string[];
id: string;
is_confirmed: boolean;
is_outgoing: boolean;
mtokens: string;
request: string;
secret: string;
tokens: number;
}
export interface PaymentsProps {
payments: PaymentProps[];
}
interface InvoiceMessagesType {
type: string;
value: string;
}
export interface InvoicePaymentProps {
confirmed_at: string;
created_at: string;
created_height: number;
in_channel: string;
is_canceled: boolean;
is_confirmed: boolean;
is_held: boolean;
messages: InvoiceMessagesType[];
mtokens: string;
pending_index: number;
tokens: number;
}
export interface InvoiceProps {
chain_address: string;
confirmed_at: string;
created_at: string;
description: string;
description_hash: string;
expires_at: string;
id: string;
is_canceled: boolean;
is_confirmed: boolean;
is_held: boolean;
is_outgoing: boolean;
is_private: boolean;
payments: InvoicePaymentProps[];
received: number;
received_mtokens: string;
request: string;
secret: string;
tokens: number;
}
export interface InvoicesProps {
invoices: InvoiceProps[];
next: string;
}
export interface NodeProps {
alias: string;
}

View File

@ -10,8 +10,16 @@ import { ContextType } from 'server/types/apiTypes';
import { requestLimiter } from 'server/helpers/rateLimiter';
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
import { to } from 'server/helpers/async';
import { ForwardCompleteProps } from '../widgets/resolvers/interface';
import { PaymentsProps, InvoicesProps } from './interface';
import {
GetInvoicesType,
GetPaymentsType,
InvoiceType,
PaymentType,
GetForwardsType,
} from 'server/types/ln-service.types';
type TransactionType = InvoiceType | PaymentType;
type TransactionWithType = { isTypeOf: string } & TransactionType;
export const transactionResolvers = {
Query: {
@ -30,7 +38,7 @@ export const transactionResolvers = {
let token = '';
let withInvoices = true;
const invoiceList: InvoicesProps = await to(
const invoiceList = await to<GetInvoicesType>(
getInvoices({
lnd,
...invoiceProps,
@ -52,10 +60,10 @@ export const transactionResolvers = {
const { date } = invoices[invoices.length - 1];
firstInvoiceDate = invoices[0].date;
lastInvoiceDate = date;
token = invoiceList.next;
token = invoiceList.next || '';
}
const paymentList: PaymentsProps = await to(
const paymentList = await to<GetPaymentsType>(
getPayments({
lnd,
})
@ -70,7 +78,7 @@ export const transactionResolvers = {
isTypeOf: 'PaymentType',
}));
const filterArray = payment => {
const filterArray = (payment: typeof payments[number]) => {
const last =
compareDesc(new Date(lastInvoiceDate), new Date(payment.date)) === 1;
const first = params.token
@ -123,7 +131,7 @@ export const transactionResolvers = {
})
);
const forwardsList: ForwardCompleteProps = await to(
const forwardsList = await to<GetForwardsType>(
getLnForwards({
lnd,
after: startDate,
@ -153,7 +161,7 @@ export const transactionResolvers = {
},
},
Transaction: {
__resolveType(parent) {
__resolveType(parent: TransactionWithType) {
return parent.isTypeOf;
},
},

View File

@ -24,7 +24,7 @@ export const transactionTypes = gql`
destination_node: Node
fee: Int!
fee_mtokens: String!
hops: [Node]
hops: [Node!]!
id: String!
index: Int
is_confirmed: Boolean!

View File

@ -49,7 +49,7 @@ export const queryTypes = gql`
getTimeHealth(auth: authType!): channelsTimeHealth
getFeeHealth(auth: authType!): channelsFeeHealth
getChannelBalance(auth: authType!): channelBalanceType
getChannels(auth: authType!, active: Boolean): [channelType]
getChannels(auth: authType!, active: Boolean): [channelType]!
getClosedChannels(auth: authType!, type: String): [closedChannelType]
getPendingChannels(auth: authType!): [pendingChannelType]
getChannelFees(auth: authType!): [channelFeeType]

View File

@ -2,14 +2,14 @@ import { gql } from 'apollo-server-micro';
export const walletTypes = gql`
type walletInfoType {
build_tags: [String]
commit_hash: String
is_autopilotrpc_enabled: Boolean
is_chainrpc_enabled: Boolean
is_invoicesrpc_enabled: Boolean
is_signrpc_enabled: Boolean
is_walletrpc_enabled: Boolean
is_watchtowerrpc_enabled: Boolean
is_wtclientrpc_enabled: Boolean
build_tags: [String!]!
commit_hash: String!
is_autopilotrpc_enabled: Boolean!
is_chainrpc_enabled: Boolean!
is_invoicesrpc_enabled: Boolean!
is_signrpc_enabled: Boolean!
is_walletrpc_enabled: Boolean!
is_watchtowerrpc_enabled: Boolean!
is_wtclientrpc_enabled: Boolean!
}
`;

View File

@ -3,6 +3,7 @@ import { ContextType } from 'server/types/apiTypes';
import { requestLimiter } from 'server/helpers/rateLimiter';
import { getLnd } from 'server/helpers/helpers';
import { to } from 'server/helpers/async';
import { GetChannelsType } from 'server/types/ln-service.types';
export const getChannelReport = async (
_: undefined,
@ -13,7 +14,7 @@ export const getChannelReport = async (
const lnd = getLnd(params.auth, context);
const info = await to(getChannels({ lnd }));
const info = await to<GetChannelsType>(getChannels({ lnd }));
if (!info || info?.channels?.length <= 0) {
return;

View File

@ -6,8 +6,11 @@ import { getNodeFromChannel } from 'server/helpers/getNodeFromChannel';
import { requestLimiter } from 'server/helpers/rateLimiter';
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
import { to } from 'server/helpers/async';
import {
GetForwardsType,
GetWalletInfoType,
} from 'server/types/ln-service.types';
import { countArray, countRoutes } from './helpers';
import { ForwardCompleteProps } from './interface';
export const getForwardChannelsReport = async (
_: undefined,
@ -76,7 +79,7 @@ export const getForwardChannelsReport = async (
})
);
const forwardsList: ForwardCompleteProps = await to(
const forwardsList = await to<GetForwardsType>(
getForwards({
lnd,
after: startDate,
@ -84,7 +87,7 @@ export const getForwardChannelsReport = async (
})
);
const walletInfo: { public_key: string } = await to(
const walletInfo = await to<GetWalletInfoType>(
getWalletInfo({
lnd,
})
@ -101,9 +104,15 @@ export const getForwardChannelsReport = async (
while (!finishedFetching) {
if (next) {
const moreForwards = await to(getForwards({ lnd, token: next }));
const moreForwards = await to<GetForwardsType>(
getForwards({ lnd, token: next })
);
forwards = [...forwards, ...moreForwards.forwards];
next = moreForwards.next;
if (moreForwards.next) {
next = moreForwards.next;
} else {
finishedFetching = true;
}
} else {
finishedFetching = true;
}

View File

@ -10,8 +10,8 @@ import { ContextType } from 'server/types/apiTypes';
import { requestLimiter } from 'server/helpers/rateLimiter';
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
import { to } from 'server/helpers/async';
import { GetForwardsType } from 'server/types/ln-service.types';
import { reduceForwardArray } from './helpers';
import { ForwardCompleteProps } from './interface';
export const getForwardReport = async (
_: undefined,
@ -45,7 +45,7 @@ export const getForwardReport = async (
startDate = subHours(endDate, 24);
}
const forwardsList: ForwardCompleteProps = await to(
const forwardsList = await to<GetForwardsType>(
getForwards({
lnd,
after: startDate,
@ -64,7 +64,9 @@ export const getForwardReport = async (
while (!finishedFetching) {
if (next) {
const moreForwards = await to(getForwards({ lnd, token: next }));
const moreForwards = await to<GetForwardsType>(
getForwards({ lnd, token: next })
);
forwards = [...forwards, ...moreForwards.forwards];
next = moreForwards.next;
} else {

View File

@ -5,6 +5,10 @@ import { ContextType } from 'server/types/apiTypes';
import { requestLimiter } from 'server/helpers/rateLimiter';
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
import { to } from 'server/helpers/async';
import {
GetInvoicesType,
GetPaymentsType,
} from 'server/types/ln-service.types';
import { reduceInOutArray } from './helpers';
export const getInOut = async (
@ -35,8 +39,10 @@ export const getInOut = async (
difference = (date: string) => differenceInHours(endDate, new Date(date));
}
const invoiceList = await to(getInvoices({ lnd, limit: 50 }));
const paymentList = await to(getPayments({ lnd }));
const invoiceList = await to<GetInvoicesType>(
getInvoices({ lnd, limit: 50 })
);
const paymentList = await to<GetPaymentsType>(getPayments({ lnd }));
let invoiceArray = invoiceList.invoices;
let next = invoiceList.next;
@ -51,7 +57,9 @@ export const getInOut = async (
const dif = difference(lastInvoice.created_at);
if (next && dif < periods) {
const newInvoices = await to(getInvoices({ lnd, token: next }));
const newInvoices = await to<GetInvoicesType>(
getInvoices({ lnd, token: next })
);
invoiceArray = [...invoiceArray, ...newInvoices.invoices];
next = newInvoices.next;
} else {

View File

@ -1,6 +1,6 @@
import { reduce, groupBy } from 'underscore';
import { ForwardType } from 'server/types/ln-service.types';
import {
ForwardProps,
ReduceObjectProps,
ListProps,
InOutProps,
@ -11,7 +11,7 @@ export const reduceForwardArray = (list: ListProps) => {
const reducedOrder = [];
for (const key in list) {
if (Object.prototype.hasOwnProperty.call(list, key)) {
const element: ForwardProps[] = list[key];
const element: ForwardType[] = list[key];
const reducedArray: ReduceObjectProps = reduce(
element,
(a: ReduceObjectProps, b: ReduceObjectProps) => {
@ -19,7 +19,8 @@ export const reduceForwardArray = (list: ListProps) => {
fee: a.fee + b.fee,
tokens: a.tokens + b.tokens,
};
}
},
{ fee: 0, tokens: 0 }
);
reducedOrder.push({
period: Number(key),
@ -39,9 +40,10 @@ export const reduceInOutArray = (list: InOutListProps) => {
const element: InOutProps[] = list[key];
const reducedArray: InOutProps = reduce(
element,
(a: ReduceObjectProps, b: ReduceObjectProps) => ({
(a, b) => ({
tokens: a.tokens + b.tokens,
})
}),
{ tokens: 0 }
);
reducedOrder.push({
period: Number(key),
@ -53,7 +55,7 @@ export const reduceInOutArray = (list: InOutListProps) => {
return reducedOrder;
};
export const countArray = (list: ForwardProps[], type: boolean) => {
export const countArray = (list: ForwardType[], type: boolean) => {
const inOrOut = type ? 'incoming_channel' : 'outgoing_channel';
const grouped = groupBy(list, inOrOut);
@ -82,7 +84,7 @@ export const countArray = (list: ForwardProps[], type: boolean) => {
return channelInfo;
};
export const countRoutes = (list: ForwardProps[]) => {
export const countRoutes = (list: ForwardType[]) => {
const grouped = groupBy(list, 'route');
const channelInfo = [];

View File

@ -1,20 +1,7 @@
export interface ForwardProps {
created_at: string;
fee: number;
fee_mtokens: string;
incoming_channel: string;
mtokens: string;
outgoing_channel: string;
tokens: number;
}
export interface ForwardCompleteProps {
forwards: ForwardProps[];
next: string;
}
import { ForwardType } from 'server/types/ln-service.types';
export interface ListProps {
[key: string]: ForwardProps[];
[key: string]: ForwardType[];
}
export interface ReduceObjectProps {

View File

@ -49,11 +49,7 @@ export const ContextMockNoSSO: ContextType = {
secret: '123456789',
ssoVerified: true,
account: 'accountID',
sso: {
macaroon: null,
cert: null,
host: null,
},
sso: null,
accounts: [
{
name: 'account',

View File

@ -1,12 +1,12 @@
import { ServerResponse } from 'http';
type SSOType = {
macaroon: string | null;
export type SSOType = {
macaroon: string;
cert: string | null;
host: string | null;
host: string;
};
type AccountType = {
export type AccountType = {
name: string;
id: string;
host: string;
@ -20,7 +20,7 @@ export type ContextType = {
secret: string;
ssoVerified: boolean;
account: string | null;
sso: SSOType;
sso: SSOType | null;
accounts: AccountType[];
res: ServerResponse;
};

View File

@ -0,0 +1 @@
export type RebalanceResponseType = { rebalance: [{}, {}, {}] };

View File

@ -0,0 +1,94 @@
export type LndObject = {};
export type ChannelType = {
id: string;
tokens: number;
is_partner_initiated: boolean;
commit_transaction_fee: number;
is_active: boolean;
local_balance: number;
remote_balance: number;
partner_public_key: string;
time_offline?: number;
time_online?: number;
};
export type DecodedType = {
destination: string;
tokens: number;
};
export type ClosedChannelsType = {
channels: [];
};
export type InvoiceType = {
id: string;
created_at: string;
confirmed_at: string;
tokens: number;
is_confirmed: boolean;
received: number;
payments: { messages: [{ type: string; value: string }] }[];
};
export type PaymentType = {
created_at: string;
is_confirmed: boolean;
tokens: number;
destination: string;
hops: string[];
};
export type ForwardType = {
tokens: number;
incoming_channel: string;
outgoing_channel: string;
created_at: string;
fee: number;
};
export type GetWalletInfoType = {
alias: string;
public_key: string;
};
export type GetNodeType = { alias: string; color: string };
export type UtxoType = {};
export type ChainTransaction = {};
export type ProbeForRouteType = { route?: { hops: [{ public_key: string }] } };
export type GetChannelType = {
policies: {
public_key: string;
base_fee_mtokens: string;
fee_rate: number;
}[];
};
export type GetChannelsType = { channels: ChannelType[] };
export type GetForwardsType = { forwards: ForwardType[]; next?: string };
export type GetInvoicesType = { invoices: InvoiceType[]; next?: string };
export type GetPaymentsType = { payments: PaymentType[]; next?: string };
export type GetChainBalanceType = { chain_balance: number };
export type GetPendingChainBalanceType = { pending_chain_balance: number };
export type GetChainTransactionsType = { transactions: ChainTransaction[] };
export type GetUtxosType = { utxos: UtxoType[] };
export type SendToChainAddressType = {
id: string;
confirmation_count: number;
is_confirmed: boolean;
is_outgoing: boolean;
tokens: number | null;
};

View File

@ -3,6 +3,7 @@ import {
useAccountDispatch,
useAccountState,
CompleteAccount,
AccountProps,
} from 'src/context/AccountContext';
import { addIdAndTypeToAccount } from 'src/context/helpers/context';
import { useGetServerAccountsQuery } from 'src/graphql/queries/__generated__/getServerAccounts.generated';
@ -12,7 +13,7 @@ import { appendBasePath } from 'src/utils/basePath';
import { getUrlParam } from 'src/utils/url';
import { useGetAuthTokenQuery } from 'src/graphql/queries/__generated__/getAuthToken.generated';
export const ServerAccounts = () => {
export const ServerAccounts: React.FC = () => {
const { hasAccount } = useAccountState();
const dispatch = useAccountDispatch();
const { push, pathname, query } = useRouter();
@ -46,7 +47,9 @@ export const ServerAccounts = () => {
const session = sessionStorage.getItem('session') || null;
const changeId = localStorage.getItem('active') || null;
const savedAccounts = JSON.parse(localStorage.getItem('accounts') || '[]');
const accountsToAdd = savedAccounts.map(a => addIdAndTypeToAccount(a));
const accountsToAdd = savedAccounts.map((a: AccountProps) =>
addIdAndTypeToAccount(a)
);
dispatch({
type: 'initialize',
accountsToAdd,

View File

@ -1,6 +1,7 @@
import React from 'react';
import { useAccountState, CLIENT_ACCOUNT } from 'src/context/AccountContext';
export const AdminSwitch = ({ children }) => {
export const AdminSwitch: React.FC = ({ children }) => {
const { account, session } = useAccountState();
if (account?.type === CLIENT_ACCOUNT) {
@ -9,5 +10,5 @@ export const AdminSwitch = ({ children }) => {
}
}
return children;
return <>{children}</>;
};

View File

@ -11,10 +11,10 @@ type PriceProps = {
};
type AnimatedProps = {
amount: number;
amount?: number;
};
export const AnimatedNumber = ({ amount = 0 }: AnimatedProps) => {
export const AnimatedNumber: React.FC<AnimatedProps> = ({ amount = 0 }) => {
const { value } = useSpring({
from: { value: 0 },
value: amount,

View File

@ -13,11 +13,16 @@ type AdminProps = {
setChecked: (state: boolean) => void;
};
export const AdminCheck = ({ host, admin, cert, setChecked }: AdminProps) => {
export const AdminCheck: React.FC<AdminProps> = ({
host,
admin,
cert,
setChecked,
}) => {
const { data, loading } = useGetCanAdminQuery({
fetchPolicy: 'network-only',
skip: !admin,
variables: { auth: getAuthObj(host, null, admin, cert) },
variables: { auth: getAuthObj(host, undefined, admin, cert) },
onError: () => {
setChecked(false);
},

View File

@ -53,7 +53,7 @@ export const ViewCheck = ({
if (loading) {
return <ScaleLoader height={20} color={themeColors.blue3} />;
}
if (data?.getNodeInfo.alias) {
if (data?.getNodeInfo?.alias) {
return <Check size={18} />;
}
return <X size={18} />;

View File

@ -105,11 +105,11 @@ export const Auth = ({ type, status, callback, setStatus }: AuthProps) => {
dispatchAccount({
type: 'addAccountAndSave',
accountToAdd: {
name,
name: name || '',
host,
admin: encryptedAdmin,
viewOnly: correctViewOnly,
cert,
admin: encryptedAdmin || '',
viewOnly: correctViewOnly || '',
cert: cert || '',
},
...(!correctViewOnly && { session: admin }),
});

View File

@ -3,7 +3,7 @@ import { useGetBitcoinFeesQuery } from 'src/graphql/queries/__generated__/getBit
import { useBitcoinDispatch } from '../../context/BitcoinContext';
import { useConfigState } from '../../context/ConfigContext';
export const BitcoinFees = () => {
export const BitcoinFees: React.FC = () => {
const { fetchFees } = useConfigState();
const setInfo = useBitcoinDispatch();
@ -27,7 +27,10 @@ export const BitcoinFees = () => {
useEffect(() => {
if (!loading && data && data.getBitcoinFees && fetchFees) {
const { fast, halfHour, hour } = data.getBitcoinFees;
setInfo({ type: 'fetched', state: { fast, halfHour, hour } });
setInfo({
type: 'fetched',
state: { fast: fast || 0, halfHour: halfHour || 0, hour: hour || 0 },
});
}
}, [data, loading, setInfo, fetchFees]);

View File

@ -3,7 +3,7 @@ import { useGetBitcoinPriceQuery } from 'src/graphql/queries/__generated__/getBi
import { usePriceDispatch } from '../../context/PriceContext';
import { useConfigState } from '../../context/ConfigContext';
export const BitcoinPrice = () => {
export const BitcoinPrice: React.FC = () => {
const { fetchPrices } = useConfigState();
const setPrices = usePriceDispatch();
const { loading, data, stopPolling } = useGetBitcoinPriceQuery({

View File

@ -27,7 +27,16 @@ export const SecureButton: React.FC<SecureButtonProps> = ({
const { session, account } = useAccountState();
if (account.type === CLIENT_ACCOUNT && !account.admin && !session) {
if (!account) {
return null;
}
if (
account &&
account.type === CLIENT_ACCOUNT &&
!account.admin &&
!session
) {
return null;
}

View File

@ -20,6 +20,10 @@ export const SecureWrapper: React.FC<SecureButtonProps> = ({
const { account, session } = useAccountState();
if (!account) {
return null;
}
if (account.type === CLIENT_ACCOUNT && !account.admin && !session) {
return null;
}

View File

@ -4,11 +4,12 @@ import { useRouter } from 'next/router';
import { useAccountState } from 'src/context/AccountContext';
import { useGetMessagesQuery } from 'src/graphql/queries/__generated__/getMessages.generated';
import { useStatusState } from 'src/context/StatusContext';
import { MessagesType } from 'src/graphql/types';
import { useChatState, useChatDispatch } from '../../context/ChatContext';
import { getErrorContent } from '../../utils/error';
import { useConfigState } from '../../context/ConfigContext';
export const ChatFetcher = () => {
export const ChatFetcher: React.FC = () => {
const newChatToastId = 'newChatToastId';
const { chatPollingSpeed } = useConfigState();
@ -39,9 +40,7 @@ export const ChatFetcher = () => {
for (let i = 0; i < messages.length; i += 1) {
if (index < 0) {
const element = messages[i];
const { id } = element;
if (id === lastChat) {
if (element?.id === lastChat) {
index = i;
}
}
@ -61,8 +60,15 @@ export const ChatFetcher = () => {
}
const newMessages = messages.slice(0, index);
const last = newMessages[0]?.id;
dispatch({ type: 'additional', chats: newMessages, lastChat: last });
if (newMessages?.length) {
const last = newMessages[0]?.id || '';
dispatch({
type: 'additional',
chats: (newMessages as MessagesType[]) || [],
lastChat: last,
});
}
}
}, [data, loading, error, dispatch, lastChat, pathname]);

View File

@ -2,10 +2,11 @@ import * as React from 'react';
import { toast } from 'react-toastify';
import { useAccountState } from 'src/context/AccountContext';
import { useGetMessagesLazyQuery } from 'src/graphql/queries/__generated__/getMessages.generated';
import { MessagesType } from 'src/graphql/types';
import { useChatDispatch } from '../../context/ChatContext';
import { getErrorContent } from '../../utils/error';
export const ChatInit = () => {
export const ChatInit: React.FC = () => {
const { auth, account } = useAccountState();
const dispatch = useChatDispatch();
@ -45,17 +46,17 @@ export const ChatInit = () => {
if (!initLoading && !initError && initData && initData.getMessages) {
const { messages } = initData.getMessages;
if (messages.length <= 0) {
if (!messages?.length) {
dispatch({ type: 'initialized' });
return;
}
const lastChat = messages[0].id || '';
const sender = messages[0].sender || '';
const lastChat = messages[0]?.id || '';
const sender = messages[0]?.sender || '';
dispatch({
type: 'initialized',
chats: messages,
chats: messages as MessagesType[],
lastChat,
sender,
});

View File

@ -78,7 +78,7 @@ export const Separation = styled.div<SeparationProps>`
`;
interface SubCardProps {
color?: string;
subColor?: string | null;
padding?: string;
withMargin?: string;
noCard?: boolean;

View File

@ -39,7 +39,8 @@ export const copyLink = (text: string) => (
</CopyToClipboard>
);
export const getTransactionLink = (transaction: string) => {
export const getTransactionLink = (transaction: string | null | undefined) => {
if (!transaction) return null;
if (disableLinks) {
return (
<>
@ -59,7 +60,8 @@ export const getTransactionLink = (transaction: string) => {
);
};
export const getWithCopy = (text: string) => {
export const getWithCopy = (text: string | null | undefined) => {
if (!text) return null;
return (
<>
{shorten(text)}
@ -68,7 +70,10 @@ export const getWithCopy = (text: string) => {
);
};
export const getNodeLink = (publicKey: string, alias?: string) => {
export const getNodeLink = (
publicKey: string | undefined | null,
alias?: string | undefined | null
) => {
if (!publicKey || (alias && alias === 'Node not found')) {
return 'Node not found';
}
@ -88,15 +93,23 @@ export const getNodeLink = (publicKey: string, alias?: string) => {
);
};
export const getDateDif = (date: string) => {
export const getDateDif = (date: string | null | undefined): string | null => {
if (!date) return null;
return formatDistanceToNowStrict(new Date(date));
};
export const getFormatDate = (date: string) => {
export const getFormatDate = (
date: string | null | undefined
): string | null => {
if (!date) return null;
return format(new Date(date), 'dd/MM/yyyy - HH:mm:ss');
};
export const getMessageDate = (date: string, formatType?: string): string => {
export const getMessageDate = (
date: string | null | undefined,
formatType?: string
): string => {
if (!date) return '';
let distance = formatDistanceToNowStrict(new Date(date));
if (distance.indexOf('minute') >= 0 || distance.indexOf('second') >= 0) {
@ -143,10 +156,10 @@ export const getStatusDot = (status: boolean, type: string) => {
export const renderLine = (
title: string,
content: number | string | JSX.Element,
content: number | string | JSX.Element | undefined | null,
key?: string | number,
deleteCallback?: () => void
) => {
): JSX.Element | null => {
if (!content) return null;
return (
<DetailLine key={key}>

View File

@ -37,9 +37,9 @@ const InputLine = styled(SingleLine)`
type InputWithDecoProps = {
title: string;
value?: string | number;
value?: string | number | null;
noInput?: boolean;
amount?: number;
amount?: number | null;
override?: string;
customAmount?: string;
color?: string;
@ -62,13 +62,14 @@ export const InputWithDeco: React.FC<InputWithDecoProps> = ({
inputCallback,
}) => {
const showAmount = !!amount || customAmount;
let correctValue = value;
let correctValue = value ? value : '';
if (inputType === 'number') {
correctValue = value > 0 ? value : undefined;
if (inputType === 'number' && value) {
correctValue = value && value > 0 ? value : '';
}
const props = noInput ? {} : { value: correctValue };
return (
<InputLine>
<InputTitleRow>

View File

@ -83,14 +83,18 @@ export const Link: React.FC<LinkProps> = ({
);
}
const linkProps = {
href: to,
...(basePath !== '' ? { as: `${basePath}${to}` } : {}),
};
if (to) {
const linkProps = {
href: to,
...(basePath !== '' ? { as: `${basePath}${to}` } : {}),
};
return (
<RouterLink {...linkProps}>
<CorrectLink {...props}>{children}</CorrectLink>
</RouterLink>
);
return (
<RouterLink {...linkProps}>
<CorrectLink {...props}>{children}</CorrectLink>
</RouterLink>
);
}
return null;
};

View File

@ -30,7 +30,7 @@ export const NodeCard = ({ account, accountId }: NodeCardProps) => {
triggerOnce: true,
});
const auth = getAuthObj(host, viewOnly, null, cert);
const auth = getAuthObj(host, viewOnly, undefined, cert);
const { data, loading, error } = useGetNodeInfoQuery({
ssr: false,
@ -64,7 +64,13 @@ export const NodeCard = ({ account, accountId }: NodeCardProps) => {
</>
);
}
if (loading || !data || !data.getNodeInfo || !data.getChannelBalance) {
if (
loading ||
!data?.getNodeInfo ||
!data?.getChannelBalance ||
!data?.getChainBalance ||
!data?.getPendingChainBalance
) {
return <ScaleLoader height={20} color={themeColors.blue3} />;
}

View File

@ -1,6 +1,7 @@
import React from 'react';
import { useAccountDispatch } from 'src/context/AccountContext';
import { GetNodeInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
import { NodeInfoType, ChannelBalanceType } from 'src/graphql/types';
import {
SubTitle,
SingleLine,
@ -13,7 +14,7 @@ import { ColorButton } from '../buttons/colorButton/ColorButton';
import { useStatusDispatch } from '../../context/StatusContext';
interface NodeInfoModalProps {
account: GetNodeInfoQuery;
account: GetNodeInfoQuery | null | undefined;
accountId: string;
}
@ -22,6 +23,10 @@ export const NodeInfoModal = ({ account, accountId }: NodeInfoModalProps) => {
const dispatchAccount = useAccountDispatch();
if (!account) {
return null;
}
const {
active_channels_count,
closed_channels_count,
@ -30,9 +35,12 @@ export const NodeInfoModal = ({ account, accountId }: NodeInfoModalProps) => {
is_synced_to_chain,
peers_count,
version,
} = account.getNodeInfo;
} = account.getNodeInfo as NodeInfoType;
const { confirmedBalance, pendingBalance } = account.getChannelBalance;
const {
confirmedBalance,
pendingBalance,
} = account.getChannelBalance as ChannelBalanceType;
const chainBalance = account.getChainBalance;
const pendingChainBalance = account.getPendingChainBalance;

View File

@ -14,14 +14,14 @@ export const Price = ({
breakNumber = false,
override,
}: {
amount: number | string;
amount: number | string | null | undefined;
breakNumber?: boolean;
override?: string;
}): JSX.Element => {
const { currency, displayValues } = useConfigState();
const { fiat, prices, dontShow } = usePriceState();
if (!displayValues) {
if (!displayValues || !amount) {
return <>-</>;
}
@ -48,7 +48,7 @@ export const Price = ({
};
interface GetPriceProps {
amount: number | string;
amount: number | string | null | undefined;
breakNumber?: boolean;
override?: string;
noUnit?: boolean;
@ -68,6 +68,7 @@ export const getPrice = (
override,
noUnit,
}: GetPriceProps): string => {
if (!amount) return '-';
const { prices, dontShow, fiat } = priceContext;
if (!displayValues) {

View File

@ -3,10 +3,11 @@ import { useRouter } from 'next/router';
import { toast } from 'react-toastify';
import { useAccountState } from 'src/context/AccountContext';
import { useGetNodeInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
import { useStatusDispatch } from '../../context/StatusContext';
import { NodeInfoType, ChannelBalanceType } from 'src/graphql/types';
import { useStatusDispatch, StatusState } from '../../context/StatusContext';
import { appendBasePath } from '../../utils/basePath';
export const StatusCheck = () => {
export const StatusCheck: React.FC = () => {
const dispatch = useStatusDispatch();
const { push } = useRouter();
@ -21,7 +22,7 @@ export const StatusCheck = () => {
useEffect(() => {
if (error) {
toast.error(`Unable to connect to ${account.name}`);
account && toast.error(`Unable to connect to ${account.name}`);
stopPolling();
dispatch({ type: 'disconnected' });
push(appendBasePath('/'));
@ -33,6 +34,7 @@ export const StatusCheck = () => {
getChannelBalance,
getNodeInfo,
} = data;
const {
alias,
color,
@ -42,14 +44,17 @@ export const StatusCheck = () => {
pending_channels_count,
closed_channels_count,
peers_count,
} = getNodeInfo;
const { confirmedBalance, pendingBalance } = getChannelBalance;
} = getNodeInfo as NodeInfoType;
const {
confirmedBalance,
pendingBalance,
} = getChannelBalance as ChannelBalanceType;
const versionNumber = version.split(' ');
const onlyVersion = versionNumber[0].split('-');
const numbers = onlyVersion[0].split('.');
const state = {
const state: StatusState = {
alias,
color,
syncedToChain: is_synced_to_chain,
@ -57,8 +62,8 @@ export const StatusCheck = () => {
mayorVersion: Number(numbers[0]),
minorVersion: Number(numbers[1]),
revision: Number(numbers[2]),
chainBalance: getChainBalance,
chainPending: getPendingChainBalance,
chainBalance: getChainBalance || 0,
chainPending: getPendingChainBalance || 0,
channelBalance: confirmedBalance,
channelPending: pendingBalance,
activeChannelCount: active_channels_count,

View File

@ -16,14 +16,6 @@ export const SERVER_ACCOUNT: SERVER_ACCOUNT_TYPE = 'server';
type HasAccountType = 'fetched' | 'false' | 'error';
export type AccountProps = {
name: string;
host: string;
admin: string;
viewOnly: string;
cert: string;
};
export type AuthType =
| {
type: ACCOUNT_TYPE;
@ -36,24 +28,34 @@ export type AuthType =
id: string;
};
export type AccountProps = {
name: string;
host: string;
admin: string;
viewOnly: string;
cert: string;
};
export type AccountType = {
type: ACCOUNT_TYPE;
id: string;
} & AccountProps;
export type CompleteAccount =
| AccountType
| {
type: SERVER_ACCOUNT_TYPE;
id: string;
name: string;
loggedIn?: boolean;
};
export type ServerAccountType = {
type: SERVER_ACCOUNT_TYPE;
id: string;
name: string;
loggedIn?: boolean;
};
export type CompleteAccount = AccountType | ServerAccountType;
export const defaultAuth = { type: SERVER_ACCOUNT, id: '' };
type State = {
initialized: boolean;
finishedFetch: boolean;
auth: AuthType | null;
auth: AuthType;
activeAccount: string | null;
session: string | null;
account: CompleteAccount | null;
@ -64,9 +66,9 @@ type State = {
type ActionType =
| {
type: 'initialize';
changeId: string;
changeId: string | null;
accountsToAdd: CompleteAccount[];
session: string;
session: string | null;
}
| {
type: 'changeAccount' | 'deleteAccount';
@ -106,7 +108,7 @@ const DispatchContext = React.createContext<Dispatch | undefined>(undefined);
const initialState: State = {
initialized: false,
finishedFetch: false,
auth: null,
auth: defaultAuth,
session: null,
activeAccount: null,
account: null,
@ -171,11 +173,11 @@ const stateReducer = (state: State, action: ActionType): State => {
...state,
account: null,
activeAccount: null,
auth: null,
auth: defaultAuth,
session: null,
};
case 'deleteAccount': {
if (!state.accounts || state?.accounts?.length <= 0) {
if (!state.accounts || state?.accounts?.length <= 0 || !state.account) {
return state;
}
const { accounts, id } = deleteAccountById(

View File

@ -45,7 +45,7 @@ const stateReducer = (state: State, action: ActionType): State => {
}
};
const BitcoinInfoProvider = ({ children }) => {
const BitcoinInfoProvider: React.FC = ({ children }) => {
const [state, dispatch] = useReducer(stateReducer, initialState);
return (

View File

@ -1,30 +1,14 @@
import React, { createContext, useContext, useReducer } from 'react';
import { MessagesType } from 'src/graphql/types';
type ChatProps = {
date?: string;
contentType?: string;
alias?: string;
message?: string;
id?: string;
sender?: string;
tokens?: number;
};
type SentChatProps = {
date?: string;
contentType?: string;
alias?: string;
message?: string;
id?: string;
sender?: string;
export interface SentChatProps extends MessagesType {
isSent?: boolean;
feePaid?: number;
tokens?: number;
};
}
type State = {
initialized: boolean;
chats: ChatProps[];
chats: MessagesType[];
sentChats: SentChatProps[];
lastChat: string;
sender: string;
@ -33,14 +17,14 @@ type State = {
type ActionType =
| {
type: 'initialized';
chats?: ChatProps[];
chats?: MessagesType[];
lastChat?: string;
sender?: string;
sentChats?: SentChatProps[];
}
| {
type: 'additional';
chats: ChatProps[];
chats: MessagesType[];
lastChat: string;
}
| {
@ -108,7 +92,7 @@ const stateReducer = (state: State, action: ActionType): State => {
}
};
const ChatProvider = ({ children }) => {
const ChatProvider: React.FC = ({ children }) => {
const [state, dispatch] = useReducer(stateReducer, initialState);
return (

View File

@ -115,11 +115,17 @@ const stateReducer = (state: State, action: ActionType): State => {
return newState;
}
case 'themeChange': {
Cookies.set('theme', action.theme, { expires: 365, sameSite: 'strict' });
return {
...state,
theme: action.theme,
};
if (settings.theme) {
Cookies.set('theme', settings.theme, {
expires: 365,
sameSite: 'strict',
});
return {
...state,
theme: settings.theme,
};
}
return state;
}
default:
return state;

View File

@ -57,7 +57,7 @@ const stateReducer = (state: State, action: ActionType): State => {
}
};
const PriceProvider = ({ children }) => {
const PriceProvider: React.FC = ({ children }) => {
const [state, dispatch] = useReducer(stateReducer, initialState);
useEffect(() => {

View File

@ -24,7 +24,7 @@ type Dispatch = (action: ActionType) => void;
export const StateContext = createContext<State | undefined>(undefined);
export const DispatchContext = createContext<Dispatch | undefined>(undefined);
const initialState = {
const initialState: State = {
inChannel: null,
outChannel: null,
};
@ -42,7 +42,7 @@ const stateReducer = (state: State, action: ActionType): State => {
}
};
const RebalanceProvider = ({ children }) => {
const RebalanceProvider: React.FC = ({ children }) => {
const [state, dispatch] = useReducer(stateReducer, initialState);
return (

View File

@ -4,7 +4,7 @@ type StateStatus = {
connected: boolean;
};
type State = {
export type StatusState = {
alias: string;
color: string;
syncedToChain: boolean;
@ -22,11 +22,11 @@ type State = {
peersCount: number;
};
type CompleteState = State & StateStatus;
type CompleteState = StatusState & StateStatus;
type ActionType = {
type: 'connected' | 'disconnected';
state?: State;
state?: StatusState;
};
type Dispatch = (action: ActionType) => void;
@ -53,10 +53,13 @@ const initialState = {
peersCount: 0,
};
const stateReducer = (state: State, action: ActionType): CompleteState => {
const stateReducer = (
state: StatusState,
action: ActionType
): CompleteState => {
switch (action.type) {
case 'connected':
return { ...action.state, connected: true } || initialState;
return { ...state, ...action.state, connected: true } || initialState;
case 'disconnected':
return initialState;
default:
@ -64,7 +67,7 @@ const stateReducer = (state: State, action: ActionType): CompleteState => {
}
};
const StatusProvider = ({ children }) => {
const StatusProvider: React.FC = ({ children }) => {
const [state, dispatch] = useReducer(stateReducer, initialState);
return (

View File

@ -4,10 +4,16 @@ import {
AccountProps,
CLIENT_ACCOUNT,
AuthType,
defaultAuth,
} from '../AccountContext';
export const getAccountById = (id: string, accounts: CompleteAccount[]) => {
const correctAccount: CompleteAccount | null = accounts.find(
export const getAccountById = (
id: string | null,
accounts: CompleteAccount[]
) => {
if (!id) return { account: null, id: null };
const correctAccount: CompleteAccount | null | undefined = accounts.find(
a => a.id === id
);
@ -48,10 +54,10 @@ export const addIdAndTypeToAccount = (
};
export const getAuthFromAccount = (
account: CompleteAccount,
session?: string
account: CompleteAccount | undefined | null,
session?: string | null
): AuthType => {
if (!account) return null;
if (!account) return defaultAuth;
if (account.type !== CLIENT_ACCOUNT) {
return {
type: account.type,
@ -60,10 +66,10 @@ export const getAuthFromAccount = (
}
const { host, viewOnly, cert } = account;
if (!host) {
return null;
return defaultAuth;
}
if (!viewOnly && !session) {
return null;
return defaultAuth;
}
return {
type: account.type,

View File

@ -22,23 +22,19 @@ export type DecodeRequestQuery = { __typename?: 'Query' } & {
| 'tokens'
> & {
destination_node: { __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<Types.NodeType, 'alias'>
>;
node: { __typename?: 'nodeType' } & Pick<Types.NodeType, 'alias'>;
};
routes?: Types.Maybe<
Array<
Types.Maybe<
Array<
Types.Maybe<
{ __typename?: 'RouteType' } & Pick<
Types.RouteType,
| 'base_fee_mtokens'
| 'channel'
| 'cltv_delta'
| 'fee_rate'
| 'public_key'
>
routes: Array<
Types.Maybe<
Array<
Types.Maybe<
{ __typename?: 'RouteType' } & Pick<
Types.RouteType,
| 'base_fee_mtokens'
| 'channel'
| 'cltv_delta'
| 'fee_rate'
| 'public_key'
>
>
>
@ -71,11 +67,9 @@ export type DecodeRequestQuery = { __typename?: 'Query' } & {
| 'timeout'
> & {
node: { __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias'
>
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias'
>;
};
}

View File

@ -16,11 +16,9 @@ export type ChannelFeesQuery = { __typename?: 'Query' } & {
'id' | 'partner_public_key'
> & {
partner_node_info: { __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'color'
>
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'color'
>;
};
channelInfo?: Types.Maybe<

View File

@ -9,65 +9,55 @@ export type GetChannelsQueryVariables = Types.Exact<{
}>;
export type GetChannelsQuery = { __typename?: 'Query' } & {
getChannels?: Types.Maybe<
Array<
Types.Maybe<
{ __typename?: 'channelType' } & Pick<
Types.ChannelType,
| 'capacity'
| 'commit_transaction_fee'
| 'commit_transaction_weight'
| 'id'
| 'is_active'
| 'is_closing'
| 'is_opening'
| 'is_partner_initiated'
| 'is_private'
| 'is_static_remote_key'
| 'local_balance'
| 'local_reserve'
| 'partner_public_key'
| 'received'
| 'remote_balance'
| 'remote_reserve'
| 'sent'
| 'time_offline'
| 'time_online'
| 'transaction_id'
| 'transaction_vout'
| 'unsettled_balance'
| 'channel_age'
> & {
partner_node_info?: Types.Maybe<
{ __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
| 'alias'
| 'capacity'
| 'channel_count'
| 'color'
| 'updated_at'
>
>;
}
getChannels: Array<
Types.Maybe<
{ __typename?: 'channelType' } & Pick<
Types.ChannelType,
| 'capacity'
| 'commit_transaction_fee'
| 'commit_transaction_weight'
| 'id'
| 'is_active'
| 'is_closing'
| 'is_opening'
| 'is_partner_initiated'
| 'is_private'
| 'is_static_remote_key'
| 'local_balance'
| 'local_reserve'
| 'partner_public_key'
| 'received'
| 'remote_balance'
| 'remote_reserve'
| 'sent'
| 'time_offline'
| 'time_online'
| 'transaction_id'
| 'transaction_vout'
| 'unsettled_balance'
| 'channel_age'
> & {
partner_node_info: { __typename?: 'Node' } & {
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'capacity' | 'channel_count' | 'color' | 'updated_at'
>;
partner_fee_info?: Types.Maybe<
{ __typename?: 'Channel' } & {
channel?: Types.Maybe<
{ __typename?: 'singleChannelType' } & {
partner_node_policies?: Types.Maybe<
{ __typename?: 'nodePolicyType' } & Pick<
Types.NodePolicyType,
'base_fee_mtokens' | 'fee_rate' | 'cltv_delta'
>
>;
}
>;
}
>;
}
>
};
partner_fee_info?: Types.Maybe<
{ __typename?: 'Channel' } & {
channel?: Types.Maybe<
{ __typename?: 'singleChannelType' } & {
partner_node_policies?: Types.Maybe<
{ __typename?: 'nodePolicyType' } & Pick<
Types.NodePolicyType,
'base_fee_mtokens' | 'fee_rate' | 'cltv_delta'
>
>;
}
>;
}
>;
}
>
>;
};

View File

@ -28,20 +28,12 @@ export type GetClosedChannelsQuery = { __typename?: 'Query' } & {
| 'transaction_id'
| 'transaction_vout'
> & {
partner_node_info?: Types.Maybe<
{ __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
| 'alias'
| 'capacity'
| 'channel_count'
| 'color'
| 'updated_at'
>
>;
}
>;
partner_node_info: { __typename?: 'Node' } & {
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'capacity' | 'channel_count' | 'color' | 'updated_at'
>;
};
}
>
>

View File

@ -46,11 +46,9 @@ export type GetFeeHealthQuery = { __typename?: 'Query' } & {
>;
partner?: Types.Maybe<
{ __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias'
>
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias'
>;
}
>;

View File

@ -32,11 +32,9 @@ export type GetForwardsQuery = { __typename?: 'Query' } & {
{ __typename?: 'nodePolicyType' } & {
node?: Types.Maybe<
{ __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'color'
>
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'color'
>;
}
>;
@ -54,11 +52,9 @@ export type GetForwardsQuery = { __typename?: 'Query' } & {
{ __typename?: 'nodePolicyType' } & {
node?: Types.Maybe<
{ __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'color'
>
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'color'
>;
}
>;

View File

@ -15,20 +15,18 @@ export type GetMessagesQuery = { __typename?: 'Query' } & {
Types.GetMessagesType,
'token'
> & {
messages?: Types.Maybe<
Array<
Types.Maybe<
{ __typename?: 'messagesType' } & Pick<
Types.MessagesType,
| 'date'
| 'contentType'
| 'alias'
| 'message'
| 'id'
| 'sender'
| 'verified'
| 'tokens'
>
messages: Array<
Types.Maybe<
{ __typename?: 'messagesType' } & Pick<
Types.MessagesType,
| 'date'
| 'contentType'
| 'alias'
| 'message'
| 'id'
| 'sender'
| 'verified'
| 'tokens'
>
>
>;

View File

@ -11,11 +11,9 @@ export type GetNodeQueryVariables = Types.Exact<{
export type GetNodeQuery = { __typename?: 'Query' } & {
getNode: { __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'capacity' | 'channel_count' | 'color' | 'updated_at'
>
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'capacity' | 'channel_count' | 'color' | 'updated_at'
>;
};
};

View File

@ -23,20 +23,12 @@ export type GetPeersQuery = { __typename?: 'Query' } & {
| 'tokens_received'
| 'tokens_sent'
> & {
partner_node_info?: Types.Maybe<
{ __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
| 'alias'
| 'capacity'
| 'channel_count'
| 'color'
| 'updated_at'
>
>;
}
>;
partner_node_info: { __typename?: 'Node' } & {
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'capacity' | 'channel_count' | 'color' | 'updated_at'
>;
};
}
>
>

View File

@ -28,20 +28,12 @@ export type GetPendingChannelsQuery = { __typename?: 'Query' } & {
| 'transaction_id'
| 'transaction_vout'
> & {
partner_node_info?: Types.Maybe<
{ __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
| 'alias'
| 'capacity'
| 'channel_count'
| 'color'
| 'updated_at'
>
>;
}
>;
partner_node_info: { __typename?: 'Node' } & {
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'capacity' | 'channel_count' | 'color' | 'updated_at'
>;
};
}
>
>

View File

@ -57,27 +57,19 @@ export type GetResumeQuery = { __typename?: 'Query' } & {
> & {
destination_node?: Types.Maybe<
{ __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias'
>
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias'
>;
}
>;
hops?: Types.Maybe<
Array<
Types.Maybe<
{ __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'public_key'
>
>;
}
>
>
hops: Array<
{ __typename?: 'Node' } & {
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias' | 'public_key'
>;
}
>;
})
>

View File

@ -27,11 +27,9 @@ export type GetTimeHealthQuery = { __typename?: 'Query' } & {
> & {
partner?: Types.Maybe<
{ __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias'
>
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias'
>;
}
>;

View File

@ -19,11 +19,9 @@ export type GetVolumeHealthQuery = { __typename?: 'Query' } & {
> & {
partner?: Types.Maybe<
{ __typename?: 'Node' } & {
node?: Types.Maybe<
{ __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias'
>
node: { __typename?: 'nodeType' } & Pick<
Types.NodeType,
'alias'
>;
}
>;

Some files were not shown because too many files have changed in this diff Show More