mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-23 14:40:27 +01:00
Merge pull request #112 from apotdevin/release/0.9.0
feat: release 0.9.0
This commit is contained in:
commit
42237a3416
351 changed files with 6756 additions and 10866 deletions
3
.env
3
.env
|
@ -10,7 +10,6 @@
|
||||||
# Server Configs
|
# Server Configs
|
||||||
# -----------
|
# -----------
|
||||||
# LOG_LEVEL='info'
|
# LOG_LEVEL='info'
|
||||||
# HODL_KEY='HODL_HODL_API_KEY'
|
|
||||||
# BASE_PATH='/basePath'
|
# BASE_PATH='/basePath'
|
||||||
|
|
||||||
# -----------
|
# -----------
|
||||||
|
@ -24,9 +23,7 @@
|
||||||
# -----------
|
# -----------
|
||||||
# FETCH_PRICES=false
|
# FETCH_PRICES=false
|
||||||
# FETCH_FEES=false
|
# FETCH_FEES=false
|
||||||
# HODL_HODL=false
|
|
||||||
# DISABLE_LINKS=true
|
# DISABLE_LINKS=true
|
||||||
# NO_CLIENT_ACCOUNTS=true
|
|
||||||
# NO_VERSION_CHECK=true
|
# NO_VERSION_CHECK=true
|
||||||
|
|
||||||
# -----------
|
# -----------
|
||||||
|
|
17
@types/index.d.ts
vendored
17
@types/index.d.ts
vendored
|
@ -3,3 +3,20 @@ declare module '*.jpg';
|
||||||
declare module '*.jpeg';
|
declare module '*.jpeg';
|
||||||
declare module '*.svg';
|
declare module '*.svg';
|
||||||
declare module '*.gif';
|
declare module '*.gif';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ln-service does not have proper types. This is slightly
|
||||||
|
* problematic, as this leads to types being `any` **a ton**
|
||||||
|
* of places.
|
||||||
|
*
|
||||||
|
* Here's an issue tracking this: https://github.com/alexbosworth/ln-service/issues/112
|
||||||
|
*
|
||||||
|
* It seems like the library is generated from Proto files.
|
||||||
|
* Could it be an idea to try and generate TypeScript declarations
|
||||||
|
* from those files?
|
||||||
|
*/
|
||||||
|
declare module 'ln-service';
|
||||||
|
declare module '@alexbosworth/request';
|
||||||
|
declare module 'balanceofsatoshis/request';
|
||||||
|
declare module 'balanceofsatoshis/swaps';
|
||||||
|
declare module 'balanceofsatoshis/balances';
|
||||||
|
|
|
@ -14,9 +14,6 @@ COPY package.json .
|
||||||
COPY package-lock.json .
|
COPY package-lock.json .
|
||||||
RUN npm install --production --silent
|
RUN npm install --production --silent
|
||||||
|
|
||||||
# Install dependencies necessary for build and start
|
|
||||||
RUN npm install -D cross-env typescript @types/react @next/bundle-analyzer
|
|
||||||
|
|
||||||
# ---------------
|
# ---------------
|
||||||
# Build App
|
# Build App
|
||||||
# ---------------
|
# ---------------
|
||||||
|
|
30
README.md
30
README.md
|
@ -102,22 +102,7 @@ This repository consists of a **NextJS** server that handles both the backend **
|
||||||
## **Requirements**
|
## **Requirements**
|
||||||
|
|
||||||
- Yarn/npm installed
|
- Yarn/npm installed
|
||||||
- Node installed (Version 12.16.0 or higher)
|
- Node installed (Version 10 or higher)
|
||||||
|
|
||||||
**Older Versions of Node**
|
|
||||||
Earlier versions of Node can be used if you replace the following commands:
|
|
||||||
|
|
||||||
```js
|
|
||||||
//Yarn
|
|
||||||
yarn start -> yarn start:compatible
|
|
||||||
yarn dev -> yarn dev:compatible
|
|
||||||
|
|
||||||
//NPM
|
|
||||||
npm start -> npm start:compatible
|
|
||||||
npm run dev -> npm run dev:compatible
|
|
||||||
```
|
|
||||||
|
|
||||||
**HodlHodl integration will not work with older versions of Node!**
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -132,7 +117,6 @@ You can define some environment variables that ThunderHub can start with. To do
|
||||||
# Server Configs
|
# Server Configs
|
||||||
# -----------
|
# -----------
|
||||||
LOG_LEVEL = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly' # Default: 'info'
|
LOG_LEVEL = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly' # Default: 'info'
|
||||||
HODL_KEY = '[Key provided by HodlHodl]' # Default: ''
|
|
||||||
BASE_PATH = '[Base path where you want to have thunderhub running i.e. '/btcpay']' # Default: ''
|
BASE_PATH = '[Base path where you want to have thunderhub running i.e. '/btcpay']' # Default: ''
|
||||||
|
|
||||||
# -----------
|
# -----------
|
||||||
|
@ -146,9 +130,7 @@ CURRENCY = 'sat' | 'btc' | 'fiat' # Default: 'sat'
|
||||||
# -----------
|
# -----------
|
||||||
FETCH_PRICES = true | false # Default: true
|
FETCH_PRICES = true | false # Default: true
|
||||||
FETCH_FEES = true | false # Default: true
|
FETCH_FEES = true | false # Default: true
|
||||||
HODL_HODL = true | false # Default: true
|
|
||||||
DISABLE_LINKS = true | false # Default: false
|
DISABLE_LINKS = true | false # Default: false
|
||||||
NO_CLIENT_ACCOUNTS = true | false # Default: false
|
|
||||||
NO_VERSION_CHECK = true | false # Default: false
|
NO_VERSION_CHECK = true | false # Default: false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -252,16 +234,6 @@ ThunderHub shows you links for quick viewing of nodes by public key on [1ml.com]
|
||||||
|
|
||||||
If you don't want to show these links, you can set `DISABLE_LINKS=true` in your `.env` file.
|
If you don't want to show these links, you can set `DISABLE_LINKS=true` in your `.env` file.
|
||||||
|
|
||||||
**HodlHodl**
|
|
||||||
ThunderHub has a HodlHodl integration to view offers from this platform.
|
|
||||||
|
|
||||||
If you want to disable this integration, you can set `HODL_HODL=false` in your `.env` file.
|
|
||||||
|
|
||||||
**Client Accounts**
|
|
||||||
ThunderHub allows you to create accounts on the browser which are also encrypted and stored in the same browser.
|
|
||||||
|
|
||||||
If you want to disable this option and only allow accounts that are created on the server, you can set `NO_CLIENT_ACCOUNTS=true` in your `.env` file.
|
|
||||||
|
|
||||||
**Version Check**
|
**Version Check**
|
||||||
ThunderHub gets the latest available version from [Github](https://api.github.com/repos/apotdevin/thunderhub/releases/latest) and shows you a message if you are on an older version.
|
ThunderHub gets the latest available version from [Github](https://api.github.com/repos/apotdevin/thunderhub/releases/latest) and shows you a message if you are on an older version.
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,6 @@ COPY package.json .
|
||||||
COPY package-lock.json .
|
COPY package-lock.json .
|
||||||
RUN npm install --production --silent
|
RUN npm install --production --silent
|
||||||
|
|
||||||
# Install dependencies necessary for build and start
|
|
||||||
RUN npm install -D cross-env typescript @types/react @next/bundle-analyzer
|
|
||||||
|
|
||||||
# ---------------
|
# ---------------
|
||||||
# Build App
|
# Build App
|
||||||
# ---------------
|
# ---------------
|
||||||
|
|
|
@ -14,9 +14,6 @@ COPY package.json .
|
||||||
COPY package-lock.json .
|
COPY package-lock.json .
|
||||||
RUN npm install --production --silent
|
RUN npm install --production --silent
|
||||||
|
|
||||||
# Install dependencies necessary for build and start
|
|
||||||
RUN npm install -D cross-env typescript @types/react @next/bundle-analyzer
|
|
||||||
|
|
||||||
# ---------------
|
# ---------------
|
||||||
# Build App
|
# Build App
|
||||||
# ---------------
|
# ---------------
|
||||||
|
|
|
@ -17,6 +17,7 @@ generates:
|
||||||
withComponent: false
|
withComponent: false
|
||||||
withHOC: false
|
withHOC: false
|
||||||
withHooks: true
|
withHooks: true
|
||||||
|
reactApolloVersion: 3
|
||||||
plugins:
|
plugins:
|
||||||
- 'typescript-operations'
|
- 'typescript-operations'
|
||||||
- 'typescript-react-apollo'
|
- 'typescript-react-apollo'
|
||||||
|
|
|
@ -1,32 +1,29 @@
|
||||||
/* eslint @typescript-eslint/no-var-requires: 0 */
|
/* eslint @typescript-eslint/no-var-requires: 0 */
|
||||||
import * as React from 'react';
|
import { IncomingMessage, ServerResponse } from 'http';
|
||||||
import Head from 'next/head';
|
import { useMemo } from 'react';
|
||||||
import { ApolloProvider } from '@apollo/react-hooks';
|
|
||||||
import { ApolloClient } from 'apollo-client';
|
|
||||||
import {
|
import {
|
||||||
|
ApolloClient,
|
||||||
InMemoryCache,
|
InMemoryCache,
|
||||||
IntrospectionFragmentMatcher,
|
NormalizedCacheObject,
|
||||||
} from 'apollo-cache-inmemory';
|
} from '@apollo/client';
|
||||||
import getConfig from 'next/config';
|
import getConfig from 'next/config';
|
||||||
import introspectionQueryResultData from 'src/graphql/fragmentTypes.json';
|
|
||||||
|
|
||||||
const fragmentMatcher = new IntrospectionFragmentMatcher({
|
|
||||||
introspectionQueryResultData,
|
|
||||||
});
|
|
||||||
|
|
||||||
let globalApolloClient = null;
|
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
const { publicRuntimeConfig } = getConfig();
|
||||||
const { apiUrl: uri } = publicRuntimeConfig;
|
const { apiUrl: uri } = publicRuntimeConfig;
|
||||||
|
|
||||||
function createIsomorphLink(ctx) {
|
let apolloClient: ReturnType<typeof createApolloClient> | null = null;
|
||||||
if (typeof window === 'undefined') {
|
|
||||||
const { SchemaLink } = require('apollo-link-schema');
|
|
||||||
const schema = require('server/schema');
|
|
||||||
return new SchemaLink({ schema, context: ctx });
|
|
||||||
} else {
|
|
||||||
const { HttpLink } = require('apollo-link-http');
|
|
||||||
|
|
||||||
|
function createIsomorphLink(req?: IncomingMessage, res?: ServerResponse) {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
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/client/link/http');
|
||||||
return new HttpLink({
|
return new HttpLink({
|
||||||
uri,
|
uri,
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
|
@ -34,132 +31,38 @@ function createIsomorphLink(ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function createApolloClient(req?: IncomingMessage, res?: ServerResponse) {
|
||||||
* Creates and configures the ApolloClient
|
|
||||||
* @param {Object} [initialState={}]
|
|
||||||
*/
|
|
||||||
function createApolloClient(ctx = {}, initialState = {}) {
|
|
||||||
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
|
|
||||||
return new ApolloClient({
|
return new ApolloClient({
|
||||||
ssrMode,
|
credentials: 'same-origin',
|
||||||
link: createIsomorphLink(ctx),
|
ssrMode: typeof window === 'undefined',
|
||||||
cache,
|
link: createIsomorphLink(req, res),
|
||||||
|
cache: new InMemoryCache({
|
||||||
|
possibleTypes: { Transaction: ['InvoiceType', 'PaymentType'] },
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function initializeApollo(
|
||||||
* Always creates a new apollo client on the server
|
initialState: NormalizedCacheObject | null = null,
|
||||||
* Creates or reuses apollo client in the browser.
|
req?: IncomingMessage,
|
||||||
* @param {Object} initialState
|
res?: ServerResponse
|
||||||
*/
|
) {
|
||||||
function initApolloClient(ctx, initialState?) {
|
const _apolloClient = apolloClient ?? createApolloClient(req, res);
|
||||||
// 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 your page has Next.js data fetching methods that use Apollo Client, the initial state
|
||||||
if (!globalApolloClient) {
|
// get hydrated here
|
||||||
globalApolloClient = createApolloClient(ctx, initialState);
|
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;
|
||||||
|
|
||||||
return globalApolloClient;
|
return _apolloClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function useApollo(initialState: NormalizedCacheObject | null) {
|
||||||
* Creates and provides the apolloContext
|
const store = useMemo(() => initializeApollo(initialState), [initialState]);
|
||||||
* to a next.js PageTree. Use it by wrapping
|
return store;
|
||||||
* your PageComponent via HOC pattern.
|
|
||||||
* @param {Function|Class} PageComponent
|
|
||||||
* @param {Object} [config]
|
|
||||||
* @param {Boolean} [config.ssr=true]
|
|
||||||
*/
|
|
||||||
export function withApollo(PageComponent, { ssr = true } = {}) {
|
|
||||||
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
|
|
||||||
const client = apolloClient || initApolloClient(undefined, apolloState);
|
|
||||||
return (
|
|
||||||
<ApolloProvider client={client}>
|
|
||||||
<PageComponent {...pageProps} />
|
|
||||||
</ApolloProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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 (ssr || PageComponent.getInitialProps) {
|
|
||||||
WithApollo.getInitialProps = async ctx => {
|
|
||||||
const { AppTree } = ctx;
|
|
||||||
|
|
||||||
// Initialize ApolloClient, add it to the ctx object so
|
|
||||||
// we can use it in `PageComponent.getInitialProp`.
|
|
||||||
const apolloClient = (ctx.apolloClient = initApolloClient({
|
|
||||||
res: ctx.res,
|
|
||||||
req: ctx.req,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,10 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||||
module.exports = withBundleAnalyzer({
|
module.exports = withBundleAnalyzer({
|
||||||
poweredByHeader: false,
|
poweredByHeader: false,
|
||||||
assetPrefix: process.env.BASE_PATH || '',
|
assetPrefix: process.env.BASE_PATH || '',
|
||||||
|
basePath: process.env.BASE_PATH || '',
|
||||||
serverRuntimeConfig: {
|
serverRuntimeConfig: {
|
||||||
nodeEnv: process.env.NODE_ENV || 'development',
|
nodeEnv: process.env.NODE_ENV || 'development',
|
||||||
logLevel: process.env.LOG_LEVEL || 'info',
|
logLevel: process.env.LOG_LEVEL || 'info',
|
||||||
hodlKey: process.env.HODL_KEY || '',
|
|
||||||
cookiePath: process.env.COOKIE_PATH || '',
|
cookiePath: process.env.COOKIE_PATH || '',
|
||||||
lnServerUrl: process.env.SSO_SERVER_URL || '',
|
lnServerUrl: process.env.SSO_SERVER_URL || '',
|
||||||
lnCertPath: process.env.SSO_CERT_PATH || '',
|
lnCertPath: process.env.SSO_CERT_PATH || '',
|
||||||
|
@ -20,15 +20,12 @@ module.exports = withBundleAnalyzer({
|
||||||
nodeEnv: process.env.NODE_ENV || 'development',
|
nodeEnv: process.env.NODE_ENV || 'development',
|
||||||
apiUrl: `${process.env.BASE_PATH || ''}/api/v1`,
|
apiUrl: `${process.env.BASE_PATH || ''}/api/v1`,
|
||||||
apiBaseUrl: `${process.env.API_BASE_URL || ''}/api/v1`,
|
apiBaseUrl: `${process.env.API_BASE_URL || ''}/api/v1`,
|
||||||
basePath: process.env.BASE_PATH || '',
|
|
||||||
npmVersion: process.env.npm_package_version || '0.0.0',
|
npmVersion: process.env.npm_package_version || '0.0.0',
|
||||||
defaultTheme: process.env.THEME || 'dark',
|
defaultTheme: process.env.THEME || 'dark',
|
||||||
defaultCurrency: process.env.CURRENCY || 'sat',
|
defaultCurrency: process.env.CURRENCY || 'sat',
|
||||||
fetchPrices: process.env.FETCH_PRICES === 'false' ? false : true,
|
fetchPrices: process.env.FETCH_PRICES === 'false' ? false : true,
|
||||||
fetchFees: process.env.FETCH_FEES === 'false' ? false : true,
|
fetchFees: process.env.FETCH_FEES === 'false' ? false : true,
|
||||||
hodlhodl: process.env.HODL_HODL === 'false' ? false : true,
|
|
||||||
disableLinks: process.env.DISABLE_LINKS === 'true' ? true : false,
|
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,
|
noVersionCheck: process.env.NO_VERSION_CHECK === 'true' ? true : false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
4563
package-lock.json
generated
4563
package-lock.json
generated
File diff suppressed because it is too large
Load diff
125
package.json
125
package.json
|
@ -5,22 +5,19 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bs": "yarn build && yarn start",
|
"bs": "yarn build && yarn start",
|
||||||
"dev": "cross-env NODE_OPTIONS='--insecure-http-parser' next",
|
"dev": "next",
|
||||||
"dev:compatible": "next",
|
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "cross-env NODE_OPTIONS='--insecure-http-parser' next start",
|
"start": "next start",
|
||||||
"start:two": "cross-env NODE_OPTIONS='--insecure-http-parser' next start -p 3001",
|
"start:two": "next start -p 3001",
|
||||||
"start:compatible": "next start",
|
|
||||||
"start:compatible:two": "next start -p 3001",
|
|
||||||
"start:cookie": "sh ./scripts/initCookie.sh",
|
"start:cookie": "sh ./scripts/initCookie.sh",
|
||||||
"lint": "eslint */**/*.{js,ts,tsx} --fix",
|
"lint": "eslint . --ext ts --ext tsx --ext js",
|
||||||
"prettier": "prettier --write **/*.{ts,tsx,js,css,html}",
|
"format": "prettier --write \"**/*.{js,ts,tsx}\"",
|
||||||
"release": "standard-version --sign",
|
"release": "standard-version --sign",
|
||||||
"release:push": "standard-version --sign && git push --follow-tags origin master",
|
"release:push": "standard-version --sign && git push --follow-tags origin master",
|
||||||
"release:test": "standard-version --sign --dry-run",
|
"release:test": "standard-version --sign --dry-run",
|
||||||
"release:minor": "standard-version --sign --release-as minor && git push --follow-tags origin master",
|
"release:minor": "standard-version --sign --release-as minor && git push --follow-tags origin master",
|
||||||
"analyze": "cross-env ANALYZE=true next build",
|
"analyze": "npx 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": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"test:coverage": "jest --coverage",
|
"test:coverage": "jest --coverage",
|
||||||
|
@ -37,35 +34,27 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/react-common": "^3.1.4",
|
"@apollo/client": "^3.1.2",
|
||||||
"@apollo/react-hooks": "^3.1.5",
|
"@next/bundle-analyzer": "^9.5.1",
|
||||||
"@apollo/react-ssr": "^3.1.5",
|
"@types/react": "^16.9.44",
|
||||||
"apollo-cache-inmemory": "^1.6.6",
|
"apollo-server-micro": "^2.16.1",
|
||||||
"apollo-client": "^2.6.10",
|
|
||||||
"apollo-link-http": "^1.5.17",
|
|
||||||
"apollo-link-schema": "^1.2.5",
|
|
||||||
"apollo-server-micro": "^2.15.1",
|
|
||||||
"apollo-utilities": "^1.3.4",
|
|
||||||
"balanceofsatoshis": "^5.40.2",
|
"balanceofsatoshis": "^5.40.2",
|
||||||
"base64url": "^3.0.1",
|
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"cookie": "^0.4.1",
|
"cookie": "^0.4.1",
|
||||||
"crypto-js": "^4.0.0",
|
"date-fns": "^2.15.0",
|
||||||
"date-fns": "^2.14.0",
|
|
||||||
"graphql": "^15.3.0",
|
"graphql": "^15.3.0",
|
||||||
"graphql-iso-date": "^3.6.1",
|
"graphql-iso-date": "^3.6.1",
|
||||||
"graphql-rate-limit": "^2.0.1",
|
"graphql-rate-limit": "^2.0.1",
|
||||||
"graphql-tag": "^2.10.4",
|
|
||||||
"intersection-observer": "^0.11.0",
|
"intersection-observer": "^0.11.0",
|
||||||
"js-cookie": "^2.2.1",
|
"js-cookie": "^2.2.1",
|
||||||
"js-yaml": "^3.14.0",
|
"js-yaml": "^3.14.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"ln-service": "^49.3.1",
|
"ln-service": "^49.4.3",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.groupby": "^4.6.0",
|
"lodash.groupby": "^4.6.0",
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"lodash.omit": "^4.5.0",
|
"lodash.omit": "^4.5.0",
|
||||||
"next": "^9.4.4",
|
"next": "^9.5.1",
|
||||||
"numeral": "^2.0.6",
|
"numeral": "^2.0.6",
|
||||||
"qrcode.react": "^1.0.0",
|
"qrcode.react": "^1.0.0",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
|
@ -77,63 +66,73 @@
|
||||||
"react-spinners": "^0.9.0",
|
"react-spinners": "^0.9.0",
|
||||||
"react-spring": "^8.0.27",
|
"react-spring": "^8.0.27",
|
||||||
"react-toastify": "^6.0.8",
|
"react-toastify": "^6.0.8",
|
||||||
"react-tooltip": "^4.2.7",
|
"react-tooltip": "^4.2.8",
|
||||||
"styled-components": "^5.1.1",
|
"styled-components": "^5.1.1",
|
||||||
"styled-react-modal": "^2.0.1",
|
"styled-react-modal": "^2.0.1",
|
||||||
"styled-theming": "^2.2.0",
|
"styled-theming": "^2.2.0",
|
||||||
|
"typescript": "^3.9.7",
|
||||||
"underscore": "^1.10.2",
|
"underscore": "^1.10.2",
|
||||||
"uuid": "^8.2.0",
|
"uuid": "^8.3.0",
|
||||||
"victory": "^35.0.3",
|
"victory": "^35.0.8",
|
||||||
"winston": "^3.3.3",
|
"winston": "^3.3.3"
|
||||||
"zxcvbn": "^4.4.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.10.4",
|
"@babel/core": "^7.11.1",
|
||||||
"@commitlint/cli": "^9.0.1",
|
"@commitlint/cli": "^9.1.1",
|
||||||
"@commitlint/config-conventional": "^9.0.1",
|
"@commitlint/config-conventional": "^9.1.1",
|
||||||
"@graphql-codegen/cli": "^1.17.0",
|
"@graphql-codegen/cli": "^1.17.7",
|
||||||
"@graphql-codegen/fragment-matcher": "^1.17.0",
|
"@graphql-codegen/fragment-matcher": "^1.17.7",
|
||||||
"@graphql-codegen/introspection": "^1.17.0",
|
"@graphql-codegen/introspection": "^1.17.7",
|
||||||
"@graphql-codegen/near-operation-file-preset": "^1.17.0",
|
"@graphql-codegen/near-operation-file-preset": "^1.17.8",
|
||||||
"@graphql-codegen/typescript": "^1.17.0",
|
"@graphql-codegen/typescript": "^1.17.7",
|
||||||
"@graphql-codegen/typescript-operations": "^1.17.0",
|
"@graphql-codegen/typescript-operations": "^1.17.7",
|
||||||
"@graphql-codegen/typescript-react-apollo": "^1.17.0",
|
"@graphql-codegen/typescript-react-apollo": "^2.0.5",
|
||||||
"@graphql-codegen/typescript-resolvers": "^1.17.0",
|
"@graphql-codegen/typescript-resolvers": "^1.17.7",
|
||||||
"@next/bundle-analyzer": "^9.4.4",
|
"@testing-library/jest-dom": "^5.11.2",
|
||||||
"@testing-library/jest-dom": "^5.11.0",
|
"@testing-library/react": "^10.4.8",
|
||||||
"@testing-library/react": "^10.4.5",
|
"@types/bcryptjs": "^2.4.2",
|
||||||
|
"@types/cookie": "^0.4.0",
|
||||||
|
"@types/graphql-iso-date": "^3.4.0",
|
||||||
|
"@types/js-cookie": "^2.2.6",
|
||||||
|
"@types/js-yaml": "^3.12.5",
|
||||||
"@types/jsonwebtoken": "^8.5.0",
|
"@types/jsonwebtoken": "^8.5.0",
|
||||||
"@types/node": "^14.0.22",
|
"@types/lodash.groupby": "^4.6.6",
|
||||||
"@types/react": "^16.9.43",
|
"@types/lodash.merge": "^4.6.6",
|
||||||
"@types/styled-components": "^5.1.1",
|
"@types/lodash.omit": "^4.5.6",
|
||||||
"@types/styled-theming": "^2.2.4",
|
"@types/lodash.sortby": "^4.7.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^3.6.0",
|
"@types/node": "^14.0.27",
|
||||||
"@typescript-eslint/parser": "^3.6.0",
|
"@types/numeral": "0.0.28",
|
||||||
"apollo-server": "^2.15.1",
|
"@types/qrcode.react": "^1.0.1",
|
||||||
"apollo-server-testing": "^2.15.1",
|
"@types/react-copy-to-clipboard": "^4.3.0",
|
||||||
"babel-jest": "^26.1.0",
|
"@types/styled-components": "^5.1.2",
|
||||||
|
"@types/styled-react-modal": "^1.2.0",
|
||||||
|
"@types/styled-theming": "^2.2.5",
|
||||||
|
"@types/underscore": "^1.10.18",
|
||||||
|
"@types/uuid": "^8.0.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^3.8.0",
|
||||||
|
"@typescript-eslint/parser": "^3.8.0",
|
||||||
|
"apollo-server": "^2.16.1",
|
||||||
|
"apollo-server-testing": "^2.16.1",
|
||||||
|
"babel-jest": "^26.2.2",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.1.0",
|
||||||
"babel-plugin-inline-react-svg": "^1.1.1",
|
"babel-plugin-inline-react-svg": "^1.1.1",
|
||||||
"babel-plugin-styled-components": "^1.10.7",
|
"babel-plugin-styled-components": "^1.11.1",
|
||||||
"babel-preset-react-app": "^9.1.2",
|
"babel-preset-react-app": "^9.1.2",
|
||||||
"cross-env": "^7.0.2",
|
|
||||||
"devmoji": "^2.1.9",
|
"devmoji": "^2.1.9",
|
||||||
"eslint": "^7.4.0",
|
"eslint": "^7.6.0",
|
||||||
"eslint-config-prettier": "^6.11.0",
|
"eslint-config-prettier": "^6.11.0",
|
||||||
"eslint-plugin-import": "^2.22.0",
|
"eslint-plugin-import": "^2.22.0",
|
||||||
"eslint-plugin-jest": "^23.18.0",
|
"eslint-plugin-jest": "^23.20.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
"eslint-plugin-jsx-a11y": "^6.3.1",
|
||||||
"eslint-plugin-prettier": "^3.1.4",
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
"eslint-plugin-react": "^7.20.3",
|
"eslint-plugin-react": "^7.20.5",
|
||||||
"eslint-plugin-react-hooks": "^4.0.7",
|
"eslint-plugin-react-hooks": "^4.0.8",
|
||||||
"fast-diff": "^1.2.0",
|
|
||||||
"husky": "^4.2.5",
|
"husky": "^4.2.5",
|
||||||
"jest": "^26.1.0",
|
"jest": "^26.2.2",
|
||||||
"jest-fetch-mock": "^3.0.3",
|
"jest-fetch-mock": "^3.0.3",
|
||||||
"lint-staged": "^10.2.11",
|
"lint-staged": "^10.2.11",
|
||||||
"prettier": "^2.0.5",
|
"prettier": "^2.0.5",
|
||||||
"standard-version": "^8.0.2",
|
"standard-version": "^8.0.2"
|
||||||
"typescript": "^3.9.6"
|
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
|
|
@ -4,6 +4,9 @@ import { ModalProvider, BaseModalBackground } from 'styled-react-modal';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { StyledToastContainer } from 'src/components/toastContainer/ToastContainer';
|
import { StyledToastContainer } from 'src/components/toastContainer/ToastContainer';
|
||||||
|
import { AppProps } from 'next/app';
|
||||||
|
import { ApolloProvider } from '@apollo/client';
|
||||||
|
import { useApollo } from 'config/client';
|
||||||
import { ContextProvider } from '../src/context/ContextProvider';
|
import { ContextProvider } from '../src/context/ContextProvider';
|
||||||
import { useConfigState, ConfigProvider } from '../src/context/ConfigContext';
|
import { useConfigState, ConfigProvider } from '../src/context/ConfigContext';
|
||||||
import { GlobalStyles } from '../src/styles/GlobalStyle';
|
import { GlobalStyles } from '../src/styles/GlobalStyle';
|
||||||
|
@ -11,7 +14,6 @@ import { Header } from '../src/layouts/header/Header';
|
||||||
import { Footer } from '../src/layouts/footer/Footer';
|
import { Footer } from '../src/layouts/footer/Footer';
|
||||||
import 'react-toastify/dist/ReactToastify.min.css';
|
import 'react-toastify/dist/ReactToastify.min.css';
|
||||||
import { PageWrapper, HeaderBodyWrapper } from '../src/layouts/Layout.styled';
|
import { PageWrapper, HeaderBodyWrapper } from '../src/layouts/Layout.styled';
|
||||||
import { parseCookies } from '../src/utils/cookies';
|
|
||||||
import 'react-circular-progressbar/dist/styles.css';
|
import 'react-circular-progressbar/dist/styles.css';
|
||||||
|
|
||||||
const Wrapper: React.FC = ({ children }) => {
|
const Wrapper: React.FC = ({ children }) => {
|
||||||
|
@ -36,12 +38,14 @@ const Wrapper: React.FC = ({ children }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const App = ({ Component, pageProps, initialConfig }) => (
|
export default function App({ Component, pageProps }: AppProps) {
|
||||||
<>
|
const apolloClient = useApollo(pageProps.initialApolloState);
|
||||||
|
return (
|
||||||
|
<ApolloProvider client={apolloClient}>
|
||||||
<Head>
|
<Head>
|
||||||
<title>ThunderHub - Lightning Node Manager</title>
|
<title>ThunderHub - Lightning Node Manager</title>
|
||||||
</Head>
|
</Head>
|
||||||
<ConfigProvider initialConfig={initialConfig}>
|
<ConfigProvider initialConfig={pageProps.initialConfig}>
|
||||||
<ContextProvider>
|
<ContextProvider>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
|
@ -49,21 +53,6 @@ const App = ({ Component, pageProps, initialConfig }) => (
|
||||||
</ContextProvider>
|
</ContextProvider>
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
<StyledToastContainer />
|
<StyledToastContainer />
|
||||||
</>
|
</ApolloProvider>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
App.getInitialProps = async props => {
|
|
||||||
const cookies = parseCookies(props.ctx.req);
|
|
||||||
|
|
||||||
if (!cookies?.theme) {
|
|
||||||
return { initialConfig: 'dark' };
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const initialConfig = cookies.theme || 'dark';
|
|
||||||
return { initialConfig };
|
|
||||||
} catch (error) {
|
|
||||||
return { initialConfig: 'dark' };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
|
|
@ -1,84 +1,24 @@
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { ApolloServer } from 'apollo-server-micro';
|
import { ApolloServer } from 'apollo-server-micro';
|
||||||
import { getIp } from 'server/helpers/helpers';
|
|
||||||
import getConfig from 'next/config';
|
import getConfig from 'next/config';
|
||||||
import jwt from 'jsonwebtoken';
|
import { readCookie } from 'server/helpers/fileHelpers';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { schema } from 'server/schema';
|
||||||
import {
|
import { getContext } from 'server/schema/context';
|
||||||
readMacaroons,
|
|
||||||
readFile,
|
|
||||||
readCookie,
|
|
||||||
getAccounts,
|
|
||||||
} from 'server/helpers/fileHelpers';
|
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
|
||||||
import cookie from 'cookie';
|
|
||||||
import schema from 'server/schema';
|
|
||||||
|
|
||||||
const { publicRuntimeConfig, serverRuntimeConfig } = getConfig();
|
const { publicRuntimeConfig, serverRuntimeConfig } = getConfig();
|
||||||
const { apiBaseUrl, nodeEnv } = publicRuntimeConfig;
|
const { apiBaseUrl, nodeEnv } = publicRuntimeConfig;
|
||||||
const {
|
const { cookiePath } = serverRuntimeConfig;
|
||||||
cookiePath,
|
|
||||||
macaroonPath,
|
|
||||||
lnCertPath,
|
|
||||||
lnServerUrl,
|
|
||||||
accountConfigPath,
|
|
||||||
} = serverRuntimeConfig;
|
|
||||||
|
|
||||||
const secret =
|
export const secret =
|
||||||
nodeEnv === 'development'
|
nodeEnv === 'development'
|
||||||
? '123456789'
|
? '123456789'
|
||||||
: crypto.randomBytes(64).toString('hex');
|
: crypto.randomBytes(64).toString('hex');
|
||||||
|
|
||||||
const ssoMacaroon = readMacaroons(macaroonPath);
|
|
||||||
const ssoCert = readFile(lnCertPath);
|
|
||||||
const accountConfig = getAccounts(accountConfigPath);
|
|
||||||
|
|
||||||
readCookie(cookiePath);
|
readCookie(cookiePath);
|
||||||
|
|
||||||
const apolloServer = new ApolloServer({
|
const apolloServer = new ApolloServer({
|
||||||
schema,
|
schema,
|
||||||
context: ({ req, res }) => {
|
context: ({ req, res }) => getContext(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 = null;
|
|
||||||
if (AccountAuth) {
|
|
||||||
logger.silly('AccountAuth cookie found in request');
|
|
||||||
try {
|
|
||||||
const cookieAccount = jwt.verify(AccountAuth, secret);
|
|
||||||
account = cookieAccount['id'] || '';
|
|
||||||
} catch (error) {
|
|
||||||
logger.silly('Account authentication cookie failed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const context: ContextType = {
|
|
||||||
ip,
|
|
||||||
secret,
|
|
||||||
ssoVerified,
|
|
||||||
account,
|
|
||||||
sso: { macaroon: ssoMacaroon, cert: ssoCert, host: lnServerUrl || null },
|
|
||||||
accounts: accountConfig,
|
|
||||||
res,
|
|
||||||
};
|
|
||||||
|
|
||||||
return context;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
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 { ChainTransactions } from '../src/views/chain/transactions/ChainTransactions';
|
||||||
import { ChainUtxos } from '../src/views/chain/utxos/ChainUtxos';
|
import { ChainUtxos } from '../src/views/chain/utxos/ChainUtxos';
|
||||||
|
|
||||||
|
@ -20,4 +22,8 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</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 React, { useState, useEffect } from 'react';
|
||||||
import { useAccountState } from 'src/context/AccountContext';
|
|
||||||
import { useGetChannelAmountInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
|
import { useGetChannelAmountInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Settings } from 'react-feather';
|
import { Settings } from 'react-feather';
|
||||||
import { IconCursor } from 'src/views/channels/channels/Channel.style';
|
import { IconCursor } from 'src/views/channels/channels/Channel.style';
|
||||||
import { ChannelManage } from 'src/views/channels/channels/ChannelManage';
|
import { ChannelManage } from 'src/views/channels/channels/ChannelManage';
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
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 { Channels } from '../src/views/channels/channels/Channels';
|
||||||
import { PendingChannels } from '../src/views/channels/pendingChannels/PendingChannels';
|
import { PendingChannels } from '../src/views/channels/pendingChannels/PendingChannels';
|
||||||
import { ClosedChannels } from '../src/views/channels/closedChannels/ClosedChannels';
|
import { ClosedChannels } from '../src/views/channels/closedChannels/ClosedChannels';
|
||||||
|
@ -41,12 +42,7 @@ const ChannelView = () => {
|
||||||
closed: 0,
|
closed: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { auth } = useAccountState();
|
const { data } = useGetChannelAmountInfoQuery();
|
||||||
|
|
||||||
const { data } = useGetChannelAmountInfoQuery({
|
|
||||||
skip: !auth,
|
|
||||||
variables: { auth },
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data && data.getNodeInfo) {
|
if (data && data.getNodeInfo) {
|
||||||
|
@ -119,4 +115,8 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</GridWrapper>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withApollo(Wrapped);
|
export default Wrapped;
|
||||||
|
|
||||||
|
export async function getServerSideProps(context: NextPageContext) {
|
||||||
|
return await getProps(context, [GET_CHANNELS]);
|
||||||
|
}
|
||||||
|
|
|
@ -2,9 +2,12 @@ import * as React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Users } from 'react-feather';
|
import { Users } from 'react-feather';
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||||
import { withApollo } from 'config/client';
|
|
||||||
import { ChatInit } from 'src/components/chat/ChatInit';
|
import { ChatInit } from 'src/components/chat/ChatInit';
|
||||||
import { ChatFetcher } from 'src/components/chat/ChatFetcher';
|
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 { useNodeInfo } from 'src/hooks/UseNodeInfo';
|
||||||
import { useChatState } from '../src/context/ChatContext';
|
import { useChatState } from '../src/context/ChatContext';
|
||||||
import { separateBySender, getSenders } from '../src/utils/chat';
|
import { separateBySender, getSenders } from '../src/utils/chat';
|
||||||
import {
|
import {
|
||||||
|
@ -16,7 +19,6 @@ import {
|
||||||
import { Contacts } from '../src/views/chat/Contacts';
|
import { Contacts } from '../src/views/chat/Contacts';
|
||||||
import { ChatBox } from '../src/views/chat/ChatBox';
|
import { ChatBox } from '../src/views/chat/ChatBox';
|
||||||
import { ChatStart } from '../src/views/chat/ChatStart';
|
import { ChatStart } from '../src/views/chat/ChatStart';
|
||||||
import { useStatusState } from '../src/context/StatusContext';
|
|
||||||
import { Text } from '../src/components/typography/Styled';
|
import { Text } from '../src/components/typography/Styled';
|
||||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||||
import { ChatCard } from '../src/views/chat/Chat.styled';
|
import { ChatCard } from '../src/views/chat/Chat.styled';
|
||||||
|
@ -29,14 +31,44 @@ const ChatLayout = styled.div`
|
||||||
withHeight && 'height: 600px'}
|
withHeight && 'height: 600px'}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
user: string;
|
||||||
|
showContacts: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Action =
|
||||||
|
| {
|
||||||
|
type: 'setUserAndHide' | 'setUser';
|
||||||
|
user: string;
|
||||||
|
}
|
||||||
|
| { type: 'toggleShow' };
|
||||||
|
|
||||||
|
const initialState: State = { user: '', showContacts: false };
|
||||||
|
|
||||||
|
const reducer = (state: State, action: Action): State => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'setUser':
|
||||||
|
return { ...state, user: action.user };
|
||||||
|
case 'setUserAndHide':
|
||||||
|
return { user: action.user, showContacts: false };
|
||||||
|
case 'toggleShow':
|
||||||
|
return { ...state, showContacts: !state.showContacts };
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const ChatView = () => {
|
const ChatView = () => {
|
||||||
const { minorVersion } = useStatusState();
|
const { minorVersion } = useNodeInfo();
|
||||||
const { chats, sender, sentChats, initialized } = useChatState();
|
const { chats, sender, sentChats, initialized } = useChatState();
|
||||||
const bySender = separateBySender([...chats, ...sentChats]);
|
const bySender = separateBySender([...chats, ...sentChats]);
|
||||||
const senders = getSenders(bySender);
|
const senders = getSenders(bySender) || [];
|
||||||
|
|
||||||
const [user, setUser] = React.useState('');
|
const [state, dispatch] = React.useReducer(reducer, initialState);
|
||||||
const [showContacts, setShowContacts] = React.useState(false);
|
const { user, showContacts } = state;
|
||||||
|
|
||||||
|
const setUser = (user: string) => dispatch({ type: 'setUserAndHide', user });
|
||||||
|
const setName = (user: string) => dispatch({ type: 'setUser', user });
|
||||||
|
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
return <LoadingCard title={'Chats'} />;
|
return <LoadingCard title={'Chats'} />;
|
||||||
|
@ -66,7 +98,7 @@ const ChatView = () => {
|
||||||
contacts={senders}
|
contacts={senders}
|
||||||
user={user}
|
user={user}
|
||||||
setUser={setUser}
|
setUser={setUser}
|
||||||
setShow={setShowContacts}
|
setName={setName}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -76,11 +108,11 @@ const ChatView = () => {
|
||||||
contacts={senders}
|
contacts={senders}
|
||||||
user={user}
|
user={user}
|
||||||
setUser={setUser}
|
setUser={setUser}
|
||||||
setShow={setShowContacts}
|
setName={setName}
|
||||||
hide={true}
|
hide={true}
|
||||||
/>
|
/>
|
||||||
{user === 'New Chat' ? (
|
{user === 'New Chat' ? (
|
||||||
<ChatStart noTitle={true} />
|
<ChatStart noTitle={true} callback={() => setUser('')} />
|
||||||
) : (
|
) : (
|
||||||
<ChatBox messages={bySender[sender]} alias={user} />
|
<ChatBox messages={bySender[sender]} alias={user} />
|
||||||
)}
|
)}
|
||||||
|
@ -99,7 +131,7 @@ const ChatView = () => {
|
||||||
</ViewSwitch>
|
</ViewSwitch>
|
||||||
<ViewSwitch>
|
<ViewSwitch>
|
||||||
<SingleLine>
|
<SingleLine>
|
||||||
<ColorButton onClick={() => setShowContacts(prev => !prev)}>
|
<ColorButton onClick={() => dispatch({ type: 'toggleShow' })}>
|
||||||
<Users size={18} />
|
<Users size={18} />
|
||||||
</ColorButton>
|
</ColorButton>
|
||||||
<SubTitle>{user}</SubTitle>
|
<SubTitle>{user}</SubTitle>
|
||||||
|
@ -109,7 +141,7 @@ const ChatView = () => {
|
||||||
)}
|
)}
|
||||||
<ChatCard mobileCardPadding={'0'}>
|
<ChatCard mobileCardPadding={'0'}>
|
||||||
{chats.length <= 0 && sentChats.length <= 0 ? (
|
{chats.length <= 0 && sentChats.length <= 0 ? (
|
||||||
<ChatStart />
|
<ChatStart callback={() => setUser('')} />
|
||||||
) : (
|
) : (
|
||||||
renderChats()
|
renderChats()
|
||||||
)}
|
)}
|
||||||
|
@ -126,4 +158,8 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</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 React, { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { ChevronRight, ChevronUp, ChevronDown } from 'react-feather';
|
import { ChevronRight, ChevronUp, ChevronDown } from 'react-feather';
|
||||||
import { useAccountState } from 'src/context/AccountContext';
|
|
||||||
import { useChannelFeesQuery } from 'src/graphql/queries/__generated__/getChannelFees.generated';
|
import { useChannelFeesQuery } from 'src/graphql/queries/__generated__/getChannelFees.generated';
|
||||||
import { useUpdateFeesMutation } from 'src/graphql/mutations/__generated__/updateFees.generated';
|
import { useUpdateFeesMutation } from 'src/graphql/mutations/__generated__/updateFees.generated';
|
||||||
import { InputWithDeco } from 'src/components/input/InputWithDeco';
|
import { InputWithDeco } from 'src/components/input/InputWithDeco';
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||||
import { withApollo } from 'config/client';
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useStatusState } from 'src/context/StatusContext';
|
|
||||||
import { ChannelFeeType } from 'src/graphql/types';
|
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 { useNodeInfo } from 'src/hooks/UseNodeInfo';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardWithTitle,
|
CardWithTitle,
|
||||||
|
@ -22,15 +24,13 @@ import {
|
||||||
import { getErrorContent } from '../src/utils/error';
|
import { getErrorContent } from '../src/utils/error';
|
||||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||||
import { FeeCard } from '../src/views/fees/FeeCard';
|
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`
|
const WithPointer = styled.div`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const FeesView = () => {
|
const FeesView = () => {
|
||||||
const { minorVersion, revision } = useStatusState();
|
const { minorVersion, revision } = useNodeInfo();
|
||||||
const canMax = (minorVersion === 7 && revision > 1) || minorVersion > 7;
|
const canMax = (minorVersion === 7 && revision > 1) || minorVersion > 7;
|
||||||
const canMin = (minorVersion === 8 && revision > 2) || minorVersion > 8;
|
const canMin = (minorVersion === 8 && revision > 2) || minorVersion > 8;
|
||||||
|
|
||||||
|
@ -43,11 +43,7 @@ const FeesView = () => {
|
||||||
const [max, setMax] = useState(0);
|
const [max, setMax] = useState(0);
|
||||||
const [min, setMin] = useState(0);
|
const [min, setMin] = useState(0);
|
||||||
|
|
||||||
const { auth } = useAccountState();
|
|
||||||
|
|
||||||
const { loading, data } = useChannelFeesQuery({
|
const { loading, data } = useChannelFeesQuery({
|
||||||
skip: !auth,
|
|
||||||
variables: { auth },
|
|
||||||
onError: error => toast.error(getErrorContent(error)),
|
onError: error => toast.error(getErrorContent(error)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -73,7 +69,6 @@ const FeesView = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AdminSwitch>
|
|
||||||
<Card>
|
<Card>
|
||||||
<WithPointer>
|
<WithPointer>
|
||||||
<SingleLine onClick={() => setIsEdit(prev => !prev)}>
|
<SingleLine onClick={() => setIsEdit(prev => !prev)}>
|
||||||
|
@ -133,17 +128,24 @@ const FeesView = () => {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<RightAlign>
|
<RightAlign>
|
||||||
<SecureButton
|
<ColorButton
|
||||||
callback={updateFees}
|
onClick={() =>
|
||||||
variables={{
|
updateFees({
|
||||||
|
variables: {
|
||||||
...(baseFee !== 0 && { base_fee_tokens: baseFee }),
|
...(baseFee !== 0 && { base_fee_tokens: baseFee }),
|
||||||
...(feeRate !== 0 && { fee_rate: feeRate }),
|
...(feeRate !== 0 && { fee_rate: feeRate }),
|
||||||
...(cltv !== 0 && { cltv_delta: cltv }),
|
...(cltv !== 0 && { cltv_delta: cltv }),
|
||||||
...(max !== 0 &&
|
...(max !== 0 &&
|
||||||
canMax && { max_htlc_mtokens: (max * 1000).toString() }),
|
canMax && {
|
||||||
|
max_htlc_mtokens: (max * 1000).toString(),
|
||||||
|
}),
|
||||||
...(min !== 0 &&
|
...(min !== 0 &&
|
||||||
canMin && { min_htlc_mtokens: (min * 1000).toString() }),
|
canMin && {
|
||||||
}}
|
min_htlc_mtokens: (min * 1000).toString(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
disabled={
|
disabled={
|
||||||
baseFee === 0 &&
|
baseFee === 0 &&
|
||||||
feeRate === 0 &&
|
feeRate === 0 &&
|
||||||
|
@ -156,18 +158,18 @@ const FeesView = () => {
|
||||||
>
|
>
|
||||||
Update Fees
|
Update Fees
|
||||||
<ChevronRight size={18} />
|
<ChevronRight size={18} />
|
||||||
</SecureButton>
|
</ColorButton>
|
||||||
</RightAlign>
|
</RightAlign>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</AdminSwitch>
|
|
||||||
<CardWithTitle>
|
<CardWithTitle>
|
||||||
<SubTitle>Channel Details</SubTitle>
|
<SubTitle>Channel Details</SubTitle>
|
||||||
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
|
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
|
||||||
{data.getChannelFees.map((channel: ChannelFeeType, index: number) => (
|
{data.getChannelFees.map((channel, index) => (
|
||||||
<FeeCard
|
<FeeCard
|
||||||
channel={channel}
|
channel={channel as ChannelFeeType}
|
||||||
index={index + 1}
|
index={index + 1}
|
||||||
setIndexOpen={setIndexOpen}
|
setIndexOpen={setIndexOpen}
|
||||||
indexOpen={indexOpen}
|
indexOpen={indexOpen}
|
||||||
|
@ -186,4 +188,8 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</GridWrapper>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withApollo(Wrapped);
|
export default Wrapped;
|
||||||
|
|
||||||
|
export async function getServerSideProps(context: NextPageContext) {
|
||||||
|
return await getProps(context, [CHANNEL_FEES]);
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useAccountState } from 'src/context/AccountContext';
|
|
||||||
import { useGetForwardsQuery } from 'src/graphql/queries/__generated__/getForwards.generated';
|
import { useGetForwardsQuery } from 'src/graphql/queries/__generated__/getForwards.generated';
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
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 {
|
import {
|
||||||
SubTitle,
|
SubTitle,
|
||||||
Card,
|
Card,
|
||||||
|
@ -28,11 +30,8 @@ const ForwardsView = () => {
|
||||||
const [time, setTime] = useState('week');
|
const [time, setTime] = useState('week');
|
||||||
const [indexOpen, setIndexOpen] = useState(0);
|
const [indexOpen, setIndexOpen] = useState(0);
|
||||||
|
|
||||||
const { auth } = useAccountState();
|
|
||||||
|
|
||||||
const { loading, data } = useGetForwardsQuery({
|
const { loading, data } = useGetForwardsQuery({
|
||||||
skip: !auth,
|
variables: { time },
|
||||||
variables: { auth, time },
|
|
||||||
onError: error => toast.error(getErrorContent(error)),
|
onError: error => toast.error(getErrorContent(error)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -63,11 +62,13 @@ const ForwardsView = () => {
|
||||||
{renderButton('threeMonths', '3M')}
|
{renderButton('threeMonths', '3M')}
|
||||||
</SingleLine>
|
</SingleLine>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
{data.getForwards.forwards.length <= 0 && renderNoForwards()}
|
{data?.getForwards?.forwards &&
|
||||||
|
data.getForwards.forwards.length <= 0 &&
|
||||||
|
renderNoForwards()}
|
||||||
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
|
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
|
||||||
{data.getForwards.forwards.map((forward, index: number) => (
|
{data?.getForwards?.forwards?.map((forward, index) => (
|
||||||
<ForwardCard
|
<ForwardCard
|
||||||
forward={forward}
|
forward={forward as ForwardType}
|
||||||
key={index}
|
key={index}
|
||||||
index={index + 1}
|
index={index + 1}
|
||||||
setIndexOpen={setIndexOpen}
|
setIndexOpen={setIndexOpen}
|
||||||
|
@ -86,4 +87,8 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</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 React from 'react';
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||||
import { withApollo } from 'config/client';
|
|
||||||
import { Version } from 'src/components/version/Version';
|
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 { NetworkInfo } from '../src/views/home/networkInfo/NetworkInfo';
|
||||||
import { AccountInfo } from '../src/views/home/account/AccountInfo';
|
import { AccountInfo } from '../src/views/home/account/AccountInfo';
|
||||||
import { QuickActions } from '../src/views/home/quickActions/QuickActions';
|
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 { ForwardBox } from '../src/views/home/reports/forwardReport';
|
||||||
import { LiquidReport } from '../src/views/home/reports/liquidReport/LiquidReport';
|
import { LiquidReport } from '../src/views/home/reports/liquidReport/LiquidReport';
|
||||||
import { ConnectCard } from '../src/views/home/connect/Connect';
|
import { ConnectCard } from '../src/views/home/connect/Connect';
|
||||||
import { NodeBar } from '../src/components/nodeInfo/NodeBar';
|
|
||||||
|
|
||||||
const HomeView = () => {
|
const HomeView = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Version />
|
<Version />
|
||||||
<AccountInfo />
|
<AccountInfo />
|
||||||
<NodeBar />
|
|
||||||
<ConnectCard />
|
<ConnectCard />
|
||||||
<QuickActions />
|
<QuickActions />
|
||||||
<FlowBox />
|
<FlowBox />
|
||||||
|
@ -33,4 +33,8 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</GridWrapper>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withApollo(Wrapped);
|
export default Wrapped;
|
||||||
|
|
||||||
|
export async function getServerSideProps(context: NextPageContext) {
|
||||||
|
return await getProps(context, [GET_NODE_INFO]);
|
||||||
|
}
|
||||||
|
|
|
@ -1,44 +1,21 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Spacer } from 'src/components/spacer/Spacer';
|
import { Spacer } from 'src/components/spacer/Spacer';
|
||||||
import { withApollo } from 'config/client';
|
|
||||||
import { ServerAccounts } from 'src/components/accounts/ServerAccounts';
|
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 { ThunderStorm } from 'src/views/homepage/HomePage.styled';
|
||||||
import { appendBasePath } from 'src/utils/basePath';
|
import { NextPageContext } from 'next';
|
||||||
import { SessionLogin } from '../src/views/login/SessionLogin';
|
import { GET_SERVER_ACCOUNTS } from 'src/graphql/queries/getServerAccounts';
|
||||||
|
import { getProps } from 'src/utils/ssr';
|
||||||
import { TopSection } from '../src/views/homepage/Top';
|
import { TopSection } from '../src/views/homepage/Top';
|
||||||
import { LoginBox } from '../src/views/homepage/LoginBox';
|
|
||||||
import { Accounts } from '../src/views/homepage/Accounts';
|
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 ContextApp = () => (
|
||||||
const { noClient } = publicRuntimeConfig;
|
|
||||||
|
|
||||||
const ContextApp = () => {
|
|
||||||
const { finishedFetch } = useAccountState();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
<>
|
||||||
<ThunderStorm alt={''} src={appendBasePath('/static/thunderstorm.gif')} />
|
<ThunderStorm alt={''} src={'/static/thunderstorm.gif'} />
|
||||||
<TopSection />
|
<TopSection />
|
||||||
{!finishedFetch && (
|
|
||||||
<Section color={'transparent'}>
|
|
||||||
<LoadingCard loadingHeight={'160px'} />
|
|
||||||
</Section>
|
|
||||||
)}
|
|
||||||
{finishedFetch && (
|
|
||||||
<>
|
|
||||||
<SessionLogin />
|
|
||||||
<Accounts />
|
<Accounts />
|
||||||
{!noClient && <LoginBox />}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Spacer />
|
<Spacer />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
const Wrapped = () => (
|
const Wrapped = () => (
|
||||||
<>
|
<>
|
||||||
|
@ -47,4 +24,8 @@ const Wrapped = () => (
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withApollo(Wrapped);
|
export default Wrapped;
|
||||||
|
|
||||||
|
export async function getServerSideProps(context: NextPageContext) {
|
||||||
|
return await getProps(context, [GET_SERVER_ACCOUNTS]);
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useAccountState } from 'src/context/AccountContext';
|
|
||||||
import { useGetPeersQuery } from 'src/graphql/queries/__generated__/getPeers.generated';
|
import { useGetPeersQuery } from 'src/graphql/queries/__generated__/getPeers.generated';
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
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 {
|
import {
|
||||||
CardWithTitle,
|
CardWithTitle,
|
||||||
SubTitle,
|
SubTitle,
|
||||||
|
@ -14,14 +16,10 @@ import { AddPeer } from '../src/views/peers/AddPeer';
|
||||||
|
|
||||||
const PeersView = () => {
|
const PeersView = () => {
|
||||||
const [indexOpen, setIndexOpen] = useState(0);
|
const [indexOpen, setIndexOpen] = useState(0);
|
||||||
const { auth } = useAccountState();
|
|
||||||
|
|
||||||
const { loading, data } = useGetPeersQuery({
|
const { loading, data } = useGetPeersQuery();
|
||||||
skip: !auth,
|
|
||||||
variables: { auth },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (loading || !data || !data.getPeers) {
|
if (loading || !data?.getPeers) {
|
||||||
return <LoadingCard title={'Peers'} />;
|
return <LoadingCard title={'Peers'} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,11 +31,11 @@ const PeersView = () => {
|
||||||
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
|
<Card mobileCardPadding={'0'} mobileNoBackground={true}>
|
||||||
{data.getPeers.map((peer, index: number) => (
|
{data.getPeers.map((peer, index: number) => (
|
||||||
<PeersCard
|
<PeersCard
|
||||||
peer={peer}
|
peer={peer as PeerType}
|
||||||
index={index + 1}
|
index={index + 1}
|
||||||
setIndexOpen={setIndexOpen}
|
setIndexOpen={setIndexOpen}
|
||||||
indexOpen={indexOpen}
|
indexOpen={indexOpen}
|
||||||
key={`${index}-${peer.public_key}`}
|
key={`${index}-${peer?.public_key}`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -52,4 +50,8 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</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 React, { useState } from 'react';
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||||
import { withApollo } from 'config/client';
|
|
||||||
import { SimpleBalance } from 'src/views/balance/SimpleBalance';
|
import { SimpleBalance } from 'src/views/balance/SimpleBalance';
|
||||||
import {
|
import {
|
||||||
CardWithTitle,
|
CardWithTitle,
|
||||||
|
@ -11,10 +10,13 @@ import {
|
||||||
} from 'src/components/generic/Styled';
|
} from 'src/components/generic/Styled';
|
||||||
import { Text } from 'src/components/typography/Styled';
|
import { Text } from 'src/components/typography/Styled';
|
||||||
import { AdvancedBalance } from 'src/views/balance/AdvancedBalance';
|
import { AdvancedBalance } from 'src/views/balance/AdvancedBalance';
|
||||||
import { useStatusState } from '../src/context/StatusContext';
|
import { NextPageContext } from 'next';
|
||||||
|
import { getProps } from 'src/utils/ssr';
|
||||||
|
import { GET_CHANNELS } from 'src/graphql/queries/getChannels';
|
||||||
|
import { useNodeInfo } from 'src/hooks/UseNodeInfo';
|
||||||
|
|
||||||
const BalanceView = () => {
|
const BalanceView = () => {
|
||||||
const { minorVersion } = useStatusState();
|
const { minorVersion } = useNodeInfo();
|
||||||
const [advancedType, advancedTypeSet] = useState(false);
|
const [advancedType, advancedTypeSet] = useState(false);
|
||||||
|
|
||||||
if (minorVersion < 9) {
|
if (minorVersion < 9) {
|
||||||
|
@ -64,4 +66,8 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</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 React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
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 { SingleLine } from '../src/components/generic/Styled';
|
||||||
import { InterfaceSettings } from '../src/views/settings/Interface';
|
import { InterfaceSettings } from '../src/views/settings/Interface';
|
||||||
import { AccountSettings } from '../src/views/settings/Account';
|
import { AccountSettings } from '../src/views/settings/Account';
|
||||||
import { DangerView } from '../src/views/settings/Danger';
|
import { DangerView } from '../src/views/settings/Danger';
|
||||||
import { CurrentSettings } from '../src/views/settings/Current';
|
|
||||||
import { ChatSettings } from '../src/views/settings/Chat';
|
import { ChatSettings } from '../src/views/settings/Chat';
|
||||||
import { PrivacySettings } from '../src/views/settings/Privacy';
|
import { PrivacySettings } from '../src/views/settings/Privacy';
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ const SettingsView = () => {
|
||||||
<InterfaceSettings />
|
<InterfaceSettings />
|
||||||
<PrivacySettings />
|
<PrivacySettings />
|
||||||
<ChatSettings />
|
<ChatSettings />
|
||||||
<CurrentSettings />
|
|
||||||
<AccountSettings />
|
<AccountSettings />
|
||||||
<DangerView />
|
<DangerView />
|
||||||
</>
|
</>
|
||||||
|
@ -38,4 +37,8 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</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 React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||||
import { withApollo } from 'config/client';
|
|
||||||
import { VolumeStats } from 'src/views/stats/FlowStats';
|
import { VolumeStats } from 'src/views/stats/FlowStats';
|
||||||
import { TimeStats } from 'src/views/stats/TimeStats';
|
import { TimeStats } from 'src/views/stats/TimeStats';
|
||||||
import { FeeStats } from 'src/views/stats/FeeStats';
|
import { FeeStats } from 'src/views/stats/FeeStats';
|
||||||
import { StatResume } from 'src/views/stats/StatResume';
|
import { StatResume } from 'src/views/stats/StatResume';
|
||||||
import { StatsProvider } from 'src/views/stats/context';
|
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';
|
import { SingleLine } from '../src/components/generic/Styled';
|
||||||
|
|
||||||
export const ButtonRow = styled.div`
|
export const ButtonRow = styled.div`
|
||||||
|
@ -37,4 +41,12 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</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 React from 'react';
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
||||||
import { withApollo } from 'config/client';
|
|
||||||
import { Bakery } from 'src/views/tools/bakery/Bakery';
|
import { Bakery } from 'src/views/tools/bakery/Bakery';
|
||||||
import { Accounting } from 'src/views/tools/accounting/Accounting';
|
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 { BackupsView } from '../src/views/tools/backups/Backups';
|
||||||
import { MessagesView } from '../src/views/tools/messages/Messages';
|
import { MessagesView } from '../src/views/tools/messages/Messages';
|
||||||
import { WalletVersion } from '../src/views/tools/WalletVersion';
|
import { WalletVersion } from '../src/views/tools/WalletVersion';
|
||||||
|
@ -23,4 +25,8 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</GridWrapper>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withApollo(Wrapped);
|
export default Wrapped;
|
||||||
|
|
||||||
|
export async function getServerSideProps(context: NextPageContext) {
|
||||||
|
return await getProps(context, [GET_WALLET_INFO]);
|
||||||
|
}
|
||||||
|
|
|
@ -1,171 +0,0 @@
|
||||||
import React, { useState } from 'react';
|
|
||||||
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 {
|
|
||||||
CardWithTitle,
|
|
||||||
SubTitle,
|
|
||||||
Card,
|
|
||||||
DarkSubTitle,
|
|
||||||
ResponsiveLine,
|
|
||||||
} from '../src/components/generic/Styled';
|
|
||||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
|
||||||
import { OfferCard } from '../src/views/trading/OfferCard';
|
|
||||||
import { OfferFilters } from '../src/views/trading/OfferFilters';
|
|
||||||
import { Link } from '../src/components/link/Link';
|
|
||||||
import { ColorButton } from '../src/components/buttons/colorButton/ColorButton';
|
|
||||||
import { decode } from '../src/utils/helpers';
|
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
|
||||||
const { hodlhodl } = publicRuntimeConfig;
|
|
||||||
|
|
||||||
export interface QueryProps {
|
|
||||||
pagination: {
|
|
||||||
limit: number;
|
|
||||||
offset: number;
|
|
||||||
};
|
|
||||||
filters: {};
|
|
||||||
sort: {
|
|
||||||
by: string;
|
|
||||||
direction: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultQuery: QueryProps = {
|
|
||||||
pagination: {
|
|
||||||
limit: 25,
|
|
||||||
offset: 0,
|
|
||||||
},
|
|
||||||
filters: {},
|
|
||||||
sort: {
|
|
||||||
by: '',
|
|
||||||
direction: '',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const TradingView = () => {
|
|
||||||
const { query } = useRouter();
|
|
||||||
|
|
||||||
let decoded: QueryProps = defaultQuery;
|
|
||||||
if (query?.filter) {
|
|
||||||
const { filter } = query;
|
|
||||||
try {
|
|
||||||
if (typeof filter === 'string') {
|
|
||||||
decoded = JSON.parse(decode(filter));
|
|
||||||
} else {
|
|
||||||
decoded = JSON.parse(decode(filter[0]));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
toast.error('Incorrect url.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryObject = {
|
|
||||||
...defaultQuery,
|
|
||||||
...decoded,
|
|
||||||
};
|
|
||||||
|
|
||||||
const [indexOpen, setIndexOpen] = useState(0);
|
|
||||||
const [page, setPage] = useState(1);
|
|
||||||
const [fetching, setFetching] = useState(false);
|
|
||||||
|
|
||||||
const { data, loading, fetchMore, error } = useGetOffersQuery({
|
|
||||||
skip: !hodlhodl,
|
|
||||||
variables: { filter: JSON.stringify(queryObject) },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!hodlhodl) {
|
|
||||||
return (
|
|
||||||
<CardWithTitle>
|
|
||||||
<SubTitle>P2P Trading</SubTitle>
|
|
||||||
<Card bottom={'16px'}>
|
|
||||||
HodlHodl integration is disabled from the server.
|
|
||||||
</Card>
|
|
||||||
</CardWithTitle>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return (
|
|
||||||
<CardWithTitle>
|
|
||||||
<SubTitle>P2P Trading</SubTitle>
|
|
||||||
<Card bottom={'16px'}>Failed to connect with HodlHodl.</Card>
|
|
||||||
</CardWithTitle>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loading || !data || !data.getOffers) {
|
|
||||||
return <LoadingCard title={'P2P Trading'} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const amountOfOffers = data.getOffers.length;
|
|
||||||
const {
|
|
||||||
pagination: { limit },
|
|
||||||
} = queryObject;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CardWithTitle>
|
|
||||||
<ResponsiveLine>
|
|
||||||
<SubTitle>P2P Trading</SubTitle>
|
|
||||||
<DarkSubTitle>
|
|
||||||
Powered by <Link href={'https://hodlhodl.com/'}>HodlHodl</Link>
|
|
||||||
</DarkSubTitle>
|
|
||||||
</ResponsiveLine>
|
|
||||||
<Card bottom={'16px'}>
|
|
||||||
<OfferFilters offerFilters={queryObject} />
|
|
||||||
</Card>
|
|
||||||
<Card bottom={'8px'} mobileCardPadding={'0'} mobileNoBackground={true}>
|
|
||||||
{amountOfOffers <= 0 && <DarkSubTitle>No Offers Found</DarkSubTitle>}
|
|
||||||
{data.getOffers.map((offer, index: number) => (
|
|
||||||
<OfferCard
|
|
||||||
offer={offer}
|
|
||||||
index={index + 1}
|
|
||||||
setIndexOpen={setIndexOpen}
|
|
||||||
indexOpen={indexOpen}
|
|
||||||
key={`${index}-${offer.id}`}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Card>
|
|
||||||
{amountOfOffers > 0 && amountOfOffers === limit * page && (
|
|
||||||
<ColorButton
|
|
||||||
loading={fetching}
|
|
||||||
disabled={fetching}
|
|
||||||
onClick={() => {
|
|
||||||
setFetching(true);
|
|
||||||
|
|
||||||
fetchMore({
|
|
||||||
variables: {
|
|
||||||
filter: JSON.stringify({
|
|
||||||
...queryObject,
|
|
||||||
pagination: { limit, offset: limit * page },
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
updateQuery: (prev, { fetchMoreResult: result }) => {
|
|
||||||
if (!result) return prev;
|
|
||||||
|
|
||||||
setFetching(false);
|
|
||||||
setPage(prev => prev + 1);
|
|
||||||
return {
|
|
||||||
getOffers: [...prev.getOffers, ...result.getOffers],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Show More
|
|
||||||
</ColorButton>
|
|
||||||
)}
|
|
||||||
</CardWithTitle>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Wrapped = () => (
|
|
||||||
<GridWrapper>
|
|
||||||
<TradingView />
|
|
||||||
</GridWrapper>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default withApollo(Wrapped);
|
|
|
@ -1,13 +1,16 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useAccountState } from 'src/context/AccountContext';
|
|
||||||
import { InvoiceCard } from 'src/views/transactions/InvoiceCard';
|
import { InvoiceCard } from 'src/views/transactions/InvoiceCard';
|
||||||
import {
|
import {
|
||||||
useGetResumeQuery,
|
useGetResumeQuery,
|
||||||
GetResumeQuery,
|
GetResumeQuery,
|
||||||
} from 'src/graphql/queries/__generated__/getResume.generated';
|
} from 'src/graphql/queries/__generated__/getResume.generated';
|
||||||
import { GridWrapper } from 'src/components/gridWrapper/GridWrapper';
|
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 {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardWithTitle,
|
CardWithTitle,
|
||||||
|
@ -23,11 +26,8 @@ const TransactionsView = () => {
|
||||||
const [indexOpen, setIndexOpen] = useState(0);
|
const [indexOpen, setIndexOpen] = useState(0);
|
||||||
const [token, setToken] = useState('');
|
const [token, setToken] = useState('');
|
||||||
|
|
||||||
const { auth } = useAccountState();
|
|
||||||
|
|
||||||
const { loading, data, fetchMore } = useGetResumeQuery({
|
const { loading, data, fetchMore } = useGetResumeQuery({
|
||||||
skip: !auth,
|
variables: { token: '' },
|
||||||
variables: { auth, token: '' },
|
|
||||||
onError: error => toast.error(getErrorContent(error)),
|
onError: error => toast.error(getErrorContent(error)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -49,7 +49,10 @@ const TransactionsView = () => {
|
||||||
<CardWithTitle>
|
<CardWithTitle>
|
||||||
<SubTitle>Transactions</SubTitle>
|
<SubTitle>Transactions</SubTitle>
|
||||||
<Card bottom={'8px'} mobileCardPadding={'0'} mobileNoBackground={true}>
|
<Card bottom={'8px'} mobileCardPadding={'0'} mobileNoBackground={true}>
|
||||||
{resumeList.map((entry, index: number) => {
|
{resumeList?.map((entry, index: number) => {
|
||||||
|
if (!entry) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (entry.__typename === 'InvoiceType') {
|
if (entry.__typename === 'InvoiceType') {
|
||||||
return (
|
return (
|
||||||
<InvoiceCard
|
<InvoiceCard
|
||||||
|
@ -61,6 +64,7 @@ const TransactionsView = () => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (entry.__typename === 'PaymentType') {
|
||||||
return (
|
return (
|
||||||
<PaymentsCard
|
<PaymentsCard
|
||||||
payment={entry}
|
payment={entry}
|
||||||
|
@ -70,23 +74,25 @@ const TransactionsView = () => {
|
||||||
indexOpen={indexOpen}
|
indexOpen={indexOpen}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
})}
|
})}
|
||||||
<ColorButton
|
<ColorButton
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
withMargin={'16px 0 0'}
|
withMargin={'16px 0 0'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
fetchMore({
|
fetchMore({
|
||||||
variables: { auth, token },
|
variables: { token },
|
||||||
updateQuery: (
|
updateQuery: (
|
||||||
prev,
|
prev,
|
||||||
{
|
{ fetchMoreResult }: { fetchMoreResult?: GetResumeQuery }
|
||||||
fetchMoreResult: result,
|
): GetResumeQuery => {
|
||||||
}: { fetchMoreResult: GetResumeQuery }
|
if (!fetchMoreResult?.getResume) return prev;
|
||||||
) => {
|
const newToken = fetchMoreResult.getResume.token || '';
|
||||||
if (!result) return prev;
|
const prevEntries = prev?.getResume
|
||||||
const newToken = result.getResume.token || '';
|
? prev.getResume.resume
|
||||||
const prevEntries = prev.getResume.resume;
|
: [];
|
||||||
const newEntries = result.getResume.resume;
|
const newEntries = fetchMoreResult.getResume.resume;
|
||||||
|
|
||||||
const allTransactions = newToken
|
const allTransactions = newToken
|
||||||
? [...prevEntries, ...newEntries]
|
? [...prevEntries, ...newEntries]
|
||||||
|
@ -117,4 +123,11 @@ const Wrapped = () => (
|
||||||
</GridWrapper>
|
</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' } },
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ describe('getParsedAccount', () => {
|
||||||
return 'something else ';
|
return 'something else ';
|
||||||
});
|
});
|
||||||
const account = getParsedAccount(raw, 0, masterPassword, 'regtest');
|
const account = getParsedAccount(raw, 0, masterPassword, 'regtest');
|
||||||
expect(account.macaroon).toContain('macaroon');
|
expect(account?.macaroon).toContain('macaroon');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('picks up other networks', () => {
|
it('picks up other networks', () => {
|
||||||
|
@ -94,7 +94,7 @@ describe('getParsedAccount', () => {
|
||||||
return 'something else ';
|
return 'something else ';
|
||||||
});
|
});
|
||||||
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
||||||
expect(account.macaroon).toContain('macaroon');
|
expect(account?.macaroon).toContain('macaroon');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('macaroon handling', () => {
|
describe('macaroon handling', () => {
|
||||||
|
@ -109,7 +109,7 @@ describe('getParsedAccount', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
||||||
expect(account.macaroon).toBe('RAW MACAROON');
|
expect(account?.macaroon).toBe('RAW MACAROON');
|
||||||
});
|
});
|
||||||
it('falls back to macaroon path after that', () => {
|
it('falls back to macaroon path after that', () => {
|
||||||
const raw = {
|
const raw = {
|
||||||
|
@ -130,7 +130,7 @@ describe('getParsedAccount', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
||||||
expect(account.macaroon).toBe('yay');
|
expect(account?.macaroon).toBe('yay');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to lnd dir finally', () => {
|
it('falls back to lnd dir finally', () => {
|
||||||
|
@ -152,7 +152,7 @@ describe('getParsedAccount', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
||||||
expect(account.macaroon).toBe('yay');
|
expect(account?.macaroon).toBe('yay');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ describe('getParsedAccount', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
||||||
expect(account.cert).toBe('RAW CERT');
|
expect(account?.cert).toBe('RAW CERT');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to certificate path after that', () => {
|
it('falls back to certificate path after that', () => {
|
||||||
|
@ -188,7 +188,7 @@ describe('getParsedAccount', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
||||||
expect(account.cert).toBe('yay');
|
expect(account?.cert).toBe('yay');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to lnd dir finally', () => {
|
it('falls back to lnd dir finally', () => {
|
||||||
|
@ -207,7 +207,7 @@ describe('getParsedAccount', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
const account = getParsedAccount(raw, 0, masterPassword, 'mainnet');
|
||||||
expect(account.cert).toBe('yay');
|
expect(account?.cert).toBe('yay');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { getErrorMsg } from './helpers';
|
import { getErrorMsg } from './helpers';
|
||||||
import { logger } from './logger';
|
import { logger } from './logger';
|
||||||
|
|
||||||
export const to = promise => {
|
export const to = async <T>(promise: Promise<T>) => {
|
||||||
return promise
|
return promise
|
||||||
.then(data => data)
|
.then(data => data)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
@ -10,6 +10,15 @@ export const to = promise => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toWithError = promise => {
|
/*
|
||||||
return promise.then(data => [data, undefined]).catch(err => [undefined, err]);
|
* This is hard/impossible to type correctly. What we are describing
|
||||||
|
* here is a set of two states: either we have a result and no error,
|
||||||
|
* _or_ we have no result and an error. Unfortunately TypeScript is
|
||||||
|
* not able to infer this correctly...
|
||||||
|
* https://github.com/microsoft/TypeScript/issues/12184
|
||||||
|
*/
|
||||||
|
export const toWithError = async <T>(promise: Promise<T>) => {
|
||||||
|
return promise
|
||||||
|
.then(data => [data, undefined] as const)
|
||||||
|
.catch(err => [undefined, err] as const);
|
||||||
};
|
};
|
||||||
|
|
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;
|
||||||
|
};
|
|
@ -68,10 +68,15 @@ export const createCustomRecords = ({
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type DecodeMessageType = {
|
||||||
|
type: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
export const decodeMessage = ({
|
export const decodeMessage = ({
|
||||||
type,
|
type,
|
||||||
value,
|
value,
|
||||||
}): { [key: string]: string } | {} => {
|
}: DecodeMessageType): { [key: string]: string } | {} => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MESSAGE_TYPE:
|
case MESSAGE_TYPE:
|
||||||
return { message: bufferHexToUtf(value) };
|
return { message: bufferHexToUtf(value) };
|
||||||
|
|
|
@ -4,8 +4,9 @@ import path from 'path';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
import { getUUID } from 'src/utils/auth';
|
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
|
import { AccountType as ContextAccountType } from 'server/types/apiTypes';
|
||||||
|
import { getUUID } from './auth';
|
||||||
|
|
||||||
type EncodingType = 'hex' | 'utf-8';
|
type EncodingType = 'hex' | 'utf-8';
|
||||||
type BitcoinNetwork = 'mainnet' | 'regtest' | 'testnet';
|
type BitcoinNetwork = 'mainnet' | 'regtest' | 'testnet';
|
||||||
|
@ -17,14 +18,14 @@ type AccountType = {
|
||||||
network?: BitcoinNetwork;
|
network?: BitcoinNetwork;
|
||||||
macaroonPath?: string;
|
macaroonPath?: string;
|
||||||
certificatePath?: string;
|
certificatePath?: string;
|
||||||
password?: string;
|
password?: string | null;
|
||||||
macaroon?: string;
|
macaroon?: string;
|
||||||
certificate?: string;
|
certificate?: string;
|
||||||
};
|
};
|
||||||
type ParsedAccount = {
|
type ParsedAccount = {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
host: string;
|
socket: string;
|
||||||
macaroon: string;
|
macaroon: string;
|
||||||
cert: string;
|
cert: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
@ -36,7 +37,7 @@ type AccountConfigType = {
|
||||||
accounts: AccountType[];
|
accounts: AccountType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const isValidNetwork = (network: string): network is BitcoinNetwork =>
|
const isValidNetwork = (network: string | null): network is BitcoinNetwork =>
|
||||||
network === 'mainnet' || network === 'regtest' || network === 'testnet';
|
network === 'mainnet' || network === 'regtest' || network === 'testnet';
|
||||||
|
|
||||||
export const PRE_PASS_STRING = 'thunderhub-';
|
export const PRE_PASS_STRING = 'thunderhub-';
|
||||||
|
@ -78,7 +79,8 @@ export const parseYaml = (filePath: string): AccountConfigType | null => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const yamlObject = yaml.safeLoad(yamlConfig);
|
const yamlObject = yaml.safeLoad(yamlConfig);
|
||||||
return yamlObject;
|
// TODO: validate this, before returning?
|
||||||
|
return yamlObject as AccountConfigType;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
logger.error(
|
||||||
'Something went wrong while parsing the YAML config file: \n' + err
|
'Something went wrong while parsing the YAML config file: \n' + err
|
||||||
|
@ -115,12 +117,15 @@ export const hashPasswords = (
|
||||||
|
|
||||||
const cloned = { ...config };
|
const cloned = { ...config };
|
||||||
|
|
||||||
let hashedMasterPassword = config.masterPassword;
|
let hashedMasterPassword = config?.masterPassword;
|
||||||
|
|
||||||
if (hashedMasterPassword?.indexOf(PRE_PASS_STRING) < 0) {
|
if (
|
||||||
|
hashedMasterPassword &&
|
||||||
|
hashedMasterPassword.indexOf(PRE_PASS_STRING) < 0
|
||||||
|
) {
|
||||||
hasChanged = true;
|
hasChanged = true;
|
||||||
hashedMasterPassword = `${PRE_PASS_STRING}${bcrypt.hashSync(
|
hashedMasterPassword = `${PRE_PASS_STRING}${bcrypt.hashSync(
|
||||||
config.masterPassword,
|
hashedMasterPassword,
|
||||||
12
|
12
|
||||||
)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
|
@ -178,7 +183,7 @@ const getCertificate = ({
|
||||||
const getMacaroon = (
|
const getMacaroon = (
|
||||||
{ macaroon, macaroonPath, network, lndDir }: AccountType,
|
{ macaroon, macaroonPath, network, lndDir }: AccountType,
|
||||||
defaultNetwork: BitcoinNetwork
|
defaultNetwork: BitcoinNetwork
|
||||||
): string => {
|
): string | null => {
|
||||||
if (macaroon) {
|
if (macaroon) {
|
||||||
return macaroon;
|
return macaroon;
|
||||||
}
|
}
|
||||||
|
@ -203,24 +208,24 @@ const getMacaroon = (
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAccounts = (filePath: string) => {
|
export const getAccounts = (filePath: string): ContextAccountType[] => {
|
||||||
if (filePath === '') {
|
if (filePath === '') {
|
||||||
logger.verbose('No account config file path provided');
|
logger.verbose('No account config file path provided');
|
||||||
return null;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountConfig = parseYaml(filePath);
|
const accountConfig = parseYaml(filePath);
|
||||||
if (!accountConfig) {
|
if (!accountConfig) {
|
||||||
logger.info(`No account config file found at path ${filePath}`);
|
logger.info(`No account config file found at path ${filePath}`);
|
||||||
return null;
|
return [];
|
||||||
}
|
}
|
||||||
return getAccountsFromYaml(accountConfig, filePath);
|
return getAccountsFromYaml(accountConfig, filePath) as ContextAccountType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getParsedAccount = (
|
export const getParsedAccount = (
|
||||||
account: AccountType,
|
account: AccountType,
|
||||||
index: number,
|
index: number,
|
||||||
masterPassword: string,
|
masterPassword: string | null,
|
||||||
defaultNetwork: BitcoinNetwork
|
defaultNetwork: BitcoinNetwork
|
||||||
): ParsedAccount | null => {
|
): ParsedAccount | null => {
|
||||||
const {
|
const {
|
||||||
|
@ -276,12 +281,12 @@ export const getParsedAccount = (
|
||||||
const id = getUUID(`${name}${serverUrl}${macaroon}${cert}`);
|
const id = getUUID(`${name}${serverUrl}${macaroon}${cert}`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name: name || '',
|
||||||
id,
|
id,
|
||||||
host: serverUrl,
|
socket: serverUrl || '',
|
||||||
macaroon,
|
macaroon,
|
||||||
cert,
|
cert: cert || '',
|
||||||
password: password || masterPassword,
|
password: password || masterPassword || '',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -297,7 +302,7 @@ export const getAccountsFromYaml = (
|
||||||
}
|
}
|
||||||
|
|
||||||
const { defaultNetwork, masterPassword, accounts } = hashPasswords(
|
const { defaultNetwork, masterPassword, accounts } = hashPasswords(
|
||||||
hashed,
|
hashed || false,
|
||||||
config,
|
config,
|
||||||
filePath
|
filePath
|
||||||
);
|
);
|
||||||
|
@ -314,7 +319,7 @@ export const getAccountsFromYaml = (
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`Server accounts that will be available: ${parsedAccounts
|
`Server accounts that will be available: ${parsedAccounts
|
||||||
.map(({ name }) => name)
|
.map(account => account?.name)
|
||||||
.join(', ')}`
|
.join(', ')}`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { getNode, getChannel } from 'ln-service';
|
import { getNode, getChannel } from 'ln-service';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import { toWithError } from 'server/helpers/async';
|
import { toWithError } from 'server/helpers/async';
|
||||||
|
import {
|
||||||
|
LndObject,
|
||||||
|
GetChannelType,
|
||||||
|
GetNodeType,
|
||||||
|
} from 'server/types/ln-service.types';
|
||||||
|
|
||||||
const errorNode = {
|
const errorNode = {
|
||||||
alias: 'Partner node not found',
|
alias: 'Partner node not found',
|
||||||
|
@ -10,7 +15,7 @@ const errorNode = {
|
||||||
export const getNodeFromChannel = async (
|
export const getNodeFromChannel = async (
|
||||||
id: string,
|
id: string,
|
||||||
publicKey: string,
|
publicKey: string,
|
||||||
lnd
|
lnd: LndObject | null
|
||||||
) => {
|
) => {
|
||||||
const [channelInfo, channelError] = await toWithError(
|
const [channelInfo, channelError] = await toWithError(
|
||||||
getChannel({
|
getChannel({
|
||||||
|
@ -19,15 +24,15 @@ export const getNodeFromChannel = async (
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (channelError) {
|
if (channelError || !channelInfo) {
|
||||||
logger.verbose(`Error getting channel with id ${id}: %o`, channelError);
|
logger.verbose(`Error getting channel with id ${id}: %o`, channelError);
|
||||||
return errorNode;
|
return errorNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const partnerPublicKey =
|
const partnerPublicKey =
|
||||||
channelInfo.policies[0].public_key !== publicKey
|
(channelInfo as GetChannelType).policies[0].public_key !== publicKey
|
||||||
? channelInfo.policies[0].public_key
|
? (channelInfo as GetChannelType).policies[0].public_key
|
||||||
: channelInfo.policies[1].public_key;
|
: (channelInfo as GetChannelType).policies[1].public_key;
|
||||||
|
|
||||||
const [nodeInfo, nodeError] = await toWithError(
|
const [nodeInfo, nodeError] = await toWithError(
|
||||||
getNode({
|
getNode({
|
||||||
|
@ -37,7 +42,7 @@ export const getNodeFromChannel = async (
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (nodeError) {
|
if (nodeError || !nodeInfo) {
|
||||||
logger.verbose(
|
logger.verbose(
|
||||||
`Error getting node with public key ${partnerPublicKey}: %o`,
|
`Error getting node with public key ${partnerPublicKey}: %o`,
|
||||||
nodeError
|
nodeError
|
||||||
|
@ -46,7 +51,7 @@ export const getNodeFromChannel = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
alias: nodeInfo.alias,
|
alias: (nodeInfo as GetNodeType).alias,
|
||||||
color: nodeInfo.color,
|
color: (nodeInfo as GetNodeType).color,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,23 +1,7 @@
|
||||||
import { authenticatedLndGrpc } from 'ln-service';
|
|
||||||
import getConfig from 'next/config';
|
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 { nodeEnv } = serverRuntimeConfig || {};
|
||||||
const { noClient } = publicRuntimeConfig || {};
|
|
||||||
|
|
||||||
type LndAuthType = {
|
|
||||||
cert: string;
|
|
||||||
macaroon: string;
|
|
||||||
host: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getIp = (req: any) => {
|
export const getIp = (req: any) => {
|
||||||
if (!req || !req.headers) {
|
if (!req || !req.headers) {
|
||||||
|
@ -31,78 +15,6 @@ export const getIp = (req: any) => {
|
||||||
return ip;
|
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) {
|
|
||||||
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 => {
|
export const getErrorMsg = (error: any[] | string): string => {
|
||||||
if (typeof error === 'string') {
|
if (typeof error === 'string') {
|
||||||
return error;
|
return error;
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
export const getHodlParams = (params: any): string => {
|
|
||||||
let paramString = '?';
|
|
||||||
|
|
||||||
for (const key in params) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(params, key)) {
|
|
||||||
const element = params[key];
|
|
||||||
|
|
||||||
for (const subKey in element) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(element, subKey)) {
|
|
||||||
const subElement = element[subKey];
|
|
||||||
paramString = `${paramString}&${key}[${subKey}]=${subElement}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return paramString;
|
|
||||||
};
|
|
|
@ -10,6 +10,7 @@ interface RateConfigProps {
|
||||||
|
|
||||||
export const RateConfig: RateConfigProps = {
|
export const RateConfig: RateConfigProps = {
|
||||||
getMessages: { max: 10, window: '5s' },
|
getMessages: { max: 10, window: '5s' },
|
||||||
|
nodeInfo: { max: 10, window: '5s' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const rateLimiter = getGraphQLRateLimiter({
|
const rateLimiter = getGraphQLRateLimiter({
|
||||||
|
|
|
@ -6,16 +6,10 @@ Object {
|
||||||
"getServerAccounts": Array [
|
"getServerAccounts": Array [
|
||||||
Object {
|
Object {
|
||||||
"id": "accountID",
|
"id": "accountID",
|
||||||
"loggedIn": true,
|
"loggedIn": false,
|
||||||
"name": "account",
|
"name": "account",
|
||||||
"type": "server",
|
"type": "server",
|
||||||
},
|
},
|
||||||
Object {
|
|
||||||
"id": "sso",
|
|
||||||
"loggedIn": true,
|
|
||||||
"name": "SSO Account",
|
|
||||||
"type": "sso",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"errors": undefined,
|
"errors": undefined,
|
||||||
|
@ -34,7 +28,7 @@ Object {
|
||||||
"getServerAccounts": Array [
|
"getServerAccounts": Array [
|
||||||
Object {
|
Object {
|
||||||
"id": "accountID",
|
"id": "accountID",
|
||||||
"loggedIn": true,
|
"loggedIn": false,
|
||||||
"name": "account",
|
"name": "account",
|
||||||
"type": "server",
|
"type": "server",
|
||||||
},
|
},
|
||||||
|
@ -56,7 +50,7 @@ Object {
|
||||||
"getServerAccounts": Array [
|
"getServerAccounts": Array [
|
||||||
Object {
|
Object {
|
||||||
"id": "accountID",
|
"id": "accountID",
|
||||||
"loggedIn": true,
|
"loggedIn": false,
|
||||||
"name": "account",
|
"name": "account",
|
||||||
"type": "server",
|
"type": "server",
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,7 +11,6 @@ describe('Account Resolvers', () => {
|
||||||
|
|
||||||
const res = await query({
|
const res = await query({
|
||||||
query: GET_SERVER_ACCOUNTS,
|
query: GET_SERVER_ACCOUNTS,
|
||||||
variables: { auth: { type: 'test' } },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(res.errors).toBe(undefined);
|
expect(res.errors).toBe(undefined);
|
||||||
|
@ -22,7 +21,6 @@ describe('Account Resolvers', () => {
|
||||||
|
|
||||||
const res = await query({
|
const res = await query({
|
||||||
query: GET_SERVER_ACCOUNTS,
|
query: GET_SERVER_ACCOUNTS,
|
||||||
variables: { auth: { type: 'test' } },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(res.errors).toBe(undefined);
|
expect(res.errors).toBe(undefined);
|
||||||
|
@ -33,7 +31,6 @@ describe('Account Resolvers', () => {
|
||||||
|
|
||||||
const res = await query({
|
const res = await query({
|
||||||
query: GET_SERVER_ACCOUNTS,
|
query: GET_SERVER_ACCOUNTS,
|
||||||
variables: { auth: { type: 'test' } },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(res.errors).toBe(undefined);
|
expect(res.errors).toBe(undefined);
|
||||||
|
|
|
@ -1,42 +1,68 @@
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { SSO_ACCOUNT, SERVER_ACCOUNT } from 'src/context/AccountContext';
|
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
|
|
||||||
export const accountResolvers = {
|
export const accountResolvers = {
|
||||||
Query: {
|
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 (
|
getServerAccounts: async (
|
||||||
_: undefined,
|
_: undefined,
|
||||||
__: undefined,
|
__: undefined,
|
||||||
context: ContextType
|
context: ContextType
|
||||||
) => {
|
) => {
|
||||||
const { ip, accounts, account, sso, ssoVerified } = context;
|
const { ip, accounts, id, sso } = context;
|
||||||
await requestLimiter(ip, 'getServerAccounts');
|
await requestLimiter(ip, 'getServerAccounts');
|
||||||
|
|
||||||
const { macaroon, cert, host } = sso;
|
|
||||||
let ssoAccount = null;
|
let ssoAccount = null;
|
||||||
if (macaroon && host && ssoVerified) {
|
if (id === 'sso' && sso) {
|
||||||
|
const { cert, socket } = sso;
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Macaroon${
|
`Macaroon${
|
||||||
cert ? ', certificate' : ''
|
cert ? ', certificate' : ''
|
||||||
} and host (${host}) found for SSO.`
|
} and host (${socket}) found for SSO.`
|
||||||
);
|
);
|
||||||
ssoAccount = {
|
ssoAccount = {
|
||||||
name: 'SSO Account',
|
name: 'SSO Account',
|
||||||
id: SSO_ACCOUNT,
|
id: 'sso',
|
||||||
loggedIn: true,
|
loggedIn: true,
|
||||||
type: SSO_ACCOUNT,
|
type: 'sso',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const withStatus =
|
const withStatus =
|
||||||
accounts?.map(a => ({
|
accounts?.map(a => ({
|
||||||
...a,
|
...a,
|
||||||
loggedIn: a.id === account,
|
loggedIn: a.id === id,
|
||||||
type: SERVER_ACCOUNT,
|
type: 'server',
|
||||||
})) || [];
|
})) || [];
|
||||||
|
|
||||||
return ssoAccount ? [...withStatus, ssoAccount] : withStatus;
|
return ssoAccount ? [ssoAccount, ...withStatus] : withStatus;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,53 +6,66 @@ import {
|
||||||
PRE_PASS_STRING,
|
PRE_PASS_STRING,
|
||||||
} from 'server/helpers/fileHelpers';
|
} from 'server/helpers/fileHelpers';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { SSO_ACCOUNT, SERVER_ACCOUNT } from 'src/context/AccountContext';
|
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import cookie from 'cookie';
|
import cookie from 'cookie';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
|
import { appConstants } from 'server/utils/appConstants';
|
||||||
|
|
||||||
const { serverRuntimeConfig } = getConfig() || {};
|
const { serverRuntimeConfig } = getConfig() || {};
|
||||||
const { cookiePath, nodeEnv } = serverRuntimeConfig || {};
|
const { cookiePath, nodeEnv } = serverRuntimeConfig || {};
|
||||||
|
|
||||||
export const authResolvers = {
|
export const authResolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
getAuthToken: async (_: undefined, params: any, context: ContextType) => {
|
getAuthToken: async (
|
||||||
|
_: undefined,
|
||||||
|
params: any,
|
||||||
|
context: ContextType
|
||||||
|
): Promise<boolean> => {
|
||||||
const { ip, secret, sso, res } = context;
|
const { ip, secret, sso, res } = context;
|
||||||
await requestLimiter(ip, 'getAuthToken');
|
await requestLimiter(ip, 'getAuthToken');
|
||||||
|
|
||||||
if (!sso.host || !sso.macaroon) {
|
if (!sso) {
|
||||||
|
logger.warn('No SSO account available');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sso.socket || !sso.macaroon) {
|
||||||
logger.warn('Host and macaroon are required for SSO');
|
logger.warn('Host and macaroon are required for SSO');
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.cookie) {
|
if (!params.cookie) {
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cookiePath === '') {
|
if (cookiePath === '') {
|
||||||
logger.warn('SSO auth not available since no cookie path was provided');
|
logger.warn('SSO auth not available since no cookie path was provided');
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cookieFile = readCookie(cookiePath);
|
const cookieFile = readCookie(cookiePath);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
cookieFile.trim() === params.cookie.trim() ||
|
(cookieFile && cookieFile.trim() === params.cookie.trim()) ||
|
||||||
nodeEnv === 'development'
|
nodeEnv === 'development'
|
||||||
) {
|
) {
|
||||||
refreshCookie(cookiePath);
|
refreshCookie(cookiePath);
|
||||||
const token = jwt.sign({ user: SSO_ACCOUNT }, secret);
|
const token = jwt.sign({ id: 'sso' }, secret);
|
||||||
|
|
||||||
res.setHeader(
|
res.setHeader(
|
||||||
'Set-Cookie',
|
'Set-Cookie',
|
||||||
cookie.serialize('SSOAuth', token, { httpOnly: true, sameSite: true })
|
cookie.serialize(appConstants.cookieName, token, {
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: true,
|
||||||
|
path: '/',
|
||||||
|
})
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Cookie ${params.cookie} different to file ${cookieFile}`);
|
logger.debug(`Cookie ${params.cookie} different to file ${cookieFile}`);
|
||||||
return null;
|
return false;
|
||||||
},
|
},
|
||||||
getSessionToken: async (
|
getSessionToken: async (
|
||||||
_: undefined,
|
_: undefined,
|
||||||
|
@ -78,17 +91,13 @@ export const authResolvers = {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(`Correct password for account ${params.id}`);
|
logger.debug(`Correct password for account ${params.id}`);
|
||||||
const token = jwt.sign(
|
const token = jwt.sign({ id: params.id }, secret);
|
||||||
{
|
|
||||||
id: params.id,
|
|
||||||
},
|
|
||||||
secret
|
|
||||||
);
|
|
||||||
res.setHeader(
|
res.setHeader(
|
||||||
'Set-Cookie',
|
'Set-Cookie',
|
||||||
cookie.serialize('AccountAuth', token, {
|
cookie.serialize(appConstants.cookieName, token, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
sameSite: true,
|
sameSite: true,
|
||||||
|
path: '/',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
|
@ -99,21 +108,11 @@ export const authResolvers = {
|
||||||
const { ip, res } = context;
|
const { ip, res } = context;
|
||||||
await requestLimiter(ip, 'logout');
|
await requestLimiter(ip, 'logout');
|
||||||
|
|
||||||
if (params.type === SSO_ACCOUNT) {
|
|
||||||
res.setHeader(
|
res.setHeader(
|
||||||
'Set-Cookie',
|
'Set-Cookie',
|
||||||
cookie.serialize('SSOAuth', '', { maxAge: 1 })
|
cookie.serialize(appConstants.cookieName, '', { maxAge: 1 })
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
if (params.type === SERVER_ACCOUNT) {
|
|
||||||
res.setHeader(
|
|
||||||
'Set-Cookie',
|
|
||||||
cookie.serialize('AccountAuth', '', { maxAge: 1 })
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { getLnd } from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import { AuthType } from 'src/context/AccountContext';
|
|
||||||
|
|
||||||
import { rebalance } from 'balanceofsatoshis/swaps';
|
import { rebalance } from 'balanceofsatoshis/swaps';
|
||||||
import { getAccountingReport } from 'balanceofsatoshis/balances';
|
import { getAccountingReport } from 'balanceofsatoshis/balances';
|
||||||
import request from '@alexbosworth/request';
|
import request from '@alexbosworth/request';
|
||||||
|
import { RebalanceResponseType } from 'server/types/balanceofsatoshis.types';
|
||||||
|
|
||||||
type RebalanceType = {
|
type RebalanceType = {
|
||||||
auth: AuthType;
|
|
||||||
avoid?: String[];
|
avoid?: String[];
|
||||||
in_through?: String;
|
in_through?: String;
|
||||||
is_avoiding_high_inbound?: Boolean;
|
is_avoiding_high_inbound?: Boolean;
|
||||||
|
@ -23,7 +20,6 @@ type RebalanceType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type AccountingType = {
|
type AccountingType = {
|
||||||
auth: AuthType;
|
|
||||||
category?: String;
|
category?: String;
|
||||||
currency?: String;
|
currency?: String;
|
||||||
fiat?: String;
|
fiat?: String;
|
||||||
|
@ -38,8 +34,7 @@ export const bosResolvers = {
|
||||||
params: AccountingType,
|
params: AccountingType,
|
||||||
context: ContextType
|
context: ContextType
|
||||||
) => {
|
) => {
|
||||||
const { auth, ...settings } = params;
|
const { lnd } = context;
|
||||||
const lnd = getLnd(auth, context);
|
|
||||||
|
|
||||||
const response = await to(
|
const response = await to(
|
||||||
getAccountingReport({
|
getAccountingReport({
|
||||||
|
@ -47,7 +42,7 @@ export const bosResolvers = {
|
||||||
logger,
|
logger,
|
||||||
request,
|
request,
|
||||||
is_csv: true,
|
is_csv: true,
|
||||||
...settings,
|
...params,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -61,7 +56,6 @@ export const bosResolvers = {
|
||||||
context: ContextType
|
context: ContextType
|
||||||
) => {
|
) => {
|
||||||
const {
|
const {
|
||||||
auth,
|
|
||||||
avoid,
|
avoid,
|
||||||
in_through,
|
in_through,
|
||||||
is_avoiding_high_inbound,
|
is_avoiding_high_inbound,
|
||||||
|
@ -73,16 +67,16 @@ export const bosResolvers = {
|
||||||
out_through,
|
out_through,
|
||||||
target,
|
target,
|
||||||
} = params;
|
} = params;
|
||||||
const lnd = getLnd(auth, context);
|
const { lnd } = context;
|
||||||
|
|
||||||
const filteredParams = {
|
const filteredParams = {
|
||||||
avoid,
|
avoid,
|
||||||
out_channels,
|
out_channels,
|
||||||
...(in_through && { in_through }),
|
...(in_through && { in_through }),
|
||||||
...(is_avoiding_high_inbound && { is_avoiding_high_inbound }),
|
...(is_avoiding_high_inbound && { is_avoiding_high_inbound }),
|
||||||
...(max_fee > 0 && { max_fee }),
|
...(max_fee && max_fee > 0 && { max_fee }),
|
||||||
...(max_fee_rate > 0 && { max_fee_rate }),
|
...(max_fee_rate && max_fee_rate > 0 && { max_fee_rate }),
|
||||||
...(max_rebalance > 0 && { max_rebalance }),
|
...(max_rebalance && max_rebalance > 0 && { max_rebalance }),
|
||||||
...(node && { node }),
|
...(node && { node }),
|
||||||
...(out_through && { out_through }),
|
...(out_through && { out_through }),
|
||||||
...(target && { target }),
|
...(target && { target }),
|
||||||
|
@ -90,7 +84,7 @@ export const bosResolvers = {
|
||||||
|
|
||||||
logger.info('Rebalance Params: %o', filteredParams);
|
logger.info('Rebalance Params: %o', filteredParams);
|
||||||
|
|
||||||
const response = await to(
|
const response = await to<RebalanceResponseType>(
|
||||||
rebalance({
|
rebalance({
|
||||||
lnd,
|
lnd,
|
||||||
logger,
|
logger,
|
||||||
|
|
|
@ -1,34 +1,31 @@
|
||||||
import { AuthMock } from 'server/tests/testMocks';
|
|
||||||
import testServer from 'server/tests/testServer';
|
import testServer from 'server/tests/testServer';
|
||||||
import gql from 'graphql-tag';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
jest.mock('ln-service');
|
jest.mock('ln-service');
|
||||||
|
|
||||||
describe('Chain Resolvers', () => {
|
describe('Chain Resolvers', () => {
|
||||||
test('getChainBalance', async () => {
|
test('getChainBalance', async () => {
|
||||||
const getChainBalance = gql`
|
const getChainBalance = gql`
|
||||||
query($auth: authType!) {
|
query {
|
||||||
getChainBalance(auth: $auth)
|
getChainBalance
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const { query } = testServer();
|
const { query } = testServer();
|
||||||
const res = await query({
|
const res = await query({
|
||||||
query: getChainBalance,
|
query: getChainBalance,
|
||||||
variables: AuthMock,
|
|
||||||
});
|
});
|
||||||
expect(res.errors).toBe(undefined);
|
expect(res.errors).toBe(undefined);
|
||||||
expect(res).toMatchSnapshot();
|
expect(res).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
test('getPendingChainBalance', async () => {
|
test('getPendingChainBalance', async () => {
|
||||||
const getPendingChainBalance = gql`
|
const getPendingChainBalance = gql`
|
||||||
query($auth: authType!) {
|
query {
|
||||||
getPendingChainBalance(auth: $auth)
|
getPendingChainBalance
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const { query } = testServer();
|
const { query } = testServer();
|
||||||
const res = await query({
|
const res = await query({
|
||||||
query: getPendingChainBalance,
|
query: getPendingChainBalance,
|
||||||
variables: AuthMock,
|
|
||||||
});
|
});
|
||||||
expect(res.errors).toBe(undefined);
|
expect(res.errors).toBe(undefined);
|
||||||
expect(res).toMatchSnapshot();
|
expect(res).toMatchSnapshot();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
getChainBalance as getBalance,
|
getChainBalance,
|
||||||
getPendingChainBalance as getPending,
|
getPendingChainBalance,
|
||||||
getChainTransactions,
|
getChainTransactions,
|
||||||
getUtxos,
|
getUtxos,
|
||||||
sendToChainAddress,
|
sendToChainAddress,
|
||||||
|
@ -9,12 +9,16 @@ import {
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import {
|
import { getErrorMsg } from 'server/helpers/helpers';
|
||||||
getAuthLnd,
|
|
||||||
getErrorMsg,
|
|
||||||
getCorrectAuth,
|
|
||||||
} from 'server/helpers/helpers';
|
|
||||||
import { sortBy } from 'underscore';
|
import { sortBy } from 'underscore';
|
||||||
|
import { to } from 'server/helpers/async';
|
||||||
|
import {
|
||||||
|
GetChainBalanceType,
|
||||||
|
GetPendingChainBalanceType,
|
||||||
|
GetChainTransactionsType,
|
||||||
|
GetUtxosType,
|
||||||
|
SendToChainAddressType,
|
||||||
|
} from 'server/types/ln-service.types';
|
||||||
|
|
||||||
interface ChainBalanceProps {
|
interface ChainBalanceProps {
|
||||||
chain_balance: number;
|
chain_balance: number;
|
||||||
|
@ -33,18 +37,14 @@ export const chainResolvers = {
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'chainBalance');
|
await requestLimiter(context.ip, 'chainBalance');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
try {
|
const value: ChainBalanceProps = await to<GetChainBalanceType>(
|
||||||
const value: ChainBalanceProps = await getBalance({
|
getChainBalance({
|
||||||
lnd,
|
lnd,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
return value.chain_balance;
|
return value.chain_balance;
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error getting chain balance: %o', error);
|
|
||||||
throw new Error(getErrorMsg(error));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
getPendingChainBalance: async (
|
getPendingChainBalance: async (
|
||||||
_: undefined,
|
_: undefined,
|
||||||
|
@ -53,18 +53,16 @@ export const chainResolvers = {
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'pendingChainBalance');
|
await requestLimiter(context.ip, 'pendingChainBalance');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
try {
|
const pendingValue: PendingChainBalanceProps = await to<
|
||||||
const pendingValue: PendingChainBalanceProps = await getPending({
|
GetPendingChainBalanceType
|
||||||
|
>(
|
||||||
|
getPendingChainBalance({
|
||||||
lnd,
|
lnd,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
return pendingValue.pending_chain_balance;
|
return pendingValue.pending_chain_balance;
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error getting pending chain balance: %o', error);
|
|
||||||
throw new Error(getErrorMsg(error));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
getChainTransactions: async (
|
getChainTransactions: async (
|
||||||
_: undefined,
|
_: undefined,
|
||||||
|
@ -73,46 +71,35 @@ export const chainResolvers = {
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'chainTransactions');
|
await requestLimiter(context.ip, 'chainTransactions');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
try {
|
const transactionList = await to<GetChainTransactionsType>(
|
||||||
const transactionList = await getChainTransactions({
|
getChainTransactions({
|
||||||
lnd,
|
lnd,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const transactions = sortBy(
|
const transactions = sortBy(
|
||||||
transactionList.transactions,
|
transactionList.transactions,
|
||||||
'created_at'
|
'created_at'
|
||||||
).reverse();
|
).reverse();
|
||||||
return transactions;
|
return transactions;
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error getting chain transactions: %o', error);
|
|
||||||
throw new Error(getErrorMsg(error));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
getUtxos: async (_: undefined, params: any, context: ContextType) => {
|
getUtxos: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'getUtxos');
|
await requestLimiter(context.ip, 'getUtxos');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
try {
|
const info = await to<GetUtxosType>(getUtxos({ lnd }));
|
||||||
const { utxos } = await getUtxos({ lnd });
|
|
||||||
|
|
||||||
return utxos;
|
return info?.utxos;
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error getting utxos: %o', error);
|
|
||||||
throw new Error(getErrorMsg(error));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
createAddress: async (_: undefined, params: any, context: ContextType) => {
|
createAddress: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'getAddress');
|
await requestLimiter(context.ip, 'getAddress');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const format = params.nested ? 'np2wpkh' : 'p2wpkh';
|
const format = params.nested ? 'np2wpkh' : 'p2wpkh';
|
||||||
|
|
||||||
|
@ -132,8 +119,7 @@ export const chainResolvers = {
|
||||||
sendToAddress: async (_: undefined, params: any, context: ContextType) => {
|
sendToAddress: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'sendToAddress');
|
await requestLimiter(context.ip, 'sendToAddress');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const props = params.fee
|
const props = params.fee
|
||||||
? { fee_tokens_per_vbyte: params.fee }
|
? { fee_tokens_per_vbyte: params.fee }
|
||||||
|
@ -143,14 +129,15 @@ export const chainResolvers = {
|
||||||
|
|
||||||
const sendAll = params.sendAll ? { is_send_all: true } : {};
|
const sendAll = params.sendAll ? { is_send_all: true } : {};
|
||||||
|
|
||||||
try {
|
const send = await to<SendToChainAddressType>(
|
||||||
const send = await sendToChainAddress({
|
sendToChainAddress({
|
||||||
lnd,
|
lnd,
|
||||||
address: params.address,
|
address: params.address,
|
||||||
...(params.tokens && { tokens: params.tokens }),
|
...(params.tokens && { tokens: params.tokens }),
|
||||||
...props,
|
...props,
|
||||||
...sendAll,
|
...sendAll,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
confirmationCount: send.confirmation_count,
|
confirmationCount: send.confirmation_count,
|
||||||
|
@ -159,10 +146,6 @@ export const chainResolvers = {
|
||||||
isOutgoing: send.is_outgoing,
|
isOutgoing: send.is_outgoing,
|
||||||
...(send.tokens && { tokens: send.tokens }),
|
...(send.tokens && { tokens: send.tokens }),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error sending to chain address: %o', error);
|
|
||||||
throw new Error(getErrorMsg(error));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,19 +2,19 @@ import { gql } from 'apollo-server-micro';
|
||||||
|
|
||||||
export const chainTypes = gql`
|
export const chainTypes = gql`
|
||||||
type getUtxosType {
|
type getUtxosType {
|
||||||
address: String
|
address: String!
|
||||||
address_format: String
|
address_format: String!
|
||||||
confirmation_count: Int
|
confirmation_count: Int!
|
||||||
output_script: String
|
output_script: String!
|
||||||
tokens: Int
|
tokens: Int!
|
||||||
transaction_id: String
|
transaction_id: String!
|
||||||
transaction_vout: Int
|
transaction_vout: Int!
|
||||||
}
|
}
|
||||||
type sendToType {
|
type sendToType {
|
||||||
confirmationCount: String
|
confirmationCount: String!
|
||||||
id: String
|
id: String!
|
||||||
isConfirmed: Boolean
|
isConfirmed: Boolean!
|
||||||
isOutgoing: Boolean
|
isOutgoing: Boolean!
|
||||||
tokens: Int
|
tokens: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,10 +22,10 @@ export const chainTypes = gql`
|
||||||
block_id: String
|
block_id: String
|
||||||
confirmation_count: Int
|
confirmation_count: Int
|
||||||
confirmation_height: Int
|
confirmation_height: Int
|
||||||
created_at: String
|
created_at: String!
|
||||||
fee: Int
|
fee: Int
|
||||||
id: String
|
id: String!
|
||||||
output_addresses: [String]
|
output_addresses: [String]!
|
||||||
tokens: Int
|
tokens: Int!
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import { toWithError } from 'server/helpers/async';
|
import { toWithError } from 'server/helpers/async';
|
||||||
import { getChannel } from 'ln-service';
|
import { getChannel } from 'ln-service';
|
||||||
|
import { GetChannelType } from 'server/types/ln-service.types';
|
||||||
import { openChannel } from './resolvers/mutation/openChannel';
|
import { openChannel } from './resolvers/mutation/openChannel';
|
||||||
import { closeChannel } from './resolvers/mutation/closeChannel';
|
import { closeChannel } from './resolvers/mutation/closeChannel';
|
||||||
import { updateFees } from './resolvers/mutation/updateFees';
|
import { updateFees } from './resolvers/mutation/updateFees';
|
||||||
|
@ -53,7 +54,7 @@ export const channelResolvers = {
|
||||||
let node_policies = null;
|
let node_policies = null;
|
||||||
let partner_node_policies = null;
|
let partner_node_policies = null;
|
||||||
|
|
||||||
channel.policies.forEach(policy => {
|
(channel as GetChannelType).policies.forEach(policy => {
|
||||||
if (localKey && localKey === policy.public_key) {
|
if (localKey && localKey === policy.public_key) {
|
||||||
node_policies = {
|
node_policies = {
|
||||||
...policy,
|
...policy,
|
||||||
|
@ -67,7 +68,11 @@ export const channelResolvers = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { ...channel, node_policies, partner_node_policies };
|
return {
|
||||||
|
...(channel as GetChannelType),
|
||||||
|
node_policies,
|
||||||
|
partner_node_policies,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import { closeChannel as lnCloseChannel } from 'ln-service';
|
import { closeChannel as lnCloseChannel } from 'ln-service';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { logger } from 'server/helpers/logger';
|
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import {
|
import { to } from 'server/helpers/async';
|
||||||
getAuthLnd,
|
import { CloseChannelType } from 'server/types/ln-service.types';
|
||||||
getErrorMsg,
|
import { logger } from 'server/helpers/logger';
|
||||||
getCorrectAuth,
|
|
||||||
} from 'server/helpers/helpers';
|
|
||||||
|
|
||||||
export const closeChannel = async (
|
export const closeChannel = async (
|
||||||
_: undefined,
|
_: undefined,
|
||||||
|
@ -15,23 +12,28 @@ export const closeChannel = async (
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'closeChannel');
|
await requestLimiter(context.ip, 'closeChannel');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
try {
|
const closeParams = {
|
||||||
const info = await lnCloseChannel({
|
|
||||||
lnd,
|
|
||||||
id: params.id,
|
id: params.id,
|
||||||
target_confirmations: params.targetConfirmations,
|
target_confirmations: params.targetConfirmations,
|
||||||
tokens_per_vbyte: params.tokensPerVByte,
|
tokens_per_vbyte: params.tokensPerVByte,
|
||||||
is_force_close: params.forceClose,
|
is_force_close: params.forceClose,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
logger.info('Closing channel with params: %o', closeParams);
|
||||||
|
|
||||||
|
const info = await to<CloseChannelType>(
|
||||||
|
lnCloseChannel({
|
||||||
|
lnd,
|
||||||
|
...closeParams,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info('Channel closed: %o', params.id);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transactionId: info.transaction_id,
|
transactionId: info.transaction_id,
|
||||||
transactionOutputIndex: info.transaction_vout,
|
transactionOutputIndex: info.transaction_vout,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error closing channel: %o', error);
|
|
||||||
throw new Error(getErrorMsg(error));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,42 +2,54 @@ import { openChannel as lnOpenChannel } from 'ln-service';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import {
|
import { to } from 'server/helpers/async';
|
||||||
getAuthLnd,
|
import { OpenChannelType } from 'server/types/ln-service.types';
|
||||||
getErrorMsg,
|
|
||||||
getCorrectAuth,
|
type OpenChannelParams = {
|
||||||
} from 'server/helpers/helpers';
|
isPrivate: boolean;
|
||||||
|
amount: number;
|
||||||
|
partnerPublicKey: string;
|
||||||
|
tokensPerVByte: number;
|
||||||
|
pushTokens: number;
|
||||||
|
};
|
||||||
|
|
||||||
export const openChannel = async (
|
export const openChannel = async (
|
||||||
_: undefined,
|
_: undefined,
|
||||||
params: any,
|
params: OpenChannelParams,
|
||||||
context: ContextType
|
context: ContextType
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'openChannel');
|
await requestLimiter(context.ip, 'openChannel');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
const {
|
||||||
|
isPrivate,
|
||||||
|
amount,
|
||||||
|
partnerPublicKey,
|
||||||
|
tokensPerVByte,
|
||||||
|
pushTokens = 0,
|
||||||
|
} = params;
|
||||||
|
|
||||||
const openParams = {
|
const openParams = {
|
||||||
is_private: params.isPrivate,
|
is_private: isPrivate,
|
||||||
local_tokens: params.amount,
|
local_tokens: amount,
|
||||||
partner_public_key: params.partnerPublicKey,
|
partner_public_key: partnerPublicKey,
|
||||||
chain_fee_tokens_per_vbyte: params.tokensPerVByte,
|
chain_fee_tokens_per_vbyte: tokensPerVByte,
|
||||||
|
give_tokens: Math.min(pushTokens, amount),
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.debug('Opening channel: %o', openParams);
|
logger.info('Opening channel with params: %o', openParams);
|
||||||
|
|
||||||
try {
|
const info = await to<OpenChannelType>(
|
||||||
const info = await lnOpenChannel({
|
lnOpenChannel({
|
||||||
lnd,
|
lnd,
|
||||||
...openParams,
|
...openParams,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info('Channel opened');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transactionId: info.transaction_id,
|
transactionId: info.transaction_id,
|
||||||
transactionOutputIndex: info.transaction_vout,
|
transactionOutputIndex: info.transaction_vout,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error opening channel: %o', error);
|
|
||||||
throw new Error(getErrorMsg(error));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { updateRoutingFees } from 'ln-service';
|
import { updateRoutingFees } from 'ln-service';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getLnd } from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
|
|
||||||
export const updateFees = async (
|
export const updateFees = async (
|
||||||
|
@ -21,7 +20,7 @@ export const updateFees = async (
|
||||||
min_htlc_mtokens,
|
min_htlc_mtokens,
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
const lnd = getLnd(params.auth, context);
|
const { lnd } = context;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!base_fee_tokens &&
|
!base_fee_tokens &&
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import { getChannelBalance as getLnChannelBalance } from 'ln-service';
|
import { getChannelBalance as getLnChannelBalance } from 'ln-service';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { logger } from 'server/helpers/logger';
|
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import {
|
|
||||||
getAuthLnd,
|
import { to } from 'server/helpers/async';
|
||||||
getErrorMsg,
|
|
||||||
getCorrectAuth,
|
|
||||||
} from 'server/helpers/helpers';
|
|
||||||
|
|
||||||
interface ChannelBalanceProps {
|
interface ChannelBalanceProps {
|
||||||
channel_balance: number;
|
channel_balance: number;
|
||||||
|
@ -20,19 +16,15 @@ export const getChannelBalance = async (
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'channelBalance');
|
await requestLimiter(context.ip, 'channelBalance');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
try {
|
const channelBalance: ChannelBalanceProps = await to(
|
||||||
const channelBalance: ChannelBalanceProps = await getLnChannelBalance({
|
getLnChannelBalance({
|
||||||
lnd,
|
lnd,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
confirmedBalance: channelBalance.channel_balance,
|
confirmedBalance: channelBalance.channel_balance,
|
||||||
pendingBalance: channelBalance.pending_balance,
|
pendingBalance: channelBalance.pending_balance,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error getting channel balance: %o', error);
|
|
||||||
throw new Error(getErrorMsg(error));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { getChannels, getWalletInfo } from 'ln-service';
|
import { getChannels, getWalletInfo } from 'ln-service';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getLnd } from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
|
|
||||||
interface GetChannelsProps {
|
interface GetChannelsProps {
|
||||||
|
@ -37,7 +36,7 @@ export const getChannelFees = async (
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'channelFees');
|
await requestLimiter(context.ip, 'channelFees');
|
||||||
|
|
||||||
const lnd = getLnd(params.auth, context);
|
const { lnd } = context;
|
||||||
|
|
||||||
const { public_key } = await to(getWalletInfo({ lnd }));
|
const { public_key } = await to(getWalletInfo({ lnd }));
|
||||||
const { channels }: GetChannelsProps = await to(getChannels({ lnd }));
|
const { channels }: GetChannelsProps = await to(getChannels({ lnd }));
|
||||||
|
|
|
@ -2,8 +2,9 @@ import { getChannels as getLnChannels, getWalletInfo } from 'ln-service';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
|
||||||
import { getChannelAge } from 'server/schema/health/helpers';
|
import { getChannelAge } from 'server/schema/health/helpers';
|
||||||
|
import { GetChannelsType } from 'server/types/ln-service.types';
|
||||||
|
|
||||||
export const getChannels = async (
|
export const getChannels = async (
|
||||||
_: undefined,
|
_: undefined,
|
||||||
|
@ -12,12 +13,11 @@ export const getChannels = async (
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'channels');
|
await requestLimiter(context.ip, 'channels');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const { public_key, current_block_height } = await to(getWalletInfo({ lnd }));
|
const { public_key, current_block_height } = await to(getWalletInfo({ lnd }));
|
||||||
|
|
||||||
const { channels } = await to(
|
const { channels } = await to<GetChannelsType>(
|
||||||
getLnChannels({
|
getLnChannels({
|
||||||
lnd,
|
lnd,
|
||||||
is_active: params.active,
|
is_active: params.active,
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { getClosedChannels as getLnClosedChannels } from 'ln-service';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
|
||||||
|
|
||||||
interface ChannelListProps {
|
interface ChannelListProps {
|
||||||
channels: ChannelProps[];
|
channels: ChannelProps[];
|
||||||
|
@ -32,8 +31,7 @@ export const getClosedChannels = async (
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'closedChannels');
|
await requestLimiter(context.ip, 'closedChannels');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const { channels }: ChannelListProps = await to(getLnClosedChannels({ lnd }));
|
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 { ContextType } from 'server/types/apiTypes';
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
|
||||||
|
|
||||||
interface PendingChannelListProps {
|
interface PendingChannelListProps {
|
||||||
pending_channels: PendingChannelProps[];
|
pending_channels: PendingChannelProps[];
|
||||||
|
@ -32,8 +31,7 @@ export const getPendingChannels = async (
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'pendingChannels');
|
await requestLimiter(context.ip, 'pendingChannels');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const { pending_channels }: PendingChannelListProps = await to(
|
const { pending_channels }: PendingChannelListProps = await to(
|
||||||
getLnPendingChannels({ lnd })
|
getLnPendingChannels({ lnd })
|
||||||
|
|
|
@ -55,36 +55,36 @@ export const channelTypes = gql`
|
||||||
}
|
}
|
||||||
|
|
||||||
type channelBalanceType {
|
type channelBalanceType {
|
||||||
confirmedBalance: Int
|
confirmedBalance: Int!
|
||||||
pendingBalance: Int
|
pendingBalance: Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
type channelType {
|
type channelType {
|
||||||
capacity: Int
|
capacity: Int!
|
||||||
commit_transaction_fee: Int
|
commit_transaction_fee: Int!
|
||||||
commit_transaction_weight: Int
|
commit_transaction_weight: Int!
|
||||||
id: String
|
id: String!
|
||||||
is_active: Boolean
|
is_active: Boolean!
|
||||||
is_closing: Boolean
|
is_closing: Boolean!
|
||||||
is_opening: Boolean
|
is_opening: Boolean!
|
||||||
is_partner_initiated: Boolean
|
is_partner_initiated: Boolean!
|
||||||
is_private: Boolean
|
is_private: Boolean!
|
||||||
is_static_remote_key: Boolean
|
is_static_remote_key: Boolean
|
||||||
local_balance: Int
|
local_balance: Int!
|
||||||
local_reserve: Int
|
local_reserve: Int!
|
||||||
partner_public_key: String
|
partner_public_key: String!
|
||||||
received: Int
|
received: Int!
|
||||||
remote_balance: Int
|
remote_balance: Int!
|
||||||
remote_reserve: Int
|
remote_reserve: Int!
|
||||||
sent: Int
|
sent: Int!
|
||||||
time_offline: Int
|
time_offline: Int
|
||||||
time_online: Int
|
time_online: Int
|
||||||
transaction_id: String
|
transaction_id: String!
|
||||||
transaction_vout: Int
|
transaction_vout: Int!
|
||||||
unsettled_balance: Int
|
unsettled_balance: Int!
|
||||||
partner_node_info: Node
|
partner_node_info: Node!
|
||||||
partner_fee_info: Channel
|
partner_fee_info: Channel
|
||||||
channel_age: Int
|
channel_age: Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
type closeChannelType {
|
type closeChannelType {
|
||||||
|
@ -93,21 +93,21 @@ export const channelTypes = gql`
|
||||||
}
|
}
|
||||||
|
|
||||||
type closedChannelType {
|
type closedChannelType {
|
||||||
capacity: Int
|
capacity: Int!
|
||||||
close_confirm_height: Int
|
close_confirm_height: Int
|
||||||
close_transaction_id: String
|
close_transaction_id: String
|
||||||
final_local_balance: Int
|
final_local_balance: Int!
|
||||||
final_time_locked_balance: Int
|
final_time_locked_balance: Int!
|
||||||
id: String
|
id: String
|
||||||
is_breach_close: Boolean
|
is_breach_close: Boolean!
|
||||||
is_cooperative_close: Boolean
|
is_cooperative_close: Boolean!
|
||||||
is_funding_cancel: Boolean
|
is_funding_cancel: Boolean!
|
||||||
is_local_force_close: Boolean
|
is_local_force_close: Boolean!
|
||||||
is_remote_force_close: Boolean
|
is_remote_force_close: Boolean!
|
||||||
partner_public_key: String
|
partner_public_key: String!
|
||||||
transaction_id: String
|
transaction_id: String!
|
||||||
transaction_vout: Int
|
transaction_vout: Int!
|
||||||
partner_node_info: Node
|
partner_node_info: Node!
|
||||||
}
|
}
|
||||||
|
|
||||||
type openChannelType {
|
type openChannelType {
|
||||||
|
@ -117,19 +117,19 @@ export const channelTypes = gql`
|
||||||
|
|
||||||
type pendingChannelType {
|
type pendingChannelType {
|
||||||
close_transaction_id: String
|
close_transaction_id: String
|
||||||
is_active: Boolean
|
is_active: Boolean!
|
||||||
is_closing: Boolean
|
is_closing: Boolean!
|
||||||
is_opening: Boolean
|
is_opening: Boolean!
|
||||||
local_balance: Int
|
local_balance: Int!
|
||||||
local_reserve: Int
|
local_reserve: Int!
|
||||||
partner_public_key: String
|
partner_public_key: String!
|
||||||
received: Int
|
received: Int!
|
||||||
remote_balance: Int
|
remote_balance: Int!
|
||||||
remote_reserve: Int
|
remote_reserve: Int!
|
||||||
sent: Int
|
sent: Int!
|
||||||
transaction_fee: Int
|
transaction_fee: Int
|
||||||
transaction_id: String
|
transaction_id: String!
|
||||||
transaction_vout: Int
|
transaction_vout: Int!
|
||||||
partner_node_info: Node
|
partner_node_info: Node!
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -10,22 +10,25 @@ import {
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { to, toWithError } from 'server/helpers/async';
|
import { to, toWithError } from 'server/helpers/async';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
|
||||||
import {
|
import {
|
||||||
createCustomRecords,
|
createCustomRecords,
|
||||||
decodeMessage,
|
decodeMessage,
|
||||||
} from 'server/helpers/customRecords';
|
} from 'server/helpers/customRecords';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
|
import {
|
||||||
|
GetInvoicesType,
|
||||||
|
GetWalletInfoType,
|
||||||
|
} from 'server/types/ln-service.types';
|
||||||
|
|
||||||
export const chatResolvers = {
|
export const chatResolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
getMessages: async (_: undefined, params: any, context: ContextType) => {
|
getMessages: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'getMessages');
|
await requestLimiter(context.ip, 'getMessages');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const invoiceList = await to(
|
const invoiceList = await to<GetInvoicesType>(
|
||||||
getInvoices({
|
getInvoices({
|
||||||
lnd,
|
lnd,
|
||||||
limit: params.initialize ? 100 : 5,
|
limit: params.initialize ? 100 : 5,
|
||||||
|
@ -72,7 +75,11 @@ export const chatResolvers = {
|
||||||
logger.debug(`Error verifying message: ${messageToVerify}`);
|
logger.debug(`Error verifying message: ${messageToVerify}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!error && verified?.signed_by === customRecords.sender) {
|
if (
|
||||||
|
!error &&
|
||||||
|
(verified as { signed_by: string })?.signed_by ===
|
||||||
|
customRecords.sender
|
||||||
|
) {
|
||||||
isVerified = true;
|
isVerified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +95,7 @@ export const chatResolvers = {
|
||||||
);
|
);
|
||||||
|
|
||||||
const filtered = await getFiltered();
|
const filtered = await getFiltered();
|
||||||
const final = filtered.filter(message => !!message);
|
const final = filtered.filter(Boolean) || [];
|
||||||
|
|
||||||
return { token: invoiceList.next, messages: final };
|
return { token: invoiceList.next, messages: final };
|
||||||
},
|
},
|
||||||
|
@ -97,8 +104,7 @@ export const chatResolvers = {
|
||||||
sendMessage: async (_: undefined, params: any, context: ContextType) => {
|
sendMessage: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'sendMessage');
|
await requestLimiter(context.ip, 'sendMessage');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
if (params.maxFee) {
|
if (params.maxFee) {
|
||||||
const tokens = Math.max(params.tokens || 100, 100);
|
const tokens = Math.max(params.tokens || 100, 100);
|
||||||
|
@ -126,7 +132,7 @@ export const chatResolvers = {
|
||||||
messageToSend = `${params.tokens},${params.message}`;
|
messageToSend = `${params.tokens},${params.message}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeInfo = await to(
|
const nodeInfo = await to<GetWalletInfoType>(
|
||||||
getWalletInfo({
|
getWalletInfo({
|
||||||
lnd,
|
lnd,
|
||||||
})
|
})
|
||||||
|
@ -167,7 +173,8 @@ export const chatResolvers = {
|
||||||
messages: customRecords,
|
messages: customRecords,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return safe_fee;
|
// +1 is needed so that a fee of 0 doesnt evaluate to false
|
||||||
|
return safe_fee + 1;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,13 +3,13 @@ import { gql } from 'apollo-server-micro';
|
||||||
export const chatTypes = gql`
|
export const chatTypes = gql`
|
||||||
type getMessagesType {
|
type getMessagesType {
|
||||||
token: String
|
token: String
|
||||||
messages: [messagesType]
|
messages: [messagesType]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type messagesType {
|
type messagesType {
|
||||||
date: String
|
date: String!
|
||||||
id: String
|
id: String!
|
||||||
verified: Boolean
|
verified: Boolean!
|
||||||
contentType: String
|
contentType: String
|
||||||
sender: String
|
sender: String
|
||||||
alias: String
|
alias: String
|
||||||
|
|
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;
|
||||||
|
};
|
|
@ -15,7 +15,7 @@ export const githubResolvers = {
|
||||||
|
|
||||||
const [response, error] = await toWithError(fetch(appUrls.github));
|
const [response, error] = await toWithError(fetch(appUrls.github));
|
||||||
|
|
||||||
if (error) {
|
if (error || !response) {
|
||||||
logger.debug('Unable to get latest github version');
|
logger.debug('Unable to get latest github version');
|
||||||
throw new Error('NoGithubVersion');
|
throw new Error('NoGithubVersion');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
import { groupBy } from 'underscore';
|
import { groupBy } from 'underscore';
|
||||||
|
import { ForwardType } from 'server/types/ln-service.types';
|
||||||
|
|
||||||
export const getChannelVolume = forwards => {
|
type GroupedObject = {
|
||||||
|
[key: string]: ForwardType[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type TotalGroupedObject = {
|
||||||
|
[key: string]: { tokens: number }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getChannelVolume = (forwards: ForwardType[]) => {
|
||||||
const orderedIncoming = groupBy(forwards, f => f.incoming_channel);
|
const orderedIncoming = groupBy(forwards, f => f.incoming_channel);
|
||||||
const orderedOutgoing = groupBy(forwards, f => f.outgoing_channel);
|
const orderedOutgoing = groupBy(forwards, f => f.outgoing_channel);
|
||||||
|
|
||||||
|
@ -14,7 +23,7 @@ export const getChannelVolume = forwards => {
|
||||||
return reduceTokens(together);
|
return reduceTokens(together);
|
||||||
};
|
};
|
||||||
|
|
||||||
const reduceTokens = array => {
|
const reduceTokens = (array: GroupedObject | TotalGroupedObject) => {
|
||||||
const reducedArray = [];
|
const reducedArray = [];
|
||||||
for (const key in array) {
|
for (const key in array) {
|
||||||
if (Object.prototype.hasOwnProperty.call(array, key)) {
|
if (Object.prototype.hasOwnProperty.call(array, key)) {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { getChannels, getChannel, getWalletInfo } from 'ln-service';
|
import { getChannels, getChannel, getWalletInfo } from 'ln-service';
|
||||||
import { getCorrectAuth, getAuthLnd } from 'server/helpers/helpers';
|
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { to, toWithError } from 'server/helpers/async';
|
import { to, toWithError } from 'server/helpers/async';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
|
import { GetChannelsType, GetChannelType } from 'server/types/ln-service.types';
|
||||||
import { getFeeScore, getAverage, getMyFeeScore } from '../helpers';
|
import { getFeeScore, getAverage, getMyFeeScore } from '../helpers';
|
||||||
|
|
||||||
type ChannelFeesType = {
|
type ChannelFeesType = {
|
||||||
|
@ -18,32 +18,33 @@ type ChannelFeesType = {
|
||||||
export default async (_: undefined, params: any, context: ContextType) => {
|
export default async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'getFeeHealth');
|
await requestLimiter(context.ip, 'getFeeHealth');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const { public_key } = await to(getWalletInfo({ lnd }));
|
const { public_key } = await to(getWalletInfo({ lnd }));
|
||||||
const { channels } = await to(getChannels({ lnd }));
|
const { channels } = await to<GetChannelsType>(getChannels({ lnd }));
|
||||||
|
|
||||||
const getChannelList = () =>
|
const getChannelList = () =>
|
||||||
Promise.all(
|
Promise.all(
|
||||||
channels
|
channels
|
||||||
.map(async channel => {
|
.map(async channel => {
|
||||||
const { id, partner_public_key: publicKey } = channel;
|
const { id, partner_public_key: publicKey } = channel;
|
||||||
const [{ policies }, channelError] = await toWithError(
|
const [channelInfo, channelError] = await toWithError(
|
||||||
getChannel({
|
getChannel({
|
||||||
lnd,
|
lnd,
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (channelError) {
|
if (channelError || !channelInfo) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Error getting channel with id ${id}: %o`,
|
`Error getting channel with id ${id}: %o`,
|
||||||
channelError
|
channelError
|
||||||
);
|
);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const policies = (channelInfo as GetChannelType).policies;
|
||||||
|
|
||||||
let partnerBaseFee = 0;
|
let partnerBaseFee = 0;
|
||||||
let partnerFeeRate = 0;
|
let partnerFeeRate = 0;
|
||||||
let myBaseFee = 0;
|
let myBaseFee = 0;
|
||||||
|
@ -77,7 +78,7 @@ export default async (_: undefined, params: any, context: ContextType) => {
|
||||||
|
|
||||||
const list = await getChannelList();
|
const list = await getChannelList();
|
||||||
|
|
||||||
const health = list.map((channel: ChannelFeesType) => {
|
const health = (list as ChannelFeesType[]).map((channel: ChannelFeesType) => {
|
||||||
const partnerRateScore = getFeeScore(2000, channel.partnerFeeRate);
|
const partnerRateScore = getFeeScore(2000, channel.partnerFeeRate);
|
||||||
const partnerBaseScore = getFeeScore(100000, channel.partnerBaseFee);
|
const partnerBaseScore = getFeeScore(100000, channel.partnerBaseFee);
|
||||||
const myRateScore = getMyFeeScore(2000, channel.myFeeRate, 200);
|
const myRateScore = getMyFeeScore(2000, channel.myFeeRate, 200);
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
import { getChannels } from 'ln-service';
|
import { getChannels } from 'ln-service';
|
||||||
import { getCorrectAuth, getAuthLnd } from 'server/helpers/helpers';
|
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
|
import { GetChannelsType } from 'server/types/ln-service.types';
|
||||||
import { getAverage } from '../helpers';
|
import { getAverage } from '../helpers';
|
||||||
|
|
||||||
const halfMonthInMilliSeconds = 1296000000;
|
const halfMonthInMilliSeconds = 1296000000;
|
||||||
|
|
||||||
export default async (_: undefined, params: any, context: ContextType) => {
|
export default async (_: undefined, __: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'getTimeHealth');
|
await requestLimiter(context.ip, 'getTimeHealth');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const { channels } = await to(getChannels({ lnd }));
|
const { channels } = await to<GetChannelsType>(getChannels({ lnd }));
|
||||||
|
|
||||||
const health = channels.map(channel => {
|
const health = channels.map(channel => {
|
||||||
const {
|
const {
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import { getForwards, getChannels, getWalletInfo } from 'ln-service';
|
import { getForwards, getChannels, getWalletInfo } from 'ln-service';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getCorrectAuth, getAuthLnd } from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
import { subMonths } from 'date-fns';
|
import { subMonths } from 'date-fns';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
|
import {
|
||||||
|
GetChannelsType,
|
||||||
|
GetForwardsType,
|
||||||
|
} from 'server/types/ln-service.types';
|
||||||
import { getChannelVolume, getChannelIdInfo, getAverage } from '../helpers';
|
import { getChannelVolume, getChannelIdInfo, getAverage } from '../helpers';
|
||||||
|
|
||||||
const monthInBlocks = 4380;
|
const monthInBlocks = 4380;
|
||||||
|
@ -11,22 +14,26 @@ const monthInBlocks = 4380;
|
||||||
export default async (_: undefined, params: any, context: ContextType) => {
|
export default async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'getVolumeHealth');
|
await requestLimiter(context.ip, 'getVolumeHealth');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const before = new Date().toISOString();
|
const before = new Date().toISOString();
|
||||||
const after = subMonths(new Date(), 1).toISOString();
|
const after = subMonths(new Date(), 1).toISOString();
|
||||||
|
|
||||||
const { current_block_height } = await to(getWalletInfo({ lnd }));
|
const { current_block_height } = await to(getWalletInfo({ lnd }));
|
||||||
const { channels } = await to(getChannels({ lnd }));
|
const { channels } = await to<GetChannelsType>(getChannels({ lnd }));
|
||||||
const { forwards } = await to(getForwards({ lnd, after, before }));
|
const { forwards } = await to<GetForwardsType>(
|
||||||
|
getForwards({ lnd, after, before })
|
||||||
|
);
|
||||||
|
|
||||||
const channelVolume = getChannelVolume(forwards);
|
const channelVolume: { channel: string; tokens: number }[] = getChannelVolume(
|
||||||
|
forwards
|
||||||
|
);
|
||||||
|
|
||||||
const channelDetails = channels
|
const channelDetails = channels
|
||||||
.map(channel => {
|
.map(channel => {
|
||||||
const { tokens } =
|
const { tokens } = channelVolume.find(c => c.channel === channel.id) || {
|
||||||
channelVolume.find(c => c.channel === channel.id) || {};
|
tokens: 0,
|
||||||
|
};
|
||||||
const info = getChannelIdInfo(channel.id);
|
const info = getChannelIdInfo(channel.id);
|
||||||
|
|
||||||
if (!info) return;
|
if (!info) return;
|
||||||
|
@ -45,9 +52,11 @@ export default async (_: undefined, params: any, context: ContextType) => {
|
||||||
})
|
})
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
const average = getAverage(channelDetails.map(c => c.volumeNormalized));
|
const average = getAverage(channelDetails.map(c => c?.volumeNormalized || 0));
|
||||||
|
|
||||||
const health = channelDetails.map(channel => {
|
const health = channelDetails
|
||||||
|
.map(channel => {
|
||||||
|
if (!channel) return null;
|
||||||
const diff = (channel.volumeNormalized - average) / average || -1;
|
const diff = (channel.volumeNormalized - average) / average || -1;
|
||||||
const score = Math.round((diff + 1) * 100);
|
const score = Math.round((diff + 1) * 100);
|
||||||
|
|
||||||
|
@ -58,10 +67,11 @@ export default async (_: undefined, params: any, context: ContextType) => {
|
||||||
averageVolumeNormalized: average,
|
averageVolumeNormalized: average,
|
||||||
partner: { publicKey: channel.publicKey, lnd },
|
partner: { publicKey: channel.publicKey, lnd },
|
||||||
};
|
};
|
||||||
});
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
const globalAverage = Math.round(
|
const globalAverage = Math.round(
|
||||||
getAverage(health.map(c => Math.min(c.score, 100)))
|
getAverage(health.map(c => Math.min(c?.score || 0, 100)))
|
||||||
);
|
);
|
||||||
|
|
||||||
return { score: globalAverage, channels: health };
|
return { score: globalAverage, channels: health };
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
import getConfig from 'next/config';
|
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
|
||||||
import { logger } from 'server/helpers/logger';
|
|
||||||
import { appUrls } from 'server/utils/appUrls';
|
|
||||||
import { getHodlParams } from 'server/helpers/hodlHelpers';
|
|
||||||
|
|
||||||
const { serverRuntimeConfig } = getConfig() || {};
|
|
||||||
const { hodlKey } = serverRuntimeConfig || {};
|
|
||||||
|
|
||||||
const defaultQuery = {
|
|
||||||
filters: {},
|
|
||||||
sort: {
|
|
||||||
by: '',
|
|
||||||
direction: '',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const hodlResolvers = {
|
|
||||||
Query: {
|
|
||||||
getCountries: async (_: undefined, params: any, context: ContextType) => {
|
|
||||||
await requestLimiter(context.ip, 'getCountries');
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
Authorization: `Bearer ${hodlKey}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${appUrls.hodlhodl}/v1/countries`, {
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
const json = await response.json();
|
|
||||||
|
|
||||||
if (json) {
|
|
||||||
const { countries } = json;
|
|
||||||
return countries;
|
|
||||||
}
|
|
||||||
throw new Error('Problem getting HodlHodl countries.');
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error getting HodlHodl countries: %o', error);
|
|
||||||
throw new Error('Problem getting HodlHodl countries.');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getCurrencies: async (_: undefined, params: any, context: ContextType) => {
|
|
||||||
await requestLimiter(context.ip, 'getCurrencies');
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
Authorization: `Bearer ${hodlKey}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${appUrls.hodlhodl}/v1/currencies`, {
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
const json = await response.json();
|
|
||||||
|
|
||||||
if (json) {
|
|
||||||
const { currencies } = json;
|
|
||||||
return currencies;
|
|
||||||
}
|
|
||||||
throw new Error('Problem getting HodlHodl currencies.');
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error getting HodlHodl currencies: %o', error);
|
|
||||||
throw new Error('Problem getting HodlHodl currencies.');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getOffers: async (_: undefined, params: any, context: ContextType) => {
|
|
||||||
await requestLimiter(context.ip, 'getOffers');
|
|
||||||
|
|
||||||
let queryParams = defaultQuery;
|
|
||||||
|
|
||||||
if (params.filter) {
|
|
||||||
try {
|
|
||||||
queryParams = JSON.parse(params.filter);
|
|
||||||
} catch (error) {
|
|
||||||
queryParams = defaultQuery;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const fullParams = {
|
|
||||||
...queryParams,
|
|
||||||
};
|
|
||||||
|
|
||||||
const paramString = getHodlParams(fullParams);
|
|
||||||
|
|
||||||
const response = await fetch(
|
|
||||||
`${appUrls.hodlhodl}/v1/offers${paramString}`
|
|
||||||
);
|
|
||||||
const json = await response.json();
|
|
||||||
|
|
||||||
if (json) {
|
|
||||||
const { offers } = json;
|
|
||||||
return offers;
|
|
||||||
}
|
|
||||||
throw new Error('Problem getting HodlHodl offers.');
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error getting HodlHodl offers: %o', error);
|
|
||||||
throw new Error('Problem getting HodlHodl offers.');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,69 +0,0 @@
|
||||||
import { gql } from 'apollo-server-micro';
|
|
||||||
|
|
||||||
export const hodlTypes = gql`
|
|
||||||
type hodlCountryType {
|
|
||||||
code: String
|
|
||||||
name: String
|
|
||||||
native_name: String
|
|
||||||
currency_code: String
|
|
||||||
currency_name: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type hodlCurrencyType {
|
|
||||||
code: String
|
|
||||||
name: String
|
|
||||||
type: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type hodlOfferFeeType {
|
|
||||||
author_fee_rate: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type hodlOfferPaymentType {
|
|
||||||
id: String
|
|
||||||
version: String
|
|
||||||
payment_method_id: String
|
|
||||||
payment_method_type: String
|
|
||||||
payment_method_name: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type hodlOfferTraderType {
|
|
||||||
login: String
|
|
||||||
online_status: String
|
|
||||||
rating: String
|
|
||||||
trades_count: Int
|
|
||||||
url: String
|
|
||||||
verified: Boolean
|
|
||||||
verified_by: String
|
|
||||||
strong_hodler: Boolean
|
|
||||||
country: String
|
|
||||||
country_code: String
|
|
||||||
average_payment_time_minutes: Int
|
|
||||||
average_release_time_minutes: Int
|
|
||||||
days_since_last_trade: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type hodlOfferType {
|
|
||||||
id: String
|
|
||||||
version: String
|
|
||||||
asset_code: String
|
|
||||||
searchable: Boolean
|
|
||||||
country: String
|
|
||||||
country_code: String
|
|
||||||
working_now: Boolean
|
|
||||||
side: String
|
|
||||||
title: String
|
|
||||||
description: String
|
|
||||||
currency_code: String
|
|
||||||
price: String
|
|
||||||
min_amount: String
|
|
||||||
max_amount: String
|
|
||||||
first_trade_limit: String
|
|
||||||
fee: hodlOfferFeeType
|
|
||||||
balance: String
|
|
||||||
payment_window_minutes: Int
|
|
||||||
confirmations: Int
|
|
||||||
payment_method_instructions: [hodlOfferPaymentType]
|
|
||||||
trader: hodlOfferTraderType
|
|
||||||
}
|
|
||||||
`;
|
|
|
@ -1,13 +1,11 @@
|
||||||
import merge from 'lodash.merge';
|
import merge from 'lodash.merge';
|
||||||
import { makeExecutableSchema } from 'apollo-server-micro';
|
import { makeExecutableSchema } from 'graphql-tools';
|
||||||
import { nodeTypes } from './node/types';
|
import { nodeTypes } from './node/types';
|
||||||
import { nodeResolvers } from './node/resolvers';
|
import { nodeResolvers } from './node/resolvers';
|
||||||
import { authResolvers } from './auth/resolvers';
|
import { authResolvers } from './auth/resolvers';
|
||||||
import { generalTypes, queryTypes, mutationTypes } from './types';
|
import { generalTypes, queryTypes, mutationTypes } from './types';
|
||||||
import { accountResolvers } from './account/resolvers';
|
import { accountResolvers } from './account/resolvers';
|
||||||
import { accountTypes } from './account/types';
|
import { accountTypes } from './account/types';
|
||||||
import { hodlTypes } from './hodlhodl/types';
|
|
||||||
import { hodlResolvers } from './hodlhodl/resolvers';
|
|
||||||
import { lnpayResolvers } from './lnpay/resolvers';
|
import { lnpayResolvers } from './lnpay/resolvers';
|
||||||
import { lnpayTypes } from './lnpay/types';
|
import { lnpayTypes } from './lnpay/types';
|
||||||
import { bitcoinResolvers } from './bitcoin/resolvers';
|
import { bitcoinResolvers } from './bitcoin/resolvers';
|
||||||
|
@ -49,7 +47,6 @@ const typeDefs = [
|
||||||
mutationTypes,
|
mutationTypes,
|
||||||
nodeTypes,
|
nodeTypes,
|
||||||
accountTypes,
|
accountTypes,
|
||||||
hodlTypes,
|
|
||||||
lnpayTypes,
|
lnpayTypes,
|
||||||
bitcoinTypes,
|
bitcoinTypes,
|
||||||
peerTypes,
|
peerTypes,
|
||||||
|
@ -72,7 +69,6 @@ const resolvers = merge(
|
||||||
nodeResolvers,
|
nodeResolvers,
|
||||||
authResolvers,
|
authResolvers,
|
||||||
accountResolvers,
|
accountResolvers,
|
||||||
hodlResolvers,
|
|
||||||
lnpayResolvers,
|
lnpayResolvers,
|
||||||
bitcoinResolvers,
|
bitcoinResolvers,
|
||||||
peerResolvers,
|
peerResolvers,
|
||||||
|
@ -93,4 +89,4 @@ const resolvers = merge(
|
||||||
tbaseResolvers
|
tbaseResolvers
|
||||||
);
|
);
|
||||||
|
|
||||||
export default makeExecutableSchema({ typeDefs, resolvers });
|
export const schema = makeExecutableSchema({ typeDefs, resolvers });
|
||||||
|
|
|
@ -9,13 +9,9 @@ import {
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import {
|
import { getErrorMsg } from 'server/helpers/helpers';
|
||||||
getAuthLnd,
|
|
||||||
getErrorMsg,
|
|
||||||
getCorrectAuth,
|
|
||||||
getLnd,
|
|
||||||
} from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
|
import { DecodedType } from 'server/types/ln-service.types';
|
||||||
|
|
||||||
const KEYSEND_TYPE = '5482373484';
|
const KEYSEND_TYPE = '5482373484';
|
||||||
|
|
||||||
|
@ -24,9 +20,9 @@ export const invoiceResolvers = {
|
||||||
decodeRequest: async (_: undefined, params: any, context: ContextType) => {
|
decodeRequest: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'decode');
|
await requestLimiter(context.ip, 'decode');
|
||||||
|
|
||||||
const lnd = getLnd(params.auth, context);
|
const { lnd } = context;
|
||||||
|
|
||||||
const decoded = await to(
|
const decoded = await to<DecodedType>(
|
||||||
decodePaymentRequest({
|
decodePaymentRequest({
|
||||||
lnd,
|
lnd,
|
||||||
request: params.request,
|
request: params.request,
|
||||||
|
@ -48,8 +44,7 @@ export const invoiceResolvers = {
|
||||||
createInvoice: async (_: undefined, params: any, context: ContextType) => {
|
createInvoice: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'createInvoice');
|
await requestLimiter(context.ip, 'createInvoice');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
return await to(
|
return await to(
|
||||||
createInvoiceRequest({
|
createInvoiceRequest({
|
||||||
|
@ -61,8 +56,8 @@ export const invoiceResolvers = {
|
||||||
keysend: async (_: undefined, params: any, context: ContextType) => {
|
keysend: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'keysend');
|
await requestLimiter(context.ip, 'keysend');
|
||||||
|
|
||||||
const { auth, destination, tokens } = params;
|
const { destination, tokens } = params;
|
||||||
const lnd = getLnd(auth, context);
|
const { lnd } = context;
|
||||||
|
|
||||||
const preimage = randomBytes(32);
|
const preimage = randomBytes(32);
|
||||||
const secret = preimage.toString('hex');
|
const secret = preimage.toString('hex');
|
||||||
|
@ -90,8 +85,7 @@ export const invoiceResolvers = {
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'circularRebalance');
|
await requestLimiter(context.ip, 'circularRebalance');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
let route;
|
let route;
|
||||||
try {
|
try {
|
||||||
|
@ -120,8 +114,8 @@ export const invoiceResolvers = {
|
||||||
payViaRoute: async (_: undefined, params: any, context: ContextType) => {
|
payViaRoute: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'payViaRoute');
|
await requestLimiter(context.ip, 'payViaRoute');
|
||||||
|
|
||||||
const { auth, route: routeJSON, id } = params;
|
const { route: routeJSON, id } = params;
|
||||||
const lnd = getLnd(auth, context);
|
const { lnd } = context;
|
||||||
|
|
||||||
let route;
|
let route;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -4,16 +4,16 @@ export const invoiceTypes = gql`
|
||||||
type decodeType {
|
type decodeType {
|
||||||
chain_address: String
|
chain_address: String
|
||||||
cltv_delta: Int
|
cltv_delta: Int
|
||||||
description: String
|
description: String!
|
||||||
description_hash: String
|
description_hash: String
|
||||||
destination: String
|
destination: String!
|
||||||
expires_at: String
|
expires_at: String!
|
||||||
id: String
|
id: String!
|
||||||
mtokens: String
|
mtokens: String!
|
||||||
payment: String
|
payment: String
|
||||||
routes: [[RouteType]]
|
routes: [[RouteType]]!
|
||||||
safe_tokens: Int
|
safe_tokens: Int!
|
||||||
tokens: Int
|
tokens: Int!
|
||||||
destination_node: Node!
|
destination_node: Node!
|
||||||
probe_route: ProbeRoute
|
probe_route: ProbeRoute
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ export const invoiceTypes = gql`
|
||||||
channel: String
|
channel: String
|
||||||
cltv_delta: Int
|
cltv_delta: Int
|
||||||
fee_rate: Int
|
fee_rate: Int
|
||||||
public_key: String
|
public_key: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type payType {
|
type payType {
|
||||||
|
|
|
@ -6,14 +6,18 @@ import { appUrls } from 'server/utils/appUrls';
|
||||||
|
|
||||||
export const lnpayResolvers = {
|
export const lnpayResolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
getLnPay: async (_: undefined, params: any, context: ContextType) => {
|
getLnPay: async (
|
||||||
|
_: undefined,
|
||||||
|
params: { amount: number },
|
||||||
|
context: ContextType
|
||||||
|
) => {
|
||||||
await requestLimiter(context.ip, 'getLnPay');
|
await requestLimiter(context.ip, 'getLnPay');
|
||||||
|
|
||||||
const [response, error] = await toWithError(
|
const [response, error] = await toWithError(
|
||||||
fetch(`${appUrls.lnpay}?amount=${params.amount}`)
|
fetch(`${appUrls.lnpay}?amount=${params.amount}`)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error || !response) {
|
||||||
logger.debug('Unable to get lnpay invoice: %o', error);
|
logger.debug('Unable to get lnpay invoice: %o', error);
|
||||||
throw new Error('NoLnPayInvoice');
|
throw new Error('NoLnPayInvoice');
|
||||||
}
|
}
|
||||||
|
@ -26,7 +30,7 @@ export const lnpayResolvers = {
|
||||||
|
|
||||||
const [response, error] = await toWithError(fetch(appUrls.lnpay));
|
const [response, error] = await toWithError(fetch(appUrls.lnpay));
|
||||||
|
|
||||||
if (error) {
|
if (error || !response) {
|
||||||
logger.debug('Unable to connect to ThunderHub LNPAY');
|
logger.debug('Unable to connect to ThunderHub LNPAY');
|
||||||
throw new Error('NoLnPay');
|
throw new Error('NoLnPay');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getLnd } from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
import { grantAccess } from 'ln-service';
|
import { grantAccess } from 'ln-service';
|
||||||
import { AuthType } from 'src/context/AccountContext';
|
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
|
|
||||||
export type PermissionsType = {
|
export type PermissionsType = {
|
||||||
|
@ -27,7 +25,6 @@ export type PermissionsType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type ParamsType = {
|
type ParamsType = {
|
||||||
auth: AuthType;
|
|
||||||
permissions: PermissionsType;
|
permissions: PermissionsType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,9 +37,8 @@ export const macaroonResolvers = {
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'createMacaroon');
|
await requestLimiter(context.ip, 'createMacaroon');
|
||||||
|
|
||||||
const { auth, permissions } = params;
|
const { permissions } = params;
|
||||||
|
const { lnd } = context;
|
||||||
const lnd = getLnd(auth, context);
|
|
||||||
|
|
||||||
const { macaroon, permissions: permissionList } = await to(
|
const { macaroon, permissions: permissionList } = await to(
|
||||||
grantAccess({ lnd, ...permissions })
|
grantAccess({ lnd, ...permissions })
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { getNetworkInfo } from 'ln-service';
|
import { getNetworkInfo } from 'ln-service';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getLnd } from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
|
|
||||||
interface NetworkInfoProps {
|
interface NetworkInfoProps {
|
||||||
|
@ -20,7 +19,7 @@ export const networkResolvers = {
|
||||||
getNetworkInfo: async (_: undefined, params: any, context: ContextType) => {
|
getNetworkInfo: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'networkInfo');
|
await requestLimiter(context.ip, 'networkInfo');
|
||||||
|
|
||||||
const lnd = getLnd(params.auth, context);
|
const { lnd } = context;
|
||||||
|
|
||||||
const info: NetworkInfoProps = await to(getNetworkInfo({ lnd }));
|
const info: NetworkInfoProps = await to(getNetworkInfo({ lnd }));
|
||||||
|
|
||||||
|
|
|
@ -1,39 +1,45 @@
|
||||||
import {
|
import { getNode, getWalletInfo, getClosedChannels } from 'ln-service';
|
||||||
getNode as getLnNode,
|
|
||||||
getWalletInfo,
|
|
||||||
getClosedChannels,
|
|
||||||
} from 'ln-service';
|
|
||||||
import { to, toWithError } from 'server/helpers/async';
|
import { to, toWithError } from 'server/helpers/async';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getAuthLnd, getCorrectAuth, getLnd } from '../../helpers/helpers';
|
import {
|
||||||
|
ClosedChannelsType,
|
||||||
|
LndObject,
|
||||||
|
GetWalletInfoType,
|
||||||
|
GetNodeType,
|
||||||
|
} from 'server/types/ln-service.types';
|
||||||
import { ContextType } from '../../types/apiTypes';
|
import { ContextType } from '../../types/apiTypes';
|
||||||
import { logger } from '../../helpers/logger';
|
import { logger } from '../../helpers/logger';
|
||||||
|
|
||||||
const errorNode = { alias: 'Node not found' };
|
const errorNode = { alias: 'Node not found' };
|
||||||
|
|
||||||
|
type NodeParent = {
|
||||||
|
lnd: LndObject;
|
||||||
|
publicKey: string;
|
||||||
|
withChannels?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export const nodeResolvers = {
|
export const nodeResolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
getNode: async (_: undefined, params: any, context: ContextType) => {
|
getNode: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'closedChannels');
|
await requestLimiter(context.ip, 'closedChannels');
|
||||||
|
|
||||||
const { auth, withoutChannels = true, publicKey } = params;
|
const { withoutChannels = true, publicKey } = params;
|
||||||
const lnd = getLnd(auth, context);
|
const { lnd } = context;
|
||||||
|
|
||||||
return { lnd, publicKey, withChannels: !withoutChannels };
|
return { lnd, publicKey, withChannels: !withoutChannels };
|
||||||
},
|
},
|
||||||
getNodeInfo: async (_: undefined, params: any, context: ContextType) => {
|
getNodeInfo: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'nodeInfo');
|
await requestLimiter(context.ip, 'nodeInfo');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const info = await to(
|
const info = await to<GetWalletInfoType>(
|
||||||
getWalletInfo({
|
getWalletInfo({
|
||||||
lnd,
|
lnd,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const closedChannels = await to(
|
const closedChannels: ClosedChannelsType = await to(
|
||||||
getClosedChannels({
|
getClosedChannels({
|
||||||
lnd,
|
lnd,
|
||||||
})
|
})
|
||||||
|
@ -46,7 +52,7 @@ export const nodeResolvers = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Node: {
|
Node: {
|
||||||
node: async parent => {
|
node: async (parent: NodeParent) => {
|
||||||
const { lnd, withChannels, publicKey } = parent;
|
const { lnd, withChannels, publicKey } = parent;
|
||||||
|
|
||||||
if (!lnd) {
|
if (!lnd) {
|
||||||
|
@ -60,19 +66,19 @@ export const nodeResolvers = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const [info, error] = await toWithError(
|
const [info, error] = await toWithError(
|
||||||
getLnNode({
|
getNode({
|
||||||
lnd,
|
lnd,
|
||||||
is_omitting_channels: !withChannels,
|
is_omitting_channels: !withChannels,
|
||||||
public_key: publicKey,
|
public_key: publicKey,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error || !info) {
|
||||||
logger.debug(`Error getting node with key: ${publicKey}`);
|
logger.debug(`Error getting node with key: ${publicKey}`);
|
||||||
return errorNode;
|
return errorNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...info, public_key: publicKey };
|
return { ...(info as GetNodeType), public_key: publicKey };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,24 +11,24 @@ export const nodeTypes = gql`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Node {
|
type Node {
|
||||||
node: nodeType
|
node: nodeType!
|
||||||
}
|
}
|
||||||
|
|
||||||
type nodeInfoType {
|
type nodeInfoType {
|
||||||
chains: [String]
|
chains: [String!]!
|
||||||
color: String
|
color: String!
|
||||||
active_channels_count: Int
|
active_channels_count: Int!
|
||||||
closed_channels_count: Int
|
closed_channels_count: Int!
|
||||||
alias: String
|
alias: String!
|
||||||
current_block_hash: String
|
current_block_hash: String!
|
||||||
current_block_height: Int
|
current_block_height: Int!
|
||||||
is_synced_to_chain: Boolean
|
is_synced_to_chain: Boolean!
|
||||||
is_synced_to_graph: Boolean
|
is_synced_to_graph: Boolean!
|
||||||
latest_block_at: String
|
latest_block_at: String!
|
||||||
peers_count: Int
|
peers_count: Int!
|
||||||
pending_channels_count: Int
|
pending_channels_count: Int!
|
||||||
public_key: String
|
public_key: String!
|
||||||
uris: [String]
|
uris: [String!]!
|
||||||
version: String
|
version: String!
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -2,11 +2,7 @@ import { getPeers, removePeer, addPeer } from 'ln-service';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import {
|
import { getErrorMsg } from 'server/helpers/helpers';
|
||||||
getAuthLnd,
|
|
||||||
getErrorMsg,
|
|
||||||
getCorrectAuth,
|
|
||||||
} from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
|
|
||||||
interface PeerProps {
|
interface PeerProps {
|
||||||
|
@ -26,8 +22,7 @@ export const peerResolvers = {
|
||||||
getPeers: async (_: undefined, params: any, context: ContextType) => {
|
getPeers: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'getPeers');
|
await requestLimiter(context.ip, 'getPeers');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const { peers }: { peers: PeerProps[] } = await to(
|
const { peers }: { peers: PeerProps[] } = await to(
|
||||||
getPeers({
|
getPeers({
|
||||||
|
@ -67,8 +62,7 @@ export const peerResolvers = {
|
||||||
peerSocket = parts[1];
|
peerSocket = parts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const success: boolean = await addPeer({
|
const success: boolean = await addPeer({
|
||||||
|
@ -86,8 +80,7 @@ export const peerResolvers = {
|
||||||
removePeer: async (_: undefined, params: any, context: ContextType) => {
|
removePeer: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'removePeer');
|
await requestLimiter(context.ip, 'removePeer');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const success: boolean = await removePeer({
|
const success: boolean = await removePeer({
|
||||||
|
|
|
@ -2,15 +2,15 @@ import { gql } from 'apollo-server-micro';
|
||||||
|
|
||||||
export const peerTypes = gql`
|
export const peerTypes = gql`
|
||||||
type peerType {
|
type peerType {
|
||||||
bytes_received: Int
|
bytes_received: Int!
|
||||||
bytes_sent: Int
|
bytes_sent: Int!
|
||||||
is_inbound: Boolean
|
is_inbound: Boolean!
|
||||||
is_sync_peer: Boolean
|
is_sync_peer: Boolean
|
||||||
ping_time: Int
|
ping_time: Int!
|
||||||
public_key: String
|
public_key: String!
|
||||||
socket: String
|
socket: String!
|
||||||
tokens_received: Int
|
tokens_received: Int!
|
||||||
tokens_sent: Int
|
tokens_sent: Int!
|
||||||
partner_node_info: Node
|
partner_node_info: Node!
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -6,15 +6,21 @@ import {
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getLnd } from 'server/helpers/helpers';
|
|
||||||
import { toWithError, to } from 'server/helpers/async';
|
import { toWithError, to } from 'server/helpers/async';
|
||||||
|
import { LndObject, ProbeForRouteType } from 'server/types/ln-service.types';
|
||||||
|
|
||||||
|
type RouteParent = {
|
||||||
|
lnd: LndObject;
|
||||||
|
destination: string;
|
||||||
|
tokens: number;
|
||||||
|
};
|
||||||
|
|
||||||
export const routeResolvers = {
|
export const routeResolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
getRoutes: async (_: undefined, params: any, context: ContextType) => {
|
getRoutes: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'getRoutes');
|
await requestLimiter(context.ip, 'getRoutes');
|
||||||
|
|
||||||
const lnd = getLnd(params.auth, context);
|
const { lnd } = context;
|
||||||
|
|
||||||
const { public_key } = await getWalletInfo({ lnd });
|
const { public_key } = await getWalletInfo({ lnd });
|
||||||
|
|
||||||
|
@ -37,7 +43,7 @@ export const routeResolvers = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProbeRoute: {
|
ProbeRoute: {
|
||||||
route: async parent => {
|
route: async (parent: RouteParent) => {
|
||||||
const { lnd, destination, tokens } = parent;
|
const { lnd, destination, tokens } = parent;
|
||||||
|
|
||||||
if (!lnd) {
|
if (!lnd) {
|
||||||
|
@ -61,19 +67,20 @@ export const routeResolvers = {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!info.route) {
|
if (!(info as ProbeForRouteType).route) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`No route found to destination ${destination} for ${tokens} tokens`
|
`No route found to destination ${destination} for ${tokens} tokens`
|
||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hopsWithNodes = info.route.hops.map(h => ({
|
const hopsWithNodes =
|
||||||
|
(info as ProbeForRouteType).route?.hops.map(h => ({
|
||||||
...h,
|
...h,
|
||||||
node: { lnd, publicKey: h.public_key },
|
node: { lnd, publicKey: h.public_key },
|
||||||
}));
|
})) || [];
|
||||||
|
|
||||||
return { ...info.route, hops: hopsWithNodes };
|
return { ...(info as ProbeForRouteType).route, hops: hopsWithNodes };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,12 +19,17 @@ export const tbaseResolvers = {
|
||||||
body: JSON.stringify({ query }),
|
body: JSON.stringify({ query }),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
if (fetchError) return [];
|
if (fetchError || !response) return [];
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
const { errors, data } = result || {};
|
const { errors, data } = result || {};
|
||||||
if (errors) return [];
|
if (errors) return [];
|
||||||
|
|
||||||
return data?.getNodes?.filter(n => n.public_key && n.socket) || [];
|
return (
|
||||||
|
data?.getNodes?.filter(
|
||||||
|
(n: { public_key: string; socket: string }) =>
|
||||||
|
n.public_key && n.socket
|
||||||
|
) || []
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ export const tbaseTypes = gql`
|
||||||
type baseNodesType {
|
type baseNodesType {
|
||||||
_id: String
|
_id: String
|
||||||
name: String
|
name: String
|
||||||
public_key: String
|
public_key: String!
|
||||||
socket: String
|
socket: String!
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -9,23 +9,18 @@ import {
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { logger } from 'server/helpers/logger';
|
import { logger } from 'server/helpers/logger';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import {
|
import { getErrorMsg } from 'server/helpers/helpers';
|
||||||
getAuthLnd,
|
|
||||||
getErrorMsg,
|
|
||||||
getCorrectAuth,
|
|
||||||
getLnd,
|
|
||||||
} from 'server/helpers/helpers';
|
|
||||||
import { toWithError } from 'server/helpers/async';
|
import { toWithError } from 'server/helpers/async';
|
||||||
|
import { ChannelType } from 'server/types/ln-service.types';
|
||||||
|
|
||||||
export const toolsResolvers = {
|
export const toolsResolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
verifyBackups: async (_: undefined, params: any, context: ContextType) => {
|
verifyBackups: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'verifyBackups');
|
await requestLimiter(context.ip, 'verifyBackups');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
let backupObj = { backup: '', channels: [] };
|
let backupObj = { backup: '', channels: [] as ChannelType[] };
|
||||||
try {
|
try {
|
||||||
backupObj = JSON.parse(params.backup);
|
backupObj = JSON.parse(params.backup);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -50,8 +45,7 @@ export const toolsResolvers = {
|
||||||
recoverFunds: async (_: undefined, params: any, context: ContextType) => {
|
recoverFunds: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'recoverFunds');
|
await requestLimiter(context.ip, 'recoverFunds');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
let backupObj = { backup: '' };
|
let backupObj = { backup: '' };
|
||||||
try {
|
try {
|
||||||
|
@ -77,8 +71,7 @@ export const toolsResolvers = {
|
||||||
getBackups: async (_: undefined, params: any, context: ContextType) => {
|
getBackups: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'getBackups');
|
await requestLimiter(context.ip, 'getBackups');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const backups = await getBackups({
|
const backups = await getBackups({
|
||||||
|
@ -93,7 +86,7 @@ export const toolsResolvers = {
|
||||||
adminCheck: async (_: undefined, params: any, context: ContextType) => {
|
adminCheck: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'adminCheck');
|
await requestLimiter(context.ip, 'adminCheck');
|
||||||
|
|
||||||
const lnd = getLnd(params.auth, context);
|
const { lnd } = context;
|
||||||
|
|
||||||
const [, error] = await toWithError(
|
const [, error] = await toWithError(
|
||||||
pay({
|
pay({
|
||||||
|
@ -122,8 +115,7 @@ export const toolsResolvers = {
|
||||||
verifyMessage: async (_: undefined, params: any, context: ContextType) => {
|
verifyMessage: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'verifyMessage');
|
await requestLimiter(context.ip, 'verifyMessage');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const message: { signed_by: string } = await verifyMessage({
|
const message: { signed_by: string } = await verifyMessage({
|
||||||
|
@ -141,8 +133,7 @@ export const toolsResolvers = {
|
||||||
signMessage: async (_: undefined, params: any, context: ContextType) => {
|
signMessage: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'signMessage');
|
await requestLimiter(context.ip, 'signMessage');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const message: { signature: string } = await signMessage({
|
const message: { signature: string } = await signMessage({
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
export interface PaymentProps {
|
|
||||||
created_at: string;
|
|
||||||
destination: string;
|
|
||||||
fee: number;
|
|
||||||
fee_mtokens: string;
|
|
||||||
hops: string[];
|
|
||||||
id: string;
|
|
||||||
is_confirmed: boolean;
|
|
||||||
is_outgoing: boolean;
|
|
||||||
mtokens: string;
|
|
||||||
request: string;
|
|
||||||
secret: string;
|
|
||||||
tokens: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PaymentsProps {
|
|
||||||
payments: PaymentProps[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InvoiceMessagesType {
|
|
||||||
type: string;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InvoicePaymentProps {
|
|
||||||
confirmed_at: string;
|
|
||||||
created_at: string;
|
|
||||||
created_height: number;
|
|
||||||
in_channel: string;
|
|
||||||
is_canceled: boolean;
|
|
||||||
is_confirmed: boolean;
|
|
||||||
is_held: boolean;
|
|
||||||
messages: InvoiceMessagesType[];
|
|
||||||
mtokens: string;
|
|
||||||
pending_index: number;
|
|
||||||
tokens: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InvoiceProps {
|
|
||||||
chain_address: string;
|
|
||||||
confirmed_at: string;
|
|
||||||
created_at: string;
|
|
||||||
description: string;
|
|
||||||
description_hash: string;
|
|
||||||
expires_at: string;
|
|
||||||
id: string;
|
|
||||||
is_canceled: boolean;
|
|
||||||
is_confirmed: boolean;
|
|
||||||
is_held: boolean;
|
|
||||||
is_outgoing: boolean;
|
|
||||||
is_private: boolean;
|
|
||||||
payments: InvoicePaymentProps[];
|
|
||||||
received: number;
|
|
||||||
received_mtokens: string;
|
|
||||||
request: string;
|
|
||||||
secret: string;
|
|
||||||
tokens: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InvoicesProps {
|
|
||||||
invoices: InvoiceProps[];
|
|
||||||
next: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NodeProps {
|
|
||||||
alias: string;
|
|
||||||
}
|
|
|
@ -8,18 +8,24 @@ import { compareDesc, subHours, subDays, subMonths, subYears } from 'date-fns';
|
||||||
import { sortBy } from 'underscore';
|
import { sortBy } from 'underscore';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
import { ForwardCompleteProps } from '../widgets/resolvers/interface';
|
import {
|
||||||
import { PaymentsProps, InvoicesProps } from './interface';
|
GetInvoicesType,
|
||||||
|
GetPaymentsType,
|
||||||
|
InvoiceType,
|
||||||
|
PaymentType,
|
||||||
|
GetForwardsType,
|
||||||
|
} from 'server/types/ln-service.types';
|
||||||
|
|
||||||
|
type TransactionType = InvoiceType | PaymentType;
|
||||||
|
type TransactionWithType = { isTypeOf: string } & TransactionType;
|
||||||
|
|
||||||
export const transactionResolvers = {
|
export const transactionResolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
getResume: async (_: undefined, params: any, context: ContextType) => {
|
getResume: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'payments');
|
await requestLimiter(context.ip, 'payments');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const invoiceProps = params.token
|
const invoiceProps = params.token
|
||||||
? { token: params.token }
|
? { token: params.token }
|
||||||
|
@ -30,7 +36,7 @@ export const transactionResolvers = {
|
||||||
let token = '';
|
let token = '';
|
||||||
let withInvoices = true;
|
let withInvoices = true;
|
||||||
|
|
||||||
const invoiceList: InvoicesProps = await to(
|
const invoiceList = await to<GetInvoicesType>(
|
||||||
getInvoices({
|
getInvoices({
|
||||||
lnd,
|
lnd,
|
||||||
...invoiceProps,
|
...invoiceProps,
|
||||||
|
@ -52,10 +58,10 @@ export const transactionResolvers = {
|
||||||
const { date } = invoices[invoices.length - 1];
|
const { date } = invoices[invoices.length - 1];
|
||||||
firstInvoiceDate = invoices[0].date;
|
firstInvoiceDate = invoices[0].date;
|
||||||
lastInvoiceDate = date;
|
lastInvoiceDate = date;
|
||||||
token = invoiceList.next;
|
token = invoiceList.next || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const paymentList: PaymentsProps = await to(
|
const paymentList = await to<GetPaymentsType>(
|
||||||
getPayments({
|
getPayments({
|
||||||
lnd,
|
lnd,
|
||||||
})
|
})
|
||||||
|
@ -70,7 +76,7 @@ export const transactionResolvers = {
|
||||||
isTypeOf: 'PaymentType',
|
isTypeOf: 'PaymentType',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const filterArray = payment => {
|
const filterArray = (payment: typeof payments[number]) => {
|
||||||
const last =
|
const last =
|
||||||
compareDesc(new Date(lastInvoiceDate), new Date(payment.date)) === 1;
|
compareDesc(new Date(lastInvoiceDate), new Date(payment.date)) === 1;
|
||||||
const first = params.token
|
const first = params.token
|
||||||
|
@ -97,8 +103,7 @@ export const transactionResolvers = {
|
||||||
getForwards: async (_: undefined, params: any, context: ContextType) => {
|
getForwards: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'forwards');
|
await requestLimiter(context.ip, 'forwards');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
let startDate = new Date();
|
let startDate = new Date();
|
||||||
const endDate = new Date();
|
const endDate = new Date();
|
||||||
|
@ -123,7 +128,7 @@ export const transactionResolvers = {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const forwardsList: ForwardCompleteProps = await to(
|
const forwardsList = await to<GetForwardsType>(
|
||||||
getLnForwards({
|
getLnForwards({
|
||||||
lnd,
|
lnd,
|
||||||
after: startDate,
|
after: startDate,
|
||||||
|
@ -153,7 +158,7 @@ export const transactionResolvers = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Transaction: {
|
Transaction: {
|
||||||
__resolveType(parent) {
|
__resolveType(parent: TransactionWithType) {
|
||||||
return parent.isTypeOf;
|
return parent.isTypeOf;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -24,7 +24,7 @@ export const transactionTypes = gql`
|
||||||
destination_node: Node
|
destination_node: Node
|
||||||
fee: Int!
|
fee: Int!
|
||||||
fee_mtokens: String!
|
fee_mtokens: String!
|
||||||
hops: [Node]
|
hops: [Node!]!
|
||||||
id: String!
|
id: String!
|
||||||
index: Int
|
index: Int
|
||||||
is_confirmed: Boolean!
|
is_confirmed: Boolean!
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
import { gql } from 'apollo-server-micro';
|
import { gql } from 'apollo-server-micro';
|
||||||
|
|
||||||
export const generalTypes = gql`
|
export const generalTypes = gql`
|
||||||
input authType {
|
|
||||||
type: String!
|
|
||||||
id: String
|
|
||||||
host: String
|
|
||||||
macaroon: String
|
|
||||||
cert: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input permissionsType {
|
input permissionsType {
|
||||||
is_ok_to_adjust_peers: Boolean
|
is_ok_to_adjust_peers: Boolean
|
||||||
is_ok_to_create_chain_addresses: Boolean
|
is_ok_to_create_chain_addresses: Boolean
|
||||||
|
@ -38,73 +30,59 @@ export const queryTypes = gql`
|
||||||
type Query {
|
type Query {
|
||||||
getBaseNodes: [baseNodesType]!
|
getBaseNodes: [baseNodesType]!
|
||||||
getAccountingReport(
|
getAccountingReport(
|
||||||
auth: authType!
|
|
||||||
category: String
|
category: String
|
||||||
currency: String
|
currency: String
|
||||||
fiat: String
|
fiat: String
|
||||||
month: String
|
month: String
|
||||||
year: String
|
year: String
|
||||||
): String!
|
): String!
|
||||||
getVolumeHealth(auth: authType!): channelsHealth
|
getVolumeHealth: channelsHealth
|
||||||
getTimeHealth(auth: authType!): channelsTimeHealth
|
getTimeHealth: channelsTimeHealth
|
||||||
getFeeHealth(auth: authType!): channelsFeeHealth
|
getFeeHealth: channelsFeeHealth
|
||||||
getChannelBalance(auth: authType!): channelBalanceType
|
getChannelBalance: channelBalanceType
|
||||||
getChannels(auth: authType!, active: Boolean): [channelType]
|
getChannels(active: Boolean): [channelType]!
|
||||||
getClosedChannels(auth: authType!, type: String): [closedChannelType]
|
getClosedChannels(type: String): [closedChannelType]
|
||||||
getPendingChannels(auth: authType!): [pendingChannelType]
|
getPendingChannels: [pendingChannelType]
|
||||||
getChannelFees(auth: authType!): [channelFeeType]
|
getChannelFees: [channelFeeType]
|
||||||
getChannelReport(auth: authType!): channelReportType
|
getChannelReport: channelReportType
|
||||||
getNetworkInfo(auth: authType!): networkInfoType
|
getNetworkInfo: networkInfoType
|
||||||
getNodeInfo(auth: authType!): nodeInfoType
|
getNodeInfo: nodeInfoType
|
||||||
adminCheck(auth: authType!): Boolean
|
adminCheck: Boolean
|
||||||
getNode(
|
getNode(publicKey: String!, withoutChannels: Boolean): Node!
|
||||||
auth: authType!
|
decodeRequest(request: String!): decodeType
|
||||||
publicKey: String!
|
getWalletInfo: walletInfoType
|
||||||
withoutChannels: Boolean
|
getResume(token: String): getResumeType
|
||||||
): Node!
|
getForwards(time: String): getForwardType
|
||||||
decodeRequest(auth: authType!, request: String!): decodeType
|
|
||||||
getWalletInfo(auth: authType!): walletInfoType
|
|
||||||
getResume(auth: authType!, token: String): getResumeType
|
|
||||||
getForwards(auth: authType!, time: String): getForwardType
|
|
||||||
getBitcoinPrice(logger: Boolean, currency: String): String
|
getBitcoinPrice(logger: Boolean, currency: String): String
|
||||||
getBitcoinFees(logger: Boolean): bitcoinFeeType
|
getBitcoinFees(logger: Boolean): bitcoinFeeType
|
||||||
getForwardReport(auth: authType!, time: String): String
|
getForwardReport(time: String): String
|
||||||
getForwardChannelsReport(
|
getForwardChannelsReport(time: String, order: String, type: String): String
|
||||||
auth: authType!
|
getInOut(time: String): InOutType
|
||||||
time: String
|
getBackups: String
|
||||||
order: String
|
verifyBackups(backup: String!): Boolean
|
||||||
type: String
|
recoverFunds(backup: String!): Boolean
|
||||||
): String
|
|
||||||
getInOut(auth: authType!, time: String): InOutType
|
|
||||||
getBackups(auth: authType!): String
|
|
||||||
verifyBackups(auth: authType!, backup: String!): Boolean
|
|
||||||
recoverFunds(auth: authType!, backup: String!): Boolean
|
|
||||||
getRoutes(
|
getRoutes(
|
||||||
auth: authType!
|
|
||||||
outgoing: String!
|
outgoing: String!
|
||||||
incoming: String!
|
incoming: String!
|
||||||
tokens: Int!
|
tokens: Int!
|
||||||
maxFee: Int
|
maxFee: Int
|
||||||
): GetRouteType
|
): GetRouteType
|
||||||
getPeers(auth: authType!): [peerType]
|
getPeers: [peerType]
|
||||||
signMessage(auth: authType!, message: String!): String
|
signMessage(message: String!): String
|
||||||
verifyMessage(auth: authType!, message: String!, signature: String!): String
|
verifyMessage(message: String!, signature: String!): String
|
||||||
getChainBalance(auth: authType!): Int
|
getChainBalance: Int
|
||||||
getPendingChainBalance(auth: authType!): Int
|
getPendingChainBalance: Int
|
||||||
getChainTransactions(auth: authType!): [getTransactionsType]
|
getChainTransactions: [getTransactionsType]
|
||||||
getUtxos(auth: authType!): [getUtxosType]
|
getUtxos: [getUtxosType]
|
||||||
getOffers(filter: String): [hodlOfferType]
|
|
||||||
getCountries: [hodlCountryType]
|
|
||||||
getCurrencies: [hodlCurrencyType]
|
|
||||||
getMessages(
|
getMessages(
|
||||||
auth: authType!
|
|
||||||
token: String
|
token: String
|
||||||
initialize: Boolean
|
initialize: Boolean
|
||||||
lastMessage: String
|
lastMessage: String
|
||||||
): getMessagesType
|
): getMessagesType
|
||||||
getAuthToken(cookie: String): Boolean
|
getAuthToken(cookie: String): Boolean!
|
||||||
getSessionToken(id: String, password: String): Boolean
|
getSessionToken(id: String, password: String): Boolean
|
||||||
getServerAccounts: [serverAccountType]
|
getServerAccounts: [serverAccountType]
|
||||||
|
getAccount: serverAccountType
|
||||||
getLnPayInfo: lnPayInfoType
|
getLnPayInfo: lnPayInfoType
|
||||||
getLnPay(amount: Int): String
|
getLnPay(amount: Int): String
|
||||||
getLatestVersion: String
|
getLatestVersion: String
|
||||||
|
@ -114,21 +92,19 @@ export const queryTypes = gql`
|
||||||
export const mutationTypes = gql`
|
export const mutationTypes = gql`
|
||||||
type Mutation {
|
type Mutation {
|
||||||
closeChannel(
|
closeChannel(
|
||||||
auth: authType!
|
|
||||||
id: String!
|
id: String!
|
||||||
forceClose: Boolean
|
forceClose: Boolean
|
||||||
targetConfirmations: Int
|
targetConfirmations: Int
|
||||||
tokensPerVByte: Int
|
tokensPerVByte: Int
|
||||||
): closeChannelType
|
): closeChannelType
|
||||||
openChannel(
|
openChannel(
|
||||||
auth: authType!
|
|
||||||
amount: Int!
|
amount: Int!
|
||||||
partnerPublicKey: String!
|
partnerPublicKey: String!
|
||||||
tokensPerVByte: Int
|
tokensPerVByte: Int
|
||||||
isPrivate: Boolean
|
isPrivate: Boolean
|
||||||
|
pushTokens: Int
|
||||||
): openChannelType
|
): openChannelType
|
||||||
updateFees(
|
updateFees(
|
||||||
auth: authType!
|
|
||||||
transaction_id: String
|
transaction_id: String
|
||||||
transaction_vout: Int
|
transaction_vout: Int
|
||||||
base_fee_tokens: Float
|
base_fee_tokens: Float
|
||||||
|
@ -137,11 +113,10 @@ export const mutationTypes = gql`
|
||||||
max_htlc_mtokens: String
|
max_htlc_mtokens: String
|
||||||
min_htlc_mtokens: String
|
min_htlc_mtokens: String
|
||||||
): Boolean
|
): Boolean
|
||||||
keysend(auth: authType!, destination: String!, tokens: Int!): payType
|
keysend(destination: String!, tokens: Int!): payType
|
||||||
createInvoice(auth: authType!, amount: Int!): newInvoiceType
|
createInvoice(amount: Int!): newInvoiceType
|
||||||
circularRebalance(auth: authType!, route: String!): Boolean
|
circularRebalance(route: String!): Boolean
|
||||||
bosRebalance(
|
bosRebalance(
|
||||||
auth: authType!
|
|
||||||
avoid: [String]
|
avoid: [String]
|
||||||
in_through: String
|
in_through: String
|
||||||
is_avoiding_high_inbound: Boolean
|
is_avoiding_high_inbound: Boolean
|
||||||
|
@ -153,10 +128,9 @@ export const mutationTypes = gql`
|
||||||
out_through: String
|
out_through: String
|
||||||
target: Int
|
target: Int
|
||||||
): bosRebalanceResultType
|
): bosRebalanceResultType
|
||||||
payViaRoute(auth: authType!, route: String!, id: String!): Boolean
|
payViaRoute(route: String!, id: String!): Boolean
|
||||||
createAddress(auth: authType!, nested: Boolean): String
|
createAddress(nested: Boolean): String
|
||||||
sendToAddress(
|
sendToAddress(
|
||||||
auth: authType!
|
|
||||||
address: String!
|
address: String!
|
||||||
tokens: Int
|
tokens: Int
|
||||||
fee: Int
|
fee: Int
|
||||||
|
@ -164,15 +138,13 @@ export const mutationTypes = gql`
|
||||||
sendAll: Boolean
|
sendAll: Boolean
|
||||||
): sendToType
|
): sendToType
|
||||||
addPeer(
|
addPeer(
|
||||||
auth: authType!
|
|
||||||
url: String
|
url: String
|
||||||
publicKey: String
|
publicKey: String
|
||||||
socket: String
|
socket: String
|
||||||
isTemporary: Boolean
|
isTemporary: Boolean
|
||||||
): Boolean
|
): Boolean
|
||||||
removePeer(auth: authType!, publicKey: String!): Boolean
|
removePeer(publicKey: String!): Boolean
|
||||||
sendMessage(
|
sendMessage(
|
||||||
auth: authType!
|
|
||||||
publicKey: String!
|
publicKey: String!
|
||||||
message: String!
|
message: String!
|
||||||
messageType: String
|
messageType: String
|
||||||
|
@ -180,6 +152,6 @@ export const mutationTypes = gql`
|
||||||
maxFee: Int
|
maxFee: Int
|
||||||
): Int
|
): Int
|
||||||
logout(type: String!): Boolean
|
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 { ContextType } from 'server/types/apiTypes';
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
|
||||||
|
|
||||||
export const walletResolvers = {
|
export const walletResolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
getWalletInfo: async (_: undefined, params: any, context: ContextType) => {
|
getWalletInfo: async (_: undefined, params: any, context: ContextType) => {
|
||||||
await requestLimiter(context.ip, 'getWalletInfo');
|
await requestLimiter(context.ip, 'getWalletInfo');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
return await to(
|
return await to(
|
||||||
getWalletVersion({
|
getWalletVersion({
|
||||||
|
|
|
@ -2,14 +2,14 @@ import { gql } from 'apollo-server-micro';
|
||||||
|
|
||||||
export const walletTypes = gql`
|
export const walletTypes = gql`
|
||||||
type walletInfoType {
|
type walletInfoType {
|
||||||
build_tags: [String]
|
build_tags: [String!]!
|
||||||
commit_hash: String
|
commit_hash: String!
|
||||||
is_autopilotrpc_enabled: Boolean
|
is_autopilotrpc_enabled: Boolean!
|
||||||
is_chainrpc_enabled: Boolean
|
is_chainrpc_enabled: Boolean!
|
||||||
is_invoicesrpc_enabled: Boolean
|
is_invoicesrpc_enabled: Boolean!
|
||||||
is_signrpc_enabled: Boolean
|
is_signrpc_enabled: Boolean!
|
||||||
is_walletrpc_enabled: Boolean
|
is_walletrpc_enabled: Boolean!
|
||||||
is_watchtowerrpc_enabled: Boolean
|
is_watchtowerrpc_enabled: Boolean!
|
||||||
is_wtclientrpc_enabled: Boolean
|
is_wtclientrpc_enabled: Boolean!
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { getChannels } from 'ln-service';
|
import { getChannels } from 'ln-service';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getLnd } from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
|
import { GetChannelsType } from 'server/types/ln-service.types';
|
||||||
|
|
||||||
export const getChannelReport = async (
|
export const getChannelReport = async (
|
||||||
_: undefined,
|
_: undefined,
|
||||||
|
@ -11,9 +11,9 @@ export const getChannelReport = async (
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'channelReport');
|
await requestLimiter(context.ip, 'channelReport');
|
||||||
|
|
||||||
const lnd = getLnd(params.auth, context);
|
const { lnd } = context;
|
||||||
|
|
||||||
const info = await to(getChannels({ lnd }));
|
const info = await to<GetChannelsType>(getChannels({ lnd }));
|
||||||
|
|
||||||
if (!info || info?.channels?.length <= 0) {
|
if (!info || info?.channels?.length <= 0) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -4,10 +4,13 @@ import { sortBy } from 'underscore';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { getNodeFromChannel } from 'server/helpers/getNodeFromChannel';
|
import { getNodeFromChannel } from 'server/helpers/getNodeFromChannel';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
|
import {
|
||||||
|
GetForwardsType,
|
||||||
|
GetWalletInfoType,
|
||||||
|
} from 'server/types/ln-service.types';
|
||||||
import { countArray, countRoutes } from './helpers';
|
import { countArray, countRoutes } from './helpers';
|
||||||
import { ForwardCompleteProps } from './interface';
|
|
||||||
|
|
||||||
export const getForwardChannelsReport = async (
|
export const getForwardChannelsReport = async (
|
||||||
_: undefined,
|
_: undefined,
|
||||||
|
@ -16,8 +19,7 @@ export const getForwardChannelsReport = async (
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'forwardChannels');
|
await requestLimiter(context.ip, 'forwardChannels');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
let startDate = new Date();
|
let startDate = new Date();
|
||||||
const endDate = new Date();
|
const endDate = new Date();
|
||||||
|
@ -76,7 +78,7 @@ export const getForwardChannelsReport = async (
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const forwardsList: ForwardCompleteProps = await to(
|
const forwardsList = await to<GetForwardsType>(
|
||||||
getForwards({
|
getForwards({
|
||||||
lnd,
|
lnd,
|
||||||
after: startDate,
|
after: startDate,
|
||||||
|
@ -84,7 +86,7 @@ export const getForwardChannelsReport = async (
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const walletInfo: { public_key: string } = await to(
|
const walletInfo = await to<GetWalletInfoType>(
|
||||||
getWalletInfo({
|
getWalletInfo({
|
||||||
lnd,
|
lnd,
|
||||||
})
|
})
|
||||||
|
@ -101,12 +103,18 @@ export const getForwardChannelsReport = async (
|
||||||
|
|
||||||
while (!finishedFetching) {
|
while (!finishedFetching) {
|
||||||
if (next) {
|
if (next) {
|
||||||
const moreForwards = await to(getForwards({ lnd, token: next }));
|
const moreForwards = await to<GetForwardsType>(
|
||||||
|
getForwards({ lnd, token: next })
|
||||||
|
);
|
||||||
forwards = [...forwards, ...moreForwards.forwards];
|
forwards = [...forwards, ...moreForwards.forwards];
|
||||||
|
if (moreForwards.next) {
|
||||||
next = moreForwards.next;
|
next = moreForwards.next;
|
||||||
} else {
|
} else {
|
||||||
finishedFetching = true;
|
finishedFetching = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
finishedFetching = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.type === 'route') {
|
if (params.type === 'route') {
|
||||||
|
|
|
@ -8,10 +8,10 @@ import {
|
||||||
} from 'date-fns';
|
} from 'date-fns';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
|
import { GetForwardsType } from 'server/types/ln-service.types';
|
||||||
import { reduceForwardArray } from './helpers';
|
import { reduceForwardArray } from './helpers';
|
||||||
import { ForwardCompleteProps } from './interface';
|
|
||||||
|
|
||||||
export const getForwardReport = async (
|
export const getForwardReport = async (
|
||||||
_: undefined,
|
_: undefined,
|
||||||
|
@ -20,8 +20,7 @@ export const getForwardReport = async (
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'forwardReport');
|
await requestLimiter(context.ip, 'forwardReport');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
let startDate = new Date();
|
let startDate = new Date();
|
||||||
const endDate = new Date();
|
const endDate = new Date();
|
||||||
|
@ -45,7 +44,7 @@ export const getForwardReport = async (
|
||||||
startDate = subHours(endDate, 24);
|
startDate = subHours(endDate, 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
const forwardsList: ForwardCompleteProps = await to(
|
const forwardsList = await to<GetForwardsType>(
|
||||||
getForwards({
|
getForwards({
|
||||||
lnd,
|
lnd,
|
||||||
after: startDate,
|
after: startDate,
|
||||||
|
@ -64,7 +63,9 @@ export const getForwardReport = async (
|
||||||
|
|
||||||
while (!finishedFetching) {
|
while (!finishedFetching) {
|
||||||
if (next) {
|
if (next) {
|
||||||
const moreForwards = await to(getForwards({ lnd, token: next }));
|
const moreForwards = await to<GetForwardsType>(
|
||||||
|
getForwards({ lnd, token: next })
|
||||||
|
);
|
||||||
forwards = [...forwards, ...moreForwards.forwards];
|
forwards = [...forwards, ...moreForwards.forwards];
|
||||||
next = moreForwards.next;
|
next = moreForwards.next;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3,8 +3,12 @@ import { differenceInHours, differenceInCalendarDays } from 'date-fns';
|
||||||
import { groupBy } from 'underscore';
|
import { groupBy } from 'underscore';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||||
import { getAuthLnd, getCorrectAuth } from 'server/helpers/helpers';
|
|
||||||
import { to } from 'server/helpers/async';
|
import { to } from 'server/helpers/async';
|
||||||
|
import {
|
||||||
|
GetInvoicesType,
|
||||||
|
GetPaymentsType,
|
||||||
|
} from 'server/types/ln-service.types';
|
||||||
import { reduceInOutArray } from './helpers';
|
import { reduceInOutArray } from './helpers';
|
||||||
|
|
||||||
export const getInOut = async (
|
export const getInOut = async (
|
||||||
|
@ -14,8 +18,7 @@ export const getInOut = async (
|
||||||
) => {
|
) => {
|
||||||
await requestLimiter(context.ip, 'getInOut');
|
await requestLimiter(context.ip, 'getInOut');
|
||||||
|
|
||||||
const auth = getCorrectAuth(params.auth, context);
|
const { lnd } = context;
|
||||||
const lnd = getAuthLnd(auth);
|
|
||||||
|
|
||||||
const endDate = new Date();
|
const endDate = new Date();
|
||||||
let periods = 7;
|
let periods = 7;
|
||||||
|
@ -35,8 +38,10 @@ export const getInOut = async (
|
||||||
difference = (date: string) => differenceInHours(endDate, new Date(date));
|
difference = (date: string) => differenceInHours(endDate, new Date(date));
|
||||||
}
|
}
|
||||||
|
|
||||||
const invoiceList = await to(getInvoices({ lnd, limit: 50 }));
|
const invoiceList = await to<GetInvoicesType>(
|
||||||
const paymentList = await to(getPayments({ lnd }));
|
getInvoices({ lnd, limit: 50 })
|
||||||
|
);
|
||||||
|
const paymentList = await to<GetPaymentsType>(getPayments({ lnd }));
|
||||||
|
|
||||||
let invoiceArray = invoiceList.invoices;
|
let invoiceArray = invoiceList.invoices;
|
||||||
let next = invoiceList.next;
|
let next = invoiceList.next;
|
||||||
|
@ -51,7 +56,9 @@ export const getInOut = async (
|
||||||
const dif = difference(lastInvoice.created_at);
|
const dif = difference(lastInvoice.created_at);
|
||||||
|
|
||||||
if (next && dif < periods) {
|
if (next && dif < periods) {
|
||||||
const newInvoices = await to(getInvoices({ lnd, token: next }));
|
const newInvoices = await to<GetInvoicesType>(
|
||||||
|
getInvoices({ lnd, token: next })
|
||||||
|
);
|
||||||
invoiceArray = [...invoiceArray, ...newInvoices.invoices];
|
invoiceArray = [...invoiceArray, ...newInvoices.invoices];
|
||||||
next = newInvoices.next;
|
next = newInvoices.next;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { reduce, groupBy } from 'underscore';
|
import { reduce, groupBy } from 'underscore';
|
||||||
|
import { ForwardType } from 'server/types/ln-service.types';
|
||||||
import {
|
import {
|
||||||
ForwardProps,
|
|
||||||
ReduceObjectProps,
|
ReduceObjectProps,
|
||||||
ListProps,
|
ListProps,
|
||||||
InOutProps,
|
InOutProps,
|
||||||
|
@ -11,7 +11,7 @@ export const reduceForwardArray = (list: ListProps) => {
|
||||||
const reducedOrder = [];
|
const reducedOrder = [];
|
||||||
for (const key in list) {
|
for (const key in list) {
|
||||||
if (Object.prototype.hasOwnProperty.call(list, key)) {
|
if (Object.prototype.hasOwnProperty.call(list, key)) {
|
||||||
const element: ForwardProps[] = list[key];
|
const element: ForwardType[] = list[key];
|
||||||
const reducedArray: ReduceObjectProps = reduce(
|
const reducedArray: ReduceObjectProps = reduce(
|
||||||
element,
|
element,
|
||||||
(a: ReduceObjectProps, b: ReduceObjectProps) => {
|
(a: ReduceObjectProps, b: ReduceObjectProps) => {
|
||||||
|
@ -19,7 +19,8 @@ export const reduceForwardArray = (list: ListProps) => {
|
||||||
fee: a.fee + b.fee,
|
fee: a.fee + b.fee,
|
||||||
tokens: a.tokens + b.tokens,
|
tokens: a.tokens + b.tokens,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
{ fee: 0, tokens: 0 }
|
||||||
);
|
);
|
||||||
reducedOrder.push({
|
reducedOrder.push({
|
||||||
period: Number(key),
|
period: Number(key),
|
||||||
|
@ -39,9 +40,10 @@ export const reduceInOutArray = (list: InOutListProps) => {
|
||||||
const element: InOutProps[] = list[key];
|
const element: InOutProps[] = list[key];
|
||||||
const reducedArray: InOutProps = reduce(
|
const reducedArray: InOutProps = reduce(
|
||||||
element,
|
element,
|
||||||
(a: ReduceObjectProps, b: ReduceObjectProps) => ({
|
(a, b) => ({
|
||||||
tokens: a.tokens + b.tokens,
|
tokens: a.tokens + b.tokens,
|
||||||
})
|
}),
|
||||||
|
{ tokens: 0 }
|
||||||
);
|
);
|
||||||
reducedOrder.push({
|
reducedOrder.push({
|
||||||
period: Number(key),
|
period: Number(key),
|
||||||
|
@ -53,7 +55,7 @@ export const reduceInOutArray = (list: InOutListProps) => {
|
||||||
return reducedOrder;
|
return reducedOrder;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const countArray = (list: ForwardProps[], type: boolean) => {
|
export const countArray = (list: ForwardType[], type: boolean) => {
|
||||||
const inOrOut = type ? 'incoming_channel' : 'outgoing_channel';
|
const inOrOut = type ? 'incoming_channel' : 'outgoing_channel';
|
||||||
const grouped = groupBy(list, inOrOut);
|
const grouped = groupBy(list, inOrOut);
|
||||||
|
|
||||||
|
@ -82,7 +84,7 @@ export const countArray = (list: ForwardProps[], type: boolean) => {
|
||||||
return channelInfo;
|
return channelInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const countRoutes = (list: ForwardProps[]) => {
|
export const countRoutes = (list: ForwardType[]) => {
|
||||||
const grouped = groupBy(list, 'route');
|
const grouped = groupBy(list, 'route');
|
||||||
|
|
||||||
const channelInfo = [];
|
const channelInfo = [];
|
||||||
|
|
|
@ -1,20 +1,7 @@
|
||||||
export interface ForwardProps {
|
import { ForwardType } from 'server/types/ln-service.types';
|
||||||
created_at: string;
|
|
||||||
fee: number;
|
|
||||||
fee_mtokens: string;
|
|
||||||
incoming_channel: string;
|
|
||||||
mtokens: string;
|
|
||||||
outgoing_channel: string;
|
|
||||||
tokens: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ForwardCompleteProps {
|
|
||||||
forwards: ForwardProps[];
|
|
||||||
next: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ListProps {
|
export interface ListProps {
|
||||||
[key: string]: ForwardProps[];
|
[key: string]: ForwardType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReduceObjectProps {
|
export interface ReduceObjectProps {
|
||||||
|
|
|
@ -2,6 +2,14 @@ import * as res from '../lnServiceResponse';
|
||||||
|
|
||||||
export const authenticatedLndGrpc = jest.fn().mockReturnValue({});
|
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
|
export const getNetworkInfo = jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockReturnValue(Promise.resolve(res.getNetworkInfoResponse));
|
.mockReturnValue(Promise.resolve(res.getNetworkInfoResponse));
|
||||||
|
|
|
@ -342,6 +342,39 @@ export const getNodeResponse = {
|
||||||
updated_at: '2011-10-05T14:48:00.000Z',
|
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 = {
|
export const getChannelResponse = {
|
||||||
capacity: 1000,
|
capacity: 1000,
|
||||||
id: '100x1x1',
|
id: '100x1x1',
|
||||||
|
|
|
@ -1,27 +1,21 @@
|
||||||
import { ServerResponse } from 'http';
|
import { ServerResponse } from 'http';
|
||||||
import { ContextType } from 'server/types/apiTypes';
|
import { ContextType } from 'server/types/apiTypes';
|
||||||
|
|
||||||
export const AuthMock = {
|
|
||||||
auth: {
|
|
||||||
type: 'test',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ContextMock: ContextType = {
|
export const ContextMock: ContextType = {
|
||||||
|
lnd: {},
|
||||||
|
id: 'test',
|
||||||
ip: '1.2.3.4',
|
ip: '1.2.3.4',
|
||||||
secret: '123456789',
|
secret: '123456789',
|
||||||
ssoVerified: true,
|
|
||||||
account: 'accountID',
|
|
||||||
sso: {
|
sso: {
|
||||||
macaroon: 'macaroon',
|
macaroon: 'macaroon',
|
||||||
cert: 'cert',
|
cert: 'cert',
|
||||||
host: 'host',
|
socket: 'host',
|
||||||
},
|
},
|
||||||
accounts: [
|
accounts: [
|
||||||
{
|
{
|
||||||
name: 'account',
|
name: 'account',
|
||||||
id: 'accountID',
|
id: 'accountID',
|
||||||
host: 'host',
|
socket: 'host',
|
||||||
macaroon: 'macaroon',
|
macaroon: 'macaroon',
|
||||||
cert: 'cert',
|
cert: 'cert',
|
||||||
password: 'password',
|
password: 'password',
|
||||||
|
@ -31,34 +25,30 @@ export const ContextMock: ContextType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ContextMockNoAccounts: ContextType = {
|
export const ContextMockNoAccounts: ContextType = {
|
||||||
|
lnd: {},
|
||||||
|
id: 'test',
|
||||||
ip: '1.2.3.4',
|
ip: '1.2.3.4',
|
||||||
secret: '123456789',
|
secret: '123456789',
|
||||||
ssoVerified: true,
|
|
||||||
account: 'accountID',
|
|
||||||
sso: {
|
sso: {
|
||||||
macaroon: 'macaroon',
|
macaroon: 'macaroon',
|
||||||
cert: 'cert',
|
cert: 'cert',
|
||||||
host: 'host',
|
socket: 'host',
|
||||||
},
|
},
|
||||||
accounts: [],
|
accounts: [],
|
||||||
res: {} as ServerResponse,
|
res: {} as ServerResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ContextMockNoSSO: ContextType = {
|
export const ContextMockNoSSO: ContextType = {
|
||||||
|
lnd: {},
|
||||||
|
id: 'test',
|
||||||
ip: '1.2.3.4',
|
ip: '1.2.3.4',
|
||||||
secret: '123456789',
|
secret: '123456789',
|
||||||
ssoVerified: true,
|
sso: null,
|
||||||
account: 'accountID',
|
|
||||||
sso: {
|
|
||||||
macaroon: null,
|
|
||||||
cert: null,
|
|
||||||
host: null,
|
|
||||||
},
|
|
||||||
accounts: [
|
accounts: [
|
||||||
{
|
{
|
||||||
name: 'account',
|
name: 'account',
|
||||||
id: 'accountID',
|
id: 'accountID',
|
||||||
host: 'host',
|
socket: 'host',
|
||||||
macaroon: 'macaroon',
|
macaroon: 'macaroon',
|
||||||
cert: 'cert',
|
cert: 'cert',
|
||||||
password: 'password',
|
password: 'password',
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {
|
||||||
ApolloServerTestClient,
|
ApolloServerTestClient,
|
||||||
} from 'apollo-server-testing';
|
} from 'apollo-server-testing';
|
||||||
import { ApolloServer } from 'apollo-server';
|
import { ApolloServer } from 'apollo-server';
|
||||||
import schema from 'server/schema';
|
import { schema } from 'server/schema';
|
||||||
import { ContextMock } from 'server/tests/testMocks';
|
import { ContextMock } from 'server/tests/testMocks';
|
||||||
|
|
||||||
export default function testServer(context?: any): ApolloServerTestClient {
|
export default function testServer(context?: any): ApolloServerTestClient {
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { ServerResponse } from 'http';
|
import { ServerResponse } from 'http';
|
||||||
|
import { LndObject } from './ln-service.types';
|
||||||
|
|
||||||
type SSOType = {
|
export type SSOType = {
|
||||||
macaroon: string | null;
|
macaroon: string;
|
||||||
cert: string | null;
|
cert: string | null;
|
||||||
host: string | null;
|
socket: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AccountType = {
|
export type AccountType = {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
host: string;
|
socket: string;
|
||||||
macaroon: string;
|
macaroon: string;
|
||||||
cert: string | null;
|
cert: string | null;
|
||||||
password: string;
|
password: string;
|
||||||
|
@ -17,10 +18,10 @@ type AccountType = {
|
||||||
|
|
||||||
export type ContextType = {
|
export type ContextType = {
|
||||||
ip: string;
|
ip: string;
|
||||||
|
lnd: LndObject | null;
|
||||||
secret: string;
|
secret: string;
|
||||||
ssoVerified: boolean;
|
id: string | null;
|
||||||
account: string | null;
|
sso: SSOType | null;
|
||||||
sso: SSOType;
|
|
||||||
accounts: AccountType[];
|
accounts: AccountType[];
|
||||||
res: ServerResponse;
|
res: ServerResponse;
|
||||||
};
|
};
|
||||||
|
|
1
server/types/balanceofsatoshis.types.ts
Normal file
1
server/types/balanceofsatoshis.types.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export type RebalanceResponseType = { rebalance: [{}, {}, {}] };
|
104
server/types/ln-service.types.ts
Normal file
104
server/types/ln-service.types.ts
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
export type LndObject = {};
|
||||||
|
|
||||||
|
export type ChannelType = {
|
||||||
|
id: string;
|
||||||
|
tokens: number;
|
||||||
|
is_partner_initiated: boolean;
|
||||||
|
commit_transaction_fee: number;
|
||||||
|
is_active: boolean;
|
||||||
|
local_balance: number;
|
||||||
|
remote_balance: number;
|
||||||
|
partner_public_key: string;
|
||||||
|
time_offline?: number;
|
||||||
|
time_online?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DecodedType = {
|
||||||
|
destination: string;
|
||||||
|
tokens: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ClosedChannelsType = {
|
||||||
|
channels: [];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CloseChannelType = {
|
||||||
|
transaction_id: string;
|
||||||
|
transaction_vout: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OpenChannelType = {
|
||||||
|
transaction_id: string;
|
||||||
|
transaction_vout: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InvoiceType = {
|
||||||
|
id: string;
|
||||||
|
created_at: string;
|
||||||
|
confirmed_at: string;
|
||||||
|
tokens: number;
|
||||||
|
is_confirmed: boolean;
|
||||||
|
received: number;
|
||||||
|
payments: { messages: [{ type: string; value: string }] }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PaymentType = {
|
||||||
|
created_at: string;
|
||||||
|
is_confirmed: boolean;
|
||||||
|
tokens: number;
|
||||||
|
destination: string;
|
||||||
|
hops: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ForwardType = {
|
||||||
|
tokens: number;
|
||||||
|
incoming_channel: string;
|
||||||
|
outgoing_channel: string;
|
||||||
|
created_at: string;
|
||||||
|
fee: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetWalletInfoType = {
|
||||||
|
alias: string;
|
||||||
|
public_key: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetNodeType = { alias: string; color: string };
|
||||||
|
|
||||||
|
export type UtxoType = {};
|
||||||
|
|
||||||
|
export type ChainTransaction = {};
|
||||||
|
|
||||||
|
export type ProbeForRouteType = { route?: { hops: [{ public_key: string }] } };
|
||||||
|
|
||||||
|
export type GetChannelType = {
|
||||||
|
policies: {
|
||||||
|
public_key: string;
|
||||||
|
base_fee_mtokens: string;
|
||||||
|
fee_rate: number;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetChannelsType = { channels: ChannelType[] };
|
||||||
|
|
||||||
|
export type GetForwardsType = { forwards: ForwardType[]; next?: string };
|
||||||
|
|
||||||
|
export type GetInvoicesType = { invoices: InvoiceType[]; next?: string };
|
||||||
|
|
||||||
|
export type GetPaymentsType = { payments: PaymentType[]; next?: string };
|
||||||
|
|
||||||
|
export type GetChainBalanceType = { chain_balance: number };
|
||||||
|
|
||||||
|
export type GetPendingChainBalanceType = { pending_chain_balance: number };
|
||||||
|
|
||||||
|
export type GetChainTransactionsType = { transactions: ChainTransaction[] };
|
||||||
|
|
||||||
|
export type GetUtxosType = { utxos: UtxoType[] };
|
||||||
|
|
||||||
|
export type SendToChainAddressType = {
|
||||||
|
id: string;
|
||||||
|
confirmation_count: number;
|
||||||
|
is_confirmed: boolean;
|
||||||
|
is_outgoing: boolean;
|
||||||
|
tokens: number | null;
|
||||||
|
};
|
3
server/utils/appConstants.ts
Normal file
3
server/utils/appConstants.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export const appConstants = {
|
||||||
|
cookieName: 'Thub-Auth',
|
||||||
|
};
|
|
@ -15,7 +15,6 @@ export const appUrls = {
|
||||||
blockchain: 'https://www.blockchain.com/btc/tx/',
|
blockchain: 'https://www.blockchain.com/btc/tx/',
|
||||||
fees: 'https://mempool.space/api/v1/fees/recommended',
|
fees: 'https://mempool.space/api/v1/fees/recommended',
|
||||||
ticker: 'https://blockchain.info/ticker',
|
ticker: 'https://blockchain.info/ticker',
|
||||||
hodlhodl: 'https://hodlhodl.com/api',
|
|
||||||
github: 'https://api.github.com/repos/apotdevin/thunderhub/releases/latest',
|
github: 'https://api.github.com/repos/apotdevin/thunderhub/releases/latest',
|
||||||
update: 'https://github.com/apotdevin/thunderhub#updating',
|
update: 'https://github.com/apotdevin/thunderhub#updating',
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,23 +1,11 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {
|
|
||||||
useAccountDispatch,
|
|
||||||
useAccountState,
|
|
||||||
CompleteAccount,
|
|
||||||
} 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 { useRouter } from 'next/router';
|
||||||
import { appendBasePath } from 'src/utils/basePath';
|
|
||||||
import { getUrlParam } from 'src/utils/url';
|
import { getUrlParam } from 'src/utils/url';
|
||||||
import { useGetAuthTokenQuery } from 'src/graphql/queries/__generated__/getAuthToken.generated';
|
import { useGetAuthTokenQuery } from 'src/graphql/queries/__generated__/getAuthToken.generated';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
export const ServerAccounts = () => {
|
export const ServerAccounts: React.FC = () => {
|
||||||
const { hasAccount } = useAccountState();
|
const { push, query } = useRouter();
|
||||||
const dispatch = useAccountDispatch();
|
|
||||||
const { push, pathname, query } = useRouter();
|
|
||||||
|
|
||||||
const { data, loading, refetch } = useGetServerAccountsQuery();
|
|
||||||
|
|
||||||
const cookieParam = getUrlParam(query?.token);
|
const cookieParam = getUrlParam(query?.token);
|
||||||
|
|
||||||
|
@ -28,41 +16,14 @@ export const ServerAccounts = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (cookieParam && authData && authData.getAuthToken) {
|
if (!cookieParam || !authData) return;
|
||||||
refetch();
|
if (authData.getAuthToken) {
|
||||||
push(appendBasePath('/'));
|
push('/');
|
||||||
}
|
}
|
||||||
}, [push, authData, cookieParam, refetch]);
|
if (!authData.getAuthToken) {
|
||||||
|
toast.warning('Unable to SSO. Check your logs.');
|
||||||
React.useEffect(() => {
|
|
||||||
if (hasAccount === 'error' && pathname !== '/') {
|
|
||||||
toast.error('No account found');
|
|
||||||
dispatch({ type: 'resetFetch' });
|
|
||||||
push(appendBasePath('/'));
|
|
||||||
}
|
}
|
||||||
}, [hasAccount, push, dispatch, pathname]);
|
}, [push, authData, cookieParam]);
|
||||||
|
|
||||||
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 => 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]);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue