mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-20 22:49:18 +00:00
refactor: add prefix to relative assets
refactor: add prefix to server entrypoints refactor: add prefix to express router refactor: add prefix to router basename
This commit is contained in:
committed by
Carlos Valente
parent
b3ce247f54
commit
b9ba416366
+25
-29
@@ -1,31 +1,27 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang='en'>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset='utf-8' />
|
<meta charset="utf-8" />
|
||||||
<base href="/">
|
<base href="/" />
|
||||||
<link rel='icon' href='/favicon.ico' />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name='theme-color' content='#101010' />
|
<meta name="theme-color" content="#101010" />
|
||||||
<meta name='ontime' content='ontime - time keeping for live events' />
|
<meta name="ontime" content="ontime - time keeping for live events" />
|
||||||
<link rel='apple-touch-icon' href='/ontime-logo.png' />
|
<link rel="apple-touch-icon" href="/ontime-logo.png" />
|
||||||
<link
|
<link rel="icon" type="image/png" href="/ontime-logo.png" />
|
||||||
rel='icon'
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
type='image/png'
|
<link rel="manifest" href="/manifest.json" />
|
||||||
href='ontime-logo.png'
|
<title>ontime</title>
|
||||||
/>
|
<style>
|
||||||
<link rel='manifest' href='/site.webmanifest' />
|
body,
|
||||||
<link rel='manifest' href='/manifest.json' />
|
html {
|
||||||
<title>ontime</title>
|
background-color: rgba(0, 0, 0, 0) !important;
|
||||||
<style>
|
}
|
||||||
body,
|
</style>
|
||||||
html {
|
</head>
|
||||||
background-color: rgba(0, 0, 0, 0) !important;
|
<body>
|
||||||
}
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
</style>
|
<div id="root"></div>
|
||||||
</head>
|
<script type="module" src="/src/index.tsx"></script>
|
||||||
<body>
|
</body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id='root'></div>
|
|
||||||
<script type='module' src='/src/index.tsx'></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { connectSocket } from './common/utils/socket';
|
|||||||
import theme from './theme/theme';
|
import theme from './theme/theme';
|
||||||
import { TranslationProvider } from './translation/TranslationProvider';
|
import { TranslationProvider } from './translation/TranslationProvider';
|
||||||
import AppRouter from './AppRouter';
|
import AppRouter from './AppRouter';
|
||||||
|
import { baseURI } from './externals';
|
||||||
|
|
||||||
connectSocket();
|
connectSocket();
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ function App() {
|
|||||||
<ChakraProvider disableGlobalStyle resetCSS theme={theme}>
|
<ChakraProvider disableGlobalStyle resetCSS theme={theme}>
|
||||||
<QueryClientProvider client={ontimeQueryClient}>
|
<QueryClientProvider client={ontimeQueryClient}>
|
||||||
<AppContextProvider>
|
<AppContextProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter basename={baseURI}>
|
||||||
<div className='App'>
|
<div className='App'>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<TranslationProvider>
|
<TranslationProvider>
|
||||||
|
|||||||
@@ -22,7 +22,25 @@ export const isOntimeCloud = Boolean(import.meta.env.VITE_IS_CLOUD);
|
|||||||
const socketProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
|
const socketProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
|
||||||
|
|
||||||
// resolve port
|
// resolve port
|
||||||
const STATIC_PORT = 4001;
|
const STATIC_PORT = 4001; // this is used as a fallback port for development
|
||||||
export const serverPort = isProduction ? window.location.port : STATIC_PORT;
|
export const serverPort = isProduction ? window.location.port : STATIC_PORT;
|
||||||
export const serverURL = `${window.location.protocol}//${location.hostname}:${serverPort}`;
|
export const baseURI = resolveBaseURI();
|
||||||
export const websocketUrl = `${socketProtocol}://${location.hostname}:${serverPort}/ws`;
|
export const serverURL = `${window.location.protocol}//${window.location.hostname}:${serverPort}${baseURI}`;
|
||||||
|
export const websocketUrl = `${socketProtocol}://${window.location.hostname}:${serverPort}${baseURI}/ws`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a base URI for a client that is not at the root segment
|
||||||
|
* ie: https://cloud.getontime.com/client-hash/timer
|
||||||
|
* This is necessary for ontime cloud and should otherwise not affect the client
|
||||||
|
*/
|
||||||
|
function resolveBaseURI() {
|
||||||
|
if (!isOntimeCloud) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const [_, base, location] = window.location.pathname.split('/');
|
||||||
|
if (!location) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `/${base}`;
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const sentryAuthToken = process.env.SENTRY_AUTH_TOKEN;
|
|||||||
const isDev = process.env.NODE_ENV === 'local' || process.env.NODE_ENV === 'development';
|
const isDev = process.env.NODE_ENV === 'local' || process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
base: './', // Ontime cloud: we use relative paths to allow them to reference a dynamic base set at runtime
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
react(),
|
||||||
svgrPlugin(),
|
svgrPlugin(),
|
||||||
@@ -33,7 +34,7 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
compression({
|
compression({
|
||||||
algorithm: 'brotliCompress',
|
algorithm: 'brotliCompress',
|
||||||
exclude: /\.(html)$/, // Exclude HTML files from compression so we can change the base property at runtime
|
exclude: /\.(html)$/, // Ontime cloud: Exclude HTML files from compression so we can change the base property at runtime
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ export class SocketServer implements IAdapter {
|
|||||||
this.wss = null;
|
this.wss = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
init(server: Server) {
|
init(server: Server, prefix?: string) {
|
||||||
this.wss = new WebSocketServer({ path: '/ws', server, maxPayload: this.MAX_PAYLOAD });
|
this.wss = new WebSocketServer({ path: `${prefix}/ws`, server, maxPayload: this.MAX_PAYLOAD });
|
||||||
|
|
||||||
this.wss.on('connection', (ws) => {
|
this.wss.on('connection', (ws) => {
|
||||||
const clientId = generateId();
|
const clientId = generateId();
|
||||||
|
|||||||
+22
-16
@@ -6,10 +6,10 @@ import expressStaticGzip from 'express-static-gzip';
|
|||||||
import http, { type Server } from 'http';
|
import http, { type Server } from 'http';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import serverTiming from 'server-timing';
|
import serverTiming from 'server-timing';
|
||||||
import { extname, resolve } from 'path';
|
import { extname } from 'node:path';
|
||||||
|
|
||||||
// import utils
|
// import utils
|
||||||
import { publicDir, srcDir } from './setup/index.js';
|
import { publicDir, srcDir, srcFiles } from './setup/index.js';
|
||||||
import { environment, isProduction, updateRouterPrefix } from './externals.js';
|
import { environment, isProduction, updateRouterPrefix } from './externals.js';
|
||||||
import { ONTIME_VERSION } from './ONTIME_VERSION.js';
|
import { ONTIME_VERSION } from './ONTIME_VERSION.js';
|
||||||
import { consoleSuccess, consoleHighlight, consoleError } from './utils/console.js';
|
import { consoleSuccess, consoleHighlight, consoleError } from './utils/console.js';
|
||||||
@@ -53,8 +53,14 @@ if (!canLog) {
|
|||||||
console.log(`Ontime public directory at ${publicDir.root} `);
|
console.log(`Ontime public directory at ${publicDir.root} `);
|
||||||
}
|
}
|
||||||
|
|
||||||
// calls an update to the client router prefix
|
/**
|
||||||
updateRouterPrefix();
|
* When running in Ontime cloud, the client is not at the root segment
|
||||||
|
* ie: https://cloud.getontime.com/client-hash/timer
|
||||||
|
* This means:
|
||||||
|
* - changing the base path in the index.html file
|
||||||
|
* - prepending all express routes with the given prefix
|
||||||
|
*/
|
||||||
|
const prefix = updateRouterPrefix();
|
||||||
|
|
||||||
// Create express APP
|
// Create express APP
|
||||||
const app = express();
|
const app = express();
|
||||||
@@ -75,20 +81,20 @@ app.use(express.urlencoded({ extended: true }));
|
|||||||
app.use(express.json({ limit: '1mb' }));
|
app.use(express.json({ limit: '1mb' }));
|
||||||
|
|
||||||
// Implement route endpoints
|
// Implement route endpoints
|
||||||
app.use('/data', appRouter); // router for application data
|
app.use(`${prefix}/data`, appRouter); // router for application data
|
||||||
app.use('/api', integrationRouter); // router for integrations
|
app.use(`${prefix}/api`, integrationRouter); // router for integrations
|
||||||
|
|
||||||
// serve static external files
|
// serve static external files
|
||||||
app.use('/external', express.static(publicDir.externalDir));
|
app.use(`${prefix}/external`, express.static(publicDir.externalDir));
|
||||||
app.use('/user', express.static(publicDir.userDir));
|
app.use(`${prefix}/external`, (req, res) => {
|
||||||
|
// if the user reaches to the root, we show a 404
|
||||||
// if the user reaches to the root, we show a 404
|
|
||||||
app.use('/external', (req, res) => {
|
|
||||||
res.status(404).send(`${req.originalUrl} not found`);
|
res.status(404).send(`${req.originalUrl} not found`);
|
||||||
});
|
});
|
||||||
|
app.use(`${prefix}/user`, express.static(publicDir.userDir));
|
||||||
|
|
||||||
// serve static - react, in dev/test mode we fetch the React app from module
|
// serve static - react, in dev/test mode we fetch the React app from module
|
||||||
app.use(
|
app.use(
|
||||||
|
prefix,
|
||||||
expressStaticGzip(srcDir.clientDir, {
|
expressStaticGzip(srcDir.clientDir, {
|
||||||
enableBrotli: true,
|
enableBrotli: true,
|
||||||
orderPreference: ['br'],
|
orderPreference: ['br'],
|
||||||
@@ -111,8 +117,8 @@ app.use(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
app.get('*', (_req, res) => {
|
app.get(`${prefix}/*`, (_req, res) => {
|
||||||
res.sendFile(resolve(srcDir.clientDir, 'index.html'));
|
res.sendFile(srcFiles.clientIndexHtml);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Implement catch all
|
// Implement catch all
|
||||||
@@ -176,7 +182,7 @@ export const startServer = async (
|
|||||||
const { serverPort } = getDataProvider().getSettings();
|
const { serverPort } = getDataProvider().getSettings();
|
||||||
|
|
||||||
expressServer = http.createServer(app);
|
expressServer = http.createServer(app);
|
||||||
socket.init(expressServer);
|
socket.init(expressServer, prefix);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module initialises the services and provides initial payload for the store
|
* Module initialises the services and provides initial payload for the store
|
||||||
@@ -221,10 +227,10 @@ export const startServer = async (
|
|||||||
|
|
||||||
expressServer.listen(serverPort, '0.0.0.0', () => {
|
expressServer.listen(serverPort, '0.0.0.0', () => {
|
||||||
const nif = getNetworkInterfaces();
|
const nif = getNetworkInterfaces();
|
||||||
consoleSuccess(`Local: http://localhost:${serverPort}/editor`);
|
consoleSuccess(`Local: http://localhost:${serverPort}${prefix}/editor`);
|
||||||
for (const key in nif) {
|
for (const key in nif) {
|
||||||
const address = nif[key].address;
|
const address = nif[key].address;
|
||||||
consoleSuccess(`Network: http://${address}:${serverPort}/editor`);
|
consoleSuccess(`Network: http://${address}:${serverPort}${prefix}/editor`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { readFileSync, writeFileSync } from 'node:fs';
|
import { readFileSync, writeFileSync } from 'node:fs';
|
||||||
import { resolve } from 'node:path';
|
|
||||||
|
import { srcFiles } from './setup/index.js';
|
||||||
|
|
||||||
// =================================================
|
// =================================================
|
||||||
// resolve running environment
|
// resolve running environment
|
||||||
@@ -19,19 +20,20 @@ export const isProduction = isDocker || (env === 'production' && !isTest);
|
|||||||
* This is only needed in the cloud environment where the client is not at the root segment
|
* This is only needed in the cloud environment where the client is not at the root segment
|
||||||
* ie: https://cloud.getontime.com/client-hash/timer
|
* ie: https://cloud.getontime.com/client-hash/timer
|
||||||
*/
|
*/
|
||||||
export function updateRouterPrefix(prefix: string | undefined = process.env.ROUTER_PREFIX) {
|
export function updateRouterPrefix(prefix: string | undefined = process.env.ROUTER_PREFIX): string {
|
||||||
if (!prefix) {
|
if (!prefix) {
|
||||||
return;
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const indexFile = resolve('.', 'client', 'index.html');
|
|
||||||
try {
|
try {
|
||||||
const data = readFileSync(indexFile, { encoding: 'utf-8', flag: 'r' }).replace(
|
const data = readFileSync(srcFiles.clientIndexHtml, { encoding: 'utf-8', flag: 'r' }).replace(
|
||||||
/<base href="[^"]*">/g,
|
'<base href="/" />',
|
||||||
`<base href="${prefix}" />`,
|
`<base href="/${prefix}/" />`,
|
||||||
);
|
);
|
||||||
writeFileSync(indexFile, data, { encoding: 'utf-8', flag: 'w' });
|
writeFileSync(srcFiles.clientIndexHtml, data, { encoding: 'utf-8', flag: 'w' });
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
/** unhandled */
|
/** unhandled */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return `/${prefix}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ export const srcDir = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const srcFiles = {
|
export const srcFiles = {
|
||||||
|
/** Path to start index.html */
|
||||||
|
clientIndexHtml: join(srcDir.clientDir, 'index.html'),
|
||||||
/** Path to bundled CSS */
|
/** Path to bundled CSS */
|
||||||
cssOverride: join(srcDir.root, config.user, config.styles.directory, config.styles.filename),
|
cssOverride: join(srcDir.root, config.user, config.styles.directory, config.styles.filename),
|
||||||
/** Path to bundled external readme */
|
/** Path to bundled external readme */
|
||||||
|
|||||||
Reference in New Issue
Block a user