feat!: new server-only, faster and immediate autoLogin (#7224)
- When autoLogin is enabled, it will no longer flash an unresponsive "login" screen. Instead, it will straight up open the admin panel. That's because, on the server, we will now always & immediately see the user as authenticated, thus no initial login view is pushed to the client until the client component sends the auth request anymore. Less useless requests. Additionally, jwt verification is now completely skipped - No more auto-login related frontend code. autoLogin handling has been removed from the frontend `Auth` component - less code to maintain, this is way simpler now **For reviewers:** - The new logic for autoFill without prefillOnly is here: [jwt auth strategy](https://github.com/payloadcms/payload/pull/7224/files#diff-7d40839079a8b2abb58233e5904513ab321023a70538229dfaf1dfee067dc8bfR21) - The new logic for autoFill with prefillOnly is here: [Server Login View](https://github.com/payloadcms/payload/pull/7224/files#diff-683770104f196196743398a698fbf8987f00e4426ca1c0ace3658d18ab80e82dL72) => [Client Login Form](https://github.com/payloadcms/payload/pull/7224/files#diff-ac3504d3b3b0489455245663649bef9e84477bf0c1185da5a4d3a612450f01eeL20) **BREAKING** `autoLogin` without `prefillOnly` set now also affects graphQL/Rest operations. Only the user specified in `autoLogin` will be returned. Within the graphQL/Rest/Local API, this should still allow you to authenticate with a different user, as the autoLogin user is only used if no token is set.
This commit is contained in:
@@ -86,19 +86,19 @@ const config = buildConfig({
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `autoLogin` | Used to automate admin log-in for dev and demonstration convenience. [More details](../authentication/overview). |
|
||||
| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
|
||||
| `buildPath` | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
|
||||
| `components` | Component overrides that affect the entirety of the Admin Panel. [More details](./components). |
|
||||
| `custom` | Any custom properties you wish to pass to the Admin Panel. |
|
||||
| `dateFormat` | The date format that will be used for all dates within the Admin Panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
|
||||
| `disable` | If set to `true`, the entire Admin Panel will be disabled. |
|
||||
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
|
||||
| `meta` | Base metadata to use for the Admin Panel. Included properties are `titleSuffix`, `icons`, and `openGraph`. Can be overridden on a per Collection or per Global basis. |
|
||||
| `routes` | Replace built-in Admin Panel routes with your own custom routes. [More details](#customizing-routes). |
|
||||
| `user` | The `slug` of the Collection that you want to allow to login to the Admin Panel. [More details](#the-admin-user-collection). |
|
||||
| Option | Description |
|
||||
|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `avatar` | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
|
||||
| `autoLogin` | Used to automate log-in for dev and demonstration convenience. [More details](../authentication/overview). |
|
||||
| `buildPath` | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
|
||||
| `components` | Component overrides that affect the entirety of the Admin Panel. [More details](./components). |
|
||||
| `custom` | Any custom properties you wish to pass to the Admin Panel. |
|
||||
| `dateFormat` | The date format that will be used for all dates within the Admin Panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
|
||||
| `disable` | If set to `true`, the entire Admin Panel will be disabled. |
|
||||
| `livePreview` | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
|
||||
| `meta` | Base metadata to use for the Admin Panel. Included properties are `titleSuffix`, `icons`, and `openGraph`. Can be overridden on a per Collection or per Global basis. |
|
||||
| `routes` | Replace built-in Admin Panel routes with your own custom routes. [More details](#customizing-routes). |
|
||||
| `user` | The `slug` of the Collection that you want to allow to login to the Admin Panel. [More details](#the-admin-user-collection). |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Reminder:</strong>
|
||||
|
||||
@@ -127,30 +127,27 @@ If set to `true`, users can log in with either their username or email address.
|
||||
|
||||
If set to `true`, an email address is required when creating a new user. If set to `false`, email is not required upon creation.
|
||||
|
||||
## Admin Auto-Login
|
||||
## Auto-Login
|
||||
|
||||
For testing and demo purposes you may want to skip forcing the admin user to login in order to access the [Admin Panel](../admin/overview). Typically, all users should be required to login to access the Admin Panel, however, you can speed up local development time by enabling auto-login.
|
||||
For testing and demo purposes you may want to skip forcing the user to login in order to access your application. Typically, all users should be required to login, however, you can speed up local development time by enabling auto-login.
|
||||
|
||||
To enable auto-login, set the `autoLogin` property in the [Admin Config](../configuration/admin):
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
admin: {
|
||||
// ...
|
||||
// highlight-start
|
||||
autoLogin:
|
||||
process.env.NEXT_PUBLIC_ENABLE_AUTOLOGIN === 'true'
|
||||
? {
|
||||
email: 'test@example.com',
|
||||
password: 'test',
|
||||
prefillOnly: true,
|
||||
}
|
||||
: false,
|
||||
// highlight-end
|
||||
},
|
||||
// highlight-start
|
||||
autoLogin:
|
||||
process.env.NEXT_PUBLIC_ENABLE_AUTOLOGIN === 'true'
|
||||
? {
|
||||
email: 'test@example.com',
|
||||
password: 'test',
|
||||
prefillOnly: true,
|
||||
}
|
||||
: false,
|
||||
// highlight-end
|
||||
})
|
||||
```
|
||||
|
||||
@@ -162,9 +159,10 @@ export default buildConfig({
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
|-------------------|-----------------------------------------------------------------------------------------------------------------|
|
||||
| **`username`** | The username of the user to login as |
|
||||
| **`email`** | The email address of the user to login as |
|
||||
| **`password`** | The password of the user to login as |
|
||||
| **`password`** | The password of the user to login as. This is only needed if `prefillOnly` is set to true |
|
||||
| **`prefillOnly`** | If set to true, the login credentials will be prefilled but the user will still need to click the login button. |
|
||||
|
||||
## Operations
|
||||
|
||||
@@ -64,38 +64,38 @@ export default buildConfig({
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`admin`** | The configuration options for the Admin Panel, including Custom Components, Live Preview, etc. [More details](../admin/overview#admin-options). |
|
||||
| **`bin`** | Register custom bin scripts for Payload to execute. |
|
||||
| **`editor`** | The Rich Text Editor which will be used by `richText` fields. [More details](../rich-text/overview). |
|
||||
| **`db`** \* | The Database Adapter which will be used by Payload. [More details](../database/overview). |
|
||||
| **`serverURL`** | A string used to define the absolute URL of your app. This includes the protocol, for example `https://example.com`. No paths allowed, only protocol, domain and (optionally) port. |
|
||||
| **`collections`** | An array of Collections for Payload to manage. [More details](./collections). |
|
||||
| **`globals`** | An array of Globals for Payload to manage. [More details](./globals). |
|
||||
| Option | Description |
|
||||
|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`admin`** | The configuration options for the Admin Panel, including Custom Components, Live Preview, etc. [More details](../admin/overview#admin-options). |
|
||||
| **`bin`** | Register custom bin scripts for Payload to execute. |
|
||||
| **`editor`** | The Rich Text Editor which will be used by `richText` fields. [More details](../rich-text/overview). |
|
||||
| **`db`** \* | The Database Adapter which will be used by Payload. [More details](../database/overview). |
|
||||
| **`serverURL`** | A string used to define the absolute URL of your app. This includes the protocol, for example `https://example.com`. No paths allowed, only protocol, domain and (optionally) port. |
|
||||
| **`collections`** | An array of Collections for Payload to manage. [More details](./collections). |
|
||||
| **`globals`** | An array of Globals for Payload to manage. [More details](./globals). |
|
||||
| **`cors`** | Cross-origin resource sharing (CORS) is a mechanism that accept incoming requests from given domains. You can also customize the `Access-Control-Allow-Headers` header. [More details](#cors). |
|
||||
| **`localization`** | Opt-in to translate your content into multiple locales. [More details](./localization). |
|
||||
| **`graphQL`** | Manage GraphQL-specific functionality, including custom queries and mutations, query complexity limits, etc. [More details](../graphql/overview#graphql-options). |
|
||||
| **`cookiePrefix`** | A string that will be prefixed to all cookies that Payload sets. |
|
||||
| **`csrf`** | A whitelist array of URLs to allow Payload to accept cookies from. [More details](../authentication/overview#csrf-protection). |
|
||||
| **`defaultDepth`** | If a user does not specify `depth` while requesting a resource, this depth will be used. [More details](../queries/depth). |
|
||||
| **`defaultMaxTextLength`** | The maximum allowed string length to be permitted application-wide. Helps to prevent malicious public document creation. |
|
||||
| **`maxDepth`** | The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries. Defaults to `10`. [More details](../queries/depth). |
|
||||
| **`indexSortableFields`** | Automatically index all sortable top-level fields in the database to improve sort performance and add database compatibility for Azure Cosmos and similar. |
|
||||
| **`upload`** | Base Payload upload configuration. [More details](../upload/overview#payload-wide-upload-options). |
|
||||
| **`routes`** | Control the routing structure that Payload binds itself to. [More details](../admin/overview#root-level-routes). |
|
||||
| **`email`** | Configure the Email Adapter for Payload to use. [More details](../email/overview). |
|
||||
| **`debug`** | Enable to expose more detailed error information. |
|
||||
| **`telemetry`** | Disable Payload telemetry by passing `false`. [More details](#telemetry). |
|
||||
| **`rateLimit`** | Control IP-based rate limiting for all Payload resources. Used to prevent DDoS attacks, etc. [More details](../production/preventing-abuse#rate-limiting-requests). |
|
||||
| **`hooks`** | An array of Root Hooks. [More details](../hooks/overview). |
|
||||
| **`plugins`** | An array of Plugins. [More details](../plugins/overview). |
|
||||
| **`endpoints`** | An array of Custom Endpoints added to the Payload router. [More details](../rest-api/overview#custom-endpoints). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins). |
|
||||
| **`i18n`** | Internationalization configuration. Pass all i18n languages you'd like the admin UI to support. Defaults to English-only. [More details](./i18n). |
|
||||
| **`secret`** \* | A secure, unguessable string that Payload will use for any encryption workflows - for example, password salt / hashing. |
|
||||
| **`sharp`** | If you would like Payload to offer cropping, focal point selection, and automatic media resizing, install and pass the Sharp module to the config here. |
|
||||
| **`typescript`** | Configure TypeScript settings here. [More details](#typescript). |
|
||||
| **`localization`** | Opt-in to translate your content into multiple locales. [More details](./localization). |
|
||||
| **`graphQL`** | Manage GraphQL-specific functionality, including custom queries and mutations, query complexity limits, etc. [More details](../graphql/overview#graphql-options). |
|
||||
| **`cookiePrefix`** | A string that will be prefixed to all cookies that Payload sets. |
|
||||
| **`csrf`** | A whitelist array of URLs to allow Payload to accept cookies from. [More details](../authentication/overview#csrf-protection). |
|
||||
| **`defaultDepth`** | If a user does not specify `depth` while requesting a resource, this depth will be used. [More details](../queries/depth). |
|
||||
| **`defaultMaxTextLength`** | The maximum allowed string length to be permitted application-wide. Helps to prevent malicious public document creation. |
|
||||
| **`maxDepth`** | The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries. Defaults to `10`. [More details](../queries/depth). |
|
||||
| **`indexSortableFields`** | Automatically index all sortable top-level fields in the database to improve sort performance and add database compatibility for Azure Cosmos and similar. |
|
||||
| **`upload`** | Base Payload upload configuration. [More details](../upload/overview#payload-wide-upload-options). |
|
||||
| **`routes`** | Control the routing structure that Payload binds itself to. [More details](../admin/overview#root-level-routes). |
|
||||
| **`email`** | Configure the Email Adapter for Payload to use. [More details](../email/overview). |
|
||||
| **`debug`** | Enable to expose more detailed error information. |
|
||||
| **`telemetry`** | Disable Payload telemetry by passing `false`. [More details](#telemetry). |
|
||||
| **`rateLimit`** | Control IP-based rate limiting for all Payload resources. Used to prevent DDoS attacks, etc. [More details](../production/preventing-abuse#rate-limiting-requests). |
|
||||
| **`hooks`** | An array of Root Hooks. [More details](../hooks/overview). |
|
||||
| **`plugins`** | An array of Plugins. [More details](../plugins/overview). |
|
||||
| **`endpoints`** | An array of Custom Endpoints added to the Payload router. [More details](../rest-api/overview#custom-endpoints). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins). |
|
||||
| **`i18n`** | Internationalization configuration. Pass all i18n languages you'd like the admin UI to support. Defaults to English-only. [More details](./i18n). |
|
||||
| **`secret`** \* | A secure, unguessable string that Payload will use for any encryption workflows - for example, password salt / hashing. |
|
||||
| **`sharp`** | If you would like Payload to offer cropping, focal point selection, and automatic media resizing, install and pass the Sharp module to the config here. |
|
||||
| **`typescript`** | Configure TypeScript settings here. [More details](#typescript). |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -17,13 +17,15 @@ import { LoginField } from '../LoginField/index.js'
|
||||
import './index.scss'
|
||||
|
||||
export const LoginForm: React.FC<{
|
||||
prefillEmail?: string
|
||||
prefillPassword?: string
|
||||
prefillUsername?: string
|
||||
searchParams: { [key: string]: string | string[] | undefined }
|
||||
}> = ({ searchParams }) => {
|
||||
}> = ({ prefillEmail, prefillPassword, prefillUsername, searchParams }) => {
|
||||
const config = useConfig()
|
||||
|
||||
const {
|
||||
admin: {
|
||||
autoLogin,
|
||||
routes: { forgot: forgotRoute },
|
||||
user: userSlug,
|
||||
},
|
||||
@@ -45,27 +47,25 @@ export const LoginForm: React.FC<{
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
const prefillForm = autoLogin && autoLogin.prefillOnly
|
||||
|
||||
const initialState: FormState = {
|
||||
password: {
|
||||
initialValue: prefillForm ? autoLogin.password : undefined,
|
||||
initialValue: prefillPassword ?? undefined,
|
||||
valid: true,
|
||||
value: prefillForm ? autoLogin.password : undefined,
|
||||
value: prefillPassword ?? undefined,
|
||||
},
|
||||
}
|
||||
|
||||
if (loginWithUsername) {
|
||||
initialState.username = {
|
||||
initialValue: prefillForm ? autoLogin.username : undefined,
|
||||
initialValue: prefillUsername ?? undefined,
|
||||
valid: true,
|
||||
value: prefillForm ? autoLogin.username : undefined,
|
||||
value: prefillUsername ?? undefined,
|
||||
}
|
||||
} else {
|
||||
initialState.email = {
|
||||
initialValue: prefillForm ? autoLogin.email : undefined,
|
||||
initialValue: prefillEmail ?? undefined,
|
||||
valid: true,
|
||||
value: prefillForm ? autoLogin.email : undefined,
|
||||
value: prefillEmail ?? undefined,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,24 @@ export const LoginView: React.FC<AdminViewProps> = ({ initPageResult, params, se
|
||||
|
||||
const collectionConfig = collections.find(({ slug }) => slug === userSlug)
|
||||
|
||||
const prefillAutoLogin =
|
||||
typeof config.admin?.autoLogin === 'object' && config.admin?.autoLogin.prefillOnly
|
||||
|
||||
const prefillUsername =
|
||||
prefillAutoLogin && typeof config.admin?.autoLogin === 'object'
|
||||
? config.admin?.autoLogin.username
|
||||
: undefined
|
||||
|
||||
const prefillEmail =
|
||||
prefillAutoLogin && typeof config.admin?.autoLogin === 'object'
|
||||
? config.admin?.autoLogin.email
|
||||
: undefined
|
||||
|
||||
const prefillPassword =
|
||||
prefillAutoLogin && typeof config.admin?.autoLogin === 'object'
|
||||
? config.admin?.autoLogin.password
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={`${loginBaseClass}__brand`}>
|
||||
@@ -84,7 +102,14 @@ export const LoginView: React.FC<AdminViewProps> = ({ initPageResult, params, se
|
||||
/>
|
||||
</div>
|
||||
{Array.isArray(BeforeLogins) && BeforeLogins.map((Component) => Component)}
|
||||
{!collectionConfig?.auth?.disableLocalStrategy && <LoginForm searchParams={searchParams} />}
|
||||
{!collectionConfig?.auth?.disableLocalStrategy && (
|
||||
<LoginForm
|
||||
prefillEmail={prefillEmail}
|
||||
prefillPassword={prefillPassword}
|
||||
prefillUsername={prefillUsername}
|
||||
searchParams={searchParams}
|
||||
/>
|
||||
)}
|
||||
{Array.isArray(AfterLogins) && AfterLogins.map((Component) => Component)}
|
||||
</Fragment>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import jwt from 'jsonwebtoken'
|
||||
|
||||
import type { Where } from '../../types/index.js'
|
||||
import type { AuthStrategyFunction, User } from '../index.js'
|
||||
|
||||
import { extractJWT } from '../extractJWT.js'
|
||||
@@ -16,6 +17,46 @@ export const JWTAuthentication: AuthStrategyFunction = async ({
|
||||
}) => {
|
||||
try {
|
||||
const token = extractJWT({ headers, payload })
|
||||
|
||||
if (
|
||||
!token &&
|
||||
typeof payload?.config?.admin?.autoLogin === 'object' &&
|
||||
!payload.config.admin?.autoLogin.prefillOnly &&
|
||||
headers.get('DisableAutologin') !== 'true'
|
||||
) {
|
||||
const collection = payload.collections[payload.config.admin.user]
|
||||
|
||||
const where: Where = {
|
||||
or: [],
|
||||
}
|
||||
if (payload.config.admin?.autoLogin.email) {
|
||||
where.or.push({
|
||||
email: {
|
||||
equals: payload.config.admin?.autoLogin.email,
|
||||
},
|
||||
})
|
||||
} else if (payload.config.admin?.autoLogin.username) {
|
||||
where.or.push({
|
||||
username: {
|
||||
equals: payload.config.admin?.autoLogin.username,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const user = (
|
||||
await payload.find({
|
||||
collection: collection.config.slug,
|
||||
depth: isGraphQL ? 0 : collection.config.auth.depth,
|
||||
where,
|
||||
})
|
||||
).docs[0]
|
||||
user.collection = collection.config.slug
|
||||
user._strategy = 'local-jwt'
|
||||
return {
|
||||
user: user as User,
|
||||
}
|
||||
}
|
||||
|
||||
const decodedPayload = jwt.verify(token, payload.secret) as JWTToken & jwt.JwtPayload
|
||||
|
||||
const collection = payload.collections[decodedPayload.collection]
|
||||
|
||||
@@ -43,6 +43,27 @@ export type ClientConfig = {
|
||||
globals: ClientGlobalConfig[]
|
||||
} & Omit<SanitizedConfig, 'admin' | 'collections' | 'globals' | ServerOnlyRootProperties>
|
||||
|
||||
const serverOnlyConfigProperties: readonly Partial<ServerOnlyRootProperties>[] = [
|
||||
'endpoints',
|
||||
'db',
|
||||
'editor',
|
||||
'plugins',
|
||||
'sharp',
|
||||
'onInit',
|
||||
'secret',
|
||||
'hooks',
|
||||
'bin',
|
||||
'typescript',
|
||||
'cors',
|
||||
'csrf',
|
||||
'email',
|
||||
'custom',
|
||||
'graphQL',
|
||||
// `admin`, `onInit`, `localization`, `collections`, and `globals` are all handled separately
|
||||
]
|
||||
|
||||
const serverOnlyAdminProperties: readonly Partial<ServerOnlyRootAdminProperties>[] = ['components']
|
||||
|
||||
export const createClientConfig = async ({
|
||||
config,
|
||||
t,
|
||||
@@ -53,49 +74,28 @@ export const createClientConfig = async ({
|
||||
}): Promise<ClientConfig> => {
|
||||
const clientConfig: ClientConfig = { ...config }
|
||||
|
||||
const serverOnlyConfigProperties: Partial<ServerOnlyRootProperties>[] = [
|
||||
'endpoints',
|
||||
'db',
|
||||
'editor',
|
||||
'plugins',
|
||||
'sharp',
|
||||
'onInit',
|
||||
'secret',
|
||||
'hooks',
|
||||
'bin',
|
||||
'typescript',
|
||||
'cors',
|
||||
'csrf',
|
||||
'email',
|
||||
'custom',
|
||||
'graphQL',
|
||||
// `admin`, `onInit`, `localization`, `collections`, and `globals` are all handled separately
|
||||
]
|
||||
|
||||
serverOnlyConfigProperties.forEach((key) => {
|
||||
for (const key of serverOnlyConfigProperties) {
|
||||
if (key in clientConfig) {
|
||||
delete clientConfig[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if ('localization' in clientConfig && clientConfig.localization) {
|
||||
clientConfig.localization = { ...clientConfig.localization }
|
||||
|
||||
clientConfig.localization.locales.forEach((locale) => {
|
||||
for (const locale of clientConfig.localization.locales) {
|
||||
delete locale.toString
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if ('admin' in clientConfig) {
|
||||
clientConfig.admin = { ...clientConfig.admin }
|
||||
|
||||
const serverOnlyAdminProperties: Partial<ServerOnlyRootAdminProperties>[] = ['components']
|
||||
|
||||
serverOnlyAdminProperties.forEach((key) => {
|
||||
for (const key of serverOnlyAdminProperties) {
|
||||
if (key in clientConfig.admin) {
|
||||
delete clientConfig.admin[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if ('livePreview' in clientConfig.admin) {
|
||||
clientConfig.admin.livePreview = { ...clientConfig.admin.livePreview }
|
||||
|
||||
@@ -453,16 +453,15 @@ export type CORSConfig = {
|
||||
export type Config = {
|
||||
/** Configure admin dashboard */
|
||||
admin?: {
|
||||
/** Automatically log in as a user when visiting the admin dashboard. */
|
||||
/** Automatically log in as a user */
|
||||
autoLogin?:
|
||||
| {
|
||||
/**
|
||||
* The email address of the user to login as
|
||||
*
|
||||
*/
|
||||
email?: string
|
||||
/** The password of the user to login as */
|
||||
password: string
|
||||
/** The password of the user to login as. This is only needed if `prefillOnly` is set to true */
|
||||
password?: string
|
||||
/**
|
||||
* If set to true, the login credentials will be prefilled but the user will still need to click the login button.
|
||||
*
|
||||
@@ -473,9 +472,9 @@ export type Config = {
|
||||
username?: string
|
||||
}
|
||||
| false
|
||||
|
||||
/** Set account profile picture. Options: gravatar, default or a custom React component. */
|
||||
avatar?: 'default' | 'gravatar' | React.ComponentType<any>
|
||||
|
||||
/**
|
||||
* Add extra and/or replace built-in components with custom components
|
||||
*
|
||||
|
||||
@@ -12,7 +12,6 @@ import { useDebounce } from '../../hooks/useDebounce.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
import { requests } from '../../utilities/api.js'
|
||||
import { useConfig } from '../Config/index.js'
|
||||
import { useSearchParams } from '../SearchParams/index.js'
|
||||
|
||||
export type AuthContext<T = ClientUser> = {
|
||||
fetchFullUser: () => Promise<void>
|
||||
@@ -34,11 +33,9 @@ const Context = createContext({} as AuthContext)
|
||||
const maxTimeoutTime = 2147483647
|
||||
|
||||
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const { searchParams } = useSearchParams()
|
||||
const [user, setUser] = useState<ClientUser | null>()
|
||||
const [tokenInMemory, setTokenInMemory] = useState<string>()
|
||||
const [tokenExpiration, setTokenExpiration] = useState<number>()
|
||||
const [strategy, setStrategy] = useState<string>()
|
||||
const pathname = usePathname()
|
||||
const router = useRouter()
|
||||
// const { code } = useLocale()
|
||||
@@ -48,9 +45,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
|
||||
const {
|
||||
admin: {
|
||||
autoLogin,
|
||||
routes: { inactivity: logoutInactivityRoute },
|
||||
routes: { login: loginRoute },
|
||||
user: userSlug,
|
||||
},
|
||||
routes: { admin, api },
|
||||
@@ -79,7 +74,6 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
const revokeTokenAndExpire = useCallback(() => {
|
||||
setTokenInMemory(undefined)
|
||||
setTokenExpiration(undefined)
|
||||
setStrategy(undefined)
|
||||
}, [])
|
||||
|
||||
const setTokenAndExpiration = useCallback(
|
||||
@@ -88,7 +82,6 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
if (token && json?.exp) {
|
||||
setTokenInMemory(token)
|
||||
setTokenExpiration(json.exp)
|
||||
if (json.strategy) setStrategy(json.strategy)
|
||||
} else {
|
||||
revokeTokenAndExpire()
|
||||
}
|
||||
@@ -215,36 +208,6 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
if (json?.token) {
|
||||
setTokenAndExpiration(json)
|
||||
}
|
||||
} else if (autoLogin && autoLogin.prefillOnly !== true) {
|
||||
// auto log-in with the provided autoLogin credentials. This is used in dev mode
|
||||
// so you don't have to log in over and over again
|
||||
const autoLoginResult = await requests.post(
|
||||
`${serverURL}${api}/${userSlug}${loginRoute}`,
|
||||
{
|
||||
body: JSON.stringify({
|
||||
email: autoLogin.email,
|
||||
password: autoLogin.password,
|
||||
username: autoLogin.username,
|
||||
}),
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
)
|
||||
if (autoLoginResult.status === 200) {
|
||||
const autoLoginJson = await autoLoginResult.json()
|
||||
setUser(autoLoginJson.user)
|
||||
if (autoLoginJson?.token) {
|
||||
setTokenAndExpiration(autoLoginJson)
|
||||
}
|
||||
router.replace(
|
||||
typeof searchParams['redirect'] === 'string' ? searchParams['redirect'] : admin,
|
||||
)
|
||||
} else {
|
||||
setUser(null)
|
||||
revokeTokenAndExpire()
|
||||
}
|
||||
} else {
|
||||
setUser(null)
|
||||
revokeTokenAndExpire()
|
||||
@@ -253,21 +216,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
} catch (e) {
|
||||
toast.error(`Fetching user failed: ${e.message}`)
|
||||
}
|
||||
}, [
|
||||
serverURL,
|
||||
api,
|
||||
userSlug,
|
||||
i18n.language,
|
||||
autoLogin,
|
||||
setTokenAndExpiration,
|
||||
router,
|
||||
searchParams,
|
||||
admin,
|
||||
revokeTokenAndExpire,
|
||||
strategy,
|
||||
tokenExpiration,
|
||||
loginRoute,
|
||||
])
|
||||
}, [serverURL, api, userSlug, i18n.language, setTokenAndExpiration, revokeTokenAndExpire])
|
||||
|
||||
// On mount, get user and set
|
||||
useEffect(() => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { expect, test } from '@playwright/test'
|
||||
import * as path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import { ensureAutoLoginAndCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { ensureCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
||||
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
||||
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||
@@ -25,7 +25,7 @@ test.describe('Admin Panel', () => {
|
||||
const context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
initPageConsoleErrorCatch(page)
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test('example test', async () => {
|
||||
|
||||
@@ -16,6 +16,9 @@ export interface Config {
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-migrations': PayloadMigration;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: string;
|
||||
};
|
||||
globals: {
|
||||
menu: Menu;
|
||||
'custom-ts': CustomT;
|
||||
@@ -30,13 +33,16 @@ export interface UserAuthOperations {
|
||||
email: string;
|
||||
};
|
||||
login: {
|
||||
password: string;
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
registerFirstUser: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
unlock: {
|
||||
email: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
|
||||
@@ -16,7 +16,7 @@ import type {
|
||||
|
||||
import {
|
||||
closeNav,
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
exactText,
|
||||
getAdminRoutes,
|
||||
initPageConsoleErrorCatch,
|
||||
@@ -92,7 +92,7 @@ describe('access control', () => {
|
||||
initPageConsoleErrorCatch(page)
|
||||
|
||||
await login({ page, serverURL })
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
|
||||
const {
|
||||
admin: {
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { Config, Geo, Post } from '../../payload-types.js'
|
||||
import {
|
||||
checkBreadcrumb,
|
||||
checkPageTitle,
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
exactText,
|
||||
getAdminRoutes,
|
||||
initPageConsoleErrorCatch,
|
||||
@@ -103,7 +103,7 @@ describe('admin1', () => {
|
||||
snapshotKey: 'adminTests1',
|
||||
})
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ customAdminRoutes, page, serverURL })
|
||||
await ensureCompilationIsDone({ customAdminRoutes, page, serverURL })
|
||||
|
||||
adminRoutes = getAdminRoutes({ customAdminRoutes })
|
||||
|
||||
@@ -115,7 +115,7 @@ describe('admin1', () => {
|
||||
snapshotKey: 'adminTests1',
|
||||
})
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ customAdminRoutes, page, serverURL })
|
||||
await ensureCompilationIsDone({ customAdminRoutes, page, serverURL })
|
||||
})
|
||||
|
||||
describe('metadata', () => {
|
||||
@@ -294,14 +294,6 @@ describe('admin1', () => {
|
||||
await expect(() => expect(page.url()).not.toContain(loginURL)).toPass({
|
||||
timeout: POLL_TOPASS_TIMEOUT,
|
||||
})
|
||||
|
||||
// Ensure auto-login logged the user back in
|
||||
|
||||
await expect(() => expect(page.url()).toBe(`${serverURL}${adminRoutes.routes.admin}`)).toPass(
|
||||
{
|
||||
timeout: POLL_TOPASS_TIMEOUT,
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as qs from 'qs-esm'
|
||||
import type { Config, Geo, Post } from '../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
exactText,
|
||||
getAdminRoutes,
|
||||
initPageConsoleErrorCatch,
|
||||
@@ -67,7 +67,7 @@ describe('admin2', () => {
|
||||
snapshotKey: 'adminTests2',
|
||||
})
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ customAdminRoutes, page, serverURL })
|
||||
await ensureCompilationIsDone({ customAdminRoutes, page, serverURL })
|
||||
|
||||
adminRoutes = getAdminRoutes({ customAdminRoutes })
|
||||
})
|
||||
@@ -77,7 +77,7 @@ describe('admin2', () => {
|
||||
snapshotKey: 'adminTests2',
|
||||
})
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ customAdminRoutes, page, serverURL })
|
||||
await ensureCompilationIsDone({ customAdminRoutes, page, serverURL })
|
||||
})
|
||||
|
||||
describe('custom CSS', () => {
|
||||
|
||||
@@ -30,6 +30,9 @@ export interface Config {
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-migrations': PayloadMigration;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: string;
|
||||
};
|
||||
globals: {
|
||||
'hidden-global': HiddenGlobal;
|
||||
'global-no-api-view': GlobalNoApiView;
|
||||
@@ -49,13 +52,16 @@ export interface UserAuthOperations {
|
||||
email: string;
|
||||
};
|
||||
login: {
|
||||
password: string;
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
registerFirstUser: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
unlock: {
|
||||
email: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { PayloadTestSDK } from '../helpers/sdk/index.js'
|
||||
import type { Config } from './payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
getAdminRoutes,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
@@ -105,7 +105,7 @@ describe('auth', () => {
|
||||
enableAPIKey: true,
|
||||
},
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
describe('authenticated users', () => {
|
||||
|
||||
@@ -179,15 +179,16 @@ export async function buildConfigWithDefaults(
|
||||
},
|
||||
}
|
||||
|
||||
config.admin = {
|
||||
autoLogin:
|
||||
if (!config.admin) {
|
||||
config.admin = {}
|
||||
}
|
||||
if (config.admin.autoLogin === undefined) {
|
||||
config.admin.autoLogin =
|
||||
process.env.PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN === 'true'
|
||||
? false
|
||||
: {
|
||||
email: 'dev@payloadcms.com',
|
||||
password: 'test',
|
||||
},
|
||||
...(config.admin || {}),
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.PAYLOAD_DISABLE_ADMIN === 'true') {
|
||||
|
||||
@@ -5,11 +5,7 @@ import { AdminUrlUtil } from 'helpers/adminUrlUtil.js'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
} from '../helpers.js'
|
||||
import { ensureCompilationIsDone, initPageConsoleErrorCatch, saveDocAndAssert } from '../helpers.js'
|
||||
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
||||
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||
import { slugs } from './shared.js'
|
||||
@@ -35,7 +31,7 @@ describe('field error states', () => {
|
||||
page = await context.newPage()
|
||||
initPageConsoleErrorCatch(page)
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test('Remove row should remove error states from parent fields', async () => {
|
||||
|
||||
@@ -16,7 +16,7 @@ import type {
|
||||
} from './payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
openCreateDocDrawer,
|
||||
openDocControls,
|
||||
@@ -67,7 +67,7 @@ describe('fields - relationship', () => {
|
||||
page = await context.newPage()
|
||||
|
||||
initPageConsoleErrorCatch(page)
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -137,7 +137,7 @@ describe('fields - relationship', () => {
|
||||
},
|
||||
})) as any
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test('should create relationship', async () => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { PayloadTestSDK } from '../../../helpers/sdk/index.js'
|
||||
import type { Config } from '../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
} from '../../../helpers.js'
|
||||
@@ -48,7 +48,7 @@ describe('Array', () => {
|
||||
snapshotKey: 'fieldsArrayTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -63,7 +63,7 @@ describe('Array', () => {
|
||||
client = new RESTClient(null, { defaultSlug: 'users', serverURL })
|
||||
await client.login()
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
let url: AdminUrlUtil
|
||||
|
||||
@@ -5,7 +5,7 @@ import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
} from '../../../helpers.js'
|
||||
@@ -43,7 +43,7 @@ describe('Block fields', () => {
|
||||
snapshotKey: 'blockFieldsTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -58,7 +58,7 @@ describe('Block fields', () => {
|
||||
client = new RESTClient(null, { defaultSlug: 'users', serverURL })
|
||||
await client.login()
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
let url: AdminUrlUtil
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { PayloadTestSDK } from '../../../helpers/sdk/index.js'
|
||||
import type { Config } from '../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
} from '../../../helpers.js'
|
||||
@@ -50,7 +50,7 @@ describe('Date', () => {
|
||||
snapshotKey: 'fieldsDateTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -65,7 +65,7 @@ describe('Date', () => {
|
||||
client = new RESTClient(null, { defaultSlug: 'users', serverURL })
|
||||
await client.login()
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test('should display formatted date in list view table cell', async () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { PayloadTestSDK } from '../../../../../helpers/sdk/index.js'
|
||||
import type { Config, LexicalField, Upload } from '../../../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
} from '../../../../../helpers.js'
|
||||
@@ -74,7 +74,7 @@ describe('lexicalBlocks', () => {
|
||||
snapshotKey: 'fieldsLexicalBlocksTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
/*await throttleTest({
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { PayloadTestSDK } from '../../../../../helpers/sdk/index.js'
|
||||
import type { Config, LexicalField } from '../../../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
} from '../../../../../helpers.js'
|
||||
@@ -73,7 +73,7 @@ describe('lexicalMain', () => {
|
||||
snapshotKey: 'fieldsLexicalMainTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
/*await throttleTest({
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { PayloadTestSDK } from '../../../helpers/sdk/index.js'
|
||||
import type { Config } from '../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
} from '../../../helpers.js'
|
||||
@@ -51,7 +51,7 @@ describe('Number', () => {
|
||||
snapshotKey: 'fieldsNumberTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -66,7 +66,7 @@ describe('Number', () => {
|
||||
client = new RESTClient(null, { defaultSlug: 'users', serverURL })
|
||||
await client.login()
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test('should display field in list view', async () => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { PayloadTestSDK } from '../../../helpers/sdk/index.js'
|
||||
import type { Config } from '../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
} from '../../../helpers.js'
|
||||
@@ -51,7 +51,7 @@ describe('Point', () => {
|
||||
snapshotKey: 'fieldsPointTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -66,7 +66,7 @@ describe('Point', () => {
|
||||
client = new RESTClient(null, { defaultSlug: 'users', serverURL })
|
||||
await client.login()
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
|
||||
filledGroupPoint = await payload.create({
|
||||
collection: pointFieldsSlug,
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { PayloadTestSDK } from '../../../helpers/sdk/index.js'
|
||||
import type { Config, RelationshipField, TextField } from '../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
exactText,
|
||||
initPageConsoleErrorCatch,
|
||||
openCreateDocDrawer,
|
||||
@@ -50,7 +50,7 @@ describe('relationship', () => {
|
||||
snapshotKey: 'fieldsRelationshipTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -65,7 +65,7 @@ describe('relationship', () => {
|
||||
client = new RESTClient(null, { defaultSlug: 'users', serverURL })
|
||||
await client.login()
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
let url: AdminUrlUtil
|
||||
|
||||
@@ -6,7 +6,7 @@ import { wait } from 'payload/shared'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
} from '../../../helpers.js'
|
||||
@@ -43,7 +43,7 @@ describe('Rich Text', () => {
|
||||
snapshotKey: 'fieldsRichTextTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -58,7 +58,7 @@ describe('Rich Text', () => {
|
||||
client = new RESTClient(null, { defaultSlug: 'users', serverURL })
|
||||
await client.login()
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
async function navigateToRichTextFields() {
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { PayloadTestSDK } from '../../../helpers/sdk/index.js'
|
||||
import type { Config } from '../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
navigateToListCellLink,
|
||||
saveDocAndAssert,
|
||||
@@ -53,7 +53,7 @@ describe('Tabs', () => {
|
||||
snapshotKey: 'fieldsTabsTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -68,7 +68,7 @@ describe('Tabs', () => {
|
||||
client = new RESTClient(null, { defaultSlug: 'users', serverURL })
|
||||
await client.login()
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test('should fill and retain a new value within a tab while switching tabs', async () => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { PayloadTestSDK } from '../../../helpers/sdk/index.js'
|
||||
import type { Config } from '../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
exactText,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
@@ -53,7 +53,7 @@ describe('Text', () => {
|
||||
snapshotKey: 'fieldsTextTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -68,7 +68,7 @@ describe('Text', () => {
|
||||
client = new RESTClient(null, { defaultSlug: 'users', serverURL })
|
||||
await client.login()
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test('should display field in list view', async () => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { PayloadTestSDK } from '../../../helpers/sdk/index.js'
|
||||
import type { Config } from '../../payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
openDocDrawer,
|
||||
saveDocAndAssert,
|
||||
@@ -52,7 +52,7 @@ describe('Upload', () => {
|
||||
snapshotKey: 'fieldsUploadTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -67,7 +67,7 @@ describe('Upload', () => {
|
||||
client = new RESTClient(null, { defaultSlug: 'users', serverURL })
|
||||
await client.login()
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
async function uploadImage() {
|
||||
|
||||
@@ -8,11 +8,7 @@ import { fileURLToPath } from 'url'
|
||||
import type { PayloadTestSDK } from '../helpers/sdk/index.js'
|
||||
import type { Config } from './payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
} from '../helpers.js'
|
||||
import { ensureCompilationIsDone, initPageConsoleErrorCatch, saveDocAndAssert } from '../helpers.js'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
||||
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
||||
import { reInitializeDB } from '../helpers/reInitializeDB.js'
|
||||
@@ -53,7 +49,7 @@ describe('fields', () => {
|
||||
snapshotKey: 'fieldsTest',
|
||||
uploadsDir: path.resolve(dirname, './collections/Upload/uploads'),
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -68,7 +64,7 @@ describe('fields', () => {
|
||||
client = new RESTClient(null, { defaultSlug: 'users', serverURL })
|
||||
await client.login()
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
describe('indexed', () => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -48,11 +48,11 @@ const networkConditions = {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load admin panel and make sure autologin has passed before running tests
|
||||
* Ensure admin panel is loaded before running tests
|
||||
* @param page
|
||||
* @param serverURL
|
||||
*/
|
||||
export async function ensureAutoLoginAndCompilationIsDone({
|
||||
export async function ensureCompilationIsDone({
|
||||
customAdminRoutes,
|
||||
customRoutes,
|
||||
page,
|
||||
@@ -64,9 +64,6 @@ export async function ensureAutoLoginAndCompilationIsDone({
|
||||
serverURL: string
|
||||
}): Promise<void> {
|
||||
const {
|
||||
admin: {
|
||||
routes: { createFirstUser: createFirstUserRoute, login: loginRoute },
|
||||
},
|
||||
routes: { admin: adminRoute },
|
||||
} = getAdminRoutes({ customAdminRoutes, customRoutes })
|
||||
|
||||
@@ -79,16 +76,6 @@ export async function ensureAutoLoginAndCompilationIsDone({
|
||||
timeout: POLL_TOPASS_TIMEOUT,
|
||||
})
|
||||
|
||||
await expect(() => expect(page.url()).not.toContain(`${adminRoute}${loginRoute}`)).toPass({
|
||||
timeout: POLL_TOPASS_TIMEOUT,
|
||||
})
|
||||
|
||||
await expect(() =>
|
||||
expect(page.url()).not.toContain(`${adminRoute}${createFirstUserRoute}`),
|
||||
).toPass({
|
||||
timeout: POLL_TOPASS_TIMEOUT,
|
||||
})
|
||||
|
||||
await expect(page.locator('.dashboard__label').first()).toBeVisible()
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,9 @@ export class NextRESTClient {
|
||||
if (options.auth !== false && this.token) {
|
||||
headers.set('Authorization', `JWT ${this.token}`)
|
||||
}
|
||||
if (options.auth === false) {
|
||||
headers.set('DisableAutologin', 'true')
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ const { beforeAll, beforeEach, describe } = test
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import { ensureAutoLoginAndCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { ensureCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
||||
import { reInitializeDB } from '../helpers/reInitializeDB.js'
|
||||
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||
@@ -39,7 +39,7 @@ describe('i18n', () => {
|
||||
serverURL,
|
||||
snapshotKey: 'i18nTests',
|
||||
})
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
@@ -47,7 +47,7 @@ describe('i18n', () => {
|
||||
snapshotKey: 'i18nTests',
|
||||
})
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test('ensure i18n labels and useTranslation hooks display correct translation', async () => {
|
||||
|
||||
@@ -5,7 +5,7 @@ import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
exactText,
|
||||
initPageConsoleErrorCatch,
|
||||
saveDocAndAssert,
|
||||
@@ -57,7 +57,7 @@ describe('Live Preview', () => {
|
||||
|
||||
initPageConsoleErrorCatch(page)
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test('collection — has tab', async () => {
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { Config, LocalizedPost } from './payload-types.js'
|
||||
|
||||
import {
|
||||
changeLocale,
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
openDocControls,
|
||||
saveDocAndAssert,
|
||||
@@ -63,7 +63,7 @@ describe('Localization', () => {
|
||||
|
||||
initPageConsoleErrorCatch(page)
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
describe('localized text', () => {
|
||||
|
||||
@@ -11,6 +11,16 @@
|
||||
"test:int": "pnpm -C \"../\" run test:int",
|
||||
"typecheck": "pnpm turbo build --filter test && tsc --project tsconfig.typecheck.json"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/package.json": "sort-package-json",
|
||||
"*.{md,mdx,yml,json}": "prettier --write",
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"prettier --write",
|
||||
"eslint --cache --fix"
|
||||
],
|
||||
"templates/website/**/*": "sh -c \"cd templates/website; pnpm install --ignore-workspace --frozen-lockfile; pnpm run lint --fix\"",
|
||||
"tsconfig.json": "node scripts/reset-tsconfig.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-s3": "^3.525.0",
|
||||
"@lexical/headless": "0.16.1",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { expect, test } from '@playwright/test'
|
||||
import * as path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import { ensureAutoLoginAndCompilationIsDone, saveDocAndAssert } from '../helpers.js'
|
||||
import { ensureCompilationIsDone, saveDocAndAssert } from '../helpers.js'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
||||
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
||||
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||
@@ -24,7 +24,7 @@ test.describe('Admin Panel', () => {
|
||||
|
||||
const context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test('should create file upload', async () => {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'url'
|
||||
import type { PayloadTestSDK } from '../helpers/sdk/index.js'
|
||||
import type { Config } from './payload-types.js'
|
||||
|
||||
import { ensureAutoLoginAndCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { ensureCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
||||
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
||||
import { POLL_TOPASS_TIMEOUT, TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||
@@ -35,7 +35,7 @@ test.describe('Form Builder', () => {
|
||||
page = await context.newPage()
|
||||
initPageConsoleErrorCatch(page)
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test.describe('Forms collection', () => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'url'
|
||||
|
||||
import type { Config, Page as PayloadPage } from './payload-types.js'
|
||||
|
||||
import { ensureAutoLoginAndCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { ensureCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
||||
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
||||
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||
@@ -69,7 +69,7 @@ describe('Nested Docs Plugin', () => {
|
||||
})
|
||||
draftChildId = draftChildPage.id
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
describe('Core functionality', () => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'url'
|
||||
|
||||
import type { Config, Page as PayloadPage } from './payload-types.js'
|
||||
|
||||
import { ensureAutoLoginAndCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { ensureCompilationIsDone, initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
||||
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
|
||||
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
|
||||
@@ -57,7 +57,7 @@ describe('SEO Plugin', () => {
|
||||
})) as unknown as PayloadPage
|
||||
id = createdPage.id
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
describe('Core functionality', () => {
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { PayloadTestSDK } from '../helpers/sdk/index.js'
|
||||
import type { Config, Media } from './payload-types.js'
|
||||
|
||||
import {
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
initPageConsoleErrorCatch,
|
||||
openDocDrawer,
|
||||
saveDocAndAssert,
|
||||
@@ -89,7 +89,7 @@ describe('uploads', () => {
|
||||
|
||||
audioDoc = findAudio.docs[0] as unknown as Media
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
test('should see upload filename in relation list', async () => {
|
||||
|
||||
@@ -35,7 +35,7 @@ import type { Config } from './payload-types.js'
|
||||
import { globalSlug } from '../admin/slugs.js'
|
||||
import {
|
||||
changeLocale,
|
||||
ensureAutoLoginAndCompilationIsDone,
|
||||
ensureCompilationIsDone,
|
||||
exactText,
|
||||
findTableCell,
|
||||
initPageConsoleErrorCatch,
|
||||
@@ -113,7 +113,7 @@ describe('versions', () => {
|
||||
snapshotKey: 'versionsTest',
|
||||
})
|
||||
|
||||
await ensureAutoLoginAndCompilationIsDone({ page, serverURL })
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
//await clearAndSeedEverything(payload)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user