diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..727217f
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,5 @@
+MONGODB_URI=your_mongodb_connection_string
+JWT_SECRET=your_jwt_secret_key
+HASH_SECRET=your_HmacSHA256_secret_key
+SYSTEM_MAIL=your_email_address
+SYSTEM_MAIL_PASS=your_email_address_password
\ No newline at end of file
diff --git a/README.md b/README.md
index 504e797..c2f042b 100644
--- a/README.md
+++ b/README.md
@@ -82,17 +82,20 @@ To set up Travel Buddy locally, follow these steps:
3. Configure environment variables:
- Create a `.env` file in the root directory.
- - Add your MongoDB Atlas connection string:
+ - Copy the content from `.env.example` to `.env`:
- ```env
- MONGODB_URI=your_mongodb_connection_string
+ ```bash
+ cp .env.example .env
```
- - Add your JWT secret key:
-
```env
+ MONGODB_URI=your_mongodb_connection_string
JWT_SECRET=your_jwt_secret_key
+ HASH_SECRET=your_HmacSHA256_secret_key
+ SYSTEM_MAIL=your_email_address
+ SYSTEM_MAIL_PASS=your_email_address_password
```
+ - Replace placeholder values in the .env file with actual values.
4. Run the application:
```sh
diff --git a/app/api/authenticate/generate/route.js b/app/api/authenticate/generate/route.js
new file mode 100644
index 0000000..e4a9324
--- /dev/null
+++ b/app/api/authenticate/generate/route.js
@@ -0,0 +1,82 @@
+import { NextResponse } from "next/server";
+import otpGenerator from "otp-generator";
+import { HmacSHA256 } from "crypto-js";
+import nodemailer from "nodemailer";
+import { cookies } from "next/headers";
+import { instituteDetails } from "@/app/utils/institute";
+
+export async function POST(req) {
+ try {
+ req = await req.json();
+
+ // Form fields:
+ // i) email – (Institute Email) – Text
+
+ const { email, instituteCode } = req;
+
+ const institute = await instituteDetails({ instituteCode });
+
+ const emailDomain = email.split("@")[1];
+
+ if (emailDomain != institute.domain) {
+ throw new Error("Invalid email. Choose the correct institute.");
+ }
+
+ const otp = otpGenerator.generate(6, { lowerCaseAlphabets: false, upperCaseAlphabets: false, specialChars: false });
+
+ const expiryTime = Date.now() + 5 * 60 * 1000; // 5 mins
+
+ const data = `${instituteCode}.${email}.${otp}`;
+
+ const hashData = HmacSHA256(data, process.env.HASH_SECRET).toString() + "." + expiryTime;
+
+ const transporter = nodemailer.createTransport({
+ service: 'gmail',
+ auth: {
+ user: process.env.SYSTEM_MAIL,
+ pass: process.env.SYSTEM_MAIL_PASS
+ }
+ });
+
+ const mailOptions = {
+ from: process.env.SYSTEM_MAIL,
+ to: email,
+ subject: 'OTP Code for Authentication',
+ text: `Your OTP code is: ${otp}. This will expire at ${new Date(expiryTime).toLocaleString()}`
+ };
+
+ await transporter.sendMail(mailOptions);
+
+ const cookieStore = cookies();
+
+ cookieStore.set({
+ name: "otpData",
+ value: JSON.stringify({ email, hashData, instituteCode }),
+ httpOnly: true,
+ path: "/",
+ maxAge: 5 * 60, // 5 mins
+ });
+
+ return NextResponse.json(
+ {
+ message: "OTP sent successfully! Please check your inbox.",
+ },
+ {
+ status: 200,
+ }
+ );
+
+ } catch (error) {
+ console.log(error.message);
+ return NextResponse.json(
+ {
+ message:
+ error.message ||
+ "Something went wrong - please try again later!",
+ },
+ {
+ status: 500,
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/app/api/authenticate/validate-jwt/route.js b/app/api/authenticate/validate-jwt/route.js
new file mode 100644
index 0000000..4033d15
--- /dev/null
+++ b/app/api/authenticate/validate-jwt/route.js
@@ -0,0 +1,65 @@
+import { NextResponse } from "next/server";
+import { cookies } from "next/headers";
+import jwt from "jsonwebtoken";
+import { instituteDetails } from "@/app/utils/institute";
+
+export async function GET(req) {
+ try {
+
+ const { searchParams } = new URL(req.url);
+
+ const instituteCode = searchParams.get("instituteCode");
+
+ const institute = await instituteDetails({ instituteCode });
+
+ const { authCookie } = institute;
+
+ const cookieStore = cookies();
+
+ const cookie = cookieStore.get(authCookie);
+
+ if (!cookie) {
+ throw new Error("No JWT cookie found in the request.");
+ }
+
+ const token = cookie.value;
+
+ if (!token) {
+ throw new Error("No JWT session token found.");
+ }
+
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
+
+ const expiryTime = decoded.exp;
+ const currentTime = Date.now();
+
+ if (currentTime >= expiryTime * 1000) {
+ cookieStore.delete(cookie);
+ throw new Error("JWT cookie token has expired.");
+ }
+
+ return NextResponse.json(
+ {
+ message: "Authentication successful.",
+ email: decoded.email,
+ },
+ {
+ status: 200,
+ }
+ );
+
+ } catch (error) {
+ console.log(error.message);
+ return NextResponse.json(
+ {
+ message:
+ error.message ||
+ "Something went wrong - please try again later!",
+ },
+ {
+ status: 500,
+ }
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/app/api/authenticate/verify/route.js b/app/api/authenticate/verify/route.js
new file mode 100644
index 0000000..aad594f
--- /dev/null
+++ b/app/api/authenticate/verify/route.js
@@ -0,0 +1,103 @@
+
+import { NextResponse } from "next/server";
+import { HmacSHA256 } from "crypto-js";
+import validator from "validator";
+import { cookies } from "next/headers";
+import { instituteDetails } from "@/app/utils/institute";
+import jwt from "jsonwebtoken";
+
+const createJwt = (payload) => {
+ const secret = process.env.JWT_SECRET;
+ const token = jwt.sign(payload, secret, { expiresIn: '1h' }); // Set expiration time
+ return token;
+};
+
+export async function POST(req) {
+ try {
+ req = await req.json();
+
+ // Form fields:
+ // i) email – (Institute Email) – Text
+ // ii) hashData – (Hashed Data for OTP verification) – Text
+ // iii) instituteCode - (Institute Code) – Text
+ // iv) otp - (Submitted OTP) - Text
+
+ const cookieStore = cookies();
+
+ const { email, hashData, instituteCode, otp } = req;
+
+ if (
+ validator.isEmpty(email) ||
+ validator.isEmpty(hashData) ||
+ validator.isEmpty(otp) ||
+ validator.isEmpty(instituteCode)
+ ) {
+ throw new Error("Please fill all the fields!");
+ }
+
+ if (otp.length > 0 && otp.length != 6) {
+ throw new Error("Please enter a valid 6-digit OTP!");
+ }
+
+ const hash = hashData.split('.');
+
+ if (hash.length !== 2) {
+ throw new Error("Invalid hash data format!");
+ }
+
+ const data = `${instituteCode}.${email}.${otp}`;
+
+ const userHash = HmacSHA256(data, process.env.HASH_SECRET).toString();
+
+ if (userHash !== hash[0]) {
+ throw new Error("Verification failed try again.");
+ }
+
+ const expiryTime = parseInt(hash[1]);
+
+ const currentTime = Date.now();
+
+ if (currentTime > expiryTime) {
+ cookieStore.delete("otpData");
+ throw new Error("OTP has expired. Please request a new one.");
+ }
+
+ const institute = await instituteDetails({ instituteCode });
+
+ const payload = { email };
+
+ const token = createJwt(payload);
+
+ cookieStore.set({
+ name: institute.authCookie,
+ value: token,
+ httpOnly: true,
+ path: "/",
+ maxAge: 60 * 60, // Temporary maxAge
+ });
+
+ cookieStore.delete("otpData");
+
+ return NextResponse.json(
+ {
+ message: "OTP verified successfully!",
+ },
+ {
+ status: 200,
+ }
+ );
+
+ } catch (error) {
+ console.log(error.message);
+ return NextResponse.json(
+ {
+ message:
+ error.message ||
+ "Something went wrong - please try again later!",
+ },
+ {
+ status: 500,
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/app/api/register/route.js b/app/api/register/route.js
index 014a247..fc21dcf 100644
--- a/app/api/register/route.js
+++ b/app/api/register/route.js
@@ -3,7 +3,7 @@ import sanitize from "mongo-sanitize";
import validator from "validator";
import { connectToDatabase } from "@/app/lib/mongodb";
import User from "@/app/models/User";
-import { cookies } from "next/headers";
+import { cookies, headers } from "next/headers";
import Axios from "axios";
import { checkUser } from "@/app/utils/auth";
import { instituteDetails } from "@/app/utils/institute";
@@ -11,7 +11,6 @@ import { instituteDetails } from "@/app/utils/institute";
export async function POST(req) {
try {
req = await req.json();
-
// Form fields:
// i) name – (Full Name) - Text
// ii) roll – (Roll Number) – Text
@@ -20,7 +19,6 @@ export async function POST(req) {
// v) instituteCode - (Institute Code) – Text
let { name, roll, number, instituteCode } = req;
-
const institute = await instituteDetails({ instituteCode });
let { authCookie, verifyAuthLink } = institute;
@@ -51,7 +49,26 @@ export async function POST(req) {
);
}
- const response = await Axios.get(verifyAuthLink, {
+ const isExternal = verifyAuthLink.startsWith("http://") || verifyAuthLink.startsWith("https://");
+
+ let verifyrouteURL;
+
+ if (isExternal) {
+ verifyrouteURL = verifyAuthLink;
+ }
+
+ else {
+ const requestHeaders = headers();
+ const host = requestHeaders.get('host');
+ const protocol = requestHeaders.get('x-forwarded-proto') || 'http';
+ verifyrouteURL = protocol + "://" + host + verifyAuthLink;
+ }
+
+ if (!verifyrouteURL || verifyrouteURL === "") {
+ throw new Error("Invalid authentication verfiy URL.");
+ }
+
+ const response = await Axios.get(verifyrouteURL, {
headers: {
Cookie: Object.entries({
[authCookie]: token,
@@ -60,6 +77,11 @@ export async function POST(req) {
.join("; "),
},
});
+
+ if (!response || !response.data || !response.data.email) {
+ throw new Error("No JWT session token found.");
+ }
+
const email = response.data.email;
if (!email) {
diff --git a/app/authenticate/AuthForm.jsx b/app/authenticate/AuthForm.jsx
index 3b04977..7b69c1d 100644
--- a/app/authenticate/AuthForm.jsx
+++ b/app/authenticate/AuthForm.jsx
@@ -2,7 +2,7 @@
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
-export default function AuthForm({ institutes }) {
+export default function AuthForm({ institutes, redirect_url }) {
const router = useRouter();
const check = () => {
@@ -28,7 +28,7 @@ export default function AuthForm({ institutes }) {
alert("Please select an institute");
return;
}
- router.push("/register?instituteCode=" + institute);
+ router.push("/register?instituteCode=" + institute + "&redirect_url=" + redirect_url);
return;
};
diff --git a/app/authenticate/generate-otp/GenerateOtpForm.jsx b/app/authenticate/generate-otp/GenerateOtpForm.jsx
new file mode 100644
index 0000000..150c124
--- /dev/null
+++ b/app/authenticate/generate-otp/GenerateOtpForm.jsx
@@ -0,0 +1,114 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+import { instituteDetails } from "@/app/utils/institute";
+
+const GenerateOtpForm = ({ instituteCode, redirect_url }) => {
+ const [loading, setLoading] = useState(false);
+ const [email, setEmail] = useState("");
+ const [institute, setInstitute] = useState("");
+ const router = useRouter();
+
+ const checkInstituteCode = async () => {
+ if (!instituteCode) {
+ alert('Institute not found.');
+ router.push("/");
+ }
+ try {
+ const selectInstitute = await instituteDetails({ instituteCode });
+ setInstitute(selectInstitute.name);
+ } catch (error) {
+ router.push("/");
+ }
+ }
+
+ useEffect(() => {
+ checkInstituteCode();
+ }, [])
+
+ const handleChange = (e) => {
+ setEmail(e.target.value);
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ const data = {
+ email: email,
+ instituteCode: instituteCode
+ }
+
+ setLoading(true);
+
+ const res = await fetch("/api/authenticate/generate", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(data),
+ });
+
+ setLoading(false);
+
+ if (res.ok) {
+ const json = await res.json();
+ alert(json.message);
+ router.push("/authenticate/verify-otp?redirect_url=" + redirect_url);
+ } else {
+ const json = await res.json();
+ alert(json.message);
+ }
+ };
+
+ return (
+
+ );
+}
+
+export default GenerateOtpForm;
diff --git a/app/authenticate/generate-otp/page.jsx b/app/authenticate/generate-otp/page.jsx
new file mode 100644
index 0000000..54eee69
--- /dev/null
+++ b/app/authenticate/generate-otp/page.jsx
@@ -0,0 +1,18 @@
+import GenerateOtpForm from "./GenerateOtpForm";
+
+export const metadata = {
+ title: "Request OTP for Email verification",
+};
+
+const Page = async ({ searchParams }) => {
+
+ let { instituteCode, redirect_url } = searchParams;
+
+ if (instituteCode) {
+ instituteCode = instituteCode.split("?")[0];
+ }
+
+ return ;
+};
+
+export default Page;
diff --git a/app/authenticate/page.jsx b/app/authenticate/page.jsx
index e0d6ded..99f926b 100644
--- a/app/authenticate/page.jsx
+++ b/app/authenticate/page.jsx
@@ -1,18 +1,29 @@
import { connectToDatabase } from "@/app/lib/mongodb";
import Institute from "@/app/models/Institute";
import AuthForm from "./AuthForm";
+import { headers } from "next/headers";
export const metadata = {
title: "Select Institute",
};
-const Page = async () => {
+const Page = async ({ searchParams }) => {
+
+ const { redirect_path } = searchParams;
+
+ const requestHeaders = headers();
+ const host = requestHeaders.get('host');
+ const protocol = requestHeaders.get('x-forwarded-proto') || 'http';
+ const redirect_url = protocol + "://" + host + redirect_path;
+
await connectToDatabase();
// Return only names of Institues
const institutes = await Institute.find({}, { _id: 0, name: 1, code: 1 });
- return ;
+ const institutesData = JSON.parse(JSON.stringify(institutes));
+
+ return ;
};
export default Page;
diff --git a/app/authenticate/verify-otp/VerifyOtpForm.jsx b/app/authenticate/verify-otp/VerifyOtpForm.jsx
new file mode 100644
index 0000000..849bd0b
--- /dev/null
+++ b/app/authenticate/verify-otp/VerifyOtpForm.jsx
@@ -0,0 +1,97 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+
+const VerifyOtpForm = ({ email, hashData, instituteCode, redirect_url }) => {
+
+ const [loading, setLoading] = useState(false);
+ const [otp, setOtp] = useState("");
+ const router = useRouter();
+
+ const handleChange = (e) => {
+ setOtp(e.target.value);
+ };
+
+ const check = () => {
+ if (!hashData || !instituteCode || !email) {
+ alert("Invalid access. Redirecting to authentication page.");
+ router.push("/authenticate");
+ }
+ }
+
+ useEffect(() => {
+ check();
+ }, [])
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ setLoading(true);
+
+ const data = {
+ email: email,
+ hashData: hashData,
+ instituteCode: instituteCode,
+ otp: otp
+ }
+
+ const res = await fetch("/api/authenticate/verify", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(data),
+ });
+
+ setLoading(false);
+
+ if (res.ok) {
+ const json = await res.json();
+ alert(json.message);
+ router.push("/register?instituteCode=" + instituteCode + "&redirect_url=" + redirect_url);
+ } else {
+ const json = await res.json();
+ alert(json.message);
+ }
+ };
+
+
+ return (
+
+ );
+}
+
+export default VerifyOtpForm;
\ No newline at end of file
diff --git a/app/authenticate/verify-otp/page.jsx b/app/authenticate/verify-otp/page.jsx
new file mode 100644
index 0000000..2591be6
--- /dev/null
+++ b/app/authenticate/verify-otp/page.jsx
@@ -0,0 +1,39 @@
+import { redirect } from "next/navigation";
+import VerifyOtpForm from "./VerifyOtpForm";
+import { cookies } from "next/headers";
+
+export const metadata = {
+ title: "Enter OTP for Email verification",
+};
+
+const Page = async ({ searchParams }) => {
+ const { redirect_url } = searchParams;
+ console.log(redirect_url);
+ const cookieStore = cookies();
+ const cookie = cookieStore.get("otpData");
+
+ if (!cookie) {
+ redirect("/authenticate/generate-otp");
+ }
+
+ const token = cookie.value;
+
+ if (!token) {
+ redirect("/authenticate/generate-otp");
+ }
+
+ const { email, hashData, instituteCode } = JSON.parse(token);
+
+ if (!email || !hashData || !instituteCode) {
+ redirect("/authenticate/generate-otp");
+ }
+
+ return ;
+};
+
+export default Page;
diff --git a/app/data.json b/app/data.json
index 8e0103f..0c9947f 100644
--- a/app/data.json
+++ b/app/data.json
@@ -1,13 +1,4 @@
{
- "locations": {
- "IIT": "IIT Kharagpur Campus",
- "CCU": "Kolkata Airport",
- "KGP": "Kharagpur Railway Station",
- "HWH": "Howrah Railway Station",
- "SDAH": "Sealdah Railway Station",
- "HIJ": "Hijli Railway Station",
- "CAT": "Kolkata CAT Centre(s)"
- },
"slots": {
"0": "12 AM - 1 AM",
"1": "1 AM - 2 AM",
diff --git a/app/models/Institute.js b/app/models/Institute.js
index 9c659f9..bd4b911 100644
--- a/app/models/Institute.js
+++ b/app/models/Institute.js
@@ -23,14 +23,8 @@ const instituteSchema = new Schema({
required: [true, "Please provide a domain for the email"],
},
locations: {
- type: [String],
+ type: Object,
required: [true, "Please provide at least one location"],
- validate: {
- validator: function (locations) {
- return locations.length > 0;
- },
- message: "Institute must have at least one location",
- },
},
authLink: {
type: String,
diff --git a/app/register/RegForm.jsx b/app/register/RegForm.jsx
index bb5f170..f4e1dcb 100644
--- a/app/register/RegForm.jsx
+++ b/app/register/RegForm.jsx
@@ -5,7 +5,7 @@ import Loading from "@/app/utils/Loading";
import { useRouter } from "next/navigation";
import { checkUser } from "@/app/utils/auth";
-export default function RegForm({ email, instituteCode }) {
+export default function RegForm({ email, instituteCode, redirect_url }) {
const router = useRouter();
const [loading, setLoading] = useState(true);
const [formData, setFormData] = useState({
@@ -76,7 +76,7 @@ export default function RegForm({ email, instituteCode }) {
const json = await res.json();
alert(json.message);
localStorage.setItem("travelbuddy", json.user);
- router.push("/");
+ router.push(redirect_url);
} else {
const json = await res.json();
alert(json.message);
diff --git a/app/register/page.jsx b/app/register/page.jsx
index d399401..67c6bcb 100644
--- a/app/register/page.jsx
+++ b/app/register/page.jsx
@@ -8,7 +8,7 @@ export const metadata = {
};
const Page = async ({ searchParams }) => {
- const { instituteCode } = searchParams;
+ const { instituteCode, redirect_url } = searchParams;
const institute = await instituteDetails({ instituteCode });
@@ -18,21 +18,21 @@ const Page = async ({ searchParams }) => {
const cookie = cookieStore.get(authCookie);
if (!cookie) {
- redirect(authLink + "?redirect_url=https://travel.metakgp.org/");
+ redirect(authLink + "&redirect_url=" + redirect_url);
}
const token = cookie.value;
if (!token) {
- redirect(authLink + "?redirect_url=https://travel.metakgp.org/");
+ redirect(authLink + "&redirect_url=" + redirect_url);
}
const email = JSON.parse(atob(token.split(".")[1])).email; // get the user email from jwt
if (!email) {
- redirect(authLink + "?redirect_url=https://travel.metakgp.org/");
+ redirect(authLink + "&redirect_url=" + redirect_url);
}
- return ;
+ return ;
};
export default Page;
diff --git a/app/trains/create/TrainForm.jsx b/app/trains/create/TrainForm.jsx
index de5d4e4..e94e976 100644
--- a/app/trains/create/TrainForm.jsx
+++ b/app/trains/create/TrainForm.jsx
@@ -1,6 +1,6 @@
"use client";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Loading from "@/app/utils/Loading";
import Link from "next/link";
@@ -9,20 +9,21 @@ import { verifyUser } from "@/app/utils/auth";
const TrainForm = () => {
const router = useRouter();
+ const pathname = usePathname();
const [loading, setLoading] = useState(true);
const [email, setEmail] = useState("");
const check = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
const token = localStorage.getItem("travelbuddy");
const email = await verifyUser({ token });
if (!email) {
localStorage.removeItem("travelbuddy");
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
setEmail(email);
diff --git a/app/trains/my-trains/MyTrains.jsx b/app/trains/my-trains/MyTrains.jsx
index 1376cf1..72b5856 100644
--- a/app/trains/my-trains/MyTrains.jsx
+++ b/app/trains/my-trains/MyTrains.jsx
@@ -1,18 +1,18 @@
"use client";
import Link from "next/link";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Loading from "@/app/utils/Loading";
const MyTrains = () => {
const router = useRouter();
-
+ const pathname = usePathname();
const [myTrains, setMyTrains] = useState(null);
const getDetails = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
const res = await fetch("/api/trains/find", {
diff --git a/app/trains/train/[trainID]/TrainDetails.jsx b/app/trains/train/[trainID]/TrainDetails.jsx
index 300d8c2..2872088 100644
--- a/app/trains/train/[trainID]/TrainDetails.jsx
+++ b/app/trains/train/[trainID]/TrainDetails.jsx
@@ -1,18 +1,18 @@
"use client";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Link from "next/link";
import Loading from "@/app/utils/Loading";
const TrainDetails = ({ trainID }) => {
const router = useRouter();
-
+ const pathname = usePathname();
const [data, setData] = useState(null);
const getDetails = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
const res = await fetch("/api/trains/find", {
diff --git a/app/trips/create/TripForm.jsx b/app/trips/create/TripForm.jsx
index 561227b..b7e207b 100644
--- a/app/trips/create/TripForm.jsx
+++ b/app/trips/create/TripForm.jsx
@@ -1,33 +1,40 @@
"use client";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Loading from "@/app/utils/Loading";
import Link from "next/link";
import { today } from "@/app/utils/date";
import { verifyUser } from "@/app/utils/auth";
import data from "@/app/data.json";
+import { userInstituteDetails } from "@/app/utils/institute";
const TripForm = () => {
const router = useRouter();
+ const pathname = usePathname();
const [loading, setLoading] = useState(true);
-
+ const [locations, setLocations] = useState({});
const [email, setEmail] = useState("");
const check = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
const token = localStorage.getItem("travelbuddy");
const email = await verifyUser({ token });
if (!email) {
localStorage.removeItem("travelbuddy");
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
setEmail(email);
-
+ try {
+ const userInstitue = await userInstituteDetails({email});
+ setLocations(userInstitue.locations);
+ } catch (error) {
+ router.push("/");
+ }
setLoading(false);
};
@@ -155,9 +162,9 @@ const TripForm = () => {
className="border rounded-md p-2 w-full"
>
- {Object.keys(data.locations).map((location) => (
+ {Object.keys(locations).map((location) => (
))}
@@ -173,9 +180,9 @@ const TripForm = () => {
className="border rounded-md p-2 w-full"
>
- {Object.keys(data.locations).map((location) => (
+ {Object.keys(locations).map((location) => (
))}
diff --git a/app/trips/my-trips/MyTrips.jsx b/app/trips/my-trips/MyTrips.jsx
index 52fb724..af9ffb2 100644
--- a/app/trips/my-trips/MyTrips.jsx
+++ b/app/trips/my-trips/MyTrips.jsx
@@ -1,19 +1,19 @@
"use client";
import Link from "next/link";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Loading from "@/app/utils/Loading";
import mapping from "@/app/data.json";
const MyTrips = () => {
const router = useRouter();
-
+ const pathname = usePathname();
const [myTrips, setMyTrips] = useState(null);
const getDetails = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
const res = await fetch("/api/trips/find", {
diff --git a/app/trips/trip/[tripID]/TripDetails.jsx b/app/trips/trip/[tripID]/TripDetails.jsx
index 3d9df84..f433fc7 100644
--- a/app/trips/trip/[tripID]/TripDetails.jsx
+++ b/app/trips/trip/[tripID]/TripDetails.jsx
@@ -1,20 +1,28 @@
"use client";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Loading from "@/app/utils/Loading";
import mapping from "@/app/data.json";
+import { userInstituteDetails } from "@/app/utils/institute";
-const TripDetails = ({ tripID }) => {
+const TripDetails = ({ tripID, email }) => {
const router = useRouter();
-
+ const pathname = usePathname();
const [data, setData] = useState(null);
+ const [locations, setLocations] = useState({});
const getDetails = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
+ try {
+ const userInstitue = await userInstituteDetails({ email });
+ setLocations(userInstitue.locations);
+ } catch (error) {
+ router.push("/");
+ }
const res = await fetch("/api/trips/find", {
method: "POST",
headers: {
@@ -107,11 +115,11 @@ const TripDetails = ({ tripID }) => {
Email: {data.trip.email}
- Source: {mapping.locations[data.trip.source]}
+ Source: {locations[data.trip.source]}
Destination:{" "}
- {mapping.locations[data.trip.destination]}
+ {locations[data.trip.destination]}
Date:{" "}
@@ -158,11 +166,11 @@ const TripDetails = ({ tripID }) => {
Source:{" "}
- {mapping.locations[trip.source]}
+ {locations[trip.source]}
Destination:{" "}
- {mapping.locations[trip.destination]}
+ {locations[trip.destination]}
Date:{" "}
diff --git a/app/trips/trip/[tripID]/page.jsx b/app/trips/trip/[tripID]/page.jsx
index 026ce4f..dcd1df0 100644
--- a/app/trips/trip/[tripID]/page.jsx
+++ b/app/trips/trip/[tripID]/page.jsx
@@ -1,4 +1,7 @@
+import { connectToDatabase } from "@/app/lib/mongodb";
import TripDetails from "./TripDetails";
+import Trip from "@/app/models/Trip";
+import { redirect } from "next/navigation";
export const metadata = {
title: "Trip Details",
@@ -7,7 +10,19 @@ export const metadata = {
const Page = async ({ params }) => {
const tripID = params.tripID;
- return ;
+ await connectToDatabase();
+
+ const trip = await Trip.findOne({
+ tripID: tripID
+ });
+
+ if(!trip){
+ redirect('/');
+ }
+
+ const tripData = JSON.parse(JSON.stringify(trip));
+
+ return ;
};
export default Page;
diff --git a/app/utils/auth.js b/app/utils/auth.js
index 158d915..c12b1b2 100644
--- a/app/utils/auth.js
+++ b/app/utils/auth.js
@@ -48,4 +48,4 @@ export async function checkAuth() {
}
return email;
-}
+}
\ No newline at end of file
diff --git a/app/utils/institute.js b/app/utils/institute.js
index fc2863a..b300461 100644
--- a/app/utils/institute.js
+++ b/app/utils/institute.js
@@ -2,6 +2,7 @@
import { connectToDatabase } from "@/app/lib/mongodb";
import Institute from "@/app/models/Institute";
+import User from "@/app/models/User";
export async function instituteDetails({ instituteCode }) {
if (!instituteCode || instituteCode == "") {
@@ -14,17 +15,31 @@ export async function instituteDetails({ instituteCode }) {
if (!institute) {
throw new Error("Institute not found.");
}
- if(!institute.authLink || institute.authLink.trim() == ""){
+ if (!institute.domain) {
+ throw new Error("Institute authentication Email domain not found.");
+ }
+ if (!institute.authLink || institute.authLink.trim() == "") {
throw new Error("Institute authentication URL not found.");
}
- if(!institute.verifyAuthLink || institute.verifyAuthLink.trim() == ""){
+ if (!institute.verifyAuthLink || institute.verifyAuthLink.trim() == "") {
throw new Error("Institute authentication verification URL not found.");
}
- if(!institute.authCookie){
+ if (!institute.authCookie) {
throw new Error("Institute authentication cookie not found.");
}
- return institute;
+ const data = JSON.parse(JSON.stringify(institute));
+ return data;
}
-
+export async function userInstituteDetails({ email }) {
+ await connectToDatabase();
+ const user = await User.findOne({
+ email: email
+ });
+ if (!user) {
+ throw new Error("User not found.");
+ }
+ const institute = await instituteDetails({ instituteCode: user.instituteCode });
+ return institute;
+}
diff --git a/package-lock.json b/package-lock.json
index bc7334e..0df003d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,10 +11,13 @@
"@next/third-parties": "^15.0.3",
"axios": "^1.7.7",
"bootstrap": "^5.3.3",
+ "crypto-js": "^4.2.0",
"jsonwebtoken": "^9.0.2",
"mongo-sanitize": "^1.1.0",
"mongoose": "^8.7.0",
"next": "14.2.13",
+ "nodemailer": "^6.9.16",
+ "otp-generator": "^4.0.1",
"react": "^18",
"react-bootstrap": "^2.10.5",
"react-dom": "^18",
@@ -705,6 +708,12 @@
"node": ">= 8"
}
},
+ "node_modules/crypto-js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+ "license": "MIT"
+ },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -1466,6 +1475,15 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/nodemailer": {
+ "version": "6.9.16",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz",
+ "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==",
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -1492,6 +1510,15 @@
"node": ">= 6"
}
},
+ "node_modules/otp-generator": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/otp-generator/-/otp-generator-4.0.1.tgz",
+ "integrity": "sha512-2TJ52vUftA0+J3eque4wwVtpaL4/NdIXDL0gFWFJFVUAZwAN7+9tltMhL7GCNYaHJtuONoier8Hayyj4HLbSag==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.10.0"
+ }
+ },
"node_modules/package-json-from-dist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
diff --git a/package.json b/package.json
index 90bbf92..8427ef1 100644
--- a/package.json
+++ b/package.json
@@ -12,10 +12,13 @@
"@next/third-parties": "^15.0.3",
"axios": "^1.7.7",
"bootstrap": "^5.3.3",
+ "crypto-js": "^4.2.0",
"jsonwebtoken": "^9.0.2",
"mongo-sanitize": "^1.1.0",
"mongoose": "^8.7.0",
"next": "14.2.13",
+ "nodemailer": "^6.9.16",
+ "otp-generator": "^4.0.1",
"react": "^18",
"react-bootstrap": "^2.10.5",
"react-dom": "^18",