Compare commits
1 Commits
richtext-l
...
feat/live-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
207623e521 |
81
.github/workflows/main.yml
vendored
81
.github/workflows/main.yml
vendored
@@ -2,9 +2,9 @@ name: build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize ]
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches: [ 'main' ]
|
||||
branches: ['main']
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
@@ -15,25 +15,25 @@ jobs:
|
||||
needs_build: ${{ steps.filter.outputs.needs_build }}
|
||||
templates: ${{ steps.filter.outputs.templates }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
needs_build:
|
||||
- '.github/workflows/**'
|
||||
- 'packages/**'
|
||||
- 'test/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'package.json'
|
||||
templates:
|
||||
- 'templates/**'
|
||||
- name: Log all filter results
|
||||
run: |
|
||||
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
|
||||
echo "templates: ${{ steps.filter.outputs.templates }}"
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
needs_build:
|
||||
- '.github/workflows/**'
|
||||
- 'packages/**'
|
||||
- 'test/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'package.json'
|
||||
templates:
|
||||
- 'templates/**'
|
||||
- name: Log all filter results
|
||||
run: |
|
||||
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
|
||||
echo "templates: ${{ steps.filter.outputs.templates }}"
|
||||
|
||||
core-build:
|
||||
needs: changes
|
||||
@@ -85,15 +85,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
database: [ mongoose, postgres, postgres-uuid, supabase ]
|
||||
database: [mongoose, postgres]
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: payloadtests
|
||||
AWS_ENDPOINT_URL: http://127.0.0.1:4566
|
||||
AWS_ACCESS_KEY_ID: localstack
|
||||
AWS_SECRET_ACCESS_KEY: localstack
|
||||
AWS_REGION: us-east-1
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
@@ -113,9 +109,6 @@ jobs:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Start LocalStack
|
||||
run: pnpm docker:start
|
||||
|
||||
- name: Start PostgreSQL
|
||||
uses: CasperWA/postgresql-action@v1.2
|
||||
with:
|
||||
@@ -123,35 +116,15 @@ jobs:
|
||||
postgresql db: ${{ env.POSTGRES_DB }}
|
||||
postgresql user: ${{ env.POSTGRES_USER }}
|
||||
postgresql password: ${{ env.POSTGRES_PASSWORD }}
|
||||
if: matrix.database == 'postgres' || matrix.database == 'postgres-uuid'
|
||||
|
||||
- name: Install Supabase CLI
|
||||
uses: supabase/setup-cli@v1
|
||||
with:
|
||||
version: latest
|
||||
if: matrix.database == 'supabase'
|
||||
|
||||
- name: Initialize Supabase
|
||||
run: |
|
||||
supabase init
|
||||
supabase start
|
||||
if: matrix.database == 'supabase'
|
||||
|
||||
- name: Wait for PostgreSQL
|
||||
run: sleep 30
|
||||
if: matrix.database == 'postgres' || matrix.database == 'postgres-uuid'
|
||||
if: matrix.database == 'postgres'
|
||||
|
||||
- run: sleep 30
|
||||
- name: Configure PostgreSQL
|
||||
run: |
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE ROLE runner SUPERUSER LOGIN;"
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "SELECT version();"
|
||||
echo "POSTGRES_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
|
||||
if: matrix.database == 'postgres' || matrix.database == 'postgres-uuid'
|
||||
|
||||
- name: Configure Supabase
|
||||
run: |
|
||||
echo "POSTGRES_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres" >> $GITHUB_ENV
|
||||
if: matrix.database == 'supabase'
|
||||
if: matrix.database == 'postgres'
|
||||
|
||||
- name: Component Tests
|
||||
run: pnpm test:components
|
||||
@@ -169,7 +142,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
part: [ 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8 ]
|
||||
part: [1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8]
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
@@ -317,7 +290,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
template: [ blank, website, ecommerce ]
|
||||
template: [blank, website, ecommerce]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,9 +6,7 @@ dist
|
||||
|
||||
test-results
|
||||
.devcontainer
|
||||
.localstack
|
||||
/migrations
|
||||
.localstack
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
|
||||
2
.idea/runConfigurations/Run_Dev_Fields.xml
generated
2
.idea/runConfigurations/Run_Dev_Fields.xml
generated
@@ -1,5 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run Dev Fields" type="NodeJSConfigurationType" application-parameters="fields" path-to-js-file="node_modules/.pnpm/nodemon@3.0.3/node_modules/nodemon/bin/nodemon.js" working-dir="$PROJECT_DIR$">
|
||||
<configuration default="false" name="Run Dev Fields" type="NodeJSConfigurationType" application-parameters="fields" path-to-js-file="node_modules/.pnpm/nodemon@3.0.1/node_modules/nodemon/bin/nodemon.js" working-dir="$PROJECT_DIR$">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
2
.idea/runConfigurations/Run_Dev__community.xml
generated
2
.idea/runConfigurations/Run_Dev__community.xml
generated
@@ -1,5 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run Dev _community" type="NodeJSConfigurationType" application-parameters="_community" path-to-js-file="node_modules/.pnpm/nodemon@3.0.3/node_modules/nodemon/bin/nodemon.js" working-dir="$PROJECT_DIR$">
|
||||
<configuration default="false" name="Run Dev _community" type="NodeJSConfigurationType" application-parameters="_community" path-to-js-file="node_modules/.pnpm/nodemon@3.0.1/node_modules/nodemon/bin/nodemon.js" working-dir="$PROJECT_DIR$">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
70
CHANGELOG.md
70
CHANGELOG.md
@@ -1,73 +1,3 @@
|
||||
## [2.11.1](https://github.com/payloadcms/payload/compare/v2.11.0...v2.11.1) (2024-02-16)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **richtext-lexical:** Update lexical from 0.12.6 to 0.13.1, port over all useful changes from playground (#5066)
|
||||
|
||||
### Features
|
||||
|
||||
* **db-postgres:** adds idType to use uuid or serial id columns ([#3864](https://github.com/payloadcms/payload/issues/3864)) ([d6c2578](https://github.com/payloadcms/payload/commit/d6c25783cfa97983bf9db27ceb5ccd39a62c62f1))
|
||||
* **db-postgres:** reconnect after disconnection from database ([#5086](https://github.com/payloadcms/payload/issues/5086)) ([bf942fd](https://github.com/payloadcms/payload/commit/bf942fdfa6ea9c26cf05295cc9db646bf31fa622))
|
||||
* **plugin-search:** add req to beforeSync args for transactions ([#5068](https://github.com/payloadcms/payload/issues/5068)) ([98b87e2](https://github.com/payloadcms/payload/commit/98b87e22782c0a788f79326f22be05a6b176ad74))
|
||||
* **richtext-lexical:** add justify aligment to AlignFeature ([#4035](https://github.com/payloadcms/payload/issues/4035)) ([#4868](https://github.com/payloadcms/payload/issues/4868)) ([6d6823c](https://github.com/payloadcms/payload/commit/6d6823c3e5609a58eeeeb8d043945a762f9463df))
|
||||
* **richtext-lexical:** AddBlock handle for all nodes, even if they aren't empty paragraphs ([#5063](https://github.com/payloadcms/payload/issues/5063)) ([00fc034](https://github.com/payloadcms/payload/commit/00fc0343dabf184d5bab418d47c403b3ad11698f))
|
||||
* **richtext-lexical:** Update lexical from 0.12.6 to 0.13.1, port over all useful changes from playground ([#5066](https://github.com/payloadcms/payload/issues/5066)) ([0d18822](https://github.com/payloadcms/payload/commit/0d18822062275c1826c8e2c3da2571a2b3483310))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-mongodb:** find versions pagination ([#5091](https://github.com/payloadcms/payload/issues/5091)) ([5d4022f](https://github.com/payloadcms/payload/commit/5d4022f1445e2809c01cb1dd599280f0a56cdc6e))
|
||||
* **db-postgres:** query using blockType ([#5044](https://github.com/payloadcms/payload/issues/5044)) ([35c2a08](https://github.com/payloadcms/payload/commit/35c2a085efa6d5ad59779960874bc9728a17e3a0))
|
||||
* filterOptions errors cause transaction to abort ([#5079](https://github.com/payloadcms/payload/issues/5079)) ([5f3d016](https://github.com/payloadcms/payload/commit/5f3d0169bee21e1c0963dbd7ede9fe5f1c46a5a5))
|
||||
* **plugin-form-builder:** hooks do not respect transactions ([#5069](https://github.com/payloadcms/payload/issues/5069)) ([82e9d31](https://github.com/payloadcms/payload/commit/82e9d31127c8df83c5bed92a5ffdab76d331900f))
|
||||
* remove collection findByID caching ([#5034](https://github.com/payloadcms/payload/issues/5034)) ([1ac943e](https://github.com/payloadcms/payload/commit/1ac943ed5e8416883b863147fdf3c23380955559))
|
||||
* **richtext-lexical:** do not remove adjacent paragraph node when inserting certain nodes in empty editor ([#5061](https://github.com/payloadcms/payload/issues/5061)) ([6323965](https://github.com/payloadcms/payload/commit/6323965c652ea68dffeb716957b124d165b9ce96))
|
||||
* **uploads:** account for serverURL when retrieving external file ([#5102](https://github.com/payloadcms/payload/issues/5102)) ([25cee8b](https://github.com/payloadcms/payload/commit/25cee8bb102bf80b3a4bfb4b4e46712722cc7f0d))
|
||||
|
||||
## [2.11.0](https://github.com/payloadcms/payload/compare/v2.10.1...v2.11.0) (2024-02-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* exposes collapsible provider with more functionality ([#5043](https://github.com/payloadcms/payload/issues/5043)) ([df39602](https://github.com/payloadcms/payload/commit/df39602758ae8dc3765bb48e51f7a657babfa559))
|
||||
|
||||
## [2.10.1](https://github.com/payloadcms/payload/compare/v2.10.0...v2.10.1) (2024-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* clearable cells handle null values ([#5038](https://github.com/payloadcms/payload/issues/5038)) ([f6d7da7](https://github.com/payloadcms/payload/commit/f6d7da751039df25066b51bb91d6453e1a4efd82))
|
||||
* **db-mongodb:** handle null values with exists ([#5037](https://github.com/payloadcms/payload/issues/5037)) ([cdc4cb9](https://github.com/payloadcms/payload/commit/cdc4cb971b9180ba2ed09741f5af1a3c18292828))
|
||||
* **db-postgres:** handle nested docs with drafts ([#5012](https://github.com/payloadcms/payload/issues/5012)) ([da184d4](https://github.com/payloadcms/payload/commit/da184d40ece74bffb224002eb5df8f6987d65043))
|
||||
* ensures docs with the same id are shown in relationship field select ([#4859](https://github.com/payloadcms/payload/issues/4859)) ([e1813fb](https://github.com/payloadcms/payload/commit/e1813fb884e0dc84203fcbab87527a99a4d3a5d7))
|
||||
* query relationships by explicit id field ([#5022](https://github.com/payloadcms/payload/issues/5022)) ([a0a58e7](https://github.com/payloadcms/payload/commit/a0a58e7fd20dff54d210c968f4d5defd67441bdd))
|
||||
* **richtext-lexical:** make editor reactive to initialValue changes ([#5010](https://github.com/payloadcms/payload/issues/5010)) ([2315781](https://github.com/payloadcms/payload/commit/2315781f1891ddde4b4c5f2f0cfa1c17af85b7a9))
|
||||
|
||||
## [2.10.0](https://github.com/payloadcms/payload/compare/v2.9.0...v2.10.0) (2024-02-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add more options to addFieldStatePromise so that it can be used for field flattening ([#4799](https://github.com/payloadcms/payload/issues/4799)) ([8725d41](https://github.com/payloadcms/payload/commit/8725d411645bb0270376e235669f46be2227ecc0))
|
||||
* extend transactions to cover after and beforeOperation hooks ([#4960](https://github.com/payloadcms/payload/issues/4960)) ([1e8a6b7](https://github.com/payloadcms/payload/commit/1e8a6b7899f7b1e6451cc4d777602208478b483c))
|
||||
* previousValue and previousSiblingDoc args added to beforeChange field hooks ([#4958](https://github.com/payloadcms/payload/issues/4958)) ([5d934ba](https://github.com/payloadcms/payload/commit/5d934ba02d07d98f781ce983228858ee5ce5c226))
|
||||
* re-use existing logger instance passed to payload.init ([#3124](https://github.com/payloadcms/payload/issues/3124)) ([471d211](https://github.com/payloadcms/payload/commit/471d2113a790dc0d54b2f8ed84e6899310efd600))
|
||||
* **richtext-lexical:** Blocks: generate type definitions for blocks fields ([#4529](https://github.com/payloadcms/payload/issues/4529)) ([90d7ee3](https://github.com/payloadcms/payload/commit/90d7ee3e6535d51290fc734b284ff3811dbda1f8))
|
||||
* use deletion success message from server if provided ([#4966](https://github.com/payloadcms/payload/issues/4966)) ([e3c8105](https://github.com/payloadcms/payload/commit/e3c8105cc2ed6fdf8007d97cd7b5556fc71ed724))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** filtering relationships with drafts enabled ([#4998](https://github.com/payloadcms/payload/issues/4998)) ([c3a3942](https://github.com/payloadcms/payload/commit/c3a39429697e9d335e9be199e7caafb82eb26219))
|
||||
* **db-postgres:** handle schema changes with supabase ([#4968](https://github.com/payloadcms/payload/issues/4968)) ([5d3659d](https://github.com/payloadcms/payload/commit/5d3659d48ad8bbf5d96fbcd80434d2287cab97e0))
|
||||
* **db-postgres:** indexes not created for non unique field names ([#4967](https://github.com/payloadcms/payload/issues/4967)) ([64f705c](https://github.com/payloadcms/payload/commit/64f705c3c94148972f67e8175e718015760d6430))
|
||||
* **db-postgres:** indexes not creating for relationships, arrays, hasmany and blocks ([#4976](https://github.com/payloadcms/payload/issues/4976)) ([47106d5](https://github.com/payloadcms/payload/commit/47106d5a1af2ebd073fbbc6e474174c3d3835e5c))
|
||||
* **db-postgres:** localized field sort count ([#4997](https://github.com/payloadcms/payload/issues/4997)) ([f3876c2](https://github.com/payloadcms/payload/commit/f3876c2a39efe19a1864213306725aadcc14f130))
|
||||
* ensures docPermissions fallback to collection permissions on create ([#4969](https://github.com/payloadcms/payload/issues/4969)) ([afa2b94](https://github.com/payloadcms/payload/commit/afa2b942e0aad90c55744ae13e0ffe1cefa4585d))
|
||||
* **migrations:** safely create migration file when no name passed ([#4995](https://github.com/payloadcms/payload/issues/4995)) ([0740d50](https://github.com/payloadcms/payload/commit/0740d5095ee1aef13e4e37f6b174d529f0f2d993))
|
||||
* **plugin-seo:** tabbedUI with email field causes duplicate field ([#4944](https://github.com/payloadcms/payload/issues/4944)) ([db22cbd](https://github.com/payloadcms/payload/commit/db22cbdf21a39ed0604ab96c57ca4242eac82ce7))
|
||||
|
||||
## [2.9.0](https://github.com/payloadcms/payload/compare/v2.8.2...v2.9.0) (2024-01-26)
|
||||
|
||||
|
||||
|
||||
@@ -635,37 +635,6 @@ export const CustomArrayManager = () => {
|
||||
]}
|
||||
/>
|
||||
|
||||
### useCollapsible
|
||||
|
||||
The `useCollapsible` hook allows you to control parent collapsibles:
|
||||
|
||||
| Property | Description |
|
||||
|---------------------------|--------------------------------------------------------------------------------------------------------------------|
|
||||
| **`collapsed`** | State of the collapsible. `true` if open, `false` if collapsed |
|
||||
| **`isVisible`** | If nested, determine if the nearest collapsible is visible. `true` if no parent is closed, `false` otherwise |
|
||||
| **`toggle`** | Toggles the state of the nearest collapsible |
|
||||
| **`withinCollapsible`** | Determine when you are within another collaspible | |
|
||||
|
||||
**Example:**
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
|
||||
import { useCollapsible } from 'payload/components/utilities'
|
||||
|
||||
const CustomComponent: React.FC = () => {
|
||||
const { collapsed, toggle } = useCollapsible()
|
||||
return (
|
||||
<div>
|
||||
<p className="field-type">I am {collapsed ? 'closed' : 'open'}</p>
|
||||
<button onClick={toggle} type="button">
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### useDocumentInfo
|
||||
|
||||
The `useDocumentInfo` hook provides lots of information about the document currently being edited, including the following:
|
||||
@@ -805,8 +774,8 @@ const MyComponent: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<span>The current theme is {theme} and autoMode is {autoMode}</span>
|
||||
<button
|
||||
type="button"
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTheme(prev => prev === "light" ? "dark" : "light")}
|
||||
>
|
||||
Toggle theme
|
||||
|
||||
@@ -46,7 +46,6 @@ export const Page: CollectionConfig = {
|
||||
- [Date](/docs/fields/date) - date / time field that saves a timestamp
|
||||
- [Email](/docs/fields/email) - validates the entry is a properly formatted email
|
||||
- [Group](/docs/fields/group) - nest fields within an object
|
||||
- [JSON](/docs/fields/json) - saves actual JSON in the database
|
||||
- [Number](/docs/fields/number) - field that enforces that its value be a number
|
||||
- [Point](/docs/fields/point) - geometric coordinates for location data
|
||||
- [Radio](/docs/fields/radio) - radio button group, allowing only one value to be selected
|
||||
|
||||
@@ -75,7 +75,6 @@ import { CollectionBeforeOperationHook } from 'payload/types'
|
||||
const beforeOperationHook: CollectionBeforeOperationHook = async ({
|
||||
args, // original arguments passed into the operation
|
||||
operation, // name of the operation
|
||||
req, // full express request
|
||||
}) => {
|
||||
return args // return modified operation arguments as necessary
|
||||
}
|
||||
@@ -210,7 +209,6 @@ import { CollectionAfterOperationHook } from 'payload/types'
|
||||
const afterOperationHook: CollectionAfterOperationHook = async ({
|
||||
args, // arguments passed into the operation
|
||||
operation, // name of the operation
|
||||
req, // full express request
|
||||
result, // the result of the operation, before modifications
|
||||
}) => {
|
||||
return result // return modified result as necessary
|
||||
|
||||
@@ -98,13 +98,6 @@ On boot, a seed script is included to scaffold a basic database for you to use a
|
||||
|
||||
> NOTICE: seeding the database is destructive because it drops your current database to populate a fresh one from the seed template. Only run this command if you are starting a new project or can afford to lose your current data.
|
||||
|
||||
### Conflicting routes
|
||||
|
||||
>In a monorepo when routes are bootstrapped to the same host, they can conflict with Payload's own routes if they have the same name. In our template we've named the Nextjs API routes to `next` to avoid this conflict.
|
||||
>
|
||||
>This can happen with any other routes conflicting with Payload such as `admin` and we recommend using different names for custom routes.
|
||||
>Alternatively you can also rename Payload's own routes via the [configuration](https://payloadcms.com/docs/configuration/overview).
|
||||
|
||||
## Production
|
||||
|
||||
To run Payload in production, you need to build and serve the Admin panel. To do so, follow these steps:
|
||||
|
||||
5
examples/custom-server/src/app/api/test-get/route.ts
Normal file
5
examples/custom-server/src/app/api/test-get/route.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(): Promise<NextResponse> {
|
||||
return NextResponse.json({ success: true })
|
||||
}
|
||||
5
examples/custom-server/src/app/api/test-post/route.ts
Normal file
5
examples/custom-server/src/app/api/test-post/route.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function POST(): Promise<NextResponse> {
|
||||
return NextResponse.json({ success: true })
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
/**
|
||||
* The Next.js API routes can conflict with Payload's own routes if they share the same path
|
||||
* To avoid this you can customise the path of Payload or the API route of Nextjs as we've done here
|
||||
* See readme: https://github.com/payloadcms/payload/tree/main/examples/custom-server#conflicting-routes
|
||||
* */
|
||||
export async function GET(): Promise<NextResponse> {
|
||||
return NextResponse.json({ success: true })
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
/**
|
||||
* The Next.js API routes can conflict with Payload's own routes if they share the same path
|
||||
* To avoid this you can customise the path of Payload or the API route of Nextjs as we've done here
|
||||
* See readme: https://github.com/payloadcms/payload/tree/main/examples/custom-server#conflicting-routes
|
||||
* */
|
||||
export async function POST(): Promise<NextResponse> {
|
||||
return NextResponse.json({ success: true })
|
||||
}
|
||||
@@ -2,27 +2,28 @@ import type { AfterLoginHook } from 'payload/dist/collections/config/types'
|
||||
|
||||
export const recordLastLoggedInTenant: AfterLoginHook = async ({ req, user }) => {
|
||||
try {
|
||||
const relatedOrg = await req.payload
|
||||
.find({
|
||||
collection: 'tenants',
|
||||
where: {
|
||||
'domains.domain': {
|
||||
in: [req.headers.host],
|
||||
},
|
||||
const relatedOrg = await req.payload.find({
|
||||
collection: 'tenants',
|
||||
where: {
|
||||
'domains.domain': {
|
||||
in: [req.headers.host],
|
||||
},
|
||||
depth: 0,
|
||||
limit: 1,
|
||||
})
|
||||
?.then(res => res.docs?.[0])
|
||||
|
||||
await req.payload.update({
|
||||
id: user.id,
|
||||
collection: 'users',
|
||||
data: {
|
||||
lastLoggedInTenant: relatedOrg?.id || null,
|
||||
},
|
||||
depth: 0,
|
||||
limit: 1,
|
||||
req,
|
||||
})
|
||||
|
||||
if (relatedOrg.docs.length > 0) {
|
||||
await req.payload.update({
|
||||
id: user.id,
|
||||
collection: 'users',
|
||||
data: {
|
||||
lastLoggedInTenant: relatedOrg.docs[0].id,
|
||||
},
|
||||
req,
|
||||
})
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
req.payload.logger.error(`Error recording last logged in tenant for user ${user.id}: ${err}`)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
24
package.json
24
package.json
@@ -15,13 +15,9 @@
|
||||
"dev:generate-graphql-schema": "ts-node -T ./test/generateGraphQLSchema.ts",
|
||||
"dev:generate-types": "ts-node -T ./test/generateTypes.ts",
|
||||
"dev:postgres": "pnpm --filter payload run dev:postgres",
|
||||
"docker:restart": "pnpm docker:stop --remove-orphans && pnpm docker:start",
|
||||
"docker:start": "docker-compose -f packages/plugin-cloud-storage/docker-compose.yml up -d",
|
||||
"docker:stop": "docker-compose -f packages/plugin-cloud-storage/docker-compose.yml down",
|
||||
"fix": "eslint \"packages/**/*.ts\" --fix",
|
||||
"lint": "eslint \"packages/**/*.ts\"",
|
||||
"lint-staged": "lint-staged",
|
||||
"prepare": "husky install",
|
||||
"pretest": "pnpm build",
|
||||
"reinstall": "pnpm clean:unix && pnpm install",
|
||||
"script:list-packages": "tsx ./scripts/list-packages.ts",
|
||||
@@ -33,10 +29,10 @@
|
||||
"test:e2e:headed": "cross-env DISABLE_LOGGING=true playwright test --headed",
|
||||
"test:int:postgres": "cross-env PAYLOAD_DATABASE=postgres DISABLE_LOGGING=true jest --forceExit --detectOpenHandles",
|
||||
"test:int": "cross-env DISABLE_LOGGING=true jest --forceExit --detectOpenHandles",
|
||||
"translateNewKeys": "pnpm --filter payload run translateNewKeys"
|
||||
"translateNewKeys": "pnpm --filter payload run translateNewKeys",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-s3": "^3.142.0",
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@playwright/test": "1.40.1",
|
||||
"@swc/cli": "^0.1.62",
|
||||
@@ -81,12 +77,12 @@
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"jwt-decode": "3.1.2",
|
||||
"lexical": "0.13.1",
|
||||
"lexical": "0.12.5",
|
||||
"lint-staged": "^14.0.1",
|
||||
"minimist": "1.2.8",
|
||||
"mongodb-memory-server": "^9",
|
||||
"node-fetch": "2.6.12",
|
||||
"nodemon": "3.0.3",
|
||||
"nodemon": "3.0.2",
|
||||
"prettier": "^3.0.3",
|
||||
"prompts": "2.4.2",
|
||||
"qs": "6.11.2",
|
||||
@@ -110,12 +106,12 @@
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"copyfiles": "$copyfiles",
|
||||
"cross-env": "$cross-env",
|
||||
"dotenv": "$dotenv",
|
||||
"drizzle-orm": "$drizzle-orm",
|
||||
"ts-node": "$ts-node",
|
||||
"typescript": "$typescript"
|
||||
"copyfiles": "2.4.1",
|
||||
"cross-env": "7.0.3",
|
||||
"dotenv": "8.6.0",
|
||||
"drizzle-orm": "0.29.3",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.2.2"
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "1.4.2",
|
||||
"version": "1.4.0",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -32,7 +32,7 @@ export const createMigration: CreateMigration = async function createMigration({
|
||||
|
||||
// Check for predefined migration.
|
||||
// Either passed in via --file or prefixed with @payloadcms/db-mongodb/
|
||||
if (file || migrationName?.startsWith('@payloadcms/db-mongodb/')) {
|
||||
if (file || migrationName.startsWith('@payloadcms/db-mongodb/')) {
|
||||
if (!file) file = migrationName
|
||||
|
||||
const predefinedMigrationName = file.replace('@payloadcms/db-mongodb/', '')
|
||||
@@ -59,8 +59,8 @@ export const createMigration: CreateMigration = async function createMigration({
|
||||
|
||||
const timestamp = `${formattedDate}_${formattedTime}`
|
||||
|
||||
const formattedName = migrationName?.replace(/\W/g, '_')
|
||||
const fileName = migrationName ? `${timestamp}_${formattedName}.ts` : `${timestamp}_migration.ts`
|
||||
const formattedName = migrationName.replace(/\W/g, '_')
|
||||
const fileName = `${timestamp}_${formattedName}.ts`
|
||||
const filePath = `${dir}/${fileName}`
|
||||
fs.writeFileSync(filePath, migrationFileContent)
|
||||
payload.logger.info({ msg: `Migration created at ${filePath}` })
|
||||
|
||||
@@ -63,6 +63,7 @@ export const findVersions: FindVersions = async function findVersions(
|
||||
lean: true,
|
||||
leanWithId: true,
|
||||
limit,
|
||||
offset: skip || 0,
|
||||
options,
|
||||
page,
|
||||
pagination,
|
||||
|
||||
@@ -157,23 +157,6 @@ export const sanitizeQueryValue = ({
|
||||
|
||||
if (operator === 'exists') {
|
||||
formattedValue = formattedValue === 'true' || formattedValue === true
|
||||
|
||||
// Clearable fields
|
||||
if (['relationship', 'select', 'upload'].includes(field.type)) {
|
||||
if (formattedValue) {
|
||||
return {
|
||||
rawQuery: {
|
||||
$and: [{ [path]: { $exists: true } }, { [path]: { $ne: null } }],
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
rawQuery: {
|
||||
$or: [{ [path]: { $exists: false } }, { [path]: { $eq: null } }],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { operator: formattedOperator, val: formattedValue }
|
||||
|
||||
@@ -17,11 +17,7 @@ export const rollbackTransaction: RollbackTransaction = async function rollbackT
|
||||
}
|
||||
|
||||
// the first call for rollback should be aborted and deleted causing any other operations with the same transaction to fail
|
||||
try {
|
||||
await this.sessions[id].abortTransaction()
|
||||
await this.sessions[id].endSession()
|
||||
} catch (error) {
|
||||
// ignore the error as it is likely a race condition from multiple errors
|
||||
}
|
||||
await this.sessions[id].abortTransaction()
|
||||
await this.sessions[id].endSession()
|
||||
delete this.sessions[id]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "0.6.0",
|
||||
"version": "0.5.0",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { Payload } from 'payload'
|
||||
import type { Connect } from 'payload/database'
|
||||
|
||||
import { eq, sql } from 'drizzle-orm'
|
||||
@@ -9,43 +8,6 @@ import prompts from 'prompts'
|
||||
|
||||
import type { PostgresAdapter } from './types'
|
||||
|
||||
const connectWithReconnect = async function ({
|
||||
adapter,
|
||||
payload,
|
||||
reconnect = false,
|
||||
}: {
|
||||
adapter: PostgresAdapter
|
||||
payload: Payload
|
||||
reconnect?: boolean
|
||||
}) {
|
||||
let result
|
||||
|
||||
if (!reconnect) {
|
||||
result = await adapter.pool.connect()
|
||||
} else {
|
||||
try {
|
||||
result = await adapter.pool.connect()
|
||||
} catch (err) {
|
||||
setTimeout(() => {
|
||||
payload.logger.info('Reconnecting to postgres')
|
||||
void connectWithReconnect({ adapter, payload, reconnect: true })
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
return
|
||||
}
|
||||
result.prependListener('error', (err) => {
|
||||
try {
|
||||
if (err.code === 'ECONNRESET') {
|
||||
void connectWithReconnect({ adapter, payload, reconnect: true })
|
||||
}
|
||||
} catch (err) {
|
||||
// swallow error
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const connect: Connect = async function connect(this: PostgresAdapter, payload) {
|
||||
this.schema = {
|
||||
...this.tables,
|
||||
@@ -55,11 +17,10 @@ export const connect: Connect = async function connect(this: PostgresAdapter, pa
|
||||
|
||||
try {
|
||||
this.pool = new Pool(this.poolOptions)
|
||||
await connectWithReconnect({ adapter: this, payload })
|
||||
|
||||
await this.pool.connect()
|
||||
const logger = this.logger || false
|
||||
|
||||
this.drizzle = drizzle(this.pool, { logger, schema: this.schema })
|
||||
this.drizzle = drizzle(this.pool, { schema: this.schema, logger })
|
||||
if (process.env.PAYLOAD_DROP_DATABASE === 'true') {
|
||||
this.payload.logger.info('---- DROPPING TABLES ----')
|
||||
await this.drizzle.execute(sql`drop schema public cascade;
|
||||
|
||||
@@ -158,7 +158,7 @@ export const findMany = async function find({
|
||||
query: db
|
||||
.select({
|
||||
count: sql<number>`count
|
||||
(DISTINCT ${adapter.tables[tableName].id})`,
|
||||
(*)`,
|
||||
})
|
||||
.from(table)
|
||||
.where(where),
|
||||
|
||||
@@ -42,7 +42,7 @@ export type { MigrateDownArgs, MigrateUpArgs } from './types'
|
||||
export function postgresAdapter(args: Args): PostgresAdapterResult {
|
||||
function adapter({ payload }: { payload: Payload }) {
|
||||
const migrationDir = findMigrationDir(args.migrationDir)
|
||||
const idType = args.idType || 'serial'
|
||||
|
||||
return createDatabaseAdapter<PostgresAdapter>({
|
||||
name: 'postgres',
|
||||
|
||||
@@ -50,7 +50,6 @@ export function postgresAdapter(args: Args): PostgresAdapterResult {
|
||||
drizzle: undefined,
|
||||
enums: {},
|
||||
fieldConstraints: {},
|
||||
idType,
|
||||
logger: args.logger,
|
||||
pool: undefined,
|
||||
poolOptions: args.pool,
|
||||
@@ -69,10 +68,7 @@ export function postgresAdapter(args: Args): PostgresAdapterResult {
|
||||
createGlobalVersion,
|
||||
createMigration,
|
||||
createVersion,
|
||||
/**
|
||||
* This represents how a default ID is treated in Payload as were a field type
|
||||
*/
|
||||
defaultIDType: idType === 'serial' ? 'number' : 'text',
|
||||
defaultIDType: 'number',
|
||||
deleteMany,
|
||||
deleteOne,
|
||||
deleteVersions,
|
||||
|
||||
@@ -9,6 +9,7 @@ import toSnakeCase from 'to-snake-case'
|
||||
import type { PostgresAdapter } from './types'
|
||||
|
||||
import { buildTable } from './schema/build'
|
||||
import { getConfigIDType } from './schema/getConfigIDType'
|
||||
|
||||
export const init: Init = async function init(this: PostgresAdapter) {
|
||||
if (this.payload.config.localization) {
|
||||
@@ -23,9 +24,9 @@ export const init: Init = async function init(this: PostgresAdapter) {
|
||||
|
||||
buildTable({
|
||||
adapter: this,
|
||||
buildTexts: true,
|
||||
buildNumbers: true,
|
||||
buildRelationships: true,
|
||||
buildTexts: true,
|
||||
disableNotNull: !!collection?.versions?.drafts,
|
||||
disableUnique: false,
|
||||
fields: collection.fields,
|
||||
@@ -37,11 +38,13 @@ export const init: Init = async function init(this: PostgresAdapter) {
|
||||
const versionsTableName = `_${tableName}_v`
|
||||
const versionFields = buildVersionCollectionFields(collection)
|
||||
|
||||
const versionsParentIDColType = getConfigIDType(collection.fields)
|
||||
|
||||
buildTable({
|
||||
adapter: this,
|
||||
buildTexts: true,
|
||||
buildNumbers: true,
|
||||
buildRelationships: true,
|
||||
buildTexts: true,
|
||||
disableNotNull: !!collection.versions?.drafts,
|
||||
disableUnique: true,
|
||||
fields: versionFields,
|
||||
@@ -56,9 +59,9 @@ export const init: Init = async function init(this: PostgresAdapter) {
|
||||
|
||||
buildTable({
|
||||
adapter: this,
|
||||
buildTexts: true,
|
||||
buildNumbers: true,
|
||||
buildRelationships: true,
|
||||
buildTexts: true,
|
||||
disableNotNull: !!global?.versions?.drafts,
|
||||
disableUnique: false,
|
||||
fields: global.fields,
|
||||
@@ -72,9 +75,9 @@ export const init: Init = async function init(this: PostgresAdapter) {
|
||||
|
||||
buildTable({
|
||||
adapter: this,
|
||||
buildTexts: true,
|
||||
buildNumbers: true,
|
||||
buildRelationships: true,
|
||||
buildTexts: true,
|
||||
disableNotNull: !!global.versions?.drafts,
|
||||
disableUnique: true,
|
||||
fields: versionFields,
|
||||
|
||||
@@ -75,7 +75,6 @@ const buildQuery = async function buildQuery({
|
||||
pathSegments: sortPath.replace(/__/g, '.').split('.'),
|
||||
selectFields,
|
||||
tableName,
|
||||
value: sortPath,
|
||||
})
|
||||
orderBy.column = sortTable?.[sortTableColumnName]
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { SQL } from 'drizzle-orm'
|
||||
import type { Field, FieldAffectingData, NumberField, TabAsField, TextField } from 'payload/types'
|
||||
import type { Field, FieldAffectingData, TabAsField } from 'payload/types'
|
||||
|
||||
import { and, eq, like, sql } from 'drizzle-orm'
|
||||
import { alias } from 'drizzle-orm/pg-core'
|
||||
@@ -44,14 +44,6 @@ type Args = {
|
||||
rootTableName?: string
|
||||
selectFields: Record<string, GenericColumn>
|
||||
tableName: string
|
||||
/**
|
||||
* If creating a new table name for arrays and blocks, this suffix should be appended to the table name
|
||||
*/
|
||||
tableNameSuffix?: string
|
||||
/**
|
||||
* The raw value of the query before sanitization
|
||||
*/
|
||||
value: unknown
|
||||
}
|
||||
/**
|
||||
* Transforms path to table and column name
|
||||
@@ -73,8 +65,6 @@ export const getTableColumnFromPath = ({
|
||||
rootTableName: incomingRootTableName,
|
||||
selectFields,
|
||||
tableName,
|
||||
tableNameSuffix = '',
|
||||
value,
|
||||
}: Args): TableColumn => {
|
||||
const fieldPath = incomingSegments[0]
|
||||
let locale = incomingLocale
|
||||
@@ -93,8 +83,8 @@ export const getTableColumnFromPath = ({
|
||||
constraints,
|
||||
field: {
|
||||
name: 'id',
|
||||
type: adapter.idType === 'uuid' ? 'text' : 'number',
|
||||
} as TextField | NumberField,
|
||||
type: 'number',
|
||||
},
|
||||
table: adapter.tables[newTableName],
|
||||
}
|
||||
}
|
||||
@@ -135,8 +125,6 @@ export const getTableColumnFromPath = ({
|
||||
rootTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
tableNameSuffix,
|
||||
value,
|
||||
})
|
||||
}
|
||||
case 'tab': {
|
||||
@@ -146,7 +134,7 @@ export const getTableColumnFromPath = ({
|
||||
aliasTable,
|
||||
collectionPath,
|
||||
columnPrefix: `${columnPrefix}${field.name}_`,
|
||||
constraintPath: `${constraintPath}${field.name}.`,
|
||||
constraintPath,
|
||||
constraints,
|
||||
fields: field.fields,
|
||||
joinAliases,
|
||||
@@ -156,8 +144,6 @@ export const getTableColumnFromPath = ({
|
||||
rootTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`,
|
||||
value,
|
||||
})
|
||||
}
|
||||
return getTableColumnFromPath({
|
||||
@@ -175,8 +161,6 @@ export const getTableColumnFromPath = ({
|
||||
rootTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
tableNameSuffix,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -201,7 +185,7 @@ export const getTableColumnFromPath = ({
|
||||
aliasTable,
|
||||
collectionPath,
|
||||
columnPrefix: `${columnPrefix}${field.name}_`,
|
||||
constraintPath: `${constraintPath}${field.name}.`,
|
||||
constraintPath,
|
||||
constraints,
|
||||
fields: field.fields,
|
||||
joinAliases,
|
||||
@@ -211,13 +195,11 @@ export const getTableColumnFromPath = ({
|
||||
rootTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
case 'array': {
|
||||
newTableName = `${tableName}_${tableNameSuffix}${toSnakeCase(field.name)}`
|
||||
newTableName = `${tableName}_${toSnakeCase(field.name)}`
|
||||
constraintPath = `${constraintPath}${field.name}.%.`
|
||||
if (locale && field.localized && adapter.payload.config.localization) {
|
||||
joins[newTableName] = and(
|
||||
@@ -250,39 +232,12 @@ export const getTableColumnFromPath = ({
|
||||
rootTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
case 'blocks': {
|
||||
let blockTableColumn: TableColumn
|
||||
let newTableName: string
|
||||
|
||||
// handle blockType queries
|
||||
if (pathSegments[1] === 'blockType') {
|
||||
// find the block config using the value
|
||||
const blockTypes = Array.isArray(value) ? value : [value]
|
||||
blockTypes.forEach((blockType) => {
|
||||
const block = field.blocks.find((block) => block.slug === blockType)
|
||||
newTableName = `${tableName}_blocks_${toSnakeCase(block.slug)}`
|
||||
joins[newTableName] = eq(
|
||||
adapter.tables[tableName].id,
|
||||
adapter.tables[newTableName]._parentID,
|
||||
)
|
||||
constraints.push({
|
||||
columnName: '_path',
|
||||
table: adapter.tables[newTableName],
|
||||
value: pathSegments[0],
|
||||
})
|
||||
})
|
||||
return {
|
||||
constraints,
|
||||
field,
|
||||
getNotNullColumnByValue: () => 'id',
|
||||
table: adapter.tables[tableName],
|
||||
}
|
||||
}
|
||||
|
||||
const hasBlockField = field.blocks.some((block) => {
|
||||
newTableName = `${tableName}_blocks_${toSnakeCase(block.slug)}`
|
||||
constraintPath = `${constraintPath}${field.name}.%.`
|
||||
@@ -303,7 +258,6 @@ export const getTableColumnFromPath = ({
|
||||
rootTableName,
|
||||
selectFields: blockSelectFields,
|
||||
tableName: newTableName,
|
||||
value,
|
||||
})
|
||||
} catch (error) {
|
||||
// this is fine, not every block will have the field
|
||||
@@ -344,6 +298,9 @@ export const getTableColumnFromPath = ({
|
||||
table: blockTableColumn.table,
|
||||
}
|
||||
}
|
||||
if (pathSegments[1] === 'blockType') {
|
||||
throw new APIError('Querying on blockType is not supported')
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
@@ -383,7 +340,7 @@ export const getTableColumnFromPath = ({
|
||||
table: newAliasTable,
|
||||
})
|
||||
|
||||
if (newCollectionPath === '' || newCollectionPath === 'id') {
|
||||
if (newCollectionPath === '') {
|
||||
return {
|
||||
columnName: `${field.relationTo}ID`,
|
||||
constraints,
|
||||
@@ -431,7 +388,6 @@ export const getTableColumnFromPath = ({
|
||||
rootTableName: newTableName,
|
||||
selectFields,
|
||||
tableName: newTableName,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,11 @@ export async function parseParams({
|
||||
where: condition,
|
||||
})
|
||||
if (builtConditions.length > 0) {
|
||||
result = operatorMap[conditionOperator](...builtConditions)
|
||||
if (result) {
|
||||
result = operatorMap[conditionOperator](result, ...builtConditions)
|
||||
} else {
|
||||
result = operatorMap[conditionOperator](...builtConditions)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// It's a path - and there can be multiple comparisons on a single path.
|
||||
@@ -73,7 +77,6 @@ export async function parseParams({
|
||||
if (typeof pathOperators === 'object') {
|
||||
for (const operator of Object.keys(pathOperators)) {
|
||||
if (validOperators.includes(operator as Operator)) {
|
||||
const val = where[relationOrPath][operator]
|
||||
const {
|
||||
columnName,
|
||||
constraints: queryConstraints,
|
||||
@@ -92,9 +95,10 @@ export async function parseParams({
|
||||
pathSegments: relationOrPath.replace(/__/g, '.').split('.'),
|
||||
selectFields,
|
||||
tableName,
|
||||
value: val,
|
||||
})
|
||||
|
||||
const val = where[relationOrPath][operator]
|
||||
|
||||
queryConstraints.forEach(({ columnName: col, table: constraintTable, value }) => {
|
||||
if (typeof value === 'string' && value.indexOf('%') > -1) {
|
||||
constraints.push(operatorMap.like(constraintTable[col], value))
|
||||
@@ -165,7 +169,6 @@ export async function parseParams({
|
||||
}
|
||||
|
||||
const sanitizedQueryValue = sanitizeQueryValue({
|
||||
adapter,
|
||||
field,
|
||||
operator,
|
||||
relationOrPath,
|
||||
|
||||
@@ -2,10 +2,7 @@ import { APIError } from 'payload/errors'
|
||||
import { type Field, type TabAsField, fieldAffectsData } from 'payload/types'
|
||||
import { createArrayFromCommaDelineated } from 'payload/utilities'
|
||||
|
||||
import type { PostgresAdapter } from '../types'
|
||||
|
||||
type SanitizeQueryValueArgs = {
|
||||
adapter: PostgresAdapter
|
||||
field: Field | TabAsField
|
||||
operator: string
|
||||
relationOrPath: string
|
||||
@@ -13,7 +10,6 @@ type SanitizeQueryValueArgs = {
|
||||
}
|
||||
|
||||
export const sanitizeQueryValue = ({
|
||||
adapter,
|
||||
field,
|
||||
operator: operatorArg,
|
||||
relationOrPath,
|
||||
@@ -31,10 +27,8 @@ export const sanitizeQueryValue = ({
|
||||
) {
|
||||
const allPossibleIDTypes: (number | string)[] = []
|
||||
formattedValue.forEach((val) => {
|
||||
if (adapter.idType !== 'uuid' && typeof val === 'string') {
|
||||
if (typeof val === 'string') {
|
||||
allPossibleIDTypes.push(val, parseInt(val))
|
||||
} else if (typeof val === 'string') {
|
||||
allPossibleIDTypes.push(val)
|
||||
} else {
|
||||
allPossibleIDTypes.push(val, String(val))
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ import {
|
||||
import { fieldAffectsData } from 'payload/types'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { GenericColumns, GenericTable, IDType, PostgresAdapter } from '../types'
|
||||
import type { GenericColumns, GenericTable, PostgresAdapter } from '../types'
|
||||
|
||||
import { getConfigIDType } from './getConfigIDType'
|
||||
import { parentIDColumnMap } from './parentIDColumnMap'
|
||||
import { setColumnID } from './setColumnID'
|
||||
import { traverseFields } from './traverseFields'
|
||||
|
||||
type Args = {
|
||||
@@ -89,8 +89,15 @@ export const buildTable = ({
|
||||
// Drizzle relations
|
||||
const relationsToBuild: Map<string, string> = new Map()
|
||||
|
||||
const idColType: IDType = setColumnID({ adapter, columns, fields })
|
||||
const idColType = getConfigIDType(fields)
|
||||
|
||||
const idColTypeMap = {
|
||||
integer: serial,
|
||||
numeric,
|
||||
varchar,
|
||||
}
|
||||
|
||||
columns.id = idColTypeMap[idColType]('id').primaryKey()
|
||||
;({
|
||||
hasLocalizedField,
|
||||
hasLocalizedManyNumberField,
|
||||
@@ -293,7 +300,7 @@ export const buildTable = ({
|
||||
|
||||
relationships.forEach((relationTo) => {
|
||||
const formattedRelationTo = toSnakeCase(relationTo)
|
||||
let colType = adapter.idType === 'uuid' ? 'uuid' : 'integer'
|
||||
let colType = 'integer'
|
||||
const relatedCollectionCustomID = adapter.payload.collections[
|
||||
relationTo
|
||||
].config.fields.find((field) => fieldAffectsData(field) && field.name === 'id')
|
||||
|
||||
17
packages/db-postgres/src/schema/getConfigIDType.ts
Normal file
17
packages/db-postgres/src/schema/getConfigIDType.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { type Field, fieldAffectsData } from 'payload/types'
|
||||
|
||||
export const getConfigIDType = (fields: Field[]): string => {
|
||||
const idField = fields.find((field) => fieldAffectsData(field) && field.name === 'id')
|
||||
|
||||
if (idField) {
|
||||
if (idField.type === 'number') {
|
||||
return 'numeric'
|
||||
}
|
||||
|
||||
if (idField.type === 'text') {
|
||||
return 'varchar'
|
||||
}
|
||||
}
|
||||
|
||||
return 'integer'
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
import { integer, numeric, uuid, varchar } from 'drizzle-orm/pg-core'
|
||||
import { integer, numeric, varchar } from 'drizzle-orm/pg-core'
|
||||
|
||||
import type { IDType } from '../types'
|
||||
|
||||
export const parentIDColumnMap: Record<
|
||||
IDType,
|
||||
typeof integer<string> | typeof numeric<string> | typeof uuid<string> | typeof varchar
|
||||
> = {
|
||||
export const parentIDColumnMap = {
|
||||
integer,
|
||||
numeric,
|
||||
uuid,
|
||||
varchar,
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import type { PgColumnBuilder } from 'drizzle-orm/pg-core'
|
||||
|
||||
import { numeric, serial, uuid, varchar } from 'drizzle-orm/pg-core'
|
||||
import { type Field, fieldAffectsData } from 'payload/types'
|
||||
import { flattenTopLevelFields } from 'payload/utilities'
|
||||
|
||||
import type { IDType, PostgresAdapter } from '../types'
|
||||
|
||||
type Args = { adapter: PostgresAdapter; columns: Record<string, PgColumnBuilder>; fields: Field[] }
|
||||
export const setColumnID = ({ adapter, columns, fields }: Args): IDType => {
|
||||
const idField = flattenTopLevelFields(fields).find(
|
||||
(field) => fieldAffectsData(field) && field.name === 'id',
|
||||
)
|
||||
if (idField) {
|
||||
if (idField.type === 'number') {
|
||||
columns.id = numeric('id').primaryKey()
|
||||
return 'numeric'
|
||||
}
|
||||
|
||||
if (idField.type === 'text') {
|
||||
columns.id = varchar('id').primaryKey()
|
||||
return 'varchar'
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter.idType === 'uuid') {
|
||||
columns.id = uuid('id').defaultRandom().primaryKey()
|
||||
return 'uuid'
|
||||
}
|
||||
|
||||
columns.id = serial('id').primaryKey()
|
||||
return 'integer'
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import type { Field, TabAsField } from 'payload/types'
|
||||
import { relations } from 'drizzle-orm'
|
||||
import {
|
||||
PgNumericBuilder,
|
||||
PgUUIDBuilder,
|
||||
PgVarcharBuilder,
|
||||
boolean,
|
||||
index,
|
||||
@@ -22,7 +21,7 @@ import { InvalidConfiguration } from 'payload/errors'
|
||||
import { fieldAffectsData, optionIsObject } from 'payload/types'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { GenericColumns, IDType, PostgresAdapter } from '../types'
|
||||
import type { GenericColumns, PostgresAdapter } from '../types'
|
||||
|
||||
import { hasLocalesTable } from '../utilities/hasLocalesTable'
|
||||
import { buildTable } from './build'
|
||||
@@ -94,8 +93,7 @@ export const traverseFields = ({
|
||||
let hasManyNumberField: 'index' | boolean = false
|
||||
let hasLocalizedManyNumberField = false
|
||||
|
||||
let parentIDColType: IDType = 'integer'
|
||||
if (columns.id instanceof PgUUIDBuilder) parentIDColType = 'uuid'
|
||||
let parentIDColType = 'integer'
|
||||
if (columns.id instanceof PgNumericBuilder) parentIDColType = 'numeric'
|
||||
if (columns.id instanceof PgVarcharBuilder) parentIDColType = 'varchar'
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import type { Pool, PoolConfig } from 'pg'
|
||||
export type DrizzleDB = NodePgDatabase<Record<string, unknown>>
|
||||
|
||||
export type Args = {
|
||||
idType?: 'serial' | 'uuid'
|
||||
logger?: DrizzleConfig['logger']
|
||||
migrationDir?: string
|
||||
pool: PoolConfig
|
||||
@@ -57,7 +56,6 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
|
||||
* Used for returning properly formed errors from unique fields
|
||||
*/
|
||||
fieldConstraints: Record<string, Record<string, string>>
|
||||
idType: Args['idType']
|
||||
logger: DrizzleConfig['logger']
|
||||
pool: Pool
|
||||
poolOptions: Args['pool']
|
||||
@@ -74,8 +72,6 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
|
||||
tables: Record<string, GenericTable>
|
||||
}
|
||||
|
||||
export type IDType = 'integer' | 'numeric' | 'uuid' | 'varchar'
|
||||
|
||||
export type PostgresAdapterResult = (args: { payload: Payload }) => PostgresAdapter
|
||||
|
||||
export type MigrateUpArgs = { payload: Payload; req?: Partial<PayloadRequest> }
|
||||
|
||||
@@ -36,7 +36,7 @@ export const insertArrays = async ({ adapter, arrays, db, parentRows }: Args): P
|
||||
}
|
||||
}
|
||||
|
||||
const parentID = parentRows[parentRowIndex].id
|
||||
const parentID = parentRows[parentRowIndex].id || parentRows[parentRowIndex]._parentID
|
||||
|
||||
// Add any sub arrays that need to be created
|
||||
// We will call this recursively below
|
||||
@@ -61,10 +61,8 @@ export const insertArrays = async ({ adapter, arrays, db, parentRows }: Args): P
|
||||
// Insert all corresponding arrays
|
||||
// (one insert per array table)
|
||||
for (const [tableName, row] of Object.entries(rowsByTable)) {
|
||||
// the nested arrays need the ID for the parentID foreign key
|
||||
let insertedRows: Args['parentRows']
|
||||
if (row.rows.length > 0) {
|
||||
insertedRows = await db.insert(adapter.tables[tableName]).values(row.rows).returning()
|
||||
await db.insert(adapter.tables[tableName]).values(row.rows).returning()
|
||||
}
|
||||
|
||||
// Insert locale rows
|
||||
@@ -78,7 +76,7 @@ export const insertArrays = async ({ adapter, arrays, db, parentRows }: Args): P
|
||||
adapter,
|
||||
arrays: row.arrays,
|
||||
db,
|
||||
parentRows: insertedRows,
|
||||
parentRows: row.rows,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
10
packages/live-preview-vue/.eslintignore
Normal file
10
packages/live-preview-vue/.eslintignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
37
packages/live-preview-vue/.eslintrc.js
Normal file
37
packages/live-preview-vue/.eslintrc.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/** @type {import('prettier').Config} */
|
||||
module.exports = {
|
||||
extends: ['@payloadcms'],
|
||||
overrides: [
|
||||
{
|
||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
|
||||
},
|
||||
{
|
||||
files: ['package.json', 'tsconfig.json'],
|
||||
rules: {
|
||||
'perfectionist/sort-array-includes': 'off',
|
||||
'perfectionist/sort-astro-attributes': 'off',
|
||||
'perfectionist/sort-classes': 'off',
|
||||
'perfectionist/sort-enums': 'off',
|
||||
'perfectionist/sort-exports': 'off',
|
||||
'perfectionist/sort-imports': 'off',
|
||||
'perfectionist/sort-interfaces': 'off',
|
||||
'perfectionist/sort-jsx-props': 'off',
|
||||
'perfectionist/sort-keys': 'off',
|
||||
'perfectionist/sort-maps': 'off',
|
||||
'perfectionist/sort-named-exports': 'off',
|
||||
'perfectionist/sort-named-imports': 'off',
|
||||
'perfectionist/sort-object-types': 'off',
|
||||
'perfectionist/sort-objects': 'off',
|
||||
'perfectionist/sort-svelte-attributes': 'off',
|
||||
'perfectionist/sort-union-types': 'off',
|
||||
'perfectionist/sort-vue-attributes': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
root: true,
|
||||
}
|
||||
10
packages/live-preview-vue/.prettierignore
Normal file
10
packages/live-preview-vue/.prettierignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
15
packages/live-preview-vue/.swcrc
Normal file
15
packages/live-preview-vue/.swcrc
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "commonjs"
|
||||
}
|
||||
}
|
||||
42
packages/live-preview-vue/package.json
Normal file
42
packages/live-preview-vue/package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-vue",
|
||||
"version": "0.2.0",
|
||||
"description": "The official live preview Vue SDK for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"author": "Payload CMS, Inc.",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "pnpm copyfiles && pnpm build:swc && pnpm build:types",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
|
||||
"prepublishOnly": "pnpm clean && pnpm build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/live-preview": "workspace:^0.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"vue": "^3.0.0",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": null,
|
||||
"main": "./dist/index.js",
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
51
packages/live-preview-vue/src/index.ts
Normal file
51
packages/live-preview-vue/src/index.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
// TODO: replace React with Vue
|
||||
|
||||
import { ready, subscribe, unsubscribe } from '@payloadcms/live-preview'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
export const useLivePreview = <T extends any>(props: {
|
||||
apiRoute?: string
|
||||
depth?: number
|
||||
initialData: T
|
||||
serverURL: string
|
||||
}): {
|
||||
data: T
|
||||
isLoading: boolean
|
||||
} => {
|
||||
const { apiRoute, depth, initialData, serverURL } = props
|
||||
const [data, setData] = useState<T>(initialData)
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true)
|
||||
const hasSentReadyMessage = useRef<boolean>(false)
|
||||
|
||||
const onChange = useCallback((mergedData) => {
|
||||
setData(mergedData)
|
||||
setIsLoading(false)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = subscribe({
|
||||
apiRoute,
|
||||
callback: onChange,
|
||||
depth,
|
||||
initialData,
|
||||
serverURL,
|
||||
})
|
||||
|
||||
if (!hasSentReadyMessage.current) {
|
||||
hasSentReadyMessage.current = true
|
||||
|
||||
ready({
|
||||
serverURL,
|
||||
})
|
||||
}
|
||||
|
||||
return () => {
|
||||
unsubscribe(subscription)
|
||||
}
|
||||
}, [serverURL, onChange, depth, initialData, apiRoute])
|
||||
|
||||
return {
|
||||
data,
|
||||
isLoading,
|
||||
}
|
||||
}
|
||||
25
packages/live-preview-vue/tsconfig.json
Normal file
25
packages/live-preview-vue/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true, // Make sure typescript knows that this module depends on their references
|
||||
"noEmit": false /* Do not emit outputs. */,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */,
|
||||
"jsx": "react"
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"build",
|
||||
"tests",
|
||||
"test",
|
||||
"node_modules",
|
||||
".eslintrc.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.tsx"
|
||||
],
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"],
|
||||
"references": [{ "path": "../payload" }] // db-mongodb depends on payload
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "2.11.1",
|
||||
"version": "2.9.0",
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -101,6 +101,7 @@
|
||||
"jwt-decode": "3.1.2",
|
||||
"md5": "2.3.0",
|
||||
"method-override": "3.0.0",
|
||||
"micro-memoize": "4.1.2",
|
||||
"minimist": "1.2.8",
|
||||
"mkdirp": "1.0.4",
|
||||
"monaco-editor": "0.38.0",
|
||||
@@ -192,7 +193,7 @@
|
||||
"get-port": "5.1.1",
|
||||
"mini-css-extract-plugin": "1.6.2",
|
||||
"node-fetch": "2.6.12",
|
||||
"nodemon": "3.0.3",
|
||||
"nodemon": "3.0.1",
|
||||
"object.assign": "4.1.4",
|
||||
"object.entries": "1.1.6",
|
||||
"passport-strategy": "1.0.0",
|
||||
|
||||
@@ -24,16 +24,11 @@ export const Collapsible: React.FC<Props> = ({
|
||||
}) => {
|
||||
const [collapsedLocal, setCollapsedLocal] = useState(Boolean(initCollapsed))
|
||||
const [hoveringToggle, setHoveringToggle] = useState(false)
|
||||
const { withinCollapsible } = useCollapsible()
|
||||
const isNested = useCollapsible()
|
||||
const { t } = useTranslation('fields')
|
||||
|
||||
const collapsed = typeof collapsedFromProps === 'boolean' ? collapsedFromProps : collapsedLocal
|
||||
|
||||
const toggleCollapsible = React.useCallback(() => {
|
||||
if (typeof onToggle === 'function') onToggle(!collapsed)
|
||||
setCollapsedLocal(!collapsed)
|
||||
}, [onToggle, collapsed])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
@@ -41,14 +36,14 @@ export const Collapsible: React.FC<Props> = ({
|
||||
className,
|
||||
dragHandleProps && `${baseClass}--has-drag-handle`,
|
||||
collapsed && `${baseClass}--collapsed`,
|
||||
withinCollapsible && `${baseClass}--nested`,
|
||||
isNested && `${baseClass}--nested`,
|
||||
hoveringToggle && `${baseClass}--hovered`,
|
||||
`${baseClass}--style-${collapsibleStyle}`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
<CollapsibleProvider collapsed={collapsed} toggle={toggleCollapsible}>
|
||||
<CollapsibleProvider>
|
||||
<div
|
||||
className={`${baseClass}__toggle-wrap`}
|
||||
onMouseEnter={() => setHoveringToggle(true)}
|
||||
@@ -70,7 +65,10 @@ export const Collapsible: React.FC<Props> = ({
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
onClick={toggleCollapsible}
|
||||
onClick={() => {
|
||||
if (typeof onToggle === 'function') onToggle(!collapsed)
|
||||
setCollapsedLocal(!collapsed)
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<span>{t('toggleBlock')}</span>
|
||||
|
||||
@@ -1,35 +1,14 @@
|
||||
import React, { createContext, useContext } from 'react'
|
||||
|
||||
type ContextType = {
|
||||
collapsed: boolean
|
||||
isVisible: boolean
|
||||
toggle: () => void
|
||||
withinCollapsible: boolean
|
||||
}
|
||||
const Context = createContext({
|
||||
collapsed: false,
|
||||
isVisible: true,
|
||||
toggle: () => {},
|
||||
withinCollapsible: true,
|
||||
})
|
||||
const Context = createContext(false)
|
||||
|
||||
export const CollapsibleProvider: React.FC<{
|
||||
children?: React.ReactNode
|
||||
collapsed?: boolean
|
||||
toggle: () => void
|
||||
withinCollapsible?: boolean
|
||||
}> = ({ children, collapsed, toggle, withinCollapsible = true }) => {
|
||||
const { collapsed: parentIsCollapsed, isVisible } = useCollapsible()
|
||||
|
||||
const contextValue = React.useMemo((): ContextType => {
|
||||
return {
|
||||
collapsed: Boolean(collapsed),
|
||||
isVisible: isVisible && !parentIsCollapsed,
|
||||
toggle,
|
||||
withinCollapsible,
|
||||
}
|
||||
}, [collapsed, withinCollapsible, toggle, parentIsCollapsed, isVisible])
|
||||
return <Context.Provider value={contextValue}>{children}</Context.Provider>
|
||||
}> = ({ children, withinCollapsible = true }) => {
|
||||
return <Context.Provider value={withinCollapsible}>{children}</Context.Provider>
|
||||
}
|
||||
|
||||
export const useCollapsible = (): ContextType => useContext(Context)
|
||||
export const useCollapsible = (): boolean => useContext(Context)
|
||||
|
||||
export default Context
|
||||
|
||||
@@ -64,7 +64,7 @@ const DeleteDocument: React.FC<Props> = (props) => {
|
||||
if (res.status < 400) {
|
||||
setDeleting(false)
|
||||
toggleModal(modalSlug)
|
||||
toast.success(json.message || t('titleDeleted', { label: getTranslation(singular, i18n), title }))
|
||||
toast.success(t('titleDeleted', { label: getTranslation(singular, i18n), title }))
|
||||
return history.push(`${admin}/collections/${slug}`)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ const Group: React.FC<Props> = (props) => {
|
||||
permissions,
|
||||
} = props
|
||||
|
||||
const { withinCollapsible } = useCollapsible()
|
||||
const isWithinCollapsible = useCollapsible()
|
||||
const isWithinGroup = useGroup()
|
||||
const isWithinRow = useRow()
|
||||
const isWithinTab = useTabs()
|
||||
@@ -43,7 +43,7 @@ const Group: React.FC<Props> = (props) => {
|
||||
const groupHasErrors = submitted && errorCount > 0
|
||||
|
||||
const path = pathFromProps || name
|
||||
const isTopLevel = !(withinCollapsible || isWithinGroup || isWithinRow)
|
||||
const isTopLevel = !(isWithinCollapsible || isWithinGroup || isWithinRow)
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -51,7 +51,7 @@ const Group: React.FC<Props> = (props) => {
|
||||
fieldBaseClass,
|
||||
baseClass,
|
||||
isTopLevel && `${baseClass}--top-level`,
|
||||
withinCollapsible && `${baseClass}--within-collapsible`,
|
||||
isWithinCollapsible && `${baseClass}--within-collapsible`,
|
||||
isWithinGroup && `${baseClass}--within-group`,
|
||||
isWithinRow && `${baseClass}--within-row`,
|
||||
isWithinTab && `${baseClass}--within-tab`,
|
||||
|
||||
@@ -9,7 +9,7 @@ const reduceToIDs = (options) =>
|
||||
return [...ids, ...reduceToIDs(option.options)]
|
||||
}
|
||||
|
||||
return [...ids, { id: option.value, relationTo: option.relationTo }]
|
||||
return [...ids, option.value]
|
||||
}, [])
|
||||
|
||||
const sortOptions = (options: Option[]): Option[] =>
|
||||
@@ -63,12 +63,10 @@ const optionsReducer = (state: OptionGroup[], action: Action): OptionGroup[] =>
|
||||
const optionsToAddTo = newOptions.find(
|
||||
(optionGroup) => optionGroup.label === collection.labels.plural,
|
||||
)
|
||||
|
||||
const newSubOptions = docs.reduce((docSubOptions, doc) => {
|
||||
if (
|
||||
loadedIDs.filter((item) => item.id === doc.id && item.relationTo === relation).length ===
|
||||
0
|
||||
) {
|
||||
loadedIDs.push({ id: doc.id, relationTo: relation })
|
||||
if (loadedIDs.indexOf(doc.id) === -1) {
|
||||
loadedIDs.push(doc.id)
|
||||
|
||||
const docTitle = formatUseAsTitle({
|
||||
collection,
|
||||
@@ -91,10 +89,7 @@ const optionsReducer = (state: OptionGroup[], action: Action): OptionGroup[] =>
|
||||
}, [])
|
||||
|
||||
ids.forEach((id) => {
|
||||
if (
|
||||
loadedIDs.filter((item) => item.id === id && item.relationTo === relation).length === 0
|
||||
) {
|
||||
loadedIDs.push({ id, relationTo: relation })
|
||||
if (!loadedIDs.includes(id)) {
|
||||
newSubOptions.push({
|
||||
label: `${i18n.t('general:untitled')} - ID: ${id}`,
|
||||
relationTo: relation,
|
||||
|
||||
@@ -83,7 +83,7 @@ const TabsField: React.FC<Props> = (props) => {
|
||||
const { preferencesKey } = useDocumentInfo()
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
const { withinCollapsible } = useCollapsible()
|
||||
const isWithinCollapsible = useCollapsible()
|
||||
const [activeTabIndex, setActiveTabIndex] = useState<number>(0)
|
||||
const tabsPrefKey = `tabs-${indexPath}`
|
||||
|
||||
@@ -138,7 +138,7 @@ const TabsField: React.FC<Props> = (props) => {
|
||||
fieldBaseClass,
|
||||
className,
|
||||
baseClass,
|
||||
withinCollapsible && `${baseClass}--within-collapsible`,
|
||||
isWithinCollapsible && `${baseClass}--within-collapsible`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
|
||||
@@ -74,22 +74,21 @@ const DefaultCell: React.FC<Props> = (props) => {
|
||||
if (collection.upload && fieldAffectsData(field) && field.name === 'filename') {
|
||||
CellComponent = cellComponents.File
|
||||
} else {
|
||||
if (!cellData && 'label' in field) {
|
||||
return (
|
||||
<WrapElement {...wrapElementProps}>
|
||||
{t('noLabel', {
|
||||
return (
|
||||
<WrapElement {...wrapElementProps}>
|
||||
{(cellData === '' || typeof cellData === 'undefined') &&
|
||||
'label' in field &&
|
||||
t('noLabel', {
|
||||
label: getTranslation(
|
||||
typeof field.label === 'function' ? 'data' : field.label || 'data',
|
||||
i18n,
|
||||
),
|
||||
})}
|
||||
</WrapElement>
|
||||
)
|
||||
} else if (typeof cellData === 'string' || typeof cellData === 'number') {
|
||||
return <WrapElement {...wrapElementProps}>{cellData}</WrapElement>
|
||||
} else if (typeof cellData === 'object') {
|
||||
return <WrapElement {...wrapElementProps}>{JSON.stringify(cellData)}</WrapElement>
|
||||
}
|
||||
{typeof cellData === 'string' && cellData}
|
||||
{typeof cellData === 'number' && cellData}
|
||||
{typeof cellData === 'object' && JSON.stringify(cellData)}
|
||||
</WrapElement>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,38 +29,37 @@ async function forgotPassword(incomingArgs: Arguments): Promise<null | string> {
|
||||
|
||||
let args = incomingArgs
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
operation: 'forgotPassword',
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
data,
|
||||
disableEmail,
|
||||
expiration,
|
||||
req: {
|
||||
payload: { config, emailOptions, sendEmail: email },
|
||||
payload,
|
||||
t,
|
||||
},
|
||||
req,
|
||||
} = args
|
||||
|
||||
try {
|
||||
const shouldCommit = await initTransaction(args.req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
operation: 'forgotPassword',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
data,
|
||||
disableEmail,
|
||||
expiration,
|
||||
req: {
|
||||
payload: { config, emailOptions, sendEmail: email },
|
||||
payload,
|
||||
t,
|
||||
},
|
||||
req,
|
||||
} = args
|
||||
const shouldCommit = await initTransaction(req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Forget password
|
||||
@@ -160,7 +159,7 @@ async function forgotPassword(incomingArgs: Arguments): Promise<null | string> {
|
||||
|
||||
return token
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
await killTransaction(req)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Payload } from '../../../payload'
|
||||
import type { Result } from '../forgotPassword'
|
||||
|
||||
import { getDataLoader } from '../../../collections/dataloader'
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import forgotPassword from '../forgotPassword'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['collections']> = {
|
||||
@@ -22,7 +24,15 @@ async function localForgotPassword<T extends keyof GeneratedTypes['collections']
|
||||
payload: Payload,
|
||||
options: Options<T>,
|
||||
): Promise<Result> {
|
||||
const { collection: collectionSlug, data, disableEmail, expiration } = options
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
data,
|
||||
disableEmail,
|
||||
expiration,
|
||||
req = {} as PayloadRequest,
|
||||
} = options
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
|
||||
@@ -34,7 +44,12 @@ async function localForgotPassword<T extends keyof GeneratedTypes['collections']
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.payload = payload
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return forgotPassword({
|
||||
collection,
|
||||
|
||||
@@ -5,8 +5,10 @@ import type { GeneratedTypes } from '../../../index'
|
||||
import type { Payload } from '../../../payload'
|
||||
import type { Result } from '../login'
|
||||
|
||||
import { getDataLoader } from '../../../collections/dataloader'
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import login from '../login'
|
||||
|
||||
export type Options<TSlug extends keyof GeneratedTypes['collections']> = {
|
||||
@@ -31,14 +33,25 @@ async function localLogin<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
): Promise<Result & { user: GeneratedTypes['collections'][TSlug] }> {
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
data,
|
||||
depth,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
req = {} as PayloadRequest,
|
||||
res,
|
||||
showHiddenFields,
|
||||
} = options
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || req?.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
@@ -46,7 +59,12 @@ async function localLogin<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.payload = payload
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
const args = {
|
||||
collection,
|
||||
@@ -58,6 +76,12 @@ async function localLogin<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
showHiddenFields,
|
||||
}
|
||||
|
||||
if (locale) args.req.locale = locale
|
||||
if (fallbackLocale) {
|
||||
args.req.fallbackLocale =
|
||||
typeof fallbackLocaleArg !== 'undefined' ? fallbackLocaleArg : fallbackLocale || defaultLocale
|
||||
}
|
||||
|
||||
return login<TSlug>(args)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Payload } from '../../../payload'
|
||||
import type { Result } from '../resetPassword'
|
||||
|
||||
import { getDataLoader } from '../../../collections/dataloader'
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import resetPassword from '../resetPassword'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['collections']> = {
|
||||
@@ -22,7 +24,15 @@ async function localResetPassword<T extends keyof GeneratedTypes['collections']>
|
||||
payload: Payload,
|
||||
options: Options<T>,
|
||||
): Promise<Result> {
|
||||
const { collection: collectionSlug, data, overrideAccess } = options
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
data,
|
||||
overrideAccess,
|
||||
req = {} as PayloadRequest,
|
||||
} = options
|
||||
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
|
||||
@@ -34,7 +44,12 @@ async function localResetPassword<T extends keyof GeneratedTypes['collections']>
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
req.payload = payload
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return resetPassword({
|
||||
collection,
|
||||
|
||||
@@ -2,8 +2,10 @@ import type { GeneratedTypes, RequestContext } from '../../../'
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Payload } from '../../../payload'
|
||||
|
||||
import { getDataLoader } from '../../../collections/dataloader'
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import unlock from '../unlock'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['collections']> = {
|
||||
@@ -20,7 +22,14 @@ async function localUnlock<T extends keyof GeneratedTypes['collections']>(
|
||||
payload: Payload,
|
||||
options: Options<T>,
|
||||
): Promise<boolean> {
|
||||
const { collection: collectionSlug, data, overrideAccess = true } = options
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
data,
|
||||
overrideAccess = true,
|
||||
req = {} as PayloadRequest,
|
||||
} = options
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
|
||||
@@ -30,7 +39,12 @@ async function localUnlock<T extends keyof GeneratedTypes['collections']>(
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
req.payload = payload
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return unlock({
|
||||
collection,
|
||||
|
||||
@@ -3,7 +3,8 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Payload } from '../../../payload'
|
||||
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import verifyEmail from '../verifyEmail'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['collections']> = {
|
||||
@@ -17,7 +18,8 @@ async function localVerifyEmail<T extends keyof GeneratedTypes['collections']>(
|
||||
payload: Payload,
|
||||
options: Options<T>,
|
||||
): Promise<boolean> {
|
||||
const { collection: collectionSlug, token } = options
|
||||
const { collection: collectionSlug, context, req = {} as PayloadRequest, token } = options
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
|
||||
@@ -27,7 +29,9 @@ async function localVerifyEmail<T extends keyof GeneratedTypes['collections']>(
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
req.payload = payload
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
return verifyEmail({
|
||||
collection,
|
||||
|
||||
@@ -45,38 +45,37 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
): Promise<Result & { user: GeneratedTypes['collections'][TSlug] }> {
|
||||
let args = incomingArgs
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
operation: 'login',
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
data,
|
||||
depth,
|
||||
overrideAccess,
|
||||
req,
|
||||
req: {
|
||||
payload,
|
||||
payload: { config, secret },
|
||||
},
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
try {
|
||||
const shouldCommit = await initTransaction(args.req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
operation: 'login',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
data,
|
||||
depth,
|
||||
overrideAccess,
|
||||
req,
|
||||
req: {
|
||||
payload,
|
||||
payload: { config, secret },
|
||||
},
|
||||
showHiddenFields,
|
||||
} = args
|
||||
const shouldCommit = await initTransaction(req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Login
|
||||
@@ -263,7 +262,7 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
await killTransaction(req)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,7 @@ import type { Document } from '../../types'
|
||||
|
||||
import { buildAfterOperation } from '../../collections/operations/utils'
|
||||
import { Forbidden } from '../../errors'
|
||||
import { commitTransaction } from '../../utilities/commitTransaction'
|
||||
import getCookieExpiration from '../../utilities/getCookieExpiration'
|
||||
import { initTransaction } from '../../utilities/initTransaction'
|
||||
import { killTransaction } from '../../utilities/killTransaction'
|
||||
import { getFieldsToSign } from './getFieldsToSign'
|
||||
|
||||
export type Result = {
|
||||
@@ -31,130 +28,120 @@ export type Arguments = {
|
||||
async function refresh(incomingArgs: Arguments): Promise<Result> {
|
||||
let args = incomingArgs
|
||||
|
||||
try {
|
||||
const shouldCommit = await initTransaction(args.req)
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
operation: 'refresh',
|
||||
req: args.req,
|
||||
})) || args
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Refresh
|
||||
// /////////////////////////////////////
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
req: {
|
||||
payload: { config, secret },
|
||||
},
|
||||
} = args
|
||||
|
||||
if (typeof args.token !== 'string' || !args.req.user) throw new Forbidden(args.req.t)
|
||||
|
||||
const parsedURL = url.parse(args.req.url)
|
||||
const isGraphQL = parsedURL.pathname === config.routes.graphQL
|
||||
|
||||
const user = await args.req.payload.findByID({
|
||||
id: args.req.user.id,
|
||||
collection: args.req.user.collection,
|
||||
depth: isGraphQL ? 0 : args.collection.config.auth.depth,
|
||||
req: args.req,
|
||||
})
|
||||
|
||||
const fieldsToSign = getFieldsToSign({
|
||||
collectionConfig,
|
||||
email: user?.email as string,
|
||||
user: args?.req?.user,
|
||||
})
|
||||
|
||||
const refreshedToken = jwt.sign(fieldsToSign, secret, {
|
||||
expiresIn: collectionConfig.auth.tokenExpiration,
|
||||
})
|
||||
|
||||
const exp = (jwt.decode(refreshedToken) as Record<string, unknown>).exp as number
|
||||
|
||||
if (args.res) {
|
||||
const cookieOptions = {
|
||||
domain: undefined,
|
||||
expires: getCookieExpiration(collectionConfig.auth.tokenExpiration),
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: collectionConfig.auth.cookies.sameSite,
|
||||
secure: collectionConfig.auth.cookies.secure,
|
||||
}
|
||||
|
||||
if (collectionConfig.auth.cookies.domain)
|
||||
cookieOptions.domain = collectionConfig.auth.cookies.domain
|
||||
|
||||
args.res.cookie(`${config.cookiePrefix}-token`, refreshedToken, cookieOptions)
|
||||
}
|
||||
|
||||
let result: Result = {
|
||||
exp,
|
||||
refreshedToken,
|
||||
user,
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// After Refresh - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.afterRefresh.reduce(async (priorHook, hook) => {
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
exp,
|
||||
req: args.req,
|
||||
res: args.res,
|
||||
token: refreshedToken,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
operation: 'refresh',
|
||||
})) || args
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
// /////////////////////////////////////
|
||||
// Refresh
|
||||
// /////////////////////////////////////
|
||||
|
||||
result = await buildAfterOperation({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
operation: 'refresh',
|
||||
result,
|
||||
})
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
req: {
|
||||
payload: { config, secret },
|
||||
},
|
||||
} = args
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Return results
|
||||
// /////////////////////////////////////
|
||||
if (typeof args.token !== 'string' || !args.req.user) throw new Forbidden(args.req.t)
|
||||
|
||||
if (collectionConfig.auth.removeTokenFromResponses) {
|
||||
delete result.refreshedToken
|
||||
const parsedURL = url.parse(args.req.url)
|
||||
const isGraphQL = parsedURL.pathname === config.routes.graphQL
|
||||
|
||||
const user = await args.req.payload.findByID({
|
||||
id: args.req.user.id,
|
||||
collection: args.req.user.collection,
|
||||
depth: isGraphQL ? 0 : args.collection.config.auth.depth,
|
||||
req: args.req,
|
||||
})
|
||||
|
||||
const fieldsToSign = getFieldsToSign({
|
||||
collectionConfig,
|
||||
email: user?.email as string,
|
||||
user: args?.req?.user,
|
||||
})
|
||||
|
||||
const refreshedToken = jwt.sign(fieldsToSign, secret, {
|
||||
expiresIn: collectionConfig.auth.tokenExpiration,
|
||||
})
|
||||
|
||||
const exp = (jwt.decode(refreshedToken) as Record<string, unknown>).exp as number
|
||||
|
||||
if (args.res) {
|
||||
const cookieOptions = {
|
||||
domain: undefined,
|
||||
expires: getCookieExpiration(collectionConfig.auth.tokenExpiration),
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: collectionConfig.auth.cookies.sameSite,
|
||||
secure: collectionConfig.auth.cookies.secure,
|
||||
}
|
||||
|
||||
if (shouldCommit) await commitTransaction(args.req)
|
||||
if (collectionConfig.auth.cookies.domain)
|
||||
cookieOptions.domain = collectionConfig.auth.cookies.domain
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
throw error
|
||||
args.res.cookie(`${config.cookiePrefix}-token`, refreshedToken, cookieOptions)
|
||||
}
|
||||
|
||||
let result: Result = {
|
||||
exp,
|
||||
refreshedToken,
|
||||
user,
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// After Refresh - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.afterRefresh.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
exp,
|
||||
req: args.req,
|
||||
res: args.res,
|
||||
token: refreshedToken,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
result = await buildAfterOperation({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
operation: 'refresh',
|
||||
result,
|
||||
})
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Return results
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.auth.removeTokenFromResponses) {
|
||||
delete result.refreshedToken
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export default refresh
|
||||
|
||||
@@ -45,7 +45,7 @@ if (tsConfig?.config?.compilerOptions?.paths) {
|
||||
// Allow disabling SWC for debugging
|
||||
if (process.env.DISABLE_SWC !== 'true') {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error - bad @swc/register types
|
||||
// @ts-ignore - bad @swc/register types
|
||||
swcRegister(swcOptions)
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ export type BeforeOperationHook = (args: {
|
||||
* Hook operation being performed
|
||||
*/
|
||||
operation: HookOperationType
|
||||
req: PayloadRequest
|
||||
}) => any
|
||||
|
||||
export type BeforeValidateHook<T extends TypeWithID = any> = (args: {
|
||||
|
||||
@@ -55,45 +55,44 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
): Promise<GeneratedTypes['collections'][TSlug]> {
|
||||
let args = incomingArgs
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'create',
|
||||
})) || args
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
const {
|
||||
autosave = false,
|
||||
collection: { config: collectionConfig },
|
||||
collection,
|
||||
depth,
|
||||
disableVerificationEmail,
|
||||
draft = false,
|
||||
overrideAccess,
|
||||
overwriteExistingFiles = false,
|
||||
req: {
|
||||
payload,
|
||||
payload: { config, emailOptions },
|
||||
},
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
try {
|
||||
const shouldCommit = await initTransaction(args.req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'create',
|
||||
req: args.req,
|
||||
})) || args
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
const {
|
||||
autosave = false,
|
||||
collection: { config: collectionConfig },
|
||||
collection,
|
||||
depth,
|
||||
disableVerificationEmail,
|
||||
draft = false,
|
||||
overrideAccess,
|
||||
overwriteExistingFiles = false,
|
||||
req: {
|
||||
payload,
|
||||
payload: { config, emailOptions },
|
||||
},
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
const shouldCommit = await initTransaction(req)
|
||||
|
||||
let { data } = args
|
||||
|
||||
@@ -368,7 +367,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
await killTransaction(req)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,42 +39,42 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
|
||||
}> {
|
||||
let args = incomingArgs
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'delete',
|
||||
})) || args
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
depth,
|
||||
overrideAccess,
|
||||
req: {
|
||||
locale,
|
||||
payload: { config },
|
||||
payload,
|
||||
t,
|
||||
},
|
||||
req,
|
||||
showHiddenFields,
|
||||
where,
|
||||
} = args
|
||||
|
||||
try {
|
||||
const shouldCommit = await initTransaction(args.req)
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'delete',
|
||||
req: args.req,
|
||||
})) || args
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
depth,
|
||||
overrideAccess,
|
||||
req: {
|
||||
locale,
|
||||
payload: { config },
|
||||
payload,
|
||||
t,
|
||||
},
|
||||
req,
|
||||
showHiddenFields,
|
||||
where,
|
||||
} = args
|
||||
const shouldCommit = await initTransaction(req)
|
||||
|
||||
if (!where) {
|
||||
throw new APIError("Missing 'where' query of documents to delete.", httpStatus.BAD_REQUEST)
|
||||
@@ -264,7 +264,7 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
await killTransaction(req)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,42 +30,41 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
): Promise<Document> {
|
||||
let args = incomingArgs
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'delete',
|
||||
})) || args
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
const {
|
||||
id,
|
||||
collection: { config: collectionConfig },
|
||||
depth,
|
||||
overrideAccess,
|
||||
req: {
|
||||
payload: { config },
|
||||
payload,
|
||||
t,
|
||||
},
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
try {
|
||||
const shouldCommit = await initTransaction(args.req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'delete',
|
||||
req: args.req,
|
||||
})) || args
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
const {
|
||||
id,
|
||||
collection: { config: collectionConfig },
|
||||
depth,
|
||||
overrideAccess,
|
||||
req: {
|
||||
payload: { config },
|
||||
payload,
|
||||
t,
|
||||
},
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
const shouldCommit = await initTransaction(req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Access
|
||||
@@ -214,7 +213,7 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
await killTransaction(req)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,43 +37,42 @@ async function find<T extends TypeWithID & Record<string, unknown>>(
|
||||
): Promise<PaginatedDocs<T>> {
|
||||
let args = incomingArgs
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'read',
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
collection,
|
||||
currentDepth,
|
||||
depth,
|
||||
disableErrors,
|
||||
draft: draftsEnabled,
|
||||
limit,
|
||||
overrideAccess,
|
||||
page,
|
||||
pagination = true,
|
||||
req: { locale, payload },
|
||||
req,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
where,
|
||||
} = args
|
||||
|
||||
try {
|
||||
const shouldCommit = await initTransaction(args.req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'read',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
collection,
|
||||
currentDepth,
|
||||
depth,
|
||||
disableErrors,
|
||||
draft: draftsEnabled,
|
||||
limit,
|
||||
overrideAccess,
|
||||
page,
|
||||
pagination = true,
|
||||
req: { locale, payload },
|
||||
req,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
where,
|
||||
} = args
|
||||
const shouldCommit = await initTransaction(req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Access
|
||||
@@ -254,7 +253,7 @@ async function find<T extends TypeWithID & Record<string, unknown>>(
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
await killTransaction(req)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
import memoize from 'micro-memoize'
|
||||
|
||||
import type { FindOneArgs } from '../../database/types'
|
||||
import type { PayloadRequest } from '../../express/types'
|
||||
import type { Collection, TypeWithID } from '../config/types'
|
||||
@@ -28,38 +30,38 @@ export type Arguments = {
|
||||
async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<T> {
|
||||
let args = incomingArgs
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'read',
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
id,
|
||||
collection: { config: collectionConfig },
|
||||
currentDepth,
|
||||
depth,
|
||||
disableErrors,
|
||||
draft: draftEnabled = false,
|
||||
overrideAccess = false,
|
||||
req: { locale, t },
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
try {
|
||||
const shouldCommit = await initTransaction(args.req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'read',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
id,
|
||||
collection: { config: collectionConfig },
|
||||
currentDepth,
|
||||
depth,
|
||||
disableErrors,
|
||||
draft: draftEnabled = false,
|
||||
overrideAccess = false,
|
||||
req: { locale, t },
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
const shouldCommit = await initTransaction(req)
|
||||
const { transactionID } = req
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Access
|
||||
@@ -87,7 +89,25 @@ async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<
|
||||
|
||||
if (!findOneArgs.where.and[0].id) throw new NotFound(t)
|
||||
|
||||
let result: T = await req.payload.db.findOne(findOneArgs)
|
||||
if (!req.findByID) {
|
||||
req.findByID = { [transactionID]: {} }
|
||||
} else if (!req.findByID[transactionID]) {
|
||||
req.findByID[transactionID] = {}
|
||||
}
|
||||
|
||||
if (!req.findByID[transactionID][collectionConfig.slug]) {
|
||||
const nonMemoizedFindByID = async (query: FindOneArgs) => req.payload.db.findOne(query)
|
||||
|
||||
req.findByID[transactionID][collectionConfig.slug] = memoize(nonMemoizedFindByID, {
|
||||
isPromise: true,
|
||||
maxSize: 100,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore This is straight from their docs, bad typings
|
||||
transformKey: JSON.stringify,
|
||||
})
|
||||
}
|
||||
|
||||
let result = (await req.findByID[transactionID][collectionConfig.slug](findOneArgs)) as T
|
||||
|
||||
if (!result) {
|
||||
if (!disableErrors) {
|
||||
@@ -97,6 +117,9 @@ async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<
|
||||
return null
|
||||
}
|
||||
|
||||
// Clone the result - it may have come back memoized
|
||||
result = JSON.parse(JSON.stringify(result))
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Replace document with draft if available
|
||||
// /////////////////////////////////////
|
||||
@@ -181,7 +204,7 @@ async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
await killTransaction(req)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ import type { Document } from '../../../types'
|
||||
import type { File } from '../../../uploads/types'
|
||||
|
||||
import { APIError } from '../../../errors'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import getFileByPath from '../../../uploads/getFileByPath'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { getDataLoader } from '../../dataloader'
|
||||
import create from '../create'
|
||||
|
||||
export type Options<TSlug extends keyof GeneratedTypes['collections']> = {
|
||||
@@ -42,17 +44,30 @@ export default async function createLocal<TSlug extends keyof GeneratedTypes['co
|
||||
): Promise<GeneratedTypes['collections'][TSlug]> {
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
data,
|
||||
depth,
|
||||
disableVerificationEmail,
|
||||
draft,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
file,
|
||||
filePath,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
overwriteExistingFiles = false,
|
||||
req = {} as PayloadRequest,
|
||||
showHiddenFields,
|
||||
user,
|
||||
} = options
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || req.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
@@ -60,18 +75,21 @@ export default async function createLocal<TSlug extends keyof GeneratedTypes['co
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
const fileToSet = (file ?? (await getFileByPath(filePath))) as UploadedFile
|
||||
if (fileToSet) {
|
||||
if (req?.files) {
|
||||
req.files.file = fileToSet
|
||||
} else {
|
||||
req.files = {
|
||||
file: fileToSet,
|
||||
}
|
||||
}
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.locale = locale
|
||||
req.fallbackLocale =
|
||||
typeof fallbackLocaleArg !== 'undefined' ? fallbackLocaleArg : fallbackLocale || defaultLocale
|
||||
req.payload = payload
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
req.files = {
|
||||
file: (file ?? (await getFileByPath(filePath))) as UploadedFile,
|
||||
}
|
||||
|
||||
if (typeof user !== 'undefined') req.user = user
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return create<TSlug>({
|
||||
collection,
|
||||
data,
|
||||
|
||||
@@ -5,7 +5,9 @@ import type { Document, Where } from '../../../types'
|
||||
import type { BulkOperationResult } from '../../config/types'
|
||||
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import { getDataLoader } from '../../dataloader'
|
||||
import deleteOperation from '../delete'
|
||||
import deleteByID from '../deleteByID'
|
||||
|
||||
@@ -57,13 +59,24 @@ async function deleteLocal<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
const {
|
||||
id,
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
depth,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
req: incomingReq = {} as PayloadRequest,
|
||||
showHiddenFields,
|
||||
user,
|
||||
where,
|
||||
} = options
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || incomingReq?.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
@@ -71,7 +84,22 @@ async function deleteLocal<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
const req = {
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n: i18nInit(payload.config.i18n),
|
||||
locale: locale,
|
||||
payload,
|
||||
payloadAPI: 'local',
|
||||
transactionID: incomingReq?.transactionID,
|
||||
user,
|
||||
} as PayloadRequest
|
||||
setRequestContext(req, context)
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
const args = {
|
||||
id,
|
||||
|
||||
@@ -5,7 +5,9 @@ import type { Payload } from '../../../payload'
|
||||
import type { Document, Where } from '../../../types'
|
||||
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import { getDataLoader } from '../../dataloader'
|
||||
import find from '../find'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['collections']> = {
|
||||
@@ -37,20 +39,32 @@ export default async function findLocal<T extends keyof GeneratedTypes['collecti
|
||||
): Promise<PaginatedDocs<GeneratedTypes['collections'][T]>> {
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
currentDepth,
|
||||
depth,
|
||||
disableErrors,
|
||||
draft = false,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
limit,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
page,
|
||||
pagination = true,
|
||||
req = {} as PayloadRequest,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
user,
|
||||
where,
|
||||
} = options
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || req.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
@@ -58,7 +72,17 @@ export default async function findLocal<T extends keyof GeneratedTypes['collecti
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.locale = locale
|
||||
req.fallbackLocale =
|
||||
typeof fallbackLocaleArg !== 'undefined' ? fallbackLocaleArg : fallbackLocale || defaultLocale
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
req.payload = payload
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
if (typeof user !== 'undefined') req.user = user
|
||||
|
||||
return find<GeneratedTypes['collections'][T]>({
|
||||
collection,
|
||||
|
||||
@@ -4,7 +4,9 @@ import type { Payload } from '../../../payload'
|
||||
import type { Document } from '../../../types'
|
||||
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import { getDataLoader } from '../../dataloader'
|
||||
import findByID from '../findByID'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['collections']> = {
|
||||
@@ -33,15 +35,27 @@ export default async function findByIDLocal<T extends keyof GeneratedTypes['coll
|
||||
const {
|
||||
id,
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
currentDepth,
|
||||
depth,
|
||||
disableErrors = false,
|
||||
draft = false,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
req = {} as PayloadRequest,
|
||||
showHiddenFields,
|
||||
user,
|
||||
} = options
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || req.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
@@ -49,7 +63,17 @@ export default async function findByIDLocal<T extends keyof GeneratedTypes['coll
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.locale = locale
|
||||
req.fallbackLocale =
|
||||
typeof fallbackLocaleArg !== 'undefined' ? fallbackLocaleArg : fallbackLocale || defaultLocale
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
req.payload = payload
|
||||
|
||||
if (typeof user !== 'undefined') req.user = user
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return findByID<GeneratedTypes['collections'][T]>({
|
||||
id,
|
||||
|
||||
@@ -5,7 +5,9 @@ import type { Document } from '../../../types'
|
||||
import type { TypeWithVersion } from '../../../versions/types'
|
||||
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import { getDataLoader } from '../../dataloader'
|
||||
import findVersionByID from '../findVersionByID'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['collections']> = {
|
||||
@@ -33,13 +35,24 @@ export default async function findVersionByIDLocal<T extends keyof GeneratedType
|
||||
const {
|
||||
id,
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
depth,
|
||||
disableErrors = false,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
req = {} as PayloadRequest,
|
||||
showHiddenFields,
|
||||
} = options
|
||||
setRequestContext(req, context)
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || req.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
@@ -49,7 +62,15 @@ export default async function findVersionByIDLocal<T extends keyof GeneratedType
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
req.payloadAPI = req.payloadAPI || 'local'
|
||||
req.locale = locale
|
||||
req.fallbackLocale =
|
||||
typeof fallbackLocaleArg !== 'undefined' ? fallbackLocaleArg : fallbackLocale || defaultLocale
|
||||
req.i18n = i18nInit(payload.config.i18n)
|
||||
req.payload = payload
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return findVersionByID({
|
||||
id,
|
||||
|
||||
@@ -6,7 +6,9 @@ import type { Document, Where } from '../../../types'
|
||||
import type { TypeWithVersion } from '../../../versions/types'
|
||||
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import { getDataLoader } from '../../dataloader'
|
||||
import findVersions from '../findVersions'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['collections']> = {
|
||||
@@ -35,16 +37,27 @@ export default async function findVersionsLocal<T extends keyof GeneratedTypes['
|
||||
): Promise<PaginatedDocs<TypeWithVersion<GeneratedTypes['collections'][T]>>> {
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
depth,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
limit,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
page,
|
||||
req: incomingReq,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
user,
|
||||
where,
|
||||
} = options
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || incomingReq?.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
@@ -52,7 +65,23 @@ export default async function findVersionsLocal<T extends keyof GeneratedTypes['
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
const req = {
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale,
|
||||
payload,
|
||||
payloadAPI: 'local',
|
||||
transactionID: incomingReq?.transactionID,
|
||||
user,
|
||||
} as PayloadRequest
|
||||
setRequestContext(req, context)
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return findVersions({
|
||||
collection,
|
||||
|
||||
@@ -4,7 +4,9 @@ import type { Payload } from '../../../payload'
|
||||
import type { Document } from '../../../types'
|
||||
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import { getDataLoader } from '../../dataloader'
|
||||
import restoreVersion from '../restoreVersion'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['collections']> = {
|
||||
@@ -28,9 +30,26 @@ export default async function restoreVersionLocal<T extends keyof GeneratedTypes
|
||||
payload: Payload,
|
||||
options: Options<T>,
|
||||
): Promise<GeneratedTypes['collections'][T]> {
|
||||
const { id, collection: collectionSlug, depth, overrideAccess = true, showHiddenFields } = options
|
||||
const {
|
||||
id,
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
depth,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
req: incomingReq,
|
||||
showHiddenFields,
|
||||
user,
|
||||
} = options
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || incomingReq?.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
@@ -40,7 +59,23 @@ export default async function restoreVersionLocal<T extends keyof GeneratedTypes
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
const req = {
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale,
|
||||
payload,
|
||||
payloadAPI: 'local',
|
||||
t: i18n.t,
|
||||
transactionID: incomingReq?.transactionID,
|
||||
user,
|
||||
} as PayloadRequest
|
||||
setRequestContext(req, context)
|
||||
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
const args = {
|
||||
id,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { UploadedFile } from 'express-fileupload'
|
||||
import type { DeepPartial } from 'ts-essentials'
|
||||
|
||||
import type { GeneratedTypes } from '../../../'
|
||||
@@ -9,8 +8,10 @@ import type { File } from '../../../uploads/types'
|
||||
import type { BulkOperationResult } from '../../config/types'
|
||||
|
||||
import { APIError } from '../../../errors'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import getFileByPath from '../../../uploads/getFileByPath'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { getDataLoader } from '../../dataloader'
|
||||
import update from '../update'
|
||||
import updateByID from '../updateByID'
|
||||
|
||||
@@ -69,18 +70,30 @@ async function updateLocal<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
id,
|
||||
autosave,
|
||||
collection: collectionSlug,
|
||||
context,
|
||||
data,
|
||||
depth,
|
||||
draft,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
file,
|
||||
filePath,
|
||||
locale: localeArg = null,
|
||||
overrideAccess = true,
|
||||
overwriteExistingFiles = false,
|
||||
req: incomingReq,
|
||||
showHiddenFields,
|
||||
user,
|
||||
where,
|
||||
} = options
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = localizationConfig ? localizationConfig.defaultLocale : null
|
||||
const locale = localeArg || incomingReq?.locale || defaultLocale
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
@@ -88,17 +101,25 @@ async function updateLocal<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
const fileToSet = (file ?? (await getFileByPath(filePath))) as UploadedFile
|
||||
if (fileToSet) {
|
||||
if (req?.files) {
|
||||
req.files.file = fileToSet
|
||||
} else {
|
||||
req.files = {
|
||||
file: fileToSet,
|
||||
}
|
||||
}
|
||||
}
|
||||
const req = {
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
files: {
|
||||
file: file ?? (await getFileByPath(filePath)),
|
||||
},
|
||||
i18n,
|
||||
locale: locale ?? defaultLocale,
|
||||
payload,
|
||||
payloadAPI: 'local',
|
||||
transactionID: incomingReq?.transactionID,
|
||||
user,
|
||||
} as PayloadRequest
|
||||
setRequestContext(req, context)
|
||||
|
||||
if (!req.t) req.t = req.i18n.t
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
const args = {
|
||||
id,
|
||||
|
||||
@@ -47,43 +47,42 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
): Promise<BulkOperationResult<TSlug>> {
|
||||
let args = incomingArgs
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'update',
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
collection,
|
||||
depth,
|
||||
draft: draftArg = false,
|
||||
overrideAccess,
|
||||
overwriteExistingFiles = false,
|
||||
req: {
|
||||
locale,
|
||||
payload: { config },
|
||||
payload,
|
||||
t,
|
||||
},
|
||||
req,
|
||||
showHiddenFields,
|
||||
where,
|
||||
} = args
|
||||
|
||||
try {
|
||||
const shouldCommit = await initTransaction(args.req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'update',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
collection,
|
||||
depth,
|
||||
draft: draftArg = false,
|
||||
overrideAccess,
|
||||
overwriteExistingFiles = false,
|
||||
req: {
|
||||
locale,
|
||||
payload: { config },
|
||||
payload,
|
||||
t,
|
||||
},
|
||||
req,
|
||||
showHiddenFields,
|
||||
where,
|
||||
} = args
|
||||
const shouldCommit = await initTransaction(req)
|
||||
|
||||
if (!where) {
|
||||
throw new APIError("Missing 'where' query of documents to update.", httpStatus.BAD_REQUEST)
|
||||
@@ -406,7 +405,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
await killTransaction(req)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,44 +46,43 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
): Promise<GeneratedTypes['collections'][TSlug]> {
|
||||
let args = incomingArgs
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'update',
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
id,
|
||||
autosave = false,
|
||||
collection: { config: collectionConfig },
|
||||
collection,
|
||||
depth,
|
||||
draft: draftArg = false,
|
||||
overrideAccess,
|
||||
overwriteExistingFiles = false,
|
||||
req: {
|
||||
locale,
|
||||
payload: { config },
|
||||
payload,
|
||||
t,
|
||||
},
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
try {
|
||||
const shouldCommit = await initTransaction(args.req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'update',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
id,
|
||||
autosave = false,
|
||||
collection: { config: collectionConfig },
|
||||
collection,
|
||||
depth,
|
||||
draft: draftArg = false,
|
||||
overrideAccess,
|
||||
overwriteExistingFiles = false,
|
||||
req: {
|
||||
locale,
|
||||
payload: { config },
|
||||
payload,
|
||||
t,
|
||||
},
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
const shouldCommit = await initTransaction(req)
|
||||
|
||||
if (!id) {
|
||||
throw new APIError('Missing ID of document to update.', httpStatus.BAD_REQUEST)
|
||||
@@ -377,7 +376,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
await killTransaction(req)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type forgotPassword from '../../auth/operations/forgotPassword'
|
||||
import type login from '../../auth/operations/login'
|
||||
import type refresh from '../../auth/operations/refresh'
|
||||
import type { PayloadRequest } from '../../express/types'
|
||||
import type { AfterOperationHook, SanitizedCollectionConfig, TypeWithID } from '../config/types'
|
||||
import type create from './create'
|
||||
import type deleteOperation from './delete'
|
||||
@@ -23,62 +22,77 @@ export type AfterOperationMap<T extends TypeWithID> = {
|
||||
update: typeof update // todo: pass correct generic
|
||||
updateByID: typeof updateByID // todo: pass correct generic
|
||||
}
|
||||
export type AfterOperationArg<T extends TypeWithID> = {
|
||||
/** The collection which this hook is being run on */
|
||||
collection: SanitizedCollectionConfig
|
||||
req: PayloadRequest
|
||||
} & (
|
||||
export type AfterOperationArg<T extends TypeWithID> =
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['create']>[0]
|
||||
/** The collection which this hook is being run on */
|
||||
collection: SanitizedCollectionConfig
|
||||
operation: 'create'
|
||||
result: Awaited<ReturnType<AfterOperationMap<T>['create']>>
|
||||
}
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['delete']>[0]
|
||||
/** The collection which this hook is being run on */
|
||||
collection: SanitizedCollectionConfig
|
||||
operation: 'delete'
|
||||
result: Awaited<ReturnType<AfterOperationMap<T>['delete']>>
|
||||
}
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['deleteByID']>[0]
|
||||
/** The collection which this hook is being run on */
|
||||
collection: SanitizedCollectionConfig
|
||||
operation: 'deleteByID'
|
||||
result: Awaited<ReturnType<AfterOperationMap<T>['deleteByID']>>
|
||||
}
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['find']>[0]
|
||||
/** The collection which this hook is being run on */
|
||||
collection: SanitizedCollectionConfig
|
||||
operation: 'find'
|
||||
result: Awaited<ReturnType<AfterOperationMap<T>['find']>>
|
||||
}
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['findByID']>[0]
|
||||
/** The collection which this hook is being run on */
|
||||
collection: SanitizedCollectionConfig
|
||||
operation: 'findByID'
|
||||
result: Awaited<ReturnType<AfterOperationMap<T>['findByID']>>
|
||||
}
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['forgotPassword']>[0]
|
||||
/** The collection which this hook is being run on */
|
||||
collection: SanitizedCollectionConfig
|
||||
operation: 'forgotPassword'
|
||||
result: Awaited<ReturnType<AfterOperationMap<T>['forgotPassword']>>
|
||||
}
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['login']>[0]
|
||||
/** The collection which this hook is being run on */
|
||||
collection: SanitizedCollectionConfig
|
||||
operation: 'login'
|
||||
result: Awaited<ReturnType<AfterOperationMap<T>['login']>>
|
||||
}
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['refresh']>[0]
|
||||
/** The collection which this hook is being run on */
|
||||
collection: SanitizedCollectionConfig
|
||||
operation: 'refresh'
|
||||
result: Awaited<ReturnType<AfterOperationMap<T>['refresh']>>
|
||||
}
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['update']>[0]
|
||||
/** The collection which this hook is being run on */
|
||||
collection: SanitizedCollectionConfig
|
||||
operation: 'update'
|
||||
result: Awaited<ReturnType<AfterOperationMap<T>['update']>>
|
||||
}
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['updateByID']>[0]
|
||||
/** The collection which this hook is being run on */
|
||||
collection: SanitizedCollectionConfig
|
||||
operation: 'updateByID'
|
||||
result: Awaited<ReturnType<AfterOperationMap<T>['updateByID']>>
|
||||
}
|
||||
)
|
||||
|
||||
// export type AfterOperationHook = typeof buildAfterOperation;
|
||||
|
||||
@@ -86,7 +100,7 @@ export const buildAfterOperation = async <
|
||||
T extends TypeWithID = any,
|
||||
O extends keyof AfterOperationMap<T> = keyof AfterOperationMap<T>,
|
||||
>(
|
||||
operationArgs: Omit<AfterOperationArg<T>, 'req'> & { operation: O },
|
||||
operationArgs: AfterOperationArg<T> & { operation: O },
|
||||
): Promise<Awaited<ReturnType<AfterOperationMap<T>[O]>>> => {
|
||||
const { args, collection, operation, result } = operationArgs
|
||||
|
||||
@@ -100,7 +114,6 @@ export const buildAfterOperation = async <
|
||||
args,
|
||||
collection,
|
||||
operation,
|
||||
req: args.req,
|
||||
result: newResult,
|
||||
} as AfterOperationArg<T>)
|
||||
|
||||
|
||||
@@ -62,17 +62,6 @@ export async function getLocalizedPaths({
|
||||
return paths
|
||||
}
|
||||
|
||||
if (!matchedField && currentPath === 'id' && i === pathSegments.length - 1) {
|
||||
lastIncompletePath.path = currentPath
|
||||
const idField: Field = {
|
||||
name: 'id',
|
||||
type: payload.db.defaultIDType as 'text',
|
||||
}
|
||||
lastIncompletePath.field = idField
|
||||
lastIncompletePath.complete = true
|
||||
return paths
|
||||
}
|
||||
|
||||
if (matchedField) {
|
||||
if ('hidden' in matchedField && matchedField.hidden && !overrideAccess) {
|
||||
lastIncompletePath.invalid = true
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export { useCollapsible } from '../../admin/components/elements/Collapsible/provider'
|
||||
export { default as buildStateFromSchema } from '../../admin/components/forms/Form/buildStateFromSchema'
|
||||
export { useAuth } from '../../admin/components/utilities/Auth'
|
||||
export { useConfig } from '../../admin/components/utilities/Config'
|
||||
|
||||
@@ -275,63 +275,55 @@ const validateFilterOptions: Validate = async (
|
||||
|
||||
await Promise.all(
|
||||
collections.map(async (collection) => {
|
||||
try {
|
||||
let optionFilter =
|
||||
typeof filterOptions === 'function'
|
||||
? await filterOptions({
|
||||
id,
|
||||
data,
|
||||
relationTo: collection,
|
||||
siblingData,
|
||||
user,
|
||||
})
|
||||
: filterOptions
|
||||
let optionFilter =
|
||||
typeof filterOptions === 'function'
|
||||
? await filterOptions({
|
||||
id,
|
||||
data,
|
||||
relationTo: collection,
|
||||
siblingData,
|
||||
user,
|
||||
})
|
||||
: filterOptions
|
||||
|
||||
if (optionFilter === true) {
|
||||
optionFilter = null
|
||||
if (optionFilter === true) {
|
||||
optionFilter = null
|
||||
}
|
||||
|
||||
const valueIDs: (number | string)[] = []
|
||||
|
||||
values.forEach((val) => {
|
||||
if (typeof val === 'object' && val?.value) {
|
||||
valueIDs.push(val.value)
|
||||
}
|
||||
|
||||
const valueIDs: (number | string)[] = []
|
||||
|
||||
values.forEach((val) => {
|
||||
if (typeof val === 'object' && val?.value) {
|
||||
valueIDs.push(val.value)
|
||||
}
|
||||
|
||||
if (typeof val === 'string' || typeof val === 'number') {
|
||||
valueIDs.push(val)
|
||||
}
|
||||
})
|
||||
|
||||
if (valueIDs.length > 0) {
|
||||
const findWhere = {
|
||||
and: [{ id: { in: valueIDs } }],
|
||||
}
|
||||
|
||||
if (optionFilter) findWhere.and.push(optionFilter)
|
||||
|
||||
if (optionFilter === false) {
|
||||
falseCollections.push(optionFilter)
|
||||
}
|
||||
|
||||
// `req` omitted to prevent transaction errors from aborting the entire transaction
|
||||
const result = await payload.find({
|
||||
collection,
|
||||
depth: 0,
|
||||
limit: 0,
|
||||
pagination: false,
|
||||
where: findWhere,
|
||||
})
|
||||
|
||||
options[collection] = result.docs.map((doc) => doc.id)
|
||||
} else {
|
||||
options[collection] = []
|
||||
if (typeof val === 'string' || typeof val === 'number') {
|
||||
valueIDs.push(val)
|
||||
}
|
||||
} catch (err) {
|
||||
req.payload.logger.error({
|
||||
err,
|
||||
msg: `Error validating filter options for collection ${collection}`,
|
||||
})
|
||||
|
||||
if (valueIDs.length > 0) {
|
||||
const findWhere = {
|
||||
and: [{ id: { in: valueIDs } }],
|
||||
}
|
||||
|
||||
if (optionFilter) findWhere.and.push(optionFilter)
|
||||
|
||||
if (optionFilter === false) {
|
||||
falseCollections.push(optionFilter)
|
||||
}
|
||||
|
||||
const result = await payload.find({
|
||||
collection,
|
||||
depth: 0,
|
||||
limit: 0,
|
||||
pagination: false,
|
||||
req,
|
||||
where: findWhere,
|
||||
})
|
||||
|
||||
options[collection] = result.docs.map((doc) => doc.id)
|
||||
} else {
|
||||
options[collection] = []
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -3,8 +3,10 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Payload } from '../../../payload'
|
||||
import type { Document } from '../../../types'
|
||||
|
||||
import { getDataLoader } from '../../../collections/dataloader'
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import findOne from '../findOne'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['globals']> = {
|
||||
@@ -25,28 +27,55 @@ export default async function findOneLocal<T extends keyof GeneratedTypes['globa
|
||||
options: Options<T>,
|
||||
): Promise<GeneratedTypes['globals'][T]> {
|
||||
const {
|
||||
slug: globalSlug,
|
||||
context,
|
||||
depth,
|
||||
draft = false,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale = payload.config.localization ? payload.config.localization?.defaultLocale : null,
|
||||
overrideAccess = true,
|
||||
showHiddenFields,
|
||||
slug: globalSlug,
|
||||
user,
|
||||
} = options
|
||||
|
||||
const globalConfig = payload.globals.config.find((config) => config.slug === globalSlug)
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
: null
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
|
||||
if (!globalConfig) {
|
||||
throw new APIError(`The global with slug ${String(globalSlug)} can't be found.`)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
const req = {
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale: locale ?? options.req?.locale ?? defaultLocale,
|
||||
payload,
|
||||
payloadAPI: 'local',
|
||||
t: i18n.t,
|
||||
user,
|
||||
} as PayloadRequest
|
||||
setRequestContext(req, context)
|
||||
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return findOne({
|
||||
slug: globalSlug as string,
|
||||
depth,
|
||||
draft,
|
||||
globalConfig,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
slug: globalSlug as string,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import type { Payload } from '../../../payload'
|
||||
import type { Document } from '../../../types'
|
||||
import type { TypeWithVersion } from '../../../versions/types'
|
||||
|
||||
import { getDataLoader } from '../../../collections/dataloader'
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import findVersionByID from '../findVersionByID'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['globals']> = {
|
||||
@@ -28,20 +30,48 @@ export default async function findVersionByIDLocal<T extends keyof GeneratedType
|
||||
): Promise<TypeWithVersion<GeneratedTypes['globals'][T]>> {
|
||||
const {
|
||||
id,
|
||||
slug: globalSlug,
|
||||
context,
|
||||
depth,
|
||||
disableErrors = false,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale = payload.config.localization ? payload.config.localization?.defaultLocale : null,
|
||||
overrideAccess = true,
|
||||
req: incomingReq,
|
||||
showHiddenFields,
|
||||
slug: globalSlug,
|
||||
user,
|
||||
} = options
|
||||
|
||||
const globalConfig = payload.globals.config.find((config) => config.slug === globalSlug)
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
: null
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!globalConfig) {
|
||||
throw new APIError(`The global with slug ${String(globalSlug)} can't be found.`)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
const req = {
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale,
|
||||
payload,
|
||||
payloadAPI: 'local',
|
||||
t: i18n.t,
|
||||
transactionID: incomingReq?.transactionID,
|
||||
user,
|
||||
} as PayloadRequest
|
||||
setRequestContext(req, context)
|
||||
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return findVersionByID({
|
||||
id,
|
||||
|
||||
@@ -5,8 +5,10 @@ import type { Payload } from '../../../payload'
|
||||
import type { Document, Where } from '../../../types'
|
||||
import type { TypeWithVersion } from '../../../versions/types'
|
||||
|
||||
import { getDataLoader } from '../../../collections/dataloader'
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import findVersions from '../findVersions'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['globals']> = {
|
||||
@@ -30,23 +32,51 @@ export default async function findVersionsLocal<T extends keyof GeneratedTypes['
|
||||
options: Options<T>,
|
||||
): Promise<PaginatedDocs<TypeWithVersion<GeneratedTypes['globals'][T]>>> {
|
||||
const {
|
||||
slug: globalSlug,
|
||||
context,
|
||||
depth,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
limit,
|
||||
locale = payload.config.localization ? payload.config.localization?.defaultLocale : null,
|
||||
overrideAccess = true,
|
||||
page,
|
||||
req: incomingReq,
|
||||
showHiddenFields,
|
||||
slug: globalSlug,
|
||||
sort,
|
||||
user,
|
||||
where,
|
||||
} = options
|
||||
|
||||
const globalConfig = payload.globals.config.find((config) => config.slug === globalSlug)
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
: null
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!globalConfig) {
|
||||
throw new APIError(`The global with slug ${String(globalSlug)} can't be found.`)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
const req = {
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale,
|
||||
payload,
|
||||
payloadAPI: 'local',
|
||||
t: i18n.t,
|
||||
transactionID: incomingReq?.transactionID,
|
||||
user,
|
||||
} as PayloadRequest
|
||||
setRequestContext(req, context)
|
||||
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return findVersions({
|
||||
depth,
|
||||
|
||||
@@ -3,8 +3,10 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Payload } from '../../../payload'
|
||||
import type { Document } from '../../../types'
|
||||
|
||||
import { getDataLoader } from '../../../collections/dataloader'
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import restoreVersion from '../restoreVersion'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['globals']> = {
|
||||
@@ -24,15 +26,49 @@ export default async function restoreVersionLocal<T extends keyof GeneratedTypes
|
||||
payload: Payload,
|
||||
options: Options<T>,
|
||||
): Promise<GeneratedTypes['globals'][T]> {
|
||||
const { id, slug: globalSlug, depth, overrideAccess = true, showHiddenFields } = options
|
||||
const {
|
||||
id,
|
||||
context,
|
||||
depth,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale = payload.config.localization ? payload.config.localization?.defaultLocale : null,
|
||||
overrideAccess = true,
|
||||
req: incomingReq,
|
||||
showHiddenFields,
|
||||
slug: globalSlug,
|
||||
user,
|
||||
} = options
|
||||
|
||||
const globalConfig = payload.globals.config.find((config) => config.slug === globalSlug)
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
: null
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!globalConfig) {
|
||||
throw new APIError(`The global with slug ${String(globalSlug)} can't be found.`)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
const req = {
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale,
|
||||
payload,
|
||||
payloadAPI: 'local',
|
||||
t: i18n.t,
|
||||
transactionID: incomingReq?.transactionID,
|
||||
user,
|
||||
} as PayloadRequest
|
||||
setRequestContext(req, context)
|
||||
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return restoreVersion({
|
||||
id,
|
||||
|
||||
@@ -5,8 +5,10 @@ import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Payload } from '../../../payload'
|
||||
import type { Document } from '../../../types'
|
||||
|
||||
import { getDataLoader } from '../../../collections/dataloader'
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import { setRequestContext } from '../../../express/setRequestContext'
|
||||
import { i18nInit } from '../../../translations/init'
|
||||
import update from '../update'
|
||||
|
||||
export type Options<TSlug extends keyof GeneratedTypes['globals']> = {
|
||||
@@ -27,18 +29,52 @@ export default async function updateLocal<TSlug extends keyof GeneratedTypes['gl
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<GeneratedTypes['globals'][TSlug]> {
|
||||
const { slug: globalSlug, data, depth, draft, overrideAccess = true, showHiddenFields } = options
|
||||
const {
|
||||
context,
|
||||
data,
|
||||
depth,
|
||||
draft,
|
||||
fallbackLocale: fallbackLocaleArg = options?.req?.fallbackLocale,
|
||||
locale = payload.config.localization ? payload.config.localization?.defaultLocale : null,
|
||||
overrideAccess = true,
|
||||
req: incomingReq,
|
||||
showHiddenFields,
|
||||
slug: globalSlug,
|
||||
user,
|
||||
} = options
|
||||
|
||||
const globalConfig = payload.globals.config.find((config) => config.slug === globalSlug)
|
||||
const localizationConfig = payload?.config?.localization
|
||||
const defaultLocale = payload?.config?.localization
|
||||
? payload?.config?.localization?.defaultLocale
|
||||
: null
|
||||
const fallbackLocale = localizationConfig
|
||||
? localizationConfig.locales.find(({ code }) => locale === code)?.fallbackLocale
|
||||
: null
|
||||
const i18n = i18nInit(payload.config.i18n)
|
||||
|
||||
if (!globalConfig) {
|
||||
throw new APIError(`The global with slug ${String(globalSlug)} can't be found.`)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
const req = {
|
||||
fallbackLocale:
|
||||
typeof fallbackLocaleArg !== 'undefined'
|
||||
? fallbackLocaleArg
|
||||
: fallbackLocale || defaultLocale,
|
||||
i18n,
|
||||
locale,
|
||||
payload,
|
||||
payloadAPI: 'local',
|
||||
t: i18n.t,
|
||||
transactionID: incomingReq?.transactionID,
|
||||
user,
|
||||
} as PayloadRequest
|
||||
setRequestContext(req, context)
|
||||
|
||||
if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req)
|
||||
|
||||
return update<TSlug>({
|
||||
slug: globalSlug as string,
|
||||
data,
|
||||
depth,
|
||||
draft,
|
||||
@@ -46,5 +82,6 @@ export default async function updateLocal<TSlug extends keyof GeneratedTypes['gl
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
slug: globalSlug as string,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
import type { Request } from 'express'
|
||||
|
||||
import type { File, FileData } from './types'
|
||||
|
||||
import { Request } from 'express'
|
||||
import { APIError } from '../errors'
|
||||
|
||||
type Args = {
|
||||
data: FileData
|
||||
req: Request
|
||||
data: FileData
|
||||
}
|
||||
export const getExternalFile = async ({ data, req }: Args): Promise<File> => {
|
||||
const { filename, url } = data
|
||||
export const getExternalFile = async ({ req, data }: Args): Promise<File> => {
|
||||
const baseUrl = req.get('origin') || `${req.protocol}://${req.get('host')}`
|
||||
const { url, filename } = data
|
||||
|
||||
if (typeof url === 'string') {
|
||||
let fileURL = url
|
||||
if (!url.startsWith('http')) {
|
||||
const baseUrl = req.get('origin') || `${req.protocol}://${req.get('host')}`
|
||||
fileURL = `${baseUrl}${url}`
|
||||
}
|
||||
|
||||
const fileURL = `${baseUrl}${url}`
|
||||
const { default: fetch } = (await import('node-fetch')) as any
|
||||
|
||||
const res = await fetch(fileURL, {
|
||||
credentials: 'include',
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...req.headers,
|
||||
},
|
||||
method: 'GET',
|
||||
})
|
||||
|
||||
if (!res.ok) throw new APIError(`Failed to fetch file from ${fileURL}`, res.status)
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import type { Payload, RequestContext } from '..'
|
||||
import type { PayloadRequest } from '../exports/types'
|
||||
|
||||
import { getDataLoader } from '../collections/dataloader'
|
||||
import { i18nInit } from '../translations/init'
|
||||
|
||||
function getRequestContext(
|
||||
req: PayloadRequest = { context: null } as PayloadRequest,
|
||||
context: RequestContext = {},
|
||||
): RequestContext {
|
||||
if (req.context) {
|
||||
if (Object.keys(req.context).length === 0 && req.context.constructor === Object) {
|
||||
// if req.context is `{}` avoid unnecessary spread
|
||||
return context
|
||||
} else {
|
||||
return { ...req.context, ...context }
|
||||
}
|
||||
} else {
|
||||
return context
|
||||
}
|
||||
}
|
||||
|
||||
type CreateLocalReq = (
|
||||
options: {
|
||||
collection?: number | string | symbol
|
||||
context?: RequestContext
|
||||
fallbackLocale?: string
|
||||
locale?: string
|
||||
req?: PayloadRequest
|
||||
user?: Document
|
||||
},
|
||||
payload: Payload,
|
||||
) => PayloadRequest
|
||||
export const createLocalReq: CreateLocalReq = (
|
||||
{ collection, context, fallbackLocale, locale, req = {} as PayloadRequest, user },
|
||||
payload,
|
||||
) => {
|
||||
const i18n = req?.i18n || i18nInit(payload.config?.i18n)
|
||||
|
||||
if (payload.config?.localization) {
|
||||
const defaultLocale = payload.config.localization.defaultLocale
|
||||
req.locale = locale || req?.locale || defaultLocale
|
||||
const fallbackLocaleFromConfig = payload.config.localization.locales.find(
|
||||
({ code }) => req.locale === code,
|
||||
)?.fallbackLocale
|
||||
if (typeof fallbackLocale !== 'undefined') {
|
||||
req.fallbackLocale = fallbackLocale
|
||||
} else if (typeof req?.fallbackLocale === 'undefined') {
|
||||
req.fallbackLocale = fallbackLocaleFromConfig || defaultLocale
|
||||
}
|
||||
}
|
||||
|
||||
req.context = getRequestContext(req, context)
|
||||
req.payloadAPI = req?.payloadAPI || 'local'
|
||||
req.payload = payload
|
||||
req.i18n = i18n
|
||||
req.t = i18n.t
|
||||
req.user = user || req?.user || null
|
||||
req.collection = collection ? payload.collections?.[collection] : null
|
||||
req.payloadDataLoader = req?.payloadDataLoader || getDataLoader(req)
|
||||
|
||||
return req
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
version: '3.2'
|
||||
services:
|
||||
localstack:
|
||||
image: localstack/localstack:latest
|
||||
container_name: localstack_demo
|
||||
ports:
|
||||
- '4563-4599:4563-4599'
|
||||
- '8055:8080'
|
||||
environment:
|
||||
- SERVICES=s3
|
||||
- DEBUG=1
|
||||
- DATA_DIR=/tmp/localstack/data
|
||||
volumes:
|
||||
- './.localstack:/var/lib/localstack'
|
||||
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||
|
||||
azure-storage:
|
||||
image: mcr.microsoft.com/azure-storage/azurite:3.18.0
|
||||
restart: always
|
||||
command: 'azurite --loose --blobHost 0.0.0.0 --tableHost 0.0.0.0 --queueHost 0.0.0.0'
|
||||
ports:
|
||||
- '10000:10000'
|
||||
- '10001:10001'
|
||||
- '10002:10002'
|
||||
volumes:
|
||||
- ./azurestoragedata:/data"
|
||||
|
||||
google-cloud-storage:
|
||||
image: fsouza/fake-gcs-server
|
||||
restart: always
|
||||
command:
|
||||
[
|
||||
'-scheme',
|
||||
'http',
|
||||
'-port',
|
||||
'4443',
|
||||
'-public-host',
|
||||
'http://localhost:4443',
|
||||
'-external-url',
|
||||
'http://localhost:4443',
|
||||
'-backend',
|
||||
'memory',
|
||||
]
|
||||
ports:
|
||||
- '4443:4443'
|
||||
volumes:
|
||||
- ./google-cloud-storage/payload-bucket:/data/payload-bucket
|
||||
|
||||
volumes:
|
||||
google-cloud-storage:
|
||||
azurestoragedata:
|
||||
@@ -52,7 +52,7 @@
|
||||
"@types/find-node-modules": "^2.1.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^8.2.0",
|
||||
"nodemon": "3.0.3",
|
||||
"nodemon": "^2.0.6",
|
||||
"payload": "workspace:*",
|
||||
"rimraf": "^4.1.2",
|
||||
"ts-node": "^9.1.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-form-builder",
|
||||
"description": "Form builder plugin for Payload CMS",
|
||||
"version": "1.2.1",
|
||||
"version": "1.2.0",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": "git@github.com:payloadcms/plugin-form-builder.git",
|
||||
"main": "dist/index.js",
|
||||
@@ -31,7 +31,7 @@
|
||||
"@types/react": "18.2.15",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"nodemon": "3.0.3",
|
||||
"nodemon": "^3.0.2",
|
||||
"payload": "workspace:*",
|
||||
"react": "^18.0.0",
|
||||
"ts-node": "10.9.1"
|
||||
|
||||
@@ -5,7 +5,7 @@ import { replaceDoubleCurlys } from '../../../utilities/replaceDoubleCurlys'
|
||||
import { serializeSlate } from '../../../utilities/slate/serializeSlate'
|
||||
|
||||
const sendEmail = async (beforeChangeData: any, formConfig: PluginConfig): Promise<any> => {
|
||||
const { data, operation, req } = beforeChangeData
|
||||
const { data, operation } = beforeChangeData
|
||||
|
||||
if (operation === 'create') {
|
||||
const {
|
||||
@@ -22,7 +22,6 @@ const sendEmail = async (beforeChangeData: any, formConfig: PluginConfig): Promi
|
||||
id: formID,
|
||||
collection: formOverrides?.slug || 'forms',
|
||||
locale,
|
||||
req,
|
||||
})
|
||||
|
||||
const { emails } = form
|
||||
|
||||
@@ -11,7 +11,6 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
|
||||
const newConfig: CollectionConfig = {
|
||||
...(formConfig?.formSubmissionOverrides || {}),
|
||||
slug: formConfig?.formSubmissionOverrides?.slug || 'form-submissions',
|
||||
access: {
|
||||
create: () => true,
|
||||
read: ({ req: { user } }) => !!user, // logged-in users,
|
||||
@@ -25,13 +24,13 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
fields: [
|
||||
{
|
||||
name: 'form',
|
||||
type: 'relationship',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
relationTo: formSlug,
|
||||
required: true,
|
||||
validate: async (value, { payload, req }) => {
|
||||
type: 'relationship',
|
||||
validate: async (value, { payload }) => {
|
||||
/* Don't run in the client side */
|
||||
if (!payload) return true
|
||||
|
||||
@@ -42,7 +41,6 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
existingForm = await payload.findByID({
|
||||
id: value,
|
||||
collection: formSlug,
|
||||
req,
|
||||
})
|
||||
|
||||
return true
|
||||
@@ -54,20 +52,19 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
},
|
||||
{
|
||||
name: 'submissionData',
|
||||
type: 'array',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'field',
|
||||
type: 'text',
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
type: 'text',
|
||||
required: true,
|
||||
type: 'text',
|
||||
validate: (value: unknown) => {
|
||||
// TODO:
|
||||
// create a validation function that dynamically
|
||||
@@ -87,6 +84,7 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'array',
|
||||
},
|
||||
...(formConfig?.formSubmissionOverrides?.fields || []),
|
||||
],
|
||||
@@ -98,6 +96,7 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
],
|
||||
...(formConfig?.formSubmissionOverrides?.hooks || {}),
|
||||
},
|
||||
slug: formConfig?.formSubmissionOverrides?.slug || 'form-submissions',
|
||||
}
|
||||
|
||||
const paymentFieldConfig = formConfig?.fields?.payment
|
||||
@@ -105,27 +104,26 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
if (paymentFieldConfig) {
|
||||
newConfig.fields.push({
|
||||
name: 'payment',
|
||||
type: 'group',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'field',
|
||||
type: 'text',
|
||||
label: 'Field',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
type: 'text',
|
||||
label: 'Status',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'amount',
|
||||
type: 'number',
|
||||
admin: {
|
||||
description: 'Amount in cents',
|
||||
},
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
name: 'paymentProcessor',
|
||||
@@ -133,27 +131,28 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
},
|
||||
{
|
||||
name: 'creditCard',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'token',
|
||||
type: 'text',
|
||||
label: 'token',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'brand',
|
||||
type: 'text',
|
||||
label: 'Brand',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'number',
|
||||
type: 'text',
|
||||
label: 'Number',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
label: 'Credit Card',
|
||||
type: 'group',
|
||||
},
|
||||
],
|
||||
type: 'group',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-search",
|
||||
"version": "1.1.0",
|
||||
"version": "1.0.1",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": "git@github.com:payloadcms/plugin-search.git",
|
||||
"description": "Search plugin for Payload",
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
import type { CollectionAfterDeleteHook } from 'payload/types'
|
||||
|
||||
const deleteFromSearch: CollectionAfterDeleteHook = async ({ doc, req: { payload }, req }) => {
|
||||
const deleteFromSearch: CollectionAfterDeleteHook = ({ doc, req: { payload } }) => {
|
||||
try {
|
||||
const searchDocQuery = await payload.find({
|
||||
collection: 'search',
|
||||
depth: 0,
|
||||
req,
|
||||
where: {
|
||||
'doc.value': {
|
||||
equals: doc.id,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (searchDocQuery?.docs?.[0]) {
|
||||
await payload.delete({
|
||||
id: searchDocQuery?.docs?.[0]?.id,
|
||||
const deleteSearchDoc = async (): Promise<any> => {
|
||||
const searchDocQuery = await payload.find({
|
||||
collection: 'search',
|
||||
req,
|
||||
depth: 0,
|
||||
where: {
|
||||
'doc.value': {
|
||||
equals: doc.id,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (searchDocQuery?.docs?.[0]) {
|
||||
payload.delete({
|
||||
id: searchDocQuery?.docs?.[0]?.id,
|
||||
collection: 'search',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deleteSearchDoc()
|
||||
} catch (err: unknown) {
|
||||
payload.logger.error({
|
||||
err: `Error deleting search doc: ${err}`,
|
||||
|
||||
@@ -6,7 +6,6 @@ const syncWithSearch: SyncWithSearch = async (args) => {
|
||||
doc,
|
||||
operation,
|
||||
req: { payload },
|
||||
req,
|
||||
// @ts-expect-error
|
||||
searchConfig,
|
||||
} = args
|
||||
@@ -27,7 +26,6 @@ const syncWithSearch: SyncWithSearch = async (args) => {
|
||||
dataToSave = await beforeSync({
|
||||
originalDoc: doc,
|
||||
payload,
|
||||
req,
|
||||
searchDoc: dataToSave,
|
||||
})
|
||||
}
|
||||
@@ -55,13 +53,13 @@ const syncWithSearch: SyncWithSearch = async (args) => {
|
||||
try {
|
||||
if (operation === 'create') {
|
||||
if (doSync) {
|
||||
await payload.create({
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
payload.create({
|
||||
collection: 'search',
|
||||
data: {
|
||||
...dataToSave,
|
||||
priority: defaultPriority,
|
||||
},
|
||||
req,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -72,7 +70,6 @@ const syncWithSearch: SyncWithSearch = async (args) => {
|
||||
const searchDocQuery = await payload.find({
|
||||
collection: 'search',
|
||||
depth: 0,
|
||||
req,
|
||||
where: {
|
||||
'doc.value': {
|
||||
equals: id,
|
||||
@@ -91,12 +88,15 @@ const syncWithSearch: SyncWithSearch = async (args) => {
|
||||
// to ensure the same, out-of-date result does not appear twice (where only syncing the first found doc)
|
||||
if (duplicativeDocs.length > 0) {
|
||||
try {
|
||||
const duplicativeDocIDs = duplicativeDocs.map(({ id }) => id)
|
||||
await payload.delete({
|
||||
collection: 'search',
|
||||
req,
|
||||
where: { id: { in: duplicativeDocIDs } },
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
Promise.all(
|
||||
duplicativeDocs.map(({ id: duplicativeDocID }) =>
|
||||
payload.delete({
|
||||
id: duplicativeDocID,
|
||||
collection: 'search',
|
||||
}),
|
||||
), // eslint-disable-line function-paren-newline
|
||||
)
|
||||
} catch (err: unknown) {
|
||||
payload.logger.error(`Error deleting duplicative search documents.`)
|
||||
}
|
||||
@@ -108,14 +108,14 @@ const syncWithSearch: SyncWithSearch = async (args) => {
|
||||
if (doSync) {
|
||||
// update the doc normally
|
||||
try {
|
||||
await payload.update({
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
payload.update({
|
||||
id: searchDocID,
|
||||
collection: 'search',
|
||||
data: {
|
||||
...dataToSave,
|
||||
priority: foundDoc.priority || defaultPriority,
|
||||
},
|
||||
req,
|
||||
})
|
||||
} catch (err: unknown) {
|
||||
payload.logger.error(`Error updating search document.`)
|
||||
@@ -124,10 +124,10 @@ const syncWithSearch: SyncWithSearch = async (args) => {
|
||||
if (deleteDrafts && status === 'draft') {
|
||||
// do not include draft docs in search results, so delete the record
|
||||
try {
|
||||
await payload.delete({
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
payload.delete({
|
||||
id: searchDocID,
|
||||
collection: 'search',
|
||||
req,
|
||||
})
|
||||
} catch (err: unknown) {
|
||||
payload.logger.error(`Error deleting search document: ${err}`)
|
||||
@@ -135,13 +135,13 @@ const syncWithSearch: SyncWithSearch = async (args) => {
|
||||
}
|
||||
} else if (doSync) {
|
||||
try {
|
||||
await payload.create({
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
payload.create({
|
||||
collection: 'search',
|
||||
data: {
|
||||
...dataToSave,
|
||||
priority: defaultPriority,
|
||||
},
|
||||
req,
|
||||
})
|
||||
} catch (err: unknown) {
|
||||
payload.logger.error(`Error creating search document: ${err}`)
|
||||
|
||||
@@ -34,7 +34,7 @@ const Search =
|
||||
afterChange: [
|
||||
...(existingHooks?.afterChange || []),
|
||||
async (args: any) => {
|
||||
await syncWithSearch({
|
||||
syncWithSearch({
|
||||
...args,
|
||||
collection: collection.slug,
|
||||
searchConfig,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Payload } from 'payload'
|
||||
import type { CollectionAfterChangeHook, CollectionConfig, PayloadRequest } from 'payload/types'
|
||||
import type { CollectionAfterChangeHook, CollectionConfig } from 'payload/types'
|
||||
|
||||
export interface DocToSync {
|
||||
[key: string]: any
|
||||
@@ -15,7 +15,6 @@ export type BeforeSync = (args: {
|
||||
[key: string]: any
|
||||
}
|
||||
payload: Payload
|
||||
req: PayloadRequest
|
||||
searchDoc: DocToSync
|
||||
}) => DocToSync | Promise<DocToSync>
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^8.2.0",
|
||||
"jest": "^29.5.0",
|
||||
"nodemon": "3.0.3",
|
||||
"nodemon": "^2.0.6",
|
||||
"payload": "workspace:*",
|
||||
"ts-jest": "^29.1.0",
|
||||
"webpack": "^5.78.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-seo",
|
||||
"version": "2.2.1",
|
||||
"version": "2.2.0",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": "git@github.com:payloadcms/plugin-seo.git",
|
||||
"description": "SEO plugin for Payload",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user