refactor: download project file

This commit is contained in:
Carlos Valente
2024-05-10 20:14:56 +02:00
committed by Carlos Valente
parent 6f80de58ca
commit 00f04fb78c
8 changed files with 263 additions and 81 deletions
+4 -4
View File
@@ -11,15 +11,15 @@ const dbPath = `${apiEntryUrl}/db`;
/**
* HTTP request to the current DB
*/
async function getDb(): Promise<AxiosResponse<DatabaseModel>> {
return axios.get(`${dbPath}/download`);
async function getDb(fileName?: string): Promise<AxiosResponse<DatabaseModel>> {
return axios.post(`${dbPath}/download/`, { fileName });
}
/**
* Request download of the current project file
* @param fileName
*/
export async function downloadProject(fileName: string = 'ontime-project') {
export async function downloadProject(fileName: string) {
try {
const { data, name } = await fileDownload(fileName);
@@ -152,7 +152,7 @@ export async function getInfo(): Promise<GetInfo> {
* @returns
*/
async function fileDownload(fileName: string): Promise<{ data: DatabaseModel; name: string }> {
const response = await getDb();
const response = await getDb(fileName);
const headerLine = response.headers['Content-Disposition'];
@@ -169,7 +169,7 @@ function ActionMenu({
<MenuItem onClick={handleRename}>Rename</MenuItem>
<MenuItem onClick={handleDuplicate}>Duplicate</MenuItem>
<MenuItem onClick={handleDownload}>Download</MenuItem>
{current && <MenuItem onClick={handleExportCSV}>Export CSV Rundown</MenuItem>}
<MenuItem onClick={handleExportCSV}>Export CSV Rundown</MenuItem>
<MenuItem isDisabled={current} onClick={handleDelete}>
Delete
</MenuItem>
+1 -2
View File
@@ -10,9 +10,8 @@
"cors": "^2.8.5",
"dotenv": "^16.0.1",
"express": "^4.18.2",
"express-session": "^1.17.3",
"express-static-gzip": "^2.1.7",
"express-validator": "^6.14.2",
"express-validator": "^7.0.1",
"fast-equals": "^5.0.1",
"google-auth-library": "^9.4.2",
"got": "^14.0.0",
+28 -5
View File
@@ -6,17 +6,19 @@ import {
ProjectData,
ProjectFileListResponse,
} from 'ontime-types';
import { getErrorMessage } from 'ontime-utils';
import { join } from 'path';
import { existsSync } from 'fs';
import type { Request, Response } from 'express';
import { failEmptyObjects } from '../../utils/routerUtils.js';
import { resolveDbPath, resolveProjectsDirectory } from '../../setup/index.js';
import { resolveDbDirectory, resolveProjectsDirectory } from '../../setup/index.js';
import * as projectService from '../../services/project-service/ProjectService.js';
import { ensureJsonExtension } from '../../utils/fileManagement.js';
import { generateUniqueFileName } from '../../utils/generateUniqueFilename.js';
import { appStateService } from '../../services/app-state-service/AppStateService.js';
import { getErrorMessage } from 'ontime-utils';
export async function patchPartialProjectFile(req: Request, res: Response<DatabaseModel | ErrorResponse>) {
// all fields are optional in validation
@@ -76,9 +78,30 @@ export async function createProjectFile(req: Request, res: Response<{ filename:
}
}
export async function projectDownload(_req: Request, res: Response) {
const fileTitle = projectService.getProjectTitle();
res.download(resolveDbPath, `${fileTitle}.json`, (error) => {
/**
* Utility function finds the correct project file to download
*/
function selectProjectFile(fileName?: string) {
const projectsDirectory = resolveDbDirectory;
const fileToDownload = fileName ? ensureJsonExtension(fileName) : projectService.getProjectTitle();
const pathToFile = join(projectsDirectory, fileToDownload);
return { pathToFile, name: fileToDownload };
}
/**
* Allows downloading of a optionally given project files
* If no {filename} is provided, loaded file will be served
*/
export async function projectDownload(req: Request, res: Response) {
const { pathToFile, name } = selectProjectFile(req.body?.fileName);
// Check if the file exists before attempting to download
if (!existsSync(pathToFile)) {
return res.status(404).send({ message: `Project ${name} not found.` });
}
res.download(pathToFile, name, (error) => {
if (error) {
const message = getErrorMessage(error);
res.status(500).send({ message });
+2 -1
View File
@@ -16,6 +16,7 @@ import { uploadProjectFile } from './db.middleware.js';
import {
projectSanitiser,
sanitizeProjectFilename,
validateDownloadProject,
validateLoadProjectFile,
validatePatchProjectFile,
validateProjectDuplicate,
@@ -24,7 +25,7 @@ import {
export const router = express.Router();
router.get('/download', projectDownload);
router.post('/download', validateDownloadProject, projectDownload);
router.post('/upload', uploadProjectFile, postProjectFile);
router.patch('/', validatePatchProjectFile, patchPartialProjectFile);
@@ -104,3 +104,19 @@ export const validateProjectRename = [
next();
},
];
/**
* @description Validates a download request which can include an optional project name.
*/
export const validateDownloadProject = [
body('fileName').isString().optional(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
next();
},
];
+1 -3
View File
@@ -20,9 +20,7 @@ export function ensureDirectory(directory: string): void {
* Ensures that a filename ends with .json extension
*/
export function ensureJsonExtension(filename: string): string {
if (!filename) return filename;
return filename.includes('.json') ? filename : `${filename}.json`;
return filename.endsWith('.json') ? filename : `${filename}.json`;
}
/**