From e1e9a12c99245a2f90522b9825ffc15020c03177 Mon Sep 17 00:00:00 2001 From: Alexandru Date: Wed, 7 Aug 2024 16:58:17 +0300 Subject: [PATCH 1/8] feat: Issue #3336148: Add support for forms --- src/module.ts | 2 +- src/runtime/composables/useDrupalCe/index.ts | 51 +++++++++++++++----- src/runtime/plugin.ts | 4 -- src/runtime/plugins/formHandler.ts | 37 ++++++++++++++ 4 files changed, 77 insertions(+), 17 deletions(-) delete mode 100644 src/runtime/plugin.ts create mode 100644 src/runtime/plugins/formHandler.ts diff --git a/src/module.ts b/src/module.ts index 4eba8e84..ab4af930 100644 --- a/src/module.ts +++ b/src/module.ts @@ -59,7 +59,7 @@ export default defineNuxtModule({ const { resolve } = createResolver(import.meta.url) const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url)) nuxt.options.build.transpile.push(runtimeDir) - addPlugin(resolve(runtimeDir, 'plugin')) + addPlugin(resolve(runtimeDir, 'plugins/formHandler')) if (options.serverLogLevel) { addServerPlugin(resolve(runtimeDir, 'server/plugins/errorLogger')) } diff --git a/src/runtime/composables/useDrupalCe/index.ts b/src/runtime/composables/useDrupalCe/index.ts index 10636f38..16ae9928 100644 --- a/src/runtime/composables/useDrupalCe/index.ts +++ b/src/runtime/composables/useDrupalCe/index.ts @@ -4,7 +4,7 @@ import { appendResponseHeader } from 'h3' import type { UseFetchOptions } from '#app' import type { $Fetch, NitroFetchRequest } from 'nitropack' import { getDrupalBaseUrl, getMenuBaseUrl } from './server' -import { useRuntimeConfig, useState, useFetch, navigateTo, createError, h, resolveComponent, setResponseStatus, useNuxtApp, useRequestHeaders, ref, watch } from '#imports' +import { useRuntimeConfig, useState, useFetch, navigateTo, createError, h, resolveComponent, setResponseStatus, useNuxtApp, useRequestHeaders, ref, watch, useRequestEvent } from '#imports' export const useDrupalCe = () => { const config = useRuntimeConfig().public.drupalCe @@ -116,24 +116,51 @@ export const useDrupalCe = () => { page_layout: 'default', title: '', })) + const serverResponse = useState('server-response', () => null) useFetchOptions.key = `page-${path}` + const page = ref(null) + const pageError = ref(null) - const { data: page, error } = await useCeApi(path, useFetchOptions, true) + if (import.meta.server) { + serverResponse.value = useRequestEvent(nuxtApp).context.nitro.response + } - if (page?.value?.redirect) { - await callWithNuxt(nuxtApp, navigateTo, [ - page.value.redirect.url, - { external: page.value.redirect.external, redirectCode: page.value.redirect.statusCode, replace: true }, - ]) - return pageState + // Check if the page data is already provided, e.g. by a form response. + if (!page.value) { + if (serverResponse.value && serverResponse.value._data) { + page.value = serverResponse.value._data + passThroughHeaders(nuxtApp, serverResponse.value.headers) + } else { + const { data, error } = await useCeApi(path, useFetchOptions, true) + page.value = data.value + pageError.value = error.value + } } - if (error.value) { - overrideErrorHandler ? overrideErrorHandler(error) : pageErrorHandler(error, { config, nuxtApp }) - page.value = error.value?.data + if (page.value?.messages) { + pushMessagesToState(page.value.messages) } - page.value?.messages && pushMessagesToState(page.value.messages) + if (page?.value?.redirect) { + const redirect = page.value.redirect + if (redirect.external) { + await navigateTo(redirect.url, { external: redirect.external, redirectCode: redirect.statusCode, replace: true }) + return { + ...pageState.value, + } + } + const { data, error } = await useCeApi(redirect.url, useFetchOptions, true) + page.value = data.value + pageError.value = error.value + if (import.meta.client) { + await navigateTo(redirect.url, { redirectCode: redirect.statusCode, replace: true }) + } + } + + if (pageError.value) { + overrideErrorHandler ? overrideErrorHandler(pageError) : pageErrorHandler(pageError, { config, nuxtApp }) + page.value = pageError.value?.data + } pageState.value = page return page diff --git a/src/runtime/plugin.ts b/src/runtime/plugin.ts deleted file mode 100644 index 83dcea71..00000000 --- a/src/runtime/plugin.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { defineNuxtPlugin } from '#app' - -export default defineNuxtPlugin((nuxtApp) => { -}) diff --git a/src/runtime/plugins/formHandler.ts b/src/runtime/plugins/formHandler.ts new file mode 100644 index 00000000..ee87592a --- /dev/null +++ b/src/runtime/plugins/formHandler.ts @@ -0,0 +1,37 @@ +import { readFormData } from 'h3' +import { getDrupalBaseUrl } from '../composables/useDrupalCe/server.js' +import { defineNuxtPlugin, addRouteMiddleware, useRuntimeConfig, useRequestEvent } from '#imports' + +export default defineNuxtPlugin(() => { + const runtimeConfig = useRuntimeConfig() + const { ceApiEndpoint } = runtimeConfig.public.drupalCe + const drupalBaseUrl = getDrupalBaseUrl() + + addRouteMiddleware('form-handler', async () => { + if (import.meta.server) { + const event = useRequestEvent() + if (event && event.node.req.method === 'POST') { + const formData = await readFormData(event) + + if (formData && formData.get('target_url')) { + const targetUrl = formData.get('target_url') + const response = await $fetch.raw(drupalBaseUrl + ceApiEndpoint + targetUrl, { + method: 'POST', + body: formData, + }) + + event.context.nitro.response = { + _data: response._data, + headers: Object.fromEntries(response.headers.entries()), + } + } else { + throw createError({ + statusCode: 400, + statusMessage: 'Bad Request', + message: 'The request contains invalid form data or no form data at all.', + }) + } + } + } + }, { global: true }) +}) From 19d5b8ffe05f51b08d2d2c6a7df2a45306da2ab4 Mon Sep 17 00:00:00 2001 From: Alexandru Date: Wed, 7 Aug 2024 17:02:10 +0300 Subject: [PATCH 2/8] LDP-2492: Remove unnecessary if --- src/runtime/composables/useDrupalCe/index.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/runtime/composables/useDrupalCe/index.ts b/src/runtime/composables/useDrupalCe/index.ts index 16ae9928..a1f55109 100644 --- a/src/runtime/composables/useDrupalCe/index.ts +++ b/src/runtime/composables/useDrupalCe/index.ts @@ -126,15 +126,13 @@ export const useDrupalCe = () => { } // Check if the page data is already provided, e.g. by a form response. - if (!page.value) { - if (serverResponse.value && serverResponse.value._data) { - page.value = serverResponse.value._data - passThroughHeaders(nuxtApp, serverResponse.value.headers) - } else { - const { data, error } = await useCeApi(path, useFetchOptions, true) - page.value = data.value - pageError.value = error.value - } + if (serverResponse.value && serverResponse.value._data) { + page.value = serverResponse.value._data + passThroughHeaders(nuxtApp, serverResponse.value.headers) + } else { + const { data, error } = await useCeApi(path, useFetchOptions, true) + page.value = data.value + pageError.value = error.value } if (page.value?.messages) { From c28d26c7323e92da8af023fe1c05c72a5490c691 Mon Sep 17 00:00:00 2001 From: Alexandru Date: Wed, 7 Aug 2024 18:15:26 +0300 Subject: [PATCH 3/8] LDP-2492: Improve redirect code --- playground/components/global/DrupalForm.vue | 18 ++++++++++++++++++ playground/middleware/redirect.global.ts | 16 ---------------- 2 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 playground/components/global/DrupalForm.vue delete mode 100644 playground/middleware/redirect.global.ts diff --git a/playground/components/global/DrupalForm.vue b/playground/components/global/DrupalForm.vue new file mode 100644 index 00000000..ee342ce1 --- /dev/null +++ b/playground/components/global/DrupalForm.vue @@ -0,0 +1,18 @@ + + + diff --git a/playground/middleware/redirect.global.ts b/playground/middleware/redirect.global.ts deleted file mode 100644 index fe904174..00000000 --- a/playground/middleware/redirect.global.ts +++ /dev/null @@ -1,16 +0,0 @@ -export default defineNuxtRouteMiddleware((to, from) => { - const config = useRuntimeConfig().public.drupalCe - - switch (true) { - case /^\/((en|de)\/)?user.*$/.test(to.path): - case /^\/((en|de)\/)?admin.*$/.test(to.path): - case /^\/((en|de)\/)?(node\/(add|[^/]+\/(edit|delete|revisions|translations))|entity_clone\/node\/[^/]+)/.test( - to.path, - ): - case /^\/((en|de)\/)?node\/[^/]+\/layout$/.test(to.path): - return navigateTo(`${config.drupalBaseUrl}${to.fullPath}`, { - external: true, - redirectCode: 301, - }) - } -}) From 784797f7a648c58918e1d77e12032a76172a5b2a Mon Sep 17 00:00:00 2001 From: Alexandru Date: Wed, 7 Aug 2024 18:15:50 +0300 Subject: [PATCH 4/8] LDP-2492: Improve internal redirect code --- src/runtime/composables/useDrupalCe/index.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/runtime/composables/useDrupalCe/index.ts b/src/runtime/composables/useDrupalCe/index.ts index a1f55109..12cebd9f 100644 --- a/src/runtime/composables/useDrupalCe/index.ts +++ b/src/runtime/composables/useDrupalCe/index.ts @@ -142,16 +142,15 @@ export const useDrupalCe = () => { if (page?.value?.redirect) { const redirect = page.value.redirect if (redirect.external) { - await navigateTo(redirect.url, { external: redirect.external, redirectCode: redirect.statusCode, replace: true }) - return { - ...pageState.value, - } - } - const { data, error } = await useCeApi(redirect.url, useFetchOptions, true) - page.value = data.value - pageError.value = error.value - if (import.meta.client) { - await navigateTo(redirect.url, { redirectCode: redirect.statusCode, replace: true }) + await callWithNuxt(nuxtApp, navigateTo, [ + redirect.url, + { external: true, redirectCode: redirect.statusCode, replace: true }, + ]) + } else { + const { data, error } = await useCeApi(redirect.url, useFetchOptions, true) + page.value = data.value + pageError.value = error.value + nuxtApp.$router.push(redirect.url) } } From 631bab2e5de39a189ace69e3cac98c26b846787e Mon Sep 17 00:00:00 2001 From: Alexandru Date: Wed, 7 Aug 2024 20:26:46 +0300 Subject: [PATCH 5/8] LDP-2492: Revert redirect code --- src/runtime/composables/useDrupalCe/index.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/runtime/composables/useDrupalCe/index.ts b/src/runtime/composables/useDrupalCe/index.ts index 12cebd9f..b0d1b46b 100644 --- a/src/runtime/composables/useDrupalCe/index.ts +++ b/src/runtime/composables/useDrupalCe/index.ts @@ -140,18 +140,11 @@ export const useDrupalCe = () => { } if (page?.value?.redirect) { - const redirect = page.value.redirect - if (redirect.external) { - await callWithNuxt(nuxtApp, navigateTo, [ - redirect.url, - { external: true, redirectCode: redirect.statusCode, replace: true }, - ]) - } else { - const { data, error } = await useCeApi(redirect.url, useFetchOptions, true) - page.value = data.value - pageError.value = error.value - nuxtApp.$router.push(redirect.url) - } + await callWithNuxt(nuxtApp, navigateTo, [ + page.value.redirect.url, + { external: page.value.redirect.external, redirectCode: page.value.redirect.statusCode, replace: true }, + ]) + return pageState } if (pageError.value) { From 36af30d5327df03e656914dae7daebed9e16da7e Mon Sep 17 00:00:00 2001 From: Alexandru Date: Tue, 20 Aug 2024 16:22:21 +0300 Subject: [PATCH 6/8] LDP-2492: Improve code and use page middleware --- package.json | 3 +- playground/components/global/DrupalForm.vue | 6 +--- playground/middleware/formHandler.ts | 34 ++++++++++++++++++ playground/pages/[...slug].vue | 1 + src/module.ts | 3 +- src/runtime/composables/useDrupalCe/index.ts | 6 +++- src/runtime/plugins/formHandler.ts | 37 -------------------- 7 files changed, 44 insertions(+), 46 deletions(-) create mode 100644 playground/middleware/formHandler.ts delete mode 100644 src/runtime/plugins/formHandler.ts diff --git a/package.json b/package.json index 5863d2b2..f9f4373b 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "dist", "playground/components", "playground/pages", - "playground/app.vue" + "playground/app.vue", + "playground/middleware" ], "scripts": { "prepack": "nuxt-module-build", diff --git a/playground/components/global/DrupalForm.vue b/playground/components/global/DrupalForm.vue index ee342ce1..94af36c8 100644 --- a/playground/components/global/DrupalForm.vue +++ b/playground/components/global/DrupalForm.vue @@ -1,7 +1,6 @@ @@ -12,7 +11,4 @@ const props = defineProps<{ method: String, content?: String, }>() - -const match = props.content ? props.content.match(/action="([^"]*)"/) : null -const target = match ? match[1] : useRoute().path diff --git a/playground/middleware/formHandler.ts b/playground/middleware/formHandler.ts new file mode 100644 index 00000000..e7d8267f --- /dev/null +++ b/playground/middleware/formHandler.ts @@ -0,0 +1,34 @@ +import { readFormData, createError } from 'h3' +import { useRuntimeConfig, useRequestEvent } from '#imports' + +export default defineNuxtRouteMiddleware(async (from) => { + if (import.meta.server) { + const runtimeConfig = useRuntimeConfig() + const event = useRequestEvent() + const { ceApiEndpoint } = runtimeConfig.public.drupalCe + const drupalBaseUrl = getDrupalBaseUrl() + + if (event?.node?.req?.method === 'POST') { + const formData = await readFormData(event) + + if (formData) { + const targetUrl = from.fullPath + const response = await $fetch.raw(drupalBaseUrl + ceApiEndpoint + targetUrl, { + method: 'POST', + body: formData, + }) + + event.context.drupalCeCustomPageResponse = { + _data: response._data, + headers: Object.fromEntries(response.headers.entries()), + } + } else { + throw createError({ + statusCode: 400, + statusMessage: 'Bad Request', + message: 'The request contains invalid form data or no form data at all.', + }) + } + } + } +}) diff --git a/playground/pages/[...slug].vue b/playground/pages/[...slug].vue index 3cd38634..f452d1b9 100644 --- a/playground/pages/[...slug].vue +++ b/playground/pages/[...slug].vue @@ -16,6 +16,7 @@ const page = await fetchPage(useRoute().path, { query: useRoute().query }) // Set to false to support custom layouts, using instead. definePageMeta({ layout: false, + middleware: 'form-handler', }) const layout = computed(() => { return page.value.page_layout || 'default' diff --git a/src/module.ts b/src/module.ts index ab4af930..c2131c9f 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,5 +1,5 @@ import { fileURLToPath } from 'url' -import { defineNuxtModule, addPlugin, addServerPlugin, createResolver, addImportsDir, addServerHandler } from '@nuxt/kit' +import { defineNuxtModule, addServerPlugin, createResolver, addImportsDir, addServerHandler } from '@nuxt/kit' import { defu } from 'defu' import type { NuxtOptionsWithDrupalCe } from './types' @@ -59,7 +59,6 @@ export default defineNuxtModule({ const { resolve } = createResolver(import.meta.url) const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url)) nuxt.options.build.transpile.push(runtimeDir) - addPlugin(resolve(runtimeDir, 'plugins/formHandler')) if (options.serverLogLevel) { addServerPlugin(resolve(runtimeDir, 'server/plugins/errorLogger')) } diff --git a/src/runtime/composables/useDrupalCe/index.ts b/src/runtime/composables/useDrupalCe/index.ts index b0d1b46b..de9fa9f9 100644 --- a/src/runtime/composables/useDrupalCe/index.ts +++ b/src/runtime/composables/useDrupalCe/index.ts @@ -122,13 +122,17 @@ export const useDrupalCe = () => { const pageError = ref(null) if (import.meta.server) { - serverResponse.value = useRequestEvent(nuxtApp).context.nitro.response + serverResponse.value = useRequestEvent(nuxtApp).context.drupalCeCustomPageResponse } // Check if the page data is already provided, e.g. by a form response. if (serverResponse.value && serverResponse.value._data) { page.value = serverResponse.value._data passThroughHeaders(nuxtApp, serverResponse.value.headers) + // Clear the server response state after it was sent to the client. + if (import.meta.client) { + serverResponse.value = null + } } else { const { data, error } = await useCeApi(path, useFetchOptions, true) page.value = data.value diff --git a/src/runtime/plugins/formHandler.ts b/src/runtime/plugins/formHandler.ts deleted file mode 100644 index ee87592a..00000000 --- a/src/runtime/plugins/formHandler.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { readFormData } from 'h3' -import { getDrupalBaseUrl } from '../composables/useDrupalCe/server.js' -import { defineNuxtPlugin, addRouteMiddleware, useRuntimeConfig, useRequestEvent } from '#imports' - -export default defineNuxtPlugin(() => { - const runtimeConfig = useRuntimeConfig() - const { ceApiEndpoint } = runtimeConfig.public.drupalCe - const drupalBaseUrl = getDrupalBaseUrl() - - addRouteMiddleware('form-handler', async () => { - if (import.meta.server) { - const event = useRequestEvent() - if (event && event.node.req.method === 'POST') { - const formData = await readFormData(event) - - if (formData && formData.get('target_url')) { - const targetUrl = formData.get('target_url') - const response = await $fetch.raw(drupalBaseUrl + ceApiEndpoint + targetUrl, { - method: 'POST', - body: formData, - }) - - event.context.nitro.response = { - _data: response._data, - headers: Object.fromEntries(response.headers.entries()), - } - } else { - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request', - message: 'The request contains invalid form data or no form data at all.', - }) - } - } - } - }, { global: true }) -}) From 83ccb91e7d3fb1a5036117132a2649fb98fb6fb7 Mon Sep 17 00:00:00 2001 From: Alexandru Date: Wed, 21 Aug 2024 18:56:33 +0300 Subject: [PATCH 7/8] LDP-2492: Add server middleware and error handling --- playground/middleware/formHandler.ts | 34 ---------------- playground/pages/[...slug].vue | 1 - .../server/middleware/drupalFormHandler.ts | 39 +++++++++++++++++++ src/runtime/composables/useDrupalCe/index.ts | 10 +++-- 4 files changed, 46 insertions(+), 38 deletions(-) delete mode 100644 playground/middleware/formHandler.ts create mode 100644 playground/server/middleware/drupalFormHandler.ts diff --git a/playground/middleware/formHandler.ts b/playground/middleware/formHandler.ts deleted file mode 100644 index e7d8267f..00000000 --- a/playground/middleware/formHandler.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { readFormData, createError } from 'h3' -import { useRuntimeConfig, useRequestEvent } from '#imports' - -export default defineNuxtRouteMiddleware(async (from) => { - if (import.meta.server) { - const runtimeConfig = useRuntimeConfig() - const event = useRequestEvent() - const { ceApiEndpoint } = runtimeConfig.public.drupalCe - const drupalBaseUrl = getDrupalBaseUrl() - - if (event?.node?.req?.method === 'POST') { - const formData = await readFormData(event) - - if (formData) { - const targetUrl = from.fullPath - const response = await $fetch.raw(drupalBaseUrl + ceApiEndpoint + targetUrl, { - method: 'POST', - body: formData, - }) - - event.context.drupalCeCustomPageResponse = { - _data: response._data, - headers: Object.fromEntries(response.headers.entries()), - } - } else { - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request', - message: 'The request contains invalid form data or no form data at all.', - }) - } - } - } -}) diff --git a/playground/pages/[...slug].vue b/playground/pages/[...slug].vue index f452d1b9..3cd38634 100644 --- a/playground/pages/[...slug].vue +++ b/playground/pages/[...slug].vue @@ -16,7 +16,6 @@ const page = await fetchPage(useRoute().path, { query: useRoute().query }) // Set to false to support custom layouts, using instead. definePageMeta({ layout: false, - middleware: 'form-handler', }) const layout = computed(() => { return page.value.page_layout || 'default' diff --git a/playground/server/middleware/drupalFormHandler.ts b/playground/server/middleware/drupalFormHandler.ts new file mode 100644 index 00000000..6b8aaaef --- /dev/null +++ b/playground/server/middleware/drupalFormHandler.ts @@ -0,0 +1,39 @@ +export default defineEventHandler(async (event) => { + const runtimeConfig = useRuntimeConfig() + const { ceApiEndpoint, drupalBaseUrl, serverDrupalBaseUrl } = runtimeConfig.public.drupalCe + const fetchUrl = serverDrupalBaseUrl ? serverDrupalBaseUrl : drupalBaseUrl + + if (event.node.req.method === 'POST') { + const formData = await readFormData(event) + + if (formData) { + const targetUrl = event.node.req.url + const response = await $fetch.raw(fetchUrl + ceApiEndpoint + targetUrl, { + method: 'POST', + body: formData, + }).catch((error) => { + event.context.drupalCeCustomPageResponse = { + error: { + data: error, + statusCode: error.statusCode || 400, + message: error.message || 'Fetch error. See drupalFormHandler.', + } + } + }) + + if (response) { + event.context.drupalCeCustomPageResponse = { + _data: response._data, + headers: Object.fromEntries(response.headers.entries()), + } + return + } + } else { + throw createError({ + statusCode: 400, + statusMessage: 'Bad Request', + message: 'No or invalid form data given. See drupalFormHandler.', + }) + } + } +}) \ No newline at end of file diff --git a/src/runtime/composables/useDrupalCe/index.ts b/src/runtime/composables/useDrupalCe/index.ts index de9fa9f9..ca073d74 100644 --- a/src/runtime/composables/useDrupalCe/index.ts +++ b/src/runtime/composables/useDrupalCe/index.ts @@ -126,9 +126,13 @@ export const useDrupalCe = () => { } // Check if the page data is already provided, e.g. by a form response. - if (serverResponse.value && serverResponse.value._data) { - page.value = serverResponse.value._data - passThroughHeaders(nuxtApp, serverResponse.value.headers) + if (serverResponse.value) { + if (serverResponse.value._data) { + page.value = serverResponse.value._data + passThroughHeaders(nuxtApp, serverResponse.value.headers) + } else if (serverResponse.value.error) { + pageError.value = serverResponse.value.error + } // Clear the server response state after it was sent to the client. if (import.meta.client) { serverResponse.value = null From 98a944148ac08b92437486891e2bc365df667849 Mon Sep 17 00:00:00 2001 From: Alexandru Date: Wed, 21 Aug 2024 19:06:39 +0300 Subject: [PATCH 8/8] test: LDP-2492: Add mocked backend for Drupal login forms --- playground/server/api/[...].ts | 7 +++++++ playground/server/api/user/login.ts | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 playground/server/api/[...].ts create mode 100644 playground/server/api/user/login.ts diff --git a/playground/server/api/[...].ts b/playground/server/api/[...].ts new file mode 100644 index 00000000..a25f1121 --- /dev/null +++ b/playground/server/api/[...].ts @@ -0,0 +1,7 @@ +export default defineEventHandler((event) => { + event.node.res.statusCode = 404 + return { + status: 'error', + message: 'Not Found' + } +}) diff --git a/playground/server/api/user/login.ts b/playground/server/api/user/login.ts new file mode 100644 index 00000000..f20101d4 --- /dev/null +++ b/playground/server/api/user/login.ts @@ -0,0 +1,20 @@ +export default defineEventHandler(async (event) => { + if (event.node.req.method === 'POST') { + const formData = await readFormData(event) + const username = formData.get('name') + const password = formData.get('pass') + + if (username === 'admin' || password === 'drupal123') { + setHeader(event, 'Set-Cookie', 'SSESSf9f2dc90f4drupal=k8WpMQCcQ-test; Expires=Fri, 13-Sep-2024 18:47:37 GMT; Max-Age=2000000; Path=/; Domain=localhost; Secure; HttpOnly; SameSite=Lax'); + + return {"redirect":{"external":false,"url":"/node/1","statusCode":301},"messages":[]} + } else if (username !== 'admin' || password !== 'drupal123') { + return { + "title":"Log in","messages":{"error":["Unrecognized username or password. \u003Ca href=\u0022\/user\/password\u0022\u003EForgot your password?\u003C\/a\u003E"]},"breadcrumbs":[{"frontpage":true,"url":"\/","label":"Home"}],"metatags":{"meta":[],"link":[]},"content_format":"json","content":{"element":"drupal-form","formId":"user_login_form","attributes":{"class":["user-login-form"],"dataDrupalSelector":"user-login-form"},"method":"post","content":"\u003Cdiv class=\u0022js-form-item form-item js-form-type-textfield form-item-name js-form-item-name\u0022\u003E\n \u003Clabel for=\u0022edit-name\u0022 class=\u0022form-item__label js-form-required form-required\u0022\u003EUsername\u003C\/label\u003E\n \u003Cinput autocorrect=\u0022none\u0022 autocapitalize=\u0022none\u0022 spellcheck=\u0022false\u0022 autofocus=\u0022autofocus\u0022 autocomplete=\u0022username\u0022 data-drupal-selector=\u0022edit-name\u0022 type=\u0022text\u0022 id=\u0022edit-name\u0022 name=\u0022name\u0022 value=\u0022admin123\u0022 size=\u002260\u0022 maxlength=\u002260\u0022 class=\u0022form-text required error form-element form-element--type-text form-element--api-textfield\u0022 required=\u0022required\u0022 aria-required=\u0022true\u0022 aria-invalid=\u0022true\u0022 \/\u003E\n\n \u003C\/div\u003E\n\u003Cdiv class=\u0022js-form-item form-item js-form-type-password form-item-pass js-form-item-pass\u0022\u003E\n \u003Clabel for=\u0022edit-pass\u0022 class=\u0022form-item__label js-form-required form-required\u0022\u003EPassword\u003C\/label\u003E\n \u003Cinput autocomplete=\u0022current-password\u0022 data-drupal-selector=\u0022edit-pass\u0022 type=\u0022password\u0022 id=\u0022edit-pass\u0022 name=\u0022pass\u0022 size=\u002260\u0022 maxlength=\u0022128\u0022 class=\u0022form-text required form-element form-element--type-password form-element--api-password\u0022 required=\u0022required\u0022 aria-required=\u0022true\u0022 \/\u003E\n\n \u003C\/div\u003E\n\u003Cinput data-drupal-selector=\u0022form-y0dcq5st-2releayznqf-qaeu2krrwig77u-4x69m1u\u0022 type=\u0022hidden\u0022 name=\u0022form_build_id\u0022 value=\u0022form-y0DCq5St-2rElEaYZnqF-QAeU2KrrWiG77u-4x69M1U\u0022 \/\u003E\n\u003Cinput data-drupal-selector=\u0022edit-user-login-form\u0022 type=\u0022hidden\u0022 name=\u0022form_id\u0022 value=\u0022user_login_form\u0022 \/\u003E\n\u003Cdiv data-drupal-selector=\u0022edit-actions\u0022 class=\u0022form-actions js-form-wrapper form-wrapper\u0022 id=\u0022edit-actions\u0022\u003E\u003Cinput class=\u0022button--primary button js-form-submit form-submit\u0022 data-drupal-selector=\u0022edit-submit\u0022 type=\u0022submit\u0022 id=\u0022edit-submit\u0022 name=\u0022op\u0022 value=\u0022Log in\u0022 \/\u003E\n\u003C\/div\u003E\n"},"page_layout":"default","local_tasks":[] + } + } + } + return { + "title":"Log in","messages":[],"breadcrumbs":[{"frontpage":true,"url":"\/","label":"Home"}],"metatags":{"meta":[],"link":[]},"content_format":"json","content":{"element":"drupal-form","formId":"user_login_form","attributes":{"class":["user-login-form"],"dataDrupalSelector":"user-login-form"},"method":"post","content":"\u003Cdiv class=\u0022js-form-item form-item js-form-type-textfield form-item-name js-form-item-name\u0022\u003E\n \u003Clabel for=\u0022edit-name\u0022 class=\u0022form-item__label js-form-required form-required\u0022\u003EUsername\u003C\/label\u003E\n \u003Cinput autocorrect=\u0022none\u0022 autocapitalize=\u0022none\u0022 spellcheck=\u0022false\u0022 autofocus=\u0022autofocus\u0022 autocomplete=\u0022username\u0022 data-drupal-selector=\u0022edit-name\u0022 type=\u0022text\u0022 id=\u0022edit-name\u0022 name=\u0022name\u0022 value=\u0022\u0022 size=\u002260\u0022 maxlength=\u002260\u0022 class=\u0022form-text required form-element form-element--type-text form-element--api-textfield\u0022 required=\u0022required\u0022 aria-required=\u0022true\u0022 \/\u003E\n\n \u003C\/div\u003E\n\u003Cdiv class=\u0022js-form-item form-item js-form-type-password form-item-pass js-form-item-pass\u0022\u003E\n \u003Clabel for=\u0022edit-pass\u0022 class=\u0022form-item__label js-form-required form-required\u0022\u003EPassword\u003C\/label\u003E\n \u003Cinput autocomplete=\u0022current-password\u0022 data-drupal-selector=\u0022edit-pass\u0022 type=\u0022password\u0022 id=\u0022edit-pass\u0022 name=\u0022pass\u0022 size=\u002260\u0022 maxlength=\u0022128\u0022 class=\u0022form-text required form-element form-element--type-password form-element--api-password\u0022 required=\u0022required\u0022 aria-required=\u0022true\u0022 \/\u003E\n\n \u003C\/div\u003E\n\u003Cinput data-drupal-selector=\u0022form-6ngyrvfrmgfogyhp4piezpfq5nj09qszrlhrch4nvzi\u0022 type=\u0022hidden\u0022 name=\u0022form_build_id\u0022 value=\u0022form-6NGYrVfrmgfoGYHP4piEzpFq5Nj09QszRLhRCH4NvZI\u0022 \/\u003E\n\u003Cinput data-drupal-selector=\u0022edit-user-login-form\u0022 type=\u0022hidden\u0022 name=\u0022form_id\u0022 value=\u0022user_login_form\u0022 \/\u003E\n\u003Cdiv data-drupal-selector=\u0022edit-actions\u0022 class=\u0022form-actions js-form-wrapper form-wrapper\u0022 id=\u0022edit-actions\u0022\u003E\u003Cinput class=\u0022button--primary button js-form-submit form-submit\u0022 data-drupal-selector=\u0022edit-submit\u0022 type=\u0022submit\u0022 id=\u0022edit-submit\u0022 name=\u0022op\u0022 value=\u0022Log in\u0022 \/\u003E\n\u003C\/div\u003E\n"},"page_layout":"default","local_tasks":[] + } +}) \ No newline at end of file