diff --git a/apps/client/src/common/api/apiConstants.ts b/apps/client/src/common/api/apiConstants.ts index 6e86b7cfb..935a102f1 100644 --- a/apps/client/src/common/api/apiConstants.ts +++ b/apps/client/src/common/api/apiConstants.ts @@ -1,5 +1,3 @@ -export const STATIC_PORT = 4001; - // REST stuff export const EVENT_DATA = ['eventdata']; export const ALIASES = ['aliases']; @@ -15,8 +13,9 @@ export const RUNTIME = ['runtimeStore']; const location = window.location; const socketProtocol = location.protocol === 'https:' ? 'wss' : 'ws'; +const STATIC_PORT = 4001; export const serverPort = import.meta.env.DEV ? STATIC_PORT : location.port; -export const serverURL = import.meta.env.DEV ? `http://${location.hostname}:${serverPort}` : location.origin; +export const serverURL = `${location.protocol}//${location.hostname}:${serverPort}`; export const websocketUrl = `${socketProtocol}://${location.hostname}:${serverPort}/ws`; export const eventURL = `${serverURL}/eventdata`; diff --git a/apps/client/src/common/api/ontimeApi.ts b/apps/client/src/common/api/ontimeApi.ts index c45e63cd1..6fc503259 100644 --- a/apps/client/src/common/api/ontimeApi.ts +++ b/apps/client/src/common/api/ontimeApi.ts @@ -1,13 +1,5 @@ import axios from 'axios'; -import { - Alias, - EventData, - OSCSettings, - OscSubscription, - Settings, - UserFields, - ViewSettings, -} from 'ontime-types'; +import { Alias, EventData, OSCSettings, OscSubscription, Settings, UserFields, ViewSettings } from 'ontime-types'; import { apiRepoLatest } from '../../externals'; import { InfoType } from '../models/Info'; diff --git a/apps/client/src/features/info/InfoNif.tsx b/apps/client/src/features/info/InfoNif.tsx index 19a0567df..1d0f8b9f7 100644 --- a/apps/client/src/features/info/InfoNif.tsx +++ b/apps/client/src/features/info/InfoNif.tsx @@ -1,5 +1,6 @@ import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp'; +import { serverPort } from '../../common/api/apiConstants'; import useInfo from '../../common/hooks-query/useInfo'; import { openLink } from '../../common/utils/linkUtils'; @@ -9,7 +10,7 @@ export default function InfoNif() { const { data } = useInfo(); const handleClick = (address: string) => { - const baseURL = 'http://__IP__:4001'; + const baseURL = `http://__IP__:${serverPort}`; openLink(baseURL.replace('__IP__', address)); }; diff --git a/apps/client/src/features/modals/settings-modal/AppSettings.tsx b/apps/client/src/features/modals/settings-modal/AppSettings.tsx index 14e90f516..983ac3d4a 100644 --- a/apps/client/src/features/modals/settings-modal/AppSettings.tsx +++ b/apps/client/src/features/modals/settings-modal/AppSettings.tsx @@ -61,7 +61,7 @@ export default function AppSettingsModal() { `http://localhost:${port}/editor`, + production: (port = 4001) => `http://localhost:${port}/editor`, }, server: { - pathToEntrypoint: '../extraResources/server/index.cjs' + pathToEntrypoint: '../extraResources/server/index.cjs', }, assets: { pathToAssets: './assets/', - } + }, }; diff --git a/apps/electron/main.js b/apps/electron/main.js index 71c0a7c45..80d9a8025 100644 --- a/apps/electron/main.js +++ b/apps/electron/main.js @@ -30,21 +30,23 @@ let splash; let tray = null; async function startBackend() { - await (async () => { - // in dev mode, we expect both UI and server to be running - if (!isProduction) { - return; - } + // in dev mode, we expect both UI and server to be running + if (!isProduction) { + return; + } - const ontimeServer = require(nodePath); - const { initAssets, startServer, startOSCServer, startIntegrations } = ontimeServer; + const ontimeServer = require(nodePath); + const { initAssets, startServer, startOSCServer, startIntegrations } = ontimeServer; - await initAssets(); + await initAssets(); - loaded = await startServer(); - await startOSCServer(); - await startIntegrations(); - })(); + const result = await startServer(); + loaded = result.message; + + await startOSCServer(); + await startIntegrations(); + + return result.serverPort; } /** @@ -155,9 +157,11 @@ app.whenReady().then(() => { }); startBackend() - .then(() => { + .then((port) => { // Load page served by node or use React dev run - const clientUrl = isProduction ? electronConfig.reactAppUrl.production : electronConfig.reactAppUrl.development; + const clientUrl = isProduction + ? electronConfig.reactAppUrl.production(port) + : electronConfig.reactAppUrl.development(port); win .loadURL(clientUrl) diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts index f27f49e54..906a9997f 100644 --- a/apps/server/src/app.ts +++ b/apps/server/src/app.ts @@ -131,7 +131,7 @@ export const initAssets = async () => { export const startServer = async () => { checkStart(OntimeStartOrder.InitServer); - const serverPort = 4001; // hardcoded for now + const { serverPort } = DataProvider.getSettings(); const returnMessage = `Ontime is listening on port ${serverPort}`; expressServer = http.createServer(app); @@ -144,7 +144,7 @@ export const startServer = async () => { expressServer.listen(serverPort, '0.0.0.0'); - return returnMessage; + return { message: returnMessage, serverPort }; }; /** diff --git a/apps/server/src/controllers/ontimeController.ts b/apps/server/src/controllers/ontimeController.ts index 09a67a0af..18f42952d 100644 --- a/apps/server/src/controllers/ontimeController.ts +++ b/apps/server/src/controllers/ontimeController.ts @@ -10,7 +10,7 @@ import { failEmptyObjects, failIsNotArray } from '../utils/routerUtils.js'; import { mergeObject } from '../utils/parserUtils.js'; import { PlaybackService } from '../services/PlaybackService.js'; import { eventStore } from '../stores/EventStore.js'; -import { resolveDbPath } from '../setup.js'; +import { isDocker, resolveDbPath } from '../setup.js'; import { oscIntegration } from '../services/integration-service/OscIntegration.js'; import { logger } from '../classes/Logger.js'; import { deleteAllEvents, forceReset } from '../services/rundown-service/RundownService.js'; @@ -208,6 +208,11 @@ export const postSettings = async (req, res) => { const editorKey = extractPin(req.body?.editorKey, settings.editorKey); const operatorKey = extractPin(req.body?.operatorKey, settings.operatorKey); + if (isDocker && req.body?.serverPort) { + return res.status(403).json({ message: `Can't change port when running inside docker` }); + } + const serverPort = parseInt(req.body?.serverPort ?? settings.serverPort, 10); + let timeFormat = settings.timeFormat; if (req.body?.timeFormat === '12' || req.body?.timeFormat === '24') { timeFormat = req.body.timeFormat; @@ -221,6 +226,7 @@ export const postSettings = async (req, res) => { operatorKey, timeFormat, language, + serverPort, }; await DataProvider.setSettings(newData); res.status(200).send(newData); diff --git a/apps/server/src/controllers/ontimeController.validate.ts b/apps/server/src/controllers/ontimeController.validate.ts index 3b3705073..5f67aa4e3 100644 --- a/apps/server/src/controllers/ontimeController.validate.ts +++ b/apps/server/src/controllers/ontimeController.validate.ts @@ -63,6 +63,7 @@ export const validateSettings = [ body('operatorKey').isString().isLength({ min: 0, max: 4 }).optional({ nullable: true }), body('timeFormat').isString().isIn(['12', '24']), body('language').isString(), + body('serverPort').isPort().optional(), (req, res, next) => { const errors = validationResult(req); if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() }); diff --git a/apps/server/src/utils/parserFunctions.ts b/apps/server/src/utils/parserFunctions.ts index fbe41ecc0..815f4e736 100644 --- a/apps/server/src/utils/parserFunctions.ts +++ b/apps/server/src/utils/parserFunctions.ts @@ -130,6 +130,7 @@ export const parseSettings = (data, enforce): Settings => { console.log('ERROR: unknown app version, skipping'); } else { const settings = { + serverPort: s.serverPort || dbModel.settings.serverPort, editorKey: s.editorKey || null, operatorKey: s.operatorKey || null, timeFormat: s.timeFormat || '24', diff --git a/packages/types/src/definitions/core/Settings.type.ts b/packages/types/src/definitions/core/Settings.type.ts index 9e7b1d0ff..4d4527989 100644 --- a/packages/types/src/definitions/core/Settings.type.ts +++ b/packages/types/src/definitions/core/Settings.type.ts @@ -3,7 +3,7 @@ import { TimeFormat } from './TimeFormat.type'; export type Settings = { app: 'ontime'; version: 2; - serverPort: 4001; + serverPort: number; editorKey: null | string; operatorKey: null | string; timeFormat: TimeFormat;