From 2d26693cd4e69df8287cffbae70625e96b8eba39 Mon Sep 17 00:00:00 2001 From: "K. Allagbe" Date: Thu, 12 Dec 2024 21:18:15 -0500 Subject: [PATCH] issue #360: get username from cookies --- package-lock.json | 18 ++++++++ package.json | 2 + public/locales/en/homePage.json | 40 +++++++++--------- public/locales/fr/homePage.json | 40 +++++++++--------- src/app/api/extract-label-data/route.ts | 2 +- src/app/api/inspections/[id]/route.ts | 2 +- src/app/api/inspections/route.ts | 3 +- src/app/api/login/route.ts | 22 ++++------ src/app/api/signup/route.ts | 22 ++++------ src/app/label-data-validation/[id]/page.tsx | 5 ++- src/app/label-data-validation/page.tsx | 9 ++-- src/app/page.tsx | 4 +- src/components/AuthComponents/RouteGuard.tsx | 21 ++++------ src/components/LabelDataValidator.tsx | 5 ++- src/components/OrganizationsForm.tsx | 2 +- ...on.test.ts => modelTransformation.test.ts} | 2 +- src/utils/client/apiErrors.ts | 17 ++++++++ src/utils/client/cookieHandler.ts | 0 src/utils/client/fieldValidation.ts | 18 ++++++++ .../{common.ts => modelTransformation.ts} | 41 +------------------ src/utils/server/{common.ts => apiErrors.ts} | 0 21 files changed, 137 insertions(+), 138 deletions(-) rename src/utils/client/__tests__/{common.test.ts => modelTransformation.test.ts} (99%) create mode 100644 src/utils/client/apiErrors.ts create mode 100644 src/utils/client/cookieHandler.ts create mode 100644 src/utils/client/fieldValidation.ts rename src/utils/client/{common.ts => modelTransformation.ts} (86%) rename src/utils/server/{common.ts => apiErrors.ts} (100%) diff --git a/package-lock.json b/package-lock.json index b0934778..ac6c1aff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "i18next-http-backend": "^2.6.2", "i18next-resources-to-backend": "^1.2.1", "jest-environment-jsdom": "^29.7.0", + "js-cookie": "^3.0.5", "next": "14.2.15", "react": "^18", "react-dom": "^18", @@ -37,6 +38,7 @@ "@testing-library/react": "^16.0.1", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.14", + "@types/js-cookie": "^3.0.6", "@types/node": "^20", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.1", @@ -2414,6 +2416,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/jsdom": { "version": "20.0.1", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", @@ -7322,6 +7331,15 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index 03b9ab26..0ddd665a 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "i18next-http-backend": "^2.6.2", "i18next-resources-to-backend": "^1.2.1", "jest-environment-jsdom": "^29.7.0", + "js-cookie": "^3.0.5", "next": "14.2.15", "react": "^18", "react-dom": "^18", @@ -51,6 +52,7 @@ "@testing-library/react": "^16.0.1", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.14", + "@types/js-cookie": "^3.0.6", "@types/node": "^20", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.1", diff --git a/public/locales/en/homePage.json b/public/locales/en/homePage.json index ec6227f8..d1447c66 100644 --- a/public/locales/en/homePage.json +++ b/public/locales/en/homePage.json @@ -1,23 +1,23 @@ { - "submit_button": "Submit", - "submit_button_disabled_hint": "You need to add a minimum of 1 file to start the analysis", - "fileList":{ - "uploadedfiles": "Uploaded Files", - "noUploadedfiles": "No uploaded files" - }, - "fileElement": { - "altText": { - "uploadedFileAlt": "Uploaded file picture", - "deleteFileAlt": "Delete file button icon" - } - }, - "dropzone": { - "altText": { - "hoveredImageAlt": "Hovered image", - "CloudIconAlt": "File upload icon" - }, - "dragDrop": "Drag and Drop Files", - "or": "OR", - "browseFile": "Browse Files" + "submitButton": "Submit", + "submitButtonDisabledHint": "You need to add a minimum of 1 file to start the analysis", + "fileList": { + "uploadedfiles": "Uploaded Files", + "noUploadedfiles": "No uploaded files" + }, + "fileElement": { + "altText": { + "uploadedFileAlt": "Uploaded file picture", + "deleteFileAlt": "Delete file button icon" } + }, + "dropzone": { + "altText": { + "hoveredImageAlt": "Hovered image", + "CloudIconAlt": "File upload icon" + }, + "dragDrop": "Drag and Drop Files", + "or": "OR", + "browseFile": "Browse Files" + } } diff --git a/public/locales/fr/homePage.json b/public/locales/fr/homePage.json index 1d4bba80..bf87e0ca 100644 --- a/public/locales/fr/homePage.json +++ b/public/locales/fr/homePage.json @@ -1,23 +1,23 @@ { - "submit_button": "Soumettre", - "submit_button_disabled_hint":"Vous devez ajouter au moins 1 fichier pour commencer l'analyse", - "fileList":{ - "uploadedfiles": "Fichiers téléversés", - "noUploadedfiles": "Aucun fichier téléversé" - }, - "fileElement": { - "altText": { - "uploadedFileAlt": "Image du fichier téléchargé", - "deleteFileAlt": "Icône du bouton pour supprimer un fichier" - } - }, - "dropzone":{ - "altText":{ - "hoveredImageAlt": "Image survolée", - "CloudIconAlt": "Icône de téléversement de fichier" - }, - "dragDrop": "Téléverser des fichiers", - "or": "OU", - "browseFile": "Parcourir les fichiers" + "submitButton": "Soumettre", + "submitButtonDisabledHint": "Vous devez ajouter au moins 1 fichier pour commencer l'analyse", + "fileList": { + "uploadedfiles": "Fichiers téléversés", + "noUploadedfiles": "Aucun fichier téléversé" + }, + "fileElement": { + "altText": { + "uploadedFileAlt": "Image du fichier téléchargé", + "deleteFileAlt": "Icône du bouton pour supprimer un fichier" } + }, + "dropzone": { + "altText": { + "hoveredImageAlt": "Image survolée", + "CloudIconAlt": "Icône de téléversement de fichier" + }, + "dragDrop": "Téléverser des fichiers", + "or": "OU", + "browseFile": "Parcourir les fichiers" + } } diff --git a/src/app/api/extract-label-data/route.ts b/src/app/api/extract-label-data/route.ts index 7d681c8b..94353629 100644 --- a/src/app/api/extract-label-data/route.ts +++ b/src/app/api/extract-label-data/route.ts @@ -1,5 +1,5 @@ +import { handleApiError } from "@/utils/server/apiErrors"; import { pipelineApi } from "@/utils/server/backend"; -import { handleApiError } from "@/utils/server/common"; export async function POST(request: Request) { const formData = await request.formData(); diff --git a/src/app/api/inspections/[id]/route.ts b/src/app/api/inspections/[id]/route.ts index aae55f3a..e0f9305f 100644 --- a/src/app/api/inspections/[id]/route.ts +++ b/src/app/api/inspections/[id]/route.ts @@ -1,5 +1,5 @@ +import { handleApiError } from "@/utils/server/apiErrors"; import { inspectionsApi } from "@/utils/server/backend"; -import { handleApiError } from "@/utils/server/common"; import { validate } from "uuid"; export async function GET( diff --git a/src/app/api/inspections/route.ts b/src/app/api/inspections/route.ts index cf914a4a..cf48e129 100644 --- a/src/app/api/inspections/route.ts +++ b/src/app/api/inspections/route.ts @@ -1,12 +1,11 @@ +import { handleApiError } from "@/utils/server/apiErrors"; import { inspectionsApi } from "@/utils/server/backend"; -import { handleApiError } from "@/utils/server/common"; export async function POST(request: Request) { const formData = await request.formData(); const files = formData.getAll("files") as File[]; const labelDataString = formData.get("labelData") as string; const labelData = JSON.parse(labelDataString); - const authHeader = request.headers.get("Authorization"); if (!authHeader) { diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts index 56e9df1a..772df7fe 100644 --- a/src/app/api/login/route.ts +++ b/src/app/api/login/route.ts @@ -1,7 +1,7 @@ +import { handleApiError } from "@/utils/server/apiErrors"; import { usersApi } from "@/utils/server/backend"; export async function POST(request: Request) { - const authHeader = request.headers.get("Authorization"); if (!authHeader) { return new Response( @@ -12,18 +12,12 @@ export async function POST(request: Request) { ); } - return usersApi.loginLoginPost({headers: { Authorization: authHeader }}).then((response) => { - return Response.json(response.data); - }).catch((error) => { - if (error.response) { - console.error("Error response:", error.response.data); - } else if (error.request) { - console.error("Error request:", error.request); - } else { - console.error("Error message:", error.message); - } - return new Response(JSON.stringify({ error: error.message }), { - status: error.status, + return usersApi + .loginLoginPost({ headers: { Authorization: authHeader } }) + .then((response) => { + return Response.json(response.data); + }) + .catch((error) => { + return handleApiError(error); }); - }); } diff --git a/src/app/api/signup/route.ts b/src/app/api/signup/route.ts index fa639259..c72f68e6 100644 --- a/src/app/api/signup/route.ts +++ b/src/app/api/signup/route.ts @@ -1,7 +1,7 @@ +import { handleApiError } from "@/utils/server/apiErrors"; import { usersApi } from "@/utils/server/backend"; export async function POST(request: Request) { - const authHeader = request.headers.get("Authorization"); if (!authHeader) { return new Response( @@ -12,18 +12,12 @@ export async function POST(request: Request) { ); } - return usersApi.signupSignupPost({headers: { Authorization: authHeader }}).then((response) => { - return Response.json(response.data); - }).catch((error) => { - if (error.response) { - console.error("Error response:", error.response.data); - } else if (error.request) { - console.error("Error request:", error.request); - } else { - console.error("Error message:", error.message); - } - return new Response(JSON.stringify({ error: error.message }), { - status: error.status, + return usersApi + .signupSignupPost({ headers: { Authorization: authHeader } }) + .then((response) => { + return Response.json(response.data); + }) + .catch((error) => { + return handleApiError(error); }); - }); } diff --git a/src/app/label-data-validation/[id]/page.tsx b/src/app/label-data-validation/[id]/page.tsx index 576d2aef..8f17e106 100644 --- a/src/app/label-data-validation/[id]/page.tsx +++ b/src/app/label-data-validation/[id]/page.tsx @@ -3,9 +3,10 @@ import LabelDataValidator from "@/components/LabelDataValidator"; import useAlertStore from "@/stores/alertStore"; import useUploadedFilesStore from "@/stores/fileStore"; import { DEFAULT_LABEL_DATA } from "@/types/types"; -import { mapInspectionToLabelData } from "@/utils/client/common"; +import { mapInspectionToLabelData } from "@/utils/client/modelTransformation"; import { Inspection } from "@/utils/server/backend"; import axios from "axios"; +import Cookies from "js-cookie"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { validate } from "uuid"; @@ -27,7 +28,7 @@ export default function Page({ params }: { params: { id: string } }) { return; } - const username = ""; + const username = atob(Cookies.get("token") ?? ""); const password = ""; const authHeader = "Basic " + btoa(`${username}:${password}`); const controller = new AbortController(); diff --git a/src/app/label-data-validation/page.tsx b/src/app/label-data-validation/page.tsx index 370f793e..07b3442c 100644 --- a/src/app/label-data-validation/page.tsx +++ b/src/app/label-data-validation/page.tsx @@ -3,12 +3,11 @@ import LabelDataValidator from "@/components/LabelDataValidator"; import useAlertStore from "@/stores/alertStore"; import useUploadedFilesStore from "@/stores/fileStore"; import { DEFAULT_LABEL_DATA } from "@/types/types"; -import { - mapLabelDataOutputToLabelData, - processAxiosError, -} from "@/utils/client/common"; +import { processAxiosError } from "@/utils/client/apiErrors"; +import { mapLabelDataOutputToLabelData } from "@/utils/client/modelTransformation"; import { Inspection, LabelDataOutput } from "@/utils/server/backend"; import axios from "axios"; +import Cookies from "js-cookie"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; @@ -37,7 +36,7 @@ function LabelDataValidationPage() { formData.append("files", file); }); - const username = ""; + const username = atob(Cookies.get("token") ?? ""); const password = ""; const authHeader = "Basic " + btoa(`${username}:${password}`); axios diff --git a/src/app/page.tsx b/src/app/page.tsx index f08b09eb..a1375d51 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -55,7 +55,7 @@ function HomePage() { > router.push("/label-data-validation")} > - {t("submit_button")} + {t("submitButton")} diff --git a/src/components/AuthComponents/RouteGuard.tsx b/src/components/AuthComponents/RouteGuard.tsx index bd1f992c..1b8d0148 100644 --- a/src/components/AuthComponents/RouteGuard.tsx +++ b/src/components/AuthComponents/RouteGuard.tsx @@ -1,9 +1,10 @@ -import { useEffect, useState } from "react"; -import SignUpModal from "@/components/AuthComponents/SignUpModal"; import LoginModal from "@/components/AuthComponents/LoginModal"; +import SignUpModal from "@/components/AuthComponents/SignUpModal"; +import useAlertStore from "@/stores/alertStore"; import axios, { AxiosError } from "axios"; +import Cookies from "js-cookie"; +import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import useAlertStore from "@/stores/alertStore"; const RouteGuard = ({ children }: Readonly<{ children: React.ReactNode }>) => { const [isAuth, setAuth] = useState(false); @@ -23,7 +24,7 @@ const RouteGuard = ({ children }: Readonly<{ children: React.ReactNode }>) => { }, ); if (res.status >= 200 && res.status < 300) { - document.cookie = "token=" + btoa(username) + "; SameSite=Strict;"; + Cookies.set("token", btoa(username), { sameSite: "Strict" }); setAuth(true); return ""; } @@ -61,7 +62,7 @@ const RouteGuard = ({ children }: Readonly<{ children: React.ReactNode }>) => { }, ); if (res.status >= 200 && res.status < 300) { - document.cookie = "token=" + btoa(username) + "; SameSite=Strict;"; + Cookies.set("token", btoa(username), { sameSite: "Strict" }); setAuth(true); return ""; } @@ -87,15 +88,7 @@ const RouteGuard = ({ children }: Readonly<{ children: React.ReactNode }>) => { }; useEffect(() => { - const cookieStore = new Map(); - const cookies = document.cookie.split(";"); - cookies.forEach((cookie) => { - const [key, value] = cookie.split("="); - if (key && value) { - cookieStore.set(key.trim(), value.trim()); - } - }); - setAuth(!!cookieStore.get("token")); + setAuth(!!Cookies.get("token")); }, []); return ( diff --git a/src/components/LabelDataValidator.tsx b/src/components/LabelDataValidator.tsx index eb55b405..63152956 100644 --- a/src/components/LabelDataValidator.tsx +++ b/src/components/LabelDataValidator.tsx @@ -12,7 +12,10 @@ import { StepStatus, } from "@/components/stepper"; import { FormComponentProps, LabelData } from "@/types/types"; -import { checkFieldArray, checkFieldRecord } from "@/utils/client/common"; +import { + checkFieldArray, + checkFieldRecord, +} from "@/utils/client/fieldValidation"; import useBreakpoints from "@/utils/client/useBreakpoints"; import { Box, Container, Typography } from "@mui/material"; import { useEffect, useState } from "react"; diff --git a/src/components/OrganizationsForm.tsx b/src/components/OrganizationsForm.tsx index ebd23af0..88259068 100644 --- a/src/components/OrganizationsForm.tsx +++ b/src/components/OrganizationsForm.tsx @@ -4,7 +4,7 @@ import { LabelData, Organization, } from "@/types/types"; -import { checkFieldRecord } from "@/utils/client/common"; +import { checkFieldRecord } from "@/utils/client/fieldValidation"; import AddIcon from "@mui/icons-material/Add"; import DeleteIcon from "@mui/icons-material/Delete"; import DoneAllIcon from "@mui/icons-material/DoneAll"; diff --git a/src/utils/client/__tests__/common.test.ts b/src/utils/client/__tests__/modelTransformation.test.ts similarity index 99% rename from src/utils/client/__tests__/common.test.ts rename to src/utils/client/__tests__/modelTransformation.test.ts index f94f84d9..d5f12e65 100644 --- a/src/utils/client/__tests__/common.test.ts +++ b/src/utils/client/__tests__/modelTransformation.test.ts @@ -12,7 +12,7 @@ import { verifiedItemPairInspectionValue, verifiedItemPairNutrientValue, verifiedTranslations, -} from "../common"; +} from "../modelTransformation"; describe("quantity", () => { it("should handle a valid PipelineInspectionValue", () => { diff --git a/src/utils/client/apiErrors.ts b/src/utils/client/apiErrors.ts new file mode 100644 index 00000000..d618c6dd --- /dev/null +++ b/src/utils/client/apiErrors.ts @@ -0,0 +1,17 @@ +import { AxiosError } from "axios"; + +export const processAxiosError = (error: AxiosError) => { + if (error.response) { + console.error("Error response:", error.response.data); + const responseData = error.response.data as { error: string }; + return responseData.error; + } + + if (error.request) { + console.error("Error request:", error.request); + return error.request; + } + + console.error("Error message:", error.message); + return error.message; +}; diff --git a/src/utils/client/cookieHandler.ts b/src/utils/client/cookieHandler.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/utils/client/fieldValidation.ts b/src/utils/client/fieldValidation.ts new file mode 100644 index 00000000..60d7512b --- /dev/null +++ b/src/utils/client/fieldValidation.ts @@ -0,0 +1,18 @@ +import { VerifiedField } from "@/types/types"; + +export const checkFieldRecord = ( + record: Record, + verified: boolean = true, +): boolean => { + return ( + record && + Object.values(record).every((field) => field.verified === verified) + ); +}; + +export const checkFieldArray = ( + fields: VerifiedField[], + verified: boolean = true, +): boolean => { + return fields.every((field) => field.verified === verified); +}; diff --git a/src/utils/client/common.ts b/src/utils/client/modelTransformation.ts similarity index 86% rename from src/utils/client/common.ts rename to src/utils/client/modelTransformation.ts index 6b307dda..f41aec58 100644 --- a/src/utils/client/common.ts +++ b/src/utils/client/modelTransformation.ts @@ -1,10 +1,4 @@ -import { - BilingualField, - LabelData, - Quantity, - VerifiedField, -} from "@/types/types"; -import { AxiosError } from "axios"; +import { BilingualField, LabelData, Quantity } from "@/types/types"; import { FertiscanDbMetadataInspectionValue, Inspection, @@ -13,39 +7,6 @@ import { PipelineInspectionValue, } from "../server/backend"; -export const checkFieldRecord = ( - record: Record, - verified: boolean = true, -): boolean => { - return ( - record && - Object.values(record).every((field) => field.verified === verified) - ); -}; - -export const checkFieldArray = ( - fields: VerifiedField[], - verified: boolean = true, -): boolean => { - return fields.every((field) => field.verified === verified); -}; - -export const processAxiosError = (error: AxiosError) => { - if (error.response) { - console.error("Error response:", error.response.data); - const responseData = error.response.data as { error: string }; - return responseData.error; - } - - if (error.request) { - console.error("Error request:", error.request); - return error.request; - } - - console.error("Error message:", error.message); - return error.message; -}; - export function quantity( val?: PipelineInspectionValue | FertiscanDbMetadataInspectionValue | null, ): Quantity { diff --git a/src/utils/server/common.ts b/src/utils/server/apiErrors.ts similarity index 100% rename from src/utils/server/common.ts rename to src/utils/server/apiErrors.ts