From c0ee0e19fb78e75ba87b77a03847d5fd0c7dfe2a Mon Sep 17 00:00:00 2001 From: Mike Shi Date: Mon, 1 Apr 2024 22:42:24 -0700 Subject: [PATCH] Improve local mode (#361) --- README.md | 9 ++- docker-compose.dev.yml | 2 +- docker-compose.yml | 1 + docker/local/Dockerfile | 1 + docker/local/README.md | 37 ++++++--- docker/local/clickhouseConfig.xml | 6 +- docker/local/entry.sh | 31 +++++--- packages/app/pages/_app.tsx | 15 +++- packages/app/pages/api/config.ts | 4 +- packages/app/src/AppNav.tsx | 21 ++--- packages/app/src/AuthPage.tsx | 16 +++- packages/app/src/JoinTeamPage.tsx | 4 +- packages/app/src/LogTable.tsx | 16 ++-- packages/app/src/PasswordResetPage.tsx | 6 +- packages/app/src/PatternTable.tsx | 2 +- packages/app/src/api.ts | 102 +++++++++++++++---------- packages/app/src/config.ts | 7 +- packages/app/src/iacUtils.tsx | 4 +- packages/app/src/search.ts | 19 ++++- 19 files changed, 202 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index f1472491c..9b713d236 100644 --- a/README.md +++ b/README.md @@ -11,22 +11,23 @@ # HyperDX -[HyperDX](https://hyperdx.io) helps engineers figure out why production is -broken faster by centralizing and correlating logs, metrics, traces, exceptions +[HyperDX](https://hyperdx.io) helps engineers quickly figure out why production is +broken by centralizing and correlating logs, metrics, traces, exceptions and session replays in one place. An open source and developer-friendly alternative to Datadog and New Relic.

- DocumentationChat on DiscordLive DemoBug ReportsContributing + DocumentationChat on DiscordLive DemoBug ReportsContributing

- 🕵️ Correlate end to end, go from browser session replay to logs and traces in just a few clicks - 🔥 Blazing fast performance powered by Clickhouse - 🔍 Intuitive full-text search and property search syntax (ex. `level:err`) +- ⏱️ Monitor health and performance from HTTP requests to DB queries (APM) - 🤖 Automatically cluster event patterns from billions of events - 📈 Dashboard high cardinality events without a complex query language -- 🔔 Set up alerts in just a few clicks +- 🔔 Set up alerts on logs, metrics, or traces in just a few clicks - `{` Automatic JSON/structured log parsing - 🔭 OpenTelemetry native diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index d5f0bb978..59a112d45 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -241,7 +241,7 @@ services: ports: - 8080:8080 environment: - NEXT_PUBLIC_API_SERVER_URL: 'http://localhost:8000' # need to be localhost (CORS) + NEXT_PUBLIC_SERVER_URL: 'http://localhost:8000' # need to be localhost (CORS) HYPERDX_API_KEY: ${HYPERDX_API_KEY} NEXT_PUBLIC_HDX_COLLECTOR_URL: 'http://localhost:4318' NEXT_PUBLIC_HDX_SERVICE_NAME: 'hdx-oss-dev-app' diff --git a/docker-compose.yml b/docker-compose.yml index b995914f1..a8dc6b23c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -189,6 +189,7 @@ services: ports: - ${HYPERDX_APP_PORT}:${HYPERDX_APP_PORT} environment: + NEXT_PUBLIC_API_SERVER_URL: 'http://localhost:8000' HYPERDX_API_KEY: ${HYPERDX_API_KEY} NODE_ENV: development PORT: ${HYPERDX_APP_PORT} diff --git a/docker/local/Dockerfile b/docker/local/Dockerfile index 22700c5f9..0b373b0c0 100644 --- a/docker/local/Dockerfile +++ b/docker/local/Dockerfile @@ -49,6 +49,7 @@ COPY --from=app ./styles ./styles ENV NEXT_TELEMETRY_DISABLED 1 ENV NEXT_OUTPUT_STANDALONE true +ENV NEXT_PUBLIC_IS_LOCAL_MODE true RUN yarn build # == Clickhouse/Base Image == diff --git a/docker/local/README.md b/docker/local/README.md index 0119f2825..8ce8fb535 100644 --- a/docker/local/README.md +++ b/docker/local/README.md @@ -3,8 +3,8 @@ HyperDX Local is a single container local-optimized version of HyperDX that allows you to pipe OpenTelemetry telemetry (logs, metrics, traces) to a local instance of HyperDX running on your own machine. This makes it easily to debug complex applications locally using the same telemetry you have in prod or to test your instrumentation before pushing it into production. HyperDX Local has a few additional benefits over the regular open source version: -- 📦 Packaged in a single container, to slot alongside your existing development environment -- 🔑 No need to create an account or login +- 📦 Packaged in a single container, to slot alongside your existing dev environment (ex. Docker Compose stack) +- 🔑 No need to auth to send and view telemetry - 🐏 Optimized for lower memory footprint And it has all the features you would expect from HyperDX: @@ -24,7 +24,7 @@ docker run -p 8000:8000 -p 4318:4318 -p 4317:4317 -p 8080:8080 -p 8002:8002 TODO Afterwards, you can visit `http://localhost:8080` and immediately jump into the HyperDX UI. -> We recommend having at least 1GB of RAM and 1 CPU core available for the container. +> We recommend having _at least_ 1GB of RAM and 1 CPU core available for the container. ## Configuring Instrumentation @@ -35,18 +35,37 @@ Configuring instrumentation for HyperDX local is similar to configuring it for t Most instrumentations can be configured using the `OTEL_EXPORTER_OTLP_ENDPOINT` environment variable. Ex: `OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318`. -If you're using a HyperDX provided SDK, you may need to give a non-empty `HYPERDX_API_KEY` as well, API keys are not validated in HyperDX Local and therefore can be any value +If you're using a HyperDX provided SDK, you may need to give a non-empty `HYPERDX_API_KEY` as well, API keys are not validated in HyperDX Local and therefore can be any non-empty value. + +## Customizing Ports + +If you need to customize the app (8080) or api (8000) ports that HyperDX Local runs on, you'll need to modify the `docker run` command to forward the appropriate ports and set a few environment variables. + +Customizing the OpenTelemetry ports can simply be changed by modifying the port forwarding flags. Ex. Replacing `-p 4318:4318` with `-p 4999:4318` to change the OpenTelemetry HTTP port to 4999. + +```bash +HYPERDX_APP_PORT=YOUR_CUSTOM_APP_PORT \ +HYPERDX_API_PORT=YOUR_CUSTOM_API_PORT; \ +docker run \ +-e HYPERDX_APP_PORT=$HYPERDX_APP_PORT \ +-e HYPERDX_API_PORT=$HYPERDX_API_PORT \ +-p $HYPERDX_API_PORT:8000 \ +-p 4318:4318 -p 4317:4317 \ +-p $HYPERDX_APP_PORT:8080 \ +-p 8002:8002 \ +sha256:a2954c091a3cf7e7de0263e6548bec075deb3259d305f5f2e09a7113a78c8e38 +``` ## Notes ### Limitations vs Regular Open Source Version There are a few minor limitations compared to the regular open source version: -- Single user mode only +- Single user mode only (due to lack of auth requirement) - No support for management APIs -- Alerts will not fire +- No alerting support (alerts will not fire) - Log and DB query patterns will not be calculated -- No persistence of data between restarts +- No persistence of data if the container is torn down ### Ports @@ -56,9 +75,9 @@ There are a few minor limitations compared to the regular open source version: - `8002` - HyperDX HTTP Logging Endpoint - `8080` - HyperDX UI (Next.js) -### Build Image +### Build Image (Only For Development) -To build the image, run the build script from the project root: +To develop this image, you can build the image locally by running the build script from project root: ``` ./docker/local/build.sh diff --git a/docker/local/clickhouseConfig.xml b/docker/local/clickhouseConfig.xml index fec55d98a..732739ee4 100644 --- a/docker/local/clickhouseConfig.xml +++ b/docker/local/clickhouseConfig.xml @@ -2,9 +2,9 @@ - true - - + false + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log diff --git a/docker/local/entry.sh b/docker/local/entry.sh index dcab87203..11b07244f 100644 --- a/docker/local/entry.sh +++ b/docker/local/entry.sh @@ -5,12 +5,15 @@ export HYPERDX_LOG_LEVEL="error" export CLICKHOUSE_LOG_LEVEL="error" # User-facing Services -export AGGREGATOR_API_URL="http://localhost:8001" export INGESTOR_API_URL="http://localhost:8002" -export SERVER_URL="http://localhost:8000" -export FRONTEND_URL="http://localhost:8080" +# User can specify either an entire SERVER_URL, or override slectively the +# HYPERDX_API_URL or HYPERDX_API_PORT from the defaults +# Same applies to the frontend/app +export SERVER_URL="${SERVER_URL:-${HYPERDX_API_URL:-http://localhost}:${HYPERDX_API_PORT:-8000}}" +export FRONTEND_URL="${FRONTEND_URL:-${HYPERDX_APP_URL:-http://localhost}:${HYPERDX_APP_PORT:-8080}}" # Internal Services +export AGGREGATOR_API_URL="http://localhost:8001" export CLICKHOUSE_HOST="http://localhost:8123" export MONGO_URI="mongodb://localhost:27017/hyperdx" export REDIS_URI="redis://localhost:6379" @@ -23,24 +26,33 @@ export NEXT_TELEMETRY_DISABLED="1" echo "127.0.0.1 ingestor" >> /etc/hosts echo "127.0.0.1 aggregator" >> /etc/hosts +echo "Visit the HyperDX UI at $FRONTEND_URL/search" +echo "" +echo "Send OpenTelemetry data via" +echo "http/protobuf: OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318" +echo "gRPC: OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317" +echo "" +echo "" + # Start Clickhouse Server /entrypoint.sh & # Start Redis Server -redis-server & +redis-server > /var/log/redis.log 2>&1 & # Start Mongo Server -mongod --quiet --dbpath /data/db & +mongod --quiet --dbpath /data/db > /var/log/mongod.log 2>&1 & # Start Vector Ingestor ENABLE_GO_PARSER="false" \ GO_PARSER_API_URL="http://go-parser:7777" \ ENABLE_TOKEN_MATCHING_CHECK="false" \ vector \ + -qq \ -c /etc/vector/sources.toml \ -c /etc/vector/core.toml \ -c /etc/vector/http-sinks.toml \ - --require-healthy true & + --require-healthy true > /var/log/vector.log 2>&1 & # Start Otel Collector otelcol-contrib --config /etc/otelcol-contrib/config.yaml & @@ -50,19 +62,20 @@ APP_TYPE=aggregator \ CLICKHOUSE_USER=aggregator \ CLICKHOUSE_PASSWORD=aggregator \ PORT=8001 \ -node /app/api/build/index.js & +node /app/api/build/index.js > /var/log/aggregator.log 2>&1 & # Api APP_TYPE=api \ CLICKHOUSE_USER=api \ CLICKHOUSE_PASSWORD=api \ PORT=8000 \ -node /app/api/build/index.js & +node /app/api/build/index.js > /var/log/api.log 2>&1 & # App NODE_ENV=production \ PORT=8080 \ -node /app/app/server.js & +NEXT_PUBLIC_SERVER_URL="${SERVER_URL}" \ +node /app/app/server.js > /var/log/app.log 2>&1 & # Wait for any process to exit wait -n diff --git a/packages/app/pages/_app.tsx b/packages/app/pages/_app.tsx index 2851b0438..d0d0e79a8 100644 --- a/packages/app/pages/_app.tsx +++ b/packages/app/pages/_app.tsx @@ -14,6 +14,7 @@ import { MantineThemeOverride, } from '@mantine/core'; +import { apiConfigs } from '../src/api'; import * as config from '../src/config'; import { useConfirmModal } from '../src/useConfirm'; import { QueryParamProvider as HDXQueryParamProvider } from '../src/useQueryParam'; @@ -125,13 +126,23 @@ export default function MyApp({ Component, pageProps }: AppPropsWithLayout) { fetch('/api/config') .then(res => res.json()) .then(_jsonData => { + // Set API url dynamically for users who aren't rebuilding + try { + const url = new URL(_jsonData.apiServerUrl); + if (url != null) { + apiConfigs.prefixUrl = url.toString().replace(/\/$/, ''); + } + } catch (err) { + // ignore + } + if (_jsonData?.apiKey) { let hostname; try { const url = new URL(_jsonData.apiServerUrl); hostname = url.hostname; } catch (err) { - // console.log(err); + // ignore } HyperDX.init({ apiKey: _jsonData.apiKey, @@ -149,7 +160,7 @@ export default function MyApp({ Component, pageProps }: AppPropsWithLayout) { .catch(err => { // ignore }); - }); + }, []); const getLayout = Component.getLayout ?? (page => page); diff --git a/packages/app/pages/api/config.ts b/packages/app/pages/api/config.ts index fa25f3d2b..58dc18bbc 100644 --- a/packages/app/pages/api/config.ts +++ b/packages/app/pages/api/config.ts @@ -1,10 +1,10 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { - API_SERVER_URL, HDX_API_KEY, HDX_COLLECTOR_URL, HDX_SERVICE_NAME, + SERVER_URL, } from '../../src/config'; type ResponseData = { @@ -20,7 +20,7 @@ export default function handler( ) { res.status(200).json({ apiKey: HDX_API_KEY, - apiServerUrl: API_SERVER_URL, + apiServerUrl: SERVER_URL, collectorUrl: HDX_COLLECTOR_URL, serviceName: HDX_SERVICE_NAME, }); diff --git a/packages/app/src/AppNav.tsx b/packages/app/src/AppNav.tsx index 099cf5fc6..d157ca9a9 100644 --- a/packages/app/src/AppNav.tsx +++ b/packages/app/src/AppNav.tsx @@ -27,8 +27,9 @@ import { version } from '../package.json'; import api from './api'; import AuthLoadingBlocker from './AuthLoadingBlocker'; import { - API_SERVER_URL, + IS_LOCAL_MODE, K8S_DASHBOARD_ENABLED, + SERVER_URL, SERVICE_DASHBOARD_ENABLED, } from './config'; import Icon from './Icon'; @@ -1370,14 +1371,16 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) { -
- - - {' '} - {!isCollapsed && Logout} - - -
+ {!IS_LOCAL_MODE && ( +
+ + + {' '} + {!isCollapsed && Logout} + + +
+ )}
v{version}
diff --git a/packages/app/src/AuthPage.tsx b/packages/app/src/AuthPage.tsx index 6e07a4313..c71fae10a 100644 --- a/packages/app/src/AuthPage.tsx +++ b/packages/app/src/AuthPage.tsx @@ -14,7 +14,7 @@ import { } from '@mantine/core'; import api from './api'; -import { API_SERVER_URL } from './config'; +import { SERVER_URL } from './config'; import * as config from './config'; import LandingHeader from './LandingHeader'; import { CheckOrX, PasswordCheck } from './PasswordCheck'; @@ -26,6 +26,17 @@ type FormData = { }; export default function AuthPage({ action }: { action: 'register' | 'login' }) { + const { data: team, isLoading: teamIsLoading } = api.useTeam(); + const router = useRouter(); + + const isLoggedIn = Boolean(!teamIsLoading && team && !team.isDemo); + + useEffect(() => { + if (isLoggedIn) { + router.push('/search'); + } + }, [isLoggedIn, router]); + const isRegister = action === 'register'; const { register, @@ -37,7 +48,6 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) { reValidateMode: 'onSubmit', }); - const router = useRouter(); const { err, msg } = router.query; const { data: installation } = api.useInstallation(); @@ -100,7 +110,7 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) { } : { controller: { - action: `${API_SERVER_URL}/login/password`, + action: `${SERVER_URL}/login/password`, method: 'POST', }, email: { name: 'email' }, diff --git a/packages/app/src/JoinTeamPage.tsx b/packages/app/src/JoinTeamPage.tsx index 38fe4dc3e..0cc9c13c9 100644 --- a/packages/app/src/JoinTeamPage.tsx +++ b/packages/app/src/JoinTeamPage.tsx @@ -2,7 +2,7 @@ import { useRouter } from 'next/router'; import { NextSeo } from 'next-seo'; import { Button, Form } from 'react-bootstrap'; -import { API_SERVER_URL } from './config'; +import { SERVER_URL } from './config'; export default function JoinTeam() { const router = useRouter(); @@ -23,7 +23,7 @@ export default function JoinTeam() {
( Message{' '} - {onShowPatternsClick != null && ( + {onShowPatternsClick != null && !IS_LOCAL_MODE && ( •{' '} - - Show Log Patterns - + Group Similar Events + )} diff --git a/packages/app/src/PasswordResetPage.tsx b/packages/app/src/PasswordResetPage.tsx index 538131158..6319049ac 100644 --- a/packages/app/src/PasswordResetPage.tsx +++ b/packages/app/src/PasswordResetPage.tsx @@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; import { NextSeo } from 'next-seo'; import { Button, Form } from 'react-bootstrap'; -import { API_SERVER_URL } from './config'; +import { SERVER_URL } from './config'; import LandingHeader from './LandingHeader'; export default function PasswordResetPage({ @@ -20,7 +20,7 @@ export default function PasswordResetPage({
@@ -65,7 +65,7 @@ export default function PasswordResetPage({
- Show Events + Show Events List )} diff --git a/packages/app/src/api.ts b/packages/app/src/api.ts index ce233c5b3..f9dbfc398 100644 --- a/packages/app/src/api.ts +++ b/packages/app/src/api.ts @@ -1,10 +1,10 @@ import Router from 'next/router'; -import type { HTTPError } from 'ky'; +import type { HTTPError, Options, ResponsePromise } from 'ky'; import ky from 'ky-universal'; import type { UseQueryOptions } from 'react-query'; import { useInfiniteQuery, useMutation, useQuery } from 'react-query'; -import { API_SERVER_URL } from './config'; +import { SERVER_URL } from './config'; import type { AlertChannel, AlertInterval, @@ -65,8 +65,12 @@ function loginHook(request: Request, options: any, response: Response) { } } +export const apiConfigs = { + prefixUrl: SERVER_URL, +}; + export const server = ky.create({ - prefixUrl: API_SERVER_URL, + prefixUrl: SERVER_URL, credentials: 'include', hooks: { afterResponse: [loginHook], @@ -74,12 +78,22 @@ export const server = ky.create({ timeout: false, }); +const hdxServer = ( + url: string, + options?: Options | undefined, +): ResponsePromise => { + return server(url, { + ...apiConfigs, + ...options, + }); +}; + const api = { usePropertyTypeMappings(options?: UseQueryOptions) { return useQuery, Error>( `logs/propertyTypeMappings`, () => - server(`logs/propertyTypeMappings`, { + hdxServer(`logs/propertyTypeMappings`, { method: 'GET', }) .json<{ data: any[] }>() @@ -106,7 +120,7 @@ const api = { refetchOnWindowFocus: false, queryKey: ['metrics/names'], queryFn: () => - server('metrics/names', { + hdxServer('metrics/names', { method: 'GET', }).json(), }); @@ -130,7 +144,7 @@ const api = { refetchOnWindowFocus: false, queryKey: ['metrics/tags', metrics], queryFn: () => - server('metrics/tags', { + hdxServer('metrics/tags', { method: 'POST', json: { metrics, @@ -177,7 +191,7 @@ const api = { groupBy, ], queryFn: () => - server('metrics/chart', { + hdxServer('metrics/chart', { method: 'POST', json: { aggFn, @@ -249,7 +263,7 @@ const api = { postGroupWhere, ], queryFn: () => - server('chart/series', { + hdxServer('chart/series', { method: 'POST', json: { series: enrichedSeries, @@ -300,7 +314,7 @@ const api = { childrenSpanWhere, ], queryFn: () => - server('logs/chart/spanPerformance', { + hdxServer('logs/chart/spanPerformance', { method: 'POST', json: { startTime, @@ -351,7 +365,7 @@ const api = { sortOrder, ], queryFn: () => - server('logs/chart', { + hdxServer('logs/chart', { method: 'GET', searchParams: [ ['aggFn', aggFn], @@ -388,7 +402,7 @@ const api = { refetchOnWindowFocus: false, queryKey: ['logs/chart/histogram', endTime, field, q, startTime], queryFn: () => - server('logs/chart/histogram', { + hdxServer('logs/chart/histogram', { method: 'GET', searchParams: [ ['endTime', endTime], @@ -424,7 +438,7 @@ const api = { return useInfiniteQuery<{ data: any[] }, Error>({ queryKey: ['logs', q, startTime, endTime, extraFields, order, limit], queryFn: async ({ pageParam = 0 }) => - server('logs', { + hdxServer('logs', { method: 'GET', searchParams: [ ['endTime', endTime], @@ -458,7 +472,7 @@ const api = { return useQuery<{ data: any[] }, Error>({ queryKey: ['logs', 'patterns', q, startTime, endTime], queryFn: async () => - server('logs/patterns', { + hdxServer('logs/patterns', { method: 'GET', searchParams: [ ['endTime', endTime], @@ -485,7 +499,7 @@ const api = { refetchOnWindowFocus: false, queryKey: [startTime, endTime, q], queryFn: () => - server('sessions', { + hdxServer('sessions', { method: 'GET', searchParams: [ ['endTime', endTime], @@ -498,14 +512,14 @@ const api = { useLogViews() { return useQuery<{ data: LogView[] }, Error>({ queryKey: ['log-views'], - queryFn: () => server.get('log-views').json(), + queryFn: () => hdxServer('log-views', { method: 'GET' }).json(), }); }, useDeleteLogView() { return useMutation( `log-views`, async (logViewId: string) => - server(`log-views/${logViewId}`, { + hdxServer(`log-views/${logViewId}`, { method: 'DELETE', }), ); @@ -520,7 +534,7 @@ const api = { tags?: string; } >(`log-views`, async ({ name, query, tags }) => - server('log-views', { + hdxServer('log-views', { method: 'POST', json: { query, @@ -540,7 +554,7 @@ const api = { tags?: string[]; } >(`log-views`, async ({ id, query, tags }) => - server(`log-views/${id}`, { + hdxServer(`log-views/${id}`, { method: 'PATCH', json: { query, @@ -550,11 +564,13 @@ const api = { ); }, useAlerts() { - return useQuery(`alerts`, () => server.get(`alerts`).json()); + return useQuery(`alerts`, () => + hdxServer(`alerts`, { method: 'GET' }).json(), + ); }, useSaveAlert() { return useMutation(`alerts`, async alert => - server('alerts', { + hdxServer('alerts', { method: 'POST', json: alert, }).json(), @@ -564,7 +580,7 @@ const api = { return useMutation( `alerts`, async alert => - server(`alerts/${alert.id}`, { + hdxServer(`alerts/${alert.id}`, { method: 'PUT', json: alert, }).json(), @@ -572,14 +588,14 @@ const api = { }, useDeleteAlert() { return useMutation(`alerts`, async (alertId: string) => - server(`alerts/${alertId}`, { + hdxServer(`alerts/${alertId}`, { method: 'DELETE', }), ); }, useSilenceAlert() { return useMutation('alerts', async alertAck => - server(`alerts/${alertAck.alertId}/silenced`, { + hdxServer(`alerts/${alertAck.alertId}/silenced`, { method: 'POST', json: alertAck, }), @@ -587,7 +603,7 @@ const api = { }, useUnsilenceAlert() { return useMutation(`alerts`, async (alertId: string) => - server(`alerts/${alertId}/silenced`, { + hdxServer(`alerts/${alertId}/silenced`, { method: 'DELETE', }), ); @@ -603,7 +619,7 @@ const api = { return useQuery({ queryKey: ['logs/histogram', q, st, et], queryFn: () => - server('logs/histogram', { + hdxServer('logs/histogram', { method: 'GET', searchParams: [ ['q', q], @@ -617,7 +633,7 @@ const api = { useDashboards(options?: UseQueryOptions) { return useQuery<{ data: Dashboard[] }, Error>( `dashboards`, - () => server.get(`dashboards`).json(), + () => hdxServer(`dashboards`, { method: 'GET' }).json(), options, ); }, @@ -627,7 +643,7 @@ const api = { HTTPError, { name: string; query: string; charts: any[]; tags?: string[] } >(async ({ name, charts, query, tags }) => - server(`dashboards`, { + hdxServer(`dashboards`, { method: 'POST', json: { name, charts, query, tags }, }).json(), @@ -645,7 +661,7 @@ const api = { tags?: string[]; } >(async ({ id, name, charts, query, tags }) => - server(`dashboards/${id}`, { + hdxServer(`dashboards/${id}`, { method: 'PUT', json: { name, charts, query, tags }, }).json(), @@ -653,7 +669,7 @@ const api = { }, useDeleteDashboard() { return useMutation(async ({ id }) => - server(`dashboards/${id}`, { + hdxServer(`dashboards/${id}`, { method: 'DELETE', }).json(), ); @@ -661,7 +677,10 @@ const api = { useServices() { return useQuery( `services`, - () => server.get(`chart/services`).json() as Promise, + () => + hdxServer(`chart/services`, { + method: 'GET', + }).json() as Promise, ); }, useLogDetails( @@ -671,7 +690,8 @@ const api = { ) { return useQuery( `logs/${logId}`, - () => server.get(`logs/${logId}?sortKey=${sortKey}`).json(), + () => + hdxServer(`logs/${logId}?sortKey=${sortKey}`, { method: 'GET' }).json(), { staleTime: 1000 * 60 * 5, // 5 min ...options, @@ -680,7 +700,7 @@ const api = { }, useRotateTeamApiKey() { return useMutation(async () => - server(`team/apiKey`, { + hdxServer(`team/apiKey`, { method: 'PATCH', }).json(), ); @@ -688,7 +708,7 @@ const api = { useSendTeamInvite() { return useMutation( async ({ name, email }) => - server(`team`, { + hdxServer(`team`, { method: 'POST', json: { name, @@ -699,20 +719,20 @@ const api = { }, useInstallation() { return useQuery(`installation`, () => - server(`installation`).json(), + hdxServer(`installation`).json(), ); }, useMe() { - return useQuery(`me`, () => server(`me`).json()); + return useQuery(`me`, () => hdxServer(`me`).json()); }, useTeam() { - return useQuery(`team`, () => server(`team`).json(), { + return useQuery(`team`, () => hdxServer(`team`).json(), { retry: 1, }); }, useTags() { return useQuery<{ data: string[] }, HTTPError>(`team/tags`, () => - server(`team/tags`).json<{ data: string[] }>(), + hdxServer(`team/tags`).json<{ data: string[] }>(), ); }, useSaveWebhook() { @@ -729,7 +749,7 @@ const api = { body?: string; } >(async ({ service, url, name, description, queryParams, headers, body }) => - server(`webhooks`, { + hdxServer(`webhooks`, { method: 'POST', json: { name, @@ -747,7 +767,7 @@ const api = { return useQuery({ queryKey: [...services], queryFn: () => - server('webhooks', { + hdxServer('webhooks', { method: 'GET', searchParams: [...services.map(service => ['service', service])], }).json(), @@ -755,7 +775,7 @@ const api = { }, useDeleteWebhook() { return useMutation(async ({ id }) => - server(`webhooks/${id}`, { + hdxServer(`webhooks/${id}`, { method: 'DELETE', }).json(), ); @@ -766,7 +786,7 @@ const api = { HTTPError, { email: string; password: string; confirmPassword: string } >(async ({ email, password, confirmPassword }) => - server(`register/password`, { + hdxServer(`register/password`, { method: 'POST', json: { email, diff --git a/packages/app/src/config.ts b/packages/app/src/config.ts index 208846668..943e8c5c8 100644 --- a/packages/app/src/config.ts +++ b/packages/app/src/config.ts @@ -1,5 +1,5 @@ -export const API_SERVER_URL = - process.env.NEXT_PUBLIC_SERVER_URL || 'http://localhost:8000'; // NEXT_PUBLIC_API_SERVER_URL can be empty string +export const SERVER_URL = + process.env.NEXT_PUBLIC_SERVER_URL || 'http://localhost:8000'; // NEXT_PUBLIC_SERVER_URL can be empty string export const HDX_API_KEY = process.env.HYPERDX_API_KEY as string; // for nextjs server export const HDX_SERVICE_NAME = @@ -9,6 +9,9 @@ export const HDX_COLLECTOR_URL = 'http://localhost:4318'; export const IS_OSS = process.env.NEXT_PUBLIC_IS_OSS ?? 'true' === 'true'; +export const IS_LOCAL_MODE = + // @ts-ignore + process.env.NEXT_PUBLIC_IS_LOCAL_MODE ?? 'false' === 'true'; // Features in development export const METRIC_ALERTS_ENABLED = process.env.NODE_ENV === 'development'; diff --git a/packages/app/src/iacUtils.tsx b/packages/app/src/iacUtils.tsx index 32a081a18..c05e1603f 100644 --- a/packages/app/src/iacUtils.tsx +++ b/packages/app/src/iacUtils.tsx @@ -1,4 +1,4 @@ -import { API_SERVER_URL } from './config'; +import { SERVER_URL } from './config'; import { Chart, Dashboard } from './types'; function getResourceName(name: string) { @@ -79,7 +79,7 @@ export function dashboardToTerraform( } provider "restapi" { - uri = "${API_SERVER_URL}" + uri = "${SERVER_URL}" write_returns_object = true debug = true id_attribute = "data/id" diff --git a/packages/app/src/search.ts b/packages/app/src/search.ts index 5b0ba0ae9..2a9640678 100644 --- a/packages/app/src/search.ts +++ b/packages/app/src/search.ts @@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { UseQueryOptions } from 'react-query'; import { fetchEventSource } from '@microsoft/fetch-event-source'; -import { API_SERVER_URL } from './config'; +import { apiConfigs } from './api'; import { usePrevious } from './utils'; let team: string | null = null; @@ -94,7 +94,7 @@ function useSearchEventStream( lastFetchStatusRef.current = 'fetching'; const fetchPromise = fetchEventSource( - `${API_SERVER_URL}${apiUrlPath}?${searchParams.toString()}`, + `${apiUrlPath}?${searchParams.toString()}`, { method: 'GET', signal: ctrl.signal, @@ -166,6 +166,21 @@ function useSearchEventStream( // if the server closes the connection unexpectedly, retry: // throw new RetriableError(); }, + fetch: ( + input: RequestInfo | URL, + init?: RequestInit | undefined, + ): Promise => { + if (typeof input === 'string') { + // Hack to dynamically resolve prefixUrl on every request + return fetch(`${apiConfigs.prefixUrl}${input}`, init); + } else { + // We should never hit this + console.error( + 'useSearchEventStream: Non-string fetch input is not supported', + ); + return fetch(input, init); + } + }, // onerror(err) { // if (err instanceof FatalError) { // throw err; // rethrow to stop the operation