Skip to content

Commit

Permalink
refactor: ♻️ server structure (#52)
Browse files Browse the repository at this point in the history
* refactor: ♻️ change schema

* chore: 🔧 small changes

* chore: 🔧 cleanup

* chore: 🔧 cleanup types

* chore: 🔧 change to absolute imports

Co-authored-by: apotdevin <[email protected]>
  • Loading branch information
apotdevin and apotdevin authored Jun 1, 2020
1 parent 63611d4 commit f80492b
Show file tree
Hide file tree
Showing 134 changed files with 3,061 additions and 3,904 deletions.
2 changes: 1 addition & 1 deletion config/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const { apiUrl: uri } = publicRuntimeConfig;
function createIsomorphLink(ctx) {
if (typeof window === 'undefined') {
const { SchemaLink } = require('apollo-link-schema');
const { thunderHubSchema: schema } = require('server/schemas');
const schema = require('server/schema');
return new SchemaLink({ schema, context: ctx });
} else {
const { HttpLink } = require('apollo-link-http');
Expand Down
4 changes: 2 additions & 2 deletions pages/api/v1.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import crypto from 'crypto';
import { ApolloServer } from 'apollo-server-micro';
import { thunderHubSchema } from 'server/schemas';
import { getIp } from 'server/helpers/helpers';
import getConfig from 'next/config';
import jwt from 'jsonwebtoken';
Expand All @@ -15,6 +14,7 @@ import { ContextType } from 'server/types/apiTypes';
import AES from 'crypto-js/aes';
import CryptoJS from 'crypto-js';
import cookie from 'cookie';
import schema from 'server/schema';

const { publicRuntimeConfig, serverRuntimeConfig } = getConfig();
const { apiBaseUrl, nodeEnv } = publicRuntimeConfig;
Expand All @@ -38,7 +38,7 @@ const accountConfig = getAccounts(accountConfigPath);
readCookie(cookiePath);

const apolloServer = new ApolloServer({
schema: thunderHubSchema,
schema,
context: ({ req, res }) => {
const ip = getIp(req);

Expand Down
6 changes: 0 additions & 6 deletions server/helpers/defaultProps.ts

This file was deleted.

File renamed without changes.
43 changes: 43 additions & 0 deletions server/schema/account/resolvers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ContextType } from 'server/types/apiTypes';
import { SSO_ACCOUNT, SERVER_ACCOUNT } from 'src/context/AccountContext';
import { logger } from 'server/helpers/logger';
import { requestLimiter } from 'server/helpers/rateLimiter';

export const accountResolvers = {
Query: {
getServerAccounts: 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 && host && ssoVerified) {
logger.debug(
`Macaroon${
cert ? ', certificate' : ''
} and host (${host}) found for SSO.`
);
ssoAccount = {
name: 'SSO Account',
id: SSO_ACCOUNT,
loggedIn: true,
type: SSO_ACCOUNT,
};
}

const currentId = account?.id;
const withStatus =
accounts?.map(a => ({
...a,
loggedIn: a.id === currentId,
type: SERVER_ACCOUNT,
})) || [];

return ssoAccount ? [...withStatus, ssoAccount] : withStatus;
},
},
};
10 changes: 10 additions & 0 deletions server/schema/account/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { gql } from 'apollo-server-micro';

export const accountTypes = gql`
type serverAccountType {
name: String!
id: String!
type: String!
loggedIn: Boolean!
}
`;
109 changes: 109 additions & 0 deletions server/schema/auth/resolvers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import getConfig from 'next/config';
import jwt from 'jsonwebtoken';
import { readCookie, refreshCookie } from 'server/helpers/fileHelpers';
import { ContextType } from 'server/types/apiTypes';
import { SSO_ACCOUNT, SERVER_ACCOUNT } from 'src/context/AccountContext';
import { logger } from 'server/helpers/logger';
import cookie from 'cookie';
import { requestLimiter } from 'server/helpers/rateLimiter';
import AES from 'crypto-js/aes';

const { serverRuntimeConfig } = getConfig();
const { cookiePath, nodeEnv } = serverRuntimeConfig;

export const authResolvers = {
Query: {
getAuthToken: async (_: undefined, params: any, context: ContextType) => {
const { ip, secret, sso, res } = context;
await requestLimiter(ip, 'getAuthToken');

if (!sso.host || !sso.macaroon) {
logger.warn('Host and macaroon are required for SSO');
return null;
}

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);

if (cookieFile === params.cookie || nodeEnv === 'development') {
refreshCookie(cookiePath);
const token = jwt.sign({ user: SSO_ACCOUNT }, secret);

res.setHeader(
'Set-Cookie',
cookie.serialize('SSOAuth', token, { httpOnly: true, sameSite: true })
);
return true;
}

return null;
},
getSessionToken: async (
_: undefined,
params: any,
context: ContextType
) => {
const { ip, secret, res } = context;
await requestLimiter(ip, 'getSessionToken');

const account = context.accounts.find(a => a.id === params.id) || null;

if (!account) {
logger.debug(`Account ${params.id} not found`);
return null;
}

try {
AES.decrypt(account.macaroon, params.password);
logger.debug(`Correct password for account ${params.id}`);
const token = jwt.sign(
{
id: params.id,
password: AES.encrypt(params.password, secret).toString(),
},
secret
);
res.setHeader(
'Set-Cookie',
cookie.serialize('AccountAuth', token, {
httpOnly: true,
sameSite: true,
})
);
return true;
} catch (error) {
throw new Error('WrongPasswordForLogin');
}
},
},
Mutation: {
logout: async (_: undefined, params: any, context: ContextType) => {
const { ip, res } = context;
await requestLimiter(ip, 'logout');

if (params.type === SSO_ACCOUNT) {
res.setHeader(
'Set-Cookie',
cookie.serialize('SSOAuth', '', { maxAge: 1 })
);
return true;
}
if (params.type === SERVER_ACCOUNT) {
res.setHeader(
'Set-Cookie',
cookie.serialize('AccountAuth', '', { maxAge: 1 })
);
return true;
}
return true;
},
},
};
47 changes: 47 additions & 0 deletions server/schema/bitcoin/resolvers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ContextType } from 'server/types/apiTypes';
import { logger } from 'server/helpers/logger';
import { requestLimiter } from 'server/helpers/rateLimiter';
import { appUrls } from 'server/utils/appUrls';

export const bitcoinResolvers = {
Query: {
getBitcoinPrice: async (
_: undefined,
params: any,
context: ContextType
) => {
await requestLimiter(context.ip, 'bitcoinPrice');

try {
const response = await fetch(appUrls.ticker);
const json = await response.json();

return JSON.stringify(json);
} catch (error) {
logger.error('Error getting bitcoin price: %o', error);
throw new Error('Problem getting Bitcoin price.');
}
},
getBitcoinFees: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'bitcoinFee');

try {
const response = await fetch(appUrls.fees);
const json = await response.json();

if (json) {
const { fastestFee, halfHourFee, hourFee } = json;
return {
fast: fastestFee,
halfHour: halfHourFee,
hour: hourFee,
};
}
throw new Error('Problem getting Bitcoin fees.');
} catch (error) {
logger.error('Error getting bitcoin fees: %o', error);
throw new Error('Problem getting Bitcoin fees.');
}
},
},
};
9 changes: 9 additions & 0 deletions server/schema/bitcoin/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { gql } from 'apollo-server-micro';

export const bitcoinTypes = gql`
type bitcoinFeeType {
fast: Int
halfHour: Int
hour: Int
}
`;
Loading

0 comments on commit f80492b

Please sign in to comment.