From 6bfe301c83c1a0df7d1da7ab7c8dcbb9cac161c9 Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 20 Aug 2025 11:45:42 -0700 Subject: [PATCH] profanity filter --- frontend/src/pages/AddRecipe.tsx | 83 ++++++++++++++++++++------- frontend/src/utils/profanityFilter.ts | 20 +++++++ 2 files changed, 81 insertions(+), 22 deletions(-) create mode 100644 frontend/src/utils/profanityFilter.ts diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index c496c9d..37bef30 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -1,11 +1,12 @@ -import React, { useState } from 'react'; +import React, { useState } from "react"; import { addRecipe } from "../services/frontendApi.js"; import { useNavigate } from "react-router-dom"; -import AddBulkIngredients from "../components/AddBulkIngredients.tsx" -import AddBulkSteps from "../components/AddBulkSteps.tsx" -import StarRating from "../components/StarRating.tsx" -import DemoModal from '../components/DemoModal.tsx' -import '../css/colorTheme.css'; +import AddBulkIngredients from "../components/AddBulkIngredients.tsx"; +import AddBulkSteps from "../components/AddBulkSteps.tsx"; +import StarRating from "../components/StarRating.tsx"; +import DemoModal from "../components/DemoModal.tsx"; +import "../css/colorTheme.css"; +import { isProfane } from "../utils/profanityFilter"; interface Step { step_number: number; @@ -28,14 +29,29 @@ function AddRecipe() { const addRecipeForm = async (event: React.FormEvent) => { event.preventDefault(); - if (process.env.NODE_ENV === 'demo') { + if ( + isProfane(recipeName) || + isProfane(recipeCuisine) || + ingredients.some((ingredient) => isProfane(ingredient)) || + steps.some((step) => isProfane(step.instruction)) || + isProfane(author) + ) { + alert("Your submission contains inappropriate language."); + return; + } + if (process.env.NODE_ENV === "demo") { setShowDemoModal(true); return; } const stepsHash = Object.fromEntries( - steps.map(step => [step.step_number, step.instruction]) + steps.map((step) => [step.step_number, step.instruction]), ); - if (recipeName && recipeCuisine && Object.keys(stepsHash).length > 0 && ingredients.length > 0) { + if ( + recipeName && + recipeCuisine && + Object.keys(stepsHash).length > 0 && + ingredients.length > 0 + ) { const recipeData = { name: recipeName, cuisine: recipeCuisine.toLowerCase(), @@ -44,13 +60,13 @@ function AddRecipe() { cook_minutes: cookMinutes, stars: stars, ingredients: ingredients, - steps: stepsHash - } - console.log(recipeData) + steps: stepsHash, + }; + console.log(recipeData); const data = await addRecipe(recipeData); setNewRecipeId(data.id); } else { - alert('missing required data') + alert("missing required data"); } }; @@ -88,7 +104,12 @@ function AddRecipe() { />
- + setPrepMinutes(parseInt(e.target.value))} /> - minutes + + minutes +
- + setCookMinutes(parseInt(e.target.value))} /> - minutes + + minutes +
- setStars(newRating)} /> + setStars(newRating)} + />
- +
{/**/} - @@ -155,6 +194,6 @@ function AddRecipe() { /> )} - ) + ); } -export default AddRecipe +export default AddRecipe; diff --git a/frontend/src/utils/profanityFilter.ts b/frontend/src/utils/profanityFilter.ts new file mode 100644 index 0000000..d670d69 --- /dev/null +++ b/frontend/src/utils/profanityFilter.ts @@ -0,0 +1,20 @@ +let profaneWords: string[] = []; + +const loadProfaneWords = async () => { + try { + const response = await fetch( + "https://raw.githubusercontent.com/web-mech/badwords/master/lib/lang.json", + ); + const data = await response.json(); + profaneWords = data.words; + } catch (error) { + console.error("Error loading profane words:", error); + } +}; + +loadProfaneWords(); + +export const isProfane = (input: string): boolean => { + const words = input.toLowerCase().split(/\s+/); + return words.some((word) => profaneWords.includes(word)); +};