diff --git a/nextjs/src/app/course/[courseName]/loading.tsx b/nextjs/src/app/loading.tsx
similarity index 100%
rename from nextjs/src/app/course/[courseName]/loading.tsx
rename to nextjs/src/app/loading.tsx
diff --git a/nextjs/src/hooks/hookHydration.ts b/nextjs/src/hooks/hookHydration.ts
index 2e088ba..a6601b8 100644
--- a/nextjs/src/hooks/hookHydration.ts
+++ b/nextjs/src/hooks/hookHydration.ts
@@ -5,6 +5,6 @@ import { fileStorageService } from "@/services/fileStorage/fileStorageService";
export const hydrateCourses = async (queryClient: QueryClient) => {
await queryClient.prefetchQuery({
queryKey: localCourseKeys.allCourses,
- queryFn: async () => await fileStorageService.loadSavedCourses(),
+ queryFn: async () => await fileStorageService.getCourseNames(),
});
};
diff --git a/nextjs/src/hooks/localCoursesHooks.ts b/nextjs/src/hooks/localCoursesHooks.ts
index 1e66276..772ebe0 100644
--- a/nextjs/src/hooks/localCoursesHooks.ts
+++ b/nextjs/src/hooks/localCoursesHooks.ts
@@ -1,4 +1,4 @@
-import { LocalCourse } from "@/models/local/localCourse";
+import { LocalCourse, LocalCourseSettings } from "@/models/local/localCourse";
import {
useMutation,
useQueryClient,
@@ -8,30 +8,29 @@ import axios from "axios";
export const localCourseKeys = {
allCourses: ["all courses"] as const,
- courseDetail: (courseName: string) => ["course details", courseName] as const,
+ courseSettings: (courseName: string) =>
+ ["course details", courseName, "settings"] as const,
};
export const useLocalCourseNamesQuery = () =>
useSuspenseQuery({
queryKey: localCourseKeys.allCourses,
- queryFn: async (): Promise => {
+ queryFn: async (): Promise => {
const url = `/api/courses`;
const response = await axios.get(url);
return response.data;
},
- select: (courses) => courses.map((c) => c.settings.name),
});
-export const useLocalCourseDetailsQuery = (courseName: string) => {
- return useSuspenseQuery({
- queryKey: localCourseKeys.courseDetail(courseName),
- queryFn: async (): Promise => {
- const url = `/api/courses/${courseName}`;
+export const useLocalCourseSettingsQuery = (courseName: string) =>
+ useSuspenseQuery({
+ queryKey: localCourseKeys.courseSettings(courseName),
+ queryFn: async (): Promise => {
+ const url = `/api/courses/${courseName}/settings`;
const response = await axios.get(url);
return response.data;
},
});
-};
export const useUpdateCourseMutation = (courseName: string) => {
const queryClient = useQueryClient();
@@ -45,7 +44,7 @@ export const useUpdateCourseMutation = (courseName: string) => {
},
onSuccess: () => {
queryClient.invalidateQueries({
- queryKey: localCourseKeys.courseDetail(courseName),
+ queryKey: localCourseKeys.courseSettings(courseName),
});
},
scope: {
diff --git a/nextjs/src/services/fileStorage/fileStorageService.ts b/nextjs/src/services/fileStorage/fileStorageService.ts
index 3083c2a..6ee284a 100644
--- a/nextjs/src/services/fileStorage/fileStorageService.ts
+++ b/nextjs/src/services/fileStorage/fileStorageService.ts
@@ -1,27 +1,70 @@
import { promises as fs } from "fs";
import path from "path";
-import { LocalCourse } from "@/models/local/localCourse";
-import { courseMarkdownLoader } from "./utils/couresMarkdownLoader";
+import {
+ LocalCourse,
+ LocalCourseSettings,
+ localCourseYamlUtils,
+} from "@/models/local/localCourse";
+import { courseMarkdownLoader } from "./utils/courseMarkdownLoader";
import { courseMarkdownSaver } from "./utils/courseMarkdownSaver";
+import {
+ directoryOrFileExists,
+ hasFileSystemEntries,
+} from "./utils/fileSystemUtils";
const basePath = process.env.STORAGE_DIRECTORY ?? "./storage";
console.log("base path", basePath);
export const fileStorageService = {
- async saveCourseAsync(
- course: LocalCourse,
- previouslyStoredCourse?: LocalCourse
- ) {
- await courseMarkdownSaver.save(course, previouslyStoredCourse);
- },
+ // async saveCourseAsync(
+ // course: LocalCourse,
+ // previouslyStoredCourse?: LocalCourse
+ // ) {
+ // await courseMarkdownSaver.save(course, previouslyStoredCourse);
+ // },
- async loadSavedCourses(): Promise {
- console.log("loading pages from file system");
- return (await courseMarkdownLoader.loadSavedCourses()) || [];
+ // async loadSavedCourses(): Promise {
+ // console.log("loading pages from file system");
+ // return (await courseMarkdownLoader.loadSavedCourses()) || [];
+ // },
+
+ async getCourseNames() {
+ console.log("loading course ids");
+ const courseDirectories = await fs.readdir(basePath, {
+ withFileTypes: true,
+ });
+ const coursePromises = courseDirectories
+ .filter((dirent) => dirent.isDirectory())
+ .filter(async (dirent) => {
+ const coursePath = path.join(basePath, dirent.name);
+ const settingsPath = path.join(coursePath, "settings.yml");
+ return await directoryOrFileExists(settingsPath);
+ });
+ const courseNamesFromDirectories = (await Promise.all(coursePromises)).map(
+ (c) => c.name
+ );
+
+ return courseNamesFromDirectories;
+ },
+
+ 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 getEmptyDirectories(): Promise {
- if (!(await this.directoryExists(basePath))) {
+ if (!(await directoryOrFileExists(basePath))) {
throw new Error(
`Cannot get empty directories, ${basePath} does not exist`
);
@@ -31,26 +74,8 @@ export const fileStorageService = {
const emptyDirectories = directories
.filter((dirent) => dirent.isDirectory())
.map((dirent) => path.join(basePath, dirent.name))
- .filter(async (dir) => !(await this.hasFileSystemEntries(dir)));
+ .filter(async (dir) => !(await hasFileSystemEntries(dir)));
return emptyDirectories;
},
-
- async directoryExists(directoryPath: string): Promise {
- try {
- await fs.access(directoryPath);
- return true;
- } catch {
- return false;
- }
- },
-
- async hasFileSystemEntries(directoryPath: string): Promise {
- try {
- const entries = await fs.readdir(directoryPath);
- return entries.length > 0;
- } catch {
- return false;
- }
- },
};
diff --git a/nextjs/src/services/fileStorage/utils/couresMarkdownLoader.ts b/nextjs/src/services/fileStorage/utils/courseMarkdownLoader.ts
similarity index 69%
rename from nextjs/src/services/fileStorage/utils/couresMarkdownLoader.ts
rename to nextjs/src/services/fileStorage/utils/courseMarkdownLoader.ts
index f8b01d0..a073a21 100644
--- a/nextjs/src/services/fileStorage/utils/couresMarkdownLoader.ts
+++ b/nextjs/src/services/fileStorage/utils/courseMarkdownLoader.ts
@@ -18,54 +18,55 @@ import {
} from "@/models/local/quiz/localQuiz";
import { promises as fs } from "fs";
import path from "path";
+import { directoryOrFileExists } from "./fileSystemUtils";
const basePath = process.env.STORAGE_DIRECTORY ?? "./storage";
export const courseMarkdownLoader = {
- async loadSavedCourses(): Promise {
- const courseDirectories = await fs.readdir(basePath, {
- withFileTypes: true,
- });
- const coursePromises = courseDirectories
- .filter((dirent) => dirent.isDirectory())
- .map(async (dirent) => {
- const coursePath = path.join(basePath, dirent.name);
- const settingsPath = path.join(coursePath, "settings.yml");
- if (await this.fileExists(settingsPath)) {
- return this.loadCourseByPath(coursePath);
- }
- return null;
- });
+ // async loadSavedCourses(): Promise {
+ // const courseDirectories = await fs.readdir(basePath, {
+ // withFileTypes: true,
+ // });
+ // const coursePromises = courseDirectories
+ // .filter((dirent) => dirent.isDirectory())
+ // .map(async (dirent) => {
+ // const coursePath = path.join(basePath, dirent.name);
+ // const settingsPath = path.join(coursePath, "settings.yml");
+ // if (await directoryOrFileExists(settingsPath)) {
+ // return this.loadCourseByPath(coursePath);
+ // }
+ // return null;
+ // });
- const courses = (await Promise.all(coursePromises)).filter(
- (course) => course !== null
- ) as LocalCourse[];
- return courses.sort((a, b) =>
- a.settings.name.localeCompare(b.settings.name)
- );
- },
+ // const courses = (await Promise.all(coursePromises)).filter(
+ // (course) => course !== null
+ // ) as LocalCourse[];
+ // return courses.sort((a, b) =>
+ // a.settings.name.localeCompare(b.settings.name)
+ // );
+ // },
- async loadCourseByPath(courseDirectory: string): Promise {
- if (!(await this.directoryExists(courseDirectory))) {
- const errorMessage = `Error loading course by name, could not find folder ${courseDirectory}`;
- console.log(errorMessage);
- throw new Error(errorMessage);
- }
+ // async loadCourseByPath(courseDirectory: string): Promise {
+ // if (!(await directoryOrFileExists(courseDirectory))) {
+ // const errorMessage = `Error loading course by name, could not find folder ${courseDirectory}`;
+ // console.log(errorMessage);
+ // throw new Error(errorMessage);
+ // }
- const settings = await this.loadCourseSettings(courseDirectory);
- const modules = await this.loadCourseModules(courseDirectory);
+ // const settings = await this.loadCourseSettings(courseDirectory);
+ // const modules = await this.loadCourseModules(courseDirectory);
- return {
- settings,
- modules,
- };
- },
+ // return {
+ // settings,
+ // modules,
+ // };
+ // },
async loadCourseSettings(
courseDirectory: string
): Promise {
const settingsPath = path.join(courseDirectory, "settings.yml");
- if (!(await this.fileExists(settingsPath))) {
+ if (!(await directoryOrFileExists(settingsPath))) {
const errorMessage = `Error loading course by name, settings file ${settingsPath}`;
console.log(errorMessage);
throw new Error(errorMessage);
@@ -110,7 +111,7 @@ export const courseMarkdownLoader = {
modulePath: string
): Promise {
const assignmentsPath = path.join(modulePath, "assignments");
- if (!(await this.directoryExists(assignmentsPath))) {
+ if (!(await directoryOrFileExists(assignmentsPath))) {
console.log(
`Error loading course by name, assignments folder does not exist in ${modulePath}`
);
@@ -132,7 +133,7 @@ export const courseMarkdownLoader = {
async loadQuizzesFromPath(modulePath: string): Promise {
const quizzesPath = path.join(modulePath, "quizzes");
- if (!(await this.directoryExists(quizzesPath))) {
+ if (!(await directoryOrFileExists(quizzesPath))) {
console.log(
`Quizzes folder does not exist in ${modulePath}, creating now`
);
@@ -156,7 +157,7 @@ export const courseMarkdownLoader = {
modulePath: string
): Promise {
const pagesPath = path.join(modulePath, "pages");
- if (!(await this.directoryExists(pagesPath))) {
+ if (!(await directoryOrFileExists(pagesPath))) {
console.log(`Pages folder does not exist in ${modulePath}, creating now`);
await fs.mkdir(pagesPath);
}
@@ -173,22 +174,4 @@ export const courseMarkdownLoader = {
return await Promise.all(pagePromises);
},
-
- async fileExists(filePath: string): Promise {
- try {
- await fs.access(filePath);
- return true;
- } catch {
- return false;
- }
- },
-
- async directoryExists(directoryPath: string): Promise {
- try {
- await fs.access(directoryPath);
- return true;
- } catch {
- return false;
- }
- },
};
diff --git a/nextjs/src/services/fileStorage/utils/fileSystemUtils.ts b/nextjs/src/services/fileStorage/utils/fileSystemUtils.ts
new file mode 100644
index 0000000..7b13925
--- /dev/null
+++ b/nextjs/src/services/fileStorage/utils/fileSystemUtils.ts
@@ -0,0 +1,20 @@
+import { promises as fs } from "fs";
+
+export const hasFileSystemEntries = async (
+ directoryPath: string
+): Promise => {
+ try {
+ const entries = await fs.readdir(directoryPath);
+ return entries.length > 0;
+ } catch {
+ return false;
+ }
+};
+export const directoryOrFileExists = async (directoryPath: string): Promise => {
+ try {
+ await fs.access(directoryPath);
+ return true;
+ } catch {
+ return false;
+ }
+};
\ No newline at end of file