mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-06 07:53:54 +00:00
722e045b20
* Revert "refactor: add version to session endpoint" This reverts commitcd40a6e55e. * Revert "Remove public event feature (#1645)" This reverts commit08d9e24871. * Revert "Fix: rearrange playing event (#1640)" This reverts commit0649678dca. * Revert "refactor: style tweaks to edit css modal" This reverts commita00ec2d02a. * Revert "refactor: remove usages of framer-motion" This reverts commit54a74ccc2a. * Revert "Upgrade expressjs (#1633)" This reverts commit6f3ab274bd. * Revert "Refactor: require trigger in all events objects (#1636)" This reverts commit90870ecfb6. * Revert "Fix: Correct boundary condition in applyDelay" This reverts commitb640e0e181. * Revert "let vite be the proxy to the dev server (#1630)" This reverts commitc41fe824cf. * Revert "refactor: migrate custom fields to transactions" This reverts commit62c8319d70. * Revert "refactor: simplify validations" This reverts commitb1d23467a2. * Revert "refactor: create transaction system and apply to adding entry (#1620)" This reverts commit9a62daf047. * Revert "Refactor: better rounding (#1594)" This reverts commitb9ab1c6fd7. * Revert "refactor: improve reorder logic" This reverts commitc1054711b0. * Revert "chore: simplify URLs" This reverts commita4d4f29a37. * Revert "refactor: order is single source of truth" This reverts commit2793aadea0. * Revert "refactor: refetch targets is enum" This reverts commite7cfb7d9d9. * Revert "refactor: small ux improvements" This reverts commit256a851c9b. * Revert "refactor: remove trivially inferred numEvents" This reverts commit4ab9c81cb8. * Revert "feat: duplicate groups" This reverts commit2f13d6c89e. * Revert "feat: create group from entry selection" This reverts commitf1f7bad25e. * Revert "feat: create block from rundown empty" This reverts commita8b52a48f7. * Revert "fix: collapsed blocks dont render children" This reverts commitcd0999b2ab. * Revert "refactor: type cleanup and test improvements" This reverts commitb4c60f3f04. * Revert "feat: allow dissolving a block" This reverts commit5cefad3666. * Revert "fix: uncontrolled prop on controlled component" This reverts commit7a6ecd8c34. * Revert "refactor: improve return of reorder" This reverts commitba96ecfd91. * Revert "refactor: mutations on batch elements must have IDs" This reverts commit7bed3757f2. * Revert "chore: upgrade dependencies" This reverts commite2e755b1d2. * Revert "refactor: extract utility to merge two arrays" This reverts commita77d23109d. * Revert "refactor: change network mode defaults" This reverts commit0eb3b8d382. * Revert "fix: delete nested events" This reverts commit0021185288. * Revert "fix: add event at end of block" This reverts commit94c72ff4f6. * Revert "refactor: make finder available in exported rundown" This reverts commite4c08dc9b2. * Revert "assert non null and update test (#1604)" This reverts commitec74af0d62. * Revert "Fix project renumber (#1597)" This reverts commitb6d72dd082. * Revert "Refactor: WebSocket from flush queue to one patch (#1595)" This reverts commit31c311daf0. * Revert "Refactor: ms for api calls (#1593)" This reverts commite9b3cc6090. * Revert "fix test (#1601)" This reverts commit543b04a097. * Revert "fix: rebase master" This reverts commitd39b85b6e6. * Revert "chore: correct test path" This reverts commitbbe107bb2b. * Revert "refactor: extract rundown parsing" This reverts commitc616240db1. * Revert "chore: improve convention entry <> event" This reverts commit166be66ce3. * Revert "refactor: maintain flat orders" This reverts commit4180d0a337. * Revert "refactor: implement operations on nested events" This reverts commit78108e316c. * Revert "refactor: process events in rundown" This reverts commit3bb8b70915. * Revert "chore: improve convention entry <> event" This reverts commit1c4f13a0ed. * Revert "chore: rename currentBlock > parent" This reverts commit3ca0abad53. * Revert "refactor: fix delay positioning in gaps" This reverts commit030c8f897f. * Revert "refactor(e2e): skip flaky test" This reverts commit68175cfa3b. * Revert "refactor: improve project loading" This reverts commitfd8f757851. * Revert "refactor: gather group metadata" This reverts commit876d111c61. * Revert "refactor: swap maintains schedule" This reverts commit730cb95c04. * Revert "chore: rename files" This reverts commit3c388d4fb5. * Revert "refactor: restructure model to contain an object of rundowns" This reverts commit2e23718d73. * Revert "refactor: clearer relationship on rundown elements" This reverts commit89ea8c470b. * Revert "refactor: use strict typing" This reverts commit178640bfc4. * Revert "refactor: remove stop as a possible end action" This reverts commit4ed38340e0. * Revert "refactor: restructure model to contain an object of rundowns" This reverts commit69eb9a5eff. * Revert "chore: remove IDE files" This reverts commit351425127a. * Revert "refactor: remove unused and legacy code" This reverts commit95f2ba37cc.
293 lines
9.4 KiB
TypeScript
293 lines
9.4 KiB
TypeScript
import { LogOrigin, Playback, runtimeStorePlaceholder, SimpleDirection, SimplePlayback } from 'ontime-types';
|
|
|
|
import 'dotenv/config';
|
|
import express from 'express';
|
|
import http, { type Server } from 'http';
|
|
import cors from 'cors';
|
|
import serverTiming from 'server-timing';
|
|
import cookieParser from 'cookie-parser';
|
|
|
|
// import utils
|
|
import { publicDir, srcDir } from './setup/index.js';
|
|
import { environment, isProduction } from './setup/environment.js';
|
|
import { updateRouterPrefix } from './externals.js';
|
|
import { ONTIME_VERSION } from './ONTIME_VERSION.js';
|
|
import { consoleSuccess, consoleHighlight, consoleError } from './utils/console.js';
|
|
|
|
// Import middleware configuration
|
|
import { bodyParser } from './middleware/bodyParser.js';
|
|
import { compressedStatic } from './middleware/staticGZip.js';
|
|
import { loginRouter, makeAuthenticateMiddleware } from './middleware/authenticate.js';
|
|
|
|
// Import Routers
|
|
import { appRouter } from './api-data/index.js';
|
|
import { integrationRouter } from './api-integration/integration.router.js';
|
|
|
|
// Import adapters
|
|
import { socket } from './adapters/WebsocketAdapter.js';
|
|
import { getDataProvider } from './classes/data-provider/DataProvider.js';
|
|
|
|
// Services
|
|
import { logger } from './classes/Logger.js';
|
|
import { populateStyles } from './setup/loadStyles.js';
|
|
import { eventStore } from './stores/EventStore.js';
|
|
import { runtimeService } from './services/runtime-service/RuntimeService.js';
|
|
import { restoreService } from './services/RestoreService.js';
|
|
import * as messageService from './services/message-service/MessageService.js';
|
|
import { populateDemo } from './setup/loadDemo.js';
|
|
import { getState } from './stores/runtimeState.js';
|
|
import { initRundown } from './services/rundown-service/RundownService.js';
|
|
import { initialiseProject } from './services/project-service/ProjectService.js';
|
|
import { getShowWelcomeDialog } from './services/app-state-service/AppStateService.js';
|
|
import { oscServer } from './adapters/OscAdapter.js';
|
|
|
|
// Utilities
|
|
import { clearUploadfolder } from './utils/upload.js';
|
|
import { generateCrashReport } from './utils/generateCrashReport.js';
|
|
import { timerConfig } from './config/config.js';
|
|
import { serverTryDesiredPort, getNetworkInterfaces } from './utils/network.js';
|
|
|
|
console.log('\n');
|
|
consoleHighlight(`Starting Ontime version ${ONTIME_VERSION}`);
|
|
|
|
const canLog = isProduction;
|
|
if (!canLog) {
|
|
console.log(`Ontime running in ${environment} environment`);
|
|
console.log(`Ontime source directory at ${srcDir.root} `);
|
|
console.log(`Ontime public directory at ${publicDir.root} `);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
const app = express();
|
|
if (!isProduction) {
|
|
// log server timings to requests
|
|
app.use(serverTiming());
|
|
}
|
|
app.disable('x-powered-by');
|
|
|
|
// Implement middleware
|
|
app.use(cors()); // setup cors for all routes
|
|
app.options('*', cors()); // enable pre-flight cors
|
|
|
|
app.use(bodyParser);
|
|
app.use(cookieParser());
|
|
const { authenticate, authenticateAndRedirect } = makeAuthenticateMiddleware(prefix);
|
|
|
|
// Implement route endpoints
|
|
app.use(`${prefix}/login`, loginRouter); // router for login flow
|
|
app.use(`${prefix}/data`, authenticate, appRouter); // router for application data
|
|
app.use(`${prefix}/api`, authenticate, integrationRouter); // router for integrations
|
|
|
|
// serve static external files
|
|
app.use(`${prefix}/external`, express.static(publicDir.externalDir));
|
|
app.use(`${prefix}/external`, (req, res) => {
|
|
// if the user reaches to the root, we show a 404
|
|
res.status(404).send(`${req.originalUrl} not found`);
|
|
});
|
|
app.use(`${prefix}/user`, express.static(publicDir.userDir));
|
|
|
|
// Base route for static files
|
|
app.use(`${prefix}`, authenticateAndRedirect, compressedStatic);
|
|
app.use(`${prefix}/*`, authenticateAndRedirect, compressedStatic);
|
|
|
|
// Implement catch all
|
|
app.use((_error, response) => {
|
|
response.status(400).send('Unhandled request');
|
|
});
|
|
|
|
/*************** START SERVICES ***************/
|
|
|
|
/* Override config
|
|
* ----------------
|
|
*
|
|
* Configuration of services comes from app general config
|
|
* It can be overridden here by the settings in the db
|
|
* It can also be overridden on call
|
|
*
|
|
* Start order
|
|
* ----------------
|
|
*
|
|
* The services need to be started in a certain order,
|
|
* the enum below enforces that
|
|
*/
|
|
|
|
enum OntimeStartOrder {
|
|
Error,
|
|
InitAssets,
|
|
InitServer,
|
|
InitIO,
|
|
}
|
|
|
|
let step = OntimeStartOrder.InitAssets;
|
|
let expressServer: Server | null = null;
|
|
|
|
const checkStart = (currentState: OntimeStartOrder) => {
|
|
if (step !== currentState) {
|
|
step = OntimeStartOrder.Error;
|
|
throw new Error('Init order error: initAssets > startServer');
|
|
} else {
|
|
if (step === 1 || step === 2) {
|
|
step = step + 1;
|
|
}
|
|
}
|
|
};
|
|
|
|
export const initAssets = async () => {
|
|
checkStart(OntimeStartOrder.InitAssets);
|
|
await clearUploadfolder();
|
|
populateStyles();
|
|
await populateDemo();
|
|
const project = await initialiseProject();
|
|
logger.info(LogOrigin.Server, `Initialised Ontime with ${project}`);
|
|
};
|
|
|
|
/**
|
|
* Starts servers
|
|
*/
|
|
export const startServer = async (
|
|
escalateErrorFn?: (error: string, unrecoverable: boolean) => void,
|
|
): Promise<{ message: string; serverPort: number }> => {
|
|
checkStart(OntimeStartOrder.InitServer);
|
|
// initialise logging service, escalateErrorFn only exists in electron
|
|
logger.init(escalateErrorFn);
|
|
const settings = getDataProvider().getSettings();
|
|
const { serverPort: desiredPort } = settings;
|
|
|
|
expressServer = http.createServer(app);
|
|
|
|
// the express server must be started before the socket otherwise the on error event listener will not attach properly
|
|
const resultPort = await serverTryDesiredPort(expressServer, desiredPort);
|
|
await getDataProvider().setSettings({ ...settings, serverPort: resultPort });
|
|
const showWelcome = await getShowWelcomeDialog();
|
|
|
|
socket.init(expressServer, showWelcome, prefix);
|
|
|
|
/**
|
|
* Module initialises the services and provides initial payload for the store
|
|
*/
|
|
const state = getState();
|
|
eventStore.init({
|
|
clock: state.clock,
|
|
timer: state.timer,
|
|
onAir: state.timer.playback !== Playback.Stop,
|
|
message: { ...runtimeStorePlaceholder.message },
|
|
runtime: state.runtime,
|
|
eventNow: state.eventNow,
|
|
currentBlock: {
|
|
block: null,
|
|
startedAt: null,
|
|
},
|
|
publicEventNow: state.publicEventNow,
|
|
eventNext: state.eventNext,
|
|
publicEventNext: state.publicEventNext,
|
|
auxtimer1: {
|
|
duration: timerConfig.auxTimerDefault,
|
|
current: timerConfig.auxTimerDefault,
|
|
playback: SimplePlayback.Stop,
|
|
direction: SimpleDirection.CountDown,
|
|
},
|
|
ping: -1,
|
|
});
|
|
|
|
// initialise rundown service
|
|
const persistedRundown = getDataProvider().getRundown();
|
|
const persistedCustomFields = getDataProvider().getCustomFields();
|
|
await initRundown(persistedRundown, persistedCustomFields);
|
|
|
|
// initialise message service
|
|
messageService.init(eventStore.set, eventStore.get);
|
|
|
|
// load restore point if it exists
|
|
const maybeRestorePoint = await restoreService.load();
|
|
|
|
// TODO: pass event store to rundownservice
|
|
runtimeService.init(maybeRestorePoint);
|
|
|
|
const nif = getNetworkInterfaces();
|
|
consoleSuccess(`Local: http://localhost:${resultPort}${prefix}/editor`);
|
|
for (const key in nif) {
|
|
const address = nif[key].address;
|
|
consoleSuccess(`Network: http://${address}:${resultPort}${prefix}/editor`);
|
|
}
|
|
|
|
const returnMessage = `Ontime is listening on port ${resultPort}`;
|
|
logger.info(LogOrigin.Server, returnMessage);
|
|
|
|
return {
|
|
message: returnMessage,
|
|
serverPort: resultPort,
|
|
};
|
|
};
|
|
|
|
/**
|
|
* starts integrations
|
|
*/
|
|
export const startIntegrations = async () => {
|
|
checkStart(OntimeStartOrder.InitIO);
|
|
const { enabledOscIn, oscPortIn } = getDataProvider().getAutomation();
|
|
if (enabledOscIn) {
|
|
oscServer.init(oscPortIn);
|
|
} else {
|
|
logger.info(LogOrigin.Server, 'Skipping OSC integration');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @description clean shutdown app services
|
|
* @param {number} exitCode
|
|
* @return {Promise<void>}
|
|
*/
|
|
export const shutdown = async (exitCode = 0) => {
|
|
consoleHighlight(`Ontime shutting down with code ${exitCode}`);
|
|
|
|
// clear the restore file if it was a normal exit
|
|
// 0 means it was a SIGNAL
|
|
// 1 means crash -> keep the file
|
|
// 2 means dev crash -> do nothing
|
|
// 99 means there was a shutdown request from the UI
|
|
if (exitCode === 0 || exitCode === 99) {
|
|
await restoreService.clear();
|
|
}
|
|
|
|
expressServer?.close();
|
|
runtimeService.shutdown();
|
|
logger.shutdown();
|
|
oscServer.shutdown();
|
|
socket.shutdown();
|
|
process.exit(exitCode);
|
|
};
|
|
|
|
process.on('exit', (code) => consoleHighlight(`Ontime shutdown with code: ${code}`));
|
|
|
|
process.on('unhandledRejection', async (error) => {
|
|
if (!isProduction && error instanceof Error && error.stack) {
|
|
consoleError(error.stack);
|
|
}
|
|
generateCrashReport(error);
|
|
logger.crash(LogOrigin.Server, `Uncaught rejection | ${error}`);
|
|
await shutdown(1);
|
|
});
|
|
|
|
process.on('uncaughtException', async (error) => {
|
|
if (!isProduction && error instanceof Error && error.stack) {
|
|
consoleError(error.stack);
|
|
}
|
|
generateCrashReport(error);
|
|
logger.crash(LogOrigin.Server, `Uncaught exception | ${error}`);
|
|
await shutdown(1);
|
|
});
|
|
|
|
// register shutdown signals
|
|
process.once('SIGHUP', async () => shutdown(0));
|
|
process.once('SIGINT', async () => shutdown(0));
|
|
process.once('SIGTERM', async () => shutdown(0));
|