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