diff --git a/middleware.ts b/src/Middleware/AuthMiddleware.ts similarity index 57% rename from middleware.ts rename to src/Middleware/AuthMiddleware.ts index 421fb49..6eafe08 100644 --- a/middleware.ts +++ b/src/Middleware/AuthMiddleware.ts @@ -1,34 +1,31 @@ import { NextResponse } from "next/server"; -import type { NextRequest } from "next/server"; import jwt from "jsonwebtoken"; import User from "@/models/user"; import { mongoDB } from "@/lib/MongoDB"; -export async function middleware(req: NextRequest) { +export async function AuthMiddleware(req: any) { try { - const token : any = req.cookies.get('accessToken') || req.headers.get('Authorization')?.replace('Bearer ', ''); + const token = req.cookies.get('accessToken') || req.headers.get('Authorization')?.replace('Bearer ', ''); - if (!token) { + if (!token || token === undefined) { return NextResponse.json({ error: 'Unauthorized Access!' }, { status: 401 }); } - const decodedToken : any = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET as string); + const decodedToken:any = jwt.verify(token, process.env.NEXTAUTH_SECRET as string); await mongoDB(); const user = await User.findById(decodedToken._id).select('-password -refreshToken'); - if (!user) return NextResponse.json({ error: 'Unauthorized Access!' }, { status: 401 }); + if (!user) { + return NextResponse.json({ error: 'Unauthorized Access!' }, { status: 401 }); + } // Pass user data in request headers for further processing in API route req.headers.set('user', JSON.stringify(user)); - + return NextResponse.next(); } catch (err) { console.log(err); return NextResponse.json({ message: 'Error occurred!' }, { status: 500 }); } } - -export const config = { - matcher: ["/dashboard"], -}; diff --git a/src/app/api/files/create/route.ts b/src/app/api/files/create/route.ts new file mode 100644 index 0000000..2dc1e39 --- /dev/null +++ b/src/app/api/files/create/route.ts @@ -0,0 +1,35 @@ +import { mongoDB } from "@/lib/MongoDB"; +import { AuthMiddleware } from "@/Middleware/AuthMiddleware"; +import FileModel from "@/models/file"; +import { ApiUser } from "@/types/types"; +import { NextResponse } from "next/server"; + +export const POST = async (req: Request) => { + + const result = await AuthMiddleware(req); + + if (result instanceof NextResponse) { + + try { + const { fileName, filePrivate } = await req.json(); + + await mongoDB(); + + const user: ApiUser = JSON.parse(req.headers.get("user") || "{}"); + + const file = await FileModel.create({ + fileName, + filePrivate, + createdBy:user._id, + readBy:[user._id], + writtenBy:[user._id] + }); + + return NextResponse.json({ status: 200 }); + } catch (err) { + return NextResponse.json(`Err : ${err}`, {status:500}); + } + } else { + return result; + } +}; diff --git a/src/app/api/files/private/route.ts b/src/app/api/files/private/route.ts index d114b20..39f3a42 100644 --- a/src/app/api/files/private/route.ts +++ b/src/app/api/files/private/route.ts @@ -22,4 +22,4 @@ export const PUT = async(req: Request) => { } catch (err) { console.log(err) } -} +} \ No newline at end of file diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts new file mode 100644 index 0000000..99a27af --- /dev/null +++ b/src/app/api/health/route.ts @@ -0,0 +1,19 @@ +import { AuthMiddleware } from "@/Middleware/AuthMiddleware"; +import { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; + +export const GET = async (req: NextRequest, res: NextResponse) => { + try { + const result = await AuthMiddleware(req); + // If middleware returns NextResponse.next(), proceed with API logic + if (result.status==200) { + return NextResponse.json({ status: "UP" }, { status: 200 }); + } else { + // Handle any errors from the middleware + return result; + } + } catch (error) { + console.error("Error in health endpoint:", error); + return NextResponse.json({ message: 'Error occurred!' }, { status: 500 }); + } +}; \ No newline at end of file diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx index 6d740c5..1baf6a7 100644 --- a/src/app/dashboard/layout.tsx +++ b/src/app/dashboard/layout.tsx @@ -13,14 +13,14 @@ import { useMutation } from "convex/react"; import Image from "next/image"; import { SessionProvider, useSession } from "next-auth/react"; import Link from "next/link"; -import { logIn } from "../Redux/Auth/auth-slice"; + function DashboardLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { - const {data:session,status} = useSession(); + const { data: session, status } = useSession(); const createTeam = useMutation(api.teams.createTeam); const convex = useConvex(); const [fileList_, setFileList_] = useState(); @@ -30,22 +30,6 @@ function DashboardLayout({ const [hasCheckedTeam, setHasCheckedTeam] = useState(false); const dispatch = useDispatch(); - // useEffect(() => { - // if (session) { - // dispatch( - // logIn({ - // id: session.user.id, - // accessToken: session.user.accessToken, - // refreshToken: session.user.refreshToken, - // email: session.user.email, - // firstName: session.user.firstName, - // lastName: session.user.lastName, - // image:session.user.image - // }) - // ); - // } - // }, [session]); - useEffect(() => { if (session && !hasCheckedTeam) { checkTeam(); @@ -88,40 +72,38 @@ function DashboardLayout({ />
-

Welcome!

+

+ Welcome! +

To access this page, please{" "}

Don't have an account? - - Signup - + Signup

); - return (!loading || session === undefined) ? ( + return !loading || session === undefined ? (
- -
-
- + +
+
+ +
+
{children}
-
{children}
-
- +
) : ( diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 63ace03..b4df977 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -15,6 +15,11 @@ import Link from "next/link"; import InviteModal from "@/components/shared/InviteModal"; import JoinTeamModal from "@/components/shared/JoinTeamModal"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import axiosProtectedInstance from "@/config/AxiosProtectedRoute"; +import { checkHealthUrl } from "@/lib/API-URLs"; +import createAxiosInstance from "@/config/AxiosProtectedRoute"; +import { useRouter } from "next/navigation"; +import { logOut } from "../Redux/Auth/auth-slice"; export interface FILE { archive: boolean; @@ -39,8 +44,22 @@ function Dashboard() { const dispatch = useDispatch(); const [userData, setUserdata] = useState(); const user = useSelector((state:RootState) => state.auth.user); + const router = useRouter() - + const accessToken = useSelector((state:RootState)=>state.auth.user.accessToken); + + const axiosInstance = createAxiosInstance(accessToken); + + useEffect(() => { + const checkHealth = async () => { + const res = await axiosInstance.get(checkHealthUrl); + if(res.status !== 200) { + router.push('/signin'); + dispatch(logOut()) + }; + }; + checkHealth(); + }, []); const checkUser = async () => { const result = await convex.query(api.user.getUser, { email: user?.email }); diff --git a/src/config/AxiosInstance.ts b/src/config/AxiosInstance.ts index 7a202a4..417e4c7 100644 --- a/src/config/AxiosInstance.ts +++ b/src/config/AxiosInstance.ts @@ -5,6 +5,7 @@ const axiosInstance = axios.create({ timeout: 10000, // Request timeout headers: { 'Content-Type': 'application/json', + }, }); diff --git a/src/config/AxiosProtectedRoute.ts b/src/config/AxiosProtectedRoute.ts new file mode 100644 index 0000000..68f2b4a --- /dev/null +++ b/src/config/AxiosProtectedRoute.ts @@ -0,0 +1,33 @@ +import axios, { AxiosInstance } from 'axios'; + +const createAxiosInstance = (accessToken?: string): AxiosInstance => { + if (accessToken === undefined) { + throw new Error('Access token is required to create an authenticated Axios instance.'); + } + + const instance = axios.create({ + baseURL: process.env.NEXT_PUBLIC_CLIENT_URL, // Replace with your API base URL + timeout: 10000, // Request timeout + headers: { + 'Content-Type': 'application/json', + // Optionally add other headers + }, + }); + + // Request interceptor to include Bearer token if accessToken is provided + instance.interceptors.request.use( + (config) => { + if (accessToken) { + config.headers.Authorization = `Bearer ${accessToken}`; + } + return config; + }, + (error) => { + return Promise.reject(error); + } + ); + + return instance; +}; + +export default createAxiosInstance; diff --git a/src/lib/API-URLs.ts b/src/lib/API-URLs.ts index d6c108c..ecb034f 100644 --- a/src/lib/API-URLs.ts +++ b/src/lib/API-URLs.ts @@ -4,4 +4,5 @@ export const getTeamMembersData = "/api/teams/members"; export const deleteTeamMemberUrl = "/api/teams/members"; export const updateReadAccessUrl = "/api/files/read"; export const updateWriteAccessUrl = "/api/files/write"; -export const registerUserUrl = "/api/auth/register"; \ No newline at end of file +export const registerUserUrl = "/api/auth/register"; +export const checkHealthUrl = "/api/health"; \ No newline at end of file diff --git a/src/models/file.ts b/src/models/file.ts index 950b06d..d3ece66 100644 --- a/src/models/file.ts +++ b/src/models/file.ts @@ -6,11 +6,9 @@ interface File { archive: boolean; document: string; whiteboard: string; - private: boolean; + filePrivate: boolean; writtenBy: any[]; readBy: any[]; - write: boolean; - read: boolean; } const FileSchema = new Schema( @@ -20,11 +18,9 @@ const FileSchema = new Schema( archive: { type: Boolean }, document: { type: String }, whiteboard: { type: String }, - private: { type: Boolean, required: true }, + filePrivate: { type: Boolean, required: true }, writtenBy: [{ type: Schema.Types.ObjectId, required: true, ref: "User" }], readBy: [{ type: Schema.Types.ObjectId, required: true, ref: "User" }], - write: { type: Boolean }, - read: { type: Boolean }, }, { timestamps: true } ); diff --git a/src/models/user.ts b/src/models/user.ts index 69c3808..9e85b6b 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -1,7 +1,7 @@ import mongoose, { Schema } from "mongoose"; import jwt from "jsonwebtoken" -interface User { +export interface User { email: string; firstName: string; lastName: string; diff --git a/src/types/types.d.ts b/src/types/types.d.ts index ece19dd..c0a717c 100644 --- a/src/types/types.d.ts +++ b/src/types/types.d.ts @@ -1,3 +1,5 @@ +import { User } from "@/models/user"; + type USER = { isAuth: boolean; id:string; @@ -7,4 +9,8 @@ type USER = { refreshToken:string; accessToken:string; image:string | undefined; - }; \ No newline at end of file + }; + +interface ApiUser extends User{ + _id:string; +} \ No newline at end of file