├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── apps ├── cms │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── access │ │ │ └── index.ts │ │ ├── blocks │ │ │ ├── CallToAction.ts │ │ │ ├── Content.ts │ │ │ └── Image.ts │ │ ├── collections │ │ │ ├── Media.ts │ │ │ ├── Pages.ts │ │ │ └── Users.ts │ │ ├── components │ │ │ └── Select.tsx │ │ ├── config.ts │ │ ├── index.ts │ │ ├── payload-schema.graphql │ │ ├── payload-types.ts │ │ ├── seed │ │ │ ├── home-page.json │ │ │ ├── index.ts │ │ │ ├── payload.jpg │ │ │ └── posts-page.ts │ │ ├── types.ts │ │ └── utilities │ │ │ └── formatSlug.ts │ └── tsconfig.json ├── server │ ├── .env │ ├── .gitignore │ ├── nodemon.json │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json └── web │ ├── .eslintrc │ ├── .gitignore │ ├── app │ ├── assets │ │ └── payload-logo.svg │ ├── components │ │ ├── Blocks │ │ │ ├── RenderBlocks.tsx │ │ │ ├── RichText.tsx │ │ │ ├── index.tsx │ │ │ └── sections │ │ │ │ ├── CallToAction.tsx │ │ │ │ ├── Content.tsx │ │ │ │ ├── Image.tsx │ │ │ │ └── sizes.js │ │ └── Logo.tsx │ ├── entry.client.tsx │ ├── entry.server.tsx │ ├── root.tsx │ ├── routes │ │ ├── __page.tsx │ │ ├── __page │ │ │ ├── $page.tsx │ │ │ └── index.tsx │ │ ├── login.tsx │ │ └── logout.tsx │ ├── styles │ │ └── global.css │ └── utils │ │ └── index.ts │ ├── express.d.ts │ ├── express.js │ ├── package.json │ ├── public │ └── favicon.ico │ ├── remix.config.js │ ├── remix.env.d.ts │ └── tsconfig.json ├── package.json ├── packages ├── eslint-config-custom │ ├── index.js │ ├── package.json │ └── remix.js ├── shared │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json └── ui │ ├── package.json │ ├── src │ ├── components │ │ └── Button.tsx │ ├── index.tsx │ └── styles │ │ └── ui.css │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── tsconfig.json └── turbo.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | !.* 3 | dist 4 | build 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@org/eslint-config'], 3 | root: true 4 | }; 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | .next/ 12 | out/ 13 | build 14 | dist 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | .npm-debug.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | 32 | # turbo 33 | .turbo 34 | 35 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | 3 | dist 4 | build 5 | node_modules 6 | .cache 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Payload 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Payload CMS](https://payloadcms.com/) and [Remix](https://remix.run/) monorepo 2 | 3 | This is an example repository of how to set up Payload CMS for content management together with Remix, in such a manner that each application is divided into its own package (including the express server app). 4 | 5 | The Payload instance is injected into Remix loaders and actions through request context. This way we are free to use the Payload Local API for data, user and authentication management, while avoiding payload having to be bundled together with the remix server build. With this setup you can even use the Payload Authentication middleware in your remix application. 6 | 7 | ## What's inside? 8 | 9 | This monorepo is using `pnpm` for package management. While it is an easy task to switch to `yarn` instead, setting this up with `npm` workspaces can be trickier. For monorepos we do recommend `pnpm` due to its ability to handle hoisting in a better way, which avoids dependency related issues. 10 | 11 | ### Apps and Packages 12 | 13 | - `/apps/cms`: a [Payload CMS](https://payloadcms.com/) application, which will act as our backend and admin interface 14 | - `/apps/web`: a [Remix](https://remix.run/) application, which will act as our frontend 15 | - `/apps/server`: a [ExpressJS](https://expressjs.com/) application that ties all our middleware, static file serving and routing needs together 16 | - `/packages/ui`: a stub React component library shared by both `web` and `cms` applications 17 | - `/packages/shared`: a package that all out apps use that contains shared dependencies, in order to reduce bundle sizes 18 | - `/packages/eslint-config-custom`: `eslint` configurations 19 | 20 | ### Utilities 21 | 22 | - [TypeScript](https://www.typescriptlang.org/) for static type checking 23 | - [ESLint](https://eslint.org/) for code linting 24 | - [Prettier](https://prettier.io) for code formatting 25 | - [Turborepo](https://turborepo.org/) for running monorepo builds and script in a DX friendly and parallel manner 26 | - [Nodemon](https://www.npmjs.com/package/nodemon) for running the express server while listening to file changes in the Payload CMS package 27 | 28 | ## Setup 29 | 30 | Get started by running `pnpm install` from the root of the monorepo. Create a `/apps/server/.env.local` file based on `/apps/server/.env` and add your connection string to MongoDB as well as a secret for PayloadCMS to use in order to keep your data secure. `.env` will be loaded first, and then `.env.local`. That way you can keep your non-secret variables in the `.env` files which also is commited to git, and secret variables in `.env.local` that isn't commited to git. 31 | 32 | ### Develop 33 | 34 | To develop all apps and packages, run `pnpm run dev` from the root of the monorepo. This will start the express server that serves both the Remix and PayloadCMS applications. 35 | When saving file changes in the `apps/cms` package, the running express server will restart in order for the Payload CMS configuration changes to take effect. 36 | Remix is reloaded without restarting the express server by purging the node `require()` cache of previously imported Remix files. 37 | 38 | ### Build 39 | 40 | To build all apps and packages, run `pnpm run build` from the root of the monorepo. Turborepo will take care of running the build scripts in order so that packages depending on other monorepo packages is built last. 41 | 42 | If you want, serve your production build with `pnpm run serve` from the root of the monorepo. 43 | -------------------------------------------------------------------------------- /apps/cms/.gitignore: -------------------------------------------------------------------------------- 1 | ### Node ### 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .npm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # Snowpack dependency directory (https://snowpack.dev/) 47 | web_modules/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Optional stylelint cache 59 | .stylelintcache 60 | 61 | # Microbundle cache 62 | .rpt2_cache/ 63 | .rts2_cache_cjs/ 64 | .rts2_cache_es/ 65 | .rts2_cache_umd/ 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # dotenv environment variable files 74 | .env 75 | .env.development.local 76 | .env.test.local 77 | .env.production.local 78 | .env.local 79 | 80 | # parcel-bundler cache (https://parceljs.org/) 81 | .cache 82 | .parcel-cache 83 | 84 | # Next.js build output 85 | .next 86 | out 87 | 88 | # Nuxt.js build / generate output 89 | .nuxt 90 | dist 91 | 92 | # Gatsby files 93 | .cache/ 94 | # Comment in the public line in if your project uses Gatsby and not Next.js 95 | # https://nextjs.org/blog/next-9-1#public-directory-support 96 | # public 97 | 98 | # vuepress build output 99 | .vuepress/dist 100 | 101 | # vuepress v2.x temp and cache directory 102 | .temp 103 | 104 | # Docusaurus cache and generated files 105 | .docusaurus 106 | 107 | # Serverless directories 108 | .serverless/ 109 | 110 | # FuseBox cache 111 | .fusebox/ 112 | 113 | # DynamoDB Local files 114 | .dynamodb/ 115 | 116 | # TernJS port file 117 | .tern-port 118 | 119 | # Stores VSCode versions used for testing VSCode extensions 120 | .vscode-test 121 | 122 | ### Node Patch ### 123 | # Serverless Webpack directories 124 | .webpack/ 125 | 126 | # Optional stylelint cache 127 | 128 | # SvelteKit build / generate output 129 | .svelte-kit 130 | 131 | ### VisualStudioCode ### 132 | .vscode/* 133 | !.vscode/settings.json 134 | !.vscode/tasks.json 135 | !.vscode/launch.json 136 | !.vscode/extensions.json 137 | !.vscode/*.code-snippets 138 | 139 | # Local History for Visual Studio Code 140 | .history/ 141 | 142 | # Built Visual Studio Code Extensions 143 | *.vsix 144 | 145 | ### VisualStudioCode Patch ### 146 | # Ignore all local history of files 147 | .history 148 | .ionide 149 | 150 | # Support for Project snippet scope 151 | .vscode/*.code-snippets 152 | 153 | # Ignore code-workspaces 154 | *.code-workspace 155 | 156 | # End of https://www.toptal.com/developers/gitignore/api/node,visualstudiocode 157 | 158 | src/media 159 | -------------------------------------------------------------------------------- /apps/cms/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@org/cms", 3 | "description": "Payload CMS instance", 4 | "version": "1.0.0", 5 | "license": "MIT", 6 | "types": "./src/index.ts", 7 | "exports": { 8 | ".": { 9 | "serve": { 10 | "types": "./dist/index.js", 11 | "require": "./dist/index.js", 12 | "import": "./dist/index.js" 13 | }, 14 | "default": { 15 | "types": "./src/index.ts", 16 | "require": "./src/index.ts", 17 | "import": "./src/index.ts" 18 | } 19 | }, 20 | "./types": { 21 | "types": "./src/types.ts", 22 | "require": "./src/types.ts", 23 | "import": "./src/types.ts" 24 | } 25 | }, 26 | "scripts": { 27 | "clean": "rm -rf node_modules dist build .turbo", 28 | "build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/config.ts payload build", 29 | "build:server": "rm -rf dist/* && tsc", 30 | "build": "pnpm build:payload && pnpm build:server && pnpm copyfiles", 31 | "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/ && copyfiles -u 1 \"build/**/*\" ../server/build", 32 | "generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/config.ts payload generate:types", 33 | "generate:graphQLSchema": "PAYLOAD_CONFIG_PATH=src/config.ts payload generate:graphQLSchema", 34 | "lint": "eslint --ext .ts,.tsx,.js,.jsx ./src" 35 | }, 36 | "dependencies": { 37 | "@org/ui": "workspace:*", 38 | "payload": "^1.6.9" 39 | }, 40 | "devDependencies": { 41 | "@org/shared": "workspace:*", 42 | "@types/express": "^4.17.17", 43 | "@types/node": "^18.13.0", 44 | "@types/react": "^18.0.27", 45 | "@types/react-router-dom": "^5.3.3", 46 | "copyfiles": "^2.4.1", 47 | "cross-env": "^7.0.3", 48 | "nodemon": "^2.0.20", 49 | "react": "^18.2.0", 50 | "react-router-dom": "^5.3.4", 51 | "ts-node": "^10.9.1", 52 | "typescript": "^4.9.5" 53 | }, 54 | "peerDependencies": { 55 | "@org/shared": "workspace:*", 56 | "react": "^18.2.0", 57 | "react-router-dom": "^5.3.4" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /apps/cms/src/access/index.ts: -------------------------------------------------------------------------------- 1 | import type { PayloadRequest, Where } from 'payload/types'; 2 | 3 | export const authenticatedAndAdmin = ({ req: { user } }: {req: PayloadRequest}) => 4 | !!user && user?.role === 'admin'; 5 | 6 | export const pageIsPublic = (): Where => ({ 7 | public: { 8 | equals: true, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /apps/cms/src/blocks/CallToAction.ts: -------------------------------------------------------------------------------- 1 | import type { Block } from 'payload/types'; 2 | 3 | type Data = Record; 4 | 5 | const customURLCondition = (_: Data, siblings: Data): boolean => 6 | siblings.type === 'custom'; 7 | 8 | export const CallToAction: Block = { 9 | slug: 'cta', 10 | labels: { 11 | singular: 'Call to Action', 12 | plural: 'Calls to Action', 13 | }, 14 | fields: [ 15 | { 16 | name: 'content', 17 | type: 'richText', 18 | }, 19 | { 20 | name: 'buttons', 21 | type: 'array', 22 | label: 'Buttons', 23 | minRows: 1, 24 | maxRows: 2, 25 | labels: { 26 | singular: 'Button', 27 | plural: 'Buttons', 28 | }, 29 | fields: [ 30 | { 31 | type: 'row', 32 | fields: [ 33 | { 34 | name: 'label', 35 | label: 'Button Label', 36 | type: 'text', 37 | required: true, 38 | admin: { 39 | width: '50%', 40 | }, 41 | }, 42 | { 43 | name: 'type', 44 | label: 'Button Type', 45 | required: true, 46 | type: 'radio', 47 | defaultValue: 'page', 48 | options: [ 49 | { 50 | label: 'Page', 51 | value: 'page', 52 | }, 53 | { 54 | label: 'Custom URL', 55 | value: 'custom', 56 | }, 57 | ], 58 | admin: { 59 | width: '50%', 60 | layout: 'horizontal', 61 | }, 62 | }, 63 | ], 64 | }, 65 | { 66 | name: 'page', 67 | label: 'Page to link to', 68 | type: 'relationship', 69 | relationTo: 'pages', 70 | admin: { 71 | condition: (_: Data, siblings: Data): boolean => 72 | siblings.type === 'page', 73 | }, 74 | }, 75 | { 76 | name: 'url', 77 | label: 'Button URL', 78 | type: 'text', 79 | admin: { 80 | condition: customURLCondition, 81 | }, 82 | }, 83 | { 84 | name: 'newTab', 85 | type: 'checkbox', 86 | label: 'Open in new tab', 87 | required: true, 88 | admin: { 89 | condition: customURLCondition, 90 | }, 91 | }, 92 | ], 93 | }, 94 | ], 95 | }; 96 | -------------------------------------------------------------------------------- /apps/cms/src/blocks/Content.ts: -------------------------------------------------------------------------------- 1 | import type { Block } from 'payload/types'; 2 | 3 | export type Type = { 4 | blockType: 'content'; 5 | blockName?: string; 6 | content: unknown; 7 | }; 8 | 9 | export const Content: Block = { 10 | slug: 'content', 11 | labels: { 12 | singular: 'Content', 13 | plural: 'Content Blocks', 14 | }, 15 | fields: [ 16 | { 17 | name: 'content', 18 | type: 'richText', 19 | }, 20 | ], 21 | }; 22 | 23 | export default Content; 24 | -------------------------------------------------------------------------------- /apps/cms/src/blocks/Image.ts: -------------------------------------------------------------------------------- 1 | import type { Block } from 'payload/types'; 2 | 3 | export const Image: Block = { 4 | slug: 'image', 5 | labels: { 6 | singular: 'Image', 7 | plural: 'Images', 8 | }, 9 | fields: [ 10 | { 11 | name: 'image', 12 | label: 'Image', 13 | type: 'upload', 14 | relationTo: 'media', 15 | required: true, 16 | }, 17 | { 18 | name: 'type', 19 | label: 'Type', 20 | type: 'radio', 21 | defaultValue: 'normal', 22 | options: [ 23 | { 24 | label: 'Card', 25 | value: 'card', 26 | }, 27 | { 28 | label: 'Feature', 29 | value: 'feature', 30 | }, 31 | ], 32 | required: true, 33 | admin: { 34 | layout: 'horizontal', 35 | }, 36 | }, 37 | { 38 | name: 'caption', 39 | label: 'Caption', 40 | type: 'richText', 41 | admin: { 42 | elements: ['link'], 43 | }, 44 | }, 45 | ], 46 | }; 47 | -------------------------------------------------------------------------------- /apps/cms/src/collections/Media.ts: -------------------------------------------------------------------------------- 1 | import { CollectionConfig } from 'payload/types'; 2 | 3 | export const mediaSlug = 'media'; 4 | const Media: CollectionConfig = { 5 | slug: mediaSlug, 6 | access: { 7 | read: (): boolean => true, // Everyone can read Media 8 | }, 9 | upload: { 10 | adminThumbnail: 'card', 11 | imageSizes: [ 12 | { 13 | name: 'card', 14 | width: 640, 15 | height: 480, 16 | }, 17 | { 18 | name: 'feature', 19 | width: 1024, 20 | height: 576, 21 | }, 22 | ], 23 | }, 24 | fields: [ 25 | { 26 | name: 'alt', 27 | label: 'Alt Text', 28 | type: 'text', 29 | required: true, 30 | }, 31 | ], 32 | }; 33 | 34 | export default Media; 35 | -------------------------------------------------------------------------------- /apps/cms/src/collections/Pages.ts: -------------------------------------------------------------------------------- 1 | import { CollectionConfig } from 'payload/types'; 2 | import formatSlug from '../utilities/formatSlug'; 3 | import { Image } from '../blocks/Image'; 4 | import { CallToAction } from '../blocks/CallToAction'; 5 | import { Content } from '../blocks/Content'; 6 | import { mediaSlug } from './Media'; 7 | import { authenticatedAndAdmin, pageIsPublic } from '../access/index'; 8 | 9 | export const pagesSlug = 'pages'; 10 | export const Pages: CollectionConfig = { 11 | slug: pagesSlug, 12 | admin: { 13 | useAsTitle: 'title', 14 | }, 15 | access: { 16 | read: ({ req }) => { 17 | if (authenticatedAndAdmin({ req })) return true; 18 | return pageIsPublic(); 19 | }, 20 | create: authenticatedAndAdmin, 21 | update: authenticatedAndAdmin, 22 | delete: authenticatedAndAdmin, 23 | }, 24 | fields: [ 25 | { 26 | name: 'title', 27 | label: 'Page Title', 28 | type: 'text', 29 | required: true, 30 | }, 31 | { 32 | name: 'image', 33 | label: 'Featured Image', 34 | type: 'upload', 35 | relationTo: mediaSlug, 36 | }, 37 | { 38 | name: 'public', 39 | label: 'Public', 40 | type: 'checkbox', 41 | defaultValue: false, 42 | }, 43 | { 44 | name: 'layout', 45 | label: 'Page Layout', 46 | type: 'blocks', 47 | minRows: 1, 48 | blocks: [CallToAction, Content, Image], 49 | }, 50 | { 51 | name: 'meta', 52 | label: 'Page Meta', 53 | type: 'group', 54 | fields: [ 55 | { 56 | name: 'title', 57 | label: 'Title', 58 | type: 'text', 59 | }, 60 | { 61 | name: 'description', 62 | label: 'Description', 63 | type: 'textarea', 64 | }, 65 | { 66 | name: 'keywords', 67 | label: 'Keywords', 68 | type: 'text', 69 | }, 70 | ], 71 | }, 72 | { 73 | name: 'slug', 74 | label: 'Page Slug', 75 | type: 'text', 76 | admin: { 77 | position: 'sidebar', 78 | }, 79 | hooks: { 80 | beforeValidate: [formatSlug('title')], 81 | }, 82 | }, 83 | ], 84 | }; 85 | 86 | export default Pages; 87 | -------------------------------------------------------------------------------- /apps/cms/src/collections/Users.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionConfig } from 'payload/types'; 2 | import { authenticatedAndAdmin } from '../access/index'; 3 | 4 | export const usersSlug = 'users'; 5 | const Users: CollectionConfig = { 6 | slug: usersSlug, 7 | auth: true, 8 | admin: { 9 | useAsTitle: 'name', 10 | }, 11 | access: { 12 | read: authenticatedAndAdmin, 13 | admin: authenticatedAndAdmin, 14 | create: authenticatedAndAdmin, 15 | delete: authenticatedAndAdmin, 16 | update: authenticatedAndAdmin, 17 | }, 18 | fields: [ 19 | { 20 | name: 'name', 21 | type: 'text', 22 | required: true, 23 | }, 24 | { 25 | name: 'role', 26 | type: 'select', 27 | required: true, 28 | options: [ 29 | { 30 | label: 'Admin', 31 | value: 'admin', 32 | }, 33 | { 34 | label: 'User', 35 | value: 'user', 36 | }, 37 | ], 38 | }, 39 | ], 40 | }; 41 | 42 | export default Users; 43 | -------------------------------------------------------------------------------- /apps/cms/src/components/Select.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SelectInput } from 'payload/components/forms'; 3 | 4 | export const Select = (props: React.ComponentProps) => { 5 | return ; 6 | }; 7 | -------------------------------------------------------------------------------- /apps/cms/src/config.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { buildConfig } from 'payload/config'; 3 | import Users from './collections/Users'; 4 | import { Payload } from 'payload'; 5 | import { seedPages, seedUsers } from './seed/index'; 6 | import Media from './collections/Media'; 7 | import Pages from './collections/Pages'; 8 | 9 | const config = buildConfig({ 10 | admin: { 11 | user: Users.slug, 12 | }, 13 | collections: [Users, Media, Pages], 14 | typescript: { 15 | outputFile: path.resolve(__dirname, 'payload-types.ts'), 16 | }, 17 | graphQL: { 18 | schemaOutputFile: path.resolve(__dirname, 'payload-schema.graphql'), 19 | }, 20 | onInit: async (payload: Payload) => { 21 | if (process.env.NODE_ENV === 'development') { 22 | await seedUsers(payload); 23 | await seedPages(payload); 24 | } 25 | }, 26 | }); 27 | 28 | export default config; 29 | -------------------------------------------------------------------------------- /apps/cms/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from 'payload'; 2 | export { default as payload } from 'payload'; 3 | export * from './payload-types'; -------------------------------------------------------------------------------- /apps/cms/src/payload-schema.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | User(id: String!, draft: Boolean): User 3 | Users(where: User_where, draft: Boolean, page: Int, limit: Int, sort: String): Users 4 | docAccessUser(id: String!): usersDocAccess 5 | meUser: usersMe 6 | initializedUser: Boolean 7 | Media(id: String!, draft: Boolean): Media 8 | allMedia(where: Media_where, draft: Boolean, page: Int, limit: Int, sort: String): allMedia 9 | docAccessMedia(id: String!): mediaDocAccess 10 | Page(id: String!, draft: Boolean): Page 11 | Pages(where: Page_where, draft: Boolean, page: Int, limit: Int, sort: String): Pages 12 | docAccessPage(id: String!): pagesDocAccess 13 | Preference(key: String): Preference 14 | Access: Access 15 | } 16 | 17 | type User { 18 | id: String 19 | createdAt: DateTime! 20 | updatedAt: DateTime! 21 | name: String! 22 | role: User_role! 23 | email: EmailAddress 24 | resetPasswordToken: String 25 | resetPasswordExpiration: DateTime 26 | loginAttempts: Float 27 | lockUntil: DateTime 28 | password: String! 29 | } 30 | 31 | """ 32 | A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. 33 | """ 34 | scalar DateTime 35 | 36 | enum User_role { 37 | admin 38 | user 39 | } 40 | 41 | """ 42 | A field whose value conforms to the standard internet email address format as specified in RFC822: https://www.w3.org/Protocols/rfc822/. 43 | """ 44 | scalar EmailAddress @specifiedBy(url: "https://www.w3.org/Protocols/rfc822/") 45 | 46 | type Users { 47 | docs: [User] 48 | totalDocs: Int 49 | offset: Int 50 | limit: Int 51 | totalPages: Int 52 | page: Int 53 | pagingCounter: Int 54 | hasPrevPage: Boolean 55 | hasNextPage: Boolean 56 | prevPage: Int 57 | nextPage: Int 58 | } 59 | 60 | input User_where { 61 | name: User_name_operator 62 | role: User_role_operator 63 | email: User_email_operator 64 | id: User_id_operator 65 | createdAt: User_createdAt_operator 66 | updatedAt: User_updatedAt_operator 67 | OR: [User_where_or] 68 | AND: [User_where_and] 69 | } 70 | 71 | input User_name_operator { 72 | equals: String 73 | not_equals: String 74 | like: String 75 | contains: String 76 | in: [String] 77 | not_in: [String] 78 | all: [String] 79 | } 80 | 81 | input User_role_operator { 82 | equals: User_role_Input 83 | not_equals: User_role_Input 84 | in: [User_role_Input] 85 | not_in: [User_role_Input] 86 | all: [User_role_Input] 87 | } 88 | 89 | enum User_role_Input { 90 | admin 91 | user 92 | } 93 | 94 | input User_email_operator { 95 | equals: EmailAddress 96 | not_equals: EmailAddress 97 | like: EmailAddress 98 | contains: EmailAddress 99 | in: [EmailAddress] 100 | not_in: [EmailAddress] 101 | all: [EmailAddress] 102 | exists: Boolean 103 | } 104 | 105 | input User_id_operator { 106 | equals: JSON 107 | not_equals: JSON 108 | in: [JSON] 109 | not_in: [JSON] 110 | all: [JSON] 111 | exists: Boolean 112 | } 113 | 114 | """ 115 | The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). 116 | """ 117 | scalar JSON 118 | 119 | input User_createdAt_operator { 120 | equals: DateTime 121 | not_equals: DateTime 122 | greater_than_equal: DateTime 123 | greater_than: DateTime 124 | less_than_equal: DateTime 125 | less_than: DateTime 126 | like: DateTime 127 | exists: Boolean 128 | } 129 | 130 | input User_updatedAt_operator { 131 | equals: DateTime 132 | not_equals: DateTime 133 | greater_than_equal: DateTime 134 | greater_than: DateTime 135 | less_than_equal: DateTime 136 | less_than: DateTime 137 | like: DateTime 138 | exists: Boolean 139 | } 140 | 141 | input User_where_or { 142 | name: User_name_operator 143 | role: User_role_operator 144 | email: User_email_operator 145 | id: User_id_operator 146 | createdAt: User_createdAt_operator 147 | updatedAt: User_updatedAt_operator 148 | } 149 | 150 | input User_where_and { 151 | name: User_name_operator 152 | role: User_role_operator 153 | email: User_email_operator 154 | id: User_id_operator 155 | createdAt: User_createdAt_operator 156 | updatedAt: User_updatedAt_operator 157 | } 158 | 159 | type usersDocAccess { 160 | fields: UsersDocAccessFields 161 | create: UsersCreateDocAccess 162 | read: UsersReadDocAccess 163 | update: UsersUpdateDocAccess 164 | delete: UsersDeleteDocAccess 165 | unlock: UsersUnlockDocAccess 166 | } 167 | 168 | type UsersDocAccessFields { 169 | name: UsersDocAccessFields_name 170 | role: UsersDocAccessFields_role 171 | email: UsersDocAccessFields_email 172 | password: UsersDocAccessFields_password 173 | } 174 | 175 | type UsersDocAccessFields_name { 176 | create: UsersDocAccessFields_name_Create 177 | read: UsersDocAccessFields_name_Read 178 | update: UsersDocAccessFields_name_Update 179 | delete: UsersDocAccessFields_name_Delete 180 | } 181 | 182 | type UsersDocAccessFields_name_Create { 183 | permission: Boolean! 184 | } 185 | 186 | type UsersDocAccessFields_name_Read { 187 | permission: Boolean! 188 | } 189 | 190 | type UsersDocAccessFields_name_Update { 191 | permission: Boolean! 192 | } 193 | 194 | type UsersDocAccessFields_name_Delete { 195 | permission: Boolean! 196 | } 197 | 198 | type UsersDocAccessFields_role { 199 | create: UsersDocAccessFields_role_Create 200 | read: UsersDocAccessFields_role_Read 201 | update: UsersDocAccessFields_role_Update 202 | delete: UsersDocAccessFields_role_Delete 203 | } 204 | 205 | type UsersDocAccessFields_role_Create { 206 | permission: Boolean! 207 | } 208 | 209 | type UsersDocAccessFields_role_Read { 210 | permission: Boolean! 211 | } 212 | 213 | type UsersDocAccessFields_role_Update { 214 | permission: Boolean! 215 | } 216 | 217 | type UsersDocAccessFields_role_Delete { 218 | permission: Boolean! 219 | } 220 | 221 | type UsersDocAccessFields_email { 222 | create: UsersDocAccessFields_email_Create 223 | read: UsersDocAccessFields_email_Read 224 | update: UsersDocAccessFields_email_Update 225 | delete: UsersDocAccessFields_email_Delete 226 | } 227 | 228 | type UsersDocAccessFields_email_Create { 229 | permission: Boolean! 230 | } 231 | 232 | type UsersDocAccessFields_email_Read { 233 | permission: Boolean! 234 | } 235 | 236 | type UsersDocAccessFields_email_Update { 237 | permission: Boolean! 238 | } 239 | 240 | type UsersDocAccessFields_email_Delete { 241 | permission: Boolean! 242 | } 243 | 244 | type UsersDocAccessFields_password { 245 | create: UsersDocAccessFields_password_Create 246 | read: UsersDocAccessFields_password_Read 247 | update: UsersDocAccessFields_password_Update 248 | delete: UsersDocAccessFields_password_Delete 249 | } 250 | 251 | type UsersDocAccessFields_password_Create { 252 | permission: Boolean! 253 | } 254 | 255 | type UsersDocAccessFields_password_Read { 256 | permission: Boolean! 257 | } 258 | 259 | type UsersDocAccessFields_password_Update { 260 | permission: Boolean! 261 | } 262 | 263 | type UsersDocAccessFields_password_Delete { 264 | permission: Boolean! 265 | } 266 | 267 | type UsersCreateDocAccess { 268 | permission: Boolean! 269 | where: JSONObject 270 | } 271 | 272 | """ 273 | The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). 274 | """ 275 | scalar JSONObject 276 | 277 | type UsersReadDocAccess { 278 | permission: Boolean! 279 | where: JSONObject 280 | } 281 | 282 | type UsersUpdateDocAccess { 283 | permission: Boolean! 284 | where: JSONObject 285 | } 286 | 287 | type UsersDeleteDocAccess { 288 | permission: Boolean! 289 | where: JSONObject 290 | } 291 | 292 | type UsersUnlockDocAccess { 293 | permission: Boolean! 294 | where: JSONObject 295 | } 296 | 297 | type usersMe { 298 | token: String 299 | user: User 300 | exp: Int 301 | collection: String 302 | } 303 | 304 | type Media { 305 | id: String 306 | createdAt: DateTime! 307 | updatedAt: DateTime! 308 | alt: String! 309 | url: String 310 | filename: String 311 | mimeType: String 312 | filesize: Float 313 | width: Float 314 | height: Float 315 | sizes: Media_Sizes 316 | } 317 | 318 | type Media_Sizes { 319 | card: Media_Sizes_Card 320 | feature: Media_Sizes_Feature 321 | } 322 | 323 | type Media_Sizes_Card { 324 | url: String 325 | width: Float 326 | height: Float 327 | mimeType: String 328 | filesize: Float 329 | filename: String 330 | } 331 | 332 | type Media_Sizes_Feature { 333 | url: String 334 | width: Float 335 | height: Float 336 | mimeType: String 337 | filesize: Float 338 | filename: String 339 | } 340 | 341 | type allMedia { 342 | docs: [Media] 343 | totalDocs: Int 344 | offset: Int 345 | limit: Int 346 | totalPages: Int 347 | page: Int 348 | pagingCounter: Int 349 | hasPrevPage: Boolean 350 | hasNextPage: Boolean 351 | prevPage: Int 352 | nextPage: Int 353 | } 354 | 355 | input Media_where { 356 | alt: Media_alt_operator 357 | url: Media_url_operator 358 | filename: Media_filename_operator 359 | mimeType: Media_mimeType_operator 360 | filesize: Media_filesize_operator 361 | width: Media_width_operator 362 | height: Media_height_operator 363 | sizes__card__url: Media_sizes__card__url_operator 364 | sizes__card__width: Media_sizes__card__width_operator 365 | sizes__card__height: Media_sizes__card__height_operator 366 | sizes__card__mimeType: Media_sizes__card__mimeType_operator 367 | sizes__card__filesize: Media_sizes__card__filesize_operator 368 | sizes__card__filename: Media_sizes__card__filename_operator 369 | sizes__feature__url: Media_sizes__feature__url_operator 370 | sizes__feature__width: Media_sizes__feature__width_operator 371 | sizes__feature__height: Media_sizes__feature__height_operator 372 | sizes__feature__mimeType: Media_sizes__feature__mimeType_operator 373 | sizes__feature__filesize: Media_sizes__feature__filesize_operator 374 | sizes__feature__filename: Media_sizes__feature__filename_operator 375 | id: Media_id_operator 376 | createdAt: Media_createdAt_operator 377 | updatedAt: Media_updatedAt_operator 378 | OR: [Media_where_or] 379 | AND: [Media_where_and] 380 | } 381 | 382 | input Media_alt_operator { 383 | equals: String 384 | not_equals: String 385 | like: String 386 | contains: String 387 | in: [String] 388 | not_in: [String] 389 | all: [String] 390 | } 391 | 392 | input Media_url_operator { 393 | equals: String 394 | not_equals: String 395 | like: String 396 | contains: String 397 | in: [String] 398 | not_in: [String] 399 | all: [String] 400 | exists: Boolean 401 | } 402 | 403 | input Media_filename_operator { 404 | equals: String 405 | not_equals: String 406 | like: String 407 | contains: String 408 | in: [String] 409 | not_in: [String] 410 | all: [String] 411 | exists: Boolean 412 | } 413 | 414 | input Media_mimeType_operator { 415 | equals: String 416 | not_equals: String 417 | like: String 418 | contains: String 419 | in: [String] 420 | not_in: [String] 421 | all: [String] 422 | exists: Boolean 423 | } 424 | 425 | input Media_filesize_operator { 426 | equals: Float 427 | not_equals: Float 428 | greater_than_equal: Float 429 | greater_than: Float 430 | less_than_equal: Float 431 | less_than: Float 432 | exists: Boolean 433 | } 434 | 435 | input Media_width_operator { 436 | equals: Float 437 | not_equals: Float 438 | greater_than_equal: Float 439 | greater_than: Float 440 | less_than_equal: Float 441 | less_than: Float 442 | exists: Boolean 443 | } 444 | 445 | input Media_height_operator { 446 | equals: Float 447 | not_equals: Float 448 | greater_than_equal: Float 449 | greater_than: Float 450 | less_than_equal: Float 451 | less_than: Float 452 | exists: Boolean 453 | } 454 | 455 | input Media_sizes__card__url_operator { 456 | equals: String 457 | not_equals: String 458 | like: String 459 | contains: String 460 | in: [String] 461 | not_in: [String] 462 | all: [String] 463 | exists: Boolean 464 | } 465 | 466 | input Media_sizes__card__width_operator { 467 | equals: Float 468 | not_equals: Float 469 | greater_than_equal: Float 470 | greater_than: Float 471 | less_than_equal: Float 472 | less_than: Float 473 | exists: Boolean 474 | } 475 | 476 | input Media_sizes__card__height_operator { 477 | equals: Float 478 | not_equals: Float 479 | greater_than_equal: Float 480 | greater_than: Float 481 | less_than_equal: Float 482 | less_than: Float 483 | exists: Boolean 484 | } 485 | 486 | input Media_sizes__card__mimeType_operator { 487 | equals: String 488 | not_equals: String 489 | like: String 490 | contains: String 491 | in: [String] 492 | not_in: [String] 493 | all: [String] 494 | exists: Boolean 495 | } 496 | 497 | input Media_sizes__card__filesize_operator { 498 | equals: Float 499 | not_equals: Float 500 | greater_than_equal: Float 501 | greater_than: Float 502 | less_than_equal: Float 503 | less_than: Float 504 | exists: Boolean 505 | } 506 | 507 | input Media_sizes__card__filename_operator { 508 | equals: String 509 | not_equals: String 510 | like: String 511 | contains: String 512 | in: [String] 513 | not_in: [String] 514 | all: [String] 515 | exists: Boolean 516 | } 517 | 518 | input Media_sizes__feature__url_operator { 519 | equals: String 520 | not_equals: String 521 | like: String 522 | contains: String 523 | in: [String] 524 | not_in: [String] 525 | all: [String] 526 | exists: Boolean 527 | } 528 | 529 | input Media_sizes__feature__width_operator { 530 | equals: Float 531 | not_equals: Float 532 | greater_than_equal: Float 533 | greater_than: Float 534 | less_than_equal: Float 535 | less_than: Float 536 | exists: Boolean 537 | } 538 | 539 | input Media_sizes__feature__height_operator { 540 | equals: Float 541 | not_equals: Float 542 | greater_than_equal: Float 543 | greater_than: Float 544 | less_than_equal: Float 545 | less_than: Float 546 | exists: Boolean 547 | } 548 | 549 | input Media_sizes__feature__mimeType_operator { 550 | equals: String 551 | not_equals: String 552 | like: String 553 | contains: String 554 | in: [String] 555 | not_in: [String] 556 | all: [String] 557 | exists: Boolean 558 | } 559 | 560 | input Media_sizes__feature__filesize_operator { 561 | equals: Float 562 | not_equals: Float 563 | greater_than_equal: Float 564 | greater_than: Float 565 | less_than_equal: Float 566 | less_than: Float 567 | exists: Boolean 568 | } 569 | 570 | input Media_sizes__feature__filename_operator { 571 | equals: String 572 | not_equals: String 573 | like: String 574 | contains: String 575 | in: [String] 576 | not_in: [String] 577 | all: [String] 578 | exists: Boolean 579 | } 580 | 581 | input Media_id_operator { 582 | equals: JSON 583 | not_equals: JSON 584 | in: [JSON] 585 | not_in: [JSON] 586 | all: [JSON] 587 | exists: Boolean 588 | } 589 | 590 | input Media_createdAt_operator { 591 | equals: DateTime 592 | not_equals: DateTime 593 | greater_than_equal: DateTime 594 | greater_than: DateTime 595 | less_than_equal: DateTime 596 | less_than: DateTime 597 | like: DateTime 598 | exists: Boolean 599 | } 600 | 601 | input Media_updatedAt_operator { 602 | equals: DateTime 603 | not_equals: DateTime 604 | greater_than_equal: DateTime 605 | greater_than: DateTime 606 | less_than_equal: DateTime 607 | less_than: DateTime 608 | like: DateTime 609 | exists: Boolean 610 | } 611 | 612 | input Media_where_or { 613 | alt: Media_alt_operator 614 | url: Media_url_operator 615 | filename: Media_filename_operator 616 | mimeType: Media_mimeType_operator 617 | filesize: Media_filesize_operator 618 | width: Media_width_operator 619 | height: Media_height_operator 620 | sizes__card__url: Media_sizes__card__url_operator 621 | sizes__card__width: Media_sizes__card__width_operator 622 | sizes__card__height: Media_sizes__card__height_operator 623 | sizes__card__mimeType: Media_sizes__card__mimeType_operator 624 | sizes__card__filesize: Media_sizes__card__filesize_operator 625 | sizes__card__filename: Media_sizes__card__filename_operator 626 | sizes__feature__url: Media_sizes__feature__url_operator 627 | sizes__feature__width: Media_sizes__feature__width_operator 628 | sizes__feature__height: Media_sizes__feature__height_operator 629 | sizes__feature__mimeType: Media_sizes__feature__mimeType_operator 630 | sizes__feature__filesize: Media_sizes__feature__filesize_operator 631 | sizes__feature__filename: Media_sizes__feature__filename_operator 632 | id: Media_id_operator 633 | createdAt: Media_createdAt_operator 634 | updatedAt: Media_updatedAt_operator 635 | } 636 | 637 | input Media_where_and { 638 | alt: Media_alt_operator 639 | url: Media_url_operator 640 | filename: Media_filename_operator 641 | mimeType: Media_mimeType_operator 642 | filesize: Media_filesize_operator 643 | width: Media_width_operator 644 | height: Media_height_operator 645 | sizes__card__url: Media_sizes__card__url_operator 646 | sizes__card__width: Media_sizes__card__width_operator 647 | sizes__card__height: Media_sizes__card__height_operator 648 | sizes__card__mimeType: Media_sizes__card__mimeType_operator 649 | sizes__card__filesize: Media_sizes__card__filesize_operator 650 | sizes__card__filename: Media_sizes__card__filename_operator 651 | sizes__feature__url: Media_sizes__feature__url_operator 652 | sizes__feature__width: Media_sizes__feature__width_operator 653 | sizes__feature__height: Media_sizes__feature__height_operator 654 | sizes__feature__mimeType: Media_sizes__feature__mimeType_operator 655 | sizes__feature__filesize: Media_sizes__feature__filesize_operator 656 | sizes__feature__filename: Media_sizes__feature__filename_operator 657 | id: Media_id_operator 658 | createdAt: Media_createdAt_operator 659 | updatedAt: Media_updatedAt_operator 660 | } 661 | 662 | type mediaDocAccess { 663 | fields: MediaDocAccessFields 664 | create: MediaCreateDocAccess 665 | read: MediaReadDocAccess 666 | update: MediaUpdateDocAccess 667 | delete: MediaDeleteDocAccess 668 | } 669 | 670 | type MediaDocAccessFields { 671 | alt: MediaDocAccessFields_alt 672 | url: MediaDocAccessFields_url 673 | filename: MediaDocAccessFields_filename 674 | mimeType: MediaDocAccessFields_mimeType 675 | filesize: MediaDocAccessFields_filesize 676 | width: MediaDocAccessFields_width 677 | height: MediaDocAccessFields_height 678 | sizes: MediaDocAccessFields_sizes 679 | } 680 | 681 | type MediaDocAccessFields_alt { 682 | create: MediaDocAccessFields_alt_Create 683 | read: MediaDocAccessFields_alt_Read 684 | update: MediaDocAccessFields_alt_Update 685 | delete: MediaDocAccessFields_alt_Delete 686 | } 687 | 688 | type MediaDocAccessFields_alt_Create { 689 | permission: Boolean! 690 | } 691 | 692 | type MediaDocAccessFields_alt_Read { 693 | permission: Boolean! 694 | } 695 | 696 | type MediaDocAccessFields_alt_Update { 697 | permission: Boolean! 698 | } 699 | 700 | type MediaDocAccessFields_alt_Delete { 701 | permission: Boolean! 702 | } 703 | 704 | type MediaDocAccessFields_url { 705 | create: MediaDocAccessFields_url_Create 706 | read: MediaDocAccessFields_url_Read 707 | update: MediaDocAccessFields_url_Update 708 | delete: MediaDocAccessFields_url_Delete 709 | } 710 | 711 | type MediaDocAccessFields_url_Create { 712 | permission: Boolean! 713 | } 714 | 715 | type MediaDocAccessFields_url_Read { 716 | permission: Boolean! 717 | } 718 | 719 | type MediaDocAccessFields_url_Update { 720 | permission: Boolean! 721 | } 722 | 723 | type MediaDocAccessFields_url_Delete { 724 | permission: Boolean! 725 | } 726 | 727 | type MediaDocAccessFields_filename { 728 | create: MediaDocAccessFields_filename_Create 729 | read: MediaDocAccessFields_filename_Read 730 | update: MediaDocAccessFields_filename_Update 731 | delete: MediaDocAccessFields_filename_Delete 732 | } 733 | 734 | type MediaDocAccessFields_filename_Create { 735 | permission: Boolean! 736 | } 737 | 738 | type MediaDocAccessFields_filename_Read { 739 | permission: Boolean! 740 | } 741 | 742 | type MediaDocAccessFields_filename_Update { 743 | permission: Boolean! 744 | } 745 | 746 | type MediaDocAccessFields_filename_Delete { 747 | permission: Boolean! 748 | } 749 | 750 | type MediaDocAccessFields_mimeType { 751 | create: MediaDocAccessFields_mimeType_Create 752 | read: MediaDocAccessFields_mimeType_Read 753 | update: MediaDocAccessFields_mimeType_Update 754 | delete: MediaDocAccessFields_mimeType_Delete 755 | } 756 | 757 | type MediaDocAccessFields_mimeType_Create { 758 | permission: Boolean! 759 | } 760 | 761 | type MediaDocAccessFields_mimeType_Read { 762 | permission: Boolean! 763 | } 764 | 765 | type MediaDocAccessFields_mimeType_Update { 766 | permission: Boolean! 767 | } 768 | 769 | type MediaDocAccessFields_mimeType_Delete { 770 | permission: Boolean! 771 | } 772 | 773 | type MediaDocAccessFields_filesize { 774 | create: MediaDocAccessFields_filesize_Create 775 | read: MediaDocAccessFields_filesize_Read 776 | update: MediaDocAccessFields_filesize_Update 777 | delete: MediaDocAccessFields_filesize_Delete 778 | } 779 | 780 | type MediaDocAccessFields_filesize_Create { 781 | permission: Boolean! 782 | } 783 | 784 | type MediaDocAccessFields_filesize_Read { 785 | permission: Boolean! 786 | } 787 | 788 | type MediaDocAccessFields_filesize_Update { 789 | permission: Boolean! 790 | } 791 | 792 | type MediaDocAccessFields_filesize_Delete { 793 | permission: Boolean! 794 | } 795 | 796 | type MediaDocAccessFields_width { 797 | create: MediaDocAccessFields_width_Create 798 | read: MediaDocAccessFields_width_Read 799 | update: MediaDocAccessFields_width_Update 800 | delete: MediaDocAccessFields_width_Delete 801 | } 802 | 803 | type MediaDocAccessFields_width_Create { 804 | permission: Boolean! 805 | } 806 | 807 | type MediaDocAccessFields_width_Read { 808 | permission: Boolean! 809 | } 810 | 811 | type MediaDocAccessFields_width_Update { 812 | permission: Boolean! 813 | } 814 | 815 | type MediaDocAccessFields_width_Delete { 816 | permission: Boolean! 817 | } 818 | 819 | type MediaDocAccessFields_height { 820 | create: MediaDocAccessFields_height_Create 821 | read: MediaDocAccessFields_height_Read 822 | update: MediaDocAccessFields_height_Update 823 | delete: MediaDocAccessFields_height_Delete 824 | } 825 | 826 | type MediaDocAccessFields_height_Create { 827 | permission: Boolean! 828 | } 829 | 830 | type MediaDocAccessFields_height_Read { 831 | permission: Boolean! 832 | } 833 | 834 | type MediaDocAccessFields_height_Update { 835 | permission: Boolean! 836 | } 837 | 838 | type MediaDocAccessFields_height_Delete { 839 | permission: Boolean! 840 | } 841 | 842 | type MediaDocAccessFields_sizes { 843 | create: MediaDocAccessFields_sizes_Create 844 | read: MediaDocAccessFields_sizes_Read 845 | update: MediaDocAccessFields_sizes_Update 846 | delete: MediaDocAccessFields_sizes_Delete 847 | fields: MediaDocAccessFields_sizes_Fields 848 | } 849 | 850 | type MediaDocAccessFields_sizes_Create { 851 | permission: Boolean! 852 | } 853 | 854 | type MediaDocAccessFields_sizes_Read { 855 | permission: Boolean! 856 | } 857 | 858 | type MediaDocAccessFields_sizes_Update { 859 | permission: Boolean! 860 | } 861 | 862 | type MediaDocAccessFields_sizes_Delete { 863 | permission: Boolean! 864 | } 865 | 866 | type MediaDocAccessFields_sizes_Fields { 867 | card: MediaDocAccessFields_sizes_card 868 | feature: MediaDocAccessFields_sizes_feature 869 | } 870 | 871 | type MediaDocAccessFields_sizes_card { 872 | create: MediaDocAccessFields_sizes_card_Create 873 | read: MediaDocAccessFields_sizes_card_Read 874 | update: MediaDocAccessFields_sizes_card_Update 875 | delete: MediaDocAccessFields_sizes_card_Delete 876 | fields: MediaDocAccessFields_sizes_card_Fields 877 | } 878 | 879 | type MediaDocAccessFields_sizes_card_Create { 880 | permission: Boolean! 881 | } 882 | 883 | type MediaDocAccessFields_sizes_card_Read { 884 | permission: Boolean! 885 | } 886 | 887 | type MediaDocAccessFields_sizes_card_Update { 888 | permission: Boolean! 889 | } 890 | 891 | type MediaDocAccessFields_sizes_card_Delete { 892 | permission: Boolean! 893 | } 894 | 895 | type MediaDocAccessFields_sizes_card_Fields { 896 | url: MediaDocAccessFields_sizes_card_url 897 | width: MediaDocAccessFields_sizes_card_width 898 | height: MediaDocAccessFields_sizes_card_height 899 | mimeType: MediaDocAccessFields_sizes_card_mimeType 900 | filesize: MediaDocAccessFields_sizes_card_filesize 901 | filename: MediaDocAccessFields_sizes_card_filename 902 | } 903 | 904 | type MediaDocAccessFields_sizes_card_url { 905 | create: MediaDocAccessFields_sizes_card_url_Create 906 | read: MediaDocAccessFields_sizes_card_url_Read 907 | update: MediaDocAccessFields_sizes_card_url_Update 908 | delete: MediaDocAccessFields_sizes_card_url_Delete 909 | } 910 | 911 | type MediaDocAccessFields_sizes_card_url_Create { 912 | permission: Boolean! 913 | } 914 | 915 | type MediaDocAccessFields_sizes_card_url_Read { 916 | permission: Boolean! 917 | } 918 | 919 | type MediaDocAccessFields_sizes_card_url_Update { 920 | permission: Boolean! 921 | } 922 | 923 | type MediaDocAccessFields_sizes_card_url_Delete { 924 | permission: Boolean! 925 | } 926 | 927 | type MediaDocAccessFields_sizes_card_width { 928 | create: MediaDocAccessFields_sizes_card_width_Create 929 | read: MediaDocAccessFields_sizes_card_width_Read 930 | update: MediaDocAccessFields_sizes_card_width_Update 931 | delete: MediaDocAccessFields_sizes_card_width_Delete 932 | } 933 | 934 | type MediaDocAccessFields_sizes_card_width_Create { 935 | permission: Boolean! 936 | } 937 | 938 | type MediaDocAccessFields_sizes_card_width_Read { 939 | permission: Boolean! 940 | } 941 | 942 | type MediaDocAccessFields_sizes_card_width_Update { 943 | permission: Boolean! 944 | } 945 | 946 | type MediaDocAccessFields_sizes_card_width_Delete { 947 | permission: Boolean! 948 | } 949 | 950 | type MediaDocAccessFields_sizes_card_height { 951 | create: MediaDocAccessFields_sizes_card_height_Create 952 | read: MediaDocAccessFields_sizes_card_height_Read 953 | update: MediaDocAccessFields_sizes_card_height_Update 954 | delete: MediaDocAccessFields_sizes_card_height_Delete 955 | } 956 | 957 | type MediaDocAccessFields_sizes_card_height_Create { 958 | permission: Boolean! 959 | } 960 | 961 | type MediaDocAccessFields_sizes_card_height_Read { 962 | permission: Boolean! 963 | } 964 | 965 | type MediaDocAccessFields_sizes_card_height_Update { 966 | permission: Boolean! 967 | } 968 | 969 | type MediaDocAccessFields_sizes_card_height_Delete { 970 | permission: Boolean! 971 | } 972 | 973 | type MediaDocAccessFields_sizes_card_mimeType { 974 | create: MediaDocAccessFields_sizes_card_mimeType_Create 975 | read: MediaDocAccessFields_sizes_card_mimeType_Read 976 | update: MediaDocAccessFields_sizes_card_mimeType_Update 977 | delete: MediaDocAccessFields_sizes_card_mimeType_Delete 978 | } 979 | 980 | type MediaDocAccessFields_sizes_card_mimeType_Create { 981 | permission: Boolean! 982 | } 983 | 984 | type MediaDocAccessFields_sizes_card_mimeType_Read { 985 | permission: Boolean! 986 | } 987 | 988 | type MediaDocAccessFields_sizes_card_mimeType_Update { 989 | permission: Boolean! 990 | } 991 | 992 | type MediaDocAccessFields_sizes_card_mimeType_Delete { 993 | permission: Boolean! 994 | } 995 | 996 | type MediaDocAccessFields_sizes_card_filesize { 997 | create: MediaDocAccessFields_sizes_card_filesize_Create 998 | read: MediaDocAccessFields_sizes_card_filesize_Read 999 | update: MediaDocAccessFields_sizes_card_filesize_Update 1000 | delete: MediaDocAccessFields_sizes_card_filesize_Delete 1001 | } 1002 | 1003 | type MediaDocAccessFields_sizes_card_filesize_Create { 1004 | permission: Boolean! 1005 | } 1006 | 1007 | type MediaDocAccessFields_sizes_card_filesize_Read { 1008 | permission: Boolean! 1009 | } 1010 | 1011 | type MediaDocAccessFields_sizes_card_filesize_Update { 1012 | permission: Boolean! 1013 | } 1014 | 1015 | type MediaDocAccessFields_sizes_card_filesize_Delete { 1016 | permission: Boolean! 1017 | } 1018 | 1019 | type MediaDocAccessFields_sizes_card_filename { 1020 | create: MediaDocAccessFields_sizes_card_filename_Create 1021 | read: MediaDocAccessFields_sizes_card_filename_Read 1022 | update: MediaDocAccessFields_sizes_card_filename_Update 1023 | delete: MediaDocAccessFields_sizes_card_filename_Delete 1024 | } 1025 | 1026 | type MediaDocAccessFields_sizes_card_filename_Create { 1027 | permission: Boolean! 1028 | } 1029 | 1030 | type MediaDocAccessFields_sizes_card_filename_Read { 1031 | permission: Boolean! 1032 | } 1033 | 1034 | type MediaDocAccessFields_sizes_card_filename_Update { 1035 | permission: Boolean! 1036 | } 1037 | 1038 | type MediaDocAccessFields_sizes_card_filename_Delete { 1039 | permission: Boolean! 1040 | } 1041 | 1042 | type MediaDocAccessFields_sizes_feature { 1043 | create: MediaDocAccessFields_sizes_feature_Create 1044 | read: MediaDocAccessFields_sizes_feature_Read 1045 | update: MediaDocAccessFields_sizes_feature_Update 1046 | delete: MediaDocAccessFields_sizes_feature_Delete 1047 | fields: MediaDocAccessFields_sizes_feature_Fields 1048 | } 1049 | 1050 | type MediaDocAccessFields_sizes_feature_Create { 1051 | permission: Boolean! 1052 | } 1053 | 1054 | type MediaDocAccessFields_sizes_feature_Read { 1055 | permission: Boolean! 1056 | } 1057 | 1058 | type MediaDocAccessFields_sizes_feature_Update { 1059 | permission: Boolean! 1060 | } 1061 | 1062 | type MediaDocAccessFields_sizes_feature_Delete { 1063 | permission: Boolean! 1064 | } 1065 | 1066 | type MediaDocAccessFields_sizes_feature_Fields { 1067 | url: MediaDocAccessFields_sizes_feature_url 1068 | width: MediaDocAccessFields_sizes_feature_width 1069 | height: MediaDocAccessFields_sizes_feature_height 1070 | mimeType: MediaDocAccessFields_sizes_feature_mimeType 1071 | filesize: MediaDocAccessFields_sizes_feature_filesize 1072 | filename: MediaDocAccessFields_sizes_feature_filename 1073 | } 1074 | 1075 | type MediaDocAccessFields_sizes_feature_url { 1076 | create: MediaDocAccessFields_sizes_feature_url_Create 1077 | read: MediaDocAccessFields_sizes_feature_url_Read 1078 | update: MediaDocAccessFields_sizes_feature_url_Update 1079 | delete: MediaDocAccessFields_sizes_feature_url_Delete 1080 | } 1081 | 1082 | type MediaDocAccessFields_sizes_feature_url_Create { 1083 | permission: Boolean! 1084 | } 1085 | 1086 | type MediaDocAccessFields_sizes_feature_url_Read { 1087 | permission: Boolean! 1088 | } 1089 | 1090 | type MediaDocAccessFields_sizes_feature_url_Update { 1091 | permission: Boolean! 1092 | } 1093 | 1094 | type MediaDocAccessFields_sizes_feature_url_Delete { 1095 | permission: Boolean! 1096 | } 1097 | 1098 | type MediaDocAccessFields_sizes_feature_width { 1099 | create: MediaDocAccessFields_sizes_feature_width_Create 1100 | read: MediaDocAccessFields_sizes_feature_width_Read 1101 | update: MediaDocAccessFields_sizes_feature_width_Update 1102 | delete: MediaDocAccessFields_sizes_feature_width_Delete 1103 | } 1104 | 1105 | type MediaDocAccessFields_sizes_feature_width_Create { 1106 | permission: Boolean! 1107 | } 1108 | 1109 | type MediaDocAccessFields_sizes_feature_width_Read { 1110 | permission: Boolean! 1111 | } 1112 | 1113 | type MediaDocAccessFields_sizes_feature_width_Update { 1114 | permission: Boolean! 1115 | } 1116 | 1117 | type MediaDocAccessFields_sizes_feature_width_Delete { 1118 | permission: Boolean! 1119 | } 1120 | 1121 | type MediaDocAccessFields_sizes_feature_height { 1122 | create: MediaDocAccessFields_sizes_feature_height_Create 1123 | read: MediaDocAccessFields_sizes_feature_height_Read 1124 | update: MediaDocAccessFields_sizes_feature_height_Update 1125 | delete: MediaDocAccessFields_sizes_feature_height_Delete 1126 | } 1127 | 1128 | type MediaDocAccessFields_sizes_feature_height_Create { 1129 | permission: Boolean! 1130 | } 1131 | 1132 | type MediaDocAccessFields_sizes_feature_height_Read { 1133 | permission: Boolean! 1134 | } 1135 | 1136 | type MediaDocAccessFields_sizes_feature_height_Update { 1137 | permission: Boolean! 1138 | } 1139 | 1140 | type MediaDocAccessFields_sizes_feature_height_Delete { 1141 | permission: Boolean! 1142 | } 1143 | 1144 | type MediaDocAccessFields_sizes_feature_mimeType { 1145 | create: MediaDocAccessFields_sizes_feature_mimeType_Create 1146 | read: MediaDocAccessFields_sizes_feature_mimeType_Read 1147 | update: MediaDocAccessFields_sizes_feature_mimeType_Update 1148 | delete: MediaDocAccessFields_sizes_feature_mimeType_Delete 1149 | } 1150 | 1151 | type MediaDocAccessFields_sizes_feature_mimeType_Create { 1152 | permission: Boolean! 1153 | } 1154 | 1155 | type MediaDocAccessFields_sizes_feature_mimeType_Read { 1156 | permission: Boolean! 1157 | } 1158 | 1159 | type MediaDocAccessFields_sizes_feature_mimeType_Update { 1160 | permission: Boolean! 1161 | } 1162 | 1163 | type MediaDocAccessFields_sizes_feature_mimeType_Delete { 1164 | permission: Boolean! 1165 | } 1166 | 1167 | type MediaDocAccessFields_sizes_feature_filesize { 1168 | create: MediaDocAccessFields_sizes_feature_filesize_Create 1169 | read: MediaDocAccessFields_sizes_feature_filesize_Read 1170 | update: MediaDocAccessFields_sizes_feature_filesize_Update 1171 | delete: MediaDocAccessFields_sizes_feature_filesize_Delete 1172 | } 1173 | 1174 | type MediaDocAccessFields_sizes_feature_filesize_Create { 1175 | permission: Boolean! 1176 | } 1177 | 1178 | type MediaDocAccessFields_sizes_feature_filesize_Read { 1179 | permission: Boolean! 1180 | } 1181 | 1182 | type MediaDocAccessFields_sizes_feature_filesize_Update { 1183 | permission: Boolean! 1184 | } 1185 | 1186 | type MediaDocAccessFields_sizes_feature_filesize_Delete { 1187 | permission: Boolean! 1188 | } 1189 | 1190 | type MediaDocAccessFields_sizes_feature_filename { 1191 | create: MediaDocAccessFields_sizes_feature_filename_Create 1192 | read: MediaDocAccessFields_sizes_feature_filename_Read 1193 | update: MediaDocAccessFields_sizes_feature_filename_Update 1194 | delete: MediaDocAccessFields_sizes_feature_filename_Delete 1195 | } 1196 | 1197 | type MediaDocAccessFields_sizes_feature_filename_Create { 1198 | permission: Boolean! 1199 | } 1200 | 1201 | type MediaDocAccessFields_sizes_feature_filename_Read { 1202 | permission: Boolean! 1203 | } 1204 | 1205 | type MediaDocAccessFields_sizes_feature_filename_Update { 1206 | permission: Boolean! 1207 | } 1208 | 1209 | type MediaDocAccessFields_sizes_feature_filename_Delete { 1210 | permission: Boolean! 1211 | } 1212 | 1213 | type MediaCreateDocAccess { 1214 | permission: Boolean! 1215 | where: JSONObject 1216 | } 1217 | 1218 | type MediaReadDocAccess { 1219 | permission: Boolean! 1220 | where: JSONObject 1221 | } 1222 | 1223 | type MediaUpdateDocAccess { 1224 | permission: Boolean! 1225 | where: JSONObject 1226 | } 1227 | 1228 | type MediaDeleteDocAccess { 1229 | permission: Boolean! 1230 | where: JSONObject 1231 | } 1232 | 1233 | type Page { 1234 | id: String 1235 | createdAt: DateTime! 1236 | updatedAt: DateTime! 1237 | title: String! 1238 | image(where: Page_Image_where): Media 1239 | public: Boolean 1240 | layout: [Page_Layout!] 1241 | meta: Page_Meta 1242 | slug: String 1243 | } 1244 | 1245 | input Page_Image_where { 1246 | alt: Page_Image_alt_operator 1247 | url: Page_Image_url_operator 1248 | filename: Page_Image_filename_operator 1249 | mimeType: Page_Image_mimeType_operator 1250 | filesize: Page_Image_filesize_operator 1251 | width: Page_Image_width_operator 1252 | height: Page_Image_height_operator 1253 | sizes__card__url: Page_Image_sizes__card__url_operator 1254 | sizes__card__width: Page_Image_sizes__card__width_operator 1255 | sizes__card__height: Page_Image_sizes__card__height_operator 1256 | sizes__card__mimeType: Page_Image_sizes__card__mimeType_operator 1257 | sizes__card__filesize: Page_Image_sizes__card__filesize_operator 1258 | sizes__card__filename: Page_Image_sizes__card__filename_operator 1259 | sizes__feature__url: Page_Image_sizes__feature__url_operator 1260 | sizes__feature__width: Page_Image_sizes__feature__width_operator 1261 | sizes__feature__height: Page_Image_sizes__feature__height_operator 1262 | sizes__feature__mimeType: Page_Image_sizes__feature__mimeType_operator 1263 | sizes__feature__filesize: Page_Image_sizes__feature__filesize_operator 1264 | sizes__feature__filename: Page_Image_sizes__feature__filename_operator 1265 | id: Page_Image_id_operator 1266 | OR: [Page_Image_where_or] 1267 | AND: [Page_Image_where_and] 1268 | } 1269 | 1270 | input Page_Image_alt_operator { 1271 | equals: String 1272 | not_equals: String 1273 | like: String 1274 | contains: String 1275 | in: [String] 1276 | not_in: [String] 1277 | all: [String] 1278 | } 1279 | 1280 | input Page_Image_url_operator { 1281 | equals: String 1282 | not_equals: String 1283 | like: String 1284 | contains: String 1285 | in: [String] 1286 | not_in: [String] 1287 | all: [String] 1288 | exists: Boolean 1289 | } 1290 | 1291 | input Page_Image_filename_operator { 1292 | equals: String 1293 | not_equals: String 1294 | like: String 1295 | contains: String 1296 | in: [String] 1297 | not_in: [String] 1298 | all: [String] 1299 | exists: Boolean 1300 | } 1301 | 1302 | input Page_Image_mimeType_operator { 1303 | equals: String 1304 | not_equals: String 1305 | like: String 1306 | contains: String 1307 | in: [String] 1308 | not_in: [String] 1309 | all: [String] 1310 | exists: Boolean 1311 | } 1312 | 1313 | input Page_Image_filesize_operator { 1314 | equals: Float 1315 | not_equals: Float 1316 | greater_than_equal: Float 1317 | greater_than: Float 1318 | less_than_equal: Float 1319 | less_than: Float 1320 | exists: Boolean 1321 | } 1322 | 1323 | input Page_Image_width_operator { 1324 | equals: Float 1325 | not_equals: Float 1326 | greater_than_equal: Float 1327 | greater_than: Float 1328 | less_than_equal: Float 1329 | less_than: Float 1330 | exists: Boolean 1331 | } 1332 | 1333 | input Page_Image_height_operator { 1334 | equals: Float 1335 | not_equals: Float 1336 | greater_than_equal: Float 1337 | greater_than: Float 1338 | less_than_equal: Float 1339 | less_than: Float 1340 | exists: Boolean 1341 | } 1342 | 1343 | input Page_Image_sizes__card__url_operator { 1344 | equals: String 1345 | not_equals: String 1346 | like: String 1347 | contains: String 1348 | in: [String] 1349 | not_in: [String] 1350 | all: [String] 1351 | exists: Boolean 1352 | } 1353 | 1354 | input Page_Image_sizes__card__width_operator { 1355 | equals: Float 1356 | not_equals: Float 1357 | greater_than_equal: Float 1358 | greater_than: Float 1359 | less_than_equal: Float 1360 | less_than: Float 1361 | exists: Boolean 1362 | } 1363 | 1364 | input Page_Image_sizes__card__height_operator { 1365 | equals: Float 1366 | not_equals: Float 1367 | greater_than_equal: Float 1368 | greater_than: Float 1369 | less_than_equal: Float 1370 | less_than: Float 1371 | exists: Boolean 1372 | } 1373 | 1374 | input Page_Image_sizes__card__mimeType_operator { 1375 | equals: String 1376 | not_equals: String 1377 | like: String 1378 | contains: String 1379 | in: [String] 1380 | not_in: [String] 1381 | all: [String] 1382 | exists: Boolean 1383 | } 1384 | 1385 | input Page_Image_sizes__card__filesize_operator { 1386 | equals: Float 1387 | not_equals: Float 1388 | greater_than_equal: Float 1389 | greater_than: Float 1390 | less_than_equal: Float 1391 | less_than: Float 1392 | exists: Boolean 1393 | } 1394 | 1395 | input Page_Image_sizes__card__filename_operator { 1396 | equals: String 1397 | not_equals: String 1398 | like: String 1399 | contains: String 1400 | in: [String] 1401 | not_in: [String] 1402 | all: [String] 1403 | exists: Boolean 1404 | } 1405 | 1406 | input Page_Image_sizes__feature__url_operator { 1407 | equals: String 1408 | not_equals: String 1409 | like: String 1410 | contains: String 1411 | in: [String] 1412 | not_in: [String] 1413 | all: [String] 1414 | exists: Boolean 1415 | } 1416 | 1417 | input Page_Image_sizes__feature__width_operator { 1418 | equals: Float 1419 | not_equals: Float 1420 | greater_than_equal: Float 1421 | greater_than: Float 1422 | less_than_equal: Float 1423 | less_than: Float 1424 | exists: Boolean 1425 | } 1426 | 1427 | input Page_Image_sizes__feature__height_operator { 1428 | equals: Float 1429 | not_equals: Float 1430 | greater_than_equal: Float 1431 | greater_than: Float 1432 | less_than_equal: Float 1433 | less_than: Float 1434 | exists: Boolean 1435 | } 1436 | 1437 | input Page_Image_sizes__feature__mimeType_operator { 1438 | equals: String 1439 | not_equals: String 1440 | like: String 1441 | contains: String 1442 | in: [String] 1443 | not_in: [String] 1444 | all: [String] 1445 | exists: Boolean 1446 | } 1447 | 1448 | input Page_Image_sizes__feature__filesize_operator { 1449 | equals: Float 1450 | not_equals: Float 1451 | greater_than_equal: Float 1452 | greater_than: Float 1453 | less_than_equal: Float 1454 | less_than: Float 1455 | exists: Boolean 1456 | } 1457 | 1458 | input Page_Image_sizes__feature__filename_operator { 1459 | equals: String 1460 | not_equals: String 1461 | like: String 1462 | contains: String 1463 | in: [String] 1464 | not_in: [String] 1465 | all: [String] 1466 | exists: Boolean 1467 | } 1468 | 1469 | input Page_Image_id_operator { 1470 | equals: JSON 1471 | not_equals: JSON 1472 | in: [JSON] 1473 | not_in: [JSON] 1474 | all: [JSON] 1475 | exists: Boolean 1476 | } 1477 | 1478 | input Page_Image_where_or { 1479 | alt: Page_Image_alt_operator 1480 | url: Page_Image_url_operator 1481 | filename: Page_Image_filename_operator 1482 | mimeType: Page_Image_mimeType_operator 1483 | filesize: Page_Image_filesize_operator 1484 | width: Page_Image_width_operator 1485 | height: Page_Image_height_operator 1486 | sizes__card__url: Page_Image_sizes__card__url_operator 1487 | sizes__card__width: Page_Image_sizes__card__width_operator 1488 | sizes__card__height: Page_Image_sizes__card__height_operator 1489 | sizes__card__mimeType: Page_Image_sizes__card__mimeType_operator 1490 | sizes__card__filesize: Page_Image_sizes__card__filesize_operator 1491 | sizes__card__filename: Page_Image_sizes__card__filename_operator 1492 | sizes__feature__url: Page_Image_sizes__feature__url_operator 1493 | sizes__feature__width: Page_Image_sizes__feature__width_operator 1494 | sizes__feature__height: Page_Image_sizes__feature__height_operator 1495 | sizes__feature__mimeType: Page_Image_sizes__feature__mimeType_operator 1496 | sizes__feature__filesize: Page_Image_sizes__feature__filesize_operator 1497 | sizes__feature__filename: Page_Image_sizes__feature__filename_operator 1498 | id: Page_Image_id_operator 1499 | } 1500 | 1501 | input Page_Image_where_and { 1502 | alt: Page_Image_alt_operator 1503 | url: Page_Image_url_operator 1504 | filename: Page_Image_filename_operator 1505 | mimeType: Page_Image_mimeType_operator 1506 | filesize: Page_Image_filesize_operator 1507 | width: Page_Image_width_operator 1508 | height: Page_Image_height_operator 1509 | sizes__card__url: Page_Image_sizes__card__url_operator 1510 | sizes__card__width: Page_Image_sizes__card__width_operator 1511 | sizes__card__height: Page_Image_sizes__card__height_operator 1512 | sizes__card__mimeType: Page_Image_sizes__card__mimeType_operator 1513 | sizes__card__filesize: Page_Image_sizes__card__filesize_operator 1514 | sizes__card__filename: Page_Image_sizes__card__filename_operator 1515 | sizes__feature__url: Page_Image_sizes__feature__url_operator 1516 | sizes__feature__width: Page_Image_sizes__feature__width_operator 1517 | sizes__feature__height: Page_Image_sizes__feature__height_operator 1518 | sizes__feature__mimeType: Page_Image_sizes__feature__mimeType_operator 1519 | sizes__feature__filesize: Page_Image_sizes__feature__filesize_operator 1520 | sizes__feature__filename: Page_Image_sizes__feature__filename_operator 1521 | id: Page_Image_id_operator 1522 | } 1523 | 1524 | union Page_Layout = Cta | Content | Image 1525 | 1526 | type Cta { 1527 | content(depth: Int): JSON 1528 | buttons: [Cta_Buttons!] 1529 | id: String 1530 | blockName: String 1531 | blockType: String 1532 | } 1533 | 1534 | type Cta_Buttons { 1535 | label: String 1536 | type: Cta_Buttons_type 1537 | page: Page 1538 | url: String 1539 | newTab: Boolean 1540 | id: String 1541 | } 1542 | 1543 | enum Cta_Buttons_type { 1544 | page 1545 | custom 1546 | } 1547 | 1548 | type Content { 1549 | content(depth: Int): JSON 1550 | id: String 1551 | blockName: String 1552 | blockType: String 1553 | } 1554 | 1555 | type Image { 1556 | image(where: Image_Image_where): Media 1557 | type: Image_type 1558 | caption(depth: Int): JSON 1559 | id: String 1560 | blockName: String 1561 | blockType: String 1562 | } 1563 | 1564 | input Image_Image_where { 1565 | alt: Image_Image_alt_operator 1566 | url: Image_Image_url_operator 1567 | filename: Image_Image_filename_operator 1568 | mimeType: Image_Image_mimeType_operator 1569 | filesize: Image_Image_filesize_operator 1570 | width: Image_Image_width_operator 1571 | height: Image_Image_height_operator 1572 | sizes__card__url: Image_Image_sizes__card__url_operator 1573 | sizes__card__width: Image_Image_sizes__card__width_operator 1574 | sizes__card__height: Image_Image_sizes__card__height_operator 1575 | sizes__card__mimeType: Image_Image_sizes__card__mimeType_operator 1576 | sizes__card__filesize: Image_Image_sizes__card__filesize_operator 1577 | sizes__card__filename: Image_Image_sizes__card__filename_operator 1578 | sizes__feature__url: Image_Image_sizes__feature__url_operator 1579 | sizes__feature__width: Image_Image_sizes__feature__width_operator 1580 | sizes__feature__height: Image_Image_sizes__feature__height_operator 1581 | sizes__feature__mimeType: Image_Image_sizes__feature__mimeType_operator 1582 | sizes__feature__filesize: Image_Image_sizes__feature__filesize_operator 1583 | sizes__feature__filename: Image_Image_sizes__feature__filename_operator 1584 | id: Image_Image_id_operator 1585 | OR: [Image_Image_where_or] 1586 | AND: [Image_Image_where_and] 1587 | } 1588 | 1589 | input Image_Image_alt_operator { 1590 | equals: String 1591 | not_equals: String 1592 | like: String 1593 | contains: String 1594 | in: [String] 1595 | not_in: [String] 1596 | all: [String] 1597 | } 1598 | 1599 | input Image_Image_url_operator { 1600 | equals: String 1601 | not_equals: String 1602 | like: String 1603 | contains: String 1604 | in: [String] 1605 | not_in: [String] 1606 | all: [String] 1607 | exists: Boolean 1608 | } 1609 | 1610 | input Image_Image_filename_operator { 1611 | equals: String 1612 | not_equals: String 1613 | like: String 1614 | contains: String 1615 | in: [String] 1616 | not_in: [String] 1617 | all: [String] 1618 | exists: Boolean 1619 | } 1620 | 1621 | input Image_Image_mimeType_operator { 1622 | equals: String 1623 | not_equals: String 1624 | like: String 1625 | contains: String 1626 | in: [String] 1627 | not_in: [String] 1628 | all: [String] 1629 | exists: Boolean 1630 | } 1631 | 1632 | input Image_Image_filesize_operator { 1633 | equals: Float 1634 | not_equals: Float 1635 | greater_than_equal: Float 1636 | greater_than: Float 1637 | less_than_equal: Float 1638 | less_than: Float 1639 | exists: Boolean 1640 | } 1641 | 1642 | input Image_Image_width_operator { 1643 | equals: Float 1644 | not_equals: Float 1645 | greater_than_equal: Float 1646 | greater_than: Float 1647 | less_than_equal: Float 1648 | less_than: Float 1649 | exists: Boolean 1650 | } 1651 | 1652 | input Image_Image_height_operator { 1653 | equals: Float 1654 | not_equals: Float 1655 | greater_than_equal: Float 1656 | greater_than: Float 1657 | less_than_equal: Float 1658 | less_than: Float 1659 | exists: Boolean 1660 | } 1661 | 1662 | input Image_Image_sizes__card__url_operator { 1663 | equals: String 1664 | not_equals: String 1665 | like: String 1666 | contains: String 1667 | in: [String] 1668 | not_in: [String] 1669 | all: [String] 1670 | exists: Boolean 1671 | } 1672 | 1673 | input Image_Image_sizes__card__width_operator { 1674 | equals: Float 1675 | not_equals: Float 1676 | greater_than_equal: Float 1677 | greater_than: Float 1678 | less_than_equal: Float 1679 | less_than: Float 1680 | exists: Boolean 1681 | } 1682 | 1683 | input Image_Image_sizes__card__height_operator { 1684 | equals: Float 1685 | not_equals: Float 1686 | greater_than_equal: Float 1687 | greater_than: Float 1688 | less_than_equal: Float 1689 | less_than: Float 1690 | exists: Boolean 1691 | } 1692 | 1693 | input Image_Image_sizes__card__mimeType_operator { 1694 | equals: String 1695 | not_equals: String 1696 | like: String 1697 | contains: String 1698 | in: [String] 1699 | not_in: [String] 1700 | all: [String] 1701 | exists: Boolean 1702 | } 1703 | 1704 | input Image_Image_sizes__card__filesize_operator { 1705 | equals: Float 1706 | not_equals: Float 1707 | greater_than_equal: Float 1708 | greater_than: Float 1709 | less_than_equal: Float 1710 | less_than: Float 1711 | exists: Boolean 1712 | } 1713 | 1714 | input Image_Image_sizes__card__filename_operator { 1715 | equals: String 1716 | not_equals: String 1717 | like: String 1718 | contains: String 1719 | in: [String] 1720 | not_in: [String] 1721 | all: [String] 1722 | exists: Boolean 1723 | } 1724 | 1725 | input Image_Image_sizes__feature__url_operator { 1726 | equals: String 1727 | not_equals: String 1728 | like: String 1729 | contains: String 1730 | in: [String] 1731 | not_in: [String] 1732 | all: [String] 1733 | exists: Boolean 1734 | } 1735 | 1736 | input Image_Image_sizes__feature__width_operator { 1737 | equals: Float 1738 | not_equals: Float 1739 | greater_than_equal: Float 1740 | greater_than: Float 1741 | less_than_equal: Float 1742 | less_than: Float 1743 | exists: Boolean 1744 | } 1745 | 1746 | input Image_Image_sizes__feature__height_operator { 1747 | equals: Float 1748 | not_equals: Float 1749 | greater_than_equal: Float 1750 | greater_than: Float 1751 | less_than_equal: Float 1752 | less_than: Float 1753 | exists: Boolean 1754 | } 1755 | 1756 | input Image_Image_sizes__feature__mimeType_operator { 1757 | equals: String 1758 | not_equals: String 1759 | like: String 1760 | contains: String 1761 | in: [String] 1762 | not_in: [String] 1763 | all: [String] 1764 | exists: Boolean 1765 | } 1766 | 1767 | input Image_Image_sizes__feature__filesize_operator { 1768 | equals: Float 1769 | not_equals: Float 1770 | greater_than_equal: Float 1771 | greater_than: Float 1772 | less_than_equal: Float 1773 | less_than: Float 1774 | exists: Boolean 1775 | } 1776 | 1777 | input Image_Image_sizes__feature__filename_operator { 1778 | equals: String 1779 | not_equals: String 1780 | like: String 1781 | contains: String 1782 | in: [String] 1783 | not_in: [String] 1784 | all: [String] 1785 | exists: Boolean 1786 | } 1787 | 1788 | input Image_Image_id_operator { 1789 | equals: JSON 1790 | not_equals: JSON 1791 | in: [JSON] 1792 | not_in: [JSON] 1793 | all: [JSON] 1794 | exists: Boolean 1795 | } 1796 | 1797 | input Image_Image_where_or { 1798 | alt: Image_Image_alt_operator 1799 | url: Image_Image_url_operator 1800 | filename: Image_Image_filename_operator 1801 | mimeType: Image_Image_mimeType_operator 1802 | filesize: Image_Image_filesize_operator 1803 | width: Image_Image_width_operator 1804 | height: Image_Image_height_operator 1805 | sizes__card__url: Image_Image_sizes__card__url_operator 1806 | sizes__card__width: Image_Image_sizes__card__width_operator 1807 | sizes__card__height: Image_Image_sizes__card__height_operator 1808 | sizes__card__mimeType: Image_Image_sizes__card__mimeType_operator 1809 | sizes__card__filesize: Image_Image_sizes__card__filesize_operator 1810 | sizes__card__filename: Image_Image_sizes__card__filename_operator 1811 | sizes__feature__url: Image_Image_sizes__feature__url_operator 1812 | sizes__feature__width: Image_Image_sizes__feature__width_operator 1813 | sizes__feature__height: Image_Image_sizes__feature__height_operator 1814 | sizes__feature__mimeType: Image_Image_sizes__feature__mimeType_operator 1815 | sizes__feature__filesize: Image_Image_sizes__feature__filesize_operator 1816 | sizes__feature__filename: Image_Image_sizes__feature__filename_operator 1817 | id: Image_Image_id_operator 1818 | } 1819 | 1820 | input Image_Image_where_and { 1821 | alt: Image_Image_alt_operator 1822 | url: Image_Image_url_operator 1823 | filename: Image_Image_filename_operator 1824 | mimeType: Image_Image_mimeType_operator 1825 | filesize: Image_Image_filesize_operator 1826 | width: Image_Image_width_operator 1827 | height: Image_Image_height_operator 1828 | sizes__card__url: Image_Image_sizes__card__url_operator 1829 | sizes__card__width: Image_Image_sizes__card__width_operator 1830 | sizes__card__height: Image_Image_sizes__card__height_operator 1831 | sizes__card__mimeType: Image_Image_sizes__card__mimeType_operator 1832 | sizes__card__filesize: Image_Image_sizes__card__filesize_operator 1833 | sizes__card__filename: Image_Image_sizes__card__filename_operator 1834 | sizes__feature__url: Image_Image_sizes__feature__url_operator 1835 | sizes__feature__width: Image_Image_sizes__feature__width_operator 1836 | sizes__feature__height: Image_Image_sizes__feature__height_operator 1837 | sizes__feature__mimeType: Image_Image_sizes__feature__mimeType_operator 1838 | sizes__feature__filesize: Image_Image_sizes__feature__filesize_operator 1839 | sizes__feature__filename: Image_Image_sizes__feature__filename_operator 1840 | id: Image_Image_id_operator 1841 | } 1842 | 1843 | enum Image_type { 1844 | card 1845 | feature 1846 | } 1847 | 1848 | type Page_Meta { 1849 | title: String 1850 | description: String 1851 | keywords: String 1852 | } 1853 | 1854 | type Pages { 1855 | docs: [Page] 1856 | totalDocs: Int 1857 | offset: Int 1858 | limit: Int 1859 | totalPages: Int 1860 | page: Int 1861 | pagingCounter: Int 1862 | hasPrevPage: Boolean 1863 | hasNextPage: Boolean 1864 | prevPage: Int 1865 | nextPage: Int 1866 | } 1867 | 1868 | input Page_where { 1869 | title: Page_title_operator 1870 | image: Page_image_operator 1871 | public: Page_public_operator 1872 | meta__title: Page_meta__title_operator 1873 | meta__description: Page_meta__description_operator 1874 | meta__keywords: Page_meta__keywords_operator 1875 | slug: Page_slug_operator 1876 | id: Page_id_operator 1877 | createdAt: Page_createdAt_operator 1878 | updatedAt: Page_updatedAt_operator 1879 | OR: [Page_where_or] 1880 | AND: [Page_where_and] 1881 | } 1882 | 1883 | input Page_title_operator { 1884 | equals: String 1885 | not_equals: String 1886 | like: String 1887 | contains: String 1888 | in: [String] 1889 | not_in: [String] 1890 | all: [String] 1891 | } 1892 | 1893 | input Page_image_operator { 1894 | equals: String 1895 | not_equals: String 1896 | exists: Boolean 1897 | } 1898 | 1899 | input Page_public_operator { 1900 | equals: Boolean 1901 | not_equals: Boolean 1902 | exists: Boolean 1903 | } 1904 | 1905 | input Page_meta__title_operator { 1906 | equals: String 1907 | not_equals: String 1908 | like: String 1909 | contains: String 1910 | in: [String] 1911 | not_in: [String] 1912 | all: [String] 1913 | exists: Boolean 1914 | } 1915 | 1916 | input Page_meta__description_operator { 1917 | equals: String 1918 | not_equals: String 1919 | like: String 1920 | contains: String 1921 | exists: Boolean 1922 | } 1923 | 1924 | input Page_meta__keywords_operator { 1925 | equals: String 1926 | not_equals: String 1927 | like: String 1928 | contains: String 1929 | in: [String] 1930 | not_in: [String] 1931 | all: [String] 1932 | exists: Boolean 1933 | } 1934 | 1935 | input Page_slug_operator { 1936 | equals: String 1937 | not_equals: String 1938 | like: String 1939 | contains: String 1940 | in: [String] 1941 | not_in: [String] 1942 | all: [String] 1943 | exists: Boolean 1944 | } 1945 | 1946 | input Page_id_operator { 1947 | equals: JSON 1948 | not_equals: JSON 1949 | in: [JSON] 1950 | not_in: [JSON] 1951 | all: [JSON] 1952 | exists: Boolean 1953 | } 1954 | 1955 | input Page_createdAt_operator { 1956 | equals: DateTime 1957 | not_equals: DateTime 1958 | greater_than_equal: DateTime 1959 | greater_than: DateTime 1960 | less_than_equal: DateTime 1961 | less_than: DateTime 1962 | like: DateTime 1963 | exists: Boolean 1964 | } 1965 | 1966 | input Page_updatedAt_operator { 1967 | equals: DateTime 1968 | not_equals: DateTime 1969 | greater_than_equal: DateTime 1970 | greater_than: DateTime 1971 | less_than_equal: DateTime 1972 | less_than: DateTime 1973 | like: DateTime 1974 | exists: Boolean 1975 | } 1976 | 1977 | input Page_where_or { 1978 | title: Page_title_operator 1979 | image: Page_image_operator 1980 | public: Page_public_operator 1981 | meta__title: Page_meta__title_operator 1982 | meta__description: Page_meta__description_operator 1983 | meta__keywords: Page_meta__keywords_operator 1984 | slug: Page_slug_operator 1985 | id: Page_id_operator 1986 | createdAt: Page_createdAt_operator 1987 | updatedAt: Page_updatedAt_operator 1988 | } 1989 | 1990 | input Page_where_and { 1991 | title: Page_title_operator 1992 | image: Page_image_operator 1993 | public: Page_public_operator 1994 | meta__title: Page_meta__title_operator 1995 | meta__description: Page_meta__description_operator 1996 | meta__keywords: Page_meta__keywords_operator 1997 | slug: Page_slug_operator 1998 | id: Page_id_operator 1999 | createdAt: Page_createdAt_operator 2000 | updatedAt: Page_updatedAt_operator 2001 | } 2002 | 2003 | type pagesDocAccess { 2004 | fields: PagesDocAccessFields 2005 | create: PagesCreateDocAccess 2006 | read: PagesReadDocAccess 2007 | update: PagesUpdateDocAccess 2008 | delete: PagesDeleteDocAccess 2009 | } 2010 | 2011 | type PagesDocAccessFields { 2012 | title: PagesDocAccessFields_title 2013 | image: PagesDocAccessFields_image 2014 | public: PagesDocAccessFields_public 2015 | layout: PagesDocAccessFields_layout 2016 | meta: PagesDocAccessFields_meta 2017 | slug: PagesDocAccessFields_slug 2018 | } 2019 | 2020 | type PagesDocAccessFields_title { 2021 | create: PagesDocAccessFields_title_Create 2022 | read: PagesDocAccessFields_title_Read 2023 | update: PagesDocAccessFields_title_Update 2024 | delete: PagesDocAccessFields_title_Delete 2025 | } 2026 | 2027 | type PagesDocAccessFields_title_Create { 2028 | permission: Boolean! 2029 | } 2030 | 2031 | type PagesDocAccessFields_title_Read { 2032 | permission: Boolean! 2033 | } 2034 | 2035 | type PagesDocAccessFields_title_Update { 2036 | permission: Boolean! 2037 | } 2038 | 2039 | type PagesDocAccessFields_title_Delete { 2040 | permission: Boolean! 2041 | } 2042 | 2043 | type PagesDocAccessFields_image { 2044 | create: PagesDocAccessFields_image_Create 2045 | read: PagesDocAccessFields_image_Read 2046 | update: PagesDocAccessFields_image_Update 2047 | delete: PagesDocAccessFields_image_Delete 2048 | } 2049 | 2050 | type PagesDocAccessFields_image_Create { 2051 | permission: Boolean! 2052 | } 2053 | 2054 | type PagesDocAccessFields_image_Read { 2055 | permission: Boolean! 2056 | } 2057 | 2058 | type PagesDocAccessFields_image_Update { 2059 | permission: Boolean! 2060 | } 2061 | 2062 | type PagesDocAccessFields_image_Delete { 2063 | permission: Boolean! 2064 | } 2065 | 2066 | type PagesDocAccessFields_public { 2067 | create: PagesDocAccessFields_public_Create 2068 | read: PagesDocAccessFields_public_Read 2069 | update: PagesDocAccessFields_public_Update 2070 | delete: PagesDocAccessFields_public_Delete 2071 | } 2072 | 2073 | type PagesDocAccessFields_public_Create { 2074 | permission: Boolean! 2075 | } 2076 | 2077 | type PagesDocAccessFields_public_Read { 2078 | permission: Boolean! 2079 | } 2080 | 2081 | type PagesDocAccessFields_public_Update { 2082 | permission: Boolean! 2083 | } 2084 | 2085 | type PagesDocAccessFields_public_Delete { 2086 | permission: Boolean! 2087 | } 2088 | 2089 | type PagesDocAccessFields_layout { 2090 | create: PagesDocAccessFields_layout_Create 2091 | read: PagesDocAccessFields_layout_Read 2092 | update: PagesDocAccessFields_layout_Update 2093 | delete: PagesDocAccessFields_layout_Delete 2094 | } 2095 | 2096 | type PagesDocAccessFields_layout_Create { 2097 | permission: Boolean! 2098 | } 2099 | 2100 | type PagesDocAccessFields_layout_Read { 2101 | permission: Boolean! 2102 | } 2103 | 2104 | type PagesDocAccessFields_layout_Update { 2105 | permission: Boolean! 2106 | } 2107 | 2108 | type PagesDocAccessFields_layout_Delete { 2109 | permission: Boolean! 2110 | } 2111 | 2112 | type PagesDocAccessFields_meta { 2113 | create: PagesDocAccessFields_meta_Create 2114 | read: PagesDocAccessFields_meta_Read 2115 | update: PagesDocAccessFields_meta_Update 2116 | delete: PagesDocAccessFields_meta_Delete 2117 | fields: PagesDocAccessFields_meta_Fields 2118 | } 2119 | 2120 | type PagesDocAccessFields_meta_Create { 2121 | permission: Boolean! 2122 | } 2123 | 2124 | type PagesDocAccessFields_meta_Read { 2125 | permission: Boolean! 2126 | } 2127 | 2128 | type PagesDocAccessFields_meta_Update { 2129 | permission: Boolean! 2130 | } 2131 | 2132 | type PagesDocAccessFields_meta_Delete { 2133 | permission: Boolean! 2134 | } 2135 | 2136 | type PagesDocAccessFields_meta_Fields { 2137 | title: PagesDocAccessFields_meta_title 2138 | description: PagesDocAccessFields_meta_description 2139 | keywords: PagesDocAccessFields_meta_keywords 2140 | } 2141 | 2142 | type PagesDocAccessFields_meta_title { 2143 | create: PagesDocAccessFields_meta_title_Create 2144 | read: PagesDocAccessFields_meta_title_Read 2145 | update: PagesDocAccessFields_meta_title_Update 2146 | delete: PagesDocAccessFields_meta_title_Delete 2147 | } 2148 | 2149 | type PagesDocAccessFields_meta_title_Create { 2150 | permission: Boolean! 2151 | } 2152 | 2153 | type PagesDocAccessFields_meta_title_Read { 2154 | permission: Boolean! 2155 | } 2156 | 2157 | type PagesDocAccessFields_meta_title_Update { 2158 | permission: Boolean! 2159 | } 2160 | 2161 | type PagesDocAccessFields_meta_title_Delete { 2162 | permission: Boolean! 2163 | } 2164 | 2165 | type PagesDocAccessFields_meta_description { 2166 | create: PagesDocAccessFields_meta_description_Create 2167 | read: PagesDocAccessFields_meta_description_Read 2168 | update: PagesDocAccessFields_meta_description_Update 2169 | delete: PagesDocAccessFields_meta_description_Delete 2170 | } 2171 | 2172 | type PagesDocAccessFields_meta_description_Create { 2173 | permission: Boolean! 2174 | } 2175 | 2176 | type PagesDocAccessFields_meta_description_Read { 2177 | permission: Boolean! 2178 | } 2179 | 2180 | type PagesDocAccessFields_meta_description_Update { 2181 | permission: Boolean! 2182 | } 2183 | 2184 | type PagesDocAccessFields_meta_description_Delete { 2185 | permission: Boolean! 2186 | } 2187 | 2188 | type PagesDocAccessFields_meta_keywords { 2189 | create: PagesDocAccessFields_meta_keywords_Create 2190 | read: PagesDocAccessFields_meta_keywords_Read 2191 | update: PagesDocAccessFields_meta_keywords_Update 2192 | delete: PagesDocAccessFields_meta_keywords_Delete 2193 | } 2194 | 2195 | type PagesDocAccessFields_meta_keywords_Create { 2196 | permission: Boolean! 2197 | } 2198 | 2199 | type PagesDocAccessFields_meta_keywords_Read { 2200 | permission: Boolean! 2201 | } 2202 | 2203 | type PagesDocAccessFields_meta_keywords_Update { 2204 | permission: Boolean! 2205 | } 2206 | 2207 | type PagesDocAccessFields_meta_keywords_Delete { 2208 | permission: Boolean! 2209 | } 2210 | 2211 | type PagesDocAccessFields_slug { 2212 | create: PagesDocAccessFields_slug_Create 2213 | read: PagesDocAccessFields_slug_Read 2214 | update: PagesDocAccessFields_slug_Update 2215 | delete: PagesDocAccessFields_slug_Delete 2216 | } 2217 | 2218 | type PagesDocAccessFields_slug_Create { 2219 | permission: Boolean! 2220 | } 2221 | 2222 | type PagesDocAccessFields_slug_Read { 2223 | permission: Boolean! 2224 | } 2225 | 2226 | type PagesDocAccessFields_slug_Update { 2227 | permission: Boolean! 2228 | } 2229 | 2230 | type PagesDocAccessFields_slug_Delete { 2231 | permission: Boolean! 2232 | } 2233 | 2234 | type PagesCreateDocAccess { 2235 | permission: Boolean! 2236 | where: JSONObject 2237 | } 2238 | 2239 | type PagesReadDocAccess { 2240 | permission: Boolean! 2241 | where: JSONObject 2242 | } 2243 | 2244 | type PagesUpdateDocAccess { 2245 | permission: Boolean! 2246 | where: JSONObject 2247 | } 2248 | 2249 | type PagesDeleteDocAccess { 2250 | permission: Boolean! 2251 | where: JSONObject 2252 | } 2253 | 2254 | type Preference { 2255 | key: String! 2256 | value: JSON 2257 | createdAt: DateTime! 2258 | updatedAt: DateTime! 2259 | } 2260 | 2261 | type Access { 2262 | canAccessAdmin: Boolean! 2263 | users: usersAccess 2264 | media: mediaAccess 2265 | pages: pagesAccess 2266 | } 2267 | 2268 | type usersAccess { 2269 | fields: UsersFields 2270 | create: UsersCreateAccess 2271 | read: UsersReadAccess 2272 | update: UsersUpdateAccess 2273 | delete: UsersDeleteAccess 2274 | unlock: UsersUnlockAccess 2275 | } 2276 | 2277 | type UsersFields { 2278 | name: UsersFields_name 2279 | role: UsersFields_role 2280 | email: UsersFields_email 2281 | password: UsersFields_password 2282 | } 2283 | 2284 | type UsersFields_name { 2285 | create: UsersFields_name_Create 2286 | read: UsersFields_name_Read 2287 | update: UsersFields_name_Update 2288 | delete: UsersFields_name_Delete 2289 | } 2290 | 2291 | type UsersFields_name_Create { 2292 | permission: Boolean! 2293 | } 2294 | 2295 | type UsersFields_name_Read { 2296 | permission: Boolean! 2297 | } 2298 | 2299 | type UsersFields_name_Update { 2300 | permission: Boolean! 2301 | } 2302 | 2303 | type UsersFields_name_Delete { 2304 | permission: Boolean! 2305 | } 2306 | 2307 | type UsersFields_role { 2308 | create: UsersFields_role_Create 2309 | read: UsersFields_role_Read 2310 | update: UsersFields_role_Update 2311 | delete: UsersFields_role_Delete 2312 | } 2313 | 2314 | type UsersFields_role_Create { 2315 | permission: Boolean! 2316 | } 2317 | 2318 | type UsersFields_role_Read { 2319 | permission: Boolean! 2320 | } 2321 | 2322 | type UsersFields_role_Update { 2323 | permission: Boolean! 2324 | } 2325 | 2326 | type UsersFields_role_Delete { 2327 | permission: Boolean! 2328 | } 2329 | 2330 | type UsersFields_email { 2331 | create: UsersFields_email_Create 2332 | read: UsersFields_email_Read 2333 | update: UsersFields_email_Update 2334 | delete: UsersFields_email_Delete 2335 | } 2336 | 2337 | type UsersFields_email_Create { 2338 | permission: Boolean! 2339 | } 2340 | 2341 | type UsersFields_email_Read { 2342 | permission: Boolean! 2343 | } 2344 | 2345 | type UsersFields_email_Update { 2346 | permission: Boolean! 2347 | } 2348 | 2349 | type UsersFields_email_Delete { 2350 | permission: Boolean! 2351 | } 2352 | 2353 | type UsersFields_password { 2354 | create: UsersFields_password_Create 2355 | read: UsersFields_password_Read 2356 | update: UsersFields_password_Update 2357 | delete: UsersFields_password_Delete 2358 | } 2359 | 2360 | type UsersFields_password_Create { 2361 | permission: Boolean! 2362 | } 2363 | 2364 | type UsersFields_password_Read { 2365 | permission: Boolean! 2366 | } 2367 | 2368 | type UsersFields_password_Update { 2369 | permission: Boolean! 2370 | } 2371 | 2372 | type UsersFields_password_Delete { 2373 | permission: Boolean! 2374 | } 2375 | 2376 | type UsersCreateAccess { 2377 | permission: Boolean! 2378 | where: JSONObject 2379 | } 2380 | 2381 | type UsersReadAccess { 2382 | permission: Boolean! 2383 | where: JSONObject 2384 | } 2385 | 2386 | type UsersUpdateAccess { 2387 | permission: Boolean! 2388 | where: JSONObject 2389 | } 2390 | 2391 | type UsersDeleteAccess { 2392 | permission: Boolean! 2393 | where: JSONObject 2394 | } 2395 | 2396 | type UsersUnlockAccess { 2397 | permission: Boolean! 2398 | where: JSONObject 2399 | } 2400 | 2401 | type mediaAccess { 2402 | fields: MediaFields 2403 | create: MediaCreateAccess 2404 | read: MediaReadAccess 2405 | update: MediaUpdateAccess 2406 | delete: MediaDeleteAccess 2407 | } 2408 | 2409 | type MediaFields { 2410 | alt: MediaFields_alt 2411 | url: MediaFields_url 2412 | filename: MediaFields_filename 2413 | mimeType: MediaFields_mimeType 2414 | filesize: MediaFields_filesize 2415 | width: MediaFields_width 2416 | height: MediaFields_height 2417 | sizes: MediaFields_sizes 2418 | } 2419 | 2420 | type MediaFields_alt { 2421 | create: MediaFields_alt_Create 2422 | read: MediaFields_alt_Read 2423 | update: MediaFields_alt_Update 2424 | delete: MediaFields_alt_Delete 2425 | } 2426 | 2427 | type MediaFields_alt_Create { 2428 | permission: Boolean! 2429 | } 2430 | 2431 | type MediaFields_alt_Read { 2432 | permission: Boolean! 2433 | } 2434 | 2435 | type MediaFields_alt_Update { 2436 | permission: Boolean! 2437 | } 2438 | 2439 | type MediaFields_alt_Delete { 2440 | permission: Boolean! 2441 | } 2442 | 2443 | type MediaFields_url { 2444 | create: MediaFields_url_Create 2445 | read: MediaFields_url_Read 2446 | update: MediaFields_url_Update 2447 | delete: MediaFields_url_Delete 2448 | } 2449 | 2450 | type MediaFields_url_Create { 2451 | permission: Boolean! 2452 | } 2453 | 2454 | type MediaFields_url_Read { 2455 | permission: Boolean! 2456 | } 2457 | 2458 | type MediaFields_url_Update { 2459 | permission: Boolean! 2460 | } 2461 | 2462 | type MediaFields_url_Delete { 2463 | permission: Boolean! 2464 | } 2465 | 2466 | type MediaFields_filename { 2467 | create: MediaFields_filename_Create 2468 | read: MediaFields_filename_Read 2469 | update: MediaFields_filename_Update 2470 | delete: MediaFields_filename_Delete 2471 | } 2472 | 2473 | type MediaFields_filename_Create { 2474 | permission: Boolean! 2475 | } 2476 | 2477 | type MediaFields_filename_Read { 2478 | permission: Boolean! 2479 | } 2480 | 2481 | type MediaFields_filename_Update { 2482 | permission: Boolean! 2483 | } 2484 | 2485 | type MediaFields_filename_Delete { 2486 | permission: Boolean! 2487 | } 2488 | 2489 | type MediaFields_mimeType { 2490 | create: MediaFields_mimeType_Create 2491 | read: MediaFields_mimeType_Read 2492 | update: MediaFields_mimeType_Update 2493 | delete: MediaFields_mimeType_Delete 2494 | } 2495 | 2496 | type MediaFields_mimeType_Create { 2497 | permission: Boolean! 2498 | } 2499 | 2500 | type MediaFields_mimeType_Read { 2501 | permission: Boolean! 2502 | } 2503 | 2504 | type MediaFields_mimeType_Update { 2505 | permission: Boolean! 2506 | } 2507 | 2508 | type MediaFields_mimeType_Delete { 2509 | permission: Boolean! 2510 | } 2511 | 2512 | type MediaFields_filesize { 2513 | create: MediaFields_filesize_Create 2514 | read: MediaFields_filesize_Read 2515 | update: MediaFields_filesize_Update 2516 | delete: MediaFields_filesize_Delete 2517 | } 2518 | 2519 | type MediaFields_filesize_Create { 2520 | permission: Boolean! 2521 | } 2522 | 2523 | type MediaFields_filesize_Read { 2524 | permission: Boolean! 2525 | } 2526 | 2527 | type MediaFields_filesize_Update { 2528 | permission: Boolean! 2529 | } 2530 | 2531 | type MediaFields_filesize_Delete { 2532 | permission: Boolean! 2533 | } 2534 | 2535 | type MediaFields_width { 2536 | create: MediaFields_width_Create 2537 | read: MediaFields_width_Read 2538 | update: MediaFields_width_Update 2539 | delete: MediaFields_width_Delete 2540 | } 2541 | 2542 | type MediaFields_width_Create { 2543 | permission: Boolean! 2544 | } 2545 | 2546 | type MediaFields_width_Read { 2547 | permission: Boolean! 2548 | } 2549 | 2550 | type MediaFields_width_Update { 2551 | permission: Boolean! 2552 | } 2553 | 2554 | type MediaFields_width_Delete { 2555 | permission: Boolean! 2556 | } 2557 | 2558 | type MediaFields_height { 2559 | create: MediaFields_height_Create 2560 | read: MediaFields_height_Read 2561 | update: MediaFields_height_Update 2562 | delete: MediaFields_height_Delete 2563 | } 2564 | 2565 | type MediaFields_height_Create { 2566 | permission: Boolean! 2567 | } 2568 | 2569 | type MediaFields_height_Read { 2570 | permission: Boolean! 2571 | } 2572 | 2573 | type MediaFields_height_Update { 2574 | permission: Boolean! 2575 | } 2576 | 2577 | type MediaFields_height_Delete { 2578 | permission: Boolean! 2579 | } 2580 | 2581 | type MediaFields_sizes { 2582 | create: MediaFields_sizes_Create 2583 | read: MediaFields_sizes_Read 2584 | update: MediaFields_sizes_Update 2585 | delete: MediaFields_sizes_Delete 2586 | fields: MediaFields_sizes_Fields 2587 | } 2588 | 2589 | type MediaFields_sizes_Create { 2590 | permission: Boolean! 2591 | } 2592 | 2593 | type MediaFields_sizes_Read { 2594 | permission: Boolean! 2595 | } 2596 | 2597 | type MediaFields_sizes_Update { 2598 | permission: Boolean! 2599 | } 2600 | 2601 | type MediaFields_sizes_Delete { 2602 | permission: Boolean! 2603 | } 2604 | 2605 | type MediaFields_sizes_Fields { 2606 | card: MediaFields_sizes_card 2607 | feature: MediaFields_sizes_feature 2608 | } 2609 | 2610 | type MediaFields_sizes_card { 2611 | create: MediaFields_sizes_card_Create 2612 | read: MediaFields_sizes_card_Read 2613 | update: MediaFields_sizes_card_Update 2614 | delete: MediaFields_sizes_card_Delete 2615 | fields: MediaFields_sizes_card_Fields 2616 | } 2617 | 2618 | type MediaFields_sizes_card_Create { 2619 | permission: Boolean! 2620 | } 2621 | 2622 | type MediaFields_sizes_card_Read { 2623 | permission: Boolean! 2624 | } 2625 | 2626 | type MediaFields_sizes_card_Update { 2627 | permission: Boolean! 2628 | } 2629 | 2630 | type MediaFields_sizes_card_Delete { 2631 | permission: Boolean! 2632 | } 2633 | 2634 | type MediaFields_sizes_card_Fields { 2635 | url: MediaFields_sizes_card_url 2636 | width: MediaFields_sizes_card_width 2637 | height: MediaFields_sizes_card_height 2638 | mimeType: MediaFields_sizes_card_mimeType 2639 | filesize: MediaFields_sizes_card_filesize 2640 | filename: MediaFields_sizes_card_filename 2641 | } 2642 | 2643 | type MediaFields_sizes_card_url { 2644 | create: MediaFields_sizes_card_url_Create 2645 | read: MediaFields_sizes_card_url_Read 2646 | update: MediaFields_sizes_card_url_Update 2647 | delete: MediaFields_sizes_card_url_Delete 2648 | } 2649 | 2650 | type MediaFields_sizes_card_url_Create { 2651 | permission: Boolean! 2652 | } 2653 | 2654 | type MediaFields_sizes_card_url_Read { 2655 | permission: Boolean! 2656 | } 2657 | 2658 | type MediaFields_sizes_card_url_Update { 2659 | permission: Boolean! 2660 | } 2661 | 2662 | type MediaFields_sizes_card_url_Delete { 2663 | permission: Boolean! 2664 | } 2665 | 2666 | type MediaFields_sizes_card_width { 2667 | create: MediaFields_sizes_card_width_Create 2668 | read: MediaFields_sizes_card_width_Read 2669 | update: MediaFields_sizes_card_width_Update 2670 | delete: MediaFields_sizes_card_width_Delete 2671 | } 2672 | 2673 | type MediaFields_sizes_card_width_Create { 2674 | permission: Boolean! 2675 | } 2676 | 2677 | type MediaFields_sizes_card_width_Read { 2678 | permission: Boolean! 2679 | } 2680 | 2681 | type MediaFields_sizes_card_width_Update { 2682 | permission: Boolean! 2683 | } 2684 | 2685 | type MediaFields_sizes_card_width_Delete { 2686 | permission: Boolean! 2687 | } 2688 | 2689 | type MediaFields_sizes_card_height { 2690 | create: MediaFields_sizes_card_height_Create 2691 | read: MediaFields_sizes_card_height_Read 2692 | update: MediaFields_sizes_card_height_Update 2693 | delete: MediaFields_sizes_card_height_Delete 2694 | } 2695 | 2696 | type MediaFields_sizes_card_height_Create { 2697 | permission: Boolean! 2698 | } 2699 | 2700 | type MediaFields_sizes_card_height_Read { 2701 | permission: Boolean! 2702 | } 2703 | 2704 | type MediaFields_sizes_card_height_Update { 2705 | permission: Boolean! 2706 | } 2707 | 2708 | type MediaFields_sizes_card_height_Delete { 2709 | permission: Boolean! 2710 | } 2711 | 2712 | type MediaFields_sizes_card_mimeType { 2713 | create: MediaFields_sizes_card_mimeType_Create 2714 | read: MediaFields_sizes_card_mimeType_Read 2715 | update: MediaFields_sizes_card_mimeType_Update 2716 | delete: MediaFields_sizes_card_mimeType_Delete 2717 | } 2718 | 2719 | type MediaFields_sizes_card_mimeType_Create { 2720 | permission: Boolean! 2721 | } 2722 | 2723 | type MediaFields_sizes_card_mimeType_Read { 2724 | permission: Boolean! 2725 | } 2726 | 2727 | type MediaFields_sizes_card_mimeType_Update { 2728 | permission: Boolean! 2729 | } 2730 | 2731 | type MediaFields_sizes_card_mimeType_Delete { 2732 | permission: Boolean! 2733 | } 2734 | 2735 | type MediaFields_sizes_card_filesize { 2736 | create: MediaFields_sizes_card_filesize_Create 2737 | read: MediaFields_sizes_card_filesize_Read 2738 | update: MediaFields_sizes_card_filesize_Update 2739 | delete: MediaFields_sizes_card_filesize_Delete 2740 | } 2741 | 2742 | type MediaFields_sizes_card_filesize_Create { 2743 | permission: Boolean! 2744 | } 2745 | 2746 | type MediaFields_sizes_card_filesize_Read { 2747 | permission: Boolean! 2748 | } 2749 | 2750 | type MediaFields_sizes_card_filesize_Update { 2751 | permission: Boolean! 2752 | } 2753 | 2754 | type MediaFields_sizes_card_filesize_Delete { 2755 | permission: Boolean! 2756 | } 2757 | 2758 | type MediaFields_sizes_card_filename { 2759 | create: MediaFields_sizes_card_filename_Create 2760 | read: MediaFields_sizes_card_filename_Read 2761 | update: MediaFields_sizes_card_filename_Update 2762 | delete: MediaFields_sizes_card_filename_Delete 2763 | } 2764 | 2765 | type MediaFields_sizes_card_filename_Create { 2766 | permission: Boolean! 2767 | } 2768 | 2769 | type MediaFields_sizes_card_filename_Read { 2770 | permission: Boolean! 2771 | } 2772 | 2773 | type MediaFields_sizes_card_filename_Update { 2774 | permission: Boolean! 2775 | } 2776 | 2777 | type MediaFields_sizes_card_filename_Delete { 2778 | permission: Boolean! 2779 | } 2780 | 2781 | type MediaFields_sizes_feature { 2782 | create: MediaFields_sizes_feature_Create 2783 | read: MediaFields_sizes_feature_Read 2784 | update: MediaFields_sizes_feature_Update 2785 | delete: MediaFields_sizes_feature_Delete 2786 | fields: MediaFields_sizes_feature_Fields 2787 | } 2788 | 2789 | type MediaFields_sizes_feature_Create { 2790 | permission: Boolean! 2791 | } 2792 | 2793 | type MediaFields_sizes_feature_Read { 2794 | permission: Boolean! 2795 | } 2796 | 2797 | type MediaFields_sizes_feature_Update { 2798 | permission: Boolean! 2799 | } 2800 | 2801 | type MediaFields_sizes_feature_Delete { 2802 | permission: Boolean! 2803 | } 2804 | 2805 | type MediaFields_sizes_feature_Fields { 2806 | url: MediaFields_sizes_feature_url 2807 | width: MediaFields_sizes_feature_width 2808 | height: MediaFields_sizes_feature_height 2809 | mimeType: MediaFields_sizes_feature_mimeType 2810 | filesize: MediaFields_sizes_feature_filesize 2811 | filename: MediaFields_sizes_feature_filename 2812 | } 2813 | 2814 | type MediaFields_sizes_feature_url { 2815 | create: MediaFields_sizes_feature_url_Create 2816 | read: MediaFields_sizes_feature_url_Read 2817 | update: MediaFields_sizes_feature_url_Update 2818 | delete: MediaFields_sizes_feature_url_Delete 2819 | } 2820 | 2821 | type MediaFields_sizes_feature_url_Create { 2822 | permission: Boolean! 2823 | } 2824 | 2825 | type MediaFields_sizes_feature_url_Read { 2826 | permission: Boolean! 2827 | } 2828 | 2829 | type MediaFields_sizes_feature_url_Update { 2830 | permission: Boolean! 2831 | } 2832 | 2833 | type MediaFields_sizes_feature_url_Delete { 2834 | permission: Boolean! 2835 | } 2836 | 2837 | type MediaFields_sizes_feature_width { 2838 | create: MediaFields_sizes_feature_width_Create 2839 | read: MediaFields_sizes_feature_width_Read 2840 | update: MediaFields_sizes_feature_width_Update 2841 | delete: MediaFields_sizes_feature_width_Delete 2842 | } 2843 | 2844 | type MediaFields_sizes_feature_width_Create { 2845 | permission: Boolean! 2846 | } 2847 | 2848 | type MediaFields_sizes_feature_width_Read { 2849 | permission: Boolean! 2850 | } 2851 | 2852 | type MediaFields_sizes_feature_width_Update { 2853 | permission: Boolean! 2854 | } 2855 | 2856 | type MediaFields_sizes_feature_width_Delete { 2857 | permission: Boolean! 2858 | } 2859 | 2860 | type MediaFields_sizes_feature_height { 2861 | create: MediaFields_sizes_feature_height_Create 2862 | read: MediaFields_sizes_feature_height_Read 2863 | update: MediaFields_sizes_feature_height_Update 2864 | delete: MediaFields_sizes_feature_height_Delete 2865 | } 2866 | 2867 | type MediaFields_sizes_feature_height_Create { 2868 | permission: Boolean! 2869 | } 2870 | 2871 | type MediaFields_sizes_feature_height_Read { 2872 | permission: Boolean! 2873 | } 2874 | 2875 | type MediaFields_sizes_feature_height_Update { 2876 | permission: Boolean! 2877 | } 2878 | 2879 | type MediaFields_sizes_feature_height_Delete { 2880 | permission: Boolean! 2881 | } 2882 | 2883 | type MediaFields_sizes_feature_mimeType { 2884 | create: MediaFields_sizes_feature_mimeType_Create 2885 | read: MediaFields_sizes_feature_mimeType_Read 2886 | update: MediaFields_sizes_feature_mimeType_Update 2887 | delete: MediaFields_sizes_feature_mimeType_Delete 2888 | } 2889 | 2890 | type MediaFields_sizes_feature_mimeType_Create { 2891 | permission: Boolean! 2892 | } 2893 | 2894 | type MediaFields_sizes_feature_mimeType_Read { 2895 | permission: Boolean! 2896 | } 2897 | 2898 | type MediaFields_sizes_feature_mimeType_Update { 2899 | permission: Boolean! 2900 | } 2901 | 2902 | type MediaFields_sizes_feature_mimeType_Delete { 2903 | permission: Boolean! 2904 | } 2905 | 2906 | type MediaFields_sizes_feature_filesize { 2907 | create: MediaFields_sizes_feature_filesize_Create 2908 | read: MediaFields_sizes_feature_filesize_Read 2909 | update: MediaFields_sizes_feature_filesize_Update 2910 | delete: MediaFields_sizes_feature_filesize_Delete 2911 | } 2912 | 2913 | type MediaFields_sizes_feature_filesize_Create { 2914 | permission: Boolean! 2915 | } 2916 | 2917 | type MediaFields_sizes_feature_filesize_Read { 2918 | permission: Boolean! 2919 | } 2920 | 2921 | type MediaFields_sizes_feature_filesize_Update { 2922 | permission: Boolean! 2923 | } 2924 | 2925 | type MediaFields_sizes_feature_filesize_Delete { 2926 | permission: Boolean! 2927 | } 2928 | 2929 | type MediaFields_sizes_feature_filename { 2930 | create: MediaFields_sizes_feature_filename_Create 2931 | read: MediaFields_sizes_feature_filename_Read 2932 | update: MediaFields_sizes_feature_filename_Update 2933 | delete: MediaFields_sizes_feature_filename_Delete 2934 | } 2935 | 2936 | type MediaFields_sizes_feature_filename_Create { 2937 | permission: Boolean! 2938 | } 2939 | 2940 | type MediaFields_sizes_feature_filename_Read { 2941 | permission: Boolean! 2942 | } 2943 | 2944 | type MediaFields_sizes_feature_filename_Update { 2945 | permission: Boolean! 2946 | } 2947 | 2948 | type MediaFields_sizes_feature_filename_Delete { 2949 | permission: Boolean! 2950 | } 2951 | 2952 | type MediaCreateAccess { 2953 | permission: Boolean! 2954 | where: JSONObject 2955 | } 2956 | 2957 | type MediaReadAccess { 2958 | permission: Boolean! 2959 | where: JSONObject 2960 | } 2961 | 2962 | type MediaUpdateAccess { 2963 | permission: Boolean! 2964 | where: JSONObject 2965 | } 2966 | 2967 | type MediaDeleteAccess { 2968 | permission: Boolean! 2969 | where: JSONObject 2970 | } 2971 | 2972 | type pagesAccess { 2973 | fields: PagesFields 2974 | create: PagesCreateAccess 2975 | read: PagesReadAccess 2976 | update: PagesUpdateAccess 2977 | delete: PagesDeleteAccess 2978 | } 2979 | 2980 | type PagesFields { 2981 | title: PagesFields_title 2982 | image: PagesFields_image 2983 | public: PagesFields_public 2984 | layout: PagesFields_layout 2985 | meta: PagesFields_meta 2986 | slug: PagesFields_slug 2987 | } 2988 | 2989 | type PagesFields_title { 2990 | create: PagesFields_title_Create 2991 | read: PagesFields_title_Read 2992 | update: PagesFields_title_Update 2993 | delete: PagesFields_title_Delete 2994 | } 2995 | 2996 | type PagesFields_title_Create { 2997 | permission: Boolean! 2998 | } 2999 | 3000 | type PagesFields_title_Read { 3001 | permission: Boolean! 3002 | } 3003 | 3004 | type PagesFields_title_Update { 3005 | permission: Boolean! 3006 | } 3007 | 3008 | type PagesFields_title_Delete { 3009 | permission: Boolean! 3010 | } 3011 | 3012 | type PagesFields_image { 3013 | create: PagesFields_image_Create 3014 | read: PagesFields_image_Read 3015 | update: PagesFields_image_Update 3016 | delete: PagesFields_image_Delete 3017 | } 3018 | 3019 | type PagesFields_image_Create { 3020 | permission: Boolean! 3021 | } 3022 | 3023 | type PagesFields_image_Read { 3024 | permission: Boolean! 3025 | } 3026 | 3027 | type PagesFields_image_Update { 3028 | permission: Boolean! 3029 | } 3030 | 3031 | type PagesFields_image_Delete { 3032 | permission: Boolean! 3033 | } 3034 | 3035 | type PagesFields_public { 3036 | create: PagesFields_public_Create 3037 | read: PagesFields_public_Read 3038 | update: PagesFields_public_Update 3039 | delete: PagesFields_public_Delete 3040 | } 3041 | 3042 | type PagesFields_public_Create { 3043 | permission: Boolean! 3044 | } 3045 | 3046 | type PagesFields_public_Read { 3047 | permission: Boolean! 3048 | } 3049 | 3050 | type PagesFields_public_Update { 3051 | permission: Boolean! 3052 | } 3053 | 3054 | type PagesFields_public_Delete { 3055 | permission: Boolean! 3056 | } 3057 | 3058 | type PagesFields_layout { 3059 | create: PagesFields_layout_Create 3060 | read: PagesFields_layout_Read 3061 | update: PagesFields_layout_Update 3062 | delete: PagesFields_layout_Delete 3063 | } 3064 | 3065 | type PagesFields_layout_Create { 3066 | permission: Boolean! 3067 | } 3068 | 3069 | type PagesFields_layout_Read { 3070 | permission: Boolean! 3071 | } 3072 | 3073 | type PagesFields_layout_Update { 3074 | permission: Boolean! 3075 | } 3076 | 3077 | type PagesFields_layout_Delete { 3078 | permission: Boolean! 3079 | } 3080 | 3081 | type PagesFields_meta { 3082 | create: PagesFields_meta_Create 3083 | read: PagesFields_meta_Read 3084 | update: PagesFields_meta_Update 3085 | delete: PagesFields_meta_Delete 3086 | fields: PagesFields_meta_Fields 3087 | } 3088 | 3089 | type PagesFields_meta_Create { 3090 | permission: Boolean! 3091 | } 3092 | 3093 | type PagesFields_meta_Read { 3094 | permission: Boolean! 3095 | } 3096 | 3097 | type PagesFields_meta_Update { 3098 | permission: Boolean! 3099 | } 3100 | 3101 | type PagesFields_meta_Delete { 3102 | permission: Boolean! 3103 | } 3104 | 3105 | type PagesFields_meta_Fields { 3106 | title: PagesFields_meta_title 3107 | description: PagesFields_meta_description 3108 | keywords: PagesFields_meta_keywords 3109 | } 3110 | 3111 | type PagesFields_meta_title { 3112 | create: PagesFields_meta_title_Create 3113 | read: PagesFields_meta_title_Read 3114 | update: PagesFields_meta_title_Update 3115 | delete: PagesFields_meta_title_Delete 3116 | } 3117 | 3118 | type PagesFields_meta_title_Create { 3119 | permission: Boolean! 3120 | } 3121 | 3122 | type PagesFields_meta_title_Read { 3123 | permission: Boolean! 3124 | } 3125 | 3126 | type PagesFields_meta_title_Update { 3127 | permission: Boolean! 3128 | } 3129 | 3130 | type PagesFields_meta_title_Delete { 3131 | permission: Boolean! 3132 | } 3133 | 3134 | type PagesFields_meta_description { 3135 | create: PagesFields_meta_description_Create 3136 | read: PagesFields_meta_description_Read 3137 | update: PagesFields_meta_description_Update 3138 | delete: PagesFields_meta_description_Delete 3139 | } 3140 | 3141 | type PagesFields_meta_description_Create { 3142 | permission: Boolean! 3143 | } 3144 | 3145 | type PagesFields_meta_description_Read { 3146 | permission: Boolean! 3147 | } 3148 | 3149 | type PagesFields_meta_description_Update { 3150 | permission: Boolean! 3151 | } 3152 | 3153 | type PagesFields_meta_description_Delete { 3154 | permission: Boolean! 3155 | } 3156 | 3157 | type PagesFields_meta_keywords { 3158 | create: PagesFields_meta_keywords_Create 3159 | read: PagesFields_meta_keywords_Read 3160 | update: PagesFields_meta_keywords_Update 3161 | delete: PagesFields_meta_keywords_Delete 3162 | } 3163 | 3164 | type PagesFields_meta_keywords_Create { 3165 | permission: Boolean! 3166 | } 3167 | 3168 | type PagesFields_meta_keywords_Read { 3169 | permission: Boolean! 3170 | } 3171 | 3172 | type PagesFields_meta_keywords_Update { 3173 | permission: Boolean! 3174 | } 3175 | 3176 | type PagesFields_meta_keywords_Delete { 3177 | permission: Boolean! 3178 | } 3179 | 3180 | type PagesFields_slug { 3181 | create: PagesFields_slug_Create 3182 | read: PagesFields_slug_Read 3183 | update: PagesFields_slug_Update 3184 | delete: PagesFields_slug_Delete 3185 | } 3186 | 3187 | type PagesFields_slug_Create { 3188 | permission: Boolean! 3189 | } 3190 | 3191 | type PagesFields_slug_Read { 3192 | permission: Boolean! 3193 | } 3194 | 3195 | type PagesFields_slug_Update { 3196 | permission: Boolean! 3197 | } 3198 | 3199 | type PagesFields_slug_Delete { 3200 | permission: Boolean! 3201 | } 3202 | 3203 | type PagesCreateAccess { 3204 | permission: Boolean! 3205 | where: JSONObject 3206 | } 3207 | 3208 | type PagesReadAccess { 3209 | permission: Boolean! 3210 | where: JSONObject 3211 | } 3212 | 3213 | type PagesUpdateAccess { 3214 | permission: Boolean! 3215 | where: JSONObject 3216 | } 3217 | 3218 | type PagesDeleteAccess { 3219 | permission: Boolean! 3220 | where: JSONObject 3221 | } 3222 | 3223 | type Mutation { 3224 | createUser(data: mutationUserInput!, draft: Boolean): User 3225 | updateUser(id: String!, data: mutationUserUpdateInput!, draft: Boolean, autosave: Boolean): User 3226 | deleteUser(id: String!): User 3227 | refreshTokenUser(token: String): usersRefreshedUser 3228 | logoutUser: String 3229 | unlockUser(email: String!): Boolean! 3230 | loginUser(email: String, password: String): usersLoginResult 3231 | forgotPasswordUser(email: String!, disableEmail: Boolean, expiration: Int): Boolean! 3232 | resetPasswordUser(token: String, password: String): usersResetPassword 3233 | verifyEmailUser(token: String): Boolean 3234 | createMedia(data: mutationMediaInput!, draft: Boolean): Media 3235 | updateMedia(id: String!, data: mutationMediaUpdateInput!, draft: Boolean, autosave: Boolean): Media 3236 | deleteMedia(id: String!): Media 3237 | createPage(data: mutationPageInput!, draft: Boolean): Page 3238 | updatePage(id: String!, data: mutationPageUpdateInput!, draft: Boolean, autosave: Boolean): Page 3239 | deletePage(id: String!): Page 3240 | updatePreference(key: String!, value: JSON): Preference 3241 | deletePreference(key: String!): Preference 3242 | } 3243 | 3244 | input mutationUserInput { 3245 | name: String! 3246 | role: User_role_MutationInput! 3247 | email: String 3248 | resetPasswordToken: String 3249 | resetPasswordExpiration: String 3250 | loginAttempts: Float 3251 | lockUntil: String 3252 | password: String! 3253 | } 3254 | 3255 | enum User_role_MutationInput { 3256 | admin 3257 | user 3258 | } 3259 | 3260 | input mutationUserUpdateInput { 3261 | name: String 3262 | role: UserUpdate_role_MutationInput 3263 | email: String 3264 | resetPasswordToken: String 3265 | resetPasswordExpiration: String 3266 | loginAttempts: Float 3267 | lockUntil: String 3268 | password: String 3269 | } 3270 | 3271 | enum UserUpdate_role_MutationInput { 3272 | admin 3273 | user 3274 | } 3275 | 3276 | type usersRefreshedUser { 3277 | user: usersJWT 3278 | refreshedToken: String 3279 | exp: Int 3280 | } 3281 | 3282 | type usersJWT { 3283 | email: EmailAddress! 3284 | collection: String! 3285 | } 3286 | 3287 | type usersLoginResult { 3288 | token: String 3289 | user: User 3290 | exp: Int 3291 | } 3292 | 3293 | type usersResetPassword { 3294 | token: String 3295 | user: User 3296 | } 3297 | 3298 | input mutationMediaInput { 3299 | alt: String! 3300 | url: String 3301 | filename: String 3302 | mimeType: String 3303 | filesize: Float 3304 | width: Float 3305 | height: Float 3306 | sizes: mutationMedia_SizesInput 3307 | } 3308 | 3309 | input mutationMedia_SizesInput { 3310 | card: mutationMedia_Sizes_CardInput 3311 | feature: mutationMedia_Sizes_FeatureInput 3312 | } 3313 | 3314 | input mutationMedia_Sizes_CardInput { 3315 | url: String 3316 | width: Float 3317 | height: Float 3318 | mimeType: String 3319 | filesize: Float 3320 | filename: String 3321 | } 3322 | 3323 | input mutationMedia_Sizes_FeatureInput { 3324 | url: String 3325 | width: Float 3326 | height: Float 3327 | mimeType: String 3328 | filesize: Float 3329 | filename: String 3330 | } 3331 | 3332 | input mutationMediaUpdateInput { 3333 | alt: String 3334 | url: String 3335 | filename: String 3336 | mimeType: String 3337 | filesize: Float 3338 | width: Float 3339 | height: Float 3340 | sizes: mutationMediaUpdate_SizesInput 3341 | } 3342 | 3343 | input mutationMediaUpdate_SizesInput { 3344 | card: mutationMediaUpdate_Sizes_CardInput 3345 | feature: mutationMediaUpdate_Sizes_FeatureInput 3346 | } 3347 | 3348 | input mutationMediaUpdate_Sizes_CardInput { 3349 | url: String 3350 | width: Float 3351 | height: Float 3352 | mimeType: String 3353 | filesize: Float 3354 | filename: String 3355 | } 3356 | 3357 | input mutationMediaUpdate_Sizes_FeatureInput { 3358 | url: String 3359 | width: Float 3360 | height: Float 3361 | mimeType: String 3362 | filesize: Float 3363 | filename: String 3364 | } 3365 | 3366 | input mutationPageInput { 3367 | title: String! 3368 | image: String 3369 | public: Boolean 3370 | layout: JSON 3371 | meta: mutationPage_MetaInput 3372 | slug: String 3373 | } 3374 | 3375 | input mutationPage_MetaInput { 3376 | title: String 3377 | description: String 3378 | keywords: String 3379 | } 3380 | 3381 | input mutationPageUpdateInput { 3382 | title: String 3383 | image: String 3384 | public: Boolean 3385 | layout: JSON 3386 | meta: mutationPageUpdate_MetaInput 3387 | slug: String 3388 | } 3389 | 3390 | input mutationPageUpdate_MetaInput { 3391 | title: String 3392 | description: String 3393 | keywords: String 3394 | } -------------------------------------------------------------------------------- /apps/cms/src/payload-types.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /** 3 | * This file was automatically generated by Payload CMS. 4 | * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config, 5 | * and re-run `payload generate:types` to regenerate this file. 6 | */ 7 | 8 | export interface Config { 9 | collections: { 10 | users: User; 11 | media: Media; 12 | pages: Page; 13 | }; 14 | // eslint-disable-next-line @typescript-eslint/ban-types 15 | globals: {}; 16 | } 17 | export interface User { 18 | id: string; 19 | name: string; 20 | role: 'admin' | 'user'; 21 | email?: string; 22 | resetPasswordToken?: string; 23 | resetPasswordExpiration?: string; 24 | loginAttempts?: number; 25 | lockUntil?: string; 26 | createdAt: string; 27 | updatedAt: string; 28 | password?: string; 29 | } 30 | export interface Media { 31 | id: string; 32 | alt: string; 33 | url?: string; 34 | filename?: string; 35 | mimeType?: string; 36 | filesize?: number; 37 | width?: number; 38 | height?: number; 39 | sizes: { 40 | card: { 41 | url?: string; 42 | width?: number; 43 | height?: number; 44 | mimeType?: string; 45 | filesize?: number; 46 | filename?: string; 47 | }; 48 | feature: { 49 | url?: string; 50 | width?: number; 51 | height?: number; 52 | mimeType?: string; 53 | filesize?: number; 54 | filename?: string; 55 | }; 56 | }; 57 | createdAt: string; 58 | updatedAt: string; 59 | } 60 | export interface Page { 61 | id: string; 62 | title: string; 63 | image?: string | Media; 64 | public?: boolean; 65 | layout: ( 66 | | { 67 | content?: { 68 | [k: string]: unknown; 69 | }[]; 70 | buttons: { 71 | label: string; 72 | type: 'page' | 'custom'; 73 | page?: string | Page; 74 | url?: string; 75 | newTab: boolean; 76 | id?: string; 77 | }[]; 78 | id?: string; 79 | blockName?: string; 80 | blockType: 'cta'; 81 | } 82 | | { 83 | content?: { 84 | [k: string]: unknown; 85 | }[]; 86 | id?: string; 87 | blockName?: string; 88 | blockType: 'content'; 89 | } 90 | | { 91 | image: string | Media; 92 | type: 'card' | 'feature'; 93 | caption?: { 94 | [k: string]: unknown; 95 | }[]; 96 | id?: string; 97 | blockName?: string; 98 | blockType: 'image'; 99 | } 100 | )[]; 101 | meta: { 102 | title?: string; 103 | description?: string; 104 | keywords?: string; 105 | }; 106 | slug?: string; 107 | createdAt: string; 108 | updatedAt: string; 109 | } 110 | -------------------------------------------------------------------------------- /apps/cms/src/seed/home-page.json: -------------------------------------------------------------------------------- 1 | { 2 | "layout": [ 3 | { 4 | "blockType": "content", 5 | "blockName": "Intro - Rich Text Demo", 6 | "content": [ 7 | { 8 | "children": [ 9 | { 10 | "text": "Here is an H2 to introduce the article" 11 | } 12 | ], 13 | "type": "h3" 14 | }, 15 | { 16 | "children": [ 17 | { 18 | "text": "Here is some content that will be rendered as an HTML paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam." 19 | } 20 | ], 21 | "type": "p" 22 | }, 23 | { 24 | "children": [ 25 | { 26 | "text": "Lorem Ipsum", 27 | "bold": true 28 | }, 29 | { 30 | "text": " is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book." 31 | } 32 | ], 33 | "type": "p" 34 | } 35 | ] 36 | }, 37 | { 38 | "type": "feature", 39 | "blockType": "image", 40 | "blockName": "Wide Image Demo", 41 | "image": "{{IMAGE_ID}}", 42 | "caption": [ 43 | { 44 | "children": [ 45 | { 46 | "text": "Here is an image caption. It's got a " 47 | }, 48 | { 49 | "type": "link", 50 | "url": "https://payloadcms.com", 51 | "newTab": true, 52 | "children": [ 53 | { 54 | "text": "link embedded in it" 55 | } 56 | ] 57 | }, 58 | { 59 | "text": "" 60 | } 61 | ] 62 | } 63 | ] 64 | }, 65 | { 66 | "type": "card", 67 | "blockType": "image", 68 | "blockName": "Normal Width Image Demo", 69 | "image": "{{IMAGE_ID}}", 70 | "caption": [ 71 | { 72 | "children": [ 73 | { 74 | "text": "This is a caption for an image." 75 | } 76 | ] 77 | } 78 | ] 79 | }, 80 | { 81 | "blockType": "cta", 82 | "blockName": "Calls to Action", 83 | "content": [ 84 | { 85 | "children": [ 86 | { 87 | "text": "Here is a Call to Action block" 88 | } 89 | ], 90 | "type": "h4" 91 | } 92 | ], 93 | "buttons": [ 94 | { 95 | "type": "custom", 96 | "label": "Click me", 97 | "url": "https://payloadcms.com", 98 | "newTab": true 99 | }, 100 | { 101 | "type": "page", 102 | "label": "Don't click me", 103 | "page": "{{SAMPLE_PAGE_ID}}" 104 | } 105 | ] 106 | } 107 | ], 108 | "title": "Home page", 109 | "image": "{{IMAGE_ID}}", 110 | "slug": "home", 111 | "public": true, 112 | "meta": { 113 | "title": "Home Page Title", 114 | "description": "This should be optimized for SEO.", 115 | "keywords": "payload, remix, headless cms, boilerplate" 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /apps/cms/src/seed/index.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import type { Payload } from 'payload'; 3 | import { pagesSlug } from '../collections/Pages'; 4 | import { mediaSlug } from '../collections/Media'; 5 | import { usersSlug } from '../collections/Users'; 6 | import home from './home-page.json'; 7 | import posts from './posts-page'; 8 | 9 | export const seedPages = async (payload: Payload) => { 10 | const { totalDocs } = await payload.find({ 11 | collection: pagesSlug, 12 | }); 13 | if (!totalDocs) { 14 | const createdMedia = await payload.create({ 15 | collection: mediaSlug, 16 | data: { 17 | alt: 'Payload', 18 | // Payloads incorrectly expects a 'sizes' object here, which should be optional since they are created during upload 19 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 20 | } as any, 21 | filePath: path.resolve(__dirname, './payload.jpg'), 22 | }); 23 | 24 | const createdPostsPage = await payload.create({ 25 | collection: pagesSlug, 26 | data: posts, 27 | }); 28 | 29 | const publicString = JSON.stringify(home) 30 | .replace(/{{IMAGE_ID}}/g, createdMedia.id) 31 | .replace(/{{SAMPLE_PAGE_ID}}/g, createdPostsPage.id); 32 | 33 | await payload.create({ 34 | collection: pagesSlug, 35 | data: JSON.parse(publicString), 36 | }); 37 | payload.logger.info(`Successfully seeded pages into database`); 38 | } 39 | }; 40 | 41 | export const seedUsers = async (payload: Payload) => { 42 | const { totalDocs } = await payload.find({ 43 | collection: usersSlug, 44 | }); 45 | if (!totalDocs) { 46 | payload.create({ 47 | collection: usersSlug, 48 | data: { 49 | email: 'dev@payloadcms.com', 50 | password: 'qwerty', 51 | name: 'Admin User', 52 | role: 'admin', 53 | }, 54 | }); 55 | payload.create({ 56 | collection: usersSlug, 57 | data: { 58 | email: 'user@payloadcms.com', 59 | password: 'qwerty', 60 | name: 'Frontend User', 61 | role: 'user', 62 | }, 63 | }); 64 | payload.logger.info(`Successfully seeded users into database`); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /apps/cms/src/seed/payload.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payloadcms/remix-server/5ed4bcd2d044ead0ea6e894610cc9df24fa86f7e/apps/cms/src/seed/payload.jpg -------------------------------------------------------------------------------- /apps/cms/src/seed/posts-page.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | "layout": [ 3 | { 4 | "blockType": "content" as const, 5 | "blockName": "Page Content", 6 | "content": [ 7 | { 8 | "children": [ 9 | { 10 | "text": "This is a sample page which is only visible to authenticated users." 11 | } 12 | ], 13 | "type": "h3" 14 | }, 15 | { 16 | "children": [ 17 | { 18 | "text": "" 19 | }, 20 | { 21 | "type": "link", 22 | "url": "/", 23 | "newTab": false, 24 | "children": [ 25 | { 26 | "text": "Go back home" 27 | } 28 | ] 29 | }, 30 | { 31 | "text": "" 32 | } 33 | ], 34 | "type": "p" 35 | } 36 | ] 37 | } 38 | ], 39 | "title": "Posts", 40 | "public": false, 41 | "slug": "posts" as const, 42 | "meta": {} 43 | } 44 | -------------------------------------------------------------------------------- /apps/cms/src/types.ts: -------------------------------------------------------------------------------- 1 | export * from 'payload/types' -------------------------------------------------------------------------------- /apps/cms/src/utilities/formatSlug.ts: -------------------------------------------------------------------------------- 1 | import { FieldHook } from 'payload/types'; 2 | 3 | const format = (val: string): string => val.replace(/ /g, '-').replace(/[^\w-/]+/g, '').toLowerCase(); 4 | 5 | const formatSlug = (fallback: string): FieldHook => ({ value, originalDoc, data }) => { 6 | if (typeof value === 'string') { 7 | return format(value); 8 | } 9 | const fallbackData = (data && data[fallback]) || (originalDoc && originalDoc[fallback]); 10 | 11 | if (fallbackData && typeof fallbackData === 'string') { 12 | return format(fallbackData); 13 | } 14 | 15 | return value; 16 | }; 17 | 18 | export default formatSlug; 19 | -------------------------------------------------------------------------------- /apps/cms/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "paths": { 6 | "payload/generated-types": ["./src/payload-types.ts"] 7 | }, 8 | }, 9 | "include": ["src/**/*"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/server/.env: -------------------------------------------------------------------------------- 1 | MONGODB_URL=mongodb://localhost/remix-server 2 | PAYLOADCMS_SECRET= 3 | -------------------------------------------------------------------------------- /apps/server/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | .next/ 12 | out/ 13 | build 14 | dist 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | .npm-debug.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | 32 | # turbo 33 | .turbo 34 | 35 | -------------------------------------------------------------------------------- /apps/server/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": true, 3 | "ext": "ts", 4 | "exec": "node -r ts-node/register src/index.ts", 5 | "watch": ["./src/index.ts", "../cms/src"] 6 | } 7 | -------------------------------------------------------------------------------- /apps/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@org/server", 3 | "description": "Server for running several web applications", 4 | "version": "1.0.0", 5 | "license": "MIT", 6 | "scripts": { 7 | "clean": "rm -rf node_modules build dist .turbo", 8 | "dev": "cross-env PAYLOAD_CONFIG_PATH=../cms/src/config.ts NODE_ENV=development nodemon", 9 | "build:server": "rm -rf dist/* && tsc", 10 | "build": "pnpm build:server", 11 | "serve": "cross-env PAYLOAD_CONFIG_PATH=../cms/dist/config.js NODE_ENV=production node --conditions=serve dist/index.js", 12 | "lint": "eslint --ext .ts,.js ./src/index.ts" 13 | }, 14 | "dependencies": { 15 | "@org/cms": "workspace:*", 16 | "@org/shared": "workspace:*", 17 | "@org/web": "workspace:*", 18 | "express": "^4.18.2" 19 | }, 20 | "devDependencies": { 21 | "@types/express": "^4.17.17", 22 | "@types/node": "^18.13.0", 23 | "copyfiles": "^2.4.1", 24 | "cross-env": "^7.0.3", 25 | "nodemon": "^2.0.20", 26 | "ts-node": "^10.9.1", 27 | "typescript": "^4.9.5" 28 | }, 29 | "engines": { 30 | "node": ">=16.0.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /apps/server/src/index.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import fs = require('fs'); 3 | 4 | import express = require('express'); 5 | import cms = require('@org/cms'); 6 | import shared = require('@org/shared'); 7 | import webExpressAdapter = require('@org/web/express'); 8 | 9 | const { payload } = cms; 10 | const { dotenv } = shared; 11 | const { createRequestHandler } = webExpressAdapter; 12 | 13 | // Loading environment variables, .env > .env.local 14 | const config = dotenv.config(); 15 | 16 | if (config.error) { 17 | throw config.error; 18 | } 19 | 20 | const localEnvFilePath = path.resolve(process.cwd(), '.env.local'); 21 | if (fs.existsSync(localEnvFilePath)) { 22 | const localConfig = dotenv.config({ 23 | path: localEnvFilePath, 24 | override: true, 25 | }); 26 | if (localConfig.error) { 27 | throw localConfig.error; 28 | } 29 | } 30 | 31 | const MONGODB_URL = process.env.MONGODB_URL ?? ""; 32 | const PAYLOADCMS_SECRET = process.env.PAYLOADCMS_SECRET ?? ""; 33 | const ENVIRONMENT = process.env.NODE_ENV; 34 | 35 | // During development this is fine. Conditionalize this for production as needed. 36 | const WEB_BUILD_DIR = path.join(process.cwd(), '../web/build'); 37 | const WEB_PUBLIC_DIR = path.join(process.cwd(), '../web/public/web'); 38 | const WEB_PUBLIC_BUILD_DIR = path.join( 39 | process.cwd(), 40 | '../web/public/web/build' 41 | ); 42 | 43 | const app = express(); 44 | app.disable('x-powered-by'); 45 | 46 | // Serving the web static files with different caching strategies 47 | app.use( 48 | '/web/build', 49 | express.static(WEB_PUBLIC_BUILD_DIR, { 50 | immutable: true, 51 | maxAge: '1y', 52 | redirect: false, 53 | }) 54 | ); 55 | app.use( 56 | '/web', 57 | express.static(WEB_PUBLIC_DIR, { maxAge: '1h', redirect: false }) 58 | ); 59 | 60 | payload.init({ 61 | express: app, 62 | mongoURL: MONGODB_URL, 63 | secret: PAYLOADCMS_SECRET, 64 | onInit: () => { 65 | payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`); 66 | }, 67 | }).then(() => { 68 | 69 | app.use(payload.authenticate); 70 | 71 | app.all( 72 | '*', 73 | ENVIRONMENT === 'development' 74 | ? (req, res, next) => { 75 | purgeRequireCache(); 76 | 77 | return createRequestHandler({ 78 | build: require(WEB_BUILD_DIR), 79 | mode: ENVIRONMENT, 80 | getLoadContext(req, res) { 81 | return { 82 | payload: req.payload, 83 | user: req?.user, 84 | res, 85 | }; 86 | }, 87 | })(req, res, next); 88 | } 89 | : createRequestHandler({ 90 | build: require(WEB_BUILD_DIR), 91 | mode: ENVIRONMENT, 92 | getLoadContext(req, res) { 93 | return { 94 | payload: req.payload, 95 | user: req?.user, 96 | res, 97 | }; 98 | }, 99 | }) 100 | ); 101 | 102 | const port = process.env.PORT || 3000; 103 | 104 | app.listen(port, () => { 105 | console.log(`Express server listening on port ${port}`); 106 | }); 107 | }); 108 | 109 | 110 | function purgeRequireCache() { 111 | // purge require cache on requests for "server side HMR" this won't let 112 | // you have in-memory objects between requests in development, 113 | // alternatively you can set up nodemon/pm2-dev to restart the server on 114 | // file changes, but then you'll have to reconnect to databases/etc on each 115 | // change. We prefer the DX of this, so we've included it for you by default 116 | 117 | for (const key in require.cache) { 118 | if (key.startsWith(WEB_BUILD_DIR)) { 119 | delete require.cache[key]; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /apps/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "baseUrl": "." 6 | }, 7 | "ts-node": { 8 | "transpileOnly": true 9 | }, 10 | "include": ["src/**/*", "remix.d.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /apps/web/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["@org/eslint-config/remix"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/web/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | /.cache 4 | /build 5 | /public/build 6 | .env 7 | -------------------------------------------------------------------------------- /apps/web/app/assets/payload-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /apps/web/app/components/Blocks/RenderBlocks.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { components } from '.'; 3 | import type { Page } from '@org/cms'; 4 | 5 | export type Layout = Page['layout'] 6 | 7 | type Props = { 8 | layout: Layout; 9 | className?: string; 10 | }; 11 | 12 | export const RenderBlocks: React.FC = ({ layout, className }) => ( 13 |
14 | {layout.map((block, i) => { 15 | const Block: React.FC = components[block.blockType]; 16 | 17 | if (Block) { 18 | return ( 19 |
20 | 21 |
22 | ); 23 | } 24 | 25 | return null; 26 | })} 27 |
28 | ); 29 | -------------------------------------------------------------------------------- /apps/web/app/components/Blocks/RichText.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import escapeHTML from 'escape-html'; 3 | import { Text } from 'slate'; 4 | 5 | type RichTextProps = JSX.IntrinsicElements['div'] & { 6 | content: any; 7 | }; 8 | 9 | const RichText = ({ className, content }: RichTextProps) => { 10 | if (!content) { 11 | return null; 12 | } 13 | 14 | return
{serialize(content)}
; 15 | }; 16 | 17 | // eslint-disable-next-line no-use-before-define 18 | type Children = Leaf[]; 19 | 20 | type Leaf = { 21 | type: string; 22 | value?: { 23 | url: string; 24 | alt: string; 25 | }; 26 | children?: Children; 27 | url?: string; 28 | [key: string]: unknown; 29 | }; 30 | 31 | const serialize = (children?: Children) => 32 | children?.map((node, i) => { 33 | if (Text.isText(node)) { 34 | let text = ( 35 | 38 | ); 39 | 40 | if (node.bold) { 41 | text = {text}; 42 | } 43 | 44 | if (node.code) { 45 | text = {text}; 46 | } 47 | 48 | if (node.italic) { 49 | text = {text}; 50 | } 51 | 52 | if (node.underline) { 53 | text = ( 54 | 55 | {text} 56 | 57 | ); 58 | } 59 | 60 | if (node.strikethrough) { 61 | text = ( 62 | 63 | {text} 64 | 65 | ); 66 | } 67 | 68 | return {text}; 69 | } 70 | 71 | if (!node) { 72 | return null; 73 | } 74 | 75 | switch (node.type) { 76 | case 'h1': 77 | return

