mirror of
https://github.com/apotdevin/thunderhub.git
synced 2024-11-19 09:50:03 +01:00
chore: π§ remove client (#111)
* chore: π§ remove client * chore: π§ change cookie name * chore: π§ remove auth param * chore: π§ remove auth components * chore: π§ add getaccount query * fix: π tests * chore: π§ get account * chore: π§ status check * chore: π§ remove log * chore: π§ update apollo client * refactor: β»οΈ server side props * chore: π§ ssr queries * chore: π§ more ssr queries * chore: π§ type check * chore: π§ increase nodeinfo limit Co-authored-by: apotdevin <apotdevincab@gmail.com>
This commit is contained in:
parent
bc4415cde7
commit
581185e6b0
@ -1,36 +1,29 @@
|
||||
/* eslint @typescript-eslint/no-var-requires: 0 */
|
||||
import * as React from 'react';
|
||||
import Head from 'next/head';
|
||||
import { ApolloProvider } from '@apollo/react-hooks';
|
||||
import { ApolloClient } from 'apollo-client';
|
||||
import { IncomingMessage, ServerResponse } from 'http';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
ApolloClient,
|
||||
InMemoryCache,
|
||||
IntrospectionFragmentMatcher,
|
||||
NormalizedCacheObject,
|
||||
} from 'apollo-cache-inmemory';
|
||||
} from '@apollo/client';
|
||||
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: ReturnType<typeof createApolloClient> | null = null;
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { apiUrl: uri } = publicRuntimeConfig;
|
||||
|
||||
type Context = SchemaLink.ResolverContextFunction | Record<string, any>;
|
||||
let apolloClient: ReturnType<typeof createApolloClient> | null = null;
|
||||
|
||||
function createIsomorphLink(ctx: Context) {
|
||||
function createIsomorphLink(req?: IncomingMessage, res?: ServerResponse) {
|
||||
if (typeof window === 'undefined') {
|
||||
const schema = require('server/schema');
|
||||
return new SchemaLink({ schema, context: ctx });
|
||||
const { SchemaLink } = require('@apollo/client/link/schema');
|
||||
const { schema } = require('server/schema');
|
||||
const { getContext } = require('server/schema/context');
|
||||
return new SchemaLink({
|
||||
schema,
|
||||
context: req && res ? getContext(req, res) : {},
|
||||
});
|
||||
} else {
|
||||
const { HttpLink } = require('apollo-link-http');
|
||||
|
||||
const { HttpLink } = require('@apollo/client/link/http');
|
||||
return new HttpLink({
|
||||
uri,
|
||||
credentials: 'same-origin',
|
||||
@ -38,147 +31,38 @@ function createIsomorphLink(ctx: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and configures the ApolloClient
|
||||
*/
|
||||
function createApolloClient(
|
||||
ctx: Context = {},
|
||||
initialState: NormalizedCacheObject = {}
|
||||
) {
|
||||
const ssrMode = typeof window === 'undefined';
|
||||
const cache = new InMemoryCache({ fragmentMatcher }).restore(initialState);
|
||||
|
||||
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
|
||||
function createApolloClient(req?: IncomingMessage, res?: ServerResponse) {
|
||||
return new ApolloClient({
|
||||
ssrMode,
|
||||
link: createIsomorphLink(ctx),
|
||||
cache,
|
||||
credentials: 'same-origin',
|
||||
ssrMode: typeof window === 'undefined',
|
||||
link: createIsomorphLink(req, res),
|
||||
cache: new InMemoryCache({
|
||||
possibleTypes: { Transaction: ['InvoiceType', 'PaymentType'] },
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Always creates a new apollo client on the server
|
||||
* Creates or reuses apollo client in the browser.
|
||||
*/
|
||||
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') {
|
||||
return createApolloClient(ctx, initialState);
|
||||
}
|
||||
|
||||
// Reuse client on the client-side
|
||||
if (!globalApolloClient) {
|
||||
globalApolloClient = createApolloClient(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.
|
||||
*/
|
||||
export function withApollo(
|
||||
PageComponent: NextPage,
|
||||
{ ssr }: WithApolloOptions = { ssr: true }
|
||||
export function initializeApollo(
|
||||
initialState: NormalizedCacheObject | null = null,
|
||||
req?: IncomingMessage,
|
||||
res?: ServerResponse
|
||||
) {
|
||||
const WithApollo: NextPage<WithApolloProps> = ({
|
||||
apolloClient,
|
||||
apolloState,
|
||||
...pageProps
|
||||
}) => {
|
||||
const client = apolloClient || initApolloClient(undefined, apolloState);
|
||||
return (
|
||||
<ApolloProvider client={client}>
|
||||
<PageComponent {...pageProps} />
|
||||
</ApolloProvider>
|
||||
);
|
||||
};
|
||||
const _apolloClient = apolloClient ?? createApolloClient(req, res);
|
||||
|
||||
// Set the correct displayName in development
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const displayName =
|
||||
PageComponent.displayName || PageComponent.name || 'Component';
|
||||
|
||||
if (displayName === 'App') {
|
||||
console.warn('This withApollo HOC only works with PageComponents.');
|
||||
}
|
||||
|
||||
WithApollo.displayName = `withApollo(${displayName})`;
|
||||
// If your page has Next.js data fetching methods that use Apollo Client, the initial state
|
||||
// get hydrated here
|
||||
if (initialState) {
|
||||
_apolloClient.cache.restore(initialState);
|
||||
}
|
||||
// For SSG and SSR always create a new Apollo Client
|
||||
if (typeof window === 'undefined') return _apolloClient;
|
||||
// Create the Apollo Client once in the client
|
||||
if (!apolloClient) apolloClient = _apolloClient;
|
||||
|
||||
if (ssr || PageComponent.getInitialProps) {
|
||||
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 = initApolloClient({
|
||||
res: ctx.res,
|
||||
req: ctx.req,
|
||||
});
|
||||
(ctx as any).apolloClient = apolloClient;
|
||||
|
||||
// Run wrapped getInitialProps methods
|
||||
let pageProps = {};
|
||||
if (PageComponent.getInitialProps) {
|
||||
pageProps = await PageComponent.getInitialProps(ctx);
|
||||
}
|
||||
|
||||
// Only on the server:
|
||||
if (typeof window === 'undefined') {
|
||||
// When redirecting, the response is finished.
|
||||
// No point in continuing to render
|
||||
if (ctx.res && ctx.res.finished) {
|
||||
return pageProps as WithApolloProps;
|
||||
}
|
||||
|
||||
// Only if ssr is enabled
|
||||
if (ssr) {
|
||||
try {
|
||||
// Run all GraphQL queries
|
||||
const { getDataFromTree } = await import('@apollo/react-ssr');
|
||||
await getDataFromTree(
|
||||
<AppTree
|
||||
pageProps={{
|
||||
...pageProps,
|
||||
apolloClient,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} catch (error) {
|
||||
// Prevent Apollo Client GraphQL errors from crashing SSR.
|
||||
// Handle them in components via the data.error prop:
|
||||
// https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
|
||||
console.error('Error while running `getDataFromTree`', error);
|
||||
}
|
||||
|
||||
// getDataFromTree does not call componentWillUnmount
|
||||
// head side effect therefore need to be cleared manually
|
||||
Head.rewind();
|
||||
}
|
||||
}
|
||||
|
||||
// Extract query data from the Apollo store
|
||||
const apolloState = apolloClient.cache.extract();
|
||||
|
||||
return {
|
||||
...pageProps,
|
||||
apolloState,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
return WithApollo;
|
||||
return _apolloClient;
|
||||
}
|
||||
|
||||
export function useApollo(initialState: NormalizedCacheObject | null) {
|
||||
const store = useMemo(() => initializeApollo(initialState), [initialState]);
|
||||
return store;
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ module.exports = withBundleAnalyzer({
|
||||
fetchFees: process.env.FETCH_FEES === 'false' ? false : true,
|
||||
hodlhodl: process.env.HODL_HODL === 'false' ? false : true,
|
||||
disableLinks: process.env.DISABLE_LINKS === 'true' ? true : false,
|
||||
noClient: process.env.NO_CLIENT_ACCOUNTS === 'true' ? true : false,
|
||||
noVersionCheck: process.env.NO_VERSION_CHECK === 'true' ? true : false,
|
||||
},
|
||||
});
|
||||
|
184
package-lock.json
generated
184
package-lock.json
generated
@ -143,6 +143,56 @@
|
||||
"cross-fetch": "3.0.4"
|
||||
}
|
||||
},
|
||||
"@apollo/client": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.1.1.tgz",
|
||||
"integrity": "sha512-c5DxrU81p0B5BsyBXm+5uPJqLCX2epnBsd87PXfRwzDLbp/NiqnWp6a6c5vT5EV2LwJuCq1movmKthoy0gFb0w==",
|
||||
"requires": {
|
||||
"@types/zen-observable": "^0.8.0",
|
||||
"@wry/context": "^0.5.2",
|
||||
"@wry/equality": "^0.2.0",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"graphql-tag": "^2.11.0",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"optimism": "^0.12.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"symbol-observable": "^1.2.0",
|
||||
"ts-invariant": "^0.4.4",
|
||||
"tslib": "^1.10.0",
|
||||
"zen-observable": "^0.8.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wry/context": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.2.tgz",
|
||||
"integrity": "sha512-B/JLuRZ/vbEKHRUiGj6xiMojST1kHhu4WcreLfNN7q9DqQFrb97cWgf/kiYsPSUCAMVN0HzfFc8XjJdzgZzfjw==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@wry/equality": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.2.0.tgz",
|
||||
"integrity": "sha512-Y4d+WH6hs+KZJUC8YKLYGarjGekBrhslDbf/R20oV+AakHPINSitHfDRQz3EGcEWc1luXYNUvMhawWtZVWNGvQ==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"graphql-tag": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.11.0.tgz",
|
||||
"integrity": "sha512-VmsD5pJqWJnQZMUeRwrDhfgoyqcfwEkvtpANqcoUG8/tOLkwNgU9mzub/Mc78OJMhHjx7gfAMTxzdG43VGg3bA=="
|
||||
},
|
||||
"optimism": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/optimism/-/optimism-0.12.1.tgz",
|
||||
"integrity": "sha512-t8I7HM1dw0SECitBYAqFOVHoBAHEQBTeKjIL9y9ImHzAVkdyPK4ifTgM4VJRDtTUY4r/u5Eqxs4XcGPHaoPkeQ==",
|
||||
"requires": {
|
||||
"@wry/context": "^0.5.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@apollo/protobufjs": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.0.4.tgz",
|
||||
@ -170,36 +220,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@apollo/react-common": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@apollo/react-common/-/react-common-3.1.4.tgz",
|
||||
"integrity": "sha512-X5Kyro73bthWSCBJUC5XYQqMnG0dLWuDZmVkzog9dynovhfiVCV4kPSdgSIkqnb++cwCzOVuQ4rDKVwo2XRzQA==",
|
||||
"requires": {
|
||||
"ts-invariant": "^0.4.4",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"@apollo/react-hooks": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@apollo/react-hooks/-/react-hooks-3.1.5.tgz",
|
||||
"integrity": "sha512-y0CJ393DLxIIkksRup4nt+vSjxalbZBXnnXxYbviq/woj+zKa431zy0yT4LqyRKpFy9ahMIwxBnBwfwIoupqLQ==",
|
||||
"requires": {
|
||||
"@apollo/react-common": "^3.1.4",
|
||||
"@wry/equality": "^0.1.9",
|
||||
"ts-invariant": "^0.4.4",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"@apollo/react-ssr": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@apollo/react-ssr/-/react-ssr-3.1.5.tgz",
|
||||
"integrity": "sha512-wuLPkKlctNn3u8EU8rlECyktpOUCeekFfb0KhIKknpGY6Lza2Qu0bThx7D9MIbVEzhKadNNrzLcpk0Y8/5UuWg==",
|
||||
"requires": {
|
||||
"@apollo/react-common": "^3.1.4",
|
||||
"@apollo/react-hooks": "^3.1.5",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"@apollographql/apollo-tools": {
|
||||
"version": "0.4.8",
|
||||
"resolved": "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.4.8.tgz",
|
||||
@ -4834,12 +4854,6 @@
|
||||
"@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",
|
||||
@ -5303,12 +5317,6 @@
|
||||
"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",
|
||||
@ -5583,15 +5591,6 @@
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
},
|
||||
"@wry/context": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@wry/context/-/context-0.4.4.tgz",
|
||||
"integrity": "sha512-LrKVLove/zw6h2Md/KZyWxIkFM6AoyKp71OqpH9Hiip1csjPVoD3tPxlbQUNxEnHENks3UGgNpSBCAfq9KWuag==",
|
||||
"requires": {
|
||||
"@types/node": ">=6",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@wry/equality": {
|
||||
"version": "0.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.1.11.tgz",
|
||||
@ -5897,15 +5896,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"apollo-cache": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.5.tgz",
|
||||
"integrity": "sha512-1XoDy8kJnyWY/i/+gLTEbYLnoiVtS8y7ikBr/IfmML4Qb+CM7dEEbIUOjnY716WqmZ/UpXIxTfJsY7rMcqiCXA==",
|
||||
"requires": {
|
||||
"apollo-utilities": "^1.3.4",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"apollo-cache-control": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.11.1.tgz",
|
||||
@ -5916,33 +5906,6 @@
|
||||
"apollo-server-plugin-base": "^0.9.1"
|
||||
}
|
||||
},
|
||||
"apollo-cache-inmemory": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.6.tgz",
|
||||
"integrity": "sha512-L8pToTW/+Xru2FFAhkZ1OA9q4V4nuvfoPecBM34DecAugUZEBhI2Hmpgnzq2hTKZ60LAMrlqiASm0aqAY6F8/A==",
|
||||
"requires": {
|
||||
"apollo-cache": "^1.3.5",
|
||||
"apollo-utilities": "^1.3.4",
|
||||
"optimism": "^0.10.0",
|
||||
"ts-invariant": "^0.4.0",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"apollo-client": {
|
||||
"version": "2.6.10",
|
||||
"resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.6.10.tgz",
|
||||
"integrity": "sha512-jiPlMTN6/5CjZpJOkGeUV0mb4zxx33uXWdj/xQCfAMkuNAC3HN7CvYDyMHHEzmcQ5GV12LszWoQ/VlxET24CtA==",
|
||||
"requires": {
|
||||
"@types/zen-observable": "^0.8.0",
|
||||
"apollo-cache": "1.3.5",
|
||||
"apollo-link": "^1.0.0",
|
||||
"apollo-utilities": "1.3.4",
|
||||
"symbol-observable": "^1.0.2",
|
||||
"ts-invariant": "^0.4.0",
|
||||
"tslib": "^1.10.0",
|
||||
"zen-observable": "^0.8.0"
|
||||
}
|
||||
},
|
||||
"apollo-datasource": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-0.7.2.tgz",
|
||||
@ -6010,35 +5973,6 @@
|
||||
"zen-observable-ts": "^0.8.21"
|
||||
}
|
||||
},
|
||||
"apollo-link-http": {
|
||||
"version": "1.5.17",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.17.tgz",
|
||||
"integrity": "sha512-uWcqAotbwDEU/9+Dm9e1/clO7hTB2kQ/94JYcGouBVLjoKmTeJTUPQKcJGpPwUjZcSqgYicbFqQSoJIW0yrFvg==",
|
||||
"requires": {
|
||||
"apollo-link": "^1.2.14",
|
||||
"apollo-link-http-common": "^0.2.16",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"apollo-link-http-common": {
|
||||
"version": "0.2.16",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz",
|
||||
"integrity": "sha512-2tIhOIrnaF4UbQHf7kjeQA/EmSorB7+HyJIIrUjJOKBgnXwuexi8aMecRlqTIDWcyVXCeqLhUnztMa6bOH/jTg==",
|
||||
"requires": {
|
||||
"apollo-link": "^1.2.14",
|
||||
"ts-invariant": "^0.4.0",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"apollo-link-schema": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/apollo-link-schema/-/apollo-link-schema-1.2.5.tgz",
|
||||
"integrity": "sha512-7XUS8fOsObJt9rzp8CUuZ/a9TNUBoChWwEDmdVmYxTlzgGcyUXxkLXkMS9CHUb0cx04jiiWjWQc41C4iakSmzA==",
|
||||
"requires": {
|
||||
"apollo-link": "^1.2.14",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"apollo-server": {
|
||||
"version": "2.15.1",
|
||||
"resolved": "https://registry.npmjs.org/apollo-server/-/apollo-server-2.15.1.tgz",
|
||||
@ -7603,11 +7537,6 @@
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
|
||||
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
|
||||
},
|
||||
"base64url": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
|
||||
"integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A=="
|
||||
},
|
||||
"basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
@ -20530,14 +20459,6 @@
|
||||
"integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==",
|
||||
"dev": true
|
||||
},
|
||||
"optimism": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/optimism/-/optimism-0.10.3.tgz",
|
||||
"integrity": "sha512-9A5pqGoQk49H6Vhjb9kPgAeeECfUDF6aIICbMDL23kDLStBn1MWk3YvcZ4xWF9CsSf6XEgvRLkXy4xof/56vVw==",
|
||||
"requires": {
|
||||
"@wry/context": "^0.4.0"
|
||||
}
|
||||
},
|
||||
"optionator": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||
@ -26674,11 +26595,6 @@
|
||||
"tslib": "^1.9.3",
|
||||
"zen-observable": "^0.8.0"
|
||||
}
|
||||
},
|
||||
"zxcvbn": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz",
|
||||
"integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
package.json
30
package.json
@ -5,22 +5,22 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"bs": "yarn build && yarn start",
|
||||
"dev": "cross-env NODE_OPTIONS='--insecure-http-parser' next",
|
||||
"dev": "next",
|
||||
"dev:compatible": "next",
|
||||
"build": "next build",
|
||||
"start": "cross-env NODE_OPTIONS='--insecure-http-parser' next start",
|
||||
"start:two": "cross-env NODE_OPTIONS='--insecure-http-parser' next start -p 3001",
|
||||
"start": "next start",
|
||||
"start:two": "next start -p 3001",
|
||||
"start:compatible": "next start",
|
||||
"start:compatible:two": "next start -p 3001",
|
||||
"start:cookie": "sh ./scripts/initCookie.sh",
|
||||
"lint": "eslint */**/*.{js,ts,tsx} --fix",
|
||||
"prettier": "prettier --write **/*.{ts,tsx,js,css,html}",
|
||||
"lint": "eslint . --ext ts --ext tsx --ext js",
|
||||
"format": "prettier --write \"**/*.{js,ts,tsx}\"",
|
||||
"release": "standard-version --sign",
|
||||
"release:push": "standard-version --sign && git push --follow-tags origin master",
|
||||
"release:test": "standard-version --sign --dry-run",
|
||||
"release:minor": "standard-version --sign --release-as minor && git push --follow-tags origin master",
|
||||
"analyze": "cross-env ANALYZE=true next build",
|
||||
"generate": "graphql-codegen --config codegen.yml && yarn lint",
|
||||
"generate": "graphql-codegen --config codegen.yml && yarn format",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
@ -37,25 +37,15 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apollo/react-common": "^3.1.4",
|
||||
"@apollo/react-hooks": "^3.1.5",
|
||||
"@apollo/react-ssr": "^3.1.5",
|
||||
"apollo-cache-inmemory": "^1.6.6",
|
||||
"apollo-client": "^2.6.10",
|
||||
"apollo-link-http": "^1.5.17",
|
||||
"apollo-link-schema": "^1.2.5",
|
||||
"@apollo/client": "^3.1.1",
|
||||
"apollo-server-micro": "^2.15.1",
|
||||
"apollo-utilities": "^1.3.4",
|
||||
"balanceofsatoshis": "^5.40.2",
|
||||
"base64url": "^3.0.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"cookie": "^0.4.1",
|
||||
"crypto-js": "^4.0.0",
|
||||
"date-fns": "^2.14.0",
|
||||
"graphql": "^15.3.0",
|
||||
"graphql-iso-date": "^3.6.1",
|
||||
"graphql-rate-limit": "^2.0.1",
|
||||
"graphql-tag": "^2.10.4",
|
||||
"intersection-observer": "^0.11.0",
|
||||
"js-cookie": "^2.2.1",
|
||||
"js-yaml": "^3.14.0",
|
||||
@ -84,8 +74,7 @@
|
||||
"underscore": "^1.10.2",
|
||||
"uuid": "^8.2.0",
|
||||
"victory": "^35.0.3",
|
||||
"winston": "^3.3.3",
|
||||
"zxcvbn": "^4.4.2"
|
||||
"winston": "^3.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.4",
|
||||
@ -104,7 +93,6 @@
|
||||
"@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",
|
||||
@ -123,7 +111,6 @@
|
||||
"@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",
|
||||
@ -143,7 +130,6 @@
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"eslint-plugin-react": "^7.20.3",
|
||||
"eslint-plugin-react-hooks": "^4.0.7",
|
||||
"fast-diff": "^1.2.0",
|
||||
"husky": "^4.2.5",
|
||||
"jest": "^26.1.0",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
|
@ -4,8 +4,9 @@ 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 { ApolloProvider } from '@apollo/client';
|
||||
import { useApollo } from 'config/client';
|
||||
import { ContextProvider } from '../src/context/ContextProvider';
|
||||
import { useConfigState, ConfigProvider } from '../src/context/ConfigContext';
|
||||
import { GlobalStyles } from '../src/styles/GlobalStyle';
|
||||
@ -13,7 +14,6 @@ import { Header } from '../src/layouts/header/Header';
|
||||
import { Footer } from '../src/layouts/footer/Footer';
|
||||
import 'react-toastify/dist/ReactToastify.min.css';
|
||||
import { PageWrapper, HeaderBodyWrapper } from '../src/layouts/Layout.styled';
|
||||
import { parseCookies } from '../src/utils/cookies';
|
||||
import 'react-circular-progressbar/dist/styles.css';
|
||||
|
||||
const Wrapper: React.FC = ({ children }) => {
|
||||
@ -38,46 +38,21 @@ const Wrapper: React.FC = ({ children }) => {
|
||||
);
|
||||
};
|
||||
|
||||
type InitialProps = { initialConfig: string };
|
||||
type MyAppProps = InitialProps & AppProps;
|
||||
|
||||
const App: NextPage<MyAppProps, InitialProps> = ({
|
||||
Component,
|
||||
pageProps,
|
||||
initialConfig,
|
||||
}) => (
|
||||
<>
|
||||
<Head>
|
||||
<title>ThunderHub - Lightning Node Manager</title>
|
||||
</Head>
|
||||
<ConfigProvider initialConfig={initialConfig}>
|
||||
<ContextProvider>
|
||||
<Wrapper>
|
||||
<Component {...pageProps} />
|
||||
</Wrapper>
|
||||
</ContextProvider>
|
||||
</ConfigProvider>
|
||||
<StyledToastContainer />
|
||||
</>
|
||||
);
|
||||
|
||||
/*
|
||||
* 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' };
|
||||
}
|
||||
try {
|
||||
const initialConfig = cookies.theme || 'dark';
|
||||
return { initialConfig };
|
||||
} catch (error) {
|
||||
return { initialConfig: 'dark' };
|
||||
}
|
||||
};
|
||||
|
||||
export default App;
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
const apolloClient = useApollo(pageProps.initialApolloState);
|
||||
return (
|
||||
<ApolloProvider client={apolloClient}>
|
||||
<Head>
|
||||
<title>ThunderHub - Lightning Node Manager</title>
|
||||
</Head>
|
||||
<ConfigProvider initialConfig={pageProps.initialConfig}>
|
||||
<ContextProvider>
|
||||
<Wrapper>
|
||||
<Component {...pageProps} />
|
||||
</Wrapper>
|
||||
</ContextProvider>
|
||||
</ConfigProvider>
|
||||
<StyledToastContainer />
|
||||
</ApolloProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,98 +1,24 @@
|
||||
import crypto from 'crypto';
|
||||
import { ApolloServer } from 'apollo-server-micro';
|
||||
import { getIp } from 'server/helpers/helpers';
|
||||
import getConfig from 'next/config';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { logger } from 'server/helpers/logger';
|
||||
import {
|
||||
readMacaroons,
|
||||
readFile,
|
||||
readCookie,
|
||||
getAccounts,
|
||||
} from 'server/helpers/fileHelpers';
|
||||
import { ContextType, SSOType } from 'server/types/apiTypes';
|
||||
import cookie from 'cookie';
|
||||
import schema from 'server/schema';
|
||||
import { readCookie } from 'server/helpers/fileHelpers';
|
||||
import { schema } from 'server/schema';
|
||||
import { getContext } from 'server/schema/context';
|
||||
|
||||
const { publicRuntimeConfig, serverRuntimeConfig } = getConfig();
|
||||
const { apiBaseUrl, nodeEnv } = publicRuntimeConfig;
|
||||
const {
|
||||
cookiePath,
|
||||
macaroonPath,
|
||||
lnCertPath,
|
||||
lnServerUrl,
|
||||
accountConfigPath,
|
||||
} = serverRuntimeConfig;
|
||||
const { cookiePath } = serverRuntimeConfig;
|
||||
|
||||
const secret =
|
||||
export const secret =
|
||||
nodeEnv === 'development'
|
||||
? '123456789'
|
||||
: crypto.randomBytes(64).toString('hex');
|
||||
|
||||
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({
|
||||
schema,
|
||||
context: ({ req, res }) => {
|
||||
const ip = getIp(req);
|
||||
|
||||
const { AccountAuth, SSOAuth } = cookie.parse(req.headers.cookie ?? '');
|
||||
|
||||
let ssoVerified = false;
|
||||
if (SSOAuth) {
|
||||
logger.silly('SSOAuth cookie found in request');
|
||||
if (nodeEnv === 'development') {
|
||||
ssoVerified = true;
|
||||
}
|
||||
try {
|
||||
jwt.verify(SSOAuth, secret);
|
||||
ssoVerified = true;
|
||||
} catch (error) {
|
||||
logger.silly('SSO authentication cookie failed');
|
||||
}
|
||||
}
|
||||
|
||||
let account = '';
|
||||
if (AccountAuth) {
|
||||
logger.silly('AccountAuth cookie found in request');
|
||||
try {
|
||||
const cookieAccount = jwt.verify(AccountAuth, secret);
|
||||
if (typeof cookieAccount === 'object') {
|
||||
account = (cookieAccount as { id?: string })['id'] ?? '';
|
||||
} else {
|
||||
account = cookieAccount;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.silly('Account authentication cookie failed');
|
||||
}
|
||||
}
|
||||
|
||||
const context: ContextType = {
|
||||
ip,
|
||||
secret,
|
||||
ssoVerified,
|
||||
account,
|
||||
sso: ssoVerified ? ssoAccount : null,
|
||||
accounts: accountConfig,
|
||||
res,
|
||||
};
|
||||
|
||||
return context;
|
||||
},
|
||||
context: ({ req, res }) => getContext(req, res),
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { withApollo } from 'config/client';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { GET_CHAIN_TRANSACTIONS } from 'src/graphql/queries/getChainTransactions';
|
||||
import { GET_UTXOS } from 'src/graphql/queries/getUtxos';
|
||||
import { ChainTransactions } from '../src/views/chain/transactions/ChainTransactions';
|
||||
import { ChainUtxos } from '../src/views/chain/utxos/ChainUtxos';
|
||||
|
||||
@ -20,4 +22,8 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [GET_CHAIN_TRANSACTIONS, GET_UTXOS]);
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { useGetChannelAmountInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
|
||||
import styled from 'styled-components';
|
||||
import { Settings } from 'react-feather';
|
||||
import { IconCursor } from 'src/views/channels/channels/Channel.style';
|
||||
import { ChannelManage } from 'src/views/channels/channels/ChannelManage';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { withApollo } from 'config/client';
|
||||
import { NextPageContext } from 'next';
|
||||
import { GET_CHANNELS } from 'src/graphql/queries/getChannels';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { Channels } from '../src/views/channels/channels/Channels';
|
||||
import { PendingChannels } from '../src/views/channels/pendingChannels/PendingChannels';
|
||||
import { ClosedChannels } from '../src/views/channels/closedChannels/ClosedChannels';
|
||||
@ -41,12 +42,7 @@ const ChannelView = () => {
|
||||
closed: 0,
|
||||
});
|
||||
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const { data } = useGetChannelAmountInfoQuery({
|
||||
skip: !auth,
|
||||
variables: { auth },
|
||||
});
|
||||
const { data } = useGetChannelAmountInfoQuery();
|
||||
|
||||
useEffect(() => {
|
||||
if (data && data.getNodeInfo) {
|
||||
@ -119,4 +115,8 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [GET_CHANNELS]);
|
||||
}
|
||||
|
@ -2,9 +2,11 @@ import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Users } from 'react-feather';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { withApollo } from 'config/client';
|
||||
import { ChatInit } from 'src/components/chat/ChatInit';
|
||||
import { ChatFetcher } from 'src/components/chat/ChatFetcher';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { GET_MESSAGES } from 'src/graphql/queries/getMessages';
|
||||
import { useChatState } from '../src/context/ChatContext';
|
||||
import { separateBySender, getSenders } from '../src/utils/chat';
|
||||
import {
|
||||
@ -126,4 +128,8 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [GET_MESSAGES]);
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ChevronRight, ChevronUp, ChevronDown } from 'react-feather';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { useChannelFeesQuery } from 'src/graphql/queries/__generated__/getChannelFees.generated';
|
||||
import { useUpdateFeesMutation } from 'src/graphql/mutations/__generated__/updateFees.generated';
|
||||
import { InputWithDeco } from 'src/components/input/InputWithDeco';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { withApollo } from 'config/client';
|
||||
import styled from 'styled-components';
|
||||
import { useStatusState } from 'src/context/StatusContext';
|
||||
import { ChannelFeeType } from 'src/graphql/types';
|
||||
import { ColorButton } from 'src/components/buttons/colorButton/ColorButton';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { CHANNEL_FEES } from 'src/graphql/queries/getChannelFees';
|
||||
import {
|
||||
Card,
|
||||
CardWithTitle,
|
||||
@ -22,8 +24,6 @@ import {
|
||||
import { getErrorContent } from '../src/utils/error';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
import { FeeCard } from '../src/views/fees/FeeCard';
|
||||
import { SecureButton } from '../src/components/buttons/secureButton/SecureButton';
|
||||
import { AdminSwitch } from '../src/components/adminSwitch/AdminSwitch';
|
||||
|
||||
const WithPointer = styled.div`
|
||||
cursor: pointer;
|
||||
@ -43,11 +43,7 @@ const FeesView = () => {
|
||||
const [max, setMax] = useState(0);
|
||||
const [min, setMin] = useState(0);
|
||||
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const { loading, data } = useChannelFeesQuery({
|
||||
skip: !auth,
|
||||
variables: { auth },
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
@ -73,95 +69,101 @@ const FeesView = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<AdminSwitch>
|
||||
<Card>
|
||||
<WithPointer>
|
||||
<SingleLine onClick={() => setIsEdit(prev => !prev)}>
|
||||
<Sub4Title>Update All Channel Details</Sub4Title>
|
||||
{isEdit ? <ChevronUp /> : <ChevronDown />}
|
||||
</SingleLine>
|
||||
</WithPointer>
|
||||
{isEdit && (
|
||||
<>
|
||||
<Separation />
|
||||
<Card>
|
||||
<WithPointer>
|
||||
<SingleLine onClick={() => setIsEdit(prev => !prev)}>
|
||||
<Sub4Title>Update All Channel Details</Sub4Title>
|
||||
{isEdit ? <ChevronUp /> : <ChevronDown />}
|
||||
</SingleLine>
|
||||
</WithPointer>
|
||||
{isEdit && (
|
||||
<>
|
||||
<Separation />
|
||||
<InputWithDeco
|
||||
title={'BaseFee'}
|
||||
value={baseFee}
|
||||
placeholder={'sats'}
|
||||
amount={baseFee}
|
||||
override={'sat'}
|
||||
inputType={'number'}
|
||||
inputCallback={value => setBaseFee(Number(value))}
|
||||
/>
|
||||
<InputWithDeco
|
||||
title={'Fee Rate'}
|
||||
value={feeRate}
|
||||
placeholder={'ppm'}
|
||||
amount={feeRate}
|
||||
override={'ppm'}
|
||||
inputType={'number'}
|
||||
inputCallback={value => setFeeRate(Number(value))}
|
||||
/>
|
||||
<InputWithDeco
|
||||
title={'CLTV Delta'}
|
||||
value={cltv}
|
||||
placeholder={'cltv delta'}
|
||||
customAmount={cltv ? cltv.toString() : ''}
|
||||
inputType={'number'}
|
||||
inputCallback={value => setCLTV(Number(value))}
|
||||
/>
|
||||
{canMax && (
|
||||
<InputWithDeco
|
||||
title={'BaseFee'}
|
||||
value={baseFee}
|
||||
title={'Max HTLC'}
|
||||
value={max}
|
||||
placeholder={'sats'}
|
||||
amount={baseFee}
|
||||
amount={max}
|
||||
override={'sat'}
|
||||
inputType={'number'}
|
||||
inputCallback={value => setBaseFee(Number(value))}
|
||||
inputCallback={value => setMax(Number(value))}
|
||||
/>
|
||||
)}
|
||||
{canMin && (
|
||||
<InputWithDeco
|
||||
title={'Fee Rate'}
|
||||
value={feeRate}
|
||||
placeholder={'ppm'}
|
||||
amount={feeRate}
|
||||
override={'ppm'}
|
||||
title={'Min HTLC'}
|
||||
value={min}
|
||||
placeholder={'sats'}
|
||||
amount={min}
|
||||
override={'sat'}
|
||||
inputType={'number'}
|
||||
inputCallback={value => setFeeRate(Number(value))}
|
||||
inputCallback={value => setMin(Number(value))}
|
||||
/>
|
||||
<InputWithDeco
|
||||
title={'CLTV Delta'}
|
||||
value={cltv}
|
||||
placeholder={'cltv delta'}
|
||||
customAmount={cltv ? cltv.toString() : ''}
|
||||
inputType={'number'}
|
||||
inputCallback={value => setCLTV(Number(value))}
|
||||
/>
|
||||
{canMax && (
|
||||
<InputWithDeco
|
||||
title={'Max HTLC'}
|
||||
value={max}
|
||||
placeholder={'sats'}
|
||||
amount={max}
|
||||
override={'sat'}
|
||||
inputType={'number'}
|
||||
inputCallback={value => setMax(Number(value))}
|
||||
/>
|
||||
)}
|
||||
{canMin && (
|
||||
<InputWithDeco
|
||||
title={'Min HTLC'}
|
||||
value={min}
|
||||
placeholder={'sats'}
|
||||
amount={min}
|
||||
override={'sat'}
|
||||
inputType={'number'}
|
||||
inputCallback={value => setMin(Number(value))}
|
||||
/>
|
||||
)}
|
||||
<RightAlign>
|
||||
<SecureButton
|
||||
callback={updateFees}
|
||||
variables={{
|
||||
...(baseFee !== 0 && { base_fee_tokens: baseFee }),
|
||||
...(feeRate !== 0 && { fee_rate: feeRate }),
|
||||
...(cltv !== 0 && { cltv_delta: cltv }),
|
||||
...(max !== 0 &&
|
||||
canMax && { max_htlc_mtokens: (max * 1000).toString() }),
|
||||
...(min !== 0 &&
|
||||
canMin && { min_htlc_mtokens: (min * 1000).toString() }),
|
||||
}}
|
||||
disabled={
|
||||
baseFee === 0 &&
|
||||
feeRate === 0 &&
|
||||
cltv === 0 &&
|
||||
max === 0 &&
|
||||
min === 0
|
||||
}
|
||||
fullWidth={true}
|
||||
withMargin={'16px 0 0'}
|
||||
>
|
||||
Update Fees
|
||||
<ChevronRight size={18} />
|
||||
</SecureButton>
|
||||
</RightAlign>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
</AdminSwitch>
|
||||
)}
|
||||
<RightAlign>
|
||||
<ColorButton
|
||||
onClick={() =>
|
||||
updateFees({
|
||||
variables: {
|
||||
...(baseFee !== 0 && { base_fee_tokens: baseFee }),
|
||||
...(feeRate !== 0 && { fee_rate: feeRate }),
|
||||
...(cltv !== 0 && { cltv_delta: cltv }),
|
||||
...(max !== 0 &&
|
||||
canMax && {
|
||||
max_htlc_mtokens: (max * 1000).toString(),
|
||||
}),
|
||||
...(min !== 0 &&
|
||||
canMin && {
|
||||
min_htlc_mtokens: (min * 1000).toString(),
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
disabled={
|
||||
baseFee === 0 &&
|
||||
feeRate === 0 &&
|
||||
cltv === 0 &&
|
||||
max === 0 &&
|
||||
min === 0
|
||||
}
|
||||
fullWidth={true}
|
||||
withMargin={'16px 0 0'}
|
||||
>
|
||||
Update Fees
|
||||
<ChevronRight size={18} />
|
||||
</ColorButton>
|
||||
</RightAlign>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<CardWithTitle>
|
||||
<SubTitle>Channel Details</SubTitle>
|
||||
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
|
||||
@ -186,4 +188,8 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [CHANNEL_FEES]);
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
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 { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { GET_FORWARDS } from 'src/graphql/queries/getForwards';
|
||||
import {
|
||||
SubTitle,
|
||||
Card,
|
||||
@ -29,11 +30,8 @@ const ForwardsView = () => {
|
||||
const [time, setTime] = useState('week');
|
||||
const [indexOpen, setIndexOpen] = useState(0);
|
||||
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const { loading, data } = useGetForwardsQuery({
|
||||
skip: !auth,
|
||||
variables: { auth, time },
|
||||
variables: { time },
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
@ -89,4 +87,8 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [GET_FORWARDS]);
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { withApollo } from 'config/client';
|
||||
import { Version } from 'src/components/version/Version';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { GET_NODE_INFO } from 'src/graphql/queries/getNodeInfo';
|
||||
import { NetworkInfo } from '../src/views/home/networkInfo/NetworkInfo';
|
||||
import { AccountInfo } from '../src/views/home/account/AccountInfo';
|
||||
import { QuickActions } from '../src/views/home/quickActions/QuickActions';
|
||||
@ -9,14 +11,12 @@ import { FlowBox } from '../src/views/home/reports/flow';
|
||||
import { ForwardBox } from '../src/views/home/reports/forwardReport';
|
||||
import { LiquidReport } from '../src/views/home/reports/liquidReport/LiquidReport';
|
||||
import { ConnectCard } from '../src/views/home/connect/Connect';
|
||||
import { NodeBar } from '../src/components/nodeInfo/NodeBar';
|
||||
|
||||
const HomeView = () => {
|
||||
return (
|
||||
<>
|
||||
<Version />
|
||||
<AccountInfo />
|
||||
<NodeBar />
|
||||
<ConnectCard />
|
||||
<QuickActions />
|
||||
<FlowBox />
|
||||
@ -33,4 +33,8 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [GET_NODE_INFO]);
|
||||
}
|
||||
|
@ -1,44 +1,22 @@
|
||||
import * as React from 'react';
|
||||
import { Spacer } from 'src/components/spacer/Spacer';
|
||||
import { withApollo } from 'config/client';
|
||||
import { ServerAccounts } from 'src/components/accounts/ServerAccounts';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import getConfig from 'next/config';
|
||||
import { ThunderStorm } from 'src/views/homepage/HomePage.styled';
|
||||
import { appendBasePath } from 'src/utils/basePath';
|
||||
import { SessionLogin } from '../src/views/login/SessionLogin';
|
||||
import { NextPageContext } from 'next';
|
||||
import { GET_SERVER_ACCOUNTS } from 'src/graphql/queries/getServerAccounts';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { TopSection } from '../src/views/homepage/Top';
|
||||
import { LoginBox } from '../src/views/homepage/LoginBox';
|
||||
import { Accounts } from '../src/views/homepage/Accounts';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
import { Section } from '../src/components/section/Section';
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { noClient } = publicRuntimeConfig;
|
||||
|
||||
const ContextApp = () => {
|
||||
const { finishedFetch } = useAccountState();
|
||||
|
||||
return (
|
||||
<>
|
||||
<ThunderStorm alt={''} src={appendBasePath('/static/thunderstorm.gif')} />
|
||||
<TopSection />
|
||||
{!finishedFetch && (
|
||||
<Section color={'transparent'}>
|
||||
<LoadingCard loadingHeight={'160px'} />
|
||||
</Section>
|
||||
)}
|
||||
{finishedFetch && (
|
||||
<>
|
||||
<SessionLogin />
|
||||
<Accounts />
|
||||
{!noClient && <LoginBox />}
|
||||
</>
|
||||
)}
|
||||
<Spacer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
const ContextApp = () => (
|
||||
<>
|
||||
<ThunderStorm alt={''} src={appendBasePath('/static/thunderstorm.gif')} />
|
||||
<TopSection />
|
||||
<Accounts />
|
||||
<Spacer />
|
||||
</>
|
||||
);
|
||||
|
||||
const Wrapped = () => (
|
||||
<>
|
||||
@ -47,4 +25,8 @@ const Wrapped = () => (
|
||||
</>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [GET_SERVER_ACCOUNTS]);
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import React, { useState } from 'react';
|
||||
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 { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { GET_PEERS } from 'src/graphql/queries/getPeers';
|
||||
import {
|
||||
CardWithTitle,
|
||||
SubTitle,
|
||||
@ -15,12 +16,8 @@ import { AddPeer } from '../src/views/peers/AddPeer';
|
||||
|
||||
const PeersView = () => {
|
||||
const [indexOpen, setIndexOpen] = useState(0);
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const { loading, data } = useGetPeersQuery({
|
||||
skip: !auth,
|
||||
variables: { auth },
|
||||
});
|
||||
const { loading, data } = useGetPeersQuery();
|
||||
|
||||
if (loading || !data?.getPeers) {
|
||||
return <LoadingCard title={'Peers'} />;
|
||||
@ -53,4 +50,8 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [GET_PEERS]);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { withApollo } from 'config/client';
|
||||
import { SimpleBalance } from 'src/views/balance/SimpleBalance';
|
||||
import {
|
||||
CardWithTitle,
|
||||
@ -11,6 +10,9 @@ import {
|
||||
} from 'src/components/generic/Styled';
|
||||
import { Text } from 'src/components/typography/Styled';
|
||||
import { AdvancedBalance } from 'src/views/balance/AdvancedBalance';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { GET_CHANNELS } from 'src/graphql/queries/getChannels';
|
||||
import { useStatusState } from '../src/context/StatusContext';
|
||||
|
||||
const BalanceView = () => {
|
||||
@ -64,4 +66,8 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [GET_CHANNELS]);
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { withApollo } from 'config/client';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { SingleLine } from '../src/components/generic/Styled';
|
||||
import { InterfaceSettings } from '../src/views/settings/Interface';
|
||||
import { AccountSettings } from '../src/views/settings/Account';
|
||||
import { DangerView } from '../src/views/settings/Danger';
|
||||
import { CurrentSettings } from '../src/views/settings/Current';
|
||||
import { ChatSettings } from '../src/views/settings/Chat';
|
||||
import { PrivacySettings } from '../src/views/settings/Privacy';
|
||||
|
||||
@ -25,7 +25,6 @@ const SettingsView = () => {
|
||||
<InterfaceSettings />
|
||||
<PrivacySettings />
|
||||
<ChatSettings />
|
||||
<CurrentSettings />
|
||||
<AccountSettings />
|
||||
<DangerView />
|
||||
</>
|
||||
@ -38,4 +37,8 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
@ -1,12 +1,16 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { withApollo } from 'config/client';
|
||||
import { VolumeStats } from 'src/views/stats/FlowStats';
|
||||
import { TimeStats } from 'src/views/stats/TimeStats';
|
||||
import { FeeStats } from 'src/views/stats/FeeStats';
|
||||
import { StatResume } from 'src/views/stats/StatResume';
|
||||
import { StatsProvider } from 'src/views/stats/context';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { GET_FEE_HEALTH } from 'src/graphql/queries/getFeeHealth';
|
||||
import { GET_VOLUME_HEALTH } from 'src/graphql/queries/getVolumeHealth';
|
||||
import { GET_TIME_HEALTH } from 'src/graphql/queries/getTimeHealth';
|
||||
import { SingleLine } from '../src/components/generic/Styled';
|
||||
|
||||
export const ButtonRow = styled.div`
|
||||
@ -37,4 +41,12 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [
|
||||
GET_FEE_HEALTH,
|
||||
GET_VOLUME_HEALTH,
|
||||
GET_TIME_HEALTH,
|
||||
]);
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { withApollo } from 'config/client';
|
||||
import { Bakery } from 'src/views/tools/bakery/Bakery';
|
||||
import { Accounting } from 'src/views/tools/accounting/Accounting';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { GET_WALLET_INFO } from 'src/graphql/queries/getWalletInfo';
|
||||
import { BackupsView } from '../src/views/tools/backups/Backups';
|
||||
import { MessagesView } from '../src/views/tools/messages/Messages';
|
||||
import { WalletVersion } from '../src/views/tools/WalletVersion';
|
||||
@ -23,4 +25,8 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [GET_WALLET_INFO]);
|
||||
}
|
||||
|
@ -3,9 +3,10 @@ import { toast } from 'react-toastify';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useGetOffersQuery } from 'src/graphql/hodlhodl/__generated__/query.generated';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { withApollo } from 'config/client';
|
||||
import getConfig from 'next/config';
|
||||
import { HodlOfferType } from 'src/graphql/types';
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import {
|
||||
CardWithTitle,
|
||||
SubTitle,
|
||||
@ -169,4 +170,8 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context);
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { InvoiceCard } from 'src/views/transactions/InvoiceCard';
|
||||
import {
|
||||
useGetResumeQuery,
|
||||
GetResumeQuery,
|
||||
} from 'src/graphql/queries/__generated__/getResume.generated';
|
||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||
import { withApollo } from 'config/client';
|
||||
|
||||
import { NextPageContext } from 'next';
|
||||
import { getProps } from 'src/utils/ssr';
|
||||
import { GET_RESUME } from 'src/graphql/queries/getResume';
|
||||
import { GET_IN_OUT } from 'src/graphql/queries/getInOut';
|
||||
import {
|
||||
Card,
|
||||
CardWithTitle,
|
||||
@ -23,11 +26,8 @@ const TransactionsView = () => {
|
||||
const [indexOpen, setIndexOpen] = useState(0);
|
||||
const [token, setToken] = useState('');
|
||||
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const { loading, data, fetchMore } = useGetResumeQuery({
|
||||
skip: !auth,
|
||||
variables: { auth, token: '' },
|
||||
variables: { token: '' },
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
@ -82,7 +82,7 @@ const TransactionsView = () => {
|
||||
withMargin={'16px 0 0'}
|
||||
onClick={() => {
|
||||
fetchMore({
|
||||
variables: { auth, token },
|
||||
variables: { token },
|
||||
updateQuery: (
|
||||
prev,
|
||||
{ fetchMoreResult }: { fetchMoreResult?: GetResumeQuery }
|
||||
@ -123,4 +123,11 @@ const Wrapped = () => (
|
||||
</GridWrapper>
|
||||
);
|
||||
|
||||
export default withApollo(Wrapped);
|
||||
export default Wrapped;
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
return await getProps(context, [
|
||||
GET_RESUME,
|
||||
{ document: GET_IN_OUT, variables: { time: 'month' } },
|
||||
]);
|
||||
}
|
||||
|
60
server/helpers/auth.ts
Normal file
60
server/helpers/auth.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { authenticatedLndGrpc } from 'ln-service';
|
||||
import { SSOType, AccountType } from 'server/types/apiTypes';
|
||||
import { LndObject } from 'server/types/ln-service.types';
|
||||
import { v5 as uuidv5 } from 'uuid';
|
||||
import { logger } from './logger';
|
||||
|
||||
type LndAuthType = {
|
||||
cert: string | null;
|
||||
macaroon: string;
|
||||
socket: string;
|
||||
};
|
||||
|
||||
const THUNDERHUB_NAMESPACE = '00000000-0000-0000-0000-000000000000';
|
||||
|
||||
export const getUUID = (text: string): string =>
|
||||
uuidv5(text, THUNDERHUB_NAMESPACE);
|
||||
|
||||
export const getAuthLnd = (
|
||||
id: string,
|
||||
sso: SSOType | null,
|
||||
accounts: AccountType[]
|
||||
): LndObject | null => {
|
||||
if (!id) {
|
||||
logger.silly('Account not authenticated');
|
||||
return null;
|
||||
}
|
||||
|
||||
let authDetails: LndAuthType | null = null;
|
||||
|
||||
if (id === 'test') {
|
||||
authDetails = {
|
||||
socket: process.env.TEST_HOST || '',
|
||||
macaroon: process.env.TEST_MACAROON || '',
|
||||
cert: process.env.TEST_CERT || '',
|
||||
};
|
||||
}
|
||||
|
||||
if (id === 'sso' && !sso) {
|
||||
logger.debug('SSO Account is not verified');
|
||||
throw new Error('AccountNotAuthenticated');
|
||||
}
|
||||
|
||||
if (id === 'sso' && sso) {
|
||||
authDetails = sso;
|
||||
}
|
||||
|
||||
if (!authDetails) {
|
||||
const verifiedAccount = accounts.find(a => a.id === id) || null;
|
||||
|
||||
if (!verifiedAccount) {
|
||||
logger.debug('Account not found in config file');
|
||||
throw new Error('AccountNotAuthenticated');
|
||||
}
|
||||
|
||||
authDetails = verifiedAccount;
|
||||
}
|
||||
|
||||
const { lnd } = authenticatedLndGrpc(authDetails);
|
||||
return lnd;
|
||||
};
|
@ -4,9 +4,9 @@ import path from 'path';
|
||||
import os from 'os';
|
||||
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';
|
||||
import { getUUID } from './auth';
|
||||
|
||||
type EncodingType = 'hex' | 'utf-8';
|
||||
type BitcoinNetwork = 'mainnet' | 'regtest' | 'testnet';
|
||||
@ -18,14 +18,14 @@ type AccountType = {
|
||||
network?: BitcoinNetwork;
|
||||
macaroonPath?: string;
|
||||
certificatePath?: string;
|
||||
password?: string;
|
||||
password?: string | null;
|
||||
macaroon?: string;
|
||||
certificate?: string;
|
||||
};
|
||||
type ParsedAccount = {
|
||||
name: string;
|
||||
id: string;
|
||||
host: string;
|
||||
socket: string;
|
||||
macaroon: string;
|
||||
cert: string;
|
||||
password: string;
|
||||
@ -117,7 +117,7 @@ export const hashPasswords = (
|
||||
|
||||
const cloned = { ...config };
|
||||
|
||||
let hashedMasterPassword = config?.masterPassword || '';
|
||||
let hashedMasterPassword = config?.masterPassword;
|
||||
|
||||
if (
|
||||
hashedMasterPassword &&
|
||||
@ -283,7 +283,7 @@ export const getParsedAccount = (
|
||||
return {
|
||||
name: name || '',
|
||||
id,
|
||||
host: serverUrl || '',
|
||||
socket: serverUrl || '',
|
||||
macaroon,
|
||||
cert: cert || '',
|
||||
password: password || masterPassword || '',
|
||||
|
@ -15,7 +15,7 @@ const errorNode = {
|
||||
export const getNodeFromChannel = async (
|
||||
id: string,
|
||||
publicKey: string,
|
||||
lnd: LndObject
|
||||
lnd: LndObject | null
|
||||
) => {
|
||||
const [channelInfo, channelError] = await toWithError(
|
||||
getChannel({
|
||||
|
@ -1,23 +1,7 @@
|
||||
import { authenticatedLndGrpc } from 'ln-service';
|
||||
import getConfig from 'next/config';
|
||||
import {
|
||||
SSO_ACCOUNT,
|
||||
SERVER_ACCOUNT,
|
||||
AuthType,
|
||||
CLIENT_ACCOUNT,
|
||||
} from 'src/context/AccountContext';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { logger } from './logger';
|
||||
|
||||
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig() || {};
|
||||
const { serverRuntimeConfig } = getConfig() || {};
|
||||
const { nodeEnv } = serverRuntimeConfig || {};
|
||||
const { noClient } = publicRuntimeConfig || {};
|
||||
|
||||
type LndAuthType = {
|
||||
cert: string | null;
|
||||
macaroon: string;
|
||||
host: string;
|
||||
};
|
||||
|
||||
export const getIp = (req: any) => {
|
||||
if (!req || !req.headers) {
|
||||
@ -31,78 +15,6 @@ export const getIp = (req: any) => {
|
||||
return ip;
|
||||
};
|
||||
|
||||
export const getCorrectAuth = (
|
||||
auth: AuthType,
|
||||
context: ContextType
|
||||
): LndAuthType => {
|
||||
if (auth.type === 'test' && nodeEnv !== 'production') {
|
||||
return {
|
||||
host: process.env.TEST_HOST || '',
|
||||
macaroon: process.env.TEST_MACAROON || '',
|
||||
cert: process.env.TEST_CERT || '',
|
||||
};
|
||||
}
|
||||
if (auth.type === SERVER_ACCOUNT) {
|
||||
const { account, accounts } = context;
|
||||
if (!account) {
|
||||
logger.debug('Account not available in request');
|
||||
throw new Error('AccountNotAuthenticated');
|
||||
}
|
||||
if (account !== auth.id) {
|
||||
logger.debug(
|
||||
`Account (${account}) in cookie different to requested account (${auth.id})`
|
||||
);
|
||||
throw new Error('AccountNotAuthenticated');
|
||||
}
|
||||
|
||||
const verifiedAccount = accounts.find(a => a.id === account) || null;
|
||||
|
||||
if (!verifiedAccount) {
|
||||
logger.debug('Account not found in config file');
|
||||
throw new Error('AccountNotAuthenticated');
|
||||
}
|
||||
|
||||
return verifiedAccount;
|
||||
}
|
||||
if (auth.type === SSO_ACCOUNT) {
|
||||
if (!context.ssoVerified || !context.sso) {
|
||||
logger.debug('SSO Account is not verified');
|
||||
throw new Error('AccountNotAuthenticated');
|
||||
}
|
||||
|
||||
return { ...context.sso };
|
||||
}
|
||||
if (auth.type === CLIENT_ACCOUNT && !noClient) {
|
||||
const { host, macaroon, cert } = auth;
|
||||
return { host, macaroon, cert };
|
||||
}
|
||||
if (auth.type === CLIENT_ACCOUNT && noClient) {
|
||||
logger.info(`Client accounts are disabled from the server.`);
|
||||
throw new Error('AccountTypeDoesNotExist');
|
||||
}
|
||||
logger.info(`No authentication for account type '${auth.type}' found`);
|
||||
throw new Error('AccountTypeDoesNotExist');
|
||||
};
|
||||
|
||||
export const getAuthLnd = (auth: LndAuthType) => {
|
||||
const cert = auth.cert || '';
|
||||
const macaroon = auth.macaroon || '';
|
||||
const socket = auth.host || '';
|
||||
|
||||
const params = {
|
||||
macaroon,
|
||||
socket,
|
||||
...(cert !== '' ? { cert } : {}),
|
||||
};
|
||||
|
||||
const { lnd } = authenticatedLndGrpc(params);
|
||||
|
||||
return lnd;
|
||||
};
|
||||
|
||||
export const getLnd = (auth: AuthType, context: ContextType) =>
|
||||
getAuthLnd(getCorrectAuth(auth, context));
|
||||
|
||||
export const getErrorMsg = (error: any[] | string): string => {
|
||||
if (typeof error === 'string') {
|
||||
return error;
|
||||
|
@ -10,6 +10,7 @@ interface RateConfigProps {
|
||||
|
||||
export const RateConfig: RateConfigProps = {
|
||||
getMessages: { max: 10, window: '5s' },
|
||||
nodeInfo: { max: 10, window: '5s' },
|
||||
};
|
||||
|
||||
const rateLimiter = getGraphQLRateLimiter({
|
||||
|
@ -6,16 +6,10 @@ Object {
|
||||
"getServerAccounts": Array [
|
||||
Object {
|
||||
"id": "accountID",
|
||||
"loggedIn": true,
|
||||
"loggedIn": false,
|
||||
"name": "account",
|
||||
"type": "server",
|
||||
},
|
||||
Object {
|
||||
"id": "sso",
|
||||
"loggedIn": true,
|
||||
"name": "SSO Account",
|
||||
"type": "sso",
|
||||
},
|
||||
],
|
||||
},
|
||||
"errors": undefined,
|
||||
@ -34,7 +28,7 @@ Object {
|
||||
"getServerAccounts": Array [
|
||||
Object {
|
||||
"id": "accountID",
|
||||
"loggedIn": true,
|
||||
"loggedIn": false,
|
||||
"name": "account",
|
||||
"type": "server",
|
||||
},
|
||||
@ -56,7 +50,7 @@ Object {
|
||||
"getServerAccounts": Array [
|
||||
Object {
|
||||
"id": "accountID",
|
||||
"loggedIn": true,
|
||||
"loggedIn": false,
|
||||
"name": "account",
|
||||
"type": "server",
|
||||
},
|
||||
|
@ -11,7 +11,6 @@ describe('Account Resolvers', () => {
|
||||
|
||||
const res = await query({
|
||||
query: GET_SERVER_ACCOUNTS,
|
||||
variables: { auth: { type: 'test' } },
|
||||
});
|
||||
|
||||
expect(res.errors).toBe(undefined);
|
||||
@ -22,7 +21,6 @@ describe('Account Resolvers', () => {
|
||||
|
||||
const res = await query({
|
||||
query: GET_SERVER_ACCOUNTS,
|
||||
variables: { auth: { type: 'test' } },
|
||||
});
|
||||
|
||||
expect(res.errors).toBe(undefined);
|
||||
@ -33,7 +31,6 @@ describe('Account Resolvers', () => {
|
||||
|
||||
const res = await query({
|
||||
query: GET_SERVER_ACCOUNTS,
|
||||
variables: { auth: { type: 'test' } },
|
||||
});
|
||||
|
||||
expect(res.errors).toBe(undefined);
|
||||
|
@ -1,42 +1,68 @@
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { SSO_ACCOUNT, SERVER_ACCOUNT } from 'src/context/AccountContext';
|
||||
import { logger } from 'server/helpers/logger';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
|
||||
export const accountResolvers = {
|
||||
Query: {
|
||||
getAccount: async (_: undefined, __: undefined, context: ContextType) => {
|
||||
const { ip, accounts, id } = context;
|
||||
await requestLimiter(ip, 'getAccount');
|
||||
|
||||
if (!id) {
|
||||
logger.error(`Not authenticated`);
|
||||
throw new Error('NotAuthenticated');
|
||||
}
|
||||
|
||||
if (id === 'sso') {
|
||||
return {
|
||||
name: 'SSO Account',
|
||||
id: 'sso',
|
||||
loggedIn: true,
|
||||
type: 'sso',
|
||||
};
|
||||
}
|
||||
|
||||
const currentAccount = accounts.find(a => a.id === id);
|
||||
|
||||
if (!currentAccount) {
|
||||
logger.error(`No account found for id ${id}`);
|
||||
throw new Error('NoAccountFound');
|
||||
}
|
||||
|
||||
return { ...currentAccount, type: 'server', loggedIn: true };
|
||||
},
|
||||
getServerAccounts: async (
|
||||
_: undefined,
|
||||
__: undefined,
|
||||
context: ContextType
|
||||
) => {
|
||||
const { ip, accounts, account, sso, ssoVerified } = context;
|
||||
const { ip, accounts, id, sso } = context;
|
||||
await requestLimiter(ip, 'getServerAccounts');
|
||||
|
||||
let ssoAccount = null;
|
||||
if (ssoVerified && sso) {
|
||||
const { cert, host } = sso;
|
||||
if (id === 'sso' && sso) {
|
||||
const { cert, socket } = sso;
|
||||
logger.debug(
|
||||
`Macaroon${
|
||||
cert ? ', certificate' : ''
|
||||
} and host (${host}) found for SSO.`
|
||||
} and host (${socket}) found for SSO.`
|
||||
);
|
||||
ssoAccount = {
|
||||
name: 'SSO Account',
|
||||
id: SSO_ACCOUNT,
|
||||
id: 'sso',
|
||||
loggedIn: true,
|
||||
type: SSO_ACCOUNT,
|
||||
type: 'sso',
|
||||
};
|
||||
}
|
||||
|
||||
const withStatus =
|
||||
accounts?.map(a => ({
|
||||
...a,
|
||||
loggedIn: a.id === account,
|
||||
type: SERVER_ACCOUNT,
|
||||
loggedIn: a.id === id,
|
||||
type: 'server',
|
||||
})) || [];
|
||||
|
||||
return ssoAccount ? [...withStatus, ssoAccount] : withStatus;
|
||||
return ssoAccount ? [ssoAccount, ...withStatus] : withStatus;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -6,11 +6,11 @@ import {
|
||||
PRE_PASS_STRING,
|
||||
} from 'server/helpers/fileHelpers';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { SSO_ACCOUNT, SERVER_ACCOUNT } from 'src/context/AccountContext';
|
||||
import { logger } from 'server/helpers/logger';
|
||||
import cookie from 'cookie';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import { appConstants } from 'server/utils/appConstants';
|
||||
|
||||
const { serverRuntimeConfig } = getConfig() || {};
|
||||
const { cookiePath, nodeEnv } = serverRuntimeConfig || {};
|
||||
@ -26,7 +26,7 @@ export const authResolvers = {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!sso.host || !sso.macaroon) {
|
||||
if (!sso.socket || !sso.macaroon) {
|
||||
logger.warn('Host and macaroon are required for SSO');
|
||||
return null;
|
||||
}
|
||||
@ -47,11 +47,15 @@ export const authResolvers = {
|
||||
nodeEnv === 'development'
|
||||
) {
|
||||
refreshCookie(cookiePath);
|
||||
const token = jwt.sign({ user: SSO_ACCOUNT }, secret);
|
||||
const token = jwt.sign({ id: 'sso' }, secret);
|
||||
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
cookie.serialize('SSOAuth', token, { httpOnly: true, sameSite: true })
|
||||
cookie.serialize(appConstants.cookieName, token, {
|
||||
httpOnly: true,
|
||||
sameSite: true,
|
||||
path: '/',
|
||||
})
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@ -83,17 +87,13 @@ export const authResolvers = {
|
||||
}
|
||||
|
||||
logger.debug(`Correct password for account ${params.id}`);
|
||||
const token = jwt.sign(
|
||||
{
|
||||
id: params.id,
|
||||
},
|
||||
secret
|
||||
);
|
||||
const token = jwt.sign({ id: params.id }, secret);
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
cookie.serialize('AccountAuth', token, {
|
||||
cookie.serialize(appConstants.cookieName, token, {
|
||||
httpOnly: true,
|
||||
sameSite: true,
|
||||
path: '/',
|
||||
})
|
||||
);
|
||||
return true;
|
||||
@ -104,20 +104,10 @@ export const authResolvers = {
|
||||
const { ip, res } = context;
|
||||
await requestLimiter(ip, 'logout');
|
||||
|
||||
if (params.type === SSO_ACCOUNT) {
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
cookie.serialize('SSOAuth', '', { maxAge: 1 })
|
||||
);
|
||||
return true;
|
||||
}
|
||||
if (params.type === SERVER_ACCOUNT) {
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
cookie.serialize('AccountAuth', '', { maxAge: 1 })
|
||||
);
|
||||
return true;
|
||||
}
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
cookie.serialize(appConstants.cookieName, '', { maxAge: 1 })
|
||||
);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
@ -1,16 +1,12 @@
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { getLnd } from 'server/helpers/helpers';
|
||||
import { to } from 'server/helpers/async';
|
||||
import { logger } from 'server/helpers/logger';
|
||||
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;
|
||||
avoid?: String[];
|
||||
in_through?: String;
|
||||
is_avoiding_high_inbound?: Boolean;
|
||||
@ -24,7 +20,6 @@ type RebalanceType = {
|
||||
};
|
||||
|
||||
type AccountingType = {
|
||||
auth: AuthType;
|
||||
category?: String;
|
||||
currency?: String;
|
||||
fiat?: String;
|
||||
@ -39,8 +34,7 @@ export const bosResolvers = {
|
||||
params: AccountingType,
|
||||
context: ContextType
|
||||
) => {
|
||||
const { auth, ...settings } = params;
|
||||
const lnd = getLnd(auth, context);
|
||||
const { lnd } = context;
|
||||
|
||||
const response = await to(
|
||||
getAccountingReport({
|
||||
@ -48,7 +42,7 @@ export const bosResolvers = {
|
||||
logger,
|
||||
request,
|
||||
is_csv: true,
|
||||
...settings,
|
||||
...params,
|
||||
})
|
||||
);
|
||||
|
||||
@ -62,7 +56,6 @@ export const bosResolvers = {
|
||||
context: ContextType
|
||||
) => {
|
||||
const {
|
||||
auth,
|
||||
avoid,
|
||||
in_through,
|
||||
is_avoiding_high_inbound,
|
||||
@ -74,7 +67,7 @@ export const bosResolvers = {
|
||||
out_through,
|
||||
target,
|
||||
} = params;
|
||||
const lnd = getLnd(auth, context);
|
||||
const { lnd } = context;
|
||||
|
||||
const filteredParams = {
|
||||
avoid,
|
||||
|
@ -1,34 +1,31 @@
|
||||
import { AuthMock } from 'server/tests/testMocks';
|
||||
import testServer from 'server/tests/testServer';
|
||||
import gql from 'graphql-tag';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
jest.mock('ln-service');
|
||||
|
||||
describe('Chain Resolvers', () => {
|
||||
test('getChainBalance', async () => {
|
||||
const getChainBalance = gql`
|
||||
query($auth: authType!) {
|
||||
getChainBalance(auth: $auth)
|
||||
query {
|
||||
getChainBalance
|
||||
}
|
||||
`;
|
||||
const { query } = testServer();
|
||||
const res = await query({
|
||||
query: getChainBalance,
|
||||
variables: AuthMock,
|
||||
});
|
||||
expect(res.errors).toBe(undefined);
|
||||
expect(res).toMatchSnapshot();
|
||||
});
|
||||
test('getPendingChainBalance', async () => {
|
||||
const getPendingChainBalance = gql`
|
||||
query($auth: authType!) {
|
||||
getPendingChainBalance(auth: $auth)
|
||||
query {
|
||||
getPendingChainBalance
|
||||
}
|
||||
`;
|
||||
const { query } = testServer();
|
||||
const res = await query({
|
||||
query: getPendingChainBalance,
|
||||
variables: AuthMock,
|
||||
});
|
||||
expect(res.errors).toBe(undefined);
|
||||
expect(res).toMatchSnapshot();
|
||||
|
@ -9,11 +9,7 @@ import {
|
||||
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 { getErrorMsg } from 'server/helpers/helpers';
|
||||
import { sortBy } from 'underscore';
|
||||
import { to } from 'server/helpers/async';
|
||||
import {
|
||||
@ -41,8 +37,7 @@ export const chainResolvers = {
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'chainBalance');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const value: ChainBalanceProps = await to<GetChainBalanceType>(
|
||||
getChainBalance({
|
||||
@ -58,8 +53,7 @@ export const chainResolvers = {
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'pendingChainBalance');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const pendingValue: PendingChainBalanceProps = await to<
|
||||
GetPendingChainBalanceType
|
||||
@ -77,8 +71,7 @@ export const chainResolvers = {
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'chainTransactions');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const transactionList = await to<GetChainTransactionsType>(
|
||||
getChainTransactions({
|
||||
@ -95,8 +88,7 @@ export const chainResolvers = {
|
||||
getUtxos: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getUtxos');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const info = await to<GetUtxosType>(getUtxos({ lnd }));
|
||||
|
||||
@ -107,8 +99,7 @@ export const chainResolvers = {
|
||||
createAddress: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getAddress');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const format = params.nested ? 'np2wpkh' : 'p2wpkh';
|
||||
|
||||
@ -128,8 +119,7 @@ export const chainResolvers = {
|
||||
sendToAddress: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'sendToAddress');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const props = params.fee
|
||||
? { fee_tokens_per_vbyte: params.fee }
|
||||
|
@ -2,11 +2,7 @@ import { closeChannel as lnCloseChannel } 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 { getErrorMsg } from 'server/helpers/helpers';
|
||||
|
||||
export const closeChannel = async (
|
||||
_: undefined,
|
||||
@ -15,8 +11,7 @@ export const closeChannel = async (
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'closeChannel');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
try {
|
||||
const info = await lnCloseChannel({
|
||||
|
@ -2,11 +2,7 @@ import { openChannel as lnOpenChannel } 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 { getErrorMsg } from 'server/helpers/helpers';
|
||||
|
||||
export const openChannel = async (
|
||||
_: undefined,
|
||||
@ -15,8 +11,7 @@ export const openChannel = async (
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'openChannel');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const openParams = {
|
||||
is_private: params.isPrivate,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { updateRoutingFees } from 'ln-service';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import { getLnd } from 'server/helpers/helpers';
|
||||
import { to } from 'server/helpers/async';
|
||||
|
||||
export const updateFees = async (
|
||||
@ -21,7 +20,7 @@ export const updateFees = async (
|
||||
min_htlc_mtokens,
|
||||
} = params;
|
||||
|
||||
const lnd = getLnd(params.auth, context);
|
||||
const { lnd } = context;
|
||||
|
||||
if (
|
||||
!base_fee_tokens &&
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { getChannelBalance as getLnChannelBalance } from 'ln-service';
|
||||
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';
|
||||
|
||||
interface ChannelBalanceProps {
|
||||
@ -16,8 +16,7 @@ export const getChannelBalance = async (
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'channelBalance');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const channelBalance: ChannelBalanceProps = await to(
|
||||
getLnChannelBalance({
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { getChannels, getWalletInfo } from 'ln-service';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import { getLnd } from 'server/helpers/helpers';
|
||||
import { to } from 'server/helpers/async';
|
||||
|
||||
interface GetChannelsProps {
|
||||
@ -37,7 +36,7 @@ export const getChannelFees = async (
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'channelFees');
|
||||
|
||||
const lnd = getLnd(params.auth, context);
|
||||
const { lnd } = context;
|
||||
|
||||
const { public_key } = await to(getWalletInfo({ lnd }));
|
||||
const { channels }: GetChannelsProps = await to(getChannels({ lnd }));
|
||||
|
@ -2,7 +2,7 @@ import { getChannels as getLnChannels, getWalletInfo } from 'ln-service';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
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';
|
||||
|
||||
@ -13,8 +13,7 @@ export const getChannels = async (
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'channels');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const { public_key, current_block_height } = await to(getWalletInfo({ lnd }));
|
||||
|
||||
|
@ -2,7 +2,6 @@ import { getClosedChannels as getLnClosedChannels } from 'ln-service';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { to } from 'server/helpers/async';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
||||
|
||||
interface ChannelListProps {
|
||||
channels: ChannelProps[];
|
||||
@ -32,8 +31,7 @@ export const getClosedChannels = async (
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'closedChannels');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const { channels }: ChannelListProps = await to(getLnClosedChannels({ lnd }));
|
||||
|
||||
|
@ -2,7 +2,6 @@ import { getPendingChannels as getLnPendingChannels } from 'ln-service';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { to } from 'server/helpers/async';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
||||
|
||||
interface PendingChannelListProps {
|
||||
pending_channels: PendingChannelProps[];
|
||||
@ -32,8 +31,7 @@ export const getPendingChannels = async (
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'pendingChannels');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const { pending_channels }: PendingChannelListProps = await to(
|
||||
getLnPendingChannels({ lnd })
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { to, toWithError } from 'server/helpers/async';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
||||
|
||||
import {
|
||||
createCustomRecords,
|
||||
decodeMessage,
|
||||
@ -26,8 +26,7 @@ export const chatResolvers = {
|
||||
getMessages: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getMessages');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const invoiceList = await to<GetInvoicesType>(
|
||||
getInvoices({
|
||||
@ -105,8 +104,7 @@ export const chatResolvers = {
|
||||
sendMessage: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'sendMessage');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
if (params.maxFee) {
|
||||
const tokens = Math.max(params.tokens || 100, 100);
|
||||
|
72
server/schema/context.ts
Normal file
72
server/schema/context.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { IncomingMessage, ServerResponse } from 'http';
|
||||
import { getIp } from 'server/helpers/helpers';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { logger } from 'server/helpers/logger';
|
||||
import {
|
||||
readMacaroons,
|
||||
readFile,
|
||||
getAccounts,
|
||||
} from 'server/helpers/fileHelpers';
|
||||
import getConfig from 'next/config';
|
||||
import { ContextType, SSOType } from 'server/types/apiTypes';
|
||||
import cookie from 'cookie';
|
||||
import { LndObject } from 'server/types/ln-service.types';
|
||||
import { getAuthLnd } from 'server/helpers/auth';
|
||||
import { appConstants } from 'server/utils/appConstants';
|
||||
import { secret } from 'pages/api/v1';
|
||||
|
||||
const { serverRuntimeConfig } = getConfig();
|
||||
const {
|
||||
macaroonPath,
|
||||
lnCertPath,
|
||||
lnServerUrl,
|
||||
accountConfigPath,
|
||||
} = serverRuntimeConfig;
|
||||
|
||||
const ssoMacaroon = readMacaroons(macaroonPath);
|
||||
const ssoCert = readFile(lnCertPath);
|
||||
const accountConfig = getAccounts(accountConfigPath);
|
||||
|
||||
let sso: SSOType | null = null;
|
||||
|
||||
if (ssoMacaroon && lnServerUrl) {
|
||||
sso = {
|
||||
macaroon: ssoMacaroon,
|
||||
socket: lnServerUrl,
|
||||
cert: ssoCert,
|
||||
};
|
||||
}
|
||||
|
||||
export const getContext = (req: IncomingMessage, res: ServerResponse) => {
|
||||
const ip = getIp(req);
|
||||
|
||||
const cookies = cookie.parse(req.headers.cookie ?? '') || {};
|
||||
const auth = cookies[appConstants.cookieName];
|
||||
|
||||
let lnd: LndObject | null = null;
|
||||
let id: string | null = null;
|
||||
|
||||
if (auth) {
|
||||
try {
|
||||
const data = jwt.verify(auth, secret) as { id: string };
|
||||
if (data && data.id) {
|
||||
lnd = getAuthLnd(data.id, sso, accountConfig);
|
||||
id = data.id;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.silly('Authentication cookie failed');
|
||||
}
|
||||
}
|
||||
|
||||
const context: ContextType = {
|
||||
ip,
|
||||
lnd,
|
||||
secret,
|
||||
id,
|
||||
sso,
|
||||
accounts: accountConfig,
|
||||
res,
|
||||
};
|
||||
|
||||
return context;
|
||||
};
|
@ -1,5 +1,4 @@
|
||||
import { getChannels, getChannel, getWalletInfo } from 'ln-service';
|
||||
import { getCorrectAuth, getAuthLnd } from 'server/helpers/helpers';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import { to, toWithError } from 'server/helpers/async';
|
||||
import { logger } from 'server/helpers/logger';
|
||||
@ -19,8 +18,7 @@ type ChannelFeesType = {
|
||||
export default async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getFeeHealth');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const { public_key } = await to(getWalletInfo({ lnd }));
|
||||
const { channels } = await to<GetChannelsType>(getChannels({ lnd }));
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { getChannels } from 'ln-service';
|
||||
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';
|
||||
@ -8,11 +7,10 @@ import { getAverage } from '../helpers';
|
||||
|
||||
const halfMonthInMilliSeconds = 1296000000;
|
||||
|
||||
export default async (_: undefined, params: any, context: ContextType) => {
|
||||
export default async (_: undefined, __: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getTimeHealth');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const { channels } = await to<GetChannelsType>(getChannels({ lnd }));
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { getForwards, getChannels, getWalletInfo } from 'ln-service';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import { getCorrectAuth, getAuthLnd } from 'server/helpers/helpers';
|
||||
import { to } from 'server/helpers/async';
|
||||
import { subMonths } from 'date-fns';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
@ -15,8 +14,7 @@ const monthInBlocks = 4380;
|
||||
export default async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getVolumeHealth');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const before = new Date().toISOString();
|
||||
const after = subMonths(new Date(), 1).toISOString();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import merge from 'lodash.merge';
|
||||
import { makeExecutableSchema } from 'apollo-server-micro';
|
||||
import { makeExecutableSchema } from 'graphql-tools';
|
||||
import { nodeTypes } from './node/types';
|
||||
import { nodeResolvers } from './node/resolvers';
|
||||
import { authResolvers } from './auth/resolvers';
|
||||
@ -93,4 +93,4 @@ const resolvers = merge(
|
||||
tbaseResolvers
|
||||
);
|
||||
|
||||
export default makeExecutableSchema({ typeDefs, resolvers });
|
||||
export const schema = makeExecutableSchema({ typeDefs, resolvers });
|
||||
|
@ -9,12 +9,7 @@ import {
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { logger } from 'server/helpers/logger';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
getLnd,
|
||||
} from 'server/helpers/helpers';
|
||||
import { getErrorMsg } from 'server/helpers/helpers';
|
||||
import { to } from 'server/helpers/async';
|
||||
import { DecodedType } from 'server/types/ln-service.types';
|
||||
|
||||
@ -25,7 +20,7 @@ export const invoiceResolvers = {
|
||||
decodeRequest: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'decode');
|
||||
|
||||
const lnd = getLnd(params.auth, context);
|
||||
const { lnd } = context;
|
||||
|
||||
const decoded = await to<DecodedType>(
|
||||
decodePaymentRequest({
|
||||
@ -49,8 +44,7 @@ export const invoiceResolvers = {
|
||||
createInvoice: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'createInvoice');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
return await to(
|
||||
createInvoiceRequest({
|
||||
@ -62,8 +56,8 @@ export const invoiceResolvers = {
|
||||
keysend: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'keysend');
|
||||
|
||||
const { auth, destination, tokens } = params;
|
||||
const lnd = getLnd(auth, context);
|
||||
const { destination, tokens } = params;
|
||||
const { lnd } = context;
|
||||
|
||||
const preimage = randomBytes(32);
|
||||
const secret = preimage.toString('hex');
|
||||
@ -91,8 +85,7 @@ export const invoiceResolvers = {
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'circularRebalance');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
let route;
|
||||
try {
|
||||
@ -121,8 +114,8 @@ export const invoiceResolvers = {
|
||||
payViaRoute: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'payViaRoute');
|
||||
|
||||
const { auth, route: routeJSON, id } = params;
|
||||
const lnd = getLnd(auth, context);
|
||||
const { route: routeJSON, id } = params;
|
||||
const { lnd } = context;
|
||||
|
||||
let route;
|
||||
try {
|
||||
|
@ -1,9 +1,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 { grantAccess } from 'ln-service';
|
||||
import { AuthType } from 'src/context/AccountContext';
|
||||
import { logger } from 'server/helpers/logger';
|
||||
|
||||
export type PermissionsType = {
|
||||
@ -27,7 +25,6 @@ export type PermissionsType = {
|
||||
};
|
||||
|
||||
type ParamsType = {
|
||||
auth: AuthType;
|
||||
permissions: PermissionsType;
|
||||
};
|
||||
|
||||
@ -40,9 +37,8 @@ export const macaroonResolvers = {
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'createMacaroon');
|
||||
|
||||
const { auth, permissions } = params;
|
||||
|
||||
const lnd = getLnd(auth, context);
|
||||
const { permissions } = params;
|
||||
const { lnd } = context;
|
||||
|
||||
const { macaroon, permissions: permissionList } = await to(
|
||||
grantAccess({ lnd, ...permissions })
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { getNetworkInfo } from 'ln-service';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import { getLnd } from 'server/helpers/helpers';
|
||||
import { to } from 'server/helpers/async';
|
||||
|
||||
interface NetworkInfoProps {
|
||||
@ -20,7 +19,7 @@ export const networkResolvers = {
|
||||
getNetworkInfo: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'networkInfo');
|
||||
|
||||
const lnd = getLnd(params.auth, context);
|
||||
const { lnd } = context;
|
||||
|
||||
const info: NetworkInfoProps = await to(getNetworkInfo({ lnd }));
|
||||
|
||||
|
@ -7,7 +7,6 @@ import {
|
||||
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';
|
||||
|
||||
@ -24,16 +23,15 @@ export const nodeResolvers = {
|
||||
getNode: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'closedChannels');
|
||||
|
||||
const { auth, withoutChannels = true, publicKey } = params;
|
||||
const lnd = getLnd(auth, context);
|
||||
const { withoutChannels = true, publicKey } = params;
|
||||
const { lnd } = context;
|
||||
|
||||
return { lnd, publicKey, withChannels: !withoutChannels };
|
||||
},
|
||||
getNodeInfo: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'nodeInfo');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const info = await to<GetWalletInfoType>(
|
||||
getWalletInfo({
|
||||
|
@ -2,11 +2,7 @@ import { getPeers, removePeer, addPeer } 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 { getErrorMsg } from 'server/helpers/helpers';
|
||||
import { to } from 'server/helpers/async';
|
||||
|
||||
interface PeerProps {
|
||||
@ -26,8 +22,7 @@ export const peerResolvers = {
|
||||
getPeers: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getPeers');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const { peers }: { peers: PeerProps[] } = await to(
|
||||
getPeers({
|
||||
@ -67,8 +62,7 @@ export const peerResolvers = {
|
||||
peerSocket = parts[1];
|
||||
}
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
try {
|
||||
const success: boolean = await addPeer({
|
||||
@ -86,8 +80,7 @@ export const peerResolvers = {
|
||||
removePeer: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'removePeer');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
try {
|
||||
const success: boolean = await removePeer({
|
||||
|
@ -6,7 +6,6 @@ import {
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
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';
|
||||
|
||||
@ -21,7 +20,7 @@ export const routeResolvers = {
|
||||
getRoutes: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getRoutes');
|
||||
|
||||
const lnd = getLnd(params.auth, context);
|
||||
const { lnd } = context;
|
||||
|
||||
const { public_key } = await getWalletInfo({ lnd });
|
||||
|
||||
|
@ -9,12 +9,7 @@ import {
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { logger } from 'server/helpers/logger';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
getLnd,
|
||||
} from 'server/helpers/helpers';
|
||||
import { getErrorMsg } from 'server/helpers/helpers';
|
||||
import { toWithError } from 'server/helpers/async';
|
||||
import { ChannelType } from 'server/types/ln-service.types';
|
||||
|
||||
@ -23,8 +18,7 @@ export const toolsResolvers = {
|
||||
verifyBackups: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'verifyBackups');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
let backupObj = { backup: '', channels: [] as ChannelType[] };
|
||||
try {
|
||||
@ -51,8 +45,7 @@ export const toolsResolvers = {
|
||||
recoverFunds: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'recoverFunds');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
let backupObj = { backup: '' };
|
||||
try {
|
||||
@ -78,8 +71,7 @@ export const toolsResolvers = {
|
||||
getBackups: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getBackups');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
try {
|
||||
const backups = await getBackups({
|
||||
@ -94,7 +86,7 @@ export const toolsResolvers = {
|
||||
adminCheck: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'adminCheck');
|
||||
|
||||
const lnd = getLnd(params.auth, context);
|
||||
const { lnd } = context;
|
||||
|
||||
const [, error] = await toWithError(
|
||||
pay({
|
||||
@ -123,8 +115,7 @@ export const toolsResolvers = {
|
||||
verifyMessage: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'verifyMessage');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
try {
|
||||
const message: { signed_by: string } = await verifyMessage({
|
||||
@ -142,8 +133,7 @@ export const toolsResolvers = {
|
||||
signMessage: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'signMessage');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
try {
|
||||
const message: { signature: string } = await signMessage({
|
||||
|
@ -8,7 +8,6 @@ import { compareDesc, subHours, subDays, subMonths, subYears } from 'date-fns';
|
||||
import { sortBy } from 'underscore';
|
||||
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,
|
||||
@ -26,8 +25,7 @@ export const transactionResolvers = {
|
||||
getResume: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'payments');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const invoiceProps = params.token
|
||||
? { token: params.token }
|
||||
@ -105,8 +103,7 @@ export const transactionResolvers = {
|
||||
getForwards: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'forwards');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
let startDate = new Date();
|
||||
const endDate = new Date();
|
||||
|
@ -1,14 +1,6 @@
|
||||
import { gql } from 'apollo-server-micro';
|
||||
|
||||
export const generalTypes = gql`
|
||||
input authType {
|
||||
type: String!
|
||||
id: String
|
||||
host: String
|
||||
macaroon: String
|
||||
cert: String
|
||||
}
|
||||
|
||||
input permissionsType {
|
||||
is_ok_to_adjust_peers: Boolean
|
||||
is_ok_to_create_chain_addresses: Boolean
|
||||
@ -38,66 +30,54 @@ export const queryTypes = gql`
|
||||
type Query {
|
||||
getBaseNodes: [baseNodesType]!
|
||||
getAccountingReport(
|
||||
auth: authType!
|
||||
category: String
|
||||
currency: String
|
||||
fiat: String
|
||||
month: String
|
||||
year: String
|
||||
): String!
|
||||
getVolumeHealth(auth: authType!): channelsHealth
|
||||
getTimeHealth(auth: authType!): channelsTimeHealth
|
||||
getFeeHealth(auth: authType!): channelsFeeHealth
|
||||
getChannelBalance(auth: authType!): channelBalanceType
|
||||
getChannels(auth: authType!, active: Boolean): [channelType]!
|
||||
getClosedChannels(auth: authType!, type: String): [closedChannelType]
|
||||
getPendingChannels(auth: authType!): [pendingChannelType]
|
||||
getChannelFees(auth: authType!): [channelFeeType]
|
||||
getChannelReport(auth: authType!): channelReportType
|
||||
getNetworkInfo(auth: authType!): networkInfoType
|
||||
getNodeInfo(auth: authType!): nodeInfoType
|
||||
adminCheck(auth: authType!): Boolean
|
||||
getNode(
|
||||
auth: authType!
|
||||
publicKey: String!
|
||||
withoutChannels: Boolean
|
||||
): Node!
|
||||
decodeRequest(auth: authType!, request: String!): decodeType
|
||||
getWalletInfo(auth: authType!): walletInfoType
|
||||
getResume(auth: authType!, token: String): getResumeType
|
||||
getForwards(auth: authType!, time: String): getForwardType
|
||||
getVolumeHealth: channelsHealth
|
||||
getTimeHealth: channelsTimeHealth
|
||||
getFeeHealth: channelsFeeHealth
|
||||
getChannelBalance: channelBalanceType
|
||||
getChannels(active: Boolean): [channelType]!
|
||||
getClosedChannels(type: String): [closedChannelType]
|
||||
getPendingChannels: [pendingChannelType]
|
||||
getChannelFees: [channelFeeType]
|
||||
getChannelReport: channelReportType
|
||||
getNetworkInfo: networkInfoType
|
||||
getNodeInfo: nodeInfoType
|
||||
adminCheck: Boolean
|
||||
getNode(publicKey: String!, withoutChannels: Boolean): Node!
|
||||
decodeRequest(request: String!): decodeType
|
||||
getWalletInfo: walletInfoType
|
||||
getResume(token: String): getResumeType
|
||||
getForwards(time: String): getForwardType
|
||||
getBitcoinPrice(logger: Boolean, currency: String): String
|
||||
getBitcoinFees(logger: Boolean): bitcoinFeeType
|
||||
getForwardReport(auth: authType!, time: String): String
|
||||
getForwardChannelsReport(
|
||||
auth: authType!
|
||||
time: String
|
||||
order: String
|
||||
type: String
|
||||
): String
|
||||
getInOut(auth: authType!, time: String): InOutType
|
||||
getBackups(auth: authType!): String
|
||||
verifyBackups(auth: authType!, backup: String!): Boolean
|
||||
recoverFunds(auth: authType!, backup: String!): Boolean
|
||||
getForwardReport(time: String): String
|
||||
getForwardChannelsReport(time: String, order: String, type: String): String
|
||||
getInOut(time: String): InOutType
|
||||
getBackups: String
|
||||
verifyBackups(backup: String!): Boolean
|
||||
recoverFunds(backup: String!): Boolean
|
||||
getRoutes(
|
||||
auth: authType!
|
||||
outgoing: String!
|
||||
incoming: String!
|
||||
tokens: Int!
|
||||
maxFee: Int
|
||||
): GetRouteType
|
||||
getPeers(auth: authType!): [peerType]
|
||||
signMessage(auth: authType!, message: String!): String
|
||||
verifyMessage(auth: authType!, message: String!, signature: String!): String
|
||||
getChainBalance(auth: authType!): Int
|
||||
getPendingChainBalance(auth: authType!): Int
|
||||
getChainTransactions(auth: authType!): [getTransactionsType]
|
||||
getUtxos(auth: authType!): [getUtxosType]
|
||||
getPeers: [peerType]
|
||||
signMessage(message: String!): String
|
||||
verifyMessage(message: String!, signature: String!): String
|
||||
getChainBalance: Int
|
||||
getPendingChainBalance: Int
|
||||
getChainTransactions: [getTransactionsType]
|
||||
getUtxos: [getUtxosType]
|
||||
getOffers(filter: String): [hodlOfferType]
|
||||
getCountries: [hodlCountryType]
|
||||
getCurrencies: [hodlCurrencyType]
|
||||
getMessages(
|
||||
auth: authType!
|
||||
token: String
|
||||
initialize: Boolean
|
||||
lastMessage: String
|
||||
@ -105,6 +85,7 @@ export const queryTypes = gql`
|
||||
getAuthToken(cookie: String): Boolean
|
||||
getSessionToken(id: String, password: String): Boolean
|
||||
getServerAccounts: [serverAccountType]
|
||||
getAccount: serverAccountType
|
||||
getLnPayInfo: lnPayInfoType
|
||||
getLnPay(amount: Int): String
|
||||
getLatestVersion: String
|
||||
@ -114,21 +95,18 @@ export const queryTypes = gql`
|
||||
export const mutationTypes = gql`
|
||||
type Mutation {
|
||||
closeChannel(
|
||||
auth: authType!
|
||||
id: String!
|
||||
forceClose: Boolean
|
||||
targetConfirmations: Int
|
||||
tokensPerVByte: Int
|
||||
): closeChannelType
|
||||
openChannel(
|
||||
auth: authType!
|
||||
amount: Int!
|
||||
partnerPublicKey: String!
|
||||
tokensPerVByte: Int
|
||||
isPrivate: Boolean
|
||||
): openChannelType
|
||||
updateFees(
|
||||
auth: authType!
|
||||
transaction_id: String
|
||||
transaction_vout: Int
|
||||
base_fee_tokens: Float
|
||||
@ -137,11 +115,10 @@ export const mutationTypes = gql`
|
||||
max_htlc_mtokens: String
|
||||
min_htlc_mtokens: String
|
||||
): Boolean
|
||||
keysend(auth: authType!, destination: String!, tokens: Int!): payType
|
||||
createInvoice(auth: authType!, amount: Int!): newInvoiceType
|
||||
circularRebalance(auth: authType!, route: String!): Boolean
|
||||
keysend(destination: String!, tokens: Int!): payType
|
||||
createInvoice(amount: Int!): newInvoiceType
|
||||
circularRebalance(route: String!): Boolean
|
||||
bosRebalance(
|
||||
auth: authType!
|
||||
avoid: [String]
|
||||
in_through: String
|
||||
is_avoiding_high_inbound: Boolean
|
||||
@ -153,10 +130,9 @@ export const mutationTypes = gql`
|
||||
out_through: String
|
||||
target: Int
|
||||
): bosRebalanceResultType
|
||||
payViaRoute(auth: authType!, route: String!, id: String!): Boolean
|
||||
createAddress(auth: authType!, nested: Boolean): String
|
||||
payViaRoute(route: String!, id: String!): Boolean
|
||||
createAddress(nested: Boolean): String
|
||||
sendToAddress(
|
||||
auth: authType!
|
||||
address: String!
|
||||
tokens: Int
|
||||
fee: Int
|
||||
@ -164,15 +140,13 @@ export const mutationTypes = gql`
|
||||
sendAll: Boolean
|
||||
): sendToType
|
||||
addPeer(
|
||||
auth: authType!
|
||||
url: String
|
||||
publicKey: String
|
||||
socket: String
|
||||
isTemporary: Boolean
|
||||
): Boolean
|
||||
removePeer(auth: authType!, publicKey: String!): Boolean
|
||||
removePeer(publicKey: String!): Boolean
|
||||
sendMessage(
|
||||
auth: authType!
|
||||
publicKey: String!
|
||||
message: String!
|
||||
messageType: String
|
||||
@ -180,6 +154,6 @@ export const mutationTypes = gql`
|
||||
maxFee: Int
|
||||
): Int
|
||||
logout(type: String!): Boolean
|
||||
createMacaroon(auth: authType!, permissions: permissionsType!): String
|
||||
createMacaroon(permissions: permissionsType!): String
|
||||
}
|
||||
`;
|
||||
|
@ -2,15 +2,13 @@ import { getWalletVersion } from 'ln-service';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { to } from 'server/helpers/async';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
||||
|
||||
export const walletResolvers = {
|
||||
Query: {
|
||||
getWalletInfo: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getWalletInfo');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
return await to(
|
||||
getWalletVersion({
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { getChannels } from 'ln-service';
|
||||
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';
|
||||
|
||||
@ -12,7 +11,7 @@ export const getChannelReport = async (
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'channelReport');
|
||||
|
||||
const lnd = getLnd(params.auth, context);
|
||||
const { lnd } = context;
|
||||
|
||||
const info = await to<GetChannelsType>(getChannels({ lnd }));
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { sortBy } from 'underscore';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
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,
|
||||
@ -19,8 +19,7 @@ export const getForwardChannelsReport = async (
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'forwardChannels');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
let startDate = new Date();
|
||||
const endDate = new Date();
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
} from 'date-fns';
|
||||
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';
|
||||
@ -20,8 +20,7 @@ export const getForwardReport = async (
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'forwardReport');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
let startDate = new Date();
|
||||
const endDate = new Date();
|
||||
|
@ -3,7 +3,7 @@ import { differenceInHours, differenceInCalendarDays } from 'date-fns';
|
||||
import { groupBy } from 'underscore';
|
||||
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,
|
||||
@ -18,8 +18,7 @@ export const getInOut = async (
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'getInOut');
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
const { lnd } = context;
|
||||
|
||||
const endDate = new Date();
|
||||
let periods = 7;
|
||||
|
@ -2,6 +2,14 @@ import * as res from '../lnServiceResponse';
|
||||
|
||||
export const authenticatedLndGrpc = jest.fn().mockReturnValue({});
|
||||
|
||||
export const probeForRoute = jest
|
||||
.fn()
|
||||
.mockReturnValue(Promise.resolve(res.probeForRouteResponse));
|
||||
|
||||
export const getNode = jest
|
||||
.fn()
|
||||
.mockReturnValue(Promise.resolve(res.getNodeResponse));
|
||||
|
||||
export const getNetworkInfo = jest
|
||||
.fn()
|
||||
.mockReturnValue(Promise.resolve(res.getNetworkInfoResponse));
|
||||
|
@ -342,6 +342,39 @@ export const getNodeResponse = {
|
||||
updated_at: '2011-10-05T14:48:00.000Z',
|
||||
};
|
||||
|
||||
export const probeForRouteResponse = {
|
||||
route: {
|
||||
confidence: 1000,
|
||||
fee: 1000,
|
||||
fee_mtokens: 'string',
|
||||
hops: [
|
||||
{
|
||||
channel: 'string',
|
||||
channel_capacity: 1000,
|
||||
fee: 1000,
|
||||
fee_mtokens: 'string',
|
||||
forward: 1000,
|
||||
forward_mtokens: 'string',
|
||||
public_key: 'string',
|
||||
timeout: 1000,
|
||||
},
|
||||
],
|
||||
messages: [
|
||||
{
|
||||
type: 'string',
|
||||
value: 'string',
|
||||
},
|
||||
],
|
||||
mtokens: 'string',
|
||||
payment: 'string',
|
||||
safe_fee: 1000,
|
||||
safe_tokens: 1000,
|
||||
timeout: 1000,
|
||||
tokens: 1000,
|
||||
total_mtokens: 'string',
|
||||
},
|
||||
};
|
||||
|
||||
export const getChannelResponse = {
|
||||
capacity: 1000,
|
||||
id: '100x1x1',
|
||||
|
@ -1,27 +1,21 @@
|
||||
import { ServerResponse } from 'http';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
|
||||
export const AuthMock = {
|
||||
auth: {
|
||||
type: 'test',
|
||||
},
|
||||
};
|
||||
|
||||
export const ContextMock: ContextType = {
|
||||
lnd: {},
|
||||
id: 'test',
|
||||
ip: '1.2.3.4',
|
||||
secret: '123456789',
|
||||
ssoVerified: true,
|
||||
account: 'accountID',
|
||||
sso: {
|
||||
macaroon: 'macaroon',
|
||||
cert: 'cert',
|
||||
host: 'host',
|
||||
socket: 'host',
|
||||
},
|
||||
accounts: [
|
||||
{
|
||||
name: 'account',
|
||||
id: 'accountID',
|
||||
host: 'host',
|
||||
socket: 'host',
|
||||
macaroon: 'macaroon',
|
||||
cert: 'cert',
|
||||
password: 'password',
|
||||
@ -31,30 +25,30 @@ export const ContextMock: ContextType = {
|
||||
};
|
||||
|
||||
export const ContextMockNoAccounts: ContextType = {
|
||||
lnd: {},
|
||||
id: 'test',
|
||||
ip: '1.2.3.4',
|
||||
secret: '123456789',
|
||||
ssoVerified: true,
|
||||
account: 'accountID',
|
||||
sso: {
|
||||
macaroon: 'macaroon',
|
||||
cert: 'cert',
|
||||
host: 'host',
|
||||
socket: 'host',
|
||||
},
|
||||
accounts: [],
|
||||
res: {} as ServerResponse,
|
||||
};
|
||||
|
||||
export const ContextMockNoSSO: ContextType = {
|
||||
lnd: {},
|
||||
id: 'test',
|
||||
ip: '1.2.3.4',
|
||||
secret: '123456789',
|
||||
ssoVerified: true,
|
||||
account: 'accountID',
|
||||
sso: null,
|
||||
accounts: [
|
||||
{
|
||||
name: 'account',
|
||||
id: 'accountID',
|
||||
host: 'host',
|
||||
socket: 'host',
|
||||
macaroon: 'macaroon',
|
||||
cert: 'cert',
|
||||
password: 'password',
|
||||
|
@ -3,7 +3,7 @@ import {
|
||||
ApolloServerTestClient,
|
||||
} from 'apollo-server-testing';
|
||||
import { ApolloServer } from 'apollo-server';
|
||||
import schema from 'server/schema';
|
||||
import { schema } from 'server/schema';
|
||||
import { ContextMock } from 'server/tests/testMocks';
|
||||
|
||||
export default function testServer(context?: any): ApolloServerTestClient {
|
||||
|
@ -1,15 +1,16 @@
|
||||
import { ServerResponse } from 'http';
|
||||
import { LndObject } from './ln-service.types';
|
||||
|
||||
export type SSOType = {
|
||||
macaroon: string;
|
||||
cert: string | null;
|
||||
host: string;
|
||||
socket: string;
|
||||
};
|
||||
|
||||
export type AccountType = {
|
||||
name: string;
|
||||
id: string;
|
||||
host: string;
|
||||
socket: string;
|
||||
macaroon: string;
|
||||
cert: string | null;
|
||||
password: string;
|
||||
@ -17,9 +18,9 @@ export type AccountType = {
|
||||
|
||||
export type ContextType = {
|
||||
ip: string;
|
||||
lnd: LndObject | null;
|
||||
secret: string;
|
||||
ssoVerified: boolean;
|
||||
account: string | null;
|
||||
id: string | null;
|
||||
sso: SSOType | null;
|
||||
accounts: AccountType[];
|
||||
res: ServerResponse;
|
||||
|
3
server/utils/appConstants.ts
Normal file
3
server/utils/appConstants.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const appConstants = {
|
||||
cookieName: 'Thub-Auth',
|
||||
};
|
@ -1,24 +1,11 @@
|
||||
import * as React from 'react';
|
||||
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';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useRouter } from 'next/router';
|
||||
import { appendBasePath } from 'src/utils/basePath';
|
||||
import { getUrlParam } from 'src/utils/url';
|
||||
import { useGetAuthTokenQuery } from 'src/graphql/queries/__generated__/getAuthToken.generated';
|
||||
|
||||
export const ServerAccounts: React.FC = () => {
|
||||
const { hasAccount } = useAccountState();
|
||||
const dispatch = useAccountDispatch();
|
||||
const { push, pathname, query } = useRouter();
|
||||
|
||||
const { data, loading, refetch } = useGetServerAccountsQuery();
|
||||
const { push, query } = useRouter();
|
||||
|
||||
const cookieParam = getUrlParam(query?.token);
|
||||
|
||||
@ -30,42 +17,9 @@ export const ServerAccounts: React.FC = () => {
|
||||
|
||||
React.useEffect(() => {
|
||||
if (cookieParam && authData && authData.getAuthToken) {
|
||||
refetch();
|
||||
push(appendBasePath('/'));
|
||||
}
|
||||
}, [push, authData, cookieParam, refetch]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (hasAccount === 'error' && pathname !== '/') {
|
||||
toast.error('No account found');
|
||||
dispatch({ type: 'resetFetch' });
|
||||
push(appendBasePath('/'));
|
||||
}
|
||||
}, [hasAccount, push, dispatch, pathname]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const session = sessionStorage.getItem('session') || null;
|
||||
const changeId = localStorage.getItem('active') || null;
|
||||
const savedAccounts = JSON.parse(localStorage.getItem('accounts') || '[]');
|
||||
const accountsToAdd = savedAccounts.map((a: AccountProps) =>
|
||||
addIdAndTypeToAccount(a)
|
||||
);
|
||||
dispatch({
|
||||
type: 'initialize',
|
||||
accountsToAdd,
|
||||
changeId,
|
||||
session,
|
||||
});
|
||||
}, [dispatch]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!loading && data && data.getServerAccounts) {
|
||||
dispatch({
|
||||
type: 'addServerAccounts',
|
||||
accountsToAdd: data.getServerAccounts as CompleteAccount[],
|
||||
});
|
||||
}
|
||||
}, [loading, data, dispatch]);
|
||||
}, [push, authData, cookieParam]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
@ -1,14 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useAccountState, CLIENT_ACCOUNT } from 'src/context/AccountContext';
|
||||
|
||||
export const AdminSwitch: React.FC = ({ children }) => {
|
||||
const { account, session } = useAccountState();
|
||||
|
||||
if (account?.type === CLIENT_ACCOUNT) {
|
||||
if (!account.admin && !session) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
@ -1,43 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
import { Sub4Title } from '../generic/Styled';
|
||||
import { fontColors, textColor } from '../../styles/Themes';
|
||||
|
||||
export const Line = styled.div`
|
||||
margin: 16px 0;
|
||||
`;
|
||||
|
||||
export const StyledTitle = styled(Sub4Title)`
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
margin-bottom: 0px;
|
||||
`;
|
||||
|
||||
export const CheckboxText = styled.div`
|
||||
font-size: 13px;
|
||||
color: ${fontColors.grey7};
|
||||
text-align: justify;
|
||||
`;
|
||||
|
||||
export const StyledContainer = styled.div`
|
||||
color: ${textColor};
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding-right: 32px;
|
||||
margin: 32px 0 8px;
|
||||
`;
|
||||
|
||||
export const FixedWidth = styled.div`
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
margin: 0px;
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
export const QRTextWrapper = styled.div`
|
||||
display: flex;
|
||||
margin: 16px 0;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
@ -1,50 +0,0 @@
|
||||
import React from 'react';
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { X, Check } from 'react-feather';
|
||||
import { getAuthObj } from 'src/utils/auth';
|
||||
import { useGetCanAdminQuery } from 'src/graphql/queries/__generated__/adminCheck.generated';
|
||||
import { SingleLine, Sub4Title } from '../../generic/Styled';
|
||||
import { themeColors } from '../../../styles/Themes';
|
||||
|
||||
type AdminProps = {
|
||||
host: string;
|
||||
admin: string;
|
||||
cert?: string;
|
||||
setChecked: (state: boolean) => void;
|
||||
};
|
||||
|
||||
export const AdminCheck: React.FC<AdminProps> = ({
|
||||
host,
|
||||
admin,
|
||||
cert,
|
||||
setChecked,
|
||||
}) => {
|
||||
const { data, loading } = useGetCanAdminQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
skip: !admin,
|
||||
variables: { auth: getAuthObj(host, undefined, admin, cert) },
|
||||
onError: () => {
|
||||
setChecked(false);
|
||||
},
|
||||
onCompleted: () => {
|
||||
setChecked(true);
|
||||
},
|
||||
});
|
||||
|
||||
const content = () => {
|
||||
if (loading) {
|
||||
return <ScaleLoader height={20} color={themeColors.blue3} />;
|
||||
}
|
||||
if (data?.adminCheck) {
|
||||
return <Check size={18} />;
|
||||
}
|
||||
return <X size={18} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<SingleLine>
|
||||
<Sub4Title>Admin Macaroon</Sub4Title>
|
||||
{content()}
|
||||
</SingleLine>
|
||||
);
|
||||
};
|
@ -1,155 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { Check, X } from 'react-feather';
|
||||
import { getAuthObj } from 'src/utils/auth';
|
||||
import { useGetCanConnectQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
|
||||
import { themeColors } from '../../../styles/Themes';
|
||||
import { SingleLine, Sub4Title, Separation } from '../../generic/Styled';
|
||||
import { ColorButton } from '../../buttons/colorButton/ColorButton';
|
||||
import { Text } from '../../typography/Styled';
|
||||
import { AdminCheck } from './AdminCheck';
|
||||
|
||||
type ViewProps = {
|
||||
host: string;
|
||||
admin?: string;
|
||||
viewOnly?: string;
|
||||
cert?: string;
|
||||
adminChecked: boolean;
|
||||
callback: () => void;
|
||||
setAdminChecked: (state: boolean) => void;
|
||||
handleConnect: () => void;
|
||||
setName: (name: string) => void;
|
||||
};
|
||||
|
||||
export const ViewCheck = ({
|
||||
host,
|
||||
admin,
|
||||
viewOnly,
|
||||
cert,
|
||||
adminChecked,
|
||||
setAdminChecked,
|
||||
handleConnect,
|
||||
callback,
|
||||
setName,
|
||||
}: ViewProps) => {
|
||||
const [confirmed, setConfirmed] = useState(false);
|
||||
|
||||
const { data, loading } = useGetCanConnectQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
variables: {
|
||||
auth: getAuthObj(host, viewOnly, admin, cert),
|
||||
},
|
||||
onCompleted: () => setConfirmed(true),
|
||||
onError: () => setConfirmed(false),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && data && data.getNodeInfo) {
|
||||
setName(data.getNodeInfo.alias);
|
||||
}
|
||||
}, [loading, data, setName]);
|
||||
|
||||
const content = () => {
|
||||
if (loading) {
|
||||
return <ScaleLoader height={20} color={themeColors.blue3} />;
|
||||
}
|
||||
if (data?.getNodeInfo?.alias) {
|
||||
return <Check size={18} />;
|
||||
}
|
||||
return <X size={18} />;
|
||||
};
|
||||
|
||||
const renderInfo = () => {
|
||||
if (!loading && data && data.getNodeInfo) {
|
||||
return (
|
||||
<>
|
||||
<SingleLine>
|
||||
<Sub4Title>Alias</Sub4Title>
|
||||
<Sub4Title>{data.getNodeInfo.alias}</Sub4Title>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Synced To Chain</Sub4Title>
|
||||
<Sub4Title>
|
||||
{data.getNodeInfo.is_synced_to_chain ? 'Yes' : 'No'}
|
||||
</Sub4Title>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Version</Sub4Title>
|
||||
<Sub4Title>{data.getNodeInfo.version.split(' ')[0]}</Sub4Title>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Active Channels</Sub4Title>
|
||||
<Sub4Title>{data.getNodeInfo.active_channels_count}</Sub4Title>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Pending Channels</Sub4Title>
|
||||
<Sub4Title>{data.getNodeInfo.pending_channels_count}</Sub4Title>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<Sub4Title>Closed Channels</Sub4Title>
|
||||
<Sub4Title>{data.getNodeInfo.closed_channels_count}</Sub4Title>
|
||||
</SingleLine>
|
||||
<Separation />
|
||||
</>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderTitle = () => {
|
||||
if (!confirmed) {
|
||||
return 'Go Back';
|
||||
}
|
||||
if (adminChecked && !viewOnly && admin) {
|
||||
return 'Connect (Admin-Only)';
|
||||
}
|
||||
if (!adminChecked && viewOnly) {
|
||||
return 'Connect (View-Only)';
|
||||
}
|
||||
if (!adminChecked && admin) {
|
||||
return 'Connect (View-Only)';
|
||||
}
|
||||
return 'Connect';
|
||||
};
|
||||
|
||||
const renderText = () => (
|
||||
<Text>
|
||||
Failed to connect to node. Please verify the information provided.
|
||||
</Text>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{renderInfo()}
|
||||
{!confirmed && !loading && renderText()}
|
||||
<SingleLine>
|
||||
<Sub4Title>{viewOnly ? 'View-Only Macaroon' : 'Connected'}</Sub4Title>
|
||||
{content()}
|
||||
</SingleLine>
|
||||
{admin && confirmed && (
|
||||
<AdminCheck
|
||||
host={host}
|
||||
admin={admin}
|
||||
cert={cert}
|
||||
setChecked={setAdminChecked}
|
||||
/>
|
||||
)}
|
||||
<ColorButton
|
||||
fullWidth={true}
|
||||
withMargin={'16px 0 0'}
|
||||
disabled={loading}
|
||||
loading={loading}
|
||||
arrow={confirmed}
|
||||
onClick={() => {
|
||||
if (confirmed) {
|
||||
handleConnect();
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{renderTitle()}
|
||||
</ColorButton>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,169 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import AES from 'crypto-js/aes';
|
||||
import { useRouter } from 'next/router';
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
useAccountState,
|
||||
useAccountDispatch,
|
||||
} from 'src/context/AccountContext';
|
||||
import { getAccountId } from '../../utils/auth';
|
||||
import { useStatusDispatch } from '../../context/StatusContext';
|
||||
import { LoadingCard } from '../loading/LoadingCard';
|
||||
import { appendBasePath } from '../../utils/basePath';
|
||||
import { useChatDispatch } from '../../context/ChatContext';
|
||||
import { ViewCheck } from './checks/ViewCheck';
|
||||
import { BTCLoginForm } from './views/BTCLogin';
|
||||
import { ConnectLoginForm } from './views/ConnectLogin';
|
||||
import { LoginForm } from './views/NormalLogin';
|
||||
|
||||
const PasswordInput = dynamic(() => import('./views/Password'), {
|
||||
ssr: false,
|
||||
loading: function Loading() {
|
||||
return <LoadingCard noCard={true} />;
|
||||
},
|
||||
});
|
||||
|
||||
type AuthProps = {
|
||||
type: string;
|
||||
status: string;
|
||||
callback: () => void;
|
||||
setStatus: (state: string) => void;
|
||||
};
|
||||
|
||||
export const Auth = ({ type, status, callback, setStatus }: AuthProps) => {
|
||||
const { accounts } = useAccountState();
|
||||
const { push } = useRouter();
|
||||
|
||||
const dispatch = useStatusDispatch();
|
||||
const dispatchChat = useChatDispatch();
|
||||
const dispatchAccount = useAccountDispatch();
|
||||
|
||||
const [name, setName] = useState<string>();
|
||||
const [host, setHost] = useState<string>();
|
||||
const [admin, setAdmin] = useState<string>();
|
||||
const [viewOnly, setViewOnly] = useState<string>();
|
||||
const [cert, setCert] = useState<string>();
|
||||
const [password, setPassword] = useState<string>();
|
||||
|
||||
const [adminChecked, setAdminChecked] = useState(false);
|
||||
|
||||
const handleSet = ({
|
||||
host,
|
||||
admin,
|
||||
viewOnly,
|
||||
cert,
|
||||
}: {
|
||||
host?: string;
|
||||
admin?: string;
|
||||
viewOnly?: string;
|
||||
cert?: string;
|
||||
}) => {
|
||||
const id = getAccountId(
|
||||
host ?? '',
|
||||
viewOnly ?? '',
|
||||
admin ?? '',
|
||||
cert ?? ''
|
||||
);
|
||||
|
||||
const accountExists = accounts
|
||||
? accounts.filter(account => account.id === id).length > 0
|
||||
: false;
|
||||
|
||||
if (accountExists) {
|
||||
toast.error('Account already exists.');
|
||||
} else if (!host) {
|
||||
toast.error('A host url is needed to connect.');
|
||||
} else if (!admin && !viewOnly) {
|
||||
toast.error('View-Only or Admin macaroon are needed to connect.');
|
||||
} else {
|
||||
host && setHost(host);
|
||||
admin && setAdmin(admin);
|
||||
viewOnly && setViewOnly(viewOnly);
|
||||
cert && setCert(cert);
|
||||
|
||||
setStatus('confirmNode');
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
if (!host) {
|
||||
toast.error('A host url is needed to connect.');
|
||||
} else if (!admin && !viewOnly) {
|
||||
toast.error('View-Only or Admin macaroon are needed to connect.');
|
||||
} else {
|
||||
let correctViewOnly = viewOnly || null;
|
||||
if (!viewOnly && admin && !password) {
|
||||
correctViewOnly = admin;
|
||||
}
|
||||
|
||||
const encryptedAdmin =
|
||||
admin && password ? AES.encrypt(admin, password).toString() : null;
|
||||
|
||||
dispatch({ type: 'disconnected' });
|
||||
dispatchChat({ type: 'disconnected' });
|
||||
dispatchAccount({
|
||||
type: 'addAccountAndSave',
|
||||
accountToAdd: {
|
||||
name: name || '',
|
||||
host,
|
||||
admin: encryptedAdmin || '',
|
||||
viewOnly: correctViewOnly || '',
|
||||
cert: cert || '',
|
||||
},
|
||||
...(!correctViewOnly && { session: admin }),
|
||||
});
|
||||
|
||||
push(appendBasePath('/home'));
|
||||
}
|
||||
};
|
||||
|
||||
const handleConnect = () => {
|
||||
if (adminChecked) {
|
||||
setStatus('password');
|
||||
} else {
|
||||
handleSave();
|
||||
}
|
||||
};
|
||||
|
||||
const renderView = () => {
|
||||
switch (type) {
|
||||
case 'login':
|
||||
return <LoginForm handleSet={handleSet} />;
|
||||
case 'connect':
|
||||
return <ConnectLoginForm handleSet={handleSet} />;
|
||||
default:
|
||||
return <BTCLoginForm handleSet={handleSet} />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{status === 'none' && renderView()}
|
||||
{status === 'confirmNode' && host && (
|
||||
<ViewCheck
|
||||
host={host}
|
||||
admin={admin}
|
||||
viewOnly={viewOnly}
|
||||
cert={cert}
|
||||
adminChecked={adminChecked}
|
||||
setAdminChecked={setAdminChecked}
|
||||
handleConnect={handleConnect}
|
||||
callback={callback}
|
||||
setName={setName}
|
||||
/>
|
||||
)}
|
||||
{status === 'password' && (
|
||||
<PasswordInput
|
||||
isPass={password}
|
||||
setPass={setPassword}
|
||||
callback={() => {
|
||||
handleSave();
|
||||
setStatus('none');
|
||||
}}
|
||||
loading={false}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,51 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getConfigLnd } from '../../../utils/auth';
|
||||
import { Input } from '../../input/Input';
|
||||
import { Line, StyledTitle } from '../Auth.styled';
|
||||
import { RiskCheckboxAndConfirm } from './Checkboxes';
|
||||
|
||||
interface AuthProps {
|
||||
handleSet: ({
|
||||
host,
|
||||
admin,
|
||||
viewOnly,
|
||||
cert,
|
||||
}: {
|
||||
host?: string;
|
||||
admin?: string;
|
||||
viewOnly?: string;
|
||||
cert?: string;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
export const BTCLoginForm = ({ handleSet }: AuthProps) => {
|
||||
const [json, setJson] = useState('');
|
||||
const [checked, setChecked] = useState(false);
|
||||
|
||||
const handleClick = () => {
|
||||
try {
|
||||
JSON.parse(json);
|
||||
const { cert, admin, viewOnly, host } = getConfigLnd(json);
|
||||
handleSet({ host, admin, viewOnly, cert });
|
||||
} catch (error) {
|
||||
toast.error('Invalid JSON');
|
||||
}
|
||||
};
|
||||
|
||||
const canConnect = json !== '' && checked;
|
||||
return (
|
||||
<>
|
||||
<Line>
|
||||
<StyledTitle>BTCPayServer Connect JSON:</StyledTitle>
|
||||
<Input onChange={e => setJson(e.target.value)} />
|
||||
</Line>
|
||||
<RiskCheckboxAndConfirm
|
||||
disabled={!canConnect}
|
||||
handleClick={handleClick}
|
||||
checked={checked}
|
||||
onChange={setChecked}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,35 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Checkbox } from '../../checkbox/Checkbox';
|
||||
import { CheckboxText } from '../Auth.styled';
|
||||
import { ColorButton } from '../../buttons/colorButton/ColorButton';
|
||||
|
||||
type CheckboxProps = {
|
||||
handleClick: () => void;
|
||||
disabled: boolean;
|
||||
checked: boolean;
|
||||
onChange: (state: boolean) => void;
|
||||
};
|
||||
|
||||
export const RiskCheckboxAndConfirm = ({
|
||||
handleClick,
|
||||
disabled,
|
||||
checked,
|
||||
onChange,
|
||||
}: CheckboxProps) => (
|
||||
<>
|
||||
<Checkbox checked={checked} onChange={onChange}>
|
||||
<CheckboxText>
|
||||
{`I'm feeling reckless. Lightning, LND and ThunderHub are under constant development and I understand that there is always a risk of losing funds.`}
|
||||
</CheckboxText>
|
||||
</Checkbox>
|
||||
<ColorButton
|
||||
disabled={disabled}
|
||||
onClick={handleClick}
|
||||
withMargin={'32px 0 0'}
|
||||
fullWidth={true}
|
||||
arrow={true}
|
||||
>
|
||||
Connect
|
||||
</ColorButton>
|
||||
</>
|
||||
);
|
@ -1,52 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { getAuthLnd, getBase64CertfromDerFormat } from '../../../utils/auth';
|
||||
import { Input } from '../../input/Input';
|
||||
import { Line, StyledTitle } from '../Auth.styled';
|
||||
import { RiskCheckboxAndConfirm } from './Checkboxes';
|
||||
|
||||
interface AuthProps {
|
||||
handleSet: ({
|
||||
host,
|
||||
admin,
|
||||
viewOnly,
|
||||
cert,
|
||||
}: {
|
||||
host?: string;
|
||||
admin?: string;
|
||||
viewOnly?: string;
|
||||
cert?: string;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
export const ConnectLoginForm = ({ handleSet }: AuthProps) => {
|
||||
const [url, setUrl] = useState('');
|
||||
const [checked, setChecked] = useState(false);
|
||||
|
||||
const handleClick = () => {
|
||||
const { cert, macaroon, socket } = getAuthLnd(url);
|
||||
const base64Cert = getBase64CertfromDerFormat(cert) || '';
|
||||
|
||||
handleSet({
|
||||
host: socket,
|
||||
admin: macaroon,
|
||||
cert: base64Cert,
|
||||
});
|
||||
};
|
||||
|
||||
const canConnect = url !== '' && checked;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Line>
|
||||
<StyledTitle>LND Connect Url:</StyledTitle>
|
||||
<Input onChange={e => setUrl(e.target.value)} />
|
||||
</Line>
|
||||
<RiskCheckboxAndConfirm
|
||||
disabled={!canConnect}
|
||||
handleClick={handleClick}
|
||||
checked={checked}
|
||||
onChange={setChecked}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,99 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Input } from '../../input/Input';
|
||||
import { Line, StyledTitle } from '../Auth.styled';
|
||||
import { SingleLine, Sub4Title } from '../../generic/Styled';
|
||||
import {
|
||||
MultiButton,
|
||||
SingleButton,
|
||||
} from '../../buttons/multiButton/MultiButton';
|
||||
import { RiskCheckboxAndConfirm } from './Checkboxes';
|
||||
|
||||
interface AuthProps {
|
||||
handleSet: ({
|
||||
host,
|
||||
admin,
|
||||
viewOnly,
|
||||
cert,
|
||||
}: {
|
||||
host?: string;
|
||||
admin?: string;
|
||||
viewOnly?: string;
|
||||
cert?: string;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
export const LoginForm = ({ handleSet }: AuthProps) => {
|
||||
const [isViewOnly, setIsViewOnly] = useState(true);
|
||||
const [checked, setChecked] = useState(false);
|
||||
|
||||
const [host, setHost] = useState('');
|
||||
const [admin, setAdmin] = useState('');
|
||||
const [viewOnly, setRead] = useState('');
|
||||
const [cert, setCert] = useState('');
|
||||
|
||||
const handleClick = () => {
|
||||
handleSet({ host, admin, viewOnly, cert });
|
||||
};
|
||||
|
||||
const canConnect =
|
||||
host !== '' && (admin !== '' || viewOnly !== '') && checked;
|
||||
return (
|
||||
<>
|
||||
<SingleLine>
|
||||
<Sub4Title>Type of Account:</Sub4Title>
|
||||
<MultiButton>
|
||||
<SingleButton
|
||||
selected={isViewOnly}
|
||||
onClick={() => setIsViewOnly(true)}
|
||||
>
|
||||
ViewOnly
|
||||
</SingleButton>
|
||||
<SingleButton
|
||||
selected={!isViewOnly}
|
||||
onClick={() => setIsViewOnly(false)}
|
||||
>
|
||||
Admin
|
||||
</SingleButton>
|
||||
</MultiButton>
|
||||
</SingleLine>
|
||||
<Line>
|
||||
<StyledTitle>{'Url (host:grpc port):'}</StyledTitle>
|
||||
<Input
|
||||
placeholder={'Host and port (e.g.: www.node.com:443)'}
|
||||
onChange={e => setHost(e.target.value)}
|
||||
/>
|
||||
</Line>
|
||||
{!isViewOnly && (
|
||||
<Line>
|
||||
<StyledTitle>Admin:</StyledTitle>
|
||||
<Input
|
||||
placeholder={'Base64 or HEX Admin macaroon'}
|
||||
onChange={e => setAdmin(e.target.value)}
|
||||
/>
|
||||
</Line>
|
||||
)}
|
||||
<Line>
|
||||
<StyledTitle>
|
||||
{!isViewOnly ? 'Readonly (optional):' : 'Readonly:'}
|
||||
</StyledTitle>
|
||||
<Input
|
||||
placeholder={'Base64 or HEX Readonly macaroon'}
|
||||
onChange={e => setRead(e.target.value)}
|
||||
/>
|
||||
</Line>
|
||||
<Line>
|
||||
<StyledTitle>Certificate:</StyledTitle>
|
||||
<Input
|
||||
placeholder={'Base64 or HEX TLS Certificate'}
|
||||
onChange={e => setCert(e.target.value)}
|
||||
/>
|
||||
</Line>
|
||||
<RiskCheckboxAndConfirm
|
||||
disabled={!canConnect}
|
||||
handleClick={handleClick}
|
||||
checked={checked}
|
||||
onChange={setChecked}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,66 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import zxcvbn from 'zxcvbn';
|
||||
import getConfig from 'next/config';
|
||||
import { Sub4Title, SubTitle, DarkSubTitle } from '../../generic/Styled';
|
||||
import { ColorButton } from '../../buttons/colorButton/ColorButton';
|
||||
import { Input } from '../../input/Input';
|
||||
import { Line, CheckboxText } from '../Auth.styled';
|
||||
import { LoadingBar } from '../../loadingBar/LoadingBar';
|
||||
import { Checkbox } from '../../checkbox/Checkbox';
|
||||
|
||||
interface PasswordProps {
|
||||
isPass?: string;
|
||||
setPass: (pass: string) => void;
|
||||
callback: () => void;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { nodeEnv } = publicRuntimeConfig;
|
||||
|
||||
const PasswordInput = ({
|
||||
isPass = '',
|
||||
setPass,
|
||||
callback,
|
||||
loading = false,
|
||||
}: PasswordProps) => {
|
||||
const [checked, setChecked] = useState(false);
|
||||
const strength = (100 * Math.min(zxcvbn(isPass).guesses_log10, 40)) / 40;
|
||||
const needed = nodeEnv === 'development' ? 1 : checked ? 10 : 20;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubTitle>Please Input a Password</SubTitle>
|
||||
<DarkSubTitle>
|
||||
This password will be used to encrypt your admin macaroon.
|
||||
</DarkSubTitle>
|
||||
<Line>
|
||||
<Sub4Title>Password:</Sub4Title>
|
||||
<Input onChange={e => setPass(e.target.value)} />
|
||||
</Line>
|
||||
<Line>
|
||||
<Sub4Title>Strength:</Sub4Title>
|
||||
<LoadingBar percent={strength} />
|
||||
</Line>
|
||||
<Line>
|
||||
<Checkbox checked={checked} onChange={setChecked}>
|
||||
<CheckboxText>
|
||||
{'Disable Strong Password Check (Not Recommended)'}
|
||||
</CheckboxText>
|
||||
</Checkbox>
|
||||
</Line>
|
||||
<ColorButton
|
||||
disabled={strength < needed}
|
||||
onClick={callback}
|
||||
withMargin={'32px 0 0'}
|
||||
fullWidth={true}
|
||||
arrow={true}
|
||||
loading={loading}
|
||||
>
|
||||
Connect
|
||||
</ColorButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PasswordInput;
|
@ -1,104 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ChevronRight } from 'react-feather';
|
||||
import {
|
||||
useAccountState,
|
||||
useAccountDispatch,
|
||||
} from 'src/context/AccountContext';
|
||||
import { getAuthFromAccount } from 'src/context/helpers/context';
|
||||
import {
|
||||
Sub4Title,
|
||||
NoWrapTitle,
|
||||
SubTitle,
|
||||
ResponsiveLine,
|
||||
} from '../../generic/Styled';
|
||||
import { ColorButton } from '../colorButton/ColorButton';
|
||||
import { Input } from '../../input/Input';
|
||||
import { MultiButton, SingleButton } from '../multiButton/MultiButton';
|
||||
|
||||
interface LoginProps {
|
||||
macaroon: string;
|
||||
color?: string;
|
||||
callback: (variables: {}) => void;
|
||||
variables: {};
|
||||
setModalOpen: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export const LoginModal = ({
|
||||
macaroon,
|
||||
color,
|
||||
setModalOpen,
|
||||
callback,
|
||||
variables,
|
||||
}: LoginProps) => {
|
||||
const [pass, setPass] = useState<string>('');
|
||||
const [storeSession, setStoreSession] = useState<boolean>(false);
|
||||
|
||||
const { account } = useAccountState();
|
||||
const dispatch = useAccountDispatch();
|
||||
|
||||
const handleClick = () => {
|
||||
try {
|
||||
const bytes = CryptoJS.AES.decrypt(macaroon, pass);
|
||||
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
||||
|
||||
if (storeSession) {
|
||||
dispatch({ type: 'addSession', session: decrypted });
|
||||
}
|
||||
|
||||
callback({
|
||||
variables: {
|
||||
...variables,
|
||||
auth: getAuthFromAccount(account, decrypted),
|
||||
},
|
||||
});
|
||||
|
||||
setModalOpen(false);
|
||||
} catch (error) {
|
||||
toast.error('Wrong Password');
|
||||
}
|
||||
};
|
||||
|
||||
const renderButton = (
|
||||
onClick: () => void,
|
||||
text: string,
|
||||
selected: boolean
|
||||
) => (
|
||||
<SingleButton selected={selected} color={color} onClick={onClick}>
|
||||
{text}
|
||||
</SingleButton>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubTitle>Unlock your Account</SubTitle>
|
||||
<ResponsiveLine>
|
||||
<Sub4Title>Password:</Sub4Title>
|
||||
<Input
|
||||
withMargin={'0 0 0 16px'}
|
||||
mobileMargin={'0'}
|
||||
type={'password'}
|
||||
onChange={e => setPass(e.target.value)}
|
||||
/>
|
||||
</ResponsiveLine>
|
||||
<ResponsiveLine>
|
||||
<NoWrapTitle>{`Don't ask me again this session:`}</NoWrapTitle>
|
||||
<MultiButton>
|
||||
{renderButton(() => setStoreSession(true), 'Yes', storeSession)}
|
||||
{renderButton(() => setStoreSession(false), 'No', !storeSession)}
|
||||
</MultiButton>
|
||||
</ResponsiveLine>
|
||||
<ColorButton
|
||||
disabled={pass === ''}
|
||||
onClick={handleClick}
|
||||
color={color}
|
||||
fullWidth={true}
|
||||
withMargin={'16px 0 0'}
|
||||
>
|
||||
Unlock
|
||||
<ChevronRight size={18} />
|
||||
</ColorButton>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,75 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useAccountState, CLIENT_ACCOUNT } from 'src/context/AccountContext';
|
||||
import { getAuthFromAccount } from 'src/context/helpers/context';
|
||||
import Modal from '../../modal/ReactModal';
|
||||
import { ColorButton, ColorButtonProps } from '../colorButton/ColorButton';
|
||||
import { LoginModal } from './LoginModal';
|
||||
|
||||
interface SecureButtonProps extends ColorButtonProps {
|
||||
callback: (variables: {}) => void;
|
||||
disabled: boolean;
|
||||
variables: {};
|
||||
color?: string;
|
||||
withMargin?: string;
|
||||
mobileMargin?: string;
|
||||
arrow?: boolean;
|
||||
}
|
||||
|
||||
export const SecureButton: React.FC<SecureButtonProps> = ({
|
||||
callback,
|
||||
color,
|
||||
disabled,
|
||||
children,
|
||||
variables,
|
||||
...props
|
||||
}) => {
|
||||
const [modalOpen, setModalOpen] = useState<boolean>(false);
|
||||
|
||||
const { session, account } = useAccountState();
|
||||
|
||||
if (!account) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
account &&
|
||||
account.type === CLIENT_ACCOUNT &&
|
||||
!account.admin &&
|
||||
!session
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const auth = getAuthFromAccount(account, session);
|
||||
|
||||
const handleClick = () => setModalOpen(true);
|
||||
|
||||
const onClick =
|
||||
session || account.type !== CLIENT_ACCOUNT
|
||||
? () => callback({ variables: { ...variables, auth } })
|
||||
: handleClick;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ColorButton
|
||||
color={color}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</ColorButton>
|
||||
{account.type === CLIENT_ACCOUNT && (
|
||||
<Modal isOpen={modalOpen} closeCallback={() => setModalOpen(false)}>
|
||||
<LoginModal
|
||||
color={color}
|
||||
macaroon={account.admin}
|
||||
setModalOpen={setModalOpen}
|
||||
callback={callback}
|
||||
variables={variables}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,58 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useAccountState, CLIENT_ACCOUNT } from 'src/context/AccountContext';
|
||||
import { getAuthFromAccount } from 'src/context/helpers/context';
|
||||
import Modal from '../../modal/ReactModal';
|
||||
import { LoginModal } from './LoginModal';
|
||||
|
||||
interface SecureButtonProps {
|
||||
callback: (variables: {}) => void;
|
||||
variables: {};
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export const SecureWrapper: React.FC<SecureButtonProps> = ({
|
||||
callback,
|
||||
children,
|
||||
variables,
|
||||
color,
|
||||
}) => {
|
||||
const [modalOpen, setModalOpen] = useState<boolean>(false);
|
||||
|
||||
const { account, session } = useAccountState();
|
||||
|
||||
if (!account) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (account.type === CLIENT_ACCOUNT && !account.admin && !session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const auth = getAuthFromAccount(account, session);
|
||||
|
||||
const handleClick = () => setModalOpen(true);
|
||||
|
||||
const onClick =
|
||||
session || account.type !== CLIENT_ACCOUNT
|
||||
? () => callback({ variables: { ...variables, auth } })
|
||||
: handleClick;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div role={'button'} onClick={onClick} onKeyDown={onClick} tabIndex={0}>
|
||||
{children}
|
||||
</div>
|
||||
{account.type === CLIENT_ACCOUNT && (
|
||||
<Modal isOpen={modalOpen} closeCallback={() => setModalOpen(false)}>
|
||||
<LoginModal
|
||||
color={color}
|
||||
macaroon={account.admin}
|
||||
setModalOpen={setModalOpen}
|
||||
callback={callback}
|
||||
variables={variables}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,10 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
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 { useAccount } from 'src/hooks/UseAccount';
|
||||
import { useChatState, useChatDispatch } from '../../context/ChatContext';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
@ -14,8 +13,7 @@ export const ChatFetcher: React.FC = () => {
|
||||
|
||||
const { chatPollingSpeed } = useConfigState();
|
||||
|
||||
const { connected } = useStatusState();
|
||||
const { auth } = useAccountState();
|
||||
const account = useAccount();
|
||||
const { pathname } = useRouter();
|
||||
const { lastChat, chats, sentChats, initialized } = useChatState();
|
||||
const dispatch = useChatDispatch();
|
||||
@ -24,10 +22,10 @@ export const ChatFetcher: React.FC = () => {
|
||||
|
||||
const { data, loading, error } = useGetMessagesQuery({
|
||||
ssr: false,
|
||||
skip: !auth || initialized || noChatsAvailable || !connected,
|
||||
skip: initialized || noChatsAvailable || !account,
|
||||
pollInterval: chatPollingSpeed,
|
||||
fetchPolicy: 'network-only',
|
||||
variables: { auth, initialize: !noChatsAvailable },
|
||||
variables: { initialize: !noChatsAvailable },
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
|
@ -1,23 +1,24 @@
|
||||
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 { useAccount } from 'src/hooks/UseAccount';
|
||||
import { useChatDispatch } from '../../context/ChatContext';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
|
||||
export const ChatInit: React.FC = () => {
|
||||
const { auth, account } = useAccountState();
|
||||
const dispatch = useChatDispatch();
|
||||
|
||||
const [
|
||||
getMessages,
|
||||
{ data: initData, loading: initLoading, error: initError },
|
||||
] = useGetMessagesLazyQuery({
|
||||
variables: { auth, initialize: true },
|
||||
variables: { initialize: true },
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
const account = useAccount();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (account) {
|
||||
const storageChats =
|
||||
|
@ -2,12 +2,10 @@ import React from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { BitcoinFees } from 'src/components/bitcoinInfo/BitcoinFees';
|
||||
import { BitcoinPrice } from 'src/components/bitcoinInfo/BitcoinPrice';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { mediaWidths } from '../../styles/Themes';
|
||||
import { Section } from '../section/Section';
|
||||
import { Navigation } from '../../layouts/navigation/Navigation';
|
||||
import { StatusCheck } from '../statusCheck/StatusCheck';
|
||||
import { LoadingCard } from '../loading/LoadingCard';
|
||||
import { ServerAccounts } from '../accounts/ServerAccounts';
|
||||
|
||||
type GridProps = {
|
||||
@ -38,24 +36,15 @@ const ContentStyle = styled.div`
|
||||
export const GridWrapper: React.FC<GridProps> = ({
|
||||
children,
|
||||
noNavigation,
|
||||
}) => {
|
||||
const { hasAccount, auth } = useAccountState();
|
||||
const renderContent = () => {
|
||||
if (hasAccount === 'false') {
|
||||
return <LoadingCard loadingHeight={'50vh'} noCard={true} />;
|
||||
}
|
||||
return children;
|
||||
};
|
||||
return (
|
||||
<Section padding={'16px 0 32px'}>
|
||||
<Container noNavigation={noNavigation}>
|
||||
<ServerAccounts />
|
||||
<BitcoinPrice />
|
||||
<BitcoinFees />
|
||||
{auth && <StatusCheck />}
|
||||
{!noNavigation && <Navigation />}
|
||||
<ContentStyle>{renderContent()}</ContentStyle>
|
||||
</Container>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
}) => (
|
||||
<Section padding={'16px 0 32px'}>
|
||||
<Container noNavigation={noNavigation}>
|
||||
<ServerAccounts />
|
||||
<BitcoinPrice />
|
||||
<BitcoinFees />
|
||||
<StatusCheck />
|
||||
{!noNavigation && <Navigation />}
|
||||
<ContentStyle>{children}</ContentStyle>
|
||||
</Container>
|
||||
</Section>
|
||||
);
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
Sub4Title,
|
||||
} from '../../generic/Styled';
|
||||
import { getErrorContent } from '../../../utils/error';
|
||||
import { SecureButton } from '../../buttons/secureButton/SecureButton';
|
||||
import { ColorButton } from '../../buttons/colorButton/ColorButton';
|
||||
import {
|
||||
MultiButton,
|
||||
@ -48,7 +47,7 @@ export const CloseChannel = ({
|
||||
const [amount, setAmount] = useState<number>(0);
|
||||
const [isConfirmed, setIsConfirmed] = useState<boolean>(false);
|
||||
|
||||
const [closeChannel] = useCloseChannelMutation({
|
||||
const [closeChannel, { loading }] = useCloseChannelMutation({
|
||||
onCompleted: data => {
|
||||
if (data.closeChannel) {
|
||||
toast.success('Channel Closed');
|
||||
@ -79,23 +78,27 @@ export const CloseChannel = ({
|
||||
<WarningCard>
|
||||
<AlertTriangle size={32} color={'red'} />
|
||||
<SubTitle>Are you sure you want to close the channel?</SubTitle>
|
||||
<SecureButton
|
||||
callback={closeChannel}
|
||||
variables={{
|
||||
id: channelId,
|
||||
forceClose: isForce,
|
||||
...(isType !== 'none'
|
||||
? isType === 'fee'
|
||||
? { tokens: amount }
|
||||
: { target: amount }
|
||||
: {}),
|
||||
}}
|
||||
color={'red'}
|
||||
disabled={false}
|
||||
<ColorButton
|
||||
disabled={loading}
|
||||
loading={loading}
|
||||
withMargin={'4px'}
|
||||
color={'red'}
|
||||
onClick={() =>
|
||||
closeChannel({
|
||||
variables: {
|
||||
id: channelId,
|
||||
forceClose: isForce,
|
||||
...(isType !== 'none'
|
||||
? isType === 'fee'
|
||||
? { tokens: amount }
|
||||
: { target: amount }
|
||||
: {}),
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
{`Close Channel [ ${channelName}/${channelId} ]`}
|
||||
</SecureButton>
|
||||
</ColorButton>
|
||||
<ColorButton withMargin={'4px'} onClick={handleOnlyClose}>
|
||||
Cancel
|
||||
</ColorButton>
|
||||
|
@ -5,7 +5,6 @@ import { toast } from 'react-toastify';
|
||||
import { useRemovePeerMutation } from 'src/graphql/mutations/__generated__/removePeer.generated';
|
||||
import { SubTitle } from '../../generic/Styled';
|
||||
import { getErrorContent } from '../../../utils/error';
|
||||
import { SecureButton } from '../../buttons/secureButton/SecureButton';
|
||||
import { ColorButton } from '../../buttons/colorButton/ColorButton';
|
||||
|
||||
interface RemovePeerProps {
|
||||
@ -42,17 +41,17 @@ export const RemovePeerModal = ({
|
||||
<WarningCard>
|
||||
<AlertTriangle size={32} color={'red'} />
|
||||
<SubTitle>Are you sure you want to remove this peer?</SubTitle>
|
||||
<SecureButton
|
||||
callback={removePeer}
|
||||
variables={{
|
||||
publicKey,
|
||||
<ColorButton
|
||||
onClick={() => {
|
||||
removePeer({ variables: { publicKey } });
|
||||
}}
|
||||
color={'red'}
|
||||
disabled={loading}
|
||||
loading={loading}
|
||||
withMargin={'4px'}
|
||||
>
|
||||
{`Remove Peer [${peerAlias || publicKey?.substring(0, 6)}]`}
|
||||
</SecureButton>
|
||||
</ColorButton>
|
||||
<ColorButton withMargin={'4px'} onClick={handleOnlyClose}>
|
||||
Cancel
|
||||
</ColorButton>
|
||||
|
@ -1,93 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { HelpCircle } from 'react-feather';
|
||||
import styled from 'styled-components';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import {
|
||||
useAccountState,
|
||||
CLIENT_ACCOUNT,
|
||||
AccountType,
|
||||
} from 'src/context/AccountContext';
|
||||
import { CardWithTitle, SubTitle } from '../generic/Styled';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
import {
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
StyledNodeBar,
|
||||
NodeBarContainer,
|
||||
} from './NodeInfo.styled';
|
||||
import { NodeCard } from './NodeCard';
|
||||
|
||||
const StyledQuestion = styled(HelpCircle)`
|
||||
margin-left: 8px;
|
||||
`;
|
||||
|
||||
export const NodeBar = () => {
|
||||
const { accounts } = useAccountState();
|
||||
const { multiNodeInfo } = useConfigState();
|
||||
const slider = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const viewOnlyAccounts = accounts.filter(
|
||||
account => account.type === CLIENT_ACCOUNT && account.viewOnly !== ''
|
||||
) as AccountType[];
|
||||
|
||||
const handleScroll = (decrease?: boolean) => {
|
||||
if (slider.current !== null) {
|
||||
if (decrease) {
|
||||
slider.current.scrollLeft -= 240;
|
||||
} else {
|
||||
slider.current.scrollLeft += 240;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (viewOnlyAccounts.length <= 1 || !multiNodeInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<CardWithTitle>
|
||||
<SubTitle>
|
||||
Your Nodes
|
||||
<span data-tip data-for="node_info_question">
|
||||
<StyledQuestion size={14} />
|
||||
</span>
|
||||
</SubTitle>
|
||||
<NodeBarContainer>
|
||||
<div
|
||||
role={'button'}
|
||||
onClick={() => {
|
||||
handleScroll(true);
|
||||
}}
|
||||
onKeyDown={() => {
|
||||
handleScroll(true);
|
||||
}}
|
||||
tabIndex={0}
|
||||
>
|
||||
<ArrowLeft size={18} />
|
||||
</div>
|
||||
<div
|
||||
role={'button'}
|
||||
onClick={() => {
|
||||
handleScroll();
|
||||
}}
|
||||
onKeyDown={() => {
|
||||
handleScroll();
|
||||
}}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ArrowRight size={18} />
|
||||
</div>
|
||||
<StyledNodeBar ref={slider}>
|
||||
{viewOnlyAccounts.map(account => (
|
||||
<React.Fragment key={account.id}>
|
||||
<NodeCard account={account} accountId={account.id} />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</StyledNodeBar>
|
||||
</NodeBarContainer>
|
||||
<ReactTooltip id={'node_info_question'} effect={'solid'} place={'right'}>
|
||||
Only accounts with a view-only macaroon will appear here.
|
||||
</ReactTooltip>
|
||||
</CardWithTitle>
|
||||
);
|
||||
};
|
@ -1,131 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import 'intersection-observer'; // Polyfill
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { useGetNodeInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
|
||||
import { AccountType } from 'src/context/AccountContext';
|
||||
import { SingleLine, DarkSubTitle, ResponsiveLine } from '../generic/Styled';
|
||||
import { themeColors } from '../../styles/Themes';
|
||||
import { Price } from '../price/Price';
|
||||
import Modal from '../modal/ReactModal';
|
||||
import { getAuthObj } from '../../utils/auth';
|
||||
import { StatusDot, StatusLine, QuickCard } from './NodeInfo.styled';
|
||||
import { NodeInfoModal } from './NodeInfoModal';
|
||||
|
||||
export const getStatusDot = (status: boolean) => {
|
||||
return status ? <StatusDot color="#95de64" /> : <StatusDot color="#ff4d4f" />;
|
||||
};
|
||||
|
||||
interface NodeCardProps {
|
||||
account: AccountType;
|
||||
accountId: string;
|
||||
}
|
||||
|
||||
export const NodeCard = ({ account, accountId }: NodeCardProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const { host, viewOnly, cert } = account;
|
||||
const [ref, inView] = useInView({
|
||||
threshold: 0,
|
||||
triggerOnce: true,
|
||||
});
|
||||
|
||||
const auth = getAuthObj(host, viewOnly, undefined, cert);
|
||||
|
||||
const { data, loading, error } = useGetNodeInfoQuery({
|
||||
ssr: false,
|
||||
skip: !inView || !auth,
|
||||
variables: { auth },
|
||||
pollInterval: 10000,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const renderContent = () => {
|
||||
if (!inView) {
|
||||
return (
|
||||
<>
|
||||
<StatusLine>{getStatusDot(false)}</StatusLine>
|
||||
<div>-</div>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Lightning</DarkSubTitle>
|
||||
<div>-</div>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Bitcoin</DarkSubTitle>
|
||||
<div>-</div>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Channels</DarkSubTitle>
|
||||
<div>-</div>
|
||||
</SingleLine>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (
|
||||
loading ||
|
||||
!data?.getNodeInfo ||
|
||||
!data?.getChannelBalance ||
|
||||
!data?.getChainBalance ||
|
||||
!data?.getPendingChainBalance
|
||||
) {
|
||||
return <ScaleLoader height={20} color={themeColors.blue3} />;
|
||||
}
|
||||
|
||||
const {
|
||||
active_channels_count: active,
|
||||
closed_channels_count: closed,
|
||||
alias,
|
||||
pending_channels_count: pending,
|
||||
is_synced_to_chain,
|
||||
} = data.getNodeInfo;
|
||||
|
||||
const { confirmedBalance, pendingBalance } = data.getChannelBalance;
|
||||
|
||||
const chainBalance = data.getChainBalance;
|
||||
const pendingChainBalance = data.getPendingChainBalance;
|
||||
|
||||
return (
|
||||
<>
|
||||
<StatusLine>{getStatusDot(is_synced_to_chain)}</StatusLine>
|
||||
<div>{alias}</div>
|
||||
<ResponsiveLine>
|
||||
<DarkSubTitle>Lightning</DarkSubTitle>
|
||||
<Price amount={confirmedBalance + pendingBalance} />
|
||||
</ResponsiveLine>
|
||||
<ResponsiveLine>
|
||||
<DarkSubTitle>Bitcoin</DarkSubTitle>
|
||||
<Price amount={chainBalance + pendingChainBalance} />
|
||||
</ResponsiveLine>
|
||||
<ResponsiveLine>
|
||||
<DarkSubTitle>Channels</DarkSubTitle>
|
||||
<div>{`${active} / ${pending} / ${closed}`}</div>
|
||||
</ResponsiveLine>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<QuickCard
|
||||
onClick={() => {
|
||||
setIsOpen(true);
|
||||
}}
|
||||
ref={ref}
|
||||
key={account.id}
|
||||
>
|
||||
{renderContent()}
|
||||
</QuickCard>
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
closeCallback={() => {
|
||||
setIsOpen(false);
|
||||
}}
|
||||
>
|
||||
<NodeInfoModal account={data} accountId={accountId} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,105 +0,0 @@
|
||||
import styled, { css } from 'styled-components';
|
||||
import { ChevronLeft, ChevronRight } from 'react-feather';
|
||||
import { Card } from '../generic/Styled';
|
||||
import {
|
||||
inverseTextColor,
|
||||
buttonBorderColor,
|
||||
textColor,
|
||||
mediaWidths,
|
||||
} from '../../styles/Themes';
|
||||
|
||||
const arrowCSS = css`
|
||||
background-color: ${inverseTextColor};
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 50%;
|
||||
display: none;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 8px 16px -8px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid ${buttonBorderColor};
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid ${textColor};
|
||||
}
|
||||
`;
|
||||
|
||||
export const ArrowLeft = styled(ChevronLeft)`
|
||||
${arrowCSS}
|
||||
transform: translate(-30%, -50%);
|
||||
`;
|
||||
|
||||
export const ArrowRight = styled(ChevronRight)`
|
||||
${arrowCSS}
|
||||
transform: translate(30%, -50%);
|
||||
right: 0;
|
||||
`;
|
||||
|
||||
export const NodeBarContainer = styled.div`
|
||||
position: relative;
|
||||
margin-bottom: 24px;
|
||||
&:hover {
|
||||
${ArrowLeft} {
|
||||
display: inline-block;
|
||||
}
|
||||
${ArrowRight} {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledNodeBar = styled.div`
|
||||
display: flex;
|
||||
overflow-x: scroll;
|
||||
-ms-overflow-style: none;
|
||||
cursor: pointer;
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const sectionColor = '#69c0ff';
|
||||
|
||||
export const QuickCard = styled(Card)`
|
||||
height: 120px;
|
||||
width: 240px;
|
||||
min-width: 240px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
margin-bottom: 0px;
|
||||
padding: 10px;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
height: unset;
|
||||
width: 160px;
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: 1px solid ${sectionColor};
|
||||
}
|
||||
`;
|
||||
|
||||
export const StatusLine = styled.div`
|
||||
width: 100%;
|
||||
position: relative;
|
||||
right: -8px;
|
||||
top: -8px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin: 0 0 -8px 0;
|
||||
`;
|
||||
|
||||
export const StatusDot = styled.div`
|
||||
margin: 0 2px;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
border-radius: 100%;
|
||||
background-color: ${({ color }: { color: string }) => color};
|
||||
`;
|
@ -1,108 +0,0 @@
|
||||
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,
|
||||
DarkSubTitle,
|
||||
Sub4Title,
|
||||
Separation,
|
||||
} from '../generic/Styled';
|
||||
import { Price } from '../price/Price';
|
||||
import { ColorButton } from '../buttons/colorButton/ColorButton';
|
||||
import { useStatusDispatch } from '../../context/StatusContext';
|
||||
|
||||
interface NodeInfoModalProps {
|
||||
account: GetNodeInfoQuery | null | undefined;
|
||||
accountId: string;
|
||||
}
|
||||
|
||||
export const NodeInfoModal = ({ account, accountId }: NodeInfoModalProps) => {
|
||||
const dispatch = useStatusDispatch();
|
||||
|
||||
const dispatchAccount = useAccountDispatch();
|
||||
|
||||
if (!account) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
active_channels_count,
|
||||
closed_channels_count,
|
||||
alias,
|
||||
pending_channels_count,
|
||||
is_synced_to_chain,
|
||||
peers_count,
|
||||
version,
|
||||
} = account.getNodeInfo as NodeInfoType;
|
||||
|
||||
const {
|
||||
confirmedBalance,
|
||||
pendingBalance,
|
||||
} = account.getChannelBalance as ChannelBalanceType;
|
||||
|
||||
const chainBalance = account.getChainBalance;
|
||||
const pendingChainBalance = account.getPendingChainBalance;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubTitle>{alias}</SubTitle>
|
||||
<Separation />
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Version:</DarkSubTitle>
|
||||
<div>{version.split(' ')[0]}</div>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Is Synced:</DarkSubTitle>
|
||||
<div>{is_synced_to_chain ? 'True' : 'False'}</div>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Peer Count:</DarkSubTitle>
|
||||
<div>{peers_count}</div>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Active Channels:</DarkSubTitle>
|
||||
<div>{active_channels_count}</div>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Pending Channels:</DarkSubTitle>
|
||||
<div>{pending_channels_count}</div>
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Closed Channels:</DarkSubTitle>
|
||||
<div>{closed_channels_count}</div>
|
||||
</SingleLine>
|
||||
<Sub4Title>Lightning</Sub4Title>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Balance:</DarkSubTitle>
|
||||
<Price amount={confirmedBalance} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Pending:</DarkSubTitle>
|
||||
<Price amount={pendingBalance} />
|
||||
</SingleLine>
|
||||
<Sub4Title>Bitcoin</Sub4Title>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Balance:</DarkSubTitle>
|
||||
<Price amount={chainBalance} />
|
||||
</SingleLine>
|
||||
<SingleLine>
|
||||
<DarkSubTitle>Pending:</DarkSubTitle>
|
||||
<Price amount={pendingChainBalance} />
|
||||
</SingleLine>
|
||||
<ColorButton
|
||||
withMargin={'16px 0 0'}
|
||||
fullWidth={true}
|
||||
onClick={() => {
|
||||
dispatch({
|
||||
type: 'disconnected',
|
||||
});
|
||||
dispatchAccount({ type: 'changeAccount', changeId: accountId });
|
||||
}}
|
||||
>
|
||||
Change to this Account
|
||||
</ColorButton>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,66 +0,0 @@
|
||||
import React from 'react';
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import styled from 'styled-components';
|
||||
import { SubTitle, Card } from '../generic/Styled';
|
||||
import { themeColors, mediaWidths, fontColors } from '../../styles/Themes';
|
||||
import { SectionTitle } from '../typography/Styled';
|
||||
import { CurrentSettings } from '../../views/settings/Current';
|
||||
import { AccountSettings } from '../../views/settings/Account';
|
||||
import { DangerView } from '../../views/settings/Danger';
|
||||
|
||||
const FullDiv = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 400px;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
justify-content: center;
|
||||
}
|
||||
`;
|
||||
|
||||
export const LoadingView = () => (
|
||||
<FullDiv>
|
||||
<SubTitle>Connecting to your Node</SubTitle>
|
||||
<ScaleLoader height={20} color={themeColors.blue3} />
|
||||
</FullDiv>
|
||||
);
|
||||
|
||||
const StyledTitle = styled(SectionTitle)`
|
||||
text-align: center;
|
||||
margin: 16px 0 32px;
|
||||
`;
|
||||
|
||||
const StyledSubtitle = styled(SubTitle)`
|
||||
text-align: center;
|
||||
margin: 48px 0 4px;
|
||||
`;
|
||||
|
||||
const StyledParagraph = styled.p`
|
||||
color: ${fontColors.grey7};
|
||||
text-align: center;
|
||||
margin: 4px 0;
|
||||
`;
|
||||
|
||||
export const ErrorView = () => (
|
||||
<>
|
||||
<Card>
|
||||
<StyledTitle textColor={fontColors.blue3}>Connection Error</StyledTitle>
|
||||
<StyledParagraph>
|
||||
ThunderHub was unable to connect to your node.
|
||||
</StyledParagraph>
|
||||
<StyledParagraph>
|
||||
{`Please make sure it's online and that the connection details are correct.`}
|
||||
</StyledParagraph>
|
||||
<StyledSubtitle>
|
||||
If the problem persists please contact us.
|
||||
</StyledSubtitle>
|
||||
</Card>
|
||||
<CurrentSettings />
|
||||
<AccountSettings />
|
||||
<DangerView />
|
||||
</>
|
||||
);
|
@ -1,7 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
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 { NodeInfoType, ChannelBalanceType } from 'src/graphql/types';
|
||||
import { useStatusDispatch, StatusState } from '../../context/StatusContext';
|
||||
@ -11,18 +10,16 @@ export const StatusCheck: React.FC = () => {
|
||||
const dispatch = useStatusDispatch();
|
||||
const { push } = useRouter();
|
||||
|
||||
const { account, auth } = useAccountState();
|
||||
const { data, loading, error, stopPolling } = useGetNodeInfoQuery({
|
||||
ssr: false,
|
||||
skip: !auth,
|
||||
fetchPolicy: 'network-only',
|
||||
variables: { auth },
|
||||
|
||||
pollInterval: 10000,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
account && toast.error(`Unable to connect to ${account.name}`);
|
||||
toast.error(`Unable to connect to node`);
|
||||
stopPolling();
|
||||
dispatch({ type: 'disconnected' });
|
||||
push(appendBasePath('/'));
|
||||
@ -74,7 +71,7 @@ export const StatusCheck: React.FC = () => {
|
||||
|
||||
dispatch({ type: 'connected', state });
|
||||
}
|
||||
}, [data, dispatch, error, loading, push, account, stopPolling]);
|
||||
}, [data, dispatch, error, loading, push, stopPolling]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
@ -1,317 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import Cookies from 'js-cookie';
|
||||
import {
|
||||
getAccountById,
|
||||
deleteAccountById,
|
||||
addIdAndTypeToAccount,
|
||||
getAuthFromAccount,
|
||||
} from './helpers/context';
|
||||
|
||||
export type SERVER_ACCOUNT_TYPE = 'sso' | 'server' | 'test';
|
||||
export type ACCOUNT_TYPE = 'client';
|
||||
|
||||
export const CLIENT_ACCOUNT: ACCOUNT_TYPE = 'client';
|
||||
export const SSO_ACCOUNT: SERVER_ACCOUNT_TYPE = 'sso';
|
||||
export const SERVER_ACCOUNT: SERVER_ACCOUNT_TYPE = 'server';
|
||||
|
||||
type HasAccountType = 'fetched' | 'false' | 'error';
|
||||
|
||||
export type AuthType =
|
||||
| {
|
||||
type: ACCOUNT_TYPE;
|
||||
host: string;
|
||||
macaroon: string;
|
||||
cert: string | null;
|
||||
}
|
||||
| {
|
||||
type: SERVER_ACCOUNT_TYPE;
|
||||
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 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;
|
||||
activeAccount: string | null;
|
||||
session: string | null;
|
||||
account: CompleteAccount | null;
|
||||
accounts: CompleteAccount[];
|
||||
hasAccount: HasAccountType;
|
||||
};
|
||||
|
||||
type ActionType =
|
||||
| {
|
||||
type: 'initialize';
|
||||
changeId: string | null;
|
||||
accountsToAdd: CompleteAccount[];
|
||||
session: string | null;
|
||||
}
|
||||
| {
|
||||
type: 'changeAccount' | 'deleteAccount';
|
||||
changeId: string;
|
||||
}
|
||||
| {
|
||||
type: 'logout';
|
||||
}
|
||||
| {
|
||||
type: 'addServerAccounts';
|
||||
accountsToAdd: CompleteAccount[];
|
||||
}
|
||||
| {
|
||||
type: 'addAccountAndSave';
|
||||
accountToAdd: AccountProps;
|
||||
session?: string;
|
||||
}
|
||||
| {
|
||||
type: 'addSession';
|
||||
session: string;
|
||||
}
|
||||
| {
|
||||
type: 'removeSession';
|
||||
}
|
||||
| {
|
||||
type: 'deleteAll';
|
||||
}
|
||||
| {
|
||||
type: 'resetFetch';
|
||||
};
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
||||
const StateContext = React.createContext<State | undefined>(undefined);
|
||||
const DispatchContext = React.createContext<Dispatch | undefined>(undefined);
|
||||
|
||||
const initialState: State = {
|
||||
initialized: false,
|
||||
finishedFetch: false,
|
||||
auth: defaultAuth,
|
||||
session: null,
|
||||
activeAccount: null,
|
||||
account: null,
|
||||
accounts: [],
|
||||
hasAccount: 'false',
|
||||
};
|
||||
|
||||
const stateReducer = (state: State, action: ActionType): State => {
|
||||
switch (action.type) {
|
||||
case 'initialize': {
|
||||
if (state.initialized) {
|
||||
return state;
|
||||
}
|
||||
const { accountsToAdd, changeId, session } = action;
|
||||
|
||||
const { account, id } = getAccountById(changeId, accountsToAdd);
|
||||
|
||||
if (!account)
|
||||
return {
|
||||
...state,
|
||||
initialized: true,
|
||||
accounts: accountsToAdd,
|
||||
activeAccount: changeId,
|
||||
session,
|
||||
};
|
||||
|
||||
const auth = getAuthFromAccount(account, session);
|
||||
return {
|
||||
...state,
|
||||
initialized: true,
|
||||
auth,
|
||||
account,
|
||||
accounts: accountsToAdd,
|
||||
activeAccount: id,
|
||||
session,
|
||||
hasAccount: 'fetched',
|
||||
};
|
||||
}
|
||||
case 'changeAccount': {
|
||||
const { account, id } = getAccountById(action.changeId, state.accounts);
|
||||
|
||||
if (!account) return state;
|
||||
|
||||
const auth = getAuthFromAccount(account);
|
||||
|
||||
localStorage.setItem('active', `${id}`);
|
||||
sessionStorage.removeItem('session');
|
||||
|
||||
return {
|
||||
...state,
|
||||
auth,
|
||||
session: null,
|
||||
account,
|
||||
activeAccount: id,
|
||||
hasAccount: 'fetched',
|
||||
};
|
||||
}
|
||||
case 'logout':
|
||||
localStorage.removeItem('active');
|
||||
sessionStorage.clear();
|
||||
return {
|
||||
...state,
|
||||
account: null,
|
||||
activeAccount: null,
|
||||
auth: defaultAuth,
|
||||
session: null,
|
||||
};
|
||||
case 'deleteAccount': {
|
||||
if (!state.accounts || state?.accounts?.length <= 0 || !state.account) {
|
||||
return state;
|
||||
}
|
||||
const { accounts, id } = deleteAccountById(
|
||||
state.account.id,
|
||||
action.changeId,
|
||||
state.accounts
|
||||
);
|
||||
localStorage.setItem('accounts', JSON.stringify(accounts));
|
||||
!id && sessionStorage.removeItem('session');
|
||||
return {
|
||||
...state,
|
||||
accounts,
|
||||
...(!id && { activeId: null, session: null, account: null }),
|
||||
};
|
||||
}
|
||||
case 'addServerAccounts': {
|
||||
const clientAccounts = state.accounts.filter(
|
||||
a => a.type === CLIENT_ACCOUNT
|
||||
);
|
||||
const completeAccounts = [...clientAccounts, ...action.accountsToAdd];
|
||||
|
||||
if (!state.activeAccount) {
|
||||
return {
|
||||
...state,
|
||||
finishedFetch: true,
|
||||
accounts: completeAccounts,
|
||||
};
|
||||
}
|
||||
|
||||
const { account } = getAccountById(state.activeAccount, completeAccounts);
|
||||
|
||||
if (!account && completeAccounts.length > 0) {
|
||||
return {
|
||||
...state,
|
||||
finishedFetch: true,
|
||||
accounts: completeAccounts,
|
||||
hasAccount: 'error',
|
||||
};
|
||||
}
|
||||
|
||||
const auth = getAuthFromAccount(account, state.session);
|
||||
return {
|
||||
...state,
|
||||
finishedFetch: true,
|
||||
hasAccount: 'fetched',
|
||||
auth,
|
||||
account,
|
||||
accounts: completeAccounts,
|
||||
};
|
||||
}
|
||||
case 'addAccountAndSave': {
|
||||
const account = addIdAndTypeToAccount(action.accountToAdd);
|
||||
const activeAccount = account.id;
|
||||
const accounts = [...state.accounts, account];
|
||||
|
||||
const auth = getAuthFromAccount(account, action.session);
|
||||
|
||||
if (action.session) {
|
||||
sessionStorage.setItem('session', action.session);
|
||||
}
|
||||
|
||||
localStorage.setItem('active', `${activeAccount}`);
|
||||
|
||||
const savedAccounts = JSON.parse(
|
||||
localStorage.getItem('accounts') || '[]'
|
||||
);
|
||||
localStorage.setItem(
|
||||
'accounts',
|
||||
JSON.stringify([...savedAccounts, action.accountToAdd])
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
auth,
|
||||
account,
|
||||
accounts,
|
||||
activeAccount,
|
||||
hasAccount: 'fetched',
|
||||
...(action.session && { session: action.session }),
|
||||
};
|
||||
}
|
||||
case 'addSession':
|
||||
sessionStorage.setItem('session', action.session);
|
||||
return {
|
||||
...state,
|
||||
auth: getAuthFromAccount(state.account, action.session),
|
||||
session: action.session,
|
||||
};
|
||||
case 'removeSession':
|
||||
sessionStorage.removeItem('session');
|
||||
return {
|
||||
...state,
|
||||
auth: getAuthFromAccount(state.account),
|
||||
session: null,
|
||||
};
|
||||
case 'deleteAll':
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
Cookies.remove('config');
|
||||
return initialState;
|
||||
case 'resetFetch':
|
||||
return {
|
||||
...state,
|
||||
hasAccount: 'false',
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const AccountProvider: React.FC = ({ children }) => {
|
||||
const [state, dispatch] = React.useReducer(stateReducer, initialState);
|
||||
|
||||
return (
|
||||
<DispatchContext.Provider value={dispatch}>
|
||||
<StateContext.Provider value={state}>{children}</StateContext.Provider>
|
||||
</DispatchContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useAccountState = () => {
|
||||
const context = React.useContext(StateContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useAccountState must be used within a AccountProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
const useAccountDispatch = () => {
|
||||
const context = React.useContext(DispatchContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useAccountDispatch must be used within a AccountProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export { AccountProvider, useAccountState, useAccountDispatch };
|
@ -27,7 +27,6 @@ type State = {
|
||||
currency: string;
|
||||
theme: string;
|
||||
sidebar: boolean;
|
||||
multiNodeInfo: boolean;
|
||||
fetchFees: boolean;
|
||||
fetchPrices: boolean;
|
||||
displayValues: boolean;
|
||||
@ -51,7 +50,6 @@ type ActionType =
|
||||
currency?: string;
|
||||
theme?: string;
|
||||
sidebar?: boolean;
|
||||
multiNodeInfo?: boolean;
|
||||
fetchFees?: boolean;
|
||||
fetchPrices?: boolean;
|
||||
displayValues?: boolean;
|
||||
@ -83,7 +81,6 @@ const initialState: State = {
|
||||
currency: currencyTypes.indexOf(defC) > -1 ? defC : 'sat',
|
||||
theme: themeTypes.indexOf(defT) > -1 ? defT : 'dark',
|
||||
sidebar: true,
|
||||
multiNodeInfo: false,
|
||||
fetchFees,
|
||||
fetchPrices,
|
||||
displayValues: true,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import { AccountProvider } from './AccountContext';
|
||||
import { BitcoinInfoProvider } from './BitcoinContext';
|
||||
import { StatusProvider } from './StatusContext';
|
||||
import { PriceProvider } from './PriceContext';
|
||||
@ -7,15 +6,13 @@ import { ChatProvider } from './ChatContext';
|
||||
import { RebalanceProvider } from './RebalanceContext';
|
||||
|
||||
export const ContextProvider: React.FC = ({ children }) => (
|
||||
<AccountProvider>
|
||||
<BitcoinInfoProvider>
|
||||
<PriceProvider>
|
||||
<ChatProvider>
|
||||
<StatusProvider>
|
||||
<RebalanceProvider>{children}</RebalanceProvider>
|
||||
</StatusProvider>
|
||||
</ChatProvider>
|
||||
</PriceProvider>
|
||||
</BitcoinInfoProvider>
|
||||
</AccountProvider>
|
||||
<BitcoinInfoProvider>
|
||||
<PriceProvider>
|
||||
<ChatProvider>
|
||||
<StatusProvider>
|
||||
<RebalanceProvider>{children}</RebalanceProvider>
|
||||
</StatusProvider>
|
||||
</ChatProvider>
|
||||
</PriceProvider>
|
||||
</BitcoinInfoProvider>
|
||||
);
|
||||
|
@ -1,9 +1,5 @@
|
||||
import React, { createContext, useContext, useReducer } from 'react';
|
||||
|
||||
type StateStatus = {
|
||||
connected: boolean;
|
||||
};
|
||||
|
||||
export type StatusState = {
|
||||
alias: string;
|
||||
color: string;
|
||||
@ -22,20 +18,19 @@ export type StatusState = {
|
||||
peersCount: number;
|
||||
};
|
||||
|
||||
type CompleteState = StatusState & StateStatus;
|
||||
|
||||
type ActionType = {
|
||||
type: 'connected' | 'disconnected';
|
||||
state?: StatusState;
|
||||
};
|
||||
type ActionType =
|
||||
| {
|
||||
type: 'connected';
|
||||
state?: StatusState;
|
||||
}
|
||||
| { type: 'disconnected' };
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
||||
const StateContext = createContext<CompleteState | undefined>(undefined);
|
||||
const StateContext = createContext<StatusState | undefined>(undefined);
|
||||
const DispatchContext = createContext<Dispatch | undefined>(undefined);
|
||||
|
||||
const initialState = {
|
||||
connected: false,
|
||||
const initialState: StatusState = {
|
||||
alias: '',
|
||||
color: '',
|
||||
syncedToChain: false,
|
||||
@ -53,15 +48,11 @@ const initialState = {
|
||||
peersCount: 0,
|
||||
};
|
||||
|
||||
const stateReducer = (
|
||||
state: StatusState,
|
||||
action: ActionType
|
||||
): CompleteState => {
|
||||
const stateReducer = (state: StatusState, action: ActionType): StatusState => {
|
||||
switch (action.type) {
|
||||
case 'connected':
|
||||
return { ...state, ...action.state, connected: true } || initialState;
|
||||
return { ...state, ...action.state } || initialState;
|
||||
case 'disconnected':
|
||||
return initialState;
|
||||
default:
|
||||
return initialState;
|
||||
}
|
||||
|
@ -1,71 +0,0 @@
|
||||
import {
|
||||
CLIENT_ACCOUNT,
|
||||
SSO_ACCOUNT,
|
||||
CompleteAccount,
|
||||
} from '../AccountContext';
|
||||
import { getAccountById, deleteAccountById } from './context';
|
||||
|
||||
const firstAccount = {
|
||||
name: 'Hola',
|
||||
host: 'Host1',
|
||||
admin: 'Admin1',
|
||||
viewOnly: 'ViewOnly1',
|
||||
cert: 'Cert1',
|
||||
id: '123',
|
||||
type: CLIENT_ACCOUNT,
|
||||
};
|
||||
const secondAccount = {
|
||||
name: 'Chao',
|
||||
id: '1234',
|
||||
type: SSO_ACCOUNT,
|
||||
};
|
||||
|
||||
const testAccounts: CompleteAccount[] = [firstAccount, secondAccount];
|
||||
|
||||
describe('Context Helpers', () => {
|
||||
describe('should getAccountById', () => {
|
||||
test('account exists', () => {
|
||||
const { account, id } = getAccountById('1234', testAccounts);
|
||||
|
||||
expect(id).toBe('1234');
|
||||
expect(account).toBe(secondAccount);
|
||||
});
|
||||
test('account does not exists', () => {
|
||||
const { account, id } = getAccountById('false id', testAccounts);
|
||||
|
||||
expect(id).toBe(null);
|
||||
expect(account).toBe(null);
|
||||
});
|
||||
});
|
||||
describe('should deleteAccountById', () => {
|
||||
test('account exists', () => {
|
||||
const { accounts, id } = deleteAccountById('123', '1234', testAccounts);
|
||||
|
||||
expect(id).toBe('123');
|
||||
expect(accounts).toStrictEqual([firstAccount]);
|
||||
});
|
||||
test('account exists and is current account', () => {
|
||||
const { accounts, id } = deleteAccountById('123', '123', testAccounts);
|
||||
|
||||
expect(id).toBe(null);
|
||||
expect(accounts).toStrictEqual([secondAccount]);
|
||||
});
|
||||
test('account does not exists', () => {
|
||||
const { accounts, id } = deleteAccountById(
|
||||
'123',
|
||||
'false id',
|
||||
testAccounts
|
||||
);
|
||||
|
||||
expect(id).toBe('123');
|
||||
expect(accounts).toStrictEqual(testAccounts);
|
||||
});
|
||||
|
||||
test('one account', () => {
|
||||
const { accounts, id } = deleteAccountById('123', '123', [firstAccount]);
|
||||
|
||||
expect(id).toBe(null);
|
||||
expect(accounts).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,80 +0,0 @@
|
||||
import { getUUID } from '../../utils/auth';
|
||||
import {
|
||||
CompleteAccount,
|
||||
AccountProps,
|
||||
CLIENT_ACCOUNT,
|
||||
AuthType,
|
||||
defaultAuth,
|
||||
} from '../AccountContext';
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
return {
|
||||
account: correctAccount || null,
|
||||
id: correctAccount ? correctAccount.id : null,
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteAccountById = (
|
||||
currentId: string,
|
||||
id: string,
|
||||
accounts: CompleteAccount[]
|
||||
) => {
|
||||
const newAccounts: CompleteAccount[] = accounts.filter(a => a.id !== id);
|
||||
|
||||
if (newAccounts.length <= 0) {
|
||||
return { accounts: [], id: null };
|
||||
}
|
||||
|
||||
let activeId: string | null = currentId;
|
||||
if (currentId === id) {
|
||||
activeId = null;
|
||||
}
|
||||
|
||||
return { accounts: newAccounts, id: activeId };
|
||||
};
|
||||
|
||||
export const addIdAndTypeToAccount = (
|
||||
account: AccountProps
|
||||
): CompleteAccount => {
|
||||
const { host, viewOnly, admin, cert } = account;
|
||||
return {
|
||||
...account,
|
||||
type: CLIENT_ACCOUNT,
|
||||
id: getUUID(`${host}-${viewOnly}-${admin !== '' ? 1 : 0}-${cert}`),
|
||||
};
|
||||
};
|
||||
|
||||
export const getAuthFromAccount = (
|
||||
account: CompleteAccount | undefined | null,
|
||||
session?: string | null
|
||||
): AuthType => {
|
||||
if (!account) return defaultAuth;
|
||||
if (account.type !== CLIENT_ACCOUNT) {
|
||||
return {
|
||||
type: account.type,
|
||||
id: account.id,
|
||||
};
|
||||
}
|
||||
const { host, viewOnly, cert } = account;
|
||||
if (!host) {
|
||||
return defaultAuth;
|
||||
}
|
||||
if (!viewOnly && !session) {
|
||||
return defaultAuth;
|
||||
}
|
||||
return {
|
||||
type: account.type,
|
||||
host,
|
||||
macaroon: session || viewOnly,
|
||||
cert,
|
||||
};
|
||||
};
|
@ -1,6 +1,11 @@
|
||||
import gql from 'graphql-tag';
|
||||
import * as ApolloReactCommon from '@apollo/react-common';
|
||||
import * as ApolloReactHooks from '@apollo/react-hooks';
|
||||
import {
|
||||
gql,
|
||||
QueryHookOptions,
|
||||
useQuery,
|
||||
useLazyQuery,
|
||||
QueryResult,
|
||||
LazyQueryHookOptions,
|
||||
} from '@apollo/client';
|
||||
import * as Types from '../../types';
|
||||
|
||||
export type GetCountriesQueryVariables = Types.Exact<{ [key: string]: never }>;
|
||||
@ -132,26 +137,23 @@ export const GetCountriesDocument = gql`
|
||||
* });
|
||||
*/
|
||||
export function useGetCountriesQuery(
|
||||
baseOptions?: ApolloReactHooks.QueryHookOptions<
|
||||
GetCountriesQuery,
|
||||
GetCountriesQueryVariables
|
||||
>
|
||||
baseOptions?: QueryHookOptions<GetCountriesQuery, GetCountriesQueryVariables>
|
||||
) {
|
||||
return ApolloReactHooks.useQuery<
|
||||
GetCountriesQuery,
|
||||
GetCountriesQueryVariables
|
||||
>(GetCountriesDocument, baseOptions);
|
||||
return useQuery<GetCountriesQuery, GetCountriesQueryVariables>(
|
||||
GetCountriesDocument,
|
||||
baseOptions
|
||||
);
|
||||
}
|
||||
export function useGetCountriesLazyQuery(
|
||||
baseOptions?: ApolloReactHooks.LazyQueryHookOptions<
|
||||
baseOptions?: LazyQueryHookOptions<
|
||||
GetCountriesQuery,
|
||||
GetCountriesQueryVariables
|
||||
>
|
||||
) {
|
||||
return ApolloReactHooks.useLazyQuery<
|
||||
GetCountriesQuery,
|
||||
GetCountriesQueryVariables
|
||||
>(GetCountriesDocument, baseOptions);
|
||||
return useLazyQuery<GetCountriesQuery, GetCountriesQueryVariables>(
|
||||
GetCountriesDocument,
|
||||
baseOptions
|
||||
);
|
||||
}
|
||||
export type GetCountriesQueryHookResult = ReturnType<
|
||||
typeof useGetCountriesQuery
|
||||
@ -159,7 +161,7 @@ export type GetCountriesQueryHookResult = ReturnType<
|
||||
export type GetCountriesLazyQueryHookResult = ReturnType<
|
||||
typeof useGetCountriesLazyQuery
|
||||
>;
|
||||
export type GetCountriesQueryResult = ApolloReactCommon.QueryResult<
|
||||
export type GetCountriesQueryResult = QueryResult<
|
||||
GetCountriesQuery,
|
||||
GetCountriesQueryVariables
|
||||
>;
|
||||
@ -189,26 +191,26 @@ export const GetCurrenciesDocument = gql`
|
||||
* });
|
||||
*/
|
||||
export function useGetCurrenciesQuery(
|
||||
baseOptions?: ApolloReactHooks.QueryHookOptions<
|
||||
baseOptions?: QueryHookOptions<
|
||||
GetCurrenciesQuery,
|
||||
GetCurrenciesQueryVariables
|
||||
>
|
||||
) {
|
||||
return ApolloReactHooks.useQuery<
|
||||
GetCurrenciesQuery,
|
||||
GetCurrenciesQueryVariables
|
||||
>(GetCurrenciesDocument, baseOptions);
|
||||
return useQuery<GetCurrenciesQuery, GetCurrenciesQueryVariables>(
|
||||
GetCurrenciesDocument,
|
||||
baseOptions
|
||||
);
|
||||
}
|
||||
export function useGetCurrenciesLazyQuery(
|
||||
baseOptions?: ApolloReactHooks.LazyQueryHookOptions<
|
||||
baseOptions?: LazyQueryHookOptions<
|
||||
GetCurrenciesQuery,
|
||||
GetCurrenciesQueryVariables
|
||||
>
|
||||
) {
|
||||
return ApolloReactHooks.useLazyQuery<
|
||||
GetCurrenciesQuery,
|
||||
GetCurrenciesQueryVariables
|
||||
>(GetCurrenciesDocument, baseOptions);
|
||||
return useLazyQuery<GetCurrenciesQuery, GetCurrenciesQueryVariables>(
|
||||
GetCurrenciesDocument,
|
||||
baseOptions
|
||||
);
|
||||
}
|
||||
export type GetCurrenciesQueryHookResult = ReturnType<
|
||||
typeof useGetCurrenciesQuery
|
||||
@ -216,7 +218,7 @@ export type GetCurrenciesQueryHookResult = ReturnType<
|
||||
export type GetCurrenciesLazyQueryHookResult = ReturnType<
|
||||
typeof useGetCurrenciesLazyQuery
|
||||
>;
|
||||
export type GetCurrenciesQueryResult = ApolloReactCommon.QueryResult<
|
||||
export type GetCurrenciesQueryResult = QueryResult<
|
||||
GetCurrenciesQuery,
|
||||
GetCurrenciesQueryVariables
|
||||
>;
|
||||
@ -285,23 +287,17 @@ export const GetOffersDocument = gql`
|
||||
* });
|
||||
*/
|
||||
export function useGetOffersQuery(
|
||||
baseOptions?: ApolloReactHooks.QueryHookOptions<
|
||||
GetOffersQuery,
|
||||
GetOffersQueryVariables
|
||||
>
|
||||
baseOptions?: QueryHookOptions<GetOffersQuery, GetOffersQueryVariables>
|
||||
) {
|
||||
return ApolloReactHooks.useQuery<GetOffersQuery, GetOffersQueryVariables>(
|
||||
return useQuery<GetOffersQuery, GetOffersQueryVariables>(
|
||||
GetOffersDocument,
|
||||
baseOptions
|
||||
);
|
||||
}
|
||||
export function useGetOffersLazyQuery(
|
||||
baseOptions?: ApolloReactHooks.LazyQueryHookOptions<
|
||||
GetOffersQuery,
|
||||
GetOffersQueryVariables
|
||||
>
|
||||
baseOptions?: LazyQueryHookOptions<GetOffersQuery, GetOffersQueryVariables>
|
||||
) {
|
||||
return ApolloReactHooks.useLazyQuery<GetOffersQuery, GetOffersQueryVariables>(
|
||||
return useLazyQuery<GetOffersQuery, GetOffersQueryVariables>(
|
||||
GetOffersDocument,
|
||||
baseOptions
|
||||
);
|
||||
@ -310,7 +306,7 @@ export type GetOffersQueryHookResult = ReturnType<typeof useGetOffersQuery>;
|
||||
export type GetOffersLazyQueryHookResult = ReturnType<
|
||||
typeof useGetOffersLazyQuery
|
||||
>;
|
||||
export type GetOffersQueryResult = ApolloReactCommon.QueryResult<
|
||||
export type GetOffersQueryResult = QueryResult<
|
||||
GetOffersQuery,
|
||||
GetOffersQueryVariables
|
||||
>;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import gql from 'graphql-tag';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_HODL_COUNTRIES = gql`
|
||||
query GetCountries {
|
||||
|
@ -1,10 +1,14 @@
|
||||
import gql from 'graphql-tag';
|
||||
import * as ApolloReactCommon from '@apollo/react-common';
|
||||
import * as ApolloReactHooks from '@apollo/react-hooks';
|
||||
import {
|
||||
gql,
|
||||
MutationFunction,
|
||||
useMutation,
|
||||
MutationHookOptions,
|
||||
BaseMutationOptions,
|
||||
MutationResult,
|
||||
} from '@apollo/client';
|
||||
import * as Types from '../../types';
|
||||
|
||||
export type AddPeerMutationVariables = Types.Exact<{
|
||||
auth: Types.AuthType;
|
||||
url?: Types.Maybe<Types.Scalars['String']>;
|
||||
publicKey?: Types.Maybe<Types.Scalars['String']>;
|
||||
socket?: Types.Maybe<Types.Scalars['String']>;
|
||||
@ -18,14 +22,12 @@ export type AddPeerMutation = { __typename?: 'Mutation' } & Pick<
|
||||
|
||||
export const AddPeerDocument = gql`
|
||||
mutation AddPeer(
|
||||
$auth: authType!
|
||||
$url: String
|
||||
$publicKey: String
|
||||
$socket: String
|
||||
$isTemporary: Boolean
|
||||
) {
|
||||
addPeer(
|
||||
auth: $auth
|
||||
url: $url
|
||||
publicKey: $publicKey
|
||||
socket: $socket
|
||||
@ -33,7 +35,7 @@ export const AddPeerDocument = gql`
|
||||
)
|
||||
}
|
||||
`;
|
||||
export type AddPeerMutationFn = ApolloReactCommon.MutationFunction<
|
||||
export type AddPeerMutationFn = MutationFunction<
|
||||
AddPeerMutation,
|
||||
AddPeerMutationVariables
|
||||
>;
|
||||
@ -51,7 +53,6 @@ export type AddPeerMutationFn = ApolloReactCommon.MutationFunction<
|
||||
* @example
|
||||
* const [addPeerMutation, { data, loading, error }] = useAddPeerMutation({
|
||||
* variables: {
|
||||
* auth: // value for 'auth'
|
||||
* url: // value for 'url'
|
||||
* publicKey: // value for 'publicKey'
|
||||
* socket: // value for 'socket'
|
||||
@ -60,21 +61,16 @@ export type AddPeerMutationFn = ApolloReactCommon.MutationFunction<
|
||||
* });
|
||||
*/
|
||||
export function useAddPeerMutation(
|
||||
baseOptions?: ApolloReactHooks.MutationHookOptions<
|
||||
AddPeerMutation,
|
||||
AddPeerMutationVariables
|
||||
>
|
||||
baseOptions?: MutationHookOptions<AddPeerMutation, AddPeerMutationVariables>
|
||||
) {
|
||||
return ApolloReactHooks.useMutation<
|
||||
AddPeerMutation,
|
||||
AddPeerMutationVariables
|
||||
>(AddPeerDocument, baseOptions);
|
||||
return useMutation<AddPeerMutation, AddPeerMutationVariables>(
|
||||
AddPeerDocument,
|
||||
baseOptions
|
||||
);
|
||||
}
|
||||
export type AddPeerMutationHookResult = ReturnType<typeof useAddPeerMutation>;
|
||||
export type AddPeerMutationResult = ApolloReactCommon.MutationResult<
|
||||
AddPeerMutation
|
||||
>;
|
||||
export type AddPeerMutationOptions = ApolloReactCommon.BaseMutationOptions<
|
||||
export type AddPeerMutationResult = MutationResult<AddPeerMutation>;
|
||||
export type AddPeerMutationOptions = BaseMutationOptions<
|
||||
AddPeerMutation,
|
||||
AddPeerMutationVariables
|
||||
>;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loadingβ¦
Reference in New Issue
Block a user