From 2c0e3d49c75bf65bd651736dce6cee41ca7a72aa Mon Sep 17 00:00:00 2001 From: tanmay4l Date: Sun, 14 Jul 2024 19:34:29 +0530 Subject: [PATCH] fixed admin --- package-lock.json | 24 ++++++++++++- package.json | 1 + pages/admin/ middleware/auth.ts | 18 ++++++++++ pages/admin/index.tsx | 23 +++++++++---- pages/admin/therapist-list.tsx | 49 +++++++++++++++++++++------ pages/admin/utils/auth.ts | 18 ++++++++++ pages/api/auth/check.ts | 15 ++++++++ pages/api/auth/login.ts | 24 +++++++++++++ pages/api/therapists/[therapistId].ts | 12 +++++-- yarn.lock | 15 +++++++- 10 files changed, 178 insertions(+), 21 deletions(-) create mode 100644 pages/admin/ middleware/auth.ts create mode 100644 pages/admin/utils/auth.ts create mode 100644 pages/api/auth/check.ts create mode 100644 pages/api/auth/login.ts diff --git a/package-lock.json b/package-lock.json index 2c765a0..a4711b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "lunr": "^2.3.9", "next": "^14.2.3", "next-auth": "^4.24.7", + "next-connect": "^1.0.0", "next-sitemap": "^4.0.9", "nextjs-google-analytics": "^2.3.3", "prisma": "^5.14.0", @@ -3754,7 +3755,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true, "license": "MIT" }, "node_modules/@types/acorn": { @@ -12108,6 +12108,19 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/next-connect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-connect/-/next-connect-1.0.0.tgz", + "integrity": "sha512-FeLURm9MdvzY1SDUGE74tk66mukSqL6MAzxajW7Gqh6DZKBZLrXmXnGWtHJZXkfvoi+V/DUe9Hhtfkl4+nTlYA==", + "license": "MIT", + "dependencies": { + "@tsconfig/node16": "^1.0.3", + "regexparam": "^2.0.1" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/next-mdx-remote": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-4.4.1.tgz", @@ -13891,6 +13904,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexparam": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-2.0.2.tgz", + "integrity": "sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/registry-auth-token": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", diff --git a/package.json b/package.json index 60639e4..f047eb6 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "lunr": "^2.3.9", "next": "^14.2.3", "next-auth": "^4.24.7", + "next-connect": "^1.0.0", "next-sitemap": "^4.0.9", "nextjs-google-analytics": "^2.3.3", "prisma": "^5.14.0", diff --git a/pages/admin/ middleware/auth.ts b/pages/admin/ middleware/auth.ts new file mode 100644 index 0000000..732f346 --- /dev/null +++ b/pages/admin/ middleware/auth.ts @@ -0,0 +1,18 @@ +// middleware/auth.ts + +import { NextApiRequest, NextApiResponse } from "next"; +import { NextHandler } from "next-connect"; + +export function isAuthenticated( + req: NextApiRequest, + res: NextApiResponse, + next: NextHandler +) { + const session = req.cookies.session; + + if (session === "admin") { + next(); + } else { + res.status(401).json({ message: "Unauthorized" }); + } +} diff --git a/pages/admin/index.tsx b/pages/admin/index.tsx index 3d27f79..7d451dd 100644 --- a/pages/admin/index.tsx +++ b/pages/admin/index.tsx @@ -9,12 +9,23 @@ const LoginPage: React.FC = () => { const [password, setPassword] = useState(""); const [error, setError] = useState(""); - const handleLogin = () => { - if (username === "admin" && password === "password") { - // Replace with secure authentication - router.push("/admin/therapist-list"); - } else { - setError("Invalid username or password"); + const handleLogin = async () => { + try { + const response = await fetch("/api/auth/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username, password }), + }); + + if (response.ok) { + router.push("/admin/therapist-list"); + } else { + setError("Invalid username or password"); + } + } catch (error) { + setError("An error occurred. Please try again."); } }; diff --git a/pages/admin/therapist-list.tsx b/pages/admin/therapist-list.tsx index 7f67547..f12d3a0 100644 --- a/pages/admin/therapist-list.tsx +++ b/pages/admin/therapist-list.tsx @@ -1,6 +1,9 @@ +/* eslint-disable react-hooks/exhaustive-deps */ // pages/admin/therapist-list.tsx import { useEffect, useState } from "react"; +import { useRouter } from "next/router"; +import { useAuth } from "./utils/auth"; interface Therapist { id: string; @@ -24,12 +27,18 @@ interface Therapist { } const AdminTherapistListPage: React.FC = () => { + useAuth(); + const router = useRouter(); const [therapistsList, setTherapistsList] = useState([]); const [error, setError] = useState(null); const fetchTherapists = async () => { try { const response = await fetch("/api/therapists"); + if (response.status === 401) { + router.push("/admin"); // Redirect to login if unauthorized + return; + } if (!response.ok) { throw new Error("Failed to fetch therapists"); } @@ -46,20 +55,24 @@ const AdminTherapistListPage: React.FC = () => { fetchTherapists(); }, []); - const toggleVerificationStatus = async (therapistId: string) => { + const updateVerificationStatus = async ( + therapistId: string, + isVerified: boolean + ) => { try { const response = await fetch(`/api/therapists/${therapistId}`, { method: "PATCH", headers: { "Content-Type": "application/json", }, + body: JSON.stringify({ isVerified }), }); if (!response.ok) { - throw new Error("Failed to toggle verification status"); + throw new Error("Failed to update verification status"); } - await fetchTherapists(); // Refresh therapists list after toggle + await fetchTherapists(); // Refresh therapists list after update } catch (error) { - console.error("Error toggling verification status:", error); + console.error("Error updating verification status:", error); setError("Failed to update therapist status. Please try again."); } }; @@ -91,12 +104,28 @@ const AdminTherapistListPage: React.FC = () => { {therapist.isVerified ? "Yes" : "No"}

- +
+ + +
))} diff --git a/pages/admin/utils/auth.ts b/pages/admin/utils/auth.ts new file mode 100644 index 0000000..f8f76ac --- /dev/null +++ b/pages/admin/utils/auth.ts @@ -0,0 +1,18 @@ +// utils/auth.ts + +import { useEffect } from "react"; +import { useRouter } from "next/router"; + +export function useAuth() { + const router = useRouter(); + + useEffect(() => { + async function checkAuth() { + const response = await fetch("/api/auth/check"); + if (!response.ok) { + router.push("/admin"); + } + } + checkAuth(); + }, [router]); +} diff --git a/pages/api/auth/check.ts b/pages/api/auth/check.ts new file mode 100644 index 0000000..ac51c31 --- /dev/null +++ b/pages/api/auth/check.ts @@ -0,0 +1,15 @@ +// pages/api/auth/check.ts + +import { NextApiRequest, NextApiResponse } from "next"; +import { isAuthenticated } from "../../admin/ middleware/auth"; + +export default function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method === "GET") { + isAuthenticated(req, res, () => { + res.status(200).json({ message: "Authenticated" }); + }); + } else { + res.setHeader("Allow", ["GET"]); + res.status(405).end(`Method ${req.method} Not Allowed`); + } +} diff --git a/pages/api/auth/login.ts b/pages/api/auth/login.ts new file mode 100644 index 0000000..3642916 --- /dev/null +++ b/pages/api/auth/login.ts @@ -0,0 +1,24 @@ +// pages/api/auth/login.ts + +import { NextApiRequest, NextApiResponse } from "next"; + +export default function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method === "POST") { + const { username, password } = req.body; + + // In a real application, you would check these credentials against a database + if (username === "admin" && password === "securepassword") { + // Set a secure, HTTP-only cookie + res.setHeader( + "Set-Cookie", + "session=admin; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600" + ); + res.status(200).json({ message: "Logged in successfully" }); + } else { + res.status(401).json({ message: "Invalid credentials" }); + } + } else { + res.setHeader("Allow", ["POST"]); + res.status(405).end(`Method ${req.method} Not Allowed`); + } +} diff --git a/pages/api/therapists/[therapistId].ts b/pages/api/therapists/[therapistId].ts index 546c484..cb98e40 100644 --- a/pages/api/therapists/[therapistId].ts +++ b/pages/api/therapists/[therapistId].ts @@ -10,11 +10,17 @@ export default async function handler( const { therapistId } = req.query; if (req.method === "PATCH") { + const { isVerified } = req.body; + + if (typeof isVerified !== "boolean") { + return res.status(400).json({ error: "Invalid isVerified value" }); + } + try { const updatedTherapist = await prisma.therapist.update({ where: { id: String(therapistId) }, data: { - isVerified: true, + isVerified: isVerified, }, include: { specializations: true, @@ -22,8 +28,8 @@ export default async function handler( }); res.status(200).json(updatedTherapist); } catch (error) { - console.error("Error toggling verification status:", error); - res.status(500).json({ error: "Failed to toggle verification status" }); + console.error("Error updating verification status:", error); + res.status(500).json({ error: "Failed to update verification status" }); } } else { res.setHeader("Allow", ["PATCH"]); diff --git a/yarn.lock b/yarn.lock index 2690ee2..1e80eb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2158,7 +2158,7 @@ resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== -"@tsconfig/node16@^1.0.2": +"@tsconfig/node16@^1.0.2", "@tsconfig/node16@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== @@ -7311,6 +7311,14 @@ next-auth@^4.24.7: preact-render-to-string "^5.1.19" uuid "^8.3.2" +next-connect@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/next-connect/-/next-connect-1.0.0.tgz" + integrity sha512-FeLURm9MdvzY1SDUGE74tk66mukSqL6MAzxajW7Gqh6DZKBZLrXmXnGWtHJZXkfvoi+V/DUe9Hhtfkl4+nTlYA== + dependencies: + "@tsconfig/node16" "^1.0.3" + regexparam "^2.0.1" + next-mdx-remote@^4.4.1: version "4.4.1" resolved "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-4.4.1.tgz" @@ -8371,6 +8379,11 @@ regexp.prototype.flags@^1.4.3: define-properties "^1.1.3" functions-have-names "^1.2.2" +regexparam@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/regexparam/-/regexparam-2.0.2.tgz" + integrity sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w== + registry-auth-token@^4.0.0: version "4.2.2" resolved "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz"