diff --git a/nextjs/src/app/api/courses/[courseName]/modules/[moduleName]/assignments/[assignmentName]/route.ts b/nextjs/src/app/api/courses/[courseName]/modules/[moduleName]/assignments/[assignmentName]/route.ts index 6e3cd08..1940f19 100644 --- a/nextjs/src/app/api/courses/[courseName]/modules/[moduleName]/assignments/[assignmentName]/route.ts +++ b/nextjs/src/app/api/courses/[courseName]/modules/[moduleName]/assignments/[assignmentName]/route.ts @@ -1,3 +1,4 @@ +import { LocalAssignment } from "@/models/local/assignment/localAssignment"; import { fileStorageService } from "@/services/fileStorage/fileStorageService"; import { withErrorHandling } from "@/services/withErrorHandling"; @@ -27,13 +28,23 @@ export const PUT = async ( } ) => await withErrorHandling(async () => { - const assignment = await request.json(); - await fileStorageService.assignments.updateOrCreateAssignment( + const { + assignment, + previousModuleName, + }: { assignment: LocalAssignment; previousModuleName?: string } = + await request.json(); + await fileStorageService.assignments.updateOrCreateAssignment({ courseName, moduleName, assignmentName, - assignment - ); + assignment, + }); + + if(previousModuleName !== moduleName) + { + fileStorageService.assignments. + } + return Response.json({}); }); @@ -47,11 +58,11 @@ export const POST = async ( ) => await withErrorHandling(async () => { const assignment = await request.json(); - await fileStorageService.assignments.updateOrCreateAssignment( + await fileStorageService.assignments.updateOrCreateAssignment({ courseName, moduleName, assignmentName, - assignment - ); + assignment, + }); return Response.json({}); }); diff --git a/nextjs/src/app/course/[courseName]/calendar/Day.tsx b/nextjs/src/app/course/[courseName]/calendar/Day.tsx index 251e6bd..0bc1079 100644 --- a/nextjs/src/app/course/[courseName]/calendar/Day.tsx +++ b/nextjs/src/app/course/[courseName]/calendar/Day.tsx @@ -29,7 +29,7 @@ export default function Day({ day, month }: { day: string; month: number }) { getDateOnlyMarkdownString(dayAsDate); const { data: settings } = useLocalCourseSettingsQuery(); - const { itemDrop } = useDraggingContext(); + const { itemDropOnDay } = useDraggingContext(); const { todaysAssignments, todaysQuizzes, todaysPages } = useTodaysItems(day); @@ -47,7 +47,7 @@ export default function Day({ day, month }: { day: string; month: number }) { return (
itemDrop(e, day)} + onDrop={(e) => itemDropOnDay(e, day)} onDragOver={(e) => e.preventDefault()} > diff --git a/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx b/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx index 5817d88..6c6dfc8 100644 --- a/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx +++ b/nextjs/src/app/course/[courseName]/context/DraggingContextProvider.tsx @@ -26,50 +26,79 @@ export default function DraggingContextProvider({ const dragStart = useCallback(() => setIsDragging(true), []); - const itemDrop = useCallback( - (e: DragEvent, day: string | undefined) => { + const itemDropOnModule = useCallback( + (e: DragEvent, moduleName: string) => { const rawData = e.dataTransfer.getData("draggableItem"); const itemBeingDragged = JSON.parse(rawData); - if (itemBeingDragged && day) { - const dayAsDate = getDateFromStringOrThrow(day, "in drop callback"); - dayAsDate.setHours(settings.defaultDueTime.hour); - dayAsDate.setMinutes(settings.defaultDueTime.minute); - dayAsDate.setSeconds(0); - - console.log("dropped on day", dayAsDate, day); + if (itemBeingDragged) { if (itemBeingDragged.type === "quiz") { - console.log("dropping quiz"); - const previousQuiz = itemBeingDragged.item as LocalQuiz; - - const quiz: LocalQuiz = { - ...previousQuiz, - dueAt: dateToMarkdownString(dayAsDate), - lockAt: getLaterDate(previousQuiz.lockAt, dayAsDate), - }; - updateQuizMutation.mutate({ - quiz: quiz, - quizName: quiz.name, - moduleName: itemBeingDragged.sourceModuleName, - }); + updateQuiz(); } else if (itemBeingDragged.type === "assignment") { - updateAssignment(dayAsDate); + updateAssignment(); } else if (itemBeingDragged.type === "page") { - console.log("dropped page"); - const previousPage = itemBeingDragged.item as LocalCoursePage; - const page: LocalCoursePage = { - ...previousPage, - dueAt: dateToMarkdownString(dayAsDate), - }; - updatePageMutation.mutate({ - page, - moduleName: itemBeingDragged.sourceModuleName, - pageName: page.name, - }); + updatePage(); } } setIsDragging(false); + function updateQuiz() {} + function updateAssignment() {} + function updatePage() {} + }, + [] + ); + + const itemDropOnDay = useCallback( + (e: DragEvent, day: string) => { + const rawData = e.dataTransfer.getData("draggableItem"); + const itemBeingDragged = JSON.parse(rawData); + + if (itemBeingDragged) { + const dayAsDate = getDateWithDefaultDueTime(); + if (itemBeingDragged.type === "quiz") { + updateQuiz(dayAsDate); + } else if (itemBeingDragged.type === "assignment") { + updateAssignment(dayAsDate); + } else if (itemBeingDragged.type === "page") { + updatePage(dayAsDate); + } + } + setIsDragging(false); + + function getDateWithDefaultDueTime() { + const dayAsDate = getDateFromStringOrThrow(day, "in drop callback"); + dayAsDate.setHours(settings.defaultDueTime.hour); + dayAsDate.setMinutes(settings.defaultDueTime.minute); + dayAsDate.setSeconds(0); + return dayAsDate; + } + function updateQuiz(dayAsDate: Date) { + const previousQuiz = itemBeingDragged.item as LocalQuiz; + + const quiz: LocalQuiz = { + ...previousQuiz, + dueAt: dateToMarkdownString(dayAsDate), + lockAt: getLaterDate(previousQuiz.lockAt, dayAsDate), + }; + updateQuizMutation.mutate({ + quiz: quiz, + quizName: quiz.name, + moduleName: itemBeingDragged.sourceModuleName, + }); + } + function updatePage(dayAsDate: Date) { + const previousPage = itemBeingDragged.item as LocalCoursePage; + const page: LocalCoursePage = { + ...previousPage, + dueAt: dateToMarkdownString(dayAsDate), + }; + updatePageMutation.mutate({ + page, + moduleName: itemBeingDragged.sourceModuleName, + pageName: page.name, + }); + } function updateAssignment(dayAsDate: Date) { const previousAssignment = itemBeingDragged.item as LocalAssignment; const assignment: LocalAssignment = { @@ -103,7 +132,8 @@ export default function DraggingContextProvider({ return ( , droppedOnDay?: string) => void; + itemDropOnDay: (e: DragEvent, droppedOnDay: string) => void; + itemDropOnModule: (e: DragEvent, moduleName: string) => void; isDragging: boolean; dragStart: () => void; } const defaultDraggingValue: DraggingContextInterface = { - itemDrop: () => {}, + itemDropOnDay: () => {}, + itemDropOnModule: () => {}, isDragging: false, dragStart: () => {}, }; diff --git a/nextjs/src/hooks/localCourse/assignmentHooks.ts b/nextjs/src/hooks/localCourse/assignmentHooks.ts index d70ca54..4bf6e8d 100644 --- a/nextjs/src/hooks/localCourse/assignmentHooks.ts +++ b/nextjs/src/hooks/localCourse/assignmentHooks.ts @@ -94,10 +94,12 @@ export const useUpdateAssignmentMutation = () => { mutationFn: async ({ assignment, moduleName, + previousModuleName, assignmentName, }: { assignment: LocalAssignment; moduleName: string; + previousModuleName: string; assignmentName: string; }) => { queryClient.setQueryData( @@ -111,7 +113,7 @@ export const useUpdateAssignmentMutation = () => { encodeURIComponent(moduleName) + "/assignments/" + encodeURIComponent(assignmentName); - await axiosClient.put(url, assignment); + await axiosClient.put(url, { assignment, previousModuleName }); }, onSuccess: (_, { moduleName, assignmentName }) => { queryClient.invalidateQueries({ @@ -128,7 +130,6 @@ export const useUpdateAssignmentMutation = () => { }); }; - export const useCreateAssignmentMutation = () => { const { courseName } = useCourseContext(); const queryClient = useQueryClient(); @@ -169,4 +170,3 @@ export const useCreateAssignmentMutation = () => { }, }); }; - diff --git a/nextjs/src/services/fileStorage/assignmentsFileStorageService.ts b/nextjs/src/services/fileStorage/assignmentsFileStorageService.ts new file mode 100644 index 0000000..ab5b16e --- /dev/null +++ b/nextjs/src/services/fileStorage/assignmentsFileStorageService.ts @@ -0,0 +1,68 @@ +import { + localAssignmentMarkdown, + LocalAssignment, +} from "@/models/local/assignment/localAssignment"; +import { assignmentMarkdownSerializer } from "@/models/local/assignment/utils/assignmentMarkdownSerializer"; +import path from "path"; +import { basePath, directoryOrFileExists } from "./utils/fileSystemUtils"; +import { promises as fs } from "fs"; + +export const assignmentsFileStorageService = { + async getAssignmentNames(courseName: string, moduleName: string) { + const filePath = path.join(basePath, courseName, moduleName, "assignments"); + if (!(await directoryOrFileExists(filePath))) { + console.log( + `Error loading course by name, assignments folder does not exist in ${filePath}` + ); + await fs.mkdir(filePath); + } + + const assignmentFiles = await fs.readdir(filePath); + return assignmentFiles.map((f) => f.replace(/\.md$/, "")); + }, + async getAssignment( + courseName: string, + moduleName: string, + assignmentName: string + ) { + const filePath = path.join( + basePath, + courseName, + moduleName, + "assignments", + assignmentName + ".md" + ); + const rawFile = (await fs.readFile(filePath, "utf-8")).replace( + /\r\n/g, + "\n" + ); + return localAssignmentMarkdown.parseMarkdown(rawFile); + }, + async updateOrCreateAssignment({ + courseName, + moduleName, + assignmentName, + assignment, + }: { + courseName: string; + moduleName: string; + assignmentName: string; + assignment: LocalAssignment; + }) { + const folder = path.join(basePath, courseName, moduleName, "assignments"); + await fs.mkdir(folder, { recursive: true }); + + const filePath = path.join( + basePath, + courseName, + moduleName, + "assignments", + assignmentName + ".md" + ); + + const assignmentMarkdown = + assignmentMarkdownSerializer.toMarkdown(assignment); + console.log(`Saving assignment ${filePath}`); + await fs.writeFile(filePath, assignmentMarkdown); + }, +}; diff --git a/nextjs/src/services/fileStorage/fileStorageService.ts b/nextjs/src/services/fileStorage/fileStorageService.ts index 384884f..09e9f41 100644 --- a/nextjs/src/services/fileStorage/fileStorageService.ts +++ b/nextjs/src/services/fileStorage/fileStorageService.ts @@ -4,11 +4,7 @@ import { LocalCourseSettings, localCourseYamlUtils, } from "@/models/local/localCourse"; -import { directoryOrFileExists } from "./utils/fileSystemUtils"; -import { - LocalAssignment, - localAssignmentMarkdown, -} from "@/models/local/assignment/localAssignment"; +import { basePath, directoryOrFileExists } from "./utils/fileSystemUtils"; import { LocalQuiz, localQuizMarkdownUtils, @@ -18,10 +14,185 @@ import { localPageMarkdownUtils, } from "@/models/local/page/localCoursePage"; import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils"; -import { assignmentMarkdownSerializer } from "@/models/local/assignment/utils/assignmentMarkdownSerializer"; +import { assignmentsFileStorageService } from "./assignmentsFileStorageService"; -const basePath = process.env.STORAGE_DIRECTORY ?? "./storage"; -console.log("base path", basePath); +const quizzes = { + async getQuizNames(courseName: string, moduleName: string) { + const filePath = path.join(basePath, courseName, moduleName, "quizzes"); + if (!(await directoryOrFileExists(filePath))) { + console.log( + `Error loading course by name, quiz folder does not exist in ${filePath}` + ); + await fs.mkdir(filePath); + } + + const files = await fs.readdir(filePath); + return files.map((f) => f.replace(/\.md$/, "")); + }, + async getQuiz(courseName: string, moduleName: string, quizName: string) { + const filePath = path.join( + basePath, + courseName, + moduleName, + "quizzes", + quizName + ".md" + ); + const rawFile = (await fs.readFile(filePath, "utf-8")).replace( + /\r\n/g, + "\n" + ); + return localQuizMarkdownUtils.parseMarkdown(rawFile); + }, + + async updateQuiz( + courseName: string, + moduleName: string, + quizName: string, + quiz: LocalQuiz + ) { + const folder = path.join(basePath, courseName, moduleName, "quizzes"); + await fs.mkdir(folder, { recursive: true }); + const filePath = path.join( + basePath, + courseName, + moduleName, + "quizzes", + quizName + ".md" + ); + + const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz); + console.log(`Saving quiz ${filePath}`); + await fs.writeFile(filePath, quizMarkdown); + }, +}; + +const pages = { + async getPageNames(courseName: string, moduleName: string) { + const filePath = path.join(basePath, courseName, moduleName, "pages"); + if (!(await directoryOrFileExists(filePath))) { + console.log( + `Error loading course by name, pages folder does not exist in ${filePath}` + ); + await fs.mkdir(filePath); + } + + const files = await fs.readdir(filePath); + return files.map((f) => f.replace(/\.md$/, "")); + }, + + async getPage(courseName: string, moduleName: string, pageName: string) { + const filePath = path.join( + basePath, + courseName, + moduleName, + "pages", + pageName + ".md" + ); + const rawFile = (await fs.readFile(filePath, "utf-8")).replace( + /\r\n/g, + "\n" + ); + return localPageMarkdownUtils.parseMarkdown(rawFile); + }, + async updatePage( + courseName: string, + moduleName: string, + pageName: string, + page: LocalCoursePage + ) { + const folder = path.join(basePath, courseName, moduleName, "pages"); + await fs.mkdir(folder, { recursive: true }); + + const filePath = path.join( + basePath, + courseName, + moduleName, + "pages", + page.name + ".md" + ); + + const pageMarkdown = localPageMarkdownUtils.toMarkdown(page); + console.log(`Saving page ${filePath}`); + await fs.writeFile(filePath, pageMarkdown); + + const pageNameIsChanged = pageName !== page.name; + if (pageNameIsChanged) { + console.log("removing old page after name change " + pageName); + const oldFilePath = path.join( + basePath, + courseName, + moduleName, + "pages", + pageName + ".md" + ); + await fs.unlink(oldFilePath); + } + }, +}; + +const modules = { + async getModuleNames(courseName: string) { + const courseDirectory = path.join(basePath, courseName); + const moduleDirectories = await fs.readdir(courseDirectory, { + withFileTypes: true, + }); + + const modulePromises = moduleDirectories + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => dirent.name); + + const modules = await Promise.all(modulePromises); + return modules.sort((a, b) => a.localeCompare(b)); + }, + async createModule(courseName: string, moduleName: string) { + const courseDirectory = path.join(basePath, courseName); + + await fs.mkdir(courseDirectory + "/" + moduleName, { recursive: true }); + }, +}; + +const settings = { + async getAllCoursesSettings() { + const courses = await fileStorageService.getCourseNames(); + + const courseSettings = await Promise.all( + courses.map( + async (c) => await fileStorageService.settings.getCourseSettings(c) + ) + ); + return courseSettings; + }, + async getCourseSettings(courseName: string): Promise { + const courseDirectory = path.join(basePath, courseName); + const settingsPath = path.join(courseDirectory, "settings.yml"); + if (!(await directoryOrFileExists(settingsPath))) { + const errorMessage = `Error loading settings for ${courseName}, settings file ${settingsPath}`; + console.log(errorMessage); + throw new Error(errorMessage); + } + + const settingsString = await fs.readFile(settingsPath, "utf-8"); + const settings = localCourseYamlUtils.parseSettingYaml(settingsString); + + const folderName = path.basename(courseDirectory); + return { ...settings, name: folderName }; + }, + + async updateCourseSettings( + courseName: string, + settings: LocalCourseSettings + ) { + const courseDirectory = path.join(basePath, courseName); + const settingsPath = path.join(courseDirectory, "settings.yml"); + + const { name, ...settingsWithoutName } = settings; + const settingsMarkdown = + localCourseYamlUtils.settingsToYaml(settingsWithoutName); + + console.log(`Saving settings ${settingsPath}`); + await fs.writeFile(settingsPath, settingsMarkdown); + }, +}; export const fileStorageService = { async getCourseNames() { @@ -49,255 +220,11 @@ export const fileStorageService = { return courseNamesFromDirectories; }, - settings: { - async getAllCoursesSettings() { - const courses = await fileStorageService.getCourseNames(); - - const courseSettings = await Promise.all( - courses.map( - async (c) => await fileStorageService.settings.getCourseSettings(c) - ) - ); - return courseSettings; - }, - async getCourseSettings(courseName: string): Promise { - const courseDirectory = path.join(basePath, courseName); - const settingsPath = path.join(courseDirectory, "settings.yml"); - if (!(await directoryOrFileExists(settingsPath))) { - const errorMessage = `Error loading settings for ${courseName}, settings file ${settingsPath}`; - console.log(errorMessage); - throw new Error(errorMessage); - } - - const settingsString = await fs.readFile(settingsPath, "utf-8"); - const settings = localCourseYamlUtils.parseSettingYaml(settingsString); - - const folderName = path.basename(courseDirectory); - return { ...settings, name: folderName }; - }, - - async updateCourseSettings( - courseName: string, - settings: LocalCourseSettings - ) { - const courseDirectory = path.join(basePath, courseName); - const settingsPath = path.join(courseDirectory, "settings.yml"); - - const { name, ...settingsWithoutName } = settings; - const settingsMarkdown = - localCourseYamlUtils.settingsToYaml(settingsWithoutName); - - console.log(`Saving settings ${settingsPath}`); - await fs.writeFile(settingsPath, settingsMarkdown); - }, - }, - modules: { - async getModuleNames(courseName: string) { - const courseDirectory = path.join(basePath, courseName); - const moduleDirectories = await fs.readdir(courseDirectory, { - withFileTypes: true, - }); - - const modulePromises = moduleDirectories - .filter((dirent) => dirent.isDirectory()) - .map((dirent) => dirent.name); - - const modules = await Promise.all(modulePromises); - return modules.sort((a, b) => a.localeCompare(b)); - }, - async createModule(courseName: string, moduleName: string) { - const courseDirectory = path.join(basePath, courseName); - - await fs.mkdir(courseDirectory + "/" + moduleName, { recursive: true }); - }, - }, - assignments: { - async getAssignmentNames(courseName: string, moduleName: string) { - const filePath = path.join( - basePath, - courseName, - moduleName, - "assignments" - ); - if (!(await directoryOrFileExists(filePath))) { - console.log( - `Error loading course by name, assignments folder does not exist in ${filePath}` - ); - await fs.mkdir(filePath); - } - - const assignmentFiles = await fs.readdir(filePath); - return assignmentFiles.map((f) => f.replace(/\.md$/, "")); - }, - async getAssignment( - courseName: string, - moduleName: string, - assignmentName: string - ) { - const filePath = path.join( - basePath, - courseName, - moduleName, - "assignments", - assignmentName + ".md" - ); - const rawFile = (await fs.readFile(filePath, "utf-8")).replace( - /\r\n/g, - "\n" - ); - return localAssignmentMarkdown.parseMarkdown(rawFile); - }, - async updateOrCreateAssignment( - courseName: string, - moduleName: string, - assignmentName: string, - assignment: LocalAssignment - ) { - const folder = path.join( - basePath, - courseName, - moduleName, - "assignments", - ); - await fs.mkdir(folder, { recursive: true }); - - - const filePath = path.join( - basePath, - courseName, - moduleName, - "assignments", - assignmentName + ".md" - ); - - const assignmentMarkdown = - assignmentMarkdownSerializer.toMarkdown(assignment); - console.log(`Saving assignment ${filePath}`); - await fs.writeFile(filePath, assignmentMarkdown); - }, - }, - quizzes: { - async getQuizNames(courseName: string, moduleName: string) { - const filePath = path.join(basePath, courseName, moduleName, "quizzes"); - if (!(await directoryOrFileExists(filePath))) { - console.log( - `Error loading course by name, quiz folder does not exist in ${filePath}` - ); - await fs.mkdir(filePath); - } - - const files = await fs.readdir(filePath); - return files.map((f) => f.replace(/\.md$/, "")); - }, - async getQuiz(courseName: string, moduleName: string, quizName: string) { - const filePath = path.join( - basePath, - courseName, - moduleName, - "quizzes", - quizName + ".md" - ); - const rawFile = (await fs.readFile(filePath, "utf-8")).replace( - /\r\n/g, - "\n" - ); - return localQuizMarkdownUtils.parseMarkdown(rawFile); - }, - - async updateQuiz( - courseName: string, - moduleName: string, - quizName: string, - quiz: LocalQuiz - ) { - const folder = path.join( - basePath, - courseName, - moduleName, - "quizzes", - ); - await fs.mkdir(folder, { recursive: true }); - const filePath = path.join( - basePath, - courseName, - moduleName, - "quizzes", - quizName + ".md" - ); - - const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz); - console.log(`Saving quiz ${filePath}`); - await fs.writeFile(filePath, quizMarkdown); - }, - }, - pages: { - async getPageNames(courseName: string, moduleName: string) { - const filePath = path.join(basePath, courseName, moduleName, "pages"); - if (!(await directoryOrFileExists(filePath))) { - console.log( - `Error loading course by name, pages folder does not exist in ${filePath}` - ); - await fs.mkdir(filePath); - } - - const files = await fs.readdir(filePath); - return files.map((f) => f.replace(/\.md$/, "")); - }, - - async getPage(courseName: string, moduleName: string, pageName: string) { - const filePath = path.join( - basePath, - courseName, - moduleName, - "pages", - pageName + ".md" - ); - const rawFile = (await fs.readFile(filePath, "utf-8")).replace( - /\r\n/g, - "\n" - ); - return localPageMarkdownUtils.parseMarkdown(rawFile); - }, - async updatePage( - courseName: string, - moduleName: string, - pageName: string, - page: LocalCoursePage - ) { - const folder = path.join( - basePath, - courseName, - moduleName, - "pages", - ); - await fs.mkdir(folder, { recursive: true }); - - const filePath = path.join( - basePath, - courseName, - moduleName, - "pages", - page.name + ".md" - ); - - const pageMarkdown = localPageMarkdownUtils.toMarkdown(page); - console.log(`Saving page ${filePath}`); - await fs.writeFile(filePath, pageMarkdown); - - const pageNameIsChanged = pageName !== page.name; - if (pageNameIsChanged) { - console.log("removing old page after name change " + pageName); - const oldFilePath = path.join( - basePath, - courseName, - moduleName, - "pages", - pageName + ".md" - ); - await fs.unlink(oldFilePath); - } - }, - }, + settings, + modules, + assignments: assignmentsFileStorageService, + quizzes, + pages, async getEmptyDirectories(): Promise { if (!(await directoryOrFileExists(basePath))) { diff --git a/nextjs/src/services/fileStorage/moduleFileStorageService.ts b/nextjs/src/services/fileStorage/moduleFileStorageService.ts new file mode 100644 index 0000000..e69de29 diff --git a/nextjs/src/services/fileStorage/pageFileStorageService.ts b/nextjs/src/services/fileStorage/pageFileStorageService.ts new file mode 100644 index 0000000..e69de29 diff --git a/nextjs/src/services/fileStorage/quizFileStorageService.ts b/nextjs/src/services/fileStorage/quizFileStorageService.ts new file mode 100644 index 0000000..e69de29 diff --git a/nextjs/src/services/fileStorage/settingsFileStorageService.ts b/nextjs/src/services/fileStorage/settingsFileStorageService.ts new file mode 100644 index 0000000..e69de29 diff --git a/nextjs/src/services/fileStorage/utils/fileSystemUtils.ts b/nextjs/src/services/fileStorage/utils/fileSystemUtils.ts index 7b13925..7095c64 100644 --- a/nextjs/src/services/fileStorage/utils/fileSystemUtils.ts +++ b/nextjs/src/services/fileStorage/utils/fileSystemUtils.ts @@ -17,4 +17,9 @@ export const directoryOrFileExists = async (directoryPath: string): Promise