diff --git a/apps/server/src/services/project-service/ProjectService.ts b/apps/server/src/services/project-service/ProjectService.ts index 2b50a0661..6cc13d947 100644 --- a/apps/server/src/services/project-service/ProjectService.ts +++ b/apps/server/src/services/project-service/ProjectService.ts @@ -1,12 +1,13 @@ import { DatabaseModel, LogOrigin, ProjectFileListResponse } from 'ontime-types'; import { getErrorMessage } from 'ontime-utils'; -import { copyFile, rename } from 'fs/promises'; +import { copyFile } from 'fs/promises'; import { logger } from '../../classes/Logger.js'; import { publicDir } from '../../setup/index.js'; import { appendToName, + dockerSafeRename, ensureDirectory, generateUniqueFileName, getFileNameFromPath, @@ -93,7 +94,7 @@ async function handleCorruptedFile(filePath: string, fileName: string): Promise< // and make a new file with the recovered data const newPath = appendToName(filePath, '(recovered)'); - await rename(filePath, newPath); + await dockerSafeRename(filePath, newPath); return getFileNameFromPath(newPath); } @@ -231,7 +232,7 @@ export async function renameProjectFile(originalFile: string, newFilename: strin } const pathToRenamed = getPathToProject(newFilename); - await rename(projectFilePath, pathToRenamed); + await dockerSafeRename(projectFilePath, pathToRenamed); // Update the last loaded project config if current loaded project is the one being renamed const isLoaded = await isLastLoadedProject(originalFile); diff --git a/apps/server/src/services/project-service/projectServiceUtils.ts b/apps/server/src/services/project-service/projectServiceUtils.ts index e28116586..1728c3d8d 100644 --- a/apps/server/src/services/project-service/projectServiceUtils.ts +++ b/apps/server/src/services/project-service/projectServiceUtils.ts @@ -1,11 +1,16 @@ import { DatabaseModel, MaybeString, ProjectFile } from 'ontime-types'; import { existsSync } from 'fs'; -import { copyFile, readFile, rename, stat } from 'fs/promises'; +import { copyFile, readFile, stat } from 'fs/promises'; import { extname, join } from 'path'; import { publicDir } from '../../setup/index.js'; -import { ensureDirectory, getFilesFromFolder, removeFileExtension } from '../../utils/fileManagement.js'; +import { + dockerSafeRename, + ensureDirectory, + getFilesFromFolder, + removeFileExtension, +} from '../../utils/fileManagement.js'; /** * Handles the upload of a new project file @@ -14,13 +19,13 @@ import { ensureDirectory, getFilesFromFolder, removeFileExtension } from '../../ */ export async function handleUploaded(filePath: string, name: string) { const newFilePath = join(publicDir.projectsDir, name); - await rename(filePath, newFilePath); + await dockerSafeRename(filePath, newFilePath); } export async function handleImageUpload(filePath: string, name: string): Promise { ensureDirectory(publicDir.logoDir); const newFilePath = join(publicDir.logoDir, name); - await rename(filePath, newFilePath); + await dockerSafeRename(filePath, newFilePath); return name; } @@ -86,7 +91,7 @@ export async function copyCorruptFile(filePath: string, name: string): Promise { const newPath = join(publicDir.corruptDir, name); - return rename(filePath, newPath); + return dockerSafeRename(filePath, newPath); } /** diff --git a/apps/server/src/utils/fileManagement.ts b/apps/server/src/utils/fileManagement.ts index 5a2d361dd..759a6ee29 100644 --- a/apps/server/src/utils/fileManagement.ts +++ b/apps/server/src/utils/fileManagement.ts @@ -1,5 +1,5 @@ -import { existsSync, mkdirSync } from 'fs'; -import { readdir, copyFile } from 'fs/promises'; +import { existsSync, mkdirSync, PathLike } from 'fs'; +import { readdir, copyFile, unlink } from 'fs/promises'; import { basename, extname, join, parse } from 'path'; /** @@ -105,3 +105,12 @@ export async function copyDirectory(src: string, dest: string) { } } } + +/** + * workaround avoids origin errors in docker deployments + * EXDEV cross-device link not permitted + */ +export async function dockerSafeRename(oldPath: PathLike, newPath: PathLike) { + await copyFile(oldPath, newPath); + await unlink(oldPath); +}