Skip to content

Commit e3fb23d

Browse files
committed
wip
1 parent 894131b commit e3fb23d

File tree

7 files changed

+125
-50
lines changed

7 files changed

+125
-50
lines changed

meteor/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"indexof": "0.0.1",
5858
"koa": "^2.15.3",
5959
"koa-bodyparser": "^4.4.1",
60+
"koa-mount": "^4.0.0",
6061
"koa-static": "^5.0.0",
6162
"meteor-node-stubs": "^1.2.12",
6263
"moment": "^2.30.1",
@@ -85,6 +86,7 @@
8586
"@types/jest": "^29.5.14",
8687
"@types/koa": "^2.15.0",
8788
"@types/koa-bodyparser": "^4.3.12",
89+
"@types/koa-mount": "^4",
8890
"@types/koa-static": "^4.0.4",
8991
"@types/koa__cors": "^5.0.0",
9092
"@types/koa__router": "^12.0.4",

meteor/server/api/rest/koa.ts

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import Koa from 'koa'
22
import cors from '@koa/cors'
33
import KoaRouter from '@koa/router'
4+
import KoaMount from 'koa-mount'
45
import { WebApp } from 'meteor/webapp'
56
import { Meteor } from 'meteor/meteor'
67
import { getRandomString } from '@sofie-automation/corelib/dist/lib'
78
import _ from 'underscore'
8-
import { public_dir } from '../../lib'
9+
import { getRootSubpath, public_dir } from '../../lib'
910
import staticServe from 'koa-static'
1011
import { logger } from '../../logging'
1112
import { PackageInfo } from '../../coreSystem'
1213
import { profiler } from '../profiler'
14+
import fs from 'fs/promises'
1315

