11import Koa from 'koa'
22import cors from '@koa/cors'
33import KoaRouter from '@koa/router'
4+ import KoaMount from 'koa-mount'
45import { WebApp } from 'meteor/webapp'
56import { Meteor } from 'meteor/meteor'
67import { getRandomString } from '@sofie-automation/corelib/dist/lib'
78import _ from 'underscore'
8- import { public_dir } from '../../lib'
9+ import { getRootSubpath , public_dir } from '../../lib'
910import staticServe from 'koa-static'
1011import { logger } from '../../logging'
1112import { PackageInfo } from '../../coreSystem'
1213import { profiler } from '../profiler'
14+ import fs from 'fs/promises'
1315
1416declare module 'http' {
1517 interface IncomingMessage {
@@ -21,13 +23,6 @@ declare module 'http' {
2123const rootRouter = new KoaRouter ( )
2224const boundRouterPaths : string [ ] = [ ]
2325
24- function getRootSubpath ( ) : string {
25- // @ts -expect-error Untyped meteor export
26- const settings : any = __meteor_runtime_config__
27-
28- return settings . ROOT_URL_PATH_PREFIX || ''
29- }
30-
3126Meteor . startup ( ( ) => {
3227 const koaApp = new Koa ( )
3328
@@ -85,28 +80,28 @@ Meteor.startup(() => {
8580
8681 // serve the webui through koa
8782 // This is to avoid meteor injecting anything into the served html
88- const webuiServer = staticServe ( public_dir )
89- koaApp . use ( webuiServer )
83+ const webuiServer = staticServe ( public_dir , {
84+ index : false , // Performed manually
85+ } )
86+ koaApp . use ( KoaMount ( getRootSubpath ( ) , webuiServer ) )
9087 logger . debug ( `Serving static files from ${ public_dir } ` )
9188
9289 // Serve the meteor runtime config
9390 rootRouter . get ( getRootSubpath ( ) + '/meteor-runtime-config.js' , async ( ctx ) => {
94- const versionExtended : string = PackageInfo . versionExtended || PackageInfo . version // package version
95-
96- ctx . body = `window.__meteor_runtime_config__ = (${ JSON . stringify ( {
97- // @ts -expect-error missing types for internal meteor detail
98- ...__meteor_runtime_config__ ,
99- sofieVersionExtended : versionExtended ,
100- } ) } )`
91+ ctx . body = getExtendedMeteorRuntimeConfig ( )
10192 } )
10293
10394 koaApp . use ( rootRouter . routes ( ) ) . use ( rootRouter . allowedMethods ( ) )
10495
10596 koaApp . use ( async ( ctx , next ) => {
10697 if ( ctx . method !== 'GET' ) return next ( )
10798
99+ // Ensure the path is scoped to the root subpath
100+ const rootSubpath = getRootSubpath ( )
101+ if ( ! ctx . path . startsWith ( rootSubpath ) ) return next ( )
102+
108103 // Don't use the fallback for certain paths
109- if ( ctx . path . startsWith ( getRootSubpath ( ) + '/assets/' ) ) return next ( )
104+ if ( ctx . path . startsWith ( rootSubpath + '/assets/' ) ) return next ( )
110105
111106 // Don't use the fallback for anything handled by another router
112107 // This does not feel efficient, but koa doesn't appear to have any shared state between the router handlers
@@ -115,11 +110,45 @@ Meteor.startup(() => {
115110 }
116111
117112 // fallback to the root file
118- ctx . path = '/'
119- return webuiServer ( ctx , next )
113+ return serveIndexHtml ( ctx , next )
120114 } )
121115} )
122116
117+ function getExtendedMeteorRuntimeConfig ( ) {
118+ const versionExtended : string = PackageInfo . versionExtended || PackageInfo . version // package version
119+
120+ return `window.__meteor_runtime_config__ = (${ JSON . stringify ( {
121+ // @ts -expect-error missing types for internal meteor detail
122+ ...__meteor_runtime_config__ ,
123+ sofieVersionExtended : versionExtended ,
124+ } ) } )`
125+ }
126+
127+ async function serveIndexHtml ( ctx : Koa . ParameterizedContext , next : Koa . Next ) {
128+ try {
129+ // Read the file
130+ const indexFileBuffer = await fs . readFile ( public_dir + '/index.html' , 'utf8' )
131+ const indexFileStr = indexFileBuffer . toString ( )
132+
133+ const rootPath = getRootSubpath ( )
134+
135+ // Perform various runtime modifications
136+ let modifiedFile = indexFileStr
137+ modifiedFile = modifiedFile . replace (
138+ // Replace the http load with injected js, to avoid risk of issues where this load fails and the app gets confused
139+ '<script type="text/javascript" src="/meteor-runtime-config.js"></script>' ,
140+ `<script type="text/javascript">${ getExtendedMeteorRuntimeConfig ( ) } </script>`
141+ )
142+ modifiedFile = modifiedFile . replaceAll ( 'href="/' , `href="${ rootPath } /` )
143+ modifiedFile = modifiedFile . replaceAll ( 'href="./' , `href="${ rootPath } /` )
144+ modifiedFile = modifiedFile . replaceAll ( 'src="./' , `src="${ rootPath } /` )
145+
146+ ctx . body = modifiedFile
147+ } catch ( e ) {
148+ return next ( )
149+ }
150+ }
151+
123152export function bindKoaRouter ( koaRouter : KoaRouter , bindPath : string ) : void {
124153 const bindPathWithPrefix = getRootSubpath ( ) + bindPath
125154
0 commit comments