From 414c20f053f12db4ebdb9e4b01dc9100644054b8 Mon Sep 17 00:00:00 2001 From: AP Date: Sat, 23 May 2020 14:46:47 +0200 Subject: [PATCH] =?UTF-8?q?chore:=20=F0=9F=94=A7=20improve=20logging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/helpers/helpers.ts | 25 ++++++++----- api/helpers/logger.ts | 6 ++-- .../query/authToken/getSessionToken.ts | 9 +++-- api/schemas/query/general/nodeInfo.ts | 35 +++++++++---------- pages/api/v1.tsx | 2 ++ src/components/chat/ChatFetcher.tsx | 6 ++-- src/components/statusCheck/StatusCheck.tsx | 7 ++-- src/utils/error.tsx | 6 ++++ src/views/login/SessionLogin.tsx | 25 +++++++++---- 9 files changed, 76 insertions(+), 45 deletions(-) diff --git a/api/helpers/helpers.ts b/api/helpers/helpers.ts index a751e28f..a43c1045 100644 --- a/api/helpers/helpers.ts +++ b/api/helpers/helpers.ts @@ -7,6 +7,7 @@ import { CLIENT_ACCOUNT, } from 'src/context/AccountContext'; import { ContextType } from 'api/types/apiTypes'; +import { logger } from './logger'; const { serverRuntimeConfig } = getConfig(); const { nodeEnv } = serverRuntimeConfig; @@ -35,24 +36,32 @@ export const getCorrectAuth = ( ): 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'); + if (!account) { + logger.debug('Account not available in request'); + throw new Error('AccountNotAuthenticated'); + } + if (account.id !== auth.id) { + logger.debug( + `Account (${account.id}) in cookie different to requested account (${auth.id})` + ); + throw new Error('AccountNotAuthenticated'); + } return account; } if (auth.type === SSO_ACCOUNT) { - if (!context.ssoVerified) - throw new Error('This account is not authenticated'); + if (!context.ssoVerified) { + logger.debug('SSO Account is not verified'); + throw new Error('AccountNotAuthenticated'); + } + 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'); + throw new Error('AccountTypeDoesNotExist'); }; export const getAuthLnd = (auth: LndAuthType) => { diff --git a/api/helpers/logger.ts b/api/helpers/logger.ts index eb4bb30c..46dc1c72 100644 --- a/api/helpers/logger.ts +++ b/api/helpers/logger.ts @@ -2,9 +2,7 @@ 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 { logLevel } = serverRuntimeConfig; const combinedFormat = // nodeEnv === 'development' ? @@ -33,7 +31,7 @@ const combinedFormat = // ); export const logger = createLogger({ - level, + level: logLevel, format: combinedFormat, transports: [new transports.Console()], }); diff --git a/api/schemas/query/authToken/getSessionToken.ts b/api/schemas/query/authToken/getSessionToken.ts index 934dffe7..d93ed1e5 100644 --- a/api/schemas/query/authToken/getSessionToken.ts +++ b/api/schemas/query/authToken/getSessionToken.ts @@ -3,6 +3,7 @@ import jwt from 'jsonwebtoken'; import CryptoJS from 'crypto-js'; import { ContextType } from 'api/types/apiTypes'; import AES from 'crypto-js/aes'; +import { logger } from 'api/helpers/logger'; import { requestLimiter } from '../../../helpers/rateLimiter'; export const getSessionToken = { @@ -17,11 +18,15 @@ export const getSessionToken = { const account = context.accounts.find(a => a.id === params.id) || null; - if (!account) return null; + if (!account) { + logger.debug(`Account ${params.id} not found`); + return null; + } try { const bytes = AES.decrypt(account.macaroon, params.password); const decrypted = bytes.toString(CryptoJS.enc.Utf8); + logger.debug(`Correct password for account ${params.id}`); const token = jwt.sign( { id: params.id, @@ -33,7 +38,7 @@ export const getSessionToken = { ); return AES.encrypt(token, secret).toString(); } catch (error) { - throw new Error('Wrong password'); + throw new Error('WrongPasswordForLogin'); } }, }; diff --git a/api/schemas/query/general/nodeInfo.ts b/api/schemas/query/general/nodeInfo.ts index 5a16975f..55ff9bce 100644 --- a/api/schemas/query/general/nodeInfo.ts +++ b/api/schemas/query/general/nodeInfo.ts @@ -1,12 +1,8 @@ import { getWalletInfo, getClosedChannels } from 'ln-service'; import { ContextType } from 'api/types/apiTypes'; -import { logger } from '../../../helpers/logger'; +import { to } from 'api/helpers/async'; import { requestLimiter } from '../../../helpers/rateLimiter'; -import { - getAuthLnd, - getErrorMsg, - getCorrectAuth, -} from '../../../helpers/helpers'; +import { getAuthLnd, getCorrectAuth } from '../../../helpers/helpers'; import { defaultParams } from '../../../helpers/defaultProps'; import { NodeInfoType } from '../../types/QueryType'; @@ -36,20 +32,21 @@ export const getNodeInfo = { const auth = getCorrectAuth(params.auth, context); const lnd = getAuthLnd(auth); - try { - const info: NodeInfoProps = await getWalletInfo({ + const info = await to( + getWalletInfo({ lnd, - }); - const closedChannels: { channels: [] } = await getClosedChannels({ + }) + ); + + const closedChannels = await to( + getClosedChannels({ lnd, - }); - return { - ...info, - closed_channels_count: closedChannels.channels.length, - }; - } catch (error) { - logger.error('Error getting node info: %o', error); - throw new Error(getErrorMsg(error)); - } + }) + ); + + return { + ...info, + closed_channels_count: closedChannels?.channels?.length || 0, + }; }, }; diff --git a/pages/api/v1.tsx b/pages/api/v1.tsx index a5768395..5ecd8aba 100644 --- a/pages/api/v1.tsx +++ b/pages/api/v1.tsx @@ -43,6 +43,7 @@ const apolloServer = new ApolloServer({ let ssoVerified = false; if (req?.cookies?.SSOAuth) { + logger.silly('SSOAuth cookie found in request'); try { jwt.verify(req.cookies.SSOAuth, secret); ssoVerified = true; @@ -53,6 +54,7 @@ const apolloServer = new ApolloServer({ let account = null; if (req?.cookies?.AccountAuth) { + logger.silly('AccountAuth cookie found in request'); try { const bytes = AES.decrypt(req.cookies.AccountAuth, secret); const decrypted = bytes.toString(CryptoJS.enc.Utf8); diff --git a/src/components/chat/ChatFetcher.tsx b/src/components/chat/ChatFetcher.tsx index 2d10dc16..c6b1bf37 100644 --- a/src/components/chat/ChatFetcher.tsx +++ b/src/components/chat/ChatFetcher.tsx @@ -3,6 +3,7 @@ import { toast } from 'react-toastify'; import { useRouter } from 'next/router'; import { useAccountState } from 'src/context/AccountContext'; import { useGetMessagesQuery } from 'src/graphql/queries/__generated__/getMessages.generated'; +import { useStatusState } from 'src/context/StatusContext'; import { useChatState, useChatDispatch } from '../../context/ChatContext'; import { getErrorContent } from '../../utils/error'; import { useConfigState } from '../../context/ConfigContext'; @@ -12,6 +13,7 @@ export const ChatFetcher = () => { const { chatPollingSpeed } = useConfigState(); + const { connected } = useStatusState(); const { auth } = useAccountState(); const { pathname } = useRouter(); const { lastChat, chats, sentChats, initialized } = useChatState(); @@ -20,7 +22,7 @@ export const ChatFetcher = () => { const noChatsAvailable = chats.length <= 0 && sentChats.length <= 0; const { data, loading, error } = useGetMessagesQuery({ - skip: !auth || initialized || noChatsAvailable, + skip: !auth || initialized || noChatsAvailable || !connected, pollInterval: chatPollingSpeed, fetchPolicy: 'network-only', variables: { auth, initialize: !noChatsAvailable }, @@ -28,7 +30,7 @@ export const ChatFetcher = () => { }); React.useEffect(() => { - if (data?.getMessages?.messages) { + if (data && data.getMessages?.messages) { const messages = [...data.getMessages.messages]; let index = -1; diff --git a/src/components/statusCheck/StatusCheck.tsx b/src/components/statusCheck/StatusCheck.tsx index 6d98c462..742429ca 100644 --- a/src/components/statusCheck/StatusCheck.tsx +++ b/src/components/statusCheck/StatusCheck.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; import { toast } from 'react-toastify'; import { useAccountState } from 'src/context/AccountContext'; import { useGetNodeInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated'; -import { useStatusDispatch } from '../../context/StatusContext'; +import { useStatusDispatch, useStatusState } from '../../context/StatusContext'; import { appendBasePath } from '../../utils/basePath'; export const StatusCheck = () => { @@ -11,9 +11,10 @@ export const StatusCheck = () => { const { push } = useRouter(); const { account, auth } = useAccountState(); + const { connected } = useStatusState(); const { data, loading, error, stopPolling } = useGetNodeInfoQuery({ - skip: !auth, + skip: !auth || !connected, fetchPolicy: 'network-only', variables: { auth }, pollInterval: 10000, @@ -55,7 +56,7 @@ export const StatusCheck = () => { dispatch({ type: 'connected', state }); } - }, [data, dispatch, error, loading, push, account]); + }, [data, dispatch, error, loading, push, account, stopPolling]); return null; }; diff --git a/src/utils/error.tsx b/src/utils/error.tsx index dd9ff4a7..4b158712 100644 --- a/src/utils/error.tsx +++ b/src/utils/error.tsx @@ -10,6 +10,12 @@ const getMessage = error => { return 'Did not find a possible route.'; case 'SendPaymentFail': return 'Failed to send this payments.'; + case 'AccountNotAuthenticated': + return 'This account is not authenticated.'; + case 'AccountTypeDoesNotExist': + return 'This account does not exist.'; + case 'WrongPasswordForLogin': + return 'Wrong password provided.'; default: return error; } diff --git a/src/views/login/SessionLogin.tsx b/src/views/login/SessionLogin.tsx index 563dbb10..54bf575d 100644 --- a/src/views/login/SessionLogin.tsx +++ b/src/views/login/SessionLogin.tsx @@ -14,6 +14,7 @@ import Cookies from 'js-cookie'; import { useGetCanConnectLazyQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated'; import { useGetSessionTokenLazyQuery } from 'src/graphql/queries/__generated__/getSessionToken.generated'; import { getAuthFromAccount } from 'src/context/helpers/context'; +import { getErrorContent } from 'src/utils/error'; import { SingleLine, Sub4Title, Card } from '../../components/generic/Styled'; import { getAuthObj } from '../../utils/auth'; import { ColorButton } from '../../components/buttons/colorButton/ColorButton'; @@ -43,8 +44,8 @@ export const SessionLogin = () => { const [getCanConnect, { data, loading }] = useGetCanConnectLazyQuery({ fetchPolicy: 'network-only', - onError: () => { - toast.error('Unable to connect to this node'); + onError: err => { + toast.error(getErrorContent(err)); dispatch({ type: 'disconnected' }); }, }); @@ -54,14 +55,14 @@ export const SessionLogin = () => { { data: sData, loading: sLoading }, ] = useGetSessionTokenLazyQuery({ fetchPolicy: 'network-only', - onError: () => { - toast.error('Wrong password'); + onError: err => { + toast.error(getErrorContent(err)); dispatch({ type: 'disconnected' }); }, }); useEffect(() => { - if (!sLoading && sData?.getSessionToken) { + if (!sLoading && sData && sData.getSessionToken) { Cookies.set('AccountAuth', sData.getSessionToken, { sameSite: 'strict', }); @@ -74,11 +75,21 @@ export const SessionLogin = () => { }, [sLoading, sData, push, getCanConnect, account]); useEffect(() => { - if (!loading && data?.getNodeInfo && account.type === SERVER_ACCOUNT) { + if ( + !loading && + data && + data.getNodeInfo && + account.type === SERVER_ACCOUNT + ) { dispatch({ type: 'connected' }); push(appendBasePath('/home')); } - if (!loading && data?.getNodeInfo && account.type === CLIENT_ACCOUNT) { + if ( + !loading && + data && + data.getNodeInfo && + account.type === CLIENT_ACCOUNT + ) { const bytes = CryptoJS.AES.decrypt(account.admin, pass); const decrypted = bytes.toString(CryptoJS.enc.Utf8);