Login backend code started
Site reacts correctly to different inputs with login
This commit is contained in:
parent
866ea00627
commit
fe5c231e89
706
package-lock.json
generated
706
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@ -7,14 +7,22 @@
|
|||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint",
|
||||||
|
"start:server": "dist/index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^6.4.1",
|
"@prisma/client": "^6.4.1",
|
||||||
"@types/mapbox-gl": "^3.4.1",
|
"@types/mapbox-gl": "^3.4.1",
|
||||||
|
"bcryptjs": "^3.0.2",
|
||||||
|
"body-parser": "^2.2.0",
|
||||||
|
"csv-parser": "^3.2.0",
|
||||||
|
"express": "^5.1.0",
|
||||||
|
"fs": "^0.0.1-security",
|
||||||
|
"jwt-decode": "^4.0.0",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"mapbox-gl": "^3.10.0",
|
"mapbox-gl": "^3.10.0",
|
||||||
"next": "15.1.7",
|
"next": "15.1.7",
|
||||||
|
"path": "^0.12.7",
|
||||||
"prisma": "^6.4.1",
|
"prisma": "^6.4.1",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
@ -24,6 +32,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
|
"@types/express": "^5.0.1",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19",
|
"@types/react": "^19",
|
||||||
"@types/react-dom": "^19",
|
"@types/react-dom": "^19",
|
||||||
|
|||||||
72
src/app/api/login/route.ts
Normal file
72
src/app/api/login/route.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
try {
|
||||||
|
const body = await request.json(); // Parse incoming JSON data
|
||||||
|
const {email, password } = body;
|
||||||
|
console.log("Login API received data");
|
||||||
|
|
||||||
|
const userData = await readUserCsv();
|
||||||
|
|
||||||
|
console.log(userData)
|
||||||
|
console.log("Email:", email); // ! remove
|
||||||
|
console.log("Password:", password);// ! remove
|
||||||
|
|
||||||
|
const foundUser = findUserByEmail(userData,email)
|
||||||
|
if (foundUser && foundUser.password === password) {
|
||||||
|
console.log("User Details Correct")
|
||||||
|
return NextResponse.json({ message: "Login successful!" }, { status: 200 });
|
||||||
|
} else {
|
||||||
|
console.log("User email or password is invalid")
|
||||||
|
return NextResponse.json({ message: "Email and/or password are invalid" }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error in signup endpoint:", error);
|
||||||
|
return NextResponse.json({ message: "Internal Server Error" }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/app/api/signup/route.ts
Normal file
19
src/app/api/signup/route.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
try {
|
||||||
|
const body = await request.json(); // Parse incoming JSON data
|
||||||
|
const { name, email, password } = body;
|
||||||
|
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,51 +1,102 @@
|
|||||||
|
// tells React if you're using its "server-side rendering" features. this component runs only on the client-side (browser)
|
||||||
"use client";
|
"use client";
|
||||||
|
// importing React hooks or utilities that enhance functionality inside the component
|
||||||
import { useState, FormEvent, useRef, MouseEvent, useEffect } from "react";
|
import { useState, FormEvent, useRef, MouseEvent, useEffect } from "react";
|
||||||
|
/*
|
||||||
|
useState: Used to manage state (data that changes over time).
|
||||||
|
FormEvent: TypeScript definition for form-related events like submission.
|
||||||
|
useRef: Used to access the DOM (the web page elements) directly.
|
||||||
|
MouseEvent: TypeScript definition for mouse-related events like clicks.
|
||||||
|
useEffect: Hook for running side effects (e.g., code that runs when something changes)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// defining a type for the props (inputs) that AuthModal expects
|
||||||
interface AuthModalProps {
|
interface AuthModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean; // bool for if the modal should be visible
|
||||||
onClose: () => void;
|
onClose: () => void; //A function that will be executed to close the modal
|
||||||
}
|
}
|
||||||
|
// creates a React functional component
|
||||||
export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
|
export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthModalProps ensures TypeScript validates the props to match the type definition above
|
||||||
const [isLogin, setIsLogin] = useState<boolean>(true);
|
const [isLogin, setIsLogin] = useState<boolean>(true);
|
||||||
const modalRef = useRef<HTMLDivElement>(null);
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [isFailed, setIsFailed] = 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.
|
||||||
|
The state updater function (setIsLogin) : A function that allows you to update the state variable. React takes care of re-rendering the component when the state is updated
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
modalRef allows direct access to the modal DOM element (the container div for the modal). This is useful for detecting if the user clicks outside of the modal
|
||||||
|
*/
|
||||||
|
|
||||||
|
// useEffect runs code after the component renders or when a dependency changes (in this case, isOpen as seen in the end [])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) setIsLogin(true);
|
if (isOpen) setIsLogin(true); // runs when isOpen changes, if it is true, the login is shown
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null; // if is open is false, the model isnt shown
|
||||||
|
|
||||||
|
// this is an arrow function. e: is used to specify that an event object is expected
|
||||||
const handleOverlayClick = (e: MouseEvent<HTMLDivElement>) => {
|
const handleOverlayClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
|
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
.current gives a reference to the actual DOM element (the inner modal container)
|
||||||
|
e.target refers to the specific element the mouse click event occurred on
|
||||||
|
.contains(e.target) checks if the clicked element (e.target) is inside the modal (modalRef.current)
|
||||||
|
as Node specifies e.target is a Node type to TypeScript
|
||||||
|
? what is a node
|
||||||
|
/!... means we get true if the click is outside the modal
|
||||||
|
Logic : if modalRef.current exists and the click target (e.target) is not inside the modal , then the condition is true
|
||||||
|
This means the user clicked outside the modal
|
||||||
|
*/
|
||||||
|
|
||||||
|
// LS - The following bit contains the more important code for what I'm focused on
|
||||||
|
/*
|
||||||
|
Note : handleSubmit is typically used as a event handler for submitting a form in react.
|
||||||
|
For example:
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<input type="text" name="example" />
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
e is the parameter passsed into the function
|
||||||
|
async indicates the function runs asyncronously, meaning it performs tasks which take time.
|
||||||
|
an example of this would be API calls
|
||||||
|
*/
|
||||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault(); // stops page from refreshing
|
||||||
const formData = new FormData(e.currentTarget);
|
setIsFailed(false)
|
||||||
const email = formData.get("email") as string;
|
const formData = new FormData(e.currentTarget); // new variable of class FormData is created. This is part of the standard Web API included in modern web browsers
|
||||||
const password = formData.get("password") as string;
|
const email = formData.get("email") as string; // gets email from form response
|
||||||
const name = isLogin ? undefined : (formData.get("name") as string);
|
const password = formData.get("password") as string; // gets password from form response
|
||||||
|
const name = isLogin ? undefined : (formData.get("name") as string);// if the form is in login mode, the name is undefine, otherwise the name is a value obtained from the response
|
||||||
|
|
||||||
const endpoint = isLogin ? "/api/login" : "/api/signup";
|
let endpoint = isLogin ? "/api/login" : "/api/signup"; // sets endpoint for backend code (either sign up or login)
|
||||||
const body = isLogin ? { email, password } : { name: name!, email, password };
|
const body = isLogin ? { email, password } : { name: name!, email, password };// creates a json body for the backend
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(endpoint, {
|
console.log("Sending data to API");
|
||||||
method: "POST",
|
const res = await fetch(endpoint, { // sends a request to the server at the end point
|
||||||
headers: { "Content-Type": "application/json" },
|
method: "POST", // Post is used since the form submission modifies the server side state
|
||||||
body: JSON.stringify(body),
|
headers: { "Content-Type": "application/json" }, //indicates it expects a json object returned
|
||||||
|
body: JSON.stringify(body), // converts the body to a JSON string to be sent
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) { //res.ok checks if the response is between 200-299
|
||||||
console.log("Success!");
|
console.log("Success!");
|
||||||
onClose();
|
onClose(); // closes UI
|
||||||
} else {
|
} else if (res.status === 401){
|
||||||
console.error("Error:", await res.text());
|
console.log("Incorrect details provided")
|
||||||
|
setIsFailed(true)
|
||||||
|
} else{
|
||||||
|
console.error("Error:", await res.text()); // logs error with error message sent to console
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {// catches any errors (e.g. Not connected to network)
|
||||||
console.error("Request failed:", error instanceof Error ? error.message : String(error));
|
console.error("Request failed:", error instanceof Error ? error.message : String(error));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -89,6 +140,14 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
{ isFailed && (
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-red-700">Incorrect email or password</label>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<button type="submit" className="w-full bg-blue-600 text-white p-2 rounded-md hover:bg-blue-700 transition">
|
<button type="submit" className="w-full bg-blue-600 text-white p-2 rounded-md hover:bg-blue-700 transition">
|
||||||
{isLogin ? "Login" : "Sign Up"}
|
{isLogin ? "Login" : "Sign Up"}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -1 +1,5 @@
|
|||||||
Users
|
name,email,password
|
||||||
|
Lukeshan Thananchayan,lukeshan@mail.com,TaylorSwift123
|
||||||
|
Emily Neighbour Carter,emily@mail.com,HarryStyles000
|
||||||
|
Izzy Patteron,izzy@mail.com,OliviaRodrigez420
|
||||||
|
Tim Howitz,tim@mail.com,TravisBarker182
|
||||||
|
Loading…
x
Reference in New Issue
Block a user