From 3b5ac81cc338558a646652fc6c75d462c286b227 Mon Sep 17 00:00:00 2001 From: toqvist Date: Sat, 6 Apr 2024 17:28:36 +0200 Subject: [PATCH] Auth flow functional --- astro/public/favicon.svg | 12 +- astro/public/kios-logo.png | Bin 0 -> 3526 bytes astro/public/lumbung-logo.png | Bin 0 -> 7269 bytes astro/public/route-guide.png | Bin 0 -> 8937 bytes astro/src/astroTypes.ts | 80 ++++ astro/src/components/KiosMap.tsx | 542 +++++++++++---------------- astro/src/components/LoginForm.tsx | 143 +++++++ astro/src/components/ui/form.tsx | 6 +- astro/src/types.ts | 76 ++-- astro/src/utils/authUtils.ts | 12 + astro/src/utils/hooks.ts | 114 ++++++ astro/yarn.lock | 28 +- payload/src/collections/Makers.ts | 8 +- payload/src/collections/Retailers.ts | 6 + payload/src/collections/Users.ts | 24 +- payload/src/server.ts | 1 + 16 files changed, 646 insertions(+), 406 deletions(-) create mode 100644 astro/public/kios-logo.png create mode 100644 astro/public/lumbung-logo.png create mode 100644 astro/public/route-guide.png create mode 100644 astro/src/astroTypes.ts create mode 100644 astro/src/components/LoginForm.tsx create mode 100644 astro/src/utils/authUtils.ts diff --git a/astro/public/favicon.svg b/astro/public/favicon.svg index f157bd1..d2e98b6 100644 --- a/astro/public/favicon.svg +++ b/astro/public/favicon.svg @@ -1,9 +1,5 @@ - - - + + + + diff --git a/astro/public/kios-logo.png b/astro/public/kios-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7e7da723893272761fbb6096591dba778eafe8fb GIT binary patch literal 3526 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 _LS33qGYCCMH8a$*F^ZQywXvNRyei z^D-xWJtHjrwnN!{%nrQ#XuK-SGvm|{D$A_xDQ==IWaNxI_YvJW>$L7n7ss`n;)(1s z(l%D`IaJ|0fi6mKi@g20@!pTOkj(_Nu6eM6xO2rJ6VHs$LWY5!!-P(YzlFTQujw7r znPUheFQDUnYy)w;fqYF&;rua;q=z~gdlYl>+2Q%^tM8Gw2Di|q6{bZZ9Tf1)NG;@8 z$TQnxSWI)%8QPPGGyr@x2NG@M&e7J1}2Of z(#X-{FNAM0)7FDuxeoJqV`F7wQJ#|C$sUU9-5oP>i+tvE-IBDC^z}@A)M1Smsx}Jg zbCA#!;EPo>5-4YFoWnZO`1t)+Do0N z#e5j(Ei-ml`EAtW$k3xqgGlv+Ne;VeiGeN-<<@j$U>$UBCDz13Zgi<@ za8jiQkJ9P6BrVE8#z39O4tW&OohSF*`(CvI5Ha);2X?`!Qj4sjcHCe><{ui z!mrgmXPoL9OvB?re!#R)M`!SSTiV^pcl2VAPZ-LK{(bG~(gt*+@9*n{uh*T&$>QEPOWS1Pk&kneODPcS%a>qYOC3LD8okTfFF zi4%iLrNYJ`ZIoN>nYBt~Y8)<~+w-U+S!Kce250p41(@lhng9R*07*qoM6N<$f^0O# AF8}}l literal 0 HcmV?d00001 diff --git a/astro/public/lumbung-logo.png b/astro/public/lumbung-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1a8efb9fb621d4a53bf2550a1f1e6a431208da6c GIT binary patch literal 7269 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 literal 0 HcmV?d00001 diff --git a/astro/public/route-guide.png b/astro/public/route-guide.png new file mode 100644 index 0000000000000000000000000000000000000000..688eb2f82445673c97e9c38965fd811cefd2519a GIT binary patch literal 8937 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 { - const url = `${API_URL}/api/makers` - console.log("Fetching url:", url) - const response = await axios.get(url); - - const makers: Maker[] = response.data.docs; - console.log(`Fetch result from ${url}`, makers) - return makers; - -} - -const useGetMakers = () => { - return useQuery({ - queryFn: () => getMakers(), - queryKey: ['makers'], - enabled: true - }) -} - -const getRetailers = async () => { - const url = `${API_URL}/api/retailers` - console.log("Fetching url:", url) - const response = await axios.get(url); - - const retailers: Retailer[] = response.data.docs; - console.log(`Fetch result from ${url}`, retailers) - return retailers; - -} - -const useGetRetailers = () => { - return useQuery({ - queryFn: () => getRetailers(), - queryKey: ['retailers'], - enabled: true - }) -} - -const getDispatches = async () => { - const url = `${API_URL}/api/dispatches` - console.log("Fetching url:", url) - const response = await axios.get(url); - - const dispatches: Dispatch[] = response.data.docs; - - console.log(`Fetch result from ${url}`, dispatches) - return dispatches; - -} - -const useGetDispatches = () => { - return useQuery({ - queryFn: () => getDispatches(), - queryKey: ['dispatches'], - enabled: true - }) -} +import { LoginForm } from './LoginForm'; +import { hasAuthCookie } from '@/utils/authUtils'; //Payload longitude and latitude are mislabeled in payload (lol) const locationSwitcharoo = (location: number[]) => { @@ -206,10 +59,14 @@ interface NodeSelection { export const KiosMap = () => { + const [authToken, setAuthToken] = useState('') + const { data: makers, isLoading: isLoadingMakers } = useGetMakers(); const { data: retailers, isLoading: isLoadingRetailers } = useGetRetailers(); const { data: dispatches, isLoading: isLoadingDispatches } = useGetDispatches(); + const { data: myself, isLoading: isLoadingMyself } = useGetMyself(authToken); + const [selectedNode, setSelectedNode] = useState({ id: "", type: "none" }) let selectedMaker: Maker | undefined = undefined; @@ -267,31 +124,64 @@ export const KiosMap = () => { ); return ( -
- { - selectedNode.type !== 'none' && ( -
-
- {selectedMaker !== undefined && ( -
- - {(selectedMaker.stock !== undefined && selectedMaker.stock.length > 0) && - - - See catalogue - +
+ +
+ + + {(myself && myself.name) && +

Logged in as: {myself.name}

+ } + { + (!hasAuthCookie() && !authToken) && + + Login + + } +
- - - {selectedMaker.name}'s stock - + + + Login + + + +
+ +
+
+
+ +
+
+ { + selectedNode.type !== 'none' && ( +
+
+ {selectedMaker !== undefined && ( +
+ + {(selectedMaker.stock !== undefined && selectedMaker.stock.length > 0) && + + + See catalogue + + + + + {selectedMaker.name}'s stock
    {selectedMaker.stock.map((product, i) => { return ( @@ -322,175 +212,175 @@ export const KiosMap = () => { ) })}
- -
-
-
- } -
- )} - - {selectedRetailer !== undefined && ( - <> - - - )} - - {selectedDispatch !== undefined && ( -
-
-

- Product{selectedDispatch.products.length > 1 && 's'} -

- {selectedDispatch.products.map((product, i) => { - return ( -
- {product.picture.alt} -

{product.name}

-
- ) - })} + + +
+ }
+ )} + {selectedRetailer !== undefined && ( + <> + + + )} - + {selectedDispatch !== undefined && ( +
+
+

+ Product{selectedDispatch.products.length > 1 && 's'} +

+ {selectedDispatch.products.map((product, i) => { + return ( +
+ {product.picture.alt} +

{product.name}

+
+ ) + })} +
- - - {selectedDispatch.courier !== undefined ? ( - ) : -
-

No courier!

- -
- } -
- )} + + + + {selectedDispatch.courier !== undefined ? ( + + + ) : +
+

No courier!

+ +
+ } +
+ )} +
- - ) - } - - - handleSelectNode("", "none") - }} - attribution='© OpenStreetMap' - /> - - {(makers && !isLoadingMakers) && - - {makers.map((maker: any, index: number) => ( - handleSelectNode(maker.id, "maker") - }} - key={maker.id} - position={[locationSwitcharoo(maker.location)[0], locationSwitcharoo(maker.location)[1]]} - icon={selectedNode.id === maker.id ? selectedDotIcon : blackDotIcon} - > - {/* {maker.name} */} - - ))} - + ) } - {(retailers && !isLoadingRetailers) && - - {retailers.map((retailer: any, index: number) => ( - handleSelectNode(retailer.id, "retailer") - }} - key={retailer.id} - position={[locationSwitcharoo(retailer.location)[0], locationSwitcharoo(retailer.location)[1]]} - icon={selectedNode.id === retailer.id ? selectedDotIcon : blackDotIcon} - > - {/* {retailer.name} */} - - ))} - - } + + handleSelectNode("", "none") + }} + attribution='© OpenStreetMap' + /> - {(dispatches && !isLoadingDispatches) && - - {dispatches.map((dispatch: any, index: number) => { + {(makers && !isLoadingMakers) && + + {makers.map((maker: any, index: number) => ( + handleSelectNode(maker.id, "maker") + }} + key={maker.id} + position={[locationSwitcharoo(maker.location)[0], locationSwitcharoo(maker.location)[1]]} + icon={selectedNode.id === maker.id ? selectedDotIcon : blackDotIcon} + > + {/* {maker.name} */} + + ))} + + } - if (dispatch.maker && dispatch.retailer) { + {(retailers && !isLoadingRetailers) && + + {retailers.map((retailer: any, index: number) => ( + handleSelectNode(retailer.id, "retailer") + }} + key={retailer.id} + position={[locationSwitcharoo(retailer.location)[0], locationSwitcharoo(retailer.location)[1]]} + icon={selectedNode.id === retailer.id ? selectedDotIcon : blackDotIcon} + > + {/* {retailer.name} */} + + ))} + + } - const start = locationSwitcharoo(dispatch.maker.location); - const end = locationSwitcharoo(dispatch.retailer.location); + {(dispatches && !isLoadingDispatches) && + + {dispatches.map((dispatch: any, index: number) => { + + if (dispatch.maker && dispatch.retailer) { + + const start = locationSwitcharoo(dispatch.maker.location); + const end = locationSwitcharoo(dispatch.retailer.location); - let productsString = ''; - dispatch.products.forEach((product: any, i: number) => { - productsString += product.productTitle + (i + 1 < dispatch.products.length ? ', ' : ''); - }); + let productsString = ''; + dispatch.products.forEach((product: any, i: number) => { + productsString += product.productTitle + (i + 1 < dispatch.products.length ? ', ' : ''); + }); - //status type should already be inferred when list of dispatches is created, weird that is is required - const status: DispatchStatus = dispatch.status; + //status type should already be inferred when list of dispatches is created, weird that is is required + const status: DispatchStatus = dispatch.status; - const dashArray: string = dashArrays[status] - const dashColor: string = dashColors[status] - const dashOpacity: number = dashOpacities[status] + const dashArray: string = dashArrays[status] + const dashColor: string = dashColors[status] + const dashOpacity: number = dashOpacities[status] - return ( - handleSelectNode(dispatch.id, "dispatch") - }} - key={dispatch.id} - positions={[[start[0], start[1]], [end[0], end[1]]]} - pathOptions={{ color: selectedNode.id === dispatch.id ? dashColorSelected : dashColor }} - opacity={dashOpacity} - dashArray={dashArray} /> - ); - } - })} - - } - + return ( + handleSelectNode(dispatch.id, "dispatch") + }} + key={dispatch.id} + positions={[[start[0], start[1]], [end[0], end[1]]]} + pathOptions={{ color: selectedNode.id === dispatch.id ? dashColorSelected : dashColor }} + opacity={dashOpacity} + dashArray={dashArray} /> + ); + } + })} + + } + + ); }; diff --git a/astro/src/components/LoginForm.tsx b/astro/src/components/LoginForm.tsx new file mode 100644 index 0000000..3344fa4 --- /dev/null +++ b/astro/src/components/LoginForm.tsx @@ -0,0 +1,143 @@ +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { z } from "zod" + +import { Button } from "@/components/ui/Button" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { Input } from "@/components/ui/input" +import axios from "axios"; +import { setAuthCookie } from "@/utils/authUtils" + +const API_URL = "http://localhost:3001"; + +const headers = { + "Content-Type": "application/json", + "Access-Control-Allow-Credentials": "true" +} + +const loginFetch = async (email: string, password: string) => { + try { + const response = await fetch(`${API_URL}/api/users/login`, { + method: "POST", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + email: email, + password: password + }), + }) + } catch (err) { + console.log(err) + } +}; + +interface LoginFormProps { + setAuthToken: (token: string) => void; + authToken: string; +} + +export function LoginForm(props: LoginFormProps) { + + + const login = async (email: string, password: string) => { + try { + const response = await axios.post(`${API_URL}/api/users/login`, { + email: email, + password: password + }, { + withCredentials: true, // include cookies in the request + headers: headers + }); + const data = response.data; + + if (response.status !== 200) { + throw Error("Login failed") + } + + setAuthCookie(data.token, 30); + props.setAuthToken(data.token) + return + + } catch (error) { + window.alert(error) + } + }; + + + const formSchema = z.object({ + email: z.string().email({ + message: "Email must be valid" + }), + password: z.string().min(1, { + }), + }) + + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + email: "", + password: "", + }, + }) + + function onSubmit(values: z.infer) { + login(values.email, values.password) + } + + return ( +
+ {props.authToken ?

Login successful

+ : +
+
+ + ( + + Email + + + + + + + + )} + /> + ( + + Password + + + + + + + + )} + /> + + + +
+ } +
+ ) +} diff --git a/astro/src/components/ui/form.tsx b/astro/src/components/ui/form.tsx index d03ab7b..719d6a3 100644 --- a/astro/src/components/ui/form.tsx +++ b/astro/src/components/ui/form.tsx @@ -3,9 +3,9 @@ import * as LabelPrimitive from "@radix-ui/react-label" import { Slot } from "@radix-ui/react-slot" import { Controller, - ControllerProps, - FieldPath, - FieldValues, + type ControllerProps, + type FieldPath, + type FieldValues, FormProvider, useFormContext, } from "react-hook-form" diff --git a/astro/src/types.ts b/astro/src/types.ts index 07caf0d..f4cd53e 100755 --- a/astro/src/types.ts +++ b/astro/src/types.ts @@ -22,8 +22,6 @@ export interface User { id: string; name: string; phoneNumber?: number; - adminOfMakers?: string[] | Maker[]; - adminOfRetailers?: string[] | Retailer[]; updatedAt: string; createdAt: string; email: string; @@ -35,28 +33,6 @@ export interface User { lockUntil?: string; password?: string; } -export interface Maker { - id: string; - name: string; - phoneNumber?: string; - email?: string; - /** - * @minItems 2 - * @maxItems 2 - */ - location: [number, number]; - stock?: string[] | Product[]; - updatedAt: string; - createdAt: string; -} -export interface Product { - id: string; - name: string; - picture: string | Media; - weight?: number; - updatedAt: string; - createdAt: string; -} export interface Media { id: string; alt?: string; @@ -69,20 +45,6 @@ export interface Media { width?: number; height?: number; } -export interface Retailer { - id: string; - name: string; - phoneNumber?: string; - email?: string; - /** - * @minItems 2 - * @maxItems 2 - */ - location: [number, number]; - stock?: string[] | Product[]; - updatedAt: string; - createdAt: string; -} export interface Courier { id: string; updatedAt: string; @@ -99,3 +61,41 @@ export interface Dispatch { updatedAt: string; createdAt: string; } +export interface Product { + id: string; + name: string; + picture: string | Media; + weight?: number; + updatedAt: string; + createdAt: string; +} +export interface Maker { + id: string; + name: string; + phoneNumber?: string; + email?: string; + /** + * @minItems 2 + * @maxItems 2 + */ + location: [number, number]; + admins?: string[] | User[]; + stock?: string[] | Product[]; + updatedAt: string; + createdAt: string; +} +export interface Retailer { + id: string; + name: string; + phoneNumber?: string; + email?: string; + /** + * @minItems 2 + * @maxItems 2 + */ + location: [number, number]; + admins?: string[] | User[]; + stock?: string[] | Product[]; + updatedAt: string; + createdAt: string; +} diff --git a/astro/src/utils/authUtils.ts b/astro/src/utils/authUtils.ts new file mode 100644 index 0000000..441e147 --- /dev/null +++ b/astro/src/utils/authUtils.ts @@ -0,0 +1,12 @@ +export const hasAuthCookie = () => { + const matches = document.cookie.match(/^(.*;)?\s*payload-token\s*=\s*[^;]+(.*)?$/) || [] + const hasMatch = matches.length > 0 + return hasMatch; +} + +export const setAuthCookie = (value: string, expirationDays: number) => { + const date = new Date(); + date.setTime(date.getTime() + (expirationDays * 24 * 60 * 60 * 1000)); + const expires = "expires=" + date.toUTCString(); + document.cookie = "payload-token=" + value + ";" + expires + ";path=/"; +} \ No newline at end of file diff --git a/astro/src/utils/hooks.ts b/astro/src/utils/hooks.ts index e69de29..6194167 100644 --- a/astro/src/utils/hooks.ts +++ b/astro/src/utils/hooks.ts @@ -0,0 +1,114 @@ +import type { User, Node, Retailer, Maker, Product, Dispatch } from '../astroTypes'; +import { useQuery, useMutation, useQueryClient, queryOptions } from "@tanstack/react-query"; +import axios from "axios"; +import { hasAuthCookie } from './authUtils'; + +const API_URL = "http://localhost:3001" + +const nonAuthHeaders = { + "Content-Type": "application/json", +} + +const authHeaders = { + "Content-Type": "application/json", +} + +const getMakers = async () => { + const url = `${API_URL}/api/makers` + console.log("Fetching url:", url) + const response = await axios.get(url); + + const makers: Maker[] = response.data.docs; + console.log(`Fetch result from ${url}`, makers) + return makers; + +} + +export const useGetMakers = () => { + return useQuery({ + queryFn: () => getMakers(), + queryKey: ['makers'], + enabled: true + }) +} + +const getRetailers = async () => { + const url = `${API_URL}/api/retailers` + console.log("Fetching url:", url) + const response = await axios.get(url); + + const retailers: Retailer[] = response.data.docs; + console.log(`Fetch result from ${url}`, retailers) + return retailers; + +} + +export const useGetRetailers = () => { + return useQuery({ + queryFn: () => getRetailers(), + queryKey: ['retailers'], + enabled: true + }) +} + +const getUser = async (userId: string) => { + const url = `${API_URL}/api/users/${userId}` + console.log("Fetching url:", url) + const response = await axios.get(url); + + const user: User = response.data.docs; + console.log(`Fetch result from ${url}`, user) + return user; + +} + +export const useGetUser = (userId: string) => { + return useQuery({ + queryFn: () => getUser(userId), + queryKey: ['user'], + enabled: true//If login cookie + }) +} + +const getMyself = async (authToken: string) => { + const url = `${API_URL}/api/users/me` + console.log("Fetching url:", url) + + const response = await axios.get(`${API_URL}/api/users/me`, { + withCredentials: true, + headers: authHeaders + }); + + const user: User = response.data.docs; + console.log(`Fetch result from ${url}`, user) + return user; + +} + +export const useGetMyself = (authToken: string) => { + return useQuery({ + queryFn: () => getMyself(authToken), + queryKey: ['myself'], + enabled: authToken !== '' + }) +} + +const getDispatches = async () => { + const url = `${API_URL}/api/dispatches` + console.log("Fetching url:", url) + const response = await axios.get(url); + + const dispatches: Dispatch[] = response.data.docs; + + console.log(`Fetch result from ${url}`, dispatches) + return dispatches; + +} + +export const useGetDispatches = () => { + return useQuery({ + queryFn: () => getDispatches(), + queryKey: ['dispatches'], + enabled: true + }) +} \ No newline at end of file diff --git a/astro/yarn.lock b/astro/yarn.lock index c7ea311..4ba7848 100644 --- a/astro/yarn.lock +++ b/astro/yarn.lock @@ -894,17 +894,17 @@ resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.2.4.tgz#1b380fad8eaccc6bec4ab1c265d70413e66e8034" integrity sha512-ClaUWpt8oTzjcF0MM1P81AeWyzc1sNSJlAjMG80CbwqbFqXSNz+NpQVUC0icobt3sZn43Sn27M4pHD/Jmp3zHw== -"@tanstack/query-core@5.28.13": - version "5.28.13" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.28.13.tgz#15c187c23b87a393e91d0fd2ea6dfc22b8a85b75" - integrity sha512-C3+CCOcza+mrZ7LglQbjeYEOTEC3LV0VN0eYaIN6GvqAZ8Foegdgch7n6QYPtT4FuLae5ALy+m+ZMEKpD6tMCQ== +"@tanstack/query-core@5.29.0": + version "5.29.0" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.29.0.tgz#d0b3d12c07d5a47f42ab0c1ed4f317106f3d4b20" + integrity sha512-WgPTRs58hm9CMzEr5jpISe8HXa3qKQ8CxewdYZeVnA54JrPY9B1CZiwsCoLpLkf0dGRZq+LcX5OiJb0bEsOFww== "@tanstack/react-query@^5.28.14": - version "5.28.14" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.28.14.tgz#9585b6300eb8f167ed374e2748043dc8d6476709" - integrity sha512-cZqt03Igb3I9tM72qNX5TAAmeYl75Z+k4Mv92VkXIXc2hCrv0fIywd7GN3JV1BBJl4mr7Cc+OOKKOPy8sNVOkA== + version "5.29.0" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.29.0.tgz#42b3a2de4ed1d63666f0af04392a34b5e70d49c0" + integrity sha512-yxlhHB73jaBla6h5B6zPaGmQjokkzAhMHN4veotkPNiQ3Ac/mCxgABRZPsJJrgCTvhpcncBZcDBFxaR2B37vug== dependencies: - "@tanstack/query-core" "5.28.13" + "@tanstack/query-core" "5.29.0" "@testing-library/dom@^9.0.0": version "9.3.4" @@ -1018,9 +1018,9 @@ "@types/unist" "^2" "@types/node@*": - version "20.12.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.4.tgz#af5921bd75ccdf3a3d8b3fa75bf3d3359268cd11" - integrity sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw== + version "20.12.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.5.tgz#74c4f31ab17955d0b5808cdc8fd2839526ad00b3" + integrity sha512-BD+BjQ9LS/D8ST9p5uqBxghlN+S42iuNxjsUGjeZobe/ciXzk2qb1B6IXc6AnRLS+yFJRpN2IPEHMzwspfDJNw== dependencies: undici-types "~5.26.4" @@ -1867,9 +1867,9 @@ eastasianwidth@^0.2.0: integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== electron-to-chromium@^1.4.668: - version "1.4.728" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.728.tgz#ac54d9d1b38752b920ec737a48c83dec2bf45ea1" - integrity sha512-Ud1v7hJJYIqehlUJGqR6PF1Ek8l80zWwxA6nGxigBsGJ9f9M2fciHyrIiNMerSHSH3p+0/Ia7jIlnDkt41h5cw== + version "1.4.729" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.729.tgz#8477d21e2a50993781950885b2731d92ad532c00" + integrity sha512-bx7+5Saea/qu14kmPTDHQxkp2UnziG3iajUQu3BxFvCOnpAJdDbMV4rSl+EqFDkkpNNVUFlR1kDfpL59xfy1HA== emoji-regex@^10.2.1, emoji-regex@^10.3.0: version "10.3.0" diff --git a/payload/src/collections/Makers.ts b/payload/src/collections/Makers.ts index cbe8be2..ca4b133 100644 --- a/payload/src/collections/Makers.ts +++ b/payload/src/collections/Makers.ts @@ -30,12 +30,18 @@ const Makers: CollectionConfig = { label: 'Location', required: true }, + { + name: 'admins', + type: 'relationship', + relationTo: 'users', + hasMany: true, + }, { name: 'stock', type: 'relationship', relationTo: 'products', hasMany: true, - }, + } ], }; diff --git a/payload/src/collections/Retailers.ts b/payload/src/collections/Retailers.ts index 442724d..2c348d8 100644 --- a/payload/src/collections/Retailers.ts +++ b/payload/src/collections/Retailers.ts @@ -31,6 +31,12 @@ const Retailers: CollectionConfig = { label: 'Location', required: true }, + { + name: 'admins', + type: 'relationship', + relationTo: 'users', + hasMany: true, + }, { name: 'stock', type: 'relationship', diff --git a/payload/src/collections/Users.ts b/payload/src/collections/Users.ts index f202b9a..c5da1f2 100644 --- a/payload/src/collections/Users.ts +++ b/payload/src/collections/Users.ts @@ -2,12 +2,17 @@ import { CollectionConfig } from 'payload/types'; const Users: CollectionConfig = { slug: 'users', - auth: true, admin: { useAsTitle: 'email', }, access: { - read: () => true, + + }, + auth: { + tokenExpiration: 7200, // How many seconds to keep the user logged in + verify: false, // Require email verification before being allowed to authenticate + maxLoginAttempts: 5, // Automatically lock a user out after X amount of failed logins + lockTime: 600 * 1000, // Time period to allow the max login attempts }, fields: [ // Email added by default @@ -20,20 +25,7 @@ const Users: CollectionConfig = { name: 'phoneNumber', type: 'number', required: false - }, - { - name: 'adminOfMakers', - type: 'relationship', - relationTo: 'makers', - hasMany: true, - }, - { - name: 'adminOfRetailers', - type: 'relationship', - relationTo: 'retailers', - - hasMany: true, - }, + } ], }; diff --git a/payload/src/server.ts b/payload/src/server.ts index e50324a..3c53fad 100644 --- a/payload/src/server.ts +++ b/payload/src/server.ts @@ -21,6 +21,7 @@ payload.init({ const corsOptions = { origin: 'http://localhost:3000', + credentials: true }; app.use(cors(corsOptions));