mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
adding file name validation
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
|||||||
getDateFromStringOrThrow,
|
getDateFromStringOrThrow,
|
||||||
} from "@/features/local/utils/timeUtils";
|
} from "@/features/local/utils/timeUtils";
|
||||||
import { useCreateAssignmentMutation } from "@/features/local/assignments/assignmentHooks";
|
import { useCreateAssignmentMutation } from "@/features/local/assignments/assignmentHooks";
|
||||||
|
import { validateFileName } from "@/services/fileNameValidation";
|
||||||
|
|
||||||
export default function NewItemForm({
|
export default function NewItemForm({
|
||||||
moduleName: defaultModuleName,
|
moduleName: defaultModuleName,
|
||||||
@@ -41,20 +42,6 @@ export default function NewItemForm({
|
|||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [nameError, setNameError] = useState("");
|
const [nameError, setNameError] = useState("");
|
||||||
|
|
||||||
const validateFileName = (fileName: string): string => {
|
|
||||||
// Check for invalid file system characters
|
|
||||||
const invalidChars = [":", "/", "\\", "*", '"', "<", ">", "|"];
|
|
||||||
|
|
||||||
for (const char of fileName) {
|
|
||||||
if (invalidChars.includes(char)) {
|
|
||||||
return `Name contains invalid character: "${char}". Please avoid: ${invalidChars.join(
|
|
||||||
" "
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNameChange = (newName: string) => {
|
const handleNameChange = (newName: string) => {
|
||||||
setName(newName);
|
setName(newName);
|
||||||
const error = validateFileName(newName);
|
const error = validateFileName(newName);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorage
|
|||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs";
|
||||||
import { courseItemFileStorageService } from "../course/courseItemFileStorageService";
|
import { courseItemFileStorageService } from "../course/courseItemFileStorageService";
|
||||||
import { assignmentMarkdownSerializer } from "./models/utils/assignmentMarkdownSerializer";
|
import { assignmentMarkdownSerializer } from "./models/utils/assignmentMarkdownSerializer";
|
||||||
|
import { assertValidFileName } from "@/services/fileNameValidation";
|
||||||
|
|
||||||
export const assignmentRouter = router({
|
export const assignmentRouter = router({
|
||||||
getAssignment: publicProcedure
|
getAssignment: publicProcedure
|
||||||
@@ -133,16 +134,7 @@ export async function updateOrCreateAssignmentFile({
|
|||||||
assignmentName: string;
|
assignmentName: string;
|
||||||
assignment: LocalAssignment;
|
assignment: LocalAssignment;
|
||||||
}) {
|
}) {
|
||||||
const illegalCharacters = ["<", ">", ":", '"', "/", "\\", "|", "?", "*"];
|
assertValidFileName(assignmentName);
|
||||||
const foundIllegalCharacters = illegalCharacters.filter((char) =>
|
|
||||||
assignmentName.includes(char)
|
|
||||||
);
|
|
||||||
if (foundIllegalCharacters.length > 0) {
|
|
||||||
throw new Error(
|
|
||||||
`"${assignmentName}" cannot contain the following characters: ${foundIllegalCharacters.join(
|
|
||||||
" "
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const courseDirectory = await getCoursePathByName(courseName);
|
const courseDirectory = await getCoursePathByName(courseName);
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import publicProcedure from "../../../services/serverFunctions/publicProcedure";
|
import publicProcedure from "../../../services/serverFunctions/publicProcedure";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { router } from "../../../services/serverFunctions/trpcSetup";
|
import { router } from "../../../services/serverFunctions/trpcSetup";
|
||||||
import { LocalCoursePage, localPageMarkdownUtils, zodLocalCoursePage } from "@/features/local/pages/localCoursePageModels";
|
import {
|
||||||
|
LocalCoursePage,
|
||||||
|
localPageMarkdownUtils,
|
||||||
|
zodLocalCoursePage,
|
||||||
|
} from "@/features/local/pages/localCoursePageModels";
|
||||||
import { courseItemFileStorageService } from "../course/courseItemFileStorageService";
|
import { courseItemFileStorageService } from "../course/courseItemFileStorageService";
|
||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
||||||
|
import { assertValidFileName } from "@/services/fileNameValidation";
|
||||||
|
|
||||||
export const pageRouter = router({
|
export const pageRouter = router({
|
||||||
getPage: publicProcedure
|
getPage: publicProcedure
|
||||||
@@ -125,6 +130,7 @@ export async function updatePageFile({
|
|||||||
pageName: string;
|
pageName: string;
|
||||||
page: LocalCoursePage;
|
page: LocalCoursePage;
|
||||||
}) {
|
}) {
|
||||||
|
assertValidFileName(pageName);
|
||||||
const courseDirectory = await getCoursePathByName(courseName);
|
const courseDirectory = await getCoursePathByName(courseName);
|
||||||
const folder = path.join(courseDirectory, moduleName, "pages");
|
const folder = path.join(courseDirectory, moduleName, "pages");
|
||||||
await fs.mkdir(folder, { recursive: true });
|
await fs.mkdir(folder, { recursive: true });
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import path from "path";
|
|||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs";
|
||||||
import { quizMarkdownUtils } from "./models/utils/quizMarkdownUtils";
|
import { quizMarkdownUtils } from "./models/utils/quizMarkdownUtils";
|
||||||
import { courseItemFileStorageService } from "../course/courseItemFileStorageService";
|
import { courseItemFileStorageService } from "../course/courseItemFileStorageService";
|
||||||
|
import { assertValidFileName } from "@/services/fileNameValidation";
|
||||||
|
|
||||||
export const quizRouter = router({
|
export const quizRouter = router({
|
||||||
getQuiz: publicProcedure
|
getQuiz: publicProcedure
|
||||||
@@ -149,6 +150,7 @@ export async function updateQuizFile({
|
|||||||
quizName: string;
|
quizName: string;
|
||||||
quiz: LocalQuiz;
|
quiz: LocalQuiz;
|
||||||
}) {
|
}) {
|
||||||
|
assertValidFileName(quizName);
|
||||||
const courseDirectory = await getCoursePathByName(courseName);
|
const courseDirectory = await getCoursePathByName(courseName);
|
||||||
const folder = path.join(courseDirectory, moduleName, "quizzes");
|
const folder = path.join(courseDirectory, moduleName, "quizzes");
|
||||||
await fs.mkdir(folder, { recursive: true });
|
await fs.mkdir(folder, { recursive: true });
|
||||||
|
|||||||
28
src/services/fileNameValidation.ts
Normal file
28
src/services/fileNameValidation.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
export function validateFileName(fileName: string): string {
|
||||||
|
if (!fileName || fileName.trim() === "") {
|
||||||
|
return "Name cannot be empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
const invalidChars = [":", "/", "\\", "*", "?", '"', "<", ">", "|"];
|
||||||
|
|
||||||
|
for (const char of fileName) {
|
||||||
|
if (invalidChars.includes(char)) {
|
||||||
|
return `Name contains invalid character: "${char}". Please avoid: ${invalidChars.join(
|
||||||
|
" "
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName !== fileName.trimEnd()) {
|
||||||
|
return "Name cannot end with whitespace";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertValidFileName(fileName: string): void {
|
||||||
|
const error = validateFileName(fileName);
|
||||||
|
if (error) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user