From 937c9bbb7e2f538ebcb40aff41209924bd259acb Mon Sep 17 00:00:00 2001 From: 8de2fdb0 <89734465+8de2fdb0@users.noreply.github.com> Date: Mon, 9 Jun 2025 14:36:00 +0000 Subject: [PATCH] feat: add base path to front end - add docker build arg to set base path frontend is served under - set vite.config.ts base value based on docker build arg - update http variant request function to inject base path during calls to /api and /images - update how static assets are loaded from public folder to inject base path during vite build To use a base path build the docker image with --build-arg BASE_PATH=/hub/. All assets and api calls from the frontend will use that base path as prefix. The backend service is not using the base path, when using a proxy the base path has to be stripped. --- Dockerfile | 6 +++++ .../http/src/utils/request.ts | 15 +++++++++++ frontend/src/App.tsx | 2 +- .../connections/AlbyConnectionCard.tsx | 7 ++++-- .../layouts/TwoColumnFullScreenLayout.tsx | 25 +++++++++++++------ .../channels/IncreaseIncomingCapacity.tsx | 10 ++++---- .../channels/IncreaseOutgoingCapacity.tsx | 10 ++++---- .../src/screens/channels/auto/AutoChannel.tsx | 7 ++++-- .../screens/channels/first/FirstChannel.tsx | 7 ++++-- .../src/screens/subwallets/SubwalletIntro.tsx | 13 ++++------ frontend/vite.config.ts | 5 ++++ 11 files changed, 74 insertions(+), 33 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7c4827f99..6681efb2e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,12 @@ FROM node:20-alpine as frontend + +# Set the base path for the frontend build +# This can be overridden at build time with --build-arg BASE_PATH= +# Allows to build a frontend that can be served from a subpath, e.g. /hub/ +ARG BASE_PATH="/" WORKDIR /build COPY frontend ./frontend +RUN echo "Building frontend with base path $BASE_PATH" RUN cd frontend && yarn install --network-timeout 3000000 && yarn build:http FROM golang:1.24 as builder diff --git a/frontend/platform_specific/http/src/utils/request.ts b/frontend/platform_specific/http/src/utils/request.ts index 809b5486a..9f67be94d 100644 --- a/frontend/platform_specific/http/src/utils/request.ts +++ b/frontend/platform_specific/http/src/utils/request.ts @@ -1,9 +1,24 @@ import { getAuthToken } from "src/lib/auth"; import { ErrorResponse } from "src/types"; +const BASE_URL = import.meta.env.BASE_URL; +const PREFIXES = ["/api", "/images"]; + +function startsWithPrefix(path: string, prefixes: string[]): boolean { + return prefixes.some((prefix) => path.startsWith(prefix)); +} + export const request = async ( ...args: Parameters ): Promise => { + if ( + BASE_URL !== "/" && + typeof args[0] === "string" && + startsWithPrefix(args[0], PREFIXES) + ) { + args[0] = BASE_URL + args[0].slice(1); + } + const token = getAuthToken(); if (token) { if (!args[1]) { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 23f44e30b..cd07ddc21 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -13,7 +13,7 @@ import routes from "src/routes.tsx"; import { isHttpMode } from "src/utils/isHttpMode"; const createRouterFunc = isHttpMode() ? createBrowserRouter : createHashRouter; -const router = createRouterFunc(routes); +const router = createRouterFunc(routes, { basename: import.meta.env.BASE_URL }); function App() { const { data: info } = useInfo(); diff --git a/frontend/src/components/connections/AlbyConnectionCard.tsx b/frontend/src/components/connections/AlbyConnectionCard.tsx index 058a35d77..dbaf802ef 100644 --- a/frontend/src/components/connections/AlbyConnectionCard.tsx +++ b/frontend/src/components/connections/AlbyConnectionCard.tsx @@ -46,6 +46,9 @@ import { useAlbyMe } from "src/hooks/useAlbyMe"; import { LinkStatus, useLinkAccount } from "src/hooks/useLinkAccount"; import { App, BudgetRenewalType } from "src/types"; +import AlbyAccountDarkSVG from "public/images/illustrations/alby-account-dark.svg"; +import AlbyAccountLightSVG from "public/images/illustrations/alby-account-light.svg"; + function AlbyConnectionCard({ connection }: { connection?: App }) { const { data: albyMe } = useAlbyMe(); const { loading, linkStatus, loadingLinkStatus, linkAccount } = @@ -109,11 +112,11 @@ function AlbyConnectionCard({ connection }: { connection?: App }) { every app you access through your Alby Account will handle payments via the Hub. You can add a budget that will restrict how much can be diff --git a/frontend/src/components/layouts/TwoColumnFullScreenLayout.tsx b/frontend/src/components/layouts/TwoColumnFullScreenLayout.tsx index b619a22a8..67067cff1 100644 --- a/frontend/src/components/layouts/TwoColumnFullScreenLayout.tsx +++ b/frontend/src/components/layouts/TwoColumnFullScreenLayout.tsx @@ -5,6 +5,15 @@ import { AlbyHubLogo } from "src/components/icons/AlbyHubLogo"; import { Button } from "src/components/ui/button.tsx"; import { useInfo } from "src/hooks/useInfo"; +import AntonopoulosSVG from "public/images/quotes/antonopoulos.svg"; +import BackSVG from "public/images/quotes/back.svg"; +import FinneySVG from "public/images/quotes/finney.svg"; +import HayekSVG from "public/images/quotes/hayek.svg"; +import NakamotoSVG from "public/images/quotes/nakamoto.svg"; +import ObamaSVG from "public/images/quotes/obama.svg"; +import RolandSVG from "public/images/quotes/roland.svg"; +import WilsonSVG from "public/images/quotes/wilson.svg"; + const quotes = [ { content: `This isn't about nation-states anymore. This isn't about who adopts @@ -13,42 +22,42 @@ const quotes = [ largest economy. It is the first transnational economy, and it needs a transnational currency.`, author: "Andreas M. Antonopoulos", - imageUrl: "/images/quotes/antonopoulos.svg", + imageUrl: AntonopoulosSVG, }, { content: `It might make sense just to get some in case it catches on. If enough people think the same way, that becomes a self fulfilling prophecy. Once it gets bootstrapped, there are so many applications if you could effortlessly pay a few cents to a website as easily as dropping coins in a vending machine.`, author: "Satoshi Nakamoto", - imageUrl: "/images/quotes/nakamoto.svg", + imageUrl: NakamotoSVG, }, { content: `Since we're all rich with bitcoins, or we will be once they're worth a million dollars like everyone expects, we ought to put some of this unearned wealth to good use.`, author: "Hal Finney", - imageUrl: "/images/quotes/finney.svg", + imageUrl: FinneySVG, }, { content: `I don't believe we shall ever have a good money again before we take the thing out of the hands of government, that is, we can't take it violently out of the hands of government, all we can do is by some sly roundabout way introduce something that they can't stop.`, author: "Friedrich August von Hayek", - imageUrl: "/images/quotes/hayek.svg", + imageUrl: HayekSVG, }, { content: `Bitcoin is what they fear it is.`, author: "Cody Wilson", - imageUrl: "/images/quotes/wilson.svg", + imageUrl: WilsonSVG, }, { content: `If in fact you can't crack that at all, government can't get in then —everybody's walking around with a Swiss bank account in their pocket.`, author: "Barack Obama", - imageUrl: "/images/quotes/obama.svg", + imageUrl: ObamaSVG, }, { content: `Bitcoin is the new wonder of the world, more work and human ingenuity, than went into the great pyramids of Egypt. The biggest computation ever done, a digital monument, a verifiable artefact of digital gold - the foundation of a new digital age.`, author: "Adam Back", - imageUrl: "/images/quotes/back.svg", + imageUrl: BackSVG, }, { content: `We who choose Bitcoin, are pioneers of a new world. A world filled with freedom, hope and peace.`, author: "Roland", - imageUrl: "/images/quotes/roland.svg", + imageUrl: RolandSVG, }, ]; diff --git a/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx b/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx index 476d9fc12..a4b22809b 100644 --- a/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx +++ b/frontend/src/screens/channels/IncreaseIncomingCapacity.tsx @@ -44,6 +44,9 @@ import { RecommendedChannelPeer, } from "src/types"; +import LightningNetworkDarkSVG from "public/images/illustrations/lightning-network-dark.svg"; +import LightningNetworkLightSVG from "public/images/illustrations/lightning-network-light.svg"; + function getPeerKey(peer: RecommendedChannelPeer) { return JSON.stringify(peer); } @@ -229,13 +232,10 @@ function NewChannelInternal({ />
- +

