Add submission key to frontend to prevent duplicate signups

This commit is contained in:
Justus Ojala
2025-09-15 13:57:00 +03:00
parent b80942ee53
commit 630c0bce05
4 changed files with 38 additions and 13 deletions
+31 -11
View File
@@ -34,7 +34,8 @@
"sharp": "^0.30.3",
"shortid": "^2.2.16",
"styled-components": "^5.3.5",
"swr": "^1.2.2"
"swr": "^1.2.2",
"uuid": "^13.0.0"
},
"devDependencies": {
"@types/jest": "^27.4.1",
@@ -13610,6 +13611,17 @@
"semver": "bin/semver"
}
},
"node_modules/testcafe-reporter-dashboard/node_modules/uuid": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"license": "MIT",
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/testcafe-reporter-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/testcafe-reporter-json/-/testcafe-reporter-json-2.2.0.tgz",
@@ -14510,13 +14522,16 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"node_modules/uuid": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "bin/uuid"
"uuid": "dist-node/bin/uuid"
}
},
"node_modules/uvu": {
@@ -25397,6 +25412,12 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true
},
"uuid": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
"dev": true
}
}
},
@@ -25859,10 +25880,9 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"uuid": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
"dev": true
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
"integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="
},
"uvu": {
"version": "0.5.3",
+2 -1
View File
@@ -89,7 +89,8 @@
"sharp": "^0.30.3",
"shortid": "^2.2.16",
"styled-components": "^5.3.5",
"swr": "^1.2.2"
"swr": "^1.2.2",
"uuid": "^13.0.0"
},
"overrides": {
"react-mde": {
+2 -1
View File
@@ -1,7 +1,8 @@
import { OptionTypes } from "@components/Widgets/SignupQuestionsWidget/common";
export interface Signup {
id?: number;
id?: number; // Database id for completed signup
submitKey?: string; // Signup request idempotency key
signupForm_id: number;
answer: string;
}
+3
View File
@@ -13,6 +13,7 @@ import PageWrapper from "@views/common/PageWrapper";
import LoadingView from "@views/common/LoadingView";
import noop from "@utils/noop";
import NotFoundPage from "@pages/404";
import { v4 as uuid } from "uuid";
type InitialProps = {
initialForm: SignupForm;
@@ -23,6 +24,7 @@ const FORM_URL = `${process.env.NEXT_PUBLIC_API_URL}/signupForm/`;
const SignUpPage: NextPage<InitialProps> = ({ initialForm }) => {
const router = useRouter();
const id = String(initialForm?.id ?? "");
const submitKey = uuid(); // Submission key, generated on page refresh
const URL = `${FORM_URL}${id}/`;
const { data: signupForm, error } = useSWR<SignupForm>(URL, (url) => axios.get(url).then((res) => res.data), { fallbackData: initialForm });
@@ -43,6 +45,7 @@ const SignUpPage: NextPage<InitialProps> = ({ initialForm }) => {
const onSubmit = async ({ formData }: ISubmitEvent<string>) => {
const payload: Signup = {
submitKey, // This is for preventing duplicate requests; NOT RELATED TO THE SIGNUP ID IN DATABASE
signupForm_id: signupForm.id,
answer: formData,
};