diff --git a/src/app/api/functions/csvReadWrite.ts b/src/app/api/functions/csvReadWrite.ts new file mode 100644 index 0000000..6683182 --- /dev/null +++ b/src/app/api/functions/csvReadWrite.ts @@ -0,0 +1,98 @@ +import path from "path"; +import fs from "fs"; +import csv from "csv-parser"; + +export type User = { + name: string; + email: string; + password: string; +}; + +/** + * @returns {array} - Array of Objects containing user data + */ +export async function readUserCsv(): Promise { + return new Promise((resolve, reject) => { + // Dynamic CSV location generation + let csvPath = path.dirname(__dirname); // /login + csvPath = path.dirname(csvPath); // /api + csvPath = path.dirname(csvPath); // /app + csvPath = path.dirname(csvPath); // /src + csvPath = path.dirname(csvPath); // /[project] + csvPath = path.dirname(csvPath); // /termor-tracker + csvPath = path.join(csvPath, "src", "databases", "Users.csv"); + + // Forms array for user data + let results: User[] = []; + // Reads data and adds it to results + fs.createReadStream(csvPath) + .pipe(csv()) + .on("data", (data) => results.push(data)) + .on("end", () => { + resolve(results); + }) + .on("error", (error) => { + reject(error); + }); + }); +} + +export function findUserByEmail(users: User[], email: string): User | undefined { + return users.find((user) => user.email === email); +} + +export async function passwordStrengthCheck(password: string): Promise { + if (password.length < 8) { + return "short"; + } else if (password.length > 16) { + return "long"; + } + const lowercaseRegex = /[a-z]/; + const uppercaseRegex = /[A-Z]/; + const digitRegex = /\d/; + const specialCharRegex = /[!@#$%^&*]/; + if (!lowercaseRegex.test(password)) { + return "no lower"; + } else if (!uppercaseRegex.test(password)) { + return "no upper"; + } else if (!digitRegex.test(password)) { + return "no digit"; + } else if (!specialCharRegex.test(password)) { + return "no special"; + } else { + return "secure"; + } + return "end of function"; +} + +/** + * Writes User objects to the Users.csv file + * @param users {User[]} Array of User objects to write to the CSV file + * @returns {Promise} + */ +export async function writeUserCsv(users: User[]): Promise { + return new Promise((resolve, reject) => { + // Dynamic CSV location generation + let csvPath = path.dirname(__dirname); // /login + csvPath = path.dirname(csvPath); // /api + csvPath = path.dirname(csvPath); // /app + csvPath = path.dirname(csvPath); // /src + csvPath = path.dirname(csvPath); // /[project] + csvPath = path.dirname(csvPath); // /termor-tracker + csvPath = path.join(csvPath, "src", "databases", "Users.csv"); + + // Prepare CSV data as a string + const headers = "name,email,password"; // CSV headers + const rows = users.map((user) => `${user.name},${user.email},${user.password}`); + const csvData = `${headers}\n${rows.join("\n")}`; + + // Write data to the file + fs.writeFile(csvPath, csvData, (error) => { + if (error) { + reject(error); // Reject promise on error + } else { + resolve(); // Resolve the promise if successful + } + }); + }); +} \ No newline at end of file diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts index 7f0c0c3..0bd9cb0 100644 --- a/src/app/api/login/route.ts +++ b/src/app/api/login/route.ts @@ -1,47 +1,5 @@ import { NextResponse } from "next/server"; -import path from "path"; -import fs from "fs"; -import csv from "csv-parser"; - -type User = { - name: string; - email: string; - password: string; -} - -/** - * @returns {array} - Array of Objects containing user data - */ -async function readUserCsv(): Promise{ - return new Promise((resolve, reject) => { - // Dynamic csv location generation - let csvPath = path.dirname(__dirname); // /login - csvPath = path.dirname(csvPath); // /api - csvPath = path.dirname(csvPath); // /app - csvPath = path.dirname(csvPath); // /src - csvPath = path.dirname(csvPath); // /[project] // ? not sure why this dirfolder is here... - csvPath = path.dirname(csvPath); // /termor-tracker - csvPath = path.join(csvPath,"src","databases","Users.csv"); - //Forms array for user data - let results : User[] = []; - - //Reads data and adds it to results - fs.createReadStream(csvPath) - .pipe(csv()) - .on("data", (data) => results.push(data)) - .on("end", () => { - resolve(results) - }) - .on("error", (error) => { - reject(error) - }) - - }); -} - -function findUserByEmail(users: User[], email: string): User | undefined { - return users.find((user) => user.email === email); -} +import {User, readUserCsv, findUserByEmail} from "../functions/csvReadWrite" export async function POST(request: Request) { try { diff --git a/src/app/api/signup/route.ts b/src/app/api/signup/route.ts index e023903..062b1b9 100644 --- a/src/app/api/signup/route.ts +++ b/src/app/api/signup/route.ts @@ -1,17 +1,54 @@ import { NextResponse } from "next/server"; +import {User, readUserCsv, writeUserCsv, findUserByEmail, passwordStrengthCheck} from "../functions/csvReadWrite" export async function POST(request: Request) { try { const body = await request.json(); // Parse incoming JSON data - const { name, email, password } = body; + const {name, email, password } = body; + console.log("Signin API received data"); + + const userData = await readUserCsv(); + + console.log(userData) + console.log("Name:", name); // ! remove + console.log("Email:", email); // ! remove + console.log("Password:", password);// ! remove + + const foundUser = findUserByEmail(userData,email) + + if (foundUser) { + console.log("Email already in the system") + return NextResponse.json({ message: "Sorry, this email is already in use" }, { status: 409 }); + } + + const passwordCheckResult = await passwordStrengthCheck(password) + + if (passwordCheckResult === "short"){ + return NextResponse.json({ message: "Your password is shorter than 8 characters" }, { status: 400 }); + } else if (passwordCheckResult === "long"){ + return NextResponse.json({ message: "Your password is longer than 16 characters" }, { status: 400 }); + } else if (passwordCheckResult === "no lower"){ + return NextResponse.json({ message: "Your password must contain a lowercase letters" }, { status: 400 }); + } else if (passwordCheckResult === "no upper"){ + return NextResponse.json({ message: "Your password must contain a uppercase letters" }, { status: 400 }); + } else if (passwordCheckResult === "no digit"){ + return NextResponse.json({ message: "Your password must contain a number" }, { status: 400 }); + } else if (passwordCheckResult === "no special"){ + return NextResponse.json({ message: "Your password must contain a special character (!@#$%^&*)" }, { status: 400 }); + } else if (passwordCheckResult === "end of function"){ + return NextResponse.json({ message: "Password check script failure" }, { status: 500 }); + } else { + try { + userData.push(body) + await writeUserCsv(userData) + return NextResponse.json({ message: "Account Created" }, { status: 201 }); + } catch(error) { + console.error("Error in writting :", error); + return NextResponse.json({ message: "Internal Server Error" }, { status: 500 }); + } + } - console.log("Signup API received data:"); - console.log("Name:", name); - console.log("Email:", email); - console.log("Password:", password); - // For now, just respond back with a success message - return NextResponse.json({ message: "Signup successful!" }, { status: 201 }); } catch (error) { console.error("Error in signup endpoint:", error); return NextResponse.json({ message: "Internal Server Error" }, { status: 500 }); diff --git a/src/components/AuthModal.tsx b/src/components/AuthModal.tsx index 367975c..b46522d 100644 --- a/src/components/AuthModal.tsx +++ b/src/components/AuthModal.tsx @@ -20,6 +20,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM const [isLogin, setIsLogin] = useState(true); const modalRef = useRef(null); const [isFailed, setIsFailed] = useState(false); + const [failMessage, setFailMessage] = useState(false); /* useState is a React Hook that declares state variables in a functional component. It returns a two-element array The state variable (isLogin) : Represents the current state value (e.g., true initially). This is the value you can use in your component. @@ -90,8 +91,10 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM if (res.ok) { //res.ok checks if the response is between 200-299 console.log("Success!"); onClose(); // closes UI - } else if (res.status === 401){ - console.log("Incorrect details provided") + } else if (res.status >= 400 && res.status <500){ + const responseBody = await res.json() + console.log("4xx error:", responseBody.message) + setFailMessage(responseBody.message) setIsFailed(true) } else{ console.error("Error:", await res.text()); // logs error with error message sent to console @@ -104,7 +107,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM return (
-

{isLogin ? "Login" : "Sign Up"}

@@ -143,7 +146,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
{ isFailed && (
- +
) }