From 770cab514fbb288e1a36ec8a37971d38eb7c9f70 Mon Sep 17 00:00:00 2001 From: Jurgis Sakalauskas Date: Fri, 14 Nov 2025 10:32:30 +0200 Subject: [PATCH] Serve built client from server --- server/src/app.ts | 35 +++++++++++++++++++++++++++++++++++ server/src/config/env.ts | 9 +++++++++ 2 files changed, 44 insertions(+) diff --git a/server/src/app.ts b/server/src/app.ts index 1c00131..b18bd5d 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -1,3 +1,5 @@ +import fs from 'node:fs'; +import path from 'node:path'; import express from 'express'; import apiRouter from './routes/api-router.js'; import { env } from './config/env.js'; @@ -7,6 +9,39 @@ const app = express(); app.use(express.json()); app.use(env.apiBasePath, apiRouter); + +if (env.client.serve) { + const clientDistPath = env.client.distPath; + const indexHtmlPath = path.join(clientDistPath, 'index.html'); + + if (fs.existsSync(clientDistPath) && fs.existsSync(indexHtmlPath)) { + app.use(express.static(clientDistPath)); + app.get('*', (req, res, next) => { + if (req.method !== 'GET' && req.method !== 'HEAD') { + return next(); + } + + const basePath = env.apiBasePath.endsWith('/') + ? env.apiBasePath + : `${env.apiBasePath}/`; + + if ( + env.apiBasePath && + env.apiBasePath !== '/' && + (req.path === env.apiBasePath || req.path.startsWith(basePath)) + ) { + return next(); + } + + return res.sendFile(indexHtmlPath); + }); + } else { + console.warn( + `Static client assets not served because the build output was not found at "${clientDistPath}".` + ); + } +} + app.use(errorHandler); export default app; diff --git a/server/src/config/env.ts b/server/src/config/env.ts index 02934f4..55abd9c 100644 --- a/server/src/config/env.ts +++ b/server/src/config/env.ts @@ -8,6 +8,9 @@ const rootDir = path.resolve(__dirname, '../../..'); config({ path: path.resolve(rootDir, '.env') }); +const resolvePath = (inputPath: string) => + path.isAbsolute(inputPath) ? inputPath : path.resolve(rootDir, inputPath); + const port = Number(process.env.SERVER_PORT ?? process.env.PORT ?? 3000); const urlReaderBaseUrl = (process.env.URL_READER_BASE_URL ?? 'https://r.jina.ai').replace( /\/$/, @@ -17,6 +20,8 @@ const urlReaderApiKey = process.env.URL_READER_API_KEY ?? ''; const llmApiUrl = process.env.LLM_API_URL ?? ''; const llmApiKey = process.env.LLM_API_KEY ?? ''; const llmModel = process.env.LLM_MODEL ?? 'gpt-4o-mini'; +const serveClient = process.env.SERVE_CLIENT !== 'false'; +const clientDistPath = resolvePath(process.env.CLIENT_DIST_PATH ?? 'client/dist'); export const env = { port, @@ -31,5 +36,9 @@ export const env = { apiUrl: llmApiUrl, apiKey: llmApiKey, model: llmModel + }, + client: { + serve: serveClient, + distPath: clientDistPath } };