create new package for schemas and use it for auth
This commit is contained in:
parent
11827c30c1
commit
b68daf7312
@ -25,7 +25,8 @@
|
||||
"winston-loki": "^6.1.3",
|
||||
"zod": "^3.25.42",
|
||||
"zod-validation-error": "^3.4.1",
|
||||
"zod_utilz": "^0.8.4"
|
||||
"zod_utilz": "^0.8.4",
|
||||
"doorman-schema": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"twilio-run": "^3.5.4",
|
||||
|
||||
@ -13,17 +13,8 @@ import { AuthMetrics, registerMetrics } from "../../../metrics/AuthMetrics";
|
||||
import { Counter } from "prom-client";
|
||||
import { DoorConfig, getDoorConfigID } from "../../../schema/DoorConfig";
|
||||
import { createLockStatusWithTimeout, getLockStatusID, isLockOpen } from "../../../schema/LockStatus";
|
||||
import { z } from "zod";
|
||||
import { setResponseJson } from "../../../utils/responseUtils";
|
||||
|
||||
export const AuthRequestSchema = z.object({
|
||||
door: z.string(),
|
||||
key: z.string(),
|
||||
ip: z.string().optional(),
|
||||
timeout: z.coerce.number().gt(0, "Timeout cannot be 0").optional(),
|
||||
});
|
||||
|
||||
export type AuthRequest = z.infer<typeof AuthRequestSchema>;
|
||||
import { AuthRequest, AuthRequestSchema } from "../../../../../doorman-schema/src";
|
||||
|
||||
export interface AuthRequestTwilio extends ServerlessEventObject<AuthRequest, UserAgentHeader> { };
|
||||
|
||||
|
||||
9
packages/doorman-schema/package.json
Normal file
9
packages/doorman-schema/package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "doorman-schema",
|
||||
"module": "src/index.ts",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"zod": "^3.25.56",
|
||||
"zod_utilz": "^0.8.4"
|
||||
}
|
||||
}
|
||||
1
packages/doorman-schema/src/index.ts
Normal file
1
packages/doorman-schema/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./routes/auth";
|
||||
10
packages/doorman-schema/src/routes/auth.ts
Normal file
10
packages/doorman-schema/src/routes/auth.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import z from "zod";
|
||||
|
||||
export const AuthRequestSchema = z.object({
|
||||
door: z.string(),
|
||||
key: z.string().min(1, "PIN cannot be empty"),
|
||||
ip: z.string().optional(),
|
||||
timeout: z.coerce.number().gt(0, "Timeout cannot be 0").optional(),
|
||||
});
|
||||
|
||||
export type AuthRequest = z.infer<typeof AuthRequestSchema>;
|
||||
@ -4,6 +4,7 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@cloudscape-design/components": "^3.0.375",
|
||||
"@hookform/resolvers": "^5.1.0",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^13.0.0",
|
||||
"@testing-library/user-event": "^13.2.1",
|
||||
@ -22,7 +23,8 @@
|
||||
"vite": "^4.4.9",
|
||||
"vite-plugin-svgr": "^4.0.0",
|
||||
"vite-tsconfig-paths": "^4.2.1",
|
||||
"web-vitals": "^2.1.0"
|
||||
"web-vitals": "^2.1.0",
|
||||
"zod": "^3.25.56"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
import { Button, FormField, Icon, Input, SpaceBetween } from "@cloudscape-design/components";
|
||||
import { Button, Form, FormField, Icon, Input, SpaceBetween } from "@cloudscape-design/components";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import type { AuthRequest } from "../../../doorman-api/src/functions/api/door/auth";
|
||||
import { AuthRequest, AuthRequestSchema } from "../../../doorman-schema/src";
|
||||
import { fetchUrlEncoded } from "../helpers/FetchHelper";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import CInput from "react-hook-form-cloudscape/components/input";
|
||||
|
||||
// COPIED FROM SERVER
|
||||
export enum IAuthMode {
|
||||
@ -10,7 +14,6 @@ export enum IAuthMode {
|
||||
RANDOM_ROTATING_KEY = "Random Rotating Key",
|
||||
}
|
||||
|
||||
|
||||
export interface IAuthComponentProps {
|
||||
door: string,
|
||||
authMode: IAuthMode,
|
||||
@ -21,17 +24,42 @@ export interface IAuthComponentProps {
|
||||
}
|
||||
|
||||
export const AuthComponent = ({ door, secret, authMode, onError, onUnlock, runCheck }: IAuthComponentProps) => {
|
||||
const [ key, setKey ] = useState(secret || "");
|
||||
const [ error, setError ] = useState("");
|
||||
const { control, setError, formState, handleSubmit, setValue, getValues } = useForm<AuthRequest>({
|
||||
resolver: zodResolver(AuthRequestSchema),
|
||||
defaultValues: {
|
||||
key: secret || "",
|
||||
door,
|
||||
}
|
||||
});
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
const onSubmit = async (body: AuthRequest) => {
|
||||
setLoading(true);
|
||||
await fetchUrlEncoded('/api/door/auth', body)
|
||||
.then(async res => {
|
||||
if (res.status !== 200) {
|
||||
setError("key", { type: "validate", message: "Incorrect PIN"});
|
||||
onError && onError(await res.json());
|
||||
return;
|
||||
}
|
||||
onUnlock();
|
||||
});
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (runCheck) {
|
||||
onSubmit();
|
||||
// don't allow us to submit while we are submitting
|
||||
if (runCheck && !isLoading) {
|
||||
handleSubmit(onSubmit)();
|
||||
}
|
||||
}, [runCheck])
|
||||
}, [runCheck]);
|
||||
|
||||
const onSubmit = async () => {
|
||||
const ip = await fetch('https://api.ipify.org?format=json')
|
||||
|
||||
// try and submit on load if the pin is passed in by query params
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
fetch('https://api.ipify.org?format=json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
return data.ip;
|
||||
@ -39,30 +67,24 @@ export const AuthComponent = ({ door, secret, authMode, onError, onUnlock, runCh
|
||||
.catch(error => {
|
||||
console.log('Error:', error);
|
||||
return "null";
|
||||
});
|
||||
})
|
||||
.then(async ip => {
|
||||
setValue("ip", ip);
|
||||
|
||||
const body: AuthRequest = {
|
||||
door,
|
||||
ip,
|
||||
key,
|
||||
};
|
||||
// if the key is there, then try submitting right away
|
||||
if (secret !== null) {
|
||||
await handleSubmit(onSubmit)();
|
||||
}
|
||||
|
||||
fetchUrlEncoded('/api/door/auth', body)
|
||||
.then(async res => {
|
||||
if (res.status !== 200) {
|
||||
setError("Incorrect PIN");
|
||||
onError && onError(await res.json());
|
||||
return;
|
||||
}
|
||||
onUnlock();
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
}, [secret]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SpaceBetween size='l'>
|
||||
<FormField errorText={error} label={"PIN"} constraintText={"Enter the PIN to unlock the door"}>
|
||||
<Input readOnly={secret !== null} value={key || ""} onChange={({ detail }) => setKey(detail.value)} />
|
||||
<FormField errorText={formState.errors.key?.message} label={"PIN"} constraintText={"Enter the PIN to unlock the door"}>
|
||||
<CInput type="password" name="key" readOnly={secret !== null} control={control} />
|
||||
</FormField>
|
||||
</SpaceBetween>
|
||||
</>
|
||||
|
||||
@ -36,7 +36,5 @@ const router = createBrowserRouter([
|
||||
])
|
||||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<RouterProvider router={router} />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user