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"