From 7d1884be41346ed8ba96585e5098760bab3d3d37 Mon Sep 17 00:00:00 2001 From: Gabriel Brown Date: Thu, 1 Oct 2026 14:42:55 -0400 Subject: [PATCH] Deploy from CI with Infisical and ship Payload changes as migrations CI reads Infisical staging as the read-only convexmonorepo-ci machine identity, deploys the Convex functions, builds and pushes the image, and asks the VPS to redeploy. Payload schema changes now ship as migrations that production runs on startup; the baseline matches the production schema and is recorded as applied there. The local stack moves to its own ports and syncs the Convex runtime env, and the scripts pick up Infisical account switching, dev-kill and a cache-safe CI install. --- .gitea/workflows/build-next.yml | 113 +- .gitignore | 3 + .infisical.json | 3 +- AGENTS.md | 54 +- README.md | 26 +- apps/next/eslint.config.ts | 2 +- .../migrations/20261001_183341_baseline.json | 4991 +++++++++++++++++ .../migrations/20261001_183341_baseline.ts | 710 +++ apps/next/src/migrations/index.ts | 9 + apps/next/src/payload.config.ts | 10 + docker/Dockerfile | 14 +- docker/compose.local.yml | 14 +- docker/compose.yml | 8 +- docs/payload-cms.md | 2 +- scripts/build-next-bundle | 23 + scripts/ci-export-env.mjs | 62 + scripts/ci-install | 14 + scripts/db/up | 11 +- scripts/dev-kill | 44 + scripts/export-env | 6 + scripts/infisical-profile | 117 + scripts/sync-local-convex-env | 47 + turbo.json | 7 +- 23 files changed, 6235 insertions(+), 55 deletions(-) create mode 100644 apps/next/src/migrations/20261001_183341_baseline.json create mode 100644 apps/next/src/migrations/20261001_183341_baseline.ts create mode 100644 apps/next/src/migrations/index.ts create mode 100755 scripts/build-next-bundle create mode 100755 scripts/ci-export-env.mjs create mode 100755 scripts/ci-install create mode 100755 scripts/dev-kill create mode 100755 scripts/infisical-profile create mode 100755 scripts/sync-local-convex-env diff --git a/.gitea/workflows/build-next.yml b/.gitea/workflows/build-next.yml index e918a0e..bac982d 100644 --- a/.gitea/workflows/build-next.yml +++ b/.gitea/workflows/build-next.yml @@ -1,56 +1,131 @@ -name: Build and Push Next App +name: Build and Deploy Next App on: push: - branches: [main] + branches: + - main paths: - - 'apps/**' + - 'apps/next/**' - 'packages/**' - 'tools/**' - 'scripts/**' - 'docker/**' - '.gitea/workflows/build-next.yml' - - '.infisical.json' - 'package.json' - 'bun.lock' - 'turbo.json' +# One production deploy at a time; a newer push waits rather than racing. +concurrency: + group: build-next-production + cancel-in-progress: false + +# Production env comes from the Infisical `staging` environment. Each job logs +# in as the read-only `convexmonorepo-ci` machine identity and writes the env to a +# temp file (scripts/ci-export-env.mjs), so no prod values live in Gitea. jobs: + # Quality gate: workspace lint (sherif), eslint, typecheck and the unit, + # integration and component suites must pass before anything touches + # production. The local-stack e2e smoke check is skipped in CI. quality: runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Bun + uses: oven-sh/setup-bun@v2 with: - bun-version: 1.3.10 - - run: bun install --frozen-lockfile - - name: Lint, typecheck, and test + bun-version: 1.3.14 + + - name: Install dependencies + run: ./scripts/ci-install + + - name: Lint and typecheck env: - DOTENV_PROD: ${{ secrets.DOTENV_PROD }} + INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }} + INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }} run: | env_file="$(mktemp)" trap 'rm -f "$env_file"' EXIT - printf '%s\n' "$DOTENV_PROD" > "$env_file" + node scripts/ci-export-env.mjs staging > "$env_file" bunx dotenv -e "$env_file" -- env NODE_ENV=test SKIP_E2E=1 bun run ci:check - build-next: + # Pushes Convex functions and schema to prod before the image that depends on + # them is built. The env file must carry CONVEX_SELF_HOSTED_URL and + # CONVEX_SELF_HOSTED_ADMIN_KEY. Codegen is disabled: _generated is committed. + convex-deploy: needs: [quality] runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v4 - - name: Log in to container registry - run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login git.gbrown.org -u "${{ secrets.REGISTRY_USER }}" --password-stdin - - name: Build image + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.14 + + - name: Install dependencies + run: ./scripts/ci-install + + - name: Deploy Convex functions env: - DOTENV_PROD: ${{ secrets.DOTENV_PROD }} + INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }} + INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }} run: | env_file="$(mktemp)" trap 'rm -f "$env_file"' EXIT - printf '%s\n' "$DOTENV_PROD" > "$env_file" + node scripts/ci-export-env.mjs staging > "$env_file" + cd packages/backend + bunx convex deploy --env-file "$env_file" --codegen disable --typecheck disable + + build-next: + needs: [convex-deploy] + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to container registry + run: | + echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login git.gbrown.org -u "${{ secrets.REGISTRY_USER }}" --password-stdin + + - name: Build app using project script + env: + INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }} + INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }} + run: | + env_file="$(mktemp)" + trap 'rm -f "$env_file"' EXIT + node scripts/ci-export-env.mjs staging > "$env_file" CI_ENV_FILE="$env_file" ./scripts/build-next-app staging - - name: Tag and push image + + - name: Tag built image run: | docker tag convexmonorepo-next:latest git.gbrown.org/gib/convexmonorepo-next:${{ gitea.sha }} docker tag convexmonorepo-next:latest git.gbrown.org/gib/convexmonorepo-next:latest + + - name: Push image tags + run: | docker push git.gbrown.org/gib/convexmonorepo-next:${{ gitea.sha }} docker push git.gbrown.org/gib/convexmonorepo-next:latest + + # Asks the VPS to pull the new image and restart podman-convexmonorepo.service + # through the forced-command deploy key (~/Server/AGENTS.md ยง15). A + # failure only warns: the image is pushed and the midnight + # podman-update.timer deploys it anyway. + - name: Trigger immediate deploy + run: | + install -m600 /dev/null "$HOME/.deploy_key" + printf '%s\n' "${{ secrets.DEPLOY_SSH_KEY }}" > "$HOME/.deploy_key" + ssh -i "$HOME/.deploy_key" \ + -o BatchMode=yes \ + -o StrictHostKeyChecking=accept-new \ + -o ConnectTimeout=15 \ + gib@192.168.2.2 convexmonorepo \ + || echo "::warning::deploy trigger failed; nightly sweep will deploy instead" + rm -f "$HOME/.deploy_key" diff --git a/.gitignore b/.gitignore index 71c6ac5..17d259b 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,6 @@ apps/next/playwright-report/ # tanstack .tanstack + +# local command flags (machine-specific, e.g. which Infisical account to use) +scripts/flags/ diff --git a/.infisical.json b/.infisical.json index 96c2371..d5b4135 100644 --- a/.infisical.json +++ b/.infisical.json @@ -1,5 +1,6 @@ { "workspaceId": "b2760150-c581-4e0d-acb5-b4da51cee58d", "defaultEnvironment": "", - "gitBranchToEnvironmentMapping": null + "gitBranchToEnvironmentMapping": null, + "domain": "https://infisical.gbrown.org/api" } diff --git a/AGENTS.md b/AGENTS.md index 690fba2..86a4919 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,8 +7,8 @@ - `packages/backend/convex`: self-hosted Convex functions, schema, and auth. - `packages/ui`: shared shadcn-based UI components. - `tools`: shared ESLint, Prettier, Tailwind, TypeScript, and Vitest config. -- Local development uses host-run apps, local Convex on ports 3210/3211, its - own data volume, and Payload Postgres on port 5432. Convex does not use +- Local development uses host-run apps, local Convex on ports 3240/3241, its + own data volume, and Payload Postgres on port 5436. Convex does not use Postgres unless a cloned project explicitly enables the commented option. ## Protected and generated files @@ -29,8 +29,17 @@ - Run `infisical login` and `infisical init` before local development. - Machine-generated values belong in `.local/.generated.env`; never put the generated Convex admin key in shared Infisical. -- CI uses Gitea-injected secrets or `CI_ENV_FILE` and must not call Infisical. -- App code imports validated variables from `@/env`, never `process.env`. +- CI reads `staging` through `scripts/ci-export-env.mjs`, logged in as the + read-only `convexmonorepo-ci` machine identity (Gitea secrets + `INFISICAL_CLIENT_ID` and `INFISICAL_CLIENT_SECRET`). `scripts/export-env` + refuses to run in CI. +- `staging` holds the production values that CI builds and deploys with. +- If you are logged in to more than one Infisical account, put + `INFISICAL_EMAIL=...` in `scripts/flags/infisical.env` (gitignored) and the + scripts switch the CLI to that account before exporting. +- Server code imports validated variables from `@/env`. Client components and + `instrumentation-client.ts` import from `@/publicEnv`, which keeps zod out of + the browser bundle. Never read `process.env` elsewhere. - Add cache-relevant variables to `turbo.json` `globalEnv`. ## Code and dependency rules @@ -52,10 +61,12 @@ bun dev:next # host Next + deploy/watch local Convex functions bun db:down # stop and preserve both data volumes bun db:down:wipe # remove both volumes, generated admin key, and seed marker bun db:sync:payload # refresh and apply the local Payload seed snapshot +bun db:sync-env # re-push Infisical dev values into the local Convex env +bun dev:kill # stop dev servers that survived a Ctrl-C ``` -Local URLs: app `http://localhost:3000`, Convex `http://localhost:3210`, -dashboard `http://localhost:6791`, Payload Postgres `localhost:5432`. +Local URLs: app `http://localhost:3000`, Convex `http://localhost:3240`, +dashboard `http://localhost:6794`, Payload Postgres `localhost:5436`. Use `INFISICAL_ENV=staging bun dev:next` for staging services. `db:sync:payload` is an explicit, destructive local operation. It validates a @@ -65,6 +76,37 @@ only under `.local/`, and never changes Convex or production data. Normal when `.local/payload-seed-state.env` is absent or stale. Do not store seed markers in Payload Postgres; schema push treats unmanaged tables as drift. +## Payload schema changes + +Payload schema changes ship as migrations in `apps/next/src/migrations/`. +Locally, Payload pushes schema edits straight into the localhost database. When +a change is ready, run `bun with-env bunx payload migrate:create ` from +`apps/next` and commit the generated files. Production runs pending migrations +on startup (`prodMigrations` in `payload.config.ts`). Push only runs against a +database on `localhost`: a push leaves a `dev` row in `payload_migrations`, and +Payload's startup migration stops on a confirmation prompt when it finds one. +For the same reason, a local production build against the local dev database +hangs; point it at a database without that row. + +## Analytics and bundle size + +- Plausible (`NEXT_PUBLIC_PLAUSIBLE_SRC`, the per-site script URL) loads through + a same-origin proxy under `/pa`, clear of Payload's `/api` routes. PostHog + (`NEXT_PUBLIC_POSTHOG_KEY`, `NEXT_PUBLIC_POSTHOG_HOST`) loads after the page + is idle through `/ingest`. Both stay off when their env is unset. +- Session replay comes from PostHog. Sentry carries errors and traces only. +- `packages/ui/src/index.tsx` holds re-exports only (`cn` and `ccn` live in + `utils.ts`). With `optimizePackageImports` in `next.config.js`, each page + loads only the UI modules it imports. Declaring anything in the index breaks + that. + +## Deployment + +A push to `main` runs `.gitea/workflows/build-next.yml`: the quality gate, then +`convex deploy` to production, then the image build and push, then +`ssh gib@192.168.2.2 convexmonorepo` (the forced-command deploy on the VPS). A +push to `main` is a production deploy. + ## Validation Use `bun typecheck`, never a production build for routine validation. The full diff --git a/README.md b/README.md index 8760230..682b7c2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ Payload CMS, shared UI/config packages, Vitest, and Docker deployment. ## Local setup -Requirements: Bun 1.3.10, Docker or Podman, Node 22, and the Infisical CLI. +Requirements: Bun 1.3.14, Docker or Podman, Node 22, the Infisical CLI, `jq`, and +`flock`. ```sh bun install --frozen-lockfile @@ -22,9 +23,9 @@ files. Select staging with `INFISICAL_ENV=staging bun dev:next`. Local services: - Next.js: `http://localhost:3000` -- Convex API: `http://localhost:3210` -- Convex dashboard: `http://localhost:6791` -- Payload Postgres: `localhost:5432` +- Convex API: `http://localhost:3240` +- Convex dashboard: `http://localhost:6794` +- Payload Postgres: `localhost:5436` Next and Expo run on the host. Payload uses the local Postgres database. Convex uses a separate self-hosted data volume and does not use Postgres by default. @@ -54,7 +55,8 @@ Convex URL with the development host's LAN address when testing Expo on-device. - Local `dev` and `staging`: Infisical. - Generated local state: `.local/.generated.env`. -- CI/CD: Gitea `DOTENV_PROD`, materialized only as a temporary runner file. +- CI/CD: Infisical `staging`, read by the read-only `convexmonorepo-ci` machine + identity and materialized only as a temporary runner file. - Docker compilation: explicit Compose build args; `.env*` stays outside the image context. @@ -87,6 +89,14 @@ Shared dependency versions belong in root catalogs. Edit the root catalog, run Production Compose retains the self-hosted Convex backend/dashboard and accepts an external Payload `PAYLOAD_DB_URL`. Its commented Postgres service remains an -optional Payload database. Gitea runs the quality gate first, builds the Next -image from a temporary Gitea-secret env file, then pushes SHA and `latest` tags. -CI never installs or invokes Infisical. +optional Payload database. + +A push to `main` runs the quality gate, deploys the Convex functions, builds the +Next image, pushes SHA and `latest` tags, and asks the VPS to redeploy +`podman-convexmonorepo.service` over the forced-command deploy key. If that last +step fails, the nightly update timer deploys the image instead. Each job reads +Infisical `staging` through `scripts/ci-export-env.mjs` as the `convexmonorepo-ci` +machine identity (Gitea secrets `INFISICAL_CLIENT_ID` and +`INFISICAL_CLIENT_SECRET`). + +Payload schema changes ship as migrations; see `AGENTS.md`. diff --git a/apps/next/eslint.config.ts b/apps/next/eslint.config.ts index 37f3d3d..7bcbd94 100644 --- a/apps/next/eslint.config.ts +++ b/apps/next/eslint.config.ts @@ -6,7 +6,7 @@ import { reactConfig } from '@gib/eslint-config/react'; export default defineConfig( { - ignores: ['.next/**', 'payload-types.ts'], + ignores: ['.next/**', 'payload-types.ts', 'src/migrations/**'], }, baseConfig, reactConfig, diff --git a/apps/next/src/migrations/20261001_183341_baseline.json b/apps/next/src/migrations/20261001_183341_baseline.json new file mode 100644 index 0000000..0fc8719 --- /dev/null +++ b/apps/next/src/migrations/20261001_183341_baseline.json @@ -0,0 +1,4991 @@ +{ + "version": "7", + "dialect": "postgresql", + "tables": { + "public.users_sessions": { + "name": "users_sessions", + "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 + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "users_sessions_order_idx": { + "name": "users_sessions_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_sessions_parent_id_idx": { + "name": "users_sessions_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_sessions_parent_id_fk": { + "name": "users_sessions_parent_id_fk", + "tableFrom": "users_sessions", + "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 + }, + "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 + }, + "reset_password_requested_at": { + "name": "reset_password_requested_at", + "type": "timestamp(3) with time zone", + "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.payload_kv": { + "name": "payload_kv", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "payload_kv_key_idx": { + "name": "payload_kv_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "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 + }, + "users_id": { + "name": "users_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_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": {} + } + }, + "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_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" + } + }, + "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 + }, + "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_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_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 + }, + "public.landing_page_blocks_hero_highlights": { + "name": "landing_page_blocks_hero_highlights", + "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 + }, + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_hero_highlights_order_idx": { + "name": "landing_page_blocks_hero_highlights_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_hero_highlights_parent_id_idx": { + "name": "landing_page_blocks_hero_highlights_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_hero_highlights_parent_id_fk": { + "name": "landing_page_blocks_hero_highlights_parent_id_fk", + "tableFrom": "landing_page_blocks_hero_highlights", + "tableTo": "landing_page_blocks_hero", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_hero": { + "name": "landing_page_blocks_hero", + "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 + }, + "badge_emoji": { + "name": "badge_emoji", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'๐Ÿš€'" + }, + "badge_text": { + "name": "badge_text", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Production-ready monorepo template'" + }, + "heading_prefix": { + "name": "heading_prefix", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Build Full-Stack Apps with'" + }, + "heading_highlight": { + "name": "heading_highlight", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'convex-monorepo'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'A Turborepo starter with Next.js, Expo, and self-hosted Convex. Ship web and mobile apps faster with shared code, type-safe backend, and complete control over your infrastructure.'" + }, + "primary_cta_label": { + "name": "primary_cta_label", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'View Source Code'" + }, + "primary_cta_url": { + "name": "primary_cta_url", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'https://git.gbrown.org/gib/convex-monorepo'" + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_hero_order_idx": { + "name": "landing_page_blocks_hero_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_hero_parent_id_idx": { + "name": "landing_page_blocks_hero_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_hero_path_idx": { + "name": "landing_page_blocks_hero_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_hero_parent_id_fk": { + "name": "landing_page_blocks_hero_parent_id_fk", + "tableFrom": "landing_page_blocks_hero", + "tableTo": "landing_page", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_logo_cloud_items": { + "name": "landing_page_blocks_logo_cloud_items", + "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 + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "accent": { + "name": "accent", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_logo_cloud_items_order_idx": { + "name": "landing_page_blocks_logo_cloud_items_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_logo_cloud_items_parent_id_idx": { + "name": "landing_page_blocks_logo_cloud_items_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_logo_cloud_items_parent_id_fk": { + "name": "landing_page_blocks_logo_cloud_items_parent_id_fk", + "tableFrom": "landing_page_blocks_logo_cloud_items", + "tableTo": "landing_page_blocks_logo_cloud", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_logo_cloud": { + "name": "landing_page_blocks_logo_cloud", + "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 + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Designed For Modern Product Teams'" + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_logo_cloud_order_idx": { + "name": "landing_page_blocks_logo_cloud_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_logo_cloud_parent_id_idx": { + "name": "landing_page_blocks_logo_cloud_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_logo_cloud_path_idx": { + "name": "landing_page_blocks_logo_cloud_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_logo_cloud_parent_id_fk": { + "name": "landing_page_blocks_logo_cloud_parent_id_fk", + "tableFrom": "landing_page_blocks_logo_cloud", + "tableTo": "landing_page", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_features_items": { + "name": "landing_page_blocks_features_items", + "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 + }, + "icon": { + "name": "icon", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_features_items_order_idx": { + "name": "landing_page_blocks_features_items_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_features_items_parent_id_idx": { + "name": "landing_page_blocks_features_items_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_features_items_parent_id_fk": { + "name": "landing_page_blocks_features_items_parent_id_fk", + "tableFrom": "landing_page_blocks_features_items", + "tableTo": "landing_page_blocks_features", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_features": { + "name": "landing_page_blocks_features", + "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 + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Everything You Need to Ship Fast'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'A complete monorepo template with all the tools and patterns you need for production-ready applications.'" + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_features_order_idx": { + "name": "landing_page_blocks_features_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_features_parent_id_idx": { + "name": "landing_page_blocks_features_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_features_path_idx": { + "name": "landing_page_blocks_features_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_features_parent_id_fk": { + "name": "landing_page_blocks_features_parent_id_fk", + "tableFrom": "landing_page_blocks_features", + "tableTo": "landing_page", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_stats_items": { + "name": "landing_page_blocks_stats_items", + "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": false + }, + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_stats_items_order_idx": { + "name": "landing_page_blocks_stats_items_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_stats_items_parent_id_idx": { + "name": "landing_page_blocks_stats_items_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_stats_items_parent_id_fk": { + "name": "landing_page_blocks_stats_items_parent_id_fk", + "tableFrom": "landing_page_blocks_stats_items", + "tableTo": "landing_page_blocks_stats", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_stats": { + "name": "landing_page_blocks_stats", + "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 + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Built To Demo Well'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'These are the kinds of outcomes you can point to when showing the template to clients.'" + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_stats_order_idx": { + "name": "landing_page_blocks_stats_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_stats_parent_id_idx": { + "name": "landing_page_blocks_stats_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_stats_path_idx": { + "name": "landing_page_blocks_stats_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_stats_parent_id_fk": { + "name": "landing_page_blocks_stats_parent_id_fk", + "tableFrom": "landing_page_blocks_stats", + "tableTo": "landing_page", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_tech_stack_categories_technologies": { + "name": "landing_page_blocks_tech_stack_categories_technologies", + "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 + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_tech_stack_categories_technologies_order_idx": { + "name": "landing_page_blocks_tech_stack_categories_technologies_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_tech_stack_categories_technologies_parent_id_idx": { + "name": "landing_page_blocks_tech_stack_categories_technologies_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_tech_stack_categories_technologies_parent_id_fk": { + "name": "landing_page_blocks_tech_stack_categories_technologies_parent_id_fk", + "tableFrom": "landing_page_blocks_tech_stack_categories_technologies", + "tableTo": "landing_page_blocks_tech_stack_categories", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_tech_stack_categories": { + "name": "landing_page_blocks_tech_stack_categories", + "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 + }, + "category": { + "name": "category", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_tech_stack_categories_order_idx": { + "name": "landing_page_blocks_tech_stack_categories_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_tech_stack_categories_parent_id_idx": { + "name": "landing_page_blocks_tech_stack_categories_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_tech_stack_categories_parent_id_fk": { + "name": "landing_page_blocks_tech_stack_categories_parent_id_fk", + "tableFrom": "landing_page_blocks_tech_stack_categories", + "tableTo": "landing_page_blocks_tech_stack", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_tech_stack": { + "name": "landing_page_blocks_tech_stack", + "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 + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Modern Tech Stack'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Built with the latest and greatest tools for maximum productivity and performance.'" + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_tech_stack_order_idx": { + "name": "landing_page_blocks_tech_stack_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_tech_stack_parent_id_idx": { + "name": "landing_page_blocks_tech_stack_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_tech_stack_path_idx": { + "name": "landing_page_blocks_tech_stack_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_tech_stack_parent_id_fk": { + "name": "landing_page_blocks_tech_stack_parent_id_fk", + "tableFrom": "landing_page_blocks_tech_stack", + "tableTo": "landing_page", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_testimonials_items": { + "name": "landing_page_blocks_testimonials_items", + "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 + }, + "quote": { + "name": "quote", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "company": { + "name": "company", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "avatar_emoji": { + "name": "avatar_emoji", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_testimonials_items_order_idx": { + "name": "landing_page_blocks_testimonials_items_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_testimonials_items_parent_id_idx": { + "name": "landing_page_blocks_testimonials_items_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_testimonials_items_parent_id_fk": { + "name": "landing_page_blocks_testimonials_items_parent_id_fk", + "tableFrom": "landing_page_blocks_testimonials_items", + "tableTo": "landing_page_blocks_testimonials", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_testimonials": { + "name": "landing_page_blocks_testimonials", + "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 + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'What Clients Actually Care About'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'The polished part is not just the frontend. Editors can shape the story, launch updates fast, and preview changes before they go live.'" + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_testimonials_order_idx": { + "name": "landing_page_blocks_testimonials_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_testimonials_parent_id_idx": { + "name": "landing_page_blocks_testimonials_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_testimonials_path_idx": { + "name": "landing_page_blocks_testimonials_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_testimonials_parent_id_fk": { + "name": "landing_page_blocks_testimonials_parent_id_fk", + "tableFrom": "landing_page_blocks_testimonials", + "tableTo": "landing_page", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_pricing_plans_features": { + "name": "landing_page_blocks_pricing_plans_features", + "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 + }, + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_pricing_plans_features_order_idx": { + "name": "landing_page_blocks_pricing_plans_features_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_pricing_plans_features_parent_id_idx": { + "name": "landing_page_blocks_pricing_plans_features_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_pricing_plans_features_parent_id_fk": { + "name": "landing_page_blocks_pricing_plans_features_parent_id_fk", + "tableFrom": "landing_page_blocks_pricing_plans_features", + "tableTo": "landing_page_blocks_pricing_plans", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_pricing_plans": { + "name": "landing_page_blocks_pricing_plans", + "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 + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "billing_period": { + "name": "billing_period", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "badge": { + "name": "badge", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "is_highlighted": { + "name": "is_highlighted", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "cta_label": { + "name": "cta_label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "cta_url": { + "name": "cta_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_pricing_plans_order_idx": { + "name": "landing_page_blocks_pricing_plans_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_pricing_plans_parent_id_idx": { + "name": "landing_page_blocks_pricing_plans_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_pricing_plans_parent_id_fk": { + "name": "landing_page_blocks_pricing_plans_parent_id_fk", + "tableFrom": "landing_page_blocks_pricing_plans", + "tableTo": "landing_page_blocks_pricing", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_pricing": { + "name": "landing_page_blocks_pricing", + "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 + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'A Great Way To Pitch Productized Delivery'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Use pricing blocks to show packaged service tiers, subscriptions, or implementation options right inside the landing page builder.'" + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_pricing_order_idx": { + "name": "landing_page_blocks_pricing_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_pricing_parent_id_idx": { + "name": "landing_page_blocks_pricing_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_pricing_path_idx": { + "name": "landing_page_blocks_pricing_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_pricing_parent_id_fk": { + "name": "landing_page_blocks_pricing_parent_id_fk", + "tableFrom": "landing_page_blocks_pricing", + "tableTo": "landing_page", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_faq_items": { + "name": "landing_page_blocks_faq_items", + "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 + }, + "question": { + "name": "question", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "answer": { + "name": "answer", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_faq_items_order_idx": { + "name": "landing_page_blocks_faq_items_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_faq_items_parent_id_idx": { + "name": "landing_page_blocks_faq_items_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_faq_items_parent_id_fk": { + "name": "landing_page_blocks_faq_items_parent_id_fk", + "tableFrom": "landing_page_blocks_faq_items", + "tableTo": "landing_page_blocks_faq", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_faq": { + "name": "landing_page_blocks_faq", + "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 + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Questions You Will Probably Get In A Demo'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'A good FAQ block makes the editor feel complete and gives clients another obvious content type they can manage themselves.'" + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_faq_order_idx": { + "name": "landing_page_blocks_faq_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_faq_parent_id_idx": { + "name": "landing_page_blocks_faq_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_faq_path_idx": { + "name": "landing_page_blocks_faq_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_faq_parent_id_fk": { + "name": "landing_page_blocks_faq_parent_id_fk", + "tableFrom": "landing_page_blocks_faq", + "tableTo": "landing_page", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page_blocks_cta": { + "name": "landing_page_blocks_cta", + "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 + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Ready to Build Something Amazing?'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Clone the repository and start building your next project with everything pre-configured.'" + }, + "command_label": { + "name": "command_label", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Quick Start'" + }, + "command": { + "name": "command", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'git clone https://git.gbrown.org/gib/convex-monorepo.git\ncd convex-monorepo\nbun i'" + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page_blocks_cta_order_idx": { + "name": "landing_page_blocks_cta_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_cta_parent_id_idx": { + "name": "landing_page_blocks_cta_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "landing_page_blocks_cta_path_idx": { + "name": "landing_page_blocks_cta_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "landing_page_blocks_cta_parent_id_fk": { + "name": "landing_page_blocks_cta_parent_id_fk", + "tableFrom": "landing_page_blocks_cta", + "tableTo": "landing_page", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.landing_page": { + "name": "landing_page", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "_status": { + "name": "_status", + "type": "enum_landing_page_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'draft'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "landing_page__status_idx": { + "name": "landing_page__status_idx", + "columns": [ + { + "expression": "_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_hero_highlights": { + "name": "_landing_page_v_blocks_hero_highlights", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_hero_highlights_order_idx": { + "name": "_landing_page_v_blocks_hero_highlights_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_hero_highlights_parent_id_idx": { + "name": "_landing_page_v_blocks_hero_highlights_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_hero_highlights_parent_id_fk": { + "name": "_landing_page_v_blocks_hero_highlights_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_hero_highlights", + "tableTo": "_landing_page_v_blocks_hero", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_hero": { + "name": "_landing_page_v_blocks_hero", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "badge_emoji": { + "name": "badge_emoji", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'๐Ÿš€'" + }, + "badge_text": { + "name": "badge_text", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Production-ready monorepo template'" + }, + "heading_prefix": { + "name": "heading_prefix", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Build Full-Stack Apps with'" + }, + "heading_highlight": { + "name": "heading_highlight", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'convex-monorepo'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'A Turborepo starter with Next.js, Expo, and self-hosted Convex. Ship web and mobile apps faster with shared code, type-safe backend, and complete control over your infrastructure.'" + }, + "primary_cta_label": { + "name": "primary_cta_label", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'View Source Code'" + }, + "primary_cta_url": { + "name": "primary_cta_url", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'https://git.gbrown.org/gib/convex-monorepo'" + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_hero_order_idx": { + "name": "_landing_page_v_blocks_hero_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_hero_parent_id_idx": { + "name": "_landing_page_v_blocks_hero_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_hero_path_idx": { + "name": "_landing_page_v_blocks_hero_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_hero_parent_id_fk": { + "name": "_landing_page_v_blocks_hero_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_hero", + "tableTo": "_landing_page_v", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_logo_cloud_items": { + "name": "_landing_page_v_blocks_logo_cloud_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": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "accent": { + "name": "accent", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_logo_cloud_items_order_idx": { + "name": "_landing_page_v_blocks_logo_cloud_items_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_logo_cloud_items_parent_id_idx": { + "name": "_landing_page_v_blocks_logo_cloud_items_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_logo_cloud_items_parent_id_fk": { + "name": "_landing_page_v_blocks_logo_cloud_items_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_logo_cloud_items", + "tableTo": "_landing_page_v_blocks_logo_cloud", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_logo_cloud": { + "name": "_landing_page_v_blocks_logo_cloud", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Designed For Modern Product Teams'" + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_logo_cloud_order_idx": { + "name": "_landing_page_v_blocks_logo_cloud_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_logo_cloud_parent_id_idx": { + "name": "_landing_page_v_blocks_logo_cloud_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_logo_cloud_path_idx": { + "name": "_landing_page_v_blocks_logo_cloud_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_logo_cloud_parent_id_fk": { + "name": "_landing_page_v_blocks_logo_cloud_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_logo_cloud", + "tableTo": "_landing_page_v", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_features_items": { + "name": "_landing_page_v_blocks_features_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": "serial", + "primaryKey": true, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_features_items_order_idx": { + "name": "_landing_page_v_blocks_features_items_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_features_items_parent_id_idx": { + "name": "_landing_page_v_blocks_features_items_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_features_items_parent_id_fk": { + "name": "_landing_page_v_blocks_features_items_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_features_items", + "tableTo": "_landing_page_v_blocks_features", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_features": { + "name": "_landing_page_v_blocks_features", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Everything You Need to Ship Fast'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'A complete monorepo template with all the tools and patterns you need for production-ready applications.'" + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_features_order_idx": { + "name": "_landing_page_v_blocks_features_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_features_parent_id_idx": { + "name": "_landing_page_v_blocks_features_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_features_path_idx": { + "name": "_landing_page_v_blocks_features_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_features_parent_id_fk": { + "name": "_landing_page_v_blocks_features_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_features", + "tableTo": "_landing_page_v", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_stats_items": { + "name": "_landing_page_v_blocks_stats_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": "serial", + "primaryKey": true, + "notNull": true + }, + "value": { + "name": "value", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_stats_items_order_idx": { + "name": "_landing_page_v_blocks_stats_items_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_stats_items_parent_id_idx": { + "name": "_landing_page_v_blocks_stats_items_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_stats_items_parent_id_fk": { + "name": "_landing_page_v_blocks_stats_items_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_stats_items", + "tableTo": "_landing_page_v_blocks_stats", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_stats": { + "name": "_landing_page_v_blocks_stats", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Built To Demo Well'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'These are the kinds of outcomes you can point to when showing the template to clients.'" + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_stats_order_idx": { + "name": "_landing_page_v_blocks_stats_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_stats_parent_id_idx": { + "name": "_landing_page_v_blocks_stats_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_stats_path_idx": { + "name": "_landing_page_v_blocks_stats_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_stats_parent_id_fk": { + "name": "_landing_page_v_blocks_stats_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_stats", + "tableTo": "_landing_page_v", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_tech_stack_categories_technologies": { + "name": "_landing_page_v_blocks_tech_stack_categories_technologies", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_tech_stack_categories_technologies_order_idx": { + "name": "_landing_page_v_blocks_tech_stack_categories_technologies_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_tech_stack_categories_technologies_parent_id_idx": { + "name": "_landing_page_v_blocks_tech_stack_categories_technologies_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_tech_stack_categories_technologies_parent_id_fk": { + "name": "_landing_page_v_blocks_tech_stack_categories_technologies_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_tech_stack_categories_technologies", + "tableTo": "_landing_page_v_blocks_tech_stack_categories", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_tech_stack_categories": { + "name": "_landing_page_v_blocks_tech_stack_categories", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "category": { + "name": "category", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_tech_stack_categories_order_idx": { + "name": "_landing_page_v_blocks_tech_stack_categories_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_tech_stack_categories_parent_id_idx": { + "name": "_landing_page_v_blocks_tech_stack_categories_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_tech_stack_categories_parent_id_fk": { + "name": "_landing_page_v_blocks_tech_stack_categories_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_tech_stack_categories", + "tableTo": "_landing_page_v_blocks_tech_stack", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_tech_stack": { + "name": "_landing_page_v_blocks_tech_stack", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Modern Tech Stack'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Built with the latest and greatest tools for maximum productivity and performance.'" + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_tech_stack_order_idx": { + "name": "_landing_page_v_blocks_tech_stack_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_tech_stack_parent_id_idx": { + "name": "_landing_page_v_blocks_tech_stack_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_tech_stack_path_idx": { + "name": "_landing_page_v_blocks_tech_stack_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_tech_stack_parent_id_fk": { + "name": "_landing_page_v_blocks_tech_stack_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_tech_stack", + "tableTo": "_landing_page_v", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_testimonials_items": { + "name": "_landing_page_v_blocks_testimonials_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": "serial", + "primaryKey": true, + "notNull": true + }, + "quote": { + "name": "quote", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "company": { + "name": "company", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "avatar_emoji": { + "name": "avatar_emoji", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_testimonials_items_order_idx": { + "name": "_landing_page_v_blocks_testimonials_items_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_testimonials_items_parent_id_idx": { + "name": "_landing_page_v_blocks_testimonials_items_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_testimonials_items_parent_id_fk": { + "name": "_landing_page_v_blocks_testimonials_items_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_testimonials_items", + "tableTo": "_landing_page_v_blocks_testimonials", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_testimonials": { + "name": "_landing_page_v_blocks_testimonials", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'What Clients Actually Care About'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'The polished part is not just the frontend. Editors can shape the story, launch updates fast, and preview changes before they go live.'" + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_testimonials_order_idx": { + "name": "_landing_page_v_blocks_testimonials_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_testimonials_parent_id_idx": { + "name": "_landing_page_v_blocks_testimonials_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_testimonials_path_idx": { + "name": "_landing_page_v_blocks_testimonials_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_testimonials_parent_id_fk": { + "name": "_landing_page_v_blocks_testimonials_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_testimonials", + "tableTo": "_landing_page_v", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_pricing_plans_features": { + "name": "_landing_page_v_blocks_pricing_plans_features", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_pricing_plans_features_order_idx": { + "name": "_landing_page_v_blocks_pricing_plans_features_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_pricing_plans_features_parent_id_idx": { + "name": "_landing_page_v_blocks_pricing_plans_features_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_pricing_plans_features_parent_id_fk": { + "name": "_landing_page_v_blocks_pricing_plans_features_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_pricing_plans_features", + "tableTo": "_landing_page_v_blocks_pricing_plans", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_pricing_plans": { + "name": "_landing_page_v_blocks_pricing_plans", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "billing_period": { + "name": "billing_period", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "badge": { + "name": "badge", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "is_highlighted": { + "name": "is_highlighted", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "cta_label": { + "name": "cta_label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "cta_url": { + "name": "cta_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_pricing_plans_order_idx": { + "name": "_landing_page_v_blocks_pricing_plans_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_pricing_plans_parent_id_idx": { + "name": "_landing_page_v_blocks_pricing_plans_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_pricing_plans_parent_id_fk": { + "name": "_landing_page_v_blocks_pricing_plans_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_pricing_plans", + "tableTo": "_landing_page_v_blocks_pricing", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_pricing": { + "name": "_landing_page_v_blocks_pricing", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'A Great Way To Pitch Productized Delivery'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Use pricing blocks to show packaged service tiers, subscriptions, or implementation options right inside the landing page builder.'" + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_pricing_order_idx": { + "name": "_landing_page_v_blocks_pricing_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_pricing_parent_id_idx": { + "name": "_landing_page_v_blocks_pricing_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_pricing_path_idx": { + "name": "_landing_page_v_blocks_pricing_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_pricing_parent_id_fk": { + "name": "_landing_page_v_blocks_pricing_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_pricing", + "tableTo": "_landing_page_v", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_faq_items": { + "name": "_landing_page_v_blocks_faq_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": "serial", + "primaryKey": true, + "notNull": true + }, + "question": { + "name": "question", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "answer": { + "name": "answer", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_faq_items_order_idx": { + "name": "_landing_page_v_blocks_faq_items_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_faq_items_parent_id_idx": { + "name": "_landing_page_v_blocks_faq_items_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_faq_items_parent_id_fk": { + "name": "_landing_page_v_blocks_faq_items_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_faq_items", + "tableTo": "_landing_page_v_blocks_faq", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_faq": { + "name": "_landing_page_v_blocks_faq", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Questions You Will Probably Get In A Demo'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'A good FAQ block makes the editor feel complete and gives clients another obvious content type they can manage themselves.'" + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_faq_order_idx": { + "name": "_landing_page_v_blocks_faq_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_faq_parent_id_idx": { + "name": "_landing_page_v_blocks_faq_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_faq_path_idx": { + "name": "_landing_page_v_blocks_faq_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_faq_parent_id_fk": { + "name": "_landing_page_v_blocks_faq_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_faq", + "tableTo": "_landing_page_v", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v_blocks_cta": { + "name": "_landing_page_v_blocks_cta", + "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": "serial", + "primaryKey": true, + "notNull": true + }, + "heading": { + "name": "heading", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Ready to Build Something Amazing?'" + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Clone the repository and start building your next project with everything pre-configured.'" + }, + "command_label": { + "name": "command_label", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'Quick Start'" + }, + "command": { + "name": "command", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'git clone https://git.gbrown.org/gib/convex-monorepo.git\ncd convex-monorepo\nbun i'" + }, + "_uuid": { + "name": "_uuid", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "block_name": { + "name": "block_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_blocks_cta_order_idx": { + "name": "_landing_page_v_blocks_cta_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_cta_parent_id_idx": { + "name": "_landing_page_v_blocks_cta_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_blocks_cta_path_idx": { + "name": "_landing_page_v_blocks_cta_path_idx", + "columns": [ + { + "expression": "_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_landing_page_v_blocks_cta_parent_id_fk": { + "name": "_landing_page_v_blocks_cta_parent_id_fk", + "tableFrom": "_landing_page_v_blocks_cta", + "tableTo": "_landing_page_v", + "columnsFrom": ["_parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public._landing_page_v": { + "name": "_landing_page_v", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "version__status": { + "name": "version__status", + "type": "enum__landing_page_v_version_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'draft'" + }, + "version_updated_at": { + "name": "version_updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "version_created_at": { + "name": "version_created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "latest": { + "name": "latest", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "autosave": { + "name": "autosave", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_landing_page_v_version_version__status_idx": { + "name": "_landing_page_v_version_version__status_idx", + "columns": [ + { + "expression": "version__status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_created_at_idx": { + "name": "_landing_page_v_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_updated_at_idx": { + "name": "_landing_page_v_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_latest_idx": { + "name": "_landing_page_v_latest_idx", + "columns": [ + { + "expression": "latest", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_landing_page_v_autosave_idx": { + "name": "_landing_page_v_autosave_idx", + "columns": [ + { + "expression": "autosave", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.enum_landing_page_status": { + "name": "enum_landing_page_status", + "schema": "public", + "values": ["draft", "published"] + }, + "public.enum__landing_page_v_version_status": { + "name": "enum__landing_page_v_version_status", + "schema": "public", + "values": ["draft", "published"] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "id": "a49a70a0-f700-463b-a2a6-a46414ff2455", + "prevId": "00000000-0000-0000-0000-000000000000" +} diff --git a/apps/next/src/migrations/20261001_183341_baseline.ts b/apps/next/src/migrations/20261001_183341_baseline.ts new file mode 100644 index 0000000..dd2b607 --- /dev/null +++ b/apps/next/src/migrations/20261001_183341_baseline.ts @@ -0,0 +1,710 @@ +import { MigrateDownArgs, MigrateUpArgs, sql } from '@payloadcms/db-postgres'; + +export const up = async ({ + db, + payload, + req, +}: MigrateUpArgs): Promise => { + await db.execute(sql` + CREATE TYPE "public"."enum_landing_page_status" AS ENUM('draft', 'published'); + CREATE TYPE "public"."enum__landing_page_v_version_status" AS ENUM('draft', 'published'); + CREATE TABLE "users_sessions" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "created_at" timestamp(3) with time zone, + "expires_at" timestamp(3) with time zone NOT NULL + ); + + CREATE TABLE "users" ( + "id" serial PRIMARY KEY NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "email" varchar NOT NULL, + "reset_password_token" varchar, + "reset_password_expiration" timestamp(3) with time zone, + "salt" varchar, + "hash" varchar, + "reset_password_requested_at" timestamp(3) with time zone, + "login_attempts" numeric DEFAULT 0, + "lock_until" timestamp(3) with time zone + ); + + CREATE TABLE "payload_kv" ( + "id" serial PRIMARY KEY NOT NULL, + "key" varchar NOT NULL, + "data" jsonb NOT NULL + ); + + CREATE TABLE "payload_locked_documents" ( + "id" serial PRIMARY KEY NOT NULL, + "global_slug" varchar, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload_locked_documents_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "users_id" integer + ); + + CREATE TABLE "payload_preferences" ( + "id" serial PRIMARY KEY NOT NULL, + "key" varchar, + "value" jsonb, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload_preferences_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "users_id" integer + ); + + CREATE TABLE "payload_migrations" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar, + "batch" numeric, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "landing_page_blocks_hero_highlights" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "label" varchar + ); + + CREATE TABLE "landing_page_blocks_hero" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "badge_emoji" varchar DEFAULT '๐Ÿš€', + "badge_text" varchar DEFAULT 'Production-ready monorepo template', + "heading_prefix" varchar DEFAULT 'Build Full-Stack Apps with', + "heading_highlight" varchar DEFAULT 'convex-monorepo', + "description" varchar DEFAULT 'A Turborepo starter with Next.js, Expo, and self-hosted Convex. Ship web and mobile apps faster with shared code, type-safe backend, and complete control over your infrastructure.', + "primary_cta_label" varchar DEFAULT 'View Source Code', + "primary_cta_url" varchar DEFAULT 'https://git.gbrown.org/gib/convex-monorepo', + "block_name" varchar + ); + + CREATE TABLE "landing_page_blocks_logo_cloud_items" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "name" varchar, + "accent" varchar + ); + + CREATE TABLE "landing_page_blocks_logo_cloud" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Designed For Modern Product Teams', + "block_name" varchar + ); + + CREATE TABLE "landing_page_blocks_features_items" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "icon" varchar, + "title" varchar, + "description" varchar + ); + + CREATE TABLE "landing_page_blocks_features" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Everything You Need to Ship Fast', + "description" varchar DEFAULT 'A complete monorepo template with all the tools and patterns you need for production-ready applications.', + "block_name" varchar + ); + + CREATE TABLE "landing_page_blocks_stats_items" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "value" varchar, + "label" varchar, + "description" varchar + ); + + CREATE TABLE "landing_page_blocks_stats" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Built To Demo Well', + "description" varchar DEFAULT 'These are the kinds of outcomes you can point to when showing the template to clients.', + "block_name" varchar + ); + + CREATE TABLE "landing_page_blocks_tech_stack_categories_technologies" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "name" varchar, + "description" varchar + ); + + CREATE TABLE "landing_page_blocks_tech_stack_categories" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "category" varchar + ); + + CREATE TABLE "landing_page_blocks_tech_stack" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Modern Tech Stack', + "description" varchar DEFAULT 'Built with the latest and greatest tools for maximum productivity and performance.', + "block_name" varchar + ); + + CREATE TABLE "landing_page_blocks_testimonials_items" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "quote" varchar, + "name" varchar, + "role" varchar, + "company" varchar, + "avatar_emoji" varchar + ); + + CREATE TABLE "landing_page_blocks_testimonials" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'What Clients Actually Care About', + "description" varchar DEFAULT 'The polished part is not just the frontend. Editors can shape the story, launch updates fast, and preview changes before they go live.', + "block_name" varchar + ); + + CREATE TABLE "landing_page_blocks_pricing_plans_features" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "label" varchar + ); + + CREATE TABLE "landing_page_blocks_pricing_plans" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "name" varchar, + "price" varchar, + "billing_period" varchar, + "description" varchar, + "badge" varchar, + "is_highlighted" boolean, + "cta_label" varchar, + "cta_url" varchar + ); + + CREATE TABLE "landing_page_blocks_pricing" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'A Great Way To Pitch Productized Delivery', + "description" varchar DEFAULT 'Use pricing blocks to show packaged service tiers, subscriptions, or implementation options right inside the landing page builder.', + "block_name" varchar + ); + + CREATE TABLE "landing_page_blocks_faq_items" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "question" varchar, + "answer" varchar + ); + + CREATE TABLE "landing_page_blocks_faq" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Questions You Will Probably Get In A Demo', + "description" varchar DEFAULT 'A good FAQ block makes the editor feel complete and gives clients another obvious content type they can manage themselves.', + "block_name" varchar + ); + + CREATE TABLE "landing_page_blocks_cta" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Ready to Build Something Amazing?', + "description" varchar DEFAULT 'Clone the repository and start building your next project with everything pre-configured.', + "command_label" varchar DEFAULT 'Quick Start', + "command" varchar DEFAULT 'git clone https://git.gbrown.org/gib/convex-monorepo.git + cd convex-monorepo + bun i', + "block_name" varchar + ); + + CREATE TABLE "landing_page" ( + "id" serial PRIMARY KEY NOT NULL, + "_status" "enum_landing_page_status" DEFAULT 'draft', + "updated_at" timestamp(3) with time zone, + "created_at" timestamp(3) with time zone + ); + + CREATE TABLE "_landing_page_v_blocks_hero_highlights" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "label" varchar, + "_uuid" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_hero" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "badge_emoji" varchar DEFAULT '๐Ÿš€', + "badge_text" varchar DEFAULT 'Production-ready monorepo template', + "heading_prefix" varchar DEFAULT 'Build Full-Stack Apps with', + "heading_highlight" varchar DEFAULT 'convex-monorepo', + "description" varchar DEFAULT 'A Turborepo starter with Next.js, Expo, and self-hosted Convex. Ship web and mobile apps faster with shared code, type-safe backend, and complete control over your infrastructure.', + "primary_cta_label" varchar DEFAULT 'View Source Code', + "primary_cta_url" varchar DEFAULT 'https://git.gbrown.org/gib/convex-monorepo', + "_uuid" varchar, + "block_name" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_logo_cloud_items" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "name" varchar, + "accent" varchar, + "_uuid" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_logo_cloud" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Designed For Modern Product Teams', + "_uuid" varchar, + "block_name" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_features_items" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "icon" varchar, + "title" varchar, + "description" varchar, + "_uuid" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_features" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Everything You Need to Ship Fast', + "description" varchar DEFAULT 'A complete monorepo template with all the tools and patterns you need for production-ready applications.', + "_uuid" varchar, + "block_name" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_stats_items" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "value" varchar, + "label" varchar, + "description" varchar, + "_uuid" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_stats" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Built To Demo Well', + "description" varchar DEFAULT 'These are the kinds of outcomes you can point to when showing the template to clients.', + "_uuid" varchar, + "block_name" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_tech_stack_categories_technologies" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "name" varchar, + "description" varchar, + "_uuid" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_tech_stack_categories" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "category" varchar, + "_uuid" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_tech_stack" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Modern Tech Stack', + "description" varchar DEFAULT 'Built with the latest and greatest tools for maximum productivity and performance.', + "_uuid" varchar, + "block_name" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_testimonials_items" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "quote" varchar, + "name" varchar, + "role" varchar, + "company" varchar, + "avatar_emoji" varchar, + "_uuid" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_testimonials" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'What Clients Actually Care About', + "description" varchar DEFAULT 'The polished part is not just the frontend. Editors can shape the story, launch updates fast, and preview changes before they go live.', + "_uuid" varchar, + "block_name" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_pricing_plans_features" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "label" varchar, + "_uuid" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_pricing_plans" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "name" varchar, + "price" varchar, + "billing_period" varchar, + "description" varchar, + "badge" varchar, + "is_highlighted" boolean, + "cta_label" varchar, + "cta_url" varchar, + "_uuid" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_pricing" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'A Great Way To Pitch Productized Delivery', + "description" varchar DEFAULT 'Use pricing blocks to show packaged service tiers, subscriptions, or implementation options right inside the landing page builder.', + "_uuid" varchar, + "block_name" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_faq_items" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "question" varchar, + "answer" varchar, + "_uuid" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_faq" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Questions You Will Probably Get In A Demo', + "description" varchar DEFAULT 'A good FAQ block makes the editor feel complete and gives clients another obvious content type they can manage themselves.', + "_uuid" varchar, + "block_name" varchar + ); + + CREATE TABLE "_landing_page_v_blocks_cta" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" serial PRIMARY KEY NOT NULL, + "heading" varchar DEFAULT 'Ready to Build Something Amazing?', + "description" varchar DEFAULT 'Clone the repository and start building your next project with everything pre-configured.', + "command_label" varchar DEFAULT 'Quick Start', + "command" varchar DEFAULT 'git clone https://git.gbrown.org/gib/convex-monorepo.git + cd convex-monorepo + bun i', + "_uuid" varchar, + "block_name" varchar + ); + + CREATE TABLE "_landing_page_v" ( + "id" serial PRIMARY KEY NOT NULL, + "version__status" "enum__landing_page_v_version_status" DEFAULT 'draft', + "version_updated_at" timestamp(3) with time zone, + "version_created_at" timestamp(3) with time zone, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "latest" boolean, + "autosave" boolean + ); + + ALTER TABLE "users_sessions" ADD CONSTRAINT "users_sessions_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_locked_documents"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_preferences"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_hero_highlights" ADD CONSTRAINT "landing_page_blocks_hero_highlights_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page_blocks_hero"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_hero" ADD CONSTRAINT "landing_page_blocks_hero_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_logo_cloud_items" ADD CONSTRAINT "landing_page_blocks_logo_cloud_items_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page_blocks_logo_cloud"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_logo_cloud" ADD CONSTRAINT "landing_page_blocks_logo_cloud_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_features_items" ADD CONSTRAINT "landing_page_blocks_features_items_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page_blocks_features"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_features" ADD CONSTRAINT "landing_page_blocks_features_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_stats_items" ADD CONSTRAINT "landing_page_blocks_stats_items_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page_blocks_stats"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_stats" ADD CONSTRAINT "landing_page_blocks_stats_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_tech_stack_categories_technologies" ADD CONSTRAINT "landing_page_blocks_tech_stack_categories_technologies_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page_blocks_tech_stack_categories"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_tech_stack_categories" ADD CONSTRAINT "landing_page_blocks_tech_stack_categories_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page_blocks_tech_stack"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_tech_stack" ADD CONSTRAINT "landing_page_blocks_tech_stack_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_testimonials_items" ADD CONSTRAINT "landing_page_blocks_testimonials_items_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page_blocks_testimonials"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_testimonials" ADD CONSTRAINT "landing_page_blocks_testimonials_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_pricing_plans_features" ADD CONSTRAINT "landing_page_blocks_pricing_plans_features_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page_blocks_pricing_plans"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_pricing_plans" ADD CONSTRAINT "landing_page_blocks_pricing_plans_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page_blocks_pricing"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_pricing" ADD CONSTRAINT "landing_page_blocks_pricing_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_faq_items" ADD CONSTRAINT "landing_page_blocks_faq_items_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page_blocks_faq"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_faq" ADD CONSTRAINT "landing_page_blocks_faq_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "landing_page_blocks_cta" ADD CONSTRAINT "landing_page_blocks_cta_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."landing_page"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_hero_highlights" ADD CONSTRAINT "_landing_page_v_blocks_hero_highlights_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v_blocks_hero"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_hero" ADD CONSTRAINT "_landing_page_v_blocks_hero_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_logo_cloud_items" ADD CONSTRAINT "_landing_page_v_blocks_logo_cloud_items_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v_blocks_logo_cloud"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_logo_cloud" ADD CONSTRAINT "_landing_page_v_blocks_logo_cloud_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_features_items" ADD CONSTRAINT "_landing_page_v_blocks_features_items_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v_blocks_features"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_features" ADD CONSTRAINT "_landing_page_v_blocks_features_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_stats_items" ADD CONSTRAINT "_landing_page_v_blocks_stats_items_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v_blocks_stats"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_stats" ADD CONSTRAINT "_landing_page_v_blocks_stats_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_tech_stack_categories_technologies" ADD CONSTRAINT "_landing_page_v_blocks_tech_stack_categories_technologies_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v_blocks_tech_stack_categories"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_tech_stack_categories" ADD CONSTRAINT "_landing_page_v_blocks_tech_stack_categories_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v_blocks_tech_stack"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_tech_stack" ADD CONSTRAINT "_landing_page_v_blocks_tech_stack_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_testimonials_items" ADD CONSTRAINT "_landing_page_v_blocks_testimonials_items_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v_blocks_testimonials"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_testimonials" ADD CONSTRAINT "_landing_page_v_blocks_testimonials_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_pricing_plans_features" ADD CONSTRAINT "_landing_page_v_blocks_pricing_plans_features_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v_blocks_pricing_plans"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_pricing_plans" ADD CONSTRAINT "_landing_page_v_blocks_pricing_plans_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v_blocks_pricing"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_pricing" ADD CONSTRAINT "_landing_page_v_blocks_pricing_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_faq_items" ADD CONSTRAINT "_landing_page_v_blocks_faq_items_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v_blocks_faq"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_faq" ADD CONSTRAINT "_landing_page_v_blocks_faq_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "_landing_page_v_blocks_cta" ADD CONSTRAINT "_landing_page_v_blocks_cta_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."_landing_page_v"("id") ON DELETE cascade ON UPDATE no action; + CREATE INDEX "users_sessions_order_idx" ON "users_sessions" USING btree ("_order"); + CREATE INDEX "users_sessions_parent_id_idx" ON "users_sessions" USING btree ("_parent_id"); + CREATE INDEX "users_updated_at_idx" ON "users" USING btree ("updated_at"); + CREATE INDEX "users_created_at_idx" ON "users" USING btree ("created_at"); + CREATE UNIQUE INDEX "users_email_idx" ON "users" USING btree ("email"); + CREATE UNIQUE INDEX "payload_kv_key_idx" ON "payload_kv" USING btree ("key"); + CREATE INDEX "payload_locked_documents_global_slug_idx" ON "payload_locked_documents" USING btree ("global_slug"); + CREATE INDEX "payload_locked_documents_updated_at_idx" ON "payload_locked_documents" USING btree ("updated_at"); + CREATE INDEX "payload_locked_documents_created_at_idx" ON "payload_locked_documents" USING btree ("created_at"); + CREATE INDEX "payload_locked_documents_rels_order_idx" ON "payload_locked_documents_rels" USING btree ("order"); + CREATE INDEX "payload_locked_documents_rels_parent_idx" ON "payload_locked_documents_rels" USING btree ("parent_id"); + CREATE INDEX "payload_locked_documents_rels_path_idx" ON "payload_locked_documents_rels" USING btree ("path"); + CREATE INDEX "payload_locked_documents_rels_users_id_idx" ON "payload_locked_documents_rels" USING btree ("users_id"); + CREATE INDEX "payload_preferences_key_idx" ON "payload_preferences" USING btree ("key"); + CREATE INDEX "payload_preferences_updated_at_idx" ON "payload_preferences" USING btree ("updated_at"); + CREATE INDEX "payload_preferences_created_at_idx" ON "payload_preferences" USING btree ("created_at"); + CREATE INDEX "payload_preferences_rels_order_idx" ON "payload_preferences_rels" USING btree ("order"); + CREATE INDEX "payload_preferences_rels_parent_idx" ON "payload_preferences_rels" USING btree ("parent_id"); + CREATE INDEX "payload_preferences_rels_path_idx" ON "payload_preferences_rels" USING btree ("path"); + CREATE INDEX "payload_preferences_rels_users_id_idx" ON "payload_preferences_rels" USING btree ("users_id"); + CREATE INDEX "payload_migrations_updated_at_idx" ON "payload_migrations" USING btree ("updated_at"); + CREATE INDEX "payload_migrations_created_at_idx" ON "payload_migrations" USING btree ("created_at"); + CREATE INDEX "landing_page_blocks_hero_highlights_order_idx" ON "landing_page_blocks_hero_highlights" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_hero_highlights_parent_id_idx" ON "landing_page_blocks_hero_highlights" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_hero_order_idx" ON "landing_page_blocks_hero" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_hero_parent_id_idx" ON "landing_page_blocks_hero" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_hero_path_idx" ON "landing_page_blocks_hero" USING btree ("_path"); + CREATE INDEX "landing_page_blocks_logo_cloud_items_order_idx" ON "landing_page_blocks_logo_cloud_items" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_logo_cloud_items_parent_id_idx" ON "landing_page_blocks_logo_cloud_items" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_logo_cloud_order_idx" ON "landing_page_blocks_logo_cloud" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_logo_cloud_parent_id_idx" ON "landing_page_blocks_logo_cloud" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_logo_cloud_path_idx" ON "landing_page_blocks_logo_cloud" USING btree ("_path"); + CREATE INDEX "landing_page_blocks_features_items_order_idx" ON "landing_page_blocks_features_items" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_features_items_parent_id_idx" ON "landing_page_blocks_features_items" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_features_order_idx" ON "landing_page_blocks_features" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_features_parent_id_idx" ON "landing_page_blocks_features" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_features_path_idx" ON "landing_page_blocks_features" USING btree ("_path"); + CREATE INDEX "landing_page_blocks_stats_items_order_idx" ON "landing_page_blocks_stats_items" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_stats_items_parent_id_idx" ON "landing_page_blocks_stats_items" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_stats_order_idx" ON "landing_page_blocks_stats" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_stats_parent_id_idx" ON "landing_page_blocks_stats" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_stats_path_idx" ON "landing_page_blocks_stats" USING btree ("_path"); + CREATE INDEX "landing_page_blocks_tech_stack_categories_technologies_order_idx" ON "landing_page_blocks_tech_stack_categories_technologies" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_tech_stack_categories_technologies_parent_id_idx" ON "landing_page_blocks_tech_stack_categories_technologies" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_tech_stack_categories_order_idx" ON "landing_page_blocks_tech_stack_categories" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_tech_stack_categories_parent_id_idx" ON "landing_page_blocks_tech_stack_categories" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_tech_stack_order_idx" ON "landing_page_blocks_tech_stack" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_tech_stack_parent_id_idx" ON "landing_page_blocks_tech_stack" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_tech_stack_path_idx" ON "landing_page_blocks_tech_stack" USING btree ("_path"); + CREATE INDEX "landing_page_blocks_testimonials_items_order_idx" ON "landing_page_blocks_testimonials_items" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_testimonials_items_parent_id_idx" ON "landing_page_blocks_testimonials_items" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_testimonials_order_idx" ON "landing_page_blocks_testimonials" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_testimonials_parent_id_idx" ON "landing_page_blocks_testimonials" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_testimonials_path_idx" ON "landing_page_blocks_testimonials" USING btree ("_path"); + CREATE INDEX "landing_page_blocks_pricing_plans_features_order_idx" ON "landing_page_blocks_pricing_plans_features" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_pricing_plans_features_parent_id_idx" ON "landing_page_blocks_pricing_plans_features" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_pricing_plans_order_idx" ON "landing_page_blocks_pricing_plans" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_pricing_plans_parent_id_idx" ON "landing_page_blocks_pricing_plans" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_pricing_order_idx" ON "landing_page_blocks_pricing" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_pricing_parent_id_idx" ON "landing_page_blocks_pricing" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_pricing_path_idx" ON "landing_page_blocks_pricing" USING btree ("_path"); + CREATE INDEX "landing_page_blocks_faq_items_order_idx" ON "landing_page_blocks_faq_items" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_faq_items_parent_id_idx" ON "landing_page_blocks_faq_items" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_faq_order_idx" ON "landing_page_blocks_faq" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_faq_parent_id_idx" ON "landing_page_blocks_faq" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_faq_path_idx" ON "landing_page_blocks_faq" USING btree ("_path"); + CREATE INDEX "landing_page_blocks_cta_order_idx" ON "landing_page_blocks_cta" USING btree ("_order"); + CREATE INDEX "landing_page_blocks_cta_parent_id_idx" ON "landing_page_blocks_cta" USING btree ("_parent_id"); + CREATE INDEX "landing_page_blocks_cta_path_idx" ON "landing_page_blocks_cta" USING btree ("_path"); + CREATE INDEX "landing_page__status_idx" ON "landing_page" USING btree ("_status"); + CREATE INDEX "_landing_page_v_blocks_hero_highlights_order_idx" ON "_landing_page_v_blocks_hero_highlights" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_hero_highlights_parent_id_idx" ON "_landing_page_v_blocks_hero_highlights" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_hero_order_idx" ON "_landing_page_v_blocks_hero" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_hero_parent_id_idx" ON "_landing_page_v_blocks_hero" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_hero_path_idx" ON "_landing_page_v_blocks_hero" USING btree ("_path"); + CREATE INDEX "_landing_page_v_blocks_logo_cloud_items_order_idx" ON "_landing_page_v_blocks_logo_cloud_items" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_logo_cloud_items_parent_id_idx" ON "_landing_page_v_blocks_logo_cloud_items" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_logo_cloud_order_idx" ON "_landing_page_v_blocks_logo_cloud" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_logo_cloud_parent_id_idx" ON "_landing_page_v_blocks_logo_cloud" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_logo_cloud_path_idx" ON "_landing_page_v_blocks_logo_cloud" USING btree ("_path"); + CREATE INDEX "_landing_page_v_blocks_features_items_order_idx" ON "_landing_page_v_blocks_features_items" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_features_items_parent_id_idx" ON "_landing_page_v_blocks_features_items" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_features_order_idx" ON "_landing_page_v_blocks_features" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_features_parent_id_idx" ON "_landing_page_v_blocks_features" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_features_path_idx" ON "_landing_page_v_blocks_features" USING btree ("_path"); + CREATE INDEX "_landing_page_v_blocks_stats_items_order_idx" ON "_landing_page_v_blocks_stats_items" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_stats_items_parent_id_idx" ON "_landing_page_v_blocks_stats_items" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_stats_order_idx" ON "_landing_page_v_blocks_stats" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_stats_parent_id_idx" ON "_landing_page_v_blocks_stats" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_stats_path_idx" ON "_landing_page_v_blocks_stats" USING btree ("_path"); + CREATE INDEX "_landing_page_v_blocks_tech_stack_categories_technologies_order_idx" ON "_landing_page_v_blocks_tech_stack_categories_technologies" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_tech_stack_categories_technologies_parent_id_idx" ON "_landing_page_v_blocks_tech_stack_categories_technologies" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_tech_stack_categories_order_idx" ON "_landing_page_v_blocks_tech_stack_categories" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_tech_stack_categories_parent_id_idx" ON "_landing_page_v_blocks_tech_stack_categories" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_tech_stack_order_idx" ON "_landing_page_v_blocks_tech_stack" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_tech_stack_parent_id_idx" ON "_landing_page_v_blocks_tech_stack" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_tech_stack_path_idx" ON "_landing_page_v_blocks_tech_stack" USING btree ("_path"); + CREATE INDEX "_landing_page_v_blocks_testimonials_items_order_idx" ON "_landing_page_v_blocks_testimonials_items" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_testimonials_items_parent_id_idx" ON "_landing_page_v_blocks_testimonials_items" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_testimonials_order_idx" ON "_landing_page_v_blocks_testimonials" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_testimonials_parent_id_idx" ON "_landing_page_v_blocks_testimonials" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_testimonials_path_idx" ON "_landing_page_v_blocks_testimonials" USING btree ("_path"); + CREATE INDEX "_landing_page_v_blocks_pricing_plans_features_order_idx" ON "_landing_page_v_blocks_pricing_plans_features" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_pricing_plans_features_parent_id_idx" ON "_landing_page_v_blocks_pricing_plans_features" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_pricing_plans_order_idx" ON "_landing_page_v_blocks_pricing_plans" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_pricing_plans_parent_id_idx" ON "_landing_page_v_blocks_pricing_plans" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_pricing_order_idx" ON "_landing_page_v_blocks_pricing" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_pricing_parent_id_idx" ON "_landing_page_v_blocks_pricing" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_pricing_path_idx" ON "_landing_page_v_blocks_pricing" USING btree ("_path"); + CREATE INDEX "_landing_page_v_blocks_faq_items_order_idx" ON "_landing_page_v_blocks_faq_items" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_faq_items_parent_id_idx" ON "_landing_page_v_blocks_faq_items" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_faq_order_idx" ON "_landing_page_v_blocks_faq" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_faq_parent_id_idx" ON "_landing_page_v_blocks_faq" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_faq_path_idx" ON "_landing_page_v_blocks_faq" USING btree ("_path"); + CREATE INDEX "_landing_page_v_blocks_cta_order_idx" ON "_landing_page_v_blocks_cta" USING btree ("_order"); + CREATE INDEX "_landing_page_v_blocks_cta_parent_id_idx" ON "_landing_page_v_blocks_cta" USING btree ("_parent_id"); + CREATE INDEX "_landing_page_v_blocks_cta_path_idx" ON "_landing_page_v_blocks_cta" USING btree ("_path"); + CREATE INDEX "_landing_page_v_version_version__status_idx" ON "_landing_page_v" USING btree ("version__status"); + CREATE INDEX "_landing_page_v_created_at_idx" ON "_landing_page_v" USING btree ("created_at"); + CREATE INDEX "_landing_page_v_updated_at_idx" ON "_landing_page_v" USING btree ("updated_at"); + CREATE INDEX "_landing_page_v_latest_idx" ON "_landing_page_v" USING btree ("latest"); + CREATE INDEX "_landing_page_v_autosave_idx" ON "_landing_page_v" USING btree ("autosave");`); +}; + +export const down = async ({ + db, + payload, + req, +}: MigrateDownArgs): Promise => { + await db.execute(sql` + DROP TABLE "users_sessions" CASCADE; + DROP TABLE "users" CASCADE; + DROP TABLE "payload_kv" CASCADE; + DROP TABLE "payload_locked_documents" CASCADE; + DROP TABLE "payload_locked_documents_rels" CASCADE; + DROP TABLE "payload_preferences" CASCADE; + DROP TABLE "payload_preferences_rels" CASCADE; + DROP TABLE "payload_migrations" CASCADE; + DROP TABLE "landing_page_blocks_hero_highlights" CASCADE; + DROP TABLE "landing_page_blocks_hero" CASCADE; + DROP TABLE "landing_page_blocks_logo_cloud_items" CASCADE; + DROP TABLE "landing_page_blocks_logo_cloud" CASCADE; + DROP TABLE "landing_page_blocks_features_items" CASCADE; + DROP TABLE "landing_page_blocks_features" CASCADE; + DROP TABLE "landing_page_blocks_stats_items" CASCADE; + DROP TABLE "landing_page_blocks_stats" CASCADE; + DROP TABLE "landing_page_blocks_tech_stack_categories_technologies" CASCADE; + DROP TABLE "landing_page_blocks_tech_stack_categories" CASCADE; + DROP TABLE "landing_page_blocks_tech_stack" CASCADE; + DROP TABLE "landing_page_blocks_testimonials_items" CASCADE; + DROP TABLE "landing_page_blocks_testimonials" CASCADE; + DROP TABLE "landing_page_blocks_pricing_plans_features" CASCADE; + DROP TABLE "landing_page_blocks_pricing_plans" CASCADE; + DROP TABLE "landing_page_blocks_pricing" CASCADE; + DROP TABLE "landing_page_blocks_faq_items" CASCADE; + DROP TABLE "landing_page_blocks_faq" CASCADE; + DROP TABLE "landing_page_blocks_cta" CASCADE; + DROP TABLE "landing_page" CASCADE; + DROP TABLE "_landing_page_v_blocks_hero_highlights" CASCADE; + DROP TABLE "_landing_page_v_blocks_hero" CASCADE; + DROP TABLE "_landing_page_v_blocks_logo_cloud_items" CASCADE; + DROP TABLE "_landing_page_v_blocks_logo_cloud" CASCADE; + DROP TABLE "_landing_page_v_blocks_features_items" CASCADE; + DROP TABLE "_landing_page_v_blocks_features" CASCADE; + DROP TABLE "_landing_page_v_blocks_stats_items" CASCADE; + DROP TABLE "_landing_page_v_blocks_stats" CASCADE; + DROP TABLE "_landing_page_v_blocks_tech_stack_categories_technologies" CASCADE; + DROP TABLE "_landing_page_v_blocks_tech_stack_categories" CASCADE; + DROP TABLE "_landing_page_v_blocks_tech_stack" CASCADE; + DROP TABLE "_landing_page_v_blocks_testimonials_items" CASCADE; + DROP TABLE "_landing_page_v_blocks_testimonials" CASCADE; + DROP TABLE "_landing_page_v_blocks_pricing_plans_features" CASCADE; + DROP TABLE "_landing_page_v_blocks_pricing_plans" CASCADE; + DROP TABLE "_landing_page_v_blocks_pricing" CASCADE; + DROP TABLE "_landing_page_v_blocks_faq_items" CASCADE; + DROP TABLE "_landing_page_v_blocks_faq" CASCADE; + DROP TABLE "_landing_page_v_blocks_cta" CASCADE; + DROP TABLE "_landing_page_v" CASCADE; + DROP TYPE "public"."enum_landing_page_status"; + DROP TYPE "public"."enum__landing_page_v_version_status";`); +}; diff --git a/apps/next/src/migrations/index.ts b/apps/next/src/migrations/index.ts new file mode 100644 index 0000000..08cf3ae --- /dev/null +++ b/apps/next/src/migrations/index.ts @@ -0,0 +1,9 @@ +import * as migration_20261001_183341_baseline from './20261001_183341_baseline'; + +export const migrations = [ + { + up: migration_20261001_183341_baseline.up, + down: migration_20261001_183341_baseline.down, + name: '20261001_183341_baseline', + }, +]; diff --git a/apps/next/src/payload.config.ts b/apps/next/src/payload.config.ts index 1f0a8cd..c67daae 100644 --- a/apps/next/src/payload.config.ts +++ b/apps/next/src/payload.config.ts @@ -4,6 +4,7 @@ import { buildConfig } from 'payload'; import sharp from 'sharp'; import { env } from './env'; +import { migrations } from './migrations'; import { Users } from './payload/collections/users'; import { LandingPage } from './payload/globals/landing-page'; @@ -17,6 +18,15 @@ export default buildConfig({ pool: { connectionString: env.PAYLOAD_DB_URL, }, + // Schema changes ship as migrations (`bunx payload migrate:create ` + // from apps/next, through `bun with-env`). Production runs pending ones on + // startup. Dev push only ever touches a local database: a push against the + // production database would mark it as push-managed, and the next startup + // migration would stop on Payload's confirmation prompt. + push: ['localhost', '127.0.0.1'].includes( + new URL(env.PAYLOAD_DB_URL).hostname, + ), + prodMigrations: migrations, }), sharp, }); diff --git a/docker/Dockerfile b/docker/Dockerfile index 7ab2cd2..22356e5 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,16 +1,18 @@ # syntax=docker/dockerfile:1 -FROM docker.io/oven/bun:1.3.10-alpine AS base +FROM docker.io/oven/bun:1.3.14-alpine AS base # Builder stage FROM base AS builder -RUN apk add --no-cache libc6-compat +RUN apk add --no-cache curl libc6-compat WORKDIR /app ARG SENTRY_AUTH_TOKEN ARG SENTRY_DISABLE_AUTO_UPLOAD=false ARG NEXT_PUBLIC_SITE_URL ARG NEXT_PUBLIC_CONVEX_URL -ARG NEXT_PUBLIC_PLAUSIBLE_URL +ARG NEXT_PUBLIC_PLAUSIBLE_SRC +ARG NEXT_PUBLIC_POSTHOG_KEY +ARG NEXT_PUBLIC_POSTHOG_HOST ARG NEXT_PUBLIC_SENTRY_DSN ARG NEXT_PUBLIC_SENTRY_URL ARG NEXT_PUBLIC_SENTRY_ORG @@ -22,7 +24,9 @@ ENV SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN ENV SENTRY_DISABLE_AUTO_UPLOAD=$SENTRY_DISABLE_AUTO_UPLOAD ENV NEXT_PUBLIC_SITE_URL=$NEXT_PUBLIC_SITE_URL ENV NEXT_PUBLIC_CONVEX_URL=$NEXT_PUBLIC_CONVEX_URL -ENV NEXT_PUBLIC_PLAUSIBLE_URL=$NEXT_PUBLIC_PLAUSIBLE_URL +ENV NEXT_PUBLIC_PLAUSIBLE_SRC=$NEXT_PUBLIC_PLAUSIBLE_SRC +ENV NEXT_PUBLIC_POSTHOG_KEY=$NEXT_PUBLIC_POSTHOG_KEY +ENV NEXT_PUBLIC_POSTHOG_HOST=$NEXT_PUBLIC_POSTHOG_HOST ENV NEXT_PUBLIC_SENTRY_DSN=$NEXT_PUBLIC_SENTRY_DSN ENV NEXT_PUBLIC_SENTRY_URL=$NEXT_PUBLIC_SENTRY_URL ENV NEXT_PUBLIC_SENTRY_ORG=$NEXT_PUBLIC_SENTRY_ORG @@ -40,7 +44,7 @@ RUN bun install --frozen-lockfile # Build with proper environment ENV NEXT_TELEMETRY_DISABLED=1 ENV NODE_ENV=production -RUN cd apps/next && bun run build:docker +RUN sh scripts/build-next-bundle RUN bun scripts/copy-next-runtime-deps.mjs # Runner stage diff --git a/docker/compose.local.yml b/docker/compose.local.yml index fd92f32..eadfc01 100644 --- a/docker/compose.local.yml +++ b/docker/compose.local.yml @@ -4,7 +4,7 @@ services: postgres: image: postgres:17 container_name: convexmonorepo-local-postgres - ports: ['${POSTGRES_PORT:-5432}:5432'] + ports: ['${POSTGRES_PORT:-5436}:5432'] environment: - POSTGRES_USER=${POSTGRES_USER:-convexmonorepo} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-localdev} @@ -22,13 +22,13 @@ services: image: ghcr.io/get-convex/convex-backend:${BACKEND_TAG:-latest} container_name: convexmonorepo-local-convex ports: - - '${BACKEND_PORT:-3210}:3210' - - '${SITE_PROXY_PORT:-3211}:3211' + - '${BACKEND_PORT:-3240}:3210' + - '${SITE_PROXY_PORT:-3241}:3211' environment: - INSTANCE_NAME=${LOCAL_INSTANCE_NAME:-convexmonorepo_local} - INSTANCE_SECRET=${LOCAL_INSTANCE_SECRET:-0000000000000000000000000000000000000000000000000000000000000000} - - CONVEX_CLOUD_ORIGIN=http://localhost:${BACKEND_PORT:-3210} - - CONVEX_SITE_ORIGIN=http://localhost:${SITE_PROXY_PORT:-3211} + - CONVEX_CLOUD_ORIGIN=http://localhost:${BACKEND_PORT:-3240} + - CONVEX_SITE_ORIGIN=http://localhost:${SITE_PROXY_PORT:-3241} - DISABLE_BEACON=true - REDACT_LOGS_TO_CLIENT=false - DO_NOT_REQUIRE_SSL=true @@ -49,9 +49,9 @@ services: convex-dashboard: image: ghcr.io/get-convex/convex-dashboard:${DASHBOARD_TAG:-latest} container_name: convexmonorepo-local-convex-dashboard - ports: ['${DASHBOARD_PORT:-6791}:6791'] + ports: ['${DASHBOARD_PORT:-6794}:6791'] environment: - - NEXT_PUBLIC_DEPLOYMENT_URL=http://localhost:${BACKEND_PORT:-3210} + - NEXT_PUBLIC_DEPLOYMENT_URL=http://localhost:${BACKEND_PORT:-3240} depends_on: convex-backend: condition: service_healthy diff --git a/docker/compose.yml b/docker/compose.yml index 25e512a..a3251c6 100644 --- a/docker/compose.yml +++ b/docker/compose.yml @@ -12,7 +12,9 @@ services: SENTRY_DISABLE_AUTO_UPLOAD: ${SENTRY_DISABLE_AUTO_UPLOAD:-false} NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL} NEXT_PUBLIC_CONVEX_URL: ${NEXT_PUBLIC_CONVEX_URL} - NEXT_PUBLIC_PLAUSIBLE_URL: ${NEXT_PUBLIC_PLAUSIBLE_URL} + NEXT_PUBLIC_PLAUSIBLE_SRC: ${NEXT_PUBLIC_PLAUSIBLE_SRC:-} + NEXT_PUBLIC_POSTHOG_KEY: ${NEXT_PUBLIC_POSTHOG_KEY:-} + NEXT_PUBLIC_POSTHOG_HOST: ${NEXT_PUBLIC_POSTHOG_HOST:-} NEXT_PUBLIC_SENTRY_DSN: ${NEXT_PUBLIC_SENTRY_DSN} NEXT_PUBLIC_SENTRY_URL: ${NEXT_PUBLIC_SENTRY_URL} NEXT_PUBLIC_SENTRY_ORG: ${NEXT_PUBLIC_SENTRY_ORG} @@ -27,7 +29,9 @@ services: - SENTRY_AUTH_TOKEN=${SENTRY_AUTH_TOKEN} - NEXT_PUBLIC_SITE_URL=${NEXT_PUBLIC_SITE_URL:-http://localhost:${NEXT_PORT:-3000}} - NEXT_PUBLIC_CONVEX_URL=${NEXT_PUBLIC_CONVEX_URL:-http://${BACKEND_CONTAINER_NAME:-convex-backend}:${BACKEND_PORT:-3210}} - - NEXT_PUBLIC_PLAUSIBLE_URL=${NEXT_PUBLIC_PLAUSIBLE_URL:-https://plausible.gbrown.org} + - NEXT_PUBLIC_PLAUSIBLE_SRC=${NEXT_PUBLIC_PLAUSIBLE_SRC:-} + - NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY:-} + - NEXT_PUBLIC_POSTHOG_HOST=${NEXT_PUBLIC_POSTHOG_HOST:-} - NEXT_PUBLIC_SENTRY_DSN=${NEXT_PUBLIC_SENTRY_DSN} - NEXT_PUBLIC_SENTRY_URL=${NEXT_PUBLIC_SENTRY_URL} - NEXT_PUBLIC_SENTRY_ORG=${NEXT_PUBLIC_SENTRY_ORG:-sentry} diff --git a/docs/payload-cms.md b/docs/payload-cms.md index 27c5a0b..e863633 100644 --- a/docs/payload-cms.md +++ b/docs/payload-cms.md @@ -6,7 +6,7 @@ the application backend for auth, realtime data, files, and workflows. Configuration lives in `apps/next/src/payload.config.ts`. Payload uses `PAYLOAD_SECRET`, `PAYLOAD_DB_URL`, and `NEXT_PUBLIC_SITE_URL` from the validated environment contract. Local `dev` values come from Infisical and point Payload -to Postgres at `localhost:5432`, because Next runs on the host. +to Postgres at `localhost:5436`, because Next runs on the host. Start the isolated services with `bun db:up`, then run `bun dev:next`. The local database persists across `bun db:down`; `bun db:down:wipe` removes it. Local diff --git a/scripts/build-next-bundle b/scripts/build-next-bundle new file mode 100755 index 0000000..a5597f3 --- /dev/null +++ b/scripts/build-next-bundle @@ -0,0 +1,23 @@ +#!/usr/bin/env sh +set -eu + +ROOT_DIR="$(cd -- "$(dirname -- "$0")/.." && pwd)" + +if [ -n "${SENTRY_AUTH_TOKEN:-}" ] && + [ -n "${NEXT_PUBLIC_SENTRY_URL:-}" ] && + ! curl \ + --connect-timeout 3 \ + --max-time 10 \ + --silent \ + --show-error \ + --output /dev/null \ + "${NEXT_PUBLIC_SENTRY_URL%/}/api/0/"; then + echo "Sentry is unreachable; continuing without source-map upload." >&2 + SENTRY_AUTH_TOKEN="" + SKIP_ENV_VALIDATION=true + NEXT_PUBLIC_SENTRY_URL="http://127.0.0.1:9" + export NEXT_PUBLIC_SENTRY_URL SENTRY_AUTH_TOKEN SKIP_ENV_VALIDATION +fi + +cd "$ROOT_DIR/apps/next" +exec bun run build:docker diff --git a/scripts/ci-export-env.mjs b/scripts/ci-export-env.mjs new file mode 100755 index 0000000..4c1915f --- /dev/null +++ b/scripts/ci-export-env.mjs @@ -0,0 +1,62 @@ +#!/usr/bin/env node +// ci-export-env.mjs [environment] +// +// CI's counterpart to scripts/export-env: prints an Infisical environment +// (default `staging`) as dotenv on stdout, in the same quoting export-env uses. +// It logs in as the `convexmonorepo-ci` machine identity through Universal Auth, using +// INFISICAL_CLIENT_ID and INFISICAL_CLIENT_SECRET from the job's Gitea secrets, +// and reads the project and host from .infisical.json. Plain Node, no CLI to +// install, so it runs in every job whether or not Bun is set up. +import { readFile } from 'node:fs/promises'; + +const environment = process.argv[2] ?? 'staging'; +const { INFISICAL_CLIENT_ID: clientId, INFISICAL_CLIENT_SECRET: clientSecret } = + process.env; + +const fail = (message) => { + console.error(`ci-export-env: ${message}`); + process.exit(1); +}; + +if (!clientId || !clientSecret) + fail('INFISICAL_CLIENT_ID and INFISICAL_CLIENT_SECRET must be set.'); + +const { workspaceId, domain } = JSON.parse( + await readFile(new URL('../.infisical.json', import.meta.url), 'utf8'), +); + +const request = async (path, init) => { + const response = await fetch(`${domain}${path}`, init); + if (!response.ok) + fail(`${init?.method ?? 'GET'} ${path} returned ${response.status}.`); + return response.json(); +}; + +const { accessToken } = await request('/v1/auth/universal-auth/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ clientId, clientSecret }), +}); + +const query = new URLSearchParams({ + workspaceId, + environment, + secretPath: '/', + expandSecretReferences: 'true', + include_imports: 'true', +}); +const { secrets, imports = [] } = await request(`/v3/secrets/raw?${query}`, { + headers: { Authorization: `Bearer ${accessToken}` }, +}); + +// Imported secrets first so the environment's own values win. +const values = new Map(); +for (const { secretKey, secretValue } of [ + ...imports.flatMap((folder) => folder.secrets), + ...secrets, +]) + values.set(secretKey, secretValue); + +const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`; +for (const [key, value] of [...values].sort(([a], [b]) => a.localeCompare(b))) + process.stdout.write(`${key}=${quote(value)}\n`); diff --git a/scripts/ci-install b/scripts/ci-install new file mode 100755 index 0000000..aec0ed2 --- /dev/null +++ b/scripts/ci-install @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Install on a runner, and survive a half-written cache entry. +# +# Runners have failed twice with "Fail extracting tarball" on a different +# package each time, on different machines, and passed on a re-run. The cache +# is shared between jobs on a host, so a job that dies mid-write leaves an +# entry the next job cannot read. Clearing it and installing again costs a +# couple of minutes; a human re-running the job costs however long it takes +# someone to notice. +set -euo pipefail +if bun install --frozen-lockfile; then exit 0; fi +echo "Install failed. Clearing the bun cache and trying once more." >&2 +rm -rf "${BUN_INSTALL_CACHE_DIR:-$HOME/.bun/install/cache}" +bun install --frozen-lockfile diff --git a/scripts/db/up b/scripts/db/up index b370f7e..12d4812 100755 --- a/scripts/db/up +++ b/scripts/db/up @@ -51,9 +51,9 @@ for i in $(seq 1 30); do sleep 1 done -info "Waiting for Convex at http://localhost:${BACKEND_PORT:-3210}" +info "Waiting for Convex at http://localhost:${BACKEND_PORT:-3240}" for i in $(seq 1 60); do - curl -fs "http://localhost:${BACKEND_PORT:-3210}/version" >/dev/null 2>&1 && break + curl -fs "http://localhost:${BACKEND_PORT:-3240}/version" >/dev/null 2>&1 && break [ "$i" -lt 60 ] || die "Convex did not become ready." sleep 2 done @@ -69,6 +69,11 @@ fi info "Deploying Convex schema and functions" (cd "$ROOT_DIR/packages/backend" && bun run setup) +# Env sync runs after the deploy: changing deployment env re-analyzes the +# deployed modules, which fails on a fresh volume before the first deploy. +info "Synchronizing the Convex function runtime environment" +sh "$ROOT_DIR/scripts/with-env" dev -- bash "$ROOT_DIR/scripts/sync-local-convex-env" + convex_env_names="$( sh "$ROOT_DIR/scripts/with-env" dev -- bash -c \ 'cd packages/backend && bunx convex env list' 2>/dev/null \ @@ -92,4 +97,4 @@ fi info "Seeding local Payload from snapshot if needed" bash "$ROOT_DIR/scripts/db/seed-payload" --yes -printf '\nLocal stack ready:\n App: http://localhost:3000\n Convex: http://localhost:%s\n Dashboard: http://localhost:%s\n Payload Postgres: localhost:%s\n' "${BACKEND_PORT:-3210}" "${DASHBOARD_PORT:-6791}" "${POSTGRES_PORT:-5432}" +printf '\nLocal stack ready:\n App: http://localhost:3000\n Convex: http://localhost:%s\n Dashboard: http://localhost:%s\n Payload Postgres: localhost:%s\n' "${BACKEND_PORT:-3240}" "${DASHBOARD_PORT:-6794}" "${POSTGRES_PORT:-5436}" diff --git a/scripts/dev-kill b/scripts/dev-kill new file mode 100755 index 0000000..220af79 --- /dev/null +++ b/scripts/dev-kill @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# +# Kill any lingering host-level dev servers (Next.js, Expo Metro, Convex CLI +# watchers, turbo orchestrators) that survived a Ctrl-C. Does not touch the +# dockerized Convex/Postgres stack โ€” use `bun db:down` for that. +# +# Usage: scripts/dev-kill (wired as `bun dev:kill`) +set -euo pipefail + +info() { printf '\033[36mโ–ถ %s\033[0m\n' "$*"; } +ok() { printf '\033[32mโœ“ %s\033[0m\n' "$*"; } + +PORTS=(3000 3001 8081 19000 19001 19002) +PATTERNS=('next-server' 'next dev' 'expo start' 'turbo run dev' 'convex dev') + +killed=false + +for port in "${PORTS[@]}"; do + pids="$(lsof -ti "tcp:$port" -sTCP:LISTEN 2>/dev/null || true)" + [ -z "$pids" ] && continue + for pid in $pids; do + cmd="$(ps -p "$pid" -o comm= 2>/dev/null || true)" + info "Killing $cmd (pid $pid) on port $port" + kill "$pid" 2>/dev/null || true + killed=true + done +done + +for pattern in "${PATTERNS[@]}"; do + pids="$(pgrep -f "$pattern" 2>/dev/null || true)" + [ -z "$pids" ] && continue + for pid in $pids; do + info "Killing '$pattern' (pid $pid)" + kill "$pid" 2>/dev/null || true + killed=true + done +done + +if [ "$killed" = true ]; then + sleep 1 + ok "Dev servers stopped." +else + ok "No dev servers were running." +fi diff --git a/scripts/export-env b/scripts/export-env index 14412fe..8c5ff7d 100755 --- a/scripts/export-env +++ b/scripts/export-env @@ -16,6 +16,12 @@ fi [ -f "$ROOT_DIR/.infisical.json" ] || { echo "export-env: run 'infisical init' in this repository." >&2; exit 1; } command -v infisical >/dev/null 2>&1 || { echo "export-env: Infisical CLI is required." >&2; exit 1; } +if [ -f "$ROOT_DIR/scripts/infisical-profile" ]; then + # shellcheck source=/dev/null + . "$ROOT_DIR/scripts/infisical-profile" + ensure_infisical_profile "$ROOT_DIR/scripts/flags/infisical.env" +fi + (cd "$ROOT_DIR" && infisical export --env="$ENVIRONMENT" --format=dotenv --silent) || { echo "export-env: failed to export '$ENVIRONMENT'; check login and project access." >&2 exit 1 diff --git a/scripts/infisical-profile b/scripts/infisical-profile new file mode 100755 index 0000000..e9941ad --- /dev/null +++ b/scripts/infisical-profile @@ -0,0 +1,117 @@ +#!/usr/bin/env sh +# +# ensure_infisical_profile +# +# Infisical's CLI keeps a single active login (~/.infisical/infisical-config.json) +# shared across every project on the machine. If you juggle multiple Infisical +# accounts (e.g. a client org vs. a personal org), running with-env in one repo +# can silently pull secrets while logged in as the wrong user. +# +# This switches the active `loggedInUserEmail` to the email configured in +# scripts/flags/infisical.env (INFISICAL_EMAIL=...) before infisical runs, +# provided that email is already logged in locally (`infisical login`). +# It is a no-op if the flag file is absent, only one user is logged in, or the +# expected user isn't logged in yet. +# +# POSIX sh, no bashisms: matches the rest of scripts/ which must stay +# busybox-compatible. + +warn_infisical_profile() { + echo "Warning: infisical-profile: $*" >&2 +} + +ensure_infisical_profile() { + profile_env_file="${1:-}" + + if [ -z "$profile_env_file" ] || [ ! -f "$profile_env_file" ]; then + return 0 + fi + + set -a + # shellcheck source=/dev/null + if ! . "$profile_env_file"; then + set +a + warn_infisical_profile "could not read $profile_env_file. Continuing with current Infisical profile." + return 0 + fi + set +a + + expected_email="${INFISICAL_EMAIL:-}" + if [ -z "$expected_email" ]; then + return 0 + fi + + config="${INFISICAL_CONFIG_PATH:-$HOME/.infisical/infisical-config.json}" + lock="${INFISICAL_LOCK_PATH:-$HOME/.infisical/.switch.lock}" + + if [ ! -f "$config" ]; then + warn_infisical_profile "Infisical config not found at $config. Continuing with current Infisical profile." + return 0 + fi + + if ! command -v jq >/dev/null 2>&1; then + warn_infisical_profile "jq is required to select Infisical profile. Continuing with current Infisical profile." + return 0 + fi + + if ! command -v flock >/dev/null 2>&1; then + warn_infisical_profile "flock is required to safely select Infisical profile. Continuing with current Infisical profile." + return 0 + fi + + if ! mkdir -p "$(dirname "$lock")"; then + warn_infisical_profile "could not create Infisical lock directory. Continuing with current Infisical profile." + return 0 + fi + + ( + if ! flock 9; then + warn_infisical_profile "could not lock Infisical profile config. Continuing with current Infisical profile." + return 0 + fi + + user_count="$(jq -r '(.loggedInUsers // []) | length' "$config" 2>/dev/null)" || { + warn_infisical_profile "could not parse Infisical config. Continuing with current Infisical profile." + return 0 + } + + if [ "$user_count" -lt 2 ]; then + return 0 + fi + + current_email="$(jq -r '.loggedInUserEmail // ""' "$config" 2>/dev/null)" || { + warn_infisical_profile "could not read current Infisical profile. Continuing with current Infisical profile." + return 0 + } + + if [ "$current_email" = "$expected_email" ]; then + return 0 + fi + + domain="$( + jq -er --arg email "$expected_email" \ + '.loggedInUsers[] | select(.email == $email) | .domain' \ + "$config" 2>/dev/null + )" || { + warn_infisical_profile "Infisical profile $expected_email is not logged in locally. Continuing with current Infisical profile." + return 0 + } + + tmp="$(mktemp)" || { + warn_infisical_profile "could not create temp file for Infisical profile switch. Continuing with current Infisical profile." + return 0 + } + + if jq --arg email "$expected_email" --arg domain "$domain" \ + '.loggedInUserEmail = $email | .LoggedInUserDomain = $domain' \ + "$config" > "$tmp"; then + chmod 600 "$tmp" + mv "$tmp" "$config" + else + rm -f "$tmp" + warn_infisical_profile "failed to update Infisical profile. Continuing with current Infisical profile." + fi + ) 9>"$lock" + + return 0 +} diff --git a/scripts/sync-local-convex-env b/scripts/sync-local-convex-env new file mode 100755 index 0000000..a31a74a --- /dev/null +++ b/scripts/sync-local-convex-env @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)" +VALUE_FILE="" + +readonly RUNTIME_ENV_NAMES=( + AUTH_AUTHENTIK_ID + AUTH_AUTHENTIK_SECRET + AUTH_AUTHENTIK_ISSUER + USESEND_API_KEY + USESEND_URL + USESEND_FROM_EMAIL +) +readonly DEPLOYMENT_ENV_NAMES=( + CONVEX_SELF_HOSTED_URL + CONVEX_SELF_HOSTED_ADMIN_KEY +) + +die() { printf 'sync-local-convex-env: %s\n' "$*" >&2; exit 1; } +cleanup() { + if [ -n "$VALUE_FILE" ] && [ -f "$VALUE_FILE" ]; then + rm -f "$VALUE_FILE" + fi +} +trap cleanup EXIT INT TERM HUP + +for name in "${DEPLOYMENT_ENV_NAMES[@]}" "${RUNTIME_ENV_NAMES[@]}"; do + [ -n "${!name:-}" ] || die "Missing required environment variable: $name" +done + +VALUE_FILE="$(mktemp "${TMPDIR:-/tmp}/convex-monorepo-convex-env.value.XXXXXX")" +chmod 600 "$VALUE_FILE" + +for name in "${RUNTIME_ENV_NAMES[@]}"; do + printf '%s' "${!name}" > "$VALUE_FILE" + ( + cd "$ROOT_DIR/packages/backend" + env -u CONVEX_DEPLOY_KEY -u CONVEX_DEPLOYMENT \ + CONVEX_SELF_HOSTED_URL="$CONVEX_SELF_HOSTED_URL" \ + CONVEX_SELF_HOSTED_ADMIN_KEY="$CONVEX_SELF_HOSTED_ADMIN_KEY" \ + bunx convex env set "$name" --from-file "$VALUE_FILE" >/dev/null + ) +done + +printf 'Local Convex runtime environment synchronized.\n' diff --git a/turbo.json b/turbo.json index dcdd51f..a65e8eb 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,5 @@ { - "$schema": "https://v2-9-18.turborepo.dev/schema.json", + "$schema": "https://v2-11-6.turborepo.dev/schema.json", "globalDependencies": [".infisical.json", ".local/*.env"], "globalEnv": [ "NODE_ENV", @@ -10,7 +10,9 @@ "PAYLOAD_DB_URL", "NEXT_PUBLIC_SITE_URL", "NEXT_PUBLIC_CONVEX_URL", - "NEXT_PUBLIC_PLAUSIBLE_URL", + "NEXT_PUBLIC_PLAUSIBLE_SRC", + "NEXT_PUBLIC_POSTHOG_KEY", + "NEXT_PUBLIC_POSTHOG_HOST", "NEXT_PUBLIC_SENTRY_DSN", "NEXT_PUBLIC_SENTRY_URL", "NEXT_PUBLIC_SENTRY_ORG", @@ -53,6 +55,7 @@ ], "globalPassThroughEnv": ["NODE_ENV"], "ui": "tui", + "agentGuidance": false, "tasks": { "topo": { "dependsOn": ["^topo"]