diff --git a/packages/next/src/routes/rest/auth/registerFirstUser.ts b/packages/next/src/routes/rest/auth/registerFirstUser.ts
index fdb6c41b04..93e113bf6b 100644
--- a/packages/next/src/routes/rest/auth/registerFirstUser.ts
+++ b/packages/next/src/routes/rest/auth/registerFirstUser.ts
@@ -1,6 +1,5 @@
import httpStatus from 'http-status'
import { generatePayloadCookie } from 'payload/auth'
-import { ValidationError } from 'payload/errors'
import { registerFirstUserOperation } from 'payload/operations'
import type { CollectionRouteHandler } from '../types.js'
@@ -10,15 +9,6 @@ import { headersWithCors } from '../../../utilities/headersWithCors.js'
export const registerFirstUser: CollectionRouteHandler = async ({ collection, req }) => {
const { data, t } = req
- if (data?.password !== data['confirm-password']) {
- throw new ValidationError([
- {
- field: 'confirm-password',
- message: req.t('Password and confirm password fields do not match.'),
- },
- ])
- }
-
const result = await registerFirstUserOperation({
collection,
data: {
diff --git a/packages/next/src/routes/rest/buildFormState.ts b/packages/next/src/routes/rest/buildFormState.ts
index bd1a4c59a7..d88260277e 100644
--- a/packages/next/src/routes/rest/buildFormState.ts
+++ b/packages/next/src/routes/rest/buildFormState.ts
@@ -62,10 +62,19 @@ export const buildFormState = async ({ req }: { req: PayloadRequest }) => {
})
}
} else {
- return Response.json(null, {
- headers,
- status: httpStatus.UNAUTHORIZED,
+ const hasUsers = await req.payload.find({
+ collection: adminUserSlug,
+ depth: 0,
+ limit: 1,
+ pagination: false,
})
+ // If there are users, we should not allow access because of /create-first-user
+ if (hasUsers.docs.length) {
+ return Response.json(null, {
+ headers,
+ status: httpStatus.UNAUTHORIZED,
+ })
+ }
}
const fieldSchemaMap = getFieldSchemaMap(req)
diff --git a/packages/next/src/views/CreateFirstUser/index.client.tsx b/packages/next/src/views/CreateFirstUser/index.client.tsx
index 23d564633e..7764318174 100644
--- a/packages/next/src/views/CreateFirstUser/index.client.tsx
+++ b/packages/next/src/views/CreateFirstUser/index.client.tsx
@@ -1,25 +1,76 @@
'use client'
+import type { FormProps } from '@payloadcms/ui/forms/Form'
import type { FieldMap } from '@payloadcms/ui/utilities/buildComponentMap'
+import type { FormState } from 'payload/types'
+import { ConfirmPassword } from '@payloadcms/ui/fields/ConfirmPassword'
+import { Email } from '@payloadcms/ui/fields/Email'
+import { Password } from '@payloadcms/ui/fields/Password'
+import { Form } from '@payloadcms/ui/forms/Form'
import { RenderFields } from '@payloadcms/ui/forms/RenderFields'
+import { FormSubmit } from '@payloadcms/ui/forms/Submit'
import { useComponentMap } from '@payloadcms/ui/providers/ComponentMap'
+import { useConfig } from '@payloadcms/ui/providers/Config'
+import { useTranslation } from '@payloadcms/ui/providers/Translation'
+import { getFormState } from '@payloadcms/ui/utilities/getFormState'
import React from 'react'
-export const CreateFirstUserFields: React.FC<{
- baseAuthFieldMap: FieldMap
+export const CreateFirstUserClient: React.FC<{
+ initialState: FormState
userSlug: string
-}> = ({ baseAuthFieldMap, userSlug }) => {
+}> = ({ initialState, userSlug }) => {
const { getFieldMap } = useComponentMap()
+ const {
+ routes: { admin, api: apiRoute },
+ serverURL,
+ } = useConfig()
+
+ const { t } = useTranslation()
+
const fieldMap = getFieldMap({ collectionSlug: userSlug })
+ const onChange: FormProps['onChange'][0] = React.useCallback(
+ async ({ formState: prevFormState }) => {
+ return getFormState({
+ apiRoute,
+ body: {
+ collectionSlug: userSlug,
+ formState: prevFormState,
+ operation: 'create',
+ schemaPath: userSlug,
+ },
+ serverURL,
+ })
+ },
+ [apiRoute, userSlug, serverURL],
+ )
+
return (
-
+
)
}
diff --git a/packages/next/src/views/CreateFirstUser/index.tsx b/packages/next/src/views/CreateFirstUser/index.tsx
index 87c0ebfdb2..aff14a7a2e 100644
--- a/packages/next/src/views/CreateFirstUser/index.tsx
+++ b/packages/next/src/views/CreateFirstUser/index.tsx
@@ -1,15 +1,14 @@
-import type { FieldTypes } from 'payload/config'
-import type { Field, WithServerSideProps as WithServerSidePropsType } from 'payload/types'
-import type { AdminViewProps } from 'payload/types'
+import type {
+ AdminViewProps,
+ Field,
+ WithServerSideProps as WithServerSidePropsType,
+} from 'payload/types'
-import { Form } from '@payloadcms/ui/forms/Form'
-import { FormSubmit } from '@payloadcms/ui/forms/Submit'
import { buildStateFromSchema } from '@payloadcms/ui/forms/buildStateFromSchema'
import { WithServerSideProps as WithServerSidePropsGeneric } from '@payloadcms/ui/providers/ComponentMap'
-import { mapFields } from '@payloadcms/ui/utilities/buildComponentMap'
import React from 'react'
-import { CreateFirstUserFields } from './index.client.js'
+import { CreateFirstUserClient } from './index.client.js'
import './index.scss'
export { generateCreateFirstUserMetadata } from './meta.js'
@@ -18,20 +17,15 @@ export const CreateFirstUserView: React.FC = async ({ initPageRe
const {
req,
req: {
- i18n,
- payload,
payload: {
- config,
config: {
admin: { user: userSlug },
- routes: { admin: adminRoute, api: apiRoute },
- serverURL,
},
},
},
} = initPageResult
- const baseAuthFields: Field[] = [
+ const fields: Field[] = [
{
name: 'email',
type: 'email',
@@ -52,64 +46,18 @@ export const CreateFirstUserView: React.FC = async ({ initPageRe
},
]
- const ssrAuthFields = [...baseAuthFields]
-
- const WithServerSideProps: WithServerSidePropsType = ({ Component, ...rest }) => {
- return
- }
-
- const userFieldSchema = config.collections.find((c) => c.slug === userSlug)
- ssrAuthFields.push(...userFieldSchema.fields)
-
const formState = await buildStateFromSchema({
- fieldSchema: ssrAuthFields,
+ fieldSchema: fields,
operation: 'create',
- preferences: {
- fields: undefined
- },
+ preferences: { fields: {} },
req,
})
- const baseAuthFieldMap = mapFields({
- WithServerSideProps,
- config,
- fieldSchema: baseAuthFields,
- i18n,
- }).map((field) => {
- // Transform field types for the password and confirm-password fields
- if (field.name === 'password') {
- const type: keyof FieldTypes = 'password'
-
- return {
- ...field,
- type,
- }
- }
- if (field.name === 'confirm-password') {
- const type: keyof FieldTypes = 'confirmPassword'
-
- return {
- ...field,
- type,
- }
- }
- return field
- })
-
return (
{req.t('general:welcome')}
{req.t('authentication:beginCreateFirstUser')}
-
+
)
}
diff --git a/packages/next/src/views/Edit/Default/Auth/index.tsx b/packages/next/src/views/Edit/Default/Auth/index.tsx
index 03bbeb42c6..a7a69edc8b 100644
--- a/packages/next/src/views/Edit/Default/Auth/index.tsx
+++ b/packages/next/src/views/Edit/Default/Auth/index.tsx
@@ -105,7 +105,7 @@ export const Auth: React.FC = (props) => {
name="password"
required
/>
-
+
)}
diff --git a/packages/next/src/views/ResetPassword/index.tsx b/packages/next/src/views/ResetPassword/index.tsx
index e68afd257d..ebe5593d1c 100644
--- a/packages/next/src/views/ResetPassword/index.tsx
+++ b/packages/next/src/views/ResetPassword/index.tsx
@@ -85,7 +85,7 @@ export const ResetPassword: React.FC = ({ initPageResult, params
name="password"
required
/>
-
+
{i18n.t('authentication:resetPassword')}
diff --git a/packages/ui/src/fields/ConfirmPassword/index.tsx b/packages/ui/src/fields/ConfirmPassword/index.tsx
index 85fd43dd9a..4eebd8069e 100644
--- a/packages/ui/src/fields/ConfirmPassword/index.tsx
+++ b/packages/ui/src/fields/ConfirmPassword/index.tsx
@@ -1,10 +1,8 @@
'use client'
-import type { Description, FormField, Validate } from 'payload/types'
+import type { FormField } from 'payload/types'
import React, { useCallback } from 'react'
-import type { FormFieldBase } from '../shared/index.js'
-
import { FieldError } from '../../forms/FieldError/index.js'
import { FieldLabel } from '../../forms/FieldLabel/index.js'
import { useFormFields } from '../../forms/Form/context.js'
@@ -12,36 +10,13 @@ import { useField } from '../../forms/useField/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { fieldBaseClass } from '../shared/index.js'
import './index.scss'
-export type ConfirmPasswordFieldProps = FormFieldBase & {
- autoComplete?: string
- className?: string
- description?: Description
+
+export type ConfirmPasswordFieldProps = {
disabled?: boolean
- label?: string
- name: string
- path?: string
- required?: boolean
- style?: React.CSSProperties
- validate?: Validate
- width?: string
}
export const ConfirmPassword: React.FC = (props) => {
- const {
- name,
- CustomError,
- CustomLabel,
- autoComplete,
- className,
- disabled,
- errorProps,
- label,
- labelProps,
- path: pathFromProps,
- required,
- style,
- width,
- } = props
+ const { disabled } = props
const password = useFormFields(([fields]) => fields.password)
const { t } = useTranslation()
@@ -61,8 +36,11 @@ export const ConfirmPassword: React.FC = (props) => {
[password, t],
)
- const { formProcessing, path, setValue, showError, value } = useField({
- path: pathFromProps || name,
+ const path = 'confirm-password'
+
+ const { setValue, showError, value } = useField({
+ disableFormData: true,
+ path,
validate,
})
@@ -80,9 +58,9 @@ export const ConfirmPassword: React.FC = (props) => {
/>
= (props) => {
const { path, setValue, showError, value } = useField({
path: pathFromContext || pathFromProps || name,
- validate: typeof validate === 'function' ? memoizedValidate : undefined,
+ validate: memoizedValidate,
})
return (
diff --git a/test/auth/config.ts b/test/auth/config.ts
index 15bed3b8a6..42dba2fc0f 100644
--- a/test/auth/config.ts
+++ b/test/auth/config.ts
@@ -216,31 +216,29 @@ export default buildConfigWithDefaults({
},
],
onInit: async (payload) => {
- if (process.env.SKIP_ON_INIT !== 'true') {
- await payload.create({
- collection: 'users',
- data: {
- custom: 'Hello, world!',
- email: devUser.email,
- password: devUser.password,
- },
- })
+ await payload.create({
+ collection: 'users',
+ data: {
+ custom: 'Hello, world!',
+ email: devUser.email,
+ password: devUser.password,
+ },
+ })
- await payload.create({
- collection: 'api-keys',
- data: {
- apiKey: uuid(),
- enableAPIKey: true,
- },
- })
+ await payload.create({
+ collection: 'api-keys',
+ data: {
+ apiKey: uuid(),
+ enableAPIKey: true,
+ },
+ })
- await payload.create({
- collection: 'api-keys',
- data: {
- apiKey: uuid(),
- enableAPIKey: true,
- },
- })
- }
+ await payload.create({
+ collection: 'api-keys',
+ data: {
+ apiKey: uuid(),
+ enableAPIKey: true,
+ },
+ })
},
})
diff --git a/test/auth/e2e.spec.ts b/test/auth/e2e.spec.ts
index 17e7c5655f..8d006fd2d7 100644
--- a/test/auth/e2e.spec.ts
+++ b/test/auth/e2e.spec.ts
@@ -1,16 +1,13 @@
import type { Page } from '@playwright/test'
import { expect, test } from '@playwright/test'
-import { devUser } from 'credentials.js'
import path from 'path'
-import { wait } from 'payload/utilities'
import { fileURLToPath } from 'url'
-import { v4 as uuid } from 'uuid'
import type { PayloadTestSDK } from '../helpers/sdk/index.js'
import type { Config } from './payload-types.js'
-import { initPageConsoleErrorCatch, saveDocAndAssert } from '../helpers.js'
+import { initPageConsoleErrorCatch, login, saveDocAndAssert } from '../helpers.js'
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
import { POLL_TOPASS_TIMEOUT } from '../playwright.config.js'
@@ -21,37 +18,25 @@ const dirname = path.dirname(filename)
let payload: PayloadTestSDK
+/**
+ * TODO: Auth
+ * create first user
+ * unlock
+ * log out
+ */
+
const { beforeAll, describe } = test
const headers = {
'Content-Type': 'application/json',
}
-const createFirstUser = async ({ page, serverURL }: { page: Page; serverURL: string }) => {
- await page.goto(serverURL + '/admin/create-first-user')
- await page.locator('#field-email').fill(devUser.email)
- await page.locator('#field-password').fill(devUser.password)
- await page.locator('#field-confirm-password').fill(devUser.password)
- await page.locator('#field-custom').fill('Hello, world!')
-
- await wait(500)
-
- await page.locator('.form-submit > button').click()
-
- await expect
- .poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT })
- .not.toContain('create-first-user')
-}
-
describe('auth', () => {
let page: Page
let url: AdminUrlUtil
let serverURL: string
let apiURL: string
- // Allows for testing create-first-user
- process.env.SKIP_ON_INIT = 'true'
-
beforeAll(async ({ browser }) => {
;({ payload, serverURL } = await initPayloadE2ENoConfig({ dirname }))
apiURL = `${serverURL}/api`
@@ -61,22 +46,11 @@ describe('auth', () => {
page = await context.newPage()
initPageConsoleErrorCatch(page)
- await createFirstUser({ page, serverURL })
+ //await delayNetwork({ context, page, delay: 'Slow 4G' })
- await payload.create({
- collection: 'api-keys',
- data: {
- apiKey: uuid(),
- enableAPIKey: true,
- },
- })
-
- await payload.create({
- collection: 'api-keys',
- data: {
- apiKey: uuid(),
- enableAPIKey: true,
- },
+ await login({
+ page,
+ serverURL,
})
})
diff --git a/test/fields-relationship/e2e.spec.ts b/test/fields-relationship/e2e.spec.ts
index 7e111ecc97..6260c934f5 100644
--- a/test/fields-relationship/e2e.spec.ts
+++ b/test/fields-relationship/e2e.spec.ts
@@ -383,11 +383,9 @@ describe('fields - relationship', () => {
})
await page.goto(url.create)
- // wait for relationship options to load
- const relationFilterOptionsReq = page.waitForResponse(/api\/relation-filter-true/)
+
// select relationshipMany field that relies on siblingData field above
await page.locator('#field-relationshipManyFiltered .rs__control').click()
- await relationFilterOptionsReq
const options = page.locator('#field-relationshipManyFiltered .rs__menu')
await expect(options).toContainText('truth')