From d6295167e03ed0a8888e76fa280c0b652b44b3ff Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:55:21 +0200 Subject: [PATCH] Bugfix/eliminate unresolved page title and range not satisfiable error (#7387) * Eliminate unresolved page title and RangeNotSatisfiableError * Update changelog --- CHANGELOG.md | 11 ++++++ apps/api/src/app/app.module.ts | 26 +------------ apps/api/src/main.ts | 3 ++ .../middlewares/html-template.middleware.ts | 8 +++- .../language-redirect.middleware.ts | 37 +++++++++++++++++++ apps/client/project.json | 3 -- apps/client/src/assets/index.html | 0 apps/client/src/index.html | 2 +- 8 files changed, 60 insertions(+), 30 deletions(-) create mode 100644 apps/api/src/middlewares/language-redirect.middleware.ts delete mode 100644 apps/client/src/assets/index.html diff --git a/CHANGELOG.md b/CHANGELOG.md index f83aa49b3..49923248a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Refactored the language redirect of the root path from the static file serving configuration to a dedicated middleware + +### Fixed + +- Fixed the `RangeNotSatisfiableError` for requests with a `Range` header to the root path caused by the empty `index.html` placeholder +- Fixed the unresolved template literal in the page title while the app is loading from the service worker cache + ## 3.30.0 - 2026-07-19 ### Added diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 4bdd50c9e..bd76ef49b 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -14,8 +14,6 @@ import { DataGatheringQueueModule } from '@ghostfolio/api/services/queues/data-g import { PortfolioSnapshotQueueModule } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.module'; import { BULL_BOARD_ROUTE, - DEFAULT_LANGUAGE_CODE, - SUPPORTED_LANGUAGE_CODES, THROTTLE_DEFAULT_LIMIT, THROTTLE_DEFAULT_TTL } from '@ghostfolio/common/config'; @@ -143,29 +141,7 @@ import { UserModule } from './user/user.module'; '/api/*wildcard', '/sitemap.xml' ], - rootPath: join(__dirname, '..', 'client'), - serveStaticOptions: { - setHeaders: (res) => { - if (res.req?.path === '/') { - let languageCode = DEFAULT_LANGUAGE_CODE; - - try { - const code = res.req.headers['accept-language'] - .split(',')[0] - .split('-')[0]; - - if ( - (SUPPORTED_LANGUAGE_CODES as readonly string[]).includes(code) - ) { - languageCode = code; - } - } catch {} - - res.set('Location', `/${languageCode}`); - res.statusCode = StatusCodes.MOVED_PERMANENTLY; - } - } - } + rootPath: join(__dirname, '..', 'client') }), ServeStaticModule.forRoot({ rootPath: join(__dirname, '..', 'client', '.well-known'), diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 33ad032e9..77d571ea0 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -1,3 +1,4 @@ +import { languageRedirectMiddleware } from '@ghostfolio/api/middlewares/language-redirect.middleware'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { BULL_BOARD_ROUTE, @@ -100,6 +101,8 @@ async function bootstrap() { }); } + app.use(languageRedirectMiddleware); + const configurationService = app.get(ConfigurationService); const trustProxy = configurationService.get('TRUST_PROXY'); diff --git a/apps/api/src/middlewares/html-template.middleware.ts b/apps/api/src/middlewares/html-template.middleware.ts index bdace3402..928a9f22c 100644 --- a/apps/api/src/middlewares/html-template.middleware.ts +++ b/apps/api/src/middlewares/html-template.middleware.ts @@ -111,7 +111,13 @@ export class HtmlTemplateMiddleware implements NestMiddleware { ); try { - map[languageCode] = readFileSync(indexHtmlPath, 'utf8'); + // Restore the interpolation token which the template replaces with a + // static fallback title to avoid showing an unresolved template + // literal when served without interpolation (e.g. by the service worker) + map[languageCode] = readFileSync(indexHtmlPath, 'utf8').replace( + /