diff --git a/.gitignore b/.gitignore index ec44698bc1b..c8e97b28dbe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.env .next .keystone dist/ diff --git a/examples/assets-local/package.json b/examples/assets-local/package.json index 9baf2e050f8..2e7655567f3 100644 --- a/examples/assets-local/package.json +++ b/examples/assets-local/package.json @@ -16,6 +16,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/assets-local" + } } diff --git a/examples/assets-local/schema.graphql b/examples/assets-local/schema.graphql index 3b74aa07b0d..aa2c66202d4 100644 --- a/examples/assets-local/schema.graphql +++ b/examples/assets-local/schema.graphql @@ -4,21 +4,11 @@ type Post { id: ID! title: String - status: PostStatusType content: String - publishDate: DateTime - author: Author - hero: ImageFieldOutput + banner: ImageFieldOutput attachment: FileFieldOutput } -enum PostStatusType { - draft - published -} - -scalar DateTime @specifiedBy(url: "https://datatracker.ietf.org/doc/html/rfc3339#section-5.6") - type ImageFieldOutput { id: ID! filesize: Int! @@ -51,10 +41,7 @@ input PostWhereInput { NOT: [PostWhereInput!] id: IDFilter title: StringFilter - status: PostStatusTypeNullableFilter content: StringFilter - publishDate: DateTimeNullableFilter - author: AuthorWhereInput } input IDFilter { @@ -96,30 +83,10 @@ input NestedStringFilter { not: NestedStringFilter } -input PostStatusTypeNullableFilter { - equals: PostStatusType - in: [PostStatusType!] - notIn: [PostStatusType!] - not: PostStatusTypeNullableFilter -} - -input DateTimeNullableFilter { - equals: DateTime - in: [DateTime!] - notIn: [DateTime!] - lt: DateTime - lte: DateTime - gt: DateTime - gte: DateTime - not: DateTimeNullableFilter -} - input PostOrderByInput { id: OrderDirection title: OrderDirection - status: OrderDirection content: OrderDirection - publishDate: OrderDirection } enum OrderDirection { @@ -129,20 +96,11 @@ enum OrderDirection { input PostUpdateInput { title: String - status: PostStatusType content: String - publishDate: DateTime - author: AuthorRelateToOneForUpdateInput - hero: ImageFieldInput + banner: ImageFieldInput attachment: FileFieldInput } -input AuthorRelateToOneForUpdateInput { - create: AuthorCreateInput - connect: AuthorWhereUniqueInput - disconnect: Boolean -} - input ImageFieldInput { upload: Upload! } @@ -161,83 +119,11 @@ input PostUpdateArgs { input PostCreateInput { title: String - status: PostStatusType content: String - publishDate: DateTime - author: AuthorRelateToOneForCreateInput - hero: ImageFieldInput + banner: ImageFieldInput attachment: FileFieldInput } -input AuthorRelateToOneForCreateInput { - create: AuthorCreateInput - connect: AuthorWhereUniqueInput -} - -type Author { - id: ID! - name: String - email: String - posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: PostWhereUniqueInput): [Post!] - postsCount(where: PostWhereInput! = {}): Int -} - -input AuthorWhereUniqueInput { - id: ID - email: String -} - -input AuthorWhereInput { - AND: [AuthorWhereInput!] - OR: [AuthorWhereInput!] - NOT: [AuthorWhereInput!] - id: IDFilter - name: StringFilter - email: StringFilter - posts: PostManyRelationFilter -} - -input PostManyRelationFilter { - every: PostWhereInput - some: PostWhereInput - none: PostWhereInput -} - -input AuthorOrderByInput { - id: OrderDirection - name: OrderDirection - email: OrderDirection -} - -input AuthorUpdateInput { - name: String - email: String - posts: PostRelateToManyForUpdateInput -} - -input PostRelateToManyForUpdateInput { - disconnect: [PostWhereUniqueInput!] - set: [PostWhereUniqueInput!] - create: [PostCreateInput!] - connect: [PostWhereUniqueInput!] -} - -input AuthorUpdateArgs { - where: AuthorWhereUniqueInput! - data: AuthorUpdateInput! -} - -input AuthorCreateInput { - name: String - email: String - posts: PostRelateToManyForCreateInput -} - -input PostRelateToManyForCreateInput { - create: [PostCreateInput!] - connect: [PostWhereUniqueInput!] -} - """ The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). """ @@ -250,21 +136,12 @@ type Mutation { updatePosts(data: [PostUpdateArgs!]!): [Post] deletePost(where: PostWhereUniqueInput!): Post deletePosts(where: [PostWhereUniqueInput!]!): [Post] - createAuthor(data: AuthorCreateInput!): Author - createAuthors(data: [AuthorCreateInput!]!): [Author] - updateAuthor(where: AuthorWhereUniqueInput!, data: AuthorUpdateInput!): Author - updateAuthors(data: [AuthorUpdateArgs!]!): [Author] - deleteAuthor(where: AuthorWhereUniqueInput!): Author - deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author] } type Query { posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: PostWhereUniqueInput): [Post!] post(where: PostWhereUniqueInput!): Post postsCount(where: PostWhereInput! = {}): Int - authors(where: AuthorWhereInput! = {}, orderBy: [AuthorOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: AuthorWhereUniqueInput): [Author!] - author(where: AuthorWhereUniqueInput!): Author - authorsCount(where: AuthorWhereInput! = {}): Int keystone: KeystoneMeta! } diff --git a/examples/assets-local/schema.prisma b/examples/assets-local/schema.prisma index 4a683987085..93afdcc7fb1 100644 --- a/examples/assets-local/schema.prisma +++ b/examples/assets-local/schema.prisma @@ -13,27 +13,14 @@ generator client { } model Post { - id String @id @default(cuid()) - title String @default("") - status String? - content String @default("") - publishDate DateTime? - author Author? @relation("Post_author", fields: [authorId], references: [id]) - authorId String? @map("author") - hero_filesize Int? - hero_extension String? - hero_width Int? - hero_height Int? - hero_id String? + id String @id @default(cuid()) + title String @default("") + content String @default("") + banner_filesize Int? + banner_extension String? + banner_width Int? + banner_height Int? + banner_id String? attachment_filesize Int? attachment_filename String? - - @@index([authorId]) -} - -model Author { - id String @id @default(cuid()) - name String @default("") - email String @unique @default("") - posts Post[] @relation("Post_author") } diff --git a/examples/assets-local/schema.ts b/examples/assets-local/schema.ts index 7ac0fdcf6b1..f2922442d89 100644 --- a/examples/assets-local/schema.ts +++ b/examples/assets-local/schema.ts @@ -1,32 +1,15 @@ import { list } from '@keystone-6/core'; import { allowAll } from '@keystone-6/core/access'; -import { select, relationship, text, timestamp, image, file } from '@keystone-6/core/fields'; +import { text, image, file } from '@keystone-6/core/fields'; export const lists = { Post: list({ access: allowAll, fields: { title: text({ validation: { isRequired: true } }), - status: select({ - type: 'enum', - options: [ - { label: 'Draft', value: 'draft' }, - { label: 'Published', value: 'published' }, - ], - }), content: text(), - publishDate: timestamp(), - author: relationship({ ref: 'Author.posts', many: false }), - hero: image({ storage: 'my_images' }), + banner: image({ storage: 'my_images' }), attachment: file({ storage: 'my_files' }), }, }), - Author: list({ - access: allowAll, - fields: { - name: text({ validation: { isRequired: true } }), - email: text({ isIndexed: 'unique', validation: { isRequired: true } }), - posts: relationship({ ref: 'Post.author', many: true }), - }, - }), }; diff --git a/examples/assets-s3/.env.example b/examples/assets-s3/.env.example index 64d19f6c765..0447931c861 100644 --- a/examples/assets-s3/.env.example +++ b/examples/assets-s3/.env.example @@ -1,4 +1,4 @@ -S3_BUCKET_NAME=S3_BUCKET_NAME -S3_ACCESS_KEY_ID=S3_ACCESS_KEY_ID -S3_SECRET_ACCESS_KEY=S3_SECRET_ACCESS_KEY -S3_REGION=S3_REGION \ No newline at end of file +S3_BUCKET_NAME= +S3_ACCESS_KEY_ID= +S3_SECRET_ACCESS_KEY= +S3_REGION= diff --git a/examples/assets-s3/.gitignore b/examples/assets-s3/.gitignore deleted file mode 100644 index d298be107f2..00000000000 --- a/examples/assets-s3/.gitignore +++ /dev/null @@ -1 +0,0 @@ -public/ \ No newline at end of file diff --git a/examples/assets-s3/keystone.ts b/examples/assets-s3/keystone.ts index 52480573cca..4458ac0dc5e 100644 --- a/examples/assets-s3/keystone.ts +++ b/examples/assets-s3/keystone.ts @@ -1,10 +1,8 @@ +import 'dotenv/config'; import { config } from '@keystone-6/core'; -import dotenv from 'dotenv'; import { fixPrismaPath } from '../example-utils'; import { lists } from './schema'; -dotenv.config(); - const { S3_BUCKET_NAME: bucketName = 'keystone-test', S3_REGION: region = 'ap-southeast-2', diff --git a/examples/assets-s3/package.json b/examples/assets-s3/package.json index 8e8d64463bf..7a40db1fba6 100644 --- a/examples/assets-s3/package.json +++ b/examples/assets-s3/package.json @@ -17,6 +17,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/assets-s3" + } } diff --git a/examples/assets-s3/schema.graphql b/examples/assets-s3/schema.graphql index 3b74aa07b0d..aa2c66202d4 100644 --- a/examples/assets-s3/schema.graphql +++ b/examples/assets-s3/schema.graphql @@ -4,21 +4,11 @@ type Post { id: ID! title: String - status: PostStatusType content: String - publishDate: DateTime - author: Author - hero: ImageFieldOutput + banner: ImageFieldOutput attachment: FileFieldOutput } -enum PostStatusType { - draft - published -} - -scalar DateTime @specifiedBy(url: "https://datatracker.ietf.org/doc/html/rfc3339#section-5.6") - type ImageFieldOutput { id: ID! filesize: Int! @@ -51,10 +41,7 @@ input PostWhereInput { NOT: [PostWhereInput!] id: IDFilter title: StringFilter - status: PostStatusTypeNullableFilter content: StringFilter - publishDate: DateTimeNullableFilter - author: AuthorWhereInput } input IDFilter { @@ -96,30 +83,10 @@ input NestedStringFilter { not: NestedStringFilter } -input PostStatusTypeNullableFilter { - equals: PostStatusType - in: [PostStatusType!] - notIn: [PostStatusType!] - not: PostStatusTypeNullableFilter -} - -input DateTimeNullableFilter { - equals: DateTime - in: [DateTime!] - notIn: [DateTime!] - lt: DateTime - lte: DateTime - gt: DateTime - gte: DateTime - not: DateTimeNullableFilter -} - input PostOrderByInput { id: OrderDirection title: OrderDirection - status: OrderDirection content: OrderDirection - publishDate: OrderDirection } enum OrderDirection { @@ -129,20 +96,11 @@ enum OrderDirection { input PostUpdateInput { title: String - status: PostStatusType content: String - publishDate: DateTime - author: AuthorRelateToOneForUpdateInput - hero: ImageFieldInput + banner: ImageFieldInput attachment: FileFieldInput } -input AuthorRelateToOneForUpdateInput { - create: AuthorCreateInput - connect: AuthorWhereUniqueInput - disconnect: Boolean -} - input ImageFieldInput { upload: Upload! } @@ -161,83 +119,11 @@ input PostUpdateArgs { input PostCreateInput { title: String - status: PostStatusType content: String - publishDate: DateTime - author: AuthorRelateToOneForCreateInput - hero: ImageFieldInput + banner: ImageFieldInput attachment: FileFieldInput } -input AuthorRelateToOneForCreateInput { - create: AuthorCreateInput - connect: AuthorWhereUniqueInput -} - -type Author { - id: ID! - name: String - email: String - posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: PostWhereUniqueInput): [Post!] - postsCount(where: PostWhereInput! = {}): Int -} - -input AuthorWhereUniqueInput { - id: ID - email: String -} - -input AuthorWhereInput { - AND: [AuthorWhereInput!] - OR: [AuthorWhereInput!] - NOT: [AuthorWhereInput!] - id: IDFilter - name: StringFilter - email: StringFilter - posts: PostManyRelationFilter -} - -input PostManyRelationFilter { - every: PostWhereInput - some: PostWhereInput - none: PostWhereInput -} - -input AuthorOrderByInput { - id: OrderDirection - name: OrderDirection - email: OrderDirection -} - -input AuthorUpdateInput { - name: String - email: String - posts: PostRelateToManyForUpdateInput -} - -input PostRelateToManyForUpdateInput { - disconnect: [PostWhereUniqueInput!] - set: [PostWhereUniqueInput!] - create: [PostCreateInput!] - connect: [PostWhereUniqueInput!] -} - -input AuthorUpdateArgs { - where: AuthorWhereUniqueInput! - data: AuthorUpdateInput! -} - -input AuthorCreateInput { - name: String - email: String - posts: PostRelateToManyForCreateInput -} - -input PostRelateToManyForCreateInput { - create: [PostCreateInput!] - connect: [PostWhereUniqueInput!] -} - """ The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). """ @@ -250,21 +136,12 @@ type Mutation { updatePosts(data: [PostUpdateArgs!]!): [Post] deletePost(where: PostWhereUniqueInput!): Post deletePosts(where: [PostWhereUniqueInput!]!): [Post] - createAuthor(data: AuthorCreateInput!): Author - createAuthors(data: [AuthorCreateInput!]!): [Author] - updateAuthor(where: AuthorWhereUniqueInput!, data: AuthorUpdateInput!): Author - updateAuthors(data: [AuthorUpdateArgs!]!): [Author] - deleteAuthor(where: AuthorWhereUniqueInput!): Author - deleteAuthors(where: [AuthorWhereUniqueInput!]!): [Author] } type Query { posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: PostWhereUniqueInput): [Post!] post(where: PostWhereUniqueInput!): Post postsCount(where: PostWhereInput! = {}): Int - authors(where: AuthorWhereInput! = {}, orderBy: [AuthorOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: AuthorWhereUniqueInput): [Author!] - author(where: AuthorWhereUniqueInput!): Author - authorsCount(where: AuthorWhereInput! = {}): Int keystone: KeystoneMeta! } diff --git a/examples/assets-s3/schema.prisma b/examples/assets-s3/schema.prisma index 4a683987085..93afdcc7fb1 100644 --- a/examples/assets-s3/schema.prisma +++ b/examples/assets-s3/schema.prisma @@ -13,27 +13,14 @@ generator client { } model Post { - id String @id @default(cuid()) - title String @default("") - status String? - content String @default("") - publishDate DateTime? - author Author? @relation("Post_author", fields: [authorId], references: [id]) - authorId String? @map("author") - hero_filesize Int? - hero_extension String? - hero_width Int? - hero_height Int? - hero_id String? + id String @id @default(cuid()) + title String @default("") + content String @default("") + banner_filesize Int? + banner_extension String? + banner_width Int? + banner_height Int? + banner_id String? attachment_filesize Int? attachment_filename String? - - @@index([authorId]) -} - -model Author { - id String @id @default(cuid()) - name String @default("") - email String @unique @default("") - posts Post[] @relation("Post_author") } diff --git a/examples/assets-s3/schema.ts b/examples/assets-s3/schema.ts index 7ac0fdcf6b1..f2922442d89 100644 --- a/examples/assets-s3/schema.ts +++ b/examples/assets-s3/schema.ts @@ -1,32 +1,15 @@ import { list } from '@keystone-6/core'; import { allowAll } from '@keystone-6/core/access'; -import { select, relationship, text, timestamp, image, file } from '@keystone-6/core/fields'; +import { text, image, file } from '@keystone-6/core/fields'; export const lists = { Post: list({ access: allowAll, fields: { title: text({ validation: { isRequired: true } }), - status: select({ - type: 'enum', - options: [ - { label: 'Draft', value: 'draft' }, - { label: 'Published', value: 'published' }, - ], - }), content: text(), - publishDate: timestamp(), - author: relationship({ ref: 'Author.posts', many: false }), - hero: image({ storage: 'my_images' }), + banner: image({ storage: 'my_images' }), attachment: file({ storage: 'my_files' }), }, }), - Author: list({ - access: allowAll, - fields: { - name: text({ validation: { isRequired: true } }), - email: text({ isIndexed: 'unique', validation: { isRequired: true } }), - posts: relationship({ ref: 'Post.author', many: true }), - }, - }), }; diff --git a/examples/auth/package.json b/examples/auth/package.json index 0aba9f5d24b..25d9a02e48f 100644 --- a/examples/auth/package.json +++ b/examples/auth/package.json @@ -17,6 +17,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/auth" + } } diff --git a/examples/cloudinary/.env.example b/examples/cloudinary/.env.example new file mode 100644 index 00000000000..1f51c128885 --- /dev/null +++ b/examples/cloudinary/.env.example @@ -0,0 +1,3 @@ +CLOUDINARY_CLOUD_NAME= +CLOUDINARY_API_KEY= +CLOUDINARY_API_SECRET= diff --git a/examples/cloudinary/keystone.ts b/examples/cloudinary/keystone.ts new file mode 100644 index 00000000000..83e6752044d --- /dev/null +++ b/examples/cloudinary/keystone.ts @@ -0,0 +1,15 @@ +import 'dotenv/config'; +import { config } from '@keystone-6/core'; +import { fixPrismaPath } from '../example-utils'; +import { lists } from './schema'; + +export default config({ + db: { + provider: 'sqlite', + url: process.env.DATABASE_URL || 'file:./keystone-example.db', + + // WARNING: this is only needed for our monorepo examples, dont do this + ...fixPrismaPath, + }, + lists, +}); diff --git a/examples/cloudinary/package.json b/examples/cloudinary/package.json new file mode 100644 index 00000000000..8c563840a1e --- /dev/null +++ b/examples/cloudinary/package.json @@ -0,0 +1,22 @@ +{ + "name": "@keystone-6/example-cloudinary", + "version": "0.0.1", + "private": true, + "license": "MIT", + "scripts": { + "dev": "keystone dev", + "start": "keystone start", + "build": "keystone build", + "postinstall": "keystone postinstall" + }, + "dependencies": { + "@keystone-6/cloudinary": "workspace:^", + "@keystone-6/core": "^5.0.0", + "@prisma/client": "^4.16.2", + "dotenv": "^16.0.0" + }, + "devDependencies": { + "prisma": "^4.16.2", + "typescript": "~5.0.0" + } +} diff --git a/examples/cloudinary/schema.graphql b/examples/cloudinary/schema.graphql new file mode 100644 index 00000000000..3f72e019b47 --- /dev/null +++ b/examples/cloudinary/schema.graphql @@ -0,0 +1,268 @@ +# This file is automatically generated by Keystone, do not modify it manually. +# Modify your Keystone config when you want to change this. + +type Post { + id: ID! + title: String + content: String + banner: CloudinaryImage_File +} + +type CloudinaryImage_File { + id: ID + filename: String + originalFilename: String + mimetype: String + encoding: String + publicUrl: String + publicUrlTransformed(transformation: CloudinaryImageFormat): String +} + +""" +Mirrors the formatting options [Cloudinary provides](https://cloudinary.com/documentation/image_transformation_reference). +All options are strings as they ultimately end up in a URL. +""" +input CloudinaryImageFormat { + """ Rewrites the filename to be this pretty string. Do not include `/` or `.` + """ + prettyName: String + width: String + height: String + crop: String + aspect_ratio: String + gravity: String + zoom: String + x: String + y: String + format: String + fetch_format: String + quality: String + radius: String + angle: String + effect: String + opacity: String + border: String + background: String + overlay: String + underlay: String + default_image: String + delay: String + color: String + color_space: String + dpr: String + page: String + density: String + flags: String + transformation: String +} + +input PostWhereUniqueInput { + id: ID +} + +input PostWhereInput { + AND: [PostWhereInput!] + OR: [PostWhereInput!] + NOT: [PostWhereInput!] + id: IDFilter + title: StringFilter + content: StringFilter +} + +input IDFilter { + equals: ID + in: [ID!] + notIn: [ID!] + lt: ID + lte: ID + gt: ID + gte: ID + not: IDFilter +} + +input StringFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringFilter +} + +input NestedStringFilter { + equals: String + in: [String!] + notIn: [String!] + lt: String + lte: String + gt: String + gte: String + contains: String + startsWith: String + endsWith: String + not: NestedStringFilter +} + +input PostOrderByInput { + id: OrderDirection + title: OrderDirection + content: OrderDirection +} + +enum OrderDirection { + asc + desc +} + +input PostUpdateInput { + title: String + content: String + banner: Upload +} + +"""The `Upload` scalar type represents a file upload.""" +scalar Upload + +input PostUpdateArgs { + where: PostWhereUniqueInput! + data: PostUpdateInput! +} + +input PostCreateInput { + title: String + content: String + banner: Upload +} + +""" +The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). +""" +scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf") + +type Mutation { + createPost(data: PostCreateInput!): Post + createPosts(data: [PostCreateInput!]!): [Post] + updatePost(where: PostWhereUniqueInput!, data: PostUpdateInput!): Post + updatePosts(data: [PostUpdateArgs!]!): [Post] + deletePost(where: PostWhereUniqueInput!): Post + deletePosts(where: [PostWhereUniqueInput!]!): [Post] +} + +type Query { + posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: PostWhereUniqueInput): [Post!] + post(where: PostWhereUniqueInput!): Post + postsCount(where: PostWhereInput! = {}): Int + keystone: KeystoneMeta! +} + +type KeystoneMeta { + adminMeta: KeystoneAdminMeta! +} + +type KeystoneAdminMeta { + lists: [KeystoneAdminUIListMeta!]! + list(key: String!): KeystoneAdminUIListMeta +} + +type KeystoneAdminUIListMeta { + key: String! + itemQueryName: String! + listQueryName: String! + hideCreate: Boolean! + hideDelete: Boolean! + path: String! + label: String! + singular: String! + plural: String! + description: String + initialColumns: [String!]! + pageSize: Int! + labelField: String! + fields: [KeystoneAdminUIFieldMeta!]! + groups: [KeystoneAdminUIFieldGroupMeta!]! + initialSort: KeystoneAdminUISort + isHidden: Boolean! + isSingleton: Boolean! +} + +type KeystoneAdminUIFieldMeta { + path: String! + label: String! + description: String + isOrderable: Boolean! + isFilterable: Boolean! + isNonNull: [KeystoneAdminUIFieldMetaIsNonNull!] + fieldMeta: JSON + viewsIndex: Int! + customViewsIndex: Int + createView: KeystoneAdminUIFieldMetaCreateView! + listView: KeystoneAdminUIFieldMetaListView! + itemView(id: ID): KeystoneAdminUIFieldMetaItemView + search: QueryMode +} + +enum KeystoneAdminUIFieldMetaIsNonNull { + read + create + update +} + +type KeystoneAdminUIFieldMetaCreateView { + fieldMode: KeystoneAdminUIFieldMetaCreateViewFieldMode! +} + +enum KeystoneAdminUIFieldMetaCreateViewFieldMode { + edit + hidden +} + +type KeystoneAdminUIFieldMetaListView { + fieldMode: KeystoneAdminUIFieldMetaListViewFieldMode! +} + +enum KeystoneAdminUIFieldMetaListViewFieldMode { + read + hidden +} + +type KeystoneAdminUIFieldMetaItemView { + fieldMode: KeystoneAdminUIFieldMetaItemViewFieldMode + fieldPosition: KeystoneAdminUIFieldMetaItemViewFieldPosition +} + +enum KeystoneAdminUIFieldMetaItemViewFieldMode { + edit + read + hidden +} + +enum KeystoneAdminUIFieldMetaItemViewFieldPosition { + form + sidebar +} + +enum QueryMode { + default + insensitive +} + +type KeystoneAdminUIFieldGroupMeta { + label: String! + description: String + fields: [KeystoneAdminUIFieldMeta!]! +} + +type KeystoneAdminUISort { + field: String! + direction: KeystoneAdminUISortDirection! +} + +enum KeystoneAdminUISortDirection { + ASC + DESC +} diff --git a/examples/cloudinary/schema.prisma b/examples/cloudinary/schema.prisma new file mode 100644 index 00000000000..f49a2dda8b2 --- /dev/null +++ b/examples/cloudinary/schema.prisma @@ -0,0 +1,20 @@ +// This file is automatically generated by Keystone, do not modify it manually. +// Modify your Keystone config when you want to change this. + +datasource sqlite { + url = env("DATABASE_URL") + shadowDatabaseUrl = env("SHADOW_DATABASE_URL") + provider = "sqlite" +} + +generator client { + provider = "prisma-client-js" + output = "node_modules/.myprisma/client" +} + +model Post { + id String @id @default(cuid()) + title String @default("") + content String @default("") + banner String? +} diff --git a/examples/cloudinary/schema.ts b/examples/cloudinary/schema.ts new file mode 100644 index 00000000000..4e5fd980a17 --- /dev/null +++ b/examples/cloudinary/schema.ts @@ -0,0 +1,22 @@ +import { list } from '@keystone-6/core'; +import { allowAll } from '@keystone-6/core/access'; +import { text } from '@keystone-6/core/fields'; +import { cloudinaryImage } from '@keystone-6/cloudinary'; + +export const lists = { + Post: list({ + access: allowAll, + fields: { + title: text({ validation: { isRequired: true } }), + content: text(), + banner: cloudinaryImage({ + cloudinary: { + cloudName: process.env.CLOUDINARY_CLOUD_NAME ?? '', + apiKey: process.env.CLOUDINARY_API_KEY ?? '', + apiSecret: process.env.CLOUDINARY_API_SECRET ?? '', + folder: '/banners', + }, + }), + }, + }), +}; diff --git a/examples/custom-admin-ui-logo/package.json b/examples/custom-admin-ui-logo/package.json index 06640afd937..a862ef469bb 100644 --- a/examples/custom-admin-ui-logo/package.json +++ b/examples/custom-admin-ui-logo/package.json @@ -20,6 +20,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/custom-admin-ui-logo" + } } diff --git a/examples/custom-admin-ui-navigation/package.json b/examples/custom-admin-ui-navigation/package.json index 2506420fe77..33fe936f7e6 100644 --- a/examples/custom-admin-ui-navigation/package.json +++ b/examples/custom-admin-ui-navigation/package.json @@ -17,6 +17,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/custom-admin-ui-navigation" + } } diff --git a/examples/custom-admin-ui-pages/package.json b/examples/custom-admin-ui-pages/package.json index b373d338c00..ebdb832bd6b 100644 --- a/examples/custom-admin-ui-pages/package.json +++ b/examples/custom-admin-ui-pages/package.json @@ -20,6 +20,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/custom-admin-ui-pages" + } } diff --git a/examples/custom-field-view/package.json b/examples/custom-field-view/package.json index cc8614021d3..cf91a787ea6 100644 --- a/examples/custom-field-view/package.json +++ b/examples/custom-field-view/package.json @@ -24,6 +24,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/custom-field-view" + } } diff --git a/examples/custom-field/package.json b/examples/custom-field/package.json index 973a1b144b9..2e459b4fb80 100644 --- a/examples/custom-field/package.json +++ b/examples/custom-field/package.json @@ -17,6 +17,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/custom-field" + } } diff --git a/examples/custom-output-paths/package.json b/examples/custom-output-paths/package.json index 3b770f33292..234d041a2a1 100644 --- a/examples/custom-output-paths/package.json +++ b/examples/custom-output-paths/package.json @@ -19,6 +19,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/custom-output-paths" + } } diff --git a/examples/default-values/package.json b/examples/default-values/package.json index eba9bd4adfc..855ed9009c1 100644 --- a/examples/default-values/package.json +++ b/examples/default-values/package.json @@ -16,6 +16,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/default-values" + } } diff --git a/examples/document-field-customisation/keystone-server/package.json b/examples/document-field-customisation/keystone-server/package.json index fefe508b302..988a3081506 100644 --- a/examples/document-field-customisation/keystone-server/package.json +++ b/examples/document-field-customisation/keystone-server/package.json @@ -3,7 +3,6 @@ "description": "Keystone example demonstrating document field customisation", "version": "0.0.5", "private": true, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/document-field-customisation/keystone-server", "scripts": { "dev": "keystone dev --seed-database", "start": "keystone start --seed-database", diff --git a/examples/document-field-customisation/nextjs-frontend/package.json b/examples/document-field-customisation/nextjs-frontend/package.json index 291dced5c55..9db5c5c237d 100644 --- a/examples/document-field-customisation/nextjs-frontend/package.json +++ b/examples/document-field-customisation/nextjs-frontend/package.json @@ -3,7 +3,6 @@ "description": "Frontend example to demonstrate DocumentRenderer customisation", "version": "0.1.0", "private": true, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/document-field-customisation/nextjs-frontend", "scripts": { "dev": "next dev --port 8000", "build": "next build", diff --git a/examples/document-field-customisation/package.json b/examples/document-field-customisation/package.json index 12a69f55d64..8c011f1dffa 100644 --- a/examples/document-field-customisation/package.json +++ b/examples/document-field-customisation/package.json @@ -4,7 +4,6 @@ "version": "0.0.1", "private": true, "author": "Dinesh Pandiyan ", - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/document-field-customisation", "scripts": { "dev": "concurrently \"cd keystone-server && pnpm dev\" \"cd nextjs-frontend && pnpm dev\"" }, diff --git a/examples/document-field/package.json b/examples/document-field/package.json index a7c67b6090a..833598ad506 100644 --- a/examples/document-field/package.json +++ b/examples/document-field/package.json @@ -23,6 +23,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/document-field" + } } diff --git a/examples/extend-express-app/package.json b/examples/extend-express-app/package.json index 6df05c62678..53d666cff72 100644 --- a/examples/extend-express-app/package.json +++ b/examples/extend-express-app/package.json @@ -20,6 +20,5 @@ "prisma": "^4.16.2", "tsx": "^3.9.0", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/extend-express-app" + } } diff --git a/examples/extend-graphql-schema-graphql-tools/package.json b/examples/extend-graphql-schema-graphql-tools/package.json index 1f718503ac4..e15c26ee44e 100644 --- a/examples/extend-graphql-schema-graphql-tools/package.json +++ b/examples/extend-graphql-schema-graphql-tools/package.json @@ -2,7 +2,6 @@ "name": "@keystone-6/example-extend-graphql-schema-graphql-tools", "version": "0.0.7", "private": true, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/extend-graphql-schema-graphql-tools", "license": "MIT", "scripts": { "dev": "keystone dev", diff --git a/examples/extend-graphql-schema-graphql-ts/package.json b/examples/extend-graphql-schema-graphql-ts/package.json index 77af0001e98..0399621d8f9 100644 --- a/examples/extend-graphql-schema-graphql-ts/package.json +++ b/examples/extend-graphql-schema-graphql-ts/package.json @@ -16,6 +16,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/extend-graphql-schema-graphql-ts" + } } diff --git a/examples/extend-graphql-schema-nexus/package.json b/examples/extend-graphql-schema-nexus/package.json index d2b04397db0..e035e8cd634 100644 --- a/examples/extend-graphql-schema-nexus/package.json +++ b/examples/extend-graphql-schema-nexus/package.json @@ -16,7 +16,6 @@ "graphql": "^16.6.0", "nexus": "^1.3.0" }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/extend-graphql-schema-nexus", "devDependencies": { "prisma": "^4.16.2" } diff --git a/examples/extend-graphql-subscriptions/package.json b/examples/extend-graphql-subscriptions/package.json index 457956bfb4a..5f43a2977a1 100644 --- a/examples/extend-graphql-subscriptions/package.json +++ b/examples/extend-graphql-subscriptions/package.json @@ -2,7 +2,6 @@ "name": "@keystone-6/example-extend-graphql-subscriptions", "version": "0.0.7", "private": true, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/extend-graphql-subscriptions", "license": "MIT", "scripts": { "dev": "keystone dev", diff --git a/examples/extend-prisma-schema/package.json b/examples/extend-prisma-schema/package.json index ce4d3d2a7d1..08d1891e172 100644 --- a/examples/extend-prisma-schema/package.json +++ b/examples/extend-prisma-schema/package.json @@ -17,6 +17,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/extend-prisma-schema" + } } diff --git a/examples/framework-astro/package.json b/examples/framework-astro/package.json index 5fdf724bc2a..5bc82065ace 100644 --- a/examples/framework-astro/package.json +++ b/examples/framework-astro/package.json @@ -2,7 +2,6 @@ "name": "@keystone-6/example-framework-astro", "version": "0.0.1", "private": true, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/framework-astro", "license": "MIT", "scripts": { "astro": "astro", diff --git a/examples/framework-nextjs-app-directory/package.json b/examples/framework-nextjs-app-directory/package.json index 2e2baa79e88..fb7b6789f35 100644 --- a/examples/framework-nextjs-app-directory/package.json +++ b/examples/framework-nextjs-app-directory/package.json @@ -33,6 +33,5 @@ "@types/react-dom": "^18.0.4", "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/framework-nextjs-app-directory" + } } diff --git a/examples/framework-nextjs-pages-directory/package.json b/examples/framework-nextjs-pages-directory/package.json index f181c1d0963..c352c1fe33e 100644 --- a/examples/framework-nextjs-pages-directory/package.json +++ b/examples/framework-nextjs-pages-directory/package.json @@ -32,6 +32,5 @@ "@types/react-dom": "^18.0.4", "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/framework-nextjs-pages-directory" + } } diff --git a/examples/framework-nextjs-two-servers/keystone-server/package.json b/examples/framework-nextjs-two-servers/keystone-server/package.json index 8362db27c37..875ffd391cb 100644 --- a/examples/framework-nextjs-two-servers/keystone-server/package.json +++ b/examples/framework-nextjs-two-servers/keystone-server/package.json @@ -3,7 +3,6 @@ "description": "An example of a Keystone backend API server", "version": "0.0.5", "private": true, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/framework-nextjs-two-servers/keystone-server", "scripts": { "dev": "keystone dev --seed-database", "start": "keystone start --seed-database", diff --git a/examples/framework-nextjs-two-servers/nextjs-frontend/package.json b/examples/framework-nextjs-two-servers/nextjs-frontend/package.json index b7d0762b89f..11c9b35f0d3 100644 --- a/examples/framework-nextjs-two-servers/nextjs-frontend/package.json +++ b/examples/framework-nextjs-two-servers/nextjs-frontend/package.json @@ -3,7 +3,6 @@ "description": "An example nextjs frontend", "version": "0.1.0", "private": true, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/framework-nextjs-two-servers/nextjs-frontend", "scripts": { "dev": "next dev --port 8000", "build": "next build", diff --git a/examples/framework-nextjs-two-servers/package.json b/examples/framework-nextjs-two-servers/package.json index 2d62ac14c03..0460900d564 100644 --- a/examples/framework-nextjs-two-servers/package.json +++ b/examples/framework-nextjs-two-servers/package.json @@ -4,7 +4,6 @@ "version": "0.0.1", "private": true, "author": "Dinesh Pandiyan ", - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/framework-nextjs-two-servers", "scripts": { "dev": "concurrently \"cd keystone-server && pnpm dev\" \"cd nextjs-frontend && pnpm dev\"" }, diff --git a/examples/graphql-ts-gql/package.json b/examples/graphql-ts-gql/package.json index 32537dfd901..c2673a5f2f1 100644 --- a/examples/graphql-ts-gql/package.json +++ b/examples/graphql-ts-gql/package.json @@ -35,6 +35,5 @@ "rules": { "@ts-gql/ts-gql": "error" } - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/graphql-ts-gql" + } } diff --git a/examples/limits/package.json b/examples/limits/package.json index 684e4eb2ca4..42dd9cb8083 100644 --- a/examples/limits/package.json +++ b/examples/limits/package.json @@ -18,6 +18,5 @@ "prisma": "^4.16.2", "tsx": "^3.9.0", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/limits" + } } diff --git a/examples/omit/package.json b/examples/omit/package.json index 52523303a10..fa1997f5d68 100644 --- a/examples/omit/package.json +++ b/examples/omit/package.json @@ -16,6 +16,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/omit" + } } diff --git a/examples/script/package.json b/examples/script/package.json index c99b160026c..77126472b59 100644 --- a/examples/script/package.json +++ b/examples/script/package.json @@ -18,6 +18,5 @@ "prisma": "^4.16.2", "tsx": "^3.9.0", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/script" + } } diff --git a/examples/testing/package.json b/examples/testing/package.json index d2d44863045..d4e01c53072 100644 --- a/examples/testing/package.json +++ b/examples/testing/package.json @@ -24,6 +24,5 @@ "jest": "^29.0.0", "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/testing" + } } diff --git a/examples/usecase-todo/package.json b/examples/usecase-todo/package.json index b38eb5d9f69..b667eb9fcf5 100644 --- a/examples/usecase-todo/package.json +++ b/examples/usecase-todo/package.json @@ -18,6 +18,5 @@ "prisma": "^4.16.2", "tsx": "^3.9.0", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/usecase-todo" + } } diff --git a/examples/usecase-versioning/package.json b/examples/usecase-versioning/package.json index 70fd19ff16e..53a198f8f7b 100644 --- a/examples/usecase-versioning/package.json +++ b/examples/usecase-versioning/package.json @@ -18,6 +18,5 @@ "prisma": "^4.16.2", "tsx": "^3.9.0", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/usecase-versioning" + } } diff --git a/examples/virtual-field/package.json b/examples/virtual-field/package.json index 40958f4b164..0d2a83c3189 100644 --- a/examples/virtual-field/package.json +++ b/examples/virtual-field/package.json @@ -16,6 +16,5 @@ "devDependencies": { "prisma": "^4.16.2", "typescript": "~5.0.0" - }, - "repository": "https://github.com/keystonejs/keystone/tree/main/examples/virtual-field" + } } diff --git a/packages/cloudinary/src/cloudinary.ts b/packages/cloudinary/src/cloudinary.ts index 6c3418a7ab9..04b49738a87 100644 --- a/packages/cloudinary/src/cloudinary.ts +++ b/packages/cloudinary/src/cloudinary.ts @@ -67,29 +67,31 @@ export class CloudinaryAdapter { apiSecret: string; folder?: string; }) { - if (!cloudName || !apiKey || !apiSecret) { - throw new Error('CloudinaryAdapter requires cloudName, apiKey, and apiSecret'); - } this.cloudName = cloudName; this.apiKey = apiKey; this.apiSecret = apiSecret; this.folder = folder || undefined; } + ready() { + return this.cloudName.length > 0 && this.apiKey.length > 0 && this.apiSecret.length > 0; + } + /** * Params: { stream, filename, id } */ save({ stream, filename, id }: { stream: fs.ReadStream; filename: string; id: string }) { - // Push to cloudinary + if (!this.ready()) throw new Error('Cloudinary adapter is not ready'); + + // push to cloudinary return uploadStream(stream, { public_id: id, folder: this.folder, - // Auth api_key: this.apiKey, api_secret: this.apiSecret, cloud_name: this.cloudName, }).then(result => ({ - // Return the relevant data for the File api + // return the relevant data for the file api id, filename, _meta: result, @@ -103,6 +105,8 @@ export class CloudinaryAdapter { * For available options refer to the [Cloudinary destroy API](https://cloudinary.com/documentation/image_upload_api_reference#destroy_method). */ delete(file?: File, options = {}) { + if (!this.ready()) throw new Error('Cloudinary adapter is not ready'); + const destroyOptions = { // Auth api_key: this.apiKey, @@ -132,18 +136,16 @@ export class CloudinaryAdapter { } publicUrlTransformed(file: File, options: CloudinaryImageFormat = {}) { - if (!file._meta) { - return null; - } + if (!file._meta) return null; const { prettyName, ...transformation } = options; + // No formatting options provided, return the publicUrl field - if (!Object.keys(transformation).length) { - return this.publicUrl(file); - } + if (!Object.keys(transformation).length) return this.publicUrl(file); + const { public_id, format } = file._meta; - // Docs: https://github.com/cloudinary/cloudinary_npm/blob/439586eac73cee7f2803cf19f885e98f237183b3/src/utils.coffee#L472 (LOL) + // ref https://github.com/cloudinary/cloudinary_npm/blob/439586eac73cee7f2803cf19f885e98f237183b3/src/utils.coffee#L472 // @ts-ignore return cloudinary.url(public_id, { type: 'upload', diff --git a/packages/cloudinary/src/index.ts b/packages/cloudinary/src/index.ts index ef39100558f..a207ebc0c1c 100644 --- a/packages/cloudinary/src/index.ts +++ b/packages/cloudinary/src/index.ts @@ -115,6 +115,7 @@ export const cloudinaryImage = if ((config as any).isIndexed === 'unique') { throw Error("isIndexed: 'unique' is not a supported option for field type cloudinaryImage"); } + const adapter = new CloudinaryAdapter(cloudinary); const inputArg = graphql.arg({ type: graphql.Upload }); const resolveInput = async ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b110f93e96..ac414a5ad72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -684,6 +684,28 @@ importers: specifier: ~5.0.0 version: 5.0.2 + examples/cloudinary: + dependencies: + '@keystone-6/cloudinary': + specifier: workspace:^ + version: link:../../packages/cloudinary + '@keystone-6/core': + specifier: ^5.0.0 + version: link:../../packages/core + '@prisma/client': + specifier: ^4.16.2 + version: 4.16.2(prisma@4.16.2) + dotenv: + specifier: ^16.0.0 + version: 16.3.1 + devDependencies: + prisma: + specifier: ^4.16.2 + version: 4.16.2 + typescript: + specifier: ~5.0.0 + version: 5.0.2 + examples/custom-admin-ui-logo: dependencies: '@keystone-6/core': @@ -11966,7 +11988,7 @@ packages: strip-ansi: 7.1.0 supports-esm: 1.0.0 tsconfig-resolver: 3.0.1 - typescript: 5.0.2 + typescript: 5.2.2 unist-util-visit: 4.1.2 vfile: 5.3.7 vite: 4.4.9(@types/node@18.11.14) @@ -13917,7 +13939,6 @@ packages: /dotenv@16.3.1: resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} engines: {node: '>=12'} - dev: true /dotenv@8.6.0: resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} @@ -23062,7 +23083,6 @@ packages: resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} engines: {node: '>=14.17'} hasBin: true - dev: true /ua-parser-js@1.0.35: resolution: {integrity: sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==}