✨ Added input-validation via yup
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import * as yup from 'yup';
|
||||
import { dataSchema } from '$lib/jupSchemas';
|
||||
|
||||
const { t } = getLocalization();
|
||||
|
||||
let schemaInvalid = false;
|
||||
let yupErrorMessage = '';
|
||||
|
||||
interface Data {
|
||||
public: boolean;
|
||||
title: string;
|
||||
@@ -38,6 +43,18 @@
|
||||
]
|
||||
};
|
||||
|
||||
const validateInput = async (data: Data) => {
|
||||
try {
|
||||
await dataSchema.validate(data, { abortEarly: false });
|
||||
schemaInvalid = false;
|
||||
yupErrorMessage = '';
|
||||
} catch (err) {
|
||||
schemaInvalid = true;
|
||||
yupErrorMessage = err.errors[0];
|
||||
}
|
||||
};
|
||||
$: validateInput(data);
|
||||
|
||||
const checkIfAllQuestionImagesComplyWithRegex = (questions: Array<Question>) => {
|
||||
let NoteverythingValid = false;
|
||||
// const regex = /^https:\/\/i\.imgur\.com\/.{7}.(jpg|png|gif)$/;
|
||||
@@ -73,6 +90,9 @@
|
||||
placeholder={$t('words.title')}
|
||||
bind:value={data.title}
|
||||
class="text-black bg-inherit border-dotted border-b-2 border-black w-full"
|
||||
class:border-red-600={!yup.reach(dataSchema, 'title').isValidSync(data.title)}
|
||||
class:border-solid={!yup.reach(dataSchema, 'title').isValidSync(data.title)}
|
||||
class:border-2={!yup.reach(dataSchema, 'title').isValidSync(data.title)}
|
||||
/>
|
||||
</label>
|
||||
<label class="pl-2 flex flex-row gap-2 w-3/5">
|
||||
@@ -81,6 +101,9 @@
|
||||
placeholder={$t('words.description')}
|
||||
bind:value={data.description}
|
||||
class="text-black w-full"
|
||||
class:border-red-600={!yup.reach(dataSchema, 'description').isValidSync(data.description)}
|
||||
class:border-solid={!yup.reach(dataSchema, 'description').isValidSync(data.description)}
|
||||
class:border-2={!yup.reach(dataSchema, 'description').isValidSync(data.description)}
|
||||
/>
|
||||
</label>
|
||||
{#each data.questions as question, index_question}
|
||||
@@ -94,6 +117,15 @@
|
||||
placeholder={$t('words.question')}
|
||||
bind:value={question.question}
|
||||
class="text-black w-full bg-inherit border-dotted border-b-2 border-black"
|
||||
class:border-red-600={!yup
|
||||
.reach(dataSchema, 'questions[].question')
|
||||
.isValidSync(question.question)}
|
||||
class:border-solid={!yup
|
||||
.reach(dataSchema, 'questions[].question')
|
||||
.isValidSync(question.question)}
|
||||
class:border-2={!yup
|
||||
.reach(dataSchema, 'questions[].question')
|
||||
.isValidSync(question.question)}
|
||||
/>
|
||||
</label>
|
||||
<label class="m-1 flex flex-row gap-2 w-3/5">
|
||||
@@ -103,6 +135,15 @@
|
||||
placeholder="https://i.imgur.com/kSPCidY.png"
|
||||
bind:value={question.image}
|
||||
class="text-black w-full bg-inherit border-dotted border-b-2 border-black"
|
||||
class:border-red-600={!yup
|
||||
.reach(dataSchema, 'questions[].image')
|
||||
.isValidSync(question.image)}
|
||||
class:border-solid={!yup
|
||||
.reach(dataSchema, 'questions[].image')
|
||||
.isValidSync(question.image)}
|
||||
class:border-2={!yup
|
||||
.reach(dataSchema, 'questions[].image')
|
||||
.isValidSync(question.image)}
|
||||
/>
|
||||
</label>
|
||||
<label class="m-1 flex flex-row gap-2 w-3/5 flex-nowrap whitespace-nowrap">
|
||||
@@ -112,6 +153,15 @@
|
||||
placeholder="20"
|
||||
bind:value={question.time}
|
||||
class="text-black w-full bg-inherit border-dotted border-b-2 border-black"
|
||||
class:border-red-600={!yup
|
||||
.reach(dataSchema, 'questions[].time')
|
||||
.isValidSync(question.time)}
|
||||
class:border-solid={!yup
|
||||
.reach(dataSchema, 'questions[].time')
|
||||
.isValidSync(question.time)}
|
||||
class:border-2={!yup
|
||||
.reach(dataSchema, 'questions[].time')
|
||||
.isValidSync(question.time)}
|
||||
/>
|
||||
</label>
|
||||
{#each question.answers as answer, index_answer}
|
||||
@@ -128,6 +178,21 @@
|
||||
placeholder={$t('words.answer')}
|
||||
bind:value={data.questions[index_question].answers[index_answer].answer}
|
||||
class="text-black w-full bg-inherit border-dotted border-b-2 border-black"
|
||||
class:border-red-600={!yup
|
||||
.reach(dataSchema, 'questions[].answers[].answer')
|
||||
.isValidSync(
|
||||
data.questions[index_question].answers[index_answer].answer
|
||||
)}
|
||||
class:border-solid={!yup
|
||||
.reach(dataSchema, 'questions[].answers[].answer')
|
||||
.isValidSync(
|
||||
data.questions[index_question].answers[index_answer].answer
|
||||
)}
|
||||
class:border-2={!yup
|
||||
.reach(dataSchema, 'questions[].answers[].answer')
|
||||
.isValidSync(
|
||||
data.questions[index_question].answers[index_answer].answer
|
||||
)}
|
||||
/>
|
||||
</label>
|
||||
<label class="m-1 flex flex-row gap-2 w-2/6 flex-nowrap whitespace-nowrap">
|
||||
@@ -186,8 +251,13 @@
|
||||
{$t('editor.not_all_links_imgur_links')}
|
||||
</p>
|
||||
{/if}
|
||||
{#if schemaInvalid}
|
||||
<p class="text-blue-600 text-xl text-center w-fit mx-auto">
|
||||
{yupErrorMessage}
|
||||
</p>
|
||||
{/if}
|
||||
<button
|
||||
type="submit"
|
||||
class="text-xl disabled:cursor-not-allowed disabled:border disabled:border-red-500 w-fit mx-auto"
|
||||
disabled={imgur_links_valid}>{submit_button_text}</button
|
||||
disabled={imgur_links_valid || schemaInvalid}>{submit_button_text}</button
|
||||
>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import * as yup from 'yup';
|
||||
|
||||
export const dataSchema = yup.object({
|
||||
public: yup.boolean().required(),
|
||||
title: yup
|
||||
.string()
|
||||
.required('A title is required')
|
||||
.min(3, 'The title has to be longer than 3 characters')
|
||||
.max(300, 'The title has to be shorter than 300 characters'),
|
||||
description: yup
|
||||
.string()
|
||||
.required()
|
||||
.min(3, 'The title has to be longer than 3 characters')
|
||||
.max(300, 'The title has to be shorter than 300 characters'),
|
||||
questions: yup
|
||||
.array()
|
||||
.of(
|
||||
yup.object({
|
||||
question: yup.string().required('A question is required').max(299),
|
||||
time: yup.number().required().positive('The time has to be positive'),
|
||||
image: yup
|
||||
.string()
|
||||
.nullable()
|
||||
.matches(
|
||||
/^(http(|s):\/\/.*(|:)\d*\/api\/v1\/storage\/download\/.{36}--.{36}|https:\/\/i\.imgur\.com\/.{7}.(jpg|png|gif))$|^$/,
|
||||
"The image-url isn't valid"
|
||||
)
|
||||
.lowercase(),
|
||||
answers: yup
|
||||
.array()
|
||||
.of(
|
||||
yup.object({
|
||||
right: yup.boolean().required(),
|
||||
answer: yup.string().required('You need an answer')
|
||||
})
|
||||
)
|
||||
.min(2, 'You need at least 2 answers')
|
||||
.max(16, "You can't have more than 16 answers")
|
||||
})
|
||||
)
|
||||
.min(1, 'You need at least one question')
|
||||
.max(50, "You can't have more than 32 questions")
|
||||
});
|
||||
@@ -32,6 +32,7 @@
|
||||
import Editor from '$lib/editor.svelte';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import { navbarVisible } from '$lib/stores';
|
||||
import { dataSchema } from '$lib/jupSchemas';
|
||||
|
||||
navbarVisible.set(true);
|
||||
|
||||
@@ -76,6 +77,9 @@
|
||||
});
|
||||
|
||||
const submit = async () => {
|
||||
if (!(await dataSchema.isValid(data))) {
|
||||
return;
|
||||
}
|
||||
const res = await fetch('/api/v1/quiz/create', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
import Editor from '$lib/editor.svelte';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import { navbarVisible } from '$lib/stores';
|
||||
import { dataSchema } from '$lib/jupSchemas';
|
||||
|
||||
navbarVisible.set(true);
|
||||
|
||||
@@ -72,6 +73,9 @@
|
||||
}
|
||||
};
|
||||
const submit = async () => {
|
||||
if (!(await dataSchema.isValid(data))) {
|
||||
return;
|
||||
}
|
||||
const res = await fetch(`/api/v1/quiz/update/${quiz_id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
|
||||
Reference in New Issue
Block a user