Alby Hub works with selected service providers (LSPs) which provide the best network connectivity and liquidity to receive payments.{" "} diff --git a/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx b/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx index 570dbdf23..d3219685a 100644 --- a/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx +++ b/frontend/src/screens/channels/IncreaseOutgoingCapacity.tsx @@ -47,6 +47,9 @@ import { } from "src/types"; import { request } from "src/utils/request"; +import LightningNetworkDarkSVG from "public/images/illustrations/lightning-network-dark.svg"; +import LightningNetworkLightSVG from "public/images/illustrations/lightning-network-light.svg"; + function getPeerKey(peer: RecommendedChannelPeer) { return JSON.stringify(peer); } @@ -200,13 +203,10 @@ function NewChannelInternal({ network }: { network: Network }) { />

- +

Open a channel with on-chain funds. Both parties are free to close the channel at any time. However, by keeping more funds on your side of diff --git a/frontend/src/screens/channels/auto/AutoChannel.tsx b/frontend/src/screens/channels/auto/AutoChannel.tsx index c96c1dbaf..e516aca04 100644 --- a/frontend/src/screens/channels/auto/AutoChannel.tsx +++ b/frontend/src/screens/channels/auto/AutoChannel.tsx @@ -20,6 +20,9 @@ import { MempoolAlert } from "src/components/MempoolAlert"; import { PayLightningInvoice } from "src/components/PayLightningInvoice"; import { ChannelPublicPrivateAlert } from "src/components/channels/ChannelPublicPrivateAlert"; +import LightningNetworkDarkSVG from "public/images/illustrations/lightning-network-dark.svg"; +import LightningNetworkLightSVG from "public/images/illustrations/lightning-network-light.svg"; + export function AutoChannel() { const { data: info } = useInfo(); const { data: channels } = useChannels(true); @@ -134,11 +137,11 @@ export function AutoChannel() { <>

diff --git a/frontend/src/screens/channels/first/FirstChannel.tsx b/frontend/src/screens/channels/first/FirstChannel.tsx index 849057e20..03f319214 100644 --- a/frontend/src/screens/channels/first/FirstChannel.tsx +++ b/frontend/src/screens/channels/first/FirstChannel.tsx @@ -23,6 +23,9 @@ import { PayLightningInvoice } from "src/components/PayLightningInvoice"; import { Table, TableBody, TableCell, TableRow } from "src/components/ui/table"; import { ALBY_MIN_HOSTED_BALANCE_FOR_FIRST_CHANNEL } from "src/constants"; +import LightningNetworkDarkSVG from "public/images/illustrations/lightning-network-dark.svg"; +import LightningNetworkLightSVG from "public/images/illustrations/lightning-network-light.svg"; + export function FirstChannel() { const { data: info } = useInfo(); const { data: channels } = useChannels(true); @@ -167,11 +170,11 @@ export function FirstChannel() { <>
{canPayForFirstChannel ? ( diff --git a/frontend/src/screens/subwallets/SubwalletIntro.tsx b/frontend/src/screens/subwallets/SubwalletIntro.tsx index bf365469e..cf3bd80a4 100644 --- a/frontend/src/screens/subwallets/SubwalletIntro.tsx +++ b/frontend/src/screens/subwallets/SubwalletIntro.tsx @@ -11,6 +11,9 @@ import ExternalLink from "src/components/ExternalLink"; import ResponsiveButton from "src/components/ResponsiveButton"; import { Button } from "src/components/ui/button"; +import SubWalletDarkSVG from "public/images/illustrations/sub-wallet-dark.svg"; +import SubWalletLightSVG from "public/images/illustrations/sub-wallet-light.svg"; + export function SubwalletIntro() { return (
@@ -33,14 +36,8 @@ export function SubwalletIntro() {
- - + +
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 0085afa27..eef2345dd 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -64,6 +64,10 @@ export default defineConfig(({ command }) => ({ alias: { src: path.resolve(__dirname, "./src"), wailsjs: path.resolve(__dirname, "./wailsjs"), + // used to refrence public assets when importing images or other + // assets from the public folder + // this is necessary to inject the base path during build + public: "", }, }, build: { @@ -75,6 +79,7 @@ export default defineConfig(({ command }) => ({ cspNonce: "DEVELOPMENT", } : undefined, + base: process.env.BASE_PATH || "/", })); const DEVELOPMENT_NONCE = "'nonce-DEVELOPMENT'";