{serialize(node.children)}

; 78 | case 'h2': 79 | return

{serialize(node.children)}

; 80 | case 'h3': 81 | return

{serialize(node.children)}

; 82 | case 'h4': 83 | return

{serialize(node.children)}

; 84 | case 'h5': 85 | return
{serialize(node.children)}
; 86 | case 'h6': 87 | return
{serialize(node.children)}
; 88 | case 'quote': 89 | return ( 90 |
{serialize(node.children)}
91 | ); 92 | case 'ul': 93 | return
    {serialize(node.children)}
; 94 | case 'ol': 95 | return
    {serialize(node.children)}
; 96 | case 'li': 97 | return
  • {serialize(node.children)}
  • ; 98 | case 'link': 99 | return ( 100 | 101 | {serialize(node.children)} 102 | 103 | ); 104 | 105 | default: 106 | return

    {serialize(node.children)}

    ; 107 | } 108 | }); 109 | 110 | export { RichText }; 111 | -------------------------------------------------------------------------------- /apps/web/app/components/Blocks/index.tsx: -------------------------------------------------------------------------------- 1 | import { CallToAction as cta } from './sections/CallToAction'; 2 | import { Image as image } from './sections/Image'; 3 | import { Content as content } from './sections/Content'; 4 | 5 | export const components = { 6 | content, 7 | cta, 8 | image, 9 | }; 10 | 11 | export { RenderBlocks } from './RenderBlocks'; 12 | -------------------------------------------------------------------------------- /apps/web/app/components/Blocks/sections/CallToAction.tsx: -------------------------------------------------------------------------------- 1 | import type { Page } from '@org/cms'; 2 | import { Link } from '@remix-run/react'; 3 | import { RichText } from '../RichText'; 4 | 5 | type CallToActionProps = Page['layout'][0]; 6 | 7 | export const CallToAction: React.FC = (props) => { 8 | if (props.blockType !== 'cta') return null; 9 | const { content, buttons } = props; 10 | 11 | return ( 12 |
    13 |
    14 | 15 | {buttons && ( 16 |
      17 | {buttons.map((button, i) => ( 18 |
    • 19 | {typeof button?.page === 'object' && ( 20 | 24 | {button.label} 25 | 26 | )} 27 | {typeof button?.page === 'string' && ( 28 | 36 | {button.label} 37 | 38 | )} 39 |
    • 40 | ))} 41 |
    42 | )} 43 |
    44 |
    45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /apps/web/app/components/Blocks/sections/Content.tsx: -------------------------------------------------------------------------------- 1 | import type { Page } from '@org/cms'; 2 | import { RichText } from '../RichText'; 3 | 4 | type ContentTypeProps = Page['layout'][0]; 5 | export const Content = (props: ContentTypeProps) => { 6 | if (props.blockType !== 'content') return null; 7 | const { content } = props; 8 | 9 | return ( 10 |
    11 | 12 |
    13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /apps/web/app/components/Blocks/sections/Image.tsx: -------------------------------------------------------------------------------- 1 | import type { Page } from '@org/cms'; 2 | import { RichText } from '../RichText'; 3 | import { sizes } from './sizes'; 4 | 5 | type ImageProps = Page['layout'][0] 6 | 7 | export const Image = (props: ImageProps) => { 8 | if (props.blockType !== 'image') return null; 9 | const { image, caption, type } = props; 10 | 11 | if (typeof image === 'object') { 12 | let filenameToRender = image.filename; 13 | let { width } = image; 14 | let { height } = image; 15 | 16 | if (image.sizes[type]) { 17 | filenameToRender = image.sizes[type].filename; 18 | width = image.sizes[type].width; 19 | height = image.sizes[type].height; 20 | } 21 | 22 | const sizesToUse = sizes 23 | .map((size) => `(max-width: ${size}px) ${size}px`) 24 | .join(', '); 25 | 26 | return ( 27 |
    28 | {image.alt} 35 | {caption && ( 36 | 37 | )} 38 |
    39 | ); 40 | } 41 | 42 | return null; 43 | }; 44 | -------------------------------------------------------------------------------- /apps/web/app/components/Blocks/sections/sizes.js: -------------------------------------------------------------------------------- 1 | export const sizes = [480, 1024, 1280, 1920, 2560]; 2 | -------------------------------------------------------------------------------- /apps/web/app/components/Logo.tsx: -------------------------------------------------------------------------------- 1 | import LogoSrc from '../assets/payload-logo.svg'; 2 | 3 | type Props = Omit; 4 | 5 | export const Logo = (props: Props) => ( 6 | Payload Logo 7 | ); 8 | -------------------------------------------------------------------------------- /apps/web/app/entry.client.tsx: -------------------------------------------------------------------------------- 1 | import { RemixBrowser } from '@remix-run/react'; 2 | import { hydrateRoot } from 'react-dom/client'; 3 | 4 | hydrateRoot(document, ); 5 | -------------------------------------------------------------------------------- /apps/web/app/entry.server.tsx: -------------------------------------------------------------------------------- 1 | import { PassThrough } from 'stream'; 2 | import type { EntryContext } from '@remix-run/node'; 3 | import { Response } from '@remix-run/node'; 4 | import { RemixServer } from '@remix-run/react'; 5 | import { renderToPipeableStream } from 'react-dom/server'; 6 | 7 | const ABORT_DELAY = 5000; 8 | 9 | export default function handleRequest( 10 | request: Request, 11 | responseStatusCode: number, 12 | responseHeaders: Headers, 13 | remixContext: EntryContext 14 | ) { 15 | return new Promise((resolve, reject) => { 16 | let didError = false; 17 | 18 | const { pipe, abort } = renderToPipeableStream( 19 | , 20 | { 21 | onShellReady: () => { 22 | const body = new PassThrough(); 23 | 24 | responseHeaders.set('Content-Type', 'text/html'); 25 | 26 | resolve( 27 | new Response(body, { 28 | headers: responseHeaders, 29 | status: didError ? 500 : responseStatusCode, 30 | }) 31 | ); 32 | 33 | pipe(body); 34 | }, 35 | onShellError: (err) => { 36 | reject(err); 37 | }, 38 | onError: (error) => { 39 | didError = true; 40 | 41 | console.error(error); 42 | }, 43 | } 44 | ); 45 | 46 | setTimeout(abort, ABORT_DELAY); 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /apps/web/app/root.tsx: -------------------------------------------------------------------------------- 1 | import type { Page, User } from '@org/cms'; 2 | import type { 3 | ErrorBoundaryComponent, 4 | LinksFunction, 5 | LoaderFunction, 6 | MetaFunction, 7 | TypedResponse, 8 | } from '@remix-run/node'; 9 | import { redirect } from '@remix-run/node'; 10 | import { 11 | Links, 12 | LiveReload, 13 | Meta, 14 | Outlet, 15 | Scripts, 16 | ScrollRestoration, 17 | } from '@remix-run/react'; 18 | 19 | import uiStyles from '@org/ui/styles.css'; 20 | import styles from './styles/global.css'; 21 | 22 | export const meta: MetaFunction = () => ({ 23 | charset: 'utf-8', 24 | title: 'Payload CMS & Remix Monorepo', 25 | viewport: 'width=device-width,initial-scale=1', 26 | }); 27 | 28 | export const links: LinksFunction = () => [ 29 | { 30 | rel: 'stylesheet', 31 | href: uiStyles, 32 | }, 33 | { 34 | rel: 'stylesheet', 35 | href: styles, 36 | }, 37 | ]; 38 | 39 | export type RootLoaderData = { 40 | pages: Page[]; 41 | user?: { 42 | user?: User; 43 | token?: string; 44 | exp?: number; 45 | }; 46 | }; 47 | export const loader: LoaderFunction = async ({ 48 | context: { payload, user }, 49 | request, 50 | }): Promise> => { 51 | const { pathname } = new URL(request.url); 52 | if (pathname === '/') { 53 | return redirect('/home'); 54 | } 55 | 56 | const { docs: pages } = await payload.find({ 57 | collection: 'pages', 58 | user, 59 | overrideAccess: false, 60 | }); 61 | 62 | return { pages, user }; 63 | }; 64 | 65 | export default function App() { 66 | return ( 67 | 68 | 69 | 70 | 71 | 72 | 77 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | ); 90 | } 91 | 92 | export const ErrorBoundary: ErrorBoundaryComponent = ({ error }) => { 93 | return ( 94 | 95 | 96 | 97 | 98 | 99 | 100 |
    ERROR: {error.message}
    101 | 102 | 103 | 104 | 105 | 106 | 107 | ); 108 | }; 109 | -------------------------------------------------------------------------------- /apps/web/app/routes/__page.tsx: -------------------------------------------------------------------------------- 1 | import { NavLink, Outlet, useMatches } from '@remix-run/react'; 2 | import { Button } from '@org/ui'; 3 | import { Logo } from '~/components/Logo'; 4 | import type { RootLoaderData } from '~/root'; 5 | 6 | export default function Index() { 7 | const matches = useMatches(); 8 | 9 | const [{ data }] = matches; 10 | const { pages, user } = (data as RootLoaderData) || {}; 11 | 12 | return !pages ? ( 13 |
    14 | ) : ( 15 |
    16 | 54 | 55 |
    56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /apps/web/app/routes/__page/$page.tsx: -------------------------------------------------------------------------------- 1 | import type { MetaFunction } from '@remix-run/node'; 2 | import { useMatches, useParams } from '@remix-run/react'; 3 | import { RenderBlocks } from '~/components/Blocks'; 4 | import type { RootLoaderData } from '~/root'; 5 | import { findPageBySlug } from '~/utils'; 6 | 7 | export const meta: MetaFunction = ({ parentsData, params }) => { 8 | const { page: pageSlug } = params; 9 | const { 10 | root: { pages }, 11 | } = parentsData; 12 | 13 | const page = findPageBySlug(pageSlug ?? 'home', pages); 14 | return { 15 | title: page?.meta.title, 16 | description: page?.meta.description, 17 | keywords: page?.meta.keywords, 18 | }; 19 | }; 20 | 21 | export default function Page() { 22 | const { page: pageSlug } = useParams(); 23 | 24 | const [{ data }] = useMatches(); 25 | const { pages } = data as RootLoaderData; 26 | const page = findPageBySlug(pageSlug ?? 'home', pages); 27 | 28 | return ( 29 |
    30 | {page?.layout ? ( 31 | 32 | ) : ( 33 | 'This page seem to be empty' 34 | )} 35 |
    36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /apps/web/app/routes/__page/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from './$page'; 2 | -------------------------------------------------------------------------------- /apps/web/app/routes/login.tsx: -------------------------------------------------------------------------------- 1 | import type { ActionFunction } from '@remix-run/node'; 2 | import { Response } from '@remix-run/node'; 3 | import { redirect } from '@remix-run/node'; 4 | import { Link } from '@remix-run/react'; 5 | import { Logo } from '~/components/Logo'; 6 | 7 | export const action: ActionFunction = async ({ 8 | context: { payload, user, res }, 9 | request, 10 | }) => { 11 | if (user) { 12 | return redirect('/'); 13 | } 14 | 15 | const form = await request.formData(); 16 | const email = form.get('email'); 17 | const password = form.get('password'); 18 | if (typeof email !== 'string' || typeof password !== 'string') { 19 | return new Response('You must provide both a email and a password', { 20 | status: 401, 21 | }); 22 | } 23 | 24 | await payload.login({ 25 | collection: 'users', 26 | data: { email, password }, 27 | res, 28 | }); 29 | 30 | return redirect('/'); 31 | }; 32 | 33 | export default function Login() { 34 | return ( 35 |
    36 |
    37 | 38 |

    Login

    39 |
    40 |
    45 |
    46 | 47 | 48 |
    49 |
    50 | 51 | 52 |
    53 | 54 |
    55 | Back to home 56 |
    57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /apps/web/app/routes/logout.tsx: -------------------------------------------------------------------------------- 1 | import type { ActionFunction } from '@remix-run/node'; 2 | import { redirect } from '@remix-run/node'; 3 | 4 | export const action: ActionFunction = async ({ context: { res } }) => { 5 | const cookieOptions = { 6 | path: '/', 7 | httpOnly: true, 8 | sameSite: 'lax' as 'lax', // Litteral types out of wack, typescript? 9 | }; 10 | res.clearCookie('payload-token', cookieOptions); 11 | return redirect('/'); 12 | }; 13 | 14 | export default () => { 15 |
    16 |

    You have been successfully logged out

    17 |
    18 | } 19 | -------------------------------------------------------------------------------- /apps/web/app/styles/global.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | ul { 7 | list-style-type: none; 8 | } 9 | 10 | body { 11 | min-height: 100vh; 12 | color: var(--font-color); 13 | font-family: 'Lato', sans-serif; 14 | } 15 | 16 | img { 17 | max-width: 100%; 18 | width: auto; 19 | height: auto; 20 | } 21 | 22 | a:link, a:visited { 23 | text-decoration: none; 24 | color: inherit; 25 | } 26 | 27 | h1 { 28 | font-size: 3rem; 29 | } 30 | 31 | h2 { 32 | font-size: 2.5rem; 33 | } 34 | 35 | h3 { 36 | font-size: 2rem; 37 | } 38 | 39 | h4 { 40 | font-size: 1.75rem; 41 | } 42 | 43 | 44 | .container { 45 | width: min(90vw, 1200px); 46 | margin-inline: auto; 47 | } 48 | 49 | nav { 50 | width: 100vw; 51 | padding-block: calc(2.5rem + .5vw); 52 | display: flex; 53 | justify-content: center; 54 | } 55 | 56 | nav ul { 57 | display: flex; 58 | justify-content: space-between; 59 | align-items: center; 60 | padding-inline: calc(1.5rem + 1.5vw); 61 | } 62 | 63 | nav ul a, nav ul a:visited { 64 | color: var(--alt-font-color); 65 | } 66 | 67 | nav ul a.active { 68 | color: var(--font-color); 69 | } 70 | 71 | .nav-pages { 72 | display: flex; 73 | align-items: center; 74 | gap: calc(1.5rem + .5vw); 75 | } 76 | 77 | .login-form { 78 | width: min(90vw, 500px); 79 | } 80 | 81 | .form-column { 82 | display: flex; 83 | flex-direction: column; 84 | gap:2rem; 85 | } 86 | .form-column div { 87 | display: flex; 88 | flex-direction: column; 89 | gap: .5rem; 90 | } 91 | 92 | form input { 93 | border: .5px solid var(--alt-font-color); 94 | border-radius: 2px; 95 | padding: 1rem; 96 | color: var(--font-color); 97 | } 98 | 99 | form input:active, form input:focus { 100 | outline: 0; 101 | border-color: var(--font-color); 102 | } 103 | 104 | form button { 105 | padding: 1rem; 106 | margin-bottom: .75rem; 107 | } 108 | 109 | a:hover { 110 | text-decoration: underline; 111 | 112 | } 113 | 114 | .viewport-size { 115 | width: 100vw; 116 | height: 100vh; 117 | } 118 | 119 | .place-content-center { 120 | display: grid; 121 | place-content: center; 122 | } 123 | 124 | .mb-2 { 125 | margin-bottom: .5rem; 126 | } 127 | 128 | .mb-8 { 129 | margin-bottom: 4rem; 130 | } 131 | 132 | .row { 133 | display: flex; 134 | } 135 | 136 | .gap-2 { 137 | gap: 1rem; 138 | } 139 | 140 | .logo-large { 141 | height: 50px; 142 | width: 50px; 143 | } 144 | 145 | .justify-center { 146 | justify-content: center; 147 | } 148 | 149 | .page-content { 150 | margin-block: 5rem; 151 | display: flex; 152 | flex-direction: column; 153 | align-items: center; 154 | } 155 | 156 | 157 | .block h1, .block h2, .block h3, .block p { 158 | margin-bottom: .65em; 159 | } 160 | 161 | .block section + section { 162 | margin-top: 4rem; 163 | } 164 | 165 | .image-wrap { 166 | margin: calc(var(--baseline) * 2) auto; 167 | } 168 | 169 | .image-normal, 170 | .image-wide { 171 | padding: 0 var(--baseline); 172 | } 173 | 174 | .image-normal { 175 | max-width: var(--max-width-s); 176 | } 177 | 178 | .image-wide { 179 | max-width: var(--max-width-m); 180 | } 181 | 182 | .image-caption { 183 | text-align: center; 184 | } 185 | 186 | .content-wrap { 187 | max-width: var(--max-width-s); 188 | margin: 0 auto; 189 | padding: 0 var(--baseline); 190 | } 191 | 192 | .cta { 193 | max-width: var(--max-width-s); 194 | margin: calc(var(--baseline) * 2) auto; 195 | } 196 | 197 | .cta-wrap { 198 | padding: var(--baseline); 199 | background: var(--primary-900); 200 | text-align: center; 201 | } 202 | 203 | .cta-content * { 204 | color: white; 205 | } 206 | 207 | .cta-content > :first-child { 208 | margin-top: 0; 209 | } 210 | 211 | .cta-content > :last-child { 212 | margin-bottom: 0; 213 | } 214 | 215 | .cta-buttons { 216 | padding: 0; 217 | margin: calc(var(--baseline) * .5) 0 0; 218 | list-style: none; 219 | display: flex; 220 | justify-content: center; 221 | } 222 | 223 | .cta-button { 224 | display: block; 225 | box-sizing: border-box; 226 | color: var(--primary-900); 227 | text-decoration: none; 228 | padding: 15px var(--baseline); 229 | background: white; 230 | border-radius: 5px; 231 | margin: 0 calc(var(--baseline) * .5); 232 | text-transform: uppercase; 233 | font-size: .8rem; 234 | } 235 | 236 | .cta-button:hover { 237 | background: #f1f1f1; 238 | } 239 | 240 | .cta-button:active { 241 | background: #e4e4e4; 242 | } 243 | 244 | 245 | @media (max-width: 600px) { 246 | .cta-buttons { 247 | margin-top: 0; 248 | display: block; 249 | } 250 | 251 | .cta-button { 252 | width: 100%; 253 | margin: calc(var(--baseline) * .5) 0 0; 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /apps/web/app/utils/index.ts: -------------------------------------------------------------------------------- 1 | import type { Page } from '@org/cms'; 2 | 3 | export const findPageBySlug = (slug: string, pages: Page[]) => { 4 | return pages?.find((page) => page.slug === slug); 5 | }; 6 | -------------------------------------------------------------------------------- /apps/web/express.d.ts: -------------------------------------------------------------------------------- 1 | import type { PayloadRequest } from '@org/cms/types'; 2 | import type { ServerBuild } from "@remix-run/node"; 3 | 4 | export type GetLoadContextFunction = (req: PayloadRequest, res: Express.Response) => RemixRequestContext; 5 | export type RequestHandler = (req: Express.Request, res: Express.Response, next: Express.NextFunction) => Promise; 6 | 7 | declare module '@remix-run/express' { 8 | export declare function createRequestHandler({ build, getLoadContext, mode, }: { 9 | build: ServerBuild; 10 | getLoadContext?: GetLoadContextFunction; 11 | mode?: string; 12 | }): RequestHandler; 13 | } 14 | 15 | export type * from '@remix-run/express'; 16 | -------------------------------------------------------------------------------- /apps/web/express.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@remix-run/express'); 2 | -------------------------------------------------------------------------------- /apps/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@org/web", 3 | "version": "1.0.0", 4 | "sideEffects": false, 5 | "license": "MIT", 6 | "exports": { 7 | ".": { 8 | "import": "./build/index.js", 9 | "require": "./build/index.js" 10 | }, 11 | "./express": { 12 | "import": "./express.js", 13 | "require": "./express.js" 14 | } 15 | }, 16 | "scripts": { 17 | "clean": "rm -rf node_modules build public/build .turbo", 18 | "build": "remix build", 19 | "dev": "remix build && run-p \"dev:*\"", 20 | "dev:remix": "remix watch", 21 | "lint": "eslint --ext .ts,.tsx,.js,.jsx ./app" 22 | }, 23 | "dependencies": { 24 | "@org/ui": "workspace:*", 25 | "@remix-run/express": "^1.12.0", 26 | "@remix-run/node": "^1.12.0", 27 | "@remix-run/react": "^1.12.0", 28 | "escape-html": "^1.0.3", 29 | "react": "^18.2.0", 30 | "react-dom": "^18.2.0", 31 | "slate": "^0.90.0" 32 | }, 33 | "devDependencies": { 34 | "@org/cms": "workspace:*", 35 | "@remix-run/dev": "^1.12.0", 36 | "@remix-run/eslint-config": "^1.12.0", 37 | "@types/escape-html": "^1.0.2", 38 | "@types/express": "^4.17.17", 39 | "@types/react": "^18.0.27", 40 | "@types/react-dom": "^18.0.10", 41 | "eslint": "^8.34.0", 42 | "nodemon": "^2.0.20", 43 | "npm-run-all": "^4.1.5", 44 | "typescript": "^4.9.5" 45 | }, 46 | "engines": { 47 | "node": ">=16" 48 | } 49 | } -------------------------------------------------------------------------------- /apps/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/payloadcms/remix-server/5ed4bcd2d044ead0ea6e894610cc9df24fa86f7e/apps/web/public/favicon.ico -------------------------------------------------------------------------------- /apps/web/remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | module.exports = { 3 | ignoredRouteFiles: ['**/.*'], 4 | assetsBuildDirectory: 'public/web/build', 5 | publicPath: '/web/build/', 6 | serverDependenciesToBundle: ['@org/ui'], 7 | // appDirectory: "app", 8 | // serverBuildPath: "build/index.js", 9 | watchPaths: ['../../packages/ui', '../../packages/shared'], 10 | }; 11 | -------------------------------------------------------------------------------- /apps/web/remix.env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | import type { Response } from 'express'; 5 | import type { Payload, User } from '@org/cms'; 6 | 7 | export interface RemixRequestContext { 8 | payload: Payload; 9 | user: { 10 | user?: User; 11 | token?: string; 12 | exp?: number; 13 | }; 14 | res: Response; 15 | } 16 | 17 | declare module '@remix-run/node' { 18 | interface AppLoadContext extends RemixRequestContext {} 19 | } 20 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["remix.env.d.ts", "express.d.ts", "**/*.ts", "**/*.tsx"], 4 | "exclude": ["node_modules", "dist", "build", ".turbo", "public"], 5 | "compilerOptions": { 6 | "lib": ["DOM", "DOM.Iterable", "ES2021"], 7 | "allowJs": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "isolatedModules": true, 11 | "jsx": "react-jsx", 12 | "resolveJsonModule": true, 13 | "baseUrl": ".", 14 | "paths": { 15 | "~/*": ["./app/*"] 16 | }, 17 | "noEmit": true, 18 | "moduleResolution": "node" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@org/remix-server", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "workspaces": [ 6 | "packages/*", 7 | "apps/*" 8 | ], 9 | "scripts": { 10 | "clean": "turbo run clean && rm -rf node_modules", 11 | "build": "turbo run build", 12 | "serve": "turbo run serve", 13 | "dev": "turbo run dev --parallel", 14 | "lint": "turbo run lint", 15 | "format": "prettier --write \"**/*.{ts,tsx,md}\"" 16 | }, 17 | "devDependencies": { 18 | "@org/eslint-config": "workspace:*", 19 | "prettier": "^2.8.4", 20 | "turbo": "^1.7.4", 21 | "typescript": "^4.9.5" 22 | }, 23 | "engines": { 24 | "node": ">=16.0.0" 25 | }, 26 | "peerDependenciesMeta": { 27 | "express": { 28 | "optional": true 29 | } 30 | }, 31 | "pnpm": { 32 | "peerDependencyRules": { 33 | "ignoreMissing": [ 34 | "express" 35 | ] 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/eslint-config-custom/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2020: true, 5 | node: true, 6 | }, 7 | extends: [ 8 | 'eslint:recommended', 9 | 'plugin:react/recommended', 10 | 'plugin:react/jsx-runtime', 11 | 'plugin:@typescript-eslint/eslint-recommended', 12 | 'plugin:@typescript-eslint/recommended', 13 | 'turbo', 14 | 'prettier', 15 | ], 16 | settings: { 17 | react: { 18 | version: 'detect', 19 | }, 20 | }, 21 | parser: '@typescript-eslint/parser', 22 | parserOptions: { 23 | ecmaFeatures: { 24 | jsx: true, 25 | }, 26 | ecmaVersion: 11, 27 | }, 28 | plugins: ['react', 'react-hooks', '@typescript-eslint'], 29 | rules: { 30 | '@typescript-eslint/no-empty-interface': 'off', 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /packages/eslint-config-custom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@org/eslint-config", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "exports": { 6 | ".": "./index.js", 7 | "./remix": "./remix.js" 8 | }, 9 | "scripts": { 10 | "clean": "rm -rf node_modules" 11 | }, 12 | "dependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.32.0", 14 | "@typescript-eslint/parser": "^5.32.0", 15 | "eslint": "^8.21.0", 16 | "eslint-config-prettier": "^8.5.0", 17 | "eslint-plugin-react": "7.30.1", 18 | "eslint-plugin-react-hooks": "^4.6.0", 19 | "eslint-config-turbo": "latest", 20 | "typescript": "^4.8.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/eslint-config-custom/remix.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | '@remix-run/eslint-config', 4 | '@remix-run/eslint-config/node', 5 | 'turbo', 6 | 'prettier', 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@org/shared", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "main": "./src/index.ts", 6 | "exports": { 7 | ".": { 8 | "serve": { 9 | "types": "./dist/index.js", 10 | "require": "./dist/index.js", 11 | "import": "./dist/index.js" 12 | }, 13 | "default": { 14 | "types": "./src/index.ts", 15 | "require": "./src/index.ts", 16 | "import": "./src/index.ts" 17 | } 18 | } 19 | }, 20 | "scripts": { 21 | "build": "rm -rf dist && tsc", 22 | "clean": "rm -rf node_modules dist .turbo", 23 | "lint": "eslint --ext .ts,.js./src" 24 | }, 25 | "dependencies": { 26 | "dotenv": "^16.0.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/shared/src/index.ts: -------------------------------------------------------------------------------- 1 | export * as dotenv from 'dotenv'; 2 | -------------------------------------------------------------------------------- /packages/shared/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist" 5 | }, 6 | "include": ["src/**/*"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@org/ui", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "sideEffects": false, 6 | "main": "./src/index.tsx", 7 | "exports": { 8 | ".": { 9 | "types": "./src/index.tsx", 10 | "import": "./src/index.tsx", 11 | "require": "./src/index.tsx" 12 | }, 13 | "./styles.css": { 14 | "import": "./src/styles/ui.css", 15 | "require": "./src/styles/ui.css" 16 | } 17 | }, 18 | "scripts": { 19 | "build": "rm -rf dist && tsc", 20 | "clean": "rm -rf node_modules dist .turbo", 21 | "lint": "eslint --ext .ts,.tsx,.js,.jsx ./src", 22 | "prepublishOnly": "pnpm run build" 23 | }, 24 | "peerDependencies": { 25 | "react": "^18.2.0" 26 | }, 27 | "devDependencies": { 28 | "@types/react": "^18.0.27", 29 | "react": "^18.2.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/ui/src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from 'react'; 3 | 4 | type Sizes = 'small' | 'medium' | 'large'; 5 | const getSize = (size: Sizes) => { 6 | const widths: Record = { 7 | small: { 8 | padding: '0.5rem 1rem', 9 | fontSize: '0.875rem', 10 | borderRadius: '2px', 11 | }, 12 | medium: { 13 | padding: '.75rem 1.5rem', 14 | fontSize: '1rem', 15 | borderRadius: '4px', 16 | }, 17 | large: { 18 | padding: '1rem 2rem', 19 | fontSize: '1.25rem', 20 | borderRadius: '6px', 21 | }, 22 | }; 23 | return widths[size]; 24 | }; 25 | 26 | type Colors = 'light' | 'dark'; 27 | const getColor = (color: Colors) => { 28 | const colors: Record = { 29 | dark: { 30 | backgroundColor: 'var(--primary-800)', 31 | color: 'white', 32 | border: 'none', 33 | }, 34 | light: { 35 | backgroundColor: 'white', 36 | color: 'var(--primary-800)', 37 | border: '.5px solid var(--primary-800)', 38 | }, 39 | }; 40 | return colors[color]; 41 | }; 42 | 43 | const baseStyles: React.CSSProperties = { 44 | outline: '0', 45 | cursor: 'pointer', 46 | }; 47 | 48 | type ButtonProps = { 49 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 50 | as?: any; 51 | size?: Sizes; 52 | color?: Colors; 53 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 54 | [x: string]: any; 55 | }; 56 | 57 | export function Button({ 58 | as = 'button', 59 | size = 'medium', 60 | color = 'light', 61 | ...rest 62 | }: ButtonProps): JSX.Element { 63 | // Using regular styles here for simplicity 64 | const Component = as; 65 | const styles = { 66 | ...baseStyles, 67 | ...getSize(size), 68 | ...getColor(color), 69 | }; 70 | 71 | return ; 72 | } 73 | -------------------------------------------------------------------------------- /packages/ui/src/index.tsx: -------------------------------------------------------------------------------- 1 | export { Button } from './components/Button'; 2 | -------------------------------------------------------------------------------- /packages/ui/src/styles/ui.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --font-color: rgb(15, 15, 15); 3 | --alt-font-color: rgb(154, 154, 154); 4 | --level1: rgb(235, 235, 235); 5 | --primary-800: rgb(47, 47, 47); 6 | --primary-900: rgb(33, 33, 33); 7 | --max-width-s: 640px; 8 | --max-width-m: 1024px; 9 | --max-width-l: 1440px; 10 | 11 | --baseline: 30px; 12 | } 13 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "baseUrl": "./src", 6 | }, 7 | "include": ["src/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/**' 3 | - 'apps/**' 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["DOM", "DOM.Iterable", "ES2021"], 4 | "jsx": "react", 5 | "module": "NodeNext", 6 | "target": "ES2021", 7 | "sourceMap": true, 8 | "declaration": true, 9 | "noEmitOnError": true, 10 | "skipLibCheck": true, 11 | "strict": true, 12 | "esModuleInterop": true, 13 | "resolveJsonModule": true, 14 | "noEmit": false 15 | }, 16 | "exclude": ["node_modules", "dist", "build", ".turbo"] 17 | } 18 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "pipeline": { 4 | "build": { 5 | "cache": false, 6 | "dependsOn": ["^build"], 7 | "outputs": ["dist/**", "build/**"] 8 | }, 9 | "lint": { 10 | "outputs": [] 11 | }, 12 | "dev": { 13 | "cache": false 14 | }, 15 | "clean": { 16 | "cache": false 17 | }, 18 | "serve": { 19 | "cache": false, 20 | "dependsOn": ["^serve"] 21 | } 22 | }, 23 | "globalEnv": [ 24 | "MONGODB_URL", 25 | "PAYLOADCMS_SECRET", 26 | "NODE_ENV", 27 | "PAYLOAD_CONFIG_PATH", 28 | "PORT" 29 | ] 30 | } 31 | --------------------------------------------------------------------------------