mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-20 14:39:06 +00:00
feat: Several project files user folder (Steps 3-5) (#664)
* feat: steps 3-5 for several project files user folder * feat: common types, better listing function * feat: error response type
This commit is contained in:
@@ -1,16 +1,25 @@
|
|||||||
import { LogOrigin } from 'ontime-types';
|
import { LogOrigin } from 'ontime-types';
|
||||||
import type { Alias, DatabaseModel, GetInfo, HttpSettings, ProjectData } from 'ontime-types';
|
import type {
|
||||||
|
Alias,
|
||||||
|
DatabaseModel,
|
||||||
|
GetInfo,
|
||||||
|
HttpSettings,
|
||||||
|
ProjectData,
|
||||||
|
ProjectFileList,
|
||||||
|
ErrorResponse,
|
||||||
|
} from 'ontime-types';
|
||||||
|
|
||||||
import { RequestHandler, Request, Response } from 'express';
|
import { RequestHandler, Request, Response } from 'express';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { networkInterfaces } from 'os';
|
import { networkInterfaces } from 'os';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
import { fileHandler } from '../utils/parser.js';
|
import { fileHandler } from '../utils/parser.js';
|
||||||
import { DataProvider } from '../classes/data-provider/DataProvider.js';
|
import { DataProvider } from '../classes/data-provider/DataProvider.js';
|
||||||
import { failEmptyObjects, failIsNotArray } from '../utils/routerUtils.js';
|
import { failEmptyObjects, failIsNotArray } from '../utils/routerUtils.js';
|
||||||
import { PlaybackService } from '../services/PlaybackService.js';
|
import { PlaybackService } from '../services/PlaybackService.js';
|
||||||
import { eventStore } from '../stores/EventStore.js';
|
import { eventStore } from '../stores/EventStore.js';
|
||||||
import { isDocker, resolveDbPath, resolveStylesPath } from '../setup.js';
|
import { getAppDataPath, isDocker, resolveDbPath, resolveStylesPath } from '../setup.js';
|
||||||
import { oscIntegration } from '../services/integration-service/OscIntegration.js';
|
import { oscIntegration } from '../services/integration-service/OscIntegration.js';
|
||||||
import { httpIntegration } from '../services/integration-service/HttpIntegration.js';
|
import { httpIntegration } from '../services/integration-service/HttpIntegration.js';
|
||||||
import { logger } from '../classes/Logger.js';
|
import { logger } from '../classes/Logger.js';
|
||||||
@@ -19,6 +28,7 @@ import { deepmerge } from 'ontime-utils';
|
|||||||
import { runtimeCacheStore } from '../stores/cachingStore.js';
|
import { runtimeCacheStore } from '../stores/cachingStore.js';
|
||||||
import { delayedRundownCacheKey } from '../services/rundown-service/delayedRundown.utils.js';
|
import { delayedRundownCacheKey } from '../services/rundown-service/delayedRundown.utils.js';
|
||||||
import { integrationService } from '../services/integration-service/IntegrationService.js';
|
import { integrationService } from '../services/integration-service/IntegrationService.js';
|
||||||
|
import { getFileListFromFolder } from '../utils/getFileListFromFolder.js';
|
||||||
|
|
||||||
// Create controller for GET request to '/ontime/poll'
|
// Create controller for GET request to '/ontime/poll'
|
||||||
// Returns data for current state
|
// Returns data for current state
|
||||||
@@ -455,3 +465,44 @@ export const postNew: RequestHandler = async (req, res) => {
|
|||||||
res.status(400).send({ message: error.toString() });
|
res.status(400).send({ message: error.toString() });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves and lists all project files from the uploads directory.
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
export const listProjects: RequestHandler = (_, res: Response<ProjectFileList | ErrorResponse>) => {
|
||||||
|
try {
|
||||||
|
const uploadsFolderPath = join(getAppDataPath(), 'uploads');
|
||||||
|
const fileList = getFileListFromFolder(uploadsFolderPath);
|
||||||
|
res.status(200).send(fileList);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).send({ message: error.toString() });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receives a `filename` from the request body and loads the project file from the uploads directory.
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
export const loadProject: RequestHandler = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const filename = req.body.filename;
|
||||||
|
|
||||||
|
const uploadsFolderPath = join(getAppDataPath(), 'uploads');
|
||||||
|
const filePath = join(uploadsFolderPath, filename);
|
||||||
|
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
return res.status(404).send({ message: 'File not found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
await parseAndApply(filePath, req, res, {});
|
||||||
|
|
||||||
|
res.status(200).send({
|
||||||
|
message: `Loaded project ${filename}`,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).send({ message: error.toString() });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -152,3 +152,17 @@ export const validatePatchProjectFile = [
|
|||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Validates the filename for loading a project file.
|
||||||
|
*/
|
||||||
|
export const validateLoadProjectFile = [
|
||||||
|
body('filename').exists().withMessage('Filename is required').isString().withMessage('Filename must be a string'),
|
||||||
|
(req, res, next) => {
|
||||||
|
const errors = validationResult(req);
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
return res.status(422).json({ errors: errors.array() });
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import {
|
|||||||
postViewSettings,
|
postViewSettings,
|
||||||
previewExcel,
|
previewExcel,
|
||||||
postHTTP,
|
postHTTP,
|
||||||
|
listProjects,
|
||||||
|
loadProject,
|
||||||
} from '../controllers/ontimeController.js';
|
} from '../controllers/ontimeController.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -32,6 +34,7 @@ import {
|
|||||||
viewValidator,
|
viewValidator,
|
||||||
validateHTTP,
|
validateHTTP,
|
||||||
validateOscSubscription,
|
validateOscSubscription,
|
||||||
|
validateLoadProjectFile,
|
||||||
} from '../controllers/ontimeController.validate.js';
|
} from '../controllers/ontimeController.validate.js';
|
||||||
import { projectSanitiser } from '../controllers/projectController.validate.js';
|
import { projectSanitiser } from '../controllers/projectController.validate.js';
|
||||||
|
|
||||||
@@ -96,3 +99,9 @@ router.post('/http', validateHTTP, postHTTP);
|
|||||||
|
|
||||||
// create route between controller and '/ontime/new' endpoint
|
// create route between controller and '/ontime/new' endpoint
|
||||||
router.post('/new', projectSanitiser, postNew);
|
router.post('/new', projectSanitiser, postNew);
|
||||||
|
|
||||||
|
// create route between controller and '/ontime/projects' endpoint
|
||||||
|
router.get('/projects', listProjects);
|
||||||
|
|
||||||
|
// create route between controller and '/ontime/load-project' endpoint
|
||||||
|
router.post('/load-project', validateLoadProjectFile, loadProject);
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { ProjectFile } from 'ontime-types';
|
||||||
|
|
||||||
|
import { readdirSync, statSync } from 'fs';
|
||||||
|
|
||||||
|
export const getFileListFromFolder = (folderPath: string): Array<ProjectFile> => {
|
||||||
|
const files = readdirSync(folderPath);
|
||||||
|
return files.map((file) => {
|
||||||
|
const filePath = `${folderPath}/${file}`;
|
||||||
|
const stats = statSync(filePath);
|
||||||
|
|
||||||
|
return {
|
||||||
|
filename: file,
|
||||||
|
createdAt: stats.birthtime.toISOString(),
|
||||||
|
updatedAt: stats.mtime.toISOString(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -12,3 +12,15 @@ export interface GetInfo {
|
|||||||
osc: OSCSettings;
|
osc: OSCSettings;
|
||||||
cssOverride: string;
|
cssOverride: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ProjectFile = {
|
||||||
|
filename: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ProjectFileList = Array<ProjectFile>;
|
||||||
|
|
||||||
|
export type ErrorResponse = {
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ export type { OSCSettings, OscSubscription, OscSubscriptionOptions } from './def
|
|||||||
export type { HttpSettings, HttpSubscription, HttpSubscriptionOptions } from './definitions/core/HttpSettings.type.js';
|
export type { HttpSettings, HttpSubscription, HttpSubscriptionOptions } from './definitions/core/HttpSettings.type.js';
|
||||||
|
|
||||||
// SERVER RESPONSES
|
// SERVER RESPONSES
|
||||||
export type { NetworkInterface, GetInfo } from './api/ontime-controller/BackendResponse.type.js';
|
export type { NetworkInterface, GetInfo, ProjectFileList, ProjectFile, ErrorResponse } from './api/ontime-controller/BackendResponse.type.js';
|
||||||
export type { GetRundownCached } from './api/rundown-controller/BackendResponse.type.js';
|
export type { GetRundownCached } from './api/rundown-controller/BackendResponse.type.js';
|
||||||
|
|
||||||
// SERVER RUNTIME
|
// SERVER RUNTIME
|
||||||
|
|||||||
Reference in New Issue
Block a user