mirror of
https://github.com/apotdevin/thunderhub.git
synced 2024-11-19 09:50:03 +01:00
feat: ✨ server authentication (#38)
* feat: ✨ server sso auth * chore: 🔧 continue sso integration * fix: 🐛 correct filter * chore: 🔧 add resolver types * chore: 🔧 linter sort imports * fix: 🐛 duplicate imports * chore: 🔧 change auth context * feat: ✨ add accounts read and query * fix: 🐛 small changes * chore: 🔧 continue integration * chore: 🔧 add auth * chore: 🔧 switch to new context * fix: 🐛 imports and account context * chore: 🔧 add session token query * chore: 🔧 change server auth * chore: 🔧 add sso to server accounts query * chore: 🔧 cleanup and small fixes * chore: 🔧 separate generated files * refactor: ♻️ change graphql imports * fix: 🐛 add id to params * fix: 🐛 auth changes * chore: 🔧 improve logging and account fixes * chore: 🔧 change footer * chore: 🔧 remove console logs * chore: 🔧 add spacing * fix: 🐛 final changes
This commit is contained in:
parent
0394209b80
commit
9d73c30fb4
49
README.md
49
README.md
@ -113,12 +113,57 @@ npm run dev -> npm run dev:compatible
|
||||
You can define some environment variables that ThunderHub can start with. To do this create a `.env` file in the root directory with the following parameters:
|
||||
|
||||
```js
|
||||
LOG_LEVEL = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly' //Default: 'info'
|
||||
THEME = 'dark' | 'light'; // Default: 'dark'
|
||||
CURRENCY = 'sat' | 'btc' | 'eur' | 'usd'; // Default: 'sat'
|
||||
FETCH_PRICES = true | false // Default: true
|
||||
FETCH_FEES = true | false // Default: true
|
||||
HODL_KEY='[Key provided by HodlHodl]' //Default: ''
|
||||
BASE_PATH='[Base path where you want to have thunderhub running i.e. '/btcpay']' //Default: '/'
|
||||
HODL_KEY = '[Key provided by HodlHodl]' //Default: ''
|
||||
BASE_PATH = '[Base path where you want to have thunderhub running i.e. '/btcpay']' //Default: '/'
|
||||
```
|
||||
|
||||
### SSO Account
|
||||
|
||||
You can define an account to work with SSO cookie authentication by adding the following parameters in the `.env` file:
|
||||
|
||||
```js
|
||||
COOKIE_PATH = '/path/to/cookie/file/.cookie'; // i.e. '/data/.cookie'
|
||||
SSO_SERVER_URL = 'url and port to node'; // i.e. '127.0.0.1:10009'
|
||||
SSO_CERT_PATH = '/path/to/tls/certificate'; // i.e. '\lnd\alice\tls.cert'
|
||||
SSO_MACAROON_PATH = '/path/to/macaroon/folder'; //i.e. '\lnd\alice\data\chain\bitcoin\regtest\'
|
||||
```
|
||||
|
||||
To login to this account you must add the cookie file content to the end of your ThunderHub url. For example:
|
||||
|
||||
```
|
||||
http://localhost:3000?token=[COOKIE]
|
||||
```
|
||||
|
||||
Replace `[COOKIE]` with the contents of the `.cookie` file.
|
||||
|
||||
### Server Accounts
|
||||
|
||||
You can add accounts on the server by adding this parameter to the `.env` file:
|
||||
|
||||
```js
|
||||
ACCOUNT_CONFIG_PATH = '/path/to/config/file.yaml'; // i.e. '/data/thubConfig.yaml'
|
||||
```
|
||||
|
||||
You must also add a YAML file at that location with the following format:
|
||||
|
||||
```yaml
|
||||
masterPassword: 'password' # Default password unless defined in account
|
||||
accounts:
|
||||
- name: 'Account 1'
|
||||
serverUrl: 'url:port'
|
||||
macaroonPath: '/path/to/admin.macaroon'
|
||||
certificatePath: '/path/to/tls.cert'
|
||||
password: 'password for account 1'
|
||||
- name: 'Account 2'
|
||||
serverUrl: 'url:port'
|
||||
macaroonPath: '/path/to/admin.macaroon'
|
||||
certificatePath: '/path/to/tls.cert'
|
||||
# password: Leave without password and it will use the master password
|
||||
```
|
||||
|
||||
### Fetching prices and fees
|
||||
|
252
api/helpers/fileHelpers.ts
Normal file
252
api/helpers/fileHelpers.ts
Normal file
@ -0,0 +1,252 @@
|
||||
import fs from 'fs';
|
||||
import crypto from 'crypto';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { logger } from 'api/helpers/logger';
|
||||
import yaml from 'js-yaml';
|
||||
import AES from 'crypto-js/aes';
|
||||
import { getUUID } from 'src/utils/auth';
|
||||
|
||||
type EncodingType = 'hex' | 'utf-8';
|
||||
|
||||
export const readFile = (
|
||||
filePath: string,
|
||||
encoding: EncodingType = 'hex'
|
||||
): string | null => {
|
||||
if (filePath === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fileExists = fs.existsSync(filePath);
|
||||
|
||||
if (!fileExists) {
|
||||
logger.error(`No file found at path: ${filePath}`);
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
const file = fs.readFileSync(filePath, encoding);
|
||||
return file;
|
||||
} catch (err) {
|
||||
logger.error('Something went wrong while reading the file: \n' + err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
type AccountType = {
|
||||
name: string;
|
||||
serverUrl: string;
|
||||
macaroonPath: string;
|
||||
certificatePath: string;
|
||||
password: string | null;
|
||||
};
|
||||
|
||||
type AccountConfigType = {
|
||||
masterPassword: string | null;
|
||||
accounts: AccountType[];
|
||||
};
|
||||
|
||||
export const parseYaml = (filePath: string): AccountConfigType | null => {
|
||||
if (filePath === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const yamlConfig = readFile(filePath, 'utf-8');
|
||||
|
||||
if (!yamlConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const yamlObject = yaml.safeLoad(yamlConfig);
|
||||
return yamlObject;
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
'Something went wrong while parsing the YAML config file: \n' + err
|
||||
);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const getAccounts = (filePath: string) => {
|
||||
if (filePath === '') {
|
||||
logger.verbose('No account config file path provided');
|
||||
return null;
|
||||
}
|
||||
|
||||
const accountConfig = parseYaml(filePath);
|
||||
|
||||
if (!accountConfig) {
|
||||
logger.info(`No account config file found at path ${filePath}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const { masterPassword, accounts } = accountConfig;
|
||||
|
||||
if (!accounts || accounts.length <= 0) {
|
||||
logger.warn(`Account config found at path ${filePath} but had no accounts`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const readAccounts = [];
|
||||
|
||||
const parsedAccounts = accounts
|
||||
.map((account, index) => {
|
||||
const {
|
||||
name,
|
||||
serverUrl,
|
||||
macaroonPath,
|
||||
certificatePath,
|
||||
password,
|
||||
} = account;
|
||||
|
||||
const missingFields: string[] = [];
|
||||
if (!name) missingFields.push('name');
|
||||
if (!serverUrl) missingFields.push('server url');
|
||||
if (!macaroonPath) missingFields.push('macaroon path');
|
||||
|
||||
if (missingFields.length > 0) {
|
||||
const text = missingFields.join(', ');
|
||||
logger.error(`Account in index ${index} is missing the fields ${text}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!password && !masterPassword) {
|
||||
logger.error(
|
||||
`You must set a password for account ${name} or set a master password`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!certificatePath)
|
||||
logger.warn(
|
||||
`No certificate for account ${name}. Make sure you don't need it to connect.`
|
||||
);
|
||||
|
||||
const cert = (certificatePath && readFile(certificatePath)) || null;
|
||||
const clearMacaroon = readFile(macaroonPath);
|
||||
|
||||
if (certificatePath && !cert)
|
||||
logger.warn(
|
||||
`No certificate for account ${name}. Make sure you don't need it to connect.`
|
||||
);
|
||||
|
||||
if (!clearMacaroon) {
|
||||
logger.error(`No macarron found for account ${name}.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const macaroon = AES.encrypt(
|
||||
clearMacaroon,
|
||||
password || masterPassword
|
||||
).toString();
|
||||
|
||||
const id = getUUID(`${name}${serverUrl}${clearMacaroon}${cert}`);
|
||||
|
||||
readAccounts.push(name);
|
||||
|
||||
return {
|
||||
name,
|
||||
id,
|
||||
host: serverUrl,
|
||||
macaroon,
|
||||
cert,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
const allAccounts = readAccounts.join(', ');
|
||||
logger.info(`Server accounts that will be available: ${allAccounts}`);
|
||||
|
||||
return parsedAccounts;
|
||||
};
|
||||
|
||||
export const readMacaroons = (macaroonPath: string): string | null => {
|
||||
if (macaroonPath === '') {
|
||||
logger.verbose('No macaroon path provided');
|
||||
return null;
|
||||
}
|
||||
|
||||
const adminExists = fs.existsSync(`${macaroonPath}admin.macaroon`);
|
||||
|
||||
if (!adminExists) {
|
||||
logger.error(`No admin.macaroon file found at path: ${macaroonPath}`);
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
const ssoAdmin = fs.readFileSync(`${macaroonPath}admin.macaroon`, 'hex');
|
||||
return ssoAdmin;
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
'Something went wrong while reading the admin.macaroon: \n' + err
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const createDirectory = (dirname: string) => {
|
||||
const initDir = path.isAbsolute(dirname) ? path.sep : '';
|
||||
dirname.split(path.sep).reduce((parentDir, childDir) => {
|
||||
const curDir = path.resolve(parentDir, childDir);
|
||||
try {
|
||||
if (!fs.existsSync(curDir)) {
|
||||
logger.verbose(`Creating cookie directory at: ${curDir}`);
|
||||
fs.mkdirSync(curDir);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.code !== 'EEXIST') {
|
||||
if (err.code === 'ENOENT') {
|
||||
throw new Error(
|
||||
`ENOENT: No such file or directory, mkdir '${dirname}'. Ensure that path separator is '${
|
||||
os.platform() === 'win32' ? '\\\\' : '/'
|
||||
}'`
|
||||
);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return curDir;
|
||||
}, initDir);
|
||||
};
|
||||
|
||||
export const readCookie = (cookieFile: string): string | null => {
|
||||
if (cookieFile === '') {
|
||||
logger.verbose('No cookie path provided');
|
||||
return null;
|
||||
}
|
||||
|
||||
const exists = fs.existsSync(cookieFile);
|
||||
if (exists) {
|
||||
try {
|
||||
const cookie = fs.readFileSync(cookieFile, 'utf-8');
|
||||
return cookie;
|
||||
} catch (err) {
|
||||
logger.error('Something went wrong while reading cookie: \n' + err);
|
||||
throw new Error(err);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const dirname = path.dirname(cookieFile);
|
||||
createDirectory(dirname);
|
||||
fs.writeFileSync(cookieFile, crypto.randomBytes(64).toString('hex'));
|
||||
|
||||
const cookie = fs.readFileSync(cookieFile, 'utf-8');
|
||||
return cookie;
|
||||
} catch (err) {
|
||||
logger.error('Something went wrong while reading the cookie: \n' + err);
|
||||
throw new Error(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const refreshCookie = (cookieFile: string) => {
|
||||
try {
|
||||
logger.verbose('Refreshing cookie for next authentication');
|
||||
fs.writeFileSync(cookieFile, crypto.randomBytes(64).toString('hex'));
|
||||
} catch (err) {
|
||||
logger.error('Something went wrong while refreshing cookie: \n' + err);
|
||||
throw new Error(err);
|
||||
}
|
||||
};
|
@ -1,10 +1,23 @@
|
||||
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 'api/types/apiTypes';
|
||||
import { logger } from './logger';
|
||||
|
||||
const { serverRuntimeConfig } = getConfig();
|
||||
const { nodeEnv } = serverRuntimeConfig;
|
||||
|
||||
type LndAuthType = {
|
||||
cert: string;
|
||||
macaroon: string;
|
||||
host: string;
|
||||
};
|
||||
|
||||
export const getIp = (req: any) => {
|
||||
if (!req || !req.headers) {
|
||||
return '';
|
||||
@ -17,11 +30,33 @@ export const getIp = (req: any) => {
|
||||
return ip;
|
||||
};
|
||||
|
||||
export const getAuthLnd = (auth: {
|
||||
cert: string;
|
||||
macaroon: string;
|
||||
host: string;
|
||||
}) => {
|
||||
export const getCorrectAuth = (
|
||||
auth: AuthType,
|
||||
context: ContextType
|
||||
): LndAuthType => {
|
||||
if (auth.type === SERVER_ACCOUNT) {
|
||||
const { account } = context;
|
||||
if (!account || account.id !== auth.id)
|
||||
throw new Error('This account is not authenticated');
|
||||
|
||||
const foundAccount = context.accounts.find(a => a.id === account.id);
|
||||
if (!foundAccount) throw new Error('This account does not exist');
|
||||
|
||||
return account;
|
||||
}
|
||||
if (auth.type === SSO_ACCOUNT) {
|
||||
if (!context.ssoVerified)
|
||||
throw new Error('This account is not authenticated');
|
||||
return { ...context.sso };
|
||||
}
|
||||
if (auth.type === CLIENT_ACCOUNT) {
|
||||
const { host, macaroon, cert } = auth;
|
||||
return { host, macaroon, cert };
|
||||
}
|
||||
throw new Error('This account type does not exist');
|
||||
};
|
||||
|
||||
export const getAuthLnd = (auth: LndAuthType) => {
|
||||
const cert = auth.cert || '';
|
||||
const macaroon = auth.macaroon || '';
|
||||
const socket = auth.host || '';
|
||||
|
@ -1,42 +1,39 @@
|
||||
import path from 'path';
|
||||
import { createLogger, format, transports } from 'winston';
|
||||
import getConfig from 'next/config';
|
||||
|
||||
const { serverRuntimeConfig } = getConfig();
|
||||
const { logLevel, nodeEnv } = serverRuntimeConfig;
|
||||
|
||||
const level = nodeEnv === 'development' ? 'debug' : logLevel;
|
||||
|
||||
const combinedFormat =
|
||||
nodeEnv === 'development'
|
||||
? format.combine(
|
||||
format.label({
|
||||
label: path.basename(
|
||||
process && process.mainModule ? process.mainModule.filename : ''
|
||||
),
|
||||
}),
|
||||
format.splat(),
|
||||
format.colorize(),
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
format.printf(
|
||||
(info: any) =>
|
||||
`${info.timestamp} ${info.level} [${info.label}]: ${info.message}`
|
||||
)
|
||||
)
|
||||
: format.combine(
|
||||
format.label({
|
||||
label: path.basename(
|
||||
process && process.mainModule ? process.mainModule.filename : ''
|
||||
),
|
||||
}),
|
||||
format.splat(),
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
format.printf(
|
||||
(info: any) =>
|
||||
`${info.timestamp} ${info.level} [${info.label}]: ${info.message}`
|
||||
)
|
||||
);
|
||||
// nodeEnv === 'development' ?
|
||||
format.combine(
|
||||
format.label({ label: 'THUB' }),
|
||||
format.splat(),
|
||||
format.colorize(),
|
||||
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
format.printf(
|
||||
(info: any) =>
|
||||
`${info.timestamp} ${info.level} [${info.label}]: ${info.message}`
|
||||
)
|
||||
);
|
||||
// : format.combine(
|
||||
// format.label({
|
||||
// label: path.basename(
|
||||
// process && process.mainModule ? process.mainModule.filename : ''
|
||||
// ),
|
||||
// }),
|
||||
// format.splat(),
|
||||
// format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
||||
// format.printf(
|
||||
// (info: any) =>
|
||||
// `${info.timestamp} ${info.level} [${info.label}]: ${info.message}`
|
||||
// )
|
||||
// );
|
||||
|
||||
export const logger = createLogger({
|
||||
level: logLevel,
|
||||
level,
|
||||
format: combinedFormat,
|
||||
transports: [new transports.Console()],
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { getGraphQLRateLimiter } from 'graphql-rate-limit';
|
||||
import { logger } from './logger';
|
||||
|
||||
interface RateConfigProps {
|
||||
[key: string]: {
|
||||
@ -7,7 +8,9 @@ interface RateConfigProps {
|
||||
};
|
||||
}
|
||||
|
||||
export const RateConfig: RateConfigProps = {};
|
||||
export const RateConfig: RateConfigProps = {
|
||||
getMessages: { max: 10, window: '5s' },
|
||||
};
|
||||
|
||||
const rateLimiter = getGraphQLRateLimiter({
|
||||
identifyContext: (ctx: string) => ctx,
|
||||
@ -25,5 +28,8 @@ export const requestLimiter = async (rate: string, field: string) => {
|
||||
},
|
||||
{ max, window }
|
||||
);
|
||||
if (errorMessage) throw new Error(errorMessage);
|
||||
if (errorMessage) {
|
||||
logger.warn(`Rate limit reached for '${field}' from ip ${rate}`);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
};
|
||||
|
@ -5,9 +5,14 @@ import {
|
||||
GraphQLInt,
|
||||
GraphQLNonNull,
|
||||
} from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { CloseChannelType } from '../../types/MutationType';
|
||||
|
||||
@ -25,10 +30,11 @@ export const closeChannel = {
|
||||
targetConfirmations: { type: GraphQLInt },
|
||||
tokensPerVByte: { type: GraphQLInt },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'closeChannel');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const info: CloseChannelProps = await lnCloseChannel({
|
||||
|
@ -5,9 +5,14 @@ import {
|
||||
GraphQLInt,
|
||||
GraphQLNonNull,
|
||||
} from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { OpenChannelType } from '../../types/MutationType';
|
||||
|
||||
@ -25,10 +30,11 @@ export const openChannel = {
|
||||
tokensPerVByte: { type: GraphQLInt },
|
||||
isPrivate: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'openChannel');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const info: OpenChannelProps = await lnOpenChannel({
|
||||
|
@ -1,8 +1,14 @@
|
||||
import { updateRoutingFees } from 'ln-service';
|
||||
import { GraphQLBoolean, GraphQLString, GraphQLInt } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getErrorMsg,
|
||||
getAuthLnd,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
|
||||
export const updateFees = {
|
||||
@ -14,11 +20,12 @@ export const updateFees = {
|
||||
baseFee: { type: GraphQLInt },
|
||||
feeRate: { type: GraphQLInt },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'updateFees');
|
||||
|
||||
const { auth, transactionId, transactionVout, baseFee, feeRate } = params;
|
||||
const { transactionId, transactionVout, baseFee, feeRate } = params;
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
if (!baseFee && !feeRate) {
|
||||
|
@ -6,8 +6,9 @@ import {
|
||||
signMessage,
|
||||
} from 'ln-service';
|
||||
import { GraphQLString, GraphQLNonNull, GraphQLInt } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, to } from '../../../helpers/helpers';
|
||||
import { getAuthLnd, to, getCorrectAuth } from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { createCustomRecords } from '../../../helpers/customRecords';
|
||||
|
||||
@ -21,9 +22,11 @@ export const sendMessage = {
|
||||
tokens: { type: GraphQLInt },
|
||||
maxFee: { type: GraphQLInt },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'sendMessage');
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
if (params.maxFee) {
|
||||
const tokens = Math.max(params.tokens || 100, 100);
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { createInvoice as createInvoiceRequest } from 'ln-service';
|
||||
import { GraphQLNonNull, GraphQLInt } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { InvoiceType } from '../../types/MutationType';
|
||||
|
||||
@ -22,10 +27,11 @@ export const createInvoice = {
|
||||
...defaultParams,
|
||||
amount: { type: new GraphQLNonNull(GraphQLInt) },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'createInvoice');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const invoice: InvoiceProps = await createInvoiceRequest({
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { parsePaymentRequest } from 'ln-service';
|
||||
import { GraphQLString, GraphQLNonNull } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { ParsePaymentType } from '../../types/MutationType';
|
||||
|
||||
@ -36,10 +41,11 @@ export const parsePayment = {
|
||||
...defaultParams,
|
||||
request: { type: new GraphQLNonNull(GraphQLString) },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'parsePayment');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const request: RequestProps = await parsePaymentRequest({
|
||||
|
@ -5,9 +5,14 @@ import {
|
||||
payViaPaymentDetails,
|
||||
} from 'ln-service';
|
||||
import { GraphQLString, GraphQLNonNull, GraphQLInt } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { PayType } from '../../types/MutationType';
|
||||
|
||||
@ -55,9 +60,11 @@ export const pay = {
|
||||
request: { type: new GraphQLNonNull(GraphQLString) },
|
||||
tokens: { type: GraphQLInt },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'pay');
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
let isRequest = false;
|
||||
try {
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { GraphQLNonNull, GraphQLBoolean, GraphQLString } from 'graphql';
|
||||
import { payViaRoutes, createInvoice } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
|
||||
export const payViaRoute = {
|
||||
@ -11,10 +16,11 @@ export const payViaRoute = {
|
||||
...defaultParams,
|
||||
route: { type: new GraphQLNonNull(GraphQLString) },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'payViaRoute');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
let route;
|
||||
try {
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { createChainAddress } from 'ln-service';
|
||||
import { GraphQLString, GraphQLBoolean } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
|
||||
interface AddressProps {
|
||||
@ -15,10 +20,11 @@ export const createAddress = {
|
||||
...defaultParams,
|
||||
nested: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getAddress');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
const format = params.nested ? 'np2wpkh' : 'p2wpkh';
|
||||
|
||||
|
@ -5,9 +5,14 @@ import {
|
||||
GraphQLBoolean,
|
||||
GraphQLInt,
|
||||
} from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { SendToType } from '../../types/MutationType';
|
||||
|
||||
@ -29,10 +34,11 @@ export const sendToAddress = {
|
||||
target: { type: GraphQLInt },
|
||||
sendAll: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'sendToAddress');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
const props = params.fee
|
||||
? { fee_tokens_per_vbyte: params.fee }
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { addPeer as addLnPeer } from 'ln-service';
|
||||
import { GraphQLBoolean, GraphQLString, GraphQLNonNull } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
|
||||
export const addPeer = {
|
||||
@ -13,10 +18,11 @@ export const addPeer = {
|
||||
socket: { type: new GraphQLNonNull(GraphQLString) },
|
||||
isTemporary: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'addPeer');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const success: boolean = await addLnPeer({
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { removePeer as removeLnPeer } from 'ln-service';
|
||||
import { GraphQLBoolean, GraphQLString, GraphQLNonNull } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
|
||||
export const removePeer = {
|
||||
@ -11,10 +16,11 @@ export const removePeer = {
|
||||
...defaultParams,
|
||||
publicKey: { type: new GraphQLNonNull(GraphQLString) },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'removePeer');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const success: boolean = await removeLnPeer({
|
||||
|
42
api/schemas/query/authToken/getAuthToken.ts
Normal file
42
api/schemas/query/authToken/getAuthToken.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { GraphQLString } from 'graphql';
|
||||
import getConfig from 'next/config';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { readCookie, refreshCookie } from 'api/helpers/fileHelpers';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { SSO_ACCOUNT } from 'src/context/AccountContext';
|
||||
import { logger } from 'api/helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
|
||||
const { serverRuntimeConfig } = getConfig();
|
||||
const { cookiePath } = serverRuntimeConfig;
|
||||
|
||||
export const getAuthToken = {
|
||||
type: GraphQLString,
|
||||
args: {
|
||||
cookie: { type: GraphQLString },
|
||||
},
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
const { ip, secret } = context;
|
||||
await requestLimiter(ip, 'getAuthToken');
|
||||
|
||||
if (!params.cookie) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cookiePath === '') {
|
||||
logger.warn('SSO auth not available since no cookie path was provided');
|
||||
return null;
|
||||
}
|
||||
|
||||
const cookieFile = readCookie(cookiePath);
|
||||
|
||||
refreshCookie(cookiePath);
|
||||
|
||||
if (cookieFile === params.cookie) {
|
||||
const token = jwt.sign({ user: SSO_ACCOUNT }, secret);
|
||||
return token;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
};
|
39
api/schemas/query/authToken/getSessionToken.ts
Normal file
39
api/schemas/query/authToken/getSessionToken.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { GraphQLString } from 'graphql';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import AES from 'crypto-js/aes';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
|
||||
export const getSessionToken = {
|
||||
type: GraphQLString,
|
||||
args: {
|
||||
id: { type: GraphQLString },
|
||||
password: { type: GraphQLString },
|
||||
},
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
const { ip, secret } = context;
|
||||
await requestLimiter(ip, 'getSessionToken');
|
||||
|
||||
const account = context.accounts.find(a => a.id === params.id) || null;
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
try {
|
||||
const bytes = AES.decrypt(account.macaroon, params.password);
|
||||
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
||||
const token = jwt.sign(
|
||||
{
|
||||
id: params.id,
|
||||
macaroon: decrypted,
|
||||
cert: account.cert,
|
||||
host: account.host,
|
||||
},
|
||||
secret
|
||||
);
|
||||
return AES.encrypt(token, secret).toString();
|
||||
} catch (error) {
|
||||
throw new Error('Wrong password');
|
||||
}
|
||||
},
|
||||
};
|
7
api/schemas/query/authToken/index.ts
Normal file
7
api/schemas/query/authToken/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { getAuthToken } from './getAuthToken';
|
||||
import { getSessionToken } from './getSessionToken';
|
||||
|
||||
export const authTokenQueries = {
|
||||
getAuthToken,
|
||||
getSessionToken,
|
||||
};
|
@ -1,17 +1,23 @@
|
||||
import { getBackups as getLnBackups } from 'ln-service';
|
||||
import { GraphQLString } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
|
||||
export const getBackups = {
|
||||
type: GraphQLString,
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getBackups');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const backups = await getLnBackups({
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { recoverFundsFromChannels } from 'ln-service';
|
||||
import { GraphQLNonNull, GraphQLString, GraphQLBoolean } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
|
||||
interface BackupProps {
|
||||
@ -15,10 +20,11 @@ export const recoverFunds = {
|
||||
...defaultParams,
|
||||
backup: { type: new GraphQLNonNull(GraphQLString) },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'recoverFunds');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
let backupObj: BackupProps = { backup: '' };
|
||||
try {
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { verifyBackups as verifyLnBackups } from 'ln-service';
|
||||
import { GraphQLNonNull, GraphQLString, GraphQLBoolean } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
|
||||
interface BackupProps {
|
||||
@ -16,10 +21,11 @@ export const verifyBackups = {
|
||||
...defaultParams,
|
||||
backup: { type: new GraphQLNonNull(GraphQLString) },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'verifyBackups');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
let backupObj: BackupProps = { backup: '', channels: [] };
|
||||
try {
|
||||
|
@ -3,9 +3,14 @@ import {
|
||||
getPendingChainBalance as getPending,
|
||||
} from 'ln-service';
|
||||
import { GraphQLInt } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
|
||||
interface ChainBalanceProps {
|
||||
@ -19,10 +24,11 @@ interface PendingChainBalanceProps {
|
||||
export const getChainBalance = {
|
||||
type: GraphQLInt,
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'chainBalance');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const value: ChainBalanceProps = await getBalance({
|
||||
@ -39,10 +45,11 @@ export const getChainBalance = {
|
||||
export const getPendingChainBalance = {
|
||||
type: GraphQLInt,
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'pendingChainBalance');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const pendingValue: PendingChainBalanceProps = await getPending({
|
||||
|
@ -1,9 +1,14 @@
|
||||
import { GraphQLList } from 'graphql';
|
||||
import { getChainTransactions as getLnChainTransactions } from 'ln-service';
|
||||
import { sortBy } from 'underscore';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { GetChainTransactionsType } from '../../types/QueryType';
|
||||
|
||||
@ -25,10 +30,11 @@ interface TransactionsProps {
|
||||
export const getChainTransactions = {
|
||||
type: new GraphQLList(GetChainTransactionsType),
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'chainTransactions');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const transactionList: TransactionsProps = await getLnChainTransactions({
|
||||
|
@ -5,9 +5,14 @@ import {
|
||||
GraphQLString,
|
||||
GraphQLList,
|
||||
} from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
|
||||
const GetUtxosType = new GraphQLObjectType({
|
||||
@ -26,10 +31,11 @@ const GetUtxosType = new GraphQLObjectType({
|
||||
export const getUtxos = {
|
||||
type: new GraphQLList(GetUtxosType),
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getUtxos');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const { utxos } = await getLnUtxos({ lnd });
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { getChannelBalance as getLnChannelBalance } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { ChannelBalanceType } from '../../types/QueryType';
|
||||
|
||||
@ -13,10 +18,11 @@ interface ChannelBalanceProps {
|
||||
export const getChannelBalance = {
|
||||
type: ChannelBalanceType,
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'channelBalance');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const channelBalance: ChannelBalanceProps = await getLnChannelBalance({
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { getFeeRates, getChannels, getNode } from 'ln-service';
|
||||
import { GraphQLList } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { ChannelFeeType } from '../../types/QueryType';
|
||||
|
||||
@ -34,10 +39,11 @@ interface NodeProps {
|
||||
export const getChannelFees = {
|
||||
type: new GraphQLList(ChannelFeeType),
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'channelFees');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const channels: GetChannelsProps = await getChannels({ lnd });
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { getChannels } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { ChannelReportType } from '../../types/QueryType';
|
||||
|
||||
@ -17,10 +22,11 @@ interface ChannelsProps {
|
||||
export const getChannelReport = {
|
||||
type: ChannelReportType,
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'channelReport');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const channels: GetChannelsProps = await getChannels({ lnd });
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { GraphQLList, GraphQLBoolean } from 'graphql';
|
||||
import { getChannels as getLnChannels, getNode } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { ChannelType } from '../../types/QueryType';
|
||||
|
||||
@ -42,10 +47,11 @@ export const getChannels = {
|
||||
...defaultParams,
|
||||
active: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'channels');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const channelList: ChannelListProps = await getLnChannels({
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { GraphQLList, GraphQLString } from 'graphql';
|
||||
import { getClosedChannels as getLnClosedChannels, getNode } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { ClosedChannelType } from '../../types/QueryType';
|
||||
@ -34,10 +39,11 @@ export const getClosedChannels = {
|
||||
...defaultParams,
|
||||
type: { type: GraphQLString },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'closedChannels');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const closedChannels: ChannelListProps = await getLnClosedChannels({
|
||||
|
@ -3,9 +3,14 @@ import {
|
||||
getNode,
|
||||
} from 'ln-service';
|
||||
import { GraphQLList } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { PendingChannelType } from '../../types/QueryType';
|
||||
|
||||
@ -33,10 +38,11 @@ interface PendingChannelProps {
|
||||
export const getPendingChannels = {
|
||||
type: new GraphQLList(PendingChannelType),
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'pendingChannels');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const pendingChannels: PendingChannelListProps = await getLnPendingChannels(
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { GraphQLString, GraphQLBoolean } from 'graphql';
|
||||
import { getInvoices, verifyMessage } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, to } from '../../../helpers/helpers';
|
||||
import { getAuthLnd, to, getCorrectAuth } from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { decodeMessage } from '../../../helpers/customRecords';
|
||||
import { GetMessagesType } from '../../types/QueryType';
|
||||
@ -14,10 +15,11 @@ export const getMessages = {
|
||||
initialize: { type: GraphQLBoolean },
|
||||
lastMessage: { type: GraphQLString },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getMessages');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
const invoiceList = await to(
|
||||
getInvoices({
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { GraphQLBoolean } from 'graphql';
|
||||
import fetch from 'node-fetch';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { BitcoinFeeType } from '../../types/QueryType';
|
||||
@ -10,7 +11,7 @@ export const getBitcoinFees = {
|
||||
args: {
|
||||
logger: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'bitcoinFee');
|
||||
|
||||
try {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { GraphQLString, GraphQLBoolean } from 'graphql';
|
||||
import fetch from 'node-fetch';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { appUrls } from '../../../utils/appUrls';
|
||||
@ -12,7 +13,7 @@ export const getBitcoinPrice = {
|
||||
type: GraphQLString,
|
||||
},
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'bitcoinPrice');
|
||||
|
||||
try {
|
||||
|
@ -5,9 +5,14 @@ import {
|
||||
} from 'ln-service';
|
||||
import { differenceInHours, differenceInCalendarDays } from 'date-fns';
|
||||
import { groupBy } from 'underscore';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { reduceInOutArray } from '../report/Helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { InOutType } from '../../types/QueryType';
|
||||
@ -19,10 +24,11 @@ export const getInOut = {
|
||||
...defaultParams,
|
||||
time: { type: GraphQLString },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getInOut');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
const endDate = new Date();
|
||||
let periods = 7;
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { pay as payRequest } from 'ln-service';
|
||||
import { GraphQLBoolean } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
|
||||
@ -10,10 +15,11 @@ export const adminCheck = {
|
||||
args: {
|
||||
...defaultParams,
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'adminCheck');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
await payRequest({
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { decodePaymentRequest } from 'ln-service';
|
||||
import { GraphQLString, GraphQLNonNull } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { DecodeType } from '../../types/QueryType';
|
||||
|
||||
@ -32,10 +37,11 @@ export const decodeRequest = {
|
||||
...defaultParams,
|
||||
request: { type: new GraphQLNonNull(GraphQLString) },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'decode');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const decode: DecodeProps = await decodePaymentRequest({
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { GraphQLString, GraphQLNonNull, GraphQLBoolean } from 'graphql';
|
||||
import { getNode as getLnNode } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { PartnerNodeType } from '../../types/QueryType';
|
||||
@ -14,10 +19,11 @@ export const getNode = {
|
||||
publicKey: { type: new GraphQLNonNull(GraphQLString) },
|
||||
withoutChannels: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'closedChannels');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const nodeInfo = await getLnNode({
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { getNetworkInfo as getLnNetworkInfo } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { NetworkInfoType } from '../../types/QueryType';
|
||||
@ -20,10 +25,11 @@ interface NetworkInfoProps {
|
||||
export const getNetworkInfo = {
|
||||
type: NetworkInfoType,
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'networkInfo');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const info: NetworkInfoProps = await getLnNetworkInfo({ lnd });
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { getWalletInfo, getClosedChannels } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { NodeInfoType } from '../../types/QueryType';
|
||||
|
||||
@ -25,10 +30,11 @@ interface NodeInfoProps {
|
||||
export const getNodeInfo = {
|
||||
type: NodeInfoType,
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'nodeInfo');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const info: NodeInfoProps = await getWalletInfo({
|
||||
|
@ -1,16 +1,18 @@
|
||||
import { getWalletVersion } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, to } from '../../../helpers/helpers';
|
||||
import { getAuthLnd, to, getCorrectAuth } from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { WalletInfoType } from '../../types/QueryType';
|
||||
|
||||
export const getWalletInfo = {
|
||||
type: WalletInfoType,
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getWalletInfo');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
return await to(
|
||||
getWalletVersion({
|
||||
|
@ -1,6 +1,7 @@
|
||||
import fetch from 'node-fetch';
|
||||
import { GraphQLList } from 'graphql';
|
||||
import getConfig from 'next/config';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { appUrls } from '../../../utils/appUrls';
|
||||
@ -12,7 +13,7 @@ const { hodlKey } = serverRuntimeConfig;
|
||||
export const getCountries = {
|
||||
type: new GraphQLList(HodlCountryType),
|
||||
args: {},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getCountries');
|
||||
|
||||
const headers = {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import fetch from 'node-fetch';
|
||||
import { GraphQLList } from 'graphql';
|
||||
import getConfig from 'next/config';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { appUrls } from '../../../utils/appUrls';
|
||||
@ -12,7 +13,7 @@ const { hodlKey } = serverRuntimeConfig;
|
||||
export const getCurrencies = {
|
||||
type: new GraphQLList(HodlCurrencyType),
|
||||
args: {},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getCurrencies');
|
||||
|
||||
const headers = {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { GraphQLList, GraphQLString } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { appUrls } from '../../../utils/appUrls';
|
||||
@ -18,7 +19,7 @@ export const getOffers = {
|
||||
args: {
|
||||
filter: { type: GraphQLString },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getOffers');
|
||||
|
||||
let queryParams = defaultQuery;
|
||||
|
@ -11,6 +11,8 @@ import { messageQueries } from './message';
|
||||
import { chainQueries } from './chain';
|
||||
import { hodlQueries } from './hodlhodl';
|
||||
import { chatQueries } from './chat';
|
||||
import { authTokenQueries } from './authToken';
|
||||
import { serverQueries } from './server';
|
||||
|
||||
export const query = {
|
||||
...channelQueries,
|
||||
@ -26,4 +28,6 @@ export const query = {
|
||||
...chainQueries,
|
||||
...hodlQueries,
|
||||
...chatQueries,
|
||||
...authTokenQueries,
|
||||
...serverQueries,
|
||||
};
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { signMessage as signLnMessage } from 'ln-service';
|
||||
import { GraphQLString, GraphQLNonNull } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
|
||||
export const signMessage = {
|
||||
@ -11,10 +16,11 @@ export const signMessage = {
|
||||
...defaultParams,
|
||||
message: { type: new GraphQLNonNull(GraphQLString) },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'signMessage');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const message: { signature: string } = await signLnMessage({
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { verifyMessage as verifyLnMessage } from 'ln-service';
|
||||
import { GraphQLString, GraphQLNonNull } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
|
||||
export const verifyMessage = {
|
||||
@ -12,10 +17,11 @@ export const verifyMessage = {
|
||||
message: { type: new GraphQLNonNull(GraphQLString) },
|
||||
signature: { type: new GraphQLNonNull(GraphQLString) },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'verifyMessage');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const message: { signed_by: string } = await verifyLnMessage({
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { GraphQLList } from 'graphql';
|
||||
import { getPeers as getLnPeers, getNode } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { PeerType } from '../../types/QueryType';
|
||||
@ -22,10 +27,11 @@ interface PeerProps {
|
||||
export const getPeers = {
|
||||
type: new GraphQLList(PeerType),
|
||||
args: defaultParams,
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getPeers');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
try {
|
||||
const { peers }: { peers: PeerProps[] } = await getLnPeers({
|
||||
|
@ -7,9 +7,14 @@ import {
|
||||
} from 'ln-service';
|
||||
import { subHours, subDays } from 'date-fns';
|
||||
import { sortBy } from 'underscore';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { countArray, countRoutes } from './Helpers';
|
||||
import { ForwardCompleteProps } from './ForwardReport.interface';
|
||||
@ -31,10 +36,11 @@ export const getForwardChannelsReport = {
|
||||
order: { type: GraphQLString },
|
||||
type: { type: GraphQLString },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'forwardChannels');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
let startDate = new Date();
|
||||
const endDate = new Date();
|
||||
|
@ -7,9 +7,14 @@ import {
|
||||
differenceInHours,
|
||||
differenceInCalendarDays,
|
||||
} from 'date-fns';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { reduceForwardArray } from './Helpers';
|
||||
import { ForwardCompleteProps } from './ForwardReport.interface';
|
||||
@ -20,10 +25,11 @@ export const getForwardReport = {
|
||||
...defaultParams,
|
||||
time: { type: GraphQLString },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'forwardReport');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
let startDate = new Date();
|
||||
const endDate = new Date();
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { GraphQLNonNull, GraphQLString, GraphQLInt } from 'graphql';
|
||||
import { getRouteToDestination, getWalletInfo } from 'ln-service';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
|
||||
export const getRoutes = {
|
||||
@ -14,10 +19,11 @@ export const getRoutes = {
|
||||
tokens: { type: new GraphQLNonNull(GraphQLInt) },
|
||||
maxFee: { type: GraphQLInt },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'getRoutes');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
const { public_key } = await getWalletInfo({ lnd });
|
||||
|
||||
|
32
api/schemas/query/server/getServerAccounts.ts
Normal file
32
api/schemas/query/server/getServerAccounts.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { GraphQLList } from 'graphql';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { ServerAccountType } from 'api/schemas/types/QueryType';
|
||||
import { SSO_ACCOUNT } from 'src/context/AccountContext';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
|
||||
export const getServerAccounts = {
|
||||
type: new GraphQLList(ServerAccountType),
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
const { ip, accounts, account, sso, ssoVerified } = context;
|
||||
await requestLimiter(ip, 'getServerAccounts');
|
||||
|
||||
const { macaroon, cert, host } = sso;
|
||||
let ssoAccount = null;
|
||||
if (macaroon && cert && host && ssoVerified) {
|
||||
ssoAccount = {
|
||||
name: 'SSO Account',
|
||||
id: SSO_ACCOUNT,
|
||||
loggedIn: true,
|
||||
};
|
||||
}
|
||||
|
||||
const currentId = account?.id;
|
||||
const withStatus =
|
||||
accounts?.map(a => ({
|
||||
...a,
|
||||
loggedIn: a.id === currentId,
|
||||
})) || [];
|
||||
|
||||
return ssoAccount ? [...withStatus, ssoAccount] : withStatus;
|
||||
},
|
||||
};
|
5
api/schemas/query/server/index.ts
Normal file
5
api/schemas/query/server/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { getServerAccounts } from './getServerAccounts';
|
||||
|
||||
export const serverQueries = {
|
||||
getServerAccounts,
|
||||
};
|
@ -7,9 +7,14 @@ import {
|
||||
} from 'ln-service';
|
||||
import { sortBy } from 'underscore';
|
||||
import { subHours, subDays, subMonths, subYears } from 'date-fns';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { ForwardCompleteProps } from '../report/ForwardReport.interface';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { GetForwardType } from '../../types/QueryType';
|
||||
@ -29,10 +34,11 @@ export const getForwards = {
|
||||
...defaultParams,
|
||||
time: { type: GraphQLString },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'forwards');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
let startDate = new Date();
|
||||
const endDate = new Date();
|
||||
|
@ -2,9 +2,14 @@ import { GraphQLString } from 'graphql';
|
||||
import { getPayments, getInvoices, getNode } from 'ln-service';
|
||||
import { compareDesc } from 'date-fns';
|
||||
import { sortBy } from 'underscore';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers';
|
||||
import {
|
||||
getAuthLnd,
|
||||
getErrorMsg,
|
||||
getCorrectAuth,
|
||||
} from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { GetResumeType } from '../../types/QueryType';
|
||||
import { PaymentsProps, InvoicesProps, NodeProps } from './resume.interface';
|
||||
@ -15,10 +20,11 @@ export const getResume = {
|
||||
...defaultParams,
|
||||
token: { type: GraphQLString },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
resolve: async (_: undefined, params: any, context: ContextType) => {
|
||||
await requestLimiter(context.ip, 'payments');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
const auth = getCorrectAuth(params.auth, context);
|
||||
const lnd = getAuthLnd(auth);
|
||||
|
||||
let payments;
|
||||
let invoices;
|
||||
|
@ -1,9 +1,15 @@
|
||||
import { GraphQLInputObjectType, GraphQLString } from 'graphql';
|
||||
import { GraphQLInputObjectType, GraphQLString, GraphQLNonNull } from 'graphql';
|
||||
|
||||
export const AuthType = new GraphQLInputObjectType({
|
||||
name: 'authType',
|
||||
fields: () => {
|
||||
return {
|
||||
type: {
|
||||
type: new GraphQLNonNull(GraphQLString),
|
||||
},
|
||||
id: {
|
||||
type: GraphQLString,
|
||||
},
|
||||
host: {
|
||||
type: GraphQLString,
|
||||
},
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
GraphQLInt,
|
||||
GraphQLBoolean,
|
||||
GraphQLList,
|
||||
GraphQLNonNull,
|
||||
} from 'graphql';
|
||||
|
||||
export const ChannelBalanceType = new GraphQLObjectType({
|
||||
@ -328,3 +329,12 @@ export const GetMessagesType = new GraphQLObjectType({
|
||||
messages: { type: new GraphQLList(MessagesType) },
|
||||
}),
|
||||
});
|
||||
|
||||
export const ServerAccountType = new GraphQLObjectType({
|
||||
name: 'serverAccountType',
|
||||
fields: () => ({
|
||||
name: { type: new GraphQLNonNull(GraphQLString) },
|
||||
id: { type: new GraphQLNonNull(GraphQLString) },
|
||||
loggedIn: { type: new GraphQLNonNull(GraphQLBoolean) },
|
||||
}),
|
||||
});
|
||||
|
26
api/types/apiTypes.ts
Normal file
26
api/types/apiTypes.ts
Normal file
@ -0,0 +1,26 @@
|
||||
type SSOType = {
|
||||
macaroon: string | null;
|
||||
cert: string | null;
|
||||
host: string | null;
|
||||
};
|
||||
|
||||
type VerifiedAccountType = {
|
||||
id: string;
|
||||
} & SSOType;
|
||||
|
||||
type AccountType = {
|
||||
name: string;
|
||||
id: string;
|
||||
host: string;
|
||||
macaroon: string;
|
||||
cert: string | null;
|
||||
};
|
||||
|
||||
export type ContextType = {
|
||||
ip: string;
|
||||
secret: string;
|
||||
ssoVerified: boolean;
|
||||
account: VerifiedAccountType | null;
|
||||
sso: SSOType;
|
||||
accounts: AccountType[];
|
||||
};
|
10
codegen.yml
10
codegen.yml
@ -2,12 +2,18 @@ overwrite: true
|
||||
schema: 'http://localhost:3000/api/v1'
|
||||
documents: 'src/graphql/**/*.ts'
|
||||
generates:
|
||||
src/generated/graphql.tsx:
|
||||
src/graphql/types.ts:
|
||||
- typescript
|
||||
src/graphql/:
|
||||
preset: near-operation-file
|
||||
presetConfig:
|
||||
baseTypesPath: types.ts
|
||||
extension: .generated.tsx
|
||||
folder: __generated__
|
||||
config:
|
||||
withComponent: false
|
||||
withHOC: false
|
||||
withHooks: true
|
||||
plugins:
|
||||
- 'typescript'
|
||||
- 'typescript-operations'
|
||||
- 'typescript-react-apollo'
|
||||
|
@ -8,8 +8,13 @@ module.exports = withBundleAnalyzer({
|
||||
assetPrefix: process.env.BASE_PATH || '',
|
||||
serverRuntimeConfig: {
|
||||
nodeEnv: process.env.NODE_ENV || 'development',
|
||||
logLevel: process.env.LOG_LEVEL || 'silly',
|
||||
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 || '',
|
||||
macaroonPath: process.env.SSO_MACAROON_PATH || '',
|
||||
accountConfigPath: process.env.ACCOUNT_CONFIG_PATH || '',
|
||||
},
|
||||
publicRuntimeConfig: {
|
||||
nodeEnv: process.env.NODE_ENV || 'development',
|
||||
|
40
package.json
40
package.json
@ -4,6 +4,7 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"bs": "yarn build && yarn start",
|
||||
"dev": "cross-env NODE_OPTIONS='--insecure-http-parser' next",
|
||||
"dev:compatible": "next",
|
||||
"build": "next build",
|
||||
@ -25,31 +26,35 @@
|
||||
"build:32": "docker build --no-cache -f arm32v7.Dockerfile -t apotdevin/thunderhub:test-arm32v7 .",
|
||||
"build:64": "docker build -f arm64v8.Dockerfile -t apotdevin/thunderhub:test-arm64v8 .",
|
||||
"build:manifest": "docker manifest create apotdevin/thunderhub:test apotdevin/thunderhub:test-amd64 apotdevin/thunderhub:test-arm32v7 apotdevin/thunderhub:test-arm64v8",
|
||||
"upgrade-latest": "yarn upgrade-interactive --latest"
|
||||
"upgrade-latest": "yarn upgrade-interactive --latest",
|
||||
"tsc": "tsc"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apollo/react-hooks": "^3.1.5",
|
||||
"apollo-boost": "^0.4.8",
|
||||
"@graphql-codegen/near-operation-file-preset": "^1.13.5",
|
||||
"apollo-boost": "^0.4.9",
|
||||
"apollo-server-micro": "^2.13.1",
|
||||
"base64url": "^3.0.1",
|
||||
"cookie": "^0.4.1",
|
||||
"crypto-js": "^4.0.0",
|
||||
"date-fns": "^2.13.0",
|
||||
"date-fns": "^2.14.0",
|
||||
"graphql": "^15.0.0",
|
||||
"graphql-iso-date": "^3.6.1",
|
||||
"graphql-rate-limit": "^2.0.1",
|
||||
"graphql-tag": "^2.10.3",
|
||||
"intersection-observer": "^0.10.0",
|
||||
"js-cookie": "^2.2.1",
|
||||
"ln-service": "^48.0.5",
|
||||
"js-yaml": "^3.13.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"ln-service": "^48.1.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.groupby": "^4.6.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"micro-cors": "^0.1.1",
|
||||
"next": "^9.4.0",
|
||||
"next": "^9.4.1",
|
||||
"next-with-apollo": "^5.0.1",
|
||||
"numeral": "^2.0.6",
|
||||
"qrcode.react": "^1.0.0",
|
||||
@ -61,14 +66,14 @@
|
||||
"react-qr-reader": "^2.2.1",
|
||||
"react-spinners": "^0.8.3",
|
||||
"react-spring": "^8.0.27",
|
||||
"react-toastify": "^6.0.1",
|
||||
"react-toastify": "^6.0.4",
|
||||
"react-tooltip": "^4.2.6",
|
||||
"styled-components": "^5.1.0",
|
||||
"styled-react-modal": "^2.0.1",
|
||||
"styled-theming": "^2.2.0",
|
||||
"underscore": "^1.10.2",
|
||||
"uuid": "^8.0.0",
|
||||
"victory": "^34.2.2",
|
||||
"victory": "^34.3.6",
|
||||
"winston": "^3.2.1",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
@ -82,19 +87,20 @@
|
||||
"@graphql-codegen/typescript-operations": "^1.13.5",
|
||||
"@graphql-codegen/typescript-react-apollo": "1.13.5",
|
||||
"@graphql-codegen/typescript-resolvers": "1.13.5",
|
||||
"@next/bundle-analyzer": "^9.4.0",
|
||||
"@next/bundle-analyzer": "^9.4.1",
|
||||
"@storybook/addon-actions": "^5.3.18",
|
||||
"@storybook/addon-knobs": "^5.3.18",
|
||||
"@storybook/addon-viewport": "^5.3.18",
|
||||
"@storybook/react": "^5.3.18",
|
||||
"@testing-library/jest-dom": "^5.7.0",
|
||||
"@testing-library/react": "^10.0.4",
|
||||
"@types/node": "^13.13.5",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/node": "^14.0.1",
|
||||
"@types/react": "^16.9.35",
|
||||
"@types/styled-components": "^5.1.0",
|
||||
"@types/styled-theming": "^2.2.2",
|
||||
"@typescript-eslint/eslint-plugin": "^2.32.0",
|
||||
"@typescript-eslint/parser": "^2.32.0",
|
||||
"@types/styled-theming": "^2.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^2.34.0",
|
||||
"@typescript-eslint/parser": "^2.34.0",
|
||||
"babel-jest": "^26.0.1",
|
||||
"babel-loader": "^8.1.0",
|
||||
"babel-plugin-inline-react-svg": "^1.1.1",
|
||||
@ -105,18 +111,18 @@
|
||||
"eslint": "^7.0.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-jest": "^23.10.0",
|
||||
"eslint-plugin-jest": "^23.13.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-react": "^7.19.0",
|
||||
"eslint-plugin-react-hooks": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.20.0",
|
||||
"eslint-plugin-react-hooks": "^4.0.2",
|
||||
"fast-diff": "^1.2.0",
|
||||
"husky": "^4.2.5",
|
||||
"jest": "^26.0.1",
|
||||
"lint-staged": "^10.2.2",
|
||||
"lint-staged": "^10.2.4",
|
||||
"prettier": "^2.0.5",
|
||||
"standard-version": "^8.0.0",
|
||||
"typescript": "^3.8.3"
|
||||
"typescript": "^3.9.2"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
|
@ -5,6 +5,12 @@ import { ApolloProvider } from '@apollo/react-hooks';
|
||||
import { useRouter } from 'next/router';
|
||||
import { toast } from 'react-toastify';
|
||||
import Head from 'next/head';
|
||||
import { AuthSSOCheck } from 'src/components/accounts/AuthSSOCheck';
|
||||
import { getUrlParam } from 'src/utils/url';
|
||||
import { ChatInit } from 'src/components/chat/ChatInit';
|
||||
import { ServerAccounts } from 'src/components/accounts/ServerAccounts';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { LoadingCard } from 'src/components/loading/LoadingCard';
|
||||
import { ContextProvider } from '../src/context/ContextProvider';
|
||||
import { useConfigState, ConfigProvider } from '../src/context/ConfigContext';
|
||||
import { GlobalStyles } from '../src/styles/GlobalStyle';
|
||||
@ -18,7 +24,6 @@ import 'react-toastify/dist/ReactToastify.css';
|
||||
import { PageWrapper, HeaderBodyWrapper } from '../src/layouts/Layout.styled';
|
||||
import { useStatusState } from '../src/context/StatusContext';
|
||||
import { ChatFetcher } from '../src/components/chat/ChatFetcher';
|
||||
import { ChatInit } from '../src/components/chat/ChatInit';
|
||||
import { parseCookies } from '../src/utils/cookies';
|
||||
|
||||
toast.configure({ draggable: false, pauseOnFocusLoss: false });
|
||||
@ -27,6 +32,7 @@ const Wrapper: React.FC = ({ children }) => {
|
||||
const { theme } = useConfigState();
|
||||
const { pathname } = useRouter();
|
||||
const { connected } = useStatusState();
|
||||
const { hasAccount } = useAccountState();
|
||||
|
||||
const isRoot = pathname === '/';
|
||||
|
||||
@ -34,6 +40,9 @@ const Wrapper: React.FC = ({ children }) => {
|
||||
if (isRoot) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
if (hasAccount === 'false') {
|
||||
return <LoadingCard noCard={true} />;
|
||||
}
|
||||
return <GridWrapper>{children}</GridWrapper>;
|
||||
};
|
||||
|
||||
@ -63,7 +72,13 @@ const Wrapper: React.FC = ({ children }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const App = ({ Component, pageProps, apollo, initialConfig }: any) => (
|
||||
const App = ({
|
||||
Component,
|
||||
pageProps,
|
||||
apollo,
|
||||
initialConfig,
|
||||
cookieParam,
|
||||
}: any) => (
|
||||
<>
|
||||
<Head>
|
||||
<title>ThunderHub - Lightning Node Manager</title>
|
||||
@ -71,6 +86,8 @@ const App = ({ Component, pageProps, apollo, initialConfig }: any) => (
|
||||
<ApolloProvider client={apollo}>
|
||||
<ConfigProvider initialConfig={initialConfig}>
|
||||
<ContextProvider>
|
||||
<AuthSSOCheck cookieParam={cookieParam} />
|
||||
<ServerAccounts />
|
||||
<Wrapper>
|
||||
<Component {...pageProps} />
|
||||
</Wrapper>
|
||||
@ -81,17 +98,21 @@ const App = ({ Component, pageProps, apollo, initialConfig }: any) => (
|
||||
);
|
||||
|
||||
App.getInitialProps = async props => {
|
||||
const cookieParam = getUrlParam(props.router?.query?.token);
|
||||
const cookies = parseCookies(props.ctx.req);
|
||||
const defaultState = {};
|
||||
|
||||
if (!cookies?.config) {
|
||||
return { initialConfig: {} };
|
||||
return { initialConfig: {}, ...defaultState };
|
||||
}
|
||||
try {
|
||||
const config = JSON.parse(cookies.config);
|
||||
return {
|
||||
initialConfig: config,
|
||||
initialConfig: { ...config, ...defaultState },
|
||||
cookieParam,
|
||||
};
|
||||
} catch (error) {
|
||||
return { initialConfig: {} };
|
||||
return { initialConfig: {}, cookieParam, ...defaultState };
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,21 +1,94 @@
|
||||
import crypto from 'crypto';
|
||||
import { ApolloServer } from 'apollo-server-micro';
|
||||
import { thunderHubSchema } from 'api/schemas';
|
||||
import { getIp } from 'api/helpers/helpers';
|
||||
import getConfig from 'next/config';
|
||||
import Cors from 'micro-cors';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { logger } from 'api/helpers/logger';
|
||||
import {
|
||||
readMacaroons,
|
||||
readFile,
|
||||
readCookie,
|
||||
getAccounts,
|
||||
} from 'api/helpers/fileHelpers';
|
||||
import { ContextType } from 'api/types/apiTypes';
|
||||
import AES from 'crypto-js/aes';
|
||||
import CryptoJS from 'crypto-js';
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { apiBaseUrl } = publicRuntimeConfig;
|
||||
const { publicRuntimeConfig, serverRuntimeConfig } = getConfig();
|
||||
const { apiBaseUrl, nodeEnv } = publicRuntimeConfig;
|
||||
const {
|
||||
cookiePath,
|
||||
macaroonPath,
|
||||
lnCertPath,
|
||||
lnServerUrl,
|
||||
accountConfigPath,
|
||||
} = serverRuntimeConfig;
|
||||
|
||||
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 cors = Cors({
|
||||
origin: true,
|
||||
allowCredentials: true,
|
||||
});
|
||||
|
||||
const apolloServer = new ApolloServer({
|
||||
schema: thunderHubSchema,
|
||||
context: async ({ req }: any) => {
|
||||
context: async ({ req }) => {
|
||||
const ip = getIp(req);
|
||||
return { ip };
|
||||
|
||||
let ssoVerified = false;
|
||||
if (req?.cookies?.SSOAuth) {
|
||||
try {
|
||||
jwt.verify(req.cookies.SSOAuth, secret);
|
||||
ssoVerified = true;
|
||||
} catch (error) {
|
||||
logger.verbose('SSO authentication cookie failed');
|
||||
}
|
||||
}
|
||||
|
||||
let account = null;
|
||||
if (req?.cookies?.AccountAuth) {
|
||||
try {
|
||||
const bytes = AES.decrypt(req.cookies.AccountAuth, secret);
|
||||
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
||||
const cookieAccount = jwt.verify(decrypted, secret);
|
||||
const accountId = cookieAccount['id'] || '';
|
||||
const accountMacaroon = cookieAccount['macaroon'] || '';
|
||||
const accountCert = cookieAccount['cert'] || '';
|
||||
const accountHost = cookieAccount['host'] || '';
|
||||
|
||||
account = {
|
||||
id: accountId,
|
||||
host: accountHost,
|
||||
cert: accountCert,
|
||||
macaroon: accountMacaroon,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.verbose('Account authentication cookie failed');
|
||||
}
|
||||
}
|
||||
|
||||
const context: ContextType = {
|
||||
ip,
|
||||
secret,
|
||||
ssoVerified,
|
||||
account,
|
||||
sso: { macaroon: ssoMacaroon, cert: ssoCert, host: lnServerUrl || null },
|
||||
accounts: accountConfig,
|
||||
};
|
||||
|
||||
return context;
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import sortBy from 'lodash.sortby';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { useGetChannelsQuery } from 'src/graphql/queries/__generated__/getChannels.generated';
|
||||
import {
|
||||
CardWithTitle,
|
||||
Card,
|
||||
@ -11,7 +13,6 @@ import {
|
||||
NoWrapTitle,
|
||||
SingleLine,
|
||||
} from '../src/components/generic/Styled';
|
||||
import { useAccount } from '../src/context/AccountContext';
|
||||
import { getErrorContent } from '../src/utils/error';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
import { getPercent } from '../src/utils/helpers';
|
||||
@ -21,11 +22,10 @@ import { BalanceRoute } from '../src/views/balance/BalanceRoute';
|
||||
import { Price } from '../src/components/price/Price';
|
||||
import { useStatusState } from '../src/context/StatusContext';
|
||||
import { Text } from '../src/components/typography/Styled';
|
||||
import { useGetChannelsQuery } from '../src/generated/graphql';
|
||||
|
||||
const BalanceView = () => {
|
||||
const { minorVersion } = useStatusState();
|
||||
const { auth } = useAccount();
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const [outgoing, setOutgoing] = useState<{ id: string } | null>();
|
||||
const [incoming, setIncoming] = useState();
|
||||
|
@ -1,4 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { useGetChannelAmountInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
|
||||
import { Channels } from '../src/views/channels/channels/Channels';
|
||||
import { PendingChannels } from '../src/views/channels/pendingChannels/PendingChannels';
|
||||
import { ClosedChannels } from '../src/views/channels/closedChannels/ClosedChannels';
|
||||
@ -9,10 +11,8 @@ import {
|
||||
SingleLine,
|
||||
ColorButton,
|
||||
} from '../src/components/generic/Styled';
|
||||
import { useAccount } from '../src/context/AccountContext';
|
||||
import { useConfigState } from '../src/context/ConfigContext';
|
||||
import { textColorMap } from '../src/styles/Themes';
|
||||
import { useGetChannelAmountInfoQuery } from '../src/generated/graphql';
|
||||
|
||||
const ChannelView = () => {
|
||||
const [view, setView] = useState<number>(1);
|
||||
@ -23,7 +23,7 @@ const ChannelView = () => {
|
||||
});
|
||||
|
||||
const { theme } = useConfigState();
|
||||
const { auth } = useAccount();
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const { data } = useGetChannelAmountInfoQuery({
|
||||
skip: !auth,
|
||||
|
@ -1,6 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ChevronRight, X } 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 {
|
||||
Card,
|
||||
CardWithTitle,
|
||||
@ -13,7 +16,6 @@ import {
|
||||
ResponsiveLine,
|
||||
NoWrapTitle,
|
||||
} from '../src/components/generic/Styled';
|
||||
import { useAccount } from '../src/context/AccountContext';
|
||||
import { getErrorContent } from '../src/utils/error';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
import { FeeCard } from '../src/views/fees/FeeCard';
|
||||
@ -21,10 +23,6 @@ import { SecureButton } from '../src/components/buttons/secureButton/SecureButto
|
||||
import { AdminSwitch } from '../src/components/adminSwitch/AdminSwitch';
|
||||
import { ColorButton } from '../src/components/buttons/colorButton/ColorButton';
|
||||
import { Input } from '../src/components/input/Input';
|
||||
import {
|
||||
useChannelFeesQuery,
|
||||
useUpdateFeesMutation,
|
||||
} from '../src/generated/graphql';
|
||||
|
||||
const FeesView = () => {
|
||||
const [indexOpen, setIndexOpen] = useState(0);
|
||||
@ -32,7 +30,7 @@ const FeesView = () => {
|
||||
const [baseFee, setBaseFee] = useState(0);
|
||||
const [feeRate, setFeeRate] = useState(0);
|
||||
|
||||
const { auth } = useAccount();
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const { loading, data } = useChannelFeesQuery({
|
||||
skip: !auth,
|
||||
|
@ -1,5 +1,7 @@
|
||||
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 {
|
||||
SubTitle,
|
||||
Card,
|
||||
@ -8,14 +10,12 @@ import {
|
||||
ColorButton,
|
||||
SingleLine,
|
||||
} from '../src/components/generic/Styled';
|
||||
import { useAccount } from '../src/context/AccountContext';
|
||||
import { getErrorContent } from '../src/utils/error';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
import { ForwardCard } from '../src/views/forwards/ForwardsCard';
|
||||
import { textColorMap } from '../src/styles/Themes';
|
||||
import { useConfigState } from '../src/context/ConfigContext';
|
||||
import { ForwardBox } from '../src/views/home/reports/forwardReport';
|
||||
import { useGetForwardsQuery } from '../src/generated/graphql';
|
||||
|
||||
const timeMap: { [key: string]: string } = {
|
||||
day: 'today',
|
||||
@ -29,7 +29,7 @@ const ForwardsView = () => {
|
||||
const [indexOpen, setIndexOpen] = useState(0);
|
||||
|
||||
const { theme } = useConfigState();
|
||||
const { auth } = useAccount();
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const { loading, data } = useGetForwardsQuery({
|
||||
skip: !auth,
|
||||
|
@ -1,73 +1,19 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useAccount } from '../src/context/AccountContext';
|
||||
import * as React from 'react';
|
||||
import { Spacer } from 'src/components/spacer/Spacer';
|
||||
import { SessionLogin } from '../src/views/login/SessionLogin';
|
||||
import { appendBasePath } from '../src/utils/basePath';
|
||||
import { TopSection } from '../src/views/homepage/Top';
|
||||
import { LoginBox } from '../src/views/homepage/LoginBox';
|
||||
import { Accounts } from '../src/views/homepage/Accounts';
|
||||
import {
|
||||
useStatusState,
|
||||
useStatusDispatch,
|
||||
} from '../src/context/StatusContext';
|
||||
import { useGetCanConnectLazyQuery } from '../src/generated/graphql';
|
||||
import { useStatusState } from '../src/context/StatusContext';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
import { Section } from '../src/components/section/Section';
|
||||
|
||||
const ContextApp = () => {
|
||||
const { push } = useRouter();
|
||||
const {
|
||||
name,
|
||||
host,
|
||||
cert,
|
||||
admin,
|
||||
viewOnly,
|
||||
sessionAdmin,
|
||||
accounts,
|
||||
} = useAccount();
|
||||
const { loading: statusLoading } = useStatusState();
|
||||
const dispatch = useStatusDispatch();
|
||||
|
||||
const change = accounts.length <= 1 && admin === '';
|
||||
const isSession = admin !== '' && viewOnly === '';
|
||||
|
||||
const [getCanConnect, { data, loading, error }] = useGetCanConnectLazyQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
onError: () => {
|
||||
toast.error(`Unable to connect to ${name}`);
|
||||
dispatch({ type: 'disconnected' });
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (loading && !error) {
|
||||
dispatch({ type: 'loading' });
|
||||
}
|
||||
if (!loading && data?.getNodeInfo && !error) {
|
||||
dispatch({ type: 'connected' });
|
||||
push(appendBasePath('/home'));
|
||||
}
|
||||
}, [loading, data, error, dispatch, push]);
|
||||
|
||||
useEffect(() => {
|
||||
if (viewOnly !== '' || sessionAdmin !== '') {
|
||||
getCanConnect({
|
||||
variables: {
|
||||
auth: {
|
||||
host,
|
||||
macaroon: viewOnly !== '' ? viewOnly : sessionAdmin,
|
||||
cert,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [viewOnly, sessionAdmin, getCanConnect, cert, host]);
|
||||
|
||||
const { loading } = useStatusState();
|
||||
return (
|
||||
<>
|
||||
<TopSection />
|
||||
{statusLoading && (
|
||||
{loading && (
|
||||
<Section withColor={false}>
|
||||
<LoadingCard
|
||||
inverseColor={true}
|
||||
@ -76,13 +22,14 @@ const ContextApp = () => {
|
||||
/>
|
||||
</Section>
|
||||
)}
|
||||
{!statusLoading && (
|
||||
{!loading && (
|
||||
<>
|
||||
{isSession && <SessionLogin />}
|
||||
<Accounts change={!isSession} />
|
||||
<LoginBox change={change} />
|
||||
<SessionLogin />
|
||||
<Accounts />
|
||||
<LoginBox />
|
||||
</>
|
||||
)}
|
||||
<Spacer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useAccount } from '../src/context/AccountContext';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { useGetPeersQuery } from 'src/graphql/queries/__generated__/getPeers.generated';
|
||||
import {
|
||||
CardWithTitle,
|
||||
SubTitle,
|
||||
@ -8,11 +9,10 @@ import {
|
||||
import { PeersCard } from '../src/views/peers/PeersCard';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
import { AddPeer } from '../src/views/peers/AddPeer';
|
||||
import { useGetPeersQuery } from '../src/generated/graphql';
|
||||
|
||||
const PeersView = () => {
|
||||
const [indexOpen, setIndexOpen] = useState(0);
|
||||
const { auth } = useAccount();
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const { loading, data } = useGetPeersQuery({
|
||||
skip: !auth,
|
||||
|
@ -1,6 +1,7 @@
|
||||
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 {
|
||||
CardWithTitle,
|
||||
SubTitle,
|
||||
@ -14,7 +15,6 @@ 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';
|
||||
import { useGetOffersQuery } from '../src/generated/graphql';
|
||||
|
||||
export interface QueryProps {
|
||||
pagination: {
|
||||
|
@ -1,25 +1,28 @@
|
||||
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 {
|
||||
Card,
|
||||
CardWithTitle,
|
||||
SubTitle,
|
||||
} from '../src/components/generic/Styled';
|
||||
import { InvoiceCard } from '../src/views/transactions/InvoiceCard';
|
||||
import { useAccount } from '../src/context/AccountContext';
|
||||
import { getErrorContent } from '../src/utils/error';
|
||||
import { PaymentsCard } from '../src/views/transactions/PaymentsCards';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
import { ColorButton } from '../src/components/buttons/colorButton/ColorButton';
|
||||
import { FlowBox } from '../src/views/home/reports/flow';
|
||||
import { useGetResumeQuery, GetResumeQuery } from '../src/generated/graphql';
|
||||
|
||||
const TransactionsView = () => {
|
||||
const [indexOpen, setIndexOpen] = useState(0);
|
||||
const [token, setToken] = useState('');
|
||||
const [fetching, setFetching] = useState(false);
|
||||
|
||||
const { auth } = useAccount();
|
||||
const { auth } = useAccountState();
|
||||
|
||||
const { loading, data, fetchMore } = useGetResumeQuery({
|
||||
skip: !auth,
|
||||
|
49
src/components/accounts/AuthSSOCheck.tsx
Normal file
49
src/components/accounts/AuthSSOCheck.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import * as React from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import Cookies from 'js-cookie';
|
||||
import {
|
||||
useAccountState,
|
||||
useAccountDispatch,
|
||||
SSO_ACCOUNT,
|
||||
} from 'src/context/AccountContext';
|
||||
import { appendBasePath } from 'src/utils/basePath';
|
||||
import { useGetAuthTokenQuery } from 'src/graphql/queries/__generated__/getAuthToken.generated';
|
||||
|
||||
type AuthCheckProps = {
|
||||
cookieParam: string | null;
|
||||
};
|
||||
|
||||
export const AuthSSOCheck = ({ cookieParam }: AuthCheckProps) => {
|
||||
const { push } = useRouter();
|
||||
const { accounts, ssoSaved } = useAccountState();
|
||||
const dispatch = useAccountDispatch();
|
||||
|
||||
const { data, loading } = useGetAuthTokenQuery({
|
||||
skip: !cookieParam,
|
||||
variables: { cookie: cookieParam },
|
||||
errorPolicy: 'ignore',
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (cookieParam && !loading && data?.getAuthToken && !ssoSaved) {
|
||||
Cookies.set('SSOAuth', data.getAuthToken, {
|
||||
sameSite: 'strict',
|
||||
});
|
||||
dispatch({
|
||||
type: 'addAccounts',
|
||||
accountsToAdd: [
|
||||
{
|
||||
name: 'SSO Account',
|
||||
id: SSO_ACCOUNT,
|
||||
loggedIn: true,
|
||||
type: SSO_ACCOUNT,
|
||||
},
|
||||
],
|
||||
isSSO: true,
|
||||
});
|
||||
push(appendBasePath('/'));
|
||||
}
|
||||
}, [push, data, loading, cookieParam, accounts, dispatch, ssoSaved]);
|
||||
|
||||
return null;
|
||||
};
|
61
src/components/accounts/ServerAccounts.tsx
Normal file
61
src/components/accounts/ServerAccounts.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
useAccountDispatch,
|
||||
SERVER_ACCOUNT,
|
||||
SSO_ACCOUNT,
|
||||
useAccountState,
|
||||
} 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';
|
||||
|
||||
export const ServerAccounts = () => {
|
||||
const { hasAccount } = useAccountState();
|
||||
const dispatch = useAccountDispatch();
|
||||
const { push, pathname } = useRouter();
|
||||
|
||||
const { data, loading } = useGetServerAccountsQuery();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (hasAccount === 'error' && pathname !== '/') {
|
||||
toast.error('No account found');
|
||||
dispatch({ type: 'resetFetch' });
|
||||
push(appendBasePath('/'));
|
||||
}
|
||||
}, [hasAccount, push, dispatch, pathname]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const session = sessionStorage.getItem('session') || null;
|
||||
const changeId = localStorage.getItem('active') || null;
|
||||
const savedAccounts = JSON.parse(localStorage.getItem('accounts') || '[]');
|
||||
const accountsToAdd = savedAccounts.map(a => addIdAndTypeToAccount(a));
|
||||
dispatch({
|
||||
type: 'initialize',
|
||||
accountsToAdd,
|
||||
changeId,
|
||||
session,
|
||||
});
|
||||
}, [dispatch]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!loading && data?.getServerAccounts) {
|
||||
const accountsToAdd = data.getServerAccounts.map(a => {
|
||||
const type = a?.id === SSO_ACCOUNT ? SSO_ACCOUNT : SERVER_ACCOUNT;
|
||||
return {
|
||||
name: a.name,
|
||||
id: a.id,
|
||||
loggedIn: a.loggedIn,
|
||||
type,
|
||||
};
|
||||
});
|
||||
dispatch({
|
||||
type: 'addAccounts',
|
||||
accountsToAdd,
|
||||
});
|
||||
}
|
||||
}, [loading, data, dispatch]);
|
||||
|
||||
return null;
|
||||
};
|
@ -1,14 +1,12 @@
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
import { useAccountState, CLIENT_ACCOUNT } from 'src/context/AccountContext';
|
||||
|
||||
interface AdminSwitchProps {
|
||||
children: any;
|
||||
}
|
||||
export const AdminSwitch = ({ children }) => {
|
||||
const { account, session } = useAccountState();
|
||||
|
||||
export const AdminSwitch = ({ children }: AdminSwitchProps) => {
|
||||
const { admin, sessionAdmin } = useAccount();
|
||||
|
||||
if (!admin && !sessionAdmin) {
|
||||
return null;
|
||||
if (account?.type === CLIENT_ACCOUNT) {
|
||||
if (!account.admin && !session) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return children;
|
||||
|
@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { X, Check } from 'react-feather';
|
||||
import { getAuthObj } from 'src/utils/auth';
|
||||
import { useGetCanAdminQuery } from 'src/graphql/queries/__generated__/adminCheck.generated';
|
||||
import { SingleLine, Sub4Title } from '../../generic/Styled';
|
||||
import { themeColors } from '../../../styles/Themes';
|
||||
import { useGetCanAdminQuery } from '../../../generated/graphql';
|
||||
|
||||
type AdminProps = {
|
||||
host: string;
|
||||
@ -16,7 +17,7 @@ export const AdminCheck = ({ host, admin, cert, setChecked }: AdminProps) => {
|
||||
const { data, loading } = useGetCanAdminQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
skip: !admin,
|
||||
variables: { auth: { host, macaroon: admin, cert } },
|
||||
variables: { auth: getAuthObj(host, null, admin, cert) },
|
||||
onError: () => {
|
||||
setChecked(false);
|
||||
},
|
||||
|
@ -1,11 +1,12 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { Check, X } from 'react-feather';
|
||||
import { getAuthObj } from 'src/utils/auth';
|
||||
import { useGetCanConnectQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
|
||||
import { themeColors } from '../../../styles/Themes';
|
||||
import { SingleLine, Sub4Title, Separation } from '../../generic/Styled';
|
||||
import { ColorButton } from '../../buttons/colorButton/ColorButton';
|
||||
import { Text } from '../../typography/Styled';
|
||||
import { useGetCanConnectQuery } from '../../../generated/graphql';
|
||||
import { AdminCheck } from './AdminCheck';
|
||||
|
||||
type ViewProps = {
|
||||
@ -35,7 +36,9 @@ export const ViewCheck = ({
|
||||
|
||||
const { data, loading } = useGetCanConnectQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
variables: { auth: { host, macaroon: viewOnly ?? admin ?? '', cert } },
|
||||
variables: {
|
||||
auth: getAuthObj(host, viewOnly, admin, cert),
|
||||
},
|
||||
onCompleted: () => setConfirmed(true),
|
||||
onError: () => setConfirmed(false),
|
||||
});
|
||||
|
@ -1,10 +1,13 @@
|
||||
import React, { useState } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import AES from 'crypto-js/aes';
|
||||
import { useRouter } from 'next/router';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
import { saveUserAuth, getAccountId } from '../../utils/auth';
|
||||
import {
|
||||
useAccountState,
|
||||
useAccountDispatch,
|
||||
} from 'src/context/AccountContext';
|
||||
import { getAccountId } from '../../utils/auth';
|
||||
import { useStatusDispatch } from '../../context/StatusContext';
|
||||
import { LoadingCard } from '../loading/LoadingCard';
|
||||
import { appendBasePath } from '../../utils/basePath';
|
||||
@ -36,11 +39,12 @@ type AuthProps = {
|
||||
};
|
||||
|
||||
export const Auth = ({ type, status, callback, setStatus }: AuthProps) => {
|
||||
const { changeAccount, accounts } = useAccount();
|
||||
const { accounts } = useAccountState();
|
||||
const { push } = useRouter();
|
||||
|
||||
const dispatchChat = useChatDispatch();
|
||||
const dispatch = useStatusDispatch();
|
||||
const dispatchChat = useChatDispatch();
|
||||
const dispatchAccount = useAccountDispatch();
|
||||
|
||||
const [name, setName] = useState<string>();
|
||||
const [host, setHost] = useState<string>();
|
||||
@ -64,20 +68,12 @@ export const Auth = ({ type, status, callback, setStatus }: AuthProps) => {
|
||||
viewOnly?: string;
|
||||
cert?: string;
|
||||
}) => {
|
||||
saveUserAuth({
|
||||
name,
|
||||
host: host || '',
|
||||
admin,
|
||||
viewOnly,
|
||||
cert,
|
||||
accounts,
|
||||
});
|
||||
|
||||
const id = getAccountId(host, viewOnly, admin, cert);
|
||||
|
||||
dispatch({ type: 'disconnected' });
|
||||
dispatchChat({ type: 'disconnected' });
|
||||
changeAccount(id);
|
||||
dispatchAccount({
|
||||
type: 'addAccountAndSave',
|
||||
accountToAdd: { name, host, admin, viewOnly, cert },
|
||||
});
|
||||
|
||||
push(appendBasePath('/'));
|
||||
};
|
||||
@ -102,8 +98,9 @@ export const Auth = ({ type, status, callback, setStatus }: AuthProps) => {
|
||||
cert ?? ''
|
||||
);
|
||||
|
||||
const accountExists =
|
||||
accounts.filter(account => account.id === id).length > 0;
|
||||
const accountExists = accounts
|
||||
? accounts.filter(account => account.id === id).length > 0
|
||||
: false;
|
||||
|
||||
if (accountExists) {
|
||||
toast.error('Account already exists.');
|
||||
@ -129,32 +126,29 @@ export const Auth = ({ type, status, callback, setStatus }: AuthProps) => {
|
||||
} else if (!admin && !viewOnly) {
|
||||
toast.error('View-Only or Admin macaroon are needed to connect.');
|
||||
} else {
|
||||
let correctViewOnly = viewOnly;
|
||||
let correctViewOnly = viewOnly || null;
|
||||
if (!viewOnly && admin && !password) {
|
||||
correctViewOnly = admin;
|
||||
}
|
||||
|
||||
const encryptedAdmin =
|
||||
admin && password
|
||||
? CryptoJS.AES.encrypt(admin, password).toString()
|
||||
: undefined;
|
||||
|
||||
saveUserAuth({
|
||||
name,
|
||||
host,
|
||||
admin: encryptedAdmin,
|
||||
viewOnly: correctViewOnly,
|
||||
cert,
|
||||
accounts,
|
||||
});
|
||||
|
||||
const id = getAccountId(host, correctViewOnly, encryptedAdmin, cert);
|
||||
admin && password ? AES.encrypt(admin, password).toString() : null;
|
||||
|
||||
dispatch({ type: 'disconnected' });
|
||||
dispatchChat({ type: 'disconnected' });
|
||||
changeAccount(id);
|
||||
dispatchAccount({
|
||||
type: 'addAccountAndSave',
|
||||
accountToAdd: {
|
||||
name,
|
||||
host,
|
||||
admin: encryptedAdmin,
|
||||
viewOnly: correctViewOnly,
|
||||
cert,
|
||||
},
|
||||
...(!correctViewOnly && { session: admin }),
|
||||
});
|
||||
|
||||
push(appendBasePath('/'));
|
||||
push(appendBasePath('/home'));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useGetBitcoinFeesQuery } from 'src/graphql/queries/__generated__/getBitcoinFees.generated';
|
||||
import { useBitcoinDispatch } from '../../context/BitcoinContext';
|
||||
import { useGetBitcoinFeesQuery } from '../../generated/graphql';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
|
||||
export const BitcoinFees = () => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useGetBitcoinPriceQuery } from 'src/graphql/queries/__generated__/getBitcoinPrice.generated';
|
||||
import { usePriceDispatch } from '../../context/PriceContext';
|
||||
import { useGetBitcoinPriceQuery } from '../../generated/graphql';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
|
||||
export const BitcoinPrice = () => {
|
||||
|
@ -2,14 +2,17 @@ import React, { useState } from 'react';
|
||||
import CryptoJS from 'crypto-js';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ChevronRight } from 'react-feather';
|
||||
import {
|
||||
useAccountState,
|
||||
useAccountDispatch,
|
||||
} from 'src/context/AccountContext';
|
||||
import { getAuthFromAccount } from 'src/context/helpers/context';
|
||||
import {
|
||||
Sub4Title,
|
||||
NoWrapTitle,
|
||||
SubTitle,
|
||||
ResponsiveLine,
|
||||
} from '../../generic/Styled';
|
||||
import { useAccount } from '../../../context/AccountContext';
|
||||
import { saveSessionAuth } from '../../../utils/auth';
|
||||
import { ColorButton } from '../colorButton/ColorButton';
|
||||
import { Input } from '../../input/Input';
|
||||
import { MultiButton, SingleButton } from '../multiButton/MultiButton';
|
||||
@ -31,7 +34,9 @@ export const LoginModal = ({
|
||||
}: LoginProps) => {
|
||||
const [pass, setPass] = useState<string>('');
|
||||
const [storeSession, setStoreSession] = useState<boolean>(false);
|
||||
const { host, cert, refreshAccount } = useAccount();
|
||||
|
||||
const { account } = useAccountState();
|
||||
const dispatch = useAccountDispatch();
|
||||
|
||||
const handleClick = () => {
|
||||
try {
|
||||
@ -39,11 +44,16 @@ export const LoginModal = ({
|
||||
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
|
||||
|
||||
if (storeSession) {
|
||||
saveSessionAuth(decrypted);
|
||||
refreshAccount();
|
||||
dispatch({ type: 'addSession', session: decrypted });
|
||||
}
|
||||
const auth = { host, macaroon: decrypted, cert };
|
||||
callback({ variables: { ...variables, auth } });
|
||||
|
||||
callback({
|
||||
variables: {
|
||||
...variables,
|
||||
auth: getAuthFromAccount(account, decrypted),
|
||||
},
|
||||
});
|
||||
|
||||
setModalOpen(false);
|
||||
} catch (error) {
|
||||
toast.error('Wrong Password');
|
||||
|
@ -1,13 +1,13 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useAccountState, CLIENT_ACCOUNT } from 'src/context/AccountContext';
|
||||
import { getAuthFromAccount } from 'src/context/helpers/context';
|
||||
import Modal from '../../modal/ReactModal';
|
||||
import { useAccount } from '../../../context/AccountContext';
|
||||
import { ColorButton, ColorButtonProps } from '../colorButton/ColorButton';
|
||||
import { LoginModal } from './LoginModal';
|
||||
|
||||
interface SecureButtonProps extends ColorButtonProps {
|
||||
callback: any;
|
||||
disabled: boolean;
|
||||
children: any;
|
||||
variables: {};
|
||||
color?: string;
|
||||
withMargin?: string;
|
||||
@ -25,19 +25,20 @@ export const SecureButton: React.FC<SecureButtonProps> = ({
|
||||
}) => {
|
||||
const [modalOpen, setModalOpen] = useState<boolean>(false);
|
||||
|
||||
const { host, cert, admin, sessionAdmin } = useAccount();
|
||||
const { session, account } = useAccountState();
|
||||
|
||||
if (!admin && !sessionAdmin) {
|
||||
if (account.type === CLIENT_ACCOUNT && !account.admin && !session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const auth = { host, macaroon: sessionAdmin, cert };
|
||||
const auth = getAuthFromAccount(account, session);
|
||||
|
||||
const handleClick = () => setModalOpen(true);
|
||||
|
||||
const onClick = sessionAdmin
|
||||
? () => callback({ variables: { ...variables, auth } })
|
||||
: handleClick;
|
||||
const onClick =
|
||||
session || account.type !== CLIENT_ACCOUNT
|
||||
? () => callback({ variables: { ...variables, auth } })
|
||||
: handleClick;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -49,15 +50,17 @@ export const SecureButton: React.FC<SecureButtonProps> = ({
|
||||
>
|
||||
{children}
|
||||
</ColorButton>
|
||||
<Modal isOpen={modalOpen} closeCallback={() => setModalOpen(false)}>
|
||||
<LoginModal
|
||||
color={color}
|
||||
macaroon={admin}
|
||||
setModalOpen={setModalOpen}
|
||||
callback={callback}
|
||||
variables={variables}
|
||||
/>
|
||||
</Modal>
|
||||
{account.type === CLIENT_ACCOUNT && (
|
||||
<Modal isOpen={modalOpen} closeCallback={() => setModalOpen(false)}>
|
||||
<LoginModal
|
||||
color={color}
|
||||
macaroon={account.admin}
|
||||
setModalOpen={setModalOpen}
|
||||
callback={callback}
|
||||
variables={variables}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useAccountState, CLIENT_ACCOUNT } from 'src/context/AccountContext';
|
||||
import { getAuthFromAccount } from 'src/context/helpers/context';
|
||||
import Modal from '../../modal/ReactModal';
|
||||
import { useAccount } from '../../../context/AccountContext';
|
||||
import { LoginModal } from './LoginModal';
|
||||
|
||||
interface SecureButtonProps {
|
||||
callback: any;
|
||||
children: any;
|
||||
variables: {};
|
||||
color?: string;
|
||||
}
|
||||
@ -18,34 +18,37 @@ export const SecureWrapper: React.FC<SecureButtonProps> = ({
|
||||
}) => {
|
||||
const [modalOpen, setModalOpen] = useState<boolean>(false);
|
||||
|
||||
const { host, cert, admin, sessionAdmin } = useAccount();
|
||||
const { account, session } = useAccountState();
|
||||
|
||||
if (!admin && !sessionAdmin) {
|
||||
if (account.type === CLIENT_ACCOUNT && !account.admin && !session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const auth = { host, macaroon: sessionAdmin, cert };
|
||||
const auth = getAuthFromAccount(account, session);
|
||||
|
||||
const handleClick = () => setModalOpen(true);
|
||||
|
||||
const onClick = sessionAdmin
|
||||
? () => callback({ variables: { ...variables, auth } })
|
||||
: handleClick;
|
||||
const onClick =
|
||||
session || account.type !== CLIENT_ACCOUNT
|
||||
? () => callback({ variables: { ...variables, auth } })
|
||||
: handleClick;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div role={'button'} onClick={onClick} onKeyDown={onClick} tabIndex={0}>
|
||||
{children}
|
||||
</div>
|
||||
<Modal isOpen={modalOpen} closeCallback={() => setModalOpen(false)}>
|
||||
<LoginModal
|
||||
color={color}
|
||||
macaroon={admin}
|
||||
setModalOpen={setModalOpen}
|
||||
callback={callback}
|
||||
variables={variables}
|
||||
/>
|
||||
</Modal>
|
||||
{account.type === CLIENT_ACCOUNT && (
|
||||
<Modal isOpen={modalOpen} closeCallback={() => setModalOpen(false)}>
|
||||
<LoginModal
|
||||
color={color}
|
||||
macaroon={account.admin}
|
||||
setModalOpen={setModalOpen}
|
||||
callback={callback}
|
||||
variables={variables}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,9 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { useGetMessagesQuery } from 'src/graphql/queries/__generated__/getMessages.generated';
|
||||
import { useChatState, useChatDispatch } from '../../context/ChatContext';
|
||||
import { useGetMessagesQuery } from '../../generated/graphql';
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
|
||||
@ -12,7 +12,7 @@ export const ChatFetcher = () => {
|
||||
|
||||
const { chatPollingSpeed } = useConfigState();
|
||||
|
||||
const { auth } = useAccount();
|
||||
const { auth } = useAccountState();
|
||||
const { pathname } = useRouter();
|
||||
const { lastChat, chats, sentChats, initialized } = useChatState();
|
||||
const dispatch = useChatDispatch();
|
||||
|
@ -1,12 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { useGetMessagesLazyQuery } from 'src/graphql/queries/__generated__/getMessages.generated';
|
||||
import { useChatDispatch } from '../../context/ChatContext';
|
||||
import { useGetMessagesLazyQuery } from '../../generated/graphql';
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
|
||||
export const ChatInit = () => {
|
||||
const { auth, id } = useAccount();
|
||||
const { auth, account } = useAccountState();
|
||||
const dispatch = useChatDispatch();
|
||||
|
||||
const [
|
||||
@ -18,25 +18,28 @@ export const ChatInit = () => {
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
const storageChats = localStorage.getItem(`${id}-sentChats`) || '';
|
||||
if (account) {
|
||||
const storageChats =
|
||||
localStorage.getItem(`${account.id}-sentChats`) || '';
|
||||
|
||||
if (storageChats !== '') {
|
||||
try {
|
||||
const savedChats = JSON.parse(storageChats);
|
||||
if (savedChats.length > 0) {
|
||||
const sender = savedChats[0].sender;
|
||||
dispatch({
|
||||
type: 'initialized',
|
||||
sentChats: savedChats,
|
||||
sender,
|
||||
});
|
||||
if (storageChats !== '') {
|
||||
try {
|
||||
const savedChats = JSON.parse(storageChats);
|
||||
if (savedChats.length > 0) {
|
||||
const sender = savedChats[0].sender;
|
||||
dispatch({
|
||||
type: 'initialized',
|
||||
sentChats: savedChats,
|
||||
sender,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
localStorage.removeItem('sentChats');
|
||||
}
|
||||
} catch (error) {
|
||||
localStorage.removeItem('sentChats');
|
||||
}
|
||||
getMessages();
|
||||
}
|
||||
getMessages();
|
||||
}, [dispatch, getMessages, id]);
|
||||
}, [dispatch, getMessages, account]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!initLoading && !initError && initData?.getMessages) {
|
||||
|
@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
||||
import { AlertTriangle } from 'react-feather';
|
||||
import styled from 'styled-components';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useCloseChannelMutation } from 'src/graphql/mutations/__generated__/closeChannel.generated';
|
||||
import {
|
||||
Separation,
|
||||
SingleLine,
|
||||
@ -17,7 +18,6 @@ import {
|
||||
} from '../../buttons/multiButton/MultiButton';
|
||||
import { Input } from '../../input/Input';
|
||||
import { useBitcoinState } from '../../../context/BitcoinContext';
|
||||
import { useCloseChannelMutation } from '../../../generated/graphql';
|
||||
|
||||
interface CloseChannelProps {
|
||||
setModalOpen: (status: boolean) => void;
|
||||
|
@ -2,11 +2,11 @@ import React from 'react';
|
||||
import { AlertTriangle } from 'react-feather';
|
||||
import styled from 'styled-components';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useRemovePeerMutation } from 'src/graphql/mutations/__generated__/removePeer.generated';
|
||||
import { SubTitle } from '../../generic/Styled';
|
||||
import { getErrorContent } from '../../../utils/error';
|
||||
import { SecureButton } from '../../buttons/secureButton/SecureButton';
|
||||
import { ColorButton } from '../../buttons/colorButton/ColorButton';
|
||||
import { useRemovePeerMutation } from '../../../generated/graphql';
|
||||
|
||||
interface RemovePeerProps {
|
||||
setModalOpen: (status: boolean) => void;
|
||||
|
@ -2,8 +2,8 @@ import * as React from 'react';
|
||||
import { HelpCircle } from 'react-feather';
|
||||
import styled from 'styled-components';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { useAccountState, CLIENT_ACCOUNT } from 'src/context/AccountContext';
|
||||
import { CardWithTitle, SubTitle } from '../generic/Styled';
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
import { useConfigState } from '../../context/ConfigContext';
|
||||
import { getTooltipType } from '../generic/helpers';
|
||||
import {
|
||||
@ -19,13 +19,15 @@ const StyledQuestion = styled(HelpCircle)`
|
||||
`;
|
||||
|
||||
export const NodeBar = () => {
|
||||
const { accounts } = useAccount();
|
||||
const { accounts } = useAccountState();
|
||||
const { multiNodeInfo, theme } = useConfigState();
|
||||
const slider = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const tooltipType: any = getTooltipType(theme);
|
||||
|
||||
const viewOnlyAccounts = accounts.filter(account => account.viewOnly !== '');
|
||||
const viewOnlyAccounts = accounts.filter(
|
||||
account => account.type === CLIENT_ACCOUNT && account.viewOnly !== ''
|
||||
);
|
||||
|
||||
const handleScroll = (decrease?: boolean) => {
|
||||
if (slider.current !== null) {
|
||||
|
@ -2,11 +2,11 @@ import React, { useState } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import 'intersection-observer'; // Polyfill
|
||||
import ScaleLoader from 'react-spinners/ScaleLoader';
|
||||
import { useGetNodeInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
|
||||
import { SingleLine, DarkSubTitle, ResponsiveLine } from '../generic/Styled';
|
||||
import { themeColors } from '../../styles/Themes';
|
||||
import { Price } from '../price/Price';
|
||||
import Modal from '../modal/ReactModal';
|
||||
import { useGetNodeInfoQuery } from '../../generated/graphql';
|
||||
import { getAuthObj } from '../../utils/auth';
|
||||
import { StatusDot, StatusLine, QuickCard } from './NodeInfo.styled';
|
||||
import { NodeInfoModal } from './NodeInfoModal';
|
||||
@ -29,7 +29,7 @@ export const NodeCard = ({ account, accountId }: NodeCardProps) => {
|
||||
triggerOnce: true,
|
||||
});
|
||||
|
||||
const auth = getAuthObj(host, viewOnly, '', cert);
|
||||
const auth = getAuthObj(host, viewOnly, null, cert);
|
||||
|
||||
const { data, loading, error } = useGetNodeInfoQuery({
|
||||
skip: !inView || !auth,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useAccountDispatch } from 'src/context/AccountContext';
|
||||
import {
|
||||
SubTitle,
|
||||
SingleLine,
|
||||
@ -9,7 +10,6 @@ import {
|
||||
import { Price } from '../price/Price';
|
||||
import { ColorButton } from '../buttons/colorButton/ColorButton';
|
||||
import { useStatusDispatch } from '../../context/StatusContext';
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
|
||||
interface NodeInfoModalProps {
|
||||
account: any;
|
||||
@ -19,7 +19,7 @@ interface NodeInfoModalProps {
|
||||
export const NodeInfoModal = ({ account, accountId }: NodeInfoModalProps) => {
|
||||
const dispatch = useStatusDispatch();
|
||||
|
||||
const { changeAccount } = useAccount();
|
||||
const dispatchAccount = useAccountDispatch();
|
||||
|
||||
const {
|
||||
active_channels_count,
|
||||
@ -89,7 +89,7 @@ export const NodeInfoModal = ({ account, accountId }: NodeInfoModalProps) => {
|
||||
dispatch({
|
||||
type: 'disconnected',
|
||||
});
|
||||
changeAccount(accountId);
|
||||
dispatchAccount({ type: 'changeAccount', changeId: accountId });
|
||||
}}
|
||||
>
|
||||
Change to this Account
|
||||
|
13
src/components/spacer/Spacer.tsx
Normal file
13
src/components/spacer/Spacer.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { mediaWidths } from 'src/styles/Themes';
|
||||
|
||||
const StyledSpacer = styled.div`
|
||||
height: 32px;
|
||||
|
||||
@media (${mediaWidths.mobile}) {
|
||||
height: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Spacer = () => <StyledSpacer />;
|
@ -1,18 +1,18 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useAccount } from '../../context/AccountContext';
|
||||
import { useAccountState } from 'src/context/AccountContext';
|
||||
import { useGetNodeInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
|
||||
import { useStatusDispatch } from '../../context/StatusContext';
|
||||
import { useGetNodeInfoQuery } from '../../generated/graphql';
|
||||
import { appendBasePath } from '../../utils/basePath';
|
||||
|
||||
export const StatusCheck = () => {
|
||||
const dispatch = useStatusDispatch();
|
||||
const { push } = useRouter();
|
||||
|
||||
const { name, auth } = useAccount();
|
||||
const { account, auth } = useAccountState();
|
||||
|
||||
const { data, loading, error } = useGetNodeInfoQuery({
|
||||
const { data, loading, error, stopPolling } = useGetNodeInfoQuery({
|
||||
skip: !auth,
|
||||
fetchPolicy: 'network-only',
|
||||
variables: { auth },
|
||||
@ -21,7 +21,8 @@ export const StatusCheck = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
toast.error(`Unable to connect to ${name}`);
|
||||
toast.error(`Unable to connect to ${account.name}`);
|
||||
stopPolling();
|
||||
dispatch({ type: 'disconnected' });
|
||||
push(appendBasePath('/'));
|
||||
}
|
||||
@ -54,7 +55,7 @@ export const StatusCheck = () => {
|
||||
|
||||
dispatch({ type: 'connected', state });
|
||||
}
|
||||
}, [data, dispatch, error, loading, name, push]);
|
||||
}, [data, dispatch, error, loading, push, account]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
@ -1,166 +1,308 @@
|
||||
/* eslint-disable @typescript-eslint/no-use-before-define */
|
||||
import React, { createContext, useState, useContext, useEffect } from 'react';
|
||||
import merge from 'lodash.merge';
|
||||
import { getAuth, getAuthObj } from '../utils/auth';
|
||||
import { saveAccounts } from '../utils/storage';
|
||||
import * as React from 'react';
|
||||
import Cookies from 'js-cookie';
|
||||
import {
|
||||
getAccountById,
|
||||
deleteAccountById,
|
||||
addIdAndTypeToAccount,
|
||||
getAuthFromAccount,
|
||||
} from './helpers/context';
|
||||
|
||||
interface SingleAccountProps {
|
||||
export type SERVER_ACCOUNT_TYPE = 'sso' | 'server';
|
||||
export type ACCOUNT_TYPE = 'client';
|
||||
|
||||
export const CLIENT_ACCOUNT: ACCOUNT_TYPE = 'client';
|
||||
export const SSO_ACCOUNT: SERVER_ACCOUNT_TYPE = 'sso';
|
||||
export const SERVER_ACCOUNT: SERVER_ACCOUNT_TYPE = 'server';
|
||||
|
||||
type HasAccountType = 'fetched' | 'false' | 'error';
|
||||
|
||||
export type AccountProps = {
|
||||
name: string;
|
||||
host: string;
|
||||
admin: string;
|
||||
viewOnly: string;
|
||||
cert: string;
|
||||
id: string;
|
||||
}
|
||||
};
|
||||
|
||||
interface AuthProps {
|
||||
host: string;
|
||||
admin: string;
|
||||
viewOnly: string;
|
||||
cert: string;
|
||||
}
|
||||
|
||||
interface ChangeProps {
|
||||
name?: string;
|
||||
host?: string;
|
||||
admin?: string;
|
||||
sessionAdmin?: string;
|
||||
viewOnly?: string;
|
||||
cert?: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
interface AccountProps {
|
||||
name: string;
|
||||
host: string;
|
||||
admin: string;
|
||||
sessionAdmin: string;
|
||||
viewOnly: string;
|
||||
cert: string;
|
||||
id: string;
|
||||
auth: AuthProps | undefined;
|
||||
accounts: SingleAccountProps[];
|
||||
changeAccount: (account: string) => void;
|
||||
deleteAccount: (account: string) => void;
|
||||
refreshAccount: () => void;
|
||||
}
|
||||
|
||||
export const AccountContext = createContext<AccountProps>({
|
||||
name: '',
|
||||
host: '',
|
||||
admin: '',
|
||||
sessionAdmin: '',
|
||||
viewOnly: '',
|
||||
cert: '',
|
||||
id: '',
|
||||
auth: undefined,
|
||||
accounts: [],
|
||||
changeAccount: () => ({}),
|
||||
deleteAccount: () => ({}),
|
||||
refreshAccount: () => ({}),
|
||||
});
|
||||
|
||||
const AccountProvider = ({ children }: any) => {
|
||||
useEffect(() => {
|
||||
refreshAccount();
|
||||
}, []);
|
||||
|
||||
const changeAccount = (changeToId: string) => {
|
||||
const currentAccounts = JSON.parse(
|
||||
localStorage.getItem('accounts') || '[]'
|
||||
);
|
||||
const index = currentAccounts.findIndex(
|
||||
(account: any) => account.id === changeToId
|
||||
);
|
||||
|
||||
if (index < 0) return;
|
||||
|
||||
sessionStorage.removeItem('session');
|
||||
localStorage.setItem('active', `${index}`);
|
||||
|
||||
refreshAccount(`${index}`);
|
||||
};
|
||||
|
||||
const deleteAccount = (deleteId: string) => {
|
||||
const currentAccounts = JSON.parse(
|
||||
localStorage.getItem('accounts') || '[]'
|
||||
);
|
||||
const current = currentAccounts.find(
|
||||
(account: any) => account.id === deleteId
|
||||
);
|
||||
|
||||
if (!current) return;
|
||||
|
||||
const isCurrentAccount = current.id === settings.id;
|
||||
|
||||
const changedAccounts = [...currentAccounts].filter(
|
||||
account => account.id !== deleteId
|
||||
);
|
||||
const length = changedAccounts.length;
|
||||
|
||||
if (isCurrentAccount) {
|
||||
sessionStorage.removeItem('session');
|
||||
localStorage.setItem('active', `${length - 1}`);
|
||||
} else {
|
||||
const newIndex = changedAccounts.findIndex(
|
||||
(account: any) => account.id === settings.id
|
||||
);
|
||||
localStorage.setItem('active', `${newIndex}`);
|
||||
export type AuthType =
|
||||
| {
|
||||
type: ACCOUNT_TYPE;
|
||||
host: string;
|
||||
macaroon: string;
|
||||
cert: string | null;
|
||||
}
|
||||
| {
|
||||
type: SERVER_ACCOUNT_TYPE;
|
||||
id: string;
|
||||
};
|
||||
|
||||
saveAccounts(changedAccounts);
|
||||
export type CompleteAccount =
|
||||
| ({
|
||||
type: 'client';
|
||||
id: string;
|
||||
} & AccountProps)
|
||||
| {
|
||||
type: 'sso' | 'server';
|
||||
id: string;
|
||||
name: string;
|
||||
loggedIn?: boolean;
|
||||
};
|
||||
|
||||
refreshAccount();
|
||||
};
|
||||
type State = {
|
||||
auth: AuthType | null;
|
||||
activeAccount: string | null;
|
||||
session: string | null;
|
||||
ssoSaved: boolean;
|
||||
account: CompleteAccount | null;
|
||||
accounts: CompleteAccount[];
|
||||
hasAccount: HasAccountType;
|
||||
};
|
||||
|
||||
const refreshAccount = (account?: string) => {
|
||||
const sessionAdmin = sessionStorage.getItem('session') || '';
|
||||
const { name, host, admin, viewOnly, cert, id, accounts } = getAuth(
|
||||
account
|
||||
);
|
||||
type ActionType =
|
||||
| {
|
||||
type: 'initialize';
|
||||
changeId: string;
|
||||
accountsToAdd: CompleteAccount[];
|
||||
session: string;
|
||||
}
|
||||
| {
|
||||
type: 'changeAccount' | 'deleteAccount';
|
||||
changeId: string;
|
||||
}
|
||||
| {
|
||||
type: 'logout';
|
||||
}
|
||||
| {
|
||||
type: 'addAccounts';
|
||||
accountsToAdd: CompleteAccount[];
|
||||
isSSO?: boolean;
|
||||
}
|
||||
| {
|
||||
type: 'addAccountAndSave';
|
||||
accountToAdd: AccountProps;
|
||||
session?: string;
|
||||
}
|
||||
| {
|
||||
type: 'addSession';
|
||||
session: string;
|
||||
}
|
||||
| {
|
||||
type: 'removeSession';
|
||||
}
|
||||
| {
|
||||
type: 'deleteAll';
|
||||
}
|
||||
| {
|
||||
type: 'resetFetch';
|
||||
};
|
||||
|
||||
updateAccount((prevState: any) => {
|
||||
const newState = { ...prevState };
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
||||
const merged = merge(newState, {
|
||||
name,
|
||||
host,
|
||||
admin,
|
||||
sessionAdmin,
|
||||
viewOnly,
|
||||
cert,
|
||||
id,
|
||||
auth: getAuthObj(host, viewOnly, sessionAdmin, cert),
|
||||
});
|
||||
const StateContext = React.createContext<State | undefined>(undefined);
|
||||
const DispatchContext = React.createContext<Dispatch | undefined>(undefined);
|
||||
|
||||
return { ...merged, accounts };
|
||||
});
|
||||
};
|
||||
const initialState: State = {
|
||||
auth: null,
|
||||
session: null,
|
||||
activeAccount: null,
|
||||
ssoSaved: false,
|
||||
account: null,
|
||||
accounts: [],
|
||||
hasAccount: 'false',
|
||||
};
|
||||
|
||||
const accountState = {
|
||||
name: '',
|
||||
host: '',
|
||||
admin: '',
|
||||
sessionAdmin: '',
|
||||
viewOnly: '',
|
||||
cert: '',
|
||||
id: '',
|
||||
auth: undefined,
|
||||
accounts: [],
|
||||
changeAccount,
|
||||
deleteAccount,
|
||||
refreshAccount,
|
||||
};
|
||||
const stateReducer = (state: State, action: ActionType): State => {
|
||||
switch (action.type) {
|
||||
case 'initialize': {
|
||||
const { accountsToAdd, changeId, session } = action;
|
||||
|
||||
const [settings, updateAccount] = useState(accountState);
|
||||
const { account, id } = getAccountById(changeId, accountsToAdd);
|
||||
|
||||
if (!account)
|
||||
return {
|
||||
...state,
|
||||
accounts: accountsToAdd,
|
||||
activeAccount: changeId,
|
||||
session,
|
||||
};
|
||||
|
||||
const auth = getAuthFromAccount(account, session);
|
||||
return {
|
||||
...state,
|
||||
auth,
|
||||
account,
|
||||
accounts: accountsToAdd,
|
||||
activeAccount: id,
|
||||
session,
|
||||
hasAccount: 'fetched',
|
||||
};
|
||||
}
|
||||
case 'changeAccount': {
|
||||
const { account, id } = getAccountById(action.changeId, state.accounts);
|
||||
|
||||
if (!account) return state;
|
||||
|
||||
const auth = getAuthFromAccount(account);
|
||||
|
||||
localStorage.setItem('active', `${id}`);
|
||||
sessionStorage.removeItem('session');
|
||||
|
||||
return {
|
||||
...state,
|
||||
auth,
|
||||
session: null,
|
||||
account,
|
||||
activeAccount: id,
|
||||
hasAccount: 'fetched',
|
||||
};
|
||||
}
|
||||
case 'logout':
|
||||
localStorage.removeItem('active');
|
||||
sessionStorage.clear();
|
||||
Cookies.remove('AccountAuth');
|
||||
Cookies.remove('SSOAuth');
|
||||
return {
|
||||
...state,
|
||||
account: null,
|
||||
activeAccount: null,
|
||||
auth: null,
|
||||
session: null,
|
||||
};
|
||||
case 'deleteAccount': {
|
||||
if (!state.accounts || state?.accounts?.length <= 0) {
|
||||
return state;
|
||||
}
|
||||
const { accounts, id } = deleteAccountById(
|
||||
state.account.id,
|
||||
action.changeId,
|
||||
state.accounts
|
||||
);
|
||||
localStorage.setItem('accounts', JSON.stringify(accounts));
|
||||
!id && sessionStorage.removeItem('session');
|
||||
return {
|
||||
...state,
|
||||
accounts,
|
||||
...(!id && { activeId: null, session: null, account: null }),
|
||||
};
|
||||
}
|
||||
case 'addAccounts': {
|
||||
const completeAccounts = [...state.accounts, ...action.accountsToAdd];
|
||||
|
||||
if (!state.activeAccount) {
|
||||
return {
|
||||
...state,
|
||||
accounts: completeAccounts,
|
||||
...(action.isSSO && { ssoSaved: true }),
|
||||
};
|
||||
}
|
||||
|
||||
const { account } = getAccountById(state.activeAccount, completeAccounts);
|
||||
|
||||
if (!account && completeAccounts.length > 0) {
|
||||
return {
|
||||
...state,
|
||||
accounts: completeAccounts,
|
||||
hasAccount: 'error',
|
||||
...(action.isSSO && { ssoSaved: true }),
|
||||
};
|
||||
}
|
||||
|
||||
const auth = getAuthFromAccount(account, state.session);
|
||||
return {
|
||||
...state,
|
||||
hasAccount: 'fetched',
|
||||
auth,
|
||||
account,
|
||||
accounts: completeAccounts,
|
||||
...(action.isSSO && { ssoSaved: true }),
|
||||
};
|
||||
}
|
||||
case 'addAccountAndSave': {
|
||||
const account = addIdAndTypeToAccount(action.accountToAdd);
|
||||
const activeAccount = account.id;
|
||||
const accounts = [...state.accounts, account];
|
||||
|
||||
const auth = getAuthFromAccount(account, action.session);
|
||||
|
||||
if (action.session) {
|
||||
sessionStorage.setItem('session', action.session);
|
||||
}
|
||||
|
||||
localStorage.setItem('active', `${activeAccount}`);
|
||||
|
||||
const savedAccounts = JSON.parse(
|
||||
localStorage.getItem('accounts') || '[]'
|
||||
);
|
||||
localStorage.setItem(
|
||||
'accounts',
|
||||
JSON.stringify([...savedAccounts, action.accountToAdd])
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
auth,
|
||||
account,
|
||||
accounts,
|
||||
activeAccount,
|
||||
hasAccount: 'fetched',
|
||||
...(action.session && { session: action.session }),
|
||||
};
|
||||
}
|
||||
case 'addSession':
|
||||
sessionStorage.setItem('session', action.session);
|
||||
return {
|
||||
...state,
|
||||
auth: getAuthFromAccount(state.account, action.session),
|
||||
session: action.session,
|
||||
};
|
||||
case 'removeSession':
|
||||
sessionStorage.removeItem('session');
|
||||
return {
|
||||
...state,
|
||||
auth: getAuthFromAccount(state.account),
|
||||
session: null,
|
||||
};
|
||||
case 'deleteAll':
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
Cookies.remove('config');
|
||||
Cookies.remove('AccountAuth');
|
||||
Cookies.remove('SSOAuth');
|
||||
return initialState;
|
||||
case 'resetFetch':
|
||||
return {
|
||||
...state,
|
||||
hasAccount: 'false',
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const AccountProvider: React.FC = ({ children }) => {
|
||||
const [state, dispatch] = React.useReducer(stateReducer, initialState);
|
||||
|
||||
return (
|
||||
<AccountContext.Provider value={settings}>
|
||||
{children}
|
||||
</AccountContext.Provider>
|
||||
<DispatchContext.Provider value={dispatch}>
|
||||
<StateContext.Provider value={state}>{children}</StateContext.Provider>
|
||||
</DispatchContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useAccount = () => useContext(AccountContext);
|
||||
const useAccountState = () => {
|
||||
const context = React.useContext(StateContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useAccountState must be used within a AccountProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export { AccountProvider, useAccount };
|
||||
const useAccountDispatch = () => {
|
||||
const context = React.useContext(DispatchContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useAccountDispatch must be used within a AccountProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
export { AccountProvider, useAccountState, useAccountDispatch };
|
||||
|
@ -13,10 +13,14 @@ type ChangeState = {
|
||||
hour: number;
|
||||
};
|
||||
|
||||
type ActionType = {
|
||||
type: 'fetched' | 'dontShow';
|
||||
state?: ChangeState;
|
||||
};
|
||||
type ActionType =
|
||||
| {
|
||||
type: 'fetched';
|
||||
state: ChangeState;
|
||||
}
|
||||
| {
|
||||
type: 'dontShow';
|
||||
};
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
||||
@ -41,7 +45,7 @@ const stateReducer = (state: State, action: ActionType): State => {
|
||||
}
|
||||
};
|
||||
|
||||
const BitcoinInfoProvider = ({ children }: any) => {
|
||||
const BitcoinInfoProvider = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, initialState);
|
||||
|
||||
return (
|
||||
|
@ -30,20 +30,33 @@ type State = {
|
||||
sender: string;
|
||||
};
|
||||
|
||||
type ActionType = {
|
||||
type:
|
||||
| 'initialized'
|
||||
| 'additional'
|
||||
| 'changeActive'
|
||||
| 'newChat'
|
||||
| 'disconnected';
|
||||
chats?: ChatProps[];
|
||||
sentChats?: SentChatProps[];
|
||||
newChat?: SentChatProps;
|
||||
lastChat?: string;
|
||||
sender?: string;
|
||||
userId?: string;
|
||||
};
|
||||
type ActionType =
|
||||
| {
|
||||
type: 'initialized';
|
||||
chats?: ChatProps[];
|
||||
lastChat?: string;
|
||||
sender?: string;
|
||||
sentChats?: SentChatProps[];
|
||||
}
|
||||
| {
|
||||
type: 'additional';
|
||||
chats: ChatProps[];
|
||||
lastChat: string;
|
||||
}
|
||||
| {
|
||||
type: 'changeActive';
|
||||
sender: string;
|
||||
userId: string;
|
||||
}
|
||||
| {
|
||||
type: 'newChat';
|
||||
sender: string;
|
||||
userId: string;
|
||||
newChat: SentChatProps;
|
||||
}
|
||||
| {
|
||||
type: 'disconnected';
|
||||
};
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
||||
@ -88,6 +101,8 @@ const stateReducer = (state: State, action: ActionType): State => {
|
||||
sentChats: [...state.sentChats, action.newChat],
|
||||
...(action.sender && { sender: action.sender }),
|
||||
};
|
||||
case 'disconnected':
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -14,10 +14,14 @@ type ChangeState = {
|
||||
prices?: { [key: string]: PriceProps };
|
||||
};
|
||||
|
||||
type ActionType = {
|
||||
type: 'fetched' | 'dontShow';
|
||||
state?: ChangeState;
|
||||
};
|
||||
type ActionType =
|
||||
| {
|
||||
type: 'fetched';
|
||||
state: ChangeState;
|
||||
}
|
||||
| {
|
||||
type: 'dontShow';
|
||||
};
|
||||
|
||||
type Dispatch = (action: ActionType) => void;
|
||||
|
||||
@ -40,7 +44,7 @@ const stateReducer = (state: State, action: ActionType): State => {
|
||||
}
|
||||
};
|
||||
|
||||
const PriceProvider = ({ children }: any) => {
|
||||
const PriceProvider = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, initialState);
|
||||
|
||||
return (
|
||||
|
@ -60,7 +60,7 @@ const stateReducer = (state: State, action: ActionType): CompleteState => {
|
||||
}
|
||||
};
|
||||
|
||||
const StatusProvider = ({ children }: any) => {
|
||||
const StatusProvider = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, initialState);
|
||||
|
||||
return (
|
||||
|
75
src/context/helpers/context.test.ts
Normal file
75
src/context/helpers/context.test.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import {
|
||||
CLIENT_ACCOUNT,
|
||||
SSO_ACCOUNT,
|
||||
CompleteAccount,
|
||||
} from '../AccountContext';
|
||||
import { getAccountById, deleteAccountById } from './context';
|
||||
|
||||
const firstAccount = {
|
||||
name: 'Hola',
|
||||
host: 'Host1',
|
||||
admin: 'Admin1',
|
||||
viewOnly: 'ViewOnly1',
|
||||
cert: 'Cert1',
|
||||
id: '123',
|
||||
type: CLIENT_ACCOUNT,
|
||||
};
|
||||
const secondAccount = {
|
||||
name: 'Chao',
|
||||
host: 'Host2',
|
||||
admin: 'Admin2',
|
||||
viewOnly: 'ViewOnly2',
|
||||
cert: 'Cert2',
|
||||
id: '1234',
|
||||
type: SSO_ACCOUNT,
|
||||
};
|
||||
|
||||
const testAccounts: CompleteAccount[] = [firstAccount, secondAccount];
|
||||
|
||||
describe('Context Helpers', () => {
|
||||
describe('should getAccountById', () => {
|
||||
test('account exists', () => {
|
||||
const { account, id } = getAccountById('1234', testAccounts);
|
||||
|
||||
expect(id).toBe('1234');
|
||||
expect(account).toBe(secondAccount);
|
||||
});
|
||||
test('account does not exists', () => {
|
||||
const { account, id } = getAccountById('false id', testAccounts);
|
||||
|
||||
expect(id).toBe(null);
|
||||
expect(account).toBe(null);
|
||||
});
|
||||
});
|
||||
describe('should deleteAccountById', () => {
|
||||
test('account exists', () => {
|
||||
const { accounts, id } = deleteAccountById('123', '1234', testAccounts);
|
||||
|
||||
expect(id).toBe('123');
|
||||
expect(accounts).toStrictEqual([firstAccount]);
|
||||
});
|
||||
test('account exists and is current account', () => {
|
||||
const { accounts, id } = deleteAccountById('123', '123', testAccounts);
|
||||
|
||||
expect(id).toBe(null);
|
||||
expect(accounts).toStrictEqual([secondAccount]);
|
||||
});
|
||||
test('account does not exists', () => {
|
||||
const { accounts, id } = deleteAccountById(
|
||||
'123',
|
||||
'false id',
|
||||
testAccounts
|
||||
);
|
||||
|
||||
expect(id).toBe('123');
|
||||
expect(accounts).toStrictEqual(testAccounts);
|
||||
});
|
||||
|
||||
test('one account', () => {
|
||||
const { accounts, id } = deleteAccountById('123', '123', [firstAccount]);
|
||||
|
||||
expect(id).toBe(null);
|
||||
expect(accounts).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
74
src/context/helpers/context.ts
Normal file
74
src/context/helpers/context.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { getUUID } from '../../utils/auth';
|
||||
import {
|
||||
CompleteAccount,
|
||||
AccountProps,
|
||||
CLIENT_ACCOUNT,
|
||||
AuthType,
|
||||
} from '../AccountContext';
|
||||
|
||||
export const getAccountById = (id: string, accounts: CompleteAccount[]) => {
|
||||
const correctAccount: CompleteAccount | null = accounts.find(
|
||||
a => a.id === id
|
||||
);
|
||||
|
||||
return {
|
||||
account: correctAccount || null,
|
||||
id: correctAccount ? correctAccount.id : null,
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteAccountById = (
|
||||
currentId: string,
|
||||
id: string,
|
||||
accounts: CompleteAccount[]
|
||||
) => {
|
||||
const newAccounts: CompleteAccount[] = accounts.filter(a => a.id !== id);
|
||||
|
||||
if (newAccounts.length <= 0) {
|
||||
return { accounts: [], id: null };
|
||||
}
|
||||
|
||||
let activeId: string | null = currentId;
|
||||
if (currentId === id) {
|
||||
activeId = null;
|
||||
}
|
||||
|
||||
return { accounts: newAccounts, id: activeId };
|
||||
};
|
||||
|
||||
export const addIdAndTypeToAccount = (
|
||||
account: AccountProps
|
||||
): CompleteAccount => {
|
||||
const { host, viewOnly, admin, cert } = account;
|
||||
return {
|
||||
...account,
|
||||
type: CLIENT_ACCOUNT,
|
||||
id: getUUID(`${host}-${viewOnly}-${admin !== '' ? 1 : 0}-${cert}`),
|
||||
};
|
||||
};
|
||||
|
||||
export const getAuthFromAccount = (
|
||||
account: CompleteAccount,
|
||||
session?: string
|
||||
): AuthType => {
|
||||
if (!account) return null;
|
||||
if (account.type !== CLIENT_ACCOUNT) {
|
||||
return {
|
||||
type: account.type,
|
||||
id: account.id,
|
||||
};
|
||||
}
|
||||
const { host, viewOnly, cert } = account;
|
||||
if (!host) {
|
||||
return null;
|
||||
}
|
||||
if (!viewOnly && !session) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: account.type,
|
||||
host,
|
||||
macaroon: viewOnly && viewOnly !== '' ? viewOnly : session,
|
||||
cert,
|
||||
};
|
||||
};
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user