refactor: settings route (#1753)

* refactor: settings route
This commit is contained in:
Alex Christoffer Rasmussen
2025-09-03 11:20:03 -04:00
committed by Carlos Valente
parent eace97ec7a
commit f219ebb8f8
7 changed files with 78 additions and 102 deletions
@@ -1,71 +0,0 @@
import { ErrorResponse, Settings } from 'ontime-types';
import { getErrorMessage, obfuscate } from 'ontime-utils';
import type { Request, Response } from 'express';
import { isDocker } from '../../setup/environment.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import * as appState from '../../services/app-state-service/AppStateService.js';
import { extractPin } from './settings.utils.js';
export async function getSettings(_req: Request, res: Response<Settings>) {
const settings = getDataProvider().getSettings();
const obfuscatedSettings = { ...settings };
if (settings.editorKey) {
obfuscatedSettings.editorKey = obfuscate(settings.editorKey);
}
if (settings.operatorKey) {
obfuscatedSettings.operatorKey = obfuscate(settings.operatorKey);
}
res.status(200).send(obfuscatedSettings);
}
export async function postSettings(req: Request, res: Response<Settings | ErrorResponse>) {
try {
const settings = getDataProvider().getSettings();
const editorKey = extractPin(req.body?.editorKey, settings.editorKey);
const operatorKey = extractPin(req.body?.operatorKey, settings.operatorKey);
const serverPort = Number(req.body?.serverPort);
//TODO: should this not be part of the validator?
if (isNaN(serverPort)) {
res.status(400).send({ message: `Invalid value found for server port: ${req.body?.serverPort}` });
return;
}
const hasChangedPort = settings.serverPort !== serverPort;
if (isDocker && hasChangedPort) {
res.status(403).json({ message: 'Can`t change port when running inside docker' });
return;
}
let timeFormat = settings.timeFormat;
if (req.body?.timeFormat === '12' || req.body?.timeFormat === '24') {
timeFormat = req.body.timeFormat;
}
const language = req.body?.language || 'en';
const newData = {
...settings,
editorKey,
operatorKey,
timeFormat,
language,
serverPort,
};
await getDataProvider().setSettings(newData);
res.status(200).send(newData);
} catch (error) {
const message = getErrorMessage(error);
res.status(400).send({ message });
}
}
export async function postWelcomeDialog(req: Request, res: Response) {
const show = await appState.setShowWelcomeDialog(req.body.show);
res.status(200).send({ show });
}
@@ -1,10 +1,58 @@
import express from 'express';
import { getSettings, postSettings, postWelcomeDialog } from './settings.controller.js';
import { matchedData } from 'express-validator';
import type { Request, Response } from 'express';
import { deepEqual } from 'fast-equals';
import { ErrorResponse, RefetchKey, Settings } from 'ontime-types';
import { getErrorMessage, obfuscate } from 'ontime-utils';
import { validateSettings, validateWelcomeDialog } from './settings.validation.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import * as appState from '../../services/app-state-service/AppStateService.js';
import { isDocker } from '../../setup/environment.js';
import { sendRefetch } from '../../adapters/WebsocketAdapter.js';
export const router = express.Router();
router.post('/welcomedialog', validateWelcomeDialog, postWelcomeDialog);
router.post('/welcomedialog', validateWelcomeDialog, async (req: Request, res: Response) => {
const show = await appState.setShowWelcomeDialog(req.body.show);
res.status(200).json({ show });
});
router.get('/', getSettings);
router.post('/', validateSettings, postSettings);
router.get('/', (_req: Request, res: Response<Settings>) => {
const settings = getDataProvider().getSettings();
const obfuscatedSettings = { ...settings };
if (settings.editorKey) {
obfuscatedSettings.editorKey = obfuscate(settings.editorKey);
}
if (settings.operatorKey) {
obfuscatedSettings.operatorKey = obfuscate(settings.operatorKey);
}
res.status(200).json(obfuscatedSettings);
});
router.post('/', validateSettings, async (req: Request, res: Response<Settings | ErrorResponse>) => {
try {
const data = matchedData<Settings>(req);
const settings = getDataProvider().getSettings();
if (isDocker && settings.serverPort !== data.serverPort) {
res.status(403).json({ message: 'Can`t change port when running inside docker' });
return;
}
data.version = settings.version;
if (!deepEqual(data, settings)) {
await getDataProvider().setSettings(data);
sendRefetch(RefetchKey.Settings);
}
res.status(200).json(data);
} catch (error) {
const message = getErrorMessage(error);
res.status(400).json({ message });
}
});
@@ -1,15 +0,0 @@
/**
* Business logic for resolving a string
*/
export function extractPin(value: string | undefined | null, fallback: string | null): string | null {
if (value === null) {
return value;
}
if (typeof value === 'undefined') {
return fallback;
}
if (value.length === 0) {
return null;
}
return value;
}
@@ -1,4 +1,4 @@
import { body } from 'express-validator';
import { body, checkExact } from 'express-validator';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
/**
@@ -6,16 +6,30 @@ import { requestValidationFunction } from '../validation-utils/validationFunctio
*/
export const validateWelcomeDialog = [body('show').isBoolean(), requestValidationFunction];
const pinValidator = (key: string) => {
return body(key)
.isString()
.trim()
.isLength({ min: 0, max: 4 })
.customSanitizer((input) => {
if (input.length === 0) {
return null;
}
return input;
});
};
/**
* @description Validates object for POST /ontime/settings
*/
export const validateSettings = [
body().notEmpty().withMessage('No object found in request'),
body('editorKey').isString().isLength({ min: 0, max: 4 }).optional({ nullable: true }),
body('operatorKey').isString().isLength({ min: 0, max: 4 }).optional({ nullable: true }),
body('timeFormat').isString().isIn(['12', '24']),
body('language').isString(),
body('serverPort').isPort().optional(),
pinValidator('editorKey'),
pinValidator('operatorKey'),
body('timeFormat').isString().isIn(['12', '24']).withMessage('Time format can only be "12" or "24"'),
body('language').isString().trim().notEmpty(),
body('serverPort').isPort().withMessage('Invalid value found for server port').toInt(),
checkExact(),
requestValidationFunction,
];
// TODO: dont allow other keys