added catches around markdown to html, might throw exception if image error

This commit is contained in:
2025-01-31 09:14:16 -07:00
parent 777d1e4659
commit 54e071b053
9 changed files with 77 additions and 48 deletions

View File

@@ -38,11 +38,9 @@ services:
environment: environment:
- TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN} - TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}
healthcheck: healthcheck:
test: ["CMD", "cloudflared", "--version"] test: [ "CMD", "cloudflared", "--version" ]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3
start_period: 10s start_period: 10s
# https://ngrok.com/docs/using-ngrok-with/docker/ # https://ngrok.com/docs/using-ngrok-with/docker/

2
run.sh
View File

@@ -9,9 +9,9 @@ docker run -it --rm \
-p 3000:3000 \ -p 3000:3000 \
-w /app \ -w /app \
-v .:/app \ -v .:/app \
-v ~/projects/faculty/1810/2024-fall-alex/modules:/app/storage/intro_to_web \
-v ~/projects/faculty/4850_AdvancedFE/2024-fall-alex/modules:/app/storage/advanced_frontend \ -v ~/projects/faculty/4850_AdvancedFE/2024-fall-alex/modules:/app/storage/advanced_frontend \
-v ~/projects/faculty/1810/2025-spring-alex/online:/app/storage/intro_to_web_online \ -v ~/projects/faculty/1810/2025-spring-alex/online:/app/storage/intro_to_web_online \
-v ~/projects/faculty/1810/2025-spring-alex/in-person:/app/storage/intro_to_web \
-v ~/projects/faculty/1400/2025_spring_alex/modules:/app/storage/1400 \ -v ~/projects/faculty/1400/2025_spring_alex/modules:/app/storage/1400 \
-v ~/projects/faculty/1405/2025_spring_alex:/app/storage/1405 \ -v ~/projects/faculty/1405/2025_spring_alex:/app/storage/1405 \
-v ~/projects/faculty/3840_Telemetry/2025_spring_alex/modules:/app/storage/telemetry \ -v ~/projects/faculty/3840_Telemetry/2025_spring_alex/modules:/app/storage/telemetry \

View File

@@ -104,15 +104,22 @@ export const getStatus = ({
), ),
}; };
const htmlIsSame = htmlIsCloseEnough( try {
markdownToHTMLSafe(assignment.description, settings), const htmlIsSame = htmlIsCloseEnough(
canvasAssignment.description markdownToHTMLSafe(assignment.description, settings),
); canvasAssignment.description
if (!htmlIsSame) );
if (!htmlIsSame)
return {
status: "incomplete",
message: "Canvas description is different",
};
} catch (exception) {
return { return {
status: "incomplete", status: "incomplete",
message: "Canvas description is different", message: "Error parsing markdown " + exception,
}; };
}
} }
return { status: "published", message: "" }; return { status: "published", message: "" };

View File

@@ -1,3 +1,5 @@
import MarkdownDisplay from "@/components/MarkdownDisplay";
import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { Lecture } from "@/models/local/lecture"; import { Lecture } from "@/models/local/lecture";
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils"; import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
@@ -8,15 +10,12 @@ export default function LecturePreview({ lecture }: { lecture: Lecture }) {
<> <>
<section className="border-b-slate-700 border-b-4"> <section className="border-b-slate-700 border-b-4">
<div className="text-center font-extrabold">{lecture.name}</div> <div className="text-center font-extrabold">{lecture.name}</div>
<div className="text-center font-bold text-slate-400">{lecture.date}</div> <div className="text-center font-bold text-slate-400">
{lecture.date}
</div>
</section> </section>
<section> <section>
<div <MarkdownDisplay markdown={lecture.content} />
className="markdownPreview "
dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe(lecture.content, settings),
}}
></div>
</section> </section>
</> </>
); );

View File

