From 5dd2955cb62a20d2e1587c83c5ce5e262207d2a4 Mon Sep 17 00:00:00 2001 From: ido Date: Mon, 4 Dec 2023 15:48:15 +0200 Subject: [PATCH] fix: forms close --- examples/simple-form/src/pages/index.astro | 5 +++++ package-lock.json | 13 +++++++++++ packages/forms/components/WebForms.astro | 2 ++ packages/forms/package.json | 2 ++ packages/forms/src/middleware.ts | 26 +++++++++++++++++++--- packages/forms/src/settings.ts | 3 ++- 6 files changed, 47 insertions(+), 4 deletions(-) diff --git a/examples/simple-form/src/pages/index.astro b/examples/simple-form/src/pages/index.astro index 75d8ec0..6092e0f 100644 --- a/examples/simple-form/src/pages/index.astro +++ b/examples/simple-form/src/pages/index.astro @@ -9,12 +9,17 @@ const form = Bind({age: 0, name: ''}); let showSubmitText: string; function formSubmit(){ + Astro.locals.session.counter ??= 0; + Astro.locals.session.counter++; showSubmitText = `You name is ${form.name}, you are ${form.age} years old. `; } --- {showSubmitText} + {Astro.locals.session.counter && +

You have submitted {Astro.locals.session.counter} times.

+ }

What you name*

diff --git a/package-lock.json b/package-lock.json index 269982a..b05e5e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1661,6 +1661,12 @@ "version": "6.0.3", "license": "MIT" }, + "node_modules/@types/promise-timeout": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/promise-timeout/-/promise-timeout-1.3.3.tgz", + "integrity": "sha512-gqmIw/4R1F1bqY5hWWZP0YE66iy6KkIu0tICpOLdXBuyHOAaSy9bNvwWHTJxyYHLozkieHM3Ej9GrYA6nuQPMA==", + "dev": true + }, "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", @@ -9459,6 +9465,11 @@ "dev": true, "license": "MIT" }, + "node_modules/promise-timeout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/promise-timeout/-/promise-timeout-1.3.0.tgz", + "integrity": "sha512-5yANTE0tmi5++POym6OgtFmwfDvOXABD9oj/jLQr5GPEyuNEb7jH4wbbANJceJid49jwhi1RddxnhnEAb/doqg==" + }, "node_modules/prompts": { "version": "2.4.2", "license": "MIT", @@ -12200,6 +12211,7 @@ "defaults": "^3.0.0", "formidable": "^3.2.5", "jsonwebtoken": "^9.0.0", + "promise-timeout": "^1.3.0", "uuid": "^9.0.0", "zod": "^3.19.1" }, @@ -12208,6 +12220,7 @@ "@types/formidable": "^2.0.5", "@types/jsonwebtoken": "^9.0.1", "@types/node": "^18.11.10", + "@types/promise-timeout": "^1.3.3", "@types/uuid": "^9.0.1", "semantic-release-commit-filter": "^1.0.2", "typescript": "^5.2.2", diff --git a/packages/forms/components/WebForms.astro b/packages/forms/components/WebForms.astro index 77fff0a..0641d02 100644 --- a/packages/forms/components/WebForms.astro +++ b/packages/forms/components/WebForms.astro @@ -11,6 +11,7 @@ const context = { webFormsSettings: {haveFileUpload: false} }; +await Astro.locals.__formsInternalUtils?.onWebFormsOpen?.(); const htmlSolt = await asyncContext(() => Astro.slots.render('default'), Astro, {name: '@astro-utils/forms', context}); const {webFormsSettings, ...props} = context; @@ -19,6 +20,7 @@ if (webFormsSettings.haveFileUpload) { } const formRequestToken = FORM_OPTIONS.session?.cookieOptions?.maxAge && await createFormToken(Astro); +await Astro.locals.__formsInternalUtils?.onWebFormClose?.(); ---
{formRequestToken && } diff --git a/packages/forms/package.json b/packages/forms/package.json index 80662c1..b59ebe1 100644 --- a/packages/forms/package.json +++ b/packages/forms/package.json @@ -52,6 +52,7 @@ "@types/formidable": "^2.0.5", "@types/jsonwebtoken": "^9.0.1", "@types/node": "^18.11.10", + "@types/promise-timeout": "^1.3.3", "@types/uuid": "^9.0.1", "semantic-release-commit-filter": "^1.0.2", "typescript": "^5.2.2", @@ -66,6 +67,7 @@ "defaults": "^3.0.0", "formidable": "^3.2.5", "jsonwebtoken": "^9.0.0", + "promise-timeout": "^1.3.0", "uuid": "^9.0.0", "zod": "^3.19.1" }, diff --git a/packages/forms/src/middleware.ts b/packages/forms/src/middleware.ts index 5190798..66429fa 100644 --- a/packages/forms/src/middleware.ts +++ b/packages/forms/src/middleware.ts @@ -5,6 +5,7 @@ import {FORM_OPTIONS, FormsSettings} from './settings.js'; import {v4 as uuid} from 'uuid'; import defaults from 'defaults'; import {deleteFormFiles} from './form-tools/post.js'; +import {timeout} from 'promise-timeout'; const DEFAULT_FORM_OPTIONS: FormsSettings = { csrf: DEFAULT_SETTINGS_CSRF, @@ -21,6 +22,7 @@ const DEFAULT_FORM_OPTIONS: FormsSettings = { maxAge: 1000 * 60 * 60 * 24 * 7 } }, + pageLoadTimeoutMS: 1000 * 5, secret: uuid() }; @@ -31,11 +33,29 @@ export default function astroForms(settings: Partial = {}){ const session = new JWTSession(cookies); locals.session = session.sessionData; + let response: Response; + let pageFinished: (data?: any) => void; + + locals.__formsInternalUtils = { + onWebFormClose() { + session.setCookieHeader(response.headers); + deleteFormFiles(request); + pageFinished(); + } + }; + await ensureValidationSecret({locals, request, cookies}); - const response = await next(); - deleteFormFiles(request); + response = await next(); + + if (!locals.webFormOff) { + try { + const pageFinishedPromise = new Promise(resolve => pageFinished = resolve); + await timeout(pageFinishedPromise, FORM_OPTIONS.pageLoadTimeoutMS); + } catch { + throw new Error('WebForms is not used in this page'); + } + } - session.setCookieHeader(response.headers); return response; } as MiddlewareEndpointHandler; } diff --git a/packages/forms/src/settings.ts b/packages/forms/src/settings.ts index 9abf84d..b6fbb3e 100644 --- a/packages/forms/src/settings.ts +++ b/packages/forms/src/settings.ts @@ -13,7 +13,8 @@ export type FormsSettings = { maxAge: number } }, - secret?: string + secret?: string, + pageLoadTimeoutMS?: number } ///