1416
declare module 'http' {
1517
interface IncomingMessage {
@@ -21,13 +23,6 @@ declare module 'http' {
2123
const rootRouter = new KoaRouter()
2224
const 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-
3126
Meteor.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+
123152
export function bindKoaRouter(koaRouter: KoaRouter, bindPath: string): void {
124153
const bindPathWithPrefix = getRootSubpath() + bindPath
125154

meteor/server/lib.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,15 @@ export type Translations = Record<string, string>
3131
export const public_dir = Meteor.isProduction
3232
? path.join(process.cwd(), '../web.browser/app')
3333
: // In development, find the webui package and use its public directory
34-
path.join(process.cwd(), '../../../../../../packages/webui/public')
34+
// path.join(process.cwd(), '../../../../../../packages/webui/public')
35+
path.join(process.cwd(), '../../../../../public') // nocommit!
36+
37+
export function getRootSubpath(): string {
38+
// @ts-expect-error Untyped meteor export
39+
const settings: any = __meteor_runtime_config__
40+
41+
return settings.ROOT_URL_PATH_PREFIX || ''
42+
}
3543

3644
/**
3745
* Get the i18next locale object for a given `languageCode`. If the translations file can not be found or it can't be

meteor/server/webmanifest.ts

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { logger } from './logging'
88
import { MongoQuery } from '@sofie-automation/corelib/dist/mongo'
99
import { DBStudio } from '@sofie-automation/corelib/dist/dataModel/Studio'
1010
import { RundownPlaylists, Rundowns, Studios } from './collections'
11-
import { getLocale, Translations } from './lib'
11+
import { getLocale, getRootSubpath, Translations } from './lib'
1212
import { generateTranslation } from './lib/tempLib'
1313
import { ITranslatableMessage } from '@sofie-automation/blueprints-integration'
1414
import { interpollateTranslation } from '@sofie-automation/corelib/dist/TranslatableMessage'
@@ -21,20 +21,6 @@ import { bindKoaRouter } from './api/rest/koa'
2121
import { stringifyError } from '@sofie-automation/shared-lib/dist/lib/stringifyError'
2222

2323
const appShortName = 'Sofie'
24-
const SOFIE_DEFAULT_ICONS: ManifestImageResource[] = [
25-
{
26-
src: '/icons/mstile-144x144.png',
27-
sizes: '144x144',
28-
purpose: 'monochrome',
29-
type: 'image/png',
30-
},
31-
{
32-
src: '/icons/maskable-96x96.png',
33-
sizes: '96x96',
34-
purpose: 'maskable',
35-
type: 'image/png',
36-
},
37-
]
3824

3925
const t = generateTranslation
4026

@@ -48,6 +34,21 @@ function getShortcutsForStudio(
4834
studio: Pick<DBStudio, '_id' | 'name'>,
4935
studioCount: number
5036
): ShortcutItem[] {
37+
const SOFIE_DEFAULT_ICONS: ManifestImageResource[] = [
38+
{
39+
src: getRootSubpath() + '/icons/mstile-144x144.png',
40+
sizes: '144x144',
41+
purpose: 'monochrome',
42+
type: 'image/png',
43+
},
44+
{
45+
src: getRootSubpath() + '/icons/maskable-96x96.png',
46+
sizes: '96x96',
47+
purpose: 'maskable',
48+
type: 'image/png',
49+
},
50+
]
51+
5152
const multiStudio = studioCount > 1
5253
return [
5354
{
@@ -61,7 +62,7 @@ function getShortcutsForStudio(
6162
: t('Active Rundown')
6263
),
6364
icons: SOFIE_DEFAULT_ICONS,
64-
url: `/activeRundown/${studio._id}`,
65+
url: getRootSubpath() + `/activeRundown/${studio._id}`,
6566
},
6667
{
6768
id: `${studio._id}_prompter`,
@@ -74,7 +75,7 @@ function getShortcutsForStudio(
7475
: t('Prompter')
7576
),
7677
icons: SOFIE_DEFAULT_ICONS,
77-
url: `/prompter/${studio._id}`,
78+
url: getRootSubpath() + `/prompter/${studio._id}`,
7879
},
7980
{
8081
id: `${studio._id}_countdowns`,
@@ -83,7 +84,7 @@ function getShortcutsForStudio(
8384
multiStudio ? t('{{studioName}}: Presenter screen', { studioName: studio.name }) : t('Presenter screen')
8485
),
8586
icons: SOFIE_DEFAULT_ICONS,
86-
url: `/countdowns/${studio._id}/presenter`,
87+
url: getRootSubpath() + `/countdowns/${studio._id}/presenter`,
8788
},
8889
]
8990
}
@@ -111,31 +112,31 @@ async function getWebManifest(languageCode: string): Promise<JSONSchemaForWebApp
111112
short_name: appShortName,
112113
icons: [
113114
{
114-
src: '/icons/android-chrome-192x192.png',
115+
src: getRootSubpath() + '/icons/android-chrome-192x192.png',
115116
sizes: '192x192',
116117
purpose: 'any',
117118
type: 'image/png',
118119
},
119120
{
120-
src: '/icons/android-chrome-512x512.png',
121+
src: getRootSubpath() + '/icons/android-chrome-512x512.png',
121122
sizes: '512x512',
122123
purpose: 'any',
123124
type: 'image/png',
124125
},
125126
{
126-
src: '/icons/mstile-144x144.png',
127+
src: getRootSubpath() + '/icons/mstile-144x144.png',
127128
sizes: '144x144',
128129
purpose: 'monochrome',
129130
type: 'image/png',
130131
},
131132
{
132-
src: '/icons/maskable-96x96.png',
133+
src: getRootSubpath() + '/icons/maskable-96x96.png',
133134
sizes: '96x96',
134135
purpose: 'maskable',
135136
type: 'image/png',
136137
},
137138
{
138-
src: '/icons/maskable-512x512.png',
139+
src: getRootSubpath() + '/icons/maskable-512x512.png',
139140
sizes: '512x512',
140141
purpose: 'maskable',
141142
type: 'image/png',
@@ -144,14 +145,14 @@ async function getWebManifest(languageCode: string): Promise<JSONSchemaForWebApp
144145
theme_color: '#2d89ef',
145146
background_color: '#252627',
146147
display: 'fullscreen',
147-
start_url: '/',
148-
scope: '/',
148+
start_url: getRootSubpath() + '/',
149+
scope: getRootSubpath() + '/',
149150
orientation: 'landscape',
150151
shortcuts: shortcuts.length > 0 ? shortcuts : undefined,
151152
protocol_handlers: [
152153
{
153154
protocol: 'web+nrcs',
154-
url: '/url/nrcs?q=%s',
155+
url: getRootSubpath() + '/url/nrcs?q=%s',
155156
},
156157
],
157158
}

meteor/tsconfig-base.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
/* At the time of writing we are not ready for stricter rules */
55
"strict": true,
66

7+
"target": "es2023",
8+
79
"skipLibCheck": true,
810
"sourceMap": true,
911
"allowJs": false,
10-
"lib": ["dom", "es6", "dom.iterable", "scripthost", "es2017", "es2018", "es2019", "ES2020.Promise"],
12+
"lib": ["dom", "es2023", "dom.iterable", "scripthost"],
1113

1214
"paths": {
1315
"meteor/*": [

meteor/yarn.lock

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1434,6 +1434,15 @@ __metadata:
14341434
languageName: node
14351435
linkType: hard
14361436

1437+
"@types/koa-mount@npm:^4":
1438+
version: 4.0.5
1439+
resolution: "@types/koa-mount@npm:4.0.5"
1440+
dependencies:
1441+
"@types/koa": "npm:*"
1442+
checksum: 10/2b794f618b44e5a6810a829fd6fc29c17dd6b7adde81d26f270215197c01184434922db7078502947f48144344471129f533a3db2b0d86b836c60fddc32e2a3c
1443+
languageName: node
1444+
linkType: hard
1445+
14371446
"@types/koa-send@npm:*":
14381447
version: 4.1.6
14391448
resolution: "@types/koa-send@npm:4.1.6"
@@ -2235,6 +2244,7 @@ __metadata:
22352244
"@types/jest": "npm:^29.5.14"
22362245
"@types/koa": "npm:^2.15.0"
22372246
"@types/koa-bodyparser": "npm:^4.3.12"
2247+
"@types/koa-mount": "npm:^4"
22382248
"@types/koa-static": "npm:^4.0.4"
22392249
"@types/koa__cors": "npm:^5.0.0"
22402250
"@types/koa__router": "npm:^12.0.4"
@@ -2267,6 +2277,7 @@ __metadata:
22672277
jest: "npm:^29.7.0"
22682278
koa: "npm:^2.15.3"
22692279
koa-bodyparser: "npm:^4.4.1"
2280+
koa-mount: "npm:^4.0.0"
22702281
koa-static: "npm:^5.0.0"
22712282
legally: "npm:^3.5.10"
22722283
meteor-node-stubs: "npm:^1.2.12"
@@ -3572,6 +3583,18 @@ __metadata:
35723583
languageName: node
35733584
linkType: hard
35743585

3586+
"debug@npm:^4.0.1":
3587+
version: 4.4.0
3588+
resolution: "debug@npm:4.4.0"
3589+
dependencies:
3590+
ms: "npm:^2.1.3"
3591+
peerDependenciesMeta:
3592+
supports-color:
3593+
optional: true
3594+
checksum: 10/1847944c2e3c2c732514b93d11886575625686056cd765336212dc15de2d2b29612b6cd80e1afba767bb8e1803b778caf9973e98169ef1a24a7a7009e1820367
3595+
languageName: node
3596+
linkType: hard
3597+
35753598
"debuglog@npm:^1.0.1":
35763599
version: 1.0.1
35773600
resolution: "debuglog@npm:1.0.1"
@@ -6818,6 +6841,16 @@ __metadata:
68186841
languageName: node
68196842
linkType: hard
68206843

6844+
"koa-mount@npm:^4.0.0":
6845+
version: 4.0.0
6846+
resolution: "koa-mount@npm:4.0.0"
6847+
dependencies:
6848+
debug: "npm:^4.0.1"
6849+
koa-compose: "npm:^4.1.0"
6850+
checksum: 10/c7e8c5cca4d2ccc4742e63c81b86b44f0290075148897b5d633acdd137e90f554c60c232fbc62e843eaedb913b67c5a49367c1142e290b8cfd9c28eb4a0480ec
6851+
languageName: node
6852+
linkType: hard
6853+
68216854
"koa-send@npm:^5.0.0":
68226855
version: 5.0.1
68236856
resolution: "koa-send@npm:5.0.1"
@@ -7760,7 +7793,7 @@ __metadata:
77607793
languageName: node
77617794
linkType: hard
77627795

7763-
"ms@npm:^2.0.0, ms@npm:^2.1.1":
7796+
"ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.3":
77647797
version: 2.1.3
77657798
resolution: "ms@npm:2.1.3"
77667799
checksum: 10/aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d

packages/webui/vite.config.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const basePath = process.env.SOFIE_BASE_PATH || '/'
3232
export default defineConfig(({ command }) => ({
3333
plugins: [react(), tsconfigPaths(), nodePolyfills()],
3434

35-
base: command === 'build' ? '/' : basePath,
35+
base: command === 'build' ? '' : basePath,
3636

3737
optimizeDeps: {
3838
include: [

0 commit comments

Comments
 (0)