Add support for configurable domain sub-paths (#74)

This commit is contained in:
Brendan Kellam 2024-11-18 12:09:26 -08:00 committed by GitHub
parent 558d049d38
commit 83270ffdc9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 101 additions and 19 deletions

View file

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Added `DOMAIN_SUB_PATH` environment variable to allow overriding the default domain subpath. ([#74](https://github.com/sourcebot-dev/sourcebot/pull/74))
## [2.4.3] - 2024-11-18
### Changed

View file

@ -26,6 +26,8 @@ ENV NEXT_TELEMETRY_DISABLED=1
# @see: https://phase.dev/blog/nextjs-public-runtime-variables/
ARG NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED=BAKED_NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED
ARG NEXT_PUBLIC_SOURCEBOT_VERSION=BAKED_NEXT_PUBLIC_SOURCEBOT_VERSION
# @note: leading "/" is required for the basePath property. @see: https://nextjs.org/docs/app/api-reference/next-config-js/basePath
ARG NEXT_PUBLIC_DOMAIN_SUB_PATH=/BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH
RUN yarn workspace @sourcebot/web build
# ------ Build Backend ------
@ -54,6 +56,11 @@ RUN echo "Sourcebot Version: $SOURCEBOT_VERSION"
# Valid values are: debug, info, warn, error
ENV SOURCEBOT_LOG_LEVEL=info
# Configures the sub-path of the domain to serve Sourcebot from.
# For example, if DOMAIN_SUB_PATH is set to "/sb", Sourcebot
# will serve from http(s)://example.com/sb
ENV DOMAIN_SUB_PATH=/
# @note: This is also set in .env
ENV POSTHOG_KEY=phc_VFn4CkEGHRdlVyOOw8mfkoj1DKVoG6y1007EClvzAnS
ENV NEXT_PUBLIC_POSTHOG_KEY=$POSTHOG_KEY

View file

@ -70,9 +70,9 @@ fi
echo -e "\e[34m[Info] Using config file at: '$CONFIG_PATH'.\e[0m"
# Update nextjs public env variables w/o requiring a rebuild.
# Update NextJs public env variables w/o requiring a rebuild.
# @see: https://phase.dev/blog/nextjs-public-runtime-variables/
{
# Infer NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED if it is not set
if [ -z "$NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED" ] && [ ! -z "$SOURCEBOT_TELEMETRY_DISABLED" ]; then
export NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED="$SOURCEBOT_TELEMETRY_DISABLED"
@ -83,10 +83,56 @@ if [ -z "$NEXT_PUBLIC_SOURCEBOT_VERSION" ] && [ ! -z "$SOURCEBOT_VERSION" ]; the
export NEXT_PUBLIC_SOURCEBOT_VERSION="$SOURCEBOT_VERSION"
fi
# Iterate over all .js files in .next & public, making substitutions for the `BAKED_` sentinal values
# with their actual desired runtime value.
find /app/packages/web/public /app/packages/web/.next -type f -name "*.js" |
while read file; do
sed -i "s|BAKED_NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED|${NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED}|g" "$file"
sed -i "s|BAKED_NEXT_PUBLIC_SOURCEBOT_VERSION|${NEXT_PUBLIC_SOURCEBOT_VERSION}|g" "$file"
done
}
# Update specifically NEXT_PUBLIC_DOMAIN_SUB_PATH w/o requiring a rebuild.
# Ultimately, the DOMAIN_SUB_PATH sets the `basePath` param in the next.config.mjs.
# Similar to above, we pass in a `BAKED_` sentinal value into next.config.mjs at build
# time. Unlike above, the `basePath` configuration is set in files other than just javascript
# code (e.g., manifest files, css files, etc.), so this section has subtle differences.
#
# @see: https://nextjs.org/docs/app/api-reference/next-config-js/basePath
# @see: https://phase.dev/blog/nextjs-public-runtime-variables/
{
if [ ! -z "$DOMAIN_SUB_PATH" ]; then
# If the sub-path is "/", this creates problems with certain replacements. For example:
# /BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH/_next/image -> //_next/image (notice the double slash...)
# To get around this, we default to an empty sub-path, which is the default when no sub-path is defined.
if [ "$DOMAIN_SUB_PATH" = "/" ]; then
DOMAIN_SUB_PATH=""
# Otherwise, we need to ensure that the sub-path starts with a slash, since this is a requirement
# for the basePath property. For example, assume DOMAIN_SUB_PATH=/bot, then:
# /BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH/_next/image -> /bot/_next/image
elif [[ ! "$DOMAIN_SUB_PATH" =~ ^/ ]]; then
DOMAIN_SUB_PATH="/$DOMAIN_SUB_PATH"
fi
fi
if [ ! -z "$DOMAIN_SUB_PATH" ]; then
echo -e "\e[34m[Info] DOMAIN_SUB_PATH was set to "$DOMAIN_SUB_PATH". Overriding default path.\e[0m"
fi
# Always set NEXT_PUBLIC_DOMAIN_SUB_PATH to DOMAIN_SUB_PATH (even if it is empty!!)
export NEXT_PUBLIC_DOMAIN_SUB_PATH="$DOMAIN_SUB_PATH"
# Iterate over _all_ files in the web directory, making substitutions for the `BAKED_` sentinal values
# with their actual desired runtime value.
find /app/packages/web -type f |
while read file; do
# @note: the leading "/" is required here as it is included at build time. See Dockerfile.
sed -i "s|/BAKED_NEXT_PUBLIC_DOMAIN_SUB_PATH|${NEXT_PUBLIC_DOMAIN_SUB_PATH}|g" "$file"
done
}
# Run supervisord
exec supervisord -c /etc/supervisor/conf.d/supervisord.conf

View file

@ -21,6 +21,11 @@ const nextConfig = {
},
// This is required to support PostHog trailing slash API requests
skipTrailingSlashRedirect: true,
// @note: this is evaluated at build time.
...(process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH ? {
basePath: process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH,
} : {})
};
export default nextConfig;

View file

@ -1,8 +1,12 @@
'use client';
import { NEXT_PUBLIC_DOMAIN_SUB_PATH } from "@/lib/environment.client";
import { fileSourceResponseSchema, listRepositoriesResponseSchema, searchResponseSchema } from "@/lib/schemas";
import { FileSourceRequest, FileSourceResponse, ListRepositoriesResponse, SearchRequest, SearchResponse } from "@/lib/types";
export const search = async (body: SearchRequest): Promise<SearchResponse> => {
const result = await fetch(`/api/search`, {
const path = resolveServerPath("/api/search");
const result = await fetch(path, {
method: "POST",
headers: {
"Content-Type": "application/json",
@ -14,7 +18,8 @@ export const search = async (body: SearchRequest): Promise<SearchResponse> => {
}
export const fetchFileSource = async (body: FileSourceRequest): Promise<FileSourceResponse> => {
const result = await fetch(`/api/source`, {
const path = resolveServerPath("/api/source");
const result = await fetch(path, {
method: "POST",
headers: {
"Content-Type": "application/json",
@ -26,7 +31,8 @@ export const fetchFileSource = async (body: FileSourceRequest): Promise<FileSour
}
export const getRepos = async (): Promise<ListRepositoriesResponse> => {
const result = await fetch('/api/repos', {
const path = resolveServerPath("/api/repos");
const result = await fetch(path, {
method: "GET",
headers: {
"Content-Type": "application/json",
@ -35,3 +41,12 @@ export const getRepos = async (): Promise<ListRepositoriesResponse> => {
return listRepositoriesResponseSchema.parse(result);
}
/**
* Given a subpath to a api route on the server (e.g., /api/search),
* returns the full path to that route on the server, taking into account
* the base path (if any).
*/
export const resolveServerPath = (path: string) => {
return `${NEXT_PUBLIC_DOMAIN_SUB_PATH}${path}`;
}

View file

@ -2,11 +2,15 @@
import { NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_UI_HOST, NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED } from '@/lib/environment.client'
import posthog from 'posthog-js'
import { PostHogProvider } from 'posthog-js/react'
import { resolveServerPath } from './api/(client)/client'
if (typeof window !== 'undefined') {
if (!NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED) {
// @see next.config.mjs for path rewrites to the "/ingest" route.
const posthogHostPath = resolveServerPath('/ingest');
posthog.init(NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: "/ingest",
api_host: posthogHostPath,
ui_host: NEXT_PUBLIC_POSTHOG_UI_HOST,
person_profiles: 'identified_only',
capture_pageview: false, // Disable automatic pageview capture

View file

@ -8,3 +8,4 @@ export const NEXT_PUBLIC_POSTHOG_UI_HOST = getEnv(process.env.NEXT_PUBLIC_POSTHO
export const NEXT_PUBLIC_POSTHOG_ASSET_HOST = getEnv(process.env.NEXT_PUBLIC_POSTHOG_ASSET_HOST);
export const NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED = getEnvBoolean(process.env.NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED, false);
export const NEXT_PUBLIC_SOURCEBOT_VERSION = getEnv(process.env.NEXT_PUBLIC_SOURCEBOT_VERSION, "unknown");
export const NEXT_PUBLIC_DOMAIN_SUB_PATH = getEnv(process.env.NEXT_PUBLIC_DOMAIN_SUB_PATH, "");