From f15e1ad853dd153d3fbc2d13a2d5daaedb4bb363 Mon Sep 17 00:00:00 2001 From: meet Date: Wed, 3 Jul 2024 19:05:42 +0530 Subject: [PATCH 01/19] Adding captcha --- admin/src/components/CaptchaAdmin.tsx | 60 +++++++++++++++++++++++++ frontend/src/App.tsx | 4 +- frontend/src/components/CaptchaUser.tsx | 60 +++++++++++++++++++++++++ frontend/src/pages/Signin.tsx | 13 ++++-- frontend/src/pages/Signup.tsx | 10 ++++- 5 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 admin/src/components/CaptchaAdmin.tsx create mode 100644 frontend/src/components/CaptchaUser.tsx diff --git a/admin/src/components/CaptchaAdmin.tsx b/admin/src/components/CaptchaAdmin.tsx new file mode 100644 index 00000000..21a1871a --- /dev/null +++ b/admin/src/components/CaptchaAdmin.tsx @@ -0,0 +1,60 @@ +import { useState, useEffect } from 'react'; + +const CaptchaAdmin = ({ onChange }: { onChange: (isValid: boolean) => void }) => { + const [captchaText, setCaptchaText] = useState(''); + const [inputValue, setInputValue] = useState(''); + const [isCaptchaValid, setIsCaptchaValid] = useState(false); + + useEffect(() => { + generateCaptcha(); + }, []); + + const generateCaptcha = () => { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let text = ''; + for (let i = 0; i < 6; i++) { + text += chars.charAt(Math.floor(Math.random() * chars.length)); + } + setCaptchaText(text); + }; + + const handleInputChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value); + if (e.target.value === captchaText) { + setIsCaptchaValid(true); + onChange(true); + } else { + setIsCaptchaValid(false); + onChange(false); + } + }; + + return ( +
+

Captcha Generator

+
+ {captchaText} + +
+ + {!isCaptchaValid && inputValue && ( +

Captcha does not match

+ )} +
+ ); +}; + +export default CaptchaAdmin; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d6ea8e7c..d7e8a19a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -31,8 +31,8 @@ import EditPost from "./pages/EditPost"; import useTheme from './hooks/useTheme'; import CodeEditor from "./pages/CodeEditor"; import TrendingPosts from "./pages/TrendingPosts"; -// import axios from "axios"; -// axios.defaults.baseURL = "http://localhost:3001/"; +import axios from "axios"; +axios.defaults.baseURL = "http://localhost:3001/"; function App() { const { theme, toggleTheme } = useTheme(); diff --git a/frontend/src/components/CaptchaUser.tsx b/frontend/src/components/CaptchaUser.tsx new file mode 100644 index 00000000..188e05d3 --- /dev/null +++ b/frontend/src/components/CaptchaUser.tsx @@ -0,0 +1,60 @@ +import { useState, useEffect } from 'react'; +import { IoMdRefresh } from "react-icons/io"; + +const CaptchaUser = ({ onChange }: { onChange: (isValid: boolean) => void }) => { + const [captchaText, setCaptchaText] = useState(''); + const [inputValue, setInputValue] = useState(''); + const [isCaptchaValid, setIsCaptchaValid] = useState(false); + + useEffect(() => { + generateCaptcha(); + }, []); + + const generateCaptcha = () => { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let text = ''; + for (let i = 0; i < 6; i++) { + text += chars.charAt(Math.floor(Math.random() * chars.length)); + } + setCaptchaText(text); + }; + + const handleInputChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value); + if (e.target.value === captchaText) { + setIsCaptchaValid(true); + onChange(true); + } else { + setIsCaptchaValid(false); + onChange(false); + } + }; + + return ( +
+
+ {captchaText} + +
+ + {!isCaptchaValid && inputValue && ( +

Captcha does not match

+ )} +
+ ); +}; + +export default CaptchaUser; diff --git a/frontend/src/pages/Signin.tsx b/frontend/src/pages/Signin.tsx index 53f253ec..02987303 100644 --- a/frontend/src/pages/Signin.tsx +++ b/frontend/src/pages/Signin.tsx @@ -7,12 +7,14 @@ import { AiOutlineEyeInvisible, AiOutlineEye } from "react-icons/ai"; import toast from "react-hot-toast"; import { useTranslation } from "react-i18next"; import bgHero from "../assets/bgHero.png"; +import CaptchaUser from "../components/CaptchaUser"; const Signin = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const { t } = useTranslation(); + const [isCaptchaValid, setIsCaptchaValid] = useState(false); const [error, setError] = useState({ email: "", @@ -22,11 +24,16 @@ const Signin = () => { const setTokenState = useSetRecoilState(tokenState); const navigate = useNavigate(); - document.title='Style Share | Login page 👋' + document.title='Style Share | Login page 👋' const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + if (!isCaptchaValid) { + toast.error('Captcha is not valid'); + return; + } + try { const response = await axios.post("/api/v1/user/signin", { email, @@ -102,12 +109,12 @@ const Signin = () => { )} - - +

{error.password}

+ setIsCaptchaValid(isValid)} />
+
+ + {!isCaptchaValid && inputValue && ( +

Captcha does not match

+ )} + + ); +}; + +export default CaptchaAdmin; \ No newline at end of file diff --git a/admin/src/pages/Signin.tsx b/admin/src/pages/Signin.tsx index e1987e23..030abd96 100644 --- a/admin/src/pages/Signin.tsx +++ b/admin/src/pages/Signin.tsx @@ -6,12 +6,13 @@ import { tokenState } from "../store/atoms/auth"; import { AiOutlineEyeInvisible, AiOutlineEye } from "react-icons/ai"; import toast from "react-hot-toast"; import bgHero from "../assets/bgHero.png"; +import CaptchaAdmin from "../components/CaptchaAdmin"; const Signin = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); - + const [isCaptchaValid, setIsCaptchaValid] = useState(false); const [error, setError] = useState({ email: "", password: "", @@ -20,11 +21,16 @@ const Signin = () => { const setTokenState = useSetRecoilState(tokenState); const navigate = useNavigate(); - document.title='Style Share | Login page 👋' + document.title='Style Share | Login page 👋' const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + if (!isCaptchaValid) { + toast.error('Captcha is not valid'); + return; + } + try { const response = await axios.post("/api/v1/admin/login", { email, @@ -99,12 +105,12 @@ const Signin = () => { )} - - +

{error.password}

+ setIsCaptchaValid(isValid)} />
+
+ + {!isCaptchaValid && inputValue && ( +

Captcha does not match

+ )} + + ); +}; + +export default CaptchaUser; \ No newline at end of file diff --git a/frontend/src/pages/Signin.tsx b/frontend/src/pages/Signin.tsx index 53f253ec..e24069d4 100644 --- a/frontend/src/pages/Signin.tsx +++ b/frontend/src/pages/Signin.tsx @@ -7,13 +7,14 @@ import { AiOutlineEyeInvisible, AiOutlineEye } from "react-icons/ai"; import toast from "react-hot-toast"; import { useTranslation } from "react-i18next"; import bgHero from "../assets/bgHero.png"; +import CaptchaUser from "../components/CaptchaUser"; const Signin = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const { t } = useTranslation(); - + const [isCaptchaValid, setIsCaptchaValid] = useState(false); const [error, setError] = useState({ email: "", password: "", @@ -22,11 +23,16 @@ const Signin = () => { const setTokenState = useSetRecoilState(tokenState); const navigate = useNavigate(); - document.title='Style Share | Login page 👋' + document.title='Style Share | Login page 👋' const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + if (!isCaptchaValid) { + toast.error('Captcha is not valid'); + return; + } + try { const response = await axios.post("/api/v1/user/signin", { email, @@ -102,11 +108,11 @@ const Signin = () => { )} - - +

{error.password}

+ setIsCaptchaValid(isValid)} /> {/* Add Captcha component */}
{
-
+
{filterTags.map((tag, index) => (
- {tag} + {tag}
))}
)} - { - setSearchQuery(e.target.value); - }} - placeholder={t("allPosts.search")} - className="p-2 w-full max-w-xs rounded-md text-[#000435] bg-white dark:text-white dark:bg-[#000435] border border-sky-400 focus:outline-none focus:ring-2 focus:ring-blue-500" - /> - -
- {filteredPosts.map((post, index) => ( - - ))} +
+ setSearchQuery(e.target.value)} + placeholder={t("allPosts.search")} + className="p-2 w-full max-w-xs rounded-md text-[#000435] bg-white dark:text-white dark:bg-[#000435] border border-sky-400 focus:outline-none focus:ring-2 focus:ring-blue-500" + /> + +
+ {filteredPosts.length === 0 ? ( +
{t("allPosts.noPosts")}
+ ) : ( +
+ {filteredPosts.map((post) => ( + + ))} +
+ )}