Deps migration (#1988)

* chore: migrate eslint to oxlint

* chore: migrate prettier to oxfmt

* chore: migrate typescript

* chore: toThrow should have a expected value

* chore: cast test value as Day

* chore: small title fix

* chore: mocks should be hoisted

* chore: incorrect async useage

* chore: test should be inside description

* chore: test sohuld include an expeced

* chore: oxfmt

---------

Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
This commit is contained in:
Carlos Valente
2026-03-08 16:22:12 +01:00
committed by GitHub
parent 9516ac21bb
commit 1849b4d39f
501 changed files with 2678 additions and 4654 deletions
+6 -7
View File
@@ -1,14 +1,13 @@
import { LogOrigin } from 'ontime-types';
import { fromBuffer, type OscPacketOutput } from 'osc-min';
import * as dgram from 'node:dgram';
import { logger } from '../classes/Logger.js';
import { dispatchFromAdapter } from '../api-integration/integration.controller.js';
import { isOntimeCloud } from '../setup/environment.js';
import { LogOrigin } from 'ontime-types';
import { type OscPacketOutput, fromBuffer } from 'osc-min';
import { integrationPayloadFromPath } from './utils/parse.js';
import { dispatchFromAdapter } from '../api-integration/integration.controller.js';
import { logger } from '../classes/Logger.js';
import { isOntimeCloud } from '../setup/environment.js';
import type { IAdapter } from './IAdapter.js';
import { integrationPayloadFromPath } from './utils/parse.js';
class OscServer implements IAdapter {
private udpSocket: dgram.Socket | null = null;
+10 -10
View File
@@ -14,26 +14,26 @@
* Payload: adds necessary payload for the request to be completed
*/
import type { Server } from 'http';
import {
Client,
LogOrigin,
WsPacketToClient,
WsPacketToServer,
MaybeNumber,
MessageTag,
RefetchKey,
MaybeNumber,
WsPacketToClient,
WsPacketToServer,
} from 'ontime-types';
import { generateId } from 'ontime-utils';
import { WebSocket, WebSocketServer } from 'ws';
import type { Server } from 'http';
import { dispatchFromAdapter } from '../api-integration/integration.controller.js';
import { logger } from '../classes/Logger.js';
import { authenticateSocket } from '../middleware/authenticate.js';
import { eventStore } from '../stores/EventStore.js';
import getRandomName from '../utils/getRandomName.js';
import type { IAdapter } from './IAdapter.js';
import { eventStore } from '../stores/EventStore.js';
import { logger } from '../classes/Logger.js';
import { dispatchFromAdapter } from '../api-integration/integration.controller.js';
import { generateId } from 'ontime-utils';
import { authenticateSocket } from '../middleware/authenticate.js';
type ClientId = string;
let instance: SocketServer | null = null;
@@ -1,11 +1,12 @@
import express from 'express';
import type { Request, Response } from 'express';
import { RefetchKey, type ErrorResponse } from 'ontime-types';
import { validatePostCss, validatePostTranslation } from './assets.validation.js';
import { readCssFile, writeCssFile, writeUserTranslation } from './assets.service.js';
import { type ErrorResponse, RefetchKey } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { defaultCss } from '../../user/styles/bundledCss.js';
import { sendRefetch } from '../../adapters/WebsocketAdapter.js';
import { defaultCss } from '../../user/styles/bundledCss.js';
import { readCssFile, writeCssFile, writeUserTranslation } from './assets.service.js';
import { validatePostCss, validatePostTranslation } from './assets.validation.js';
export const router = express.Router();
@@ -1,8 +1,8 @@
import type { TranslationObject } from 'ontime-types';
import { existsSync } from 'node:fs';
import { readFile, writeFile } from 'node:fs/promises';
import type { TranslationObject } from 'ontime-types';
import { publicFiles } from '../../setup/index.js';
import { defaultCss } from '../../user/styles/bundledCss.js';
@@ -1,41 +1,37 @@
import { TriggerDTO, TimerLifeCycle, AutomationDTO, Automation, ProjectRundowns } from 'ontime-types';
import { Automation, AutomationDTO, ProjectRundowns, TimerLifeCycle, TriggerDTO } from 'ontime-types';
import { makeOntimeEvent } from '../../rundown/__mocks__/rundown.mocks.js';
import {
addTrigger,
addAutomation,
addTrigger,
deleteAll,
deleteAllTriggers,
deleteTrigger,
deleteAutomation,
editTrigger,
deleteTrigger,
editAutomation,
editTrigger,
getAutomationTriggers,
getAutomations,
} from '../automation.dao.js';
import { makeHTTPAction, makeOSCAction } from './testUtils.js';
import { makeOSCAction, makeHTTPAction } from './testUtils.js';
beforeAll(() => {
vi.mock('../../../classes/data-provider/DataProvider.js', () => {
// Create a small mock store
let automations = {
enabledAutomations: true,
enabledOscIn: true,
oscPortIn: 8888,
triggers: [],
automations: {},
};
return {
getDataProvider: vi.fn().mockImplementation(() => {
return {
getAutomation: vi.fn().mockImplementation(() => automations),
setAutomation: vi.fn().mockImplementation((newData) => (automations = newData)),
};
}),
};
});
vi.mock('../../../classes/data-provider/DataProvider.js', () => {
// Create a small mock store
let automations = {
enabledAutomations: true,
enabledOscIn: true,
oscPortIn: 8888,
triggers: [],
automations: {},
};
return {
getDataProvider: vi.fn().mockImplementation(() => {
return {
getAutomation: vi.fn().mockImplementation(() => automations),
setAutomation: vi.fn().mockImplementation((newData) => (automations = newData)),
};
}),
};
});
afterAll(() => {
@@ -141,7 +137,7 @@ describe('addAutomation()', () => {
});
});
describe('editAutomation()', async () => {
describe('editAutomation()', () => {
// saving the ID of the added automation
let firstAutomation: Automation;
beforeEach(async () => {
@@ -1,35 +1,31 @@
import { PlayableEvent, TimerLifeCycle } from 'ontime-types';
import { makeRuntimeStoreData } from '../../../stores/__mocks__/runtimeStore.mocks.js';
import { deleteAllTriggers, addTrigger, addAutomation } from '../automation.dao.js';
import { testConditions, triggerAutomations } from '../automation.service.js';
import * as oscClient from '../clients/osc.client.js';
import * as httpClient from '../clients/http.client.js';
import { makeOSCAction, makeHTTPAction } from './testUtils.js';
import { RuntimeState } from '../../../stores/runtimeState.js';
import { makeOntimeEvent } from '../../rundown/__mocks__/rundown.mocks.js';
import { addAutomation, addTrigger, deleteAllTriggers } from '../automation.dao.js';
import { testConditions, triggerAutomations } from '../automation.service.js';
import * as httpClient from '../clients/http.client.js';
import * as oscClient from '../clients/osc.client.js';
import { makeHTTPAction, makeOSCAction } from './testUtils.js';
beforeAll(() => {
vi.mock('../../../classes/data-provider/DataProvider.js', () => {
// Create a small mock store
let automations = {
enabledAutomations: true,
enabledOscIn: true,
oscPortIn: 8888,
triggers: [],
automations: {},
};
return {
getDataProvider: vi.fn().mockImplementation(() => {
return {
getAutomation: vi.fn().mockImplementation(() => automations),
setAutomation: vi.fn().mockImplementation((newData) => (automations = newData)),
};
}),
};
});
vi.mock('../../../classes/data-provider/DataProvider.js', () => {
// Create a small mock store
let automations = {
enabledAutomations: true,
enabledOscIn: true,
oscPortIn: 8888,
triggers: [],
automations: {},
};
return {
getDataProvider: vi.fn().mockImplementation(() => {
return {
getAutomation: vi.fn().mockImplementation(() => automations),
setAutomation: vi.fn().mockImplementation((newData) => (automations = newData)),
};
}),
};
});
afterAll(() => {
@@ -41,7 +37,7 @@ describe('triggerAction()', () => {
let httpSpy = vi.spyOn(httpClient, 'emitHTTP');
beforeAll(() => {
vi.mock('../../../stores/EventStore.js', () => {
vi.doMock('../../../stores/EventStore.js', () => {
// Create a small mock store
return {
eventStore: {
@@ -49,11 +45,11 @@ describe('triggerAction()', () => {
},
};
});
})
});
beforeEach(async () => {
oscSpy = vi.spyOn(oscClient, 'emitOSC').mockImplementation(() => { });
httpSpy = vi.spyOn(httpClient, 'emitHTTP').mockImplementation(() => { });
oscSpy = vi.spyOn(oscClient, 'emitOSC').mockImplementation(() => {});
httpSpy = vi.spyOn(httpClient, 'emitHTTP').mockImplementation(() => {});
await deleteAllTriggers();
const oscAutomation = await addAutomation({
@@ -1,7 +1,6 @@
import { ProjectRundowns, TimerLifeCycle } from 'ontime-types';
import { makeOntimeEvent } from '../../rundown/__mocks__/rundown.mocks.js';
import { isAutomationUsed, parseTemplateNested, stringToOSCArgs } from '../automation.utils.js';
describe('parseTemplateNested()', () => {
@@ -22,7 +22,7 @@ describe('parseOutput', () => {
address: '/test',
args: 'test',
};
expect(() => parseOutput(payload)).toThrow();
expect(() => parseOutput(payload)).toThrow('Unexpected payload type:');
});
});
describe('handles HTTP outputs', () => {
@@ -39,7 +39,7 @@ describe('parseOutput', () => {
const payload = {
type: 'http',
};
expect(() => parseOutput(payload)).toThrow();
expect(() => parseOutput(payload)).toThrow('Unexpected payload type:');
});
});
describe('handles Ontime outputs', () => {
@@ -79,7 +79,7 @@ describe('parseOutput', () => {
type: 'ontime',
action: 'not-exist',
};
expect(() => parseOutput(payload)).toThrow();
expect(() => parseOutput(payload)).toThrow('Invalid Ontime action');
});
it('parses message-set', () => {
@@ -122,45 +122,46 @@ describe('parseOutput', () => {
text: 123,
visible: '',
}),
).toThrow();
).toThrow('Unexpected payload type:');
});
it('parses message-secondary', () => {});
expect(
parseOutput({
type: 'ontime',
action: 'message-secondary',
secondarySource: 'test',
}),
).toMatchObject({
secondarySource: null,
});
expect(
parseOutput({
type: 'ontime',
action: 'message-secondary',
secondarySource: '',
}),
).toMatchObject({
secondarySource: null,
});
expect(
parseOutput({
type: 'ontime',
action: 'message-secondary',
it('parses message-secondary', () => {
expect(
parseOutput({
type: 'ontime',
action: 'message-secondary',
secondarySource: 'test',
}),
).toMatchObject({
secondarySource: null,
});
expect(
parseOutput({
type: 'ontime',
action: 'message-secondary',
secondarySource: '',
}),
).toMatchObject({
secondarySource: null,
});
expect(
parseOutput({
type: 'ontime',
action: 'message-secondary',
secondarySource: 'aux1',
}),
).toMatchObject({
secondarySource: 'aux1',
}),
).toMatchObject({
secondarySource: 'aux1',
});
expect(
parseOutput({
type: 'ontime',
action: 'message-secondary',
});
expect(
parseOutput({
type: 'ontime',
action: 'message-secondary',
secondarySource: 'secondary',
}),
).toMatchObject({
secondarySource: 'secondary',
}),
).toMatchObject({
secondarySource: 'secondary',
});
});
});
});
@@ -1,4 +1,4 @@
import { OSCOutput, HTTPOutput } from 'ontime-types';
import { HTTPOutput, OSCOutput } from 'ontime-types';
export function makeOSCAction(action?: Partial<OSCOutput>): OSCOutput {
return {
@@ -1,11 +1,9 @@
import { getErrorMessage } from 'ontime-utils';
import { Automation, AutomationSettings, ErrorResponse, Trigger } from 'ontime-types';
import type { Request, Response } from 'express';
import { Automation, AutomationSettings, ErrorResponse, Trigger } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { oscServer } from '../../adapters/OscAdapter.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import * as automationDao from './automation.dao.js';
import * as automationService from './automation.service.js';
import { parseOutput } from './automation.validation.js';
@@ -10,7 +10,6 @@ import type {
import { deleteAtIndex, generateId } from 'ontime-utils';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { isAutomationUsed } from './automation.utils.js';
/**
@@ -1,7 +1,7 @@
import { DatabaseModel, AutomationSettings, NormalisedAutomation, Trigger } from 'ontime-types';
import { AutomationSettings, DatabaseModel, NormalisedAutomation, Trigger } from 'ontime-types';
import type { ErrorEmitter } from '../../utils/parserUtils.js';
import { getPartialProject } from '../../models/dataModel.js';
import type { ErrorEmitter } from '../../utils/parserUtils.js';
export function parseAutomationSettings(data: Partial<DatabaseModel>, emitError?: ErrorEmitter): AutomationSettings {
const defaultAutomation: AutomationSettings = getPartialProject('automation');
@@ -1,25 +1,25 @@
import express from 'express';
import { paramsWithId } from '../validation-utils/validationFunction.js';
import {
deleteTrigger,
deleteAutomation,
deleteTrigger,
editAutomation,
getAutomationSettings,
postTrigger,
postAutomation,
putTrigger,
postAutomationSettings,
postTrigger,
putTrigger,
testOutput,
} from './automation.controller.js';
import {
validateAutomationSettings,
validateAutomation,
validateAutomationPatch,
validateAutomationSettings,
validateTestPayload,
validateTrigger,
validateTriggerPatch,
} from './automation.validation.js';
import { paramsWithId } from '../validation-utils/validationFunction.js';
export const router = express.Router();
@@ -1,25 +1,24 @@
import {
isHTTPOutput,
isOntimeAction,
isOSCOutput,
LogOrigin,
RuntimeStore,
TimerLifeCycle,
type AutomationFilter,
type AutomationOutput,
type FilterRule,
LogOrigin,
RuntimeStore,
TimerLifeCycle,
isHTTPOutput,
isOSCOutput,
isOntimeAction,
} from 'ontime-types';
import { getPropertyFromPath } from 'ontime-utils';
import { emitOSC } from './clients/osc.client.js';
import { emitHTTP } from './clients/http.client.js';
import { getAutomationsEnabled, getAutomations, getAutomationTriggers } from './automation.dao.js';
import { isContained, isEquivalent, isGreaterThan, isLessThan } from './automation.utils.js';
import { toOntimeAction } from './clients/ontime.client.js';
import { logger } from '../../classes/Logger.js';
import { isOntimeCloud } from '../../setup/environment.js';
import { eventStore } from '../../stores/EventStore.js';
import { getAutomationTriggers, getAutomations, getAutomationsEnabled } from './automation.dao.js';
import { isContained, isEquivalent, isGreaterThan, isLessThan } from './automation.utils.js';
import { emitHTTP } from './clients/http.client.js';
import { toOntimeAction } from './clients/ontime.client.js';
import { emitOSC } from './clients/osc.client.js';
/**
* Exposes a method for triggering actions based on a TimerLifeCycle event
@@ -1,15 +1,15 @@
import {
EntryId,
FilterRule,
isOntimeEvent,
MaybeNumber,
OntimeAction,
ontimeActionKeyValues,
ProjectRundowns,
RundownEntries,
isOntimeEvent,
ontimeActionKeyValues,
} from 'ontime-types';
import { millisToString, removeLeadingZero, splitWhitespace, getPropertyFromPath } from 'ontime-utils';
import type { OscArgOrArrayInput, OscArgInput } from 'osc-min';
import { getPropertyFromPath, millisToString, removeLeadingZero, splitWhitespace } from 'ontime-utils';
import type { OscArgInput, OscArgOrArrayInput } from 'osc-min';
type FilterOperator = 'equals' | 'not_equals' | 'greater_than' | 'less_than' | 'contains';
@@ -1,20 +1,18 @@
import { body, oneOf, param } from 'express-validator';
import {
Automation,
AutomationFilter,
AutomationOutput,
HTTPOutput,
OntimeAction,
OSCOutput,
OntimeAction,
SecondarySource,
timerLifecycleValues,
} from 'ontime-types';
import { body, oneOf, param } from 'express-validator';
import * as assert from '../../utils/assert.js';
import { isFilterOperator, isFilterRule, isOntimeActionAction } from './automation.utils.js';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
import { isFilterOperator, isFilterRule, isOntimeActionAction } from './automation.utils.js';
export const validateAutomationSettings = [
body('enabledAutomations').isBoolean(),
@@ -1,8 +1,7 @@
import { DeepReadonly } from 'ts-essentials';
import { HTTPOutput, LogOrigin, RuntimeStore } from 'ontime-types';
import { DeepReadonly } from 'ts-essentials';
import { logger } from '../../../classes/Logger.js';
import { parseTemplateNested } from '../automation.utils.js';
/**
@@ -1,9 +1,9 @@
import { LogOrigin, OntimeAction } from 'ontime-types';
import { parseUserTime } from 'ontime-utils';
import { logger } from '../../../classes/Logger.js';
import { auxTimerService } from '../../../services/aux-timer-service/AuxTimerService.js';
import * as messageService from '../../../services/message-service/message.service.js';
import { parseUserTime } from 'ontime-utils';
export function toOntimeAction(action: OntimeAction) {
const actionType = action.action;
@@ -1,11 +1,11 @@
import { LogOrigin, OSCOutput, RuntimeStore } from 'ontime-types';
import { type OscPacketInput, toBuffer as oscPacketToBuffer } from 'osc-min';
import * as dgram from 'node:dgram';
import { LogOrigin, OSCOutput, RuntimeStore } from 'ontime-types';
import { type OscPacketInput, toBuffer as oscPacketToBuffer } from 'osc-min';
import { DeepReadonly } from 'ts-essentials';
import { logger } from '../../../classes/Logger.js';
import { parseTemplateNested, stringToOSCArgs } from '../automation.utils.js';
import { DeepReadonly } from 'ts-essentials';
const udpClient = dgram.createSocket('udp4');
@@ -1,4 +1,4 @@
import { DatabaseModel, CustomFields, CustomField } from 'ontime-types';
import { CustomField, CustomFields, DatabaseModel } from 'ontime-types';
import { checkRegex, customFieldLabelToKey } from 'ontime-utils';
import type { ErrorEmitter } from '../../utils/parserUtils.js';
@@ -1,14 +1,11 @@
import express from 'express';
import type { Request, Response } from 'express';
import { CustomField, CustomFields, ErrorResponse } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import express from 'express';
import type { Request, Response } from 'express';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { getProjectCustomFields } from '../rundown/rundown.dao.js';
import { createCustomField, editCustomField, deleteCustomField } from '../rundown/rundown.service.js';
import { createCustomField, deleteCustomField, editCustomField } from '../rundown/rundown.service.js';
import { validateCustomField, validateDeleteCustomField, validateEditCustomField } from './customFields.validation.js';
export const router = express.Router();
@@ -1,6 +1,6 @@
import { body, param } from 'express-validator';
import { checkRegex } from 'ontime-utils';
import { body, param } from 'express-validator';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
export const validateCustomField = [
@@ -3,21 +3,18 @@
import { SupportedEntry } from 'ontime-types';
import { demoDb } from '../../../models/demoProject.js';
import { parseDatabaseModel } from '../db.parser.js';
// mock data provider
beforeAll(() => {
vi.mock('../../classes/data-provider/DataProvider.js', () => {
return {
getDataProvider: vi.fn().mockImplementation(() => {
return {
setRundown: vi.fn().mockImplementation((newData) => newData),
setCustomFields: vi.fn().mockImplementation((newData) => newData),
};
}),
};
});
vi.mock('../../classes/data-provider/DataProvider.js', () => {
return {
getDataProvider: vi.fn().mockImplementation(() => {
return {
setRundown: vi.fn().mockImplementation((newData) => newData),
setCustomFields: vi.fn().mockImplementation((newData) => newData),
};
}),
};
});
describe('test parseDatabaseModel() with demo project (valid)', () => {
+3 -4
View File
@@ -1,3 +1,4 @@
import type { Request, Response } from 'express';
import {
DatabaseModel,
ErrorResponse,
@@ -7,17 +8,15 @@ import {
TimeFormat,
} from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import type { Request, Response } from 'express';
import sanitize from 'sanitize-filename';
import { getPartialProject } from '../../models/dataModel.js';
import * as projectService from '../../services/project-service/ProjectService.js';
import {
doesProjectExist,
handleImageUpload,
handleProjectUploaded,
} from '../../services/project-service/projectServiceUtils.js';
import * as projectService from '../../services/project-service/ProjectService.js';
import { getPartialProject } from '../../models/dataModel.js';
export async function patchPartialProjectFile(req: Request, res: Response<DatabaseModel | ErrorResponse>) {
try {
+4 -9
View File
@@ -1,18 +1,17 @@
import { DatabaseModel, LogOrigin } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { logger } from '../../classes/Logger.js';
import { portManager } from '../../classes/port-manager/PortManager.js';
import { parseAutomationSettings } from '../automation/automation.parser.js';
import { parseCustomFields } from '../custom-fields/customFields.parser.js';
import { parseProjectData } from '../project-data/projectData.parser.js';
import { parseRundowns } from '../rundown/rundown.parser.js';
import { parseSettings } from '../settings/settings.parser.js';
import { parseUrlPresets } from '../url-presets/urlPresets.parser.js';
import { parseViewSettings } from '../view-settings/viewSettings.parser.js';
import { parseCustomFields } from '../custom-fields/customFields.parser.js';
import * as v3 from './migration/db.migration.v3.js';
import * as v4 from './migration/db.migration.v4.js';
import { portManager } from '../../classes/port-manager/PortManager.js';
import { getErrorMessage } from 'ontime-utils';
type ParsingError = {
context: string;
@@ -32,7 +31,6 @@ export function parseDatabaseModel(
errors: ParsingError[];
migrated: boolean;
} {
let migrated = false;
let migratedData = jsonData;
const errors: ParsingError[] = [];
@@ -46,11 +44,9 @@ export function parseDatabaseModel(
logger.error(LogOrigin.Server, 'Failed to migrate the data');
errors.push({ context: 'v3 migration', message: getErrorMessage(error) });
migratedData = jsonData;
};
}
}
if (v4.shouldMigrateServerPort(migratedData)) {
try {
migrated = true;
@@ -62,7 +58,6 @@ export function parseDatabaseModel(
logger.error(LogOrigin.Server, 'Failed to migrate serverPort');
errors.push({ context: 'v4 migration', message: getErrorMessage(error) });
migratedData = jsonData;
}
}
+2 -2
View File
@@ -16,11 +16,11 @@ import {
} from './db.controller.js';
import { uploadProjectFile } from './db.middleware.js';
import {
validateNewProject,
validatePatchProject,
validateFilenameBody,
validateFilenameParam,
validateNewFilenameBody,
validateNewProject,
validatePatchProject,
validateQuickProject,
} from './db.validation.js';
@@ -1,5 +1,6 @@
import { body, param } from 'express-validator';
import sanitize from 'sanitize-filename';
import { ensureJsonExtension } from '../../utils/fileManagement.js';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
@@ -5,8 +5,8 @@ import {
EndAction,
EntryCustomFields,
NormalisedAutomation,
OntimeGroup,
OntimeEntry,
OntimeGroup,
ProjectData,
ProjectRundowns,
Rundown,
@@ -19,15 +19,15 @@ import {
Day,
} from 'ontime-types';
import {
customFieldLabelToKey,
checkRegex,
customFieldLabelToKey,
eventDef as eventModel,
isKnownTimerType,
validateEndAction,
eventDef as eventModel,
} from 'ontime-utils';
import { is } from '../../../utils/is.js';
import { getPartialProject } from '../../../models/dataModel.js';
import { is } from '../../../utils/is.js';
// the methodology of the migrations is to just change the necessary keys to match with v4
// and then let the normal project parser handle ensuring the the file is correct
@@ -1,4 +1,5 @@
import { DatabaseModel, Settings } from 'ontime-types';
import { is } from '../../../utils/is.js';
export function shouldMigrateServerPort(jsonData: object): boolean {
@@ -1,6 +1,7 @@
import {
AutomationSettings,
CustomFields,
type Day,
DatabaseModel,
EndAction,
OntimeView,
@@ -8,12 +9,13 @@ import {
Rundown,
Settings,
SupportedEntry,
TimeStrategy,
TimerLifeCycle,
TimerType,
TimeStrategy,
URLPreset,
ViewSettings,
} from 'ontime-types';
import * as v3 from './db.migration.v3.js';
import * as v4 from './db.migration.v4.js';
@@ -295,7 +297,7 @@ describe('v3 to v4', () => {
flag: false,
parent: null,
delay: 0,
dayOffset: 0,
dayOffset: 0 as Day,
gap: 0,
},
group0: {
@@ -340,7 +342,7 @@ describe('v3 to v4', () => {
parent: 'group0',
revision: -1,
delay: 0,
dayOffset: 0,
dayOffset: 0 as Day,
gap: 0,
},
event3: {
@@ -371,7 +373,7 @@ describe('v3 to v4', () => {
parent: 'group0',
revision: -1,
delay: 0,
dayOffset: 0,
dayOffset: 0 as Day,
gap: 0,
},
group1: {
@@ -1,10 +1,9 @@
import { CustomFields, OntimeEvent, OntimeGroup, SupportedEntry, TimerType } from 'ontime-types';
import { ImportMap, MILLIS_PER_MINUTE, millisToString } from 'ontime-utils';
import { parseExcel } from '../excel.parser.js';
import { dataFromExcelTemplate } from './mockData.js';
import { simpleTemplate } from '../__mocks__/simpleTemplate.js';
import { parseExcel } from '../excel.parser.js';
import { dataFromExcelTemplate } from './mockData.js';
describe('parseExcel()', () => {
it('parses the simple template file', () => {
@@ -1,8 +1,7 @@
import { CustomFields } from 'ontime-types';
import { defaultImportMap, ImportMap } from 'ontime-utils';
import { ImportMap, defaultImportMap } from 'ontime-utils';
import { demoDb } from '../../../models/demoProject.js';
import { getCustomFieldData, rundownToTabular } from '../excel.utils.js';
describe('getCustomFieldData()', () => {
@@ -2,7 +2,6 @@ import type { Request } from 'express';
import multer, { type FileFilterCallback } from 'multer';
import { storage } from '../../utils/upload.js';
import { EXCEL_MIME } from './excel.constants.js';
const filterExcel = (_req: Request, file: Express.Multer.File, cb: FileFilterCallback) => {
+9 -10
View File
@@ -1,14 +1,14 @@
import {
CustomFields,
Rundown,
EntryCustomFields,
OntimeEntry,
OntimeEvent,
OntimeGroup,
EntryCustomFields,
SupportedEntry,
isOntimeGroup,
TimerType,
OntimeMilestone,
OntimeEntry,
Rundown,
SupportedEntry,
TimerType,
isOntimeGroup,
isOntimeMilestone,
} from 'ontime-types';
import {
@@ -16,16 +16,15 @@ import {
defaultImportMap,
generateId,
isKnownTimerType,
validateTimerType,
validateEndAction,
makeString,
validateEndAction,
validateTimerType,
} from 'ontime-utils';
import { Prettify } from 'ts-essentials';
import { is } from '../../utils/is.js';
import { parseExcelDate } from '../../utils/time.js';
import { generateImportHandlers, getCustomFieldData, parseBooleanString, SheetMetadata } from './excel.utils.js';
import { SheetMetadata, generateImportHandlers, getCustomFieldData, parseBooleanString } from './excel.utils.js';
type MergedOntimeEntry = Prettify<
Omit<Omit<Omit<OntimeEvent, keyof OntimeGroup> & OntimeGroup, keyof OntimeMilestone> & OntimeMilestone, 'type'> & {
@@ -3,13 +3,11 @@ import type { Request, Response } from 'express';
import { CustomFields, ErrorResponse, Rundown, RundownSummary } from 'ontime-types';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { getProjectCustomFields } from '../rundown/rundown.dao.js';
import { uploadExcel } from './excel.middleware.js';
import { validateFileExists, validateImportMapOptions, validateRundownExport } from './excel.validation.js';
import { generateExcelFile, generateRundownPreview, readExcelFile } from './excel.service.js';
import { EXCEL_MIME } from './excel.constants.js';
import { uploadExcel } from './excel.middleware.js';
import { generateExcelFile, generateRundownPreview, readExcelFile } from './excel.service.js';
import { validateFileExists, validateImportMapOptions, validateRundownExport } from './excel.validation.js';
export const router = express.Router();
@@ -3,20 +3,18 @@
* Google Sheets
*/
import { existsSync } from 'fs';
import { extname } from 'path';
import { CustomFields, Rundown, RundownSummary } from 'ontime-types';
import { type ImportMap } from 'ontime-utils';
import { extname } from 'path';
import { existsSync } from 'fs';
import xlsx from 'xlsx';
import type { WorkBook } from 'xlsx';
import { deleteFile } from '../../utils/fileManagement.js';
import { parseRundown } from '../rundown/rundown.parser.js';
import { getProjectCustomFields, processRundown } from '../rundown/rundown.dao.js';
import { parseCustomFields } from '../custom-fields/customFields.parser.js';
import { getProjectCustomFields, processRundown } from '../rundown/rundown.dao.js';
import { parseRundown } from '../rundown/rundown.parser.js';
import { parseExcel } from './excel.parser.js';
import { rundownToTabular } from './excel.utils.js';
@@ -1,5 +1,5 @@
import { CustomFieldKey, CustomFields, MaybeNumber, OntimeEntry, Rundown } from 'ontime-types';
import { checkRegex, customFieldLabelToKey, ImportMap, millisToString } from 'ontime-utils';
import { ImportMap, checkRegex, customFieldLabelToKey, millisToString } from 'ontime-utils';
/**
* Receives an import map which contains custom field labels and a custom fields object
@@ -1,6 +1,5 @@
import { isImportMap } from 'ontime-utils';
import { body, param } from 'express-validator';
import { isImportMap } from 'ontime-utils';
import {
requestValidationFunction,
+5 -5
View File
@@ -1,18 +1,18 @@
import express from 'express';
import { router as assetsRouter } from './assets/assets.router.js';
import { router as automationsRouter } from './automation/automation.router.js';
import { router as urlPresetsRouter } from './url-presets/urlPresets.router.js';
import { router as customFieldsRouter } from './custom-fields/customFields.router.js';
import { router as dbRouter } from './db/db.router.js';
import { router as excelRouter } from './excel/excel.router.js';
import { router as projectRouter } from './project-data/projectData.router.js';
import { router as reportRouter } from './report/report.router.js';
import { router as rundownsRouter } from './rundown/rundown.router.js';
import { router as sessionRouter } from './session/session.router.js';
import { router as settingsRouter } from './settings/settings.router.js';
import { router as sheetsRouter } from './sheets/sheets.router.js';
import { router as excelRouter } from './excel/excel.router.js';
import { router as sessionRouter } from './session/session.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 reportRouter } from './report/report.router.js';
import { router as assetsRouter } from './assets/assets.router.js';
export const appRouter = express.Router();
@@ -1,10 +1,11 @@
import { join } from 'node:path';
import { ProjectData, RefetchKey } from 'ontime-types';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { join } from 'node:path';
import { deleteFile } from '../../utils/fileManagement.js';
import { publicDir } from '../../setup/index.js';
import { sendRefetch } from '../../adapters/WebsocketAdapter.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { publicDir } from '../../setup/index.js';
import { deleteFile } from '../../utils/fileManagement.js';
/**
* Gets the stored project data
@@ -1,7 +1,7 @@
import { DatabaseModel, ProjectData } from 'ontime-types';
import { ErrorEmitter } from '../../utils/parserUtils.js';
import { getPartialProject } from '../../models/dataModel.js';
import { ErrorEmitter } from '../../utils/parserUtils.js';
/**
* Parse event portion of an entry
@@ -3,11 +3,11 @@ import type { Request, Response } from 'express';
import type { ErrorResponse, ProjectData } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { projectSanitiser } from './projectData.validation.js';
import { uploadImageFile } from '../db/db.middleware.js';
import { postProjectLogo } from '../db/db.controller.js';
import * as projectDao from './projectData.dao.js';
import { removeUndefined } from '../../utils/parserUtils.js';
import { postProjectLogo } from '../db/db.controller.js';
import { uploadImageFile } from '../db/db.middleware.js';
import * as projectDao from './projectData.dao.js';
import { projectSanitiser } from './projectData.validation.js';
export const router = express.Router();
@@ -1,4 +1,5 @@
import { body } from 'express-validator';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
export const projectSanitiser = [
@@ -1,5 +1,6 @@
import express from 'express';
import type { Request, Response } from 'express';
import { paramsWithId } from '../validation-utils/validationFunction.js';
import * as report from './report.service.js';
@@ -1,7 +1,8 @@
import { OntimeReport, OntimeEventReport, TimerLifeCycle, RefetchKey } from 'ontime-types';
import { RuntimeState } from '../../stores/runtimeState.js';
import { OntimeEventReport, OntimeReport, RefetchKey, TimerLifeCycle } from 'ontime-types';
import { DeepReadonly } from 'ts-essentials';
import { sendRefetch } from '../../adapters/WebsocketAdapter.js';
import { RuntimeState } from '../../stores/runtimeState.js';
const report = new Map<string, OntimeEventReport>();
@@ -1,11 +1,11 @@
import {
SupportedEntry,
OntimeEvent,
OntimeDelay,
OntimeGroup,
Rundown,
CustomField,
OntimeDelay,
OntimeEvent,
OntimeGroup,
OntimeMilestone,
Rundown,
SupportedEntry,
} from 'ontime-types';
import { makeNewRundown } from '../../../models/dataModel.js';
@@ -1,23 +1,24 @@
import {
type Day,
CustomFields,
OntimeGroup,
OntimeDelay,
OntimeEvent,
OntimeGroup,
OntimeMilestone,
SupportedEntry,
TimeStrategy,
OntimeMilestone,
} from 'ontime-types';
import { dayInMs, MILLIS_PER_HOUR, MILLIS_PER_MINUTE } from 'ontime-utils';
import { MILLIS_PER_HOUR, MILLIS_PER_MINUTE, dayInMs } from 'ontime-utils';
import { demoDb } from '../../../models/demoProject.js';
import {
makeOntimeEvent,
makeRundown,
makeOntimeGroup,
makeOntimeDelay,
makeCustomField,
makeOntimeDelay,
makeOntimeEvent,
makeOntimeGroup,
makeOntimeMilestone,
makeRundown,
} from '../__mocks__/rundown.mocks.js';
import {
createTransaction,
customFieldMutation,
@@ -25,23 +26,20 @@ import {
rundownCache,
rundownMutation,
} from '../rundown.dao.js';
import { demoDb } from '../../../models/demoProject.js';
import { type ProcessedRundownMetadata } from '../rundown.parser.js';
const setRundownMock = vi.fn();
const setCustomFieldsMock = vi.fn();
beforeAll(() => {
vi.mock('../../../classes/data-provider/DataProvider.js', () => {
return {
getDataProvider: vi.fn().mockImplementation(() => {
return {
setRundown: setRundownMock,
setCustomFields: setCustomFieldsMock,
};
}),
};
});
vi.mock('../../../classes/data-provider/DataProvider.js', () => {
return {
getDataProvider: vi.fn().mockImplementation(() => {
return {
setRundown: setRundownMock,
setCustomFields: setCustomFieldsMock,
};
}),
};
});
afterAll(() => {
@@ -100,6 +98,7 @@ describe('processRundown()', () => {
Object.keys(result?.entries ?? {}).length,
'events',
);
expect(t2 - t1).lessThan(100);
});
it('generates metadata from given rundown', () => {
@@ -1345,7 +1344,7 @@ describe('rundownMutation.applyDelay()', () => {
'1': makeOntimeEvent({
id: '1',
gap: 0,
dayOffset: 0,
dayOffset: 0 as Day,
timeStart: 46800000, // 13:00:00
timeEnd: 50400000, // 14:00:00
duration: MILLIS_PER_HOUR,
@@ -1354,7 +1353,7 @@ describe('rundownMutation.applyDelay()', () => {
'2': makeOntimeEvent({
id: '2',
gap: 1 * MILLIS_PER_HOUR,
dayOffset: 0,
dayOffset: 0 as Day,
timeStart: 54000000, // 15:00:00
timeEnd: 57600000, // 16:00:00
duration: MILLIS_PER_HOUR,
@@ -1,9 +1,8 @@
import { SupportedEntry, OntimeEvent, OntimeGroup, Rundown, CustomFields } from 'ontime-types';
import { CustomFields, OntimeEvent, OntimeGroup, Rundown, SupportedEntry } from 'ontime-types';
import { makeNewRundown } from '../../../models/dataModel.js';
import { makeOntimeGroup, makeOntimeEvent, makeOntimeMilestone } from '../__mocks__/rundown.mocks.js';
import { parseRundowns, parseRundown, sanitiseCustomFields } from '../rundown.parser.js';
import { makeOntimeEvent, makeOntimeGroup, makeOntimeMilestone } from '../__mocks__/rundown.mocks.js';
import { parseRundown, parseRundowns, sanitiseCustomFields } from '../rundown.parser.js';
describe('parseRundowns()', () => {
it('returns a default project rundown if nothing is given', () => {
@@ -1,10 +1,9 @@
import { TimeStrategy, EndAction, TimerType, OntimeEvent } from 'ontime-types';
import { createEvent, MILLIS_PER_HOUR } from 'ontime-utils';
import { EndAction, OntimeEvent, TimeStrategy, TimerType } from 'ontime-types';
import { MILLIS_PER_HOUR, createEvent } from 'ontime-utils';
import { assertType } from 'vitest';
import { demoDb } from '../../../models/demoProject.js';
import { makeOntimeEvent, makeOntimeGroup, makeRundown } from '../__mocks__/rundown.mocks.js';
import {
calculateDayOffset,
deleteById,
@@ -13,7 +12,6 @@ import {
hasChanges,
makeDeepClone,
} from '../rundown.utils.js';
import { makeOntimeGroup, makeOntimeEvent, makeRundown } from '../__mocks__/rundown.mocks.js';
describe('test event validator', () => {
it('validates a good object', () => {
@@ -15,22 +15,22 @@ import {
CustomFieldKey,
CustomFields,
EntryId,
isOntimeGroup,
isOntimeEvent,
isPlayableEvent,
OntimeGroup,
InsertOptions,
OntimeDelay,
OntimeEntry,
OntimeEvent,
OntimeGroup,
PatchWithId,
Rundown,
InsertOptions,
isOntimeEvent,
isOntimeGroup,
isPlayableEvent,
} from 'ontime-types';
import { addToRundown, customFieldLabelToKey, getInsertAfterId, insertAtIndex, createGroup } from 'ontime-utils';
import { addToRundown, createGroup, customFieldLabelToKey, getInsertAfterId, insertAtIndex } from 'ontime-utils';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { consoleError } from '../../utils/console.js';
import { ProcessedRundownMetadata, makeRundownMetadata } from './rundown.parser.js';
import type { RundownMetadata } from './rundown.types.js';
import {
applyPatchToEntry,
@@ -40,7 +40,6 @@ import {
getUniqueId,
makeDeepClone,
} from './rundown.utils.js';
import { makeRundownMetadata, ProcessedRundownMetadata } from './rundown.parser.js';
/**
* The currently loaded rundown in cache
@@ -1,40 +1,39 @@
import {
DatabaseModel,
CustomFields,
ProjectRundowns,
Rundown,
OntimeEvent,
isOntimeEvent,
isOntimeDelay,
isOntimeGroup,
CustomFieldKey,
CustomFields,
DatabaseModel,
Day,
EntryId,
OntimeEntry,
PlayableEvent,
RundownEntries,
isPlayableEvent,
isOntimeMilestone,
OntimeMilestone,
OntimeEvent,
OntimeGroup,
Day,
OntimeMilestone,
PlayableEvent,
ProjectRundowns,
Rundown,
RundownEntries,
isOntimeDelay,
isOntimeEvent,
isOntimeGroup,
isOntimeMilestone,
isPlayableEvent,
} from 'ontime-types';
import {
isObjectEmpty,
generateId,
getLinkedTimes,
getTimeFrom,
isNewLatest,
createDelay,
createEvent,
createGroup,
createMilestone,
generateId,
getLinkedTimes,
getTimeFrom,
isNewLatest,
isObjectEmpty,
} from 'ontime-utils';
import { makeNewRundown } from '../../models/dataModel.js';
import type { ErrorEmitter } from '../../utils/parserUtils.js';
import { calculateDayOffset, cleanupCustomFields } from './rundown.utils.js';
import { RundownMetadata } from './rundown.types.js';
import { calculateDayOffset, cleanupCustomFields } from './rundown.utils.js';
/**
* Parse a rundowns object along with the project custom fields
@@ -1,14 +1,11 @@
import type { Request, Response } from 'express';
import express from 'express';
import { ErrorResponse, OntimeEntry, ProjectRundownsList, Rundown } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import type { Request, Response } from 'express';
import express from 'express';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { makeNewRundown } from '../../models/dataModel.js';
import { paramsWithId } from '../validation-utils/validationFunction.js';
import { getCurrentRundown } from './rundown.dao.js';
import {
addEntry,
@@ -20,23 +17,23 @@ import {
editEntry,
groupEntries,
initRundown,
loadRundown,
reorderEntry,
swapEvents,
loadRundown,
ungroupEntries,
} from './rundown.service.js';
import { duplicateRundown, normalisedToRundownArray } from './rundown.utils.js';
import {
rundownArrayOfIds,
clonePostValidator,
entryBatchPutValidator,
entryPostValidator,
rundownPostValidator,
entryPutValidator,
entryReorderValidator,
entrySwapValidator,
rundownArrayOfIds,
rundownPostValidator,
validateRundownMutation,
clonePostValidator,
} from './rundown.validation.js';
import { duplicateRundown, normalisedToRundownArray } from './rundown.utils.js';
export const router = express.Router();
@@ -4,26 +4,26 @@ import {
CustomFields,
EntryId,
EventPostPayload,
isOntimeGroup,
isOntimeDelay,
isOntimeEvent,
OntimeGroup,
InsertOptions,
LogOrigin,
OntimeEntry,
OntimeGroup,
PatchWithId,
ProjectRundowns,
RefetchKey,
Rundown,
LogOrigin,
ProjectRundowns,
InsertOptions,
isOntimeDelay,
isOntimeEvent,
isOntimeGroup,
} from 'ontime-types';
import { customFieldLabelToKey, getInsertAfterId, resolveInsertParent } from 'ontime-utils';
import { updateRundownData } from '../../stores/runtimeState.js';
import { runtimeService } from '../../services/runtime-service/runtime.service.js';
import { sendRefetch } from '../../adapters/WebsocketAdapter.js';
import { setLastLoadedRundown } from '../../services/app-state-service/AppStateService.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { logger } from '../../classes/Logger.js';
import { setLastLoadedRundown } from '../../services/app-state-service/AppStateService.js';
import { runtimeService } from '../../services/runtime-service/runtime.service.js';
import { updateRundownData } from '../../stores/runtimeState.js';
import {
createTransaction,
customFieldMutation,
@@ -34,7 +34,6 @@ import {
} from './rundown.dao.js';
import type { RundownMetadata } from './rundown.types.js';
import { generateEvent, hasChanges } from './rundown.utils.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
/**
* creates a new entry with given data
@@ -2,30 +2,30 @@ import {
CustomFields,
EntryCustomFields,
EntryId,
isOntimeGroup,
isOntimeDelay,
isOntimeEvent,
isOntimeMilestone,
OntimeBaseEvent,
OntimeGroup,
OntimeDelay,
OntimeEntry,
OntimeEvent,
OntimeGroup,
OntimeMilestone,
ProjectRundown,
ProjectRundowns,
Rundown,
SupportedEntry,
TimeStrategy,
ProjectRundown,
ProjectRundowns,
isOntimeDelay,
isOntimeEvent,
isOntimeGroup,
isOntimeMilestone,
} from 'ontime-types';
import {
dayInMs,
generateId,
getCueCandidate,
createDelay,
createEvent,
createGroup,
createMilestone,
dayInMs,
generateId,
getCueCandidate,
makeString,
validateEndAction,
validateTimerType,
@@ -1,4 +1,4 @@
import type { Request, Response, NextFunction } from 'express';
import type { NextFunction, Request, Response } from 'express';
import { body, param } from 'express-validator';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
@@ -1,9 +1,10 @@
import express from 'express';
import type { Request, Response } from 'express';
import { getErrorMessage } from 'ontime-utils';
import type { ErrorResponse, GetInfo, GetUrl, SessionStats } from 'ontime-types';
import { validateGenerateUrl } from './session.validation.js';
import { getErrorMessage } from 'ontime-utils';
import * as sessionService from './session.service.js';
import { validateGenerateUrl } from './session.validation.js';
export const router = express.Router();
@@ -1,17 +1,17 @@
import { GetInfo, LinkOptions, OntimeView, SessionStats } from 'ontime-types';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { publicDir } from '../../setup/index.js';
import { socket } from '../../adapters/WebsocketAdapter.js';
import { getLastRequest } from '../../api-integration/integration.controller.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { portManager } from '../../classes/port-manager/PortManager.js';
import { password, routerPrefix } from '../../externals.js';
import { ONTIME_VERSION } from '../../ONTIME_VERSION.js';
import { getCurrentProject } from '../../services/project-service/ProjectService.js';
import { runtimeService } from '../../services/runtime-service/runtime.service.js';
import { publicDir } from '../../setup/index.js';
import { hashPassword } from '../../utils/hash.js';
import { getNetworkInterfaces } from '../../utils/network.js';
import { getTimezoneLabel } from '../../utils/time.js';
import { password, routerPrefix } from '../../externals.js';
import { hashPassword } from '../../utils/hash.js';
import { ONTIME_VERSION } from '../../ONTIME_VERSION.js';
import { portManager } from '../../classes/port-manager/PortManager.js';
const startedAt = new Date();
@@ -1,4 +1,5 @@
import { body } from 'express-validator';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
export const validateGenerateUrl = [
@@ -4,7 +4,7 @@ import { parseSettings } from '../settings.parser.js';
describe('parseSettings()', () => {
it('throws if settings object does not exist', () => {
expect(() => parseSettings({})).toThrow();
expect(() => parseSettings({})).toThrow('ERROR: unable to parse settings, missing or incorrect version');
});
it('returns an a base model as long as we have the app version', () => {
@@ -1,29 +1,24 @@
import express from "express";
import { matchedData } from "express-validator";
import type { Request, Response } from "express";
import { deepEqual } from "fast-equals";
import express from 'express';
import type { Request, Response } from 'express';
import { matchedData } from 'express-validator';
import { deepEqual } from 'fast-equals';
import { ErrorResponse, PortInfo, RefetchKey, Settings } from 'ontime-types';
import { getErrorMessage, obfuscate } from 'ontime-utils';
import { ErrorResponse, PortInfo, RefetchKey, Settings } from "ontime-types";
import { getErrorMessage, obfuscate } from "ontime-utils";
import {
validateSettings,
validateWelcomeDialog,
validateServerPort,
} from "./settings.validation.js";
import { getDataProvider } from "../../classes/data-provider/DataProvider.js";
import * as appState from "../../services/app-state-service/AppStateService.js";
import { sendRefetch } from "../../adapters/WebsocketAdapter.js";
import { portManager } from "../../classes/port-manager/PortManager.js";
import { sendRefetch } from '../../adapters/WebsocketAdapter.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { portManager } from '../../classes/port-manager/PortManager.js';
import * as appState from '../../services/app-state-service/AppStateService.js';
import { validateSettings, validateWelcomeDialog, validateServerPort } from './settings.validation.js';
export const 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);
res.status(200).json({ show });
});
router.get("/", (_req: Request, res: Response<Settings>) => {
router.get('/', (_req: Request, res: Response<Settings>) => {
const settings = getDataProvider().getSettings();
const obfuscatedSettings = { ...settings };
if (settings.editorKey) {
@@ -37,30 +32,26 @@ router.get("/", (_req: Request, res: Response<Settings>) => {
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();
router.post('/', validateSettings, async (req: Request, res: Response<Settings | ErrorResponse>) => {
try {
const data = matchedData<Settings>(req);
const settings = getDataProvider().getSettings();
data.version = settings.version;
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 });
if (!deepEqual(data, settings)) {
await getDataProvider().setSettings(data);
sendRefetch(RefetchKey.Settings);
}
},
);
router.get("/serverport", (_req: Request, res: Response<PortInfo | ErrorResponse>) => {
res.status(200).json(data);
} catch (error) {
const message = getErrorMessage(error);
res.status(400).json({ message });
}
});
router.get('/serverport', (_req: Request, res: Response<PortInfo | ErrorResponse>) => {
try {
const { port, pendingRestart } = portManager.getPort();
res.status(200).json({ port, pendingRestart });
@@ -70,19 +61,15 @@ router.get("/serverport", (_req: Request, res: Response<PortInfo | ErrorResponse
}
});
router.post(
"/serverport",
validateServerPort,
async (req: Request, res: Response<PortInfo | ErrorResponse>) => {
try {
const { serverPort } = matchedData<{ serverPort: number }>(req);
portManager.changePort(serverPort);
const { port, pendingRestart } = portManager.getPort();
router.post('/serverport', validateServerPort, async (req: Request, res: Response<PortInfo | ErrorResponse>) => {
try {
const { serverPort } = matchedData<{ serverPort: number }>(req);
portManager.changePort(serverPort);
const { port, pendingRestart } = portManager.getPort();
res.status(200).json({ port, pendingRestart });
} catch (error) {
const message = getErrorMessage(error);
res.status(400).json({ message });
}
},
);
res.status(200).json({ port, pendingRestart });
} catch (error) {
const message = getErrorMessage(error);
res.status(400).json({ message });
}
});
@@ -1,4 +1,5 @@
import { body } from 'express-validator';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
/**
@@ -1,7 +1,7 @@
import { EndAction, OntimeEvent, SupportedEntry, TimeStrategy, TimerType } from 'ontime-types';
import { type Day, EndAction, OntimeEvent, SupportedEntry, TimeStrategy, TimerType } from 'ontime-types';
import { millisToString } from 'ontime-utils';
import { getA1Notation, cellRequestFromEvent } from '../sheets.utils.js';
import { cellRequestFromEvent, getA1Notation } from '../sheets.utils.js';
describe('getA1Notation()', () => {
test('A1', () => {
@@ -38,7 +38,7 @@ describe('cellRequestFromEvent()', () => {
colour: 'red',
delay: 0,
gap: 0,
dayOffset: 0,
dayOffset: 0 as Day,
parent: null,
revision: 0,
id: '1358',
@@ -89,7 +89,7 @@ describe('cellRequestFromEvent()', () => {
revision: 0,
delay: 0,
gap: 0,
dayOffset: 0,
dayOffset: 0 as Day,
id: '1358',
timeWarning: 0,
timeDanger: 0,
@@ -140,7 +140,7 @@ describe('cellRequestFromEvent()', () => {
revision: 0,
delay: 0,
gap: 0,
dayOffset: 0,
dayOffset: 0 as Day,
id: '1358',
timeWarning: 0,
timeDanger: 0,
@@ -187,7 +187,7 @@ describe('cellRequestFromEvent()', () => {
colour: 'red',
delay: 0,
gap: 0,
dayOffset: 0,
dayOffset: 0 as Day,
parent: null,
revision: 0,
id: '1358',
@@ -226,7 +226,7 @@ describe('cellRequestFromEvent()', () => {
revision: 0,
delay: 0,
gap: 0,
dayOffset: 0,
dayOffset: 0 as Day,
id: '1358',
timeWarning: 0,
timeDanger: 0,
@@ -264,7 +264,7 @@ describe('cellRequestFromEvent()', () => {
revision: 0,
delay: 0,
gap: 0,
dayOffset: 0,
dayOffset: 0 as Day,
id: '1358',
timeWarning: 0,
timeDanger: 0,
@@ -3,22 +3,21 @@
* Google Sheets
*/
import { readFileSync } from 'fs';
import { Request, Response } from 'express';
import type { AuthenticationStatus, CustomFields, ErrorResponse, Rundown, RundownSummary } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { Request, Response } from 'express';
import { readFileSync } from 'fs';
import { deleteFile } from '../../utils/fileManagement.js';
import {
revoke,
download,
getWorksheetOptions,
handleClientSecret,
handleInitialConnection,
hasAuth,
download,
revoke,
upload,
getWorksheetOptions,
} from './sheets.service.js';
export async function requestConnection(
@@ -4,35 +4,32 @@
* @link https://developers.google.com/identity/protocols/oauth2/limited-input-device
*/
import { sheets, type sheets_v4 } from '@googleapis/sheets';
import { Credentials, OAuth2Client } from 'google-auth-library';
import {
AuthenticationStatus,
CustomFields,
DatabaseModel,
EntryId,
isOntimeEvent,
isOntimeMilestone,
LogOrigin,
MaybeString,
OntimeGroup,
Rundown,
RundownSummary,
SupportedEntry,
isOntimeEvent,
isOntimeMilestone,
} from 'ontime-types';
import { ImportMap, getErrorMessage } from 'ontime-utils';
import { sheets, type sheets_v4 } from '@googleapis/sheets';
import { Credentials, OAuth2Client } from 'google-auth-library';
import { logger } from '../../classes/Logger.js';
import { parseRundowns } from '../rundown/rundown.parser.js';
import { getCurrentRundown, getProjectCustomFields, processRundown } from '../rundown/rundown.dao.js';
import { parseExcel } from '../excel/excel.parser.js';
import { parseCustomFields } from '../custom-fields/customFields.parser.js';
import { consoleSubdued } from '../../utils/console.js';
import { cellRequestFromEvent, type ClientSecret, getA1Notation, isClientSecret } from './sheets.utils.js';
import { parseCustomFields } from '../custom-fields/customFields.parser.js';
import { parseExcel } from '../excel/excel.parser.js';
import { getCurrentRundown, getProjectCustomFields, processRundown } from '../rundown/rundown.dao.js';
import { parseRundowns } from '../rundown/rundown.parser.js';
import { catchCommonImportXlsxError } from './googleApi.utils.js';
import { type ClientSecret, cellRequestFromEvent, getA1Notation, isClientSecret } from './sheets.utils.js';
const sheetScope = 'https://www.googleapis.com/auth/spreadsheets';
const codesUrl = 'https://oauth2.googleapis.com/device/code';
@@ -1,15 +1,14 @@
import type { sheets_v4 } from '@googleapis/sheets';
import {
OntimeEntry,
OntimeEntryCommonKeys,
RGBColour,
isOntimeDelay,
OntimeEntryCommonKeys,
isOntimeGroup,
isOntimeMilestone,
} from 'ontime-types';
import { cssOrHexToColour, isLightColour, millisToString, mixColours } from 'ontime-utils';
import type { sheets_v4 } from '@googleapis/sheets';
import { is } from '../../utils/is.js';
export type ClientSecret = {
@@ -104,6 +103,7 @@ export function cellRequestFromEvent(
const thisCol = e[1].col;
const diff = thisCol - prevCol;
if (diff > 1) {
// TODO: we need to clarify this logic
const fillArr = new Array<(typeof rowData)[0]>(1).fill(['blank', { row: e[1].row, col: prevCol + 1 }]);
rowData.splice(index, 0, ...fillArr);
}
@@ -1,7 +1,7 @@
import { NextFunction, Request, Response } from 'express';
import { body, param, validationResult } from 'express-validator';
import { isImportMap } from 'ontime-utils';
import { body, param, validationResult } from 'express-validator';
import { NextFunction, Request, Response } from 'express';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
export const validateRequestConnection = [
@@ -1,10 +1,11 @@
import express from 'express';
import type { Request, Response } from 'express';
import { RefetchKey, type ErrorResponse, type URLPreset } from 'ontime-types';
import { type ErrorResponse, RefetchKey, type URLPreset } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { sendRefetch } from '../../adapters/WebsocketAdapter.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { validateNewPreset, validatePresetParam, validateUpdatePreset } from './urlPresets.validation.js';
import { sendRefetch } from '../../adapters/WebsocketAdapter.js';
export const router = express.Router();
@@ -1,6 +1,5 @@
import { OntimeView } from 'ontime-types';
import { body, param } from 'express-validator';
import { OntimeView } from 'ontime-types';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
@@ -1,7 +1,8 @@
import type { Request, Response, NextFunction } from 'express';
import type { NextFunction, Request, Response } from 'express';
import { param, validationResult } from 'express-validator';
export const paramsWithId = [param('id').isString().trim().notEmpty(), requestValidationFunction];
type RequestWithFile = Request & { file?: unknown };
// #region operations on project rundowns =========================
@@ -27,8 +28,9 @@ export function requestValidationFunctionWithFile(req: Request, res: Response, n
res.status(422).json({ errors: errors.array() });
return;
}
const request = req as RequestWithFile;
// check that the file exists
if (!req.file) {
if (!request.file) {
res.status(422).json({ errors: 'File not found' });
return;
}
@@ -1,11 +1,11 @@
import express from 'express';
import type { Request, Response } from 'express';
import { RefetchKey, type ErrorResponse, type ViewSettings } from 'ontime-types';
import { type ErrorResponse, RefetchKey, type ViewSettings } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { validateViewSettings } from './viewSettings.validation.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { sendRefetch } from '../../adapters/WebsocketAdapter.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { validateViewSettings } from './viewSettings.validation.js';
export const router = express.Router();
@@ -1,4 +1,5 @@
import { body } from 'express-validator';
import { requestValidationFunction } from '../validation-utils/validationFunction.js';
/**
@@ -1,140 +1,141 @@
import { CustomFields, EntryCustomFields, OntimeDelay, OntimeEvent } from 'ontime-types';
import { isValidChangeProperty } from '../integration.utils.js';
describe('isValidChangeProperty()', () => {
test('allows changing a valid event property with valid value', () => {
const testEvent = {
id: 'test',
duration: 111,
} as OntimeEvent;
expect(isValidChangeProperty(testEvent, 'duration', 123, {})).toBeTruthy();
});
test('forbids changing a valid event property with undefined value', () => {
const testEvent = {
id: 'test',
duration: 111,
} as OntimeEvent;
expect(isValidChangeProperty(testEvent, 'duration', undefined, {})).toBeFalsy();
});
test('forbids changing a non-existing property', () => {
const testEvent = {
id: 'test',
duration: 111,
} as OntimeEvent;
expect(isValidChangeProperty(testEvent, 'missing', 123, {})).toBeFalsy();
});
test('forbids changing customValues in event without custom fields', () => {
const testDelay = {
id: 'test',
duration: 111,
} as OntimeDelay;
const testCustomFields = {
test: {
type: 'text',
colour: '#ffffff',
label: 'Test Field',
},
} satisfies CustomFields;
expect(isValidChangeProperty(testDelay, 'custom:test', 123, testCustomFields)).toBeFalsy();
});
test('forbids changing a non-existing custom value', () => {
const testEvent = {
id: 'test',
duration: 111,
custom: {
field: 'test',
} as EntryCustomFields,
} as OntimeEvent;
expect(isValidChangeProperty(testEvent, 'custom:test', 123, {})).toBeFalsy();
});
test('allows changing an existing custom value', () => {
const testEvent = {
id: 'test',
duration: 111,
custom: {
test: 'test',
} as EntryCustomFields,
} as OntimeEvent;
const testCustomFields = {
test: {
type: 'text',
colour: '#ffffff',
label: 'Test Field',
},
} satisfies CustomFields;
expect(isValidChangeProperty(testEvent, 'custom:test', 123, testCustomFields)).toBeTruthy();
});
test('forbids changing a custom field with a missing value', () => {
const testEvent = {
id: 'test',
duration: 111,
custom: {
test: 'test',
} as EntryCustomFields,
} as OntimeEvent;
const testCustomFields = {
test: {
type: 'text',
colour: '#ffffff',
label: 'Test Field',
},
} satisfies CustomFields;
expect(isValidChangeProperty(testEvent, 'custom:', 123, testCustomFields)).toBeFalsy();
});
test('forbids references to prototype properties in custom object', () => {
const testEvent = {
id: 'test',
duration: 111,
custom: {
test: 'test',
} as EntryCustomFields,
} as OntimeEvent;
const testCustomFields = {
test: {
type: 'text',
colour: '#ffffff',
label: 'Test Field',
},
} satisfies CustomFields;
expect(isValidChangeProperty(testEvent, 'custom:toString', 123, testCustomFields)).toBeFalsy();
});
test('forbids references to prototype properties in object', () => {
const testEvent = {
id: 'test',
duration: 111,
custom: {
test: 'test',
} as EntryCustomFields,
} as OntimeEvent;
const testCustomFields = {
test: {
type: 'text',
colour: '#ffffff',
label: 'Test Field',
},
} satisfies CustomFields;
expect(isValidChangeProperty(testEvent, 'toString', 123, testCustomFields)).toBeFalsy();
});
});
import { CustomFields, EntryCustomFields, OntimeDelay, OntimeEvent } from 'ontime-types';
import { isValidChangeProperty } from '../integration.utils.js';
describe('isValidChangeProperty()', () => {
test('allows changing a valid event property with valid value', () => {
const testEvent = {
id: 'test',
duration: 111,
} as OntimeEvent;
expect(isValidChangeProperty(testEvent, 'duration', 123, {})).toBeTruthy();
});
test('forbids changing a valid event property with undefined value', () => {
const testEvent = {
id: 'test',
duration: 111,
} as OntimeEvent;
expect(isValidChangeProperty(testEvent, 'duration', undefined, {})).toBeFalsy();
});
test('forbids changing a non-existing property', () => {
const testEvent = {
id: 'test',
duration: 111,
} as OntimeEvent;
expect(isValidChangeProperty(testEvent, 'missing', 123, {})).toBeFalsy();
});
test('forbids changing customValues in event without custom fields', () => {
const testDelay = {
id: 'test',
duration: 111,
} as OntimeDelay;
const testCustomFields = {
test: {
type: 'text',
colour: '#ffffff',
label: 'Test Field',
},
} satisfies CustomFields;
expect(isValidChangeProperty(testDelay, 'custom:test', 123, testCustomFields)).toBeFalsy();
});
test('forbids changing a non-existing custom value', () => {
const testEvent = {
id: 'test',
duration: 111,
custom: {
field: 'test',
} as EntryCustomFields,
} as OntimeEvent;
expect(isValidChangeProperty(testEvent, 'custom:test', 123, {})).toBeFalsy();
});
test('allows changing an existing custom value', () => {
const testEvent = {
id: 'test',
duration: 111,
custom: {
test: 'test',
} as EntryCustomFields,
} as OntimeEvent;
const testCustomFields = {
test: {
type: 'text',
colour: '#ffffff',
label: 'Test Field',
},
} satisfies CustomFields;
expect(isValidChangeProperty(testEvent, 'custom:test', 123, testCustomFields)).toBeTruthy();
});
test('forbids changing a custom field with a missing value', () => {
const testEvent = {
id: 'test',
duration: 111,
custom: {
test: 'test',
} as EntryCustomFields,
} as OntimeEvent;
const testCustomFields = {
test: {
type: 'text',
colour: '#ffffff',
label: 'Test Field',
},
} satisfies CustomFields;
expect(isValidChangeProperty(testEvent, 'custom:', 123, testCustomFields)).toBeFalsy();
});
test('forbids references to prototype properties in custom object', () => {
const testEvent = {
id: 'test',
duration: 111,
custom: {
test: 'test',
} as EntryCustomFields,
} as OntimeEvent;
const testCustomFields = {
test: {
type: 'text',
colour: '#ffffff',
label: 'Test Field',
},
} satisfies CustomFields;
expect(isValidChangeProperty(testEvent, 'custom:toString', 123, testCustomFields)).toBeFalsy();
});
test('forbids references to prototype properties in object', () => {
const testEvent = {
id: 'test',
duration: 111,
custom: {
test: 'test',
} as EntryCustomFields,
} as OntimeEvent;
const testCustomFields = {
test: {
type: 'text',
colour: '#ffffff',
label: 'Test Field',
},
} satisfies CustomFields;
expect(isValidChangeProperty(testEvent, 'toString', 123, testCustomFields)).toBeFalsy();
});
});
@@ -8,9 +8,12 @@ import {
SimplePlayback,
} from 'ontime-types';
import { MILLIS_PER_HOUR } from 'ontime-utils';
import { DeepPartial } from 'ts-essentials';
import { socket } from '../adapters/WebsocketAdapter.js';
import { getCurrentRundown, getProjectCustomFields } from '../api-data/rundown/rundown.dao.js';
import { editEntry } from '../api-data/rundown/rundown.service.js';
import { willCauseRegeneration } from '../api-data/rundown/rundown.utils.js';
import { ONTIME_VERSION } from '../ONTIME_VERSION.js';
import { auxTimerService } from '../services/aux-timer-service/AuxTimerService.js';
import * as messageService from '../services/message-service/message.service.js';
@@ -18,12 +21,8 @@ import { validateMessage, validateTimerMessage } from '../services/message-servi
import { runtimeService } from '../services/runtime-service/runtime.service.js';
import { eventStore } from '../stores/EventStore.js';
import * as assert from '../utils/assert.js';
import { parseProperty, isValidChangeProperty } from './integration.utils.js';
import { socket } from '../adapters/WebsocketAdapter.js';
import { coerceEnum } from '../utils/coerceType.js';
import { editEntry } from '../api-data/rundown/rundown.service.js';
import { willCauseRegeneration } from '../api-data/rundown/rundown.utils.js';
import { getCurrentRundown, getProjectCustomFields } from '../api-data/rundown/rundown.dao.js';
import { isValidChangeProperty, parseProperty } from './integration.utils.js';
let lastRequest: Date | null = null;
@@ -5,16 +5,14 @@
*
*/
import { ErrorResponse, LogOrigin } from 'ontime-types';
import express, { type Request, type Response } from 'express';
import { logger } from '../classes/Logger.js';
import { integrationPayloadFromPath } from '../adapters/utils/parse.js';
import { dispatchFromAdapter } from './integration.controller.js';
import { ErrorResponse, LogOrigin } from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { integrationPayloadFromPath } from '../adapters/utils/parse.js';
import { logger } from '../classes/Logger.js';
import { isEmptyObject } from '../utils/parserUtils.js';
import { dispatchFromAdapter } from './integration.controller.js';
export const integrationRouter = express.Router();
+31 -37
View File
@@ -1,53 +1,47 @@
import { LogOrigin, runtimeStorePlaceholder, SimpleDirection, SimplePlayback } from 'ontime-types';
import http, { type Server } from 'http';
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 cors from 'cors';
import express from 'express';
import { LogOrigin, SimpleDirection, SimplePlayback, runtimeStorePlaceholder } from 'ontime-types';
import serverTiming from 'server-timing';
// 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 { makeLoginRouter, makeAuthenticateMiddleware } from './middleware/authenticate.js';
import { oscServer } from './adapters/OscAdapter.js';
// Import adapters
import { socket } from './adapters/WebsocketAdapter.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, flushPendingWrites } from './classes/data-provider/DataProvider.js';
import { flushPendingWrites, getDataProvider } from './classes/data-provider/DataProvider.js';
// Services
import { logger } from './classes/Logger.js';
import { populateDemo } from './setup/loadDemo.js';
import { populateTranslation } from './setup/loadTranslations.js';
import { populateStyles } from './setup/loadStyles.js';
import { eventStore } from './stores/EventStore.js';
import { runtimeService } from './services/runtime-service/runtime.service.js';
import { portManager } from './classes/port-manager/PortManager.js';
import { updateRouterPrefix } from './externals.js';
import { makeAuthenticateMiddleware, makeLoginRouter } from './middleware/authenticate.js';
// Import middleware configuration
import { bodyParser } from './middleware/bodyParser.js';
import { compressedStatic } from './middleware/staticGZip.js';
import { ONTIME_VERSION } from './ONTIME_VERSION.js';
import { getShowWelcomeDialog } from './services/app-state-service/AppStateService.js';
import * as messageService from './services/message-service/message.service.js';
import { initialiseProject } from './services/project-service/ProjectService.js';
import { restoreService } from './services/restore-service/restore.service.js';
import type { RestorePoint } from './services/restore-service/restore.type.js';
import * as messageService from './services/message-service/message.service.js';
import { getState } from './stores/runtimeState.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 { runtimeService } from './services/runtime-service/runtime.service.js';
import { timerConfig } from './setup/config.js';
import { environment, isProduction } from './setup/environment.js';
// import utils
import { publicDir, srcDir } from './setup/index.js';
import { populateDemo } from './setup/loadDemo.js';
import { populateStyles } from './setup/loadStyles.js';
import { populateTranslation } from './setup/loadTranslations.js';
import { eventStore } from './stores/EventStore.js';
import { getState } from './stores/runtimeState.js';
import { consoleError, consoleHighlight, consoleSuccess } from './utils/console.js';
import { generateCrashReport } from './utils/generateCrashReport.js';
import { getNetworkInterfaces } from './utils/network.js';
import { portManager } from './classes/port-manager/PortManager.js';
import { clearUploadfolder } from './utils/upload.js';
console.log('\n');
consoleHighlight(`Starting Ontime version ${ONTIME_VERSION}`);
+2 -2
View File
@@ -2,9 +2,9 @@ import { Log, LogLevel, MessageTag } from 'ontime-types';
import { generateId, millisToString } from 'ontime-utils';
import { socket } from '../adapters/WebsocketAdapter.js';
import { consoleSubdued, consoleError } from '../utils/console.js';
import { timeNow } from '../utils/time.js';
import { isProduction } from '../setup/environment.js';
import { consoleError, consoleSubdued } from '../utils/console.js';
import { timeNow } from '../utils/time.js';
class Logger {
private queue: Log[];
@@ -1,22 +1,20 @@
import {
ProjectData,
ViewSettings,
DatabaseModel,
Settings,
CustomFields,
URLPreset,
AutomationSettings,
Rundown,
ProjectRundowns,
} from 'ontime-types';
import type { Low } from 'lowdb';
import { JSONFilePreset } from 'lowdb/node';
import {
AutomationSettings,
CustomFields,
DatabaseModel,
ProjectData,
ProjectRundowns,
Rundown,
Settings,
URLPreset,
ViewSettings,
} from 'ontime-types';
import { isPath } from '../../utils/fileManagement.js';
import { shouldCrashDev } from '../../utils/development.js';
import { isTest } from '../../setup/environment.js';
import { shouldCrashDev } from '../../utils/development.js';
import { isPath } from '../../utils/fileManagement.js';
import { safeMerge } from './DataProvider.utils.js';
type ReadonlyPromise<T> = Promise<Readonly<T>>;
@@ -1,8 +1,7 @@
import { AutomationSettings, CustomField, DatabaseModel, Settings, TimerLifeCycle, URLPreset } from 'ontime-types';
import { makeNewProject } from '../../../models/dataModel.js';
import { makeOntimeEvent, makeRundown } from '../../../api-data/rundown/__mocks__/rundown.mocks.js';
import { makeNewProject } from '../../../models/dataModel.js';
import { safeMerge } from '../DataProvider.utils.js';
const baseDb = makeNewProject('demo');
@@ -1,11 +1,13 @@
import { Server } from "http";
import { config } from "../../setup/config.js";
import { envPort, isDocker, isOntimeCloud } from "../../setup/environment.js";
import * as appState from "../../services/app-state-service/AppStateService.js";
import { logger } from "../Logger.js";
import { LogOrigin, MaybeNumber } from "ontime-types";
import { isAddressInfo, isPortInUseError } from "./PortManager.utils.js";
import { shouldCrashDev } from "../../utils/development.js";
import { Server } from 'http';
import { LogOrigin, MaybeNumber } from 'ontime-types';
import * as appState from '../../services/app-state-service/AppStateService.js';
import { config } from '../../setup/config.js';
import { envPort, isDocker, isOntimeCloud } from '../../setup/environment.js';
import { shouldCrashDev } from '../../utils/development.js';
import { logger } from '../Logger.js';
import { isAddressInfo, isPortInUseError } from './PortManager.utils.js';
class PortManager {
private static port: number;
@@ -27,7 +29,7 @@ class PortManager {
* @returns {void}
*/
public changePort(newPort: number): void {
if (isDocker) throw new Error("Can not change port when running inside docker");
if (isDocker) throw new Error('Can not change port when running inside docker');
if (PortManager.port === newPort) return;
PortManager.newPort = newPort;
PortManager.pendingRestart = true;
@@ -36,7 +38,7 @@ class PortManager {
public migratePortFromProjectFile(port: number) {
shouldCrashDev(
PortManager.port !== undefined,
"this function should not be called after `PortManager.port` has been initialized",
'this function should not be called after `PortManager.port` has been initialized',
);
appState.setServerPort(port);
}
@@ -61,8 +63,7 @@ class PortManager {
if (isOntimeCloud) {
PortManager.port = await this.forceCloudPort(server);
} else {
PortManager.port =
this.parsePort(envPort) || (await appState.getServerPort()) || config.defaultServerPort;
PortManager.port = this.parsePort(envPort) || (await appState.getServerPort()) || config.defaultServerPort;
PortManager.port = await this.tryServerPort(server);
}
await appState.setServerPort(PortManager.port);
@@ -70,8 +71,8 @@ class PortManager {
}
private parsePort(port: string | undefined) {
if (typeof port !== "string") return null;
if (port === "") return null;
if (typeof port !== 'string') return null;
if (port === '') return null;
const maybePort = Number(port);
if (isNaN(maybePort)) return null;
return maybePort;
@@ -79,7 +80,7 @@ class PortManager {
private async tryServerPort(server: Server): Promise<number> {
return new Promise((resolve, reject) => {
server.once("error", (error) => {
server.once('error', (error) => {
// we should only move ports if we are in a desktop environment
if (isDocker) {
reject(error);
@@ -93,10 +94,10 @@ class PortManager {
// if we get an address in use error, we will try to open the server in an ephemeral port
// port 0 will assign an ephemeral port
server.listen(0, "0.0.0.0", () => {
server.listen(0, '0.0.0.0', () => {
const address = server.address();
if (!isAddressInfo(address)) {
reject(new Error("Unknown port type, unable to proceed"));
reject(new Error('Unknown port type, unable to proceed'));
return;
}
logger.error(
@@ -109,10 +110,10 @@ class PortManager {
});
});
server.listen(PortManager.port, "0.0.0.0", () => {
server.listen(PortManager.port, '0.0.0.0', () => {
const address = server.address();
if (!isAddressInfo(address)) {
reject(new Error("Unknown port type, unable to proceed"));
reject(new Error('Unknown port type, unable to proceed'));
return;
}
resolve(address.port);
@@ -122,13 +123,13 @@ class PortManager {
private forceCloudPort(server: Server): Promise<number> {
return new Promise((resolve, reject) => {
server.once("error", (error) => {
server.once('error', (error) => {
reject(error);
});
server.listen(config.defaultServerPort, "0.0.0.0", () => {
server.listen(config.defaultServerPort, '0.0.0.0', () => {
const address = server.address();
if (!isAddressInfo(address)) {
reject(new Error("Unknown port type, unable to proceed"));
reject(new Error('Unknown port type, unable to proceed'));
return;
}
resolve(address.port);
+2 -1
View File
@@ -2,7 +2,8 @@
This folder contains resources which will be distributed as part of Ontime's infrastructure.
Folders added here would be available at
``` bash
```bash
http://<ip-address>:<port>/external/<folder-name>
```
+111 -109
View File
@@ -1,114 +1,116 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<!-- For detailed explanations and examples, refer to the README.md file in this directory -->
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>ontime demo</title>
<link href="./styles.css" rel="stylesheet" />
</head>
<!-- For detailed explanations and examples, refer to the README.md file in this directory -->
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>ontime demo</title>
<link href="./styles.css" rel="stylesheet" />
</head>
<body>
<header class="title-card">
<div class="logo-title">
<img src="https://www.getontime.no/images/icons/ontime-logo.png" alt="Ontime logo"
onerror="this.style.display='none'" />
<h1 class="title">Ontime demo</h1>
</div>
<div>
<span>Last message received at</span>
<span id="clock">-</span>
</div>
<nav>
<a href="https://docs.getontime.no/api/data/runtime-data" target="_blank">Help? See docs</a>
<div>See <a href="README.md">README.md</a> details.</div>
</nav>
</header>
<body>
<header class="title-card">
<div class="logo-title">
<img
src="https://www.getontime.no/images/icons/ontime-logo.png"
alt="Ontime logo"
onerror="this.style.display = 'none'"
/>
<h1 class="title">Ontime demo</h1>
</div>
<div>
<span>Last message received at</span>
<span id="clock">-</span>
</div>
<nav>
<a href="https://docs.getontime.no/api/data/runtime-data" target="_blank">Help? See docs</a>
<div>See <a href="README.md">README.md</a> details.</div>
</nav>
</header>
<main class="container">
<section class="column">
<details class="card" open>
<summary class="title">Timer</summary>
<figure>
<figcaption class="description">Current timer values</figcaption>
<code id="timer">-</code>
</figure>
</details>
<details class="card" open>
<summary class="title">Rundown</summary>
<figure>
<figcaption class="description">Progress of the current rundown</figcaption>
<code id="rundown">-</code>
</figure>
</details>
<details class="card" open>
<summary class="title">Offset</summary>
<figure>
<figcaption class="description">Runtime offset and timings for upcoming targets</figcaption>
<code id="offset">-</code>
</figure>
</details>
</section>
<section class="column">
<details class="card" open>
<summary class="title">Event now</summary>
<figure>
<figcaption class="description">Currently loaded event</figcaption>
<code id="eventNow">-</code>
</figure>
</details>
<details class="card" open>
<summary class="title">Event next</summary>
<figure>
<figcaption class="description">Next scheduled event</figcaption>
<code id="eventNext">-</code>
</figure>
</details>
</section>
<section class="column">
<details class="card" open>
<summary class="title">Group now</summary>
<figure>
<figcaption class="description">Currently active group</figcaption>
<code id="groupNow">-</code>
</figure>
</details>
<details class="card" open>
<summary class="title">Event flag</summary>
<figure>
<figcaption class="description">Currently targeted flag</figcaption>
<code id="eventFlag">-</code>
</figure>
</details>
</section>
<section class="column">
<details class="card" open>
<summary class="title">Message</summary>
<figure>
<figcaption class="description">Messaging feature</figcaption>
<code id="message">-</code>
</figure>
</details>
<details class="card" open>
<summary class="title">Aux timers</summary>
<figure>
<figcaption class="description">Auxiliary Timer 1</figcaption>
<code id="auxtimer1">-</code>
</figure>
<figure>
<figcaption class="description">Auxiliary Timer 2</figcaption>
<code id="auxtimer2">-</code>
</figure>
<figure>
<figcaption class="description">Auxiliary Timer 3</figcaption>
<code id="auxtimer3">-</code>
</figure>
</details>
</section>
</main>
<script src="./app.js" type="text/javascript"></script>
<main class="container">
<section class="column">
<details class="card" open>
<summary class="title">Timer</summary>
<figure>
<figcaption class="description">Current timer values</figcaption>
<code id="timer">-</code>
</figure>
</details>
<details class="card" open>
<summary class="title">Rundown</summary>
<figure>
<figcaption class="description">Progress of the current rundown</figcaption>
<code id="rundown">-</code>
</figure>
</details>
<details class="card" open>
<summary class="title">Offset</summary>
<figure>
<figcaption class="description">Runtime offset and timings for upcoming targets</figcaption>
<code id="offset">-</code>
</figure>
</details>
</section>
<section class="column">
<details class="card" open>
<summary class="title">Event now</summary>
<figure>
<figcaption class="description">Currently loaded event</figcaption>
<code id="eventNow">-</code>
</figure>
</details>
<details class="card" open>
<summary class="title">Event next</summary>
<figure>
<figcaption class="description">Next scheduled event</figcaption>
<code id="eventNext">-</code>
</figure>
</details>
</section>
<section class="column">
<details class="card" open>
<summary class="title">Group now</summary>
<figure>
<figcaption class="description">Currently active group</figcaption>
<code id="groupNow">-</code>
</figure>
</details>
<details class="card" open>
<summary class="title">Event flag</summary>
<figure>
<figcaption class="description">Currently targeted flag</figcaption>
<code id="eventFlag">-</code>
</figure>
</details>
</section>
<section class="column">
<details class="card" open>
<summary class="title">Message</summary>
<figure>
<figcaption class="description">Messaging feature</figcaption>
<code id="message">-</code>
</figure>
</details>
<details class="card" open>
<summary class="title">Aux timers</summary>
<figure>
<figcaption class="description">Auxiliary Timer 1</figcaption>
<code id="auxtimer1">-</code>
</figure>
<figure>
<figcaption class="description">Auxiliary Timer 2</figcaption>
<code id="auxtimer2">-</code>
</figure>
<figure>
<figcaption class="description">Auxiliary Timer 3</figcaption>
<code id="auxtimer3">-</code>
</figure>
</details>
</section>
</main>
</html>
<script src="./app.js" type="text/javascript"></script>
</body>
</html>
+1 -1
View File
@@ -4,8 +4,8 @@
import { readFileSync, writeFileSync } from 'node:fs';
import { srcFiles } from './setup/index.js';
import { isOntimeCloud } from './setup/environment.js';
import { srcFiles } from './setup/index.js';
// =================================================
export const password = process.env.SESSION_PASSWORD;
+2 -2
View File
@@ -10,8 +10,8 @@
<style>
body,
html {
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana,
sans-serif;
font-family:
'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
background: #101010;
text-align: center;
box-sizing: border-box;
+2 -2
View File
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
import { consoleHighlight, consoleError } from './utils/console.js';
import { initAssets, startIntegrations, startServer } from './app.js';
/* eslint-disable no-console */
import { consoleError, consoleHighlight } from './utils/console.js';
async function startOntime() {
try {
@@ -1,5 +1,5 @@
import { dayInMs, MILLIS_PER_HOUR, MILLIS_PER_MINUTE, millisToString } from 'ontime-utils';
import { Duration, Instant, TimeOfDay } from 'ontime-types';
import { dayInMs, MILLIS_PER_HOUR, MILLIS_PER_MINUTE, millisToString } from 'ontime-utils';
import { timeNow } from '../../../utils/time.js';
import * as timeCore from '../timeCore.js';
+1 -1
View File
@@ -1,5 +1,5 @@
import { Day, Duration, Instant, TimeOfDay } from 'ontime-types';
import { dayInMs, MILLIS_PER_MINUTE } from 'ontime-utils';
import { MILLIS_PER_MINUTE, dayInMs } from 'ontime-utils';
/** Returns the current instant */
export function now(): Instant {
+5 -5
View File
@@ -1,12 +1,12 @@
import express, { type Request, type Response, type NextFunction } from 'express';
import type { IncomingMessage } from 'node:http';
import type { WebSocket } from 'ws';
import { parse as parseCookie } from 'cookie';
import express, { type NextFunction, type Request, type Response } from 'express';
import type { WebSocket } from 'ws';
import { hashPassword } from '../utils/hash.js';
import { hasPassword, hashedPassword } from '../api-data/session/session.service.js';
import { srcFiles } from '../setup/index.js';
import { hashedPassword, hasPassword } from '../api-data/session/session.service.js';
import { hashPassword } from '../utils/hash.js';
import { noopMiddleware } from './noop.js';
/**
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Request, Response, NextFunction } from 'express';
import type { NextFunction, Request, Response } from 'express';
export function noopMiddleware(_req: Request, _res: Response, next: NextFunction) {
next();
+2 -1
View File
@@ -1,6 +1,7 @@
import expressStaticGzip from 'express-static-gzip';
import { extname } from 'node:path';
import expressStaticGzip from 'express-static-gzip';
import { srcDir } from '../setup/index.js';
// serve static - react, in dev/test mode we fetch the React app from module
+2 -1
View File
@@ -1,7 +1,8 @@
import { DatabaseModel, Rundown } from 'ontime-types';
import { ONTIME_VERSION } from '../ONTIME_VERSION.js';
import { generateId } from 'ontime-utils';
import { ONTIME_VERSION } from '../ONTIME_VERSION.js';
const defaultRundown: Rundown = {
id: 'default',
title: 'Default',
@@ -1,24 +1,20 @@
import { dayInMs, MILLIS_PER_HOUR, MILLIS_PER_MINUTE } from 'ontime-utils';
import { PlayableEvent } from 'ontime-types';
import { MILLIS_PER_HOUR, MILLIS_PER_MINUTE, dayInMs } from 'ontime-utils';
import { initRundown } from '../../api-data/rundown/rundown.service.js';
import { processRundown, rundownCache } from '../../api-data/rundown/rundown.dao.js';
import { makeOntimeEvent, makeRundown } from '../../api-data/rundown/__mocks__/rundown.mocks.js';
import { processRundown, rundownCache } from '../../api-data/rundown/rundown.dao.js';
import { initRundown } from '../../api-data/rundown/rundown.service.js';
import { loadRoll } from '../rollUtils.js';
beforeAll(() => {
vi.mock('../../classes/data-provider/DataProvider.js', () => {
return {
getDataProvider: vi.fn().mockImplementation(() => {
return {
setCustomFields: vi.fn().mockImplementation((newData) => newData),
setRundown: vi.fn().mockImplementation((newData) => newData),
};
}),
};
});
vi.mock('../../classes/data-provider/DataProvider.js', () => {
return {
getDataProvider: vi.fn().mockImplementation(() => {
return {
setCustomFields: vi.fn().mockImplementation((newData) => newData),
setRundown: vi.fn().mockImplementation((newData) => newData),
};
}),
};
});
const mockEvent = {
@@ -727,7 +723,7 @@ describe('loadRoll() test that roll behaviour multi day event edge cases', () =>
id: '2',
timeStart: 11 * MILLIS_PER_HOUR,
timeEnd: 2 * MILLIS_PER_HOUR,
duration: 14 * MILLIS_PER_HOUR
duration: 14 * MILLIS_PER_HOUR,
},
},
}),
@@ -782,7 +778,7 @@ describe('loadRoll() test that roll behaviour multi day event edge cases', () =>
});
describe('loadRoll() should not roll skipped events', () => {
test('', async () => {
test('load roll', async () => {
vi.useFakeTimers();
await initRundown(
makeRundown({
@@ -1,6 +1,7 @@
import { dayInMs, MILLIS_PER_HOUR, MILLIS_PER_MINUTE, MILLIS_PER_SECOND, millisToString } from 'ontime-utils';
import { EndAction, Playback, TimeOfDay, TimeStrategy, TimerPhase, TimerType } from 'ontime-types';
import { MILLIS_PER_HOUR, MILLIS_PER_MINUTE, MILLIS_PER_SECOND, dayInMs, millisToString } from 'ontime-utils';
import type { RuntimeState } from '../../stores/runtimeState.js';
import {
findDayOffset,
getCurrent,
@@ -11,7 +12,8 @@ import {
normaliseEndTime,
skippedOutOfEvent,
} from '../timerUtils.js';
import type { RuntimeState } from '../../stores/runtimeState.js';
const asTimeOfDay = (value: number): RuntimeState['clock'] => value as RuntimeState['clock'];
describe('getExpectedFinish()', () => {
it('is null if we havent started', () => {
@@ -574,7 +576,7 @@ describe('skippedOutOfEvent()', () => {
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
state.clock += testSkipLimit;
state.clock = asTimeOfDay(state.clock + testSkipLimit);
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
});
@@ -594,7 +596,7 @@ describe('skippedOutOfEvent()', () => {
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
state.clock += testSkipLimit;
state.clock = asTimeOfDay(state.clock + testSkipLimit);
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
});
@@ -613,7 +615,7 @@ describe('skippedOutOfEvent()', () => {
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
state.clock = testSkipLimit - 2;
state.clock = asTimeOfDay(testSkipLimit - 2);
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
});
@@ -632,7 +634,7 @@ describe('skippedOutOfEvent()', () => {
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
state.clock -= testSkipLimit;
state.clock = asTimeOfDay(state.clock - testSkipLimit);
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
});
@@ -652,7 +654,7 @@ describe('skippedOutOfEvent()', () => {
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
state.clock += testSkipLimit + 1;
state.clock = asTimeOfDay(state.clock + testSkipLimit + 1);
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(true);
});
@@ -672,7 +674,7 @@ describe('skippedOutOfEvent()', () => {
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
state.clock -= testSkipLimit + 1;
state.clock = asTimeOfDay(state.clock - testSkipLimit - 1);
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(true);
});
@@ -691,7 +693,7 @@ describe('skippedOutOfEvent()', () => {
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
state.clock = testSkipLimit - 2;
state.clock = asTimeOfDay(testSkipLimit - 2);
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(true);
});
@@ -710,7 +712,7 @@ describe('skippedOutOfEvent()', () => {
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false);
state.clock -= testSkipLimit + 1;
state.clock = asTimeOfDay(state.clock - testSkipLimit - 1);
expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(true);
});
@@ -1253,7 +1255,7 @@ describe('getTimerPhase()', () => {
expect(phase).toBe(TimerPhase.Warning);
});
it('it default if the timer is playing and there is none of the above', () => {
it('is default if the timer is playing and there is none of the above', () => {
const state = {
timer: {
addedTime: 0,
@@ -1,10 +1,10 @@
import { Low } from 'lowdb';
import { JSONFile } from 'lowdb/node';
import { publicFiles } from '../../setup/index.js';
import { isTest } from '../../setup/environment.js';
import { isPath } from '../../utils/fileManagement.js';
import { publicFiles } from '../../setup/index.js';
import { shouldCrashDev } from '../../utils/development.js';
import { isPath } from '../../utils/fileManagement.js';
interface AppState {
projectName?: string;
@@ -1,8 +1,8 @@
import { RuntimeStore, SimpleDirection, SimplePlayback } from 'ontime-types';
import { SimpleTimer } from '../../classes/simple-timer/SimpleTimer.js';
import { eventStore } from '../../stores/EventStore.js';
import { timerConfig } from '../../setup/config.js';
import { eventStore } from '../../stores/EventStore.js';
type AuxTimerStateUpdate = Partial<Pick<RuntimeStore, 'auxtimer1' | 'auxtimer2' | 'auxtimer3'>>;
type EmitFn = (state: AuxTimerStateUpdate) => void;
@@ -2,8 +2,8 @@ import { MessageState, runtimeStorePlaceholder } from 'ontime-types';
import { withoutUndefinedValues } from 'ontime-utils';
import { DeepPartial } from 'ts-essentials';
import type { PublishFn, StoreGetter } from '../../stores/EventStore.js';
import { throttle } from '../../utils/throttle.js';
import type { StoreGetter, PublishFn } from '../../stores/EventStore.js';
/**
* Create a throttled version of the set function
@@ -1,11 +1,22 @@
import { DatabaseModel, LogOrigin, ProjectFileListResponse } from 'ontime-types';
import { getErrorMessage, getFirstRundown } from 'ontime-utils';
import { copyFile } from 'fs/promises';
import { join } from 'path';
import { DatabaseModel, LogOrigin, ProjectFileListResponse } from 'ontime-types';
import { getErrorMessage, getFirstRundown } from 'ontime-utils';
import { parseCustomFields } from '../../api-data/custom-fields/customFields.parser.js';
import { parseDatabaseModel } from '../../api-data/db/db.parser.js';
import { getCurrentRundown } from '../../api-data/rundown/rundown.dao.js';
import { parseRundowns } from '../../api-data/rundown/rundown.parser.js';
import { initRundown } from '../../api-data/rundown/rundown.service.js';
import { getDataProvider, initPersistence } from '../../classes/data-provider/DataProvider.js';
import { safeMerge } from '../../classes/data-provider/DataProvider.utils.js';
import { logger } from '../../classes/Logger.js';
import { makeNewProject } from '../../models/dataModel.js';
import { demoDb } from '../../models/demoProject.js';
import { config } from '../../setup/config.js';
import { publicDir } from '../../setup/index.js';
import { populateOntimeLogo } from '../../setup/loadOntimeLogo.js';
import {
appendToName,
deleteFile,
@@ -16,20 +27,8 @@ import {
getFileNameFromPath,
removeFileExtension,
} from '../../utils/fileManagement.js';
import { parseRundowns } from '../../api-data/rundown/rundown.parser.js';
import { demoDb } from '../../models/demoProject.js';
import { config } from '../../setup/config.js';
import { getDataProvider, initPersistence } from '../../classes/data-provider/DataProvider.js';
import { initRundown } from '../../api-data/rundown/rundown.service.js';
import { parseDatabaseModel } from '../../api-data/db/db.parser.js';
import { parseCustomFields } from '../../api-data/custom-fields/customFields.parser.js';
import { makeNewProject } from '../../models/dataModel.js';
import { safeMerge } from '../../classes/data-provider/DataProvider.utils.js';
import { getCurrentRundown } from '../../api-data/rundown/rundown.dao.js';
import { getLastLoaded, isLastLoadedProject, setLastLoaded } from '../app-state-service/AppStateService.js';
import { runtimeService } from '../runtime-service/runtime.service.js';
import {
doesProjectExist,
getPathToProject,
@@ -37,7 +36,6 @@ import {
moveCorruptFile,
parseJsonFile,
} from './projectServiceUtils.js';
import { populateOntimeLogo } from '../../setup/loadOntimeLogo.js';
type ProjectState =
| {
@@ -1,7 +1,6 @@
import { Mock } from 'vitest';
import { isLastLoadedProject } from '../../app-state-service/AppStateService.js';
import { deleteProjectFile, duplicateProjectFile, renameProjectFile } from '../ProjectService.js';
import { doesProjectExist } from '../projectServiceUtils.js';
@@ -1,9 +1,9 @@
import { DatabaseModel, MaybeString, ProjectFile } from 'ontime-types';
import { existsSync } from 'fs';
import { readFile, stat } from 'fs/promises';
import { extname, join } from 'path';
import { DatabaseModel, MaybeString, ProjectFile } from 'ontime-types';
import { publicDir } from '../../setup/index.js';
import { dockerSafeRename, getFilesFromFolder, removeFileExtension } from '../../utils/fileManagement.js';
@@ -1,8 +1,11 @@
import { Playback } from 'ontime-types';
import type { Instant } from 'ontime-types';
import { isRestorePoint } from '../restore.parser.js';
import { RestorePoint } from '../restore.type.js';
const asInstant = (value: number): Instant => value as Instant;
describe('isRestorePoint()', () => {
it('validates a well defined object', () => {
let restorePoint: RestorePoint = {
@@ -12,7 +15,7 @@ describe('isRestorePoint()', () => {
addedTime: 2,
pausedAt: 3,
firstStart: 1,
startEpoch: 1,
startEpoch: asInstant(1),
currentDay: 0,
};
expect(isRestorePoint(restorePoint)).toBe(true);
@@ -24,7 +27,7 @@ describe('isRestorePoint()', () => {
addedTime: 0,
pausedAt: null,
firstStart: 1,
startEpoch: 1,
startEpoch: asInstant(1),
currentDay: 2,
};
expect(isRestorePoint(restorePoint)).toBe(true);
@@ -1,10 +1,12 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Playback } from 'ontime-types';
import type { Instant } from 'ontime-types';
import { vi } from 'vitest';
import { RestorePoint } from '../restore.type.js';
import { restoreService } from '../restore.service.js';
import { RestorePoint } from '../restore.type.js';
const asInstant = (value: number): Instant => value as Instant;
describe('restoreService', () => {
describe('load()', () => {
@@ -16,7 +18,7 @@ describe('restoreService', () => {
addedTime: 5678,
pausedAt: 9087,
firstStart: 1234,
startEpoch: 1234,
startEpoch: asInstant(1234),
currentDay: 0,
};
@@ -35,7 +37,7 @@ describe('restoreService', () => {
addedTime: 0,
pausedAt: null,
firstStart: 1234,
startEpoch: 1234,
startEpoch: asInstant(1234),
currentDay: null,
};
@@ -83,7 +85,7 @@ describe('restoreService', () => {
addedTime: 1234,
pausedAt: 1234,
firstStart: 1234,
startEpoch: 1234,
startEpoch: asInstant(1234),
currentDay: 0,
};
@@ -101,7 +103,7 @@ describe('restoreService', () => {
addedTime: 2345,
pausedAt: 2345,
firstStart: 2345,
startEpoch: 2345,
startEpoch: asInstant(2345),
currentDay: 0,
};
@@ -112,7 +114,7 @@ describe('restoreService', () => {
addedTime: 3456,
pausedAt: 3456,
firstStart: 3456,
startEpoch: 3456,
startEpoch: asInstant(3456),
currentDay: 1,
};
@@ -134,7 +136,7 @@ describe('restoreService', () => {
addedTime: 5678,
pausedAt: 5678,
firstStart: 5678,
startEpoch: 5678,
startEpoch: asInstant(5678),
currentDay: 0,
};

Some files were not shown because too many files have changed in this diff Show More