Fixed warehouse api auth and extended artefact type

This commit is contained in:
Tim Howitz 2025-05-19 18:05:40 +01:00
parent 3b2927e896
commit 8341d9d8ce
11 changed files with 91 additions and 121 deletions

8
package-lock.json generated
View File

@ -35,7 +35,7 @@
"react-leaflet": "^5.0.0",
"react-node": "^1.0.2",
"swr": "^2.3.3",
"zod": "^3.24.4"
"zod": "^3.25.3"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
@ -8095,9 +8095,9 @@
}
},
"node_modules/zod": {
"version": "3.25.0",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.0.tgz",
"integrity": "sha512-ficnZKUW0mlNivqeJkosTEkGbJ6NKCtSaOHGx5aXbtfeWMdRyzXLbAIn19my4C/KB7WPY/p9vlGPt+qpOp6c4Q==",
"version": "3.25.3",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.3.tgz",
"integrity": "sha512-VGZqnyYNrl8JpEJRZaFPqeVNIuqgXNu4cXZ5cOb6zEUO1OxKbRnWB4UdDIXMmiERWncs0yDQukssHov8JUxykQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"

View File

@ -38,7 +38,7 @@
"react-leaflet": "^5.0.0",
"react-node": "^1.0.2",
"swr": "^2.3.3",
"zod": "^3.24.4"
"zod": "^3.25.3"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
@ -52,4 +52,4 @@
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}
}

View File