@@ -1,4 +1,5 @@
import ClientOnly from "@/components/ClientOnly"; import ClientOnly from "@/components/ClientOnly";
import MarkdownDisplay from "@/components/MarkdownDisplay";
import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling"; import { SuspenseAndErrorHandling } from "@/components/SuspenseAndErrorHandling";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { LocalAssignment } from "@/models/local/assignment/localAssignment"; import { LocalAssignment } from "@/models/local/assignment/localAssignment";
@@ -18,7 +19,6 @@ export default function AssignmentPreview({
(sum, cur) => (rubricItemIsExtraCredit(cur) ? sum + cur.points : sum), (sum, cur) => (rubricItemIsExtraCredit(cur) ? sum + cur.points : sum),
0 0
); );
const htmlPreview = markdownToHTMLSafe(assignment.description, settings);
return ( return (
<div className="h-full overflow-y-auto"> <div className="h-full overflow-y-auto">
<section> <section>
@@ -59,12 +59,13 @@ export default function AssignmentPreview({
<hr /> <hr />
<br /> <br />
<section> <section>
<div <MarkdownDisplay markdown={assignment.description} />
{/* <div
className="markdownPreview" className="markdownPreview"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: htmlPreview, __html: htmlPreview,
}} }}
></div> ></div> */}
</section> </section>
<hr /> <hr />
<section> <section>

View File

@@ -1,16 +1,11 @@
import MarkdownDisplay from "@/components/MarkdownDisplay";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { LocalCoursePage } from "@/models/local/page/localCoursePage"; import { LocalCoursePage } from "@/models/local/page/localCoursePage";
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils"; import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
import React from "react"; import React from "react";
export default function PagePreview({ page }: { page: LocalCoursePage }) { export default function PagePreview({ page }: { page: LocalCoursePage }) {
const [settings] = useLocalCourseSettingsQuery();
return ( return (
<div <MarkdownDisplay markdown={page.text} />
className="markdownPreview"
dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe(page.text, settings),
}}
></div>
); );
} }

View File

@@ -1,4 +1,5 @@
import CheckIcon from "@/components/icons/CheckIcon"; import CheckIcon from "@/components/icons/CheckIcon";
import MarkdownDisplay from "@/components/MarkdownDisplay";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks"; import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { useQuizQuery } from "@/hooks/localCourse/quizHooks"; import { useQuizQuery } from "@/hooks/localCourse/quizHooks";
import { import {
@@ -53,12 +54,7 @@ export default function QuizPreview({
<div className="text-end">Assignment Group Name</div> <div className="text-end">Assignment Group Name</div>
<div>{quiz.localAssignmentGroupName}</div> <div>{quiz.localAssignmentGroupName}</div>
</div> </div>
<div <MarkdownDisplay markdown={quiz.description} className="p-3" />
className="p-3 markdownPreview"
dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe(quiz.description, settings),
}}
></div>
<div className="p-3 rounded-md bg-slate-950 m-5 flex flex-col gap-3"> <div className="p-3 rounded-md bg-slate-950 m-5 flex flex-col gap-3">
{quiz.questions.map((question, i) => ( {quiz.questions.map((question, i) => (
<QuizQuestionPreview key={i} question={question} /> <QuizQuestionPreview key={i} question={question} />
@@ -88,13 +84,7 @@ function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
{question.points} {question.points === 1 ? " Point" : " Points"} {question.points} {question.points === 1 ? " Point" : " Points"}
</div> </div>
</div> </div>
<MarkdownDisplay markdown={question.text} className="ms-4 mb-2" />
<div
className="ms-4 mb-2 markdownPreview"
dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe(question.text, settings),
}}
></div>
{question.questionType === QuestionType.MATCHING && ( {question.questionType === QuestionType.MATCHING && (
<div> <div>
{question.answers.map((answer) => ( {question.answers.map((answer) => (
@@ -134,12 +124,7 @@ function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
<div></div> <div></div>
)} )}
</div> </div>
<div <MarkdownDisplay markdown={answer.text} className="markdownQuizAnswerPreview" />
className="markdownQuizAnswerPreview"
dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe(answer.text, settings),
}}
/>
</div> </div>
))} ))}
</div> </div>

View File

@@ -0,0 +1,42 @@
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { SuspenseAndErrorHandling } from "./SuspenseAndErrorHandling";
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
export default function MarkdownDisplay({
markdown,
className = "",
}: {
markdown: string;
className?: string;
}) {
const [settings] = useLocalCourseSettingsQuery();
return (
<SuspenseAndErrorHandling>
<DangerousInnerMarkdown
markdown={markdown}
settings={settings}
className={className}
/>
</SuspenseAndErrorHandling>
);
}
function DangerousInnerMarkdown({
markdown,
settings,
className,
}: {
markdown: string;
settings: LocalCourseSettings;
className: string;
}) {
return (
<div
className={"markdownPreview " + className}
dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe(markdown, settings),
}}
></div>
);
}

View File

@@ -46,10 +46,12 @@ export function convertImagesToCanvasImages(
export function markdownToHTMLSafe( export function markdownToHTMLSafe(
markdownString: string, markdownString: string,
settings: LocalCourseSettings settings: LocalCourseSettings,
convertImages: boolean = true
) { ) {
const html = markdownToHtmlNoImages(markdownString); const html = markdownToHtmlNoImages(markdownString);
return convertImagesToCanvasImages(html, settings); if (convertImages) return convertImagesToCanvasImages(html, settings);
else return html;
} }
export function markdownToHtmlNoImages(markdownString: string) { export function markdownToHtmlNoImages(markdownString: string) {