diff --git a/.vscode/settings.json b/.vscode/settings.json index beef62bee..601386d11 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,6 +17,7 @@ "runAllTestsOnStartup": false, "type": "on-save" }, + "nxConsole.generateAiAgentRules": true, "vitest.filesWatcherInclude": "apps/**,libs/**,packages/**", "[json]": { "editor.codeActionsOnSave": { diff --git a/apps/cms/.env.local b/apps/cms/.env.local index a212099bb..d1eb5285d 100644 --- a/apps/cms/.env.local +++ b/apps/cms/.env.local @@ -38,12 +38,12 @@ SENDGRID_FROM_NAME= # Ethereal email credentials (provide all details to enable) # Create an account at https://ethereal.email/create # Read your email at https://ethereal.email/messages -ETHEREAL_FROM_ADDRESS= -ETHEREAL_FROM_NAME= +ETHEREAL_FROM_ADDRESS=info@ethereal.email +ETHEREAL_FROM_NAME=Codeware Ethereal ETHEREAL_HOST=smtp.ethereal.email ETHEREAL_PORT=587 -ETHEREAL_USERNAME= -ETHEREAL_PASSWORD= +ETHEREAL_USERNAME=nestor.jones@ethereal.email +ETHEREAL_PASSWORD=1TgEpNJqdeeKpuQFF7 # Set to true to prevent database sync and behave as if it's production. # This is required when serving the app after running migrations locally. diff --git a/apps/cms/src/app/(payload)/admin/importMap.js b/apps/cms/src/app/(payload)/admin/importMap.js index f95a3ea7a..5fd163298 100644 --- a/apps/cms/src/app/(payload)/admin/importMap.js +++ b/apps/cms/src/app/(payload)/admin/importMap.js @@ -4,8 +4,8 @@ import { GlobalViewRedirect as GlobalViewRedirect_d6d5f193a167989e2ee7d14202901e import { TenantSelectionProvider as TenantSelectionProvider_d6d5f193a167989e2ee7d14202901e62 } from '@payloadcms/plugin-multi-tenant/rsc'; import { OverviewComponent as OverviewComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client'; import { MetaTitleComponent as MetaTitleComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client'; -import { MetaImageComponent as MetaImageComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client'; import { MetaDescriptionComponent as MetaDescriptionComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client'; +import { MetaImageComponent as MetaImageComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client'; import { PreviewComponent as PreviewComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client'; import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'; import { FixedToolbarFeatureClient as FixedToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'; @@ -34,6 +34,7 @@ import { S3ClientUploadHandler as S3ClientUploadHandler_f97aa6c64367fa259c5bc056 import { default as default_1b21cdd8d72b60f58886e03c7a7a4ebd } from '@codeware/app-cms/ui/blocks/card/CardBlockArrayRowLabel.client'; import { default as default_75fddbc22d1b88f24f1cec1b82919953 } from '@codeware/app-cms/ui/blocks/social-media/SocialMediaBlockArrayRowLabel.client'; +import { default as default_4cdb396fdb94ce9776953fed2fe6bc6a } from '@codeware/app-cms/ui/components/Callout'; import { default as default_83b0dfab156f3636ed94b94854d15ad5 } from '@codeware/app-cms/ui/components/RedirectNotifier'; import { default as default_7925a79d2af6389df70d2dd269ffbfbb } from '@codeware/app-cms/ui/components/VerifyTenantDomain'; import { default as default_06af4458abd1296f9d6bccce90425927 } from '@codeware/app-cms/ui/fields/code/Code.client'; @@ -41,6 +42,7 @@ import { default as default_8586e6ac5ddd6a3f87ffe1dd472673f5 } from '@codeware/a import { default as default_ae19db27eee762af26f037dd7af0b736 } from '@codeware/app-cms/ui/fields/icon-picker/IconPickerField.client'; import { default as default_42ab7a6f795fd44e8c166a2bb6b2adc0 } from '@codeware/apps/cms/components/Logo.client'; import { default as default_d497a38447405736d600359900364450 } from '@codeware/apps/cms/components/NavigationArrayRowLabel'; +import { default as default_4a3552dc4f000a1797b4eb36166f8ff8 } from '@codeware/apps/cms/components/TenantsArrayField'; import { default as default_dec1059b7bb8eb8da3a9f0fc400fffbd } from '@codeware/apps/cms/components/TenantsArrayRowLabel'; export const importMap = { @@ -88,6 +90,8 @@ export const importMap = { BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, '@payloadcms/richtext-lexical/client#ItalicFeatureClient': ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + '@codeware/app-cms/ui/components/Callout#default': + default_4cdb396fdb94ce9776953fed2fe6bc6a, '@codeware/apps/cms/components/NavigationArrayRowLabel#default': default_d497a38447405736d600359900364450, '@payloadcms/plugin-seo/client#OverviewComponent': @@ -114,6 +118,8 @@ export const importMap = { default_06af4458abd1296f9d6bccce90425927, '@codeware/app-cms/ui/blocks/social-media/SocialMediaBlockArrayRowLabel.client#default': default_75fddbc22d1b88f24f1cec1b82919953, + '@codeware/apps/cms/components/TenantsArrayField#default': + default_4a3552dc4f000a1797b4eb36166f8ff8, '@codeware/apps/cms/components/TenantsArrayRowLabel#default': default_dec1059b7bb8eb8da3a9f0fc400fffbd, '@codeware/apps/cms/components/Logo.client#default': diff --git a/apps/cms/src/collections/categories/categories.collection.ts b/apps/cms/src/collections/categories/categories.collection.ts index 8ea4878b4..fecfe4b00 100644 --- a/apps/cms/src/collections/categories/categories.collection.ts +++ b/apps/cms/src/collections/categories/categories.collection.ts @@ -14,7 +14,7 @@ const categories: CollectionConfig = { slug: 'categories', admin: { group: adminGroups.content, - defaultColumns: ['name', 'slug', 'tenant'], + defaultColumns: ['name', 'slug'], useAsTitle: 'name' }, access: { diff --git a/apps/cms/src/collections/media/access/external-or-api-key-access.ts b/apps/cms/src/collections/media/access/external-or-api-key-access.ts new file mode 100644 index 000000000..132b41b18 --- /dev/null +++ b/apps/cms/src/collections/media/access/external-or-api-key-access.ts @@ -0,0 +1,73 @@ +import type { Access, Where } from 'payload'; + +import { verifyApiKeyAccess } from '@codeware/app-cms/util/access'; +import type { Media } from '@codeware/shared/util/payload-types'; + +/** + * This access control ensures unauthenticated static file request + * must have external property enabled. + * + * For all other requests, api key access is verified via `verifyApiKeyAccess`, + * which is required for all tenant enabled collections. + * + * @param secret - The secret used to verify the api key + */ +export const externalOrApiKeyAccess = + (secret: string): Access => + async (args) => { + const { data, isReadingStaticFile, req } = args; + const { payload, user } = req; + + // If the request is for a static file and no user is authenticated, + // lookup the document via filename and check if external is enabled. + if (isReadingStaticFile && !user) { + const filename = data?.filename; + if (!filename) { + payload.logger.error( + 'externalOrApiKeyAccess: Expected a filename value in data' + ); + return false; + } + const { config } = payload.collections.media; + + // File name can be the main name or one of the image sizes + // e.g. filename: 'image.jpg', sizes: { small: { filename: 'image-small.jpg' } } + const filenamesQuery: Array = []; + + // Main filename + filenamesQuery.push({ + filename: { + equals: filename + } + }); + + // Image sizes filenames + if (config.upload.imageSizes) { + config.upload.imageSizes.forEach(({ name }) => { + filenamesQuery.push({ + [`sizes.${name}.filename`]: { + equals: filename + } + }); + }); + } + + const doc = await payload.find({ + collection: 'media', + limit: 1, + depth: 0, + where: { or: filenamesQuery }, + req + }); + + if (!doc.totalDocs) { + return false; + } + + // Allow access if the file is marked external + return doc.docs[0].external === true; + } + + // Default: Resolve api key access for tenant enabled collection + return verifyApiKeyAccess({ secret })(args); + }; diff --git a/apps/cms/src/collections/media/media.collection.ts b/apps/cms/src/collections/media/media.collection.ts index 9972bc820..360851a60 100644 --- a/apps/cms/src/collections/media/media.collection.ts +++ b/apps/cms/src/collections/media/media.collection.ts @@ -1,43 +1,95 @@ import path from 'path'; import { fileURLToPath } from 'url'; -import type { CollectionConfig, GenerateImageName } from 'payload'; +import mimeTypes from 'mime-types'; +import type { + CollectionBeforeValidateHook, + CollectionConfig, + Condition, + GenerateImageName, + TypeWithID +} from 'payload'; -import { adminGroups } from '@codeware/app-cms/util/definitions'; +import { getEnv } from '@codeware/app-cms/feature/env-loader'; +import { tagsSelectField } from '@codeware/app-cms/ui/fields'; +import { adminGroups, getMimeTypes } from '@codeware/app-cms/util/definitions'; +import { Media } from '@codeware/shared/util/payload-types'; + +import { externalOrApiKeyAccess } from './access/external-or-api-key-access'; const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); +const env = getEnv(); /** Custom image name */ const imageName: GenerateImageName = ({ extension, originalName, sizeName }) => `${originalName}-${sizeName}.${extension}`; +const isImageOrVideo: Condition = (_, siblingData) => + !!siblingData.mimeType && siblingData.mimeType.match(/image|video/) !== null; + +// Extracting mime type during seed has a flaky bewhavior. +// The mime type is not always available when the file is uploaded. +const ensureMimeType: CollectionBeforeValidateHook = ({ + data, + operation +}) => { + if (!data) { + return data; + } + if (data.mimeType) { + return data; + } + if (operation === 'create' || operation === 'update') { + // Try to lookup the mime type from the filename + data.mimeType = mimeTypes.lookup(data.filename ?? '') || undefined; + } + return data; +}; + /** - * Media images collection. + * Media collection for files with supported mime types. + * + * Uploaded images are converted to webp format. * - * Upload limited to `image/*` mime types and images are converted to webp format. + * **Mime types** + * + * `@codeware/app-cms/util/definitions` */ const media: CollectionConfig = { slug: 'media', admin: { group: adminGroups.fileArea, - defaultColumns: ['filename', 'alt', 'tenant', 'updatedAt'], - description: { - en: 'Media images to use in posts and pages.', - sv: 'Bilder som kan användas i inlägg och sidor.' + defaultColumns: ['filename', 'mimeType', 'fileSize', 'tags', 'createdAt'], + components: { + beforeListTable: [ + { + path: '@codeware/app-cms/ui/components/Callout', + serverProps: { + kind: 'tip', + title: 'Using tags to organize media files', + description: [ + 'Use tags to organize your media files and easily select them in file areas.', + 'Tags can be created in the "Tags" collection.', + 'You can assign multiple tags to a media file.' + ] + } + } + ] } }, access: { - // Media files like images are not fetched, hence no api key to verify. - // For admin access, the plugin appends proper permission filters. - read: () => true + read: externalOrApiKeyAccess(env.SIGNATURE_SECRET) + }, + hooks: { + beforeValidate: [ensureMimeType] }, labels: { singular: { en: 'Media', sv: 'Media' }, plural: { en: 'Media', sv: 'Media' } }, upload: { - mimeTypes: ['image/*'], + mimeTypes: getMimeTypes(), // Uploaded image is converted to a backward compatible format known by all browsers. // This image should be used as the default image in a `` element. formatOptions: { format: 'jpeg' }, @@ -102,28 +154,35 @@ const media: CollectionConfig = { type: 'richText', localized: true, admin: { + condition: isImageOrVideo, description: { - en: 'The caption for the media.', - sv: 'Bildtext för media.' + en: 'Caption to display below an image or video.', + sv: 'Text som visas under en bild eller video.' } } }, + tagsSelectField({ + buildIndex: true, + overrides: { + // TODO: Would like to use 'drawer' but it doesn't work with bulk upload media. + // Better to be safe and wait for a fix. + admin: { appearance: 'select' } + } + }), { - type: 'tabs', - tabs: [ - { - label: { en: 'Posts', sv: 'Inlägg' }, - fields: [ - { - name: 'relatedPosts', - label: { en: 'Posts', sv: 'Inlägg' }, - type: 'join', - collection: 'posts', - on: 'heroImage' - } - ] - } - ] + // Media files are not fetched, hence there's no api key to verify. + // This property will be used as alternative access control for static file requests. + name: 'external', + type: 'checkbox', + label: { en: 'External access', sv: 'Extern åtkomst' }, + admin: { + description: { + en: 'Allow external access to the file without authentication. For example, this is required for file areas and document images.', + sv: 'Tillåt extern åtkomst till filen utan autentisering. Detta krävs till exempel för filytor och bilder i dokument.' + }, + position: 'sidebar' + }, + defaultValue: false } ] }; diff --git a/apps/cms/src/collections/pages/pages.collection.ts b/apps/cms/src/collections/pages/pages.collection.ts index 980b5e93c..75633a410 100644 --- a/apps/cms/src/collections/pages/pages.collection.ts +++ b/apps/cms/src/collections/pages/pages.collection.ts @@ -18,12 +18,16 @@ const env = getEnv(); const blocks: Record = { content: true, card: true, + 'file-area': true, form: true, + image: true, media: true, code: true, 'reusable-content': true, 'social-media': true, - spacing: true + spacing: true, + // Unsupported blocks + video: false }; /** @@ -33,7 +37,7 @@ const pages: CollectionConfig<'pages'> = { slug: 'pages', admin: { group: adminGroups.content, - defaultColumns: ['name', 'slug', 'tenant', 'updatedAt'], + defaultColumns: ['name', 'slug', 'updatedAt'], useAsTitle: 'name', description: { en: 'Pages are the building blocks of the site and are used to create menus and navigation.', diff --git a/apps/cms/src/collections/posts/posts.collection.ts b/apps/cms/src/collections/posts/posts.collection.ts index 3eca851be..36ca64cc4 100644 --- a/apps/cms/src/collections/posts/posts.collection.ts +++ b/apps/cms/src/collections/posts/posts.collection.ts @@ -3,7 +3,7 @@ import { BlocksFeature } from '@payloadcms/richtext-lexical'; import type { CollectionConfig } from 'payload'; import { getEnv } from '@codeware/app-cms/feature/env-loader'; -import { slugField } from '@codeware/app-cms/ui/fields'; +import { mediaUploadField, slugField } from '@codeware/app-cms/ui/fields'; import { multiTenantLinkFeature } from '@codeware/app-cms/ui/lexical'; import { seoTab } from '@codeware/app-cms/ui/tabs'; import { verifyApiKeyAccess } from '@codeware/app-cms/util/access'; @@ -24,12 +24,15 @@ const blocks: Record = { card: true, media: true, code: true, + image: true, 'social-media': true, spacing: true, // Unsupported blocks + 'file-area': false, form: false, content: false, - 'reusable-content': false + 'reusable-content': false, + video: false }; /** @@ -39,7 +42,7 @@ const posts: CollectionConfig<'posts'> = { slug: 'posts', admin: { group: adminGroups.content, - defaultColumns: ['title', 'tenant', 'updatedAt'], + defaultColumns: ['title', 'updatedAt'], useAsTitle: 'title', description: { en: 'Posts are standalone pages such as articles or blog posts and can be categorized.', @@ -73,11 +76,10 @@ const posts: CollectionConfig<'posts'> = { { label: { en: 'Content', sv: 'Innehåll' }, fields: [ - { + mediaUploadField({ name: 'heroImage', - type: 'upload', - relationTo: 'media' - }, + mimeTypeSlugs: ['image'] + }), { name: 'content', type: 'richText', diff --git a/apps/cms/src/collections/reusable-content/reusable-content.collection.ts b/apps/cms/src/collections/reusable-content/reusable-content.collection.ts index 952a99aa9..89b5089bb 100644 --- a/apps/cms/src/collections/reusable-content/reusable-content.collection.ts +++ b/apps/cms/src/collections/reusable-content/reusable-content.collection.ts @@ -16,11 +16,14 @@ const blocks: Record = { card: true, code: true, content: true, + 'file-area': true, form: true, + image: true, media: true, 'social-media': true, spacing: true, - 'reusable-content': false + 'reusable-content': false, + video: false }; /** diff --git a/apps/cms/src/collections/tags/tags.collection.ts b/apps/cms/src/collections/tags/tags.collection.ts new file mode 100644 index 000000000..8d1c5ddfc --- /dev/null +++ b/apps/cms/src/collections/tags/tags.collection.ts @@ -0,0 +1,99 @@ +import type { CollectionConfig } from 'payload'; + +import { getEnv } from '@codeware/app-cms/feature/env-loader'; +import { + colorPickerField, + iconPickerField, + slugField +} from '@codeware/app-cms/ui/fields'; +import { verifyApiKeyAccess } from '@codeware/app-cms/util/access'; +import { adminGroups } from '@codeware/app-cms/util/definitions'; + +const env = getEnv(); + +/** + * Tags collection + */ +const tags: CollectionConfig = { + slug: 'tags', + admin: { + group: adminGroups.content, + defaultColumns: ['name', 'slug'], + useAsTitle: 'name' + }, + access: { + read: verifyApiKeyAccess({ secret: env.SIGNATURE_SECRET }) + }, + labels: { + singular: { en: 'Tag', sv: 'Etikett' }, + plural: { en: 'Tags', sv: 'Etiketter' } + }, + fields: [ + { + name: 'name', + type: 'text', + required: true, + maxLength: 20, + admin: { + description: { + en: 'The name of the tag.', + sv: 'Namnet på etiketten.' + } + } + }, + { + name: 'brand', + type: 'group', + admin: { + description: { + en: 'Select an icon and color that represent the tag.', + sv: 'Välj en ikon och färg som representerar etiketten.' + }, + hideGutter: true + }, + fields: [ + { + type: 'row', + fields: [ + iconPickerField({ + props: { hideLabel: true }, + override: { + admin: { + width: '25%' + } + } + }), + colorPickerField({ + props: { hideLabel: true }, + override: { + admin: { + width: '25%' + } + } + }) + ] + } + ] + }, + { + type: 'tabs', + tabs: [ + { + label: 'Media', + fields: [ + { + name: 'relatedMedia', + label: 'Media', + type: 'join', + collection: 'media', + on: 'tags' + } + ] + } + ] + }, + slugField({ sourceField: 'name', maxLength: 20 }) + ] +}; + +export default tags; diff --git a/apps/cms/src/collections/users/fields/tenants-array.field.ts b/apps/cms/src/collections/users/fields/tenants-array.field.ts index d47b1910d..8cbef4f8c 100644 --- a/apps/cms/src/collections/users/fields/tenants-array.field.ts +++ b/apps/cms/src/collections/users/fields/tenants-array.field.ts @@ -66,6 +66,8 @@ export const tenantsArrayField = (): Field => { sv: 'Användare kan begränsas till en eller flera arbetsytor.' }, components: { + // Dynamic "add workspace" button behavior + Field: '@codeware/apps/cms/components/TenantsArrayField', // Hide the top level label since we have a similar label in the tab Label: undefined, // Custom tenant/role header diff --git a/apps/cms/src/components/TenantsArrayField.tsx b/apps/cms/src/components/TenantsArrayField.tsx new file mode 100644 index 000000000..3ef1fb80c --- /dev/null +++ b/apps/cms/src/components/TenantsArrayField.tsx @@ -0,0 +1,38 @@ +import { ArrayField } from '@payloadcms/ui'; +import type { ArrayFieldServerComponent } from 'payload'; + +import { getUserTenantIDs, hasRole } from '@codeware/app-cms/util/misc'; + +/** + * Custom server component for the tenants array field. + * + * This component is used to limit the number of rows + * a tenant admin can add based on the number of tenants they administer. + * + * The desired effect is that the "add workspace" button is hidden + * when the tenant admin has added all tenants they administer. + */ +export const TenantsArrayField: ArrayFieldServerComponent = ({ + clientField, + path, + schemaPath, + user, + permissions +}) => { + const tenantIds = getUserTenantIDs(user, 'admin').length; + + if (!hasRole(user, 'system-user') && tenantIds > 0) { + clientField.maxRows = tenantIds; + } + + return ( + + ); +}; + +export default TenantsArrayField; diff --git a/apps/cms/src/migrations/20250515_201951_cod_292.json b/apps/cms/src/migrations/20250515_201951_cod_292.json new file mode 100644 index 000000000..3b88d542e --- /dev/null +++ b/apps/cms/src/migrations/20250515_201951_cod_292.json @@ -0,0 +1,10124 @@ +{ + "id": "12fa868f-41b9-417f-9def-7e1d27d33f12", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "categories_tenant_idx": { + "name": "categories_tenant_idx", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "categories_slug_idx": { + "name": "categories_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "categories_updated_at_idx": { + "name": "categories_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "categories_created_at_idx": { + "name": "categories_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "categories_tenant_id_tenants_id_fk": { + "name": "categories_tenant_id_tenants_id_fk", + "tableFrom": "categories", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categories_locales": { + "name": "categories_locales", + "schema": "", + "columns": { + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "categories_locales_locale_parent_id_unique": { + "name": "categories_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "categories_locales_parent_id_fk": { + "name": "categories_locales_parent_id_fk", + "tableFrom": "categories_locales", + "tableTo": "categories", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media": { + "name": "media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "alt": { + "name": "alt", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "external": { + "name": "external", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "prefix": { + "name": "prefix", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'media'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "thumbnail_u_r_l": { + "name": "thumbnail_u_r_l", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "filename": { + "name": "filename", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "filesize": { + "name": "filesize", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "focal_x": { + "name": "focal_x", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "focal_y": { + "name": "focal_y", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_thumbnail_url": { + "name": "sizes_thumbnail_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_thumbnail_width": { + "name": "sizes_thumbnail_width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_thumbnail_height": { + "name": "sizes_thumbnail_height", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_thumbnail_mime_type": { + "name": "sizes_thumbnail_mime_type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_thumbnail_filesize": { + "name": "sizes_thumbnail_filesize", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_thumbnail_filename": { + "name": "sizes_thumbnail_filename", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_small_url": { + "name": "sizes_small_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_small_width": { + "name": "sizes_small_width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_small_height": { + "name": "sizes_small_height", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_small_mime_type": { + "name": "sizes_small_mime_type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_small_filesize": { + "name": "sizes_small_filesize", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_small_filename": { + "name": "sizes_small_filename", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_medium_url": { + "name": "sizes_medium_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_medium_width": { + "name": "sizes_medium_width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_medium_height": { + "name": "sizes_medium_height", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_medium_mime_type": { + "name": "sizes_medium_mime_type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_medium_filesize": { + "name": "sizes_medium_filesize", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_medium_filename": { + "name": "sizes_medium_filename", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_large_url": { + "name": "sizes_large_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_large_width": { + "name": "sizes_large_width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_large_height": { + "name": "sizes_large_height", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_large_mime_type": { + "name": "sizes_large_mime_type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_large_filesize": { + "name": "sizes_large_filesize", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_large_filename": { + "name": "sizes_large_filename", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_meta_url": { + "name": "sizes_meta_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_meta_width": { + "name": "sizes_meta_width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_meta_height": { + "name": "sizes_meta_height", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_meta_mime_type": { + "name": "sizes_meta_mime_type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "sizes_meta_filesize": { + "name": "sizes_meta_filesize", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "sizes_meta_filename": { + "name": "sizes_meta_filename", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "media_tenant_idx": { + "name": "media_tenant_idx", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_updated_at_idx": { + "name": "media_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_created_at_idx": { + "name": "media_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_filename_idx": { + "name": "media_filename_idx", + "columns": [ + { + "expression": "filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_sizes_thumbnail_sizes_thumbnail_filename_idx": { + "name": "media_sizes_thumbnail_sizes_thumbnail_filename_idx", + "columns": [ + { + "expression": "sizes_thumbnail_filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_sizes_small_sizes_small_filename_idx": { + "name": "media_sizes_small_sizes_small_filename_idx", + "columns": [ + { + "expression": "sizes_small_filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_sizes_medium_sizes_medium_filename_idx": { + "name": "media_sizes_medium_sizes_medium_filename_idx", + "columns": [ + { + "expression": "sizes_medium_filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_sizes_large_sizes_large_filename_idx": { + "name": "media_sizes_large_sizes_large_filename_idx", + "columns": [ + { + "expression": "sizes_large_filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_sizes_meta_sizes_meta_filename_idx": { + "name": "media_sizes_meta_sizes_meta_filename_idx", + "columns": [ + { + "expression": "sizes_meta_filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "media_tenant_id_tenants_id_fk": { + "name": "media_tenant_id_tenants_id_fk", + "tableFrom": "media", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media_locales": { + "name": "media_locales", + "schema": "", + "columns": { + "caption": { + "name": "caption", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "media_locales_locale_parent_id_unique": { + "name": "media_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "media_locales_parent_id_fk": { + "name": "media_locales_parent_id_fk", + "tableFrom": "media_locales", + "tableTo": "media", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.media_rels": { + "name": "media_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "tags_id": { + "name": "tags_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "media_rels_order_idx": { + "name": "media_rels_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_rels_parent_idx": { + "name": "media_rels_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_rels_path_idx": { + "name": "media_rels_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_rels_tags_id_idx": { + "name": "media_rels_tags_id_idx", + "columns": [ + { + "expression": "tags_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "media_rels_parent_fk": { + "name": "media_rels_parent_fk", + "tableFrom": "media_rels", + "tableTo": "media", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "media_rels_tags_fk": { + "name": "media_rels_tags_fk", + "tableFrom": "media_rels", + "tableTo": "tags", + "columnsFrom": ["tags_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.navigation_items": { + "name": "navigation_items", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "label_source": { + "name": "label_source", + "type": "enum_navigation_label_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'document'" + }, + "custom_label": { + "name": "custom_label", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "navigation_items_order_idx": { + "name": "navigation_items_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "navigation_items_parent_id_idx": { + "name": "navigation_items_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "navigation_items_parent_id_fk": { + "name": "navigation_items_parent_id_fk", + "tableFrom": "navigation_items", + "tableTo": "navigation", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.navigation": { + "name": "navigation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "navigation_tenant_idx": { + "name": "navigation_tenant_idx", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "navigation_updated_at_idx": { + "name": "navigation_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "navigation_created_at_idx": { + "name": "navigation_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "navigation_tenant_id_tenants_id_fk": { + "name": "navigation_tenant_id_tenants_id_fk", + "tableFrom": "navigation", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.navigation_rels": { + "name": "navigation_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "pages_id": { + "name": "pages_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "posts_id": { + "name": "posts_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "navigation_rels_order_idx": { + "name": "navigation_rels_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "navigation_rels_parent_idx": { + "name": "navigation_rels_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "navigation_rels_path_idx": { + "name": "navigation_rels_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "navigation_rels_pages_id_idx": { + "name": "navigation_rels_pages_id_idx", + "columns": [ + { + "expression": "pages_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "navigation_rels_posts_id_idx": { + "name": "navigation_rels_posts_id_idx", + "columns": [ + { + "expression": "posts_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "navigation_rels_parent_fk": { + "name": "navigation_rels_parent_fk", + "tableFrom": "navigation_rels", + "tableTo": "navigation", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "navigation_rels_pages_fk": { + "name": "navigation_rels_pages_fk", + "tableFrom": "navigation_rels", + "tableTo": "pages", + "columnsFrom": ["pages_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "navigation_rels_posts_fk": { + "name": "navigation_rels_posts_fk", + "tableFrom": "navigation_rels", + "tableTo": "posts", + "columnsFrom": ["posts_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_content_columns": { + "name": "pages_blocks_content_columns", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "size": { + "name": "size", + "type": "enum_content_column_size", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'full'" + }, + "rich_text": { + "name": "rich_text", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_content_columns_order_idx": { + "name": "pages_blocks_content_columns_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_content_columns_parent_id_idx": { + "name": "pages_blocks_content_columns_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_content_columns_locale_idx": { + "name": "pages_blocks_content_columns_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_content_columns_parent_id_fk": { + "name": "pages_blocks_content_columns_parent_id_fk", + "tableFrom": "pages_blocks_content_columns", + "tableTo": "pages_blocks_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_content": { + "name": "pages_blocks_content", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_content_order_idx": { + "name": "pages_blocks_content_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_content_parent_id_idx": { + "name": "pages_blocks_content_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_content_path_idx": { + "name": "pages_blocks_content_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_content_locale_idx": { + "name": "pages_blocks_content_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_content_parent_id_fk": { + "name": "pages_blocks_content_parent_id_fk", + "tableFrom": "pages_blocks_content", + "tableTo": "pages", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_card_cards": { + "name": "pages_blocks_card_cards", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "brand_icon": { + "name": "brand_icon", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "brand_color": { + "name": "brand_color", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "enable_link": { + "name": "enable_link", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "link_type": { + "name": "link_type", + "type": "enum_link_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'reference'" + }, + "link_new_tab": { + "name": "link_new_tab", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "link_url": { + "name": "link_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "link_nav_trigger": { + "name": "link_nav_trigger", + "type": "enum_nav_trigger", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'card'" + }, + "link_label": { + "name": "link_label", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_card_cards_order_idx": { + "name": "pages_blocks_card_cards_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_card_cards_parent_id_idx": { + "name": "pages_blocks_card_cards_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_card_cards_locale_idx": { + "name": "pages_blocks_card_cards_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_card_cards_parent_id_fk": { + "name": "pages_blocks_card_cards_parent_id_fk", + "tableFrom": "pages_blocks_card_cards", + "tableTo": "pages_blocks_card", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_card": { + "name": "pages_blocks_card", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_card_order_idx": { + "name": "pages_blocks_card_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_card_parent_id_idx": { + "name": "pages_blocks_card_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_card_path_idx": { + "name": "pages_blocks_card_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_card_locale_idx": { + "name": "pages_blocks_card_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_card_parent_id_fk": { + "name": "pages_blocks_card_parent_id_fk", + "tableFrom": "pages_blocks_card", + "tableTo": "pages", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_file_area": { + "name": "pages_blocks_file_area", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_file_area_order_idx": { + "name": "pages_blocks_file_area_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_file_area_parent_id_idx": { + "name": "pages_blocks_file_area_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_file_area_path_idx": { + "name": "pages_blocks_file_area_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_file_area_locale_idx": { + "name": "pages_blocks_file_area_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_file_area_parent_id_fk": { + "name": "pages_blocks_file_area_parent_id_fk", + "tableFrom": "pages_blocks_file_area", + "tableTo": "pages", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_form": { + "name": "pages_blocks_form", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "form_id": { + "name": "form_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "enable_intro": { + "name": "enable_intro", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "intro_content": { + "name": "intro_content", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_form_order_idx": { + "name": "pages_blocks_form_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_form_parent_id_idx": { + "name": "pages_blocks_form_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_form_path_idx": { + "name": "pages_blocks_form_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_form_locale_idx": { + "name": "pages_blocks_form_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_form_form_idx": { + "name": "pages_blocks_form_form_idx", + "columns": [ + { + "expression": "form_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_form_form_id_forms_id_fk": { + "name": "pages_blocks_form_form_id_forms_id_fk", + "tableFrom": "pages_blocks_form", + "tableTo": "forms", + "columnsFrom": ["form_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pages_blocks_form_parent_id_fk": { + "name": "pages_blocks_form_parent_id_fk", + "tableFrom": "pages_blocks_form", + "tableTo": "pages", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_image": { + "name": "pages_blocks_image", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_image_order_idx": { + "name": "pages_blocks_image_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_image_parent_id_idx": { + "name": "pages_blocks_image_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_image_path_idx": { + "name": "pages_blocks_image_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_image_locale_idx": { + "name": "pages_blocks_image_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_image_media_idx": { + "name": "pages_blocks_image_media_idx", + "columns": [ + { + "expression": "media_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_image_media_id_media_id_fk": { + "name": "pages_blocks_image_media_id_media_id_fk", + "tableFrom": "pages_blocks_image", + "tableTo": "media", + "columnsFrom": ["media_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pages_blocks_image_parent_id_fk": { + "name": "pages_blocks_image_parent_id_fk", + "tableFrom": "pages_blocks_image", + "tableTo": "pages", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_media": { + "name": "pages_blocks_media", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_media_order_idx": { + "name": "pages_blocks_media_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_media_parent_id_idx": { + "name": "pages_blocks_media_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_media_path_idx": { + "name": "pages_blocks_media_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_media_locale_idx": { + "name": "pages_blocks_media_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_media_media_idx": { + "name": "pages_blocks_media_media_idx", + "columns": [ + { + "expression": "media_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_media_media_id_media_id_fk": { + "name": "pages_blocks_media_media_id_media_id_fk", + "tableFrom": "pages_blocks_media", + "tableTo": "media", + "columnsFrom": ["media_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pages_blocks_media_parent_id_fk": { + "name": "pages_blocks_media_parent_id_fk", + "tableFrom": "pages_blocks_media", + "tableTo": "pages", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_code": { + "name": "pages_blocks_code", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "language": { + "name": "language", + "type": "enum_code_language", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ts'" + }, + "code": { + "name": "code", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_code_order_idx": { + "name": "pages_blocks_code_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_code_parent_id_idx": { + "name": "pages_blocks_code_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_code_path_idx": { + "name": "pages_blocks_code_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_code_locale_idx": { + "name": "pages_blocks_code_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_code_parent_id_fk": { + "name": "pages_blocks_code_parent_id_fk", + "tableFrom": "pages_blocks_code", + "tableTo": "pages", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_reusable_content": { + "name": "pages_blocks_reusable_content", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "reusable_content_id": { + "name": "reusable_content_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "ref_id": { + "name": "ref_id", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_reusable_content_order_idx": { + "name": "pages_blocks_reusable_content_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_reusable_content_parent_id_idx": { + "name": "pages_blocks_reusable_content_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_reusable_content_path_idx": { + "name": "pages_blocks_reusable_content_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_reusable_content_locale_idx": { + "name": "pages_blocks_reusable_content_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_reusable_content_reusable_content_idx": { + "name": "pages_blocks_reusable_content_reusable_content_idx", + "columns": [ + { + "expression": "reusable_content_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_reusable_content_reusable_content_id_reusable_content_id_fk": { + "name": "pages_blocks_reusable_content_reusable_content_id_reusable_content_id_fk", + "tableFrom": "pages_blocks_reusable_content", + "tableTo": "reusable_content", + "columnsFrom": ["reusable_content_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pages_blocks_reusable_content_parent_id_fk": { + "name": "pages_blocks_reusable_content_parent_id_fk", + "tableFrom": "pages_blocks_reusable_content", + "tableTo": "pages", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_social_media_social": { + "name": "pages_blocks_social_media_social", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "enum_social_media_platform", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "with_label": { + "name": "with_label", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_social_media_social_order_idx": { + "name": "pages_blocks_social_media_social_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_social_media_social_parent_id_idx": { + "name": "pages_blocks_social_media_social_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_social_media_social_locale_idx": { + "name": "pages_blocks_social_media_social_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_social_media_social_parent_id_fk": { + "name": "pages_blocks_social_media_social_parent_id_fk", + "tableFrom": "pages_blocks_social_media_social", + "tableTo": "pages_blocks_social_media", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_social_media": { + "name": "pages_blocks_social_media", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "enum_social_media_direction", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'horizontal'" + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_social_media_order_idx": { + "name": "pages_blocks_social_media_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_social_media_parent_id_idx": { + "name": "pages_blocks_social_media_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_social_media_path_idx": { + "name": "pages_blocks_social_media_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_social_media_locale_idx": { + "name": "pages_blocks_social_media_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_social_media_parent_id_fk": { + "name": "pages_blocks_social_media_parent_id_fk", + "tableFrom": "pages_blocks_social_media", + "tableTo": "pages", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_blocks_spacing": { + "name": "pages_blocks_spacing", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "size": { + "name": "size", + "type": "enum_spacing_size", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'regular'" + }, + "divider": { + "name": "divider", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_blocks_spacing_order_idx": { + "name": "pages_blocks_spacing_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_spacing_parent_id_idx": { + "name": "pages_blocks_spacing_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_spacing_path_idx": { + "name": "pages_blocks_spacing_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_blocks_spacing_locale_idx": { + "name": "pages_blocks_spacing_locale_idx", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_blocks_spacing_parent_id_fk": { + "name": "pages_blocks_spacing_parent_id_fk", + "tableFrom": "pages_blocks_spacing", + "tableTo": "pages", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages": { + "name": "pages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "published_at": { + "name": "published_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pages_tenant_idx": { + "name": "pages_tenant_idx", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_slug_idx": { + "name": "pages_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_updated_at_idx": { + "name": "pages_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_created_at_idx": { + "name": "pages_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_tenant_id_tenants_id_fk": { + "name": "pages_tenant_id_tenants_id_fk", + "tableFrom": "pages", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_locales": { + "name": "pages_locales", + "schema": "", + "columns": { + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "header": { + "name": "header", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "meta_title": { + "name": "meta_title", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "meta_image_id": { + "name": "meta_image_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "meta_description": { + "name": "meta_description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "pages_meta_meta_image_idx": { + "name": "pages_meta_meta_image_idx", + "columns": [ + { + "expression": "meta_image_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_locales_locale_parent_id_unique": { + "name": "pages_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_locales_meta_image_id_media_id_fk": { + "name": "pages_locales_meta_image_id_media_id_fk", + "tableFrom": "pages_locales", + "tableTo": "media", + "columnsFrom": ["meta_image_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "pages_locales_parent_id_fk": { + "name": "pages_locales_parent_id_fk", + "tableFrom": "pages_locales", + "tableTo": "pages", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pages_rels": { + "name": "pages_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "locale": { + "name": "locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "pages_id": { + "name": "pages_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "posts_id": { + "name": "posts_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tags_id": { + "name": "tags_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_rels_order_idx": { + "name": "pages_rels_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_rels_parent_idx": { + "name": "pages_rels_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_rels_path_idx": { + "name": "pages_rels_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_rels_locale_idx": { + "name": "pages_rels_locale_idx", + "columns": [ + { + "expression": "locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_rels_pages_id_idx": { + "name": "pages_rels_pages_id_idx", + "columns": [ + { + "expression": "pages_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_rels_posts_id_idx": { + "name": "pages_rels_posts_id_idx", + "columns": [ + { + "expression": "posts_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_rels_tags_id_idx": { + "name": "pages_rels_tags_id_idx", + "columns": [ + { + "expression": "tags_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pages_rels_parent_fk": { + "name": "pages_rels_parent_fk", + "tableFrom": "pages_rels", + "tableTo": "pages", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pages_rels_pages_fk": { + "name": "pages_rels_pages_fk", + "tableFrom": "pages_rels", + "tableTo": "pages", + "columnsFrom": ["pages_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pages_rels_posts_fk": { + "name": "pages_rels_posts_fk", + "tableFrom": "pages_rels", + "tableTo": "posts", + "columnsFrom": ["posts_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pages_rels_tags_fk": { + "name": "pages_rels_tags_fk", + "tableFrom": "pages_rels", + "tableTo": "tags", + "columnsFrom": ["tags_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.posts": { + "name": "posts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "hero_image_id": { + "name": "hero_image_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "published_at": { + "name": "published_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "posts_tenant_idx": { + "name": "posts_tenant_idx", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "posts_hero_image_idx": { + "name": "posts_hero_image_idx", + "columns": [ + { + "expression": "hero_image_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "posts_slug_idx": { + "name": "posts_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "posts_updated_at_idx": { + "name": "posts_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "posts_created_at_idx": { + "name": "posts_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "posts_tenant_id_tenants_id_fk": { + "name": "posts_tenant_id_tenants_id_fk", + "tableFrom": "posts", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "posts_hero_image_id_media_id_fk": { + "name": "posts_hero_image_id_media_id_fk", + "tableFrom": "posts", + "tableTo": "media", + "columnsFrom": ["hero_image_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.posts_locales": { + "name": "posts_locales", + "schema": "", + "columns": { + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "meta_title": { + "name": "meta_title", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "meta_image_id": { + "name": "meta_image_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "meta_description": { + "name": "meta_description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "posts_meta_meta_image_idx": { + "name": "posts_meta_meta_image_idx", + "columns": [ + { + "expression": "meta_image_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "posts_locales_locale_parent_id_unique": { + "name": "posts_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "posts_locales_meta_image_id_media_id_fk": { + "name": "posts_locales_meta_image_id_media_id_fk", + "tableFrom": "posts_locales", + "tableTo": "media", + "columnsFrom": ["meta_image_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "posts_locales_parent_id_fk": { + "name": "posts_locales_parent_id_fk", + "tableFrom": "posts_locales", + "tableTo": "posts", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.posts_rels": { + "name": "posts_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "posts_id": { + "name": "posts_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "categories_id": { + "name": "categories_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "users_id": { + "name": "users_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "posts_rels_order_idx": { + "name": "posts_rels_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "posts_rels_parent_idx": { + "name": "posts_rels_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "posts_rels_path_idx": { + "name": "posts_rels_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "posts_rels_posts_id_idx": { + "name": "posts_rels_posts_id_idx", + "columns": [ + { + "expression": "posts_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "posts_rels_categories_id_idx": { + "name": "posts_rels_categories_id_idx", + "columns": [ + { + "expression": "categories_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "posts_rels_users_id_idx": { + "name": "posts_rels_users_id_idx", + "columns": [ + { + "expression": "users_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "posts_rels_parent_fk": { + "name": "posts_rels_parent_fk", + "tableFrom": "posts_rels", + "tableTo": "posts", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "posts_rels_posts_fk": { + "name": "posts_rels_posts_fk", + "tableFrom": "posts_rels", + "tableTo": "posts", + "columnsFrom": ["posts_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "posts_rels_categories_fk": { + "name": "posts_rels_categories_fk", + "tableFrom": "posts_rels", + "tableTo": "categories", + "columnsFrom": ["categories_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "posts_rels_users_fk": { + "name": "posts_rels_users_fk", + "tableFrom": "posts_rels", + "tableTo": "users", + "columnsFrom": ["users_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_card_cards": { + "name": "reusable_content_blocks_card_cards", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "brand_icon": { + "name": "brand_icon", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "brand_color": { + "name": "brand_color", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "enable_link": { + "name": "enable_link", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "link_type": { + "name": "link_type", + "type": "enum_link_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'reference'" + }, + "link_new_tab": { + "name": "link_new_tab", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "link_url": { + "name": "link_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "link_nav_trigger": { + "name": "link_nav_trigger", + "type": "enum_nav_trigger", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'card'" + } + }, + "indexes": { + "reusable_content_blocks_card_cards_order_idx": { + "name": "reusable_content_blocks_card_cards_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_card_cards_parent_id_idx": { + "name": "reusable_content_blocks_card_cards_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_card_cards_parent_id_fk": { + "name": "reusable_content_blocks_card_cards_parent_id_fk", + "tableFrom": "reusable_content_blocks_card_cards", + "tableTo": "reusable_content_blocks_card", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_card_cards_locales": { + "name": "reusable_content_blocks_card_cards_locales", + "schema": "", + "columns": { + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "link_label": { + "name": "link_label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "reusable_content_blocks_card_cards_locales_locale_parent_id_unique": { + "name": "reusable_content_blocks_card_cards_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_card_cards_locales_parent_id_fk": { + "name": "reusable_content_blocks_card_cards_locales_parent_id_fk", + "tableFrom": "reusable_content_blocks_card_cards_locales", + "tableTo": "reusable_content_blocks_card_cards", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_card": { + "name": "reusable_content_blocks_card", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_blocks_card_order_idx": { + "name": "reusable_content_blocks_card_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_card_parent_id_idx": { + "name": "reusable_content_blocks_card_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_card_path_idx": { + "name": "reusable_content_blocks_card_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_card_parent_id_fk": { + "name": "reusable_content_blocks_card_parent_id_fk", + "tableFrom": "reusable_content_blocks_card", + "tableTo": "reusable_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_code": { + "name": "reusable_content_blocks_code", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "language": { + "name": "language", + "type": "enum_code_language", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ts'" + }, + "code": { + "name": "code", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_blocks_code_order_idx": { + "name": "reusable_content_blocks_code_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_code_parent_id_idx": { + "name": "reusable_content_blocks_code_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_code_path_idx": { + "name": "reusable_content_blocks_code_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_code_parent_id_fk": { + "name": "reusable_content_blocks_code_parent_id_fk", + "tableFrom": "reusable_content_blocks_code", + "tableTo": "reusable_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_content_columns": { + "name": "reusable_content_blocks_content_columns", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "size": { + "name": "size", + "type": "enum_content_column_size", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'full'" + }, + "rich_text": { + "name": "rich_text", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_blocks_content_columns_order_idx": { + "name": "reusable_content_blocks_content_columns_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_content_columns_parent_id_idx": { + "name": "reusable_content_blocks_content_columns_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_content_columns_parent_id_fk": { + "name": "reusable_content_blocks_content_columns_parent_id_fk", + "tableFrom": "reusable_content_blocks_content_columns", + "tableTo": "reusable_content_blocks_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_content": { + "name": "reusable_content_blocks_content", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_blocks_content_order_idx": { + "name": "reusable_content_blocks_content_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_content_parent_id_idx": { + "name": "reusable_content_blocks_content_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_content_path_idx": { + "name": "reusable_content_blocks_content_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_content_parent_id_fk": { + "name": "reusable_content_blocks_content_parent_id_fk", + "tableFrom": "reusable_content_blocks_content", + "tableTo": "reusable_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_file_area": { + "name": "reusable_content_blocks_file_area", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_blocks_file_area_order_idx": { + "name": "reusable_content_blocks_file_area_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_file_area_parent_id_idx": { + "name": "reusable_content_blocks_file_area_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_file_area_path_idx": { + "name": "reusable_content_blocks_file_area_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_file_area_parent_id_fk": { + "name": "reusable_content_blocks_file_area_parent_id_fk", + "tableFrom": "reusable_content_blocks_file_area", + "tableTo": "reusable_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_form": { + "name": "reusable_content_blocks_form", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "form_id": { + "name": "form_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "enable_intro": { + "name": "enable_intro", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "intro_content": { + "name": "intro_content", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_blocks_form_order_idx": { + "name": "reusable_content_blocks_form_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_form_parent_id_idx": { + "name": "reusable_content_blocks_form_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_form_path_idx": { + "name": "reusable_content_blocks_form_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_form_form_idx": { + "name": "reusable_content_blocks_form_form_idx", + "columns": [ + { + "expression": "form_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_form_form_id_forms_id_fk": { + "name": "reusable_content_blocks_form_form_id_forms_id_fk", + "tableFrom": "reusable_content_blocks_form", + "tableTo": "forms", + "columnsFrom": ["form_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "reusable_content_blocks_form_parent_id_fk": { + "name": "reusable_content_blocks_form_parent_id_fk", + "tableFrom": "reusable_content_blocks_form", + "tableTo": "reusable_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_image": { + "name": "reusable_content_blocks_image", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_blocks_image_order_idx": { + "name": "reusable_content_blocks_image_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_image_parent_id_idx": { + "name": "reusable_content_blocks_image_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_image_path_idx": { + "name": "reusable_content_blocks_image_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_image_media_idx": { + "name": "reusable_content_blocks_image_media_idx", + "columns": [ + { + "expression": "media_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_image_media_id_media_id_fk": { + "name": "reusable_content_blocks_image_media_id_media_id_fk", + "tableFrom": "reusable_content_blocks_image", + "tableTo": "media", + "columnsFrom": ["media_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "reusable_content_blocks_image_parent_id_fk": { + "name": "reusable_content_blocks_image_parent_id_fk", + "tableFrom": "reusable_content_blocks_image", + "tableTo": "reusable_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_media": { + "name": "reusable_content_blocks_media", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_blocks_media_order_idx": { + "name": "reusable_content_blocks_media_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_media_parent_id_idx": { + "name": "reusable_content_blocks_media_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_media_path_idx": { + "name": "reusable_content_blocks_media_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_media_media_idx": { + "name": "reusable_content_blocks_media_media_idx", + "columns": [ + { + "expression": "media_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_media_media_id_media_id_fk": { + "name": "reusable_content_blocks_media_media_id_media_id_fk", + "tableFrom": "reusable_content_blocks_media", + "tableTo": "media", + "columnsFrom": ["media_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "reusable_content_blocks_media_parent_id_fk": { + "name": "reusable_content_blocks_media_parent_id_fk", + "tableFrom": "reusable_content_blocks_media", + "tableTo": "reusable_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_social_media_social": { + "name": "reusable_content_blocks_social_media_social", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "platform": { + "name": "platform", + "type": "enum_social_media_platform", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "with_label": { + "name": "with_label", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_blocks_social_media_social_order_idx": { + "name": "reusable_content_blocks_social_media_social_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_social_media_social_parent_id_idx": { + "name": "reusable_content_blocks_social_media_social_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_social_media_social_parent_id_fk": { + "name": "reusable_content_blocks_social_media_social_parent_id_fk", + "tableFrom": "reusable_content_blocks_social_media_social", + "tableTo": "reusable_content_blocks_social_media", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_social_media": { + "name": "reusable_content_blocks_social_media", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "direction": { + "name": "direction", + "type": "enum_social_media_direction", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'horizontal'" + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_blocks_social_media_order_idx": { + "name": "reusable_content_blocks_social_media_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_social_media_parent_id_idx": { + "name": "reusable_content_blocks_social_media_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_social_media_path_idx": { + "name": "reusable_content_blocks_social_media_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_social_media_parent_id_fk": { + "name": "reusable_content_blocks_social_media_parent_id_fk", + "tableFrom": "reusable_content_blocks_social_media", + "tableTo": "reusable_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_blocks_spacing": { + "name": "reusable_content_blocks_spacing", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "size": { + "name": "size", + "type": "enum_spacing_size", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'regular'" + }, + "divider": { + "name": "divider", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_blocks_spacing_order_idx": { + "name": "reusable_content_blocks_spacing_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_spacing_parent_id_idx": { + "name": "reusable_content_blocks_spacing_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_blocks_spacing_path_idx": { + "name": "reusable_content_blocks_spacing_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_blocks_spacing_parent_id_fk": { + "name": "reusable_content_blocks_spacing_parent_id_fk", + "tableFrom": "reusable_content_blocks_spacing", + "tableTo": "reusable_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content": { + "name": "reusable_content", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reusable_content_tenant_idx": { + "name": "reusable_content_tenant_idx", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_updated_at_idx": { + "name": "reusable_content_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_created_at_idx": { + "name": "reusable_content_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_tenant_id_tenants_id_fk": { + "name": "reusable_content_tenant_id_tenants_id_fk", + "tableFrom": "reusable_content", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_locales": { + "name": "reusable_content_locales", + "schema": "", + "columns": { + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "reusable_content_locales_locale_parent_id_unique": { + "name": "reusable_content_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_locales_parent_id_fk": { + "name": "reusable_content_locales_parent_id_fk", + "tableFrom": "reusable_content_locales", + "tableTo": "reusable_content", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reusable_content_rels": { + "name": "reusable_content_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "pages_id": { + "name": "pages_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "posts_id": { + "name": "posts_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tags_id": { + "name": "tags_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "reusable_content_rels_order_idx": { + "name": "reusable_content_rels_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_rels_parent_idx": { + "name": "reusable_content_rels_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_rels_path_idx": { + "name": "reusable_content_rels_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_rels_pages_id_idx": { + "name": "reusable_content_rels_pages_id_idx", + "columns": [ + { + "expression": "pages_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_rels_posts_id_idx": { + "name": "reusable_content_rels_posts_id_idx", + "columns": [ + { + "expression": "posts_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reusable_content_rels_tags_id_idx": { + "name": "reusable_content_rels_tags_id_idx", + "columns": [ + { + "expression": "tags_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reusable_content_rels_parent_fk": { + "name": "reusable_content_rels_parent_fk", + "tableFrom": "reusable_content_rels", + "tableTo": "reusable_content", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reusable_content_rels_pages_fk": { + "name": "reusable_content_rels_pages_fk", + "tableFrom": "reusable_content_rels", + "tableTo": "pages", + "columnsFrom": ["pages_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reusable_content_rels_posts_fk": { + "name": "reusable_content_rels_posts_fk", + "tableFrom": "reusable_content_rels", + "tableTo": "posts", + "columnsFrom": ["posts_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "reusable_content_rels_tags_fk": { + "name": "reusable_content_rels_tags_fk", + "tableFrom": "reusable_content_rels", + "tableTo": "tags", + "columnsFrom": ["tags_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.site_settings": { + "name": "site_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "general_app_name": { + "name": "general_app_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "general_landing_page_id": { + "name": "general_landing_page_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "site_settings_tenant_idx": { + "name": "site_settings_tenant_idx", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "site_settings_general_general_landing_page_idx": { + "name": "site_settings_general_general_landing_page_idx", + "columns": [ + { + "expression": "general_landing_page_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "site_settings_updated_at_idx": { + "name": "site_settings_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "site_settings_created_at_idx": { + "name": "site_settings_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "site_settings_tenant_id_tenants_id_fk": { + "name": "site_settings_tenant_id_tenants_id_fk", + "tableFrom": "site_settings", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "site_settings_general_landing_page_id_pages_id_fk": { + "name": "site_settings_general_landing_page_id_pages_id_fk", + "tableFrom": "site_settings", + "tableTo": "pages", + "columnsFrom": ["general_landing_page_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tags": { + "name": "tags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "brand_icon": { + "name": "brand_icon", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "brand_color": { + "name": "brand_color", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tags_tenant_idx": { + "name": "tags_tenant_idx", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tags_slug_idx": { + "name": "tags_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tags_updated_at_idx": { + "name": "tags_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tags_created_at_idx": { + "name": "tags_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tags_tenant_id_tenants_id_fk": { + "name": "tags_tenant_id_tenants_id_fk", + "tableFrom": "tags", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tenants_domains_page_types": { + "name": "tenants_domains_page_types", + "schema": "", + "columns": { + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "enum_tenant_domain_page_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + } + }, + "indexes": { + "tenants_domains_page_types_order_idx": { + "name": "tenants_domains_page_types_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tenants_domains_page_types_parent_idx": { + "name": "tenants_domains_page_types_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tenants_domains_page_types_parent_fk": { + "name": "tenants_domains_page_types_parent_fk", + "tableFrom": "tenants_domains_page_types", + "tableTo": "tenants_domains", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tenants_domains": { + "name": "tenants_domains", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "tenants_domains_order_idx": { + "name": "tenants_domains_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tenants_domains_parent_id_idx": { + "name": "tenants_domains_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tenants_domains_parent_id_fk": { + "name": "tenants_domains_parent_id_fk", + "tableFrom": "tenants_domains", + "tableTo": "tenants", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tenants": { + "name": "tenants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "enable_a_p_i_key": { + "name": "enable_a_p_i_key", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "api_key": { + "name": "api_key", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "api_key_index": { + "name": "api_key_index", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "tenants_slug_idx": { + "name": "tenants_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tenants_updated_at_idx": { + "name": "tenants_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tenants_created_at_idx": { + "name": "tenants_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users_tenants": { + "name": "users_tenants", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "enum_tenant_user_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'user'" + } + }, + "indexes": { + "users_tenants_order_idx": { + "name": "users_tenants_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_tenants_parent_id_idx": { + "name": "users_tenants_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_tenants_tenant_idx": { + "name": "users_tenants_tenant_idx", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_tenants_tenant_id_tenants_id_fk": { + "name": "users_tenants_tenant_id_tenants_id_fk", + "tableFrom": "users_tenants", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "users_tenants_parent_id_fk": { + "name": "users_tenants_parent_id_fk", + "tableFrom": "users_tenants", + "tableTo": "users", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "enum_user_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "email": { + "name": "email", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "reset_password_token": { + "name": "reset_password_token", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "reset_password_expiration": { + "name": "reset_password_expiration", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "salt": { + "name": "salt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "hash": { + "name": "hash", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "login_attempts": { + "name": "login_attempts", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "lock_until": { + "name": "lock_until", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_updated_at_idx": { + "name": "users_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_created_at_idx": { + "name": "users_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_checkbox": { + "name": "forms_blocks_checkbox", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "default_value": { + "name": "default_value", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forms_blocks_checkbox_order_idx": { + "name": "forms_blocks_checkbox_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_checkbox_parent_id_idx": { + "name": "forms_blocks_checkbox_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_checkbox_path_idx": { + "name": "forms_blocks_checkbox_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_checkbox_parent_id_fk": { + "name": "forms_blocks_checkbox_parent_id_fk", + "tableFrom": "forms_blocks_checkbox", + "tableTo": "forms", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_checkbox_locales": { + "name": "forms_blocks_checkbox_locales", + "schema": "", + "columns": { + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_checkbox_locales_locale_parent_id_unique": { + "name": "forms_blocks_checkbox_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_checkbox_locales_parent_id_fk": { + "name": "forms_blocks_checkbox_locales_parent_id_fk", + "tableFrom": "forms_blocks_checkbox_locales", + "tableTo": "forms_blocks_checkbox", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_country": { + "name": "forms_blocks_country", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forms_blocks_country_order_idx": { + "name": "forms_blocks_country_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_country_parent_id_idx": { + "name": "forms_blocks_country_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_country_path_idx": { + "name": "forms_blocks_country_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_country_parent_id_fk": { + "name": "forms_blocks_country_parent_id_fk", + "tableFrom": "forms_blocks_country", + "tableTo": "forms", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_country_locales": { + "name": "forms_blocks_country_locales", + "schema": "", + "columns": { + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "placeholder": { + "name": "placeholder", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_country_locales_locale_parent_id_unique": { + "name": "forms_blocks_country_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_country_locales_parent_id_fk": { + "name": "forms_blocks_country_locales_parent_id_fk", + "tableFrom": "forms_blocks_country_locales", + "tableTo": "forms_blocks_country", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_email": { + "name": "forms_blocks_email", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forms_blocks_email_order_idx": { + "name": "forms_blocks_email_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_email_parent_id_idx": { + "name": "forms_blocks_email_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_email_path_idx": { + "name": "forms_blocks_email_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_email_parent_id_fk": { + "name": "forms_blocks_email_parent_id_fk", + "tableFrom": "forms_blocks_email", + "tableTo": "forms", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_email_locales": { + "name": "forms_blocks_email_locales", + "schema": "", + "columns": { + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "placeholder": { + "name": "placeholder", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_email_locales_locale_parent_id_unique": { + "name": "forms_blocks_email_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_email_locales_parent_id_fk": { + "name": "forms_blocks_email_locales_parent_id_fk", + "tableFrom": "forms_blocks_email_locales", + "tableTo": "forms_blocks_email", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_message": { + "name": "forms_blocks_message", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forms_blocks_message_order_idx": { + "name": "forms_blocks_message_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_message_parent_id_idx": { + "name": "forms_blocks_message_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_message_path_idx": { + "name": "forms_blocks_message_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_message_parent_id_fk": { + "name": "forms_blocks_message_parent_id_fk", + "tableFrom": "forms_blocks_message", + "tableTo": "forms", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_message_locales": { + "name": "forms_blocks_message_locales", + "schema": "", + "columns": { + "message": { + "name": "message", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_message_locales_locale_parent_id_unique": { + "name": "forms_blocks_message_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_message_locales_parent_id_fk": { + "name": "forms_blocks_message_locales_parent_id_fk", + "tableFrom": "forms_blocks_message_locales", + "tableTo": "forms_blocks_message", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_number": { + "name": "forms_blocks_number", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "default_value": { + "name": "default_value", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forms_blocks_number_order_idx": { + "name": "forms_blocks_number_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_number_parent_id_idx": { + "name": "forms_blocks_number_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_number_path_idx": { + "name": "forms_blocks_number_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_number_parent_id_fk": { + "name": "forms_blocks_number_parent_id_fk", + "tableFrom": "forms_blocks_number", + "tableTo": "forms", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_number_locales": { + "name": "forms_blocks_number_locales", + "schema": "", + "columns": { + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "placeholder": { + "name": "placeholder", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_number_locales_locale_parent_id_unique": { + "name": "forms_blocks_number_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_number_locales_parent_id_fk": { + "name": "forms_blocks_number_locales_parent_id_fk", + "tableFrom": "forms_blocks_number_locales", + "tableTo": "forms_blocks_number", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_select_options": { + "name": "forms_blocks_select_options", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "value": { + "name": "value", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_select_options_order_idx": { + "name": "forms_blocks_select_options_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_select_options_parent_id_idx": { + "name": "forms_blocks_select_options_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_select_options_parent_id_fk": { + "name": "forms_blocks_select_options_parent_id_fk", + "tableFrom": "forms_blocks_select_options", + "tableTo": "forms_blocks_select", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_select_options_locales": { + "name": "forms_blocks_select_options_locales", + "schema": "", + "columns": { + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_select_options_locales_locale_parent_id_unique": { + "name": "forms_blocks_select_options_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_select_options_locales_parent_id_fk": { + "name": "forms_blocks_select_options_locales_parent_id_fk", + "tableFrom": "forms_blocks_select_options_locales", + "tableTo": "forms_blocks_select_options", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_select": { + "name": "forms_blocks_select", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "placeholder": { + "name": "placeholder", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forms_blocks_select_order_idx": { + "name": "forms_blocks_select_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_select_parent_id_idx": { + "name": "forms_blocks_select_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_select_path_idx": { + "name": "forms_blocks_select_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_select_parent_id_fk": { + "name": "forms_blocks_select_parent_id_fk", + "tableFrom": "forms_blocks_select", + "tableTo": "forms", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_select_locales": { + "name": "forms_blocks_select_locales", + "schema": "", + "columns": { + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "default_value": { + "name": "default_value", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_select_locales_locale_parent_id_unique": { + "name": "forms_blocks_select_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_select_locales_parent_id_fk": { + "name": "forms_blocks_select_locales_parent_id_fk", + "tableFrom": "forms_blocks_select_locales", + "tableTo": "forms_blocks_select", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_text": { + "name": "forms_blocks_text", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forms_blocks_text_order_idx": { + "name": "forms_blocks_text_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_text_parent_id_idx": { + "name": "forms_blocks_text_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_text_path_idx": { + "name": "forms_blocks_text_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_text_parent_id_fk": { + "name": "forms_blocks_text_parent_id_fk", + "tableFrom": "forms_blocks_text", + "tableTo": "forms", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_text_locales": { + "name": "forms_blocks_text_locales", + "schema": "", + "columns": { + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "placeholder": { + "name": "placeholder", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "default_value": { + "name": "default_value", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_text_locales_locale_parent_id_unique": { + "name": "forms_blocks_text_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_text_locales_parent_id_fk": { + "name": "forms_blocks_text_locales_parent_id_fk", + "tableFrom": "forms_blocks_text_locales", + "tableTo": "forms_blocks_text", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_textarea": { + "name": "forms_blocks_textarea", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forms_blocks_textarea_order_idx": { + "name": "forms_blocks_textarea_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_textarea_parent_id_idx": { + "name": "forms_blocks_textarea_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_textarea_path_idx": { + "name": "forms_blocks_textarea_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_textarea_parent_id_fk": { + "name": "forms_blocks_textarea_parent_id_fk", + "tableFrom": "forms_blocks_textarea", + "tableTo": "forms", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_textarea_locales": { + "name": "forms_blocks_textarea_locales", + "schema": "", + "columns": { + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "placeholder": { + "name": "placeholder", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "default_value": { + "name": "default_value", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_textarea_locales_locale_parent_id_unique": { + "name": "forms_blocks_textarea_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_textarea_locales_parent_id_fk": { + "name": "forms_blocks_textarea_locales_parent_id_fk", + "tableFrom": "forms_blocks_textarea_locales", + "tableTo": "forms_blocks_textarea", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_radio_options": { + "name": "forms_blocks_radio_options", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "value": { + "name": "value", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_radio_options_order_idx": { + "name": "forms_blocks_radio_options_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_radio_options_parent_id_idx": { + "name": "forms_blocks_radio_options_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_radio_options_parent_id_fk": { + "name": "forms_blocks_radio_options_parent_id_fk", + "tableFrom": "forms_blocks_radio_options", + "tableTo": "forms_blocks_radio", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_radio_options_locales": { + "name": "forms_blocks_radio_options_locales", + "schema": "", + "columns": { + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_radio_options_locales_locale_parent_id_unique": { + "name": "forms_blocks_radio_options_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_radio_options_locales_parent_id_fk": { + "name": "forms_blocks_radio_options_locales_parent_id_fk", + "tableFrom": "forms_blocks_radio_options_locales", + "tableTo": "forms_blocks_radio_options", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_radio": { + "name": "forms_blocks_radio", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_path": { + "name": "_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "required": { + "name": "required", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forms_blocks_radio_order_idx": { + "name": "forms_blocks_radio_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_radio_parent_id_idx": { + "name": "forms_blocks_radio_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_blocks_radio_path_idx": { + "name": "forms_blocks_radio_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_radio_parent_id_fk": { + "name": "forms_blocks_radio_parent_id_fk", + "tableFrom": "forms_blocks_radio", + "tableTo": "forms", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_blocks_radio_locales": { + "name": "forms_blocks_radio_locales", + "schema": "", + "columns": { + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "default_value": { + "name": "default_value", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_blocks_radio_locales_locale_parent_id_unique": { + "name": "forms_blocks_radio_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_blocks_radio_locales_parent_id_fk": { + "name": "forms_blocks_radio_locales_parent_id_fk", + "tableFrom": "forms_blocks_radio_locales", + "tableTo": "forms_blocks_radio", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_emails": { + "name": "forms_emails", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "email_to": { + "name": "email_to", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "cc": { + "name": "cc", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "bcc": { + "name": "bcc", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "reply_to": { + "name": "reply_to", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "email_from": { + "name": "email_from", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forms_emails_order_idx": { + "name": "forms_emails_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_emails_parent_id_idx": { + "name": "forms_emails_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_emails_parent_id_fk": { + "name": "forms_emails_parent_id_fk", + "tableFrom": "forms_emails", + "tableTo": "forms", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_emails_locales": { + "name": "forms_emails_locales", + "schema": "", + "columns": { + "subject": { + "name": "subject", + "type": "varchar", + "primaryKey": false, + "notNull": true, + "default": "'You''''ve received a new message.'" + }, + "message": { + "name": "message", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_emails_locales_locale_parent_id_unique": { + "name": "forms_emails_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_emails_locales_parent_id_fk": { + "name": "forms_emails_locales_parent_id_fk", + "tableFrom": "forms_emails_locales", + "tableTo": "forms_emails", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms": { + "name": "forms", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "confirmation_type": { + "name": "confirmation_type", + "type": "enum_forms_confirmation_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'message'" + }, + "redirect_type": { + "name": "redirect_type", + "type": "enum_forms_redirect_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'reference'" + }, + "redirect_url": { + "name": "redirect_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "forms_tenant_idx": { + "name": "forms_tenant_idx", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_updated_at_idx": { + "name": "forms_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_created_at_idx": { + "name": "forms_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_tenant_id_tenants_id_fk": { + "name": "forms_tenant_id_tenants_id_fk", + "tableFrom": "forms", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_locales": { + "name": "forms_locales", + "schema": "", + "columns": { + "submit_button_label": { + "name": "submit_button_label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "confirmation_message": { + "name": "confirmation_message", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_locale": { + "name": "_locale", + "type": "_locales", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "forms_locales_locale_parent_id_unique": { + "name": "forms_locales_locale_parent_id_unique", + "columns": [ + { + "expression": "_locale", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_locales_parent_id_fk": { + "name": "forms_locales_parent_id_fk", + "tableFrom": "forms_locales", + "tableTo": "forms", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.forms_rels": { + "name": "forms_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "pages_id": { + "name": "pages_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "forms_rels_order_idx": { + "name": "forms_rels_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_rels_parent_idx": { + "name": "forms_rels_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_rels_path_idx": { + "name": "forms_rels_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "forms_rels_pages_id_idx": { + "name": "forms_rels_pages_id_idx", + "columns": [ + { + "expression": "pages_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "forms_rels_parent_fk": { + "name": "forms_rels_parent_fk", + "tableFrom": "forms_rels", + "tableTo": "forms", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "forms_rels_pages_fk": { + "name": "forms_rels_pages_fk", + "tableFrom": "forms_rels", + "tableTo": "pages", + "columnsFrom": ["pages_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.form_submissions_submission_data": { + "name": "form_submissions_submission_data", + "schema": "", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "field": { + "name": "field", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "form_submissions_submission_data_order_idx": { + "name": "form_submissions_submission_data_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "form_submissions_submission_data_parent_id_idx": { + "name": "form_submissions_submission_data_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "form_submissions_submission_data_parent_id_fk": { + "name": "form_submissions_submission_data_parent_id_fk", + "tableFrom": "form_submissions_submission_data", + "tableTo": "form_submissions", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.form_submissions": { + "name": "form_submissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "tenant_id": { + "name": "tenant_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "form_id": { + "name": "form_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "form_submissions_tenant_idx": { + "name": "form_submissions_tenant_idx", + "columns": [ + { + "expression": "tenant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "form_submissions_form_idx": { + "name": "form_submissions_form_idx", + "columns": [ + { + "expression": "form_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "form_submissions_updated_at_idx": { + "name": "form_submissions_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "form_submissions_created_at_idx": { + "name": "form_submissions_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "form_submissions_tenant_id_tenants_id_fk": { + "name": "form_submissions_tenant_id_tenants_id_fk", + "tableFrom": "form_submissions", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "form_submissions_form_id_forms_id_fk": { + "name": "form_submissions_form_id_forms_id_fk", + "tableFrom": "form_submissions", + "tableTo": "forms", + "columnsFrom": ["form_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payload_locked_documents": { + "name": "payload_locked_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "global_slug": { + "name": "global_slug", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "payload_locked_documents_global_slug_idx": { + "name": "payload_locked_documents_global_slug_idx", + "columns": [ + { + "expression": "global_slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_updated_at_idx": { + "name": "payload_locked_documents_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_created_at_idx": { + "name": "payload_locked_documents_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payload_locked_documents_rels": { + "name": "payload_locked_documents_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "categories_id": { + "name": "categories_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "media_id": { + "name": "media_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "navigation_id": { + "name": "navigation_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pages_id": { + "name": "pages_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "posts_id": { + "name": "posts_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "reusable_content_id": { + "name": "reusable_content_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "site_settings_id": { + "name": "site_settings_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tags_id": { + "name": "tags_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "tenants_id": { + "name": "tenants_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "users_id": { + "name": "users_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "forms_id": { + "name": "forms_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "form_submissions_id": { + "name": "form_submissions_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "payload_locked_documents_rels_order_idx": { + "name": "payload_locked_documents_rels_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_parent_idx": { + "name": "payload_locked_documents_rels_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_path_idx": { + "name": "payload_locked_documents_rels_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_categories_id_idx": { + "name": "payload_locked_documents_rels_categories_id_idx", + "columns": [ + { + "expression": "categories_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_media_id_idx": { + "name": "payload_locked_documents_rels_media_id_idx", + "columns": [ + { + "expression": "media_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_navigation_id_idx": { + "name": "payload_locked_documents_rels_navigation_id_idx", + "columns": [ + { + "expression": "navigation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_pages_id_idx": { + "name": "payload_locked_documents_rels_pages_id_idx", + "columns": [ + { + "expression": "pages_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_posts_id_idx": { + "name": "payload_locked_documents_rels_posts_id_idx", + "columns": [ + { + "expression": "posts_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_reusable_content_id_idx": { + "name": "payload_locked_documents_rels_reusable_content_id_idx", + "columns": [ + { + "expression": "reusable_content_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_site_settings_id_idx": { + "name": "payload_locked_documents_rels_site_settings_id_idx", + "columns": [ + { + "expression": "site_settings_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_tags_id_idx": { + "name": "payload_locked_documents_rels_tags_id_idx", + "columns": [ + { + "expression": "tags_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_tenants_id_idx": { + "name": "payload_locked_documents_rels_tenants_id_idx", + "columns": [ + { + "expression": "tenants_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_users_id_idx": { + "name": "payload_locked_documents_rels_users_id_idx", + "columns": [ + { + "expression": "users_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_forms_id_idx": { + "name": "payload_locked_documents_rels_forms_id_idx", + "columns": [ + { + "expression": "forms_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_form_submissions_id_idx": { + "name": "payload_locked_documents_rels_form_submissions_id_idx", + "columns": [ + { + "expression": "form_submissions_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "payload_locked_documents_rels_parent_fk": { + "name": "payload_locked_documents_rels_parent_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "payload_locked_documents", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_categories_fk": { + "name": "payload_locked_documents_rels_categories_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "categories", + "columnsFrom": ["categories_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_media_fk": { + "name": "payload_locked_documents_rels_media_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "media", + "columnsFrom": ["media_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_navigation_fk": { + "name": "payload_locked_documents_rels_navigation_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "navigation", + "columnsFrom": ["navigation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_pages_fk": { + "name": "payload_locked_documents_rels_pages_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "pages", + "columnsFrom": ["pages_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_posts_fk": { + "name": "payload_locked_documents_rels_posts_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "posts", + "columnsFrom": ["posts_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_reusable_content_fk": { + "name": "payload_locked_documents_rels_reusable_content_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "reusable_content", + "columnsFrom": ["reusable_content_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_site_settings_fk": { + "name": "payload_locked_documents_rels_site_settings_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "site_settings", + "columnsFrom": ["site_settings_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_tags_fk": { + "name": "payload_locked_documents_rels_tags_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "tags", + "columnsFrom": ["tags_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_tenants_fk": { + "name": "payload_locked_documents_rels_tenants_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "tenants", + "columnsFrom": ["tenants_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_users_fk": { + "name": "payload_locked_documents_rels_users_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "users", + "columnsFrom": ["users_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_forms_fk": { + "name": "payload_locked_documents_rels_forms_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "forms", + "columnsFrom": ["forms_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_form_submissions_fk": { + "name": "payload_locked_documents_rels_form_submissions_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "form_submissions", + "columnsFrom": ["form_submissions_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payload_preferences": { + "name": "payload_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "payload_preferences_key_idx": { + "name": "payload_preferences_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_updated_at_idx": { + "name": "payload_preferences_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_created_at_idx": { + "name": "payload_preferences_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payload_preferences_rels": { + "name": "payload_preferences_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "tenants_id": { + "name": "tenants_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "users_id": { + "name": "users_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "payload_preferences_rels_order_idx": { + "name": "payload_preferences_rels_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_rels_parent_idx": { + "name": "payload_preferences_rels_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_rels_path_idx": { + "name": "payload_preferences_rels_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_rels_tenants_id_idx": { + "name": "payload_preferences_rels_tenants_id_idx", + "columns": [ + { + "expression": "tenants_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_rels_users_id_idx": { + "name": "payload_preferences_rels_users_id_idx", + "columns": [ + { + "expression": "users_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "payload_preferences_rels_parent_fk": { + "name": "payload_preferences_rels_parent_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "payload_preferences", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_preferences_rels_tenants_fk": { + "name": "payload_preferences_rels_tenants_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "tenants", + "columnsFrom": ["tenants_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_preferences_rels_users_fk": { + "name": "payload_preferences_rels_users_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "users", + "columnsFrom": ["users_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payload_migrations": { + "name": "payload_migrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "batch": { + "name": "batch", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "payload_migrations_updated_at_idx": { + "name": "payload_migrations_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_migrations_created_at_idx": { + "name": "payload_migrations_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public._locales": { + "name": "_locales", + "schema": "public", + "values": ["en", "sv"] + }, + "public.enum_navigation_label_source": { + "name": "enum_navigation_label_source", + "schema": "public", + "values": ["document", "custom"] + }, + "public.enum_content_column_size": { + "name": "enum_content_column_size", + "schema": "public", + "values": ["one-third", "half", "two-thirds", "full"] + }, + "public.enum_link_type": { + "name": "enum_link_type", + "schema": "public", + "values": ["reference", "custom"] + }, + "public.enum_nav_trigger": { + "name": "enum_nav_trigger", + "schema": "public", + "values": ["card", "link"] + }, + "public.enum_code_language": { + "name": "enum_code_language", + "schema": "public", + "values": ["ts", "plaintext", "tsx", "js", "jsx"] + }, + "public.enum_social_media_platform": { + "name": "enum_social_media_platform", + "schema": "public", + "values": [ + "discord", + "facebook", + "github", + "instagram", + "linkedin", + "npm", + "web", + "x", + "youtube" + ] + }, + "public.enum_social_media_direction": { + "name": "enum_social_media_direction", + "schema": "public", + "values": ["horizontal", "vertical"] + }, + "public.enum_spacing_size": { + "name": "enum_spacing_size", + "schema": "public", + "values": ["tight", "regular", "loose"] + }, + "public.enum_tenant_domain_page_type": { + "name": "enum_tenant_domain_page_type", + "schema": "public", + "values": ["cms", "client", "disabled"] + }, + "public.enum_tenant_user_role": { + "name": "enum_tenant_user_role", + "schema": "public", + "values": ["user", "admin"] + }, + "public.enum_user_role": { + "name": "enum_user_role", + "schema": "public", + "values": ["user", "system-user"] + }, + "public.enum_forms_confirmation_type": { + "name": "enum_forms_confirmation_type", + "schema": "public", + "values": ["message", "redirect"] + }, + "public.enum_forms_redirect_type": { + "name": "enum_forms_redirect_type", + "schema": "public", + "values": ["reference", "custom"] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} diff --git a/apps/cms/src/migrations/20250515_201951_cod_292.ts b/apps/cms/src/migrations/20250515_201951_cod_292.ts new file mode 100644 index 000000000..fc722d84e --- /dev/null +++ b/apps/cms/src/migrations/20250515_201951_cod_292.ts @@ -0,0 +1,196 @@ +import { MigrateDownArgs, MigrateUpArgs, sql } from '@payloadcms/db-postgres'; + +export async function up({ db, payload, req }: MigrateUpArgs): Promise { + await db.execute(sql` + CREATE TABLE IF NOT EXISTS "media_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "tags_id" integer + ); + + CREATE TABLE IF NOT EXISTS "pages_blocks_file_area" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "_locale" "_locales" NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "block_name" varchar + ); + + CREATE TABLE IF NOT EXISTS "pages_blocks_image" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "_locale" "_locales" NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "media_id" integer NOT NULL, + "block_name" varchar + ); + + CREATE TABLE IF NOT EXISTS "reusable_content_blocks_file_area" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "block_name" varchar + ); + + CREATE TABLE IF NOT EXISTS "reusable_content_blocks_image" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "media_id" integer NOT NULL, + "block_name" varchar + ); + + CREATE TABLE IF NOT EXISTS "tags" ( + "id" serial PRIMARY KEY NOT NULL, + "tenant_id" integer, + "name" varchar NOT NULL, + "brand_icon" varchar, + "brand_color" varchar, + "slug" varchar, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + ALTER TABLE "media" ADD COLUMN "external" boolean DEFAULT false; + ALTER TABLE "pages_rels" ADD COLUMN "tags_id" integer; + ALTER TABLE "reusable_content_rels" ADD COLUMN "tags_id" integer; + ALTER TABLE "payload_locked_documents_rels" ADD COLUMN "tags_id" integer; + DO $$ BEGIN + ALTER TABLE "media_rels" ADD CONSTRAINT "media_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."media"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "media_rels" ADD CONSTRAINT "media_rels_tags_fk" FOREIGN KEY ("tags_id") REFERENCES "public"."tags"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "pages_blocks_file_area" ADD CONSTRAINT "pages_blocks_file_area_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "pages_blocks_image" ADD CONSTRAINT "pages_blocks_image_media_id_media_id_fk" FOREIGN KEY ("media_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "pages_blocks_image" ADD CONSTRAINT "pages_blocks_image_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "reusable_content_blocks_file_area" ADD CONSTRAINT "reusable_content_blocks_file_area_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."reusable_content"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "reusable_content_blocks_image" ADD CONSTRAINT "reusable_content_blocks_image_media_id_media_id_fk" FOREIGN KEY ("media_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "reusable_content_blocks_image" ADD CONSTRAINT "reusable_content_blocks_image_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."reusable_content"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "tags" ADD CONSTRAINT "tags_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE set null ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + CREATE INDEX IF NOT EXISTS "media_rels_order_idx" ON "media_rels" USING btree ("order"); + CREATE INDEX IF NOT EXISTS "media_rels_parent_idx" ON "media_rels" USING btree ("parent_id"); + CREATE INDEX IF NOT EXISTS "media_rels_path_idx" ON "media_rels" USING btree ("path"); + CREATE INDEX IF NOT EXISTS "media_rels_tags_id_idx" ON "media_rels" USING btree ("tags_id"); + CREATE INDEX IF NOT EXISTS "pages_blocks_file_area_order_idx" ON "pages_blocks_file_area" USING btree ("_order"); + CREATE INDEX IF NOT EXISTS "pages_blocks_file_area_parent_id_idx" ON "pages_blocks_file_area" USING btree ("_parent_id"); + CREATE INDEX IF NOT EXISTS "pages_blocks_file_area_path_idx" ON "pages_blocks_file_area" USING btree ("_path"); + CREATE INDEX IF NOT EXISTS "pages_blocks_file_area_locale_idx" ON "pages_blocks_file_area" USING btree ("_locale"); + CREATE INDEX IF NOT EXISTS "pages_blocks_image_order_idx" ON "pages_blocks_image" USING btree ("_order"); + CREATE INDEX IF NOT EXISTS "pages_blocks_image_parent_id_idx" ON "pages_blocks_image" USING btree ("_parent_id"); + CREATE INDEX IF NOT EXISTS "pages_blocks_image_path_idx" ON "pages_blocks_image" USING btree ("_path"); + CREATE INDEX IF NOT EXISTS "pages_blocks_image_locale_idx" ON "pages_blocks_image" USING btree ("_locale"); + CREATE INDEX IF NOT EXISTS "pages_blocks_image_media_idx" ON "pages_blocks_image" USING btree ("media_id"); + CREATE INDEX IF NOT EXISTS "reusable_content_blocks_file_area_order_idx" ON "reusable_content_blocks_file_area" USING btree ("_order"); + CREATE INDEX IF NOT EXISTS "reusable_content_blocks_file_area_parent_id_idx" ON "reusable_content_blocks_file_area" USING btree ("_parent_id"); + CREATE INDEX IF NOT EXISTS "reusable_content_blocks_file_area_path_idx" ON "reusable_content_blocks_file_area" USING btree ("_path"); + CREATE INDEX IF NOT EXISTS "reusable_content_blocks_image_order_idx" ON "reusable_content_blocks_image" USING btree ("_order"); + CREATE INDEX IF NOT EXISTS "reusable_content_blocks_image_parent_id_idx" ON "reusable_content_blocks_image" USING btree ("_parent_id"); + CREATE INDEX IF NOT EXISTS "reusable_content_blocks_image_path_idx" ON "reusable_content_blocks_image" USING btree ("_path"); + CREATE INDEX IF NOT EXISTS "reusable_content_blocks_image_media_idx" ON "reusable_content_blocks_image" USING btree ("media_id"); + CREATE INDEX IF NOT EXISTS "tags_tenant_idx" ON "tags" USING btree ("tenant_id"); + CREATE INDEX IF NOT EXISTS "tags_slug_idx" ON "tags" USING btree ("slug"); + CREATE INDEX IF NOT EXISTS "tags_updated_at_idx" ON "tags" USING btree ("updated_at"); + CREATE INDEX IF NOT EXISTS "tags_created_at_idx" ON "tags" USING btree ("created_at"); + DO $$ BEGIN + ALTER TABLE "pages_rels" ADD CONSTRAINT "pages_rels_tags_fk" FOREIGN KEY ("tags_id") REFERENCES "public"."tags"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "reusable_content_rels" ADD CONSTRAINT "reusable_content_rels_tags_fk" FOREIGN KEY ("tags_id") REFERENCES "public"."tags"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_tags_fk" FOREIGN KEY ("tags_id") REFERENCES "public"."tags"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + CREATE INDEX IF NOT EXISTS "pages_rels_tags_id_idx" ON "pages_rels" USING btree ("tags_id","locale"); + CREATE INDEX IF NOT EXISTS "reusable_content_rels_tags_id_idx" ON "reusable_content_rels" USING btree ("tags_id"); + CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_tags_id_idx" ON "payload_locked_documents_rels" USING btree ("tags_id");`); +} + +export async function down({ + db, + payload, + req +}: MigrateDownArgs): Promise { + await db.execute(sql` + ALTER TABLE "media_rels" DISABLE ROW LEVEL SECURITY; + ALTER TABLE "pages_blocks_file_area" DISABLE ROW LEVEL SECURITY; + ALTER TABLE "pages_blocks_image" DISABLE ROW LEVEL SECURITY; + ALTER TABLE "reusable_content_blocks_file_area" DISABLE ROW LEVEL SECURITY; + ALTER TABLE "reusable_content_blocks_image" DISABLE ROW LEVEL SECURITY; + ALTER TABLE "tags" DISABLE ROW LEVEL SECURITY; + DROP TABLE "media_rels" CASCADE; + DROP TABLE "pages_blocks_file_area" CASCADE; + DROP TABLE "pages_blocks_image" CASCADE; + DROP TABLE "reusable_content_blocks_file_area" CASCADE; + DROP TABLE "reusable_content_blocks_image" CASCADE; + DROP TABLE "tags" CASCADE; + ALTER TABLE "pages_rels" DROP CONSTRAINT "pages_rels_tags_fk"; + + ALTER TABLE "reusable_content_rels" DROP CONSTRAINT "reusable_content_rels_tags_fk"; + + ALTER TABLE "payload_locked_documents_rels" DROP CONSTRAINT "payload_locked_documents_rels_tags_fk"; + + DROP INDEX IF EXISTS "pages_rels_tags_id_idx"; + DROP INDEX IF EXISTS "reusable_content_rels_tags_id_idx"; + DROP INDEX IF EXISTS "payload_locked_documents_rels_tags_id_idx"; + ALTER TABLE "media" DROP COLUMN IF EXISTS "external"; + ALTER TABLE "pages_rels" DROP COLUMN IF EXISTS "tags_id"; + ALTER TABLE "reusable_content_rels" DROP COLUMN IF EXISTS "tags_id"; + ALTER TABLE "payload_locked_documents_rels" DROP COLUMN IF EXISTS "tags_id";`); +} diff --git a/apps/cms/src/migrations/index.ts b/apps/cms/src/migrations/index.ts index 88527de13..34dcae3f8 100644 --- a/apps/cms/src/migrations/index.ts +++ b/apps/cms/src/migrations/index.ts @@ -21,6 +21,7 @@ import * as migration_20250419_153152_cod_320 from './20250419_153152_cod_320'; import * as migration_20250419_225444_cod_320_2 from './20250419_225444_cod_320_2'; import * as migration_20250421_181801_cod_326 from './20250421_181801_cod_326'; import * as migration_20250428_172053_cod_323 from './20250428_172053_cod_323'; +import * as migration_20250515_201951_cod_292 from './20250515_201951_cod_292'; export const migrations = [ { @@ -137,5 +138,10 @@ export const migrations = [ up: migration_20250428_172053_cod_323.up, down: migration_20250428_172053_cod_323.down, name: '20250428_172053_cod_323' + }, + { + up: migration_20250515_201951_cod_292.up, + down: migration_20250515_201951_cod_292.down, + name: '20250515_201951_cod_292' } ]; diff --git a/apps/cms/src/payload.config.ts b/apps/cms/src/payload.config.ts index 2e47f48eb..117f0a616 100644 --- a/apps/cms/src/payload.config.ts +++ b/apps/cms/src/payload.config.ts @@ -11,11 +11,14 @@ import { cardBlock, codeBlock, contentBlock, + fileAreaBlock, formBlock, + imageBlock, mediaBlock, reusableContentBlock, socialMediaBlock, - spacingBlock + spacingBlock, + videoBlock } from '@codeware/app-cms/ui/blocks'; import { defaultLexical } from '@codeware/app-cms/ui/fields'; import { getEmailAdapter } from '@codeware/app-cms/util/email'; @@ -28,6 +31,7 @@ import pages from './collections/pages/pages.collection'; import posts from './collections/posts/posts.collection'; import reusableContent from './collections/reusable-content/reusable-content.collection'; import siteSettings from './collections/site-settings/site-settings.collection'; +import tags from './collections/tags/tags.collection'; import tenants from './collections/tenants/tenants.collection'; import users from './collections/users/users.collection'; import { migrations } from './migrations'; @@ -55,11 +59,14 @@ export default buildConfig({ cardBlock, codeBlock, contentBlock, + fileAreaBlock, formBlock, + imageBlock, mediaBlock, reusableContentBlock, socialMediaBlock, - spacingBlock + spacingBlock, + videoBlock ], collections: [ categories, @@ -69,6 +76,7 @@ export default buildConfig({ posts, reusableContent, siteSettings, + tags, tenants, users ], @@ -128,6 +136,7 @@ export default buildConfig({ await seed({ environment: env.DEPLOY_ENV, payload, + remoteDataUrl: env.SEED_DATA_URL, source: env.SEED_SOURCE, strategy: env.SEED_STRATEGY }); diff --git a/apps/cms/src/utils/reset-db.ts b/apps/cms/src/utils/reset-db.ts index 79bd3cea4..5872725ae 100644 --- a/apps/cms/src/utils/reset-db.ts +++ b/apps/cms/src/utils/reset-db.ts @@ -1,4 +1,4 @@ -import { sql } from '@payloadcms/db-postgres'; +import type { DrizzleAdapter } from '@payloadcms/drizzle'; import { getPayload } from 'payload'; import { loadEnv } from '@codeware/app-cms/feature/env-loader'; @@ -24,40 +24,48 @@ async function reset() { process.exit(0); } - const payload = await getPayload({ config }); - - console.log('⏳ Resetting database...'); const start = Date.now(); - const query = sql` - -- Delete all tables - DO $$ DECLARE - r RECORD; - BEGIN - FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = current_schema()) LOOP - EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE'; - END LOOP; - END $$; + const payload = await getPayload({ config }); - -- Delete enums - DO $$ DECLARE - r RECORD; - BEGIN - FOR r IN (select t.typname as enum_name - from pg_type t - join pg_enum e on t.oid = e.enumtypid - join pg_catalog.pg_namespace n ON n.oid = t.typnamespace - where n.nspname = current_schema()) LOOP - EXECUTE 'DROP TYPE IF EXISTS ' || quote_ident(r.enum_name); - END LOOP; - END $$; + // Query a collection to check if the database is empty + try { + await payload.db.count({ + collection: 'pages' + }); + } catch (e) { + const err = e as Error; + if (err.message.match(/relation "pages" does not exist/)) { + console.log('✅ Database is empty, skipping reset'); + process.exit(0); + } + console.error('❌ Failed to connect to database'); + process.exit(1); + } - `; + // Truncate media collection separately to also delete the files on disk + const { docs, errors } = await payload.delete({ + collection: 'media', + where: {} + }); + if (errors.length) { + console.error('❌ Failed to delete media files'); + console.error(errors); + process.exit(1); + } + if (docs.length) { + console.log(`✅ Deleted ${docs.length} media files`); + } else { + console.log('✅ No media files to delete'); + } - await payload.db.drizzle.execute(query); + // Now drop the database + const adapter = payload.db as DrizzleAdapter; + await payload.db.dropDatabase({ adapter }); + console.log('✅ Dropped database'); const end = Date.now(); - console.log(`✅ Reset took ${end - start}ms`); + console.log(`✅ Reset took ${end - start} ms`); process.exit(0); } diff --git a/apps/web/eslint.config.mjs b/apps/web/eslint.config.mjs index 4cb459c7f..5a5881311 100644 --- a/apps/web/eslint.config.mjs +++ b/apps/web/eslint.config.mjs @@ -30,13 +30,16 @@ export default [ '@payloadcms/richtext-lexical', 'prism-react-renderer', // Payload components - Tailwind/Shadcn + '@radix-ui/react-aspect-ratio', '@radix-ui/react-checkbox', + '@radix-ui/react-context-menu', '@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu', '@radix-ui/react-label', '@radix-ui/react-menubar', '@radix-ui/react-popover', '@radix-ui/react-radio-group', + '@radix-ui/react-scroll-area', '@radix-ui/react-select', '@radix-ui/react-separator', '@radix-ui/react-slot', diff --git a/apps/web/package.json b/apps/web/package.json index 5ecf4ea07..ee48e6f85 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -13,13 +13,16 @@ "@icons-pack/react-simple-icons": "^12.7.0", "@infisical/sdk": "^3.0.4", "@payloadcms/richtext-lexical": "^3.23.0", + "@radix-ui/react-aspect-ratio": "^1.1.8", "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-context-menu": "^2.2.14", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-menubar": "^1.1.6", "@radix-ui/react-popover": "^1.1.10", "@radix-ui/react-radio-group": "^1.2.3", + "@radix-ui/react-scroll-area": "^1.2.8", "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-slot": "^1.1.2", diff --git a/libs/app-cms/feature/seed/src/lib/load-infisical-data.ts b/libs/app-cms/feature/seed/src/lib/load-infisical-data.ts index d04c90f39..0994d4422 100644 --- a/libs/app-cms/feature/seed/src/lib/load-infisical-data.ts +++ b/libs/app-cms/feature/seed/src/lib/load-infisical-data.ts @@ -29,8 +29,10 @@ export const loadInfisicalData = async (args: { const seedData: SeedData = { categories: [], + media: [], pages: [], posts: [], + tags: [], tenants: [], users: [] }; diff --git a/libs/app-cms/feature/seed/src/lib/load-static-data.ts b/libs/app-cms/feature/seed/src/lib/load-static-data.ts index edd5c97fc..01d0581c1 100644 --- a/libs/app-cms/feature/seed/src/lib/load-static-data.ts +++ b/libs/app-cms/feature/seed/src/lib/load-static-data.ts @@ -11,6 +11,7 @@ import type { import { generateCategories } from './utils/generate-categories'; import { generatePages } from './utils/generate-pages'; import { generatePosts } from './utils/generate-posts'; +import { generateTags } from './utils/generate-tags'; import { generateTenants } from './utils/generate-tenants'; import { generateUsers } from './utils/generate-users'; @@ -29,7 +30,8 @@ export const defaultSeedRules: Prettify< roleUser: { min: 1, max: 5 } }, tenantPosts: { min: 5, max: 10 }, - tenantPages: { min: 4, max: 6 } + tenantPages: { min: 4, max: 6 }, + tenantTags: { min: 4, max: 6 } } as const; /** @@ -48,17 +50,20 @@ export const loadStaticData = (args: { options?: StaticSeedOptions; payload: Payload; }): SeedData => { - const { environment, options = {}, payload } = args; - const { constantSeedKey, seedRules = {} } = options; + const { environment, options, payload } = args; + const { constantSeedKey, remoteDataUrl, seedRules = {} } = options ?? {}; if (environment === 'production') { throw new Error('Static seeding is not allowed in production'); } const jsonData = manageSeedData.load(environment, { - error: (msg, ...args) => payload.logger.error(msg, args), - log: (msg, ...args) => payload.logger.info(msg, args), - warn: (msg, ...args) => payload.logger.warn(msg, args) + logger: { + error: (msg, ...args) => payload.logger.error(msg, args), + log: (msg, ...args) => payload.logger.info(msg, args), + warn: (msg, ...args) => payload.logger.warn(msg, args) + }, + remoteDataUrl }); if (jsonData) { @@ -113,6 +118,12 @@ export const loadStaticData = (args: { tenants ); + // Generate tags + const tags = generateTags( + seedRules.tenantTags ?? defaultSeedRules.tenantTags, + tenants + ); + // Generate pages const pages = generatePages( seedRules.tenantPages ?? defaultSeedRules.tenantPages, @@ -141,8 +152,10 @@ export const loadStaticData = (args: { // Combine all data to the final seed object const seedData: SeedData = { categories, + media: [], // media is generated from proper files pages, posts, + tags, tenants, users }; diff --git a/libs/app-cms/feature/seed/src/lib/local-api/custom-seed.ts b/libs/app-cms/feature/seed/src/lib/local-api/custom-seed.ts new file mode 100644 index 000000000..07d0771dd --- /dev/null +++ b/libs/app-cms/feature/seed/src/lib/local-api/custom-seed.ts @@ -0,0 +1,84 @@ +import { getId } from '@codeware/app-cms/util/misc'; +import type { FileAreaBlock } from '@codeware/shared/util/payload-types'; +import type { Payload } from 'payload'; + +import { ensureNavigation } from './ensure-navigation'; +import { ensurePage } from './ensure-page'; + +/** + * Custom seed queries for documents that doesn't fit the generic seed data type. + * + * - Creates a file area page for tenants that have the 'file-area' tag + * and add this page to navigation. + * + * Runs without transaction to be able to use existing collection documents. + * Everything else should be prepared before running this seed. + * + * @param payload - Payload instance + * @param seedData - Seed data + */ +export const customSeed = async ( + payload: Payload, + transactionID: string | number | undefined +): Promise => { + // Common file area slug for both tag and page + const fileAreaSlug = 'file-area'; + + // Create a file area page for tenants that have the 'file-area' tag + const { docs } = await payload.find({ + collection: 'tags', + select: { tenant: true }, + where: { + and: [{ slug: { equals: fileAreaSlug } }, { tenant: { exists: true } }] + }, + depth: 0, + pagination: false, + req: { transactionID } + }); + for (const { id: tagId, tenant } of docs) { + const tenantId = getId(tenant); + // Check if the page already exists + const { docs } = await payload.find({ + collection: 'pages', + where: { slug: { equals: fileAreaSlug }, tenant: { in: [tenantId] } }, + depth: 0, + limit: 1, + req: { transactionID } + }); + let pageId = docs.length ? docs[0]?.id : 0; + + // Create a file area page for the tenant when missing + if (!pageId) { + const pageOrId = await ensurePage(payload, transactionID, { + name: 'File area', + slug: fileAreaSlug, + layout: [ + { + blockType: 'file-area', + tags: [tagId], + files: null + } as FileAreaBlock + ], + tenant + }); + if (typeof pageOrId === 'object') { + payload.logger.info( + `[SEED] Page '${pageOrId.slug}' on tenant #${tenantId} (custom seed)` + ); + } + pageId = getId(pageOrId); + } + + // Add the page to navigation when missing + const { items } = await ensureNavigation(payload, transactionID, { + items: [{ reference: { relationTo: 'pages', value: pageId } }], + tenant + }); + for (const { reference } of items) { + const refId = getId(reference.value); + payload.logger.info( + `[SEED] Navigation to '${reference.relationTo}' #${refId} on tenant #${tenantId} (custom seed)` + ); + } + } +}; diff --git a/libs/app-cms/feature/seed/src/lib/local-api/ensure-category.ts b/libs/app-cms/feature/seed/src/lib/local-api/ensure-category.ts index 31529b2ab..b91f1bdba 100644 --- a/libs/app-cms/feature/seed/src/lib/local-api/ensure-category.ts +++ b/libs/app-cms/feature/seed/src/lib/local-api/ensure-category.ts @@ -16,7 +16,7 @@ export async function ensureCategory( payload: Payload, transactionID: string | number | undefined, data: CategoryData -): Promise { +): Promise { const { name, slug, tenant } = data; // Check if the category exists with the given slug and tenant diff --git a/libs/app-cms/feature/seed/src/lib/local-api/ensure-media.ts b/libs/app-cms/feature/seed/src/lib/local-api/ensure-media.ts new file mode 100644 index 000000000..0b5f312cf --- /dev/null +++ b/libs/app-cms/feature/seed/src/lib/local-api/ensure-media.ts @@ -0,0 +1,94 @@ +import { existsSync } from 'fs'; + +import { getId } from '@codeware/app-cms/util/misc'; +import type { Media } from '@codeware/shared/util/payload-types'; +import { fetchFileByURL } from '@codeware/shared/util/ui'; +import type { File, Payload } from 'payload'; + +export type MediaData = Pick & { + filename: string; + filePath: string; +}; + +/** + * Ensure that a media file exists with the given filename from the file path. + * + * It's assumed that seeded file names are not reused across file types. + * Otherwise images could be seeded multiple times and thus create duplicates. + * + * @param payload - Payload instance + * @param transactionID - Transaction ID when supported by the database + * @param data - Media data + * @returns The created media file or the id if the media file exists + */ +export async function ensureMedia( + payload: Payload, + transactionID: string | number | undefined, + data: MediaData +): Promise { + let remoteFile: File | undefined = undefined; + let localFile: string | undefined = undefined; + + const { alt, external, filename, filePath, tags, tenant } = data; + + // Remote files are uploaded as buffers and local files are absolute filesystem paths + if (filePath.match(/^http/)) { + const file = await fetchFileByURL(filePath, 'nullOnError'); + if (file) { + remoteFile = { + name: file.name, + data: Buffer.from(await file.arrayBuffer()), + mimetype: file.type, + size: file.size + }; + } + } else { + localFile = filePath; + if (!existsSync(localFile)) { + throw new Error(`Media file does not exist: ${localFile}`); + } + } + + if (!remoteFile && !localFile) { + throw new Error(`Media file could not be resolved: ${filePath}`); + } + + // File names should be unique by design + const filenameWithoutExtension = filename.replace(/\.[^/.]+$/, ''); + + // Check if the media file exists with the given filename and tenant + const media = await payload.find({ + collection: 'media', + where: { + and: [ + { filename: { contains: filenameWithoutExtension } }, + tenant ? { tenant: { in: [getId(tenant)] } } : {} + ] + }, + depth: 0, + limit: 1, + req: { transactionID } + }); + + if (media.totalDocs) { + return media.docs[0].id; + } + + // No media file found, create one by uploading the local file + + const mediaFile = await payload.create({ + collection: 'media', + data: { + alt, + external, + filename, + tags, + tenant + }, + file: remoteFile, + filePath: localFile, + req: { transactionID } + }); + + return mediaFile; +} diff --git a/libs/app-cms/feature/seed/src/lib/local-api/ensure-navigation.ts b/libs/app-cms/feature/seed/src/lib/local-api/ensure-navigation.ts index d87699f9b..0ab317424 100644 --- a/libs/app-cms/feature/seed/src/lib/local-api/ensure-navigation.ts +++ b/libs/app-cms/feature/seed/src/lib/local-api/ensure-navigation.ts @@ -14,27 +14,35 @@ export type NavigationData = NonNullable> & { /** * Ensure that navigation items exist for the given tenant. * + * Navigation collection is handled like a global via multi-tenant plugin. + * This means each tenant has one row with all the items defined. + * * Navigation labels is limited to the document title only. * - * Navigation will not be updated if atleast one item exist, - * though the items do not match the given data. + * Navigation is updated when provided data contains new items. + * Existing items will be kept unchanged and nothing will be removed. * * @param payload - Payload instance * @param transactionID - Transaction ID when supported by the database * @param data - Navigation data - * @returns The navigation ID if exists or the object when created, otherwise undefined + * @returns The navigation ID if exists or the object when created, otherwise undefined, with the items that was added. */ export async function ensureNavigation( payload: Payload, transactionID: string | number | undefined, data: NavigationData -): Promise { - const { items, tenant } = data; +): Promise<{ + navigation: Navigation | number; + items: Array; +}> { + const { items: dataItems, tenant } = data; if (!tenant) { throw new Error('Tenant is required'); } + let itemsToAdd: Array = []; + // Check if the navigation exists with the given tenant const navigations = await payload.find({ collection: 'navigation', @@ -47,26 +55,60 @@ export async function ensureNavigation( }); if (navigations.totalDocs) { - const { items, id } = navigations.docs[0]; + const { items, id: docId } = navigations.docs[0]; if (items?.length) { - return id; + // Add the missing items + const missingItems = dataItems.filter( + ({ reference }) => + !items.some( + (item) => + item.reference.relationTo === reference.relationTo && + getId(item.reference.value) === getId(reference.value) + ) + ); + if (missingItems.length) { + // Merge current items with the new ones + itemsToAdd = items + .concat(missingItems) + .map(({ customLabel, id, reference }) => ({ + id, + customLabel, + reference, + labelSource: 'document' + })); + + await payload.update({ + collection: 'navigation', + id: docId, + data: { + items: itemsToAdd + }, + req: { transactionID } + }); + } + return { + navigation: docId, + items: missingItems + }; } } - // No navigation found, create one + // No navigation found, create one with data items + + itemsToAdd = dataItems.map(({ reference }) => ({ + reference, + labelSource: 'document' + })); const navigation = await payload.create({ collection: 'navigation', data: { - items: items.map(({ reference }) => ({ - reference, - labelSource: 'document' - })), + items: itemsToAdd, tenant }, req: { transactionID } }); - return navigation; + return { navigation, items: itemsToAdd }; } diff --git a/libs/app-cms/feature/seed/src/lib/local-api/ensure-page.ts b/libs/app-cms/feature/seed/src/lib/local-api/ensure-page.ts index 03680a353..b7d639dd4 100644 --- a/libs/app-cms/feature/seed/src/lib/local-api/ensure-page.ts +++ b/libs/app-cms/feature/seed/src/lib/local-api/ensure-page.ts @@ -21,7 +21,7 @@ export async function ensurePage( payload: Payload, transactionID: string | number | undefined, data: PageData -): Promise { +): Promise { const { header, layout, name, slug, tenant } = data; // Check if the page exists with the given slug and tenant diff --git a/libs/app-cms/feature/seed/src/lib/local-api/ensure-post.ts b/libs/app-cms/feature/seed/src/lib/local-api/ensure-post.ts index 926d2dc26..bcf384e56 100644 --- a/libs/app-cms/feature/seed/src/lib/local-api/ensure-post.ts +++ b/libs/app-cms/feature/seed/src/lib/local-api/ensure-post.ts @@ -20,7 +20,7 @@ export async function ensurePost( payload: Payload, transactionID: string | number | undefined, data: PostData -): Promise { +): Promise { const { authors, categories, content, slug, tenant, title } = data; // Check if the post exists with the given slug diff --git a/libs/app-cms/feature/seed/src/lib/local-api/ensure-site-setting.ts b/libs/app-cms/feature/seed/src/lib/local-api/ensure-site-setting.ts index 8601b1d94..5be8cdb75 100644 --- a/libs/app-cms/feature/seed/src/lib/local-api/ensure-site-setting.ts +++ b/libs/app-cms/feature/seed/src/lib/local-api/ensure-site-setting.ts @@ -18,7 +18,7 @@ export async function ensureSiteSetting( payload: Payload, transactionID: string | number | undefined, data: SiteSettingData -): Promise { +): Promise { const { general: generalFromProps, tenant } = data; if (!tenant) { diff --git a/libs/app-cms/feature/seed/src/lib/local-api/ensure-tag.ts b/libs/app-cms/feature/seed/src/lib/local-api/ensure-tag.ts new file mode 100644 index 000000000..6923295bf --- /dev/null +++ b/libs/app-cms/feature/seed/src/lib/local-api/ensure-tag.ts @@ -0,0 +1,56 @@ +import { getId } from '@codeware/app-cms/util/misc'; +import type { Tag } from '@codeware/shared/util/payload-types'; +import type { Payload } from 'payload'; + +export type TagData = Pick & { + slug: string; +}; + +/** + * Ensure that a tag exists with the given slug. + * + * @param payload - Payload instance + * @param transactionID - Transaction ID when supported by the database + * @param data - Tag data + * @returns The created tag or the id if the tag exists + */ +export async function ensureTag( + payload: Payload, + transactionID: string | number | undefined, + data: TagData +): Promise { + const { brand, name, slug, tenant } = data; + + // Check if the tag exists with the given slug and tenant + const tags = await payload.find({ + collection: 'tags', + where: { + and: [ + { slug: { equals: slug } }, + tenant ? { tenant: { in: [getId(tenant)] } } : {} + ] + }, + depth: 0, + limit: 1, + req: { transactionID } + }); + + if (tags.totalDocs) { + return tags.docs[0].id; + } + + // No tag found, create one + + const tag = await payload.create({ + collection: 'tags', + data: { + brand, + name, + slug, + tenant + }, + req: { transactionID } + }); + + return tag; +} diff --git a/libs/app-cms/feature/seed/src/lib/local-api/ensure-tenant.ts b/libs/app-cms/feature/seed/src/lib/local-api/ensure-tenant.ts index e9819308d..7508ddfca 100644 --- a/libs/app-cms/feature/seed/src/lib/local-api/ensure-tenant.ts +++ b/libs/app-cms/feature/seed/src/lib/local-api/ensure-tenant.ts @@ -23,7 +23,7 @@ export async function ensureTenant( payload: Payload, transactionID: string | number | undefined, data: TenantData -): Promise { +): Promise { const { apiKey, description, domains, name } = data; // Check if the tenant exists with the given name and domains (ignore page types) diff --git a/libs/app-cms/feature/seed/src/lib/local-api/ensure-user.ts b/libs/app-cms/feature/seed/src/lib/local-api/ensure-user.ts index 87c54bbda..e54d8581c 100644 --- a/libs/app-cms/feature/seed/src/lib/local-api/ensure-user.ts +++ b/libs/app-cms/feature/seed/src/lib/local-api/ensure-user.ts @@ -18,7 +18,7 @@ export async function ensureUser( payload: Payload, transactionID: string | number | undefined, data: UserData -): Promise { +): Promise { const { description, email, name, password, role, tenants } = data; // Check if the user exists with the given email diff --git a/libs/app-cms/feature/seed/src/lib/seed-types.ts b/libs/app-cms/feature/seed/src/lib/seed-types.ts index d86384baf..b0d256d6e 100644 --- a/libs/app-cms/feature/seed/src/lib/seed-types.ts +++ b/libs/app-cms/feature/seed/src/lib/seed-types.ts @@ -1,13 +1,17 @@ import type { CategoryLookup, + SeedOptions, + TagLookup, TenantLookup, UserLookup } from '@codeware/shared/util/seed'; import type { Prettify } from '@codeware/shared/util/typesafe'; import type { CategoryData } from './local-api/ensure-category'; +import type { MediaData } from './local-api/ensure-media'; import type { PageData } from './local-api/ensure-page'; import type { PostData } from './local-api/ensure-post'; +import type { TagData } from './local-api/ensure-tag'; import type { TenantData } from './local-api/ensure-tenant'; import type { UserData } from './local-api/ensure-user'; export type SeedEnvironment = 'development' | 'preview' | 'production'; @@ -18,6 +22,12 @@ type CategoryDataLookup = Prettify< tenant: Pick; } >; +type MediaDataLookup = Prettify< + Omit & { + tags: Array; + tenant: Pick; + } +>; type PostDataLookup = Prettify< Omit & { authors: Array; @@ -32,13 +42,19 @@ type PageDataLookup = Prettify< tenant: Pick; } >; +type TagDataLookup = Prettify< + Omit & { + brand: NonNullable; + slug: string; + tenant: Pick; + } +>; type UserDataLookup = Prettify< Omit & { tenants: Array; password: string; } >; - type TenantDataLookup = Prettify< Omit & { apiKey: string } >; @@ -46,8 +62,10 @@ type TenantDataLookup = Prettify< // TODO: infer from seedDataSchema when it's refactored export type SeedData = { categories: Array; + media: Array; pages: Array; posts: Array; + tags: Array; tenants: Array; users: Array; }; @@ -92,6 +110,11 @@ export type SeedRules = { */ tenantCategories?: ItemsRange; + /** + * Number of tags to generate per tenant. + */ + tenantTags?: ItemsRange; + /** * Number of posts to generate per tenant. */ @@ -115,4 +138,4 @@ export type StaticSeedOptions = { * Defines the amount of data to generate. */ seedRules?: SeedRules; -}; +} & Pick; diff --git a/libs/app-cms/feature/seed/src/lib/seed.ts b/libs/app-cms/feature/seed/src/lib/seed.ts index 09ca35746..efd8818b6 100644 --- a/libs/app-cms/feature/seed/src/lib/seed.ts +++ b/libs/app-cms/feature/seed/src/lib/seed.ts @@ -7,14 +7,21 @@ import type { Payload } from 'payload'; import { loadInfisicalData } from './load-infisical-data'; import { loadStaticData } from './load-static-data'; +import { customSeed } from './local-api/custom-seed'; import { ensureCategory } from './local-api/ensure-category'; +import { ensureMedia } from './local-api/ensure-media'; import { ensureNavigation } from './local-api/ensure-navigation'; import { ensurePage } from './local-api/ensure-page'; import { ensurePost } from './local-api/ensure-post'; import { ensureSiteSetting } from './local-api/ensure-site-setting'; +import { ensureTag } from './local-api/ensure-tag'; import { ensureTenant } from './local-api/ensure-tenant'; import { ensureUser } from './local-api/ensure-user'; -import type { SeedData, SeedEnvironment } from './seed-types'; +import type { + SeedData, + SeedEnvironment, + StaticSeedOptions +} from './seed-types'; import { convertMarkdownToLexical } from './utils/convert-markdown-to-lexical'; import { tempStore } from './utils/temp-store'; @@ -33,13 +40,15 @@ import { tempStore } from './utils/temp-store'; * @returns `true` if seeding was successful or skipped for a reason, otherwise `false` * @throws Never - just logs errors */ -export const seed = async (args: { - environment: SeedEnvironment; - payload: Payload; - source: SeedSource; - strategy: SeedStrategy; -}): Promise => { - const { environment, payload, source, strategy } = args; +export const seed = async ( + args: { + environment: SeedEnvironment; + payload: Payload; + source: SeedSource; + strategy: SeedStrategy; + } & Pick +): Promise => { + const { environment, payload, remoteDataUrl, source, strategy } = args; // Support transactions let transactionID: string | number | undefined; @@ -103,7 +112,7 @@ export const seed = async (args: { // Try to load seed data from Infisical when source has cloud if (source === 'cloud' || source === 'cloud-local') { - seedData = await loadInfisicalData(args); + seedData = await loadInfisicalData({ environment, payload }); } // !! Production guard !! // @@ -121,12 +130,20 @@ export const seed = async (args: { payload.logger.info( '[SEED] Could not load secrets from cloud, fallback to local data' ); - seedData = loadStaticData(args); + seedData = loadStaticData({ + environment, + payload, + options: { remoteDataUrl } + }); } // Still no seed data, which is expected for local only, so get it if (!seedData && source === 'local') { - seedData = loadStaticData(args); + seedData = loadStaticData({ + environment, + payload, + options: { remoteDataUrl } + }); } // Check seed data is loaded @@ -297,6 +314,59 @@ export const seed = async (args: { seedError = seedError || categoryFailed > 0; } + // TAGS + + if (seedData.tags.length > 0) { + let tagFailed = 0; + + for (const tag of seedData.tags) { + const [entity] = tempStore.lookupTenant(payload, [tag.tenant]); + + try { + const response = await ensureTag(payload, transactionID, { + brand: tag.brand, + name: tag.name, + slug: tag.slug, + tenant: entity.tenant + }); + + let tagId: number; + if (typeof response === 'object') { + payload.logger.info( + `[SEED] Tag '${tag.slug}' on tenant #${entity.tenant}` + ); + tagId = response.id; + } else { + tagId = Number(response); + } + // Save tag to map to lookup id's later + tempStore.storeTag( + { apiKey: tag.tenant.lookupApiKey, slug: tag.slug }, + tagId + ); + } catch (e) { + const error = e as Error; + payload.logger.error(error.message); + if ('data' in error) { + payload.logger.error( + `Tag '${tag.slug}'\n${JSON.stringify(error.data, null, 2)}` + ); + } + tagFailed++; + } + } + const { totalDocs: tagCount } = await payload.count({ + collection: 'tags', + req: { transactionID } + }); + payload.logger.info( + tagFailed + ? `[SEED] Problem occurred for ${tagFailed}/${seedData.tags.length} tags (count: ${tagCount})` + : `[SEED] >> Tags up to date (count: ${tagCount})` + ); + seedError = seedError || tagFailed > 0; + } + // PAGES if (seedData.pages.length > 0) { @@ -364,9 +434,75 @@ export const seed = async (args: { seedError = seedError || pageFailed > 0; } - // Need to commit since posts require previous seed data + // !! COMMIT POINT !! + // Need to commit since the collections that follow require previous seed data await endTransaction(seedError ? 'rollback' : 'commit'); + // MEDIA + + // Only seed media when no errors occurred + if (!seedError && seedData.media.length > 0) { + await ensureTransaction(); + + let mediaFailed = 0; + + for (const media of seedData.media) { + const tags = tempStore.lookupTag( + payload, + media.tags.map(({ lookupSlug }) => ({ + apiKey: media.tenant.lookupApiKey, + slug: lookupSlug + })) + ); + const [entity] = tempStore.lookupTenant(payload, [media.tenant]); + + try { + const response = await ensureMedia(payload, transactionID, { + alt: media.alt, + external: media.external, + filename: media.filename, + filePath: media.filePath, + tags, + tenant: entity.tenant + }); + + let mediaId: number; + if (typeof response === 'object') { + payload.logger.info( + `[SEED] Media '${media.filename}' on tenant #${entity.tenant}` + ); + mediaId = response.id; + } else { + mediaId = Number(response); + } + // Save media to map to lookup id's later + tempStore.storeMedia( + { apiKey: media.tenant.lookupApiKey, slug: media.filename }, + mediaId + ); + } catch (e) { + const error = e as Error; + payload.logger.error(error.message); + if ('data' in error) { + payload.logger.error( + `Media '${media.filePath}'\n${JSON.stringify(error.data, null, 2)}` + ); + } + mediaFailed++; + } + } + const { totalDocs: mediaCount } = await payload.count({ + collection: 'media', + req: { transactionID } + }); + payload.logger.info( + mediaFailed + ? `[SEED] Problem occurred for ${mediaFailed}/${seedData.media.length} media (count: ${mediaCount})` + : `[SEED] >> Media up to date (count: ${mediaCount})` + ); + seedError = seedError || mediaFailed > 0; + } + // POSTS // Only seed posts when no errors occurred @@ -429,7 +565,7 @@ export const seed = async (args: { // NAVIGATION - // Create navigation for each tenant + // Create navigation for each tenant using the first 5 tenant pages excluding the home page if (seedData.tenants.length > 0) { await ensureTransaction(); @@ -455,16 +591,20 @@ export const seed = async (args: { ); try { - const response = await ensureNavigation(payload, transactionID, { - items: pageIds.map((id) => ({ - reference: { relationTo: 'pages', value: id } - })), - tenant: tenantEntity.tenant - }); + const { navigation, items } = await ensureNavigation( + payload, + transactionID, + { + items: pageIds.map((id) => ({ + reference: { relationTo: 'pages', value: id } + })), + tenant: tenantEntity.tenant + } + ); - if (typeof response === 'object') { + if (typeof navigation === 'object') { payload.logger.info( - `[SEED] Navigation for tenant '${tenant.apiKey}' created` + `[SEED] Navigation with ${items.length} items for tenant '${tenant.apiKey}' created` ); } } catch (e) { @@ -550,6 +690,25 @@ export const seed = async (args: { seedError = seedError || siteSettingFailed > 0; } + // CUSTOM SEED + + // Only run when no errors occurred + if (!seedError) { + await ensureTransaction(); + try { + await customSeed(payload, transactionID); + } catch (e) { + const error = e as Error; + payload.logger.error(error.message); + if ('data' in error) { + payload.logger.error( + `Custom seed error\n${JSON.stringify(error.data, null, 2)}` + ); + } + seedError = true; + } + } + await endTransaction(seedError ? 'rollback' : 'commit'); if (seedError) { diff --git a/libs/app-cms/feature/seed/src/lib/utils/generate-tags.ts b/libs/app-cms/feature/seed/src/lib/utils/generate-tags.ts new file mode 100644 index 000000000..c9a8a7473 --- /dev/null +++ b/libs/app-cms/feature/seed/src/lib/utils/generate-tags.ts @@ -0,0 +1,49 @@ +import { capitalize } from '@codeware/shared/util/pure'; +import { tailwind } from '@codeware/shared/util/tailwind'; +import { randNumber } from '@ngneat/falso'; + +import { SeedData, SeedRules } from '../seed-types'; + +// Filter out black and white colors since they have no range +const colorNames = tailwind.names.filter( + (name) => name.match(/black|white/) === null +); + +// Return random Tailwind colors +const randColors = (length: number) => + Array.from( + { length }, + () => colorNames[randNumber({ min: 0, max: colorNames.length - 1 })] + ); + +/** + * Generate tags seed data for tenants. + */ +export const generateTags = ( + range: NonNullable, + tenants: SeedData['tenants'] +): SeedData['tags'] => { + return tenants.reduce( + (tags, tenant) => { + const length = randNumber({ + min: range.min, + max: range.max + }); + + // Generate tags for tenant + const tenantTags: SeedData['tags'] = randColors(length).map((name) => ({ + name: capitalize(name), + slug: name, + brand: { + color: `${name}-500`, + icon: 'TagIcon' // https://heroicons.com + }, + tenant: { lookupApiKey: tenant.apiKey } + })); + tags.push(...tenantTags); + + return tags; + }, + [] as SeedData['tags'] + ); +}; diff --git a/libs/app-cms/feature/seed/src/lib/utils/temp-store.ts b/libs/app-cms/feature/seed/src/lib/utils/temp-store.ts index 7d4bea60a..990857e45 100644 --- a/libs/app-cms/feature/seed/src/lib/utils/temp-store.ts +++ b/libs/app-cms/feature/seed/src/lib/utils/temp-store.ts @@ -11,9 +11,15 @@ const mapper = { // Map to category id (unique per tenant) category: new Map(), + // Map to media id (unique per tenant) + media: new Map(), + // Map to page id (unique per tenant) page: new Map(), + // Map to tag id (unique per tenant) + tag: new Map(), + // Map api key to tenant id (unique across tenants) tenant: new Map(), @@ -46,6 +52,25 @@ export const tempStore = { return lookupCategory(payload, categories); }, + /** + * Store media to map. + * + * @param media - The media to store. + * @param mediaId - The id of the media. + */ + storeMedia: (media: MapKey, mediaId: number) => { + mapper.media.set(JSON.stringify(media), mediaId); + }, + /** + * Lookup media id's. + * + * @param payload - The payload instance. + * @param media - The media to lookup. + */ + lookupMedia: (payload: Payload, media: Array) => { + return lookupMedia(payload, media); + }, + /** * Store page to map. * @@ -65,6 +90,25 @@ export const tempStore = { return lookupPage(payload, pages); }, + /** + * Store tag to map. + * + * @param tag - The tag to store. + * @param tagId - The id of the tag. + */ + storeTag: (tag: MapKey, tagId: number) => { + mapper.tag.set(JSON.stringify(tag), tagId); + }, + /** + * Lookup tag id's. + * + * @param payload - The payload instance. + * @param tags - The tags to lookup. + */ + lookupTag: (payload: Payload, tags: Array) => { + return lookupTag(payload, tags); + }, + /** * Store tenant api key and tenant id in map. * @@ -132,6 +176,20 @@ function lookupCategory( }, [] as Array); } +function lookupMedia(payload: Payload, media: Array): Array { + return media.reduce((acc, media) => { + const mediaId = mapper.category.get(JSON.stringify(media)); + if (!mediaId) { + payload.logger.error( + `Skip: Media '${media.slug}' for tenant '${media.apiKey}' not found` + ); + return acc; + } + acc.push(mediaId); + return acc; + }, [] as Array); +} + function lookupPage(payload: Payload, pages: Array): Array { return pages.reduce((acc, page) => { const pageId = mapper.page.get(JSON.stringify(page)); @@ -146,6 +204,20 @@ function lookupPage(payload: Payload, pages: Array): Array { }, [] as Array); } +function lookupTag(payload: Payload, tags: Array): Array { + return tags.reduce((acc, tag) => { + const tagId = mapper.tag.get(JSON.stringify(tag)); + if (!tagId) { + payload.logger.error( + `Skip: Tag '${tag.slug}' for tenant '${tag.apiKey}' not found` + ); + return acc; + } + acc.push(tagId); + return acc; + }, [] as Array); +} + function lookupUser(payload: Payload, users: Array): Array { return users.reduce((acc, user) => { const userId = mapper.user.get(user.lookupEmail); diff --git a/libs/app-cms/ui/blocks/src/index.ts b/libs/app-cms/ui/blocks/src/index.ts index c91a8ab7f..0f7c10f1b 100644 --- a/libs/app-cms/ui/blocks/src/index.ts +++ b/libs/app-cms/ui/blocks/src/index.ts @@ -1,8 +1,11 @@ export { cardBlock } from './lib/card/card.block'; export { codeBlock } from './lib/code/code.block'; export { contentBlock } from './lib/content/content.block'; +export { fileAreaBlock } from './lib/file-area/file-area.block'; export { formBlock } from './lib/form/form.block'; +export { imageBlock } from './lib/image/image.block'; export { mediaBlock } from './lib/media/media.block'; export { reusableContentBlock } from './lib/reusable-content/reusable-content.block'; export { socialMediaBlock } from './lib/social-media/social-media.block'; export { spacingBlock } from './lib/spacing/spacing.block'; +export { videoBlock } from './lib/video/video.block'; diff --git a/libs/app-cms/ui/blocks/src/lib/content/content.block.ts b/libs/app-cms/ui/blocks/src/lib/content/content.block.ts index 8ae4e50aa..0f85dd252 100644 --- a/libs/app-cms/ui/blocks/src/lib/content/content.block.ts +++ b/libs/app-cms/ui/blocks/src/lib/content/content.block.ts @@ -13,12 +13,15 @@ const blocks: Record = { card: true, code: true, form: true, + image: true, media: true, 'social-media': true, spacing: true, // Unsupported blocks content: false, - 'reusable-content': false + 'file-area': false, + 'reusable-content': false, + video: false }; /** diff --git a/libs/app-cms/ui/blocks/src/lib/file-area/compose-files-field.hook.ts b/libs/app-cms/ui/blocks/src/lib/file-area/compose-files-field.hook.ts new file mode 100644 index 000000000..782ba8b2b --- /dev/null +++ b/libs/app-cms/ui/blocks/src/lib/file-area/compose-files-field.hook.ts @@ -0,0 +1,48 @@ +import type { FileAreaBlock } from '@codeware/shared/util/payload-types'; +import type { FieldHook, TypeWithID, Where } from 'payload'; + +type TypeWithTenant = TypeWithID & { + tenant: string; +}; + +export const composeFilesHook: FieldHook< + TypeWithTenant, // collection type + FileAreaBlock['files'], // the virtual compose field type + FileAreaBlock // block/sibling type +> = async ({ + operation, + req: { payload }, + data, + siblingData: { tags }, + value +}) => { + if (operation !== 'read') { + return value; + } + + if (!tags?.length) { + return []; + } + + // Query on tenant just in case, though the selected tags should be tenant scoped + const tenantQuery: Where = data?.tenant + ? { tenant: { equals: data.tenant } } + : {}; + + const res = await payload.find({ + collection: 'media', + select: { tenant: false }, + where: { and: [{ tags: { in: tags } }, tenantQuery] }, + depth: 0, + pagination: false + }); + + if (res.totalDocs) { + value = res.docs.map((media) => ({ + media, + id: String(media.id) + })); + } + + return value; +}; diff --git a/libs/app-cms/ui/blocks/src/lib/file-area/file-area.block.ts b/libs/app-cms/ui/blocks/src/lib/file-area/file-area.block.ts new file mode 100644 index 000000000..f57dbd072 --- /dev/null +++ b/libs/app-cms/ui/blocks/src/lib/file-area/file-area.block.ts @@ -0,0 +1,44 @@ +import { virtualComposeField } from '@codeware/app-cms/ui/fields'; +import type { Block } from 'payload'; + +import { composeFilesHook } from './compose-files-field.hook'; + +/** + * File area block to select which media collection files to show in the file area. + */ +export const fileAreaBlock: Block = { + slug: 'file-area', + interfaceName: 'FileAreaBlock', + fields: [ + { + name: 'tags', + type: 'relationship', + relationTo: 'tags', + label: { + en: 'Tags', + sv: 'Etiketter' + }, + hasMany: true, + admin: { + description: { + en: 'Select tags that represent the files to show in the file area.', + sv: 'Välj etiketter som representerar de filer som ska visas i filområdet.' + } + } + }, + virtualComposeField({ + field: { + name: 'files', + type: 'array', + fields: [ + { + name: 'media', + type: 'relationship', + relationTo: 'media' + } + ] + }, + value: composeFilesHook + }) + ] +}; diff --git a/libs/app-cms/ui/blocks/src/lib/image/image.block.ts b/libs/app-cms/ui/blocks/src/lib/image/image.block.ts new file mode 100644 index 000000000..19c2829ff --- /dev/null +++ b/libs/app-cms/ui/blocks/src/lib/image/image.block.ts @@ -0,0 +1,28 @@ +import type { Block } from 'payload'; + +/** + * Image block for displaying an image from media collection. + * + * Exposes the `ImageBlock` interface. + */ +export const imageBlock: Block = { + slug: 'image', + interfaceName: 'ImageBlock', + fields: [ + { + name: 'media', + type: 'upload', + relationTo: 'media', + filterOptions: { + mimeType: { contains: 'image' } + }, + admin: { + description: { + en: 'Select an image.', + sv: 'Välj en bild.' + } + }, + required: true + } + ] +}; diff --git a/libs/app-cms/ui/blocks/src/lib/media/media.block.ts b/libs/app-cms/ui/blocks/src/lib/media/media.block.ts index d7a69ac11..6c17e8572 100644 --- a/libs/app-cms/ui/blocks/src/lib/media/media.block.ts +++ b/libs/app-cms/ui/blocks/src/lib/media/media.block.ts @@ -4,6 +4,8 @@ import type { Block } from 'payload'; * Media block for displaying an image media element. * * Note! Currently only images are supported. + * + * @deprecated This block is replaced with `imageBlock` and `videoBlock` and will be removed in the near future. */ export const mediaBlock: Block = { slug: 'media', diff --git a/libs/app-cms/ui/blocks/src/lib/video/video.block.ts b/libs/app-cms/ui/blocks/src/lib/video/video.block.ts new file mode 100644 index 000000000..2c2041002 --- /dev/null +++ b/libs/app-cms/ui/blocks/src/lib/video/video.block.ts @@ -0,0 +1,28 @@ +import type { Block } from 'payload'; + +/** + * Video block for displaying a video from media collection. + * + * Exposes the `VideoBlock` interface. + */ +export const videoBlock: Block = { + slug: 'video', + interfaceName: 'VideoBlock', + fields: [ + { + name: 'media', + type: 'upload', + relationTo: 'media', + filterOptions: { + mimeType: { contains: 'video' } + }, + admin: { + description: { + en: 'Select a video.', + sv: 'Välj en video.' + } + }, + required: true + } + ] +}; diff --git a/libs/app-cms/ui/components/src/index.ts b/libs/app-cms/ui/components/src/index.ts index f83892ee6..dc9840e9f 100644 --- a/libs/app-cms/ui/components/src/index.ts +++ b/libs/app-cms/ui/components/src/index.ts @@ -1,2 +1,3 @@ +export * from './lib/Callout'; export * from './lib/Logo.client'; export * from './lib/VerifyTenantDomain'; diff --git a/libs/app-cms/ui/components/src/lib/Callout.tsx b/libs/app-cms/ui/components/src/lib/Callout.tsx new file mode 100644 index 000000000..aa3b879fe --- /dev/null +++ b/libs/app-cms/ui/components/src/lib/Callout.tsx @@ -0,0 +1,25 @@ +import { + Callout as CalloutComponent, + type CalloutProps +} from '@codeware/shared/ui/react-universal-components'; +import type { ServerProps } from 'payload'; + +/** + * Server component to display a callout. + * @props `CalloutProps` - Props for the Callout component. + */ +export function Callout({ + kind, + description, + title +}: ServerProps & CalloutProps) { + if (!description?.length) { + console.warn('Callout: No description provided'); + return null; + } + return ( + + ); +} + +export default Callout; diff --git a/libs/app-cms/ui/fields/src/index.ts b/libs/app-cms/ui/fields/src/index.ts index dc58284cf..ba9622f0e 100644 --- a/libs/app-cms/ui/fields/src/index.ts +++ b/libs/app-cms/ui/fields/src/index.ts @@ -1,6 +1,10 @@ export { defaultLexical } from './lib/default-lexical'; +export { calloutField } from './lib/callout/callout.field'; export { codeField } from './lib/code/code.field'; export { colorPickerField } from './lib/color-picker/color-picker.field'; export { iconPickerField } from './lib/icon-picker/icon-picker.field'; export { linkGroupField } from './lib/link-group/link-group.field'; +export { mediaUploadField } from './lib/media-upload/media-upload.field'; export { slugField } from './lib/slug/slug.field'; +export { tagsSelectField } from './lib/tags-select/tags-select.field'; +export { virtualComposeField } from './lib/virtual-compose/virtual-compose.field'; diff --git a/libs/app-cms/ui/fields/src/lib/callout/CalloutField.client.tsx b/libs/app-cms/ui/fields/src/lib/callout/CalloutField.client.tsx new file mode 100644 index 000000000..e594f5156 --- /dev/null +++ b/libs/app-cms/ui/fields/src/lib/callout/CalloutField.client.tsx @@ -0,0 +1,62 @@ +'use client'; + +import { + HeroIcon, + HeroIconName +} from '@codeware/shared/ui/react-universal-components'; +import { + Alert, + AlertDescription, + AlertTitle +} from '@codeware/shared/ui/shadcn/components/alert'; +import { TailwindColor } from '@codeware/shared/util/tailwind'; +import type { UIFieldClientProps } from 'payload'; + +import type { CalloutFieldProps } from './callout.field'; + +const iconMap: Record< + NonNullable, + { icon: HeroIconName; color: TailwindColor } +> = { + info: { + icon: 'InformationCircleIcon', + color: 'blue-500' + }, + warning: { + icon: 'ExclamationCircleIcon', + color: 'orange-500' + }, + tip: { + icon: 'LightBulbIcon', + color: 'purple-500' + } +}; + +/** + * Callout field component for client-side rendering. + * + * Displays a callout field with a title and content. + */ +export const CalloutField: React.FC = ({ + description, + kind, + title +}) => { + return ( +
+ + {kind && ( + + )} + {title && {title}} + + {description.map((line, index) => ( +

{line}

+ ))} +
+
+
+ ); +}; + +export default CalloutField; diff --git a/libs/app-cms/ui/fields/src/lib/callout/callout.field.ts b/libs/app-cms/ui/fields/src/lib/callout/callout.field.ts new file mode 100644 index 000000000..3e048e8e5 --- /dev/null +++ b/libs/app-cms/ui/fields/src/lib/callout/callout.field.ts @@ -0,0 +1,42 @@ +import { HeroIconName } from '@codeware/shared/ui/react-universal-components'; +import { deepMerge } from '@codeware/shared/util/pure'; +import { ExtractTypes } from '@codeware/shared/util/typesafe'; +import type { UIField } from 'payload'; + +export type CalloutFieldProps = { + description: Array; + kind?: 'info' | 'warning' | 'tip'; + title?: string; +}; + +type Props = { + /** Custom field component properties */ + props: CalloutFieldProps; + + /** Override field configuration with selected properties */ + override?: Partial; +}; + +/** + * Callout UI field for informational content. + */ +export const calloutField = ({ props, override }: Props): UIField => { + const field = deepMerge( + { + name: 'callout', + type: 'ui', + admin: { + components: { + Field: { + path: '@codeware/app-cms/ui/fields/callout/CalloutField.client', + clientProps: props + } + }, + disableListColumn: true + } + }, + override ?? {} + ); + + return field; +}; diff --git a/libs/app-cms/ui/fields/src/lib/media-upload/media-upload.field.ts b/libs/app-cms/ui/fields/src/lib/media-upload/media-upload.field.ts new file mode 100644 index 000000000..a4f2b35bb --- /dev/null +++ b/libs/app-cms/ui/fields/src/lib/media-upload/media-upload.field.ts @@ -0,0 +1,68 @@ +import { + type MimeTypeSlug, + getMimeTypes +} from '@codeware/app-cms/util/definitions'; +import { deepMerge } from '@codeware/shared/util/pure'; +import type { UploadField, Where } from 'payload'; + +type Options = { + /** + * Limit media to specific mime types. + */ + mimeTypeSlugs?: Array; + + /** + * The name of the field. + */ + name: string; + + /** + * Override properties which will be deep merged with the field. + */ + overrides?: Partial; + + /** + * Whether the field is required. + * @default false + */ + required?: boolean; +}; + +/** + * Media field that can be used to select or upload media files. + * + * Media files to select from can optionally be limited to specific mime types. + * + * @param options - Options for the field + */ +export const mediaUploadField = ({ + mimeTypeSlugs, + name, + overrides, + required = false +}: Options): UploadField => { + const field: UploadField = { + name, + type: 'upload', + relationTo: 'media', + required + }; + + if (mimeTypeSlugs && mimeTypeSlugs.length) { + const mimeTypes = getMimeTypes({ + limit: mimeTypeSlugs, + stripWildcard: true + }); + // Query for all provided mime types + const query: Where = { + or: [ + ...mimeTypes.map((mimeType) => ({ + mimeType: { contains: mimeType } + })) + ] + }; + field.filterOptions = query; + } + + return overrides ? deepMerge(field, overrides) : field; +}; diff --git a/libs/app-cms/ui/fields/src/lib/slug/slug.field.ts b/libs/app-cms/ui/fields/src/lib/slug/slug.field.ts index 88bd1ab93..52ef67e54 100644 --- a/libs/app-cms/ui/fields/src/lib/slug/slug.field.ts +++ b/libs/app-cms/ui/fields/src/lib/slug/slug.field.ts @@ -8,6 +8,8 @@ type Slug = (args: { sourceField: string; /** Whether the slug is required. Defaults to `false`. */ required?: boolean; + /** The maximum length of the slug. Defaults to no limit. */ + maxLength?: number; }) => Field; /** The name of the slug field */ @@ -20,11 +22,16 @@ export const slugName = 'slug' as const; * * Also assures that the slug is unique across the collection and tenant. */ -export const slugField: Slug = ({ sourceField, required = false }) => ({ +export const slugField: Slug = ({ + sourceField, + required = false, + maxLength +}) => ({ name: slugName, type: 'text', index: true, required, + maxLength, label: 'Slug', admin: { description: { diff --git a/libs/app-cms/ui/fields/src/lib/tags-select/tags-select.field.ts b/libs/app-cms/ui/fields/src/lib/tags-select/tags-select.field.ts new file mode 100644 index 000000000..89be76ff1 --- /dev/null +++ b/libs/app-cms/ui/fields/src/lib/tags-select/tags-select.field.ts @@ -0,0 +1,70 @@ +import { deepMerge } from '@codeware/shared/util/pure'; +import type { RelationshipField } from 'payload'; + +type Options = { + /** + * Whether to build an index for the field. + * + * Set this field to true if your users will perform queries + * on this field's data often. + * @default false + */ + buildIndex?: boolean; + + /** + * Limit tags to specific tag slugs. + */ + limitTags?: Array; + + /** + * Override properties which will be deep merged with the field. + */ + overrides?: Partial; + + /** + * Whether the field is required. + * @default false + */ + required?: boolean; +}; + +/** + * Tags field that can be used to select one or many tags. + * + * Opinionated configuration: + * - Field name is 'tags' + * - Multiple tags are allowed + * - Tags can be sorted using drag and drop + * - Label is provided + * + * **Tip!** + * + * For advanced selection of tags, override admin appearance to 'drawer'. + * + * @param options - Options for the field + */ +export const tagsSelectField = ({ + buildIndex = false, + limitTags = [], + overrides, + required = false +}: Options = {}): RelationshipField => { + const field: RelationshipField = { + name: 'tags', + type: 'relationship', + label: { en: 'Tags', sv: 'Etiketter' }, + relationTo: 'tags', + admin: { + isSortable: true + }, + hasMany: true, + index: buildIndex, + required + }; + + if (limitTags.length > 0) { + field.filterOptions = { slug: { in: limitTags } }; + } + + return overrides ? deepMerge(field, overrides) : field; +}; diff --git a/libs/app-cms/ui/fields/src/lib/virtual-compose/virtual-compose.field.ts b/libs/app-cms/ui/fields/src/lib/virtual-compose/virtual-compose.field.ts new file mode 100644 index 000000000..d3c65e585 --- /dev/null +++ b/libs/app-cms/ui/fields/src/lib/virtual-compose/virtual-compose.field.ts @@ -0,0 +1,70 @@ +import type { Field, FieldBase, FieldHook } from 'payload'; + +type Options = { + /** + * The field of choice to use. + * @see https://payloadcms.com/docs/fields/overview + */ + field: TField; + /** + * A hook function that should return the composed value of the field. + * @see https://payloadcms.com/docs/hooks/fields + */ + value: FieldHook; +}; + +/** + * Virtual compose field that is used to combine multiple fields from different collections + * into a field that is not stored in the database. + * + * At its core it's a virtual field, setup according to Payload best practices + * and with required hook function responsible for composing the field value. + * @see https://payloadcms.com/docs/fields/overview#virtual-fields + * + * **Tip!** + * + * The field is hidden by default in the admin panel but can be shown + * by setting the `admin.hidden` property to `false`. + * + * @param options The options for the virtual compose field. + * @returns Mutated `field` property. + */ +export const virtualComposeField = < + TField extends Field & { + access?: FieldBase['access']; + hooks?: FieldBase['hooks']; + virtual?: true; + } +>({ + field, + value: valueHookFn +}: Options): TField => { + return { + ...field, + virtual: true, + access: { + ...(field.access ?? {}), + create: () => false, + update: () => false + }, + admin: { + hidden: true, + ...(field.admin ?? {}) + }, + hooks: { + afterRead: [valueHookFn, ...(field.hooks?.afterRead ?? [])], + beforeChange: [ + // Ensures data is not stored in DB + ({ field: { name }, siblingData }) => { + if (name && name in siblingData) { + console.log( + `Virtual field ${name} is not stored in the database. Removing from siblingData.` + ); + delete siblingData[name]; + } + }, + ...(field.hooks?.beforeChange ?? []) + ] + } + }; +}; diff --git a/libs/app-cms/util/access/src/lib/system-user-or-tenant-admin.access.ts b/libs/app-cms/util/access/src/lib/system-user-or-tenant-admin.access.ts index 253ad1692..f21051243 100644 --- a/libs/app-cms/util/access/src/lib/system-user-or-tenant-admin.access.ts +++ b/libs/app-cms/util/access/src/lib/system-user-or-tenant-admin.access.ts @@ -1,18 +1,17 @@ -import { getUserTenantIDs, hasRole } from '@codeware/app-cms/util/misc'; -import type { AccessArgs, AccessResult, FieldAccess } from 'payload'; +import { hasRole } from '@codeware/app-cms/util/misc'; +import type { + FieldAccessArgs, + FieldAccessResponse +} from '@codeware/shared/util/payload-types'; +import type { AccessArgs, AccessResult } from 'payload'; -type FieldAccessArgs = Parameters[0]; -type FieldAccessResponse = ReturnType; +import { tenantAdminAccess } from './tenant-admin.access'; /** * Access control supporting both collection and field level. * - * Always allow access to system users. - * - * Allow access to tenant admins for the tenant the document belongs to. - * This is determined by looking for the `tenant` property in the document data. - * - * Otherwise, allow access if the user is a tenant admin for any tenant. + * This function combines `systemUserAccess` and `tenantAdminAccess` + * for better DX. */ export function systemUserOrTenantAdminAccess(args: AccessArgs): AccessResult; export function systemUserOrTenantAdminAccess( @@ -30,33 +29,5 @@ export function systemUserOrTenantAdminAccess( return true; } - // Get tenant IDs where the user is a tenant admin - const tenantIDs = getUserTenantIDs(user, 'admin'); - - // If tenant property exists in data or doc, we can verify tenant admin access for the document - if ( - // doc is original document data for field access on update - ('doc' in args && args.doc?.tenant) || - // data is only null on list requests - (args.data && args.data?.tenant) - ) { - // Fields access doesn't support query constraints - // https://payloadcms.com/docs/access-control/fields - if ('doc' in args) { - const tenant = args.doc?.tenant ?? args.data?.['tenant']; - if (!tenant) { - throw new Error('Expected to find tenant in fields doc or data'); - } - return tenantIDs.includes(tenant); - } - - return { - tenant: { - in: tenantIDs - } - }; - } - - // Otherwise, just check if the user is a tenant admin for any tenant - return tenantIDs.length > 0; + return tenantAdminAccess(args); } diff --git a/libs/app-cms/util/access/src/lib/tenant-admin.access.ts b/libs/app-cms/util/access/src/lib/tenant-admin.access.ts new file mode 100644 index 000000000..88b987d99 --- /dev/null +++ b/libs/app-cms/util/access/src/lib/tenant-admin.access.ts @@ -0,0 +1,54 @@ +import { getUserTenantIDs, hasRole } from '@codeware/app-cms/util/misc'; +import type { + FieldAccessArgs, + FieldAccessResponse +} from '@codeware/shared/util/payload-types'; +import type { AccessArgs, AccessResult } from 'payload'; + +/** + * Access control supporting both collection and field level. + * + * Allow access to tenant admins for the tenant the document belongs to. + * This is determined by looking for the `tenant` property in the document data. + * + * Otherwise, allow access if the user is a tenant admin for any tenant. + */ +export function tenantAdminAccess(args: AccessArgs): AccessResult; +export function tenantAdminAccess(args: FieldAccessArgs): FieldAccessResponse; +export function tenantAdminAccess( + args: AccessArgs | FieldAccessArgs +): AccessResult | FieldAccessResponse { + const { + req: { user } + } = args; + + // Get tenant IDs where the user is a tenant admin + const tenantIDs = getUserTenantIDs(user, 'admin'); + + // If tenant property exists in data or doc, we can verify tenant admin access for the document + if ( + // doc is original document data for field access on update + ('doc' in args && args.doc?.tenant) || + // data is only null on list requests + (args.data && args.data?.tenant) + ) { + // Fields access doesn't support query constraints + // https://payloadcms.com/docs/access-control/fields + if ('doc' in args) { + const tenant = args.doc?.tenant ?? args.data?.['tenant']; + if (!tenant) { + throw new Error('Expected to find tenant in fields doc or data'); + } + return tenantIDs.includes(tenant); + } + + return { + tenant: { + in: tenantIDs + } + }; + } + + // Otherwise, just check if the user is a tenant admin for any tenant + return tenantIDs.length > 0; +} diff --git a/libs/app-cms/util/definitions/src/index.ts b/libs/app-cms/util/definitions/src/index.ts index f8fdc802c..7d2ae89a5 100644 --- a/libs/app-cms/util/definitions/src/index.ts +++ b/libs/app-cms/util/definitions/src/index.ts @@ -6,3 +6,4 @@ export { type CodeLanguage, type MonacoLanguage } from './lib/code-languages'; +export { type MimeTypeSlug, getMimeTypes } from './lib/mime-types'; diff --git a/libs/app-cms/util/definitions/src/lib/mime-types.ts b/libs/app-cms/util/definitions/src/lib/mime-types.ts new file mode 100644 index 000000000..31c1c0bb2 --- /dev/null +++ b/libs/app-cms/util/definitions/src/lib/mime-types.ts @@ -0,0 +1,66 @@ +/** + * Supported mime types grouped by a friendly slug. + */ +const mimeTypesMap = { + // All audio types + audio: ['audio/*'], + // All image types + image: ['image/*'], + // JSON files + json: ['application/json'], + // PDF files + pdf: ['application/pdf'], + // Plain text files + text: ['text/plain'], + // All video types + video: ['video/*'], + // Word documents (.doc, .docx) + word: [ + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' + ] +}; + +export type MimeTypeSlug = keyof typeof mimeTypesMap; + +type Options = { + /** + * Limit mime types to specific mime type slugs. + * + * @default undefined + */ + limit?: Array; + /** + * Remove wildcard from mime types. + * + * This is useful when you want to use the mime type in a query. + * + * For example, 'image/*' will be converted to 'image/'. + * + * @default false + */ + stripWildcard?: boolean; +}; +/** + * Get all supported mime types or limit via options. + * + * Mime types can be explicit (e.g. 'application/json') or wildcard (e.g. 'image/*'). + * + * @param options Optional options + * @returns Array of mime types + */ +export const getMimeTypes = ({ + limit, + stripWildcard +}: Options = {}): Array => { + const mimeTypes = limit + ? limit.flatMap((type) => mimeTypesMap[type]) + : Object.values(mimeTypesMap).flat(); + + const filteredMimeTypes = stripWildcard + ? mimeTypes.map((type) => type.replace(/\*$/, '')) + : mimeTypes; + + // Remove duplicates + return [...new Set(filteredMimeTypes)]; +}; diff --git a/libs/app-cms/util/env-schema/src/lib/env.schema.ts b/libs/app-cms/util/env-schema/src/lib/env.schema.ts index 6972bc3dd..3fe259c6d 100644 --- a/libs/app-cms/util/env-schema/src/lib/env.schema.ts +++ b/libs/app-cms/util/env-schema/src/lib/env.schema.ts @@ -54,6 +54,9 @@ export const EnvSchema = withEnvVars( }), // Seed configuration + SEED_DATA_URL: z + .string({ description: 'URL to public seed data files' }) + .optional(), SEED_SOURCE: SeedSourceSchema.default('cloud-local'), SEED_STRATEGY: SeedStrategySchema.default('delta'), diff --git a/libs/app-cms/util/misc/src/lib/get-id.ts b/libs/app-cms/util/misc/src/lib/get-id.ts index 0363da6f7..51f86f5b6 100644 --- a/libs/app-cms/util/misc/src/lib/get-id.ts +++ b/libs/app-cms/util/misc/src/lib/get-id.ts @@ -10,6 +10,6 @@ import type { CollectionType } from '@codeware/shared/util/payload-types'; * @returns The id of the entity or 0 if the entity is undefined. */ export const getId = ( - entity: T | T['id'] | undefined + entity: T | T['id'] | null | undefined ): T['id'] => entity && typeof entity === 'object' ? entity.id : (entity ?? 0); diff --git a/libs/app-cms/util/plugins/src/lib/plugins/get-multi-tenant-plugin.ts b/libs/app-cms/util/plugins/src/lib/plugins/get-multi-tenant-plugin.ts index 692084475..60827da82 100644 --- a/libs/app-cms/util/plugins/src/lib/plugins/get-multi-tenant-plugin.ts +++ b/libs/app-cms/util/plugins/src/lib/plugins/get-multi-tenant-plugin.ts @@ -20,7 +20,8 @@ export const getMultiTenantPlugin = () => pages: {}, posts: {}, 'reusable-content': {}, - 'site-settings': { isGlobal: true } + 'site-settings': { isGlobal: true }, + tags: {} }, tenantsArrayField: { includeDefaultField: false diff --git a/libs/shared/ui/payload-components/src/lib/RenderBlocks.tsx b/libs/shared/ui/payload-components/src/lib/RenderBlocks.tsx index 084371d44..d4f8ccd1e 100644 --- a/libs/shared/ui/payload-components/src/lib/RenderBlocks.tsx +++ b/libs/shared/ui/payload-components/src/lib/RenderBlocks.tsx @@ -9,10 +9,13 @@ import { useRef } from 'react'; import { CardBlock } from './blocks/CardBlock'; import { CodeBlock } from './blocks/CodeBlock'; import { ContentBlock } from './blocks/ContentBlock'; +import { FileAreaBlock } from './blocks/FileAreaBlock'; import { FormBlock } from './blocks/FormBlock'; +import { ImageBlock } from './blocks/ImageBlock'; import { MediaBlock } from './blocks/MediaBlock'; import { SocialMediaBlock } from './blocks/SocialMediaBlock'; import { SpacingBlock } from './blocks/SpacingBlock'; +import { VideoBlock } from './blocks/VideoBlock'; const blocksMap: Record< BlockSlug, @@ -22,10 +25,13 @@ const blocksMap: Record< card: CardBlock, code: CodeBlock, content: ContentBlock, + 'file-area': FileAreaBlock, form: FormBlock, + image: ImageBlock, media: MediaBlock, 'social-media': SocialMediaBlock, spacing: SpacingBlock, + video: VideoBlock, // Reusable content block invokes RenderBlocks to resolve the nested blocks. // Keep implementation here to avoid circular dependency. 'reusable-content': ({ reusableContent, refId }: ReusableContentBlock) => { diff --git a/libs/shared/ui/payload-components/src/lib/blocks/FileAreaBlock.tsx b/libs/shared/ui/payload-components/src/lib/blocks/FileAreaBlock.tsx new file mode 100644 index 000000000..10ba47268 --- /dev/null +++ b/libs/shared/ui/payload-components/src/lib/blocks/FileAreaBlock.tsx @@ -0,0 +1,24 @@ +import { FileArea } from '@codeware/shared/ui/react-components'; +import type { FileAreaBlock as FileAreaBlockProps } from '@codeware/shared/util/payload-types'; + +import { usePayload } from '../providers/PayloadProvider'; + +type Props = FileAreaBlockProps; + +export const FileAreaBlock: React.FC = ({ files: filesFromProps }) => { + const { payloadUrl } = usePayload(); + + const files = (filesFromProps ?? []) + .map(({ media }) => (media && typeof media === 'object' ? media : null)) + .filter((media) => media !== null) + .map(({ createdAt, id, filename, filesize, mimeType, url }) => ({ + dateAdded: new Date(createdAt), + id: String(id), + name: String(filename), + size: Number(filesize), + mimeType: String(mimeType), + previewUrl: `${payloadUrl}/${url}` + })); + + return ; +}; diff --git a/libs/shared/ui/payload-components/src/lib/blocks/ImageBlock.tsx b/libs/shared/ui/payload-components/src/lib/blocks/ImageBlock.tsx new file mode 100644 index 000000000..7ff58637e --- /dev/null +++ b/libs/shared/ui/payload-components/src/lib/blocks/ImageBlock.tsx @@ -0,0 +1,66 @@ +import { Image, type Size } from '@codeware/shared/ui/react-components'; +import type { ImageBlock as ImageBlockProps } from '@codeware/shared/util/payload-types'; +import React from 'react'; + +import { usePayload } from '../providers/PayloadProvider'; + +import { RichText } from './RichText'; + +type Props = ImageBlockProps; + +/** + * Renders image block with optional caption and alt text + * with responsive image sizes. + */ +export const ImageBlock: React.FC = ({ media }) => { + const { payloadUrl } = usePayload(); + + if (!media || typeof media !== 'object') { + return null; + } + + const { alt, caption, sizes = {}, url = '' } = media; + + const src = `${payloadUrl}${url}`; + const mediaSizes = Object.values(sizes); + + // Convert media sizes to responsive image sizes + let sizeCount = 0; + const responsiveSizes = mediaSizes.reduce((acc, { mimeType, url, width }) => { + sizeCount++; + if (!url) { + return acc; + } + const size = { + src: `${payloadUrl}${url}`, + width: width ?? undefined, + mimeType: mimeType ?? undefined, + ignoreMedia: false + }; + acc.push(size); + // Append a copy of the last size with ignored media query + // to ensure a modern image format is used as fallback + // before the component fallback image + if (sizeCount === mediaSizes.length) { + acc.push({ ...size, ignoreMedia: true }); + } + return acc; + }, [] as Array); + + return ( + <> + {alt} + {caption && ( +
+ +
+ )} + + ); +}; diff --git a/libs/shared/ui/payload-components/src/lib/blocks/MediaBlock.tsx b/libs/shared/ui/payload-components/src/lib/blocks/MediaBlock.tsx index 6d6591e1e..887ea9474 100644 --- a/libs/shared/ui/payload-components/src/lib/blocks/MediaBlock.tsx +++ b/libs/shared/ui/payload-components/src/lib/blocks/MediaBlock.tsx @@ -8,6 +8,9 @@ import { RichText } from './RichText'; type Props = MediaBlockProps; +/** + * @deprecated Replaced by `ImageBlock` and `VideoBlock`. + */ export const MediaBlock: React.FC = ({ media }) => { const { payloadUrl } = usePayload(); @@ -59,7 +62,7 @@ export const MediaBlock: React.FC = ({ media }) => { width={media.width ?? undefined} /> )} - {isVideo &&