Moved functions from login/route.ts to another file

Signup route created
This commit is contained in:
Lukeshan Thananchayan 2025-04-28 09:48:02 +01:00
parent fe5c231e89
commit c64b953a08
4 changed files with 150 additions and 54 deletions

View File

@ -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<User[]> {
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<string> {
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<void>}
*/
export async function writeUserCsv(users: User[]): Promise<void> {
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
}
});
});
}

View File

@ -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<User[]>{
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 {

View File

@ -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 });

View File

@ -20,6 +20,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
const [isLogin, setIsLogin] = useState<boolean>(true);
const modalRef = useRef<HTMLDivElement>(null);
const [isFailed, setIsFailed] = useState<boolean>(false);
const [failMessage, setFailMessage] = useState<boolean>(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 (
<div className="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50" onClick={handleOverlayClick}>
<div ref={modalRef} className="bg-white rounded-lg shadow-lg p-6 w-full max-w-md relative">
<button onClick={onClose} className="absolute text-xl top-0 right-2 text-gray-500 hover:text-gray-700" aria-label="Close modal">
<button onClick={() => {setIsFailed(false);onClose();}} className="absolute text-xl top-0 right-2 text-gray-500 hover:text-gray-700" aria-label="Close modal">
×
</button>
<h2 className="text-2xl font-bold text-center mb-4">{isLogin ? "Login" : "Sign Up"}</h2>
@ -143,7 +146,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
<div>
{ isFailed && (
<div>
<label className="block text-sm font-medium text-red-700">Incorrect email or password</label>
<label className="block text-sm font-medium text-red-700">{failMessage}</label>
</div>
)
}