refactor: small cleanups (#2059)

* refactor: remove unneeded async

* chore: add express Router type to all routes

* refactor: don't use index in react key

* refactor: correctly get error message in excel route

* refactor: avoid exporting muteable values
This commit is contained in:
Alex Christoffer Rasmussen
2026-04-26 17:32:50 +02:00
committed by GitHub
parent 23a44b2f04
commit 7c1d2f4554
22 changed files with 55 additions and 49 deletions
@@ -1,10 +1,9 @@
// skipcq: JS-C1003 - sentry does not expose itself as an ES Module. // skipcq: JS-C1003 - sentry does not expose itself as an ES Module.
import * as Sentry from '@sentry/react'; import * as Sentry from '@sentry/react';
/* eslint-disable react/destructuring-assignment */
import React from 'react'; import React from 'react';
import { hasConnected, reconnectAttempts } from '../../../common/utils/socket';
import { runtimeStore } from '../../stores/runtime'; import { runtimeStore } from '../../stores/runtime';
import { getConnectionState, getReconnectAttempts } from '../../utils/socket';
import style from './ErrorBoundary.module.scss'; import style from './ErrorBoundary.module.scss';
@@ -37,7 +36,7 @@ class ErrorBoundary extends React.Component {
scope.setExtras({ scope.setExtras({
error, error,
store: appState, store: appState,
hasSocket: { hasConnected, reconnectAttempts }, hasSocket: { hasConnected: getConnectionState(), reconnectAttempts: getReconnectAttempts() },
}); });
const eventId = Sentry.captureException(error); const eventId = Sentry.captureException(error);
this.setState({ eventId, info }); this.setState({ eventId, info });
@@ -64,8 +64,8 @@ function SectionContents({ options, collapsed }: SectionContentsProps) {
function HiddenContents({ options }: { options: ParamField[] }) { function HiddenContents({ options }: { options: ParamField[] }) {
return ( return (
<> <>
{options.map((option, index) => { {options.map((option) => {
return <ParamInput key={option.title + index} paramField={option} />; return <ParamInput key={option.id} paramField={option} />;
})} })}
</> </>
); );
+4 -2
View File
@@ -51,8 +51,10 @@ const socketConfig = {
offlineAttemptsThreshold: 2, // when we consider the client disconnected offlineAttemptsThreshold: 2, // when we consider the client disconnected
} as const; } as const;
export let hasConnected = false; export const getConnectionState = () => hasConnected;
export let reconnectAttempts = 0; export const getReconnectAttempts = () => reconnectAttempts;
let hasConnected = false;
let reconnectAttempts = 0;
export const connectSocket = () => { export const connectSocket = () => {
websocket = new WebSocket(websocketUrl); websocket = new WebSocket(websocketUrl);
@@ -80,6 +80,7 @@ function ProjectInfo({ projectData, isMirrored }: ProjectInfoData) {
{projectData.custom.map((info, idx) => { {projectData.custom.map((info, idx) => {
const hasUrl = Boolean(info.url); const hasUrl = Boolean(info.url);
return ( return (
// oxlint-disable-next-line react/no-array-index-key - we only have the index to go of here
<div key={`${info.title}-${idx}`} className='info__custom'> <div key={`${info.title}-${idx}`} className='info__custom'>
{hasUrl && ( {hasUrl && (
<div className='info__image-container'> <div className='info__image-container'>
@@ -1,4 +1,4 @@
import express from 'express'; import express, { Router } from 'express';
import { paramsWithId } from '../validation-utils/validationFunction.js'; import { paramsWithId } from '../validation-utils/validationFunction.js';
import { import {
@@ -21,7 +21,7 @@ import {
validateTriggerPatch, validateTriggerPatch,
} from './automation.validation.js'; } from './automation.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
router.get('/', getAutomationSettings); router.get('/', getAutomationSettings);
router.post('/', validateAutomationSettings, postAutomationSettings); router.post('/', validateAutomationSettings, postAutomationSettings);
@@ -1,5 +1,5 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express'; import type { Request, Response, Router } from 'express';
import { CustomField, CustomFields, ErrorResponse } from 'ontime-types'; import { CustomField, CustomFields, ErrorResponse } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils'; import { getErrorMessage } from 'ontime-utils';
@@ -8,12 +8,12 @@ import { getProjectCustomFields } from '../rundown/rundown.dao.js';
import { createCustomField, deleteCustomField, editCustomField } from '../rundown/rundown.service.js'; import { createCustomField, deleteCustomField, editCustomField } from '../rundown/rundown.service.js';
import { validateCustomField, validateDeleteCustomField, validateEditCustomField } from './customFields.validation.js'; import { validateCustomField, validateDeleteCustomField, validateEditCustomField } from './customFields.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
/** /**
* Gets all the custom fields for the project * Gets all the custom fields for the project
*/ */
router.get('/', async (_req: Request, res: Response<CustomFields>) => { router.get('/', (_req: Request, res: Response<CustomFields>) => {
const customFields = getProjectCustomFields(); const customFields = getProjectCustomFields();
res.status(200).json(customFields); res.status(200).json(customFields);
}); });
@@ -1,5 +1,5 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express'; import type { Request, Response, Router } from 'express';
import { type CustomViewsListResponse, type ErrorResponse, type MessageResponse } from 'ontime-types'; import { type CustomViewsListResponse, type ErrorResponse, type MessageResponse } from 'ontime-types';
import { handleCustomViewsError } from './customViews.errors.js'; import { handleCustomViewsError } from './customViews.errors.js';
@@ -13,7 +13,7 @@ import {
} from './customViews.service.js'; } from './customViews.service.js';
import { validateCustomViewSlugParam } from './customViews.validation.js'; import { validateCustomViewSlugParam } from './customViews.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
router.get('/', async (_req: Request, res: Response<CustomViewsListResponse | ErrorResponse>) => { router.get('/', async (_req: Request, res: Response<CustomViewsListResponse | ErrorResponse>) => {
try { try {
+2 -2
View File
@@ -1,4 +1,4 @@
import express from 'express'; import express, { Router } from 'express';
import { import {
createProjectFile, createProjectFile,
@@ -24,7 +24,7 @@ import {
validateQuickProject, validateQuickProject,
} from './db.validation.js'; } from './db.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
router.get('/', currentProjectDownload); router.get('/', currentProjectDownload);
router.post('/download', validateFilenameBody, projectDownload); router.post('/download', validateFilenameBody, projectDownload);
+10 -6
View File
@@ -1,11 +1,12 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express'; import type { Request, Response, Router } from 'express';
import type { import type {
ErrorResponse, ErrorResponse,
SpreadsheetPreviewResponse, SpreadsheetPreviewResponse,
SpreadsheetWorksheetMetadata, SpreadsheetWorksheetMetadata,
SpreadsheetWorksheetOptions, SpreadsheetWorksheetOptions,
} from 'ontime-types'; } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js'; import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { getProjectCustomFields } from '../rundown/rundown.dao.js'; import { getProjectCustomFields } from '../rundown/rundown.dao.js';
@@ -19,7 +20,7 @@ import {
validateWorksheetMetadataRequest, validateWorksheetMetadataRequest,
} from './excel.validation.js'; } from './excel.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
router.post( router.post(
'/upload', '/upload',
@@ -32,7 +33,8 @@ router.post(
const worksheetOptions = await readExcelFile(filePath); const worksheetOptions = await readExcelFile(filePath);
res.status(200).send(worksheetOptions); res.status(200).send(worksheetOptions);
} catch (error) { } catch (error) {
res.status(500).send({ message: String(error) }); const message = getErrorMessage(error);
res.status(500).send({ message });
} }
}, },
); );
@@ -46,7 +48,8 @@ router.post(
const data = generateRundownPreview(options); const data = generateRundownPreview(options);
res.status(200).send(data); res.status(200).send(data);
} catch (error) { } catch (error) {
res.status(500).send({ message: String(error) }); const message = getErrorMessage(error);
res.status(500).send({ message });
} }
}, },
); );
@@ -60,7 +63,8 @@ router.post(
const data = getWorksheetMetadata(worksheet); const data = getWorksheetMetadata(worksheet);
res.status(200).send(data); res.status(200).send(data);
} catch (error) { } catch (error) {
res.status(500).send({ message: String(error) }); const message = getErrorMessage(error);
res.status(500).send({ message });
} }
}, },
); );
@@ -75,7 +79,7 @@ router.get('/:rundownId/export', validateRundownExport, (req: Request, res: Resp
res.setHeader('Content-Type', EXCEL_MIME); res.setHeader('Content-Type', EXCEL_MIME);
res.setHeader('Content-Length', buffer.length.toString()); res.setHeader('Content-Length', buffer.length.toString());
res.status(200).send(buffer); res.status(200).send(buffer);
} catch (error) { } catch (_error) {
res.status(500).send({ message: 'Failed to generate Excel file' }); res.status(500).send({ message: 'Failed to generate Excel file' });
} }
}); });
+2 -2
View File
@@ -1,4 +1,4 @@
import express from 'express'; import express, { Router } from 'express';
import { router as assetsRouter } from './assets/assets.router.js'; import { router as assetsRouter } from './assets/assets.router.js';
import { router as automationsRouter } from './automation/automation.router.js'; import { router as automationsRouter } from './automation/automation.router.js';
@@ -15,7 +15,7 @@ import { router as sheetsRouter } from './sheets/sheets.router.js';
import { router as urlPresetsRouter } from './url-presets/urlPresets.router.js'; import { router as urlPresetsRouter } from './url-presets/urlPresets.router.js';
import { router as viewSettingsRouter } from './view-settings/viewSettings.router.js'; import { router as viewSettingsRouter } from './view-settings/viewSettings.router.js';
export const appRouter = express.Router(); export const appRouter: Router = express.Router();
appRouter.use('/automations', automationsRouter); appRouter.use('/automations', automationsRouter);
appRouter.use('/custom-fields', customFieldsRouter); appRouter.use('/custom-fields', customFieldsRouter);
@@ -1,5 +1,5 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express'; import type { Request, Response, Router } from 'express';
import type { ErrorResponse, ProjectData } from 'ontime-types'; import type { ErrorResponse, ProjectData } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils'; import { getErrorMessage } from 'ontime-utils';
@@ -9,7 +9,7 @@ import { uploadImageFile } from '../db/db.middleware.js';
import * as projectDao from './projectData.dao.js'; import * as projectDao from './projectData.dao.js';
import { projectSanitiser } from './projectData.validation.js'; import { projectSanitiser } from './projectData.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
router.get('/', (_req: Request, res: Response<ProjectData>) => { router.get('/', (_req: Request, res: Response<ProjectData>) => {
res.status(200).json(projectDao.getProjectData()); res.status(200).json(projectDao.getProjectData());
@@ -1,10 +1,10 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express'; import type { Request, Response, Router } from 'express';
import { paramsWithId } from '../validation-utils/validationFunction.js'; import { paramsWithId } from '../validation-utils/validationFunction.js';
import * as report from './report.service.js'; import * as report from './report.service.js';
export const router = express.Router(); export const router: Router = express.Router();
router.get('/', (_req: Request, res: Response) => { router.get('/', (_req: Request, res: Response) => {
res.status(200).json(report.generate()); res.status(200).json(report.generate());
@@ -1,4 +1,4 @@
import type { Request, Response } from 'express'; import type { Request, Response, Router } from 'express';
import express from 'express'; import express from 'express';
import { ErrorResponse, OntimeEntry, ProjectRundownsList, Rundown } from 'ontime-types'; import { ErrorResponse, OntimeEntry, ProjectRundownsList, Rundown } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils'; import { getErrorMessage } from 'ontime-utils';
@@ -35,7 +35,7 @@ import {
validateRundownMutation, validateRundownMutation,
} from './rundown.validation.js'; } from './rundown.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
// #region operations on project rundowns ========================= // #region operations on project rundowns =========================
@@ -1,12 +1,12 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express'; import type { Request, Response, Router } from 'express';
import type { ErrorResponse, GetInfo, GetUrl, SessionStats } from 'ontime-types'; import type { ErrorResponse, GetInfo, GetUrl, SessionStats } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils'; import { getErrorMessage } from 'ontime-utils';
import * as sessionService from './session.service.js'; import * as sessionService from './session.service.js';
import { validateGenerateUrl } from './session.validation.js'; import { validateGenerateUrl } from './session.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
router.get('/', async (_req: Request, res: Response<SessionStats | ErrorResponse>) => { router.get('/', async (_req: Request, res: Response<SessionStats | ErrorResponse>) => {
try { try {
@@ -18,9 +18,9 @@ router.get('/', async (_req: Request, res: Response<SessionStats | ErrorResponse
} }
}); });
router.get('/info', async (_req: Request, res: Response<GetInfo | ErrorResponse>) => { router.get('/info', (_req: Request, res: Response<GetInfo | ErrorResponse>) => {
try { try {
const info = await sessionService.getInfo(); const info = sessionService.getInfo();
res.status(200).send(info); res.status(200).send(info);
} catch (error) { } catch (error) {
const message = getErrorMessage(error); const message = getErrorMessage(error);
@@ -37,7 +37,7 @@ export async function getSessionStats(): Promise<SessionStats> {
/** /**
* Adds business logic to gathering data for the info endpoint * Adds business logic to gathering data for the info endpoint
*/ */
export async function getInfo(): Promise<GetInfo> { export function getInfo(): GetInfo {
const { version } = getDataProvider().getSettings(); const { version } = getDataProvider().getSettings();
const { port } = portManager.getPort(); const { port } = portManager.getPort();
@@ -1,5 +1,5 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express'; import type { Request, Response, Router } from 'express';
import { matchedData } from 'express-validator'; import { matchedData } from 'express-validator';
import { deepEqual } from 'fast-equals'; import { deepEqual } from 'fast-equals';
import { ErrorResponse, PortInfo, RefetchKey, Settings } from 'ontime-types'; import { ErrorResponse, PortInfo, RefetchKey, Settings } from 'ontime-types';
@@ -11,7 +11,7 @@ import { portManager } from '../../classes/port-manager/PortManager.js';
import * as appState from '../../services/app-state-service/AppStateService.js'; import * as appState from '../../services/app-state-service/AppStateService.js';
import { validateSettings, validateWelcomeDialog, validateServerPort } from './settings.validation.js'; import { validateSettings, validateWelcomeDialog, validateServerPort } from './settings.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
router.post('/welcomedialog', validateWelcomeDialog, async (req: Request, res: Response) => { router.post('/welcomedialog', validateWelcomeDialog, async (req: Request, res: Response) => {
const show = await appState.setShowWelcomeDialog(req.body.show); const show = await appState.setShowWelcomeDialog(req.body.show);
@@ -56,7 +56,7 @@ export async function requestConnection(
/** /**
* Returns the current Google Sheets authentication status for this server session. * Returns the current Google Sheets authentication status for this server session.
*/ */
export async function verifyAuthentication( export function verifyAuthentication(
_req: Request, _req: Request,
res: Response<{ authenticated: AuthenticationStatus } | ErrorResponse>, res: Response<{ authenticated: AuthenticationStatus } | ErrorResponse>,
) { ) {
@@ -72,7 +72,7 @@ export async function verifyAuthentication(
/** /**
* Clears the current Google Sheets authentication session. * Clears the current Google Sheets authentication session.
*/ */
export async function revokeAuthentication( export function revokeAuthentication(
_req: Request, _req: Request,
res: Response<{ authenticated: AuthenticationStatus } | ErrorResponse>, res: Response<{ authenticated: AuthenticationStatus } | ErrorResponse>,
) { ) {
@@ -2,7 +2,7 @@
* This is a feature specific router for integration with google sheets * This is a feature specific router for integration with google sheets
*/ */
import express from 'express'; import express, { Router } from 'express';
import { import {
getWorksheetMetadataFromSheet, getWorksheetMetadataFromSheet,
@@ -21,7 +21,7 @@ import {
validateWorksheetMetadata, validateWorksheetMetadata,
} from './sheets.validation.js'; } from './sheets.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
router.get('/connect', verifyAuthentication); router.get('/connect', verifyAuthentication);
router.post('/:sheetId/connect', uploadClientSecret, validateRequestConnection, requestConnection); router.post('/:sheetId/connect', uploadClientSecret, validateRequestConnection, requestConnection);
@@ -1,5 +1,5 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express'; import type { Request, Response, Router } from 'express';
import { type ErrorResponse, RefetchKey, type URLPreset } from 'ontime-types'; import { type ErrorResponse, RefetchKey, type URLPreset } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils'; import { getErrorMessage } from 'ontime-utils';
@@ -7,7 +7,7 @@ import { sendRefetch } from '../../adapters/WebsocketAdapter.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js'; import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { validateNewPreset, validatePresetParam, validateUpdatePreset } from './urlPresets.validation.js'; import { validateNewPreset, validatePresetParam, validateUpdatePreset } from './urlPresets.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
router.get('/', (_req: Request, res: Response<URLPreset[]>) => { router.get('/', (_req: Request, res: Response<URLPreset[]>) => {
const presets = getDataProvider().getUrlPresets(); const presets = getDataProvider().getUrlPresets();
@@ -1,5 +1,5 @@
import express from 'express'; import express from 'express';
import type { Request, Response } from 'express'; import type { Request, Response, Router } from 'express';
import { type ErrorResponse, RefetchKey, type ViewSettings } from 'ontime-types'; import { type ErrorResponse, RefetchKey, type ViewSettings } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils'; import { getErrorMessage } from 'ontime-utils';
@@ -7,7 +7,7 @@ import { sendRefetch } from '../../adapters/WebsocketAdapter.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js'; import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { validateViewSettings } from './viewSettings.validation.js'; import { validateViewSettings } from './viewSettings.validation.js';
export const router = express.Router(); export const router: Router = express.Router();
router.get('/', (_req: Request, res: Response<ViewSettings>) => { router.get('/', (_req: Request, res: Response<ViewSettings>) => {
const views = getDataProvider().getViewSettings(); const views = getDataProvider().getViewSettings();
@@ -5,7 +5,7 @@
* *
*/ */
import express, { type Request, type Response } from 'express'; import express, { Router, type Request, type Response } from 'express';
import { ErrorResponse, LogOrigin } from 'ontime-types'; import { ErrorResponse, LogOrigin } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils'; import { getErrorMessage } from 'ontime-utils';
@@ -14,7 +14,7 @@ import { logger } from '../classes/Logger.js';
import { isEmptyObject } from '../utils/parserUtils.js'; import { isEmptyObject } from '../utils/parserUtils.js';
import { dispatchFromAdapter } from './integration.controller.js'; import { dispatchFromAdapter } from './integration.controller.js';
export const integrationRouter = express.Router(); export const integrationRouter: Router = express.Router();
const helloMessage = 'You have reached Ontime API server'; const helloMessage = 'You have reached Ontime API server';
+2 -2
View File
@@ -1,7 +1,7 @@
import type { IncomingMessage } from 'node:http'; import type { IncomingMessage } from 'node:http';
import { parse as parseCookie } from 'cookie'; import { parse as parseCookie } from 'cookie';
import express, { type NextFunction, type Request, type Response } from 'express'; import express, { Router, type NextFunction, type Request, type Response } from 'express';
import type { WebSocket } from 'ws'; import type { WebSocket } from 'ws';
import { hasPassword, hashedPassword } from '../api-data/session/session.service.js'; import { hasPassword, hashedPassword } from '../api-data/session/session.service.js';
@@ -40,7 +40,7 @@ export function isPublicAssetRequest(originalUrl: string, prefix: string): boole
* @param {string} prefix - Prefix is used for the client hashes in Ontime Cloud * @param {string} prefix - Prefix is used for the client hashes in Ontime Cloud
*/ */
export function makeLoginRouter(prefix: string) { export function makeLoginRouter(prefix: string) {
const router = express.Router(); const router: Router = express.Router();
// serve static files at root // serve static files at root
router.use('/', express.static(srcFiles.login)); router.use('/', express.static(srcFiles.login));