diff --git a/apps/docs/docs/configuration/basic.mdx b/apps/docs/docs/configuration/basic.mdx index 273150976..bc0cae067 100644 --- a/apps/docs/docs/configuration/basic.mdx +++ b/apps/docs/docs/configuration/basic.mdx @@ -32,6 +32,14 @@ The port where the express backend is running (the backend serves the frontend, - type: `number` - default: `3001` +## `DASHDOT_ROUTING_PATH` + +The path where dash. is being served. This is useful if you want to run dash. behind a reverse proxy on a sub-path +(e.g `https://example.com/dashdot` would need a value of `/dashdot`). + +- type: `string` +- default: `/` + ## `DASHDOT_PAGE_TITLE` If you want to show a custom string in the browser page title. diff --git a/apps/server/src/config.ts b/apps/server/src/config.ts index 5ff41621d..12802569e 100644 --- a/apps/server/src/config.ts +++ b/apps/server/src/config.ts @@ -28,6 +28,7 @@ const kv = ( export const CONFIG: Config = { port: numNull(penv('PORT')) ?? 3001, + routing_path: penv('ROUTING_PATH') ?? '/', running_in_docker: penv('RUNNING_IN_DOCKER') === 'true', accept_ookla_eula: penv('ACCEPT_OOKLA_EULA') === 'true', use_network_interface: penv('USE_NETWORK_INTERFACE'), diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index d0cefc793..1c0a54319 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -15,6 +15,7 @@ import { timeout, } from 'rxjs'; import { Server } from 'socket.io'; +import Path from 'path'; import { CONFIG } from './config'; import getNetworkInfo from './data/network'; import { getDynamicServerInfo } from './dynamic-info'; @@ -33,6 +34,7 @@ import { } from './static-info'; const app = express(); +const router = express.Router(); const server = http.createServer(app); const io = new Server(server, { cors: CONFIG.disable_integrations @@ -40,6 +42,7 @@ const io = new Server(server, { : { origin: '*', }, + path: Path.join(CONFIG.routing_path, '/socket'), }); if (!CONFIG.disable_integrations) { @@ -47,10 +50,11 @@ if (!CONFIG.disable_integrations) { } app.use(compression()); +app.use(CONFIG.routing_path, router); if (environment.production) { // Serve static files from the React app - app.use( + router.use( express.static(path.join(__dirname, '../view'), { maxAge: '1y', setHeaders: (res, path) => { @@ -78,7 +82,7 @@ if (!CONFIG.disable_integrations) { }; const versionFile = getVersionFile(); - app.get('/config', (_, res) => { + router.get('/config', (_, res) => { res.send({ config: { ...CONFIG, @@ -89,7 +93,7 @@ if (!CONFIG.disable_integrations) { }); }); - app.get('/info', (_, res) => { + router.get('/info', (_, res) => { res.send({ ...getStaticServerInfo(), config: undefined }); }); } @@ -118,19 +122,19 @@ server.listen(CONFIG.port, async () => { } }; - app.get('/load/cpu', async (_, res) => { + router.get('/load/cpu', async (_, res) => { res.send(await getCurrentValue(obs.cpu)); }); - app.get('/load/ram', async (_, res) => { + router.get('/load/ram', async (_, res) => { res.send({ load: await getCurrentValue(obs.ram) }); }); - app.get('/load/storage', async (_, res) => { + router.get('/load/storage', async (_, res) => { res.send(await getCurrentValue(obs.storage)); }); - app.get('/load/network', async (_, res) => { + router.get('/load/network', async (_, res) => { res.send(await getCurrentValue(obs.network)); }); - app.get('/load/gpu', async (_, res) => { + router.get('/load/gpu', async (_, res) => { res.send(await getCurrentValue(obs.gpu)); }); } diff --git a/apps/view/project.json b/apps/view/project.json index 158623cfd..49ebd07ff 100644 --- a/apps/view/project.json +++ b/apps/view/project.json @@ -10,7 +10,7 @@ "defaultConfiguration": "production", "options": { "outputPath": "dist/apps/view", - "base": "/", + "base": "./", "sourcemap": true }, "configurations": { diff --git a/apps/view/src/services/page-data.ts b/apps/view/src/services/page-data.ts index 3fef00685..d09fd61df 100644 --- a/apps/view/src/services/page-data.ts +++ b/apps/view/src/services/page-data.ts @@ -7,7 +7,9 @@ import { StorageLoad, } from '@dash/common'; import { useEffect, useState } from 'react'; -import { Socket, io } from 'socket.io-client'; +import { io } from 'socket.io-client'; +import { URL } from 'url'; +import * as Path from 'path'; import { environment } from '../environment'; export const usePageData = () => { @@ -23,10 +25,15 @@ export const usePageData = () => { const config = serverInfo?.config; useEffect(() => { - const socket = io(environment.backendUrl); + const { origin: wlOrigin, pathname: wlPathname } = window.location; + const socket = io(environment.backendUrl, { + path: new URL(Path.join(wlPathname, '/socket'), wlOrigin).pathname, + }); + let localConfig: ServerInfo['config']; socket.on('static-info', data => { setServerInfo(data); + localConfig = data.config; }); socket.on('connect', () => { @@ -34,68 +41,62 @@ export const usePageData = () => { setSocketError(false); }); socket.on('connect_error', () => { + setTimeout(() => setPageLoaded(true), 50); setSocketError(true); }); - return () => { - socket.close(); - }; - }, []); - - useEffect(() => { - let socket: Socket | undefined; - if (config) { - socket = io(environment.backendUrl); - - socket.on('cpu-load', data => { + socket.on('cpu-load', data => { + if (localConfig) setCpuLoad(oldData => { - if (oldData.length >= (config.cpu_shown_datapoints ?? 0)) { + if (oldData.length >= (localConfig.cpu_shown_datapoints ?? 0)) { return [...oldData.slice(1), data]; } else { return [...oldData, data]; } }); - }); + }); - socket.on('ram-load', data => { + socket.on('ram-load', data => { + if (localConfig) setRamLoad(oldData => { - if (oldData.length >= (config.ram_shown_datapoints ?? 0)) { + if (oldData.length >= (localConfig.ram_shown_datapoints ?? 0)) { return [...oldData.slice(1), data]; } else { return [...oldData, data]; } }); - }); + }); - socket.on('network-load', data => { + socket.on('network-load', data => { + if (localConfig) setNetworkLoad(oldData => { - if (oldData.length >= (config.network_shown_datapoints ?? 0)) { + if (oldData.length >= (localConfig.network_shown_datapoints ?? 0)) { return [...oldData.slice(1), data]; } else { return [...oldData, data]; } }); - }); + }); - socket.on('gpu-load', data => { + socket.on('gpu-load', data => { + if (localConfig) setGpuLoad(oldData => { - if (oldData.length >= (config.gpu_shown_datapoints ?? 0)) { + if (oldData.length >= (localConfig.gpu_shown_datapoints ?? 0)) { return [...oldData.slice(1), data]; } else { return [...oldData, data]; } }); - }); + }); - socket.on('storage-load', data => { - setStorageLoad(data); - }); - } + socket.on('storage-load', data => { + setStorageLoad(data); + }); return () => { socket?.close(); }; - }, [config]); + }, []); const errors = [ { @@ -107,7 +108,7 @@ export const usePageData = () => { text: 'Unable to connect to backend!', }, ]; - const error = errors.find(e => e.condition && pageLoaded === true); + const error = errors.find(e => e.condition && pageLoaded); return { pageLoaded, diff --git a/apps/view/vite.config.ts b/apps/view/vite.config.ts index 9700a021d..4b76335b0 100644 --- a/apps/view/vite.config.ts +++ b/apps/view/vite.config.ts @@ -1,5 +1,6 @@ /// import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { nodePolyfills } from 'vite-plugin-node-polyfills'; import react from '@vitejs/plugin-react'; import { defineConfig } from 'vite'; @@ -17,7 +18,12 @@ export default defineConfig({ host: '0.0.0.0', }, - plugins: [react(), nxViteTsPaths()], + plugins:[ + react(), nxViteTsPaths(), + nodePolyfills({ + include: ['url', 'path'] + }) + ], //@ts-ignore test: { diff --git a/libs/common/src/types.ts b/libs/common/src/types.ts index 22c65afc7..e93769b50 100644 --- a/libs/common/src/types.ts +++ b/libs/common/src/types.ts @@ -92,6 +92,7 @@ export type HardwareInfo = { export type Config = { // General port: number; + routing_path: string; running_in_docker: boolean; use_network_interface?: string; speed_test_from_path?: string; diff --git a/package.json b/package.json index 85dd5556a..0ece76b3d 100644 --- a/package.json +++ b/package.json @@ -158,6 +158,7 @@ "typescript": "5.2.2", "vite": "5.0.13", "vite-plugin-eslint": "1.8.1", + "vite-plugin-node-polyfills": "0.22.0", "vite-tsconfig-paths": "4.2.3", "vitest": "0.32.4" },