Added prisma stuff to auth api routes
This commit is contained in:
parent
668b8729a8
commit
67dfee01b2
103
package-lock.json
generated
103
package-lock.json
generated
@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^6.4.1",
|
"@prisma/client": "^6.4.1",
|
||||||
"@types/mapbox-gl": "^3.4.1",
|
"@types/mapbox-gl": "^3.4.1",
|
||||||
|
"axios": "^1.9.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
@ -2291,6 +2292,12 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/available-typed-arrays": {
|
"node_modules/available-typed-arrays": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||||
@ -2317,6 +2324,17 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
|
||||||
|
"integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/axobject-query": {
|
"node_modules/axobject-query": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
||||||
@ -2671,6 +2689,18 @@
|
|||||||
"color-support": "bin.js"
|
"color-support": "bin.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/commander": {
|
"node_modules/commander": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||||
@ -2961,6 +2991,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/delegates": {
|
"node_modules/delegates": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
@ -3274,7 +3313,6 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@ -4030,6 +4068,26 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.15.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||||
|
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.5",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||||
@ -4063,6 +4121,42 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/form-data/node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/form-data/node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
@ -4508,7 +4602,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-symbols": "^1.0.3"
|
"has-symbols": "^1.0.3"
|
||||||
@ -6431,6 +6524,12 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/punycode": {
|
"node_modules/punycode": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^6.4.1",
|
"@prisma/client": "^6.4.1",
|
||||||
"@types/mapbox-gl": "^3.4.1",
|
"@types/mapbox-gl": "^3.4.1",
|
||||||
|
"axios": "^1.9.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
|
|||||||
@ -1,31 +1,45 @@
|
|||||||
import { NextResponse } from "next/server";
|
import bcrypt from 'bcrypt';
|
||||||
import {User, readUserCsv, findUserByEmail} from "../functions/csvReadWrite"
|
import { NextResponse } from 'next/server';
|
||||||
import bcrypt from "bcrypt"
|
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
import { findUserByEmail, readUserCsv, User } from '../functions/csvReadWrite';
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
const usingPrisma = false;
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
try {
|
try {
|
||||||
const body = await request.json(); // Parse incoming JSON data
|
const body = await request.json(); // Parse incoming JSON data
|
||||||
const {email, password } = body;
|
const { email, password } = body;
|
||||||
console.log("Login API received data");
|
|
||||||
|
|
||||||
const userData = await readUserCsv();
|
const userData = await readUserCsv();
|
||||||
|
console.log(userData);
|
||||||
|
console.log("Email:", email); // ! remove
|
||||||
|
console.log("Password:", password); // ! remove
|
||||||
|
|
||||||
console.log(userData)
|
let foundUser;
|
||||||
console.log("Email:", email); // ! remove
|
|
||||||
console.log("Password:", password);// ! remove
|
|
||||||
|
|
||||||
const foundUser = findUserByEmail(userData,email)
|
if (usingPrisma) {
|
||||||
if (foundUser && await bcrypt.compare(password, foundUser.password)) {
|
foundUser = await prisma.user.findUnique({
|
||||||
console.log("User Details Correct")
|
where: {
|
||||||
return NextResponse.json({ message: "Login successful!" }, { status: 200 });
|
email: email, // use the email to uniquely identify the user
|
||||||
} else {
|
},
|
||||||
console.log("User email or password is invalid")
|
});
|
||||||
return NextResponse.json({ message: "Email and/or password are invalid" }, { status: 401 });
|
} else {
|
||||||
}
|
foundUser = findUserByEmail(userData, email);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundUser && (await bcrypt.compare(password, usingPrisma ? foundUser.hashedPassword : foundUser.password))) {
|
||||||
} catch (error) {
|
// todo remove password from returned user
|
||||||
console.error("Error in signup endpoint:", error);
|
return NextResponse.json({ message: "Login successful!", user: foundUser }, { status: 200 });
|
||||||
return NextResponse.json({ message: "Internal Server Error" }, { status: 500 });
|
} else {
|
||||||
}
|
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 });
|
||||||
|
} finally {
|
||||||
|
if (usingPrisma) await prisma.$disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,59 +1,84 @@
|
|||||||
import { NextResponse } from "next/server";
|
import bcrypt from 'bcrypt';
|
||||||
import {User, readUserCsv, writeUserCsv, findUserByEmail, passwordStrengthCheck} from "../functions/csvReadWrite";
|
import { NextResponse } from 'next/server';
|
||||||
import bcrypt from "bcrypt"
|
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
import {
|
||||||
|
findUserByEmail, passwordStrengthCheck, readUserCsv, User, writeUserCsv
|
||||||
|
} from '../functions/csvReadWrite';
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
const usingPrisma = false;
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
try {
|
try {
|
||||||
const body = await request.json(); // Parse incoming JSON data
|
const body = await request.json(); // Parse incoming JSON data
|
||||||
let {name, email, password } = body;
|
let { email, password, name } = body;
|
||||||
const accessLevel = "basic";
|
const accessLevel = "basic";
|
||||||
console.log("Signin API received data");
|
|
||||||
|
|
||||||
const userData = await readUserCsv();
|
const userData = await readUserCsv();
|
||||||
|
|
||||||
console.log(userData)
|
console.log(userData);
|
||||||
console.log("Name:", name); // ! remove
|
console.log("Name:", name); // ! remove
|
||||||
console.log("Email:", email); // ! remove
|
console.log("Email:", email); // ! remove
|
||||||
console.log("Password:", password);// ! remove
|
console.log("Password:", password); // ! remove
|
||||||
|
|
||||||
const foundUser = findUserByEmail(userData,email)
|
let foundUser;
|
||||||
|
|
||||||
if (foundUser) {
|
if (usingPrisma) {
|
||||||
console.log("Email already in the system")
|
foundUser = await prisma.user.findUnique({
|
||||||
return NextResponse.json({ message: "Sorry, this email is already in use" }, { status: 409 });
|
where: {
|
||||||
}
|
email: email, // use the email to uniquely identify the user
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
foundUser = findUserByEmail(userData, email);
|
||||||
|
}
|
||||||
|
|
||||||
const passwordCheckResult = await passwordStrengthCheck(password)
|
if (foundUser) {
|
||||||
|
return NextResponse.json({ message: "Sorry, this email is already in use" }, { status: 409 });
|
||||||
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 {
|
|
||||||
password = await bcrypt.hash(password, 10);
|
|
||||||
userData.push({name,email,password,accessLevel})
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const passwordCheckResult = await passwordStrengthCheck(password);
|
||||||
|
|
||||||
} catch (error) {
|
if (passwordCheckResult === "short") {
|
||||||
console.error("Error in signup endpoint:", error);
|
return NextResponse.json({ message: "Your password is shorter than 8 characters" }, { status: 400 });
|
||||||
return NextResponse.json({ message: "Internal Server Error" }, { status: 500 });
|
} 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 {
|
||||||
|
const passwordHash = await bcrypt.hash(password, 10);
|
||||||
|
if (usingPrisma) {
|
||||||
|
// todo add sending back newUser
|
||||||
|
const newUser = await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
passwordHash,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
userData.push({ name, email, password: passwordHash, accessLevel });
|
||||||
|
}
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error in signup endpoint:", error);
|
||||||
|
return NextResponse.json({ message: "Internal Server Error" }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,109 +1,53 @@
|
|||||||
// 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 axios from 'axios';
|
||||||
import { FormEvent, MouseEvent, useEffect, useRef, useState } from "react";
|
import { FormEvent, MouseEvent, useEffect, useRef, useState } 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; // bool for if the modal should be visible
|
isOpen: boolean; // bool for if the modal should be visible
|
||||||
onClose: () => void; //A function that will be executed to close the modal
|
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);
|
const [isFailed, setIsFailed] = useState<boolean>(false);
|
||||||
const [failMessage, setFailMessage] = 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.
|
|
||||||
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); // runs when isOpen changes, if it is true, the login is shown
|
if (isOpen) setIsLogin(true); // runs when isOpen changes, if it is true, the login is shown
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
if (!isOpen) return null; // if is open is false, the model isnt shown
|
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(); // stops page from refreshing
|
e.preventDefault(); // stops page from refreshing
|
||||||
setIsFailed(false);
|
setIsFailed(false);
|
||||||
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 formData = new FormData(e.currentTarget);
|
||||||
const email = formData.get("email") as string; // gets email from form response
|
const email = formData.get("email") as string;
|
||||||
const password = formData.get("password") as string; // gets password from form response
|
const password = formData.get("password") as string;
|
||||||
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 name = isLogin ? undefined : (formData.get("name") as string);
|
||||||
|
|
||||||
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 }; // creates a json body for the backend
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log("Sending data to API");
|
const res = await axios.post(`/api/${isLogin ? "login" : "signup"}`, {
|
||||||
const res = await fetch(endpoint, {
|
headers: { "Content-Type": "application/json" },
|
||||||
// sends a request to the server at the end point
|
body: isLogin ? { email, password } : { name: name!, email, password },
|
||||||
method: "POST", // Post is used since the form submission modifies the server side state
|
|
||||||
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.status) {
|
||||||
//res.ok checks if the response is between 200-299
|
onClose();
|
||||||
console.log("Success!");
|
|
||||||
onClose(); // closes UI
|
|
||||||
} else if (res.status >= 400 && res.status < 500) {
|
} else if (res.status >= 400 && res.status < 500) {
|
||||||
const responseBody = await res.json();
|
console.log("4xx error:", res.data);
|
||||||
console.log("4xx error:", responseBody.message);
|
setFailMessage(res.data.message);
|
||||||
setFailMessage(responseBody.message);
|
|
||||||
setIsFailed(true);
|
setIsFailed(true);
|
||||||
} else {
|
} else {
|
||||||
console.error("Error:", await res.text()); // logs error with error message sent to console
|
console.error("Error:", await res.data.message);
|
||||||
}
|
}
|
||||||
} 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));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,67 +1,66 @@
|
|||||||
// Datasource configuration
|
// Datasource configuration
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "sqlserver"
|
provider = "sqlserver"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
// User model
|
// User model
|
||||||
model User {
|
model User {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
email String @unique
|
email String @unique
|
||||||
passwordHash String
|
passwordHash String
|
||||||
firstname String
|
name String
|
||||||
surname String
|
role String @default("USER") @db.VarChar(10)
|
||||||
role String @default("USER") @db.VarChar(10)
|
scientist Scientist? @relation // Optional relation to Scientist
|
||||||
scientist Scientist? @relation // Optional relation to Scientist
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Earthquake model
|
// Earthquake model
|
||||||
model Earthquake {
|
model Earthquake {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
name String @db.VarChar(255)
|
name String @db.VarChar(255)
|
||||||
date DateTime
|
date DateTime
|
||||||
location String
|
location String
|
||||||
magnitude Float
|
magnitude Float
|
||||||
depth Float
|
depth Float
|
||||||
casualties Int
|
casualties Int
|
||||||
creatorId Int? // Creator's ID (Foreign Key referencing Scientist)
|
creatorId Int? // Creator's ID (Foreign Key referencing Scientist)
|
||||||
creator Scientist? @relation("ScientistEarthquakeCreator", fields: [creatorId], references: [id], onDelete: NoAction, onUpdate: NoAction) // Points back to Scientist
|
creator Scientist? @relation("ScientistEarthquakeCreator", fields: [creatorId], references: [id], onDelete: NoAction, onUpdate: NoAction) // Points back to Scientist
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observatory model
|
// Observatory model
|
||||||
model Observatory {
|
model Observatory {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
name String @db.VarChar(255)
|
name String @db.VarChar(255)
|
||||||
location String
|
location String
|
||||||
longitude String
|
longitude String
|
||||||
latitude String
|
latitude String
|
||||||
dateEstablished Int?
|
dateEstablished Int?
|
||||||
functional Boolean
|
functional Boolean
|
||||||
description String? @db.Text
|
description String? @db.Text
|
||||||
creatorId Int? // Creator's ID (Foreign Key referencing Scientist)
|
creatorId Int? // Creator's ID (Foreign Key referencing Scientist)
|
||||||
creator Scientist? @relation("ScientistObservatoryCreator", fields: [creatorId], references: [id], onDelete: NoAction, onUpdate: NoAction) // Points back to Scientist
|
creator Scientist? @relation("ScientistObservatoryCreator", fields: [creatorId], references: [id], onDelete: NoAction, onUpdate: NoAction) // Points back to Scientist
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scientist model
|
// Scientist model
|
||||||
model Scientist {
|
model Scientist {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
name String
|
name String
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
level String @db.VarChar(10) // Junior or Senior
|
level String @db.VarChar(10) // Junior or Senior
|
||||||
user User @relation(fields: [userId], references: [id]) // Relates to the User model
|
user User @relation(fields: [userId], references: [id]) // Relates to the User model
|
||||||
userId Int @unique
|
userId Int @unique
|
||||||
|
|
||||||
// Self-referencing relation: superior and subordinates
|
// Self-referencing relation: superior and subordinates
|
||||||
superior Scientist? @relation("SuperiorRelation", fields: [superiorId], references: [id], onDelete: NoAction, onUpdate: NoAction) // Parent scientist
|
superior Scientist? @relation("SuperiorRelation", fields: [superiorId], references: [id], onDelete: NoAction, onUpdate: NoAction) // Parent scientist
|
||||||
superiorId Int?
|
superiorId Int?
|
||||||
subordinates Scientist[] @relation("SuperiorRelation") // Scientists who view this one as a superior
|
subordinates Scientist[] @relation("SuperiorRelation") // Scientists who view this one as a superior
|
||||||
|
|
||||||
// Earthquake and Observatory relations
|
// Earthquake and Observatory relations
|
||||||
earthquakes Earthquake[] @relation("ScientistEarthquakeCreator") // Scientists can create earthquakes
|
earthquakes Earthquake[] @relation("ScientistEarthquakeCreator") // Scientists can create earthquakes
|
||||||
observatories Observatory[] @relation("ScientistObservatoryCreator") // Scientists can create observatories
|
observatories Observatory[] @relation("ScientistObservatoryCreator") // Scientists can create observatories
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user