Compare commits
18 Commits
v3.0.0-bet
...
feat/beta-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
008437d62f | ||
|
|
95112a873b | ||
|
|
25da9b62d1 | ||
|
|
1e5e07489c | ||
|
|
bcb1d4eb57 | ||
|
|
268fff2645 | ||
|
|
349d68c719 | ||
|
|
9fefe79998 | ||
|
|
9f73743806 | ||
|
|
cb00bc5856 | ||
|
|
bd22bb28aa | ||
|
|
ed1e460ae7 | ||
|
|
78ac4fd89e | ||
|
|
c328c42b72 | ||
|
|
8ede4d0098 | ||
|
|
31ad34595e | ||
|
|
33278aa8d8 | ||
|
|
aad186c8b4 |
43
.github/workflows/main.yml
vendored
43
.github/workflows/main.yml
vendored
@@ -196,7 +196,6 @@ jobs:
|
||||
- postgres-custom-schema
|
||||
- postgres-uuid
|
||||
- supabase
|
||||
- sqlite
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -549,45 +548,3 @@ jobs:
|
||||
steps:
|
||||
- if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
|
||||
run: exit 1
|
||||
|
||||
publish-canary:
|
||||
name: Publish Canary
|
||||
if: github.ref == 'refs/heads/beta'
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- all-green
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Load npm token
|
||||
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Canary release script
|
||||
# dry run hard-coded to true for testing and no npm token provided
|
||||
run: pnpm tsx ./scripts/publish-canary.ts
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
|
||||
@@ -33,6 +33,9 @@ export default buildConfig({
|
||||
| Option | Description |
|
||||
| -------------------- | ----------- |
|
||||
| `autoPluralization` | Tell Mongoose to auto-pluralize any collection names if it encounters any singular words used as collection `slug`s. |
|
||||
| `schemaOptions` | Customize schema options for all Mongoose schemas created internally. |
|
||||
| `collections` | Options on a collection-by-collection basis. [More](#collections-options) |
|
||||
| `globals` | Options for the Globals collection created by Payload. [More](#globals-options) |
|
||||
| `connectOptions` | Customize MongoDB connection options. Payload will connect to your MongoDB database using default options which you can override and extend to include all the [options](https://mongoosejs.com/docs/connections.html#options) available to mongoose. |
|
||||
| `disableIndexHints` | Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination, as it increases the speed of the count function used in that query. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false |
|
||||
| `migrationDir` | Customize the directory that migrations are stored. |
|
||||
@@ -49,3 +52,27 @@ You can access Mongoose models as follows:
|
||||
- Collection models - `payload.db.collections[myCollectionSlug]`
|
||||
- Globals model - `payload.db.globals`
|
||||
- Versions model (both collections and globals) - `payload.db.versions[myEntitySlug]`
|
||||
|
||||
### Collections Options
|
||||
|
||||
You can configure the way the MongoDB adapter works on a collection-by-collection basis, including customizing Mongoose `schemaOptions` for each collection schema created.
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
const db = mongooseAdapter({
|
||||
url: 'your-url-here',
|
||||
collections: {
|
||||
users: {
|
||||
//
|
||||
schemaOptions: {
|
||||
strict: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Global Options
|
||||
|
||||
Payload automatically creates a single `globals` collection that correspond with any Payload globals that you define. When you initialize the `mongooseAdapter`, you can specify settings here for your globals in a similar manner to how you can for collections above. Right now, the only property available is `schemaOptions` but more may be added in the future.
|
||||
|
||||
@@ -205,9 +205,7 @@ export const MyField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
Default values can be defined as a static value or a function that returns a value. When a `defaultValue` is defined statically, Payload's DB adapters will apply it to the database schema or models.
|
||||
|
||||
Functions can be written to make use of the following argument properties:
|
||||
Default values can be defined as a static string or a function that returns a string. Functions are called with the following arguments:
|
||||
|
||||
- `user` - the authenticated user object
|
||||
- `locale` - the currently selected locale string
|
||||
|
||||
@@ -57,7 +57,6 @@ export const MyUploadField: Field = {
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`displayPreview`** | Enable displaying preview of the uploaded file. Overrides related Collection's `displayPreview` option. [More](/docs/upload/overview#collection-upload-options). |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [Admin Options](../admin/fields#admin-options). |
|
||||
|
||||
@@ -94,7 +94,6 @@ _An asterisk denotes that an option is required._
|
||||
| **`adminThumbnail`** | Set the way that the [Admin Panel](../admin/overview) will display thumbnails for this Collection. [More](#admin-thumbnails) |
|
||||
| **`crop`** | Set to `false` to disable the cropping tool in the [Admin Panel](../admin/overview). Crop is enabled by default. [More](#crop-and-focal-point-selector) |
|
||||
| **`disableLocalStorage`** | Completely disable uploading files to disk locally. [More](#disabling-local-upload-storage) |
|
||||
| **`displayPreview`** | Enable displaying preview of the uploaded file in Upload fields related to this Collection. Can be locally overridden by `displayPreview` option in Upload field. [More](/docs/fields/upload#config-options). |
|
||||
| **`externalFileHeaderFilter`** | Accepts existing headers and returns the headers after filtering or modifying. |
|
||||
| **`filesRequiredOnCreate`** | Mandate file data on creation, default is true. |
|
||||
| **`focalPoint`** | Set to `false` to disable the focal point selection tool in the [Admin Panel](../admin/overview). The focal point selector is only available when `imageSizes` or `resizeOptions` are defined. [More](#crop-and-focal-point-selector) |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -119,7 +119,7 @@
|
||||
"create-payload-app": "workspace:*",
|
||||
"cross-env": "7.0.3",
|
||||
"dotenv": "16.4.5",
|
||||
"drizzle-orm": "0.32.1",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"escape-html": "^1.0.3",
|
||||
"execa": "5.1.1",
|
||||
"form-data": "3.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-payload-app",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -29,22 +29,9 @@ const postgresReplacement: DbAdapterReplacement = {
|
||||
packageName: '@payloadcms/db-postgres',
|
||||
}
|
||||
|
||||
const sqliteReplacement: DbAdapterReplacement = {
|
||||
configReplacement: (envName = 'DATABASE_URI') => [
|
||||
' db: sqliteAdapter({',
|
||||
' client: {',
|
||||
` url: process.env.${envName} || '',`,
|
||||
' },',
|
||||
' }),',
|
||||
],
|
||||
importReplacement: "import { sqliteAdapter } from '@payloadcms/db-sqlite'",
|
||||
packageName: '@payloadcms/db-sqlite',
|
||||
}
|
||||
|
||||
export const dbReplacements: Record<DbType, DbAdapterReplacement> = {
|
||||
mongodb: mongodbReplacement,
|
||||
postgres: postgresReplacement,
|
||||
sqlite: sqliteReplacement,
|
||||
}
|
||||
|
||||
type StorageAdapterReplacement = {
|
||||
|
||||
@@ -5,7 +5,6 @@ import type { CliArgs, DbDetails, DbType } from '../types.js'
|
||||
|
||||
type DbChoice = {
|
||||
dbConnectionPrefix: `${string}/`
|
||||
dbConnectionSuffix?: string
|
||||
title: string
|
||||
value: DbType
|
||||
}
|
||||
@@ -21,12 +20,6 @@ const dbChoiceRecord: Record<DbType, DbChoice> = {
|
||||
title: 'PostgreSQL (beta)',
|
||||
value: 'postgres',
|
||||
},
|
||||
sqlite: {
|
||||
dbConnectionPrefix: 'file:./',
|
||||
dbConnectionSuffix: '.db',
|
||||
title: 'SQLite (beta)',
|
||||
value: 'sqlite',
|
||||
},
|
||||
}
|
||||
|
||||
export async function selectDb(args: CliArgs, projectName: string): Promise<DbDetails> {
|
||||
@@ -44,10 +37,10 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
|
||||
dbType = await p.select<{ label: string; value: DbType }[], DbType>({
|
||||
initialValue: 'mongodb',
|
||||
message: `Select a database`,
|
||||
options: Object.values(dbChoiceRecord).map((dbChoice) => ({
|
||||
label: dbChoice.title,
|
||||
value: dbChoice.value,
|
||||
})),
|
||||
options: [
|
||||
{ label: 'MongoDB', value: 'mongodb' },
|
||||
{ label: 'Postgres', value: 'postgres' },
|
||||
],
|
||||
})
|
||||
if (p.isCancel(dbType)) process.exit(0)
|
||||
}
|
||||
@@ -57,7 +50,7 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
|
||||
let dbUri: string | symbol | undefined = undefined
|
||||
const initialDbUri = `${dbChoice.dbConnectionPrefix}${
|
||||
projectName === '.' ? `payload-${getRandomDigitSuffix()}` : slugify(projectName)
|
||||
}${dbChoice.dbConnectionSuffix || ''}`
|
||||
}`
|
||||
|
||||
if (args['--db-accept-recommended']) {
|
||||
dbUri = initialDbUri
|
||||
|
||||
@@ -57,7 +57,7 @@ interface Template {
|
||||
|
||||
export type PackageManager = 'bun' | 'npm' | 'pnpm' | 'yarn'
|
||||
|
||||
export type DbType = 'mongodb' | 'postgres' | 'sqlite'
|
||||
export type DbType = 'mongodb' | 'postgres'
|
||||
|
||||
export type DbDetails = {
|
||||
dbUri: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -64,7 +64,7 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV
|
||||
forceCountFn: hasNearConstraint,
|
||||
lean: true,
|
||||
leanWithId: true,
|
||||
limit,
|
||||
offset: skip,
|
||||
options,
|
||||
page,
|
||||
pagination,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CollationOptions, TransactionOptions } from 'mongodb'
|
||||
import type { MongoMemoryReplSet } from 'mongodb-memory-server'
|
||||
import type { ClientSession, ConnectOptions, Connection } from 'mongoose'
|
||||
import type { ClientSession, ConnectOptions, Connection, SchemaOptions } from 'mongoose'
|
||||
import type { BaseDatabaseAdapter, DatabaseAdapterObj, Payload } from 'payload'
|
||||
|
||||
import fs from 'fs'
|
||||
@@ -66,6 +66,15 @@ export interface Args {
|
||||
* Defaults to disabled.
|
||||
*/
|
||||
collation?: Omit<CollationOptions, 'locale'>
|
||||
/** Define Mongoose options on a collection-by-collection basis.
|
||||
*/
|
||||
collections?: {
|
||||
[slug: string]: {
|
||||
/** Define Mongoose schema options for a given collection.
|
||||
*/
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
}
|
||||
/** Extra configuration options */
|
||||
connectOptions?: {
|
||||
/** Set false to disable $facet aggregation in non-supporting databases, Defaults to true */
|
||||
@@ -73,23 +82,40 @@ export interface Args {
|
||||
} & ConnectOptions
|
||||
/** Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false */
|
||||
disableIndexHints?: boolean
|
||||
/** Define Mongoose options for the globals collection.
|
||||
*/
|
||||
globals?: {
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
migrationDir?: string
|
||||
/**
|
||||
* typed as any to avoid dependency
|
||||
*/
|
||||
mongoMemoryServer?: MongoMemoryReplSet
|
||||
/** Define default Mongoose schema options for all schemas created.
|
||||
*/
|
||||
schemaOptions?: SchemaOptions
|
||||
transactionOptions?: TransactionOptions | false
|
||||
/** The URL to connect to MongoDB or false to start payload and prevent connecting */
|
||||
url: false | string
|
||||
}
|
||||
|
||||
export type MongooseAdapter = {
|
||||
collectionOptions: {
|
||||
[slug: string]: {
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
}
|
||||
collections: {
|
||||
[slug: string]: CollectionModel
|
||||
}
|
||||
connection: Connection
|
||||
globals: GlobalModel
|
||||
globalsOptions: {
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
mongoMemoryServer: MongoMemoryReplSet
|
||||
schemaOptions?: SchemaOptions
|
||||
sessions: Record<number | string, ClientSession>
|
||||
versions: {
|
||||
[slug: string]: CollectionModel
|
||||
@@ -100,13 +126,22 @@ export type MongooseAdapter = {
|
||||
declare module 'payload' {
|
||||
export interface DatabaseAdapter
|
||||
extends Omit<BaseDatabaseAdapter, 'sessions'>,
|
||||
Omit<Args, 'migrationDir'> {
|
||||
Omit<Args, 'collections' | 'globals' | 'migrationDir'> {
|
||||
collectionOptions: {
|
||||
[slug: string]: {
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
}
|
||||
collections: {
|
||||
[slug: string]: CollectionModel
|
||||
}
|
||||
connection: Connection
|
||||
globals: GlobalModel
|
||||
globalsOptions: {
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
mongoMemoryServer: MongoMemoryReplSet
|
||||
schemaOptions?: SchemaOptions
|
||||
sessions: Record<number | string, ClientSession>
|
||||
transactionOptions: TransactionOptions
|
||||
versions: {
|
||||
@@ -117,10 +152,13 @@ declare module 'payload' {
|
||||
|
||||
export function mongooseAdapter({
|
||||
autoPluralization = true,
|
||||
collections,
|
||||
connectOptions,
|
||||
disableIndexHints = false,
|
||||
globals,
|
||||
migrationDir: migrationDirArg,
|
||||
mongoMemoryServer,
|
||||
schemaOptions,
|
||||
transactionOptions = {},
|
||||
url,
|
||||
}: Args): DatabaseAdapterObj {
|
||||
@@ -133,17 +171,21 @@ export function mongooseAdapter({
|
||||
|
||||
// Mongoose-specific
|
||||
autoPluralization,
|
||||
collectionOptions: collections || {},
|
||||
collections: {},
|
||||
connectOptions: connectOptions || {},
|
||||
connection: undefined,
|
||||
count,
|
||||
disableIndexHints,
|
||||
globals: undefined,
|
||||
globalsOptions: globals || {},
|
||||
mongoMemoryServer,
|
||||
schemaOptions: schemaOptions || {},
|
||||
sessions: {},
|
||||
transactionOptions: transactionOptions === false ? undefined : transactionOptions,
|
||||
url,
|
||||
versions: {},
|
||||
|
||||
// DatabaseAdapter
|
||||
beginTransaction: transactionOptions ? beginTransaction : undefined,
|
||||
commitTransaction,
|
||||
|
||||
@@ -16,20 +16,22 @@ import { getDBName } from './utilities/getDBName.js'
|
||||
|
||||
export const init: Init = function init(this: MongooseAdapter) {
|
||||
this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => {
|
||||
const schema = buildCollectionSchema(collection, this.payload.config)
|
||||
const schema = buildCollectionSchema(collection, this)
|
||||
|
||||
if (collection.versions) {
|
||||
const versionModelName = getDBName({ config: collection, versions: true })
|
||||
|
||||
const versionCollectionFields = buildVersionCollectionFields(collection)
|
||||
|
||||
const versionSchema = buildSchema(this.payload.config, versionCollectionFields, {
|
||||
const versionSchema = buildSchema(this, versionCollectionFields, {
|
||||
disableUnique: true,
|
||||
draftsEnabled: true,
|
||||
indexSortableFields: this.payload.config.indexSortableFields,
|
||||
options: {
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
...this.schemaOptions,
|
||||
...(this.collectionOptions[collection.slug]?.schemaOptions || {}),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -57,7 +59,7 @@ export const init: Init = function init(this: MongooseAdapter) {
|
||||
this.collections[collection.slug] = model
|
||||
})
|
||||
|
||||
const model = buildGlobalModel(this.payload.config)
|
||||
const model = buildGlobalModel(this)
|
||||
this.globals = model
|
||||
|
||||
this.payload.config.globals.forEach((global) => {
|
||||
@@ -66,13 +68,15 @@ export const init: Init = function init(this: MongooseAdapter) {
|
||||
|
||||
const versionGlobalFields = buildVersionGlobalFields(global)
|
||||
|
||||
const versionSchema = buildSchema(this.payload.config, versionGlobalFields, {
|
||||
const versionSchema = buildSchema(this, versionGlobalFields, {
|
||||
disableUnique: true,
|
||||
draftsEnabled: true,
|
||||
indexSortableFields: this.payload.config.indexSortableFields,
|
||||
options: {
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
...this.schemaOptions,
|
||||
...(this.globalsOptions.schemaOptions || {}),
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
import type { PaginateOptions, Schema } from 'mongoose'
|
||||
import type { SanitizedCollectionConfig, SanitizedConfig } from 'payload'
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import paginate from 'mongoose-paginate-v2'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
import getBuildQueryPlugin from '../queries/buildQuery.js'
|
||||
import buildSchema from './buildSchema.js'
|
||||
|
||||
const buildCollectionSchema = (
|
||||
collection: SanitizedCollectionConfig,
|
||||
config: SanitizedConfig,
|
||||
schemaOptions = {},
|
||||
adapter: MongooseAdapter,
|
||||
): Schema => {
|
||||
const schema = buildSchema(config, collection.fields, {
|
||||
const schema = buildSchema(adapter, collection.fields, {
|
||||
draftsEnabled: Boolean(typeof collection?.versions === 'object' && collection.versions.drafts),
|
||||
indexSortableFields: config.indexSortableFields,
|
||||
indexSortableFields: adapter.payload.config.indexSortableFields,
|
||||
options: {
|
||||
minimize: false,
|
||||
timestamps: collection.timestamps !== false,
|
||||
...schemaOptions,
|
||||
...adapter.schemaOptions,
|
||||
...(adapter.collectionOptions[collection.slug]?.schemaOptions || {}),
|
||||
},
|
||||
})
|
||||
|
||||
if (config.indexSortableFields && collection.timestamps !== false) {
|
||||
if (adapter.payload.config.indexSortableFields && collection.timestamps !== false) {
|
||||
schema.index({ updatedAt: 1 })
|
||||
schema.index({ createdAt: 1 })
|
||||
}
|
||||
|
||||
@@ -1,27 +1,34 @@
|
||||
import type { SanitizedConfig } from 'payload'
|
||||
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
import type { GlobalModel } from '../types.js'
|
||||
|
||||
import getBuildQueryPlugin from '../queries/buildQuery.js'
|
||||
import buildSchema from './buildSchema.js'
|
||||
|
||||
export const buildGlobalModel = (config: SanitizedConfig): GlobalModel | null => {
|
||||
if (config.globals && config.globals.length > 0) {
|
||||
export const buildGlobalModel = (adapter: MongooseAdapter): GlobalModel | null => {
|
||||
if (adapter.payload.config.globals && adapter.payload.config.globals.length > 0) {
|
||||
const globalsSchema = new mongoose.Schema(
|
||||
{},
|
||||
{ discriminatorKey: 'globalType', minimize: false, timestamps: true },
|
||||
{
|
||||
discriminatorKey: 'globalType',
|
||||
minimize: false,
|
||||
...adapter.schemaOptions,
|
||||
...(adapter.globalsOptions.schemaOptions || {}),
|
||||
timestamps: true,
|
||||
},
|
||||
)
|
||||
|
||||
globalsSchema.plugin(getBuildQueryPlugin())
|
||||
|
||||
const Globals = mongoose.model('globals', globalsSchema, 'globals') as unknown as GlobalModel
|
||||
|
||||
Object.values(config.globals).forEach((globalConfig) => {
|
||||
const globalSchema = buildSchema(config, globalConfig.fields, {
|
||||
Object.values(adapter.payload.config.globals).forEach((globalConfig) => {
|
||||
const globalSchema = buildSchema(adapter, globalConfig.fields, {
|
||||
options: {
|
||||
minimize: false,
|
||||
...adapter.schemaOptions,
|
||||
...(adapter.globalsOptions.schemaOptions || {}),
|
||||
},
|
||||
})
|
||||
Globals.discriminator(globalConfig.slug, globalSchema)
|
||||
|
||||
@@ -19,7 +19,6 @@ import type {
|
||||
RelationshipField,
|
||||
RichTextField,
|
||||
RowField,
|
||||
SanitizedConfig,
|
||||
SanitizedLocalizationConfig,
|
||||
SelectField,
|
||||
Tab,
|
||||
@@ -37,6 +36,8 @@ import {
|
||||
tabHasName,
|
||||
} from 'payload/shared'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
export type BuildSchemaOptions = {
|
||||
allowIDField?: boolean
|
||||
disableUnique?: boolean
|
||||
@@ -48,23 +49,13 @@ export type BuildSchemaOptions = {
|
||||
type FieldSchemaGenerator = (
|
||||
field: Field,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
) => void
|
||||
|
||||
/**
|
||||
* get a field's defaultValue only if defined and not dynamic so that it can be set on the field schema
|
||||
* @param field
|
||||
*/
|
||||
const formatDefaultValue = (field: FieldAffectingData) =>
|
||||
typeof field.defaultValue !== 'undefined' && typeof field.defaultValue !== 'function'
|
||||
? field.defaultValue
|
||||
: undefined
|
||||
|
||||
const formatBaseSchema = (field: FieldAffectingData, buildSchemaOptions: BuildSchemaOptions) => {
|
||||
const { disableUnique, draftsEnabled, indexSortableFields } = buildSchemaOptions
|
||||
const schema: SchemaTypeOptions<unknown> = {
|
||||
default: formatDefaultValue(field),
|
||||
index: field.index || (!disableUnique && field.unique) || indexSortableFields || false,
|
||||
required: false,
|
||||
unique: (!disableUnique && field.unique) || false,
|
||||
@@ -97,10 +88,10 @@ const localizeSchema = (
|
||||
if (fieldIsLocalized(entity) && localization && Array.isArray(localization.locales)) {
|
||||
return {
|
||||
type: localization.localeCodes.reduce(
|
||||
(localeSchema, locale) => ({
|
||||
...localeSchema,
|
||||
[locale]: schema,
|
||||
}),
|
||||
(localeSchema, locale) => {
|
||||
localeSchema[locale] = schema
|
||||
return localeSchema
|
||||
},
|
||||
{
|
||||
_id: false,
|
||||
},
|
||||
@@ -112,7 +103,7 @@ const localizeSchema = (
|
||||
}
|
||||
|
||||
const buildSchema = (
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
configFields: Field[],
|
||||
buildSchemaOptions: BuildSchemaOptions = {},
|
||||
): Schema => {
|
||||
@@ -140,7 +131,7 @@ const buildSchema = (
|
||||
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[field.type]
|
||||
|
||||
if (addFieldSchema) {
|
||||
addFieldSchema(field, schema, config, buildSchemaOptions)
|
||||
addFieldSchema(field, schema, adapter, buildSchemaOptions)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -152,55 +143,77 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
array: (
|
||||
field: ArrayField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
) => {
|
||||
const baseSchema = {
|
||||
...formatBaseSchema(field, buildSchemaOptions),
|
||||
type: [
|
||||
buildSchema(config, field.fields, {
|
||||
buildSchema(adapter, field.fields, {
|
||||
allowIDField: true,
|
||||
disableUnique: buildSchemaOptions.disableUnique,
|
||||
draftsEnabled: buildSchemaOptions.draftsEnabled,
|
||||
options: {
|
||||
minimize: false,
|
||||
...(buildSchemaOptions.options || {}),
|
||||
_id: false,
|
||||
id: false,
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
},
|
||||
}),
|
||||
],
|
||||
default: undefined,
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
blocks: (
|
||||
field: BlockField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const fieldSchema = {
|
||||
type: [new mongoose.Schema({}, { _id: false, discriminatorKey: 'blockType' })],
|
||||
type: [
|
||||
new mongoose.Schema(
|
||||
{},
|
||||
{
|
||||
_id: false,
|
||||
discriminatorKey: 'blockType',
|
||||
...(buildSchemaOptions.options || {}),
|
||||
timestamps: false,
|
||||
},
|
||||
),
|
||||
],
|
||||
default: undefined,
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, fieldSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, fieldSchema, adapter.payload.config.localization),
|
||||
})
|
||||
|
||||
field.blocks.forEach((blockItem: Block) => {
|
||||
const blockSchema = new mongoose.Schema({}, { _id: false, id: false })
|
||||
const blockSchema = new mongoose.Schema(
|
||||
{},
|
||||
{
|
||||
...(buildSchemaOptions.options || {}),
|
||||
_id: false,
|
||||
id: false,
|
||||
timestamps: false,
|
||||
},
|
||||
)
|
||||
|
||||
blockItem.fields.forEach((blockField) => {
|
||||
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[blockField.type]
|
||||
if (addFieldSchema) {
|
||||
addFieldSchema(blockField, blockSchema, config, buildSchemaOptions)
|
||||
addFieldSchema(blockField, blockSchema, adapter, buildSchemaOptions)
|
||||
}
|
||||
})
|
||||
|
||||
if (field.localized && config.localization) {
|
||||
config.localization.localeCodes.forEach((localeCode) => {
|
||||
if (field.localized && adapter.payload.config.localization) {
|
||||
adapter.payload.config.localization.localeCodes.forEach((localeCode) => {
|
||||
// @ts-expect-error Possible incorrect typing in mongoose types, this works
|
||||
schema.path(`${field.name}.${localeCode}`).discriminator(blockItem.slug, blockSchema)
|
||||
})
|
||||
@@ -213,69 +226,69 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
checkbox: (
|
||||
field: CheckboxField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: Boolean }
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
code: (
|
||||
field: CodeField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String }
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
collapsible: (
|
||||
field: CollapsibleField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
field.fields.forEach((subField: Field) => {
|
||||
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[subField.type]
|
||||
|
||||
if (addFieldSchema) {
|
||||
addFieldSchema(subField, schema, config, buildSchemaOptions)
|
||||
addFieldSchema(subField, schema, adapter, buildSchemaOptions)
|
||||
}
|
||||
})
|
||||
},
|
||||
date: (
|
||||
field: DateField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: Date }
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
email: (
|
||||
field: EmailField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String }
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
group: (
|
||||
field: GroupField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const formattedBaseSchema = formatBaseSchema(field, buildSchemaOptions)
|
||||
@@ -288,26 +301,28 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
|
||||
const baseSchema = {
|
||||
...formattedBaseSchema,
|
||||
type: buildSchema(config, field.fields, {
|
||||
type: buildSchema(adapter, field.fields, {
|
||||
disableUnique: buildSchemaOptions.disableUnique,
|
||||
draftsEnabled: buildSchemaOptions.draftsEnabled,
|
||||
indexSortableFields,
|
||||
options: {
|
||||
minimize: false,
|
||||
...(buildSchemaOptions.options || {}),
|
||||
_id: false,
|
||||
id: false,
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
json: (
|
||||
field: JSONField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -316,13 +331,13 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
number: (
|
||||
field: NumberField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -331,13 +346,13 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
point: (
|
||||
field: PointField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema: SchemaTypeOptions<unknown> = {
|
||||
@@ -347,7 +362,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
},
|
||||
coordinates: {
|
||||
type: [Number],
|
||||
default: formatDefaultValue(field),
|
||||
default: field.defaultValue || undefined,
|
||||
required: false,
|
||||
},
|
||||
}
|
||||
@@ -356,7 +371,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
|
||||
if (field.index === true || field.index === undefined) {
|
||||
@@ -365,8 +380,8 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
indexOptions.sparse = true
|
||||
indexOptions.unique = true
|
||||
}
|
||||
if (field.localized && config.localization) {
|
||||
config.localization.locales.forEach((locale) => {
|
||||
if (field.localized && adapter.payload.config.localization) {
|
||||
adapter.payload.config.localization.locales.forEach((locale) => {
|
||||
schema.index({ [`${field.name}.${locale.code}`]: '2dsphere' }, indexOptions)
|
||||
})
|
||||
} else {
|
||||
@@ -377,7 +392,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
radio: (
|
||||
field: RadioField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -390,21 +405,21 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
relationship: (
|
||||
field: RelationshipField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
) => {
|
||||
const hasManyRelations = Array.isArray(field.relationTo)
|
||||
let schemaToReturn: { [key: string]: any } = {}
|
||||
|
||||
if (field.localized && config.localization) {
|
||||
if (field.localized && adapter.payload.config.localization) {
|
||||
schemaToReturn = {
|
||||
type: config.localization.localeCodes.reduce((locales, locale) => {
|
||||
type: adapter.payload.config.localization.localeCodes.reduce((locales, locale) => {
|
||||
let localeSchema: { [key: string]: any } = {}
|
||||
|
||||
if (hasManyRelations) {
|
||||
@@ -428,9 +443,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
|
||||
return {
|
||||
...locales,
|
||||
[locale]: field.hasMany
|
||||
? { type: [localeSchema], default: formatDefaultValue(field) }
|
||||
: localeSchema,
|
||||
[locale]: field.hasMany ? { type: [localeSchema], default: undefined } : localeSchema,
|
||||
}
|
||||
}, {}),
|
||||
localized: true,
|
||||
@@ -450,7 +463,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
if (field.hasMany) {
|
||||
schemaToReturn = {
|
||||
type: [schemaToReturn],
|
||||
default: formatDefaultValue(field),
|
||||
default: undefined,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -463,7 +476,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
if (field.hasMany) {
|
||||
schemaToReturn = {
|
||||
type: [schemaToReturn],
|
||||
default: formatDefaultValue(field),
|
||||
default: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -475,7 +488,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
richText: (
|
||||
field: RichTextField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -484,27 +497,27 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
row: (
|
||||
field: RowField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
field.fields.forEach((subField: Field) => {
|
||||
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[subField.type]
|
||||
|
||||
if (addFieldSchema) {
|
||||
addFieldSchema(subField, schema, config, buildSchemaOptions)
|
||||
addFieldSchema(subField, schema, adapter, buildSchemaOptions)
|
||||
}
|
||||
})
|
||||
},
|
||||
select: (
|
||||
field: SelectField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -524,39 +537,41 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
[field.name]: localizeSchema(
|
||||
field,
|
||||
field.hasMany ? [baseSchema] : baseSchema,
|
||||
config.localization,
|
||||
adapter.payload.config.localization,
|
||||
),
|
||||
})
|
||||
},
|
||||
tabs: (
|
||||
field: TabsField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
field.tabs.forEach((tab) => {
|
||||
if (tabHasName(tab)) {
|
||||
const baseSchema = {
|
||||
type: buildSchema(config, tab.fields, {
|
||||
type: buildSchema(adapter, tab.fields, {
|
||||
disableUnique: buildSchemaOptions.disableUnique,
|
||||
draftsEnabled: buildSchemaOptions.draftsEnabled,
|
||||
options: {
|
||||
minimize: false,
|
||||
...(buildSchemaOptions.options || {}),
|
||||
_id: false,
|
||||
id: false,
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[tab.name]: localizeSchema(tab, baseSchema, config.localization),
|
||||
[tab.name]: localizeSchema(tab, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
} else {
|
||||
tab.fields.forEach((subField: Field) => {
|
||||
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[subField.type]
|
||||
|
||||
if (addFieldSchema) {
|
||||
addFieldSchema(subField, schema, config, buildSchemaOptions)
|
||||
addFieldSchema(subField, schema, adapter, buildSchemaOptions)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -565,7 +580,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
text: (
|
||||
field: TextField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -574,25 +589,25 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
textarea: (
|
||||
field: TextareaField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String }
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
upload: (
|
||||
field: UploadField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -602,7 +617,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -47,8 +47,8 @@
|
||||
"dependencies": {
|
||||
"@payloadcms/drizzle": "workspace:*",
|
||||
"console-table-printer": "2.11.2",
|
||||
"drizzle-kit": "0.23.1-7816536",
|
||||
"drizzle-orm": "0.32.1",
|
||||
"drizzle-kit": "0.20.14-1f2c838",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"pg": "8.11.3",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
|
||||
@@ -53,7 +53,6 @@ export const connect: Connect = async function connect(
|
||||
const { hotReload } = options
|
||||
|
||||
this.schema = {
|
||||
pgSchema: this.pgSchema,
|
||||
...this.tables,
|
||||
...this.relations,
|
||||
...this.enums,
|
||||
|
||||
@@ -49,12 +49,6 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
|
||||
let drizzleJsonBefore = defaultDrizzleSnapshot
|
||||
|
||||
if (this.schemaName) {
|
||||
drizzleJsonBefore.schemas = {
|
||||
[this.schemaName]: this.schemaName,
|
||||
}
|
||||
}
|
||||
|
||||
if (!upSQL) {
|
||||
// Get latest migration snapshot
|
||||
const latestSnapshot = fs
|
||||
|
||||
@@ -7,11 +7,10 @@ export const defaultDrizzleSnapshot: DrizzleSnapshotJSON = {
|
||||
schemas: {},
|
||||
tables: {},
|
||||
},
|
||||
dialect: 'postgresql',
|
||||
dialect: 'pg',
|
||||
enums: {},
|
||||
prevId: '00000000-0000-0000-0000-00000000000',
|
||||
schemas: {},
|
||||
sequences: {},
|
||||
tables: {},
|
||||
version: '7',
|
||||
version: '5',
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import {
|
||||
updateOne,
|
||||
updateVersion,
|
||||
} from '@payloadcms/drizzle'
|
||||
import { type PgSchema, pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
|
||||
import { createDatabaseAdapter } from 'payload'
|
||||
|
||||
import type { Args, PostgresAdapter } from './types.js'
|
||||
@@ -63,19 +62,12 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
const migrationDir = findMigrationDir(args.migrationDir)
|
||||
let resolveInitializing
|
||||
let rejectInitializing
|
||||
let adapterSchema: PostgresAdapter['pgSchema']
|
||||
|
||||
const initializing = new Promise<void>((res, rej) => {
|
||||
resolveInitializing = res
|
||||
rejectInitializing = rej
|
||||
})
|
||||
|
||||
if (args.schemaName) {
|
||||
adapterSchema = pgSchema(args.schemaName)
|
||||
} else {
|
||||
adapterSchema = { enum: pgEnum, table: pgTable }
|
||||
}
|
||||
|
||||
return createDatabaseAdapter<PostgresAdapter>({
|
||||
name: 'postgres',
|
||||
defaultDrizzleSnapshot,
|
||||
@@ -91,7 +83,7 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
localesSuffix: args.localesSuffix || '_locales',
|
||||
logger: args.logger,
|
||||
operators: operatorMap,
|
||||
pgSchema: adapterSchema,
|
||||
pgSchema: undefined,
|
||||
pool: undefined,
|
||||
poolOptions: args.pool,
|
||||
push: args.push,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Init, SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import { createTableName } from '@payloadcms/drizzle'
|
||||
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
|
||||
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
@@ -9,8 +10,13 @@ import type { PostgresAdapter } from './types.js'
|
||||
import { buildTable } from './schema/build.js'
|
||||
|
||||
export const init: Init = function init(this: PostgresAdapter) {
|
||||
if (this.schemaName) {
|
||||
this.pgSchema = pgSchema(this.schemaName)
|
||||
} else {
|
||||
this.pgSchema = { table: pgTable }
|
||||
}
|
||||
if (this.payload.config.localization) {
|
||||
this.enums.enum__locales = this.pgSchema.enum(
|
||||
this.enums.enum__locales = pgEnum(
|
||||
'_locales',
|
||||
this.payload.config.localization.locales.map(({ code }) => code) as [string, ...string[]],
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { Relation } from 'drizzle-orm'
|
||||
import type {
|
||||
ForeignKeyBuilder,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { index, uniqueIndex } from 'drizzle-orm/pg-core'
|
||||
|
||||
import type { GenericColumn } from '../types.js'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { Relation } from 'drizzle-orm'
|
||||
import type { IndexBuilder, PgColumnBuilder } from 'drizzle-orm/pg-core'
|
||||
import type { Field, TabAsField } from 'payload'
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
integer,
|
||||
jsonb,
|
||||
numeric,
|
||||
pgEnum,
|
||||
text,
|
||||
timestamp,
|
||||
varchar,
|
||||
@@ -33,7 +35,6 @@ import { buildTable } from './build.js'
|
||||
import { createIndex } from './createIndex.js'
|
||||
import { idToUUID } from './idToUUID.js'
|
||||
import { parentIDColumnMap } from './parentIDColumnMap.js'
|
||||
import { withDefault } from './withDefault.js'
|
||||
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
@@ -169,14 +170,14 @@ export const traverseFields = ({
|
||||
)
|
||||
}
|
||||
} else {
|
||||
targetTable[fieldName] = withDefault(varchar(columnName), field)
|
||||
targetTable[fieldName] = varchar(columnName)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'email':
|
||||
case 'code':
|
||||
case 'textarea': {
|
||||
targetTable[fieldName] = withDefault(varchar(columnName), field)
|
||||
targetTable[fieldName] = varchar(columnName)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -198,26 +199,23 @@ export const traverseFields = ({
|
||||
)
|
||||
}
|
||||
} else {
|
||||
targetTable[fieldName] = withDefault(numeric(columnName), field)
|
||||
targetTable[fieldName] = numeric(columnName)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'richText':
|
||||
case 'json': {
|
||||
targetTable[fieldName] = withDefault(jsonb(columnName), field)
|
||||
targetTable[fieldName] = jsonb(columnName)
|
||||
break
|
||||
}
|
||||
|
||||
case 'date': {
|
||||
targetTable[fieldName] = withDefault(
|
||||
timestamp(columnName, {
|
||||
mode: 'string',
|
||||
precision: 3,
|
||||
withTimezone: true,
|
||||
}),
|
||||
field,
|
||||
)
|
||||
targetTable[fieldName] = timestamp(columnName, {
|
||||
mode: 'string',
|
||||
precision: 3,
|
||||
withTimezone: true,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
@@ -236,7 +234,7 @@ export const traverseFields = ({
|
||||
throwValidationError,
|
||||
})
|
||||
|
||||
adapter.enums[enumName] = adapter.pgSchema.enum(
|
||||
adapter.enums[enumName] = pgEnum(
|
||||
enumName,
|
||||
field.options.map((option) => {
|
||||
if (optionIsObject(option)) {
|
||||
@@ -313,13 +311,13 @@ export const traverseFields = ({
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
targetTable[fieldName] = withDefault(adapter.enums[enumName](fieldName), field)
|
||||
targetTable[fieldName] = adapter.enums[enumName](fieldName)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'checkbox': {
|
||||
targetTable[fieldName] = withDefault(boolean(columnName), field)
|
||||
targetTable[fieldName] = boolean(columnName)
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import type { PgColumnBuilder } from 'drizzle-orm/pg-core'
|
||||
import type { FieldAffectingData } from 'payload'
|
||||
|
||||
export const withDefault = (
|
||||
column: PgColumnBuilder,
|
||||
field: FieldAffectingData,
|
||||
): PgColumnBuilder => {
|
||||
if (typeof field.defaultValue === 'undefined' || typeof field.defaultValue === 'function')
|
||||
return column
|
||||
|
||||
if (typeof field.defaultValue === 'string' && field.defaultValue.includes("'")) {
|
||||
const escapedString = field.defaultValue.replaceAll("'", "''")
|
||||
return column.default(escapedString)
|
||||
}
|
||||
|
||||
return column.default(field.defaultValue)
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import type {
|
||||
PgSchema,
|
||||
PgTableWithColumns,
|
||||
PgTransactionConfig,
|
||||
pgEnum,
|
||||
} from 'drizzle-orm/pg-core'
|
||||
import type { PgTableFn } from 'drizzle-orm/pg-core/table'
|
||||
import type { Payload, PayloadRequest } from 'payload'
|
||||
@@ -107,13 +106,6 @@ type PostgresDrizzleAdapter = Omit<
|
||||
| 'relations'
|
||||
>
|
||||
|
||||
type Schema =
|
||||
| {
|
||||
enum: typeof pgEnum
|
||||
table: PgTableFn
|
||||
}
|
||||
| PgSchema
|
||||
|
||||
export type PostgresAdapter = {
|
||||
countDistinct: CountDistinct
|
||||
defaultDrizzleSnapshot: DrizzleSnapshotJSON
|
||||
@@ -133,7 +125,7 @@ export type PostgresAdapter = {
|
||||
localesSuffix?: string
|
||||
logger: DrizzleConfig['logger']
|
||||
operators: Operators
|
||||
pgSchema?: Schema
|
||||
pgSchema?: { table: PgTableFn } | PgSchema
|
||||
pool: Pool
|
||||
poolOptions: Args['pool']
|
||||
push: boolean
|
||||
@@ -141,6 +133,7 @@ export type PostgresAdapter = {
|
||||
relations: Record<string, GenericRelation>
|
||||
relationshipsSuffix?: string
|
||||
resolveInitializing: () => void
|
||||
schema: Record<string, GenericEnum | GenericRelation | GenericTable>
|
||||
schemaName?: Args['schemaName']
|
||||
sessions: {
|
||||
[id: string]: {
|
||||
@@ -182,7 +175,7 @@ declare module 'payload' {
|
||||
rejectInitializing: () => void
|
||||
relationshipsSuffix?: string
|
||||
resolveInitializing: () => void
|
||||
schema: Record<string, unknown>
|
||||
schema: Record<string, GenericEnum | GenericRelation | GenericTable>
|
||||
schemaName?: Args['schemaName']
|
||||
tableNameMap: Map<string, string>
|
||||
versionsSuffix?: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-sqlite",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "The officially supported SQLite database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -46,8 +46,8 @@
|
||||
"@libsql/client": "^0.6.2",
|
||||
"@payloadcms/drizzle": "workspace:*",
|
||||
"console-table-printer": "2.11.2",
|
||||
"drizzle-kit": "0.23.1-7816536",
|
||||
"drizzle-orm": "0.32.1",
|
||||
"drizzle-kit": "0.20.14-1f2c838",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "9.0.0"
|
||||
|
||||
@@ -10,5 +10,5 @@ export const defaultDrizzleSnapshot: DrizzleSQLiteSnapshotJSON = {
|
||||
enums: {},
|
||||
prevId: '00000000-0000-0000-0000-00000000000',
|
||||
tables: {},
|
||||
version: '6',
|
||||
version: '5',
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
|
||||
versionsSuffix: args.versionsSuffix || '_v',
|
||||
|
||||
// DatabaseAdapter
|
||||
beginTransaction: args.transactionOptions ? beginTransaction : undefined,
|
||||
beginTransaction: args.transactionOptions === false ? undefined : beginTransaction,
|
||||
commitTransaction,
|
||||
connect,
|
||||
convertPathToJSONTraversal,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { Relation } from 'drizzle-orm'
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { ColumnDataType, Relation } from 'drizzle-orm'
|
||||
import type {
|
||||
AnySQLiteColumn,
|
||||
ForeignKeyBuilder,
|
||||
IndexBuilder,
|
||||
SQLiteColumn,
|
||||
SQLiteColumnBuilder,
|
||||
SQLiteTableWithColumns,
|
||||
UniqueConstraintBuilder,
|
||||
@@ -31,7 +32,18 @@ import { traverseFields } from './traverseFields.js'
|
||||
export type BaseExtraConfig = Record<
|
||||
string,
|
||||
(cols: {
|
||||
[x: string]: AnySQLiteColumn
|
||||
[x: string]: SQLiteColumn<{
|
||||
baseColumn: never
|
||||
columnType: string
|
||||
data: unknown
|
||||
dataType: ColumnDataType
|
||||
driverParam: unknown
|
||||
enumValues: string[]
|
||||
hasDefault: false
|
||||
name: string
|
||||
notNull: false
|
||||
tableName: string
|
||||
}>
|
||||
}) => ForeignKeyBuilder | IndexBuilder | UniqueConstraintBuilder
|
||||
>
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { AnySQLiteColumn} from 'drizzle-orm/sqlite-core';
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { index, uniqueIndex } from 'drizzle-orm/sqlite-core'
|
||||
|
||||
import type { GenericColumn } from '../types.js'
|
||||
|
||||
type CreateIndexArgs = {
|
||||
columnName: string
|
||||
name: string | string[]
|
||||
@@ -10,7 +11,7 @@ type CreateIndexArgs = {
|
||||
}
|
||||
|
||||
export const createIndex = ({ name, columnName, tableName, unique }: CreateIndexArgs) => {
|
||||
return (table: { [x: string]: AnySQLiteColumn }) => {
|
||||
return (table: { [x: string]: GenericColumn }) => {
|
||||
let columns
|
||||
if (Array.isArray(name)) {
|
||||
columns = name
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { Relation } from 'drizzle-orm'
|
||||
import type { IndexBuilder, SQLiteColumnBuilder } from 'drizzle-orm/sqlite-core'
|
||||
import type { Field, TabAsField } from 'payload'
|
||||
@@ -29,7 +30,6 @@ import { buildTable } from './build.js'
|
||||
import { createIndex } from './createIndex.js'
|
||||
import { getIDColumn } from './getIDColumn.js'
|
||||
import { idToUUID } from './idToUUID.js'
|
||||
import { withDefault } from './withDefault.js'
|
||||
|
||||
type Args = {
|
||||
adapter: SQLiteAdapter
|
||||
@@ -166,14 +166,14 @@ export const traverseFields = ({
|
||||
)
|
||||
}
|
||||
} else {
|
||||
targetTable[fieldName] = withDefault(text(columnName), field)
|
||||
targetTable[fieldName] = text(columnName)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'email':
|
||||
case 'code':
|
||||
case 'textarea': {
|
||||
targetTable[fieldName] = withDefault(text(columnName), field)
|
||||
targetTable[fieldName] = text(columnName)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -195,19 +195,19 @@ export const traverseFields = ({
|
||||
)
|
||||
}
|
||||
} else {
|
||||
targetTable[fieldName] = withDefault(numeric(columnName), field)
|
||||
targetTable[fieldName] = numeric(columnName)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'richText':
|
||||
case 'json': {
|
||||
targetTable[fieldName] = withDefault(text(columnName, { mode: 'json' }), field)
|
||||
targetTable[fieldName] = text(columnName, { mode: 'json' })
|
||||
break
|
||||
}
|
||||
|
||||
case 'date': {
|
||||
targetTable[fieldName] = withDefault(text(columnName), field)
|
||||
targetTable[fieldName] = text(columnName)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -295,13 +295,13 @@ export const traverseFields = ({
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
targetTable[fieldName] = withDefault(text(fieldName, { enum: options }), field)
|
||||
targetTable[fieldName] = text(fieldName, { enum: options })
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'checkbox': {
|
||||
targetTable[fieldName] = withDefault(integer(columnName, { mode: 'boolean' }), field)
|
||||
targetTable[fieldName] = integer(columnName, { mode: 'boolean' })
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import type { SQLiteColumnBuilder } from 'drizzle-orm/sqlite-core'
|
||||
import type { FieldAffectingData } from 'payload'
|
||||
|
||||
export const withDefault = (
|
||||
column: SQLiteColumnBuilder,
|
||||
field: FieldAffectingData,
|
||||
): SQLiteColumnBuilder => {
|
||||
if (typeof field.defaultValue === 'undefined' || typeof field.defaultValue === 'function')
|
||||
return column
|
||||
|
||||
if (typeof field.defaultValue === 'string' && field.defaultValue.includes("'")) {
|
||||
const escapedString = field.defaultValue.replaceAll("'", "''")
|
||||
return column.default(escapedString)
|
||||
}
|
||||
|
||||
return column.default(field.defaultValue)
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Client, Config, ResultSet } from '@libsql/client'
|
||||
import type { Operators } from '@payloadcms/drizzle'
|
||||
import type { BuildQueryJoinAliases, DrizzleAdapter } from '@payloadcms/drizzle/types'
|
||||
import type { DrizzleConfig, Relation, Relations, SQL } from 'drizzle-orm'
|
||||
import type { ColumnDataType, DrizzleConfig, Relation, Relations, SQL } from 'drizzle-orm'
|
||||
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
|
||||
import type {
|
||||
AnySQLiteColumn,
|
||||
SQLiteColumn,
|
||||
SQLiteInsertOnConflictDoUpdateConfig,
|
||||
SQLiteTableWithColumns,
|
||||
SQLiteTransactionConfig,
|
||||
@@ -25,8 +25,24 @@ export type Args = {
|
||||
versionsSuffix?: string
|
||||
}
|
||||
|
||||
export type GenericColumn = SQLiteColumn<
|
||||
{
|
||||
baseColumn: never
|
||||
columnType: string
|
||||
data: unknown
|
||||
dataType: ColumnDataType
|
||||
driverParam: unknown
|
||||
enumValues: string[]
|
||||
hasDefault: false
|
||||
name: string
|
||||
notNull: false
|
||||
tableName: string
|
||||
},
|
||||
object
|
||||
>
|
||||
|
||||
export type GenericColumns = {
|
||||
[x: string]: AnySQLiteColumn
|
||||
[x: string]: GenericColumn
|
||||
}
|
||||
|
||||
export type GenericTable = SQLiteTableWithColumns<{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/drizzle",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "A library of shared functions used by different payload database adapters",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -39,7 +39,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"console-table-printer": "2.11.2",
|
||||
"drizzle-orm": "0.32.1",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "9.0.0"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Count , SanitizedCollectionConfig } from 'payload'
|
||||
import type { Count } from 'payload'
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
@@ -14,7 +15,7 @@ export const count: Count = async function count(
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))
|
||||
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
|
||||
const { joins, where } = await buildQuery({
|
||||
adapter: this,
|
||||
|
||||
@@ -10,7 +10,7 @@ export const create: Create = async function create(
|
||||
this: DrizzleAdapter,
|
||||
{ collection: collectionSlug, data, req },
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collection.slug))
|
||||
|
||||
@@ -10,7 +10,7 @@ export async function createGlobal<T extends Record<string, unknown>>(
|
||||
this: DrizzleAdapter,
|
||||
{ slug, data, req = {} as PayloadRequest }: CreateGlobalArgs,
|
||||
): Promise<T> {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(globalConfig.slug))
|
||||
|
||||
@@ -12,7 +12,7 @@ export async function createGlobalVersion<T extends TypeWithID>(
|
||||
this: DrizzleAdapter,
|
||||
{ autosave, globalSlug, req = {} as PayloadRequest, versionData }: CreateGlobalVersionArgs,
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const global = this.payload.globals.config.find(({ slug }) => slug === globalSlug)
|
||||
|
||||
const tableName = this.tableNameMap.get(`_${toSnakeCase(global.slug)}${this.versionsSuffix}`)
|
||||
|
||||
@@ -18,7 +18,7 @@ export async function createVersion<T extends TypeWithID>(
|
||||
versionData,
|
||||
}: CreateVersionArgs<T>,
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
const defaultTableName = toSnakeCase(collection.slug)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ export const deleteMany: DeleteMany = async function deleteMany(
|
||||
this: DrizzleAdapter,
|
||||
{ collection, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))
|
||||
|
||||
@@ -14,7 +14,7 @@ export const deleteOne: DeleteOne = async function deleteOne(
|
||||
this: DrizzleAdapter,
|
||||
{ collection: collectionSlug, req = {} as PayloadRequest, where: whereArg },
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collection.slug))
|
||||
|
||||
@@ -12,7 +12,7 @@ export const deleteVersions: DeleteVersions = async function deleteVersion(
|
||||
this: DrizzleAdapter,
|
||||
{ collection, locale, req = {} as PayloadRequest, where: where },
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
|
||||
const tableName = this.tableNameMap.get(
|
||||
|
||||
@@ -120,7 +120,6 @@ export type RequireDrizzleKit = () => {
|
||||
pushSchema: (
|
||||
schema: Record<string, unknown>,
|
||||
drizzle: DrizzleAdapter['drizzle'],
|
||||
filterSchema?: string[],
|
||||
) => Promise<{ apply; hasDataLoss; warnings }>
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
this: DrizzleAdapter,
|
||||
{ id, collection: collectionSlug, data, draft, locale, req, where: whereArg },
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collection.slug))
|
||||
const whereToUse = whereArg || { id: { equals: id } }
|
||||
|
||||
@@ -10,7 +10,7 @@ export async function updateGlobal<T extends Record<string, unknown>>(
|
||||
this: DrizzleAdapter,
|
||||
{ slug, data, req = {} as PayloadRequest }: UpdateGlobalArgs,
|
||||
): Promise<T> {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(globalConfig.slug))
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function updateGlobalVersion<T extends TypeWithID>(
|
||||
where: whereArg,
|
||||
}: UpdateGlobalVersionArgs<T>,
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const globalConfig: SanitizedGlobalConfig = this.payload.globals.config.find(
|
||||
({ slug }) => slug === global,
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function updateVersion<T extends TypeWithID>(
|
||||
where: whereArg,
|
||||
}: UpdateVersionArgs<T>,
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
const whereToUse = whereArg || { id: { equals: id } }
|
||||
const tableName = this.tableNameMap.get(
|
||||
|
||||
@@ -4,10 +4,8 @@ import { fieldAffectsData, fieldHasSubFields } from 'payload/shared'
|
||||
|
||||
export const hasLocalesTable = (fields: Field[]): boolean => {
|
||||
return fields.some((field) => {
|
||||
// arrays always get a separate table
|
||||
if (field.type === 'array') return false
|
||||
if (fieldAffectsData(field) && field.localized) return true
|
||||
if (fieldHasSubFields(field)) return hasLocalesTable(field.fields)
|
||||
if (fieldHasSubFields(field) && field.type !== 'array') return hasLocalesTable(field.fields)
|
||||
if (field.type === 'tabs') return field.tabs.some((tab) => hasLocalesTable(tab.fields))
|
||||
return false
|
||||
})
|
||||
|
||||
@@ -12,11 +12,7 @@ export const pushDevSchema = async (adapter: DrizzleAdapter) => {
|
||||
const { pushSchema } = adapter.requireDrizzleKit()
|
||||
|
||||
// This will prompt if clarifications are needed for Drizzle to push new schema
|
||||
const { apply, hasDataLoss, warnings } = await pushSchema(
|
||||
adapter.schema,
|
||||
adapter.drizzle,
|
||||
adapter.schemaName ? [adapter.schemaName] : undefined,
|
||||
)
|
||||
const { apply, hasDataLoss, warnings } = await pushSchema(adapter.schema, adapter.drizzle)
|
||||
|
||||
if (warnings.length) {
|
||||
let message = `Warnings detected during schema push: \n\n${warnings.join('\n')}\n\n`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-nodemailer",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "Payload Nodemailer Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-resend",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "Payload Resend Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -7,7 +7,6 @@ import type { Context } from '../types.js'
|
||||
export type Resolver = (
|
||||
_: unknown,
|
||||
args: {
|
||||
draft?: boolean
|
||||
id: number | string
|
||||
},
|
||||
context: {
|
||||
@@ -21,7 +20,6 @@ export default function restoreVersionResolver(collection: Collection): Resolver
|
||||
id: args.id,
|
||||
collection,
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
req: isolateObjectProperty(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import type { Context } from '../types.js'
|
||||
type Resolver = (
|
||||
_: unknown,
|
||||
args: {
|
||||
draft?: boolean
|
||||
id: number | string
|
||||
},
|
||||
context: {
|
||||
@@ -19,7 +18,6 @@ export default function restoreVersionResolver(globalConfig: SanitizedGlobalConf
|
||||
const options = {
|
||||
id: args.id,
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
globalConfig,
|
||||
req: isolateObjectProperty(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
@@ -342,7 +342,6 @@ function initCollectionsGraphQL({ config, graphqlResult }: InitCollectionsGraphQ
|
||||
type: collection.graphQL.type,
|
||||
args: {
|
||||
id: { type: versionIDType },
|
||||
draft: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: restoreVersionResolver(collection),
|
||||
}
|
||||
|
||||
@@ -133,7 +133,6 @@ function initGlobalsGraphQL({ config, graphqlResult }: InitGlobalsGraphQLArgs):
|
||||
type: graphqlResult.globals.graphQL[slug].versionType,
|
||||
args: {
|
||||
id: { type: idType },
|
||||
draft: { type: GraphQLBoolean },
|
||||
...(config.localization
|
||||
? {
|
||||
fallbackLocale: { type: graphqlResult.types.fallbackLocaleInputType },
|
||||
@@ -172,7 +171,6 @@ function initGlobalsGraphQL({ config, graphqlResult }: InitGlobalsGraphQLArgs):
|
||||
type: graphqlResult.globals.graphQL[slug].type,
|
||||
args: {
|
||||
id: { type: idType },
|
||||
draft: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: restoreVersionResolver(global),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-react",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "The official React SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-vue",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "The official Vue SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "The official live preview JavaScript SDK for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/next",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import { useDocumentInfo } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import { baseClass } from '../../Tab/index.js'
|
||||
|
||||
@@ -12,14 +12,13 @@ export const VersionsPill: React.FC = () => {
|
||||
// documents that are version enabled _always_ have at least one version
|
||||
const hasVersions = versions?.totalDocs > 0
|
||||
|
||||
if (hasVersions)
|
||||
return (
|
||||
<span
|
||||
className={[`${baseClass}__count`, hasVersions ? `${baseClass}__count--has-count` : '']
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{versions.totalDocs.toString()}
|
||||
</span>
|
||||
)
|
||||
return (
|
||||
<span
|
||||
className={[`${baseClass}__count`, hasVersions ? `${baseClass}__count--has-count` : '']
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{hasVersions ? versions.totalDocs.toString() : <Fragment> </Fragment>}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -85,18 +85,9 @@ export const DefaultNavClient: React.FC = () => {
|
||||
|
||||
const LinkElement = Link || 'a'
|
||||
|
||||
const activeCollection = window?.location?.pathname
|
||||
?.split('/')
|
||||
.find(
|
||||
(_, index, arr) =>
|
||||
arr[index - 1] === 'collections' || arr[index - 1] === 'globals',
|
||||
)
|
||||
|
||||
return (
|
||||
<LinkElement
|
||||
className={[`${baseClass}__link`, activeCollection === entity?.slug && `active`]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
className={`${baseClass}__link`}
|
||||
href={href}
|
||||
id={id}
|
||||
key={i}
|
||||
|
||||
@@ -14,7 +14,6 @@ export const restoreVersion: CollectionRouteHandlerWithID = async ({
|
||||
}) => {
|
||||
const { searchParams } = req
|
||||
const depth = searchParams.get('depth')
|
||||
const draft = searchParams.get('draft')
|
||||
|
||||
const id = sanitizeCollectionID({
|
||||
id: incomingID,
|
||||
@@ -26,7 +25,6 @@ export const restoreVersion: CollectionRouteHandlerWithID = async ({
|
||||
id,
|
||||
collection,
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
draft: draft === 'true' ? true : undefined,
|
||||
req,
|
||||
})
|
||||
|
||||
|
||||
@@ -9,12 +9,10 @@ import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
export const restoreVersion: GlobalRouteHandlerWithID = async ({ id, globalConfig, req }) => {
|
||||
const { searchParams } = req
|
||||
const depth = searchParams.get('depth')
|
||||
const draft = searchParams.get('draft')
|
||||
|
||||
const doc = await restoreVersionOperationGlobal({
|
||||
id,
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
draft: draft === 'true' ? true : undefined,
|
||||
globalConfig,
|
||||
req,
|
||||
})
|
||||
|
||||
@@ -35,17 +35,18 @@ export const CreateFirstUserClient: React.FC<{
|
||||
const fieldMap = getFieldMap({ collectionSlug: userSlug })
|
||||
|
||||
const onChange: FormProps['onChange'][0] = React.useCallback(
|
||||
async ({ formState: prevFormState }) =>
|
||||
getFormState({
|
||||
async ({ formState: prevFormState }) => {
|
||||
return getFormState({
|
||||
apiRoute,
|
||||
body: {
|
||||
collectionSlug: userSlug,
|
||||
formState: prevFormState,
|
||||
operation: 'create',
|
||||
schemaPath: `_${userSlug}.auth`,
|
||||
schemaPath: userSlug,
|
||||
},
|
||||
serverURL,
|
||||
}),
|
||||
})
|
||||
},
|
||||
[apiRoute, userSlug, serverURL],
|
||||
)
|
||||
|
||||
@@ -63,15 +64,14 @@ export const CreateFirstUserClient: React.FC<{
|
||||
<LoginField required={requireEmail} type="email" />
|
||||
)}
|
||||
<PasswordField
|
||||
autoComplete="off"
|
||||
label={t('authentication:newPassword')}
|
||||
name="password"
|
||||
path="password"
|
||||
required
|
||||
/>
|
||||
<ConfirmPasswordField />
|
||||
<RenderFields
|
||||
fieldMap={fieldMap}
|
||||
forceRender
|
||||
operation="create"
|
||||
path=""
|
||||
readOnly={false}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { AdminViewProps } from 'payload'
|
||||
import type { AdminViewProps, Field } from 'payload'
|
||||
|
||||
import { buildStateFromSchema } from '@payloadcms/ui/forms/buildStateFromSchema'
|
||||
import React from 'react'
|
||||
|
||||
import type { LoginFieldProps } from '../Login/LoginField/index.js'
|
||||
|
||||
import { getDocumentData } from '../Document/getDocumentData.js'
|
||||
import { CreateFirstUserClient } from './index.client.js'
|
||||
import './index.scss'
|
||||
|
||||
@@ -12,7 +12,6 @@ export { generateCreateFirstUserMetadata } from './meta.js'
|
||||
|
||||
export const CreateFirstUserView: React.FC<AdminViewProps> = async ({ initPageResult }) => {
|
||||
const {
|
||||
locale,
|
||||
req,
|
||||
req: {
|
||||
payload: {
|
||||
@@ -27,6 +26,7 @@ export const CreateFirstUserView: React.FC<AdminViewProps> = async ({ initPageRe
|
||||
const collectionConfig = config.collections?.find((collection) => collection?.slug === userSlug)
|
||||
const { auth: authOptions } = collectionConfig
|
||||
const loginWithUsername = authOptions.loginWithUsername
|
||||
const loginWithEmail = !loginWithUsername || loginWithUsername.allowEmailLogin
|
||||
const emailRequired = loginWithUsername && loginWithUsername.requireEmail
|
||||
|
||||
let loginType: LoginFieldProps['type'] = loginWithUsername ? 'username' : 'email'
|
||||
@@ -34,11 +34,42 @@ export const CreateFirstUserView: React.FC<AdminViewProps> = async ({ initPageRe
|
||||
loginType = 'emailOrUsername'
|
||||
}
|
||||
|
||||
const { formState } = await getDocumentData({
|
||||
collectionConfig,
|
||||
locale,
|
||||
const emailField = {
|
||||
name: 'email',
|
||||
type: 'email',
|
||||
label: req.t('general:emailAddress'),
|
||||
required: emailRequired ? true : false,
|
||||
}
|
||||
|
||||
const usernameField = {
|
||||
name: 'username',
|
||||
type: 'text',
|
||||
label: req.t('authentication:username'),
|
||||
required: true,
|
||||
}
|
||||
|
||||
const fields = [
|
||||
...(loginWithUsername ? [usernameField] : []),
|
||||
...(emailRequired || loginWithEmail ? [emailField] : []),
|
||||
{
|
||||
name: 'password',
|
||||
type: 'text',
|
||||
label: req.t('general:password'),
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'confirm-password',
|
||||
type: 'text',
|
||||
label: req.t('authentication:confirmPassword'),
|
||||
required: true,
|
||||
},
|
||||
]
|
||||
|
||||
const formState = await buildStateFromSchema({
|
||||
fieldSchema: fields as Field[],
|
||||
operation: 'create',
|
||||
preferences: { fields: {} },
|
||||
req,
|
||||
schemaPath: `_${collectionConfig.slug}.auth`,
|
||||
})
|
||||
|
||||
return (
|
||||
|
||||
@@ -15,11 +15,8 @@ export const getDocumentData = async (args: {
|
||||
id?: number | string
|
||||
locale: Locale
|
||||
req: PayloadRequest
|
||||
schemaPath?: string
|
||||
}): Promise<Data> => {
|
||||
const { id, collectionConfig, globalConfig, locale, req, schemaPath: schemaPathFromProps } = args
|
||||
|
||||
const schemaPath = schemaPathFromProps || collectionConfig?.slug || globalConfig?.slug
|
||||
const { id, collectionConfig, globalConfig, locale, req } = args
|
||||
|
||||
try {
|
||||
const formState = await buildFormState({
|
||||
@@ -31,7 +28,7 @@ export const getDocumentData = async (args: {
|
||||
globalSlug: globalConfig?.slug,
|
||||
locale: locale?.code,
|
||||
operation: (collectionConfig && id) || globalConfig ? 'update' : 'create',
|
||||
schemaPath,
|
||||
schemaPath: collectionConfig?.slug || globalConfig?.slug,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -78,7 +78,7 @@ export const APIKey: React.FC<{ enabled: boolean; readOnly?: boolean }> = ({
|
||||
if (!apiKeyValue && enabled) {
|
||||
setValue(initialAPIKey)
|
||||
}
|
||||
if (!enabled && apiKeyValue) {
|
||||
if (!enabled) {
|
||||
setValue(null)
|
||||
}
|
||||
}, [apiKeyValue, enabled, setValue, initialAPIKey])
|
||||
@@ -100,7 +100,6 @@ export const APIKey: React.FC<{ enabled: boolean; readOnly?: boolean }> = ({
|
||||
<div className={[fieldBaseClass, 'api-key', 'read-only'].filter(Boolean).join(' ')}>
|
||||
<FieldLabel CustomLabel={APIKeyLabel} htmlFor={path} />
|
||||
<input
|
||||
aria-label="API Key"
|
||||
className={highlightedField ? 'highlight' : undefined}
|
||||
disabled
|
||||
id="apiKey"
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
useFormModified,
|
||||
useTranslation,
|
||||
} from '@payloadcms/ui'
|
||||
import { email as emailValidation } from 'payload/shared'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
@@ -35,8 +34,6 @@ export const Auth: React.FC<Props> = (props) => {
|
||||
operation,
|
||||
readOnly,
|
||||
requirePassword,
|
||||
setSchemaPath,
|
||||
setValidateBeforeSubmit,
|
||||
useAPIKey,
|
||||
username,
|
||||
verify,
|
||||
@@ -45,7 +42,6 @@ export const Auth: React.FC<Props> = (props) => {
|
||||
const { permissions } = useAuth()
|
||||
const [changingPassword, setChangingPassword] = useState(requirePassword)
|
||||
const enableAPIKey = useFormFields(([fields]) => (fields && fields?.enableAPIKey) || null)
|
||||
const forceOpenChangePassword = useFormFields(([fields]) => (fields && fields?.password) || null)
|
||||
const dispatchFields = useFormFields((reducer) => reducer[1])
|
||||
const modified = useFormModified()
|
||||
const { i18n, t } = useTranslation()
|
||||
@@ -74,32 +70,15 @@ export const Auth: React.FC<Props> = (props) => {
|
||||
}, [permissions, collectionSlug])
|
||||
|
||||
const handleChangePassword = useCallback(
|
||||
(showPasswordFields: boolean) => {
|
||||
if (showPasswordFields) {
|
||||
setValidateBeforeSubmit(true)
|
||||
setSchemaPath(`_${collectionSlug}.auth`)
|
||||
dispatchFields({
|
||||
type: 'UPDATE',
|
||||
errorMessage: t('validation:required'),
|
||||
path: 'password',
|
||||
valid: false,
|
||||
})
|
||||
dispatchFields({
|
||||
type: 'UPDATE',
|
||||
errorMessage: t('validation:required'),
|
||||
path: 'confirm-password',
|
||||
valid: false,
|
||||
})
|
||||
} else {
|
||||
setValidateBeforeSubmit(false)
|
||||
setSchemaPath(collectionSlug)
|
||||
(state: boolean) => {
|
||||
if (!state) {
|
||||
dispatchFields({ type: 'REMOVE', path: 'password' })
|
||||
dispatchFields({ type: 'REMOVE', path: 'confirm-password' })
|
||||
}
|
||||
|
||||
setChangingPassword(showPasswordFields)
|
||||
setChangingPassword(state)
|
||||
},
|
||||
[dispatchFields, t, collectionSlug, setSchemaPath, setValidateBeforeSubmit],
|
||||
[dispatchFields],
|
||||
)
|
||||
|
||||
const unlock = useCallback(async () => {
|
||||
@@ -120,7 +99,7 @@ export const Auth: React.FC<Props> = (props) => {
|
||||
} else {
|
||||
toast.error(t('authentication:failedToUnlock'))
|
||||
}
|
||||
}, [i18n, serverURL, api, collectionSlug, email, username, t, loginWithUsername])
|
||||
}, [i18n, serverURL, api, collectionSlug, email, username, t])
|
||||
|
||||
useEffect(() => {
|
||||
if (!modified) {
|
||||
@@ -134,8 +113,6 @@ export const Auth: React.FC<Props> = (props) => {
|
||||
|
||||
const disabled = readOnly || isInitializing
|
||||
|
||||
const showPasswordFields = changingPassword || forceOpenChangePassword
|
||||
|
||||
return (
|
||||
<div className={[baseClass, className].filter(Boolean).join(' ')}>
|
||||
{!disableLocalStrategy && (
|
||||
@@ -159,33 +136,22 @@ export const Auth: React.FC<Props> = (props) => {
|
||||
name="email"
|
||||
readOnly={readOnly}
|
||||
required={!loginWithUsername || loginWithUsername?.requireEmail}
|
||||
validate={(value) =>
|
||||
emailValidation(value, {
|
||||
name: 'email',
|
||||
type: 'email',
|
||||
data: {},
|
||||
preferences: { fields: {} },
|
||||
req: { t } as any,
|
||||
required: true,
|
||||
siblingData: {},
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{(showPasswordFields || requirePassword) && (
|
||||
{(changingPassword || requirePassword) && (
|
||||
<div className={`${baseClass}__changing-password`}>
|
||||
<PasswordField
|
||||
autoComplete="off"
|
||||
disabled={disabled}
|
||||
label={t('authentication:newPassword')}
|
||||
name="password"
|
||||
path="password"
|
||||
required
|
||||
/>
|
||||
<ConfirmPasswordField disabled={readOnly} />
|
||||
</div>
|
||||
)}
|
||||
<div className={`${baseClass}__controls`}>
|
||||
{showPasswordFields && !requirePassword && (
|
||||
{changingPassword && !requirePassword && (
|
||||
<Button
|
||||
buttonStyle="secondary"
|
||||
disabled={disabled}
|
||||
@@ -195,7 +161,7 @@ export const Auth: React.FC<Props> = (props) => {
|
||||
{t('general:cancel')}
|
||||
</Button>
|
||||
)}
|
||||
{!showPasswordFields && !requirePassword && (
|
||||
{!changingPassword && !requirePassword && (
|
||||
<Button
|
||||
buttonStyle="secondary"
|
||||
disabled={disabled}
|
||||
|
||||
@@ -9,8 +9,6 @@ export type Props = {
|
||||
operation: 'create' | 'update'
|
||||
readOnly: boolean
|
||||
requirePassword?: boolean
|
||||
setSchemaPath: (path: string) => void
|
||||
setValidateBeforeSubmit: (validate: boolean) => void
|
||||
useAPIKey?: boolean
|
||||
username: string
|
||||
verify?: VerifyConfig | boolean
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
} from '@payloadcms/ui'
|
||||
import { formatAdminURL, getFormState } from '@payloadcms/ui/shared'
|
||||
import { useRouter, useSearchParams } from 'next/navigation.js'
|
||||
import React, { Fragment, useCallback, useState } from 'react'
|
||||
import React, { Fragment, useCallback } from 'react'
|
||||
|
||||
import { LeaveWithoutSaving } from '../../../elements/LeaveWithoutSaving/index.js'
|
||||
import { Auth } from './Auth/index.js'
|
||||
@@ -102,9 +102,6 @@ export const DefaultEditView: React.FC = () => {
|
||||
|
||||
const classes = [baseClass, id && `${baseClass}--is-editing`].filter(Boolean).join(' ')
|
||||
|
||||
const [schemaPath, setSchemaPath] = React.useState(entitySlug)
|
||||
const [validateBeforeSubmit, setValidateBeforeSubmit] = useState(false)
|
||||
|
||||
const onSave = useCallback(
|
||||
(json) => {
|
||||
reportUpdate({
|
||||
@@ -161,6 +158,7 @@ export const DefaultEditView: React.FC = () => {
|
||||
const onChange: FormProps['onChange'][0] = useCallback(
|
||||
async ({ formState: prevFormState }) => {
|
||||
const docPreferences = await getDocPreferences()
|
||||
|
||||
return getFormState({
|
||||
apiRoute,
|
||||
body: {
|
||||
@@ -170,12 +168,12 @@ export const DefaultEditView: React.FC = () => {
|
||||
formState: prevFormState,
|
||||
globalSlug,
|
||||
operation,
|
||||
schemaPath,
|
||||
schemaPath: entitySlug,
|
||||
},
|
||||
serverURL,
|
||||
})
|
||||
},
|
||||
[apiRoute, collectionSlug, schemaPath, getDocPreferences, globalSlug, id, operation, serverURL],
|
||||
[serverURL, apiRoute, id, operation, entitySlug, collectionSlug, globalSlug, getDocPreferences],
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -184,7 +182,7 @@ export const DefaultEditView: React.FC = () => {
|
||||
<Form
|
||||
action={action}
|
||||
className={`${baseClass}__form`}
|
||||
disableValidationOnSubmit={!validateBeforeSubmit}
|
||||
disableValidationOnSubmit
|
||||
disabled={isInitializing || !hasSavePermission}
|
||||
initialState={!isInitializing && initialState}
|
||||
isInitializing={isInitializing}
|
||||
@@ -233,8 +231,6 @@ export const DefaultEditView: React.FC = () => {
|
||||
operation={operation}
|
||||
readOnly={!hasSavePermission}
|
||||
requirePassword={!id}
|
||||
setSchemaPath={setSchemaPath}
|
||||
setValidateBeforeSubmit={setValidateBeforeSubmit}
|
||||
useAPIKey={auth.useAPIKey}
|
||||
username={data?.username}
|
||||
verify={auth.verify}
|
||||
@@ -259,7 +255,7 @@ export const DefaultEditView: React.FC = () => {
|
||||
docPermissions={docPermissions}
|
||||
fieldMap={fieldMap}
|
||||
readOnly={!hasSavePermission}
|
||||
schemaPath={schemaPath}
|
||||
schemaPath={entitySlug}
|
||||
/>
|
||||
{AfterDocument}
|
||||
</Form>
|
||||
|
||||
@@ -18,7 +18,6 @@ export const LoginField: React.FC<LoginFieldProps> = ({ type, required = true })
|
||||
autoComplete="email"
|
||||
label={t('general:email')}
|
||||
name="email"
|
||||
path="email"
|
||||
required={required}
|
||||
validate={(value) =>
|
||||
email(value, {
|
||||
@@ -40,7 +39,6 @@ export const LoginField: React.FC<LoginFieldProps> = ({ type, required = true })
|
||||
<TextField
|
||||
label={t('authentication:username')}
|
||||
name="username"
|
||||
path="username"
|
||||
required
|
||||
validate={(value) =>
|
||||
username(value, {
|
||||
@@ -67,7 +65,6 @@ export const LoginField: React.FC<LoginFieldProps> = ({ type, required = true })
|
||||
<TextField
|
||||
label={t('authentication:emailOrUsername')}
|
||||
name="username"
|
||||
path="username"
|
||||
required
|
||||
validate={(value) => {
|
||||
const passesUsername = username(value, {
|
||||
|
||||
@@ -6,10 +6,11 @@ import React from 'react'
|
||||
const baseClass = 'login__form'
|
||||
const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.default
|
||||
|
||||
import type { FormState } from 'payload'
|
||||
import type { FormState, PayloadRequest } from 'payload'
|
||||
|
||||
import { Form, FormSubmit, PasswordField, useConfig, useTranslation } from '@payloadcms/ui'
|
||||
import { formatAdminURL } from '@payloadcms/ui/shared'
|
||||
import { password } from 'payload/shared'
|
||||
|
||||
import type { LoginFieldProps } from '../LoginField/index.js'
|
||||
|
||||
@@ -81,7 +82,28 @@ export const LoginForm: React.FC<{
|
||||
>
|
||||
<div className={`${baseClass}__inputWrap`}>
|
||||
<LoginField type={loginType} />
|
||||
<PasswordField label={t('general:password')} name="password" required />
|
||||
<PasswordField
|
||||
autoComplete="off"
|
||||
label={t('general:password')}
|
||||
name="password"
|
||||
required
|
||||
validate={(value) =>
|
||||
password(value, {
|
||||
name: 'password',
|
||||
type: 'text',
|
||||
data: {},
|
||||
preferences: { fields: {} },
|
||||
req: {
|
||||
payload: {
|
||||
config,
|
||||
},
|
||||
t,
|
||||
} as PayloadRequest,
|
||||
required: true,
|
||||
siblingData: {},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<Link
|
||||
href={formatAdminURL({
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
PasswordField,
|
||||
useAuth,
|
||||
useConfig,
|
||||
useFormFields,
|
||||
useTranslation,
|
||||
} from '@payloadcms/ui'
|
||||
import { formatAdminURL } from '@payloadcms/ui/shared'
|
||||
@@ -63,7 +64,7 @@ export const ResetPasswordClient: React.FC<Args> = ({ token }) => {
|
||||
toast.success(i18n.t('general:updatedSuccessfully'))
|
||||
}
|
||||
},
|
||||
[adminRoute, fetchFullUser, history, i18n, loginRoute],
|
||||
[fetchFullUser, history, adminRoute, i18n],
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -73,15 +74,42 @@ export const ResetPasswordClient: React.FC<Args> = ({ token }) => {
|
||||
method="POST"
|
||||
onSuccess={onSuccess}
|
||||
>
|
||||
<PasswordField
|
||||
label={i18n.t('authentication:newPassword')}
|
||||
name="password"
|
||||
path="password"
|
||||
required
|
||||
/>
|
||||
<PasswordToConfirm />
|
||||
<ConfirmPasswordField />
|
||||
<HiddenField forceUsePathFromProps name="token" value={token} />
|
||||
<FormSubmit>{i18n.t('authentication:resetPassword')}</FormSubmit>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
||||
const PasswordToConfirm = () => {
|
||||
const { t } = useTranslation()
|
||||
const { value: confirmValue } = useFormFields(
|
||||
([fields]) => (fields && fields?.['confirm-password']) || null,
|
||||
)
|
||||
|
||||
const validate = React.useCallback(
|
||||
(value: string) => {
|
||||
if (!value) {
|
||||
return t('validation:required')
|
||||
}
|
||||
|
||||
if (value === confirmValue) {
|
||||
return true
|
||||
}
|
||||
|
||||
return t('fields:passwordsDoNotMatch')
|
||||
},
|
||||
[confirmValue, t],
|
||||
)
|
||||
|
||||
return (
|
||||
<PasswordField
|
||||
autoComplete="off"
|
||||
label={t('authentication:newPassword')}
|
||||
name="password"
|
||||
required
|
||||
validate={validate}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -111,7 +111,6 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
|
||||
globalSlug={globalSlug}
|
||||
label={collectionConfig?.labels.singular || globalConfig?.label}
|
||||
originalDocID={id}
|
||||
status={doc?.version?._status}
|
||||
versionDate={versionCreatedAt}
|
||||
versionID={versionID}
|
||||
/>
|
||||
|
||||
@@ -2,37 +2,6 @@
|
||||
|
||||
.restore-version {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
|
||||
.popup-button {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__chevron {
|
||||
background-color: var(--theme-elevation-150);
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
cursor: pointer;
|
||||
|
||||
.stroke {
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--theme-elevation-100);
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
border-top-right-radius: 0px;
|
||||
border-bottom-right-radius: 0px;
|
||||
margin-right: 2px;
|
||||
|
||||
&:focus {
|
||||
border-radius: 0;
|
||||
outline-offset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__modal {
|
||||
@include blur-bg;
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
'use client'
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import {
|
||||
Button,
|
||||
ChevronIcon,
|
||||
Modal,
|
||||
Pill,
|
||||
Popup,
|
||||
PopupList,
|
||||
useConfig,
|
||||
useModal,
|
||||
useTranslation,
|
||||
} from '@payloadcms/ui'
|
||||
import { Button, Modal, Pill, useConfig, useModal, useTranslation } from '@payloadcms/ui'
|
||||
import { formatAdminURL, requests } from '@payloadcms/ui/shared'
|
||||
import { useRouter } from 'next/navigation.js'
|
||||
import React, { Fragment, useCallback, useState } from 'react'
|
||||
@@ -30,7 +20,6 @@ const Restore: React.FC<Props> = ({
|
||||
globalSlug,
|
||||
label,
|
||||
originalDocID,
|
||||
status,
|
||||
versionDate,
|
||||
versionID,
|
||||
}) => {
|
||||
@@ -43,7 +32,6 @@ const Restore: React.FC<Props> = ({
|
||||
const [processing, setProcessing] = useState(false)
|
||||
const router = useRouter()
|
||||
const { i18n, t } = useTranslation()
|
||||
const [draft, setDraft] = useState(false)
|
||||
|
||||
const restoreMessage = t('version:aboutToRestoreGlobal', {
|
||||
label: getTranslation(label, i18n),
|
||||
@@ -52,10 +40,9 @@ const Restore: React.FC<Props> = ({
|
||||
|
||||
let fetchURL = `${serverURL}${apiRoute}`
|
||||
let redirectURL: string
|
||||
const canRestoreAsDraft = status !== 'draft'
|
||||
|
||||
if (collectionSlug) {
|
||||
fetchURL = `${fetchURL}/${collectionSlug}/versions/${versionID}?draft=${draft}`
|
||||
fetchURL = `${fetchURL}/${collectionSlug}/versions/${versionID}`
|
||||
redirectURL = formatAdminURL({
|
||||
adminRoute,
|
||||
path: `/collections/${collectionSlug}/${originalDocID}`,
|
||||
@@ -63,7 +50,7 @@ const Restore: React.FC<Props> = ({
|
||||
}
|
||||
|
||||
if (globalSlug) {
|
||||
fetchURL = `${fetchURL}/globals/${globalSlug}/versions/${versionID}?draft=${draft}`
|
||||
fetchURL = `${fetchURL}/globals/${globalSlug}/versions/${versionID}`
|
||||
redirectURL = formatAdminURL({
|
||||
adminRoute,
|
||||
path: `/globals/${globalSlug}`,
|
||||
@@ -87,35 +74,15 @@ const Restore: React.FC<Props> = ({
|
||||
toast.error(t('version:problemRestoringVersion'))
|
||||
}
|
||||
}, [fetchURL, redirectURL, t, i18n, router])
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={[baseClass, className].filter(Boolean).join(' ')}>
|
||||
<Pill
|
||||
className={[canRestoreAsDraft && `${baseClass}__button`].filter(Boolean).join(' ')}
|
||||
onClick={() => toggleModal(modalSlug)}
|
||||
>
|
||||
{t('version:restoreThisVersion')}
|
||||
</Pill>
|
||||
{canRestoreAsDraft && (
|
||||
<Popup
|
||||
button={
|
||||
<Pill className={`${baseClass}__chevron`}>
|
||||
<ChevronIcon />
|
||||
</Pill>
|
||||
}
|
||||
caret={false}
|
||||
render={() => (
|
||||
<PopupList.ButtonGroup>
|
||||
<PopupList.Button onClick={() => [setDraft(true), toggleModal(modalSlug)]}>
|
||||
{t('version:restoreAsDraft')}
|
||||
</PopupList.Button>
|
||||
</PopupList.ButtonGroup>
|
||||
)}
|
||||
size="large"
|
||||
verticalAlign="bottom"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<Pill
|
||||
className={[baseClass, className].filter(Boolean).join(' ')}
|
||||
onClick={() => toggleModal(modalSlug)}
|
||||
>
|
||||
{t('version:restoreThisVersion')}
|
||||
</Pill>
|
||||
<Modal className={`${baseClass}__modal`} slug={modalSlug}>
|
||||
<MinimalTemplate className={`${baseClass}__modal-template`}>
|
||||
<h1>{t('version:confirmVersionRestoration')}</h1>
|
||||
@@ -127,7 +94,7 @@ const Restore: React.FC<Props> = ({
|
||||
>
|
||||
{t('general:cancel')}
|
||||
</Button>
|
||||
<Button onClick={processing ? undefined : () => void handleRestore()}>
|
||||
<Button onClick={processing ? undefined : handleRestore}>
|
||||
{processing ? t('version:restoring') : t('general:confirm')}
|
||||
</Button>
|
||||
</MinimalTemplate>
|
||||
|
||||
@@ -6,7 +6,6 @@ export type Props = {
|
||||
globalSlug?: SanitizedGlobalConfig['slug']
|
||||
label: SanitizedCollectionConfig['labels']['singular']
|
||||
originalDocID: number | string
|
||||
status?: string
|
||||
versionDate: string
|
||||
versionID: string
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export const VersionsViewClient: React.FC<{
|
||||
|
||||
const { getComponentMap } = useComponentMap()
|
||||
const { collectionSlug, globalSlug } = useDocumentInfo()
|
||||
const { data, handlePageChange, handlePerPageChange } = useListQuery()
|
||||
const { data, handlePerPageChange } = useListQuery()
|
||||
|
||||
const componentMap = getComponentMap({
|
||||
collectionSlug,
|
||||
@@ -59,7 +59,6 @@ export const VersionsViewClient: React.FC<{
|
||||
limit={data.limit}
|
||||
nextPage={data.nextPage}
|
||||
numberOfNeighbors={1}
|
||||
onChange={() => handlePageChange}
|
||||
page={data.page}
|
||||
prevPage={data.prevPage}
|
||||
totalPages={data.totalPages}
|
||||
@@ -74,7 +73,7 @@ export const VersionsViewClient: React.FC<{
|
||||
{i18n.t('general:of')} {data.totalDocs}
|
||||
</div>
|
||||
<PerPage
|
||||
handleChange={() => handlePerPageChange}
|
||||
handleChange={handlePerPageChange}
|
||||
limit={limit ? Number(limit) : 10}
|
||||
limits={paginationLimits}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
|
||||
"keywords": [
|
||||
"admin panel",
|
||||
|
||||
@@ -19,7 +19,6 @@ export type CellComponentProps = {
|
||||
}[]
|
||||
className?: string
|
||||
dateDisplayFormat?: DateField['admin']['date']['displayFormat']
|
||||
displayPreview?: boolean
|
||||
fieldType?: Field['type']
|
||||
isFieldAffectingData?: boolean
|
||||
label?: FormFieldBase['label']
|
||||
|
||||
@@ -19,5 +19,6 @@ export type GenericDescriptionProps = {
|
||||
}
|
||||
export type FieldDescriptionProps<T extends keyof FieldTypes = any> = {
|
||||
type: T
|
||||
} & GenericDescriptionProps &
|
||||
} & FieldComponentProps &
|
||||
GenericDescriptionProps &
|
||||
Partial<ServerProps>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { CustomComponent, ServerProps } from '../../config/types.js'
|
||||
import type { FieldComponentProps } from '../fields/index.js'
|
||||
import type { FormFieldBase } from './Field.js'
|
||||
import type { FieldTypes } from './FieldTypes.js'
|
||||
|
||||
@@ -10,11 +11,11 @@ export type GenericLabelProps = {
|
||||
} & FormFieldBase
|
||||
|
||||
export type LabelProps<T extends keyof FieldTypes = any> = {
|
||||
label?: FormFieldBase['label']
|
||||
required?: boolean
|
||||
} & {
|
||||
type: T
|
||||
} & GenericLabelProps
|
||||
} & FieldComponentProps &
|
||||
GenericLabelProps &
|
||||
Partial<ServerProps>
|
||||
|
||||
export type SanitizedLabelProps<T extends keyof FieldTypes = any> = Omit<
|
||||
LabelProps<T>,
|
||||
'label' | 'required'
|
||||
|
||||
@@ -70,7 +70,6 @@ export const resetPasswordOperation = async (args: Arguments): Promise<Result> =
|
||||
const { hash, salt } = await generatePasswordSaltHash({
|
||||
collection: collectionConfig,
|
||||
password: data.password,
|
||||
req,
|
||||
})
|
||||
|
||||
user.salt = salt
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import crypto from 'crypto'
|
||||
|
||||
import type { SanitizedCollectionConfig } from '../../../collections/config/types.js'
|
||||
import type { PayloadRequest } from '../../../types/index.js'
|
||||
|
||||
import { ValidationError } from '../../../errors/index.js'
|
||||
import { password } from '../../../fields/validations.js'
|
||||
|
||||
const defaultPasswordValidator = (password: string): string | true => {
|
||||
if (!password) return 'No password was given'
|
||||
if (password.length < 3) return 'Password must be at least 3 characters'
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function randomBytes(): Promise<Buffer> {
|
||||
return new Promise((resolve, reject) =>
|
||||
@@ -23,23 +28,13 @@ function pbkdf2Promisified(password: string, salt: string): Promise<Buffer> {
|
||||
type Args = {
|
||||
collection: SanitizedCollectionConfig
|
||||
password: string
|
||||
req: PayloadRequest
|
||||
}
|
||||
|
||||
export const generatePasswordSaltHash = async ({
|
||||
collection,
|
||||
password: passwordToSet,
|
||||
req,
|
||||
password,
|
||||
}: Args): Promise<{ hash: string; salt: string }> => {
|
||||
const validationResult = password(passwordToSet, {
|
||||
name: 'password',
|
||||
type: 'text',
|
||||
data: {},
|
||||
preferences: { fields: {} },
|
||||
req,
|
||||
required: true,
|
||||
siblingData: {},
|
||||
})
|
||||
const validationResult = defaultPasswordValidator(password)
|
||||
|
||||
if (typeof validationResult === 'string') {
|
||||
throw new ValidationError({
|
||||
@@ -51,7 +46,7 @@ export const generatePasswordSaltHash = async ({
|
||||
const saltBuffer = await randomBytes()
|
||||
const salt = saltBuffer.toString('hex')
|
||||
|
||||
const hashRaw = await pbkdf2Promisified(passwordToSet, salt)
|
||||
const hashRaw = await pbkdf2Promisified(password, salt)
|
||||
const hash = hashRaw.toString('hex')
|
||||
|
||||
return { hash, salt }
|
||||
|
||||
@@ -55,7 +55,7 @@ export const registerLocalStrategy = async ({
|
||||
})
|
||||
}
|
||||
|
||||
const { hash, salt } = await generatePasswordSaltHash({ collection, password, req })
|
||||
const { hash, salt } = await generatePasswordSaltHash({ collection, password })
|
||||
|
||||
const sanitizedDoc = { ...doc }
|
||||
if (sanitizedDoc.password) delete sanitizedDoc.password
|
||||
|
||||
@@ -18,7 +18,6 @@ export type Arguments = {
|
||||
currentDepth?: number
|
||||
depth?: number
|
||||
disableErrors?: boolean
|
||||
draft?: boolean
|
||||
id: number | string
|
||||
overrideAccess?: boolean
|
||||
req: PayloadRequest
|
||||
@@ -32,7 +31,6 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
|
||||
id,
|
||||
collection: { config: collectionConfig },
|
||||
depth,
|
||||
draft,
|
||||
overrideAccess = false,
|
||||
req,
|
||||
req: { fallbackLocale, locale, payload },
|
||||
@@ -128,7 +126,7 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
|
||||
parent: parentDocID,
|
||||
req,
|
||||
updatedAt: new Date().toISOString(),
|
||||
versionData: draft ? { ...rawVersion.version, _status: 'draft' } : rawVersion.version,
|
||||
versionData: rawVersion.version,
|
||||
})
|
||||
|
||||
// /////////////////////////////////////
|
||||
|
||||
@@ -263,7 +263,6 @@ export const updateByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
const { hash, salt } = await generatePasswordSaltHash({
|
||||
collection: collectionConfig,
|
||||
password,
|
||||
req,
|
||||
})
|
||||
dataToUpdate.salt = salt
|
||||
dataToUpdate.hash = hash
|
||||
|
||||
@@ -521,7 +521,6 @@ export type UploadField = {
|
||||
Label?: LabelComponent
|
||||
}
|
||||
}
|
||||
displayPreview?: boolean
|
||||
filterOptions?: FilterOptions
|
||||
/**
|
||||
* Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached.
|
||||
|
||||
@@ -33,6 +33,7 @@ type Args<T> = {
|
||||
* The original siblingData (not modified by any hooks)
|
||||
*/
|
||||
siblingDoc: JsonObject
|
||||
siblingDocKeys: Set<string>
|
||||
}
|
||||
|
||||
// This function is responsible for the following actions, in order:
|
||||
@@ -57,6 +58,7 @@ export const promise = async <T>({
|
||||
req,
|
||||
siblingData,
|
||||
siblingDoc,
|
||||
siblingDocKeys,
|
||||
}: Args<T>): Promise<void> => {
|
||||
const { path: fieldPath, schemaPath: fieldSchemaPath } = getFieldPaths({
|
||||
field,
|
||||
@@ -65,6 +67,15 @@ export const promise = async <T>({
|
||||
})
|
||||
|
||||
if (fieldAffectsData(field)) {
|
||||
// Remove the key from siblingDocKeys
|
||||
// the goal is to keep any existing data present
|
||||
// before updating, for users that want to maintain
|
||||
// external data in the same collections as Payload manages,
|
||||
// without having fields defined for them
|
||||
if (siblingDocKeys.has(field.name)) {
|
||||
siblingDocKeys.delete(field.name)
|
||||
}
|
||||
|
||||
if (field.name === 'id') {
|
||||
if (field.type === 'number' && typeof siblingData[field.name] === 'string') {
|
||||
const value = siblingData[field.name] as string
|
||||
@@ -308,6 +319,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData: groupData as JsonObject,
|
||||
siblingDoc: groupDoc as JsonObject,
|
||||
siblingDocKeys,
|
||||
})
|
||||
|
||||
break
|
||||
@@ -335,6 +347,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData: row as JsonObject,
|
||||
siblingDoc: getExistingRowDoc(row as JsonObject, siblingDoc[field.name]),
|
||||
siblingDocKeys,
|
||||
}),
|
||||
)
|
||||
})
|
||||
@@ -372,6 +385,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData: row as JsonObject,
|
||||
siblingDoc: rowSiblingDoc,
|
||||
siblingDocKeys,
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -399,6 +413,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData,
|
||||
siblingDoc,
|
||||
siblingDocKeys,
|
||||
})
|
||||
|
||||
break
|
||||
@@ -407,7 +422,10 @@ export const promise = async <T>({
|
||||
case 'tab': {
|
||||
let tabSiblingData
|
||||
let tabSiblingDoc
|
||||
if (tabHasName(field)) {
|
||||
|
||||
const isNamedTab = tabHasName(field)
|
||||
|
||||
if (isNamedTab) {
|
||||
if (typeof siblingData[field.name] !== 'object') siblingData[field.name] = {}
|
||||
if (typeof siblingDoc[field.name] !== 'object') siblingDoc[field.name] = {}
|
||||
|
||||
@@ -433,6 +451,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData: tabSiblingData,
|
||||
siblingDoc: tabSiblingDoc,
|
||||
siblingDocKeys: isNamedTab ? undefined : siblingDocKeys,
|
||||
})
|
||||
|
||||
break
|
||||
@@ -454,6 +473,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData,
|
||||
siblingDoc,
|
||||
siblingDocKeys,
|
||||
})
|
||||
|
||||
break
|
||||
|
||||
@@ -26,6 +26,7 @@ type Args<T> = {
|
||||
* The original siblingData (not modified by any hooks)
|
||||
*/
|
||||
siblingDoc: JsonObject
|
||||
siblingDocKeys?: Set<string>
|
||||
}
|
||||
|
||||
export const traverseFields = async <T>({
|
||||
@@ -43,8 +44,11 @@ export const traverseFields = async <T>({
|
||||
schemaPath,
|
||||
siblingData,
|
||||
siblingDoc,
|
||||
siblingDocKeys: incomingSiblingDocKeys,
|
||||
}: Args<T>): Promise<void> => {
|
||||
const promises = []
|
||||
const siblingDocKeys = incomingSiblingDocKeys || new Set(Object.keys(siblingDoc))
|
||||
|
||||
fields.forEach((field) => {
|
||||
promises.push(
|
||||
promise({
|
||||
@@ -62,8 +66,18 @@ export const traverseFields = async <T>({
|
||||
req,
|
||||
siblingData,
|
||||
siblingDoc,
|
||||
siblingDocKeys,
|
||||
}),
|
||||
)
|
||||
})
|
||||
await Promise.all(promises)
|
||||
|
||||
// For any siblingDocKeys that have not been deleted,
|
||||
// we will move the data to the siblingData object
|
||||
// to preserve it
|
||||
siblingDocKeys.forEach((key) => {
|
||||
if (!['createdAt', 'globalType', 'id', 'updatedAt'].includes(key)) {
|
||||
siblingData[key] = siblingDoc[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ export const password: Validate<string, unknown, unknown, TextField> = (
|
||||
value,
|
||||
{
|
||||
maxLength: fieldMaxLength,
|
||||
minLength = 3,
|
||||
minLength,
|
||||
req: {
|
||||
payload: { config },
|
||||
t,
|
||||
@@ -115,28 +115,6 @@ export const password: Validate<string, unknown, unknown, TextField> = (
|
||||
return true
|
||||
}
|
||||
|
||||
export const confirmPassword: Validate<string, unknown, unknown, TextField> = (
|
||||
value,
|
||||
{ req: { data, t }, required },
|
||||
) => {
|
||||
if (required && !value) {
|
||||
return t('validation:required')
|
||||
}
|
||||
|
||||
if (
|
||||
value &&
|
||||
typeof data.formState === 'object' &&
|
||||
'password' in data.formState &&
|
||||
typeof data.formState.password === 'object' &&
|
||||
'value' in data.formState.password &&
|
||||
value !== data.formState.password.value
|
||||
) {
|
||||
return t('fields:passwordsDoNotMatch')
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export const email: Validate<string, unknown, unknown, EmailField> = (
|
||||
value,
|
||||
{ req: { t }, required },
|
||||
@@ -713,7 +691,6 @@ export default {
|
||||
blocks,
|
||||
checkbox,
|
||||
code,
|
||||
confirmPassword,
|
||||
date,
|
||||
email,
|
||||
json,
|
||||
|
||||
@@ -17,7 +17,6 @@ export type Arguments = {
|
||||
limit?: number
|
||||
overrideAccess?: boolean
|
||||
page?: number
|
||||
pagination?: boolean
|
||||
req?: PayloadRequest
|
||||
showHiddenFields?: boolean
|
||||
sort?: string
|
||||
@@ -33,7 +32,6 @@ export const findVersionsOperation = async <T extends TypeWithVersion<T>>(
|
||||
limit,
|
||||
overrideAccess,
|
||||
page,
|
||||
pagination = true,
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
showHiddenFields,
|
||||
@@ -71,7 +69,6 @@ export const findVersionsOperation = async <T extends TypeWithVersion<T>>(
|
||||
limit: limit ?? 10,
|
||||
locale,
|
||||
page: page || 1,
|
||||
pagination,
|
||||
req,
|
||||
sort,
|
||||
where: fullWhere,
|
||||
|
||||
@@ -12,7 +12,6 @@ import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
|
||||
export type Arguments = {
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
globalConfig: SanitizedGlobalConfig
|
||||
id: number | string
|
||||
overrideAccess?: boolean
|
||||
@@ -26,7 +25,6 @@ export const restoreVersionOperation = async <T extends TypeWithVersion<T> = any
|
||||
const {
|
||||
id,
|
||||
depth,
|
||||
draft,
|
||||
globalConfig,
|
||||
overrideAccess,
|
||||
req: { fallbackLocale, locale, payload },
|
||||
@@ -65,11 +63,6 @@ export const restoreVersionOperation = async <T extends TypeWithVersion<T> = any
|
||||
// Patch globalType onto version doc
|
||||
rawVersion.version.globalType = globalConfig.slug
|
||||
|
||||
// Overwrite draft status if draft is true
|
||||
|
||||
if (draft) {
|
||||
rawVersion.version._status = 'draft'
|
||||
}
|
||||
// /////////////////////////////////////
|
||||
// fetch previousDoc
|
||||
// /////////////////////////////////////
|
||||
@@ -97,18 +90,6 @@ export const restoreVersionOperation = async <T extends TypeWithVersion<T> = any
|
||||
data: result,
|
||||
req,
|
||||
})
|
||||
|
||||
const now = new Date().toISOString()
|
||||
|
||||
result = await payload.db.createGlobalVersion({
|
||||
autosave: false,
|
||||
createdAt: result.createdAt ? new Date(result.createdAt).toISOString() : now,
|
||||
globalSlug: globalConfig.slug,
|
||||
parent: id,
|
||||
req,
|
||||
updatedAt: draft ? now : new Date(result.updatedAt).toISOString(),
|
||||
versionData: result,
|
||||
})
|
||||
} else {
|
||||
result = await payload.db.createGlobal({
|
||||
slug: globalConfig.slug,
|
||||
|
||||
@@ -93,12 +93,6 @@ export type UploadConfig = {
|
||||
* @default false
|
||||
*/
|
||||
disableLocalStorage?: boolean
|
||||
/**
|
||||
* Enable displaying preview of the uploaded file in Upload fields related to this Collection.
|
||||
* Can be locally overridden by `displayPreview` option in Upload field.
|
||||
* @default false
|
||||
*/
|
||||
displayPreview?: boolean
|
||||
/**
|
||||
* Ability to filter/modify Request Headers when fetching a file.
|
||||
*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud-storage",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "The official cloud storage plugin for Payload CMS",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud",
|
||||
"version": "3.0.0-beta.73",
|
||||
"version": "3.0.0-beta.71",
|
||||
"description": "The official Payload Cloud plugin",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user