Skip to content

Commit 83270ff

Browse files
Add support for configurable domain sub-paths (#74)
1 parent 558d049 commit 83270ff

File tree

7 files changed

+101
-19
lines changed

7 files changed

+101
-19
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added `DOMAIN_SUB_PATH` environment variable to allow overriding the default domain subpath. ([#74](https://github.com/sourcebot-dev/sourcebot/pull/74))
13+
1014
## [2.4.3] - 2024-11-18
1115

1216
### Changed

Dockerfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ ENV NEXT_TELEMETRY_DISABLED=1
2626
# @see: https://phase.dev/blog/nextjs-public-runtime-variables/
2727
ARG NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED=BAKED_NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED
2828
ARG NEXT_PUBLIC_SOURCEBOT_VERSION=BAKED_NEXT_PUBLIC_SOURCEBOT_VERSION
29+
# @note: leading "/" is required for the basePath property. @see: https://nextjs.org/docs/app/api-reference/next-config-js/basePath
30+
ARG NEXT_PUBLIC_DOMAIN_SUB_PATH=/BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH
2931
RUN yarn workspace @sourcebot/web build
3032

3133
# ------ Build Backend ------
@@ -54,6 +56,11 @@ RUN echo "Sourcebot Version: $SOURCEBOT_VERSION"
5456
# Valid values are: debug, info, warn, error
5557
ENV SOURCEBOT_LOG_LEVEL=info
5658

59+
# Configures the sub-path of the domain to serve Sourcebot from.
60+
# For example, if DOMAIN_SUB_PATH is set to "/sb", Sourcebot
61+
# will serve from http(s)://example.com/sb
62+
ENV DOMAIN_SUB_PATH=/
63+
5764
# @note: This is also set in .env
5865
ENV POSTHOG_KEY=phc_VFn4CkEGHRdlVyOOw8mfkoj1DKVoG6y1007EClvzAnS
5966
ENV NEXT_PUBLIC_POSTHOG_KEY=$POSTHOG_KEY

entrypoint.sh

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,23 +70,69 @@ fi
7070

7171
echo -e "\e[34m[Info] Using config file at: '$CONFIG_PATH'.\e[0m"
7272

73-
# Update nextjs public env variables w/o requiring a rebuild.
73+
# Update NextJs public env variables w/o requiring a rebuild.
7474
# @see: https://phase.dev/blog/nextjs-public-runtime-variables/
75+
{
76+
# Infer NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED if it is not set
77+
if [ -z "$NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED" ] && [ ! -z "$SOURCEBOT_TELEMETRY_DISABLED" ]; then
78+
export NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED="$SOURCEBOT_TELEMETRY_DISABLED"
79+
fi
7580

76-
# Infer NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED if it is not set
77-
if [ -z "$NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED" ] && [ ! -z "$SOURCEBOT_TELEMETRY_DISABLED" ]; then
78-
export NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED="$SOURCEBOT_TELEMETRY_DISABLED"
79-
fi
81+
# Infer NEXT_PUBLIC_SOURCEBOT_VERSION if it is not set
82+
if [ -z "$NEXT_PUBLIC_SOURCEBOT_VERSION" ] && [ ! -z "$SOURCEBOT_VERSION" ]; then
83+
export NEXT_PUBLIC_SOURCEBOT_VERSION="$SOURCEBOT_VERSION"
84+
fi
8085

81-
# Infer NEXT_PUBLIC_SOURCEBOT_VERSION if it is not set
82-
if [ -z "$NEXT_PUBLIC_SOURCEBOT_VERSION" ] && [ ! -z "$SOURCEBOT_VERSION" ]; then
83-
export NEXT_PUBLIC_SOURCEBOT_VERSION="$SOURCEBOT_VERSION"
84-
fi
86+
# Iterate over all .js files in .next & public, making substitutions for the `BAKED_` sentinal values
87+
# with their actual desired runtime value.
88+
find /app/packages/web/public /app/packages/web/.next -type f -name "*.js" |
89+
while read file; do
90+
sed -i "s|BAKED_NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED|${NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED}|g" "$file"
91+
sed -i "s|BAKED_NEXT_PUBLIC_SOURCEBOT_VERSION|${NEXT_PUBLIC_SOURCEBOT_VERSION}|g" "$file"
92+
done
93+
}
94+
95+
96+
# Update specifically NEXT_PUBLIC_DOMAIN_SUB_PATH w/o requiring a rebuild.
97+
# Ultimately, the DOMAIN_SUB_PATH sets the `basePath` param in the next.config.mjs.
98+
# Similar to above, we pass in a `BAKED_` sentinal value into next.config.mjs at build
99+
# time. Unlike above, the `basePath` configuration is set in files other than just javascript
100+
# code (e.g., manifest files, css files, etc.), so this section has subtle differences.
101+
#
102+
# @see: https://nextjs.org/docs/app/api-reference/next-config-js/basePath
103+
# @see: https://phase.dev/blog/nextjs-public-runtime-variables/
104+
{
105+
if [ ! -z "$DOMAIN_SUB_PATH" ]; then
106+
# If the sub-path is "/", this creates problems with certain replacements. For example:
107+
# /BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH/_next/image -> //_next/image (notice the double slash...)
108+
# To get around this, we default to an empty sub-path, which is the default when no sub-path is defined.
109+
if [ "$DOMAIN_SUB_PATH" = "/" ]; then
110+
DOMAIN_SUB_PATH=""
111+
112+
# Otherwise, we need to ensure that the sub-path starts with a slash, since this is a requirement
113+
# for the basePath property. For example, assume DOMAIN_SUB_PATH=/bot, then:
114+
# /BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH/_next/image -> /bot/_next/image
115+
elif [[ ! "$DOMAIN_SUB_PATH" =~ ^/ ]]; then
116+
DOMAIN_SUB_PATH="/$DOMAIN_SUB_PATH"
117+
fi
118+
fi
119+
120+
if [ ! -z "$DOMAIN_SUB_PATH" ]; then
121+
echo -e "\e[34m[Info] DOMAIN_SUB_PATH was set to "$DOMAIN_SUB_PATH". Overriding default path.\e[0m"
122+
fi
123+
124+
# Always set NEXT_PUBLIC_DOMAIN_SUB_PATH to DOMAIN_SUB_PATH (even if it is empty!!)
125+
export NEXT_PUBLIC_DOMAIN_SUB_PATH="$DOMAIN_SUB_PATH"
126+
127+
# Iterate over _all_ files in the web directory, making substitutions for the `BAKED_` sentinal values
128+
# with their actual desired runtime value.
129+
find /app/packages/web -type f |
130+
while read file; do
131+
# @note: the leading "/" is required here as it is included at build time. See Dockerfile.
132+
sed -i "s|/BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH|${NEXT_PUBLIC_DOMAIN_SUB_PATH}|g" "$file"
133+
done
134+
}
85135

86-
find /app/packages/web/public /app/packages/web/.next -type f -name "*.js" |
87-
while read file; do
88-
sed -i "s|BAKED_NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED|${NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED}|g" "$file"
89-
sed -i "s|BAKED_NEXT_PUBLIC_SOURCEBOT_VERSION|${NEXT_PUBLIC_SOURCEBOT_VERSION}|g" "$file"
90-
done
91136

137+
# Run supervisord
92138
exec supervisord -c /etc/supervisor/conf.d/supervisord.conf

packages/web/next.config.mjs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ const nextConfig = {
2020
];
2121
},
2222
// This is required to support PostHog trailing slash API requests
23-
skipTrailingSlashRedirect: true,
23+
skipTrailingSlashRedirect: true,
24+
25+
// @note: this is evaluated at build time.
26+
...(process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH ? {
27+
basePath: process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH,
28+
} : {})
2429
};
2530

2631
export default nextConfig;

packages/web/src/app/api/(client)/client.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
'use client';
2+
3+
import { NEXT_PUBLIC_DOMAIN_SUB_PATH } from "@/lib/environment.client";
14
import { fileSourceResponseSchema, listRepositoriesResponseSchema, searchResponseSchema } from "@/lib/schemas";
25
import { FileSourceRequest, FileSourceResponse, ListRepositoriesResponse, SearchRequest, SearchResponse } from "@/lib/types";
36

47
export const search = async (body: SearchRequest): Promise<SearchResponse> => {
5-
const result = await fetch(`/api/search`, {
8+
const path = resolveServerPath("/api/search");
9+
const result = await fetch(path, {
610
method: "POST",
711
headers: {
812
"Content-Type": "application/json",
@@ -14,7 +18,8 @@ export const search = async (body: SearchRequest): Promise<SearchResponse> => {
1418
}
1519

1620
export const fetchFileSource = async (body: FileSourceRequest): Promise<FileSourceResponse> => {
17-
const result = await fetch(`/api/source`, {
21+
const path = resolveServerPath("/api/source");
22+
const result = await fetch(path, {
1823
method: "POST",
1924
headers: {
2025
"Content-Type": "application/json",
@@ -26,7 +31,8 @@ export const fetchFileSource = async (body: FileSourceRequest): Promise<FileSour
2631
}
2732

2833
export const getRepos = async (): Promise<ListRepositoriesResponse> => {
29-
const result = await fetch('/api/repos', {
34+
const path = resolveServerPath("/api/repos");
35+
const result = await fetch(path, {
3036
method: "GET",
3137
headers: {
3238
"Content-Type": "application/json",
@@ -35,3 +41,12 @@ export const getRepos = async (): Promise<ListRepositoriesResponse> => {
3541

3642
return listRepositoriesResponseSchema.parse(result);
3743
}
44+
45+
/**
46+
* Given a subpath to a api route on the server (e.g., /api/search),
47+
* returns the full path to that route on the server, taking into account
48+
* the base path (if any).
49+
*/
50+
export const resolveServerPath = (path: string) => {
51+
return `${NEXT_PUBLIC_DOMAIN_SUB_PATH}${path}`;
52+
}

packages/web/src/app/posthogProvider.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
import { NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_UI_HOST, NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED } from '@/lib/environment.client'
33
import posthog from 'posthog-js'
44
import { PostHogProvider } from 'posthog-js/react'
5+
import { resolveServerPath } from './api/(client)/client'
56

67
if (typeof window !== 'undefined') {
78
if (!NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED) {
9+
// @see next.config.mjs for path rewrites to the "/ingest" route.
10+
const posthogHostPath = resolveServerPath('/ingest');
11+
812
posthog.init(NEXT_PUBLIC_POSTHOG_KEY!, {
9-
api_host: "/ingest",
13+
api_host: posthogHostPath,
1014
ui_host: NEXT_PUBLIC_POSTHOG_UI_HOST,
1115
person_profiles: 'identified_only',
1216
capture_pageview: false, // Disable automatic pageview capture

packages/web/src/lib/environment.client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ export const NEXT_PUBLIC_POSTHOG_UI_HOST = getEnv(process.env.NEXT_PUBLIC_POSTHO
88
export const NEXT_PUBLIC_POSTHOG_ASSET_HOST = getEnv(process.env.NEXT_PUBLIC_POSTHOG_ASSET_HOST);
99
export const NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED = getEnvBoolean(process.env.NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED, false);
1010
export const NEXT_PUBLIC_SOURCEBOT_VERSION = getEnv(process.env.NEXT_PUBLIC_SOURCEBOT_VERSION, "unknown");
11+
export const NEXT_PUBLIC_DOMAIN_SUB_PATH = getEnv(process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH, "");

0 commit comments

Comments
 (0)