refactor: simplify setup of asset paths

This commit is contained in:
Carlos Valente
2024-10-13 13:53:55 +02:00
committed by Carlos Valente
parent e46948772e
commit 5950551da2
17 changed files with 177 additions and 144 deletions
@@ -3,8 +3,8 @@ import { getErrorMessage, obfuscate } from 'ontime-utils';
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
import { isDocker } from '../../externals.js';
import { failEmptyObjects } from '../../utils/routerUtils.js'; import { failEmptyObjects } from '../../utils/routerUtils.js';
import { isDocker } from '../../setup/index.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js'; import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { extractPin } from './settings.utils.js'; import { extractPin } from './settings.utils.js';
+9 -17
View File
@@ -9,15 +9,8 @@ import serverTiming from 'server-timing';
// import utils // import utils
import { resolve } from 'path'; import { resolve } from 'path';
import { import { publicDir, srcDir } from './setup/index.js';
srcDirectory, import { environment, isProduction } from './externals.js';
environment,
isProduction,
resolveExternalsDirectory,
resolveStylesDirectory,
resolvedPath,
resolvePublicDirectoy,
} from './setup/index.js';
import { ONTIME_VERSION } from './ONTIME_VERSION.js'; import { ONTIME_VERSION } from './ONTIME_VERSION.js';
import { consoleSuccess, consoleHighlight, consoleError } from './utils/console.js'; import { consoleSuccess, consoleHighlight, consoleError } from './utils/console.js';
@@ -56,8 +49,8 @@ consoleHighlight(`Starting Ontime version ${ONTIME_VERSION}`);
const canLog = isProduction; const canLog = isProduction;
if (!canLog) { if (!canLog) {
console.log(`Ontime running in ${environment} environment`); console.log(`Ontime running in ${environment} environment`);
console.log(`Ontime source directory at ${srcDirectory} `); console.log(`Ontime source directory at ${srcDir.root} `);
console.log(`Ontime public directory at ${resolvePublicDirectoy} `); console.log(`Ontime public directory at ${publicDir.root} `);
} }
// Create express APP // Create express APP
@@ -82,17 +75,16 @@ app.use(express.json({ limit: '1mb' }));
app.use('/data', appRouter); // router for application data app.use('/data', appRouter); // router for application data
app.use('/api', integrationRouter); // router for integrations app.use('/api', integrationRouter); // router for integrations
// serve static - css // serve static external files
app.use('/external/styles', express.static(resolveStylesDirectory)); app.use('/external/', express.static(publicDir.externalDir));
app.use('/external/', express.static(resolveExternalsDirectory)); // if the user reaches to the root, we show a 404
app.use('/external', (req, res) => { app.use('/external', (req, res) => {
res.status(404).send(`${req.originalUrl} not found`); res.status(404).send(`${req.originalUrl} not found`);
}); });
// serve static - react, in dev/test mode we fetch the React app from module // serve static - react, in dev/test mode we fetch the React app from module
const reactAppPath = resolvedPath();
app.use( app.use(
expressStaticGzip(reactAppPath, { expressStaticGzip(srcDir.clientDir, {
enableBrotli: true, enableBrotli: true,
orderPreference: ['br'], orderPreference: ['br'],
// when we build the client all the react subfiles will get a hashed name we can the immutable tag // when we build the client all the react subfiles will get a hashed name we can the immutable tag
@@ -103,7 +95,7 @@ app.use(
); );
app.get('*', (_req, res) => { app.get('*', (_req, res) => {
res.sendFile(resolve(reactAppPath, 'index.html')); res.sendFile(resolve(srcDir.clientDir, 'index.html'));
}); });
// Implement catch all // Implement catch all
+1 -1
View File
@@ -2,9 +2,9 @@ import { Log, LogLevel } from 'ontime-types';
import { generateId, millisToString } from 'ontime-utils'; import { generateId, millisToString } from 'ontime-utils';
import { clock } from '../services/Clock.js'; import { clock } from '../services/Clock.js';
import { isProduction } from '../setup/index.js';
import { socket } from '../adapters/WebsocketAdapter.js'; import { socket } from '../adapters/WebsocketAdapter.js';
import { consoleSubdued, consoleError } from '../utils/console.js'; import { consoleSubdued, consoleError } from '../utils/console.js';
import { isProduction } from '../externals.js';
class Logger { class Logger {
private queue: Log[]; private queue: Log[];
@@ -13,11 +13,11 @@ import {
import type { Low } from 'lowdb'; import type { Low } from 'lowdb';
import { JSONFilePreset } from 'lowdb/node'; import { JSONFilePreset } from 'lowdb/node';
import { isTest } from '../../setup/index.js';
import { isPath } from '../../utils/fileManagement.js'; import { isPath } from '../../utils/fileManagement.js';
import { shouldCrashDev } from '../../utils/development.js';
import { isTest } from '../../externals.js';
import { safeMerge } from './DataProvider.utils.js'; import { safeMerge } from './DataProvider.utils.js';
import { shouldCrashDev } from '../../utils/development.js';
type ReadonlyPromise<T> = Promise<Readonly<T>>; type ReadonlyPromise<T> = Promise<Readonly<T>>;
+12
View File
@@ -0,0 +1,12 @@
/**
* This file contains a list of constants that may need to be resolved at runtime
*/
// =================================================
// resolve running environment
const env = process.env.NODE_ENV || 'production';
export const isTest = Boolean(process.env.IS_TEST);
export const environment = isTest ? 'test' : env;
export const isDocker = env === 'docker';
export const isProduction = isDocker || (env === 'production' && !isTest);
+3 -2
View File
@@ -1,9 +1,10 @@
import { MaybeNumber, MaybeString, Playback } from 'ontime-types'; import { MaybeNumber, MaybeString, Playback } from 'ontime-types';
import { JSONFile } from 'lowdb/node'; import { JSONFile } from 'lowdb/node';
import { resolveRestoreFile } from '../setup/index.js';
import { deepEqual } from 'fast-equals'; import { deepEqual } from 'fast-equals';
import { publicFiles } from '../setup/index.js';
export type RestorePoint = { export type RestorePoint = {
playback: Playback; playback: Playback;
selectedEventId: MaybeString; selectedEventId: MaybeString;
@@ -147,4 +148,4 @@ export class RestoreService {
} }
} }
export const restoreService = new RestoreService(resolveRestoreFile); export const restoreService = new RestoreService(publicFiles.restoreFile);
@@ -1,7 +1,8 @@
import { Low } from 'lowdb'; import { Low } from 'lowdb';
import { JSONFile } from 'lowdb/node'; import { JSONFile } from 'lowdb/node';
import { appStatePath, isTest } from '../../setup/index.js'; import { publicFiles } from '../../setup/index.js';
import { isTest } from '../../externals.js';
import { isPath } from '../../utils/fileManagement.js'; import { isPath } from '../../utils/fileManagement.js';
import { shouldCrashDev } from '../../utils/development.js'; import { shouldCrashDev } from '../../utils/development.js';
@@ -9,7 +10,7 @@ interface AppState {
lastLoadedProject?: string; lastLoadedProject?: string;
} }
const adapter = new JSONFile<AppState>(appStatePath); const adapter = new JSONFile<AppState>(publicFiles.appState);
const config = new Low<AppState>(adapter, {}); const config = new Low<AppState>(adapter, {});
export async function isLastLoadedProject(projectName: string): Promise<boolean> { export async function isLastLoadedProject(projectName: string): Promise<boolean> {
@@ -5,7 +5,7 @@ import { copyFile, rename } from 'fs/promises';
import { logger } from '../../classes/Logger.js'; import { logger } from '../../classes/Logger.js';
import { getNetworkInterfaces } from '../../utils/networkInterfaces.js'; import { getNetworkInterfaces } from '../../utils/networkInterfaces.js';
import { resolveCorruptDirectory, resolveProjectsDirectory, resolveStylesPath } from '../../setup/index.js'; import { publicDir, publicFiles } from '../../setup/index.js';
import { import {
appendToName, appendToName,
ensureDirectory, ensureDirectory,
@@ -47,8 +47,8 @@ init();
* Ensure services has its dependencies initialized * Ensure services has its dependencies initialized
*/ */
function init() { function init() {
ensureDirectory(resolveProjectsDirectory); ensureDirectory(publicDir.projectsDir);
ensureDirectory(resolveCorruptDirectory); ensureDirectory(publicDir.corruptDir);
} }
export async function getCurrentProject() { export async function getCurrentProject() {
@@ -63,7 +63,7 @@ export async function getCurrentProject() {
* to be composed in the loading functions * to be composed in the loading functions
*/ */
async function loadDemoProject(): Promise<string> { async function loadDemoProject(): Promise<string> {
const pathToNewFile = generateUniqueFileName(resolveProjectsDirectory, config.demoProject); const pathToNewFile = generateUniqueFileName(publicDir.projectsDir, config.demoProject);
await initPersistence(getPathToProject(pathToNewFile), demoDb); await initPersistence(getPathToProject(pathToNewFile), demoDb);
const newName = getFileNameFromPath(pathToNewFile); const newName = getFileNameFromPath(pathToNewFile);
await setLastLoadedProject(newName); await setLastLoadedProject(newName);
@@ -75,7 +75,7 @@ async function loadDemoProject(): Promise<string> {
* to be composed in the loading functions * to be composed in the loading functions
*/ */
async function loadNewProject(): Promise<string> { async function loadNewProject(): Promise<string> {
const pathToNewFile = generateUniqueFileName(resolveProjectsDirectory, config.newProject); const pathToNewFile = generateUniqueFileName(publicDir.projectsDir, config.newProject);
await initPersistence(getPathToProject(pathToNewFile), dbModel); await initPersistence(getPathToProject(pathToNewFile), dbModel);
const newName = getFileNameFromPath(pathToNewFile); const newName = getFileNameFromPath(pathToNewFile);
await setLastLoadedProject(newName); await setLastLoadedProject(newName);
@@ -273,7 +273,7 @@ export async function createProject(filename: string, projectData: ProjectData)
}, },
}; };
const uniqueFileName = generateUniqueFileName(resolveProjectsDirectory, filename); const uniqueFileName = generateUniqueFileName(publicDir.projectsDir, filename);
const newFile = getPathToProject(uniqueFileName); const newFile = getPathToProject(uniqueFileName);
// change LowDB to point to new file // change LowDB to point to new file
@@ -316,14 +316,13 @@ export async function getInfo(): Promise<GetInfo> {
// get nif and inject localhost // get nif and inject localhost
const ni = getNetworkInterfaces(); const ni = getNetworkInterfaces();
ni.unshift({ name: 'localhost', address: '127.0.0.1' }); ni.unshift({ name: 'localhost', address: '127.0.0.1' });
const cssOverride = resolveStylesPath;
return { return {
networkInterfaces: ni, networkInterfaces: ni,
version, version,
serverPort, serverPort,
osc, osc,
cssOverride, cssOverride: publicFiles.cssOverride,
}; };
} }
@@ -4,7 +4,7 @@ import { existsSync } from 'fs';
import { copyFile, readFile, rename, stat } from 'fs/promises'; import { copyFile, readFile, rename, stat } from 'fs/promises';
import { extname, join } from 'path'; import { extname, join } from 'path';
import { resolveCorruptDirectory, resolveProjectsDirectory } from '../../setup/index.js'; import { publicDir } from '../../setup/index.js';
import { getFilesFromFolder, removeFileExtension } from '../../utils/fileManagement.js'; import { getFilesFromFolder, removeFileExtension } from '../../utils/fileManagement.js';
/** /**
@@ -13,7 +13,7 @@ import { getFilesFromFolder, removeFileExtension } from '../../utils/fileManagem
* @param name * @param name
*/ */
export async function handleUploaded(filePath: string, name: string) { export async function handleUploaded(filePath: string, name: string) {
const newFilePath = join(resolveProjectsDirectory, name); const newFilePath = join(publicDir.projectsDir, name);
await rename(filePath, newFilePath); await rename(filePath, newFilePath);
} }
@@ -29,12 +29,12 @@ export async function handleUploaded(filePath: string, name: string) {
* @throws {Error} Throws an error if there is an issue in reading the directory or fetching file statistics. * @throws {Error} Throws an error if there is an issue in reading the directory or fetching file statistics.
*/ */
export async function getProjectFiles(): Promise<ProjectFile[]> { export async function getProjectFiles(): Promise<ProjectFile[]> {
const allFiles = await getFilesFromFolder(resolveProjectsDirectory); const allFiles = await getFilesFromFolder(publicDir.projectsDir);
const filteredFiles = filterProjectFiles(allFiles); const filteredFiles = filterProjectFiles(allFiles);
const projectFiles: ProjectFile[] = []; const projectFiles: ProjectFile[] = [];
for (const file of filteredFiles) { for (const file of filteredFiles) {
const filePath = join(resolveProjectsDirectory, file); const filePath = join(publicDir.projectsDir, file);
const stats = await stat(filePath); const stats = await stat(filePath);
projectFiles.push({ projectFiles.push({
@@ -62,14 +62,14 @@ export function doesProjectExist(name: string): MaybeString {
* Returns the absolute path to a project file * Returns the absolute path to a project file
*/ */
export function getPathToProject(name: string): string { export function getPathToProject(name: string): string {
return join(resolveProjectsDirectory, name); return join(publicDir.projectsDir, name);
} }
/** /**
* Makes a copy of a given project to the corrupted directory * Makes a copy of a given project to the corrupted directory
*/ */
export async function copyCorruptFile(filePath: string, name: string): Promise<void> { export async function copyCorruptFile(filePath: string, name: string): Promise<void> {
const newPath = join(resolveCorruptDirectory, name); const newPath = join(publicDir.corruptDir, name);
return copyFile(filePath, newPath); return copyFile(filePath, newPath);
} }
@@ -77,7 +77,7 @@ export async function copyCorruptFile(filePath: string, name: string): Promise<v
* Moves a file permanently to the corrupted directory * Moves a file permanently to the corrupted directory
*/ */
export async function moveCorruptFile(filePath: string, name: string): Promise<void> { export async function moveCorruptFile(filePath: string, name: string): Promise<void> {
const newPath = join(resolveCorruptDirectory, name); const newPath = join(publicDir.corruptDir, name);
return rename(filePath, newPath); return rename(filePath, newPath);
} }
@@ -11,7 +11,7 @@ import { sheets, sheets_v4 } from '@googleapis/sheets';
import { Credentials, OAuth2Client } from 'google-auth-library'; import { Credentials, OAuth2Client } from 'google-auth-library';
import got from 'got'; import got from 'got';
import { resolveSheetsDirectory } from '../../setup/index.js'; import { publicDir } from '../../setup/index.js';
import { ensureDirectory } from '../../utils/fileManagement.js'; import { ensureDirectory } from '../../utils/fileManagement.js';
import { cellRequestFromEvent, type ClientSecret, getA1Notation, validateClientSecret } from './sheetUtils.js'; import { cellRequestFromEvent, type ClientSecret, getA1Notation, validateClientSecret } from './sheetUtils.js';
import { parseExcel } from '../../utils/parser.js'; import { parseExcel } from '../../utils/parser.js';
@@ -57,7 +57,7 @@ function reset() {
*/ */
export function init() { export function init() {
reset(); reset();
ensureDirectory(resolveSheetsDirectory); ensureDirectory(publicDir.sheetsDir);
} }
/** /**
+83 -75
View File
@@ -1,18 +1,26 @@
/**
* This file handles resolving paths for the server resources
* There are two main directories
* - 1. the installation directory, exposed by __dirname
* - 2. the public directory, exposed by getAppDataPath()
*/
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { dirname, join } from 'path'; import { dirname, join } from 'path';
import { config } from './config.js'; import { config } from './config.js';
import { ensureDirectory } from '../utils/fileManagement.js'; import { ensureDirectory } from '../utils/fileManagement.js';
import { isProduction } from '../externals.js';
// =================================================
// resolve public path
/** /**
* @description Returns public path depending on OS * Returns public path depending on OS
* This is the correct path for the app running in production mode * This is the correct path for the app running in production mode
*/ */
export function getAppDataPath(): string { export function getAppDataPath(): string {
// handle docker /**
* If we are running in docker, the ONTIME_DATA environment variable
* allows moving the public directory to a user defined location
*/
if (process.env.ONTIME_DATA) { if (process.env.ONTIME_DATA) {
return join(process.env.ONTIME_DATA); return join(process.env.ONTIME_DATA);
} }
@@ -33,86 +41,86 @@ export function getAppDataPath(): string {
} }
} }
// ================================================= /**
// resolve running environment * 1. Paths relative to the installation (or source in development)
const env = process.env.NODE_ENV || 'production'; * ------------------------------------------------------------------
*/
export const isTest = Boolean(process.env.IS_TEST); /** resolve file URL in both CJS and ESM (build and dev) */
export const environment = isTest ? 'test' : env;
export const isDocker = env === 'docker';
export const isProduction = isDocker || (env === 'production' && !isTest);
// =================================================
// Resolve directory paths
// resolve file URL in both CJS and ESM (build and dev)
if (import.meta.url) { if (import.meta.url) {
globalThis.__dirname = fileURLToPath(import.meta.url); globalThis.__dirname = fileURLToPath(import.meta.url);
} }
// path to server src folder
const currentDir = dirname(__dirname); const currentDir = dirname(__dirname);
// locally we are in src/setup, in the production build, this is a single file at src
export const srcDirectory = isProduction ? currentDir : join(currentDir, '../');
// TODO: simplify logic /**
// resolve path to client * path to server src folder
const productionPath = join(srcDirectory, 'client/'); * when running in dev, this file is located in src/setup, so we go one level up
const devPath = join(srcDirectory, '../../client/build/'); * */
export const resolvedPath = (): string => { const srcDirectory = isProduction ? currentDir : join(currentDir, '../');
if (isTest) {
return devPath; export const srcDir = {
} root: srcDirectory,
if (isProduction) { /** Path to the react app */
return productionPath; clientDir: isProduction ? join(srcDirectory, 'client/') : join(srcDirectory, '../../client/build/'),
} /** Path to the demo app */
return devPath; demoDir: join(srcDirectory, '/external/demo/'),
} as const;
export const srcFiles = {
/** Path to bundled CSS */
cssOverride: join(srcDir.root, '/external/styles/', config.styles.filename),
}; };
// resolve public directory /**
export const resolvePublicDirectoy = getAppDataPath(); * 2. Paths relative to the user public directory
ensureDirectory(resolvePublicDirectoy); * ------------------------------------------------
*/
export const externalsStartDirectory = isProduction ? resolvePublicDirectoy : join(srcDirectory, 'external'); /** Resolve root to public directory */
// TODO: we only need one when they are all in the same folder const resolvePublicDirectory = getAppDataPath();
export const resolveExternalsDirectory = join(isProduction ? resolvePublicDirectoy : srcDirectory, 'external'); // Ensure directory tree is created
ensureDirectory(resolvePublicDirectory);
// project files /**
export const appStatePath = join(resolvePublicDirectoy, config.appState); * Path to external
export const uploadsFolderPath = join(resolvePublicDirectoy, config.uploads); * This is unique in the way that we use the src directory in development
* For simplicity we still bundle this in the public directory object
*/
const externalsStartDirectory = isProduction ? resolvePublicDirectory : join(srcDirectory, 'external');
// path to public styles export const publicDir = {
export const resolveStylesDirectory = join(externalsStartDirectory, config.styles.directory); root: resolvePublicDirectory,
export const resolveStylesPath = join(resolveStylesDirectory, config.styles.filename); /** path to sheets folder */
sheetsDir: join(resolvePublicDirectory, config.sheets.directory),
/** path to crash reports folder */
crashDir: join(resolvePublicDirectory, config.crash),
/** path to projects folder */
projectsDir: join(resolvePublicDirectory, config.projects),
/** path to corrupt folder */
corruptDir: join(resolvePublicDirectory, config.corrupt),
/** path to uploads folder */
uploadsDir: join(resolvePublicDirectory, config.uploads),
/** path to external folder */
externalDir: externalsStartDirectory,
/** path to demo project folder */
demoDir: join(
externalsStartDirectory,
isProduction ? '/external/' : '', // move to external folder in production
config.demo.directory,
),
/** path to external styles override */
stylesDir: join(externalsStartDirectory, config.styles.directory),
} as const;
export const pathToStartStyles = join(srcDirectory, '/external/styles/', config.styles.filename); /**
* Resolve path to specific files
// path to public demo */
export const resolveDemoDirectory = join( export const publicFiles = {
externalsStartDirectory, /** path to app state file */
isProduction ? '/external/' : '', // move to external folder in production appState: join(publicDir.root, config.appState),
config.demo.directory, /** path to restore file */
); restoreFile: join(publicDir.root, config.restoreFile),
export const resolveDemoPath = config.demo.filename.map((file) => { /** path to CSS override file */
return join(resolveDemoDirectory, file); cssOverride: join(publicDir.stylesDir, config.styles.filename),
}); };
// path to demo project
export const pathToStartDemo = config.demo.filename.map((file) => {
return join(srcDirectory, '/external/demo/', file);
});
// path to restore file
export const resolveRestoreFile = join(resolvePublicDirectoy, config.restoreFile);
// path to sheets folder
export const resolveSheetsDirectory = join(resolvePublicDirectoy, config.sheets.directory);
// path to crash reports
export const resolveCrashReportDirectory = join(resolvePublicDirectoy, config.crash);
// path to projects
export const resolveProjectsDirectory = join(resolvePublicDirectoy, config.projects);
// path to corrupt files
export const resolveCorruptDirectory = join(resolvePublicDirectoy, config.corrupt);
+6 -10
View File
@@ -1,20 +1,16 @@
import { copyFile } from 'fs/promises'; import { copyDirectory, ensureDirectory } from '../utils/fileManagement.js';
import { pathToStartDemo, resolveDemoDirectory, resolveDemoPath } from './index.js';
import { ensureDirectory } from '../utils/fileManagement.js'; import { publicDir, srcDir } from './index.js';
/** /**
* @description ensures directories exist and populates demo folder * @description ensures directories exist and populates demo folder
*/ */
export const populateDemo = () => { export const populateDemo = () => {
ensureDirectory(resolveDemoDirectory); ensureDirectory(publicDir.demoDir);
// even if demo exist we want to use startup demo // even if demo exist we want to use startup demo
try { try {
Promise.all( copyDirectory(srcDir.demoDir, publicDir.demoDir);
resolveDemoPath.map((to, index) => {
const from = pathToStartDemo[index];
return copyFile(from, to);
}),
);
} catch (_) { } catch (_) {
/* we do not handle this */ /* we do not handle this */
} }
+8 -5
View File
@@ -1,16 +1,19 @@
import { copyFileSync, existsSync } from 'fs'; import { copyFileSync, existsSync } from 'fs';
import { pathToStartStyles, resolveStylesDirectory, resolveStylesPath } from './index.js';
import { ensureDirectory } from '../utils/fileManagement.js'; import { ensureDirectory } from '../utils/fileManagement.js';
import { publicDir, publicFiles, srcFiles } from './index.js';
/** /**
* @description ensures directories exist and populates stylesheet * ensures directories exist and populates stylesheet
*/ */
export const populateStyles = () => { export const populateStyles = () => {
ensureDirectory(resolveStylesDirectory); ensureDirectory(publicDir.stylesDir);
// if styles doesn't exist we want to use startup stylesheet // if styles doesn't exist we want to use startup stylesheet
if (!existsSync(resolveStylesPath)) { if (!existsSync(publicFiles.cssOverride)) {
try { try {
copyFileSync(pathToStartStyles, resolveStylesPath); // copy the startup stylesheet to the public directory
copyFileSync(srcFiles.cssOverride, publicFiles.cssOverride);
} catch (_) { } catch (_) {
/* we do not handle this */ /* we do not handle this */
} }
+1 -1
View File
@@ -1,4 +1,4 @@
import { isProduction } from '../setup/index.js'; import { isProduction } from '../externals.js';
import { consoleError } from '../utils/console.js'; import { consoleError } from '../utils/console.js';
/** /**
+22 -2
View File
@@ -1,5 +1,5 @@
import { existsSync, mkdirSync } from 'fs'; import { existsSync, mkdirSync } from 'fs';
import { readdir } from 'fs/promises'; import { readdir, copyFile } from 'fs/promises';
import { basename, extname, join, parse } from 'path'; import { basename, extname, join, parse } from 'path';
/** /**
@@ -80,8 +80,28 @@ export function getFileNameFromPath(filePath: string): string {
} }
/** /**
* Utility naivly checks for paths on whether it includes directories * Utility naively checks for paths on whether it includes directories
*/ */
export function isPath(filePath: string): boolean { export function isPath(filePath: string): boolean {
return filePath !== basename(filePath); return filePath !== basename(filePath);
} }
/**
* Recursively copies a directory and its contents.
* @param {string} src - The source directory.
* @param {string} dest - The destination directory.
*/
export async function copyDirectory(src: string, dest: string) {
const entries = await readdir(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = join(src, entry.name);
const destPath = join(dest, entry.name);
if (entry.isDirectory()) {
await copyDirectory(srcPath, destPath);
} else {
await copyFile(srcPath, destPath);
}
}
}
+3 -3
View File
@@ -4,7 +4,7 @@ import { join } from 'path';
import { ONTIME_VERSION } from '../ONTIME_VERSION.js'; import { ONTIME_VERSION } from '../ONTIME_VERSION.js';
import { get } from '../services/rundown-service/rundownCache.js'; import { get } from '../services/rundown-service/rundownCache.js';
import { getState } from '../stores/runtimeState.js'; import { getState } from '../stores/runtimeState.js';
import { resolveCrashReportDirectory } from '../setup/index.js'; import { publicDir } from '../setup/index.js';
import { ensureDirectory } from './fileManagement.js'; import { ensureDirectory } from './fileManagement.js';
/** /**
@@ -13,8 +13,8 @@ import { ensureDirectory } from './fileManagement.js';
* @param content * @param content
*/ */
function writeToFile(fileName: string, content: object) { function writeToFile(fileName: string, content: object) {
const path = join(resolveCrashReportDirectory, fileName); const path = join(publicDir.crashDir, fileName);
ensureDirectory(resolveCrashReportDirectory); ensureDirectory(publicDir.crashDir);
try { try {
const textContent = JSON.stringify(content, null, 2); const textContent = JSON.stringify(content, null, 2);
+7 -6
View File
@@ -3,8 +3,9 @@ import path from 'path';
import fs from 'fs'; import fs from 'fs';
import { rm } from 'fs/promises'; import { rm } from 'fs/promises';
import { getAppDataPath, publicDir } from '../setup/index.js';
import { ensureDirectory } from './fileManagement.js'; import { ensureDirectory } from './fileManagement.js';
import { getAppDataPath, uploadsFolderPath } from '../setup/index.js';
function generateNewFileName(filePath: string, callback: (newName: string) => void) { function generateNewFileName(filePath: string, callback: (newName: string) => void) {
const baseName = path.basename(filePath, path.extname(filePath)); const baseName = path.basename(filePath, path.extname(filePath));
@@ -36,19 +37,19 @@ export const storage = multer.diskStorage({
throw new Error('Could not resolve public folder for platform'); throw new Error('Could not resolve public folder for platform');
} }
ensureDirectory(uploadsFolderPath); ensureDirectory(publicDir.uploadsDir);
const filePath = path.join(uploadsFolderPath, file.originalname); const filePath = path.join(publicDir.uploadsDir, file.originalname);
// Check if file already exists // Check if file already exists
fs.access(filePath, fs.constants.F_OK, (err) => { fs.access(filePath, fs.constants.F_OK, (err) => {
if (err) { if (err) {
// File does not exist, can safely proceed to this destination // File does not exist, can safely proceed to this destination
cb(null, uploadsFolderPath); cb(null, publicDir.uploadsDir);
} else { } else {
generateNewFileName(filePath, (newName) => { generateNewFileName(filePath, (newName) => {
file.originalname = newName; file.originalname = newName;
cb(null, uploadsFolderPath); cb(null, publicDir.uploadsDir);
}); });
} }
}); });
@@ -63,7 +64,7 @@ export const storage = multer.diskStorage({
*/ */
export async function clearUploadfolder() { export async function clearUploadfolder() {
try { try {
await rm(uploadsFolderPath, { recursive: true }); await rm(publicDir.uploadsDir, { recursive: true });
} catch (_) { } catch (_) {
// we dont care that there was no folder // we dont care that there was no folder
} }