refactor!: 💥 ♻️ basepath (#187)

This commit is contained in:
Anthony Potdevin 2020-12-16 13:52:59 +01:00 committed by GitHub
parent 1dcea164d5
commit 49cde99d69
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 183 additions and 87 deletions

1
.env
View file

@ -10,7 +10,6 @@
# Server Configs
# -----------
# LOG_LEVEL='info'
# BASE_PATH='/basePath'
# -----------
# Interface Configs

View file

@ -1,18 +1,33 @@
# ---------------
# Install Dependencies
# ---------------
FROM node:14.15-alpine as build
FROM node:14.15-alpine as deps
WORKDIR /app
# Install dependencies neccesary for node-gyp on node alpine
RUN apk add --update --no-cache \
libc6-compat \
python \
make \
g++
# Install app dependencies
COPY package.json .
COPY package-lock.json .
RUN npm install --silent
COPY package.json package-lock.json ./
RUN npm install
# ---------------
# Build App
# ---------------
FROM deps as build
WORKDIR /app
# Set env variables
ARG BASE_PATH=""
ENV BASE_PATH=${BASE_PATH}
ENV NEXT_TELEMETRY_DISABLED=1
# Build the NextJS application
COPY . .
@ -22,18 +37,24 @@ RUN npm run build
RUN npm prune --production
# ---------------
# Build App
# Release App
# ---------------
FROM node:14.15-alpine
WORKDIR /app
# Copy dependencies and build from build stage
COPY --from=build node_modules node_modules
COPY --from=build .next .next
# Set env variables
ARG BASE_PATH=""
ENV BASE_PATH=${BASE_PATH}
ENV NEXT_TELEMETRY_DISABLED=1
# Bundle app source
COPY . .
EXPOSE 3000
COPY --from=build /app/package.json /app/package-lock.json /app/next.config.js ./
COPY --from=build /app/public ./public
COPY --from=build /app/node_modules/ ./node_modules
COPY --from=build /app/.next/ ./.next
COPY ./scripts/initCookie.sh ./scripts/initCookie.sh
EXPOSE 3000
CMD [ "npm", "start" ]

View file

@ -112,7 +112,6 @@ You can define some environment variables that ThunderHub can start with. To do
# Server Configs
# -----------
LOG_LEVEL = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly' # Default: 'info'
BASE_PATH = '[Base path where you want to have thunderhub running i.e. '/btcpay']' # Default: ''
# -----------
# Interface Configs
@ -298,19 +297,38 @@ If you want to disable this option you can set `NO_VERSION_CHECK=true` in your `
### Running on different base path
Adding a BASE_PATH will run the ThunderHub server on a different base path.
```bash
# -----------
# Server Configs
# -----------
BASE_PATH = '[Base path where you want to have thunderhub running i.e. '/btcpay']' # Default: ''
```
For example:
- by default ThunderHub runs on `http://localhost:3000`
- base path of `/thub` runs ThunderHub on `http://localhost:3000/thub`
**Notice - If you don't need to run ThunderHub on a different base path don't add this variable in your file**
**You need to add this environment variable BEFORE building the application**
To run on a base path, ThunderHub needs to be behind a proxy with the following configuration (NGINX example):
There is a prebuilt [Docker](https://hub.docker.com/repository/docker/apotdevin/thunderhub) image with a preset `BASE_PATH=/thub` in case you need it and prefer not building your own Docker image.
```bash
# Normal docker image
docker pull apotdevin/thunderhub:v0.11.1
# Preset basePath docker image
docker pull apotdevin/thunderhub:base-v0.11.1
```
To build your own docker image with the `basePath` of your choice you can use `docker build --build-arg BASE_PATH='/thub' -t myOwnDockerImage .`
You can run ThunderHub behind a proxy with the following configuration (NGINX example):
```nginx
location /thub/ {
rewrite ^/thub(.*)$ $1 break;
proxy_pass http://localhost:3000/;
location /thub {
proxy_pass http://localhost:3000/thub;
}
```
@ -422,7 +440,7 @@ ThunderHub also provides docker images for easier deployment. [Docker Hub](https
To get ThunderHub running with docker follow these steps:
1. `docker pull apotdevin/thunderhub:v0.5.5` (Or the latest version you find)
1. `docker pull apotdevin/thunderhub:v0.11.1` (Or the latest version you find)
2. `docker run --rm -it -p 3000:3000/tcp apotdevin/thunderhub:v0.5.5`
You can now go to `localhost:3000` to see your running instance of ThunderHub

View file

@ -1,18 +1,33 @@
# ---------------
# Install Dependencies
# ---------------
FROM arm32v7/node:14.15-alpine as build
FROM arm32v7/node:14.15-alpine as deps
WORKDIR /app
# Install dependencies neccesary for node-gyp on node alpine
RUN apk add --update --no-cache \
libc6-compat \
python \
make \
g++
# Install app dependencies
COPY package.json .
COPY package-lock.json .
RUN npm install --silent
COPY package.json package-lock.json ./
RUN npm install
# ---------------
# Build App
# ---------------
FROM deps as build
WORKDIR /app
# Set env variables
ARG BASE_PATH=""
ENV BASE_PATH=${BASE_PATH}
ENV NEXT_TELEMETRY_DISABLED=1
# Build the NextJS application
COPY . .
@ -22,18 +37,24 @@ RUN npm run build
RUN npm prune --production
# ---------------
# Build App
# Release App
# ---------------
FROM arm32v7/node:14.15-alpine
WORKDIR /app
# Copy dependencies and build from build stage
COPY --from=build node_modules node_modules
COPY --from=build .next .next
# Set env variables
ARG BASE_PATH=""
ENV BASE_PATH=${BASE_PATH}
ENV NEXT_TELEMETRY_DISABLED=1
# Bundle app source
COPY . .
EXPOSE 3000
COPY --from=build /app/package.json /app/package-lock.json /app/next.config.js ./
COPY --from=build /app/public ./public
COPY --from=build /app/node_modules/ ./node_modules
COPY --from=build /app/.next/ ./.next
COPY ./scripts/initCookie.sh ./scripts/initCookie.sh
EXPOSE 3000
CMD [ "npm", "start" ]

View file

@ -1,18 +1,33 @@
# ---------------
# Install Dependencies
# ---------------
FROM arm64v8/node:14.15-alpine as build
FROM arm64v8/node:14.15-alpine as deps
WORKDIR /app
# Install dependencies neccesary for node-gyp on node alpine
RUN apk add --update --no-cache \
libc6-compat \
python \
make \
g++
# Install app dependencies
COPY package.json .
COPY package-lock.json .
RUN npm install --silent
COPY package.json package-lock.json ./
RUN npm install
# ---------------
# Build App
# ---------------
FROM deps as build
WORKDIR /app
# Set env variables
ARG BASE_PATH=""
ENV BASE_PATH=${BASE_PATH}
ENV NEXT_TELEMETRY_DISABLED=1
# Build the NextJS application
COPY . .
@ -22,18 +37,24 @@ RUN npm run build
RUN npm prune --production
# ---------------
# Build App
# Release App
# ---------------
FROM arm64v8/node:14.15-alpine
WORKDIR /app
# Copy dependencies and build from build stage
COPY --from=build node_modules node_modules
COPY --from=build .next .next
# Set env variables
ARG BASE_PATH=""
ENV BASE_PATH=${BASE_PATH}
ENV NEXT_TELEMETRY_DISABLED=1
# Bundle app source
COPY . .
EXPOSE 3000
COPY --from=build /app/package.json /app/package-lock.json /app/next.config.js ./
COPY --from=build /app/public ./public
COPY --from=build /app/node_modules/ ./node_modules
COPY --from=build /app/.next/ ./.next
COPY ./scripts/initCookie.sh ./scripts/initCookie.sh
EXPOSE 3000
CMD [ "npm", "start" ]

View file

@ -5,7 +5,7 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
module.exports = withBundleAnalyzer({
poweredByHeader: false,
assetPrefix: process.env.BASE_PATH || '',
basePath: process.env.BASE_PATH || '',
serverRuntimeConfig: {
nodeEnv: process.env.NODE_ENV || 'development',
logLevel: process.env.LOG_LEVEL || 'info',

View file

@ -1,4 +1,3 @@
import * as React from 'react';
import { ThemeProvider } from 'styled-components';
import { ModalProvider, BaseModalBackground } from 'styled-react-modal';
import { useRouter } from 'next/router';
@ -40,14 +39,17 @@ const Wrapper: React.FC = ({ children }) => {
};
export default function App({ Component, pageProps }: AppProps) {
const apolloClient = useApollo(pageProps.initialApolloState);
const { initialApolloState, initialConfig, hasToken } = pageProps;
const apolloClient = useApollo(initialApolloState);
return (
<ApolloProvider client={apolloClient}>
<Head>
<title>ThunderHub - Lightning Node Manager</title>
</Head>
<ConfigProvider initialConfig={pageProps.initialConfig}>
<BaseProvider initialHasToken={pageProps.hasToken}>
<ConfigProvider initialConfig={initialConfig}>
<BaseProvider initialHasToken={hasToken}>
<ContextProvider>
<Wrapper>
<Component {...pageProps} />

View file

@ -5,7 +5,6 @@ import { NodeInfo } from 'src/views/scores/NodeInfo';
import { useRouter } from 'next/router';
import { useBaseDispatch, useBaseState } from 'src/context/BaseContext';
import { useEffect, useState } from 'react';
import { appendBasePath } from 'src/utils/basePath';
import { NodeScores } from 'src/views/scores/NodeScores';
import { Graph } from 'src/views/scores/NodeGraph';
import { useDeleteBaseTokenMutation } from 'src/graphql/mutations/__generated__/deleteBaseToken.generated';
@ -22,14 +21,14 @@ const NodeScoreView = () => {
useEffect(() => {
if (!hasToken) {
push(appendBasePath('/token'));
push('/token');
}
}, [hasToken, push]);
const handleAuthError = () => {
dispatch({ type: 'change', hasToken: false });
deleteToken();
push(appendBasePath('/token'));
push('/token');
};
return (

View file

@ -1,6 +1,7 @@
#!/bin/sh
REPO=apotdevin/thunderhub
BASE=base
echo
echo
@ -47,6 +48,9 @@ echo
docker build --pull -t $REPO:$VERSION-amd64 -f Dockerfile .
docker push $REPO:$VERSION-amd64
docker build --build-arg BASE_PATH='/thub' --pull -t $REPO:$BASE-$VERSION-amd64 -f Dockerfile .
docker push $REPO:$BASE-$VERSION-amd64
ENDAMD=`date +%s`
echo
@ -60,6 +64,9 @@ echo
docker build --pull -t $REPO:$VERSION-arm32v7 -f arm32v7.Dockerfile .
docker push $REPO:$VERSION-arm32v7
docker build --build-arg BASE_PATH='/thub' --pull -t $REPO:$BASE-$VERSION-arm32v7 -f arm32v7.Dockerfile .
docker push $REPO:$BASE-$VERSION-arm32v7
ENDARM32=`date +%s`
echo
@ -73,6 +80,9 @@ echo
docker build --pull -t $REPO:$VERSION-arm64v8 -f arm64v8.Dockerfile .
docker push $REPO:$VERSION-arm64v8
docker build --build-arg BASE_PATH='/thub' --pull -t $REPO:$BASE-$VERSION-arm64v8 -f arm64v8.Dockerfile .
docker push $REPO:$BASE-$VERSION-arm64v8
ENDARM64=`date +%s`
echo
@ -89,6 +99,28 @@ docker manifest annotate $REPO:$VERSION $REPO:$VERSION-arm32v7 --os linux --arch
docker manifest annotate $REPO:$VERSION $REPO:$VERSION-arm64v8 --os linux --arch arm64 --variant v8
docker manifest push $REPO:$VERSION -p
echo
echo
echo "------------------------------------------"
echo "Creating manifest for version" $BASE $VERSION
echo "------------------------------------------"
echo
echo
docker manifest create --amend $REPO:$BASE-$VERSION $REPO:$BASE-$VERSION-amd64 $REPO:$BASE-$VERSION-arm32v7 $REPO:$BASE-$VERSION-arm64v8
docker manifest annotate $REPO:$BASE-$VERSION $REPO:$BASE-$VERSION-amd64 --os linux --arch amd64
docker manifest annotate $REPO:$BASE-$VERSION $REPO:$BASE-$VERSION-arm32v7 --os linux --arch arm --variant v7
docker manifest annotate $REPO:$BASE-$VERSION $REPO:$BASE-$VERSION-arm64v8 --os linux --arch arm64 --variant v8
docker manifest push $REPO:$BASE-$VERSION -p
echo
echo
echo "------------------------------------------"
echo "Build Stats"
echo "------------------------------------------"
echo
echo
RUNTIME=$((ENDAMD-START))
RUNTIME1=$((ENDARM32-ENDAMD))
RUNTIME2=$((ENDARM64-ENDARM32))
@ -103,7 +135,7 @@ echo "DONE"
echo "------------------------------------------"
echo
echo
echo "Finished building and pushing images for" $REPO:$VERSION
echo "Finished building and pushing images for" $REPO:$VERSION "and for" $REPO:$BASE-$VERSION
echo
echo "amd64 took" $RUNTIME "seconds"
echo "arm32v7 took" $RUNTIME1 "seconds"

View file

@ -1,6 +1,5 @@
import * as React from 'react';
import { useRouter } from 'next/router';
import { appendBasePath } from 'src/utils/basePath';
import { getUrlParam } from 'src/utils/url';
import { toast } from 'react-toastify';
import { getErrorContent } from 'src/utils/error';
@ -20,7 +19,7 @@ export const ServerAccounts: React.FC = () => {
refetchQueries: ['GetNodeInfo'],
onError: error => {
toast.error(getErrorContent(error));
push(logoutUrl || appendBasePath('/login'));
logoutUrl ? (window.location.href = logoutUrl) : push('/login');
},
});
@ -28,14 +27,14 @@ export const ServerAccounts: React.FC = () => {
if (cookieParam) {
getToken();
} else {
push(logoutUrl || appendBasePath('/login'));
logoutUrl ? (window.location.href = logoutUrl) : push('/login');
}
}, [cookieParam, push, getToken]);
React.useEffect(() => {
if (!cookieParam || !data) return;
if (data.getAuthToken) {
push(appendBasePath('/'));
push('/');
}
if (!data.getAuthToken) {
toast.warning('Unable to SSO. Check your logs.');

View file

@ -2,7 +2,6 @@ import React from 'react';
import styled, { css } from 'styled-components';
import { ThemeSet } from 'styled-theming';
import RouterLink from 'next/link';
import getConfig from 'next/config';
import { textColor, linkHighlight } from '../../styles/Themes';
interface StyledProps {
@ -51,9 +50,6 @@ interface LinkProps {
newTab?: boolean;
}
const { publicRuntimeConfig } = getConfig();
const { basePath } = publicRuntimeConfig;
export const Link: React.FC<LinkProps> = ({
children,
href,
@ -84,13 +80,8 @@ export const Link: React.FC<LinkProps> = ({
}
if (to) {
const linkProps = {
href: to,
...(basePath !== '' ? { as: `${basePath}${to}` } : {}),
};
return (
<RouterLink {...linkProps}>
<RouterLink href={to}>
<CorrectLink {...props}>{children}</CorrectLink>
</RouterLink>
);

View file

@ -8,7 +8,6 @@ import styled from 'styled-components';
import { themeColors } from 'src/styles/Themes';
import ScaleLoader from 'react-spinners/ScaleLoader';
import getConfig from 'next/config';
import { appendBasePath } from '../../utils/basePath';
import { useChatDispatch } from '../../context/ChatContext';
const { publicRuntimeConfig } = getConfig();
@ -38,7 +37,7 @@ export const LogoutButton = () => {
dispatchChat({ type: 'disconnected' });
client.clearStore();
push(logoutUrl || appendBasePath('/login'));
logoutUrl ? (window.location.href = logoutUrl) : push('/login');
}
}, [data, dispatchChat, push, client]);

View file

@ -3,7 +3,6 @@ import { useRouter } from 'next/router';
import { toast } from 'react-toastify';
import { useGetNodeInfoQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
import getConfig from 'next/config';
import { appendBasePath } from '../../utils/basePath';
const { publicRuntimeConfig } = getConfig();
const { logoutUrl } = publicRuntimeConfig;
@ -21,7 +20,7 @@ export const StatusCheck: React.FC = () => {
if (error) {
toast.error(`Unable to connect to node`);
stopPolling();
push(logoutUrl || appendBasePath('/login'));
logoutUrl ? (window.location.href = logoutUrl) : push('/login');
}
}, [error, push, stopPolling]);

View file

@ -7,7 +7,7 @@ import { GET_AUTH_TOKEN } from 'src/graphql/mutations/getAuthToken';
import getConfig from 'next/config';
const { publicRuntimeConfig } = getConfig();
const { logoutUrl } = publicRuntimeConfig;
const { logoutUrl, basePath } = publicRuntimeConfig;
const cookieProps = (
context: NextPageContext,
@ -21,7 +21,7 @@ const cookieProps = (
const hasToken = !!cookies[appConstants.tokenCookieName];
if (!cookies[appConstants.cookieName] && !noAuth) {
context.res?.writeHead(302, { Location: logoutUrl || '/login' });
context.res?.writeHead(302, { Location: logoutUrl || `${basePath}/login` });
context.res?.end();
return { theme: 'dark', authenticated: false, hasToken };
@ -83,7 +83,7 @@ export const getProps = async (
}
}
} else {
return { props: { initialConfig: { theme }, hasToken } };
return { props: { initialConfig: { theme }, hasToken, authenticated } };
}
return {
@ -91,6 +91,7 @@ export const getProps = async (
initialApolloState: apolloClient.cache.extract(),
initialConfig: { theme },
hasToken,
authenticated,
},
};
};

View file

@ -7,7 +7,6 @@ import { getPercent } from 'src/utils/helpers';
import { ChannelType } from 'src/graphql/types';
import { useRebalanceState } from 'src/context/RebalanceContext';
import { useRouter } from 'next/router';
import { appendBasePath } from 'src/utils/basePath';
import { Card } from '../../../components/generic/Styled';
import { getErrorContent } from '../../../utils/error';
import { LoadingCard } from '../../../components/loading/LoadingCard';
@ -35,7 +34,7 @@ export const Channels: React.FC = () => {
position: 'bottom-right',
autoClose: false,
closeButton: false,
onClick: () => push(appendBasePath('/rebalance')),
onClick: () => push('/rebalance'),
}
);
}

View file

@ -6,7 +6,6 @@ import { LoadingCard } from 'src/components/loading/LoadingCard';
import { useConfigDispatch, useConfigState } from 'src/context/ConfigContext';
import { useLnMarketsLoginMutation } from 'src/graphql/mutations/__generated__/lnMarkets.generated';
import { useGetLnMarketsStatusQuery } from 'src/graphql/queries/__generated__/getLnMarketsStatus.generated';
import { appendBasePath } from 'src/utils/basePath';
import { getErrorContent } from 'src/utils/error';
import getConfig from 'next/config';
import { QuickCard, QuickTitle } from '../QuickActions';
@ -36,7 +35,7 @@ export const LnMarketsCard = () => {
useEffect(() => {
if (data?.lnMarketsLogin?.status === 'OK') {
dispatch({ type: 'change', lnMarketsAuth: true });
push(appendBasePath('/lnmarkets'));
push('/lnmarkets');
}
}, [data, push, dispatch]);
@ -54,7 +53,7 @@ export const LnMarketsCard = () => {
if (lnMarketsAuth) {
return (
<QuickCard onClick={() => push(appendBasePath('/lnmarkets'))}>
<QuickCard onClick={() => push('/lnmarkets')}>
<Activity size={24} />
<QuickTitle>LnMarkets</QuickTitle>
</QuickCard>

View file

@ -4,7 +4,6 @@ import { toast } from 'react-toastify';
import { Lock, Unlock, ChevronDown, ChevronUp } from 'react-feather';
import { chartColors } from 'src/styles/Themes';
import { useRouter } from 'next/router';
import { appendBasePath } from 'src/utils/basePath';
import { useGetCanConnectLazyQuery } from 'src/graphql/queries/__generated__/getNodeInfo.generated';
import { Link } from 'src/components/link/Link';
import { useGetServerAccountsQuery } from 'src/graphql/queries/__generated__/getServerAccounts.generated';
@ -82,7 +81,7 @@ export const Accounts = () => {
const [logout] = useLogoutMutation({ refetchQueries: ['GetServerAccounts'] });
React.useEffect(() => {
prefetch(appendBasePath('/'));
prefetch('/');
}, [prefetch]);
const {
@ -100,7 +99,7 @@ export const Accounts = () => {
React.useEffect(() => {
if (!loading && data && data.getNodeInfo) {
push(appendBasePath('/'));
push('/');
}
}, [data, loading, push]);

View file

@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
import { toast } from 'react-toastify';
import styled from 'styled-components';
import { useRouter } from 'next/router';
import { appendBasePath } from 'src/utils/basePath';
import { getErrorContent } from 'src/utils/error';
import { Lock } from 'react-feather';
import { ServerAccountType } from 'src/graphql/types';
@ -56,7 +55,7 @@ export const Login = ({ account }: LoginProps) => {
'ThunderHub supports LND version 0.11.0 and higher. Please update your node, you are in risk of losing funds.'
);
} else {
push(appendBasePath('/'));
push('/');
}
}, [data, loading, push]);

View file

@ -4,7 +4,6 @@ import { getNodeLink, renderLine } from 'src/components/generic/helpers';
import { Link } from 'src/components/link/Link';
import { BosScore } from 'src/graphql/types';
import { themeColors } from 'src/styles/Themes';
import { appendBasePath } from 'src/utils/basePath';
import styled from 'styled-components';
type ScoreCardProps = {
@ -72,7 +71,7 @@ export const ScoreCard: FC<ScoreCardProps> = ({ score }) => {
const handleClick = () => {
if (score.public_key) {
push(appendBasePath(`/scores/${score.public_key}`));
push(`/scores/${score.public_key}`);
}
};

View file

@ -13,7 +13,6 @@ import {
} from '../../components/generic/Styled';
import { fontColors } from '../../styles/Themes';
import { ColorButton } from '../../components/buttons/colorButton/ColorButton';
import { appendBasePath } from '../../utils/basePath';
import { useChatDispatch } from '../../context/ChatContext';
const { publicRuntimeConfig } = getConfig();
@ -60,7 +59,8 @@ export const DangerView = () => {
const { push } = useRouter();
const [logout] = useLogoutMutation({
onCompleted: () => push(logoutUrl || appendBasePath('/login')),
onCompleted: () =>
logoutUrl ? (window.location.href = logoutUrl) : push('/login'),
});
const handleDeleteAll = () => {

View file

@ -6,7 +6,6 @@ import { Card, CardWithTitle, SubTitle } from 'src/components/generic/Styled';
import { InputWithDeco } from 'src/components/input/InputWithDeco';
import { useBaseDispatch } from 'src/context/BaseContext';
import { useCreateBaseTokenMutation } from 'src/graphql/mutations/__generated__/createBaseToken.generated';
import { appendBasePath } from 'src/utils/basePath';
import { getErrorContent } from 'src/utils/error';
export const RecoverToken = () => {
@ -22,7 +21,7 @@ export const RecoverToken = () => {
if (loading || !data?.createBaseToken) return;
dispatch({ type: 'change', hasToken: true });
toast.success('Succesfully recovered token');
push(appendBasePath('/scores'));
push('/scores');
}, [loading, data, dispatch, push]);
return (