@ -11,10 +11,7 @@ export async function POST(req: Request) {
try {
cookieStore = await cookies();
const token = cookieStore.get("jwt")?.value;
if (!token) {
return NextResponse.json({ error: "No JWT found" }, { status: 401 });
}
if (!token) return NextResponse.json({ error: "No JWT found" }, { status: 401 });
const payload = await verifyJwt({ token, secret: env.JWT_SECRET_KEY });

View File

@ -1,105 +1,46 @@
import { NextResponse } from "next/server";
import { JWTPayload } from "@appTypes/JWT";
import { cookies } from "next/headers";
import { PrismaClient } from "@prismaclient";
import { env } from "@utils/env";
import { verifyJwt } from "@utils/verifyJwt";
import { ExtendedArtefact } from "@appTypes/ApiTypes";
import { apiAuthMiddleware } from "@utils/apiAuthMiddleware";
const usingPrisma = false;
let prisma: PrismaClient;
if (usingPrisma) prisma = new PrismaClient();
// Artefact type
interface Artefact {
id: number;
name: string;
description: string;
location: string;
earthquakeId: string;
isRequired: boolean;
isSold: boolean;
isCollected: boolean;
dateAdded: string;
}
const prisma = new PrismaClient();
export async function POST(req: Request) {
try {
// todo fix, moron the token will be in the cookie header
const json = await req.json(); // Parse incoming JSON data
const { token } = json.body;
if (!token) return NextResponse.json({ message: "Unauthorised" }, { status: 401 });
await verifyJwt({ token, secret: env.JWT_SECRET_KEY });
const authResult = await apiAuthMiddleware();
if ("user" in authResult === false) return authResult; // Handle error response
const warehouseArtefacts: Artefact[] = [
{
id: 1,
name: "Solidified Lava Chunk",
description: "A chunk of solidified lava from the 2023 Iceland eruption.",
location: "Reykjanes, Iceland",
earthquakeId: "EQ2023ICL",
isRequired: true,
isSold: false,
isCollected: false,
dateAdded: "2025-05-04",
},
{
id: 2,
name: "Tephra Sample",
description: "Foreign debris from the 2022 Tonga volcanic eruption.",
location: "Tonga",
earthquakeId: "EQ2022TGA",
isRequired: false,
isSold: true,
isCollected: true,
dateAdded: "2025-05-03",
},
{
id: 3,
name: "Ash Sample",
description: "Volcanic ash from the 2021 La Palma eruption.",
location: "La Palma, Spain",
earthquakeId: "EQ2021LPA",
isRequired: false,
isSold: false,
isCollected: false,
dateAdded: "2025-05-04",
},
{
id: 4,
name: "Ground Soil",
description: "Soil sample from the 2020 Croatia earthquake site.",
location: "Zagreb, Croatia",
earthquakeId: "EQ2020CRO",
isRequired: true,
isSold: false,
isCollected: false,
dateAdded: "2025-05-02",
},
{
id: 5,
name: "Basalt Fragment",
description: "Basalt rock from the 2019 New Zealand eruption.",
location: "White Island, New Zealand",
earthquakeId: "EQ2019NZL",
isRequired: false,
isSold: true,
isCollected: false,
dateAdded: "2025-05-04",
},
];
const { user } = authResult;
let artefacts;
if (usingPrisma) artefacts = await prisma.artefact.findMany();
if (user.role !== "SCIENTIST" && user.role !== "ADMIN") {
return NextResponse.json({ message: "Not authorised" }, { status: 401 });
}
const artefacts = await prisma.artefact.findMany({
include: {
earthquake: true,
},
});
if (artefacts) {
return NextResponse.json({ message: "Got artefacts successfully", artefacts }, { status: 200 });
const extendedArtefacts: ExtendedArtefact[] = artefacts.map((x) => ({
...x,
location: x.earthquake.location,
date: x.earthquake.date,
}));
return NextResponse.json({ message: "Got artefacts successfully", artefacts: extendedArtefacts }, { status: 200 });
} else {
return NextResponse.json({ message: "Got earthquakes successfully", earthquakes: warehouseArtefacts }, { status: 200 });
// return NextResponse.json({ message: "Failed to get earthquakes" }, { status: 401 });
return NextResponse.json({ message: "Failed to get earthquakes" }, { status: 401 });
}
} catch (error) {
console.error("Error in artefacts endpoint:", error);
return NextResponse.json({ message: "Internal Server Error" }, { status: 500 });
} finally {
if (usingPrisma) await prisma.$disconnect();
await prisma.$disconnect();
}
}

View File

@ -2,12 +2,11 @@
import Image from "next/image";
import { Dispatch, SetStateAction, useCallback, useState } from "react";
import Artefact from "@appTypes/Artefact";
import { ExtendedArtefact } from "@appTypes/ApiTypes";
import { Currency } from "@appTypes/StoreModel";
import { useStoreState } from "@hooks/store";
// Artefacts Data
const artefacts: Artefact[] = [
const artefacts: ExtendedArtefact[] = [
{
id: 1,
name: "Golden Scarab",

View File

@ -3,17 +3,14 @@ import { Dispatch, SetStateAction, useMemo, useState } from "react";
import { FaTimes } from "react-icons/fa";
import { FaCalendarPlus, FaCartShopping, FaWarehouse } from "react-icons/fa6";
import { IoFilter, IoFilterCircleOutline, IoFilterOutline, IoToday } from "react-icons/io5";
import { ExtendedArtefact } from "@appTypes/ApiTypes";
// import { Artefact } from "@appTypes/Prisma";
import type { Artefact } from "@prismaclient";
interface WarehouseArtefact extends Artefact {
location: string;
}
// Warehouse Artefacts Data
const warehouseArtefacts: WarehouseArtefact[] = [
const extendedArtefacts: ExtendedArtefact[] = [
{
id: 1,
name: "Solidified Lava Chunk",
@ -699,7 +696,7 @@ export default function Warehouse() {
// Apply filters with loading state
const filteredArtefacts = useMemo(() => {
setIsFiltering(true);
const result = applyFilters(warehouseArtefacts, filters);
const result = applyFilters(extendedArtefacts, filters);
setIsFiltering(false);
return result;
}, [filters]);
@ -707,10 +704,10 @@ export default function Warehouse() {
const currentArtefacts = filteredArtefacts.slice(indexOfFirstArtefact, indexOfLastArtefact);
// Overview stats
const totalArtefacts = warehouseArtefacts.length;
const totalArtefacts = extendedArtefacts.length;
const today = new Date();
const artefactsAddedToday = warehouseArtefacts.filter((a) => a.createdAt.toDateString() === today.toDateString()).length;
const artefactsSoldToday = warehouseArtefacts.filter(
const artefactsAddedToday = extendedArtefacts.filter((a) => a.createdAt.toDateString() === today.toDateString()).length;
const artefactsSoldToday = extendedArtefacts.filter(
(a) => a.isSold && a.createdAt.toDateString() === today.toDateString()
).length;
@ -803,8 +800,11 @@ export default function Warehouse() {
</div>
</div>
</div>
{/* Modals */}
{/* // todo only admins and senior scientists can log, add not allowed modal for juniors */}
{/* // todo add new artefact/pallet saving */}
{/* // todo add existing artefact modifying */}
{/* // todo add pallet display */}
{showLogModal && <LogModal onClose={() => setShowLogModal(false)} />}
{showBulkLogModal && <BulkLogModal onClose={() => setShowBulkLogModal(false)} />}
{editArtefact && <EditModal artefact={editArtefact} onClose={() => setEditArtefact(null)} />}

View File

@ -10,6 +10,7 @@ interface AuthModalProps {
}
export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
// todo add login successful message
const [isLogin, setIsLogin] = useState<boolean>(true);
const modalRef = useRef<HTMLDivElement>(null);
const [isFailed, setIsFailed] = useState<boolean>(false);

8
src/types/ApiTypes.ts Normal file
View File

@ -0,0 +1,8 @@
import { Artefact } from "@prismaclient";
interface ExtendedArtefact extends Artefact {
location: string;
date: Date;
}
export type { ExtendedArtefact };

View File

@ -1,14 +0,0 @@
interface Artefact {
// todo change to string
id: number;
name: string;
description: string;
location: string;
earthquakeID: string;
observatory: string;
dateReleased: string;
image: string;
price: number;
}
export default Artefact;

5
src/types/JWT.ts Normal file
View File

@ -0,0 +1,5 @@
interface JWTPayload {
userId: number;
}
export type { JWTPayload };

View File

@ -0,0 +1,33 @@
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
import { verifyJwt } from "@utils/verifyJwt";
import { PrismaClient } from "@prismaclient";
import type { JWTPayload } from "@/types/JWT";
import { env } from "@utils/env";
const prisma = new PrismaClient();
export async function apiAuthMiddleware() {
const cookieStore = await cookies();
const token = cookieStore.get("jwt")?.value;
if (!token) {
return NextResponse.json({ error: "No JWT found" }, { status: 401 });
}
const payload = (await verifyJwt({
token,
secret: env.JWT_SECRET_KEY,
})) as unknown as JWTPayload;
const user = await prisma.user.findUnique({
where: { id: payload.userId },
});
if (!user) {
cookieStore.delete("jwt");
return NextResponse.json({ error: "Failed to get user" }, { status: 401 });
}
return { user, payload };
}